From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qv1-f49.google.com (mail-qv1-f49.google.com [209.85.219.49]) (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 2B410217722 for ; Fri, 26 Sep 2025 14:19:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896360; cv=none; b=CXzPRr2WnQWfIFWW+VIVSWUH1PrP4wDTcNlloFcyEoIwoxZFFfkBdI1RrNSlbSfWlHW5LFebVWv/MbmyRHnPIQACTvH365YuHbsEz/ZrI6Rgm6ssdqYjCFYORZqKM2167+uViOEx6ClCyYAGspo1xGk7Seq/tqpZAVTkBbD5uSw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896360; c=relaxed/simple; bh=qgE1DRlyxJJu7Ruu1746EYrt+DDtbSYwSzn0fLVg2DA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=b/yQ+ix2TELrxKWcD51pT1VeXVmNw6x8WfFqrqAnPX4YHZXiUYiKpKqJ6F/6jiJi2jXGldsmjrxgLcCveKjg90XR4lcvtoh2QNPksonc8J4a4CUifw9ytsvaJnN6G44sEUq0Fa8qeYR51pO3Iw29eLXim77opkHHZJasgt47jO4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FJQ+l8Mm; arc=none smtp.client-ip=209.85.219.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="FJQ+l8Mm" Received: by mail-qv1-f49.google.com with SMTP id 6a1803df08f44-796fe71deecso14750886d6.1 for ; Fri, 26 Sep 2025 07:19:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896357; x=1759501157; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S9vnEObE36FL1+xYk2YooCCgZ/a3BnQWUxhVd6RBMoo=; b=FJQ+l8MmEX2Qtea8bjGxw6u71ksOjX53DfR+35hKgc867QXA1HihlqiDkNgIqc/YP4 rFggfMYR9K/TbdsWgy2gOA2wmOoj7UomlewV/Ua3vOEx90Pn981WzGPZTsNyqRrV0W+F 6W4js4xRYjKI7cqVgho0MrGw+zKHYJPzz97M3pnok0JkAcc0lmwu/CPZNjDrthF1kxng 4wLvHMY3gWZMXmzcJwUHQKXW+avdZt/x3CEEDBtX7ufevNTinN/Em4+shPHok8su2xQ+ EOsMmB+tNQV/YoW9/5qm15AZ//ZsAV8gJ4a1W2STWdS1UZy+s5RzlEv8sG9KOhvS10r+ Juvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896357; x=1759501157; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S9vnEObE36FL1+xYk2YooCCgZ/a3BnQWUxhVd6RBMoo=; b=gBqplY9L7pPKNMpVPP2agV6y0eDrTM02ReUB6Gfprr1qKdfSiSV2sbDK9yzr0jRWiL kiQqZPzAhWQXYnhLQkyzlAen+2N2jxWJ6/BSP4O+hZFtWFnrMyIYjZ1gG80NCpn37+am n+z1d6iu+ruFO9Ygfwq7plrTdYHfnRR6m+0QnOEVTmxwhkJd1KR0+TDEv1m1t/ZjIDZd iMXnCICACNm2VGG9fp2X6iv84AecDC0RNKwYASKNebqAot4eZfwgHcxYinEhL/aLMBn1 226PaO37TGZTBS84Wz6ejvdSL+GGeqYaZZq3Qv1EfseXCOyYVc4GFCItbfzV6/PeI0I5 PpGg== X-Gm-Message-State: AOJu0Yx/jaq5GymGyY58A7pc0SVhmHN0i+hfczlxC9SMMuob/8OAEpsu n2Kj26ba6GgYZ3st6n9zPW8pmh6OrHdGOIvf4Wkw0E/JtUk2t3FUpEoH X-Gm-Gg: ASbGncvcgT1opdr/t2UgoCORof/qlnLpRFOc85Win6DUAfhCOn286cSH4fPvRvKhIDX rd4vCS+jEzZsnuwa70UYsbpsfEQr6on+0WwWjEAOyJtFBSy4R09+cjheHYST49IAjq0ntJao4yY DQGkDywwkGcBvv4vb7ythHsUnZRnwq/JpYdvN1o+GP12jgMEWzyynGHXq9Et+0ywLhYr3Oi2uvZ +amOGu7iBT5PqGTno6Y+9qvHY6ws8syjYhZ/nwjYBo6YgG4HnzlkJtD91wuSNzJpJjA0dznFeX7 B/An0tsjvfGDkZa4aSme3ZTChe6cLZ/1uveBdfm+BQfhhnq06WqGR/57S2GB147WmJUOJ12nwdM gt5/yBaT4ExdOV3M/AOdAu4IZ2Zy92wQKl/26/cM18Un6gi7hwodAgFSu2nMLetLRJWP3 X-Google-Smtp-Source: AGHT+IHX+1NCTF/ARuPbB6IXv8deE5J4rPSitNxlvB/PPa7wmNUGhXtpQbzNPBYGW0Ohfyq0EGQcGA== X-Received: by 2002:a05:6214:e44:b0:7ad:19a4:53e0 with SMTP id 6a1803df08f44-7fc2fd77573mr93039126d6.26.1758896356702; Fri, 26 Sep 2025 07:19:16 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-80142f5d6d1sm26649476d6.33.2025.09.26.07.19.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:16 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org, Conor Dooley , =?UTF-8?q?Andreas=20F=C3=A4rber?= Subject: [PATCH v5 1/7] dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton, winrise, wxicore Date: Fri, 26 Sep 2025 10:19:02 -0400 Message-ID: <20250926141913.25919-2-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add vendor prefixes of chip manufacturers supported by the TM16xx 7-segment LED matrix display controllers driver: - fdhisi: Fuzhou Fuda Hisi Microelectronics Co., Ltd. - titanmec: Shenzhen Titan Micro Electronics Co., Ltd. - princeton: Princeton Technology Corp. - winrise: Shenzhen Winrise Technology Co., Ltd. - wxicore: Wuxi i-Core Electronics Co., Ltd. The titanmec prefix is based on the company's domain name titanmec.com. The remaining prefixes are based on company names, as these manufacturers either lack active .com domains or their .com domains are occupied by unrelated businesses. The fdhisi and titanmec prefixes were originally identified by Andreas F=C3=A4rber. Acked-by: Conor Dooley Signed-off-by: Jean-Fran=C3=A7ois Lessard --- CC: Andreas F=C3=A4rber Documentation/devicetree/bindings/vendor-prefixes.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Docum= entation/devicetree/bindings/vendor-prefixes.yaml index 9ec8947dfcad..cd42bf7a96fb 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -542,6 +542,8 @@ patternProperties: description: Fastrax Oy "^fcs,.*": description: Fairchild Semiconductor + "^fdhisi,.*": + description: Fuzhou Fuda Hisi Microelectronics Co., Ltd. "^feixin,.*": description: Shenzhen Feixin Photoelectic Co., Ltd "^feiyang,.*": @@ -1235,6 +1237,8 @@ patternProperties: description: Prime View International (PVI) "^primux,.*": description: Primux Trading, S.L. + "^princeton,.*": + description: Princeton Technology Corp. "^probox2,.*": description: PROBOX2 (by W2COMP Co., Ltd.) "^pri,.*": @@ -1569,6 +1573,8 @@ patternProperties: description: Texas Instruments "^tianma,.*": description: Tianma Micro-electronics Co., Ltd. + "^titanmec,.*": + description: Shenzhen Titan Micro Electronics Co., Ltd. "^tlm,.*": description: Trusted Logic Mobility "^tmt,.*": @@ -1726,6 +1732,8 @@ patternProperties: description: Wingtech Technology Co., Ltd. "^winlink,.*": description: WinLink Co., Ltd + "^winrise,.*": + description: Shenzhen Winrise Technology Co., Ltd. "^winsen,.*": description: Winsen Corp. "^winstar,.*": @@ -1742,6 +1750,8 @@ patternProperties: description: Wobo "^wolfvision,.*": description: WolfVision GmbH + "^wxicore,.*": + description: Wuxi i-Core Electronics Co., Ltd. "^x-powers,.*": description: X-Powers "^xen,.*": --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qt1-f178.google.com (mail-qt1-f178.google.com [209.85.160.178]) (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 14F271C8603 for ; Fri, 26 Sep 2025 14:19:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896361; cv=none; b=MDvxoFNys8OUMf+wUyTXh3Kk6xKG0oz+zMbHF/qRMa2VXoOQIY87aw1uA8gDzGAzYjmpHsdCnmCC/Dpb+1Jd1zbx3l96DXk3LnBqB8k0zPasnKfnlm1nth8CH+1sdN0a2NwmjZ3uZ+gGO3i7zMiJEu/Q2AeWqLcwiDuY600vUiI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896361; c=relaxed/simple; bh=6/JQRqBUjkyOthrpHav3m3u/rhn4XRG3F00Nzd3iX7M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=LeGgskMkeb2T/QUVNuKdFDRb3FsOA8AAjO4HBKreUQeFmJEKzZbhkUbu6IM7kcqjfnKDi5CxSI5e/3rUqp9eiAY15MgJ+62tzCbZqKJev4b/CXzDbsBLahPDENrdIjLxbfNUlUG8H/N/HMd8Ea/fco/ysYPAC+GHCMZR7M7XPL4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Qdk9624X; arc=none smtp.client-ip=209.85.160.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Qdk9624X" Received: by mail-qt1-f178.google.com with SMTP id d75a77b69052e-4b109c6b9fcso21054851cf.3 for ; Fri, 26 Sep 2025 07:19:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896359; x=1759501159; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=B7RhkLG3JZ6AWWqhGvapjhsuUBsTMt1EMhEwLlIwGFE=; b=Qdk9624XheBy8q8fKYUANY0V5dF8L3t56Yq3iVWKsKT6+g7pCi3AChpD5g6arDP2q4 KswmDsFhZjbAlrlv+bqPWFQgIZvl5s8g347+dWsfiBUMPN5sSKj5WTS2v6s891s3Vhjy Cb9nN8fIStc4KFcjYtRyfI4fwd1i1Op7ikFQ2yRIHJqNoewhWtF763XaZb+2VhG8FyI6 Lk2n0T8zzT3SYzdQR3a+QYSDBmU3XAwXOaqr+x9GiZMititSL7G34yEUI2pMRzidMHZc pZ2PJVMy1IpZjm6htdHrTf607v3RRfi+xeBAwc8wZ4gnTx67F7V9Ki7Bd5Vq12aQSe+9 NKxQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896359; x=1759501159; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=B7RhkLG3JZ6AWWqhGvapjhsuUBsTMt1EMhEwLlIwGFE=; b=JbByRSKGewJoLg+BPvBZgGSdOvdfIi01PULONRMR4PPl/YS2zP4bt4ddPZp3jdBx03 3qURQbJ3ML6G/LQoko4ugM+LT3GIFxbEoUdH4CwovwITV29fKqiUSG0OwIQ5yl5mV8Cl vANnPqJ1FkE7rYpvM2yAqbA0RuTpqxinWSwhlCfERrZBowE6lGWA+rfatSOmyHhKuodV 0O1H5sXJ+Xy6tEvHO8njl1R2rTVOojASnilWaAMcjej7wbwRTBsCMPKm+UEA11ipR66M 4CwcUdCAtAIFQ2F6pwsj9FX/VPZ2SWTvdfvgIsw2QWjLuKlGXUaGbUDdiTtvvGMIriEu A9tg== X-Gm-Message-State: AOJu0Yyk28MHG49LsuFy7JqFRZM8xRIgKT36vtsIiqY1ZCs5S2r8QPec IMYyp/+W4csXax7xL5ODJfwAYeYhRA712wtb/AEXayrZHinGUKF6y1yu X-Gm-Gg: ASbGncviEjm/GvZBFK11KSr1cp+BoZo19ckR/qG6c1+fzkyYvyC3J9jCSMijcgvqkWn cza9m9V5Vj2uKQWyM/1sQO4uGixZCsTyFPj800RxMUNDqz3AdlOctL9v6783F9X8oOp9S2h2Wga 9k5i0wKExWj703Hd7yY446zgN2E8ICKceEj0OlTg6WRnFDanJnwF8L0NEp3H6AcffySzVHXRO6b jgn9YSzopo1yACts6tVcYS9+tn8uZdCh1VnbTxZKV9ztIYPH0370QGNO7Fyjdl5VzrFXrUf2/H8 t4bYMZbfY1zKfFUMhnFbPWFlpkh2HIIJmMNj1FVDVOtrsT+aNYJGvOH2dpc0vj5Sc98OjLPNrn5 DBOROEjT9mvK1QzZBwAXdJmi2f0nbStRdDWApgXpFr8dBwsvN6BdnCvjt/Vyw+Le7ZZIx8CauDo qXPDc= X-Google-Smtp-Source: AGHT+IFYT5zqtCX6jEUpwYjGWGW6f0XlIeWfVAPlXuLdY4tpOyCPjaihGRB/xqEcUQS2/YwFMG0jYg== X-Received: by 2002:a05:622a:4a87:b0:4d2:be57:c380 with SMTP id d75a77b69052e-4da4bfc0801mr99026071cf.62.1758896358460; Fri, 26 Sep 2025 07:19:18 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id af79cd13be357-85c28a89bdfsm287474585a.18.2025.09.26.07.19.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:17 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v5 2/7] dt-bindings: leds: add default-brightness property to common.yaml Date: Fri, 26 Sep 2025 10:19:03 -0400 Message-ID: <20250926141913.25919-3-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add default-brightness property to leds/common.yaml to establish a single canonical definition for LED brightness initialization. The property is currently defined locally in leds/leds-pwm.yaml and is needed by auxdisplay/titanmec,tm16xx.yaml. Properties should be defined in only one location to avoid type inconsistencies across bindings. Signed-off-by: Jean-Fran=C3=A7ois Lessard --- Documentation/devicetree/bindings/leds/common.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documenta= tion/devicetree/bindings/leds/common.yaml index 3e8319e44339..96bd7fd0f053 100644 --- a/Documentation/devicetree/bindings/leds/common.yaml +++ b/Documentation/devicetree/bindings/leds/common.yaml @@ -173,6 +173,12 @@ properties: led-max-microamp. $ref: /schemas/types.yaml#/definitions/uint32 =20 + default-brightness: + description: + Brightness to be set if LED's default state is on. Used only during + initialization. If the option is not set then max brightness is used. + $ref: /schemas/types.yaml#/definitions/uint32 + panic-indicator: description: This property specifies that the LED should be used, if at all possi= ble, --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qt1-f175.google.com (mail-qt1-f175.google.com [209.85.160.175]) (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 E28981D5154 for ; Fri, 26 Sep 2025 14:19:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896368; cv=none; b=COWeJp06XBVTNlwJH8pazceyReHM+/VI8R8DA9/zMN5Lj99T2gt7RHU575dgqKR4gMU/+XiBwA6+DpvIyWQ1UGhysDlW8OQTIlD/8mHgDg3j3i5OnbjS+EXbPwM+oBYAUT7qGhRAh3VO+PHoEnwVj5yaj1VFVOXwBiF06+GuSFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896368; c=relaxed/simple; bh=qR2a+sIy1dcLg4TApO3hcXxTpdtuA3Glq5C890ldghM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=jf0kkJpCb9H/m73VMVMEjzQYR3foH1raeRrK0oi1Z5IxXmXYJt015aCKSvNTkxa0OrbaDe4pkH608SjFDeLm37VjozhbRKQTBp/7A/0i2TiKONWGFOIFDxEDpjsjLhfgh5hfqHCOwOq/YJHbu0lF6TvqmXOeunbW+DV07gRhSI0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PvkrRJjQ; arc=none smtp.client-ip=209.85.160.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PvkrRJjQ" Received: by mail-qt1-f175.google.com with SMTP id d75a77b69052e-4de584653cfso6853791cf.0 for ; Fri, 26 Sep 2025 07:19:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896363; x=1759501163; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9mRRNbCZTXtY9rhLVn7v5LDS/spOOkK98V0GNyagck8=; b=PvkrRJjQ4oNJLZGf5EyCtF68/3fyal4UdAIK5/WTnVfvbkP9leAbCpL2kGz8UDF7B9 WEDAZinJTTJq70pBz1K15VCTWM1zaz8w/z93VUhD0s4YiKQKHalo3fgmKZbZVAgxjhsf vOQBT4zGPW8o2KJsHZQcSfW7bH1eVRfMUBmqCNAYol63ViRoEzoQ2stpsCj0tovp6WHq +kp7jn/bwXjYSZSy/ESPDKsctEmUzqttYsbn83IOLxT18su0sTwjQCGJhRQBEmps5Ixa 4JvBFmuO1tHgor5DBoBkbj8r3c8O9z36Ad/VrWMagw3YU7bpsgEVeaz2Aup4+4uQW8gY wuiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896363; x=1759501163; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9mRRNbCZTXtY9rhLVn7v5LDS/spOOkK98V0GNyagck8=; b=Pi3qmoCBJfI6vyZogxlLXXZ1xWh63adOwQOOpQrxisv76q31C6bbLhMSgIwRu/ZeXW 18rslPZVdJEGdyte5aTrA/BnOS7/Ki1f0ZOE8n/JMUfzPFN0vJpyiMLs9T3v1iTkE9WC /d7LWykvFiEpQSbwES9BBPnJcL/cV7Ak32zVWL3jrwMK1XqR24PQQNvIcuVxX1xKUYP0 YlOLwrGK+hNkypyqLB86G5tp3r7YgoFYqxt74bDIuvXfAuNBgt5j14whchaofCjXiwz7 m8MTJ0zGnpdj29Qzeh/3TUVpGjlZgoCCH1xBFhmEraWtljx92OWfrbTNWPvyB4HRY4R2 0E/g== X-Gm-Message-State: AOJu0YydgBJWYOaxGnvtW5cQ5tQHQv5+u/KHh/scbFG25u3pHH0c7FQs 7EfJ6hhwKnCmxlZ7wAWAOacrGWqxYii+VGm+iaQXiwLdgmCoZpKlsPk5 X-Gm-Gg: ASbGncsj01iwQndhNnnUClIaPY9SUB5g0DfpCEq/I1kMQZYv3xKq3LN8xdTmQwjQ9jz kJVXppQ/QnvEbsRKS+me3kYtiNjG88Q2rBzEeCTGoadWnvnz5qBfXFIx5PLNUaD3cJBrUTGSEZA rHjzbPynnego7eLEZ4laS1P7ZwUCK6oDwosmaDTOFE0DE3jbXlpzmVFZ1D8f0vN6rW8bXSX/9ad TW9YR/KiYmnVaMf7zKG6O0Y/49mcYR7SwkLIIICycbparKKEyIOzAgYYnNF90eTOXeL+y38zfw8 v7b6RmAnpWx4KSqmYUm5YyMsl0yEfJpzTrL6T/5fqvuifNS8oYM7w+LTYeaDQn7FMCKMsGFI6bg CKmFgEV94FpsVgth7Y+7Sfcu/kzkYM+GQ3mCaHOr3jnBBwPhL+wSkmj6Qi9l5SrTSW1MWeHKwsi C+r90YEQGg5+4f8A== X-Google-Smtp-Source: AGHT+IEQRquuaaARqWA3ptXlfqCpzgIfaknBU2dPbrE0a9uXyM4/oQ1KfvORDQ1cqWtH62rjngjY+Q== X-Received: by 2002:ac8:5fd3:0:b0:4d3:a453:c503 with SMTP id d75a77b69052e-4da4e099ef0mr105385861cf.84.1758896359975; Fri, 26 Sep 2025 07:19:19 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4db0b9458e1sm26078801cf.15.2025.09.26.07.19.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:19 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v5 3/7] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx Date: Fri, 26 Sep 2025 10:19:04 -0400 Message-ID: <20250926141913.25919-4-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add documentation for TM16xx-compatible 7-segment LED display controllers with keyscan. Signed-off-by: Jean-Fran=C3=A7ois Lessard --- Notes: The 'segments' property is intentionally not vendor-prefixed as it defines a generic hardware description concept applicable to any 7-segment display controller. The property describes the fundamental grid/segment coordinate mapping that is controller-agnostic and could be reused by other LED matrix display bindings. Similar to how 'gpios' describes GPIO connections generically, 'segments' describes segment connections in a standardized way using uint32-matrix format. =20 The property uses explicit coordinate pairs to handle real-world hardware variations. Some board manufacturers use standard layouts (same grid, different segments per digit) while others use transposed layouts (same segment, different grids per digit). The coordinate-pair approach accommodates both patterns without requiring separate arrays or boolean flags, as confirmed acceptable by DT maintainers. .../bindings/auxdisplay/titanmec,tm16xx.yaml | 463 ++++++++++++++++++ MAINTAINERS | 5 + 2 files changed, 468 insertions(+) create mode 100644 Documentation/devicetree/bindings/auxdisplay/titanmec,t= m16xx.yaml diff --git a/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.y= aml b/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml new file mode 100644 index 000000000000..d324023bbffb --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml @@ -0,0 +1,463 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/auxdisplay/titanmec,tm16xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Auxiliary displays based on TM16xx and compatible LED controllers + +maintainers: + - Jean-Fran=C3=A7ois Lessard + +description: | + LED matrix controllers used in auxiliary display devices that drive indi= vidual + LED icons and 7-segment digit groups through a grid/segment addressing s= cheme. + Controllers manage a matrix of LEDs organized as grids (columns/banks in + vendor datasheets) and segments (rows/bit positions in vendor datasheets= ). + Maximum brightness and grid/segment indices are controller-specific. + Controller-specific maximum are validated in the driver. + + The controller is agnostic of the display layout. Board-specific LED wir= ing is + described through child nodes that specify grid/segment coordinates for + individual icons and segment mapping for 7-segment digits. + + The bindings use separate 'leds' and 'digits' containers to accommodate + different addressing schemes: + - LEDs use 2-cell addressing (grid, segment) for matrix coordinates + - Digits use 1-cell addressing with explicit segment mapping + + The controller node exposes a logical LED-like control for the aggregate + display brightness. Child nodes describe individual icons and 7-seg digi= ts. + The top-level control supports only label and brightness-related propert= ies + and does not support other common LED properties such as color or functi= on. + Child LED nodes use the standard LED binding. + + Optional keypad scanning is supported when both 'linux,keymap' and + 'poll-interval' properties are specified. + +properties: + compatible: + oneOf: + - items: + - enum: + - fdhisi,fd628 + - princeton,pt6964 + - wxicore,aip1628 + - const: titanmec,tm1628 + - items: + - enum: + - wxicore,aip1618 + - const: titanmec,tm1618 + - items: + - enum: + - fdhisi,fd650 + - wxicore,aip650 + - const: titanmec,tm1650 + - enum: + - fdhisi,fd620 + - fdhisi,fd655 + - fdhisi,fd6551 + - titanmec,tm1618 + - titanmec,tm1620 + - titanmec,tm1628 + - titanmec,tm1638 + - titanmec,tm1650 + - winrise,hbs658 + + reg: + maxItems: 1 + + label: + description: + The label for the top-level LED. If omitted, the label is taken from= the + node name (excluding the unit address). It has to uniquely identify a + device, i.e. no other LED class device can be assigned the same labe= l. + + max-brightness: + minimum: 0 # 0=3Doff + maximum: 8 # Maximum across all TM16xx controllers + description: + Normally the maximum brightness is determined by the hardware and th= is + property is not required. This property is used to put a software li= mit + on the brightness apart from what the driver says, as it could happen + that a LED can be made so bright that it gets damaged or causes dama= ge + due to restrictions in a specific system, such as mounting condition= s. + + default-brightness: + minimum: 0 # 0=3Doff + maximum: 8 # Maximum across all TM16xx controllers + description: + Brightness to be set if LED's default state is on. Used only during + initialization. If the option is not set then max brightness is used. + + digits: + type: object + description: Container for 7-segment digit group definitions + additionalProperties: false + + properties: + "#address-cells": + const: 1 + "#size-cells": + const: 0 + + patternProperties: + "^digit@[0-9]+$": + type: object + unevaluatedProperties: false + + properties: + reg: + description: + Digit position identifier numbered sequentially left-to-righ= t, + with reg=3D0 representing the leftmost digit position as dis= played + to the user. + maxItems: 1 + + segments: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + description: | + Array of grid/segment coordinate pairs for each 7-segment po= sition. + Each entry is mapping to standard 7-segment p= ositions + in order: a, b, c, d, e, f, g + + Standard 7-segment layout: + aaa + f b + f b + ggg + e c + e c + ddd + items: + items: + - description: Grid index + - description: Segment index + minItems: 7 + maxItems: 7 + + required: + - reg + - segments + + leds: + type: object + description: Container for individual LED icon definitions + additionalProperties: false + + properties: + "#address-cells": + const: 2 + "#size-cells": + const: 0 + + patternProperties: + "^led@[0-9]+,[0-9]+$": + type: object + $ref: /schemas/leds/common.yaml# + unevaluatedProperties: false + + properties: + reg: + description: + Grid and segment indices as of this individua= l LED icon + + required: + - reg + +dependencies: + poll-interval: + - linux,keymap + linux,keymap: + - poll-interval + autorepeat: + - linux,keymap + - poll-interval + +required: + - compatible + - reg + +allOf: + - $ref: /schemas/leds/common.yaml# + properties: + color: false + function: false + function-enumerator: false + - $ref: /schemas/input/input.yaml# + - $ref: /schemas/input/matrix-keymap.yaml# + # SPI controllers require 3-wire (combined MISO/MOSI line) + - if: + properties: + compatible: + contains: + enum: + - fdhisi,fd620 + - fdhisi,fd628 + - princeton,pt6964 + - titanmec,tm1618 + - titanmec,tm1620 + - titanmec,tm1628 + - titanmec,tm1638 + - wxicore,aip1618 + - wxicore,aip1628 + then: + $ref: /schemas/spi/spi-peripheral-props.yaml# + properties: + spi-3wire: true + required: + - spi-3wire + +unevaluatedProperties: false + +examples: + - | + #include + + // I2C example: Magicsee N5 TV box with fd655 controller + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + display@24 { + reg =3D <0x24>; + compatible =3D "fdhisi,fd655"; + + digits { + #address-cells =3D <1>; + #size-cells =3D <0>; + digit@0 { + reg =3D <0>; + segments =3D <1 3>, <1 4>, <1 5>, <1 0>, <1 1>, <1 2>, <1 6>; + }; + digit@1 { + reg =3D <1>; + segments =3D <2 3>, <2 4>, <2 5>, <2 0>, <2 1>, <2 2>, <2 6>; + }; + digit@2 { + reg =3D <2>; + segments =3D <3 3>, <3 4>, <3 5>, <3 0>, <3 1>, <3 2>, <3 6>; + }; + digit@3 { + reg =3D <3>; + segments =3D <4 3>, <4 4>, <4 5>, <4 0>, <4 1>, <4 2>, <4 6>; + }; + }; + + leds { + #address-cells =3D <2>; + #size-cells =3D <0>; + led@0,0 { + reg =3D <0 0>; + function =3D LED_FUNCTION_ALARM; + }; + led@0,1 { + reg =3D <0 1>; + function =3D LED_FUNCTION_USB; + }; + led@0,2 { + reg =3D <0 2>; + function =3D "play"; + }; + led@0,3 { + reg =3D <0 3>; + function =3D "pause"; + }; + led@0,4 { + reg =3D <0 4>; + function =3D "colon"; + }; + led@0,5 { + reg =3D <0 5>; + function =3D LED_FUNCTION_LAN; + }; + led@0,6 { + reg =3D <0 6>; + function =3D LED_FUNCTION_WLAN; + }; + }; + }; + }; + + - | + #include + + // SPI example: TM1638 module with keypad support + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + display@0 { + reg =3D <0>; + compatible =3D "titanmec,tm1638"; + spi-3wire; + spi-lsb-first; + spi-max-frequency =3D <500000>; + + label =3D "tm1638"; + default-brightness =3D <2>; + max-brightness =3D <4>; + poll-interval =3D <100>; + linux,keymap =3D ; + + digits { + #address-cells =3D <1>; + #size-cells =3D <0>; + + digit@0 { + reg =3D <0>; + segments =3D <7 0>, <7 1>, <7 2>, <7 3>, <7 4>, <7 5>, <7 6>; + }; + + digit@1 { + reg =3D <1>; + segments =3D <6 0>, <6 1>, <6 2>, <6 3>, <6 4>, <6 5>, <6 6>; + }; + + digit@2 { + reg =3D <2>; + segments =3D <5 0>, <5 1>, <5 2>, <5 3>, <5 4>, <5 5>, <5 6>; + }; + + digit@3 { + reg =3D <3>; + segments =3D <4 0>, <4 1>, <4 2>, <4 3>, <4 4>, <4 5>, <4 6>; + }; + + digit@4 { + reg =3D <4>; + segments =3D <3 0>, <3 1>, <3 2>, <3 3>, <3 4>, <3 5>, <3 6>; + }; + + digit@5 { + reg =3D <5>; + segments =3D <2 0>, <2 1>, <2 2>, <2 3>, <2 4>, <2 5>, <2 6>; + }; + + digit@6 { + reg =3D <6>; + segments =3D <1 0>, <1 1>, <1 2>, <1 3>, <1 4>, <1 5>, <1 6>; + }; + + digit@7 { + reg =3D <7>; + segments =3D <0 0>, <0 1>, <0 2>, <0 3>, <0 4>, <0 5>, <0 6>; + }; + }; + + leds { + #address-cells =3D <2>; + #size-cells =3D <0>; + + led@0,7 { + reg =3D <0 7>; + }; + + led@1,7 { + reg =3D <1 7>; + }; + + led@2,7 { + reg =3D <2 7>; + }; + + led@3,7 { + reg =3D <3 7>; + }; + + led@4,7 { + reg =3D <4 7>; + }; + + led@5,7 { + reg =3D <5 7>; + }; + + led@6,7 { + reg =3D <6 7>; + }; + + led@7,7 { + reg =3D <7 7>; + }; + }; + }; + }; + + - | + #include + + // SPI example: X96 Max with transposed layout (fd628 with tm1628 fall= back) + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + display@0 { + reg =3D <0>; + compatible =3D "fdhisi,fd628", "titanmec,tm1628"; + spi-3wire; + spi-lsb-first; + spi-max-frequency =3D <500000>; + + digits { + #address-cells =3D <1>; + #size-cells =3D <0>; + digit@0 { + reg =3D <0>; + segments =3D <0 7>, <1 7>, <2 7>, <3 7>, <4 7>, <5 7>, <6 7>; + }; + digit@1 { + reg =3D <1>; + segments =3D <0 6>, <1 6>, <2 6>, <3 6>, <4 6>, <5 6>, <6 6>; + }; + digit@2 { + reg =3D <2>; + segments =3D <0 5>, <1 5>, <2 5>, <3 5>, <4 5>, <5 5>, <6 5>; + }; + digit@3 { + reg =3D <3>; + segments =3D <0 4>, <1 4>, <2 4>, <3 4>, <4 4>, <5 4>, <6 4>; + }; + }; + + leds { + #address-cells =3D <2>; + #size-cells =3D <0>; + led@0,3 { + reg =3D <0 3>; + function =3D "apps"; + }; + led@1,3 { + reg =3D <1 3>; + function =3D "setup"; + }; + led@2,3 { + reg =3D <2 3>; + function =3D LED_FUNCTION_USB; + }; + led@3,3 { + reg =3D <3 3>; + function =3D LED_FUNCTION_SD; + }; + led@4,3 { + reg =3D <4 3>; + function =3D "colon"; + }; + led@5,3 { + reg =3D <5 3>; + function =3D "hdmi"; + }; + led@6,3 { + reg =3D <6 3>; + function =3D "video"; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f6206963efbf..9449dfc43a15 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25441,6 +25441,11 @@ W: http://sourceforge.net/projects/tlan/ F: Documentation/networking/device_drivers/ethernet/ti/tlan.rst F: drivers/net/ethernet/ti/tlan.* =20 +TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER +M: Jean-Fran=C3=A7ois Lessard +S: Maintained +F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml + TMIO/SDHI MMC DRIVER M: Wolfram Sang L: linux-mmc@vger.kernel.org --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qk1-f179.google.com (mail-qk1-f179.google.com [209.85.222.179]) (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 22A29231842 for ; Fri, 26 Sep 2025 14:19:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896368; cv=none; b=fuK+Txmkr+5ffa2x6LQH8OK1g2hFVjuuEnxtIVdwhmF1kz4dArC0yVqPMVo5/WQump0+3kYZ30lZyvtjepk8GAo2yZ3WmHpMS59Cv8cB15REVfxo5b1rAQIH/tkFX3S3q56vt/AYZTseZLQOxqnPmlm/o54sPjRmfnnojou2Sls= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896368; c=relaxed/simple; bh=2OMwKz/1ATmuVTJbraJlU8IK2jN/MaoeuTI10GJZZvQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GPLMxPuFJaqx5mNZXXtRb4oB0kYxhApIJTpfb50oeGpWWGU3RU6wTrAfQc/iM1ceXihFOqWFt8C2NjNdmp3G+w286lTSMh/E4pKaTMBJpEmCwDJNSaoqAy0+Pvatjf4u96UtNwkdWEiezd1pjZZVEIOkuSBoJ77b3/68PP8IPQQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=A/G/Hq2r; arc=none smtp.client-ip=209.85.222.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A/G/Hq2r" Received: by mail-qk1-f179.google.com with SMTP id af79cd13be357-85630d17586so289944785a.1 for ; Fri, 26 Sep 2025 07:19:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896362; x=1759501162; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=4Uy9+jaFxRcqCv0UwqDi99lZCm8ucSVuU36R2L4NzEY=; b=A/G/Hq2r3cemsMsnoBfmqU959W1YxlmrqU869O2rpzrRmRufK8ZnIl40Mg9lkHCA7V Ii4tayhXaF3OilVUrxgXLveSGIGQTn6/70Q8HV9oLrVQHozPhfk572jLk0A8KSqFjcaX Ex5EXBsyzKI8Vhl6HBcGx7dytgPHSd9ct0DSw/0WmOIdKGw5zzSqHhr8b1nNjFAoBQbU ACPdM4NBkQFNA3GaCv14IeAT3EPuaHS6t24IYh6swxEQMLPUnfJ+MrGI9j8pzILOlnaD ruc9dHoZWv0OWcldNdrnRfcUonV0q5ZG0fNxxODYDyslT9npVolziaXzRFdJUDB7F+mf aL7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896362; x=1759501162; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=4Uy9+jaFxRcqCv0UwqDi99lZCm8ucSVuU36R2L4NzEY=; b=kQ2p78gEtQ858XrN5+u+Z0Sv7rYa6xWaPD7zMnIznGHLU6oblIhm/H6ClOZID3S6Y/ jo+1vdDkRBbAI9v0Nv6/lInjf4z9agko37lBL1PbMz9eMQyYpAEzgXbhtzb0rQHl9tQP n+DVHpu0763/zm7UDoR3HVQneWSh1+kBfQe9F+9nYCIFgMmVfCURqv9S31tDuxOv0d/T 7SDBRhQj4citSl81r89Trr6nLaQCxe5H8f4iIl8YIbl/iyYFhrQ5VoXpTohP1lmnuEpb kOkBjuOiVDC2Kl52PIiXJxFcUa55FzRDt807YSLFtHkmCglqan07AmtAbXgkYiu7wOf9 nG9w== X-Gm-Message-State: AOJu0YzttMz/eA2r9Hup9p7AXpVOQ967zgVjOoB5paMfXlaU/nxgDE9E /MMtVA77FqXy6Im7sHuj4bzqNeYM1QBmditupOuUd1ZPHyVEJg9+HBxI X-Gm-Gg: ASbGncsyH+9/FhNCTBk1jHUUzT78IkqsO5SsLM4NODTzYG0dqfIiKZm1WFid8qPndp5 1nUVi6qaVkEVSAeUHONvxRkttWYEAwpX7Qw+zG9eztpehxnX7tcwJ7hU6CUjzmJfqwmYpK4ZpE0 zm/8FucPB4znel/dQ01dergfSWMsK/mGo5Mbs5UpW1dT3n6I4Cd+cNDaFit1iL3bVwr/w6xiWDM kK3FZcsJpbRKQD5nLroYnMYvLYo0H5v0qM+he+6+N8G93ZH7rQDdbRzKeiUUO7gNCTBAxqsXcWi AShFFshAqNdBZaUiuriLEkyJbxp0zkQu0YY/LZoVWsGYbo/qQswIP8LF/WaIPSe+zK/obrpITa7 iQZuaQk+SbJDDUrQK1DMG3V4iAMN8ZaME9BdbkUA+1s1OxUhJPZaNbc6HxtFzGke3Qa0gP0S2eo 38qJc= X-Google-Smtp-Source: AGHT+IH4DaQf5/6yYaeUP1YUsHvoy00yGi30pFOaVpofDbsKFOyEiBpS4GTO1pu03hhfewNCaKwpGw== X-Received: by 2002:a05:6214:2528:b0:78d:5c0b:eda5 with SMTP id 6a1803df08f44-7fc3ec907cdmr109095236d6.45.1758896361560; Fri, 26 Sep 2025 07:19:21 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-801667815bbsm27303716d6.40.2025.09.26.07.19.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:21 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org, Paolo Sabatino , Christian Hewitt , Martin Blumenstingl Subject: [PATCH v5 4/7] auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver Date: Fri, 26 Sep 2025 10:19:05 -0400 Message-ID: <20250926141913.25919-5-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add driver for TM16xx family LED controllers and compatible chips from multiple vendors including Titan Micro, Fuda Hisi, i-Core, Princeton, and Winrise. These controllers drive 7-segment digits and individual LED icons through either I2C or SPI buses. Successfully tested on various ARM TV boxes including H96 Max, Magicsee N5, Tanix TX3 Mini, Tanix TX6, X92, and X96 Max across different SoC platforms (Rockchip, Amlogic, Allwinner). Acked-by: Paolo Sabatino # As primary user, inte= grated tm16xx into Armbian rockchip64 Acked-by: Christian Hewitt # As primary user, = integrated tm16xx into LibreElec Tested-by: Christian Hewitt # Tested on X96 Ma= x, Tanix TX3 Mini Tested-by: Martin Blumenstingl # Teste= d on Tanix TX3 Mini Signed-off-by: Jean-Fran=C3=A7ois Lessard --- Notes: checkpatch reports false positives that are intentionally ignored: BIT_MACRO: bit shifts are used for field values while GENMASK/BIT are used for bit positions per semantic convention =20 LED registration uses non-devm variant on-purpose to allow explicit unregistration on device removal, ensuring LED triggers are immediately stopped. This prevents stale LED trigger activity from continuing after the hardware is gone, avoiding the need for complex state tracking in brightness callbacks. MAINTAINERS | 3 + drivers/auxdisplay/Kconfig | 9 + drivers/auxdisplay/Makefile | 2 + drivers/auxdisplay/tm16xx.h | 172 ++++++++++++ drivers/auxdisplay/tm16xx_core.c | 459 +++++++++++++++++++++++++++++++ 5 files changed, 645 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx.h create mode 100644 drivers/auxdisplay/tm16xx_core.c diff --git a/MAINTAINERS b/MAINTAINERS index 9449dfc43a15..7d5912f2d954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25444,7 +25444,10 @@ F: drivers/net/ethernet/ti/tlan.* TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER M: Jean-Fran=C3=A7ois Lessard S: Maintained +F: Documentation/ABI/testing/sysfs-class-leds-tm16xx F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml +F: drivers/auxdisplay/tm16xx.h +F: drivers/auxdisplay/tm16xx_core.c =20 TMIO/SDHI MMC DRIVER M: Wolfram Sang diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index bedc6133f970..7bacf11112b5 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -526,6 +526,15 @@ config SEG_LED_GPIO This driver can also be built as a module. If so, the module will be called seg-led-gpio. =20 +config TM16XX + tristate + select LEDS_CLASS + select LEDS_TRIGGERS + select LINEDISP + select NEW_LEDS + help + Core TM16XX-compatible 7-segment LED controllers module + # # Character LCD with non-conforming interface section # diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index f5c13ed1cd4f..7ecf3cd4a0d3 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_LINEDISP) +=3D line-display.o obj-$(CONFIG_MAX6959) +=3D max6959.o obj-$(CONFIG_PARPORT_PANEL) +=3D panel.o obj-$(CONFIG_SEG_LED_GPIO) +=3D seg-led-gpio.o +obj-$(CONFIG_TM16XX) +=3D tm16xx.o +tm16xx-y +=3D tm16xx_core.o diff --git a/drivers/auxdisplay/tm16xx.h b/drivers/auxdisplay/tm16xx.h new file mode 100644 index 000000000000..973b6ac19515 --- /dev/null +++ b/drivers/auxdisplay/tm16xx.h @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#ifndef _TM16XX_H +#define _TM16XX_H + +#include +#include +#include + +#include "line-display.h" + +/* Common bit field definitions */ + +/* Command type bits (bits 7-6) */ +#define TM16XX_CMD_MASK GENMASK(7, 6) +#define TM16XX_CMD_MODE (0 << 6) +#define TM16XX_CMD_DATA (1 << 6) +#define TM16XX_CMD_CTRL (2 << 6) +#define TM16XX_CMD_ADDR (3 << 6) +#define TM16XX_CMD_WRITE (TM16XX_CMD_DATA | TM16XX_DATA_MODE_WRITE) +#define TM16XX_CMD_READ (TM16XX_CMD_DATA | TM16XX_DATA_MODE_READ) + +/* Mode command grid settings (bits 1-0) */ +#define TM16XX_MODE_GRID_MASK GENMASK(1, 0) +#define TM16XX_MODE_4GRIDS (0 << 0) +#define TM16XX_MODE_5GRIDS (1 << 0) +#define TM16XX_MODE_6GRIDS (2 << 0) +#define TM16XX_MODE_7GRIDS (3 << 0) + +/* Data command settings */ +#define TM16XX_DATA_ADDR_MASK BIT(2) +#define TM16XX_DATA_ADDR_AUTO (0 << 2) +#define TM16XX_DATA_ADDR_FIXED (1 << 2) +#define TM16XX_DATA_MODE_MASK GENMASK(1, 0) +#define TM16XX_DATA_MODE_WRITE (0 << 0) +#define TM16XX_DATA_MODE_READ (2 << 0) + +/* Control command settings */ +#define TM16XX_CTRL_BR_MASK GENMASK(2, 0) +#define TM16XX_CTRL_ON (1 << 3) + +/* TM1618 specific constants */ +#define TM1618_BYTE1_MASK GENMASK(4, 0) +#define TM1618_BYTE2_MASK GENMASK(7, 5) +#define TM1618_BYTE2_SHIFT 3 +#define TM1618_KEY_READ_LEN 3 +#define TM1618_KEY_MASK (BIT(4) | BIT(1)) + +/* TM1628 specific constants */ +#define TM1628_BYTE1_MASK GENMASK(7, 0) +#define TM1628_BYTE2_MASK GENMASK(13, 8) +#define TM1628_KEY_READ_LEN 5 +#define TM1628_KEY_MASK (GENMASK(4, 3) | GENMASK(1, 0)) + +/* TM1638 specific constants */ +#define TM1638_KEY_READ_LEN 4 +#define TM1638_KEY_MASK (GENMASK(6, 4) | GENMASK(2, 0)) + +/* FD620 specific constants */ +#define FD620_BYTE1_MASK GENMASK(6, 0) + +#define FD620_BYTE2_MASK BIT(7) +#define FD620_BYTE2_SHIFT 5 +#define FD620_KEY_READ_LEN 4 +#define FD620_KEY_MASK (BIT(3) | BIT(0)) + +/* I2C controller addresses and control settings */ +#define TM1650_CMD_CTRL 0x48 +#define TM1650_CMD_READ 0x4F +#define TM1650_CMD_ADDR 0x68 +#define TM1650_CTRL_BR_MASK GENMASK(6, 4) +#define TM1650_CTRL_ON (1 << 0) +#define TM1650_CTRL_SLEEP (1 << 2) +#define TM1650_CTRL_SEG_MASK BIT(3) +#define TM1650_CTRL_SEG8_MODE (0 << 3) +#define TM1650_CTRL_SEG7_MODE (1 << 3) +#define TM1650_KEY_ROW_MASK GENMASK(1, 0) +#define TM1650_KEY_COL_MASK GENMASK(5, 3) +#define TM1650_KEY_DOWN_MASK BIT(6) +#define TM1650_KEY_COMBINED GENMASK(5, 3) + +#define FD655_CMD_CTRL 0x48 +#define FD655_CMD_ADDR 0x66 +#define FD655_CTRL_BR_MASK GENMASK(6, 5) +#define FD655_CTRL_ON (1 << 0) + +#define FD6551_CMD_CTRL 0x48 +#define FD6551_CTRL_BR_MASK GENMASK(3, 1) +#define FD6551_CTRL_ON (1 << 0) + +#define HBS658_KEY_COL_MASK GENMASK(7, 5) + +#define TM16XX_CTRL_BRIGHTNESS(on, val, prefix) \ + ((on) ? (FIELD_PREP(prefix##_CTRL_BR_MASK, (val)) | prefix##_CTRL_ON) : 0) + +/* Forward declarations */ +struct tm16xx_display; +struct tm16xx_digit; +struct tm16xx_led; + +/** + * DOC: struct tm16xx_controller - Controller-specific operations and limi= ts + * @max_grids: Maximum number of grids supported by the controller. + * @max_segments: Maximum number of segments supported by the controller. + * @max_brightness: Maximum brightness level supported by the controller. + * @max_key_rows: Maximum number of key input rows supported by the contro= ller. + * @max_key_cols: Maximum number of key input columns supported by the con= troller. + * @init: Pointer to controller mode/brightness configuration function. + * @data: Pointer to function writing display data to the controller. + * @keys: Pointer to function reading controller key state into bitmap. + * + * Holds function pointers and limits for controller-specific operations. + */ +struct tm16xx_controller { + const u8 max_grids; + const u8 max_segments; + const u8 max_brightness; + const u8 max_key_rows; + const u8 max_key_cols; + int (*const init)(struct tm16xx_display *display); + int (*const data)(struct tm16xx_display *display, u8 index, unsigned int = grid); + int (*const keys)(struct tm16xx_display *display); +}; + +/** + * struct tm16xx_display - Main driver structure for the display + * @dev: Pointer to device struct. + * @controller: Controller-specific function table and limits. + * @linedisp: character line display structure + * @spi_buffer: DMA-safe buffer for SPI transactions, or NULL for I2C. + * @num_hwgrid: Number of controller grids in use. + * @num_hwseg: Number of controller segments in use. + * @main_led: LED class device for the entire display. + * @leds: Array of individual LED icon structures. + * @num_leds: Number of individual LED icons. + * @digits: Array of 7-segment digit structures. + * @num_digits: Number of 7-segment digits. + * @flush_init: Work struct for configuration update. + * @flush_display: Work struct for display update. + * @flush_status: Status/result of last flush work. + * @lock: Mutex protecting concurrent access to work operations. + * @state: Bitmap holding current raw display state. + */ +struct tm16xx_display { + struct device *dev; + const struct tm16xx_controller *controller; + struct linedisp linedisp; + u8 *spi_buffer; + u8 num_hwgrid; + u8 num_hwseg; + struct led_classdev main_led; + struct tm16xx_led *leds; + u8 num_leds; + struct tm16xx_digit *digits; + u8 num_digits; + struct work_struct flush_init; + struct work_struct flush_display; + int flush_status; + struct mutex lock; /* prevents concurrent work operations */ + unsigned long *state; +}; + +int tm16xx_probe(struct tm16xx_display *display); +void tm16xx_remove(struct tm16xx_display *display); + +#endif /* _TM16XX_H */ diff --git a/drivers/auxdisplay/tm16xx_core.c b/drivers/auxdisplay/tm16xx_c= ore.c new file mode 100644 index 000000000000..e090c578f8a0 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_core.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "line-display.h" +#include "tm16xx.h" + +#define TM16XX_DIGIT_SEGMENTS 7 + +#define linedisp_to_tm16xx(display) \ + container_of(display, struct tm16xx_display, linedisp) + +/** + * struct tm16xx_led - Individual LED icon mapping + * @cdev: LED class device for sysfs interface. + * @hwgrid: Controller grid index of the LED. + * @hwseg: Controller segment index of the LED. + */ +struct tm16xx_led { + struct led_classdev cdev; + u8 hwgrid; + u8 hwseg; +}; + +/** + * struct tm16xx_digit - 7-segment digit mapping and value + * @hwgrids: Array mapping each 7-segment position to a grid on the contro= ller. + * @hwsegs: Array mapping each 7-segment position to a segment on the cont= roller. + * @value: Current character value displayed on this digit. + */ +struct tm16xx_digit { + u8 hwgrids[TM16XX_DIGIT_SEGMENTS]; + u8 hwsegs[TM16XX_DIGIT_SEGMENTS]; +}; + +/* state bitmap helpers */ +/** + * tm16xx_led_nbits() - Number of bits used for the display state bitmap + * @display: pointer to tm16xx_display + * + * Return: total bits in the display state bitmap (grids * segments) + */ +static inline unsigned int tm16xx_led_nbits(const struct tm16xx_display *d= isplay) +{ + return display->num_hwgrid * display->num_hwseg; +} + +/** + * tm16xx_set_seg() - Set the display state for a specific grid/segment + * @display: pointer to tm16xx_display + * @hwgrid: grid index + * @hwseg: segment index + * @on: true to turn on, false to turn off + */ +static inline void tm16xx_set_seg(const struct tm16xx_display *display, + const u8 hwgrid, const u8 hwseg, const bool on) +{ + assign_bit(hwgrid * display->num_hwseg + hwseg, display->state, on); +} + +/** + * tm16xx_get_grid() - Get the current segment pattern for a grid + * @display: pointer to tm16xx_display + * @index: grid index + * + * Return: bit pattern of all segments for the given grid + */ +static inline unsigned int tm16xx_get_grid(const struct tm16xx_display *di= splay, + const unsigned int index) +{ + return bitmap_read(display->state, index * display->num_hwseg, + display->num_hwseg); +} + +/* main display */ +/** + * tm16xx_display_flush_init() - Workqueue to configure controller and set= brightness + * @work: pointer to work_struct + */ +static void tm16xx_display_flush_init(struct work_struct *work) +{ + struct tm16xx_display *display =3D container_of(work, + struct tm16xx_display, + flush_init); + int ret; + + if (display->controller->init) { + scoped_guard(mutex, &display->lock) { + ret =3D display->controller->init(display); + display->flush_status =3D ret; + } + if (ret) + dev_err(display->dev, + "Failed to configure controller: %d\n", ret); + } +} + +/** + * tm16xx_display_flush_data() - Workqueue to update display data to contr= oller + * @work: pointer to work_struct + */ +static void tm16xx_display_flush_data(struct work_struct *work) +{ + struct tm16xx_display *display =3D container_of(work, + struct tm16xx_display, + flush_display); + unsigned int grid, i; + int ret =3D 0; + + scoped_guard(mutex, &display->lock) { + if (display->controller->data) { + for (i =3D 0; i < display->num_hwgrid; i++) { + grid =3D tm16xx_get_grid(display, i); + ret =3D display->controller->data(display, i, grid); + if (ret) { + dev_err(display->dev, + "Failed to write display data: %d\n", + ret); + break; + } + } + } + + display->flush_status =3D ret; + } +} + +/** + * tm16xx_brightness_set() - Set display main LED brightness + * @led_cdev: pointer to led_classdev + * @brightness: new brightness value + */ +static void tm16xx_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct tm16xx_display *display =3D dev_get_drvdata(led_cdev->dev->parent); + + led_cdev->brightness =3D brightness; + schedule_work(&display->flush_init); +} + +/** + * tm16xx_led_set() - Set state of an individual LED icon + * @led_cdev: pointer to led_classdev + * @value: new brightness (0/1) + */ +static void tm16xx_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct tm16xx_led *led =3D container_of(led_cdev, struct tm16xx_led, cdev= ); + struct tm16xx_display *display =3D dev_get_drvdata(led_cdev->dev->parent); + + tm16xx_set_seg(display, led->hwgrid, led->hwseg, value); + schedule_work(&display->flush_display); +} + +static int tm16xx_display_value(struct tm16xx_display *display, const char= *buf, size_t count) +{ + struct linedisp *linedisp =3D &display->linedisp; + struct linedisp_map *map =3D linedisp->map; + struct tm16xx_digit *digit; + unsigned int i, j; + int seg_pattern; + bool val; + + for (i =3D 0; i < display->num_digits && i < count; i++) { + digit =3D &display->digits[i]; + seg_pattern =3D map_to_seg7(&map->map.seg7, buf[i]); + + for (j =3D 0; j < TM16XX_DIGIT_SEGMENTS; j++) { + val =3D seg_pattern & BIT(j); + tm16xx_set_seg(display, digit->hwgrids[j], digit->hwsegs[j], val); + } + } + + for (; i < display->num_digits; i++) { + digit =3D &display->digits[i]; + for (j =3D 0; j < TM16XX_DIGIT_SEGMENTS; j++) + tm16xx_set_seg(display, digit->hwgrids[j], digit->hwsegs[j], 0); + } + + schedule_work(&display->flush_display); + return 0; +} + +static int tm16xx_linedisp_get_map_type(struct linedisp *linedisp) +{ + return LINEDISP_MAP_SEG7; +} + +static void tm16xx_linedisp_update(struct linedisp *linedisp) +{ + struct tm16xx_display *display =3D linedisp_to_tm16xx(linedisp); + + tm16xx_display_value(display, linedisp->buf, linedisp->num_chars); +} + +static const struct linedisp_ops tm16xx_linedisp_ops =3D { + .get_map_type =3D tm16xx_linedisp_get_map_type, + .update =3D tm16xx_linedisp_update, +}; + +static int tm16xx_display_init(struct tm16xx_display *display) +{ + schedule_work(&display->flush_init); + flush_work(&display->flush_init); + if (display->flush_status) + return display->flush_status; + + return 0; +} + +static int tm16xx_parse_fwnode(struct device *dev, struct tm16xx_display *= display) +{ + struct tm16xx_led *led; + struct tm16xx_digit *digit; + unsigned int max_hwgrid =3D 0, max_hwseg =3D 0; + unsigned int i, j; + int ret; + u32 segments[TM16XX_DIGIT_SEGMENTS * 2]; + u32 reg[2]; + + struct fwnode_handle *digits_node __free(fwnode_handle) =3D + device_get_named_child_node(dev, "digits"); + struct fwnode_handle *leds_node __free(fwnode_handle) =3D + device_get_named_child_node(dev, "leds"); + + /* parse digits */ + if (digits_node) { + display->num_digits =3D fwnode_get_child_node_count(digits_node); + + if (display->num_digits) { + display->digits =3D devm_kcalloc(dev, display->num_digits, + sizeof(*display->digits), + GFP_KERNEL); + if (!display->digits) + return -ENOMEM; + + i =3D 0; + fwnode_for_each_available_child_node_scoped(digits_node, child) { + digit =3D &display->digits[i]; + + ret =3D fwnode_property_read_u32(child, "reg", reg); + if (ret) + return ret; + + ret =3D fwnode_property_read_u32_array(child, + "segments", segments, + TM16XX_DIGIT_SEGMENTS * 2); + if (ret < 0) + return ret; + + for (j =3D 0; j < TM16XX_DIGIT_SEGMENTS; ++j) { + digit->hwgrids[j] =3D segments[2 * j]; + digit->hwsegs[j] =3D segments[2 * j + 1]; + max_hwgrid =3D umax(max_hwgrid, digit->hwgrids[j]); + max_hwseg =3D umax(max_hwseg, digit->hwsegs[j]); + } + i++; + } + } + } + + /* parse leds */ + if (leds_node) { + display->num_leds =3D fwnode_get_child_node_count(leds_node); + + if (display->num_leds) { + display->leds =3D devm_kcalloc(dev, display->num_leds, + sizeof(*display->leds), + GFP_KERNEL); + if (!display->leds) + return -ENOMEM; + + i =3D 0; + fwnode_for_each_available_child_node_scoped(leds_node, child) { + led =3D &display->leds[i]; + ret =3D fwnode_property_read_u32_array(child, "reg", reg, 2); + if (ret < 0) + return ret; + + led->hwgrid =3D reg[0]; + led->hwseg =3D reg[1]; + max_hwgrid =3D umax(max_hwgrid, led->hwgrid); + max_hwseg =3D umax(max_hwseg, led->hwseg); + i++; + } + } + } + + if (max_hwgrid >=3D display->controller->max_grids) { + dev_err(dev, "grid %u exceeds controller max_grids %u\n", + max_hwgrid, display->controller->max_grids); + return -EINVAL; + } + + if (max_hwseg >=3D display->controller->max_segments) { + dev_err(dev, "segment %u exceeds controller max_segments %u\n", + max_hwseg, display->controller->max_segments); + return -EINVAL; + } + + display->num_hwgrid =3D max_hwgrid + 1; + display->num_hwseg =3D max_hwseg + 1; + + return 0; +} + +/** + * tm16xx_probe() - Probe and initialize display device, register LEDs + * @display: pointer to tm16xx_display + * + * Return: 0 on success, negative error code on failure + */ +int tm16xx_probe(struct tm16xx_display *display) +{ + struct device *dev =3D display->dev; + struct led_classdev *main =3D &display->main_led; + struct led_init_data led_init =3D {0}; + struct fwnode_handle *leds_node; + struct tm16xx_led *led; + unsigned int nbits, i; + int ret; + + ret =3D tm16xx_parse_fwnode(dev, display); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse device tree\n"); + + nbits =3D tm16xx_led_nbits(display); + display->state =3D devm_bitmap_zalloc(dev, nbits, GFP_KERNEL); + if (!display->state) + return -ENOMEM; + + ret =3D devm_mutex_init(display->dev, &display->lock); + if (ret) + return dev_err_probe(dev, ret, "Failed to initialize mutex\n"); + + INIT_WORK(&display->flush_init, tm16xx_display_flush_init); + INIT_WORK(&display->flush_display, tm16xx_display_flush_data); + + /* Initialize main LED properties */ + led_init.fwnode =3D dev_fwnode(dev); /* apply label property */ + main->max_brightness =3D display->controller->max_brightness; + device_property_read_u32(dev, "max-brightness", &main->max_brightness); + main->max_brightness =3D umin(main->max_brightness, + display->controller->max_brightness); + + main->brightness =3D main->max_brightness; + device_property_read_u32(dev, "default-brightness", &main->brightness); + main->brightness =3D umin(main->brightness, main->max_brightness); + + main->brightness_set =3D tm16xx_brightness_set; + main->flags =3D LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME; + + /* Register individual LEDs from device tree */ + ret =3D led_classdev_register_ext(dev, main, &led_init); + if (ret) + return dev_err_probe(dev, ret, "Failed to register main LED\n"); + + i =3D 0; + led_init.devicename =3D dev_name(main->dev); + led_init.devname_mandatory =3D true; + led_init.default_label =3D "led"; + leds_node =3D device_get_named_child_node(dev, "leds"); + fwnode_for_each_available_child_node_scoped(leds_node, child) { + led_init.fwnode =3D child; + led =3D &display->leds[i]; + led->cdev.max_brightness =3D 1; + led->cdev.brightness_set =3D tm16xx_led_set; + led->cdev.flags =3D LED_RETAIN_AT_SHUTDOWN | LED_CORE_SUSPENDRESUME; + + ret =3D led_classdev_register_ext(dev, &led->cdev, &led_init); + if (ret) { + dev_err_probe(dev, ret, "Failed to register LED %s\n", + led->cdev.name); + goto unregister_leds; + } + + i++; + } + + ret =3D tm16xx_display_init(display); + if (ret) { + dev_err_probe(dev, ret, "Failed to initialize display\n"); + goto unregister_leds; + } + + ret =3D linedisp_attach(&display->linedisp, display->main_led.dev, + display->num_digits, &tm16xx_linedisp_ops); + if (ret) { + dev_err_probe(dev, ret, "Failed to initialize line-display\n"); + goto unregister_leds; + } + + return 0; + +unregister_leds: + while (i--) + led_classdev_unregister(&display->leds[i].cdev); + + led_classdev_unregister(main); + return ret; +} +EXPORT_SYMBOL_NS(tm16xx_probe, "TM16XX"); + +/** + * tm16xx_remove() - Remove display, unregister LEDs, blank output + * @display: pointer to tm16xx_display + */ +void tm16xx_remove(struct tm16xx_display *display) +{ + unsigned int nbits =3D tm16xx_led_nbits(display); + struct tm16xx_led *led; + + linedisp_detach(display->main_led.dev); + + /* + * Unregister LEDs first to immediately stop trigger activity. + * This prevents LED triggers from attempting to access hardware + * after it's been disconnected or driver unloaded. + */ + for (int i =3D 0; i < display->num_leds; i++) { + led =3D &display->leds[i]; + led_classdev_unregister(&led->cdev); + } + led_classdev_unregister(&display->main_led); + + /* Clear display state */ + bitmap_zero(display->state, nbits); + schedule_work(&display->flush_display); + flush_work(&display->flush_display); + + /* Turn off display */ + display->main_led.brightness =3D LED_OFF; + schedule_work(&display->flush_init); + flush_work(&display->flush_init); +} +EXPORT_SYMBOL_NS(tm16xx_remove, "TM16XX"); + +MODULE_AUTHOR("Jean-Fran=C3=A7ois Lessard"); +MODULE_DESCRIPTION("TM16xx LED Display Controllers"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("LINEDISP"); --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qk1-f178.google.com (mail-qk1-f178.google.com [209.85.222.178]) (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 6D1C6233140 for ; Fri, 26 Sep 2025 14:19:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896367; cv=none; b=iuyYgIuV6+98s2cVgTcCetEvSPt80c+TK4fwB1EOJC3kZfhwPhzLlIkPnshIO/1EvHvleCHYvFz0EwMtlmaafOkqAXLiy/DiS6gAt28I3Vqltd9eaLlKmAF8+V6+vqHkBXrRsQDtp4u9SpT0tO1B3Pwmj8XOvuv3H0BaflTkZqM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896367; c=relaxed/simple; bh=9Ui2JWuuTQlHA15UIIklNn4NgeHePSypmggyfQPN8kg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kWhHki8ZTCXCDoup/n8fdUHwSuseRUZuufoBR78VqcRVgJe+co7hjjDDHWsif0nnqeJUUhu9I6bKBgXIUy2Qx88/nWLkJuP9Kzg27aItSqHvBrQoGZjk/2A/LXCd613eZbNAkGUQMd94IQFys1iu4J2jUjQ1BrmPnIxJhQ7gyYQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HNGlKgw6; arc=none smtp.client-ip=209.85.222.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HNGlKgw6" Received: by mail-qk1-f178.google.com with SMTP id af79cd13be357-85c66a8a462so235803685a.0 for ; Fri, 26 Sep 2025 07:19:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896363; x=1759501163; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+5Z53YhKXh1/2eQ3YwIKhOHKLvwo4LoMLJEfYVEMvUI=; b=HNGlKgw658Ucv5HHtvRGqsNqARdQQrJReUPphh+XBfJzL/iXF7tZvjVSolDTjpnQdA pHDWHKHUzRACD70/E2B7rUqcnUCR0IXWeeHI/au0PPEZoEHrS/dORGzT9Vi5ZiUN7PT7 pdubRPwc80D4WKudbGVevgZo+Oxmwpwl+/9MvkTOrb/03BdNGsWR2aE2hkU47lV3g1nu 22ez9RXLG2Ck/rP9jOP/MNUbUXbp0NnXGOZK3aTvFuwNamTWGQj4wo1IAnBDd1eFAqH1 ajPraanAkeJY9+ZFOcarVOpst9a3jVbD4O0IECq01kuYmRoddyNl4s97vzG3b4p77pT3 xxiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896363; x=1759501163; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+5Z53YhKXh1/2eQ3YwIKhOHKLvwo4LoMLJEfYVEMvUI=; b=Gjwi1WexWBAIgwcGQHs3CnG9LCVN0VnNeOH3AZN+xruWtwNeqRmM7mZawNHNcrwOUy d4XtAPtlhJxFCcCCfODiMRqYNiRCwVEo40RHW82yzzA4p/eLyXinFPnPzvjdYEacFy5y 8PsfHUdHE5LVPll3pXzo39Fw7QbZJPBA4YkPnEScBAiv+T2K9JqfuTsXzkdhSqDWRjSq W9OJkn2behMYPOTNtfoAD7vP4eKBXo3O0zxOZz8SUBOMhBfvKiFj4yWtZId5exXTKpts 9YMPm/iJzb1aSvx8MZWbwfiBsuh1Jf6Xh+zhp46x8jh6ZSSpBNEdEAOuTftBoBZU6MVK xrGw== X-Gm-Message-State: AOJu0Yy/8YZ6r7JL8LuSalb9WxH65k0HIPsAhxax5psUgq7c79qGDEmC cUqxy/I6Whqs+6wtyP8HofSXBxbCOF5ADvbXZ4mChJYEfIURJVJ6lfbe X-Gm-Gg: ASbGncu9LUUYQfCcpqEOWC2fU6iZJviQYXXWKApz81QrlAAlIaDTU93B/2Fv0wy7Ue1 VWQTlDgFyGPRgU1NOeWu3Ecs6uBf+RyPSRIc+ALTOh3zZYdgFDgB0Kd4B3fSFCj6wXNL7qYHrs7 mfhVGyTGqx/9IKGxSklo2hpqWMwCoHd3cXFfxmyQ0CsztCVtVBwDortAvXJkpvpNfFsOQo2Klz7 6qNp2XHLO3vQPtrhDdDVUXWlePmRz5HYKqc/bZOvbvnZHkWwftujT7hIzj9yBPlhdST93xvswW4 CbDqMqMq0NRf7Y387x6fCDc9ZNXoVqgz5ESQzDNLf4pboUetuS5wb3mIaxj1jY+qOd9svJnGnpK +GcjYXszaba00Q1/UBAlcxRgNYqhVuf9GBl/yAwpFVP0JM1X/ryxZtuhoN54smada9aUNhlQJif xYxoE= X-Google-Smtp-Source: AGHT+IFwBDa6Bv27oC2ANkT+MkZSchV0/EWcocBzIVG3sPxwhPkAS2pGZM+hWYxQ2fmqa9tvX80cQA== X-Received: by 2002:a05:6214:529d:b0:7ea:136e:92ab with SMTP id 6a1803df08f44-7fc414d0cb8mr118869026d6.57.1758896363152; Fri, 26 Sep 2025 07:19:23 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-80173f45ee9sm26791986d6.64.2025.09.26.07.19.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:22 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v5 5/7] auxdisplay: TM16xx: Add keypad support for scanning matrix keys Date: Fri, 26 Sep 2025 10:19:06 -0400 Message-ID: <20250926141913.25919-6-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add support for keypad scanning on TM16xx-compatible auxiliary display controllers. It handles keypad initialization, scanning, and input reporting for matrix keys managed by the TM16xx devices. Key features include: - Input device registration configured by device properties (poll-interval, linux,keymap, autorepeat) - Key state tracking using managed bitmaps - Matrix scanning and event reporting integrated with Linux input subsystem This code is separated from main core driver to improve maintainability and reviewability. Signed-off-by: Jean-Fran=C3=A7ois Lessard --- Notes: checkpatch reports false positives that are intentionally ignored: COMPLEX_MACRO/MACRO_ARG_REUSE for tm16xx_for_each_key(): This is a standard iterator pattern following kernel conventions (similar to for_each_* macros throughout the kernel). The nested for loops are the correct implementation for matrix iteration. MAINTAINERS | 1 + drivers/auxdisplay/Kconfig | 9 ++ drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/tm16xx.h | 25 ++++ drivers/auxdisplay/tm16xx_core.c | 4 + drivers/auxdisplay/tm16xx_keypad.c | 196 +++++++++++++++++++++++++++++ 6 files changed, 236 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_keypad.c diff --git a/MAINTAINERS b/MAINTAINERS index 7d5912f2d954..84f2135903cd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25448,6 +25448,7 @@ F: Documentation/ABI/testing/sysfs-class-leds-tm16xx F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml F: drivers/auxdisplay/tm16xx.h F: drivers/auxdisplay/tm16xx_core.c +F: drivers/auxdisplay/tm16xx_keypad.c =20 TMIO/SDHI MMC DRIVER M: Wolfram Sang diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index 7bacf11112b5..f9a2c0641c3c 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -528,13 +528,22 @@ config SEG_LED_GPIO =20 config TM16XX tristate + depends on INPUT + select INPUT_MATRIXKMAP select LEDS_CLASS select LEDS_TRIGGERS select LINEDISP select NEW_LEDS + select TM16XX_KEYPAD if (INPUT) help Core TM16XX-compatible 7-segment LED controllers module =20 +config TM16XX_KEYPAD + bool + depends on TM16XX + help + Enable keyscan support for TM16XX driver. + # # Character LCD with non-conforming interface section # diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index 7ecf3cd4a0d3..a9b9c8ff05e8 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_PARPORT_PANEL) +=3D panel.o obj-$(CONFIG_SEG_LED_GPIO) +=3D seg-led-gpio.o obj-$(CONFIG_TM16XX) +=3D tm16xx.o tm16xx-y +=3D tm16xx_core.o +tm16xx-$(CONFIG_TM16XX_KEYPAD) +=3D tm16xx_keypad.o diff --git a/drivers/auxdisplay/tm16xx.h b/drivers/auxdisplay/tm16xx.h index 973b6ac19515..c503c6136807 100644 --- a/drivers/auxdisplay/tm16xx.h +++ b/drivers/auxdisplay/tm16xx.h @@ -103,6 +103,7 @@ struct tm16xx_display; struct tm16xx_digit; struct tm16xx_led; +struct tm16xx_keypad; =20 /** * DOC: struct tm16xx_controller - Controller-specific operations and limi= ts @@ -133,6 +134,7 @@ struct tm16xx_controller { * @dev: Pointer to device struct. * @controller: Controller-specific function table and limits. * @linedisp: character line display structure + * @keypad: Opaque pointer to tm16xx_keypad struct. * @spi_buffer: DMA-safe buffer for SPI transactions, or NULL for I2C. * @num_hwgrid: Number of controller grids in use. * @num_hwseg: Number of controller segments in use. @@ -150,6 +152,7 @@ struct tm16xx_controller { struct tm16xx_display { struct device *dev; const struct tm16xx_controller *controller; + struct tm16xx_keypad *keypad; struct linedisp linedisp; u8 *spi_buffer; u8 num_hwgrid; @@ -169,4 +172,26 @@ struct tm16xx_display { int tm16xx_probe(struct tm16xx_display *display); void tm16xx_remove(struct tm16xx_display *display); =20 +/* keypad support */ +#if IS_ENABLED(CONFIG_TM16XX_KEYPAD) +int tm16xx_keypad_probe(struct tm16xx_display *display); +void tm16xx_set_key(const struct tm16xx_display *display, const int row, + const int col, const bool pressed); +#else +static inline int tm16xx_keypad_probe(struct tm16xx_display *display) +{ + return 0; +} + +static inline void tm16xx_set_key(const struct tm16xx_display *display, + const int row, const int col, + const bool pressed) +{ +} +#endif + +#define tm16xx_for_each_key(display, _r, _c) \ + for (int _r =3D 0; _r < (display)->controller->max_key_rows; _r++) \ + for (int _c =3D 0; _c < (display)->controller->max_key_cols; _c++) + #endif /* _TM16XX_H */ diff --git a/drivers/auxdisplay/tm16xx_core.c b/drivers/auxdisplay/tm16xx_c= ore.c index e090c578f8a0..1d474d980254 100644 --- a/drivers/auxdisplay/tm16xx_core.c +++ b/drivers/auxdisplay/tm16xx_core.c @@ -408,6 +408,10 @@ int tm16xx_probe(struct tm16xx_display *display) goto unregister_leds; } =20 + ret =3D tm16xx_keypad_probe(display); + if (ret) + dev_warn(dev, "Failed to initialize keypad: %d\n", ret); + return 0; =20 unregister_leds: diff --git a/drivers/auxdisplay/tm16xx_keypad.c b/drivers/auxdisplay/tm16xx= _keypad.c new file mode 100644 index 000000000000..daa6afaf749a --- /dev/null +++ b/drivers/auxdisplay/tm16xx_keypad.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#include +#include +#include +#include +#include +#include + +#include "tm16xx.h" + +/** + * struct tm16xx_keypad - Keypad matrix state and input device + * @input: Input device for reporting key events. + * @state: Current bitmap of key states. + * @last_state: Previous bitmap of key states for change detection. + * @changes: Bitmap of key state changes since last poll. + * @row_shift: Row shift for keymap encoding. + */ +struct tm16xx_keypad { + struct input_dev *input; + unsigned long *state; + unsigned long *last_state; + unsigned long *changes; + int row_shift; +}; + +/** + * tm16xx_key_nbits() - Number of bits for the keypad state bitmap + * @display: pointer to tm16xx_display + * + * Return: total bits in keypad state bitmap (max_key_rows * max_key_cols) + */ +static inline unsigned int tm16xx_key_nbits(const struct tm16xx_display *d= isplay) +{ + return display->controller->max_key_rows * + display->controller->max_key_cols; +} + +/** + * tm16xx_get_key_row() - Get row index from keypad bit index + * @display: pointer to tm16xx_display + * @bit: bit index in state bitmap + * + * Return: row index + */ +static inline int tm16xx_get_key_row(const struct tm16xx_display *display, + const unsigned int bit) +{ + return bit / display->controller->max_key_cols; +} + +/** + * tm16xx_get_key_col() - Get column index from keypad bit index + * @display: pointer to tm16xx_display + * @bit: bit index in state bitmap + * + * Return: column index + */ +static inline int tm16xx_get_key_col(const struct tm16xx_display *display, + const unsigned int bit) +{ + return bit % display->controller->max_key_cols; +} + +/** + * tm16xx_set_key() - Set the keypad state for a key + * @display: pointer to tm16xx_display + * @row: row index + * @col: column index + * @pressed: true if pressed, false otherwise + */ +void tm16xx_set_key(const struct tm16xx_display *display, const int row, + const int col, const bool pressed) +{ + __assign_bit(row * display->controller->max_key_cols + col, + display->keypad->state, pressed); +} +EXPORT_SYMBOL_NS(tm16xx_set_key, "TM16XX"); + +/** + * tm16xx_keypad_poll() - Polls the keypad, reports events + * @input: pointer to input_dev + * + * Reads the matrix keypad state, compares with previous state, and + * reports key events to the input subsystem. + */ +static void tm16xx_keypad_poll(struct input_dev *input) +{ + struct tm16xx_display *display =3D input_get_drvdata(input); + struct tm16xx_keypad *keypad =3D display->keypad; + const unsigned short *keycodes =3D keypad->input->keycode; + unsigned int nbits =3D tm16xx_key_nbits(display); + unsigned int bit; + int row, col, scancode; + bool pressed; + int ret; + + bitmap_zero(keypad->state, nbits); + bitmap_zero(keypad->changes, nbits); + + scoped_guard(mutex, &display->lock) { + ret =3D display->controller->keys(display); + } + + if (ret) { + dev_err(display->dev, "Reading failed: %d\n", ret); + return; + } + + bitmap_xor(keypad->changes, keypad->state, keypad->last_state, nbits); + + for_each_set_bit(bit, keypad->changes, nbits) { + row =3D tm16xx_get_key_row(display, bit); + col =3D tm16xx_get_key_col(display, bit); + pressed =3D _test_bit(bit, keypad->state); + scancode =3D MATRIX_SCAN_CODE(row, col, keypad->row_shift); + + input_event(keypad->input, EV_MSC, MSC_SCAN, scancode); + input_report_key(keypad->input, keycodes[scancode], pressed); + } + input_sync(keypad->input); + + bitmap_copy(keypad->last_state, keypad->state, nbits); +} + +/** + * tm16xx_keypad_probe() - Initialize keypad/input device + * @display: pointer to tm16xx_display + * + * Return: 0 on success, negative error code on failure + */ +int tm16xx_keypad_probe(struct tm16xx_display *display) +{ + const unsigned int rows =3D display->controller->max_key_rows; + const unsigned int cols =3D display->controller->max_key_cols; + struct tm16xx_keypad *keypad; + struct input_dev *input; + unsigned int poll_interval, nbits; + int ret; + + if (!display->controller->keys || !rows || !cols) + return 0; /* keypad not supported */ + + if (!device_property_present(display->dev, "poll-interval") || + !device_property_present(display->dev, "linux,keymap")) + return 0; /* keypad disabled */ + + ret =3D device_property_read_u32(display->dev, "poll-interval", &poll_int= erval); + if (ret) + return dev_err_probe(display->dev, ret, + "Failed to read poll-interval\n"); + + keypad =3D devm_kzalloc(display->dev, sizeof(*keypad), GFP_KERNEL); + if (!keypad) + return -ENOMEM; + display->keypad =3D keypad; + + nbits =3D tm16xx_key_nbits(display); + keypad->state =3D devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL); + keypad->last_state =3D devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL= ); + keypad->changes =3D devm_bitmap_zalloc(display->dev, nbits, GFP_KERNEL); + if (!keypad->state || !keypad->last_state || !keypad->changes) + return -ENOMEM; + + input =3D devm_input_allocate_device(display->dev); + if (!input) + return -ENOMEM; + input->name =3D "tm16xx-keypad"; + keypad->input =3D input; + input_set_drvdata(input, display); + + keypad->row_shift =3D get_count_order(cols); /* !cols already checked */ + ret =3D matrix_keypad_build_keymap(NULL, "linux,keymap", rows, cols, NULL= , input); + if (ret) + return dev_err_probe(display->dev, ret, + "Failed to build keymap\n"); + + if (device_property_read_bool(display->dev, "autorepeat")) + __set_bit(EV_REP, input->evbit); + + input_setup_polling(input, tm16xx_keypad_poll); + input_set_poll_interval(input, poll_interval); + ret =3D input_register_device(input); + if (ret) + return dev_err_probe(display->dev, ret, + "Failed to register input device\n"); + + return 0; +} --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) (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 58C2D2F5A03 for ; Fri, 26 Sep 2025 14:19:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896369; cv=none; b=a39BPi0GIktjFXSOhEqF5gK9mldBVZ3OUqGdVE9ySk6zdFUo/cSuY3gYZKGTqyZCJ8ixNy9vybZMBnt6w/M7SGLNUlhqDMt2dRtM/JL7ND6ryldheveGF/iH8LJmuCUL/0NOQir0YMZ+q9CVwGzMnriy4vE445GCFfjvXaPhPDg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896369; c=relaxed/simple; bh=asZ2Wlro/pS3p0jUjhzoemq9ZGMzI94hYes+iD0+aRM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=L7IZzLpppzs1hBsIWE70Ce9FxQwoIS7/mb3m1+SbcyFJsNgmtHnPrR1DCSf0G3KzZ4fMbyq1X+ckfAX75Orud6jjTHbPTGUl8WtMZSvupBFVgEFK//x02ZG/UTYLYit5DylppmHkzhCg/PyX9TwF1gLUDiUXqUo0FiCbfEyKgEs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=lHY9dPzd; arc=none smtp.client-ip=209.85.219.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="lHY9dPzd" Received: by mail-qv1-f48.google.com with SMTP id 6a1803df08f44-796fe71deecso14752246d6.1 for ; Fri, 26 Sep 2025 07:19:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896365; x=1759501165; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XOfQE5oBLiaIDCba7tiIUOL+BqgettvGOPK0RrU22aU=; b=lHY9dPzd4lUrVzlRNmRqfIlpYtIlLoER+t0uRW+IFxygx+ak0F0I6O5hc2RKWIEDnk fk/Ng0jUUipIZ3jICVeeSufbMdNa3gJDns+7DGWzQh0PSpO5vFWoAvVjNIYjEwWizJ1u D/0mF3hj/wdeTpfKVoSM26D1skr7uksnHiUUlu5Z4D82vuS/IiN9x+CDOoUa37CNj8Iy cksnOjlkAujPaf4VsCMrVZPAD9eEVdlazvRWwrfBxag/UYvN5293xOVV4UU9uwchX0W0 T+TRSSfdjGmTA/L+noy6vkJykRL4Z13O5bCBhOif4Wm4Dv7c6re83A2xj1fnFIie4Tff kuMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896365; x=1759501165; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XOfQE5oBLiaIDCba7tiIUOL+BqgettvGOPK0RrU22aU=; b=csaO80hEEmbtZ09oYCVkz8fS/wOHSDFZtujNkSLsxkRl8JH3eMazjhMgXypf9y5T6Z F8fDI/AJ88laNHXzpI8sIyzJOxT/UowzMkOZxh5NY9whX89duvuKvohUqp0Ba6nCXlfz pqTB24mP/0/lVKqEelFSyVFJ6PWYEi3IQyVXq4xyeeKlroqDt1SksCDAxATZ3/vnk2pJ CApq8P5n8M8DU1aOuM5WmyOdXjiU46fsr3FSyrfetmqIMubVDC4NoewVZnVX9Y3zk8p1 IXOH7W4MjoOj0pPPJo0xAwvCL9quixDKCMb+lLZJZvMs0jnrEZlRDOpGz2hAuTIYYibw Oreg== X-Gm-Message-State: AOJu0Yx5UVaHOn4ZbNpJMsbrrdQ5b6gQzP8kutpzi8CPVTAyFthmI1D8 WOEVh1ooMna4igXYtHC/EydexfYU9WkwlLzgGIlM3eq0qTNzHY3EXboE X-Gm-Gg: ASbGncsbZUMZUZioKo9b2nLuHpQlhXjsaMm9lFYBsuMWzaR+cITRbpqakSzP5AN0U0u HGMaiAjT2hsQim0WeqAcAOkcVx7NUDyC60bD6Rcs1Xx3ct0MW0EKfWEQbL4Mn0EZ1zSQ/I2EvWM fJp3wSWIN6GfrlN51nWjKcSRy8BlV+MRUSJ3EjxqALPcFUwiO3jeJtpGkA+YAs2l5Dk2CQ/6ia/ lIutC1gjmPPSBLUsqGrDiZcESuNC0gMbJv5q/Af3mL5pY9cLgPrAE2zi/eyVZYTESWHbgZ7TSz+ AfLK6ouuD88EEPCPjnCzmAiEOosOeSZijtybVAKEm5MPnF/KFLbzf0BjCAn4/FnRGQ2aQBylZEN DOrzA0rWSUAIf5Q4rBefFuSzWIqXGB2eqnt9e1GLmOttrvS5XF7XrnA7PoEGLLQsdIeITrBhI1n 555GE= X-Google-Smtp-Source: AGHT+IFho9L0RXvygQQ4dAD7RopW7MUrD+j17K+DNATeBJS2QCR9SIEeHfamEuhl3BtZ4/91V2mnJw== X-Received: by 2002:a05:6214:e44:b0:7ad:19a4:53e0 with SMTP id 6a1803df08f44-7fc2fd77573mr93046146d6.26.1758896364657; Fri, 26 Sep 2025 07:19:24 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-801351c3283sm27501326d6.3.2025.09.26.07.19.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:24 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v5 6/7] auxdisplay: TM16xx: Add support for I2C-based controllers Date: Fri, 26 Sep 2025 10:19:07 -0400 Message-ID: <20250926141913.25919-7-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add support for TM16xx-compatible auxiliary display controllers connected via the I2C bus. The implementation includes: - I2C driver registration and initialization - Probe/remove logic for I2C devices - Controller-specific handling and communication sequences - Integration with the TM16xx core driver for common functionality This allows platforms using TM16xx or compatible controllers over I2C to be managed by the TM16xx driver infrastructure. Signed-off-by: Jean-Fran=C3=A7ois Lessard --- MAINTAINERS | 1 + drivers/auxdisplay/Kconfig | 16 ++ drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/tm16xx_i2c.c | 332 ++++++++++++++++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index 84f2135903cd..8a8a53efee52 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25448,6 +25448,7 @@ F: Documentation/ABI/testing/sysfs-class-leds-tm16xx F: Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml F: drivers/auxdisplay/tm16xx.h F: drivers/auxdisplay/tm16xx_core.c +F: drivers/auxdisplay/tm16xx_i2c.c F: drivers/auxdisplay/tm16xx_keypad.c =20 TMIO/SDHI MMC DRIVER diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index f9a2c0641c3c..d48c2f18950e 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -544,6 +544,22 @@ config TM16XX_KEYPAD help Enable keyscan support for TM16XX driver. =20 +config TM16XX_I2C + tristate "TM16XX-compatible I2C 7-segment LED controllers with keyscan" + depends on I2C + select TM16XX + help + This driver supports the following TM16XX compatible + I2C (2-wire) 7-segment led display chips: + - Titanmec: TM1650 + - Fuda Hisi: FD650, FD655, FD6551 + - i-Core: AiP650 + - Winrise: HBS658 + + To compile this driver as a module, choose M here: the module + will be called tm16xx_i2c and you will also get tm16xx for the + core module. + # # Character LCD with non-conforming interface section # diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index a9b9c8ff05e8..ba7b310f5667 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_SEG_LED_GPIO) +=3D seg-led-gpio.o obj-$(CONFIG_TM16XX) +=3D tm16xx.o tm16xx-y +=3D tm16xx_core.o tm16xx-$(CONFIG_TM16XX_KEYPAD) +=3D tm16xx_keypad.o +obj-$(CONFIG_TM16XX_I2C) +=3D tm16xx_i2c.o diff --git a/drivers/auxdisplay/tm16xx_i2c.c b/drivers/auxdisplay/tm16xx_i2= c.c new file mode 100644 index 000000000000..013becedac11 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_i2c.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#include +#include +#include +#include +#include + +#include "tm16xx.h" + +static int tm16xx_i2c_probe(struct i2c_client *client) +{ + const struct tm16xx_controller *controller; + struct tm16xx_display *display; + int ret; + + controller =3D i2c_get_match_data(client); + if (!controller) + return -EINVAL; + + display =3D devm_kzalloc(&client->dev, sizeof(*display), GFP_KERNEL); + if (!display) + return -ENOMEM; + + display->dev =3D &client->dev; + display->controller =3D controller; + + i2c_set_clientdata(client, display); + + ret =3D tm16xx_probe(display); + if (ret) + return ret; + + return 0; +} + +static void tm16xx_i2c_remove(struct i2c_client *client) +{ + struct tm16xx_display *display =3D i2c_get_clientdata(client); + + tm16xx_remove(display); +} + +/** + * tm16xx_i2c_write() - I2C write helper for controller + * @display: pointer to tm16xx_display structure + * @data: command and data bytes to send + * @len: number of bytes in @data + * + * Return: 0 on success, negative error code on failure + */ +static int tm16xx_i2c_write(struct tm16xx_display *display, u8 *data, size= _t len) +{ + struct i2c_client *i2c =3D to_i2c_client(display->dev); + + /* expected sequence: S Command [A] Data [A] P */ + struct i2c_msg msg =3D { + .addr =3D data[0] >> 1, + .flags =3D 0, + .len =3D len - 1, + .buf =3D &data[1], + }; + int ret; + + ret =3D i2c_transfer(i2c->adapter, &msg, 1); + if (ret < 0) + return ret; + + return (ret =3D=3D 1) ? 0 : -EIO; +} + +/** + * tm16xx_i2c_read() - I2C read helper for controller + * @display: pointer to tm16xx_display structure + * @cmd: command/address byte to send before reading + * @data: buffer to receive data + * @len: number of bytes to read into @data + * + * Return: 0 on success, negative error code on failure + */ +static int tm16xx_i2c_read(struct tm16xx_display *display, u8 cmd, u8 *dat= a, size_t len) +{ + struct i2c_client *i2c =3D to_i2c_client(display->dev); + + /* expected sequence: S Command [A] [Data] [A] P */ + struct i2c_msg msgs[1] =3D {{ + .addr =3D cmd >> 1, + .flags =3D I2C_M_RD | I2C_M_NO_RD_ACK, + .len =3D len, + .buf =3D data, + }}; + int ret; + + ret =3D i2c_transfer(i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + return (ret =3D=3D ARRAY_SIZE(msgs)) ? 0 : -EIO; +} + +/* I2C controller-specific functions */ +static int tm1650_init(struct tm16xx_display *display) +{ + const enum led_brightness brightness =3D display->main_led.brightness; + u8 cmds[2]; + + cmds[0] =3D TM1650_CMD_CTRL; + cmds[1] =3D TM16XX_CTRL_BRIGHTNESS(brightness, brightness, TM1650) | + TM1650_CTRL_SEG8_MODE; + + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static int tm1650_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 cmds[2]; + + cmds[0] =3D TM1650_CMD_ADDR + index * 2; + cmds[1] =3D grid; /* SEG 1 to 8 */ + + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static int tm1650_keys(struct tm16xx_display *display) +{ + int row, col; + bool pressed; + u8 keycode; + int ret; + + ret =3D tm16xx_i2c_read(display, TM1650_CMD_READ, &keycode, 1); + if (ret) + return ret; + + if (keycode =3D=3D 0x00 || keycode =3D=3D 0xFF) + return -EINVAL; + + row =3D FIELD_GET(TM1650_KEY_ROW_MASK, keycode); + pressed =3D FIELD_GET(TM1650_KEY_DOWN_MASK, keycode) !=3D 0; + if ((keycode & TM1650_KEY_COMBINED) =3D=3D TM1650_KEY_COMBINED) { + tm16xx_set_key(display, row, 0, pressed); + tm16xx_set_key(display, row, 1, pressed); + } else { + col =3D FIELD_GET(TM1650_KEY_COL_MASK, keycode); + tm16xx_set_key(display, row, col, pressed); + } + + return 0; +} + +static int fd655_init(struct tm16xx_display *display) +{ + const enum led_brightness brightness =3D display->main_led.brightness; + u8 cmds[2]; + + cmds[0] =3D FD655_CMD_CTRL; + cmds[1] =3D TM16XX_CTRL_BRIGHTNESS(brightness, brightness % 3, FD655); + + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static int fd655_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 cmds[2]; + + cmds[0] =3D FD655_CMD_ADDR + index * 2; + cmds[1] =3D grid; /* SEG 1 to 8 */ + + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static int fd6551_init(struct tm16xx_display *display) +{ + const enum led_brightness brightness =3D display->main_led.brightness; + u8 cmds[2]; + + cmds[0] =3D FD6551_CMD_CTRL; + cmds[1] =3D TM16XX_CTRL_BRIGHTNESS(brightness, ~(brightness - 1), FD6551); + + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static void hbs658_swap_nibbles(u8 *data, size_t len) +{ + for (size_t i =3D 0; i < len; i++) + data[i] =3D (data[i] << 4) | (data[i] >> 4); +} + +static int hbs658_init(struct tm16xx_display *display) +{ + const enum led_brightness brightness =3D display->main_led.brightness; + u8 cmd; + int ret; + + /* Set data command */ + cmd =3D TM16XX_CMD_WRITE | TM16XX_DATA_ADDR_AUTO; + hbs658_swap_nibbles(&cmd, 1); + ret =3D tm16xx_i2c_write(display, &cmd, 1); + if (ret) + return ret; + + /* Set control command with brightness */ + cmd =3D TM16XX_CMD_CTRL | + TM16XX_CTRL_BRIGHTNESS(brightness, brightness - 1, TM16XX); + hbs658_swap_nibbles(&cmd, 1); + ret =3D tm16xx_i2c_write(display, &cmd, 1); + if (ret) + return ret; + + return 0; +} + +static int hbs658_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 cmds[2]; + + cmds[0] =3D TM16XX_CMD_ADDR + index * 2; + cmds[1] =3D grid; + + hbs658_swap_nibbles(cmds, ARRAY_SIZE(cmds)); + return tm16xx_i2c_write(display, cmds, ARRAY_SIZE(cmds)); +} + +static int hbs658_keys(struct tm16xx_display *display) +{ + u8 cmd, keycode; + int col; + int ret; + + cmd =3D TM16XX_CMD_READ; + hbs658_swap_nibbles(&cmd, 1); + ret =3D tm16xx_i2c_read(display, cmd, &keycode, 1); + if (ret) + return ret; + + hbs658_swap_nibbles(&keycode, 1); + + if (keycode !=3D 0xFF) { + col =3D FIELD_GET(HBS658_KEY_COL_MASK, keycode); + tm16xx_set_key(display, 0, col, true); + } + + return 0; +} + +/* I2C controller definitions */ +static const struct tm16xx_controller tm1650_controller =3D { + .max_grids =3D 4, + .max_segments =3D 8, + .max_brightness =3D 8, + .max_key_rows =3D 4, + .max_key_cols =3D 7, + .init =3D tm1650_init, + .data =3D tm1650_data, + .keys =3D tm1650_keys, +}; + +static const struct tm16xx_controller fd655_controller =3D { + .max_grids =3D 5, + .max_segments =3D 7, + .max_brightness =3D 3, + .max_key_rows =3D 5, + .max_key_cols =3D 7, + .init =3D fd655_init, + .data =3D fd655_data, + .keys =3D tm1650_keys, +}; + +static const struct tm16xx_controller fd6551_controller =3D { + .max_grids =3D 5, + .max_segments =3D 7, + .max_brightness =3D 8, + .max_key_rows =3D 0, + .max_key_cols =3D 0, + .init =3D fd6551_init, + .data =3D fd655_data, +}; + +static const struct tm16xx_controller hbs658_controller =3D { + .max_grids =3D 5, + .max_segments =3D 8, + .max_brightness =3D 8, + .max_key_rows =3D 1, + .max_key_cols =3D 8, + .init =3D hbs658_init, + .data =3D hbs658_data, + .keys =3D hbs658_keys, +}; + +static const struct of_device_id tm16xx_i2c_of_match[] =3D { + { .compatible =3D "titanmec,tm1650", .data =3D &tm1650_controller }, + { .compatible =3D "fdhisi,fd6551", .data =3D &fd6551_controller }, + { .compatible =3D "fdhisi,fd655", .data =3D &fd655_controller }, + { .compatible =3D "winrise,hbs658", .data =3D &hbs658_controller }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tm16xx_i2c_of_match); + +static const struct i2c_device_id tm16xx_i2c_id[] =3D { + { "tm1650", (kernel_ulong_t)&tm1650_controller }, + { "fd6551", (kernel_ulong_t)&fd6551_controller }, + { "fd655", (kernel_ulong_t)&fd655_controller }, + { "hbs658", (kernel_ulong_t)&hbs658_controller }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tm16xx_i2c_id); + +static struct i2c_driver tm16xx_i2c_driver =3D { + .driver =3D { + .name =3D "tm16xx-i2c", + .of_match_table =3D tm16xx_i2c_of_match, + }, + .probe =3D tm16xx_i2c_probe, + .remove =3D tm16xx_i2c_remove, + .shutdown =3D tm16xx_i2c_remove, + .id_table =3D tm16xx_i2c_id, +}; +module_i2c_driver(tm16xx_i2c_driver); + +MODULE_AUTHOR("Jean-Fran=C3=A7ois Lessard"); +MODULE_DESCRIPTION("TM16xx-i2c LED Display Controllers"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("TM16XX"); --=20 2.43.0 From nobody Wed Oct 1 23:30:06 2025 Received: from mail-qt1-f173.google.com (mail-qt1-f173.google.com [209.85.160.173]) (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 9CFBF2FB0A9 for ; Fri, 26 Sep 2025 14:19:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896371; cv=none; b=jOMM8Aw4AirEkBhuNPNe1IHoA2grWVUCzqPGsdcWNlBh9wC/Q3XAaUmrXdbc93uD2k0Kx7y8Qwy998QKfXgrTcvq8F9vkQhMoWeONnl0ejHDndkjuot2p4zXiHkpMLEopNuz18lCdlFxcTEyupx7xcHGZrgko6xpV9rUgwQqxYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1758896371; c=relaxed/simple; bh=AFFdWPUAXAFNGiiOEJ3coICAhEAc2Pe4JDD1q1kvAsk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Z8sGnwrJPtDlm4mJyHuXKC5GxaUSQpF0wFht9l7qq+mQKBR5aZF/QczA1AGSiHmAEzSmWEIcIiBlw0DIqadeDb9bwWB98CkcCuvgcrkbb+En4Iz28r+ZSaRXEy0hsMnuGLPXCxj5uWnq/HnY2bhKg4RnNinFS/MqG8hPLAHz9kE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RQEC6Lpl; arc=none smtp.client-ip=209.85.160.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RQEC6Lpl" Received: by mail-qt1-f173.google.com with SMTP id d75a77b69052e-4dca66a31f7so16116351cf.3 for ; Fri, 26 Sep 2025 07:19:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1758896366; x=1759501166; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UkNfR44T1+aQMXki6JhTMzsXZD4Y5zeYdM+FX7jC1nQ=; b=RQEC6Lplq2Ua6dRSq4fQUzYnO/zK6IQfuNrn28qSLjrZCJH5R0LBYfNIgsWfAEqPz+ xjAxsWrmfDUb2HB17YdHFEwNap8AkpM4AgjPLO6o4u+I0ykTzfPkY0BCH0pQbTqnBqjY FdG5S8RopxZ5Womk/j6NdCc0E+Z9TS9MrUIVfFQ+U0VCHoFAm1/hxSRtRiOe6gEpspFa JeVZg8i82H6cbIOmzUZ11xsLgX51ICPk+CLrOaAXlDm3i4mdrEEOOFR8+Aw217YjqDTH sMgOplgNO5lZRx9KAvMCN4p8EiIA/jlQrrYHVuMK13cYA4PHC1kre+yfrRe9/3XpMj1P YrHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1758896366; x=1759501166; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UkNfR44T1+aQMXki6JhTMzsXZD4Y5zeYdM+FX7jC1nQ=; b=xSUOKORu0nD7qiFq1jaCkYUg8FWbZ98rnUFqJ9RhUmcpdfUJBWoLejb8t3D5eIm/z7 ESHz+mna+k2ylSFgFqGyNg4wu+h2JMs7PjYzW8oz+GbKE7KvW1Evhl4CbKN8JzqJMjyl RMFhx+XqreQ1S5TLxRAYUKwK/BE35RBJooUoEWL616MXwBcilVUljcndAz/bHg3BpKhb YItaVxbnvqfREE/YSOmAPWe4kDrL84DlMxUYWpSYh2jXbt885Ou2ju2Wjlj48akXaF/f j08x/UNFD3RWFNaCszw8mQTwlVmfQ43qiLVPPCWVepXVIrOoyDKgmjPm8XHRgO8K0DID NIVQ== X-Gm-Message-State: AOJu0YzTESKe9Eyj9GTw0zHqScUcGNuHsh6prCoXOAXYMZLuG5le85ZT geMHg9NDHZtRSwmlK5SmggpWoLIPW6bPkycwDXYURJ3cNY4Q/9U/Axvo X-Gm-Gg: ASbGncsZH2zTnOmNvU2WATKp8FDOXlTvKrdmM8ErCZHCxwlOrO2VlaYeOmTYyJama+9 Y/29dhP2i1K4KqwJFUqQRmy3LXfzpoJvLnKBA33q1zCRo1LzFAEeOaDhlx8Mkr3oKflC6brML+z hwHuSwCbLJC0tlEZzqoOVe4258Wm6dwTvgDc0Va5h0FYPf2Yr35MfwAQS62ttE8TpXMJre947il i4+JRHwyC9vQOnxz2APZkrS/BDtgK1vR+QMjyQlUJjEJ/niTLbt/BNbQNaPWJiDZAfUqN9NpC76 hvYx3xV07gWfTcHJRvZSRn0CmpmMn/iWYlvB37vaFiVyj2l+fME5sORamg0BbdemzWttf2sSFqW kST3zI/kLkvLQFgy87HTh1A03nM0HvNXSDmokKjr9hSMqA+pfQ9OR19b3ZrOUbIBIncVr X-Google-Smtp-Source: AGHT+IGODqsrQGtDZCGx0icngB4w/TaZU1Mpxmyd+O5pnNZPVLJSOQqzJ+8c4HQwg7u/RJ/qyoQEbw== X-Received: by 2002:a05:622a:2ce:b0:4d8:cb2f:7fac with SMTP id d75a77b69052e-4da4934ac23mr102100951cf.35.1758896366349; Fri, 26 Sep 2025 07:19:26 -0700 (PDT) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id af79cd13be357-85c341319besm286940985a.64.2025.09.26.07.19.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 26 Sep 2025 07:19:25 -0700 (PDT) From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Lessard?= To: Andy Shevchenko , Geert Uytterhoeven , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: linux-kernel@vger.kernel.org, linux-leds@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH v5 7/7] auxdisplay: TM16xx: Add support for SPI-based controllers Date: Fri, 26 Sep 2025 10:19:08 -0400 Message-ID: <20250926141913.25919-8-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250926141913.25919-1-jefflessard3@gmail.com> References: <20250926141913.25919-1-jefflessard3@gmail.com> 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 Add support for TM16xx-compatible auxiliary display controllers connected via the SPI bus. The implementation includes: - SPI driver registration and initialization - Probe/remove logic for SPI devices - Controller-specific handling and communication sequences - Integration with the TM16xx core driver for common functionality This allows platforms using TM16xx or compatible controllers over SPI to be managed by the TM16xx driver infrastructure. Signed-off-by: Jean-Fran=C3=A7ois Lessard --- MAINTAINERS | 1 + drivers/auxdisplay/Kconfig | 16 ++ drivers/auxdisplay/Makefile | 1 + drivers/auxdisplay/tm16xx_spi.c | 397 ++++++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index 8a8a53efee52..5d5e5f01e8ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25450,6 +25450,7 @@ F: drivers/auxdisplay/tm16xx.h F: drivers/auxdisplay/tm16xx_core.c F: drivers/auxdisplay/tm16xx_i2c.c F: drivers/auxdisplay/tm16xx_keypad.c +F: drivers/auxdisplay/tm16xx_spi.c =20 TMIO/SDHI MMC DRIVER M: Wolfram Sang diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig index d48c2f18950e..61e5af8d0a3d 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -560,6 +560,22 @@ config TM16XX_I2C will be called tm16xx_i2c and you will also get tm16xx for the core module. =20 +config TM16XX_SPI + tristate "TM16XX-compatible SPI 7-segment LED controllers with keyscan" + depends on SPI + select TM16XX + help + This driver supports the following TM16XX compatible + SPI (3-wire) 7-segment led display chips: + - Titanmec: TM1618, TM1620, TM1628, TM1638 + - Fuda Hisi: FD620, FD628 + - i-Core: AiP1618, AiP1628 + - Princeton: PT6964 + + To compile this driver as a module, choose M here: the module + will be called tm16xx_spi and you will also get tm16xx for the + core module. + # # Character LCD with non-conforming interface section # diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile index ba7b310f5667..2485a3a6769d 100644 --- a/drivers/auxdisplay/Makefile +++ b/drivers/auxdisplay/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_TM16XX) +=3D tm16xx.o tm16xx-y +=3D tm16xx_core.o tm16xx-$(CONFIG_TM16XX_KEYPAD) +=3D tm16xx_keypad.o obj-$(CONFIG_TM16XX_I2C) +=3D tm16xx_i2c.o +obj-$(CONFIG_TM16XX_SPI) +=3D tm16xx_spi.o diff --git a/drivers/auxdisplay/tm16xx_spi.c b/drivers/auxdisplay/tm16xx_sp= i.c new file mode 100644 index 000000000000..b305301f918c --- /dev/null +++ b/drivers/auxdisplay/tm16xx_spi.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#include +#include +#include +#include +#include + +#include "tm16xx.h" + +#define TM16XX_SPI_BUFFER_SIZE 8 +#define TM16XX_SPI_TWAIT_US 2 + +static int tm16xx_spi_probe(struct spi_device *spi) +{ + const struct tm16xx_controller *controller; + struct tm16xx_display *display; + int ret; + + controller =3D spi_get_device_match_data(spi); + if (!controller) + return -EINVAL; + + display =3D devm_kzalloc(&spi->dev, sizeof(*display), GFP_KERNEL); + if (!display) + return -ENOMEM; + + /* Allocate DMA-safe buffer */ + display->spi_buffer =3D devm_kzalloc(&spi->dev, TM16XX_SPI_BUFFER_SIZE, G= FP_KERNEL); + if (!display->spi_buffer) + return -ENOMEM; + + display->dev =3D &spi->dev; + display->controller =3D controller; + + spi_set_drvdata(spi, display); + + ret =3D tm16xx_probe(display); + if (ret) + return ret; + + return 0; +} + +static void tm16xx_spi_remove(struct spi_device *spi) +{ + struct tm16xx_display *display =3D spi_get_drvdata(spi); + + tm16xx_remove(display); +} + +/** + * tm16xx_spi_read() - SPI read helper for controller + * @display: pointer to tm16xx_display + * @cmd: command to send + * @cmd_len: length of command + * @data: buffer for received data + * @data_len: length of data to read + * + * Return: 0 on success, negative error code on failure + */ +static int tm16xx_spi_read(struct tm16xx_display *display, u8 *cmd, + size_t cmd_len, u8 *data, size_t data_len) +{ + struct spi_device *spi =3D to_spi_device(display->dev); + struct spi_message msg; + int ret; + + /* If STB is high during transmission, command is invalid. + * Reading requires a minimum 2 microseconds wait (Twait) + * after the 8th CLK rising edge before reading on falling edge. + */ + struct spi_transfer xfers[2] =3D { + { + .tx_buf =3D cmd, + .len =3D cmd_len, + .cs_change =3D 0, /* NO CS toggle */ + .delay.value =3D TM16XX_SPI_TWAIT_US, + .delay.unit =3D SPI_DELAY_UNIT_USECS, + }, { + .rx_buf =3D data, + .len =3D data_len, + } + }; + + spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers)); + + ret =3D spi_sync(spi, &msg); + + return ret; +} + +/** + * tm16xx_spi_write() - SPI write helper for controller + * @display: pointer to tm16xx_display + * @data: data to write + * @len: number of bytes to write + * + * Return: 0 on success, negative error code on failure + */ +static int tm16xx_spi_write(struct tm16xx_display *display, u8 *data, size= _t len) +{ + struct spi_device *spi =3D to_spi_device(display->dev); + + return spi_write(spi, data, len); +} + +/* SPI controller-specific functions */ +static int tm1628_init(struct tm16xx_display *display) +{ + const enum led_brightness brightness =3D display->main_led.brightness; + const u8 num_hwgrid =3D display->num_hwgrid; + u8 *cmd =3D display->spi_buffer; + int ret; + + /* Set mode command based on grid count */ + cmd[0] =3D TM16XX_CMD_MODE; + if (num_hwgrid <=3D 4) + cmd[0] |=3D TM16XX_MODE_4GRIDS; + else if (num_hwgrid =3D=3D 5) + cmd[0] |=3D TM16XX_MODE_5GRIDS; + else if (num_hwgrid =3D=3D 6) + cmd[0] |=3D TM16XX_MODE_6GRIDS; + else + cmd[0] |=3D TM16XX_MODE_7GRIDS; + + ret =3D tm16xx_spi_write(display, cmd, 1); + if (ret) + return ret; + + /* Set data command */ + cmd[0] =3D TM16XX_CMD_WRITE | TM16XX_DATA_ADDR_AUTO; + ret =3D tm16xx_spi_write(display, cmd, 1); + if (ret) + return ret; + + /* Set control command with brightness */ + cmd[0] =3D TM16XX_CMD_CTRL | + TM16XX_CTRL_BRIGHTNESS(brightness, brightness - 1, TM16XX); + ret =3D tm16xx_spi_write(display, cmd, 1); + if (ret) + return ret; + + return 0; +} + +static int tm1618_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 *cmd =3D display->spi_buffer; + + cmd[0] =3D TM16XX_CMD_ADDR + index * 2; + cmd[1] =3D FIELD_GET(TM1618_BYTE1_MASK, grid); + cmd[2] =3D FIELD_GET(TM1618_BYTE2_MASK, grid) << TM1618_BYTE2_SHIFT; + + return tm16xx_spi_write(display, cmd, 3); +} + +static int tm1628_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 *cmd =3D display->spi_buffer; + + cmd[0] =3D TM16XX_CMD_ADDR + index * 2; + cmd[1] =3D FIELD_GET(TM1628_BYTE1_MASK, grid); + cmd[2] =3D FIELD_GET(TM1628_BYTE2_MASK, grid); + + return tm16xx_spi_write(display, cmd, 3); +} + +static int tm1628_keys(struct tm16xx_display *display) +{ + u8 *cmd =3D display->spi_buffer; + u8 *codes =3D display->spi_buffer; + unsigned int i; + int bit, byte; + bool value; + int ret; + + cmd[0] =3D TM16XX_CMD_READ; + ret =3D tm16xx_spi_read(display, cmd, 1, codes, TM1628_KEY_READ_LEN); + if (ret) + return ret; + + /* prevent false readings */ + for (i =3D 0; i < TM1628_KEY_READ_LEN; i++) { + if (codes[i] & ~TM1628_KEY_MASK) + return -EINVAL; + } + + tm16xx_for_each_key(display, row, col) { + byte =3D col >> 1; + bit =3D row + ((col & 1) * 3); + value =3D !!(codes[byte] & BIT(bit)); + + tm16xx_set_key(display, row, col, value); + } + + return 0; +} + +static int tm1638_keys(struct tm16xx_display *display) +{ + u8 *cmd =3D display->spi_buffer; + u8 *codes =3D display->spi_buffer; + unsigned int i; + int bit, byte; + bool value; + int ret; + + cmd[0] =3D TM16XX_CMD_READ; + ret =3D tm16xx_spi_read(display, cmd, 1, codes, TM1638_KEY_READ_LEN); + if (ret) + return ret; + + /* prevent false readings */ + for (i =3D 0; i < TM1638_KEY_READ_LEN; i++) { + if (codes[i] & ~TM1638_KEY_MASK) + return -EINVAL; + } + + tm16xx_for_each_key(display, row, col) { + byte =3D col >> 1; + bit =3D (2 - row) + ((col & 1) << 2); + value =3D !!(codes[byte] & BIT(bit)); + + tm16xx_set_key(display, row, col, value); + } + + return 0; +} + +static int tm1618_keys(struct tm16xx_display *display) +{ + u8 *cmd =3D display->spi_buffer; + u8 *codes =3D display->spi_buffer; + unsigned int i; + int ret; + + cmd[0] =3D TM16XX_CMD_READ; + ret =3D tm16xx_spi_read(display, cmd, 1, codes, TM1618_KEY_READ_LEN); + if (ret) + return ret; + + /* prevent false readings */ + for (i =3D 0; i < TM1618_KEY_READ_LEN; i++) { + if (codes[i] & ~TM1618_KEY_MASK) + return -EINVAL; + } + + tm16xx_set_key(display, 0, 0, !!(codes[0] & BIT(1))); + tm16xx_set_key(display, 0, 1, !!(codes[0] & BIT(4))); + tm16xx_set_key(display, 0, 2, !!(codes[1] & BIT(1))); + tm16xx_set_key(display, 0, 3, !!(codes[1] & BIT(4))); + tm16xx_set_key(display, 0, 4, !!(codes[2] & BIT(1))); + + return 0; +} + +static int fd620_data(struct tm16xx_display *display, u8 index, + unsigned int grid) +{ + u8 *cmd =3D display->spi_buffer; + + cmd[0] =3D TM16XX_CMD_ADDR + index * 2; + cmd[1] =3D FIELD_GET(FD620_BYTE1_MASK, grid); + cmd[2] =3D FIELD_GET(FD620_BYTE2_MASK, grid) << FD620_BYTE2_SHIFT; + + return tm16xx_spi_write(display, cmd, 3); +} + +static int fd620_keys(struct tm16xx_display *display) +{ + u8 *cmd =3D display->spi_buffer; + u8 *codes =3D display->spi_buffer; + unsigned int i; + int ret; + + cmd[0] =3D TM16XX_CMD_READ; + ret =3D tm16xx_spi_read(display, cmd, 1, codes, FD620_KEY_READ_LEN); + if (ret) + return ret; + + /* prevent false readings */ + for (i =3D 0; i < FD620_KEY_READ_LEN; i++) { + if (codes[i] & ~FD620_KEY_MASK) + return -EINVAL; + } + + tm16xx_set_key(display, 0, 0, codes[0] & BIT(0)); + tm16xx_set_key(display, 0, 1, codes[0] & BIT(3)); + tm16xx_set_key(display, 0, 2, codes[1] & BIT(0)); + tm16xx_set_key(display, 0, 3, codes[1] & BIT(3)); + tm16xx_set_key(display, 0, 4, codes[2] & BIT(0)); + tm16xx_set_key(display, 0, 5, codes[2] & BIT(3)); + tm16xx_set_key(display, 0, 6, codes[3] & BIT(0)); + + return 0; +} + +/* SPI controller definitions */ +static const struct tm16xx_controller tm1618_controller =3D { + .max_grids =3D 7, + .max_segments =3D 8, + .max_brightness =3D 8, + .max_key_rows =3D 1, + .max_key_cols =3D 5, + .init =3D tm1628_init, + .data =3D tm1618_data, + .keys =3D tm1618_keys, +}; + +static const struct tm16xx_controller tm1620_controller =3D { + .max_grids =3D 6, + .max_segments =3D 10, + .max_brightness =3D 8, + .max_key_rows =3D 0, + .max_key_cols =3D 0, + .init =3D tm1628_init, + .data =3D tm1628_data, +}; + +static const struct tm16xx_controller tm1628_controller =3D { + .max_grids =3D 7, + .max_segments =3D 14, /* seg 11 unused */ + .max_brightness =3D 8, + .max_key_rows =3D 2, + .max_key_cols =3D 10, + .init =3D tm1628_init, + .data =3D tm1628_data, + .keys =3D tm1628_keys, +}; + +static const struct tm16xx_controller tm1638_controller =3D { + .max_grids =3D 8, + .max_segments =3D 10, + .max_brightness =3D 8, + .max_key_rows =3D 3, + .max_key_cols =3D 8, + .init =3D tm1628_init, + .data =3D tm1628_data, + .keys =3D tm1638_keys, +}; + +static const struct tm16xx_controller fd620_controller =3D { + .max_grids =3D 5, + .max_segments =3D 8, + .max_brightness =3D 8, + .max_key_rows =3D 1, + .max_key_cols =3D 7, + .init =3D tm1628_init, + .data =3D fd620_data, + .keys =3D fd620_keys, +}; + +static const struct of_device_id tm16xx_spi_of_match[] =3D { + { .compatible =3D "titanmec,tm1618", .data =3D &tm1618_controller }, + { .compatible =3D "titanmec,tm1620", .data =3D &tm1620_controller }, + { .compatible =3D "titanmec,tm1628", .data =3D &tm1628_controller }, + { .compatible =3D "titanmec,tm1638", .data =3D &tm1638_controller }, + { .compatible =3D "fdhisi,fd620", .data =3D &fd620_controller }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tm16xx_spi_of_match); + +static const struct spi_device_id tm16xx_spi_id[] =3D { + { "tm1618", (kernel_ulong_t)&tm1618_controller }, + { "tm1620", (kernel_ulong_t)&tm1620_controller }, + { "tm1628", (kernel_ulong_t)&tm1628_controller }, + { "tm1638", (kernel_ulong_t)&tm1638_controller }, + { "fd620", (kernel_ulong_t)&fd620_controller }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, tm16xx_spi_id); + +static struct spi_driver tm16xx_spi_driver =3D { + .driver =3D { + .name =3D "tm16xx-spi", + .of_match_table =3D tm16xx_spi_of_match, + }, + .probe =3D tm16xx_spi_probe, + .remove =3D tm16xx_spi_remove, + .shutdown =3D tm16xx_spi_remove, + .id_table =3D tm16xx_spi_id, +}; +module_spi_driver(tm16xx_spi_driver); + +MODULE_AUTHOR("Jean-Fran=C3=A7ois Lessard"); +MODULE_DESCRIPTION("TM16xx-spi LED Display Controllers"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("TM16XX"); --=20 2.43.0