From nobody Sat Jul 25 06:11:35 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 9FF4832A3FE for ; Fri, 17 Jul 2026 04:41:26 +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=1784263288; cv=none; b=CqUvLZkbYZ0xVuY81FoiLbK533wsFjPpv+DSt+oqgGlhs6RZLc1/zbE2AbFtL3VKX/SF3ClBPtfvDU3r3Do6iiS+PUEgu3tyCcFhsOyd++yDOpULSxO6cNVHVmYbqLVNzliu85WtDUgLbctWr2axw875kBBcKI+zg0d+GLv75NM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784263288; c=relaxed/simple; bh=qw3UlfHqx9g7QO6+Mmao8Q1/I71j8qvLvF1DF9oIM9w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=tupGEAIQY04XA9PE2pdPbhbOAGt+yNTFs+nqj4Yk0ml5jW9R4snmMGS1Hr0n0d/VsnA89l/dEEQ1nOV2IU5zYYkBtOdFbW+WumnKk7DsizdJq0mTeLKk7KKGFO6fW4l01rH3lSGHXENxQp7eWGkFydy4VHPQiKPoANlAtkZLPZk= 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=PcHlIJ5b; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=XzLqURzL; 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="PcHlIJ5b"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="XzLqURzL" 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 66H33rb2607324 for ; Fri, 17 Jul 2026 04:41:25 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:content-type:date:from:message-id :mime-version:subject:to; s=qcppdkim1; bh=FRBWIdccCY+PmppSXcBYV3 S4pjZTWpMiCSXziGIBsQs=; b=PcHlIJ5b8O3SVu5n3G0BaeJ+tz1w+fTeccmhQH wElHDzdp8d4FJucDnDWwkU8OX0CLMLB6UmqhijeGlJfu3SPcHB7yWEvhIqazn3Bk LrQNWg8Q4FHhkloM0M3/6jJmjqMHjO6r5p6gWBLqJ2exKTTrFm1xhs4kSJvKT/kB yL6sEuxzSQPyha0OfK49VQFtPba9fNDhQWsRg0q8cI2w8yx7E3+lEZb0DYSgUUhg PAdFSlHnXF5d4J40ZYc7Lvc5F6K8llHdYHuqC60FGzfNQWciVKylT86+wYG5l54R OnwLLkL53j4W+w9H8fPaWbAfkDAKyust6TWOTjriUdz0OROA== 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 4feu10cneb-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Fri, 17 Jul 2026 04:41:25 +0000 (GMT) Received: by mail-pj1-f71.google.com with SMTP id 98e67ed59e1d1-3812b0c6f23so3853837a91.1 for ; Thu, 16 Jul 2026 21:41:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1784263284; x=1784868084; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:content-type :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to:content-type; bh=FRBWIdccCY+PmppSXcBYV3S4pjZTWpMiCSXziGIBsQs=; b=XzLqURzLMjac2GvdVQdAqjjLGl9phXDqpVxiCbeReOMVwyGYedGFBMvMpgFq2CvVss ksB1Gagr6XHDOdLnaKWbskXfQVwUx3Jqq92lm0eFX7+DJjJC5rkwiy1cb+x2cyMJoFbU H0MERWGgEhEt9h/gZUWKgqo00xo4dJX21Y3sjAmNBsjg8bVBJxXPJ/jSP69CrIiE965H OFwR2AI2AoZCNZe3aO0GqQkCYLyn2gGsINdS2RMcoM1ocYCJsswgIAvV0uHQCbu6uFhU G7D0dqlpF+tuCFzOabgB72d/KKdJgzXYEG/kFG3P7ciChcqykw3gXKKI7ahSda/IKSh0 eZig== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784263284; x=1784868084; h=cc:to: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=FRBWIdccCY+PmppSXcBYV3S4pjZTWpMiCSXziGIBsQs=; b=K26Zh+Ts1VygbxKCVNzDTmi5jp5kFi2d4QgsUCJVqQkJ8E1ZK9vQXdrntV7FWn5GzU vxRYCB9p0pbQ9953yspBC7zF5nQQnfHQJS/PdPCJTEqrSX+0V6oOFCmW42F65XmsI/ML aavYsry4CYRJcyJyOuOk2OGpustmHvRMlMY6oO9uf2FzpPM/mDKGWxOsN5VauoOgevWD R0YOBSHySRnYEMu/lCnfpIXI6sSqndpOzKAHyLGGO/DP/pE35wxbfoAVaIWbxLf+Bfo1 u55Yuam3c/WVS1Mjdbc6Lo0zhxurBT78N5rNn/nRDL6OoRfy2d1li0qpq4VbNwFh0+1L Vj7Q== X-Forwarded-Encrypted: i=1; AHgh+RraagUTYs/8x6BVHmlwpOlWg/LZQAW5xIsEe8EvPJDU6VGELkdGwWgtb3Y4h912ysKJFBhqOW1IrTdNDls=@vger.kernel.org X-Gm-Message-State: AOJu0YyZclQcCNpq8+9rLw0hF0UbQ2sEBNoxbRtX57Hjjtw6nNdU5Q5u tKZ7qr65FePQPOefY/h5zXqH26NpX43NpUh1g55biVFGbHQew4b1qp40UTc+5l1oXR63WaEjNYx MB28HYY2lxMxLIoMBGBQVz3QqbDjOmI3C/4jeMuuwe+dhpR9YioDriWxR8f2DZUbSSAY= X-Gm-Gg: AfdE7cnVBJwSbzNkxDjPcj5BY/TGCxfliccsvVFi0BmhO0TzfzQa3HQz7XFwVqTAy+O JIM9IGRRNOgRlVFoTOKxKWfd4LIzCYhssDBgAD9LnFqFiWc8rk+g05HAuRbJ4mBl4y5kqgk51qE RDOtqQlDxeRW49g429BuHYYD11w5EXZoRMDP0G92u7GBD0+2ARpWojlgUYlx3pudutpVgoHRSGn 6I10ZsMFHpXMnGjVRhPHKvkmG+tpwlLfIlktpyukJzs5eej72wa5mQoaWxqwgPTe5D/bDBqGizp NNu53Jjs8CUfujgJgCg4BU7rx8nwWxxGH7flDOz+LVKAuRXb21mni/Z/5gi5+rXwgOlhlchjsaY BbSj+F/VFEqNK+AVQy1l+ClmqSun/KDRpRA1yqxwxkhN79WVwCB58 X-Received: by 2002:a17:90b:5344:b0:38e:2e86:ed02 with SMTP id 98e67ed59e1d1-38e4b423596mr959853a91.14.1784263284074; Thu, 16 Jul 2026 21:41:24 -0700 (PDT) X-Received: by 2002:a17:90b:5344:b0:38e:2e86:ed02 with SMTP id 98e67ed59e1d1-38e4b423596mr959824a91.14.1784263283507; Thu, 16 Jul 2026 21:41:23 -0700 (PDT) Received: from hu-fenglinw-lv.qualcomm.com (Global_NAT1.qualcomm.com. [129.46.96.20]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-3142a1ddeb6sm2451437eec.20.2026.07.16.21.41.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 16 Jul 2026 21:41:22 -0700 (PDT) From: Fenglin Wu Date: Thu, 16 Jul 2026 21:41:17 -0700 Subject: [PATCH v6] leds: rgb: leds-qcom-lpg: Fix LED color balancing in HW pattern mode 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: <20260716-lpg-rgb-color-balance-fix-v6-1-b49d51528f61@oss.qualcomm.com> X-B4-Tracking: v=1; b=H4sIAG2yWWoC/43PzY7CIBQF4FcxrAcDXH6KK99jMgugUJnUoqDNT EzfXaqLcTFt3Nzk3Fy+E26o+Bx9QbvNDWU/xhLTUIP82CB3MEPncWxrRowwSSQRuD91OHcWu9S njK3pzeA8DvEHN4yDlFwbGwDV96fs6/phf349c7nab+8uMzhfHGK5pPz7KB/pfPdOz0gxxcAAJ OcGoBH7VMr2fDW9S8fjtg40143sBWR8DWQVdIRa4gVxJsgFEF5BvQZCBalSWhoiW6X1AsjfBnk FeWsboVmw4Ja+LP5ARdQaKCqotWetAtLw4P4Bp2m6A6A9xm0fAgAA X-Change-ID: 20260605-lpg-rgb-color-balance-fix-82436649abf3 To: linux-arm-msm@vger.kernel.org, Lee Jones , Pavel Machek , Bjorn Andersson , Marijn Suijten , Anjelique Melendez , Guru Das Srinagesh , Nathan Chancellor , Nick Desaulniers , Bill Wendling , Justin Stitt Cc: David Collins , Subbaraman Narayanamurthy , Kamal Wadhwa , kernel@oss.qualcomm.com, Pavel Machek , linux-leds@vger.kernel.org, linux-kernel@vger.kernel.org, llvm@lists.linux.dev, Fenglin Wu X-Mailer: b4 0.16-dev-17187 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784263282; l=10916; i=fenglin.wu@oss.qualcomm.com; s=20260324; h=from:subject:message-id; bh=qw3UlfHqx9g7QO6+Mmao8Q1/I71j8qvLvF1DF9oIM9w=; b=kYkQjIOuGiFnhdeBQez7iOOUk6LDE4LC9699I+Mdz2HnEe4+14N4NbrQdV+v/K5kbIVRqQzd7 4gAR7JK9f6LBKVmYzsiDSxhU1DPd9ZBz2ep4HYN6dHcF15Kfyttep0f X-Developer-Key: i=fenglin.wu@oss.qualcomm.com; a=ed25519; pk=hJdt3E7o54lql+miD2GaxwF74cDyhgNwMbmFOZ46bRU= X-Proofpoint-ORIG-GUID: GLSdAu5N54fOoTCMZBPlxepqMveVNWFA X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwNzE3MDA0MiBTYWx0ZWRfX+5XxvHE52ph/ CYRP7EMZaIDFBLPdRiuPl1u24QkCQNBA7oAnsejST/oOdMKabP7MAA27SOvKeqnc8M2PZIlUWYA GzVtb41MJ0lwblYLCaEcIOMumn+h34bOZqWZW+8C7zce5tno6fGsO7AVAEY+uu/342tUf5tn4yQ X6W2OhFbBQPfWdG9fHufNkUOJxZSx2U6R7DTPIMKMuSUE9AEbyQiSNolZRPo3i4LcRtJD+T6gqy JeqsFd697tYv8gAjwLBzHgoamI9xlZNijHug7Yro6d3PtmT8wrJtGXinP1wKVggih4ryQ8c8U5Y 2ISLwi3kfjkX2zDcxKRbKGl9PRwGiSM+/4cDYfl5EdCiC6cwlyBJ7pLii74x4ohRyXx5e1V0Oum jQs8h4rDhYoQ6AVc+xCHaHBsHhSyRBomDmyWIjgLjkj16xo8GPF2otyt3Z/QLlGMx0gQGDuZbcS ic9mBipHtl6M6n917fg== X-Proofpoint-GUID: GLSdAu5N54fOoTCMZBPlxepqMveVNWFA X-Authority-Analysis: v=2.4 cv=BPODalQG c=1 sm=1 tr=0 ts=6a59b275 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=_glEPmIy2e8OvE2BGh3C:22 a=bC-a23v3AAAA:8 a=EUspDBNiAAAA:8 a=63AshnvTA_EL1TQD_80A:9 a=QEXdDO2ut3YA:10 a=uKXjsCUrEbL0IQVhDsJ9:22 a=FO4_E8m0qiDe52t0p3_H:22 X-Proofpoint-Spam-Info: AW1haW4tMjYwNzE3MDA0MiBTYWx0ZWRfX12WsLGnWy9aa R33v1cfEp+XdhK9uAtQDbgwjLBUx/QcziBumc7wp+IkI7a5ARPRkyP/a9JiKmLKP2uiSMhB+UH2 niX4Ohibg6hglAeYTYzTLWxlLZCQU6I= 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_01,2026-07-15_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 suspectscore=0 lowpriorityscore=0 adultscore=0 malwarescore=0 priorityscore=1501 phishscore=0 bulkscore=0 spamscore=0 impostorscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2606150000 definitions=main-2607170042 Currently, when the LED is configured as a RGB LED or a multi-color LED device, the same pattern is programmed for all LED channels regardless of the sub-led intensities when triggered by HW pattern. It results that the LED device is always working in a white-balanced mode regardless of the intensity settings. To fix this, scale the pattern data according to the sub-led intensity and program the HW pattern separately for each LPG channel. Fixes: 24e2d05d1b68 ("leds: Add driver for Qualcomm LPG") Fixes: 6ab1f766a80a ("leds: rgb: leds-qcom-lpg: Add support for PPG through= single SDAM") Fixes: 5e9ff626861a ("leds: rgb: leds-qcom-lpg: Include support for PPG wit= h dedicated LUT SDAM") Assisted-by: Claude:claude-4-6-sonnet Signed-off-by: Fenglin Wu --- Changes in v6: - Fixed comments form Sashiko, and transfer 'pattern.data' to a '__free(kree)' local variable to get rid of the goto. - Link to v5: https://patch.msgid.link/20260707-lpg-rgb-color-balance-fix-v= 5-1-99e2d73084fc@oss.qualcomm.com Changes in v5: - Add no_free_ptr(pattern) to avoid it being cleaned up - Link to v4: https://patch.msgid.link/20260629-lpg-rgb-color-balance-fix-v= 4-1-4db8592fb3c5@oss.qualcomm.com Changes in v4: - Fixing LLVM compilation issue: avoid jumping over guard(mutex) initializa= tion - Link to v3: https://patch.msgid.link/20260629-lpg-rgb-color-balance-fix-v= 3-1-17796a06d799@oss.qualcomm.com Changes in v3: - update to use __free() and guard(mutex) for easy cleanup - Link to v2: https://patch.msgid.link/20260624-lpg-rgb-color-balance-fix-v= 2-1-c01b0e50caf6@oss.qualcomm.com Changes in v2: - Change to use tab for the indention in the comments of 'struct lpg_patter= n' - Remove the comment in lpg_prepare_pattern() as the function name is self-explantory. - Link to v1: https://patch.msgid.link/20260605-lpg-rgb-color-balance-fix-v= 1-1-3233644a3385@oss.qualcomm.com --- drivers/leds/rgb/leds-qcom-lpg.c | 172 +++++++++++++++++++++++++++++------= ---- 1 file changed, 130 insertions(+), 42 deletions(-) diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-= lpg.c index d7d6518de30f..24b1f570f524 100644 --- a/drivers/leds/rgb/leds-qcom-lpg.c +++ b/drivers/leds/rgb/leds-qcom-lpg.c @@ -148,6 +148,24 @@ struct lpg_channel { unsigned int pattern_hi_idx; }; =20 +/** + * struct lpg_pattern - The LPG pattern normalized from the LED pattern + * @data: The pattern data array (caller must kfree) + * @len: number of entries to write to the LUT + * @delta_t: common step duration in ms + * @lo_pause: low-pause duration in ms + * @hi_pause: high-pause duration in ms + * @ping_pong: true if the pattern support reverse + */ +struct lpg_pattern { + struct led_pattern *data; + unsigned int len; + unsigned int delta_t; + unsigned int lo_pause; + unsigned int hi_pause; + bool ping_pong; +}; + /** * struct lpg_led - logical LED object * @lpg: lpg context reference @@ -959,23 +977,15 @@ static int lpg_blink_mc_set(struct led_classdev *cdev, return ret; } =20 -static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pa= ttern, - u32 len, int repeat) +static int lpg_prepare_pattern(struct lpg *lpg, struct led_pattern *led_pa= ttern, + u32 len, int repeat, struct lpg_pattern *prep) { - struct lpg_channel *chan; - struct lpg *lpg =3D led->lpg; - struct led_pattern *pattern; unsigned int brightness_a; unsigned int brightness_b; - unsigned int hi_pause =3D 0; - unsigned int lo_pause =3D 0; unsigned int actual_len; unsigned int delta_t; - unsigned int lo_idx; - unsigned int hi_idx; unsigned int i; bool ping_pong =3D true; - int ret =3D -EINVAL; =20 /* Hardware only support oneshot or indefinite loops */ if (repeat !=3D -1 && repeat !=3D 1) @@ -995,15 +1005,16 @@ static int lpg_pattern_set(struct lpg_led *led, stru= ct led_pattern *led_pattern, if (len % 2) return -EINVAL; =20 - pattern =3D kzalloc_objs(*pattern, len / 2); + struct led_pattern *pattern __free(kfree) =3D kzalloc_objs(*pattern, len = / 2); + if (!pattern) return -ENOMEM; =20 for (i =3D 0; i < len; i +=3D 2) { if (led_pattern[i].brightness !=3D led_pattern[i + 1].brightness) - goto out_free_pattern; + return -EINVAL; if (led_pattern[i + 1].delta_t !=3D 0) - goto out_free_pattern; + return -EINVAL; =20 pattern[i / 2].brightness =3D led_pattern[i].brightness; pattern[i / 2].delta_t =3D led_pattern[i].delta_t; @@ -1016,7 +1027,7 @@ static int lpg_pattern_set(struct lpg_led *led, struc= t led_pattern *led_pattern, * through the entire LUT, so prohibit this. */ if (len < 2) - goto out_free_pattern; + return -EINVAL; =20 /* * The LPG plays patterns with at a fixed pace, a "low pause" can be @@ -1073,13 +1084,13 @@ static int lpg_pattern_set(struct lpg_led *led, str= uct led_pattern *led_pattern, * specify hi pause. Reject other variations. */ if (i !=3D actual_len - 1) - goto out_free_pattern; + return -EINVAL; } } =20 /* LPG_RAMP_DURATION_REG is a 9bit */ if (delta_t >=3D BIT(9)) - goto out_free_pattern; + return -EINVAL; =20 /* * Find "low pause" and "high pause" in the pattern in the LUT case. @@ -1087,43 +1098,64 @@ static int lpg_pattern_set(struct lpg_led *led, str= uct led_pattern *led_pattern, * duration of all steps. */ if (lpg->lut_base || lpg->lut_sdam) { - lo_pause =3D pattern[0].delta_t; - hi_pause =3D pattern[actual_len - 1].delta_t; + prep->lo_pause =3D pattern[0].delta_t; + prep->hi_pause =3D pattern[actual_len - 1].delta_t; } else { if (delta_t !=3D pattern[0].delta_t || delta_t !=3D pattern[actual_len -= 1].delta_t) - goto out_free_pattern; + return -EINVAL; + prep->lo_pause =3D 0; + prep->hi_pause =3D 0; } =20 + prep->data =3D no_free_ptr(pattern); + prep->len =3D actual_len; + prep->delta_t =3D delta_t; + prep->ping_pong =3D ping_pong; =20 - mutex_lock(&lpg->lock); + return 0; +} + +static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pa= ttern, + u32 len, int repeat) +{ + struct lpg_channel *chan; + struct lpg *lpg =3D led->lpg; + struct lpg_pattern pattern; + unsigned int lo_idx; + unsigned int hi_idx; + unsigned int i; + int ret; + + ret =3D lpg_prepare_pattern(lpg, led_pattern, len, repeat, &pattern); + if (ret < 0) + return ret; + + struct led_pattern *prep_data __free(kfree) =3D pattern.data; + + guard(mutex)(&lpg->lock); =20 if (lpg->lut_base) - ret =3D lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx); + ret =3D lpg_lut_store(lpg, prep_data, pattern.len, &lo_idx, &hi_idx); else - ret =3D lpg_lut_store_sdam(lpg, pattern, actual_len, &lo_idx, &hi_idx); + ret =3D lpg_lut_store_sdam(lpg, prep_data, pattern.len, &lo_idx, &hi_idx= ); =20 if (ret < 0) - goto out_unlock; + return ret; =20 for (i =3D 0; i < led->num_channels; i++) { chan =3D led->channels[i]; =20 - chan->ramp_tick_ms =3D delta_t; - chan->ramp_ping_pong =3D ping_pong; + chan->ramp_tick_ms =3D pattern.delta_t; + chan->ramp_ping_pong =3D pattern.ping_pong; chan->ramp_oneshot =3D repeat !=3D -1; =20 - chan->ramp_lo_pause_ms =3D lo_pause; - chan->ramp_hi_pause_ms =3D hi_pause; + chan->ramp_lo_pause_ms =3D pattern.lo_pause; + chan->ramp_hi_pause_ms =3D pattern.hi_pause; =20 chan->pattern_lo_idx =3D lo_idx; chan->pattern_hi_idx =3D hi_idx; } =20 -out_unlock: - mutex_unlock(&lpg->lock); -out_free_pattern: - kfree(pattern); - return ret; } =20 @@ -1144,23 +1176,81 @@ static int lpg_pattern_single_set(struct led_classd= ev *cdev, } =20 static int lpg_pattern_mc_set(struct led_classdev *cdev, - struct led_pattern *pattern, u32 len, + struct led_pattern *led_pattern, u32 len, int repeat) { struct led_classdev_mc *mc =3D lcdev_to_mccdev(cdev); struct lpg_led *led =3D container_of(mc, struct lpg_led, mcdev); + struct lpg *lpg =3D led->lpg; + struct lpg_channel *chan; + struct lpg_pattern pattern; unsigned int triled_mask =3D 0; - int ret, i; - - for (i =3D 0; i < led->num_channels; i++) - triled_mask |=3D led->channels[i]->triled_mask; - triled_set(led->lpg, triled_mask, 0); + unsigned int lo_idx; + unsigned int hi_idx; + unsigned int scale; + unsigned int i, j; + int ret; =20 - ret =3D lpg_pattern_set(led, pattern, len, repeat); + ret =3D lpg_prepare_pattern(lpg, led_pattern, len, repeat, &pattern); if (ret < 0) return ret; =20 + struct led_pattern *prep_data __free(kfree) =3D pattern.data; + + /* Allocate buffer for the per-channel scaled pattern copy */ + struct led_pattern *scaled __free(kfree) =3D + kmalloc_array(pattern.len, sizeof(*scaled), GFP_KERNEL); + if (!scaled) + return -ENOMEM; + + for (i =3D 0; i < led->num_channels; i++) + triled_mask |=3D led->channels[i]->triled_mask; + triled_set(lpg, triled_mask, 0); + led_mc_calc_color_components(mc, LED_FULL); + + /* + * Each channel gets its own LUT block scaled by subled_info[i].brightness + * so the pattern respects the configured colour balance. + */ + guard(mutex)(&lpg->lock); + + for (i =3D 0; i < led->num_channels; i++) { + chan =3D led->channels[i]; + scale =3D mc->subled_info[i].brightness; + + for (j =3D 0; j < pattern.len; j++) { + scaled[j].brightness =3D DIV_ROUND_CLOSEST( + (u32)prep_data[j].brightness * scale, LED_FULL); + scaled[j].delta_t =3D prep_data[j].delta_t; + } + + if (lpg->lut_base) + ret =3D lpg_lut_store(lpg, scaled, pattern.len, &lo_idx, &hi_idx); + else + ret =3D lpg_lut_store_sdam(lpg, scaled, pattern.len, &lo_idx, &hi_idx); + + if (ret < 0) { + /* Free LUT slots already allocated for previous channels */ + while (i-- > 0) { + chan =3D led->channels[i]; + lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); + chan->pattern_lo_idx =3D 0; + chan->pattern_hi_idx =3D 0; + } + + return ret; + } + + chan->ramp_tick_ms =3D pattern.delta_t; + chan->ramp_ping_pong =3D pattern.ping_pong; + chan->ramp_oneshot =3D repeat !=3D -1; + chan->ramp_lo_pause_ms =3D pattern.lo_pause; + chan->ramp_hi_pause_ms =3D pattern.hi_pause; + chan->pattern_lo_idx =3D lo_idx; + chan->pattern_hi_idx =3D hi_idx; + } + lpg_brightness_set(led, cdev, mc->subled_info); =20 return 0; @@ -1174,11 +1264,9 @@ static int lpg_pattern_clear(struct lpg_led *led) =20 mutex_lock(&lpg->lock); =20 - chan =3D led->channels[0]; - lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); - for (i =3D 0; i < led->num_channels; i++) { chan =3D led->channels[i]; + lpg_lut_free(lpg, chan->pattern_lo_idx, chan->pattern_hi_idx); lpg_sdam_configure_triggers(chan, 0); lpg_clear_pbs_trigger(chan->lpg, chan->lut_mask); chan->pattern_lo_idx =3D 0; --- base-commit: 1795fd2dbe84ef4d393b69a0b2a3b371f810bde5 change-id: 20260605-lpg-rgb-color-balance-fix-82436649abf3 Best regards, -- =20 Fenglin Wu