From nobody Tue Dec 2 01:28:18 2025 Received: from mail-qt1-f179.google.com (mail-qt1-f179.google.com [209.85.160.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 15157313549 for ; Fri, 21 Nov 2025 14:59:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737160; cv=none; b=eiLi6qX8Wbv1AXHVmYs1xQKbmo5IePIfEtMDRf6jX5XXllPWJ8Y41lYCvX1A5Ux6pgSDW8KHOnW/SVHYV10MtzYHy1F6rb5FrnHO/FfsNcyayZL8v51X8RQTt+RrEDJk54xZzic/tPizMjM3VF1a0C0p46oloUlUNzboW/Cjidc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737160; c=relaxed/simple; bh=Xw0nStpLYuZulMcP9HgJTo/LqtWsv+lpf6jHuGJrwzo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=e2DDByOEte49ObnzIopYOOwRudyF5a59D/DD28u5iMM4W75h87vJz1lPerdWQNmK4BDUZJuKdfcwAw3v1+7DEh8NlBE57dwG52pzEkBRSC4GRjko64zHtn3NqBsiIPjUkj5ODBrGJUKWSnBasLbaZ6i40WF3tk+JgZkhRwxfEnY= 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=I2tWCChj; arc=none smtp.client-ip=209.85.160.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="I2tWCChj" Received: by mail-qt1-f179.google.com with SMTP id d75a77b69052e-4ee4c64190cso14791281cf.0 for ; Fri, 21 Nov 2025 06:59:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737156; x=1764341956; 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=4Q3lvOppbjkr07npv+SsBguvzh6HdJrUD4Vz5F4TU94=; b=I2tWCChjbuPROV77OMfy8YKgd766mJ40Ia/pLQWQ1jxRspd7oGQFBTCENKQ+DkxIqE ZKnGtmuShokrRYMGmXqp9H4YFMPeiERkiTcSVhen62VdTY5UK8j2F8pZ7PMgu66x4anE XqZHki6+IElugfnLTapwk3ZIpyfSoFTtTsxaNRGqgXR7Y7/2MCJki5MPZkNRmqBQ2yPl nuGrUN0e0Px6kgg9+/7b30ycaomDbXkf8bYoT+ft6whi/qJxjdLAKUpSOqe8yOoJaF4Y 2PPRCwWbTRV0eiu/IZkmNMEQuB8+lNPChCtd/0di3xRYEQhqFJnkAVvtFSuGVbsas9Y1 2mmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737156; x=1764341956; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=4Q3lvOppbjkr07npv+SsBguvzh6HdJrUD4Vz5F4TU94=; b=rCIccosI9UiCox5FPxinlTIXfh6Q/n9fnlmNG/TLP9LdU9uunp1afodSsGYo6wfyy6 6Ye1vOqCY5MYAB/muwF7oOdYyqYCX0BI+vHjTUgfg0DEgPQRO9YEmy/PFd2/ZZ6WP1m+ hLCLSDFTKvKgmvEKhI+vkrpuzDwke74b8pYovzNuIfNZHdXsao4VsweJQkiGFusfWIBW t3hXlLyR5CwGJx0e15UQIi0vhslTAane7xk7Gqhe0GDXkyTIW9qbjSAIMPCMJJgOUAPD SiozYHJk/bj/NGfqq7EicPtHvFGF/BXxLA2jd1NiMZ+Z8i9DatGzz1LA9aC+WIqTu6YE nz1w== X-Gm-Message-State: AOJu0YxVvfrQle6OzfEDdAzWxKg1wNVIuWJ+jJOX3lrEKDGo7fI+f9ch TedauyZfZ0EzfE2ansfQbrrE282oMMMUvHJdDaKT5P2o3LRsIZMR8yrR X-Gm-Gg: ASbGncsc7apFj9dy5H+1k7j9CEbuqhGiSqnlyqvQWz84N7zml7MZrd1dGaDQT0VjdCA BiGmKuDKz93fkoXcmUKd7fLWgg5GfYFrPbRKF2PwTxRyxlQBsQl0KiMvTfTwYsU+Oz9Turrxkhm yibxU4E8oK8qOWqfCS6GwAb1rFXrm/5Ia+0CM2nLusF4IzL3OScUxQ2fTAsjwapLO0gzdqXgL5Z 1pculNHzMpRC6VGz67duTD5gSq9KxZMdNZdLcTJaNO+j/dvIWvQJhjHBLA98hIBvK6cLa8Pb+Uv +j8cIGqYmcwG9UnaLT9HU6pE35BoJbO6LnR7yBi6mnBar+4FMqoM5sM+LD8SqmNPEj09gmgoxBg n79SJyhhnym4uHjataoaaWUvalOnrn2KANIbQcKodXYP3MKRWIzB5TbcZrNglExdKJ1ml3koJF2 qHjj3KJxUsr/8GTOsrjhgtWzYtAud8Mx4vdAN3QGdTt4pfs4pjyRKeiGSF X-Google-Smtp-Source: AGHT+IHWPQD2okrwn9ev0vEcZ0xOBm/fG65jQn48GrsJOgu31kH078v2zB1OvF3Cn3sE+j2X44acTQ== X-Received: by 2002:a05:622a:86:b0:4e8:b446:c01b with SMTP id d75a77b69052e-4ee58af12dbmr34796481cf.61.1763737155901; Fri, 21 Nov 2025 06:59:15 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4ee48d5241esm36424261cf.11.2025.11.21.06.59.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:15 -0800 (PST) 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 v6 1/7] dt-bindings: vendor-prefixes: Add fdhisi, titanmec, princeton, winrise, wxicore Date: Fri, 21 Nov 2025 09:59:01 -0500 Message-ID: <20251121145911.176033-2-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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 f1d1882009ba..6dd77c9ef111 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -562,6 +562,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,.*": @@ -1279,6 +1281,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,.*": @@ -1632,6 +1636,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,.*": @@ -1791,6 +1797,8 @@ patternProperties: description: Wingtech Technology Co., Ltd. "^winlink,.*": description: WinLink Co., Ltd + "^winrise,.*": + description: Shenzhen Winrise Technology Co., Ltd. "^winsen,.*": description: Winsen Corp. "^winstar,.*": @@ -1807,6 +1815,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 Tue Dec 2 01:28:18 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 4A65E2C0F8C for ; Fri, 21 Nov 2025 14:59:18 +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=1763737161; cv=none; b=dEWlDg71vMwpdUSpe5ZxYYJae3FOgYxNpU7Bw62leZKrTW9kDYfAokLhDfw6ny71lO4ZdwW5/x/p2Z6kNwc7vyoVyYoWzjVZhj2xILcJ7jaYLx1pHJpSTfIiDU6vhZcA9LlBuXKkh/Uj+Rd6b2s792Z9gsyXVG9W5ZlioKUFsLM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737161; c=relaxed/simple; bh=9YhlRXNK/AS0U6vPrcc+iqniROeSUye2gI5ifHJfW/g=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Yxy8TdZSfAwHq3O6fKpTmq7jIlwacAijBBYLBaG5Lc617hurTx7cybRvLvCNi8ks1oNRCw/e/l4lwTKzlCN0fP09//8iqU8iqiDLkOi4wHn46bn08X9NV6+qpMa9HIMD143WCyA1O3amsqbDetLh0hlunKAdWrMBrOU64Lk+dw4= 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=MzSVmZXC; 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="MzSVmZXC" Received: by mail-qk1-f178.google.com with SMTP id af79cd13be357-8b2ed01b95dso208715185a.0 for ; Fri, 21 Nov 2025 06:59:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737157; x=1764341957; 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=M8ea4pMOQnhPykgGqMiIn66ht6L9UZRItA8Bf1WZ4Jg=; b=MzSVmZXCXIRXWWYdnrOA/NJqPY3tg5EYOUbQKZ3uk6DuyY2iglU4dm3vLYvmh7K9OA 0MRDG1cQbAKK7egRpdDKZglmJP10HtMlUBXDNLnRK0ejMwU09cNvQmUBDkzFmXZdcMp7 /z3dkbVejM40F67Ewru1WW/ZohP8kwkVQG+lWn88Yp2YDKBGGhexwTXXuIuqjqn8wX6p r//9Mca2IcVqoaPvYwN8PsSbJJD6RhCVnFtmuF+fuXI2cdZwxWHRq9CHaY1CTUgYCaMc dVJMiQrsaVyupAZal6fH+yusq/QAe8b5Aju1mSchD5z8jj+LL1KSgpfTk6sWP+VAOQ1Y m3zA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737157; x=1764341957; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=M8ea4pMOQnhPykgGqMiIn66ht6L9UZRItA8Bf1WZ4Jg=; b=Mw3kBVskKbmtwVjnNYNyRpCVtirPS6P1OLHv2UJ/j+EbHNtP2jRBgl5ijX845vXCBW F3c1bglXlzzbLRGOP4M0JpgV6bc3uTZYC8tKZkraulSkKYvl8j3H+/ZfqLyJUJfBjWdU Tz0uPIeNlVyCh2iv/q7RAnHT3VnTr4tHr47VXztPl3uFNyHHJeqiXUYkpZX97DREO6Kq 4q2/Xr190QOC9YbEA6MVx5MJZNp7VNBcE8f84ISabpMbJGmmFmhbnKwkHtbfCASVHGnh gsmFrYCBl+e3vlJ14s/UUEfQR9M+qxg6SfdA9oP5wNzbE9gpEXTRSCn5aDNAlCjz77TS HfMQ== X-Gm-Message-State: AOJu0YzMLRhwLD6AfrxhQEIQPtDMMEL2EPWHjWATpnjGXDpuiL6R/ifC UADFc7HKEZ511K5CJXk9RoxGilluqHbVpakSz0gcebyRPKHcX51v/ghW X-Gm-Gg: ASbGncugxLP5HCNClQyMNtOsqfku9IrGIJ/QHeZEof9uSMsz1PtRj8OdzvRgn4XcKld EFvD/klE30ewzD8SDyY2e/RFetWO9sUHWNozCE1fYBIPdR3CcD8czwBvtQHkkHC31OVrmRNVSei vgwJfkQbCOIpNBuEz9ldQR/1acov2EHJop1o6i5icEoUVWoV01OcpyFSNfkMKuZTM64DOEJmhuv +FQ1WGVMjVjCyODXnBWm6s2C7L3omrhSrVC2xIpq5BJyeUaYzRjnqCS+0XqittE8ypW+lqPFOJ8 Xr189F/jUXTmyJLfXUpB0oSgm/DoCVA0SHOA6ezSXR3l5jIWBd7RoZWc8FlGyzWg0yxP/GxZyz/ KT+rdtYpvP57lb4T1wAUECNtl8alQPvFA0Kyr+P+joUHoMS2uyU4QItPw+HWzrLXaptcEtzTIeC UgOtiBkJPsF1Tl6U3qaFpvBd20nmDMaCBKwRyxQFLz3y37MTulaWuudh0d X-Google-Smtp-Source: AGHT+IGCuldQWb2AEu8tnF23ocVRpmUPhz8HWemssH2EO8+OXTAE32Y7Ibmho+V21o8Bh6+9GtDicw== X-Received: by 2002:a05:620a:4593:b0:8b2:74e5:b13 with SMTP id af79cd13be357-8b33d1d1118mr302903085a.32.1763737157429; Fri, 21 Nov 2025 06:59:17 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8b3295c3306sm383882685a.31.2025.11.21.06.59.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:17 -0800 (PST) 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 v6 2/7] dt-bindings: leds: add default-brightness property to common.yaml Date: Fri, 21 Nov 2025 09:59:02 -0500 Message-ID: <20251121145911.176033-3-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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. Reviewed-by: Rob Herring (Arm) 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 274f83288a92..f4e44b33f56d 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 Tue Dec 2 01:28:18 2025 Received: from mail-qk1-f176.google.com (mail-qk1-f176.google.com [209.85.222.176]) (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 BDE612F6939 for ; Fri, 21 Nov 2025 14:59:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737164; cv=none; b=G01Gxc0kGtGxeQ8fyjcBpe5gP/GwDvTKTPV/NZszWG4V9kFcSOKjj8iK+66i27mHyxDQMK47pu4Z2oVlLDGGWBGAUtNDkj2a1aewcNqHtRQa76pI6ks3tXzNfIVItNpeQMwtkbm6njYop8jW3i+KscHlJXJHOKgaMzLuUi6NVnw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737164; c=relaxed/simple; bh=0RYxF2O1I50G0xCMEk9os6H+1FU7Nj2Qsc46pGikx3E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=sg13ZUQNrW0hWHCGy6FXabo059CgdhZxPmR3Iv9trMnn+68oLF0H5D4soHFoqzgxL/i0Y4+NZmNCVv296TT72Mz7Z87LlJQkbLDVGHcNbQM4UL+yPRNwGY/AOBKp1p/JIcglg0g4WeUYAudrw4lv8xCHeGx47+gZsmpf6+zwh74= 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=T6BFudg/; arc=none smtp.client-ip=209.85.222.176 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="T6BFudg/" Received: by mail-qk1-f176.google.com with SMTP id af79cd13be357-8b22624bcdaso248259585a.3 for ; Fri, 21 Nov 2025 06:59:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737159; x=1764341959; 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=TFyXZv60JjVqu0d66mH+BvtprWywEdPKCOEamb24pyg=; b=T6BFudg/EFkSEHyoKcuvqcUbrkgHwO1x1NhLBZh9XG92dvPL3MpFYZvLTM4lBHXdpR m7czsHr3uk5Trfomm+Fs0g0E+dUS0ZWI/zlICbS47d91As9WaRJgOZ8xJ26r22O0CBeM adpV36IYjuEzTAkjf/QDNMdUBmgLmQLNB9d7sS94NCHlWEAVLm/YPhNL8JNAwWUSTr2S Jg+PFp6t0xXbjKmsIZlhTNWgMsfPkJMP+Wp43TzRj73YpvFJr4IAVUaqnhqj7RJa9Bp5 k1vkY91uQ5GaEJUw4857tStam9YpOS4EJMZiLLrIiOproPcLv1Z5g34+H7e6ibtxP0Xw iZcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737159; x=1764341959; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=TFyXZv60JjVqu0d66mH+BvtprWywEdPKCOEamb24pyg=; b=Bk4OUgPbQzZzJ4VcTZGZozz06BbKm3wUnyd7dxGR9Ta4zRXxTy6k8mxI4MgM4ycwS/ NvYzCZKnFOhFsdDxCzCFw1qgDQE8+CYfBtQbOAxETsEnHlvrjud9QBHRBZWNikiRl5z/ yRFavXlQVOvvUVkQMgGx874gKbgVha+/4OFgq+HNkf/b7GNidDHMD+sBiqY6aWdVPQKA HM/4V0wnw5YyJziKxwJhqZXHOq7B0o7FLmZ1T/VW5ZKst8veOzVUvrexUvOdto/nLV8O Ei4pyNkFSWN0Cdqb2npSNNv5/UcpHVzBdwizeGlC9IYoRTkxG0vX56iboFMPwyH8M7Ig J5Ew== X-Gm-Message-State: AOJu0YzQOdrxMDOuotmAci2AL4Wi9xqwf5t7mQUZ31FgEdyAQYVdqIYB z7g5bPz1yjar7zjHsFmrfgzb4KsupC3yhkmn4fj4RKNNO45Wigswzxtf X-Gm-Gg: ASbGncv2jAEhUcu3typMeloWQbvvm3MBmnbgl81E5zM7OWz4HTd7O6nc2uwiCFNEKb7 tGU5eNqKHFwp5t8tT5jtE1kKXUkLZxb1ncLssjU6Utm4SwoXC70jcmg4QBymMtKzTtDVdHP8p9R DPUbeNV9rBiLXDWdCxk6VgjNsbOfcOo5RqO7q3u+u0UMUEd1flY68BCLlKVByyLfviamkLztPRp C684dIQSuy1tfoZQWcLkbRWzR3WCJ2+21U0+8MJW53EciWmtdloOPre2pRJ2XCQVxTkts0/lhdz s3fbVb0oztGbmEumaBOQ/H/g+/ooZjRM0SJ/hCYAsk9ijwP2tSXNZu6uXdMx7tf8OEMxgqu0geB UtUvwpp89lh7Op5ZCZYsOWQt1fAijK9BP/4aTvw5NsPaXx2rsCrCiUEnjvAWLCKjTivZ6N/lDwn OCTgSrafRr6RVGzFHL3gEFQXlo4I3u9buAYedzlJyDMZ+mtB3XJXwi320q X-Google-Smtp-Source: AGHT+IFd4caIoqL9aFEyCoew15z3I1kr3N4w1lYRKu3YEsWf+q0KAidZs97UdhZL8qN6RoYTZlCEYA== X-Received: by 2002:a05:620a:46a0:b0:8b2:7165:544f with SMTP id af79cd13be357-8b33d1fd878mr311333285a.25.1763737158857; Fri, 21 Nov 2025 06:59:18 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8b32932ba76sm388744785a.4.2025.11.21.06.59.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:18 -0800 (PST) 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 v6 3/7] dt-bindings: auxdisplay: add Titan Micro Electronics TM16xx Date: Fri, 21 Nov 2025 09:59:03 -0500 Message-ID: <20251121145911.176033-4-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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. Retained 'properties:' wrapper for spi-3wire in conditional block. Rob Herring suggested removing it, but dt_binding_check requires explicit 'properties:' context when referencing peripheral properties within allOf conditional sections to satisfy unevaluatedProperties validation. This follows the pattern in spi-controller.yaml itself. .../bindings/auxdisplay/titanmec,tm16xx.yaml | 465 ++++++++++++++++++ MAINTAINERS | 5 + 2 files changed, 470 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..a852d8b8882c --- /dev/null +++ b/Documentation/devicetree/bindings/auxdisplay/titanmec,tm16xx.yaml @@ -0,0 +1,465 @@ +# 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-9a-f]+$": + 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-9a-f]+,[0-9a-f]+$": + type: object + description: + Individual LED icon addressed by , matrix coordin= ates. + $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 e64b94e6b5a9..8ccf02ca2544 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25914,6 +25914,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 Tue Dec 2 01:28:18 2025 Received: from mail-qv1-f41.google.com (mail-qv1-f41.google.com [209.85.219.41]) (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 B994F33CE91 for ; Fri, 21 Nov 2025 14:59:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737167; cv=none; b=eu1tfn0KLYsS1lKn+4F2GteqiaS7rull2vWIJbRMEc8xMNOS7OgqdsvQDFC5L4SA6l9G0rLIGsdcY+p9qccaXBteXKEUfHyw5KCVHdW3QEN5iBJyR7xA9YSdjrGnB+zauncC9RZibCh7WqQKjWQ47zzdTycAMSkn+9BI9n15YZI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737167; c=relaxed/simple; bh=S4sSF+WliB2XKbxy0gR75BFAM4Ei9WLGFCfdqq+fbPc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VjsDF9KVbesL9cbb70J4B6nXxFM3V5Su+ksShfWXq7WPHzlwbnUnivFivljJfWtK2hCs9Ll58I1ueICdDFQPuAIx/Eq5+Oz5VzkQoakMyDwVnmCFvwqPJi+X18ANncrK+GuFb7JMN14OMZGmg9v3EJpsalz6uQuDxft5Fd1c36U= 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=IMta0Ziy; arc=none smtp.client-ip=209.85.219.41 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="IMta0Ziy" Received: by mail-qv1-f41.google.com with SMTP id 6a1803df08f44-88051279e87so21007386d6.3 for ; Fri, 21 Nov 2025 06:59:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737161; x=1764341961; 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=bKQjWFZ6s5v1/CJZOjpbR5CY3BsDZRl41Ly30PSlyZM=; b=IMta0ZiyGtgrrT6svJNSK2hIxT7s5ugMrI+hIRG4v6AMSXmt2nUeYURTsT1uxG/tgW kFvjZlst4TE5fzGYw1EFBwE6Dz6BurLHLsOtM3DpXilqn9WCg9SQ3pSLTZrtbkVpzLhX yhyZMDZw0t0Xes3muOAWYn2wLDtHtEyAJPRVAaY0N8Xkct05IfaHZvdvejNSoKjWIjkw dMUKABQZaQ231ahYMGpb6SN50R3gKrz9zMXQy4tl8supTOyjIUpKYNSBHghavnk2oY5y jirUkuzFxkzK75gmm2zGFZoKQ2DPpqh+SYgY7e/ZHOOZHMbkr2RYRW1ssss+SukoWjOl acmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737161; x=1764341961; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=bKQjWFZ6s5v1/CJZOjpbR5CY3BsDZRl41Ly30PSlyZM=; b=tw8mVO4mZpSKdJlE2JNcGQeUY8wUQzsihGdjQ7CN6ylfhaKQxPb+J5GNUH16Qzwy5p iHNT3m8wfAzW7w4kpopmHltR2iEzsuevKinVwMZAAL87TKKdI7+mtk5QHNWpMq0Qy2S4 f3hAu/nrkqft3wgyElbyD6jYvMs3VSnlXvsJyYfHWfW1o6bUEEjyggWHaDW9kit3fgjx GzFby8mhIQBBg/1/dwyA5rTXKyhHHBRj2aujeLz7NLkbc1UO1I9MXtA2Y6ZRqHuRiNkB JqHzX/3fhRtbNSJVZ17q+4KqWS+Q3rtddNgjtxLQ10LD/uqzZIrXO0yVPbeyDEqev3gr m3bA== X-Gm-Message-State: AOJu0YwQFxX4H8YkZy/nM3pEJLryq+CRCxWhm+BckX0+ZVL9XiP/4+wU JFsEbO225zfRNnBi+/1kHoFUg5LEMbaufUcPr6CfNO7nUOQOASyGyq0R X-Gm-Gg: ASbGncsFwIYQ3oWtYR1C3+e1fhHrM2Tsc1Zd4SzcU5LRTbifMzdq4h0nuREikRPKDRM DlpBGjo1bq1c0Ewj7UIISRMUIna2iysOA0ifAEqWwVyzckEzNymwq6ow0l3DZAEdFQkej7YpjPm uruvl2LZhM5I8ea9DaPrOxv/tqGXJ5EQsD66L4bk6BS0a9iiPJromPX5s+pUYYAXfMZJAlJXzMc vaZbt3uDR5uqtZ0UzJmr41BIUmnp82b5gnHk3H/ooO0R4Xn8beIYJhvyUmYtuYTZhf4f0I9J6ct s1e/eu+Ns9q5/euwvtLyl/Y7aNLvWDdDQKqK2ZPCvhyLN+G92MXIoH0H3noZsAnpZ3RQTOMPflA ElAYwOzHWm6Ep62/wmWvPCpEi36yBOjW4h4Yq5cridS6Z9qQLEP+mf46M5SNrM4Lq4NlK+Q/n6R BlhgIo74SVGGwiDrQM9c9IHm9gLOsKo+2onmu/5O+2S3G2sm99JKl6OPyd X-Google-Smtp-Source: AGHT+IHwXqp7GKbFTLcYiJEWWM4gZ6I0auGFdL7cg8+sE9liHZy0zat6xP/oDFOT/uo59MrPrE0qng== X-Received: by 2002:a05:620a:7106:b0:8b2:e5da:d316 with SMTP id af79cd13be357-8b33d48b7d4mr277231785a.87.1763737160462; Fri, 21 Nov 2025 06:59:20 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id af79cd13be357-8b329431460sm376873585a.15.2025.11.21.06.59.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:20 -0800 (PST) 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 v6 4/7] auxdisplay: Add TM16xx 7-segment LED matrix display controllers driver Date: Fri, 21 Nov 2025 09:59:04 -0500 Message-ID: <20251121145911.176033-5-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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: Paolo Sabatino # Tested on H96 Max (X= Y_RK3328) 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 | 2 + drivers/auxdisplay/Kconfig | 12 + drivers/auxdisplay/Makefile | 2 + drivers/auxdisplay/tm16xx.h | 175 +++++++++++ drivers/auxdisplay/tm16xx_core.c | 484 +++++++++++++++++++++++++++++++ 5 files changed, 675 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx.h create mode 100644 drivers/auxdisplay/tm16xx_core.c diff --git a/MAINTAINERS b/MAINTAINERS index 8ccf02ca2544..d9badf2c24ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25918,6 +25918,8 @@ TM16XX-COMPATIBLE LED CONTROLLERS DISPLAY DRIVER M: Jean-Fran=C3=A7ois Lessard S: Maintained 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..6b7c04902649 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -526,6 +526,18 @@ 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 "TM16xx LED matrix display controllers" if COMPILE_TEST + select LEDS_CLASS + select LEDS_TRIGGERS + select LINEDISP + select NEW_LEDS + help + Core support for TM16xx-compatible 7-segment LED matrix display + controllers from multiple vendors (Titan Micro, Fuda Hisi, i-Core, + Princeton, Winrise). Provides LED class integration for display + control and optional keypad scanning support. + # # 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..ef6c004f9d89 --- /dev/null +++ b/drivers/auxdisplay/tm16xx.h @@ -0,0 +1,175 @@ +/* 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 +#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_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 device; +struct tm16xx_display; +struct tm16xx_digit; +struct tm16xx_led; + +/** + * struct tm16xx_controller - Controller-specific operations and limits + * @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 { + u8 max_grids; + u8 max_segments; + u8 max_brightness; + u8 max_key_rows; + u8 max_key_cols; + int (*init)(struct tm16xx_display *display); + int (*data)(struct tm16xx_display *display, u8 index, unsigned int grid); + int (*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..9c29b7fb1635 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_core.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TM16xx and compatible LED display/keypad controller driver + * Supports TM16xx, FD6xx, PT6964, HBS658, AIP16xx and related chips. + * + * Concurrency model: + * - Atomic display state bitmap writes for LED triggers in atomic context + * - Non-atomic display state reads in flush work provide eventual consist= ency + * - Mutex serializes hardware I2C/SPI transactions (sleeping context) + * - Workqueue prevents same work item running concurrently + * + * Uses explicit resource management (non-devm) for LEDs and workqueues + * to enforce removal ordering: unregister LEDs first to stop triggers + * before hardware cleanup, preventing use-after-free. + * + * Copyright (C) 2025 Jean-Fran=C3=A7ois Lessard + */ + +#include +#include +#include +#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_di= splay, 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 + * + * Atomic display state bitmap writes. May execute in atomic context. + */ +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 + * + * Non-atomic display state reads. Flush work provide eventual consistency. + * + * 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->n= um_hwseg); +} + +/* main display */ +/** + * tm16xx_display_flush_init() - Workqueue to configure controller and set= brightness + * @work: pointer to work_struct + * + * Configures controller and sets brightness. If an error occurs the error= code + * is stored in flush_status for upper layers to handle. + * + * Flush operations use mutex to serialize hardware transactions. Workqueue + * allows non-atomic context and ensures the same work never runs concurre= ntly. + */ +static void tm16xx_display_flush_init(struct work_struct *work) +{ + struct tm16xx_display *display =3D container_of(work, struct tm16xx_displ= ay, flush_init); + int ret; + + if (!display->controller->init) + return; + + 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 + * + * Updates all hardware grids with current display state. If an error occu= rs + * during any grid write, the operation is interrupted and the error code = is + * stored in flush_status for upper layers to handle. + * + * Flush operations use mutex to serialize hardware transactions. Workqueue + * allows non-atomic context and ensures the same work never runs concurre= ntly. + */ +static void tm16xx_display_flush_data(struct work_struct *work) +{ + struct tm16xx_display *display =3D container_of(work, struct tm16xx_displ= ay, flush_display); + unsigned int grid, i; + int ret =3D 0; + + if (!display->controller->data) + return; + + guard(mutex)(&display->lock); + + 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 + * + * Cannot sleep. Display brightness can be set by LED trigger in atomic co= ntext. + */ +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) + * + * Cannot sleep. LED brightness can be set by LED trigger in atomic contex= t. + */ +static void tm16xx_led_set(struct led_classdev *led_cdev, enum led_brightn= ess 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; + int seg_pattern, ret =3D 0; + unsigned int i, j; + bool val; + + for (i =3D 0; i < display->num_digits; i++) { + digit =3D &display->digits[i]; + + if (i < count) { + seg_pattern =3D map_to_seg7(&map->map.seg7, buf[i]); + if (seg_pattern < 0) { + dev_err(display->dev, + "Invalid mapping to 7 segment at position %u: %c", + i, buf[i]); + ret =3D -EINVAL; + seg_pattern =3D 0; + } + } else { + seg_pattern =3D 0; + } + + 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); + } + } + + schedule_work(&display->flush_display); + return ret; +} + +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) +{ + unsigned int max_hwgrid =3D 0, max_hwseg =3D 0; + u32 segments[TM16XX_DIGIT_SEGMENTS * 2]; + struct tm16xx_digit *digit; + struct tm16xx_led *led; + unsigned int i, j; + u32 reg[2]; + int ret; + + 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 */ + 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 */ + 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; +} + +int tm16xx_probe(struct tm16xx_display *display) +{ + struct led_classdev *main =3D &display->main_led; + struct led_init_data led_init =3D {}; + struct device *dev =3D display->dev; + 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 ret; + + /* + * Explicit (non-devm) resource management and specific order shutdown se= quence + * required to prevent hardware access races when triggers attempt to upd= ate + * the display during removal: + * 1. unregister LEDs to stop triggers + * 2. clear display + * 3. turn off display + */ + + 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); + /* max_brightness: handle default value and enforce hardware ceiling */ + 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); + + /* brightness: handle default value and enforce max ceiling */ + 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]; + /* Individual leds are hardware-constrained to on/off */ + 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_GPL(tm16xx_probe, "TM16XX"); + +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_GPL(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 Tue Dec 2 01:28:18 2025 Received: from mail-qv1-f47.google.com (mail-qv1-f47.google.com [209.85.219.47]) (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 C4DAA2C11E5 for ; Fri, 21 Nov 2025 14:59:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737167; cv=none; b=VLQ9HlKFkd3FtT6mH7Uqe+esa94ISI2zs2s56LlsYXavNWnhaxMxHbXdmasz75I4zWMpdWUChnRcHeUSvcyBgELTpyQqWctujTz9j+NvfP140UgCeA0mvxTQhH9iY5hHeE+PI2AZvsh6M0VZHJIIGWDP8m9MM7zoL5YvJ6s1LiQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737167; c=relaxed/simple; bh=CDsAuPIiLayhdT+9qQThRp44J92JGxMkfUix5OCTJ9M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=L4WCqEspQA8CkcLDR5j3iXKoBQ8vHjNOU16ldt2EWHZUSTwxsVKP82slN7HUVA4mXRaqmfxX9vk6p5hI1+dbUBYWeMs3wCTLXP53pGFwxudN22MWUdeY9hzQFbrU4uKlZEhlFB/So3MS1K5yhQ+5wq79sqBTytSrj861XDjK/5I= 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=bKmA8etP; arc=none smtp.client-ip=209.85.219.47 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="bKmA8etP" Received: by mail-qv1-f47.google.com with SMTP id 6a1803df08f44-8823e39c581so30587956d6.3 for ; Fri, 21 Nov 2025 06:59:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737162; x=1764341962; 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=oKqQLX6Fb4cYelB2ki4ZNF0Xx/caSApe6NjsM2eEIbk=; b=bKmA8etP/Cw4f/A4twLz/A3Y0qzQZA3FcGzTGXOYv5DErKChrStwmIXOCsHy3lGnvH SkzlBYLp9tqW4lQLZjClcs+nMWEwIH3AiWSEsYvUCukANhay5iz2cnXVb/lnk+zBbHV/ 33XAgvf+ZiitR+SxQROqk1XlIse0hzHXPps5pjbUzliAI494DeZUVt4BPvnYwdIN+PoB W9P/jEqgd/dXrNPLlIFqEPBSwsdCmdLYazQQlUk7BOwp9WlyYV65DVLMtwlJCM6zs8Hi Ca0pyLiylQEAiKm6ToTCvK38ETkkOAYiSeEXARe7zs/18SYop6Ifk5eMPo7G+VwZ7V2m Xcpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737162; x=1764341962; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=oKqQLX6Fb4cYelB2ki4ZNF0Xx/caSApe6NjsM2eEIbk=; b=NREWuChAT14Z5EpGjDSVJu5SlLGMmumqOTn1ViU4c2pGxOw/v2tGXU3ZJP+hwl1p/9 frQkCrPvNGicCXg/ymV/J0teS/qgw/sNQZtCjDXQz63oclTi/tlL13I+IA4+X/YMBtNI QtIeDFgteUZHynIqFae8qSRdfIaPLFrDeUfAazA3HOivppKCJeDT6194+CAvEB4sOjjV whcZSdXSlOFRE1MmDa83mrmMTnnlCpntYsAoAISglcpad5wZj+uM/oRcjFygtouj5Uei OnSLpv6RKLqq6gWhg/sAOJmlplcyXdnDzk4N5NWWWykdsDGLJppItXQ1o66G0Fu+lH9j Wspg== X-Gm-Message-State: AOJu0Yx1elpXED/ZiqzJ9urHDZ92YG487wdW2iKbDjcm+7XsYAQMfsiJ tobe89vvaDHFdvoJKk5S6hqApjc6u/ndov67P/lm9tKIiimivcbmF+JW X-Gm-Gg: ASbGncum2RYN3gJh0nFJWhzjtUz+J19qUfAXMNhetQfaAOMfRU/+GYr0MN1WrcuGCPt 59fZ4rWK1C4v3EpubBGQ8TecZHHhYX1pwRatGO6bqMIQDZcbc38Q6Z8nyP51jk4/9ca4fJPoidG WlzIlTRAFGgPs0nBthdP1RWjJOSp/j95ctLwUqeqgWk0vhmiNwSCo007Ng7lNNl2EfSsNY93kMA SOpXctrRMVXV3u8cK+tzGu7HLd8fcU7BKfjU7FJ6wHOiC6amO7Fu6VrGJsfF8p0/hp3fs0dzhjJ YGZnrlugaBYemGsQ01AotH8S88Jdn5X7blFlKijZ1unFvI/XM0Gwt5fwAw0Un7QsnQBYctzKqwH AnD95nQcTrZ5iXdbNWsfpDXs7G466QlaDQXWUCXy4go4rLsgobKu0u276TTBYTyzsUXYxqmeifO be6v+QK8ckpkhFtdoaj+AMaZ6Y6YkI4d4PrjCd2u0OBkcAkf0Ui8cn8r4mcqE2YqmCLE4= X-Google-Smtp-Source: AGHT+IEMZ/IEcjT3fWhKQDHpDJEhDw1HCETsf++WNjGnSznrc3BYXDVGkEfPQyNyF3PrUUbfQJ0YnQ== X-Received: by 2002:a05:6214:4a87:b0:87f:e1b3:2014 with SMTP id 6a1803df08f44-8847c546ba3mr33837306d6.66.1763737161822; Fri, 21 Nov 2025 06:59:21 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8846e54d13dsm40327896d6.35.2025.11.21.06.59.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:21 -0800 (PST) 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 v6 5/7] auxdisplay: TM16xx: Add keypad support for scanning matrix keys Date: Fri, 21 Nov 2025 09:59:05 -0500 Message-ID: <20251121145911.176033-6-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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 | 192 +++++++++++++++++++++++++++++ 6 files changed, 232 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_keypad.c diff --git a/MAINTAINERS b/MAINTAINERS index d9badf2c24ba..21ba2a99b581 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25920,6 +25920,7 @@ S: Maintained 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 6b7c04902649..afd8ce05c668 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -528,16 +528,25 @@ config SEG_LED_GPIO =20 config TM16XX tristate "TM16xx LED matrix display controllers" if COMPILE_TEST + depends on INPUT + select INPUT_MATRIXKMAP select LEDS_CLASS select LEDS_TRIGGERS select LINEDISP select NEW_LEDS + select TM16XX_KEYPAD if (INPUT) help Core support for TM16xx-compatible 7-segment LED matrix display controllers from multiple vendors (Titan Micro, Fuda Hisi, i-Core, Princeton, Winrise). Provides LED class integration for display control and optional keypad scanning support. =20 +config TM16XX_KEYPAD + bool + depends on TM16XX + help + Enable optional 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 ef6c004f9d89..af0ed889ab1a 100644 --- a/drivers/auxdisplay/tm16xx.h +++ b/drivers/auxdisplay/tm16xx.h @@ -106,6 +106,7 @@ struct device; struct tm16xx_display; struct tm16xx_digit; struct tm16xx_led; +struct tm16xx_keypad; =20 /** * struct tm16xx_controller - Controller-specific operations and limits @@ -136,6 +137,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. @@ -153,6 +155,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; @@ -172,4 +175,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 9c29b7fb1635..03e9484235b0 100644 --- a/drivers/auxdisplay/tm16xx_core.c +++ b/drivers/auxdisplay/tm16xx_core.c @@ -437,6 +437,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..be867b250da5 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_keypad.c @@ -0,0 +1,192 @@ +// 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 "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_GPL(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); + int row, col, scancode; + unsigned int bit; + 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); +} + +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; + unsigned int poll_interval, nbits; + struct tm16xx_keypad *keypad; + struct input_dev *input; + 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 Tue Dec 2 01:28:18 2025 Received: from mail-qv1-f46.google.com (mail-qv1-f46.google.com [209.85.219.46]) (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 EB3252C178E for ; Fri, 21 Nov 2025 14:59:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737168; cv=none; b=W/n9rZDaQsUsKuqqp4QpcEYj08bMwvTCTRA99EYZaATpSgLJYwcGcc7HhDfNl3a1w5VKVMgpqmnLPsQf16Ef4l6ihYvQmu7NMmYa2AaPUHvStLplOfD0g7jpnTwtzta1LBgn8MjLR7I12I6MJdwXxIfetjQFREfjgPtbP+b4pDc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737168; c=relaxed/simple; bh=mGW92khnnO3PrOI+/H3ty3104c+ZoynQCMWUQw5yFbk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=iM5VyxEI4S2DKQPecFtMCpAKi/xHHSyw7y0GXDYbVpCPVDU8iD10N7lVaUdeMZUmw/n5whDUdWkFHZKIKqLH7wx/u+TW7pyP0bDdmgRHbjs7oid4MP/h6EvDWmzJezb/zM0gjAKsGWaUJG3/WmOR5N4SNO9jlpDfvc4+9EsMXPE= 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=kEg6JJW+; arc=none smtp.client-ip=209.85.219.46 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="kEg6JJW+" Received: by mail-qv1-f46.google.com with SMTP id 6a1803df08f44-882360ca0e2so15505426d6.0 for ; Fri, 21 Nov 2025 06:59:24 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737163; x=1764341963; 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=cyP4s9+W8f5HjZlC55gU7BK+yTyqgscqIRWflvGV340=; b=kEg6JJW+f5gjlQj72FFpxa8boI/LUCvU9GKnDtZK/sR3FzsqVO0+Xp5P2iGETDNpgS s1IYlKTv6dnZob/EdeMCQdEHT6aly1ICkDlKztCNkXKmk2CyB16+3U2j1UA2IG4n0aR0 cepVOALMjk6aksTso5KbYdyq3kUZhutj9loZZyUne3FnpOCu1SpPYwpI1Cq1wL9FEXRJ ElhYEpUWOMuxtPe73r2XjOoMjHUQY5cGza9YtoxAZHyrE63Q88ga3G7+GQb5rRCOd/q4 Cf19Sy/ijO2/ZR1hsGSJtaAbsT7LnTYblCLE9G8XEJkYnLpZRxMac5AoNR9JSPm0UMuX i1Vg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737163; x=1764341963; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=cyP4s9+W8f5HjZlC55gU7BK+yTyqgscqIRWflvGV340=; b=UDQtiD+3BQAdyF6kciiHdugmB33GFiuVTz6lDjCXFfRtUn3ctMKPaicl5V/05iH8m1 tdwjr4nSp0oSMtsfuKxNhZGhBBAJWDjlu2ENCnr2XlTxtZcqCW1+73+Zd2LYPdTMWHzQ HGkKEiS4GTVydA15t4ui1aXjGD/YQuxVfPsO86Hsq7Zul2UQq5VKXZtsp2OOFaykcfsI 4kYZJyHflc4rkZPMwBV8iJqCnDfwIeQHGLL4qiJYQOjShE/Wao2D+aYJhpUOqKVzM83I EMzE0t1DkdtL1iNMrBZuK6+SzwU6Ok8yNuomRpacKA4Al7jzg2j35dnMluFu28DDQtSW RYzw== X-Gm-Message-State: AOJu0Yw9syWOsOEpqbgry/5Qh5VfO1lErArHh2BxzY4n62UPozUJE8kU 4DfXoVNJvLd1BuglSQaZr0tkXcGFdYHJezD8LlL7Z5l63F0qHWcrEIbi X-Gm-Gg: ASbGnctKT07FsGfOXzM6e4UgFpGno1QxApz15PuaKkFHDswrUnltAEhGA1kiFd3vUUy o78/PfHcptvKnnKi9tBOivbfeRhSAypsyj02T8AoVqvuMvh7y3Q7sQhgPWPxRpECdczTDTplu/Q lmvRBV8vJf55Ha+ZUDMEmumRqzLrZ5uj5PcerApWYGFtwV3LFZUHlVm2x+/dAUqpoMXSY8LrXYt mYjgEifXSZ2oAB1nNANi32ayTx8Dm1QeTRL0ZOcZuvpb5+m5Swi1rFNxbJuNsSBC0ph17mpzJDD g9CHNWbkfiaKZBbck94BmuWFwaoLmLeiT2pIkLaphTzRG3ZZmNAh03WsZtWwdzFYmW7bPzEDRdE lGgW2yJ8kDf8PhsILCEbwiOA83CoAmeC0je1H+XbsvqAvUCBU+kiuspOTbo2CM1yMv+sQaNLcI/ hJ5ZHgI0SIvwJyUgKpkQawTeyRjPjSF0YsfdIYLgTmh+y1/ew5f8KeCmvE X-Google-Smtp-Source: AGHT+IG4tMffng+qIKk+7urxmnDQ+VfyYaTFCm6M+ieiRI56T2TLr0lRPRbcNO3WNOuF0TLA32S50Q== X-Received: by 2002:a05:6214:43c1:b0:807:8020:1055 with SMTP id 6a1803df08f44-8847c525e0cmr35914156d6.37.1763737163321; Fri, 21 Nov 2025 06:59:23 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8846e54c9d0sm39584546d6.26.2025.11.21.06.59.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:23 -0800 (PST) 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 v6 6/7] auxdisplay: TM16xx: Add support for I2C-based controllers Date: Fri, 21 Nov 2025 09:59:06 -0500 Message-ID: <20251121145911.176033-7-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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 | 333 ++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index 21ba2a99b581..17b3f101a0c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25920,6 +25920,7 @@ S: Maintained 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 afd8ce05c668..104a43b5baf4 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -547,6 +547,22 @@ config TM16XX_KEYPAD help Enable optional 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..29031cea4d07 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_i2c.c @@ -0,0 +1,333 @@ +// 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" + +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 FD655_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 Tue Dec 2 01:28:18 2025 Received: from mail-qt1-f182.google.com (mail-qt1-f182.google.com [209.85.160.182]) (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 04C2B347BBB for ; Fri, 21 Nov 2025 14:59:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737170; cv=none; b=UNnwU00F/XFMW8gLnme4yhoEbvyPyQQYwO6yI2PjD/hmefi2vsGnwwNjm03Co5MnuIY4xY0G4gqFgtuH0OFpdE5ASbK+6W2NFM1sbYFMEh746Qa4H3HNZv0/dZBV0bWtx0qWmRbzKLMeX/MpfDfvR3bAU4yTMWgUl28/KiD3Xek= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763737170; c=relaxed/simple; bh=w6gUg8zbITnA9iOwESIFWOP2yC/0Mg16aDL2afBAhgg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=esKQuMTVlgv8zx1mcCS4yOikXDW33YWGQeCHVRtVM2/+3JTPZFgTBcsq1oDuWsvygbPovW0a3MUeNZ58cDB3Xf8a0VeHPGchGAMsze4IOitDFBj9kfVMqjGi8JFk7I/kSR5UVBPDgu8Kl7ZW4K0TQBNfshc6wK6qeDJq4GJTXBA= 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=Wf7PeytL; arc=none smtp.client-ip=209.85.160.182 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="Wf7PeytL" Received: by mail-qt1-f182.google.com with SMTP id d75a77b69052e-4ee14ba3d9cso19833111cf.1 for ; Fri, 21 Nov 2025 06:59:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763737166; x=1764341966; 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=V6nXK5M+s4bk+xgTC+paCir4tH47tvqUs9CsNE6p2bo=; b=Wf7PeytL1uiaBndZssXOV+/1l8tAniFfsge/5b156CVHLyXfNvhlyLeLYP5yJBiFZV 6cc3fIe5trx4XipjMEri2YqmqTFGsBktIg3RBcFi8SrgCIHmfqYBke0+gOJqfyNXGczv eDDQgJFKSnHJe6gzSsb/CyTvDebjRpEru7+LvHZx9e3CsR4gpeDTTh17zxDEVVSxdx4W SIeHLnMnyBu3E2PY6Qfvt7Lf9jQmb1du28BGysj5HEqCZgYGJ9u0i6jWSLc9hYxL5M9f UMKfWStD8Ft1vPcLOBLINK8W20XJdNRGRroxMwWzRp1HQ5JXUuVDHjXKl2He8b5ygZRJ cNlQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763737166; x=1764341966; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=V6nXK5M+s4bk+xgTC+paCir4tH47tvqUs9CsNE6p2bo=; b=N+Xe975C6FQudE96NW7SgczxU4X93o4TZEsK3vIV8GyjB/FnSk3lmjbtDy1zEi8nRA Sx5iAzX1nqM+Wkk0h98iTY6efd5Lalrsog1nUvnXzxPLG7Q+BTrz46zef07AjHnmDgs9 pCz8ah/al92Ew9HIutjmYzjpDipxfUCgHoiGZ9gfAnPFfau7F+EOBKQ1sat/TObMie+D 7NOhJfFrqW1L3rC/XKDvzVsMjpuOlM5qVqjfkXP+6HdeiIEURNOnDQ4kVS0r0tNTc1AQ 7bQMTnj7WXMuNV3TP3/t8s7kIC4X7ka9yLaxmVBnLKuSKij9LzgCYAfkpX8qV9sLMosD QtcQ== X-Gm-Message-State: AOJu0YwV/w5C2CFd+RtpbI9TXvwLSJg07PeNh+C4em+0gZTzwh7UT3z7 xiPXyeyj6xWL5sxg//1KHvX1i6ivbZoPOWARDH3oD+HuuneISix5FDkZMTfkhWMF X-Gm-Gg: ASbGncs9Icx4RwtnbfsU+CCOmJqSjIRSOmx/v793+6O6cBEXsK2kvz3k+T3a2aC/yTh BU018KJvu/Ez+OL2Jb/kvoWXc6/3dtz49FiqMCdbRckpzg9q0VGBjgZgutwBaYvxrr7FkHYxuLN 5G4faHZUXdRPxR+dfZcDNTRUkXdDkKNibW73azm8KkaPEChm472lzzcNJFGkoeJ6XVvkrn3goQ9 7vu03ZRNjcYYb0M9TecYflkwbcDKKXub7Tr8M+o79wndsKgSWFKeXyjj/mJUl4aywY2AvOJm8Vm F97uUB3bN8WuDdpt26e3P/WTHImNBq4Bd4A7bW5vavdqn5bVVDYdGz4OxYb71toKZK44jykmz0M G6BEn8LPjQrWJ+XILOYCUGqOWGDL1PYQtH01pQKaRN4PC6MUgGNKZ8xTCkB+rDaLaAWlHGCQima bIDSCmTFuK2v5tKdcX40+hkjjQ+EnXXNJMa8Fn+db7di4VF5PXLCN92m6n X-Google-Smtp-Source: AGHT+IEBLzV/ukPeqUG34S+rJgTyaHubDtffkthvOR+ZeWjqJAV4raQ6UCkgtoXzo43gR37TWavkzw== X-Received: by 2002:a05:622a:190f:b0:4ee:fe8:9348 with SMTP id d75a77b69052e-4ee5890e8d6mr35091391cf.72.1763737165698; Fri, 21 Nov 2025 06:59:25 -0800 (PST) Received: from localhost (modemcable197.17-162-184.mc.videotron.ca. [184.162.17.197]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-4ee48b45ccesm36619521cf.0.2025.11.21.06.59.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 21 Nov 2025 06:59:25 -0800 (PST) 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 v6 7/7] auxdisplay: TM16xx: Add support for SPI-based controllers Date: Fri, 21 Nov 2025 09:59:07 -0500 Message-ID: <20251121145911.176033-8-jefflessard3@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251121145911.176033-1-jefflessard3@gmail.com> References: <20251121145911.176033-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 | 398 ++++++++++++++++++++++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 drivers/auxdisplay/tm16xx_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index 17b3f101a0c6..b32ccca2b7f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25922,6 +25922,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 104a43b5baf4..c7409df72d85 100644 --- a/drivers/auxdisplay/Kconfig +++ b/drivers/auxdisplay/Kconfig @@ -563,6 +563,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..9bd3882ca051 --- /dev/null +++ b/drivers/auxdisplay/tm16xx_spi.c @@ -0,0 +1,398 @@ +// 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" + +#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 *codes =3D display->spi_buffer; + u8 *cmd =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 *codes =3D display->spi_buffer; + u8 *cmd =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 *codes =3D display->spi_buffer; + u8 *cmd =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 *codes =3D display->spi_buffer; + u8 *cmd =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