From patchwork Tue Jun 6 14:31:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 689824 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9431CC77B73 for ; Tue, 6 Jun 2023 14:32:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237813AbjFFOcQ (ORCPT ); Tue, 6 Jun 2023 10:32:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36730 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236092AbjFFOcO (ORCPT ); Tue, 6 Jun 2023 10:32:14 -0400 Received: from mail-wm1-x32b.google.com (mail-wm1-x32b.google.com [IPv6:2a00:1450:4864:20::32b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8D21910FD for ; Tue, 6 Jun 2023 07:32:04 -0700 (PDT) Received: by mail-wm1-x32b.google.com with SMTP id 5b1f17b1804b1-3f6e1393f13so51972855e9.0 for ; Tue, 06 Jun 2023 07:32:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1686061923; x=1688653923; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=G/RmLqBdqHlKB/fV0OvGO4bPEeaDKdfC66+DK/AUlm8=; b=uHI1NVueW/Wm5qhNtR28P71UiCHaIVk5+netraou9AR2FJeD26iWlBgE4zGA8Xqmw4 hpHBkk5+atMXnJ8XlmzqjfL3Bs7bavUoW6m+5aw2Zfv6ez4xK4TaH9ycFTCy7v9yJ3oC vmQRfJiUvm61KGMnytw6YVX6v7j53a6SSzGIR2hQIDdLk0G2Jj+8DH0k6twXRL+8P6/d aElkzHTfT6uFXgTIaXK+JY8bZo2tPe1RZbH1fMNhk8oAjg4n5jDyGl5rr1x8KDvhOt+J DEFcVl2Mx6oH/vHsaObAbVaoAu6pf3iD83eIdWyvypyp7KRJo69BUBEYPXF0gMULzqwi fqbw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686061923; x=1688653923; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=G/RmLqBdqHlKB/fV0OvGO4bPEeaDKdfC66+DK/AUlm8=; b=QDWX4Hnfdvc1uCOJd8IWnkJWw+hkQTy0WwV3F3Hajmd7uP9GLPTXX+FA1ZzQ4aM3rN OfSvfobGptalwyPjzI/Hqb5j66caKNccQfS6obBirYU2Y2DZ6BLuOM9LQ2SXEPaPYrk3 3xN+LRn/WmPKV/TR8eM+CbshGN6unD2i8xSD8OJ7k2qIcl3ePP0Q+a5dM/q4pyxmF1zS JeMWOmqjUCUsWMmXNOcLRfxH339gD44CaALpNpJ4ZgjFLJy+fbeqvg1wMdl0ju1BEKce HGqz7PsU6gi56+HPhDqc2P9CmK6+AnbJSwuSo13cbqOqatiR88GdltSWEGCJ92reIr1Y UOOw== X-Gm-Message-State: AC+VfDzdxrE4zCLUw7B/jLG6tM57JIiMuR16wflHmo1/X84BaJgcSl06 EgB4njBto2YN04jqFG+fIvwt3A== X-Google-Smtp-Source: ACHHUZ7hwY1Z3l5Yvc2X3Oz1LL3KoEWaAMM42b+UPLtpglXJY6hMFGUqOxJyIlVzjXL5CDVP8bRRBA== X-Received: by 2002:a7b:ca48:0:b0:3f4:28db:f5ff with SMTP id m8-20020a7bca48000000b003f428dbf5ffmr2197281wml.35.1686061923019; Tue, 06 Jun 2023 07:32:03 -0700 (PDT) Received: from arrakeen.starnux.net ([2a01:e0a:982:cbb0:52eb:f6ff:feb3:451a]) by smtp.gmail.com with ESMTPSA id fc14-20020a05600c524e00b003f61177faffsm3883600wmb.0.2023.06.06.07.32.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 07:32:02 -0700 (PDT) From: Neil Armstrong Date: Tue, 06 Jun 2023 16:31:56 +0200 Subject: [PATCH RFC 1/4] dt-bindings: input: document Goodix Berlin Touchscreen IC MIME-Version: 1.0 Message-Id: <20230606-topic-goodix-berlin-upstream-initial-v1-1-4a0741b8aefd@linaro.org> References: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> In-Reply-To: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bastien Nocera , Hans de Goede , Henrik Rydberg Cc: linux-input@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Neil Armstrong X-Mailer: b4 0.12.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=2520; i=neil.armstrong@linaro.org; h=from:subject:message-id; bh=+IS00VmbfdsOx5expv8gh1ojPzZ6FMwcyZcX5s15570=; b=owEBbQKS/ZANAwAKAXfc29rIyEnRAcsmYgBkf0Nf9Wmua32M+ZPjt3hEHjRmfw4gESMlRxDWbXXq 6dxqA4SJAjMEAAEKAB0WIQQ9U8YmyFYF/h30LIt33NvayMhJ0QUCZH9DXwAKCRB33NvayMhJ0d7FEA CzhWwc/mrRmSEptYgxIYR/jJoYyo5IDsYy4WdkOTeRnazmkjzRtdWaOSrQamQylY+GyALqzK3FBoUt T6vab4eQ3e8TVkae5GaUQM/pGaBP5KNtDky63yV1x1IgoLlPlXddGZQfD7YpfYF28VewjjSVfW+dJ/ it27CACjJO5Js5a7y6Niuw8FJ0zDpQ7BjYdRhtNY2hF7L5xyaE9/hBpEDhax+157e4KsxBqDPvFN2t eFsUB3rYI/iJ4P9lFmyGaDo7NTax8KKIYdwtJReNoka5c/DVufAnH5a03tMHfTsDNbU1C92XssH8lQ cUUgBPTopKIAG6ZzCe23vK9C9+ianwMMQ5Ts2OdpdLFOaE/wp1XPTQ59aUEe8eJE8fLFZ0B0ala2ko CfrkP5EGW41Js+BB+i+DxcoSvKzkLopv6WfKm/TIQmQu3FUuxIcra4zAaoEsVXRYQgENHUAtelFAhK aYmV39gmeCUBar9Sj5E4SGxW+JGxHyEorKNKwYIFhejdd+6TPPRxHuJ8XmiVoYyVygLKFEXjcUd7p1 kWBf6orPo0/38QXXex+Ccmr1quUr8OwVdkAtnDCVy07gC8VzPTCR5Hb1y1Rsow0LNXl6njSSsTDRpY jrXbTeN8gNFk+xOlowEoiIrcFrbJYziD5y1r6g4vbNd43HWLjXt7mLzGY6VA== X-Developer-Key: i=neil.armstrong@linaro.org; a=openpgp; fpr=89EC3D058446217450F22848169AB7B1A4CFF8AE Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Document the Goodix GT9916 wich is part of the "Berlin" serie of Touchscreen controllers IC from Goodix. Signed-off-by: Neil Armstrong --- .../bindings/input/touchscreen/goodix-berlin.yaml | 81 ++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/goodix-berlin.yaml b/Documentation/devicetree/bindings/input/touchscreen/goodix-berlin.yaml new file mode 100644 index 000000000000..4c24a541e919 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/goodix-berlin.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/goodix-berlin.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Goodix Belin series touchscreen controller + +maintainers: + - Neil Armstrong + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + enum: + - goodix,gt9916 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + reset-gpios: + maxItems: 1 + + avdd-supply: + description: Analog power supply regulator on AVDD pin + + vddio-supply: + description: GPIO power supply regulator on VDDIO pin + + spi-max-frequency: true + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-swapped-x-y: true + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + gt9916@5d { + compatible = "goodix,gt9916"; + reg = <0x5d>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; + }; + }; + - | + #include + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + num-cs = <1>; + cs-gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + gt9916@0 { + compatible = "goodix,gt9916"; + reg = <0>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_LEVEL_LOW>; + reset-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; + }; + }; + +... From patchwork Tue Jun 6 14:31:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 689823 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 726BDC7EE2F for ; Tue, 6 Jun 2023 14:32:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233632AbjFFOcp (ORCPT ); Tue, 6 Jun 2023 10:32:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36776 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237831AbjFFOcS (ORCPT ); Tue, 6 Jun 2023 10:32:18 -0400 Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8ED941707 for ; Tue, 6 Jun 2023 07:32:05 -0700 (PDT) Received: by mail-wm1-x32e.google.com with SMTP id 5b1f17b1804b1-3f735bfcbbbso25314515e9.2 for ; Tue, 06 Jun 2023 07:32:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1686061924; x=1688653924; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=aJmkENoeYIxcCZuPTERw706fBNCsnp0mTzF1iZ/txlU=; b=f2YTYba1h2fSRqLW92etH/3YXfLyAoxqEgs7ZSuWfQ5yrO9WfjOTzJcokzJMNleYyw 6S9G/FcbsFmN1+XX06sAj4Ldsfou/H4q8bYLjosc8KRRTy94A/q1maZfNTgo2jvje4R8 Nyxh/JW5mSx3Pix5dzQwuRPhOHAJk8m0u0bCz7UfTdiak+kGLxCmS1dV9lbyCjgfgKJL Nn821izDbDO/P6DimCrVNVWH0R3zp7/cr7NpOKF8EaoqszIS85WZVPKCYAQBEBlh/QN4 Rz3H2ZOAbKOqH9HrL6cCWMbPM4oWMN7AXbeEFBwCEOyQKbjJFeZpnPa9a0+AnnzTGb/b KW+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686061924; x=1688653924; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=aJmkENoeYIxcCZuPTERw706fBNCsnp0mTzF1iZ/txlU=; b=dkQHMlAcivNhn+k9TaSdGjnWbWVi+v2yrdEDjbU/HpKc4f4aRjoI8DFM/ulaZ/kLf9 6hOUWoWWLcIi39qyu4SgZqf5/nBba9yn32DiVP9t+qEmd5MadrUQw1UZZJO3656xBn/R KQ8ZyHISrFOeKtEvuL3lbLOu41C66lQLHcWWMlffeYGBsXsJUVH3kNo3X68PCZYUYJS1 vgbpEu8vSFlOEAsOvkwPznbVEFAmOSmyYq9JS7pE9Xwi3XsjImhZ2ieY7pBFfRlV3WBe it4AMXr0BCjK+enmB6NgUpPDZGaiAx+UiOdc4upgNjhKnTCqJQ6rJFYBf0ijZRszBmBC q7sA== X-Gm-Message-State: AC+VfDx2s3VFYn3J7KmiKCbLDd6ZcO7KPidy+cjzMiZ4wuBEA6Jje4Bg b3y8m/uI9XzYX9040zVJubxagg== X-Google-Smtp-Source: ACHHUZ6r0jlHi3M3MP5zsIEtxL59FdYYAaTl0OWPo9RGxpsPAxiZ4F2qVTlr5RxYcPzm+rUPEC0fTg== X-Received: by 2002:a7b:ce88:0:b0:3f6:1508:950c with SMTP id q8-20020a7bce88000000b003f61508950cmr2368404wmj.29.1686061923889; Tue, 06 Jun 2023 07:32:03 -0700 (PDT) Received: from arrakeen.starnux.net ([2a01:e0a:982:cbb0:52eb:f6ff:feb3:451a]) by smtp.gmail.com with ESMTPSA id fc14-20020a05600c524e00b003f61177faffsm3883600wmb.0.2023.06.06.07.32.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 07:32:03 -0700 (PDT) From: Neil Armstrong Date: Tue, 06 Jun 2023 16:31:57 +0200 Subject: [PATCH RFC 2/4] input: touchscreen: add core support for Goodix Berlin Touchscreen IC MIME-Version: 1.0 Message-Id: <20230606-topic-goodix-berlin-upstream-initial-v1-2-4a0741b8aefd@linaro.org> References: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> In-Reply-To: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bastien Nocera , Hans de Goede , Henrik Rydberg Cc: linux-input@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Neil Armstrong X-Mailer: b4 0.12.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=36191; i=neil.armstrong@linaro.org; h=from:subject:message-id; bh=ILK55+3rcQFd0dPRS7Pcqox8J1tft+54xSQfGmJYSr4=; b=owEBbQKS/ZANAwAKAXfc29rIyEnRAcsmYgBkf0NgFCd6v+wpQxp5kchOfulLoGz5MpwsssEtwB43 8MagbXWJAjMEAAEKAB0WIQQ9U8YmyFYF/h30LIt33NvayMhJ0QUCZH9DYAAKCRB33NvayMhJ0b3+D/ 0fgTdSrvwenu+eY4E/rWFOZ7ISwN5s6NbSU+KZI2thJefxnQja2mZ5Z7PaM6giHBzSmT7inXwCsW6S BGKa1FZzH5lmf53WjraU99TULX6srnV9wft4eBW13xmyztc2ykejwC7QnJhaV8HYplzaoSqMF9KPsI Y1YlT0aB0UK42ybrjaD7+kZU8U5msJw4Ym5Vi7h5WLQlHzdjeMRv3mzSlkSsoqVyGCk198Tq+mh85Y uhZ29LwJj8f0nZupAabozfzOitLcCLqYvtU05K2PxqiMojMvlJqCJ6ietBIUiR4CbiBJvPEodq0wAl QeYNdAwlyhBiOmQYxl4/2bJmuHoOA9V3veg+yj01wAT9rgSlazED+2pdIlFrMCHH+hmufbbtvSEB5H 0ou5ts7EEyUmpjC373+WLs0XEeduIDJDOoAYzrC4nOPVq7znx1RIA9CYaecLgo2hfrTQObS1PTJ6A/ UrY1fpKeyfcV37vu6f418xv02Cjt6cIqdfDIqRmHfUxrhEZ6M8ncB9DZLsLGZYbJoWM9vVb8gAUw3Q scfhnIdkLKdPWiYtNuAjfGI+vwHYVjIyntc5zcCak+tHJhMEb5TqCo0YW2yFG/aHvKcQbrCMN5tYqk LXwHkA3l4PRegWlspPt0v4g6hHhwgiD9J+q9DxPiP9qJfq87O9ixq/vi5mxg== X-Developer-Key: i=neil.armstrong@linaro.org; a=openpgp; fpr=89EC3D058446217450F22848169AB7B1A4CFF8AE Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Add initial support for the new Goodix "Berlin" touchscreen ICs. These touchscreen ICs support SPI, I2C and I3C interface, up to 10 finger touch, stylus and gestures events. This initial driver is derived from the Goodix goodix_ts_berlin available at [1] and [2] and only supports the GT9916 IC present on the Qualcomm SM8550 MTP & QRD touch panel. The current implementation only supports BerlinD, aka GT9916. Support for advanced features like: - Firmware & config update - Stylus events - Gestures events - Previous revisions support (BerlinA or BerlinB) is not included in current version. The current support will work with currently flashed firmware and config, and bail out if firmware or config aren't flashed yet. [1] https://github.com/goodix/goodix_ts_berlin [2] https://git.codelinaro.org/clo/la/platform/vendor/opensource/touch-drivers Signed-off-by: Neil Armstrong --- drivers/input/touchscreen/Kconfig | 5 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/goodix_berlin.h | 228 ++++++ drivers/input/touchscreen/goodix_berlin_core.c | 935 +++++++++++++++++++++++++ 4 files changed, 1169 insertions(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index c2cbd332af1d..1a6f6f6da991 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -416,6 +416,11 @@ config TOUCHSCREEN_GOODIX To compile this driver as a module, choose M here: the module will be called goodix. +config TOUCHSCREEN_GOODIX_BERLIN_CORE + depends on GPIOLIB || COMPILE_TEST + depends on REGMAP + tristate + config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 159cd5136fdb..29cdb042e104 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/goodix_berlin.h b/drivers/input/touchscreen/goodix_berlin.h new file mode 100644 index 000000000000..83f16201c354 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_berlin_berlin driver. + */ + +#ifndef __GOODIX_BERLIN_H_ +#define __GOODIX_BERLIN_H_ + +#include +#include +#include +#include + +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_MAX_FRAMEDATA_LEN 1700 + +#define GOODIX_NORMAL_RESET_DELAY_MS 100 +#define GOODIX_HOLD_CPU_RESET_DELAY_MS 5 + +enum CHECKSUM_MODE { + CHECKSUM_MODE_U8_LE, + CHECKSUM_MODE_U16_LE, +}; + +#define MAX_SCAN_FREQ_NUM 8 +#define MAX_SCAN_RATE_NUM 8 +#define MAX_FREQ_NUM_STYLUS 8 + +#define IRQ_EVENT_HEAD_LEN 8 +#define BYTES_PER_POINT 8 +#define COOR_DATA_CHECKSUM_SIZE 2 + +#define GOODIX_TOUCH_EVENT BIT(7) +#define GOODIX_REQUEST_EVENT BIT(6) +#define GOODIX_GESTURE_EVENT BIT(5) + +#define POINT_TYPE_STYLUS_HOVER 0x01 +#define POINT_TYPE_STYLUS 0x03 + +#define DEV_CONFIRM_VAL 0xAA +#define BOOTOPTION_ADDR 0x10000 +#define FW_VERSION_INFO_ADDR 0x10014 + +#define GOODIX_IC_INFO_MAX_LEN 1024 +#define GOODIX_IC_INFO_ADDR 0x10070 + +enum brl_request_code { + BRL_REQUEST_CODE_CONFIG = 1, + BRL_REQUEST_CODE_REF_ERR = 2, + BRL_REQUEST_CODE_RESET = 3, + BRL_REQUEST_CODE_CLOCK = 4, +}; + +struct goodix_fw_version { + u8 rom_pid[6]; /* rom PID */ + u8 rom_vid[3]; /* Mask VID */ + u8 rom_vid_reserved; + u8 patch_pid[8]; /* Patch PID */ + u8 patch_vid[4]; /* Patch VID */ + u8 patch_vid_reserved; + u8 sensor_id; + u8 reserved[2]; + u16 checksum; +} __packed; + +struct goodix_ic_info_version { + u8 info_customer_id; + u8 info_version_id; + u8 ic_die_id; + u8 ic_version_id; + u32 config_id; + u8 config_version; + u8 frame_data_customer_id; + u8 frame_data_version_id; + u8 touch_data_customer_id; + u8 touch_data_version_id; + u8 reserved[3]; +} __packed; + +struct goodix_ic_info_feature { /* feature info*/ + u16 freqhop_feature; + u16 calibration_feature; + u16 gesture_feature; + u16 side_touch_feature; + u16 stylus_feature; +} __packed; + +struct goodix_ic_info_param { /* param */ + u8 drv_num; + u8 sen_num; + u8 button_num; + u8 force_num; + u8 active_scan_rate_num; + u16 active_scan_rate[MAX_SCAN_RATE_NUM]; + u8 mutual_freq_num; + u16 mutual_freq[MAX_SCAN_FREQ_NUM]; + u8 self_tx_freq_num; + u16 self_tx_freq[MAX_SCAN_FREQ_NUM]; + u8 self_rx_freq_num; + u16 self_rx_freq[MAX_SCAN_FREQ_NUM]; + u8 stylus_freq_num; + u16 stylus_freq[MAX_FREQ_NUM_STYLUS]; +} __packed; + +struct goodix_ic_info_misc { /* other data */ + u32 cmd_addr; + u16 cmd_max_len; + u32 cmd_reply_addr; + u16 cmd_reply_len; + u32 fw_state_addr; + u16 fw_state_len; + u32 fw_buffer_addr; + u16 fw_buffer_max_len; + u32 frame_data_addr; + u16 frame_data_head_len; + u16 fw_attr_len; + u16 fw_log_len; + u8 pack_max_num; + u8 pack_compress_version; + u16 stylus_struct_len; + u16 mutual_struct_len; + u16 self_struct_len; + u16 noise_struct_len; + u32 touch_data_addr; + u16 touch_data_head_len; + u16 point_struct_len; + u16 reserved1; + u16 reserved2; + u32 mutual_rawdata_addr; + u32 mutual_diffdata_addr; + u32 mutual_refdata_addr; + u32 self_rawdata_addr; + u32 self_diffdata_addr; + u32 self_refdata_addr; + u32 iq_rawdata_addr; + u32 iq_refdata_addr; + u32 im_rawdata_addr; + u16 im_readata_len; + u32 noise_rawdata_addr; + u16 noise_rawdata_len; + u32 stylus_rawdata_addr; + u16 stylus_rawdata_len; + u32 noise_data_addr; + u32 esd_addr; +} __packed; + +struct goodix_ic_info { + u16 length; + struct goodix_ic_info_version version; + struct goodix_ic_info_feature feature; + struct goodix_ic_info_param parm; + struct goodix_ic_info_misc misc; +} __packed; + +/* interrupt event type */ +enum ts_event_type { + EVENT_INVALID, + EVENT_TOUCH, /* finger touch event */ + EVENT_REQUEST, +}; + +enum ts_request_type { + REQUEST_TYPE_RESET, +}; + +enum touch_point_status { + TS_NONE, + TS_RELEASE, + TS_TOUCH, +}; + +/* coordinate package */ +struct goodix_berlin_coords { + int status; /* NONE, RELEASE, TOUCH */ + unsigned int x, y, w, p; +}; + +/* touch event data */ +struct goodix_touch_data { + int touch_num; + struct goodix_berlin_coords coords[GOODIX_MAX_TOUCH]; +}; + +/* touch event struct */ +struct goodix_berlin_event { + enum ts_event_type event_type; + u8 request_code; /* represent the request type */ + struct goodix_touch_data touch_data; +}; + +struct goodix_berlin_core { + struct device *dev; + struct regmap *regmap; + + struct regulator *avdd; + struct regulator *iovdd; + struct gpio_desc *reset_gpio; + + struct touchscreen_properties props; + + struct goodix_fw_version fw_version; + struct goodix_ic_info ic_info; + + struct input_dev *input_dev; + struct goodix_berlin_event ts_event; + + int irq; + + struct dentry *debugfs_root; +}; + +u32 goodix_append_checksum(u8 *data, int len, int mode); +int checksum_cmp(const u8 *data, int size, int mode); +int is_risk_data(const u8 *data, int size); +u32 goodix_get_file_config_id(u8 *ic_config); +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data); + +int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id, + struct regmap *regmap); +int goodix_berlin_remove(struct device *dev); + +extern const struct dev_pm_ops goodix_berlin_pm_ops; + +#endif diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c new file mode 100644 index 000000000000..0719690a5bd3 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_core.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + */ +#include +#include +#include +#include + +#include "goodix_berlin.h" + +/* + * Goodix "Berlin" Touchscreen ID driver + * + * Currently only handles Multitouch events with already + * programmed firmware and "config" for "Revision D" Berlin IC. + * + * Support is missing for: + * - ESD Management + * - Firmware update/flashing + * - "Config" update/flashing + * - PEN Events + * - Gesture Events + * - Support for older revisions (A, B & C) + */ + +static bool goodix_berlin_check_checksum(const u8 *data, int size) +{ + u32 cal_checksum = 0; + u32 r_checksum = 0; + u32 i; + + if (size < COOR_DATA_CHECKSUM_SIZE) + return false; + + for (i = 0; i < size - COOR_DATA_CHECKSUM_SIZE; i++) + cal_checksum += data[i]; + + r_checksum += data[i++]; + r_checksum += (data[i] << 8); + + return (cal_checksum & 0xFFFF) == r_checksum; +} + +static bool goodix_berlin_is_risk_data(struct goodix_berlin_core *cd, + const u8 *data, int size) +{ + int zero_count = 0; + int ff_count = 0; + int i; + + for (i = 0; i < size; i++) { + if (data[i] == 0) + zero_count++; + else if (data[i] == 0xff) + ff_count++; + } + if (zero_count == size || ff_count == size) { + dev_warn(cd->dev, "warning data is all %s\n", + zero_count == size ? "zero" : "0xff"); + return true; + } + + return false; +} + +static int goodix_berlin_dev_confirm(struct goodix_berlin_core *cd) +{ + u8 tx_buf[8] = { 0 }; + u8 rx_buf[8] = { 0 }; + int retry = 3; + int ret = 0; + + memset(tx_buf, DEV_CONFIRM_VAL, sizeof(tx_buf)); + while (retry--) { + ret = regmap_raw_write(cd->regmap, BOOTOPTION_ADDR, tx_buf, + sizeof(tx_buf)); + if (ret < 0) + return ret; + ret = regmap_raw_read(cd->regmap, BOOTOPTION_ADDR, rx_buf, + sizeof(rx_buf)); + if (ret < 0) + return ret; + if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) + break; + usleep_range(5000, 5100); + } + + if (retry < 0) { + ret = -EINVAL; + dev_err(cd->dev, "device confirm failed, rx_buf: %*ph\n", 8, rx_buf); + } + + return ret; +} + +static int goodix_berlin_power_on(struct goodix_berlin_core *cd, bool on) +{ + int ret = 0; + + if (on) { + ret = regulator_enable(cd->iovdd); + if (ret < 0) { + dev_err(cd->dev, "Failed to enable iovdd: %d\n", ret); + goto power_off; + } + + usleep_range(3000, 3100); + + ret = regulator_enable(cd->avdd); + if (ret < 0) { + dev_err(cd->dev, "Failed to enable avdd: %d\n", ret); + goto power_off; + } + + usleep_range(15000, 15100); + + gpiod_set_value(cd->reset_gpio, 0); + + usleep_range(4000, 4100); + + ret = goodix_berlin_dev_confirm(cd); + if (ret < 0) + goto power_off; + + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + + return 0; + } + +power_off: + gpiod_set_value(cd->reset_gpio, 1); + regulator_disable(cd->iovdd); + regulator_disable(cd->avdd); + return ret; +} + +static int goodix_berlin_do_suspend(struct goodix_berlin_core *cd) +{ + if (goodix_berlin_power_on(cd, 0)) + dev_err(cd->dev, "failed power off\n"); + + return 0; +} + +static int goodix_berlin_do_resume(struct goodix_berlin_core *cd) +{ + int ret = 0; + + ret = goodix_berlin_power_on(cd, 1); + if (ret) { + dev_err(cd->dev, "failed power on\n"); + return ret; + } + + return ret; +} + +static int goodix_berlin_reset(struct goodix_berlin_core *cd, int delay) +{ + gpiod_set_value(cd->reset_gpio, 1); + usleep_range(2000, 2100); + gpiod_set_value(cd->reset_gpio, 0); + + if (delay < 20) + usleep_range(delay * 1000, delay * 1000 + 100); + else + msleep(delay); + + return 0; +} + +static int goodix_berlin_irq_enable(struct goodix_berlin_core *cd, bool enable) +{ + if (enable) + enable_irq(cd->irq); + else + disable_irq(cd->irq); + + return 0; +} + +static int goodix_berlin_read_version(struct goodix_berlin_core *cd, + struct goodix_fw_version *version) +{ + u8 buf[sizeof(struct goodix_fw_version)] = { 0 }; + int ret, i; + + for (i = 0; i < 2; i++) { + ret = regmap_raw_read(cd->regmap, FW_VERSION_INFO_ADDR, buf, sizeof(buf)); + if (ret) { + dev_dbg(cd->dev, "read fw version: %d, retry %d\n", ret, i); + usleep_range(5000, 5100); + continue; + } + + if (goodix_berlin_check_checksum(buf, sizeof(buf))) + break; + + dev_dbg(cd->dev, "invalid fw version: checksum error\n"); + + ret = -EINVAL; + usleep_range(10000, 11000); + } + + if (ret) { + dev_err(cd->dev, "failed to get fw version"); + return ret; + } + + memcpy(version, buf, sizeof(*version)); + + return 0; +} + +static int goodix_berlin_fw_version_show(struct seq_file *s, void *unused) +{ + struct goodix_berlin_core *cd = s->private; + struct goodix_fw_version *version = &cd->fw_version; + u8 temp_pid[9] = { 0 }; + + memcpy(temp_pid, version->rom_pid, sizeof(version->rom_pid)); + temp_pid[sizeof(version->rom_pid)] = '\0'; + + seq_printf(s, "rom_pid: %s\n", temp_pid); + seq_printf(s, "rom_vid: %*ph\n", + (int)sizeof(version->rom_vid), + version->rom_vid); + + memcpy(temp_pid, version->patch_pid, sizeof(version->patch_pid)); + temp_pid[sizeof(version->patch_pid)] = '\0'; + + seq_printf(s, "patch_pid: %s\n", temp_pid); + seq_printf(s, "patch_vid: %*ph\n", + (int)sizeof(version->patch_vid), + version->patch_vid); + seq_printf(s, "sensor_id: %d\n", + version->sensor_id); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(goodix_berlin_fw_version); + +static int goodix_berlin_convert_ic_info(struct goodix_berlin_core *cd, + struct goodix_ic_info *info, + const u8 *data) +{ + int i; + struct goodix_ic_info_version *version = &info->version; + struct goodix_ic_info_feature *feature = &info->feature; + struct goodix_ic_info_param *parm = &info->parm; + struct goodix_ic_info_misc *misc = &info->misc; + + info->length = le16_to_cpup((__le16 *)data); + + data += 2; + memcpy(version, data, sizeof(*version)); + version->config_id = le32_to_cpu(version->config_id); + + data += sizeof(struct goodix_ic_info_version); + memcpy(feature, data, sizeof(*feature)); + feature->freqhop_feature = le16_to_cpu(feature->freqhop_feature); + feature->calibration_feature = + le16_to_cpu(feature->calibration_feature); + feature->gesture_feature = le16_to_cpu(feature->gesture_feature); + feature->side_touch_feature = le16_to_cpu(feature->side_touch_feature); + feature->stylus_feature = le16_to_cpu(feature->stylus_feature); + + data += sizeof(struct goodix_ic_info_feature); + parm->drv_num = *(data++); + parm->sen_num = *(data++); + parm->button_num = *(data++); + parm->force_num = *(data++); + parm->active_scan_rate_num = *(data++); + + if (parm->active_scan_rate_num > MAX_SCAN_RATE_NUM) { + dev_err(cd->dev, "invalid scan rate num %d > %d\n", + parm->active_scan_rate_num, MAX_SCAN_RATE_NUM); + return -EINVAL; + } + + for (i = 0; i < parm->active_scan_rate_num; i++) + parm->active_scan_rate[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->active_scan_rate_num * 2; + parm->mutual_freq_num = *(data++); + + if (parm->mutual_freq_num > MAX_SCAN_FREQ_NUM) { + dev_err(cd->dev, "invalid mntual freq num %d > %d\n", + parm->mutual_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + + for (i = 0; i < parm->mutual_freq_num; i++) + parm->mutual_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->mutual_freq_num * 2; + parm->self_tx_freq_num = *(data++); + + if (parm->self_tx_freq_num > MAX_SCAN_FREQ_NUM) { + dev_err(cd->dev, "invalid tx freq num %d > %d\n", + parm->self_tx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + + for (i = 0; i < parm->self_tx_freq_num; i++) + parm->self_tx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->self_tx_freq_num * 2; + parm->self_rx_freq_num = *(data++); + + if (parm->self_rx_freq_num > MAX_SCAN_FREQ_NUM) { + dev_err(cd->dev, "invalid rx freq num %d > %d\n", + parm->self_rx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + + for (i = 0; i < parm->self_rx_freq_num; i++) + parm->self_rx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->self_rx_freq_num * 2; + parm->stylus_freq_num = *(data++); + + if (parm->stylus_freq_num > MAX_FREQ_NUM_STYLUS) { + dev_err(cd->dev, "invalid stylus freq num %d > %d\n", + parm->stylus_freq_num, MAX_FREQ_NUM_STYLUS); + return -EINVAL; + } + + for (i = 0; i < parm->stylus_freq_num; i++) + parm->stylus_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->stylus_freq_num * 2; + memcpy(misc, data, sizeof(*misc)); + misc->cmd_addr = le32_to_cpu(misc->cmd_addr); + misc->cmd_max_len = le16_to_cpu(misc->cmd_max_len); + misc->cmd_reply_addr = le32_to_cpu(misc->cmd_reply_addr); + misc->cmd_reply_len = le16_to_cpu(misc->cmd_reply_len); + misc->fw_state_addr = le32_to_cpu(misc->fw_state_addr); + misc->fw_state_len = le16_to_cpu(misc->fw_state_len); + misc->fw_buffer_addr = le32_to_cpu(misc->fw_buffer_addr); + misc->fw_buffer_max_len = le16_to_cpu(misc->fw_buffer_max_len); + misc->frame_data_addr = le32_to_cpu(misc->frame_data_addr); + misc->frame_data_head_len = le16_to_cpu(misc->frame_data_head_len); + + misc->fw_attr_len = le16_to_cpu(misc->fw_attr_len); + misc->fw_log_len = le16_to_cpu(misc->fw_log_len); + misc->stylus_struct_len = le16_to_cpu(misc->stylus_struct_len); + misc->mutual_struct_len = le16_to_cpu(misc->mutual_struct_len); + misc->self_struct_len = le16_to_cpu(misc->self_struct_len); + misc->noise_struct_len = le16_to_cpu(misc->noise_struct_len); + misc->touch_data_addr = le32_to_cpu(misc->touch_data_addr); + misc->touch_data_head_len = le16_to_cpu(misc->touch_data_head_len); + misc->point_struct_len = le16_to_cpu(misc->point_struct_len); + misc->mutual_rawdata_addr = le32_to_cpu(misc->mutual_rawdata_addr); + misc->mutual_diffdata_addr = le32_to_cpu(misc->mutual_diffdata_addr); + misc->mutual_refdata_addr = le32_to_cpu(misc->mutual_refdata_addr); + misc->self_rawdata_addr = le32_to_cpu(misc->self_rawdata_addr); + misc->self_diffdata_addr = le32_to_cpu(misc->self_diffdata_addr); + misc->self_refdata_addr = le32_to_cpu(misc->self_refdata_addr); + misc->iq_rawdata_addr = le32_to_cpu(misc->iq_rawdata_addr); + misc->iq_refdata_addr = le32_to_cpu(misc->iq_refdata_addr); + misc->im_rawdata_addr = le32_to_cpu(misc->im_rawdata_addr); + misc->im_readata_len = le16_to_cpu(misc->im_readata_len); + misc->noise_rawdata_addr = le32_to_cpu(misc->noise_rawdata_addr); + misc->noise_rawdata_len = le16_to_cpu(misc->noise_rawdata_len); + misc->stylus_rawdata_addr = le32_to_cpu(misc->stylus_rawdata_addr); + misc->stylus_rawdata_len = le16_to_cpu(misc->stylus_rawdata_len); + misc->noise_data_addr = le32_to_cpu(misc->noise_data_addr); + misc->esd_addr = le32_to_cpu(misc->esd_addr); + + return 0; +} + +/* Convert to debugfs */ +static int goodix_berlin_ic_info_show(struct seq_file *s, void *unused) +{ + struct goodix_berlin_core *cd = s->private; + struct goodix_ic_info_version *version = &cd->ic_info.version; + struct goodix_ic_info_feature *feature = &cd->ic_info.feature; + struct goodix_ic_info_param *parm = &cd->ic_info.parm; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + + seq_printf(s, "ic_info_length: %d\n", cd->ic_info.length); + seq_printf(s, "info_customer_id: 0x%01X\n", + version->info_customer_id); + seq_printf(s, "info_version_id: 0x%01X\n", + version->info_version_id); + seq_printf(s, "ic_die_id: 0x%01X\n", + version->ic_die_id); + seq_printf(s, "ic_version_id: 0x%01X\n", + version->ic_version_id); + seq_printf(s, "config_id: 0x%4X\n", + version->config_id); + seq_printf(s, "config_version: 0x%01X\n", + version->config_version); + seq_printf(s, "frame_data_customer_id: 0x%01X\n", + version->frame_data_customer_id); + seq_printf(s, "frame_data_version_id: 0x%01X\n", + version->frame_data_version_id); + seq_printf(s, "touch_data_customer_id: 0x%01X\n", + version->touch_data_customer_id); + seq_printf(s, "touch_data_version_id: 0x%01X\n", + version->touch_data_version_id); + seq_printf(s, "freqhop_feature: 0x%04X\n", + feature->freqhop_feature); + seq_printf(s, "calibration_feature: 0x%04X\n", + feature->calibration_feature); + seq_printf(s, "gesture_feature: 0x%04X\n", + feature->gesture_feature); + seq_printf(s, "side_touch_feature: 0x%04X\n", + feature->side_touch_feature); + seq_printf(s, "stylus_feature: 0x%04X\n", + feature->stylus_feature); + seq_printf(s, "Drv*Sen,Button,Force num: %d x %d, %d, %d\n", + parm->drv_num, parm->sen_num, parm->button_num, parm->force_num); + seq_printf(s, "Cmd: 0x%04X, %d\n", + misc->cmd_addr, misc->cmd_max_len); + seq_printf(s, "Cmd-Reply: 0x%04X, %d\n", + misc->cmd_reply_addr, misc->cmd_reply_len); + seq_printf(s, "FW-State: 0x%04X, %d\n", + misc->fw_state_addr, misc->fw_state_len); + seq_printf(s, "FW-Buffer: 0x%04X, %d\n", + misc->fw_buffer_addr, misc->fw_buffer_max_len); + seq_printf(s, "Touch-Data: 0x%04X, %d\n", + misc->touch_data_addr, misc->touch_data_head_len); + seq_printf(s, "point_struct_len: %d\n", + misc->point_struct_len); + seq_printf(s, "mutual_rawdata_addr: 0x%04X\n", + misc->mutual_rawdata_addr); + seq_printf(s, "mutual_diffdata_addr: 0x%04X\n", + misc->mutual_diffdata_addr); + seq_printf(s, "self_rawdata_addr: 0x%04X\n", + misc->self_rawdata_addr); + seq_printf(s, "self_diffdata_addr: 0x%04X\n", + misc->self_diffdata_addr); + seq_printf(s, "stylus_rawdata_addr: 0x%04X, %d\n", + misc->stylus_rawdata_addr, misc->stylus_rawdata_len); + seq_printf(s, "esd_addr: 0x%04X\n", + misc->esd_addr); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(goodix_berlin_ic_info); + +static int goodix_berlin_get_ic_info(struct goodix_berlin_core *cd, + struct goodix_ic_info *ic_info) +{ + int ret, i; + u16 length = 0; + u32 ic_addr; + u8 afe_data[GOODIX_IC_INFO_MAX_LEN] = { 0 }; + + ic_addr = GOODIX_IC_INFO_ADDR; + + for (i = 0; i < 3; i++) { + ret = regmap_raw_read(cd->regmap, ic_addr, (u8 *)&length, sizeof(length)); + if (ret) { + dev_info(cd->dev, "failed get ic info length, %d\n", ret); + usleep_range(5000, 5100); + continue; + } + + length = le16_to_cpu(length); + if (length >= GOODIX_IC_INFO_MAX_LEN) { + dev_info(cd->dev, "invalid ic info length %d, retry %d\n", length, i); + continue; + } + + ret = regmap_raw_read(cd->regmap, ic_addr, afe_data, length); + if (ret) { + dev_info(cd->dev, "failed get ic info data, %d\n", ret); + usleep_range(5000, 5100); + continue; + } + + /* judge whether the data is valid */ + if (goodix_berlin_is_risk_data(cd, (const uint8_t *)afe_data, + length)) { + dev_info(cd->dev, "fw info data invalid\n"); + usleep_range(5000, 5100); + continue; + } + + if (!goodix_berlin_check_checksum((const uint8_t *)afe_data, length)) { + dev_info(cd->dev, "fw info checksum error\n"); + usleep_range(5000, 5100); + continue; + } + + break; + } + if (i == 3) { + dev_err(cd->dev, "failed get ic info\n"); + return -EINVAL; + } + + ret = goodix_berlin_convert_ic_info(cd, ic_info, afe_data); + if (ret) { + dev_err(cd->dev, "error converting ic info\n"); + return ret; + } + + /* check some key info */ + if (!ic_info->misc.cmd_addr || !ic_info->misc.fw_buffer_addr || + !ic_info->misc.touch_data_addr) { + dev_err(cd->dev, "cmd_addr fw_buf_addr and touch_data_addr is null\n"); + return -EINVAL; + } + + return 0; +} + +static int goodix_berlin_after_event_handler(struct goodix_berlin_core *cd) +{ + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + u8 sync_clean = 0; + + return regmap_raw_write(cd->regmap, misc->touch_data_addr, &sync_clean, 1); +} + +static void goodix_berlin_parse_finger(struct goodix_berlin_core *cd, + struct goodix_touch_data *touch_data, + u8 *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_EVENT_HEAD_LEN]; + + for (i = 0; i < touch_num; i++) { + id = (coor_data[0] >> 4) & 0x0F; + + if (id >= GOODIX_MAX_TOUCH) { + dev_warn(cd->dev, "invalid finger id %d\n", id); + + touch_data->touch_num = 0; + return; + } + + x = le16_to_cpup((__le16 *)(coor_data + 2)); + y = le16_to_cpup((__le16 *)(coor_data + 4)); + w = le16_to_cpup((__le16 *)(coor_data + 6)); + + touch_data->coords[id].status = TS_TOUCH; + touch_data->coords[id].x = x; + touch_data->coords[id].y = y; + touch_data->coords[id].w = w; + + coor_data += BYTES_PER_POINT; + } + + touch_data->touch_num = touch_num; +} + +static int goodix_berlin_touch_handler(struct goodix_berlin_core *cd, + u8 *pre_buf, u32 pre_buf_len) +{ + static u8 buffer[IRQ_EVENT_HEAD_LEN + BYTES_PER_POINT * GOODIX_MAX_TOUCH + 2]; + struct goodix_touch_data *touch_data = &cd->ts_event.touch_data; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + u8 point_type = 0; + u8 touch_num = 0; + int ret = 0; + + /* clean event buffer */ + memset(&cd->ts_event, 0, sizeof(cd->ts_event)); + + /* copy pre-data to buffer */ + memcpy(buffer, pre_buf, pre_buf_len); + + touch_num = buffer[2] & 0x0F; + + if (touch_num > GOODIX_MAX_TOUCH) { + dev_warn(cd->dev, "invalid touch num %d\n", touch_num); + return -EINVAL; + } + + /* read more data if more than 2 touch events */ + if (unlikely(touch_num > 2)) { + ret = regmap_raw_read(cd->regmap, + misc->touch_data_addr + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 2) * BYTES_PER_POINT); + if (ret) { + dev_err(cd->dev, "failed get touch data\n"); + return ret; + } + } + + /* read done */ + goodix_berlin_after_event_handler(cd); + + if (touch_num == 0) + return 0; + + point_type = buffer[IRQ_EVENT_HEAD_LEN] & 0x0F; + + /* TOFIX: Not handled */ + if (point_type == POINT_TYPE_STYLUS || point_type == POINT_TYPE_STYLUS_HOVER) { + dev_warn_once(cd->dev, "Stylus event type not handled\n"); + return 0; + } + + if (!goodix_berlin_check_checksum(&buffer[IRQ_EVENT_HEAD_LEN], + touch_num * BYTES_PER_POINT + 2)) { + dev_dbg(cd->dev, "touch data checksum error\n"); + dev_dbg(cd->dev, "data: %*ph\n", + touch_num * BYTES_PER_POINT + 2, &buffer[IRQ_EVENT_HEAD_LEN]); + return -EINVAL; + } + + cd->ts_event.event_type = EVENT_TOUCH; + goodix_berlin_parse_finger(cd, touch_data, buffer, touch_num); + + return 0; +} + +static int goodix_berlin_event_handler(struct goodix_berlin_core *cd) +{ + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + int pre_read_len; + u8 pre_buf[32]; + u8 event_status; + int ret; + + pre_read_len = IRQ_EVENT_HEAD_LEN + BYTES_PER_POINT * 2 + + COOR_DATA_CHECKSUM_SIZE; + + ret = regmap_raw_read(cd->regmap, misc->touch_data_addr, pre_buf, + pre_read_len); + if (ret) { + dev_err(cd->dev, "failed get event head data\n"); + return ret; + } + + if (pre_buf[0] == 0x00) + return -EINVAL; + + if (!goodix_berlin_check_checksum(pre_buf, IRQ_EVENT_HEAD_LEN)) { + dev_warn(cd->dev, "touch head checksum err : %*ph\n", + IRQ_EVENT_HEAD_LEN, pre_buf); + return -EINVAL; + } + + event_status = pre_buf[0]; + if (event_status & GOODIX_TOUCH_EVENT) + return goodix_berlin_touch_handler(cd, pre_buf, pre_read_len); + + if (event_status & GOODIX_REQUEST_EVENT) { + cd->ts_event.event_type = EVENT_REQUEST; + if (pre_buf[2] == BRL_REQUEST_CODE_RESET) + cd->ts_event.request_code = REQUEST_TYPE_RESET; + else + dev_warn(cd->dev, "unsupported request code 0x%x\n", pre_buf[2]); + } + + goodix_berlin_after_event_handler(cd); + + return 0; +} + +static void goodix_berlin_report_finger(struct goodix_berlin_core *cd) +{ + struct goodix_touch_data *touch_data = &cd->ts_event.touch_data; + int i; + + mutex_lock(&cd->input_dev->mutex); + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (touch_data->coords[i].status == TS_TOUCH) { + dev_dbg(cd->dev, "report: id[%d], x %d, y %d, w %d\n", i, + touch_data->coords[i].x, + touch_data->coords[i].y, + touch_data->coords[i].w); + + input_mt_slot(cd->input_dev, i); + input_mt_report_slot_state(cd->input_dev, + MT_TOOL_FINGER, true); + touchscreen_report_pos(cd->input_dev, &cd->props, + touch_data->coords[i].x, + touch_data->coords[i].y, true); + input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR, + touch_data->coords[i].w); + } else { + input_mt_slot(cd->input_dev, i); + input_mt_report_slot_state(cd->input_dev, + MT_TOOL_FINGER, false); + } + } + + input_mt_sync_frame(cd->input_dev); + input_sync(cd->input_dev); + + mutex_unlock(&cd->input_dev->mutex); +} + +static int goodix_berlin_request_handle(struct goodix_berlin_core *cd) +{ + /* TOFIX: Handle more request codes */ + if (cd->ts_event.request_code != REQUEST_TYPE_RESET) { + dev_info(cd->dev, "can't handle request type 0x%x\n", + cd->ts_event.request_code); + return -EINVAL; + } + + return goodix_berlin_reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); +} + +static irqreturn_t goodix_berlin_threadirq_func(int irq, void *data) +{ + struct goodix_berlin_core *cd = data; + int ret; + + ret = goodix_berlin_event_handler(cd); + if (likely(!ret)) { + switch (cd->ts_event.event_type) { + case EVENT_TOUCH: + goodix_berlin_report_finger(cd); + break; + + case EVENT_REQUEST: + goodix_berlin_request_handle(cd); + break; + + /* TOFIX: Handle more request types */ + case EVENT_INVALID: + break; + } + } + + return IRQ_HANDLED; +} + +static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd, + const struct input_id *id) +{ + struct input_dev *input_dev = devm_input_allocate_device(cd->dev); + + if (!input_dev) + return -ENOMEM; + + cd->input_dev = input_dev; + input_set_drvdata(input_dev, cd); + + input_dev->name = "Goodix Berlin Capacitive TouchScreen"; + input_dev->phys = "input/ts"; + input_dev->dev.parent = cd->dev; + + memcpy(&input_dev->id, id, sizeof(*id)); + + /* set input parameters */ + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_X, 0, SZ_64K - 1, 0, 0); + input_set_abs_params(cd->input_dev, ABS_MT_POSITION_Y, 0, SZ_64K - 1, 0, 0); + input_set_abs_params(cd->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + /* Override with DT */ + touchscreen_parse_properties(cd->input_dev, true, &cd->props); + + input_mt_init_slots(cd->input_dev, GOODIX_MAX_TOUCH, INPUT_MT_DIRECT); + + return input_register_device(cd->input_dev); +} + +static void goodix_berlin_release_connects(struct goodix_berlin_core *cd) +{ + struct input_dev *input_dev = cd->input_dev; + int i; + + mutex_lock(&input_dev->mutex); + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + input_mt_sync_frame(input_dev); + input_sync(input_dev); + + mutex_unlock(&input_dev->mutex); +} + +static int goodix_berlin_suspend(struct goodix_berlin_core *cd) +{ + /* disable irq */ + goodix_berlin_irq_enable(cd, false); + + /* enter sleep mode or power off */ + goodix_berlin_do_suspend(cd); + + goodix_berlin_release_connects(cd); + + return 0; +} + +static int goodix_berlin_resume(struct goodix_berlin_core *cd) +{ + goodix_berlin_irq_enable(cd, false); + + /* reset device or power on*/ + goodix_berlin_do_resume(cd); + + /* enable irq */ + goodix_berlin_irq_enable(cd, true); + + return 0; +} + +int goodix_berlin_pm_suspend(struct device *dev) +{ + struct goodix_berlin_core *cd = dev_get_drvdata(dev); + + return goodix_berlin_suspend(cd); +} + +int goodix_berlin_pm_resume(struct device *dev) +{ + struct goodix_berlin_core *cd = dev_get_drvdata(dev); + + return goodix_berlin_resume(cd); +} + +EXPORT_GPL_SIMPLE_DEV_PM_OPS(goodix_berlin_pm_ops, + goodix_berlin_pm_suspend, + goodix_berlin_pm_resume); + +int goodix_berlin_probe(struct device *dev, int irq, const struct input_id *id, + struct regmap *regmap) +{ + struct goodix_berlin_core *cd; + int ret; + + if (irq <= 0) + return -EINVAL; + + cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL); + if (!cd) + return -ENOMEM; + + cd->dev = dev; + cd->regmap = regmap; + cd->irq = irq; + + cd->reset_gpio = devm_gpiod_get(cd->dev, "reset", GPIOF_OUT_INIT_HIGH); + if (IS_ERR(cd->reset_gpio)) + return dev_err_probe(cd->dev, PTR_ERR(cd->reset_gpio), + "Failed to request reset gpio\n"); + + cd->avdd = devm_regulator_get(cd->dev, "avdd"); + if (IS_ERR(cd->avdd)) + return dev_err_probe(cd->dev, PTR_ERR(cd->avdd), + "Failed to request avdd regulator\n"); + + cd->iovdd = devm_regulator_get(cd->dev, "iovdd"); + if (IS_ERR(cd->iovdd)) + return dev_err_probe(cd->dev, PTR_ERR(cd->iovdd), + "Failed to request iovdd regulator\n"); + + ret = goodix_berlin_input_dev_config(cd, id); + if (ret < 0) { + dev_err(cd->dev, "failed set input device"); + return ret; + } + + ret = devm_request_threaded_irq(dev, irq, NULL, + goodix_berlin_threadirq_func, + IRQF_ONESHOT, "goodix-berlin", cd); + if (ret) { + dev_err(dev, "request threaded irq failed: %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, cd); + + ret = goodix_berlin_power_on(cd, true); + if (ret) { + dev_err(cd->dev, "failed power on"); + goto err_out; + } + + ret = goodix_berlin_read_version(cd, &cd->fw_version); + if (ret < 0) { + dev_err(cd->dev, "failed to get version info"); + goto err_out; + } + + ret = goodix_berlin_get_ic_info(cd, &cd->ic_info); + if (ret) { + dev_err(cd->dev, "invalid ic info, abort"); + goto err_out; + } + + cd->debugfs_root = debugfs_create_dir(dev_name(dev), NULL); + debugfs_create_file("fw_version", 0444, cd->debugfs_root, + cd, &goodix_berlin_fw_version_fops); + debugfs_create_file("ic_info", 0444, cd->debugfs_root, + cd, &goodix_berlin_ic_info_fops); + + dev_info(cd->dev, "Goodix Berlin %s Touchscreen Controller", cd->fw_version.patch_pid); + + return 0; + +err_out: + goodix_berlin_power_on(cd, false); + + return ret; +} +EXPORT_SYMBOL_GPL(goodix_berlin_probe); + +int goodix_berlin_remove(struct device *dev) +{ + struct goodix_berlin_core *cd = dev_get_drvdata(dev); + + goodix_berlin_release_connects(cd); + + goodix_berlin_irq_enable(cd, false); + + goodix_berlin_power_on(cd, false); + + debugfs_remove(cd->debugfs_root); + + return 0; +} +EXPORT_SYMBOL_GPL(goodix_berlin_remove); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin Core Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong "); From patchwork Tue Jun 6 14:31:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 690259 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AFDE6C7EE24 for ; Tue, 6 Jun 2023 14:32:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237796AbjFFOcp (ORCPT ); Tue, 6 Jun 2023 10:32:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36856 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237778AbjFFOcW (ORCPT ); Tue, 6 Jun 2023 10:32:22 -0400 Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6734B1711 for ; Tue, 6 Jun 2023 07:32:06 -0700 (PDT) Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-3f7378a75c0so23464665e9.3 for ; Tue, 06 Jun 2023 07:32:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1686061924; x=1688653924; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=BaHTT4L54o4lyD54ULBVWcdH3eh4l/G0DgyOj0OxglQ=; b=lyHAp2Ki4IUG3ND+Pm3w3ES8t5fIEqnPQbmU2AT1JRFvNixyRsbDrHc3xHVx4e/dPT w/kvqEesYRid6RAQSQLZsXfHGFyvdEE45WlcPllnPKVaL/bixO0iwbJBtgPr5KAssTjU wXstsgAgtZaEB3f+ks6wRtF1jnQg8vY+1dm2Z5DH7qL8l+8ZsknngOfR1OkCcbyMfAfO 9aNrWk+fAgjWyIi+AjibXb1jFoj3yBqKrB9DRJCVDR8IKGvOKO/3+Ayr0gcyy+DRqqHE tSnFqEiXVfD5jaU21iGSez9cwT7+38Q8GIbWUyGSVqPcw4tUhqwVbDQctjpn1XcaeWoC QDpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686061924; x=1688653924; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=BaHTT4L54o4lyD54ULBVWcdH3eh4l/G0DgyOj0OxglQ=; b=hOinRxeBqkZgvmakfnuzC5S4LLE8OLkJNdV6+0nfBV1vOKy4FJ0W2Ki0zIW5sQ8x+6 QnsATPw+Q4ZhcKZLhHTq7gF/HGrSgCu2lhs2hLn18rmvVx7v3y2tD+9nTxGV/GCOCXiO rYtZLnAjegpxZCr0W+X1uoDPvFWPA51YaJ0uRWe3S6ZZIRBS4oSVNP6GAGRuEybSiH3y IOhw7TBreAG8AC92FSFPRBH3hwREMmJ0sBg0b9mNLBpjRmJaDCb18Bzcq5uBzt0dOepo KGEwgtSdTMFbAzUVMiKm/dQn98b3ePvGkdWD5T9glWh2cpHMh6Iia7zCnL1LDU7h487A ZsFA== X-Gm-Message-State: AC+VfDx7YXt8nyvqrVl7JjENRmflI4d/CnmwRmYikSBYsgczsdTMGZ5K AF+ufwWk40QVPidVrIKqepeHlA== X-Google-Smtp-Source: ACHHUZ5D95xh261RJ6PfI8uKeQkOkcSpJEzvvMZ7Uyr9LwAu4svha0J9D2NO9ir5vb4f7/a1sAIoXg== X-Received: by 2002:a05:600c:2298:b0:3f5:ffe7:bf11 with SMTP id 24-20020a05600c229800b003f5ffe7bf11mr2344366wmf.36.1686061924727; Tue, 06 Jun 2023 07:32:04 -0700 (PDT) Received: from arrakeen.starnux.net ([2a01:e0a:982:cbb0:52eb:f6ff:feb3:451a]) by smtp.gmail.com with ESMTPSA id fc14-20020a05600c524e00b003f61177faffsm3883600wmb.0.2023.06.06.07.32.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 07:32:04 -0700 (PDT) From: Neil Armstrong Date: Tue, 06 Jun 2023 16:31:58 +0200 Subject: [PATCH RFC 3/4] input: touchscreen: add I2C support for Goodix Berlin Touchscreen IC MIME-Version: 1.0 Message-Id: <20230606-topic-goodix-berlin-upstream-initial-v1-3-4a0741b8aefd@linaro.org> References: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> In-Reply-To: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bastien Nocera , Hans de Goede , Henrik Rydberg Cc: linux-input@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Neil Armstrong X-Mailer: b4 0.12.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=4534; i=neil.armstrong@linaro.org; h=from:subject:message-id; bh=wsKfTnQC6EFR3cKO2NR7jLv7N5kTOgbQbywPtOlxRB4=; b=owEBbQKS/ZANAwAKAXfc29rIyEnRAcsmYgBkf0Ng7xG7pOZLo/ZtfQTrbNF7T5tjMFmCVZgHPhMn 43ahMAuJAjMEAAEKAB0WIQQ9U8YmyFYF/h30LIt33NvayMhJ0QUCZH9DYAAKCRB33NvayMhJ0dpCEA CCBgJYpDCYwDi+/YJ78yTc352J7UcV3cMCcJhEAEXVOsuGFpNb3Teh0iKl1q9YWT1wlCmin1kWRCvF 2uA4CGAbZNRq98n45A88CBE5+B6XtrCbC3lUfEoJKA53y0kWjrI9JTWKJmfBYJJHOJJaFF7q9p4BYP AZYItbUe9pjgVmzQas5PBXwzH0C5V0/f+KUb4ijWwadyqIQjFivOFXKgDqNYz2A+ojyXHvcfbj7mng 5HpNnc4HzCPXmE6yquSqHBCPPfHuPZ6P8e5BgEuLDVWw3kSBryLHadewkdh4om2pVjmFU2EVZwtTXe 6MAhWSoY5NPJE2jO5xmYHPHRwSD2FKPEZcjt7RxoAeDlyT3KbVTBg5ylNbvmMVYlcTVMTf13zpd1MF CQ0bkaJwwxE8dWuMn/keiQ9KXqsXDa8dosWl807eouIHG+G1jiU/4to+SEtX8kxUOr9KwjUhpWMR1c 6Le7oSCue8so+zSrcK3bOakjeAd72JcoPUol03MGjMxe54napOr6yZAIbsyen8WBsClCFQJcFbgCQi vsMQBL7/Vb9zUJgMgKxuqKFiIR67h8BOrKeMnONqVWrgsDJmof8XCKi6Fa9djKMBg1yiI9sAtk0AFz b3AN/rLzBk4/LC7wY+Ma3667EwJMOTsQ7Ex6fhQt0n25AO/2t9eWV0Rbz8RQ== X-Developer-Key: i=neil.armstrong@linaro.org; a=openpgp; fpr=89EC3D058446217450F22848169AB7B1A4CFF8AE Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Add initial support for the new Goodix "Berlin" touchscreen ICs over the I2C interface. This initial driver is derived from the Goodix goodix_ts_berlin available at [1] and [2] and only supports the GT9916 IC present on the Qualcomm SM8550 MTP & QRD touch panel. The current implementation only supports BerlinD, aka GT9916. [1] https://github.com/goodix/goodix_ts_berlin [2] https://git.codelinaro.org/clo/la/platform/vendor/opensource/touch-drivers Signed-off-by: Neil Armstrong --- drivers/input/touchscreen/Kconfig | 14 +++++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/goodix_berlin_i2c.c | 76 +++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 1a6f6f6da991..da6d5d75c42d 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -421,6 +421,20 @@ config TOUCHSCREEN_GOODIX_BERLIN_CORE depends on REGMAP tristate +config TOUCHSCREEN_GOODIX_BERLIN_I2C + tristate "Goodix Berlin I2C touchscreen" + depends on I2C + depends on REGMAP_I2C + select TOUCHSCREEN_GOODIX_BERLIN_CORE + help + Say Y here if you have the a touchscreen connected to your + system using the Goodix Berlin IC connection via I2C. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix_berlin_i2c. + config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 29cdb042e104..921a2da0c2be 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/goodix_berlin_i2c.c b/drivers/input/touchscreen/goodix_berlin_i2c.c new file mode 100644 index 000000000000..fc32b8077287 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_i2c.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix Berlin Touchscreen Driver + * + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + */ +#include +#include +#include +#include +#include + +#include "goodix_berlin.h" + +#define I2C_MAX_TRANSFER_SIZE 256 + +static const struct regmap_config goodix_berlin_i2c_regmap_conf = { + .reg_bits = 32, + .val_bits = 8, + .max_raw_read = I2C_MAX_TRANSFER_SIZE, + .max_raw_write = I2C_MAX_TRANSFER_SIZE, +}; + +static const struct input_id goodix_berlin_i2c_input_id = { + .bustype = BUS_I2C, + .vendor = 0x0416, + .product = 0x1001, +}; + +static int goodix_berlin_i2c_probe(struct i2c_client *client) +{ + struct regmap *map; + + map = devm_regmap_init_i2c(client, &goodix_berlin_i2c_regmap_conf); + if (IS_ERR(map)) + return PTR_ERR(map); + + return goodix_berlin_probe(&client->dev, client->irq, + &goodix_berlin_i2c_input_id, map); +} + +static void goodix_berlin_i2c_remove(struct i2c_client *client) +{ + goodix_berlin_remove(&client->dev); +} + +static const struct i2c_device_id goodix_berlin_i2c_id[] = { + { "gt9916", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, goodix_berlin_i2c_id); + +static const struct of_device_id goodix_berlin_i2c_of_match[] = { + { .compatible = "goodix,gt9916", }, + { }, +}; + +static struct i2c_driver goodix_berlin_i2c_driver = { + .driver = { + .name = "goodix-berlin-i2c", + .of_match_table = goodix_berlin_i2c_of_match, + .pm = pm_sleep_ptr(&goodix_berlin_pm_ops), + }, + .probe = goodix_berlin_i2c_probe, + .remove = goodix_berlin_i2c_remove, + .id_table = goodix_berlin_i2c_id, +}; +module_i2c_driver(goodix_berlin_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin I2C Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong "); From patchwork Tue Jun 6 14:31:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Neil Armstrong X-Patchwork-Id: 689822 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AFD1BC7EE37 for ; Tue, 6 Jun 2023 14:32:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237937AbjFFOcr (ORCPT ); Tue, 6 Jun 2023 10:32:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36890 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S237959AbjFFOcX (ORCPT ); Tue, 6 Jun 2023 10:32:23 -0400 Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 54DEB10D9 for ; Tue, 6 Jun 2023 07:32:07 -0700 (PDT) Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-3f7a8089709so25535345e9.1 for ; Tue, 06 Jun 2023 07:32:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1686061926; x=1688653926; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=AAHo2Ft1e4SvLLm6iTKloBaUK4EchMGqUU8Wjhy/k70=; b=i7rKKYAWjtux0sW8cNhrKOwcwFUPxVcbo/0SjSgImMZjkSaffUeTyAQYXhRPJc1gZl AUl/HK4V1nU+4UAT/Q3XFtiDKQHgAvHSw4Y0INDupYdTUCYtbnl7IUiTHjmp60bW+JiH 68uBJJr76114iRVOYdaCBA0mq5KkTjJ6Hm23mNIO/K3xtyKNQsXiNACkguJ56p0QfOQK Ir7qYx7p6+9sCEUjRG0/iEbju382qlUY/Ps84PxuqbUegtZRwABk7rZfosQJ5Ol0I8m2 mHgX2ZcmLgs5StnCzANrCAqU2y3cu5I6Y3G01toV32miX0r35p2f8F0bv24yZGDQAZhR zgiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686061926; x=1688653926; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AAHo2Ft1e4SvLLm6iTKloBaUK4EchMGqUU8Wjhy/k70=; b=Ikvnq9LUa+DOZuSyaZ/mx0zLi2utT3cpsU3/fIkLVvdnAoUvm0SI8KnwybwpLfmHSz qjx6hCz3NSI3QXgCT6yhqzL97J9aYfO2FrS2efIGtrwGT83QEPGgCUzK6LzhcoAYnPCF ngrgNt/Tz/Ze74zqBjC6nPRfUWeJD73zU7EBBMQq899UbNW3SnG16a3HibA4qxDxgANY rj6wmw2aXljXBdJLZ20A2tdKpk83Z49Vcr+bNnLltlZdSFAgFymYS878Hge7ofZPnd2U YE2Rf3G78aA5VuyxxS9TNsxoL+V9laaufigJ88UbIENZgP/jmWsVIr8wdDsvr/EV21M9 yN1A== X-Gm-Message-State: AC+VfDwYAngh9aonlQH9814x/zVkdydft55SaDFbDZehWy9LvIPLIvlb 7v9/pPZTz2M8qlnjwu/pFmod9A== X-Google-Smtp-Source: ACHHUZ65Rt0pJyezM2Tl8YuCIG2oNvDJONeF0pQC1k3stBKHW6YpNweuacyHAcVHvem4ha8kuDcTYA== X-Received: by 2002:a05:600c:b45:b0:3f4:d18f:b2fb with SMTP id k5-20020a05600c0b4500b003f4d18fb2fbmr2481431wmr.8.1686061925651; Tue, 06 Jun 2023 07:32:05 -0700 (PDT) Received: from arrakeen.starnux.net ([2a01:e0a:982:cbb0:52eb:f6ff:feb3:451a]) by smtp.gmail.com with ESMTPSA id fc14-20020a05600c524e00b003f61177faffsm3883600wmb.0.2023.06.06.07.32.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 07:32:05 -0700 (PDT) From: Neil Armstrong Date: Tue, 06 Jun 2023 16:31:59 +0200 Subject: [PATCH RFC 4/4] input: touchscreen: add SPI support for Goodix Berlin Touchscreen IC MIME-Version: 1.0 Message-Id: <20230606-topic-goodix-berlin-upstream-initial-v1-4-4a0741b8aefd@linaro.org> References: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> In-Reply-To: <20230606-topic-goodix-berlin-upstream-initial-v1-0-4a0741b8aefd@linaro.org> To: Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Bastien Nocera , Hans de Goede , Henrik Rydberg Cc: linux-input@vger.kernel.org, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Neil Armstrong X-Mailer: b4 0.12.2 X-Developer-Signature: v=1; a=openpgp-sha256; l=7697; i=neil.armstrong@linaro.org; h=from:subject:message-id; bh=vn7duBP7sfXMbumxh4swTtaBAVKnUxfLgRt1So/mze8=; b=owEBbQKS/ZANAwAKAXfc29rIyEnRAcsmYgBkf0Ng06P6FQgdVTW3g+9T+7UF1C2g0KM+3+N0Zltm QPbDh/2JAjMEAAEKAB0WIQQ9U8YmyFYF/h30LIt33NvayMhJ0QUCZH9DYAAKCRB33NvayMhJ0YjhEA C6nwIRpZK0MNlYr4k7ypGtVDu4d1S4ESVBQ4+4z9/GVYV/XfGPn1uE1gSG7XWYrdoK4xzoC34dxDAz crMw8S83+OLAF2lGMpBqWX4tZMpyE/2NuZR2hLlV8PpqUYK5YohbCAoHOOVhh4aaic9h3HYhvciqGK JPd27nq2mvNEME76uId3mvcZCTkOBcr7Tk3LQXBQXOQoML/79xIZWWC4MLld9pHLJ5NAfZgdUSym/A zAOH795HyLMK6ShMPcclujqrIW8LjScuEXhgGyXLP+Tp3xP47XO5gv5NmXW7uZh8Z7LcbwDJWE7LrT LMajy8rMmsQGbnx1EIl3x8Ls3EAXD5FxCFm0alAMduMWaPRE5xG5sbCNiJkt44H2K7OCJhb2ji2dRA idjd0gXbOKyAqT03/uKlUV2Y6vcf8nVPJ/nvXgum8oMNFQHtzQ9//74J9oe6raK5UM/Pwa9v36mE74 QAMejwwVgUjXgAP0zGGaTdDcH1hWbltQrVeUrnLF37is7tX5EgTWN/qtlxwY1V987aHnUVaEJycxql cG+pxpgUEb3Vhoxo6Op5l0ZkijGZbqvxAen/kCAcsdh7fgXtQ28rVm/OpdbwmZQm3RtETgMn8i1e+N aEjp7f3uqw00eklQdojjwBN1BwnHh7rSN74Bei9EayVj52XQnL7/9JOM4XSg== X-Developer-Key: i=neil.armstrong@linaro.org; a=openpgp; fpr=89EC3D058446217450F22848169AB7B1A4CFF8AE Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org Add initial support for the new Goodix "Berlin" touchscreen ICs over the SPI interface. The driver doesn't use the regmap_spi code since the SPI messages needs to be prefixed, thus this custom regmap code. This initial driver is derived from the Goodix goodix_ts_berlin available at [1] and [2] and only supports the GT9916 IC present on the Qualcomm SM8550 MTP & QRD touch panel. The current implementation only supports BerlinD, aka GT9916. [1] https://github.com/goodix/goodix_ts_berlin [2] https://git.codelinaro.org/clo/la/platform/vendor/opensource/touch-drivers Signed-off-by: Neil Armstrong --- drivers/input/touchscreen/Kconfig | 14 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/goodix_berlin_spi.c | 183 ++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index da6d5d75c42d..ffe0c0a4cd15 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -435,6 +435,20 @@ config TOUCHSCREEN_GOODIX_BERLIN_I2C To compile this driver as a module, choose M here: the module will be called goodix_berlin_i2c. +config TOUCHSCREEN_GOODIX_BERLIN_SPI + tristate "Goodix Berlin SPI touchscreen" + depends on SPI_MASTER + depends on REGMAP + select TOUCHSCREEN_GOODIX_BERLIN_CORE + help + Say Y here if you have the a touchscreen connected to your + system using the Goodix Berlin IC connection via SPI. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called goodix_berlin_spi. + config TOUCHSCREEN_HIDEEP tristate "HiDeep Touch IC" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 921a2da0c2be..29524e8a83db 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) += goodix_berlin_core.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) += goodix_berlin_i2c.o +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) += goodix_berlin_spi.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) += hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o diff --git a/drivers/input/touchscreen/goodix_berlin_spi.c b/drivers/input/touchscreen/goodix_berlin_spi.c new file mode 100644 index 000000000000..0f4f650fdf3f --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_spi.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Goodix Berlin Touchscreen Driver + * + * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (C) 2023 Linaro Ltd. + * + * Based on goodix_ts_berlin driver. + */ +#include +#include +#include +#include +#include + +#include "goodix_berlin.h" + +#define SPI_TRANS_PREFIX_LEN 1 +#define REGISTER_WIDTH 4 +#define SPI_READ_DUMMY_LEN 3 +#define SPI_READ_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN) +#define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH) + +#define SPI_WRITE_FLAG 0xF0 +#define SPI_READ_FLAG 0xF1 + +static int goodix_berlin_spi_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, + size_t val_size) +{ + struct spi_device *spi = context; + struct spi_transfer xfers; + struct spi_message spi_msg; + const u32 *reg = reg_buf; /* reg is stored as native u32 at start of buffer */ + u8 *buf = NULL; + int ret = 0; + + if (reg_size != REGISTER_WIDTH) + return -EINVAL; + + buf = kzalloc(SPI_READ_PREFIX_LEN + val_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + /* buffer format: 0xF1 + addr(4bytes) + dummy(3bytes) + data */ + buf[0] = SPI_READ_FLAG; + put_unaligned_be32(*reg, buf + SPI_TRANS_PREFIX_LEN); + memset(buf + SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH, 0xff, + SPI_READ_DUMMY_LEN); + + xfers.tx_buf = buf; + xfers.rx_buf = buf; + xfers.len = SPI_READ_PREFIX_LEN + val_size; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + + ret = spi_sync(spi, &spi_msg); + if (ret < 0) { + dev_err(&spi->dev, "transfer error:%d", ret); + goto exit; + } + + memcpy(val_buf, buf + SPI_READ_PREFIX_LEN, val_size); +exit: + kfree(buf); + return ret; +} + +static int goodix_berlin_spi_write(void *context, const void *data, + size_t count) +{ + unsigned int len = count - REGISTER_WIDTH; + struct spi_device *spi = context; + struct spi_transfer xfers; + struct spi_message spi_msg; + const u32 *reg = data; /* reg is stored as native u32 at start of buffer */ + u8 *buf = NULL; + int ret = 0; + + buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + buf[0] = SPI_WRITE_FLAG; + put_unaligned_be32(*reg, buf + SPI_TRANS_PREFIX_LEN); + memcpy(buf + SPI_WRITE_PREFIX_LEN, data + REGISTER_WIDTH, len); + + xfers.tx_buf = buf; + xfers.len = SPI_WRITE_PREFIX_LEN + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + + ret = spi_sync(spi, &spi_msg); + if (ret < 0) + dev_err(&spi->dev, "transfer error:%d", ret); + + kfree(buf); + return ret; +} + +static const struct regmap_config goodix_berlin_spi_regmap_conf = { + .reg_bits = 32, + .val_bits = 8, + .read = goodix_berlin_spi_read, + .write = goodix_berlin_spi_write, +}; + +static const struct input_id goodix_berlin_spi_input_id = { + .bustype = BUS_SPI, + .vendor = 0x0416, + .product = 0x1001, +}; + +static int goodix_berlin_spi_probe(struct spi_device *spi) +{ + struct regmap_config *cfg; + struct regmap *map; + size_t max_size; + int ret = 0; + + cfg = devm_kmemdup(&spi->dev, &goodix_berlin_spi_regmap_conf, + sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + return ret; + + max_size = spi_max_transfer_size(spi); + cfg->max_raw_read = max_size - SPI_READ_PREFIX_LEN; + cfg->max_raw_write = max_size - SPI_WRITE_PREFIX_LEN; + + map = devm_regmap_init(&spi->dev, NULL, spi, cfg); + if (IS_ERR(map)) + return PTR_ERR(map); + + return goodix_berlin_probe(&spi->dev, spi->irq, + &goodix_berlin_spi_input_id, map); +} + +static void goodix_berlin_spi_remove(struct spi_device *spi) +{ + goodix_berlin_remove(&spi->dev); +} + +static const struct of_device_id goodix_berlin_spi_of_match[] = { + { + .compatible = "goodix,gt9916", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, goodix_berlin_spi_of_match); + +static const struct spi_device_id goodix_berlin_spi_ids[] = { + { "gt9916" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, goodix_berlin_spi_ids); + +static struct spi_driver goodix_berlin_spi_driver = { + .driver = { + .name = "goodix-berlin-spi", + .of_match_table = goodix_berlin_spi_of_match, + .pm = pm_sleep_ptr(&goodix_berlin_pm_ops), + }, + .id_table = goodix_berlin_spi_ids, + .probe = goodix_berlin_spi_probe, + .remove = goodix_berlin_spi_remove, +}; +module_spi_driver(goodix_berlin_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Goodix Berlin SPI Touchscreen driver"); +MODULE_AUTHOR("Neil Armstrong ");