From nobody Sat Jul 25 06:00:10 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A14F93DDAEE for ; Fri, 17 Jul 2026 07:29:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273359; cv=none; b=sYUyMpynyyvWtT4b035Z7ta6ePM1KR6NZgEU45Z1nNc/JORfd60rG9t6DbFCcyWrAJIUaZ+/2T3nyB7SpmCGRoY0tm+eU/CZ8iaHMbnKMHHODBrdLy5kcbakwioziKI20jVFYtzj2Pf2ivNLOqjxbA2bLA5GQIEw1cCPab38jzQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273359; c=relaxed/simple; bh=6OpzcuW/vXrBd8EEvVAzR0F/OfZ6uSTpUvNzXmgWi9w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=eaMuQ5VxFYXvYiVvtoPZHdakt3ghyNOu+SEjm+NXpoloAFNb34mWH97k3VW1Y9dK2OmksynqE+JY+o4zCRXrR1XEJOalKCUsx5awpuwXlMZJmlcGv566Lwp8oMIwhhonoO34qyihYbCFJu+/rZzZpDIX7AyKYtnL0rWVjeReYB0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=fYBxCXtY; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=gpFnaHDW; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="fYBxCXtY"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="gpFnaHDW" Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66H6cTKo851698 for ; Fri, 17 Jul 2026 07:29:07 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 68gUZ7LqB0jU7pBbWZu2QhSNvnpL1hcdM6zSdwGD5Nw=; b=fYBxCXtYZkeNo41o uyRZpBnHJ5TxP2jE/HAnBX9qM1DId9iHDawfJvmwAgOE4fog+2uXQaQLYTjU4e/R LOauO3m9jfmsO3LgRcgew3DWHKKM9vU8mciy9EtuWWNkj+JkXQmZ4/yfcvHGGvuk vgZ7kEq9+IOQ+fntuzl7Tk+l7PKB4Mqd/nyQY0NQHwRvohLC3tSkQRAwQGQ3uAOl w/a9ODbuH3zRfUl57NfKgLNd5iM3BoWHw/z3Q1x2SP/X7GkgUDTIvNVdjUnjMEDQ /P3wq1NPGctotM+9qbfhJ0BQZAfLZebKkbO4S7vRcdJPBPHbgC0Kw35qXHdRqn38 ezh8Zw== Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fff9p86t9-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 17 Jul 2026 07:29:06 +0000 (GMT) Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2ccb687f82eso111432695ad.3 for ; Fri, 17 Jul 2026 00:29:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784273345; x=1784878145; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=68gUZ7LqB0jU7pBbWZu2QhSNvnpL1hcdM6zSdwGD5Nw=; b=gpFnaHDW0fludPs3VD5Dq13X1KzIY+xLfdG6k42tJyVYryqCn9C8wx2NL9mipWcVeH Zath6vdi2pBLq9C/Fz6gACKKWI/3ReQV2lyDNScn7KxZgLZiV4ATGBbXCVYRIwEeeaY5 8t4SeG4ih8NlG/ytw1KGQ2LHDrtq3kReCi5DqHb+wl79l/gVADJhJ+pd+PxZGaaNwtBS pQvVkyLI1MA3W7NCBDxQgdEQ+kr4M4HFwn1XwxJuwNwCmtBePrmWiM22m9jiNLzYPusI Xe3j8ySwh6NebdcozLfDf9gjiNWGMyd55EEYI8SQNt6qpdaaAjLBin90svjarHKw468h Ym+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784273345; x=1784878145; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=68gUZ7LqB0jU7pBbWZu2QhSNvnpL1hcdM6zSdwGD5Nw=; b=dnemPcuBq6ogEbcKzpwMAngKB9vFNBOofok97diz39UlNib5Gaopcc9hOOw/o5bWKz LkrdjLaxZX4Bg5eOoeQ1mlbB2v2lwfxQJoqwY9oPihb10U19zq3/b/7tniiiEGgobLU/ 8YgcM2FcPJ/yKIUGYIKUZSNXZhrbfRehlAVMGxeNDG143Qo6GklpO2Oo8qltNpwgOQHD q+cLFnpOYnxiz8MF9bPl9198Jy4vIimbcbUfoRgiLZnf8gM/8F1SGvcc7G7/XnZnSdn0 ZFfW9YxYR2QThvw1my/uS4cdo9/rsvxl5knZbf/ROcpWjFW9VWboupJB7NNTMIQV9Bnt 9ETw== X-Forwarded-Encrypted: i=1; AHgh+Ro1nRi3/OSkpwSWTSV//QeUYPDVJHv7iPFXMcwf8rfyMVcWWdRCSoCeC58JFNuK4CVlmukytAL/RA1DZNM=@vger.kernel.org X-Gm-Message-State: AOJu0Yxkvli4fQyULFPndVuFUG0agrzn4e7lK54J1dqk5ViGJXH/gl2x vTdtdMjfde91jlfnrzKiXk7ybI10XYJz4Rw0AhrPqhHQ5DFzz7yVF4e0kOvDPSZUuO8fkyousnI HXl7Fu9qqAdGZlbd241fX6i1XU45uCm3A1zxP2j2f/YFtSvmo/s0QviQ1YbOjFQ5Prck= X-Gm-Gg: AfdE7cn9tcSirKfMZ2hVvLoIM38D7JncdSdQCcfNKqGNMiXpL+yvKWHVceLy4j+rbwd x32tYn/2wcHrJETZjAvUeTKf91/LK6QtrDJq5SCcvZqjm0fyKGLZtVWqkLcFRHIzoy9hq394YXM JDOsRpOO7elJBcMFlslJHG+EvGxpPkYGZr1bAoFTBFzFTz+UPwxo9wUyDQOMy+5UBt+CchIeLY9 0G0Lf5rYxLEcnFSNTFw/jdHSXgqA6sEoMdu4pBJJPPbTYKeLTxO8aIk4fCD9wksXZD6tvqmf7uy M8OgvRXH+gfIZ97Ca3JHxeBb31PCXKiksOU7VBZ7xotLZZlENEvxNThod14NBiSmf2lY3UozjMe v32HMLwDvUxLntwZckBQb6drx2rJmQTzh167whiZex01plktz6HCO X-Received: by 2002:a17:903:40cc:b0:2ca:e5c:7fba with SMTP id d9443c01a7336-2cf349f210fmr16115895ad.39.1784273345390; Fri, 17 Jul 2026 00:29:05 -0700 (PDT) X-Received: by 2002:a17:903:40cc:b0:2ca:e5c:7fba with SMTP id d9443c01a7336-2cf349f210fmr16115575ad.39.1784273344902; Fri, 17 Jul 2026 00:29:04 -0700 (PDT) Received: from hu-fenglinw-lv.qualcomm.com (Global_NAT1.qualcomm.com. [129.46.96.20]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2cf346da071sm5724065ad.47.2026.07.17.00.29.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jul 2026 00:29:04 -0700 (PDT) From: Fenglin Wu Date: Fri, 17 Jul 2026 00:28:59 -0700 Subject: [PATCH v4 1/3] dt-bindings: input: Add Qualcomm SPMI PMIC haptics Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260717-qcom-spmi-haptics-v4-1-b0fe0ed30849@oss.qualcomm.com> References: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> In-Reply-To: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> To: linux-arm-msm@vger.kernel.org, Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Lee Jones , Stephen Boyd , Bjorn Andersson , Konrad Dybcio Cc: David Collins , Subbaraman Narayanamurthy , Kamal Wadhwa , kernel@oss.qualcomm.com, linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Fenglin Wu , Krzysztof Kozlowski X-Mailer: b4 0.16-dev-17187 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784273341; l=6327; i=fenglin.wu@oss.qualcomm.com; s=20260324; h=from:subject:message-id; bh=6OpzcuW/vXrBd8EEvVAzR0F/OfZ6uSTpUvNzXmgWi9w=; b=IJOaTBrSz2TSiz/oZ08xrnlS4x34XLA3wVRdhBIROx29351X1ibEmuT4KXyiABnMl8kxVS4Ls FNUTqP4X90EDl4gEdD7BlQrKcCv0ZxqbACv2yYCN+0S1yPCsKaHzPzr X-Developer-Key: i=fenglin.wu@oss.qualcomm.com; a=ed25519; pk=hJdt3E7o54lql+miD2GaxwF74cDyhgNwMbmFOZ46bRU= X-Proofpoint-GUID: KyE9ZHyBFAaBZJfgcbxoIYEdM8YP-EUc X-Proofpoint-ORIG-GUID: KyE9ZHyBFAaBZJfgcbxoIYEdM8YP-EUc X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfX/xDUE3u35jsc kH5MCsRk1GcJ8SceGOER7049zzB0t+qLC3jDGn0tZBjKK9nRiSEKYehN1biPpW3WNzEpaBCHPVK k9W4Ce9wuigRlEWbiQQJjF78BxuRiR01+sx6jmPVHPGjMs+XEOy+wOPAYUp/cCbwqWAAj/c1l8a z5WeEPhBdFI8lWBg8ZqLS9iCdoe8VzXLHY2oCvsQSDHLWdbKG3zxVyuE3e7NkJuAk5lKW5hUv9v 3eeXFXEI4Kb0TjdXSWDWsNR9ECPx691uh0Xphi/Sej9FqcxQ0fF5CmgyhaWCArpSUU0F/rW8bUL JgbDCx3bp93zFDfzl5VVUQgmodjz2vumiJXEgdItHhM5hW1zDRix404ZzWuVvNBZC9fuM6W2T1K UA3fr/rWSYBufEOFJ6fkvrGmZayc5hyqAMfKgpw0IoScQL/OlTslQ+5/dtwpHf5TRrh9p6b6iUT Pkgfwmq1EnzxaA1SFBQ== X-Authority-Analysis: v=2.4 cv=TaqmcxQh c=1 sm=1 tr=0 ts=6a59d9c2 cx=c_pps a=cmESyDAEBpBGqyK7t0alAg==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=yx91gb_oNiZeI1HMLzn7:22 a=gEfo2CItAAAA:8 a=EUspDBNiAAAA:8 a=bI6yuv9Qsz7HNVLwNVgA:9 a=QEXdDO2ut3YA:10 a=1OuFwYUASf3TG4hYMiVC:22 a=sptkURWiP4Gy88Gu7hUp:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfX4XLF2kYJC3uj lGm7fSVxsKr1umSrX0mMv0hUJljZQXUadZOxkhG9R85C3S9i67pVroVKxaSV/gVa4IDtjy4mC3V qvS7Ej7tEgfoC704CT1u3Kz6PVuAgVI= X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-17_02,2026-07-15_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 impostorscore=0 suspectscore=0 lowpriorityscore=0 phishscore=0 adultscore=0 spamscore=0 bulkscore=0 priorityscore=1501 malwarescore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607170073 Add binding document for the haptics module inside Qualcomm PMIC PMIH0108. Assisted-by: Claude:claude-4-8-opus Reviewed-by: Krzysztof Kozlowski Signed-off-by: Fenglin Wu --- .../bindings/input/qcom,spmi-haptics.yaml | 136 +++++++++++++++++= ++++ 1 file changed, 136 insertions(+) diff --git a/Documentation/devicetree/bindings/input/qcom,spmi-haptics.yaml= b/Documentation/devicetree/bindings/input/qcom,spmi-haptics.yaml new file mode 100644 index 000000000000..b1a69e9886ee --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,spmi-haptics.yaml @@ -0,0 +1,136 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/qcom,spmi-haptics.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Haptics device inside Qualcomm Technologies, Inc. PMIC + +maintainers: + - Fenglin Wu + +description: | + Certain Qualcomm PMICs integrate a haptics module, such as the HAP530_HV= haptics + module in the PMIH0108 PMIC, which drives an LRA (Linear Resonant Actuat= or) with + an output voltage up to 10 V. Several play modes are supported in HAP530= _HV: + + DIRECT_PLAY: The hardware outputs sinusoidal waveforms whose period is + defined by qcom,lra-period-us and whose peak voltage is defined by + qcom,vmax-microvolt. The driving amplitude can be scaled in the range + [0, 255] via a single register byte. Hardware-based LRA auto-resona= nce + tracking is enabled by default in this mode, allowing the haptics en= gine + to follow the actual resonant frequency of the LRA and update the dr= iving + period accordingly to achieve stronger vibration magnitude. + + FIFO: The hardware can play an arbitrary waveform composed of a sequen= ce + of 8-bit samples at a configurable play rate. Samples are pre-filled + into the internal FIFO memory of the haptics module and continuously + replenished via the FIFO-empty IRQ until all samples have been playe= d. + An 8K-byte FIFO memory bank is available in the HAP530_HV haptics mo= dule, + shared between the FIFO and PAT_MEM play modes. The memory partition + between the two modes is configurable via registers, and FIFO mode a= lways + uses the 1st partition starting from offset 0. + + PAT_MEM: This mode is very similar to FIFO streaming mode but without = the + data refilling capability. It is designed mainly for short, latency-= critical + vibrations. The memory space for PAT_MEM mode must be reserved for d= edicated + usage, and the waveform data should be preloaded and remain unchanged + thereafter. The haptics module can play the waveform data from the m= emory + region specified by the PAT_MEM play start address and length regist= ers. + + In either FIFO mode or PAT_MEM mode, the following play rates are supp= orted: + -- 0(T_LRA): each FIFO byte drives one full sinusoidal cycle with the + period defined in qcom,lra-period-us. + -- 1/2/3(T_LRA_DIV_2/4/8): each FIFO byte drives a half/quarter/eigh= th + sinusoidal cycle with the period defined in qcom,lra-period-us. + -- 4/5/6(T_LRA_X_2/4/8): each FIFO byte drives 2/4/8 sinusoidal cycl= es + with the period defined in qcom,lra-period-us. + -- 8/9/10/11/12/13(8KHz/16KHz/24KHz/32KHz/44.1KHz/48KHz): the FIFO + data is treated as PCM samples and drives the output with an + arbitrarily shaped waveform. This mode is typically used to define + custom driving waveforms for specific vibration effects such as fa= st + attack, crisp brake, etc. + + The drive voltage in FIFO or PAT_MEM mode can exceed the value defined= in + qcom,vmax-microvolt to achieve a special vibration effect, but the wav= eform + must be short enough to prevent the LRA from being damaged by operatin= g at + an overvoltage. + + Also, hardware-based LRA auto-resonance tracking is normally disabled = in + FIFO or PAT_MEM mode, as these modes are intended to drive arbitrary + waveforms that may not follow the resonant frequency; autonomous hardw= are + resonance correction would interfere with the intended output. + +properties: + compatible: + items: + - const: qcom,pmih0108-haptics + - const: qcom,spmi-haptics + + reg: + items: + - description: HAP_CFG module base address + - description: HAP_PTN module base address + + reg-names: + items: + - const: cfg + - const: ptn + + interrupts: + maxItems: 1 + + interrupt-names: + items: + - const: fifo-empty + + qcom,vmax-microvolt: + description: + Maximum allowed output driving voltage in microvolts, must be a mult= iple + of 50,000 uV. This is the peak driving voltage in DIRECT_PLAY mode, + which outputs sinusoidal waveforms. The value should be equal to the + square root of 2 times the Vrms voltage of the LRA. + minimum: 50000 + maximum: 10000000 + multipleOf: 50000 + + qcom,lra-period-us: + description: + LRA actuator initial resonance period in microseconds + (1,000,000 / resonant_freq_hz). Used to configure T_LRA-based play + rates and the auto-resonance zero-crossing window. It could be also = used + as the initial period if the LRA wants to be driven off resonance. + minimum: 5 + maximum: 20475 + multipleOf: 5 + +required: + - compatible + - reg + - reg-names + - interrupts + - interrupt-names + - qcom,vmax-microvolt + - qcom,lra-period-us + +additionalProperties: false + +examples: + - | + #include + + pmic { + #address-cells =3D <1>; + #size-cells =3D <0>; + + haptics@f000 { + compatible =3D "qcom,pmih0108-haptics", "qcom,spmi-haptics"; + reg =3D <0xf000>, <0xf100>; + reg-names =3D "cfg", "ptn"; + interrupts =3D <0x7 0xf0 0x1 IRQ_TYPE_EDGE_RISING>; + interrupt-names =3D "fifo-empty"; + + qcom,vmax-microvolt =3D <1300000>; + qcom,lra-period-us =3D <5880>; + }; + }; --=20 2.43.0 From nobody Sat Jul 25 06:00:10 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A32E63DDAF4 for ; Fri, 17 Jul 2026 07:29:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273356; cv=none; b=ON/i5CNKYEraWNfmiXk0baOik+Bi2sR9isD2vO0YOnEJTDE8BvyF4uWpZGxqdllBYVPFLdUD+N2hm0GiybWbOs4Nuj3PzNf4QNykyBQEJ7Wb1GmZl77gsGX8rwaWhIs+KGdBji7jl7MFI6vTFcgLJrEAf1cuGq61+F1s9jrSz34= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273356; c=relaxed/simple; bh=Ys197YC3XOD9CnVoetaGWg4eqH96ao9lELwA5QMf5CA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OEIaLFCNmp87a7Iz0juIqQv8Tl9wqkJCPNq0twxNRfdFqvopBUMCjUyJsQB6Fp21wIm+rTo01Z5+wCMzOLyVVvQbFIIzDqIvdOeTBVHNGluLHP3Ewl9mBgcD6ia4Vyf8WooVnrz6arlBkOgkkyK9OI/SxayH77aQLcluY85YZFo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=BrQurFQ/; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=KBuptXT6; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="BrQurFQ/"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="KBuptXT6" Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66H6cRg2979782 for ; Fri, 17 Jul 2026 07:29:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= jPg7wDJPn0dLENWw36JR9/8Qv11GyQ72iD0SDfmmmZ0=; b=BrQurFQ/gy+V65UN 97cpB6n77PboqFX79tDf2BNfB5U64o442NoHkiZbI0g6uiQnPGeP13RE32WM7lQJ nJICXmp2YPLxQJZa7/XbMeQ0+uM+yFYdhrJeZdfooL+n8mn5v7n3CCTlDy+zDLh/ /P4FukMMeLZvD4YW+QrKBLfBKCQfZPzlMeFR3SZl3qgmsksG4dTJC7pqISzX+pp5 z9l9UwwO8r/vIsV3eu5Cm/4FKJ4+zEPUAMeWcRpgZcPB9J4c/IuAAo4loqwJ8vbQ qD4erNjJH8xARh6wGOj5jIyryTneBlZdm45wIQD0oGJCKzbOGvcvypHH7GEuCLnw rvI8hQ== Received: from mail-pj1-f71.google.com (mail-pj1-f71.google.com [209.85.216.71]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4fff9gr6dt-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 17 Jul 2026 07:29:07 +0000 (GMT) Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-3811279d51aso7688214a91.1 for ; Fri, 17 Jul 2026 00:29:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784273347; x=1784878147; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jPg7wDJPn0dLENWw36JR9/8Qv11GyQ72iD0SDfmmmZ0=; b=KBuptXT6EygyIqVs3Z8Ql3qPLJYsvfEpHcMsBiPHDfESTW1WtkV9Ea6U1huaMVfoFg ZyZVeStbyY55msxNW/Ti4KerUS0eXP0ha9x8G6s2hQTcrQz+hd1RRiVYB53iVRFIvFpP n40ziscs/EUd1MHuQHI7MUJcQAmqQ2HVeg1d3/KpoZ297ks+Gxc8pcAJBoEpx8mqQcDv 8K0D3uiJicDSb1FnStkNLG3w7JZgYMSxPDtBwTFi09otXy67ntxZt+qKiFhroq+bUIOV 5C9comW5zTojoV8ymkMBgDkwPqHzj5Z2Tf2sFujZ+0sWvVKV3uN+lCb+D6hxgldRXCld 59uA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784273347; x=1784878147; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=jPg7wDJPn0dLENWw36JR9/8Qv11GyQ72iD0SDfmmmZ0=; b=UJ18GcgVe7oy1Q+X7B3TWBnQKTO+B6STGgZ94Hi0JrhXbFqM4pZBrL9WVc+Nh6453K Hd9zxIRSdymQv/OGwKtwJxHeolOwCFQqfhuAuq7n7lPjiiJPKEROE+vkKjAwymyM1oVK 7cjaMz5nNE+V8HLCFhJ8P4AoXhJcsKOb8rg87U+yzSkQLJfnrROs+otTsoufXC29ylQB 8ZD8XKSIkcdksEFY1s4ZDcFLhYsJCtdRv1wUXirPEiWrTdHRCPpLrs9efIblD85/Bbc9 XXbWOHzXTf1lC1d5pI6n5S/cEH6SYM2N9Yw1PxjgtxADy06JfM1hZUmfEPa5QXKz+xHf trbQ== X-Forwarded-Encrypted: i=1; AHgh+Rooo56UXU2fYtWUgXdXVoYsxEC7JHfzILOwSHIu0Redi9yXneH2vm7EQrBxD7wN3js+xasqlR8szKTy38s=@vger.kernel.org X-Gm-Message-State: AOJu0YyNyFPnDA/G5n5kpyqjNqOYRY7wEkjO6pFvAvm5xg8S1Wz6rCaS A3kg0i8Byu5/bUgJshkhO7H5VbcVM817/5Qp1tXFMocTqVbiopWKso/n9MeUqmsO4Sb4u67l/fg UV3tCT8N4B/0hFJPTdtqtobcP3at39mMEb2MT0FusmQCBOOcJ5SFiM+bsJYqxsJEqwd1hYhKq68 0= X-Gm-Gg: AfdE7cl5EYHxqCftdV006qBhKEzGdpwi07mkWvCnAtppIW67tsKecPia3X3WqRRdObj YUkxrlY7RGAiD9E/ZCBhGIoO6ZuR8zfQ68EUEoL5jvDyMkgszsIpIbdSGJGnELwsjTeaFYXY2ft AfRi2XY3k+sRaOk9EFyg+cYFFRdzvT57CQm30Oi0bXf6jvEDxRXNY+FJcC0I1V/Kpw2L6sq9Xe4 16aF0sghvWtOOFFZF/rpBfqtxgbO8uYg/EY8yFbBKcxdXQX1DbhhnG5UELNtGkOmXB0uyvpZ3mw gzF/ZHzwpdsPl677WosI4NhrgmyFKmAD2rDUiHF4Q3ksNAvKHnF627F91htATpoRYi/zE6QkYI6 smpycwsZuPP46YaMwkcd2aEJ+IVu8dGS4IvmHm/OyTtovTm9EJwS1 X-Received: by 2002:a17:90b:582f:b0:387:e0db:3fb1 with SMTP id 98e67ed59e1d1-38e4b5f9bbbmr1434700a91.42.1784273346857; Fri, 17 Jul 2026 00:29:06 -0700 (PDT) X-Received: by 2002:a17:90b:582f:b0:387:e0db:3fb1 with SMTP id 98e67ed59e1d1-38e4b5f9bbbmr1434679a91.42.1784273346457; Fri, 17 Jul 2026 00:29:06 -0700 (PDT) Received: from hu-fenglinw-lv.qualcomm.com (Global_NAT1.qualcomm.com. [129.46.96.20]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2cf346da071sm5724065ad.47.2026.07.17.00.29.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jul 2026 00:29:06 -0700 (PDT) From: Fenglin Wu Date: Fri, 17 Jul 2026 00:29:00 -0700 Subject: [PATCH v4 2/3] dt-bindings: mfd: qcom,spmi-pmic: Document haptics device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260717-qcom-spmi-haptics-v4-2-b0fe0ed30849@oss.qualcomm.com> References: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> In-Reply-To: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> To: linux-arm-msm@vger.kernel.org, Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Lee Jones , Stephen Boyd , Bjorn Andersson , Konrad Dybcio Cc: David Collins , Subbaraman Narayanamurthy , Kamal Wadhwa , kernel@oss.qualcomm.com, linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Fenglin Wu , Krzysztof Kozlowski X-Mailer: b4 0.16-dev-17187 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784273341; l=964; i=fenglin.wu@oss.qualcomm.com; s=20260324; h=from:subject:message-id; bh=Ys197YC3XOD9CnVoetaGWg4eqH96ao9lELwA5QMf5CA=; b=cXBU3K268G67pLiiWZQEM99iACL5l9Dw3dfn/rTlzktErJv1I8FMLcmb/LJlyILzIKL5PN0h3 8GbwJnxAEemCYpJeNcHrFrXQFn5pf1CHT/Dy5qjpddVr8j8tEz5MWXz X-Developer-Key: i=fenglin.wu@oss.qualcomm.com; a=ed25519; pk=hJdt3E7o54lql+miD2GaxwF74cDyhgNwMbmFOZ46bRU= X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfX+yB+Kj7NbkHI WyWrzhGq8SWBytBixvlmcmusCG43n+Zg5N1jFw8XuHxKWAU3fZjgvLGykSr6yopoXnWNht3H/p/ WZ6AR2WYdG8rPtPGmqw+8nwzgh2fwig= X-Proofpoint-ORIG-GUID: TYJzeXt6E7Cr714ikzrD9HsKc2H1rgOr X-Proofpoint-GUID: TYJzeXt6E7Cr714ikzrD9HsKc2H1rgOr X-Authority-Analysis: v=2.4 cv=TaumcxQh c=1 sm=1 tr=0 ts=6a59d9c3 cx=c_pps a=UNFcQwm+pnOIJct1K4W+Mw==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=ZpdpYltYx_vBUK5n70dp:22 a=EUspDBNiAAAA:8 a=GfP_bOASi7LDpkAm3wIA:9 a=QEXdDO2ut3YA:10 a=uKXjsCUrEbL0IQVhDsJ9:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfX7RoglmbXHynB 5M1itRl9QSq1Ut53lZ7Zuk1psfVKCXydiULdNDUqbTUfAubs5fdI0YM0RfGrkpnA9T3CcVvMb4y pY9rmWVOQ6Qkcn/12j51+uDRUnk0eWPXX/lW105/kNcJrt4Y3txeLmJtHVwH+wbUjdEMU0FgLrD jZpwc1wcWZ/Zf/gf2QECo7nXvyo+5YSATuPLNyCukAlMjpYmQ05yRfOKRT0uktXzLMq1VTabI5n 6GWiqdPFcLBqA76bzUbfPS4yw1ejCqskODQ2gIUsLwvgApHt/c9l1xmVQgl2+1VtPRokc7gmHZG uv7/zPqWAEpW7+XPGXLsft/yY7hYAnlVLk97lxGCAxQWeH/xacklWywY4rG7njL4MbAsKARf8wL nl3K0mih3LaXFAvCg1RfyM29ekgueUIZ5QF6uBerFBQN3RB3c7oW6fnzT3bFQkh06Me3qLV3hhg TZwRl6hOSvn0+Ro0qxQ== X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-17_02,2026-07-15_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 impostorscore=0 clxscore=1015 adultscore=0 phishscore=0 spamscore=0 bulkscore=0 malwarescore=0 suspectscore=0 lowpriorityscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607170073 Some of the Qualcomm SPMI PMIC has haptics device in it, add it in the device list. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Fenglin Wu --- Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Do= cumentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml index 644c42b5e2e5..773f4cba5935 100644 --- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml @@ -165,6 +165,10 @@ patternProperties: type: object $ref: /schemas/pinctrl/qcom,pmic-gpio.yaml# =20 + "^haptics@[0-9a-f]+$": + type: object + $ref: /schemas/input/qcom,spmi-haptics.yaml# + "^led-controller@[0-9a-f]+$": type: object $ref: /schemas/leds/qcom,spmi-flash-led.yaml# --=20 2.43.0 From nobody Sat Jul 25 06:00:10 2026 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 57F7337E5D1 for ; Fri, 17 Jul 2026 07:29:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273369; cv=none; b=TsaMSQ3w5FxEeE3ZX+Okttu0MHi/rGSSL92a6zieYSMB4h0Ua9Enw6HyS5uMVvQVj2SJWPa3uSPifbTta4ZXJxxkHF1rHoLE9zkMG1TwV2OK88nUT6oj1um9oRyv9/DKa2b4NU0n+J+OrC4M6RQQcS3RG3t2U2h/QXwguQb6NSo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784273369; c=relaxed/simple; bh=HgUJMgRoo/HtVNAfGT20jeysXO4QsjvvwnYuW4HdiWg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WiUEHoX10JfrT3utXmo+hextytReXc9AAqanfmIyIqFB+D4kuyYQ1dph+fUQMpOPxSX3+9pLlFFCOXfO5+4vxgopFaO5CTiQWKtOvR7Xnh0d3YTeQxenCsnydiar6uNgxbq7UCtsXBU9CUaI1ccbkKnj/+tJ0/8nrfppLqCwz68= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=NdLgeKhR; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Aof3jOd0; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="NdLgeKhR"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Aof3jOd0" Received: from pps.filterd (m0279869.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 66H6cklC1093246 for ; Fri, 17 Jul 2026 07:29:10 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= TUGSup2wBwHeU+vN2oks5cTSTSmbudcv++TTWawyxHg=; b=NdLgeKhRwjkHZOHZ oZR/8mvfGI+8WJ1+bQydx/Fp5q8uui9BVpLTiHA9Q6pGXM/+uzHHSbTbHTpzvSL6 pSpA3ZwHCzWTmsgevIu/WSQBV16geQn9b6XglPZ9gVTbMyTeOJfzARvlm4f71plN 1fU+iZ5iM6VPQmdaQ7mgdv3g6GrWrI5cZPVU9p+Yepa+LvRoC07qUtZK+G+VtUYd i9xCa1xnrmm0v0lCoZxykQl3nnDzkNYWeRVfmM4jv3Tb40TdgNdIHkXurdlzR0NE Gf5BxuEqDTUpnZGDv9PmvdHmq+JQvYPk9qcCIz1zJet/qrjlKHlZ9wdRNnapIE3T ZAQ51g== Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4ffe0urgk9-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 17 Jul 2026 07:29:10 +0000 (GMT) Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2cca5e0a0c9so145015715ad.1 for ; Fri, 17 Jul 2026 00:29:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784273349; x=1784878149; darn=vger.kernel.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TUGSup2wBwHeU+vN2oks5cTSTSmbudcv++TTWawyxHg=; b=Aof3jOd0WiQeqRK1qwMzOK79Zngswzl4Svrpcz5ASnMqaEq6X8B7duWDYZaYxUAC5I IWoZihe8JgJM92VnWBlXSmQKyuVZYEDDZ7GmFxrVYxYGNM3WW1yN8hojS9FGMm9rNZkx P2pvqBxYJ5xFtpv3tc8k46+Iua1ZCrueyxayir8Ckgae8YBPIFWmzhnk/bmz7kcblE7E TLCMdHyog3+splePFbtO4ULz8W+0qwqZdxiayOrYyeNoGZ1p+QTw7DkVGrn0skAyx8Su MzMOEC+1lXZp2v7c1fFZdOf8LrCQHaET7LyjyIT7kRRZ1IpBQJ8Wmo22OdmSo2pdcjoG F4ew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784273349; x=1784878149; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :content-type:mime-version:subject:date:from:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=TUGSup2wBwHeU+vN2oks5cTSTSmbudcv++TTWawyxHg=; b=P7l38354ioOMmsLyk2jQqYk7Uy9SBEb5laz11DFz+cZENPkPjLhvlcv+eHr0kpQ4RJ PXdz7D231qaNonpTIZKClVDxZVR0CDpVivgt0BhmvRwK9VDH8Y5bq4quyu7/mgIlbk54 if3LjpB/l1TYFvMDSN5ibXBlYBbiQ5zgd6yJA+4tGR6Rt7ms1KeY3RhSd+lnzOo9JA8N e6k5uDUjRHowFdjJK+vhKNK6ojliCzoG1sQuLf1n3YaRCo2snOCUTuqSOfjsDPVKcBNx RZ8Qho4xUpvO9JSvypxQPiDR4JjDK3OtIxYzT9VSrDHMh6YS2dEWzEqPZdzTy069NZZ/ shnw== X-Forwarded-Encrypted: i=1; AHgh+RoPiECw/xBL9HPkQv76VX9MhnTc5TOkYfNZFsmiX2zVuer1lh9qkN9phhank+fyAK0oWTSn1VtpZHl0zb0=@vger.kernel.org X-Gm-Message-State: AOJu0YyIO8UHVljP/G5VEAzb5R1jjqMV6GnayNmniCAxLkTVSI+MFjWw 7VJ2OE04O+7cY0Ymss00TGAOeukTrEkJ3Gp+ECdOEwvmkBfaneKkKtw2WtWhuevgoZEzHpVjSft h48ghYWFgfVE7HGB1HGU+Gwrnys3hTeq8yEb5QjaPn8IwwhdYcL4yD4ILOI96TKysnPE= X-Gm-Gg: AfdE7cl10fKOniMpAPq55sTGt8E5gW0+u2qS2EkfKXnuoqa2ubYH5RPfnyJFaO3ak85 G/y2MXuGNgtMJoCpF4E/U7vS/rrEXiyAhWAGVzIe+4RaT+msR516F9gSc3oP3jnLPERF4rDYhT7 VHeMjOXEbtXAKn6dm3SQiqXEGgwYaJx76LuCEPCBr2IrGNw9tMZEUu04lggON3oTiOiRY/AeMJ7 ga1HKJ2/tGE65yyvus/mZlV4Jetx+7OsY5WvzmTEnw4Bm+v8UxzerlYyUoNf3pwEwjVFzS9GyNm NN2m6Ou1kYuOLYbxxE21gxwLuh2rGA6ckZs48jCKHzXIrxntiTj5TrHbbVJzGpCAUy8OZgEuvCx 4sGMq0/R6Bx7y5rlvXIPrqu1jS02+C8a83CaYK/sMbcTIaxQYD7at X-Received: by 2002:a17:902:d2d2:b0:2ca:d7a0:1312 with SMTP id d9443c01a7336-2cf34961d82mr16161595ad.25.1784273348845; Fri, 17 Jul 2026 00:29:08 -0700 (PDT) X-Received: by 2002:a17:902:d2d2:b0:2ca:d7a0:1312 with SMTP id d9443c01a7336-2cf34961d82mr16161165ad.25.1784273348034; Fri, 17 Jul 2026 00:29:08 -0700 (PDT) Received: from hu-fenglinw-lv.qualcomm.com (Global_NAT1.qualcomm.com. [129.46.96.20]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2cf346da071sm5724065ad.47.2026.07.17.00.29.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 17 Jul 2026 00:29:07 -0700 (PDT) From: Fenglin Wu Date: Fri, 17 Jul 2026 00:29:01 -0700 Subject: [PATCH v4 3/3] input: misc: Add Qualcomm SPMI PMIC haptics driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260717-qcom-spmi-haptics-v4-3-b0fe0ed30849@oss.qualcomm.com> References: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> In-Reply-To: <20260717-qcom-spmi-haptics-v4-0-b0fe0ed30849@oss.qualcomm.com> To: linux-arm-msm@vger.kernel.org, Dmitry Torokhov , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Lee Jones , Stephen Boyd , Bjorn Andersson , Konrad Dybcio Cc: David Collins , Subbaraman Narayanamurthy , Kamal Wadhwa , kernel@oss.qualcomm.com, linux-input@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Fenglin Wu X-Mailer: b4 0.16-dev-17187 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784273341; l=35807; i=fenglin.wu@oss.qualcomm.com; s=20260324; h=from:subject:message-id; bh=HgUJMgRoo/HtVNAfGT20jeysXO4QsjvvwnYuW4HdiWg=; b=6tH2EAM3sHtU4mGNGovL8BurH3Nq906k72/jLDO4cpdanG0Q6gBGDcQRBhwMwzJtCjRQ5RpHN KmLD/Ou4qqpA2Bd2xsP4pYnIu/8iz9FmzvC6ui4kiXZV2n8rd6Y/ED2 X-Developer-Key: i=fenglin.wu@oss.qualcomm.com; a=ed25519; pk=hJdt3E7o54lql+miD2GaxwF74cDyhgNwMbmFOZ46bRU= X-Authority-Analysis: v=2.4 cv=deWwG3Xe c=1 sm=1 tr=0 ts=6a59d9c6 cx=c_pps a=MTSHoo12Qbhz2p7MsH1ifg==:117 a=ouPCqIW2jiPt+lZRy3xVPw==:17 a=IkcTkHD0fZMA:10 a=RAioF0-LDSMA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=_glEPmIy2e8OvE2BGh3C:22 a=EUspDBNiAAAA:8 a=HzfoYw7G_s99FY6qeE0A:9 a=wVMyrRT0KRx5ck5g:21 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 a=GvdueXVYPmCkWapjIL-Q:22 X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfXwd8B0mZYa3ki U8TJWgI/ZWArxl+zMAda56Ubbnwnjuyv0Q2HZzgyWKohVeglUHnztjBDxYGhwHy6XtO9GnY4+bL WBFSuivWhVsVxenpFYjKQKilYT7QOL94fE9NuTsf5iGW9dUboxA6uLyGzIHYo2z/fr58++HX+GH 56QkcOjaEu8CcxZnHvqPoaNY5Vllavft7aoIBGed1NYSaNmHKAYs6XRKCvO1GPe1M93e4N6vUht FofrZX65NOz3nuIGTaqDbmBifAX38P/EJSZ0xU8if6hr0THTmAqPs25oDfFec9MVloyA40Nam8p Jw/Veh3uoivyc2DTozMP7ZovygH57q9gJS6+IwntXDb7GkowXXETr+aa1w8YKMAm8L4D3aHy9jd 1ZvbOyAY8usV1M51L7PqBC4HsP8xg1XTJUr+yBb4m0EBiBz/6HC8hjZ+NMj+BuvGiiWvA2ln3sM V9hUjkOAo27gNzBkyAw== X-Proofpoint-ORIG-GUID: w3gOUA24RISnjXroIz8TKtSMscb7iJYu X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE3MDA3MyBTYWx0ZWRfXwrZhOXm+c23I a7DR80wfYOKEQEDOiumNT9kPMpLyVQlx9iqryQq1jn2Q8YbRqr4/e7OdS8yN+VTQEaZxTupUKmH hqGF4o4FYDOO+Q/Vd6BdrClpNQBfPm4= X-Proofpoint-GUID: w3gOUA24RISnjXroIz8TKtSMscb7iJYu X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-07-17_02,2026-07-15_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 spamscore=0 bulkscore=0 priorityscore=1501 malwarescore=0 lowpriorityscore=0 adultscore=0 phishscore=0 impostorscore=0 suspectscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607170073 Add an initial driver for the Qualcomm PMIH0108 PMIC haptics module, named as HAP530_HV. This module supports several play modes, including DIRECT_PLAY, FIFO, PAT_MEM, and SWR, each with distinct data sourcing and hardware data handling logic. Currently, the driver provides support for two play modes using the input force-feedback framework: FF_CONSTANT effect for DIRECT_PLAY mode and FF_PERIODIC effect with FF_CUSTOM waveform for FIFO mode. Assisted-by: Claude:claude-4-8-opus Signed-off-by: Fenglin Wu --- drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/qcom-spmi-haptics.c | 1178 ++++++++++++++++++++++++++++= ++++ 3 files changed, 1190 insertions(+) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1f6c57dba030..4f40940973e4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -236,6 +236,17 @@ config INPUT_PMIC8XXX_PWRKEY To compile this driver as a module, choose M here: the module will be called pmic8xxx-pwrkey. =20 +config INPUT_QCOM_SPMI_HAPTICS + tristate "Qualcomm SPMI PMIC haptics support" + depends on MFD_SPMI_PMIC + help + Say Y to enable support for the Qualcomm PMIH0108 SPMI PMIC haptics + module. Supports DIRECT_PLAY, FIFO streaming play modes via the + Linux input force-feedback framework. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-haptics. + config INPUT_SPARCSPKR tristate "SPARC Speaker support" depends on PCI && SPARC64 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 2281d6803fce..c5c9aa139a11 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) +=3D pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) +=3D powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) +=3D pwm-beeper.o obj-$(CONFIG_INPUT_PWM_VIBRA) +=3D pwm-vibra.o +obj-$(CONFIG_INPUT_QCOM_SPMI_HAPTICS) +=3D qcom-spmi-haptics.o obj-$(CONFIG_INPUT_QNAP_MCU) +=3D qnap-mcu-input.o obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) +=3D rave-sp-pwrbutton.o obj-$(CONFIG_INPUT_RB532_BUTTON) +=3D rb532_button.o diff --git a/drivers/input/misc/qcom-spmi-haptics.c b/drivers/input/misc/qc= om-spmi-haptics.c new file mode 100644 index 000000000000..235f77e0229c --- /dev/null +++ b/drivers/input/misc/qcom-spmi-haptics.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* HAP_CFG register offsets, bit fields, value constants */ +#define HAP_CFG_INT_RT_STS_REG 0x10 +#define FIFO_EMPTY_BIT BIT(1) +#define HAP_CFG_EN_CTL_REG 0x46 +#define HAPTICS_EN_BIT BIT(7) +#define HAP_CFG_VMAX_REG 0x48 +#define VMAX_STEP_MV 50 +#define VMAX_MV_MAX 10000 +#define HAP_CFG_SPMI_PLAY_REG 0x4C +#define PLAY_EN_BIT BIT(7) +#define PAT_SRC_MASK GENMASK(2, 0) +#define PAT_SRC_FIFO 0 +#define PAT_SRC_DIRECT_PLAY 1 +#define HAP_CFG_TLRA_OL_HIGH_REG 0x5C +#define TLRA_OL_MSB_MASK GENMASK(3, 0) +#define TLRA_STEP_US 5 +#define TLRA_US_MAX 20475 +#define HAP_CFG_TLRA_OL_LOW_REG 0x5D +#define HAP_CFG_DRV_DUTY_CFG_REG 0x60 +#define ADT_DRV_DUTY_EN_BIT BIT(7) +#define ADT_BRK_DUTY_EN_BIT BIT(6) +#define DRV_DUTY_MASK GENMASK(5, 3) +#define AUTORES_DRV_DUTY_62P5 2 +#define BRK_DUTY_MASK GENMASK(2, 0) +#define AUTORES_BRK_DUTY_62P5 5 +#define HAP_CFG_ZX_WIND_CFG_REG 0x62 +#define ZX_DEBOUNCE_MASK GENMASK(6, 4) +#define AUTORES_ZX_DEBOUNCE 3 +#define ZX_WIN_HEIGHT_MASK GENMASK(2, 0) +#define AUTORES_ZX_WIN_HEIGHT 2 +#define HAP_CFG_AUTORES_CFG_REG 0x63 +#define AUTORES_EN_BIT BIT(7) +#define AUTORES_EN_DLY_MASK GENMASK(6, 2) +#define AUTORES_EN_DLY_CYCLES 10 +#define AUTORES_ERR_WIN_MASK GENMASK(1, 0) +#define AUTORES_ERR_WIN_25PCT 1 +#define HAP_CFG_FAULT_CLR_REG 0x66 +#define ZX_TO_FAULT_CLR_BIT BIT(4) +#define SC_CLR_BIT BIT(2) +#define AUTO_RES_ERR_CLR_BIT BIT(1) +#define HPWR_RDY_FAULT_CLR_BIT BIT(0) +#define FAULT_CLR_ALL (ZX_TO_FAULT_CLR_BIT | SC_CLR_BIT | \ + AUTO_RES_ERR_CLR_BIT | HPWR_RDY_FAULT_CLR_BIT) +#define HAP_CFG_RAMP_DN_CFG2_REG 0x86 +#define AUTORES_PRE_HIZ_DLY_10US 1 + +/* HAP_PTN register offsets, bit fields, value constants */ +#define HAP_PTN_REVISION2_REG 0x01 +#define HAP_PTN_FIFO_DIN_0_REG 0x20 +#define HAP_PTN_FIFO_PLAY_RATE_REG 0x24 +#define FIFO_PLAY_RATE_MASK GENMASK(3, 0) +#define HAP_PTN_DIRECT_PLAY_REG 0x26 +#define HAP_PTN_FIFO_EMPTY_CFG_REG 0x2A +#define FIFO_THRESH_LSB 64 +#define HAP_PTN_FIFO_DIN_1B_REG 0x2C +#define HAP_PTN_MEM_OP_ACCESS_REG 0x2D +#define MEM_FLUSH_RELOAD_BIT BIT(0) +#define HAP_PTN_MMAP_FIFO_REG 0xA0 +#define MMAP_FIFO_EXIST_BIT BIT(7) +#define MMAP_FIFO_LEN_MASK GENMASK(6, 0) +#define HAP_PTN_PATX_PLAY_CFG_REG 0xA2 + +#define HAP530_MEM_TOTAL_BYTES 8192 +#define FIFO_EMPTY_THRESH 280 +#define FIFO_INIT_FILL 320 + +#define HAPTICS_AUTOSUSPEND_MS 1000 + +/* + * FF_CUSTOM data layout (custom_data[] of type s16): + * [0] =3D play rate (PLAY_RATE_*) + * [1] =3D vmax in mV (0 =3D use device default from qcom,vmax-microvolt) + * [2..N-1] =3D signed 8-bit PCM samples packed one per s16 (lower byte = used) + */ +#define CUSTOM_DATA_RATE_IDX 0 +#define CUSTOM_DATA_VMAX_IDX 1 +#define CUSTOM_DATA_SAMPLE_START 2 +#define CUSTOM_DATA_MAX_LEN (48 * 1024) + +#define HAPTICS_MAX_EFFECTS 8 + +enum qcom_haptics_mode { + HAPTICS_MODE_NONE, + HAPTICS_DIRECT_PLAY, + HAPTICS_FIFO, +}; + +enum qcom_haptics_play_rate { + PLAY_RATE_T_LRA =3D 0, + PLAY_RATE_T_LRA_DIV_2 =3D 1, + PLAY_RATE_T_LRA_DIV_4 =3D 2, + PLAY_RATE_T_LRA_DIV_8 =3D 3, + PLAY_RATE_T_LRA_X_2 =3D 4, + PLAY_RATE_T_LRA_X_4 =3D 5, + PLAY_RATE_T_LRA_X_8 =3D 6, + PLAY_RATE_RESERVED =3D 7, + PLAY_RATE_F_8KHZ =3D 8, + PLAY_RATE_F_16KHZ =3D 9, + PLAY_RATE_F_24KHZ =3D 10, + PLAY_RATE_F_32KHZ =3D 11, + PLAY_RATE_F_44P1KHZ =3D 12, + PLAY_RATE_F_48KHZ =3D 13, + PLAY_RATE_MAX =3D PLAY_RATE_F_48KHZ, +}; + +/** + * struct qcom_haptics_effect: A haptics effect + * @mode: haptics HW play mode + * @vmax_mv: peak voltage of the haptics output waveform + * @length_us: vibration play duration + * @amplitude: DIRECT_PLAY mode output waveform amplitude + * @play_rate: FIFO mode play rate + * @fifo_data: 8-bit data samples consumed in FIFO mode + * @data_len: length of the FIFO data samples + */ +struct qcom_haptics_effect { + enum qcom_haptics_mode mode; + u32 vmax_mv; + u32 length_us; + + u8 amplitude; + + enum qcom_haptics_play_rate play_rate; + s8 *fifo_data; + u32 data_len; +}; + +/** + * struct haptics_play_req: A haptics play request + * @node: list node of the request + * @effect_id: effect index of the request + * @play: flag of starting or stopping the play + */ +struct haptics_play_req { + struct list_head node; + int effect_id; + bool play; +}; + +/** + * struct qcom_haptics + * @dev: underlying SPMI device + * @regmap: regmap for SPMI register access + * @input: input device exposing the FF interface + * @cfg_base: base address of the CFG peripheral + * @ptn_base: base address of the PTN peripheral + * @t_lra_us: LRA resonance period in microseconds + * @vmax_mv: maximum actuator drive voltage in millivolts + * @fifo_len: programmed HW FIFO depth in bytes + * @gain: playback gain scaler + * @play_work: delayed work that plays the queued requests + * @play_lock: mutex lock to serialize playbacks + * @play_queue_lock: spinlock protecting @play_queue + * @play_queue: The list of pending start/stop requests + * @active_effect_id: index into @effects[] currently under play + * @active_mode: mode of the effect currently armed in hardware + * @fifo_empty_irq: IRQ number for the FIFO-empty interrupt + * @pm_ref_held: true while a pm_runtime_get is held + * @irq_enabled: true if fifo_empty_irq is enabled + * @fifo_lock: mutex protecting the FIFO streaming data + * @fifo_data: pointer of the data buffer for FIFO streaming + * @data_len: length of the data buffer for current effect + * @data_written: number of samples written to the hardware FIFO + * @effects: table of the effects + */ +struct qcom_haptics { + struct device *dev; + struct regmap *regmap; + struct input_dev *input; + + u32 cfg_base; + u32 ptn_base; + u32 t_lra_us; + u32 vmax_mv; + u32 fifo_len; + atomic_t gain; + + struct delayed_work play_work; + struct mutex play_lock; /* mutex used to serialize playbacks */ + + spinlock_t play_queue_lock; /* protects play_queue */ + struct list_head play_queue; + + int active_effect_id; + enum qcom_haptics_mode active_mode; + + int fifo_empty_irq; + bool pm_ref_held; + bool irq_enabled; + + struct mutex fifo_lock; /* protect the FIFO data during play */ + const s8 *fifo_data; + u32 data_len; + u32 data_written; + + struct qcom_haptics_effect effects[HAPTICS_MAX_EFFECTS]; +}; + +static int cfg_write(struct qcom_haptics *h, u32 off, u32 val) +{ + return regmap_write(h->regmap, h->cfg_base + off, val); +} + +static int cfg_update_bits(struct qcom_haptics *h, u32 off, u32 mask, u32 = val) +{ + return regmap_update_bits(h->regmap, h->cfg_base + off, mask, val); +} + +static int ptn_write(struct qcom_haptics *h, u32 off, u32 val) +{ + return regmap_write(h->regmap, h->ptn_base + off, val); +} + +static int ptn_update_bits(struct qcom_haptics *h, u32 off, u32 mask, u32 = val) +{ + return regmap_update_bits(h->regmap, h->ptn_base + off, mask, val); +} + +static int ptn_bulk_write(struct qcom_haptics *h, u32 off, + const void *buf, size_t count) +{ + return regmap_bulk_write(h->regmap, h->ptn_base + off, buf, count); +} + +static int haptics_clear_faults(struct qcom_haptics *h) +{ + return cfg_write(h, HAP_CFG_FAULT_CLR_REG, FAULT_CLR_ALL); +} + +static int haptics_set_vmax(struct qcom_haptics *h, u32 vmax_mv) +{ + return cfg_write(h, HAP_CFG_VMAX_REG, vmax_mv / VMAX_STEP_MV); +} + +static int haptics_config_lra_period(struct qcom_haptics *h) +{ + u32 tmp =3D h->t_lra_us / TLRA_STEP_US; + int ret; + + ret =3D cfg_write(h, HAP_CFG_TLRA_OL_HIGH_REG, (tmp >> 8) & TLRA_OL_MSB_M= ASK); + if (ret) + return ret; + + return cfg_write(h, HAP_CFG_TLRA_OL_LOW_REG, tmp & 0xFF); +} + +static int haptics_enable_module(struct qcom_haptics *h, bool enable) +{ + return cfg_update_bits(h, HAP_CFG_EN_CTL_REG, HAPTICS_EN_BIT, + enable ? HAPTICS_EN_BIT : 0); +} + +static int haptics_configure_autores(struct qcom_haptics *h) +{ + int ret; + + /* AUTORES_CFG: enable, 10-cycle delay, 25% error window */ + ret =3D cfg_write(h, HAP_CFG_AUTORES_CFG_REG, + AUTORES_EN_BIT | + FIELD_PREP(AUTORES_EN_DLY_MASK, AUTORES_EN_DLY_CYCLES) | + FIELD_PREP(AUTORES_ERR_WIN_MASK, AUTORES_ERR_WIN_25PCT)); + if (ret) + return ret; + + /* DRV_DUTY: adaptive drive/brake duty cycles at 62.5% */ + ret =3D cfg_write(h, HAP_CFG_DRV_DUTY_CFG_REG, + ADT_DRV_DUTY_EN_BIT | ADT_BRK_DUTY_EN_BIT | + FIELD_PREP(DRV_DUTY_MASK, AUTORES_DRV_DUTY_62P5) | + FIELD_PREP(BRK_DUTY_MASK, AUTORES_BRK_DUTY_62P5)); + if (ret) + return ret; + + /* Pre-HIZ delay: 10 =C2=B5s */ + ret =3D cfg_write(h, HAP_CFG_RAMP_DN_CFG2_REG, AUTORES_PRE_HIZ_DLY_10US); + if (ret) + return ret; + + /* Zero-cross window: debounce 3, no hysteresis, height 2 */ + return cfg_write(h, HAP_CFG_ZX_WIND_CFG_REG, + FIELD_PREP(ZX_DEBOUNCE_MASK, AUTORES_ZX_DEBOUNCE) | + FIELD_PREP(ZX_WIN_HEIGHT_MASK, AUTORES_ZX_WIN_HEIGHT)); +} + +static int haptics_write_fifo_chunk(struct qcom_haptics *h, + const s8 *data, u32 len) +{ + u32 bulk_len =3D ALIGN_DOWN(len, 4); + int i, ret; + + /* + * FIFO data writing supports both 4-byte bulk writes using registers + * [HAP_PTN_FIFO_DIN_0_REG ... HAP_PTN_FIFO_DIN_3_REG], and 1-byte writes + * using the HAP_PTN_FIFO_DIN_1B_REG register. The 4-byte bulk write is m= ore + * efficient, so use 4-byte writes for the initial 4-byte aligned data, + * and 1-byte writes for any trailing remainder. + */ + for (i =3D 0; i < bulk_len; i +=3D 4) { + ret =3D ptn_bulk_write(h, HAP_PTN_FIFO_DIN_0_REG, &data[i], 4); + if (ret) + return ret; + } + + for (; i < len; i++) { + ret =3D ptn_write(h, HAP_PTN_FIFO_DIN_1B_REG, (u8)data[i]); + if (ret) + return ret; + } + + return 0; +} + +static int haptics_configure_fifo_mmap(struct qcom_haptics *h) +{ + u32 fifo_len, fifo_units; + + /* Config all memory space for FIFO usage for now */ + fifo_len =3D HAP530_MEM_TOTAL_BYTES; + fifo_len =3D ALIGN_DOWN(fifo_len, 64); + fifo_units =3D fifo_len / 64; + h->fifo_len =3D fifo_len; + + return ptn_write(h, HAP_PTN_MMAP_FIFO_REG, + MMAP_FIFO_EXIST_BIT | + FIELD_PREP(MMAP_FIFO_LEN_MASK, fifo_units - 1)); +} + +static u32 haptics_gain_scaled_vmax(struct qcom_haptics *h, u32 vmax_mv) +{ + u16 gain =3D atomic_read(&h->gain); + u32 v =3D mult_frac(vmax_mv, gain, 0xFFFF); + + return max_t(u32, v, VMAX_STEP_MV); +} + +static void haptics_fifo_irq_enable(struct qcom_haptics *h, bool enable) +{ + if (h->irq_enabled =3D=3D enable) + return; + + if (enable) + enable_irq(h->fifo_empty_irq); + else + disable_irq(h->fifo_empty_irq); + + h->irq_enabled =3D enable; +} + +static int haptics_enqueue_play_req(struct qcom_haptics *h, int effect_id,= bool play) +{ + struct haptics_play_req *req; + + guard(spinlock_irqsave)(&h->play_queue_lock); + + /* + * Coalesce with an already queued request for + * the same effect instead of appending. + */ + list_for_each_entry(req, &h->play_queue, node) { + if (req->effect_id =3D=3D effect_id) { + req->play =3D play; + return 0; + } + } + + req =3D kmalloc_obj(*req, GFP_ATOMIC); + if (!req) + return -ENOMEM; + + req->effect_id =3D effect_id; + req->play =3D play; + + list_add_tail(&req->node, &h->play_queue); + + return 0; +} + +static struct haptics_play_req *haptics_dequeue_play_req(struct qcom_hapti= cs *h) +{ + struct haptics_play_req *req =3D NULL; + + guard(spinlock_irqsave)(&h->play_queue_lock); + + if (!list_empty(&h->play_queue)) { + req =3D list_first_entry(&h->play_queue, struct haptics_play_req, node); + list_del(&req->node); + } + + return req; +} + +static bool haptics_queue_pending(struct qcom_haptics *h) +{ + guard(spinlock_irqsave)(&h->play_queue_lock); + + return !list_empty(&h->play_queue); +} + +static void haptics_queue_remove_effect(struct qcom_haptics *h, int effect= _id) +{ + struct haptics_play_req *req, *tmp; + + guard(spinlock_irqsave)(&h->play_queue_lock); + + list_for_each_entry_safe(req, tmp, &h->play_queue, node) { + if (req->effect_id =3D=3D effect_id) { + list_del(&req->node); + kfree(req); + } + } +} + +static void haptics_queue_flush(struct qcom_haptics *h) +{ + struct haptics_play_req *req, *tmp; + + guard(spinlock_irqsave)(&h->play_queue_lock); + + list_for_each_entry_safe(req, tmp, &h->play_queue, node) { + list_del(&req->node); + kfree(req); + } +} + +static int haptics_start_direct_play(struct qcom_haptics *h, int effect_id) +{ + struct qcom_haptics_effect *eff =3D &h->effects[effect_id]; + int ret; + + ret =3D haptics_clear_faults(h); + if (ret) + return ret; + + /* Enable auto-resonance for DIRECT_PLAY mode */ + ret =3D cfg_update_bits(h, HAP_CFG_AUTORES_CFG_REG, + AUTORES_EN_BIT, AUTORES_EN_BIT); + if (ret) + return ret; + + ret =3D haptics_set_vmax(h, haptics_gain_scaled_vmax(h, h->vmax_mv)); + if (ret) + return ret; + + ret =3D ptn_write(h, HAP_PTN_DIRECT_PLAY_REG, eff->amplitude); + if (ret) + return ret; + + return cfg_write(h, HAP_CFG_SPMI_PLAY_REG, + PLAY_EN_BIT | FIELD_PREP(PAT_SRC_MASK, PAT_SRC_DIRECT_PLAY)); +} + +static int haptics_start_fifo(struct qcom_haptics *h, int effect_id) +{ + struct qcom_haptics_effect *eff =3D &h->effects[effect_id]; + u32 vmax =3D eff->vmax_mv ? eff->vmax_mv : h->vmax_mv; + u32 init_len; + bool data_done; + int ret; + + if (!eff->fifo_data || !eff->data_len) + return -EINVAL; + + ret =3D haptics_clear_faults(h); + if (ret) + return ret; + + /* Disable auto-resonance for FIFO mode */ + ret =3D cfg_update_bits(h, HAP_CFG_AUTORES_CFG_REG, AUTORES_EN_BIT, 0); + if (ret) + return ret; + + ret =3D haptics_set_vmax(h, haptics_gain_scaled_vmax(h, vmax)); + if (ret) + return ret; + + ret =3D ptn_update_bits(h, HAP_PTN_FIFO_PLAY_RATE_REG, + FIFO_PLAY_RATE_MASK, + FIELD_PREP(FIFO_PLAY_RATE_MASK, eff->play_rate)); + if (ret) + return ret; + + /* Flush FIFO before loading new data */ + ret =3D ptn_write(h, HAP_PTN_MEM_OP_ACCESS_REG, MEM_FLUSH_RELOAD_BIT); + if (ret) + return ret; + ret =3D ptn_write(h, HAP_PTN_MEM_OP_ACCESS_REG, 0); + if (ret) + return ret; + + guard(mutex)(&h->fifo_lock); + + /* Write the initial chunk and initialise streaming state */ + init_len =3D min_t(u32, eff->data_len, FIFO_INIT_FILL); + ret =3D haptics_write_fifo_chunk(h, eff->fifo_data, init_len); + if (ret) + return ret; + + h->fifo_data =3D eff->fifo_data; + h->data_len =3D eff->data_len; + h->data_written =3D init_len; + + /* + * Set empty threshold. When threshold > 0 the hardware fires the + * FIFO-empty interrupt when occupancy drops below the threshold, + * allowing the driver to refill. + */ + data_done =3D (h->data_written >=3D h->data_len); + if (!data_done) { + ret =3D ptn_write(h, HAP_PTN_FIFO_EMPTY_CFG_REG, + FIFO_EMPTY_THRESH / FIFO_THRESH_LSB); + if (ret) { + dev_err(h->dev, "set FIFO empty threshold failed, ret=3D%d\n", ret); + h->fifo_data =3D NULL; + return ret; + } + + haptics_fifo_irq_enable(h, true); + } + + ret =3D cfg_write(h, HAP_CFG_SPMI_PLAY_REG, + PLAY_EN_BIT | FIELD_PREP(PAT_SRC_MASK, PAT_SRC_FIFO)); + if (ret) { + dev_err(h->dev, "trigger FIFO play failed, ret=3D%d\n", ret); + ptn_write(h, HAP_PTN_FIFO_EMPTY_CFG_REG, 0); + /* + * HW play never started since this SPMI write itself failed, + * and the FIFO empty IRQ has never fired, so disabling the + * IRQ synchronously here can't deadlock against 'fifo_lock'. + */ + haptics_fifo_irq_enable(h, false); + h->fifo_data =3D NULL; + return ret; + } + + return 0; +} + +/* + * haptics_fifo_empty_irq: Threaded IRQ handler for the FIFO-empty interru= pt. + * + * While a FIFO play is in progress the hardware fires this interrupt when + * the number of samples in the FIFO drops below the programmed threshold. + * The handler refills the FIFO from the effect's data buffer. When all + * samples have been written the threshold is set to zero. The HW would + * stop the play automatically after all of the samples in FIFO memory are + * played out. + */ +static irqreturn_t haptics_fifo_empty_irq(int irq, void *dev_id) +{ + struct qcom_haptics *h =3D dev_id; + u32 sts, to_write; + int ret; + + ret =3D regmap_read(h->regmap, + h->cfg_base + HAP_CFG_INT_RT_STS_REG, &sts); + if (ret || !(sts & FIFO_EMPTY_BIT)) + return IRQ_HANDLED; + + guard(mutex)(&h->fifo_lock); + + if (!h->fifo_data) + return IRQ_HANDLED; + + /* Refill: write the next chunk */ + to_write =3D min_t(u32, h->data_len - h->data_written, + h->fifo_len - FIFO_EMPTY_THRESH); + ret =3D haptics_write_fifo_chunk(h, &h->fifo_data[h->data_written], to_wr= ite); + if (ret) { + dev_err(h->dev, "refill FIFO samples failed, ret=3D%d\n", ret); + /* + * If data refilling is failed,stop the HW play and disable the + * IRQ to prevent the FIFO empty IRQ being fired continuously. + */ + cfg_write(h, HAP_CFG_SPMI_PLAY_REG, 0); + disable_irq_nosync(h->fifo_empty_irq); + h->irq_enabled =3D false; + return IRQ_HANDLED; + } + + h->data_written +=3D to_write; + + /* Disable the interrupt after all the data is queued */ + if (h->data_written >=3D h->data_len) + ptn_write(h, HAP_PTN_FIFO_EMPTY_CFG_REG, 0); + + return IRQ_HANDLED; +} + +/* + * haptics_stop_locked: stop play in HW and put runtime PM + * @h: haptics device + * @put_noidle: a flag to put noidle + * + * Stop HW play, clear the FIFO data if the active effect was FIFO-mode, a= nd + * put runtime PM into either noidle or autosuspend based on put_noidle fl= ag. + * + * Must be called with play_lock held. + */ +static void haptics_stop_locked(struct qcom_haptics *h, bool put_noidle) +{ + cfg_write(h, HAP_CFG_SPMI_PLAY_REG, 0); + + if (h->active_mode =3D=3D HAPTICS_FIFO) { + ptn_write(h, HAP_PTN_FIFO_EMPTY_CFG_REG, 0); + haptics_fifo_irq_enable(h, false); + scoped_guard(mutex, &h->fifo_lock) { + h->fifo_data =3D NULL; + } + } + + h->active_effect_id =3D -1; + h->active_mode =3D HAPTICS_MODE_NONE; + + if (h->pm_ref_held) { + if (put_noidle) + pm_runtime_put_noidle(h->dev); + else + pm_runtime_put_autosuspend(h->dev); + + h->pm_ref_held =3D false; + } +} + +/* + * haptics_start_locked: start to play an effect + * @h: haptics device + * @effect_id: the index of the effect + * + * If an effect is currently active, stop it 1st. Acquires a PM ref + * if not already held, and then trigger the play based on the + * play mode. Update active_effect_id/active_mode on success. + * + * Must be called with play_lock held. + */ +static int haptics_start_locked(struct qcom_haptics *h, int effect_id) +{ + int ret; + + if (h->active_effect_id !=3D -1) + haptics_stop_locked(h, false); + + if (!h->pm_ref_held) { + ret =3D pm_runtime_resume_and_get(h->dev); + if (ret < 0) { + dev_err(h->dev, "failed to resume device: %d\n", ret); + return ret; + } + + h->pm_ref_held =3D true; + } + + switch (h->effects[effect_id].mode) { + case HAPTICS_DIRECT_PLAY: + ret =3D haptics_start_direct_play(h, effect_id); + break; + case HAPTICS_FIFO: + ret =3D haptics_start_fifo(h, effect_id); + break; + default: + ret =3D -EINVAL; + } + + if (ret) { + dev_err(h->dev, "failed to start effect %d: %d\n", effect_id, ret); + if (h->pm_ref_held) { + pm_runtime_put_autosuspend(h->dev); + h->pm_ref_held =3D false; + } + + return ret; + } + + h->active_effect_id =3D effect_id; + h->active_mode =3D h->effects[effect_id].mode; + + return 0; +} + +static void haptics_play_work(struct work_struct *work) +{ + struct qcom_haptics *h =3D container_of(to_delayed_work(work), + struct qcom_haptics, play_work); + struct haptics_play_req *req =3D haptics_dequeue_play_req(h); + bool rearmed =3D false; + u32 length_us; + int ret; + + guard(mutex)(&h->play_lock); + + /* No valid request available */ + if (req =3D=3D NULL) + return; + + if (req->play) { + ret =3D haptics_start_locked(h, req->effect_id); + if (ret) { + dev_err(h->dev, "play haptics failed, ret=3D%d\n", ret); + } else { + /* + * Re-arm the work to stop the play or advance + * to next play after the play duration + */ + length_us =3D h->effects[req->effect_id].length_us; + if (length_us) { + schedule_delayed_work(&h->play_work, + usecs_to_jiffies(length_us)); + rearmed =3D true; + } + } + } else { + if (req->effect_id !=3D h->active_effect_id) + dev_warn(h->dev, "effect %d is not under playing\n", req->effect_id); + else + haptics_stop_locked(h, false); + } + + kfree(req); + + /* + * Handle the remaining requests if doesn't need to wait + */ + if (!rearmed && haptics_queue_pending(h)) + schedule_delayed_work(&h->play_work, 0); +} + +static void haptics_clear_effect(struct qcom_haptics *h, + struct qcom_haptics_effect *effect) +{ + guard(mutex)(&h->fifo_lock); + + if (h->fifo_data =3D=3D effect->fifo_data) + h->fifo_data =3D NULL; + + kvfree(effect->fifo_data); + effect->fifo_data =3D NULL; + effect->data_len =3D 0; +} + +/* + * haptics_fifo_length_us: Calculate the play duration of a FIFO effect. + * @h: haptics device pointer + * @rate: FIFO data play rate + * @data_len: FFO data length + * + * Each FIFO sample is played out over one play-rate period, so the total + * duration is the number of samples times that period: + * + * length_us =3D (clk_base * data_len * multiplier) / divider + * + * For T_LRA-based rates the period derives from the LRA resonance period + * (h->t_lra_us): DIV_2/4/8 shorten it, X_2/4/8 lengthen it. For the + * kHz-based rates the period is 1000 us / freq_khz. + */ +static u32 haptics_fifo_length_us(struct qcom_haptics *h, + enum qcom_haptics_play_rate rate, + u32 data_len) +{ + u32 clk_base =3D 1000, multiplier =3D 1, divider =3D 1; + + switch (rate) { + case PLAY_RATE_T_LRA: + clk_base =3D h->t_lra_us; + break; + case PLAY_RATE_T_LRA_DIV_2: + clk_base =3D h->t_lra_us; + divider =3D 2; + break; + case PLAY_RATE_T_LRA_DIV_4: + clk_base =3D h->t_lra_us; + divider =3D 4; + break; + case PLAY_RATE_T_LRA_DIV_8: + clk_base =3D h->t_lra_us; + divider =3D 8; + break; + case PLAY_RATE_T_LRA_X_2: + clk_base =3D h->t_lra_us; + multiplier =3D 2; + break; + case PLAY_RATE_T_LRA_X_4: + clk_base =3D h->t_lra_us; + multiplier =3D 4; + break; + case PLAY_RATE_T_LRA_X_8: + clk_base =3D h->t_lra_us; + multiplier =3D 8; + break; + case PLAY_RATE_F_8KHZ: + divider =3D 8; + break; + case PLAY_RATE_F_16KHZ: + divider =3D 16; + break; + case PLAY_RATE_F_24KHZ: + divider =3D 24; + break; + case PLAY_RATE_F_32KHZ: + divider =3D 32; + break; + case PLAY_RATE_F_44P1KHZ: + clk_base *=3D 10; + divider =3D 441; + break; + case PLAY_RATE_F_48KHZ: + divider =3D 48; + break; + default: + /* Unexpected rate: fall back to the resonance period. */ + clk_base =3D h->t_lra_us; + break; + } + + return div_u64((u64)clk_base * data_len * multiplier, divider); +} + +static int haptics_upload_effect(struct input_dev *dev, + struct ff_effect *effect, + struct ff_effect *old) +{ + struct qcom_haptics *h =3D input_get_drvdata(dev); + struct qcom_haptics_effect *priv; + int id =3D effect->id; + u32 data_len, level; + s8 *fifo; + + s16 *buf __free(kvfree) =3D NULL; + + guard(mutex)(&h->play_lock); + + if (id < 0 || id >=3D HAPTICS_MAX_EFFECTS) + return -EINVAL; + + if (id =3D=3D h->active_effect_id) { + dev_err(h->dev, "effect %d is under playing\n", id); + return -EBUSY; + } + + priv =3D &h->effects[id]; + + switch (effect->type) { + case FF_CONSTANT: + haptics_clear_effect(h, priv); + level =3D effect->u.constant.level <=3D 0 ? 0 : effect->u.constant.level; + priv->amplitude =3D (u8)mult_frac(level, 255, 0x7FFF); + priv->length_us =3D effect->replay.length * USEC_PER_MSEC; + priv->mode =3D HAPTICS_DIRECT_PLAY; + return 0; + + case FF_PERIODIC: + if (effect->u.periodic.waveform !=3D FF_CUSTOM) + return -EINVAL; + /* + * Minimum 3 elements: play-rate code + vmax + at least one sample. + * Limit the maximum data length to ~48K so that it can at least + * handle ~1s vibration at the fast (48K) play rate. + */ + if (effect->u.periodic.custom_len < 3 || + effect->u.periodic.custom_len > CUSTOM_DATA_MAX_LEN + 2) + return -EINVAL; + + buf =3D vmemdup_array_user(effect->u.periodic.custom_data, + effect->u.periodic.custom_len, + sizeof(s16)); + if (IS_ERR(buf)) + return PTR_ERR(no_free_ptr(buf)); + + if (buf[CUSTOM_DATA_RATE_IDX] < 0 || + buf[CUSTOM_DATA_RATE_IDX] > PLAY_RATE_MAX || + buf[CUSTOM_DATA_RATE_IDX] =3D=3D PLAY_RATE_RESERVED) + return -EINVAL; + + data_len =3D effect->u.periodic.custom_len - CUSTOM_DATA_SAMPLE_START; + + fifo =3D kvmalloc(data_len, GFP_KERNEL); + if (!fifo) + return -ENOMEM; + + /* Pack: one s8 sample per s16 slot (lower byte) */ + for (int i =3D 0; i < data_len; i++) + fifo[i] =3D (s8)buf[CUSTOM_DATA_SAMPLE_START + i]; + + haptics_clear_effect(h, priv); + + scoped_guard(mutex, &h->fifo_lock) { + priv->fifo_data =3D fifo; + priv->data_len =3D data_len; + } + + priv->play_rate =3D (u8)buf[CUSTOM_DATA_RATE_IDX]; + priv->vmax_mv =3D (u32)clamp_val(buf[CUSTOM_DATA_VMAX_IDX], 0, VMAX_MV= _MAX); + priv->length_us =3D haptics_fifo_length_us(h, priv->play_rate, data_len); + priv->mode =3D HAPTICS_FIFO; + + return 0; + + default: + return -EINVAL; + } +} + +static int haptics_playback(struct input_dev *dev, int effect_id, int val) +{ + struct qcom_haptics *h =3D input_get_drvdata(dev); + int ret; + + ret =3D haptics_enqueue_play_req(h, effect_id, val > 0); + if (ret) + return ret; + + if (val > 0) + /* + * Queue the play. If a duration re-arm is already pending this + * is a no-op, so the new play waits for the current effect to + * finish before the worker dequeues it. + */ + schedule_delayed_work(&h->play_work, 0); + else + /* + * Run the worker now, cancelling any pending duration re-arm, + * so an explicit stop takes effect immediately. + */ + mod_delayed_work(system_percpu_wq, &h->play_work, 0); + + return 0; +} + +static int haptics_erase(struct input_dev *dev, int effect_id) +{ + struct qcom_haptics *h =3D input_get_drvdata(dev); + struct qcom_haptics_effect *priv =3D &h->effects[effect_id]; + + guard(mutex)(&h->play_lock); + + haptics_queue_remove_effect(h, effect_id); + + if (h->active_effect_id =3D=3D effect_id) + haptics_stop_locked(h, false); + + haptics_clear_effect(h, priv); + priv->mode =3D HAPTICS_MODE_NONE; + + return 0; +} + +static void haptics_set_gain(struct input_dev *dev, u16 gain) +{ + struct qcom_haptics *h =3D input_get_drvdata(dev); + + atomic_set(&h->gain, gain); +} + +static int qcom_haptics_probe(struct platform_device *pdev) +{ + struct qcom_haptics *h; + struct input_dev *input; + struct ff_device *ff; + u32 regs[2], vmax_uv; + int ret, irq; + + h =3D devm_kzalloc(&pdev->dev, sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + + h->dev =3D &pdev->dev; + + h->regmap =3D dev_get_regmap(pdev->dev.parent, NULL); + if (!h->regmap) + return dev_err_probe(h->dev, -ENODEV, "no regmap from parent\n"); + + ret =3D device_property_read_u32_array(h->dev, "reg", regs, ARRAY_SIZE(re= gs)); + if (ret) + return dev_err_probe(h->dev, ret, "failed to read 'reg' property\n"); + + h->cfg_base =3D regs[0]; + h->ptn_base =3D regs[1]; + + ret =3D device_property_read_u32(h->dev, "qcom,lra-period-us", &h->t_lra_= us); + if (ret) + return dev_err_probe(h->dev, ret, "missing qcom,lra-period-us\n"); + + h->t_lra_us =3D clamp(h->t_lra_us, (u32)TLRA_STEP_US, (u32)TLRA_US_MAX); + + ret =3D device_property_read_u32(h->dev, "qcom,vmax-microvolt", &vmax_uv); + if (ret) + return dev_err_probe(h->dev, ret, "missing qcom,vmax-microvolt\n"); + + h->vmax_mv =3D clamp(vmax_uv / 1000, (u32)VMAX_STEP_MV, (u32)VMAX_MV_MAX); + + ret =3D haptics_config_lra_period(h); + if (ret) + return ret; + + ret =3D haptics_configure_autores(h); + if (ret) + return ret; + + ret =3D haptics_set_vmax(h, h->vmax_mv); + if (ret) + return ret; + + ret =3D haptics_configure_fifo_mmap(h); + if (ret) + return ret; + + mutex_init(&h->play_lock); + mutex_init(&h->fifo_lock); + spin_lock_init(&h->play_queue_lock); + INIT_LIST_HEAD(&h->play_queue); + INIT_DELAYED_WORK(&h->play_work, haptics_play_work); + atomic_set(&h->gain, 0xFFFF); + h->active_effect_id =3D -1; + + irq =3D platform_get_irq_byname(pdev, "fifo-empty"); + if (irq < 0) + return dev_err_probe(h->dev, irq, "failed to get fifo-empty IRQ\n"); + + ret =3D devm_request_threaded_irq(h->dev, irq, NULL, + haptics_fifo_empty_irq, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + "qcom-haptics-fifo-empty", h); + if (ret) + return dev_err_probe(h->dev, ret, "failed to request fifo-empty IRQ\n"); + + h->fifo_empty_irq =3D irq; + platform_set_drvdata(pdev, h); + + pm_runtime_use_autosuspend(h->dev); + pm_runtime_set_autosuspend_delay(h->dev, HAPTICS_AUTOSUSPEND_MS); + ret =3D devm_pm_runtime_enable(h->dev); + if (ret) + return dev_err_probe(h->dev, ret, "enable runtime PM failed\n"); + + input =3D devm_input_allocate_device(h->dev); + if (!input) + return -ENOMEM; + + input->name =3D "qcom-spmi-haptics"; + input_set_drvdata(input, h); + h->input =3D input; + + input_set_capability(input, EV_FF, FF_CONSTANT); + input_set_capability(input, EV_FF, FF_PERIODIC); + input_set_capability(input, EV_FF, FF_CUSTOM); + input_set_capability(input, EV_FF, FF_GAIN); + + ret =3D input_ff_create(input, HAPTICS_MAX_EFFECTS); + if (ret) + return ret; + + ff =3D input->ff; + ff->upload =3D haptics_upload_effect; + ff->playback =3D haptics_playback; + ff->erase =3D haptics_erase; + ff->set_gain =3D haptics_set_gain; + + ret =3D input_register_device(input); + if (ret) { + input_ff_destroy(input); + return dev_err_probe(h->dev, ret, "failed to register input device\n"); + } + + return 0; +} + +static void qcom_haptics_remove(struct platform_device *pdev) +{ + struct qcom_haptics *h =3D platform_get_drvdata(pdev); + + /* + * Unregister the input device explicitly at the beginning + * to avoid the input device being used after the resources + * are freed. + */ + input_unregister_device(h->input); + disable_delayed_work_sync(&h->play_work); + scoped_guard(mutex, &h->play_lock) { + haptics_queue_flush(h); + haptics_stop_locked(h, true); + } + + haptics_enable_module(h, false); + + scoped_guard(mutex, &h->fifo_lock) { + for (int i =3D 0; i < HAPTICS_MAX_EFFECTS; i++) { + kvfree(h->effects[i].fifo_data); + h->effects[i].fifo_data =3D NULL; + } + } +} + +static int qcom_haptics_runtime_suspend(struct device *dev) +{ + struct qcom_haptics *h =3D dev_get_drvdata(dev); + + return haptics_enable_module(h, false); +} + +static int qcom_haptics_runtime_resume(struct device *dev) +{ + struct qcom_haptics *h =3D dev_get_drvdata(dev); + + return haptics_enable_module(h, true); +} + +static int qcom_haptics_suspend(struct device *dev) +{ + struct qcom_haptics *h =3D dev_get_drvdata(dev); + int ret; + + disable_delayed_work_sync(&h->play_work); + scoped_guard(mutex, &h->play_lock) { + haptics_queue_flush(h); + haptics_stop_locked(h, true); + } + + ret =3D pm_runtime_force_suspend(dev); + if (ret) + enable_delayed_work(&h->play_work); + + return ret; +} + +static int qcom_haptics_resume(struct device *dev) +{ + struct qcom_haptics *h =3D dev_get_drvdata(dev); + + enable_delayed_work(&h->play_work); + + return pm_runtime_force_resume(dev); +} + +static const struct dev_pm_ops qcom_haptics_pm_ops =3D { + SYSTEM_SLEEP_PM_OPS(qcom_haptics_suspend, qcom_haptics_resume) + RUNTIME_PM_OPS(qcom_haptics_runtime_suspend, qcom_haptics_runtime_resume, + NULL) +}; + +static const struct of_device_id qcom_haptics_of_match[] =3D { + { .compatible =3D "qcom,spmi-haptics" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_haptics_of_match); + +static struct platform_driver qcom_haptics_driver =3D { + .probe =3D qcom_haptics_probe, + .remove =3D qcom_haptics_remove, + .driver =3D { + .name =3D "qcom-spmi-haptics", + .of_match_table =3D qcom_haptics_of_match, + .pm =3D pm_ptr(&qcom_haptics_pm_ops), + }, +}; +module_platform_driver(qcom_haptics_driver); + +MODULE_DESCRIPTION("Qualcomm SPMI PMIC Haptics driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0