Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2222976/?format=api
{ "id": 2222976, "url": "http://patchwork.ozlabs.org/api/patches/2222976/?format=api", "web_url": "http://patchwork.ozlabs.org/project/rtc-linux/patch/20260414-s2mu005-pmic-v4-10-7fe7480577e6@disroot.org/", "project": { "id": 9, "url": "http://patchwork.ozlabs.org/api/projects/9/?format=api", "name": "Linux RTC development", "link_name": "rtc-linux", "list_id": "linux-rtc.vger.kernel.org", "list_email": "linux-rtc@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260414-s2mu005-pmic-v4-10-7fe7480577e6@disroot.org>", "list_archive_url": null, "date": "2026-04-14T06:33:02", "name": "[v4,10/13] leds: rgb: add support for Samsung S2M series PMIC RGB LED device", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "7043741ac3a4c6238067ef6fb7229c035f4964c5", "submitter": { "id": 88698, "url": "http://patchwork.ozlabs.org/api/people/88698/?format=api", "name": "Kaustabh Chakraborty", "email": "kauschluss@disroot.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/rtc-linux/patch/20260414-s2mu005-pmic-v4-10-7fe7480577e6@disroot.org/mbox/", "series": [ { "id": 499781, "url": "http://patchwork.ozlabs.org/api/series/499781/?format=api", "web_url": "http://patchwork.ozlabs.org/project/rtc-linux/list/?series=499781", "date": "2026-04-14T06:32:53", "name": "Support for Samsung S2MU005 PMIC and its sub-devices", "version": 4, "mbox": "http://patchwork.ozlabs.org/series/499781/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2222976/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2222976/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-rtc+bounces-6326-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-rtc@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n secure) header.d=disroot.org header.i=@disroot.org header.a=rsa-sha256\n header.s=mail header.b=Dddi+8z5;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-rtc+bounces-6326-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=disroot.org header.i=@disroot.org\n header.b=\"Dddi+8z5\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=178.21.23.139", "smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=disroot.org", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=disroot.org" ], "Received": [ "from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fvvln5SsHz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2026 16:40:05 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 6FDD430DEE0A\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 14 Apr 2026 06:34:24 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id EB02136E466;\n\tTue, 14 Apr 2026 06:34:23 +0000 (UTC)", "from layka.disroot.org (layka.disroot.org [178.21.23.139])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id 119CD36DA02;\n\tTue, 14 Apr 2026 06:34:21 +0000 (UTC)", "from [127.0.0.1] (localhost [127.0.0.1])\n\tby disroot.org (Postfix) with ESMTP id 93FC825D82;\n\tTue, 14 Apr 2026 08:34:20 +0200 (CEST)", "from layka.disroot.org ([127.0.0.1])\n by localhost (disroot.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id 7gVT6s3IUv2e; Tue, 14 Apr 2026 08:34:19 +0200 (CEST)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776148463; cv=none;\n b=ECrbYDFr/+FkHS69MfMvB9w2Si3tpMHCzfm8JWQWOI4oK0VZd/ddZeM7teimskSKyB4rxYk+AIQZMrWFZQiio9XI5gIAd29jDuLgSZ2DqLctV5PLm+6j1dzqaCMWFL4Kf4qY1CGsAaADYQdSCzq8tIlJZVaDS2+Gm9Y7ZwhC1Yg=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776148463; c=relaxed/simple;\n\tbh=zbWLA9HhoqFN2pF1fYTL6ftBe5NDhn0wLGl856dgrbY=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References:\n\t In-Reply-To:To:Cc;\n b=aCmrTExj2D87Kw3gNbv+IgeWkZLNYrr/bPsFTMzETkUABLxK3OaqkdIogoHvCPxIRgBy0cMKDvqCsFJZU2jk1d+FNBc/vxr49wyCQCJvsaCVpTiuRsDInzCSgzV8W7cdTkNyY68MGlUOXd/Jzrt+yRXA9DKv3b8YUQ+LIORJHBo=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=reject dis=none) header.from=disroot.org;\n spf=pass smtp.mailfrom=disroot.org;\n dkim=pass (2048-bit key) header.d=disroot.org header.i=@disroot.org\n header.b=Dddi+8z5; arc=none smtp.client-ip=178.21.23.139", "X-Virus-Scanned": "SPAM Filter at disroot.org", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail;\n\tt=1776148459; bh=zbWLA9HhoqFN2pF1fYTL6ftBe5NDhn0wLGl856dgrbY=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc;\n\tb=Dddi+8z5QhQToWEkT8ZXaDzeS+D7glQnhMK2TGvxLVoyqdgi9EMgiHX+47l3Hp7SX\n\t idW0yfGCbMaN/V9mSa2ujAJ+8CB37GdRf6CJxSCFW/UXsxVOA7ypqinQZRilVaZef4\n\t Q9848HOn5h2CQzoLj17vAPFfF+D5o2H9AulLC7cMUq/I2yFOFVo5Jv9nGhp67aO2ck\n\t KCW7RkXiiPNyCezNaIpAMKxqQV0FX/kovaBDeOuCtBE8bGDLGBYxGd70KIZnnv+bEf\n\t LEh7hUQ593faPXHdQab8G1QyZPeyeWmJ6U4LScuLqTEwqUXKHPQlSYdzOc/MQF/W2N\n\t 7NNqBwsfa3gVQ==", "From": "Kaustabh Chakraborty <kauschluss@disroot.org>", "Date": "Tue, 14 Apr 2026 12:03:02 +0530", "Subject": "[PATCH v4 10/13] leds: rgb: add support for Samsung S2M series\n PMIC RGB LED device", "Precedence": "bulk", "X-Mailing-List": "linux-rtc@vger.kernel.org", "List-Id": "<linux-rtc.vger.kernel.org>", "List-Subscribe": "<mailto:linux-rtc+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-rtc+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260414-s2mu005-pmic-v4-10-7fe7480577e6@disroot.org>", "References": "<20260414-s2mu005-pmic-v4-0-7fe7480577e6@disroot.org>", "In-Reply-To": "<20260414-s2mu005-pmic-v4-0-7fe7480577e6@disroot.org>", "To": "Lee Jones <lee@kernel.org>, Pavel Machek <pavel@kernel.org>,\n Rob Herring <robh@kernel.org>, Krzysztof Kozlowski <krzk+dt@kernel.org>,\n Conor Dooley <conor+dt@kernel.org>, MyungJoo Ham <myungjoo.ham@samsung.com>,\n Chanwoo Choi <cw00.choi@samsung.com>, Sebastian Reichel <sre@kernel.org>,\n Krzysztof Kozlowski <krzk@kernel.org>,\n =?utf-8?q?Andr=C3=A9_Draszik?= <andre.draszik@linaro.org>,\n Alexandre Belloni <alexandre.belloni@bootlin.com>,\n Jonathan Corbet <corbet@lwn.net>, Shuah Khan <skhan@linuxfoundation.org>,\n Nam Tran <trannamatk@gmail.com>,\n =?utf-8?b?xYF1a2FzeiBMZWJpZWR6acWEc2tp?= <kernel@lvkasz.us>", "Cc": "linux-leds@vger.kernel.org, devicetree@vger.kernel.org,\n linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,\n linux-samsung-soc@vger.kernel.org, linux-rtc@vger.kernel.org,\n linux-doc@vger.kernel.org, Kaustabh Chakraborty <kauschluss@disroot.org>" }, "content": "Add support for the RGB LEDs found in certain Samsung S2M series PMICs.\nThe device has three LED channels, controlled as a single device. These\nLEDs are typically used as status indicators in mobile phones.\n\nThe driver includes initial support for the S2MU005 PMIC RGB LEDs.\n\nSigned-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>\n---\n drivers/leds/rgb/Kconfig | 11 +\n drivers/leds/rgb/Makefile | 1 +\n drivers/leds/rgb/leds-s2m-rgb.c | 446 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 458 insertions(+)", "diff": "diff --git a/drivers/leds/rgb/Kconfig b/drivers/leds/rgb/Kconfig\nindex 28ef4c487367c..30051342f4e4d 100644\n--- a/drivers/leds/rgb/Kconfig\n+++ b/drivers/leds/rgb/Kconfig\n@@ -75,6 +75,17 @@ config LEDS_QCOM_LPG\n \n \t If compiled as a module, the module will be named leds-qcom-lpg.\n \n+config LEDS_S2M_RGB\n+\ttristate \"Samsung S2M series PMICs RGB LED support\"\n+\tdepends on LEDS_CLASS\n+\tdepends on MFD_SEC_CORE\n+\tselect REGMAP_IRQ\n+\thelp\n+\t This option enables support for the S2MU005 RGB LEDs. These\n+\t devices have three LED channels, with 8-bit brightness control\n+\t for each channel. It's usually found in mobile phones as\n+\t status indicators.\n+\n config LEDS_MT6370_RGB\n \ttristate \"LED Support for MediaTek MT6370 PMIC\"\n \tdepends on MFD_MT6370\ndiff --git a/drivers/leds/rgb/Makefile b/drivers/leds/rgb/Makefile\nindex be45991f63f50..98050e1aa4255 100644\n--- a/drivers/leds/rgb/Makefile\n+++ b/drivers/leds/rgb/Makefile\n@@ -6,4 +6,5 @@ obj-$(CONFIG_LEDS_LP5812)\t\t+= leds-lp5812.o\n obj-$(CONFIG_LEDS_NCP5623)\t\t+= leds-ncp5623.o\n obj-$(CONFIG_LEDS_PWM_MULTICOLOR)\t+= leds-pwm-multicolor.o\n obj-$(CONFIG_LEDS_QCOM_LPG)\t\t+= leds-qcom-lpg.o\n+obj-$(CONFIG_LEDS_S2M_RGB)\t\t+= leds-s2m-rgb.o\n obj-$(CONFIG_LEDS_MT6370_RGB)\t\t+= leds-mt6370-rgb.o\ndiff --git a/drivers/leds/rgb/leds-s2m-rgb.c b/drivers/leds/rgb/leds-s2m-rgb.c\nnew file mode 100644\nindex 0000000000000..5cefe8b990fb4\n--- /dev/null\n+++ b/drivers/leds/rgb/leds-s2m-rgb.c\n@@ -0,0 +1,446 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * RGB LED Driver for Samsung S2M series PMICs.\n+ *\n+ * Copyright (c) 2015 Samsung Electronics Co., Ltd\n+ * Copyright (c) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>\n+ */\n+\n+#include <linux/container_of.h>\n+#include <linux/led-class-multicolor.h>\n+#include <linux/mfd/samsung/core.h>\n+#include <linux/mfd/samsung/s2mu005.h>\n+#include <linux/minmax.h>\n+#include <linux/module.h>\n+#include <linux/mutex.h>\n+#include <linux/of.h>\n+#include <linux/platform_device.h>\n+#include <linux/regmap.h>\n+\n+struct s2m_rgb {\n+\tstruct device *dev;\n+\tstruct regmap *regmap;\n+\tstruct led_classdev_mc mc;\n+\tenum sec_device_type device_type;\n+\t/*\n+\t * The mutex object prevents race conditions when evaluation and\n+\t * application of LED pattern state.\n+\t */\n+\tstruct mutex lock;\n+\t/*\n+\t * State variables representing the current LED pattern, these only to\n+\t * be accessed when lock is held.\n+\t */\n+\tu8 ramp_up;\n+\tu8 ramp_dn;\n+\tu8 stay_hi;\n+\tu8 stay_lo;\n+};\n+\n+static struct led_classdev_mc *to_s2m_mc(struct led_classdev *cdev)\n+{\n+\treturn container_of(cdev, struct led_classdev_mc, led_cdev);\n+}\n+\n+static struct s2m_rgb *to_s2m_rgb(struct led_classdev_mc *mc)\n+{\n+\treturn container_of(mc, struct s2m_rgb, mc);\n+}\n+\n+static const u32 s2mu005_rgb_lut_ramp[] = {\n+\t0,\t100,\t200,\t300,\t400,\t500,\t600,\t700,\n+\t800,\t1000,\t1200,\t1400,\t1600,\t1800,\t2000,\t2200,\n+};\n+\n+static const u32 s2mu005_rgb_lut_stay_hi[] = {\n+\t100,\t200,\t300,\t400,\t500,\t750,\t1000,\t1250,\n+\t1500,\t1750,\t2000,\t2250,\t2500,\t2750,\t3000,\t3250,\n+};\n+\n+static const u32 s2mu005_rgb_lut_stay_lo[] = {\n+\t0,\t500,\t1000,\t1500,\t2000,\t2500,\t3000,\t3500,\n+\t4000,\t4500,\t5000,\t6000,\t7000,\t8000,\t10000,\t12000,\n+};\n+\n+static int s2mu005_rgb_apply_params(struct s2m_rgb *rgb)\n+{\n+\tstruct regmap *regmap = rgb->regmap;\n+\tunsigned int ramp_val = 0;\n+\tunsigned int stay_val = 0;\n+\tint ret;\n+\tint i;\n+\n+\tramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_UP, rgb->ramp_up);\n+\tramp_val |= FIELD_PREP(S2MU005_RGB_CH_RAMP_DN, rgb->ramp_dn);\n+\n+\tstay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_HI, rgb->stay_hi);\n+\tstay_val |= FIELD_PREP(S2MU005_RGB_CH_STAY_LO, rgb->stay_lo);\n+\n+\tret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);\n+\tif (ret < 0) {\n+\t\tdev_err(rgb->dev, \"failed to reset RGB LEDs\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tfor (i = 0; i < rgb->mc.num_colors; i++) {\n+\t\tret = regmap_write(regmap, S2MU005_REG_RGB_CH_CTRL(i),\n+\t\t\t\t rgb->mc.subled_info[i].brightness);\n+\t\tif (ret < 0) {\n+\t\t\tdev_err(rgb->dev, \"failed to set LED brightness\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = regmap_write(regmap, S2MU005_REG_RGB_CH_RAMP(i), ramp_val);\n+\t\tif (ret < 0) {\n+\t\t\tdev_err(rgb->dev, \"failed to set ramp timings\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\n+\t\tret = regmap_write(regmap, S2MU005_REG_RGB_CH_STAY(i), stay_val);\n+\t\tif (ret < 0) {\n+\t\t\tdev_err(rgb->dev, \"failed to set stay timings\\n\");\n+\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tret = regmap_update_bits(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_SLOPE,\n+\t\t\t\t S2MU005_RGB_SLOPE_SMOOTH);\n+\tif (ret < 0) {\n+\t\tdev_err(rgb->dev, \"failed to set ramp slope\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int s2mu005_rgb_reset_params(struct s2m_rgb *rgb)\n+{\n+\tstruct regmap *regmap = rgb->regmap;\n+\tint ret;\n+\n+\tret = regmap_write(regmap, S2MU005_REG_RGB_EN, S2MU005_RGB_RESET);\n+\tif (ret < 0) {\n+\t\tdev_err(rgb->dev, \"failed to reset RGB LEDs\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\trgb->ramp_up = 0;\n+\trgb->ramp_dn = 0;\n+\trgb->stay_hi = 0;\n+\trgb->stay_lo = 0;\n+\n+\treturn 0;\n+}\n+\n+static int s2m_rgb_lut_calc_timing(const u32 *lut, const size_t len,\n+\t\t\t\t const u32 req_time, u8 *idx)\n+{\n+\tint lo = 0;\n+\tint hi = len - 2;\n+\n+\t/* Bounds checking */\n+\tif (req_time < lut[0] || req_time > lut[len - 1])\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * Perform a binary search to pick the best timing from the LUT.\n+\t *\n+\t * The search algorithm picks two consecutive elements of the\n+\t * LUT and tries to search the pair between which the requested\n+\t * time lies.\n+\t */\n+\twhile (lo <= hi) {\n+\t\t*idx = (lo + hi) / 2;\n+\n+\t\tif ((lut[*idx] <= req_time) && (req_time <= lut[*idx + 1]))\n+\t\t\tbreak;\n+\n+\t\tif ((req_time < lut[*idx]) && (req_time < lut[*idx + 1]))\n+\t\t\thi = *idx - 1;\n+\t\telse\n+\t\t\tlo = *idx + 1;\n+\t}\n+\n+\t/*\n+\t * The searched timing is always less than the requested time. At\n+\t * times, the succeeding timing in the LUT is closer thus more\n+\t * accurate. Adjust the resulting value if that's the case.\n+\t */\n+\tif (abs(req_time - lut[*idx]) > abs(lut[*idx + 1] - req_time))\n+\t\t(*idx)++;\n+\n+\treturn 0;\n+}\n+\n+static int s2m_rgb_pattern_set(struct led_classdev *cdev, struct led_pattern *pattern,\n+\t\t\t u32 len, int repeat)\n+{\n+\tstruct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));\n+\tconst u32 *lut_ramp_up, *lut_ramp_dn, *lut_stay_hi, *lut_stay_lo;\n+\tsize_t lut_ramp_up_len, lut_ramp_dn_len, lut_stay_hi_len, lut_stay_lo_len;\n+\tint brightness_peak = 0;\n+\tu32 time_hi = 0, time_lo = 0;\n+\tbool ramp_up_en, ramp_dn_en;\n+\tint ret;\n+\tint i;\n+\n+\t/*\n+\t * The typical pattern supported by this device can be\n+\t * represented with the following graph:\n+\t *\n+\t * 255 T ''''''-. .-'''''''-.\n+\t * | '. .' '.\n+\t * | \\ / \\\n+\t * | '. .' '.\n+\t * | '-...........-' '-\n+\t * 0 +----------------------------------------------------> time (s)\n+\t *\n+\t * <---- HIGH ----><-- LOW --><-------- HIGH --------->\n+\t * <-----><-------><---------><-------><-----><------->\n+\t * stay_hi ramp_dn stay_lo ramp_up stay_hi ramp_dn\n+\t *\n+\t * There are two states, named HIGH and LOW. HIGH has a non-zero\n+\t * brightness level, while LOW is of zero brightness. The\n+\t * pattern provided should mention only one zero and non-zero\n+\t * brightness level. The hardware always starts the pattern from\n+\t * the HIGH state, as shown in the graph.\n+\t *\n+\t * The HIGH state can be divided in three somewhat equal timings:\n+\t * ramp_up, stay_hi, and ramp_dn. The LOW state has only one\n+\t * timing: stay_lo.\n+\t */\n+\n+\t/* Only indefinitely looping patterns are supported. */\n+\tif (repeat != -1)\n+\t\treturn -EINVAL;\n+\n+\t/* Pattern should consist of at least two tuples. */\n+\tif (len < 2)\n+\t\treturn -EINVAL;\n+\n+\tfor (i = 0; i < len; i++) {\n+\t\tint brightness = pattern[i].brightness;\n+\t\tu32 delta_t = pattern[i].delta_t;\n+\n+\t\tif (brightness) {\n+\t\t\t/*\n+\t\t\t * The pattern shold define only one non-zero\n+\t\t\t * brightness in the HIGH state. The device\n+\t\t\t * doesn't have any provisions to handle\n+\t\t\t * multiple peak brightness levels.\n+\t\t\t */\n+\t\t\tif (brightness_peak && brightness_peak != brightness)\n+\t\t\t\treturn -EINVAL;\n+\n+\t\t\tbrightness_peak = brightness;\n+\t\t\ttime_hi += delta_t;\n+\t\t\tramp_dn_en = !!delta_t;\n+\t\t} else {\n+\t\t\ttime_lo += delta_t;\n+\t\t\tramp_up_en = !!delta_t;\n+\t\t}\n+\t}\n+\n+\tswitch (rgb->device_type) {\n+\tcase S2MU005:\n+\t\tlut_ramp_up = s2mu005_rgb_lut_ramp;\n+\t\tlut_ramp_up_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);\n+\t\tlut_ramp_dn = s2mu005_rgb_lut_ramp;\n+\t\tlut_ramp_dn_len = ARRAY_SIZE(s2mu005_rgb_lut_ramp);\n+\t\tlut_stay_hi = s2mu005_rgb_lut_stay_hi;\n+\t\tlut_stay_hi_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_hi);\n+\t\tlut_stay_lo = s2mu005_rgb_lut_stay_lo;\n+\t\tlut_stay_lo_len = ARRAY_SIZE(s2mu005_rgb_lut_stay_lo);\n+\t\tbreak;\n+\tdefault:\n+\t\t/* execution shouldn't reach here */\n+\t\tbreak;\n+\t}\n+\n+\tmutex_lock(&rgb->lock);\n+\n+\t/*\n+\t * The timings ramp_up, stay_hi, and ramp_dn of the HIGH state\n+\t * are roughly equal. Firstly, calculate and set timings for\n+\t * ramp_up and ramp_dn (making sure they're exactly equal).\n+\t */\n+\trgb->ramp_up = 0;\n+\trgb->ramp_dn = 0;\n+\n+\tif (ramp_up_en) {\n+\t\tret = s2m_rgb_lut_calc_timing(lut_ramp_up, lut_ramp_up_len, time_hi / 3,\n+\t\t\t\t\t &rgb->ramp_up);\n+\t\tif (ret < 0)\n+\t\t\tgoto param_fail;\n+\t}\n+\n+\tif (ramp_dn_en) {\n+\t\tret = s2m_rgb_lut_calc_timing(lut_ramp_dn, lut_ramp_dn_len, time_hi / 3,\n+\t\t\t\t\t &rgb->ramp_dn);\n+\t\tif (ret < 0)\n+\t\t\tgoto param_fail;\n+\t}\n+\n+\t/*\n+\t * Subtract the allocated ramp timings from time_hi (and also\n+\t * making sure it doesn't underflow!). The remaining time is\n+\t * allocated to stay_hi.\n+\t */\n+\ttime_hi -= min(time_hi, lut_ramp_up[rgb->ramp_up]);\n+\ttime_hi -= min(time_hi, lut_ramp_dn[rgb->ramp_dn]);\n+\n+\tret = s2m_rgb_lut_calc_timing(lut_stay_hi, lut_stay_hi_len, time_hi, &rgb->stay_hi);\n+\tif (ret < 0)\n+\t\tgoto param_fail;\n+\n+\tret = s2m_rgb_lut_calc_timing(lut_stay_lo, lut_stay_lo_len, time_lo, &rgb->stay_lo);\n+\tif (ret < 0)\n+\t\tgoto param_fail;\n+\n+\tled_mc_calc_color_components(&rgb->mc, brightness_peak);\n+\tswitch (rgb->device_type) {\n+\tcase S2MU005:\n+\t\tret = s2mu005_rgb_apply_params(rgb);\n+\t\tbreak;\n+\tdefault:\n+\t\t/* execution shouldn't reach here */\n+\t\tbreak;\n+\t}\n+\tif (ret < 0)\n+\t\tgoto param_fail;\n+\n+\tmutex_unlock(&rgb->lock);\n+\n+\treturn 0;\n+\n+param_fail:\n+\trgb->ramp_up = 0;\n+\trgb->ramp_dn = 0;\n+\trgb->stay_hi = 0;\n+\trgb->stay_lo = 0;\n+\n+\tmutex_unlock(&rgb->lock);\n+\n+\treturn ret;\n+}\n+\n+static int s2m_rgb_pattern_clear(struct led_classdev *cdev)\n+{\n+\tstruct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));\n+\tint ret = 0;\n+\n+\tmutex_lock(&rgb->lock);\n+\n+\tswitch (rgb->device_type) {\n+\tcase S2MU005:\n+\t\tret = s2mu005_rgb_reset_params(rgb);\n+\t\tbreak;\n+\tdefault:\n+\t\t/* execution shouldn't reach here */\n+\t\tbreak;\n+\t}\n+\n+\tmutex_unlock(&rgb->lock);\n+\n+\treturn ret;\n+}\n+\n+static int s2m_rgb_brightness_set(struct led_classdev *cdev, enum led_brightness value)\n+{\n+\tstruct s2m_rgb *rgb = to_s2m_rgb(to_s2m_mc(cdev));\n+\tint ret = 0;\n+\n+\tif (!value)\n+\t\treturn s2m_rgb_pattern_clear(cdev);\n+\n+\tmutex_lock(&rgb->lock);\n+\n+\tled_mc_calc_color_components(&rgb->mc, value);\n+\tswitch (rgb->device_type) {\n+\tcase S2MU005:\n+\t\tret = s2mu005_rgb_apply_params(rgb);\n+\t\tbreak;\n+\tdefault:\n+\t\t/* execution shouldn't reach here */\n+\t\tbreak;\n+\t}\n+\n+\tmutex_unlock(&rgb->lock);\n+\n+\treturn ret;\n+}\n+\n+static struct mc_subled s2mu005_rgb_subled_info[] = {\n+\t{ .channel = 0, .color_index = LED_COLOR_ID_BLUE },\n+\t{ .channel = 1, .color_index = LED_COLOR_ID_GREEN },\n+\t{ .channel = 2, .color_index = LED_COLOR_ID_RED },\n+};\n+\n+static int s2m_rgb_probe(struct platform_device *pdev)\n+{\n+\tstruct device *dev = &pdev->dev;\n+\tstruct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);\n+\tstruct s2m_rgb *rgb;\n+\tstruct led_init_data init_data = {};\n+\tint ret;\n+\n+\trgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL);\n+\tif (!rgb)\n+\t\treturn -ENOMEM;\n+\n+\tplatform_set_drvdata(pdev, rgb);\n+\trgb->dev = dev;\n+\trgb->regmap = pmic_drvdata->regmap_pmic;\n+\trgb->device_type = platform_get_device_id(pdev)->driver_data;\n+\n+\tswitch (rgb->device_type) {\n+\tcase S2MU005:\n+\t\trgb->mc.subled_info = s2mu005_rgb_subled_info;\n+\t\trgb->mc.num_colors = ARRAY_SIZE(s2mu005_rgb_subled_info);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn dev_err_probe(dev, -ENODEV, \"device type %d is not supported by driver\\n\",\n+\t\t\t\t pmic_drvdata->device_type);\n+\t}\n+\n+\trgb->mc.led_cdev.max_brightness = 255;\n+\trgb->mc.led_cdev.brightness_set_blocking = s2m_rgb_brightness_set;\n+\trgb->mc.led_cdev.pattern_set = s2m_rgb_pattern_set;\n+\trgb->mc.led_cdev.pattern_clear = s2m_rgb_pattern_clear;\n+\n+\tret = devm_mutex_init(dev, &rgb->lock);\n+\tif (ret)\n+\t\treturn dev_err_probe(dev, ret, \"failed to create mutex lock\\n\");\n+\n+\tinit_data.fwnode = of_fwnode_handle(dev->of_node);\n+\tret = devm_led_classdev_multicolor_register_ext(dev, &rgb->mc, &init_data);\n+\tif (ret < 0)\n+\t\treturn dev_err_probe(dev, ret, \"failed to create LED device\\n\");\n+\n+\treturn 0;\n+}\n+\n+static const struct platform_device_id s2m_rgb_id_table[] = {\n+\t{ \"s2mu005-rgb\", S2MU005 },\n+\t{ /* sentinel */ },\n+};\n+MODULE_DEVICE_TABLE(platform, s2m_rgb_id_table);\n+\n+static const struct of_device_id s2m_rgb_of_match_table[] = {\n+\t{ .compatible = \"samsung,s2mu005-rgb\", .data = (void *)S2MU005 },\n+\t{ /* sentinel */ },\n+};\n+MODULE_DEVICE_TABLE(of, s2m_rgb_of_match_table);\n+\n+static struct platform_driver s2m_rgb_driver = {\n+\t.driver = {\n+\t\t.name = \"s2m-rgb\",\n+\t},\n+\t.probe = s2m_rgb_probe,\n+\t.id_table = s2m_rgb_id_table,\n+};\n+module_platform_driver(s2m_rgb_driver);\n+\n+MODULE_DESCRIPTION(\"RGB LED Driver For Samsung S2M Series PMICs\");\n+MODULE_AUTHOR(\"Kaustabh Chakraborty <kauschluss@disroot.org>\");\n+MODULE_LICENSE(\"GPL\");\n", "prefixes": [ "v4", "10/13" ] }