From nobody Thu Oct 2 14:22:16 2025 Received: from mail-wm1-f68.google.com (mail-wm1-f68.google.com [209.85.128.68]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DD19B31D727 for ; Mon, 15 Sep 2025 14:20:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.68 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757946015; cv=none; b=lutcEZRpKjZ7VALISlX3y+VqhyD8N55AgvJlLC5AMXx+rXuKbhHI22fEdcuIVit7pEe5N0fkNck7yqajr1YMWFiSeC8rYyICcXTHAAZnQTnoYZQniySL6QImMLKNNkLWvd5G/seb131NctdzWT1rf7GCNmT7fmnzFjrFPhVwkfs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757946015; c=relaxed/simple; bh=8Qnm3ZHlu5D9iJRuLjIbduVppAN2k0oBQs6Ewj9hYZ4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=amo/RgQQXIZYnitx5Aau6yfXehUoRMiWJcBhRkYjKXekXYMjGZwnvksFaOyOgty7viAjjg9pXwd/dPoo7en/iCT16Ay4/91CLnRIFc7wTEmI3NN30jrUenyKgnsggva69pCzaQ39vYiBb9wEHQVWk0fNl/6ZvyHIrH03AclmSsE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=R1EoUba4; arc=none smtp.client-ip=209.85.128.68 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="R1EoUba4" Received: by mail-wm1-f68.google.com with SMTP id 5b1f17b1804b1-45dec1ae562so38575695e9.1 for ; Mon, 15 Sep 2025 07:20:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1757946011; x=1758550811; darn=vger.kernel.org; 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=6BaeP+u139pVz62zC0j4CWHyJGRFuQubU7wGhgVzl9I=; b=R1EoUba4vyWzx//sQNU4jdYKU/y/xQSKScw5wRU6V8ZYJ+OSEpsVAogeWLCrnVRqM+ zthFjYbMiM9yTwN0oRtyJyNOb2XSrRxvLawJaoJ7sLUgn39WE9KwNPyrtyh3D5fiBWv0 y98VDyugUFBy7NACYJDfuTJ22FIK8XchhmLLXBF9QWzj54ISJJA7hcBQweXtKoii8Xof VYTbDMYMai+WMS6KbV5getJNTdO0aqL2v01VaD21nfidscWTVpv5GuvI1NaVlEhiDiIZ fxe3VnvmEyDBhj3sWEFZvwpC1wvRneSV445IIo0chbUTWPpPHBL/s9Nlr/obW3i+hVE0 +RKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757946011; x=1758550811; 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=6BaeP+u139pVz62zC0j4CWHyJGRFuQubU7wGhgVzl9I=; b=GaH7qj5uLpSmusivMInerFACQYEG+fV/POsT6OiGW8Qc/LxfLWmj3503i2sHBCr4lX YsNdyR8xhNM5JVAV90yTb4e6APa/ecH6lqc5Dmx0hQJes70DDMyyZINeSuFBX65nmxkp dCzNfUdAnCSz7g02qgEtZC9N8uIl/NMQL4dwAfuGMMI77FjudcYB3Z2gAiCX+Spawd55 ZBzZPwMgVAMVH0juiuf8Tsj94XcGBqKshr7BfIQ6YtVeGXCWkPctgMfcHA9Xo4ax6Ovc 85S/cJTvXv7aXDsW50duXnFRZxHmzXkhfySNMt5peGhaEqanlW85s8FU/5sGm+2fO6gg zeEQ== X-Forwarded-Encrypted: i=1; AJvYcCXg1JmeUChvc28ljSe/Cvb1fwpqOn4e8niX49h5pXkWBSWSP1p7VA6+Y52QtzqFHDYNAYD+geqUO1tjt4E=@vger.kernel.org X-Gm-Message-State: AOJu0YzupKACa1d6y60dbOZ0JxQ4vDxAZkVkXACWCuoPlnbmPQSi3QRp /zqqBWVvHVFw6zULB9LD0EoDgXxXfHPogGWeA8wOzTXuQ+FXvhRuvA4Wj801kbcTDTE= X-Gm-Gg: ASbGncsRY7UyOpftkiq5RG0XDo60seNLy+9w/vd0ftotBWUtzFiX8qyEyPz4z/fhbt7 +XI/sAIP9CdCGXFb1hf3Edu7OyLtaGubdkTa82G/I0XqRtMSXf/nt+1shF3EnERDuIVsy0CJKfV B/VQJZq0gz0y13U1+8qfohDj3KjkeuFlA+a4mX25od3Z6dE/YDAwhD27X8xh/1iVjWTRdBMDj+7 mhvZoV2gfyqGpqwsXGMIpk0GdQGYCE8J7/1KuwnfHRpDnkWCSU6Rymn4J4/o/QhYEV7NDfYg3dP y3iZKStDeDdO+flyjgyJLk5JILe6nBT4RDCpTCYHDdi7W9dfVppwmk8eUDqm6RqHsoOQZg9+GNq bO0W9mc7dGp6CpSwqidJkeKQssR0Z0QY4xrg= X-Google-Smtp-Source: AGHT+IHX7I6b0UJWPq4ArFJRqvCVjzHn8Mc/xKDrgkX25TYBbgOR2ULGGsJQIolv1a5OJ2m+Eq3Qxg== X-Received: by 2002:a05:600c:1384:b0:458:b01c:8f with SMTP id 5b1f17b1804b1-45f211cc941mr129811515e9.8.1757946011048; Mon, 15 Sep 2025 07:20:11 -0700 (PDT) Received: from [127.0.0.2] ([2a02:2454:ff21:41:eee1:5042:e713:2e9a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3e7ff9f77c4sm11801928f8f.27.2025.09.15.07.20.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Sep 2025 07:20:10 -0700 (PDT) From: Stephan Gerhold Date: Mon, 15 Sep 2025 16:19:56 +0200 Subject: [PATCH v5 1/2] dt-bindings: input: touchscreen: document Himax HX852x(ES) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250915-hx852x-v5-1-b938182f1056@linaro.org> References: <20250915-hx852x-v5-0-b938182f1056@linaro.org> In-Reply-To: <20250915-hx852x-v5-0-b938182f1056@linaro.org> To: Dmitry Torokhov Cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Henrik Rydberg , Jeff LaBundy , Jonathan Albrieux , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Stephan Gerhold , Krzysztof Kozlowski X-Mailer: b4 0.14.2 From: Stephan Gerhold Himax HX852x(ES) is a touch panel controller with optional support for capacitive touch keys. Unfortunately, the model naming is quite unclear and confusing. There seems to be a distinction between models (e.g. HX8526) and the "series" suffix (e.g. -A, -B, -C, -D, -E, -ES). But this doesn't seem to be applied very consistently because e.g. HX8527-E(44) actually seems to belong to the -ES series. The compatible consists of the actual part number followed by the "series" as fallback compatible. Typically only the latter will be interesting for drivers as there is no relevant difference on the driver side. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stephan Gerhold --- .../bindings/input/touchscreen/himax,hx852es.yaml | 81 ++++++++++++++++++= ++++ 1 file changed, 81 insertions(+) diff --git a/Documentation/devicetree/bindings/input/touchscreen/himax,hx85= 2es.yaml b/Documentation/devicetree/bindings/input/touchscreen/himax,hx852e= s.yaml new file mode 100644 index 0000000000000000000000000000000000000000..40a60880111de7f4994cd73457b= b8a1d4bfd6a88 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/himax,hx852es.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/himax,hx852es.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Himax HX852x(ES) touch panel controller + +maintainers: + - Stephan Gerhold + +allOf: + - $ref: touchscreen.yaml# + +properties: + compatible: + items: + - enum: + - himax,hx8525e + - himax,hx8526e + - himax,hx8527e + - const: himax,hx852es + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: Touch Screen Interrupt (TSIX), active low + + reset-gpios: + maxItems: 1 + description: External Reset (XRES), active low + + vcca-supply: + description: Analog power supply (VCCA) + + vccd-supply: + description: Digital power supply (VCCD) + + touchscreen-inverted-x: true + touchscreen-inverted-y: true + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-swapped-x-y: true + + linux,keycodes: + minItems: 1 + maxItems: 4 + +required: + - compatible + - reg + - interrupts + - reset-gpios + +additionalProperties: false + +examples: + - | + #include + #include + #include + + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + touchscreen@48 { + compatible =3D "himax,hx8527e", "himax,hx852es"; + reg =3D <0x48>; + interrupt-parent =3D <&tlmm>; + interrupts =3D <13 IRQ_TYPE_LEVEL_LOW>; + reset-gpios =3D <&tlmm 12 GPIO_ACTIVE_LOW>; + vcca-supply =3D <®_ts_vcca>; + vccd-supply =3D <&pm8916_l6>; + linux,keycodes =3D ; + }; + }; + +... --=20 2.50.1 From nobody Thu Oct 2 14:22:16 2025 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDED831D745 for ; Mon, 15 Sep 2025 14:20:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757946016; cv=none; b=jROCYm4hXD2gkuT8ALK7w5MCzyDKEEvSscxhQM/b5MDNJhu9pMTzQwBiCgrgvCvgRsVR563xxveRB8rGQscBl7/TllSov1PehZohQV0UaDvPFkJ01E8QbV0urBfUYICXAmjk4bFUCR9blhLXA1zo60oRPbxGeSwN9n/rzW8h0bM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757946016; c=relaxed/simple; bh=IaKoxFq9bosUrFMFRmC4pglgB2Ez6v2Wwe4EF54jWxA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DkouekPnhxQCUjEshoO6NhyiVBN/Wws61hWnbSEz4jHAb05YMf9lMK0OiHtmd8s2lzTbAF7Ti5fiEGPD6VOwJVCmZL5Ra4dqo4eZf07PXNo2cbnsMBiBJD7YEvNFBMHRmSa0uuQikj4c4fnNOlGCZwwfdfRvD//YgQ2wwA50REM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=cxrGFFiY; arc=none smtp.client-ip=209.85.221.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="cxrGFFiY" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-3eb0a50a60aso769472f8f.3 for ; Mon, 15 Sep 2025 07:20:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1757946012; x=1758550812; darn=vger.kernel.org; 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=SAAhdahObRTXBffDn8987Td/pNcUh1CI1nUDnSU/rNo=; b=cxrGFFiY2LMs8Il2T0Qf6j9elJG9vmWZJ+90+IaOTegBj+8TNAuVthPl0GqgjuOsMV BAI+XFijvCUFT1IW7UxNUBdELQqrevj9lU7ll4Yde5hvwHPsQ15TXH0iy3e2e+yTZwvo 8HMJh7c7NgByXRN4uHD4o9RikOnlTVWtE7MiUDtbquSrcnPlYr9FE4BX7Jvp2hZIT1gs btdYghA1QLZyfAY2V8sAyuXPZ+g1kTJ6YdohZYtD0FkrWF5rzu4dYLRl7v7OY5JpYlTq 9x7YGlN4m2U3P5aUfx1Qpd16G2+6arq9DVTkisvMY2GStUyyGUp5MDmMZBSUXaDQx2jY +Y+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1757946012; x=1758550812; 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=SAAhdahObRTXBffDn8987Td/pNcUh1CI1nUDnSU/rNo=; b=jmy0fJcns1+4pQMf/kxq4FF6DJnBv0uhccmHuY+QWLZ8iW4AaD2hXC6OQonwv0eqlp ZoUKUPjttcJ3ROaOLc6kwe+jySc1eLIkORX9zRJLdchjQz0bQvwnQpQWnSMkvBR2Zbnr tVIG8GpPXOc8LjjQXaN962h2XkmB0Vp+HNnAASWpvPV2Iutbmw5XHhbRWhFmJB6roTdg AwpCcohZYDsDKDVu9kcQMVX1B4RdujvZKfuuVMFJzumNXvpeb7kZstYifoiC5pXlsKsl ZBowwOccDkTcAkuRHFnXebSXOiUakGsE1dgq/fxiSXrOdyQ4M6tPWKEaKz5kJcBlBY1O eoYw== X-Forwarded-Encrypted: i=1; AJvYcCVoM+7F9/dOmfo+0ySO//PPuaedLv82F8/PCHGhZIbMxsTyI5G942d6eodssy2zF9cBISz3TjGArrjvtRs=@vger.kernel.org X-Gm-Message-State: AOJu0YxwUTpkRbPyHAu5xlP+hmjv8QOuk4NlmIJnqy0sD0t6ByW1SDp5 Y+ziR01ni9+QbzDosLK2ad3Z90XgteEgzrwQIc/Y2hmorj9zB5/V8cUXQhWk/6q1wJM= X-Gm-Gg: ASbGncuDPDCoawa9dR5LLtfiKMuKLKDBKO1vd+S8rlAAmRnPInFjBXj2jQc/7UIs3uZ JxCrwgSrU/MpUyuzbVtKnpfLqeWIc9oK7BpiZywvI9MP7Gb7A77N7rk/NxrH4QBXNkFgvSwbIZj je7tIgvh+rthDMT7DLkHI5EdcFpDk4GZqw6K/k5XVQKazep+3pp2wAoPRO4eLB9tFFjRg+JC2ml MokDkMZ55C8/ZZXipOAeI98pMwqbu1CDrqNINGNbiHutqQmu6glE/SI0ICBfSavookg0yVfeDmr 04Q9HjCZCo2owJnnmcD5pK7nPjhnxapVWToJD4ZJ8DuOY4KPN3g0wGy1wXPSqjqm/7S2bGMtWeh iTBrrSDmJydc/8Uvl5vOWyGn5pmS2BRSuJsY= X-Google-Smtp-Source: AGHT+IEzGre1dIp83vLTlA3sojlaeFrXKoLd050PHxQ/X6FOOB9Pbf0NxDTDzTJw5ypYJ6at6GDvdg== X-Received: by 2002:a05:6000:2283:b0:3e7:48ab:7695 with SMTP id ffacd0b85a97d-3e7659f931fmr11354056f8f.59.1757946012024; Mon, 15 Sep 2025 07:20:12 -0700 (PDT) Received: from [127.0.0.2] ([2a02:2454:ff21:41:eee1:5042:e713:2e9a]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-3e7ff9f77c4sm11801928f8f.27.2025.09.15.07.20.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 15 Sep 2025 07:20:11 -0700 (PDT) From: Stephan Gerhold Date: Mon, 15 Sep 2025 16:19:57 +0200 Subject: [PATCH v5 2/2] Input: add Himax HX852x(ES) touchscreen driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250915-hx852x-v5-2-b938182f1056@linaro.org> References: <20250915-hx852x-v5-0-b938182f1056@linaro.org> In-Reply-To: <20250915-hx852x-v5-0-b938182f1056@linaro.org> To: Dmitry Torokhov Cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Henrik Rydberg , Jeff LaBundy , Jonathan Albrieux , linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Stephan Gerhold X-Mailer: b4 0.14.2 From: Stephan Gerhold Add a simple driver for the Himax HX852x(ES) touch panel controller, with support for multi-touch and capacitive touch keys. The driver is somewhat based on sample code from Himax. However, that code was rather confusing, so that we spent a significant amount of time just trying to understand the packet format and register commands. In this driver they are described with clean structs and defines rather than magic numbers and offset calculations. Co-developed-by: Jonathan Albrieux Signed-off-by: Jonathan Albrieux Reviewed-by: Jeff LaBundy Signed-off-by: Stephan Gerhold --- MAINTAINERS | 7 + drivers/input/touchscreen/Kconfig | 10 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/himax_hx852x.c | 500 +++++++++++++++++++++++++++= ++++ 4 files changed, 518 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index daf520a13bdf6a991c0160a96620f40308c29ee0..8d9c8d68f3fd0d50db0235cd17c= 4d1f1021a78be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10965,6 +10965,13 @@ S: Maintained F: Documentation/devicetree/bindings/input/touchscreen/himax,hx83112b.yaml F: drivers/input/touchscreen/himax_hx83112b.c =20 +HIMAX HX852X TOUCHSCREEN DRIVER +M: Stephan Gerhold +L: linux-input@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/input/touchscreen/himax,hx852es.yaml +F: drivers/input/touchscreen/himax_hx852x.c + HIPPI M: Jes Sorensen S: Maintained diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/= Kconfig index 196905162945d59e775c3e0bff6540a82842229a..762d71f79abd43ebd58f9caf3c9= 9f07a276c4b16 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -441,6 +441,16 @@ config TOUCHSCREEN_HIDEEP To compile this driver as a module, choose M here : the module will be called hideep_ts. =20 +config TOUCHSCREEN_HIMAX_HX852X + tristate "Himax HX852x(ES) touchscreen" + depends on I2C + help + Say Y here if you have a Himax HX852x(ES) touchscreen. + If unsure, say N. + + To compile this driver as a module, choose M here: the module + will be called himax_hx852x. + config TOUCHSCREEN_HYCON_HY46XX tristate "Hycon hy46xx touchscreen support" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen= /Makefile index 97a025c6a3770fb80255246eb63c11688ebd79eb..1e7c2f324e12a413e112ad0a116= 784f0042e2af7 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_CORE) +=3D goodix_= berlin_core.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_I2C) +=3D goodix_berlin_i2c.o obj-$(CONFIG_TOUCHSCREEN_GOODIX_BERLIN_SPI) +=3D goodix_berlin_spi.o obj-$(CONFIG_TOUCHSCREEN_HIDEEP) +=3D hideep.o +obj-$(CONFIG_TOUCHSCREEN_HIMAX_HX852X) +=3D himax_hx852x.o obj-$(CONFIG_TOUCHSCREEN_HYNITRON_CSTXXX) +=3D hynitron_cstxxx.o obj-$(CONFIG_TOUCHSCREEN_ILI210X) +=3D ili210x.o obj-$(CONFIG_TOUCHSCREEN_ILITEK) +=3D ilitek_ts_i2c.o diff --git a/drivers/input/touchscreen/himax_hx852x.c b/drivers/input/touch= screen/himax_hx852x.c new file mode 100644 index 0000000000000000000000000000000000000000..3a418557645b561ddf3df342a10= 4c012f57c9dc0 --- /dev/null +++ b/drivers/input/touchscreen/himax_hx852x.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Himax HX852x(ES) Touchscreen Driver + * Copyright (c) 2020-2024 Stephan Gerhold + * Copyright (c) 2020 Jonathan Albrieux + * + * Based on the Himax Android Driver Sample Code Ver 0.3 for HMX852xES chi= pset: + * Copyright (c) 2014 Himax Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HX852X_COORD_SIZE(fingers) ((fingers) * sizeof(struct hx852x_coord= )) +#define HX852X_WIDTH_SIZE(fingers) ALIGN(fingers, 4) +#define HX852X_BUF_SIZE(fingers) (HX852X_COORD_SIZE(fingers) + \ + HX852X_WIDTH_SIZE(fingers) + \ + sizeof(struct hx852x_touch_info)) + +#define HX852X_MAX_FINGERS 12 +#define HX852X_MAX_KEY_COUNT 4 +#define HX852X_MAX_BUF_SIZE HX852X_BUF_SIZE(HX852X_MAX_FINGERS) + +#define HX852X_TS_SLEEP_IN 0x80 +#define HX852X_TS_SLEEP_OUT 0x81 +#define HX852X_TS_SENSE_OFF 0x82 +#define HX852X_TS_SENSE_ON 0x83 +#define HX852X_READ_ONE_EVENT 0x85 +#define HX852X_READ_ALL_EVENTS 0x86 +#define HX852X_READ_LATEST_EVENT 0x87 +#define HX852X_CLEAR_EVENT_STACK 0x88 + +#define HX852X_REG_SRAM_SWITCH 0x8c +#define HX852X_REG_SRAM_ADDR 0x8b +#define HX852X_REG_FLASH_RPLACE 0x5a + +#define HX852X_SRAM_SWITCH_TEST_MODE 0x14 +#define HX852X_SRAM_ADDR_CONFIG 0x7000 + +struct hx852x { + struct i2c_client *client; + struct input_dev *input_dev; + struct touchscreen_properties props; + struct gpio_desc *reset_gpiod; + struct regulator_bulk_data supplies[2]; + unsigned int max_fingers; + unsigned int keycount; + unsigned int keycodes[HX852X_MAX_KEY_COUNT]; +}; + +struct hx852x_config { + u8 rx_num; + u8 tx_num; + u8 max_pt; + u8 padding1[3]; + __be16 x_res; + __be16 y_res; + u8 padding2[2]; +} __packed __aligned(4); + +struct hx852x_coord { + __be16 x; + __be16 y; +} __packed __aligned(4); + +struct hx852x_touch_info { + u8 finger_num; + __le16 finger_pressed; + u8 padding; +} __packed __aligned(4); + +static int hx852x_i2c_read(struct hx852x *hx, u8 cmd, void *data, u16 len) +{ + struct i2c_client *client =3D hx->client; + int ret; + + struct i2c_msg msg[] =3D { + { + .addr =3D client->addr, + .flags =3D 0, + .len =3D 1, + .buf =3D &cmd, + }, + { + .addr =3D client->addr, + .flags =3D I2C_M_RD, + .len =3D len, + .buf =3D data, + }, + }; + + ret =3D i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret !=3D ARRAY_SIZE(msg)) { + dev_err(&client->dev, "failed to read %#x: %d\n", cmd, ret); + return ret; + } + + return 0; +} + +static int hx852x_power_on(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + int error; + + error =3D regulator_bulk_enable(ARRAY_SIZE(hx->supplies), hx->supplies); + if (error) { + dev_err(dev, "failed to enable regulators: %d\n", error); + return error; + } + + gpiod_set_value_cansleep(hx->reset_gpiod, 1); + msleep(20); + gpiod_set_value_cansleep(hx->reset_gpiod, 0); + msleep(50); + + return 0; +} + +static int hx852x_start(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + int error; + + error =3D i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_OUT); + if (error) { + dev_err(dev, "failed to send TS_SLEEP_OUT: %d\n", error); + return error; + } + msleep(30); + + error =3D i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_ON); + if (error) { + dev_err(dev, "failed to send TS_SENSE_ON: %d\n", error); + return error; + } + msleep(20); + + return 0; +} + +static int hx852x_stop(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + int error; + + error =3D i2c_smbus_write_byte(hx->client, HX852X_TS_SENSE_OFF); + if (error) { + dev_err(dev, "failed to send TS_SENSE_OFF: %d\n", error); + return error; + } + msleep(20); + + error =3D i2c_smbus_write_byte(hx->client, HX852X_TS_SLEEP_IN); + if (error) { + dev_err(dev, "failed to send TS_SLEEP_IN: %d\n", error); + return error; + } + msleep(30); + + return 0; +} + +static int hx852x_power_off(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + int error; + + error =3D regulator_bulk_disable(ARRAY_SIZE(hx->supplies), hx->supplies); + if (error) { + dev_err(dev, "failed to disable regulators: %d\n", error); + return error; + } + + return 0; +} + +static int hx852x_read_config(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + struct hx852x_config conf; + int x_res, y_res; + int error; + + error =3D hx852x_power_on(hx); + if (error) + return error; + + /* Sensing must be turned on briefly to load the config */ + error =3D hx852x_start(hx); + if (error) + goto err_power_off; + + error =3D hx852x_stop(hx); + if (error) + goto err_power_off; + + error =3D i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH, + HX852X_SRAM_SWITCH_TEST_MODE); + if (error) + goto err_power_off; + + error =3D i2c_smbus_write_word_data(hx->client, HX852X_REG_SRAM_ADDR, + HX852X_SRAM_ADDR_CONFIG); + if (error) + goto err_test_mode; + + error =3D hx852x_i2c_read(hx, HX852X_REG_FLASH_RPLACE, &conf, sizeof(conf= )); + if (error) + goto err_test_mode; + + x_res =3D be16_to_cpu(conf.x_res); + y_res =3D be16_to_cpu(conf.y_res); + hx->max_fingers =3D (conf.max_pt & 0xf0) >> 4; + dev_dbg(dev, "x res: %u, y res: %u, max fingers: %u\n", + x_res, y_res, hx->max_fingers); + + if (hx->max_fingers > HX852X_MAX_FINGERS) { + dev_err(dev, "max supported fingers: %u, found: %u\n", + HX852X_MAX_FINGERS, hx->max_fingers); + error =3D -EINVAL; + goto err_test_mode; + } + + if (x_res && y_res) { + input_set_abs_params(hx->input_dev, ABS_MT_POSITION_X, 0, x_res - 1, 0, = 0); + input_set_abs_params(hx->input_dev, ABS_MT_POSITION_Y, 0, y_res - 1, 0, = 0); + } + +err_test_mode: + error =3D i2c_smbus_write_byte_data(hx->client, HX852X_REG_SRAM_SWITCH, 0= ) ? : error; +err_power_off: + return hx852x_power_off(hx) ? : error; +} + +static int hx852x_handle_events(struct hx852x *hx) +{ + /* + * The event packets have variable size, depending on the amount of + * supported fingers (hx->max_fingers). They are laid out as follows: + * - struct hx852x_coord[hx->max_fingers]: Coordinates for each finger + * - u8[ALIGN(hx->max_fingers, 4)]: Touch width for each finger + * with padding for 32-bit alignment + * - struct hx852x_touch_info + * + * Load everything into a 32-bit aligned buffer so the coordinates + * can be assigned directly, without using get_unaligned_*(). + */ + u8 buf[HX852X_MAX_BUF_SIZE] __aligned(4); + struct hx852x_coord *coord =3D (struct hx852x_coord *)buf; + u8 *width =3D &buf[HX852X_COORD_SIZE(hx->max_fingers)]; + struct hx852x_touch_info *info =3D (struct hx852x_touch_info *) + &width[HX852X_WIDTH_SIZE(hx->max_fingers)]; + unsigned long finger_pressed, key_pressed; + unsigned int i, x, y, w; + int error; + + error =3D hx852x_i2c_read(hx, HX852X_READ_ALL_EVENTS, buf, + HX852X_BUF_SIZE(hx->max_fingers)); + if (error) + return error; + + finger_pressed =3D get_unaligned_le16(&info->finger_pressed); + key_pressed =3D finger_pressed >> HX852X_MAX_FINGERS; + + /* All bits are set when no touch is detected */ + if (info->finger_num =3D=3D 0xff || !(info->finger_num & 0x0f)) + finger_pressed =3D 0; + if (key_pressed =3D=3D 0xf) + key_pressed =3D 0; + + for_each_set_bit(i, &finger_pressed, hx->max_fingers) { + x =3D be16_to_cpu(coord[i].x); + y =3D be16_to_cpu(coord[i].y); + w =3D width[i]; + + input_mt_slot(hx->input_dev, i); + input_mt_report_slot_state(hx->input_dev, MT_TOOL_FINGER, 1); + touchscreen_report_pos(hx->input_dev, &hx->props, x, y, true); + input_report_abs(hx->input_dev, ABS_MT_TOUCH_MAJOR, w); + } + input_mt_sync_frame(hx->input_dev); + + for (i =3D 0; i < hx->keycount; i++) + input_report_key(hx->input_dev, hx->keycodes[i], key_pressed & BIT(i)); + + input_sync(hx->input_dev); + return 0; +} + +static irqreturn_t hx852x_interrupt(int irq, void *ptr) +{ + struct hx852x *hx =3D ptr; + int error; + + error =3D hx852x_handle_events(hx); + if (error) { + dev_err_ratelimited(&hx->client->dev, "failed to handle events: %d\n", e= rror); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int hx852x_input_open(struct input_dev *dev) +{ + struct hx852x *hx =3D input_get_drvdata(dev); + int error; + + error =3D hx852x_power_on(hx); + if (error) + return error; + + error =3D hx852x_start(hx); + if (error) { + hx852x_power_off(hx); + return error; + } + + enable_irq(hx->client->irq); + return 0; +} + +static void hx852x_input_close(struct input_dev *dev) +{ + struct hx852x *hx =3D input_get_drvdata(dev); + + hx852x_stop(hx); + disable_irq(hx->client->irq); + hx852x_power_off(hx); +} + +static int hx852x_parse_properties(struct hx852x *hx) +{ + struct device *dev =3D &hx->client->dev; + int error, count; + + count =3D device_property_count_u32(dev, "linux,keycodes"); + if (count =3D=3D -EINVAL) { + /* Property does not exist, keycodes are optional */ + return 0; + } else if (count < 0) { + dev_err(dev, "Failed to read linux,keycodes: %d\n", count); + return count; + } else if (count > HX852X_MAX_KEY_COUNT) { + dev_err(dev, "max supported keys: %u, found: %u\n", + HX852X_MAX_KEY_COUNT, hx->keycount); + return -EINVAL; + } + hx->keycount =3D count; + + error =3D device_property_read_u32_array(dev, "linux,keycodes", + hx->keycodes, hx->keycount); + if (error) { + dev_err(dev, "failed to read linux,keycodes: %d\n", error); + return error; + } + + return 0; +} + +static int hx852x_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct hx852x *hx; + int error, i; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA)) { + dev_err(dev, "not all required i2c functionality supported\n"); + return -ENXIO; + } + + hx =3D devm_kzalloc(dev, sizeof(*hx), GFP_KERNEL); + if (!hx) + return -ENOMEM; + + hx->client =3D client; + hx->input_dev =3D devm_input_allocate_device(dev); + if (!hx->input_dev) + return -ENOMEM; + + hx->input_dev->name =3D "Himax HX852x"; + hx->input_dev->id.bustype =3D BUS_I2C; + hx->input_dev->open =3D hx852x_input_open; + hx->input_dev->close =3D hx852x_input_close; + + i2c_set_clientdata(client, hx); + input_set_drvdata(hx->input_dev, hx); + + hx->supplies[0].supply =3D "vcca"; + hx->supplies[1].supply =3D "vccd"; + error =3D devm_regulator_bulk_get(dev, ARRAY_SIZE(hx->supplies), hx->supp= lies); + if (error) + return dev_err_probe(dev, error, "failed to get regulators\n"); + + hx->reset_gpiod =3D devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(hx->reset_gpiod)) + return dev_err_probe(dev, PTR_ERR(hx->reset_gpiod), + "failed to get reset gpio\n"); + + error =3D devm_request_threaded_irq(dev, client->irq, NULL, hx852x_interr= upt, + IRQF_ONESHOT | IRQF_NO_AUTOEN, NULL, hx); + if (error) + return dev_err_probe(dev, error, "failed to request irq %d", client->irq= ); + + error =3D hx852x_read_config(hx); + if (error) + return error; + + input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(hx->input_dev, EV_ABS, ABS_MT_POSITION_Y); + input_set_abs_params(hx->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + touchscreen_parse_properties(hx->input_dev, true, &hx->props); + error =3D hx852x_parse_properties(hx); + if (error) + return error; + + hx->input_dev->keycode =3D hx->keycodes; + hx->input_dev->keycodemax =3D hx->keycount; + hx->input_dev->keycodesize =3D sizeof(hx->keycodes[0]); + for (i =3D 0; i < hx->keycount; i++) + input_set_capability(hx->input_dev, EV_KEY, hx->keycodes[i]); + + error =3D input_mt_init_slots(hx->input_dev, hx->max_fingers, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (error) + return dev_err_probe(dev, error, "failed to init MT slots\n"); + + error =3D input_register_device(hx->input_dev); + if (error) + return dev_err_probe(dev, error, "failed to register input device\n"); + + return 0; +} + +static int hx852x_suspend(struct device *dev) +{ + struct hx852x *hx =3D dev_get_drvdata(dev); + int error =3D 0; + + mutex_lock(&hx->input_dev->mutex); + if (input_device_enabled(hx->input_dev)) + error =3D hx852x_stop(hx); + mutex_unlock(&hx->input_dev->mutex); + + return error; +} + +static int hx852x_resume(struct device *dev) +{ + struct hx852x *hx =3D dev_get_drvdata(dev); + int error =3D 0; + + mutex_lock(&hx->input_dev->mutex); + if (input_device_enabled(hx->input_dev)) + error =3D hx852x_start(hx); + mutex_unlock(&hx->input_dev->mutex); + + return error; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hx852x_pm_ops, hx852x_suspend, hx852x_resu= me); + +#ifdef CONFIG_OF +static const struct of_device_id hx852x_of_match[] =3D { + { .compatible =3D "himax,hx852es" }, + { } +}; +MODULE_DEVICE_TABLE(of, hx852x_of_match); +#endif + +static struct i2c_driver hx852x_driver =3D { + .probe =3D hx852x_probe, + .driver =3D { + .name =3D "himax_hx852x", + .pm =3D pm_sleep_ptr(&hx852x_pm_ops), + .of_match_table =3D of_match_ptr(hx852x_of_match), + }, +}; +module_i2c_driver(hx852x_driver); + +MODULE_DESCRIPTION("Himax HX852x(ES) Touchscreen Driver"); +MODULE_AUTHOR("Jonathan Albrieux "); +MODULE_AUTHOR("Stephan Gerhold "); +MODULE_LICENSE("GPL"); --=20 2.50.1