* [PATCH v6 1/2] Input: add ABS_SND_PROFILE
From: David Heidelberg via B4 Relay @ 2025-07-31 21:17 UTC (permalink / raw)
To: Dmitry Torokhov, Jonathan Corbet, Jiri Kosina, Benjamin Tissoires,
Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-input, linux-doc, linux-kernel, linux-arm-msm, devicetree,
Gergo Koteles, David Heidelberg
In-Reply-To: <20250731-op6-tri-state-v6-0-569c25cbc8c2@ixit.cz>
From: Gergo Koteles <soyer@irl.hu>
ABS_SND_PROFILE used to describe the state of a multi-value sound profile
switch. This will be used for the alert-slider on OnePlus phones or other
phones.
Profile values added as SND_PROFLE_(SILENT|VIBRATE|RING) identifiers
to input-event-codes.h so they can be used from DTS.
Signed-off-by: Gergo Koteles <soyer@irl.hu>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
Documentation/input/event-codes.rst | 6 ++++++
drivers/hid/hid-debug.c | 1 +
include/uapi/linux/input-event-codes.h | 9 +++++++++
3 files changed, 16 insertions(+)
diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
index b4557462edd7b3fef9e9cd6c2c3cb2d05bb531ab..d43336e64d6aa4fe8a41b7e9947f4f214df6e1ab 100644
--- a/Documentation/input/event-codes.rst
+++ b/Documentation/input/event-codes.rst
@@ -241,6 +241,12 @@ A few EV_ABS codes have special meanings:
emitted only when the selected profile changes, indicating the newly
selected profile value.
+* ABS_SND_PROFILE:
+
+ - Used to describe the state of a multi-value sound profile switch.
+ An event is emitted only when the selected profile changes,
+ indicating the newly selected profile value.
+
* ABS_MT_<name>:
- Used to describe multitouch input events. Please see
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 7107071c7c516af48c0c5fc1206c1e01bae3889f..c58500d8b94b581e41ae098d6ce99db7783986b7 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -3513,6 +3513,7 @@ static const char *absolutes[ABS_CNT] = {
[ABS_DISTANCE] = "Distance", [ABS_TILT_X] = "XTilt",
[ABS_TILT_Y] = "YTilt", [ABS_TOOL_WIDTH] = "ToolWidth",
[ABS_VOLUME] = "Volume", [ABS_PROFILE] = "Profile",
+ [ABS_SND_PROFILE] = "SoundProfile",
[ABS_MISC] = "Misc",
[ABS_MT_SLOT] = "MTSlot",
[ABS_MT_TOUCH_MAJOR] = "MTMajor",
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
index 08cb157ab59364a41ac425fc9a4ea8eb2fae0e86..f443f61f9bb8e3c212da522d9b99a386a13c4475 100644
--- a/include/uapi/linux/input-event-codes.h
+++ b/include/uapi/linux/input-event-codes.h
@@ -875,6 +875,7 @@
#define ABS_VOLUME 0x20
#define ABS_PROFILE 0x21
+#define ABS_SND_PROFILE 0x22
#define ABS_MISC 0x28
@@ -984,4 +985,12 @@
#define SND_MAX 0x07
#define SND_CNT (SND_MAX+1)
+/*
+ * ABS_SND_PROFILE values
+ */
+
+#define SND_PROFILE_SILENT 0x00
+#define SND_PROFILE_VIBRATE 0x01
+#define SND_PROFILE_RING 0x02
+
#endif
--
2.50.1
^ permalink raw reply related
* [PATCH v6 2/2] arm64: dts: qcom: sdm845-oneplus: Add alert-slider
From: David Heidelberg via B4 Relay @ 2025-07-31 21:17 UTC (permalink / raw)
To: Dmitry Torokhov, Jonathan Corbet, Jiri Kosina, Benjamin Tissoires,
Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-input, linux-doc, linux-kernel, linux-arm-msm, devicetree,
Gergo Koteles, Casey Connolly, David Heidelberg, Konrad Dybcio
In-Reply-To: <20250731-op6-tri-state-v6-0-569c25cbc8c2@ixit.cz>
From: Gergo Koteles <soyer@irl.hu>
The alert-slider is a tri-state sound profile switch found on the
OnePlus 6, Android maps the states to "silent", "vibrate" and "ring".
Expose them as ABS_SND_PROFILE events.
The previous GPIO numbers were wrong. Update them to the correct ones.
Co-developed-by: Casey Connolly <casey@connolly.tech>
Signed-off-by: Casey Connolly <casey@connolly.tech>
Signed-off-by: Gergo Koteles <soyer@irl.hu>
Reviewed-by: Konrad Dybcio <konrad.dybcio@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
.../arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi | 39 ++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
index b118d666e535a433f44b66c71b36e55df2ce5c80..242c78828992f9fbb384b690352660a496b22411 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
@@ -21,6 +21,41 @@
/delete-node/ &rmtfs_mem;
/ {
+ alert-slider {
+ compatible = "gpio-keys";
+ label = "Alert slider";
+
+ pinctrl-0 = <&alert_slider_default>;
+ pinctrl-names = "default";
+
+ switch-top {
+ label = "Silent";
+ linux,input-type = <EV_ABS>;
+ linux,code = <ABS_SND_PROFILE>;
+ linux,input-value = <SND_PROFILE_SILENT>;
+ gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
+ linux,can-disable;
+ };
+
+ switch-middle {
+ label = "Vibrate";
+ linux,input-type = <EV_ABS>;
+ linux,code = <ABS_SND_PROFILE>;
+ linux,input-value = <SND_PROFILE_VIBRATE>;
+ gpios = <&tlmm 52 GPIO_ACTIVE_LOW>;
+ linux,can-disable;
+ };
+
+ switch-bottom {
+ label = "Ring";
+ linux,input-type = <EV_ABS>;
+ linux,code = <ABS_SND_PROFILE>;
+ linux,input-value = <SND_PROFILE_RING>;
+ gpios = <&tlmm 24 GPIO_ACTIVE_LOW>;
+ linux,can-disable;
+ };
+ };
+
aliases {
serial0 = &uart9;
serial1 = &uart6;
@@ -799,8 +834,8 @@ hall_sensor_default: hall-sensor-default-state {
bias-disable;
};
- tri_state_key_default: tri-state-key-default-state {
- pins = "gpio40", "gpio42", "gpio26";
+ alert_slider_default: alert-slider-default-state {
+ pins = "gpio126", "gpio52", "gpio24";
function = "gpio";
drive-strength = <2>;
bias-disable;
--
2.50.1
^ permalink raw reply related
* [PATCH v6 0/2] Add support for sound profile switching and leverage for OnePlus 6 slider
From: David Heidelberg via B4 Relay @ 2025-07-31 21:17 UTC (permalink / raw)
To: Dmitry Torokhov, Jonathan Corbet, Jiri Kosina, Benjamin Tissoires,
Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: linux-input, linux-doc, linux-kernel, linux-arm-msm, devicetree,
Gergo Koteles, David Heidelberg, Casey Connolly, Konrad Dybcio
This code was tested for two years within the downstream Snapdragon 845 tree.
It is now perfectly integrated with feedbackd in the Phosh environment.
Changes in v6:
- Rebased again next-20250731, otherwise just a resent.
- Link to v5: https://lore.kernel.org/r/20250419-op6-tri-state-v5-0-443127078517@ixit.cz
Changes in v5:
- Dropped merged
"Input: gpio-keys - add support for linux,input-value DTS property"
- Link to v4: https://lore.kernel.org/all/cover.1677022414.git.soyer@irl.hu/
Changes in v4:
- DTS: use default debounce-interval, order alphabetically
- Link to v3: https://lore.kernel.org/lkml/cover.1676850819.git.soyer@irl.hu/
Changes in v3:
- rename tri-state-key to alert-slider, fix DTS warnings,
Changes in v2:
- rebase to qcom/for-next
add SND_PROFILE_* identifiers to input-event-codes.h
base-commit: 02ac8d2a011b630481d959298a1cc76ca0717f3e
---
Gergo Koteles (2):
Input: add ABS_SND_PROFILE
arm64: dts: qcom: sdm845-oneplus: Add alert-slider
Documentation/input/event-codes.rst | 6 ++++
.../arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi | 39 ++++++++++++++++++++--
drivers/hid/hid-debug.c | 1 +
include/uapi/linux/input-event-codes.h | 9 +++++
4 files changed, 53 insertions(+), 2 deletions(-)
---
base-commit: 84b92a499e7eca54ba1df6f6c6e01766025943f1
change-id: 20250419-op6-tri-state-ed1a05a11125
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* [PATCH RESEND v5 6/7] Input: synaptics-rmi4 - read product ID on aftermarket touch ICs
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Kaustabh Chakraborty <kauschluss@disroot.org>
Some replacement displays include third-party touch ICs which do not
report the product ID correctly unless we read directly from the
product ID register. Add a check and a fallback read to handle this.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_f01.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 47be64284b25ede8103ada86d6b58fd3a26976bb..2278e9b6a920774b07ec9dd3e452cedc69469be8 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -250,6 +250,20 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
}
}
+ /*
+ * Some aftermarket ICs put garbage into the product id field unless
+ * we read directly from the product id register.
+ */
+ if (props->product_id[0] < 0x20) {
+ ret = rmi_read_block(rmi_dev, query_base_addr + 11,
+ props->product_id, RMI_PRODUCT_ID_LENGTH);
+ if (ret) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read product id: %d\n", ret);
+ return ret;
+ }
+ }
+
return 0;
}
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 4/7] Input: synaptics-rmi4 - f55: handle zero electrode count
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Kaustabh Chakraborty <kauschluss@disroot.org>
Some third party ICs claim to support f55 but report an electrode count
of 0. Catch this and bail out early so that we don't confuse the i2c bus
with 0 sized reads.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[simplify code, adjust wording]
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_f55.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c
index 488adaca4dd00482cd1106d813b32871092c83a0..776c915b82e72b8a6eb5ec701cce9059c87089c4 100644
--- a/drivers/input/rmi4/rmi_f55.c
+++ b/drivers/input/rmi4/rmi_f55.c
@@ -52,6 +52,11 @@ static int rmi_f55_detect(struct rmi_function *fn)
f55->num_rx_electrodes = f55->qry[F55_NUM_RX_OFFSET];
f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET];
+ if (!f55->num_rx_electrodes || !f55->num_tx_electrodes) {
+ dev_err(&fn->dev, "%s: F55 query returned no electrodes, giving up\n",
+ __func__);
+ return -EINVAL;
+ }
f55->cfg_num_rx_electrodes = f55->num_rx_electrodes;
f55->cfg_num_tx_electrodes = f55->num_rx_electrodes;
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 5/7] Input: synaptics-rmi4 - don't do unaligned reads in IRQ context
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Kaustabh Chakraborty <kauschluss@disroot.org>
Some replacement displays include third-party touch ICs which incur a
significant penalty (1-2 seconds) when doing certain unaligned reads.
This is enough to break functionality when it happens in the hot path,
so adjust the interrupt handler to not read from an unaligned address.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_driver.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 0f6dbe586c652d641178f43ec27df3613126ca66..f977541d8913525d53a59e1d53c33897f1c93901 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -136,9 +136,14 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
return 0;
if (!data->attn_data.data) {
+ /*
+ * Read the device status register as well and ignore it.
+ * Some aftermarket ICs have issues with interrupt requests
+ * otherwise.
+ */
error = rmi_read_block(rmi_dev,
- data->f01_container->fd.data_base_addr + 1,
- data->irq_status, data->num_of_irq_regs);
+ data->f01_container->fd.data_base_addr,
+ (u8 *)data->irq_status - 1, data->num_of_irq_regs + 1);
if (error < 0) {
dev_err(dev, "Failed to read irqs, code=%d\n", error);
return error;
@@ -1078,16 +1083,17 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
data->num_of_irq_regs = (data->irq_count + 7) / 8;
size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
- data->irq_memory = devm_kcalloc(dev, size, 4, GFP_KERNEL);
+ data->irq_memory = devm_kzalloc(dev, size * 4 + 1, GFP_KERNEL);
if (!data->irq_memory) {
dev_err(dev, "Failed to allocate memory for irq masks.\n");
return -ENOMEM;
}
- data->irq_status = data->irq_memory + size * 0;
- data->fn_irq_bits = data->irq_memory + size * 1;
- data->current_irq_mask = data->irq_memory + size * 2;
- data->new_irq_mask = data->irq_memory + size * 3;
+ /* The first byte is reserved for the device status register */
+ data->irq_status = data->irq_memory + size * 0 + 1;
+ data->fn_irq_bits = data->irq_memory + size * 1 + 1;
+ data->current_irq_mask = data->irq_memory + size * 2 + 1;
+ data->new_irq_mask = data->irq_memory + size * 3 + 1;
return retval;
}
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 7/7] Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Kaustabh Chakraborty <kauschluss@disroot.org>
Some replacement displays include third-party touch ICs which do not
expose the function number and the interrupt status in its PDT entries.
OnePlus 6 (original touch IC)
rmi4_i2c 12-0020: read 6 bytes at 0x00e3: 0 (2b 22 0d 06 01 01)
OnePlus 6 (aftermarket touch IC)
rmi4_i2c 12-0020: read 6 bytes at 0x00e3: 0 (2c 23 0d 06 00 00)
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[codeflow adjustments, checkpatch fixes, wording]
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_driver.c | 62 +++++++++++++++++++++++++++++++++++------
drivers/input/rmi4/rmi_driver.h | 2 ++
include/linux/rmi.h | 3 ++
3 files changed, 59 insertions(+), 8 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index f977541d8913525d53a59e1d53c33897f1c93901..e736e90c071466cc61a441bcb3061564e039bfc8 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -461,9 +461,10 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
return 0;
}
-static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
- struct pdt_entry *entry, u16 pdt_address)
+static int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
+ struct pdt_scan_state *state, u16 pdt_address)
{
+ const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
u8 buf[RMI_PDT_ENTRY_SIZE];
int error;
@@ -474,6 +475,21 @@ static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
return error;
}
+ if (pdata->pdt_fallback_size > state->pdt_count * RMI_OF_PDT_DESC_CELLS + 1) {
+ /* Use the description bytes from the driver */
+ buf[5] = pdata->pdt_fallback_desc[state->pdt_count * RMI_OF_PDT_DESC_CELLS];
+ buf[4] = pdata->pdt_fallback_desc[state->pdt_count * RMI_OF_PDT_DESC_CELLS + 1];
+
+ error = rmi_read_block(rmi_dev, pdt_address, buf,
+ RMI_PDT_ENTRY_SIZE - 2);
+ if (error) {
+ dev_err(&rmi_dev->dev,
+ "Read PDT entry at %#06x failed, code: %d.\n",
+ pdt_address, error);
+ return error;
+ }
+ }
+
entry->page_start = pdt_address & RMI4_PAGE_MASK;
entry->query_base_addr = buf[0];
entry->command_base_addr = buf[1];
@@ -546,7 +562,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
int retval;
for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) {
- error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr);
+ error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, state, addr);
if (error)
return error;
@@ -1023,9 +1039,13 @@ static int rmi_driver_remove(struct device *dev)
}
#ifdef CONFIG_OF
-static int rmi_driver_of_probe(struct device *dev,
- struct rmi_device_platform_data *pdata)
+static const u8 rmi_s3706_fallback_pdt[] = {34, 41, 01, 01, 12, 01};
+
+static int rmi_driver_of_probe(struct rmi_device *rmi_dev,
+ struct rmi_device_platform_data *pdata)
{
+ struct device *dev = rmi_dev->xport->dev;
+ u8 buf[RMI_PDT_ENTRY_SIZE];
int retval;
retval = rmi_of_property_read_u32(dev, &pdata->reset_delay_ms,
@@ -1033,11 +1053,37 @@ static int rmi_driver_of_probe(struct device *dev,
if (retval)
return retval;
+ /*
+ * In some aftermerket touch ICs, the first PDT entry is empty and
+ * the function number register is 0. If so, the driver
+ * may have provide backup PDT entries.
+ */
+
+ retval = rmi_read_block(rmi_dev, PDT_START_SCAN_LOCATION,
+ buf, RMI_PDT_ENTRY_SIZE);
+ if (retval) {
+ dev_err(dev, "Read PDT entry at %#06x failed, code: %d.\n",
+ PDT_START_SCAN_LOCATION, retval);
+ return retval;
+ }
+
+ if (!RMI4_END_OF_PDT(buf[5]))
+ return 0;
+
+ /* List of known PDT entries per compatible. */
+ if (of_device_is_compatible(dev->of_node, "syna,rmi4-s3706b")) {
+ pdata->pdt_fallback_desc = rmi_s3706_fallback_pdt;
+ pdata->pdt_fallback_size = ARRAY_SIZE(rmi_s3706_fallback_pdt);
+ } else {
+ dev_err(dev, "First PDT entry is empty and no backup values provided.\n");
+ return -EINVAL;
+ }
+
return 0;
}
#else
-static inline int rmi_driver_of_probe(struct device *dev,
- struct rmi_device_platform_data *pdata)
+static inline int rmi_driver_of_probe(struct rmi_device *rmi_dev,
+ struct rmi_device_platform_data *pdata)
{
return -ENODEV;
}
@@ -1158,7 +1204,7 @@ static int rmi_driver_probe(struct device *dev)
pdata = rmi_get_platform_data(rmi_dev);
if (rmi_dev->xport->dev->of_node) {
- retval = rmi_driver_of_probe(rmi_dev->xport->dev, pdata);
+ retval = rmi_driver_of_probe(rmi_dev, pdata);
if (retval)
return retval;
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index cb1cacd013a3f39db96935f705f18018bf15adff..3b87d177db59591691a56ce7ac03dd2e8671421d 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -31,6 +31,8 @@
#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
+#define RMI_OF_PDT_DESC_CELLS 2
+
#define PDT_START_SCAN_LOCATION 0x00e9
#define PDT_END_SCAN_LOCATION 0x0005
#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index ab7eea01ab4274bfc9efcefcdb0cced6ec34966f..4ba2cefac85583a4ba65c70dca418a2c7c65362a 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -214,6 +214,9 @@ struct rmi_device_platform_data {
int reset_delay_ms;
int irq;
+ unsigned int pdt_fallback_size;
+ const u8 *pdt_fallback_desc;
+
struct rmi_device_platform_data_spi spi_data;
/* function handler pdata */
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 3/7] Input: synaptics-rmi4 - f12: use hardcoded values for aftermarket touch ICs
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Kaustabh Chakraborty <kauschluss@disroot.org>
Some replacement displays include third-party touch ICs which are
devoid of register descriptors. Create a fake data register descriptor
for such ICs and provide hardcoded default values.
It isn't possible to reliably determine if the touch IC is original or
not, so these fallback values are offered as an alternative to the error
path when register descriptors aren't available.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[changes for readability / codeflow, checkpatch fixes]
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_f12.c | 117 +++++++++++++++++++++++++++++++++----------
1 file changed, 91 insertions(+), 26 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c
index 8246fe77114bbd8b795ba35d5a37ede8727fc7cb..1a103cc5f2235a6eafd7a3f5b89cbfc9e53203d2 100644
--- a/drivers/input/rmi4/rmi_f12.c
+++ b/drivers/input/rmi4/rmi_f12.c
@@ -218,6 +218,41 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size)
rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i);
}
+static void rmi_f12_set_hardcoded_desc(struct rmi_function *fn, struct f12_data *f12)
+{
+ struct rmi_2d_sensor *sensor = &f12->sensor;
+ struct rmi_register_desc_item *reg_desc;
+
+ /* We have no f12->data_reg_desc, so the pkt_size is 0, override it with
+ * a somewhat sensible default (this corresponds to 10 fingers).
+ */
+ sensor->pkt_size = 88;
+
+ /*
+ * There are no register descriptors to get these values from.
+ * We set them to high values to either be overwritten by the clip
+ * properties from devicetree, or to just not get in the way.
+ */
+ sensor->max_x = 65535;
+ sensor->max_y = 65535;
+
+ /*
+ * Create the Data1 register descriptor so that touch events
+ * can work properly.
+ */
+ reg_desc = devm_kcalloc(&fn->dev, 1,
+ sizeof(struct rmi_register_desc_item), GFP_KERNEL);
+ reg_desc->reg = 1;
+ reg_desc->reg_size = 80;
+ reg_desc->num_subpackets = 10;
+
+ f12->data1 = reg_desc;
+ f12->data1_offset = 0;
+ sensor->nbr_fingers = reg_desc->num_subpackets;
+ sensor->report_abs = 1;
+ sensor->attn_size += reg_desc->reg_size;
+}
+
static irqreturn_t rmi_f12_attention(int irq, void *ctx)
{
int retval;
@@ -338,6 +373,40 @@ static int rmi_f12_config(struct rmi_function *fn)
return 0;
}
+static int rmi_f12_sensor_init(struct rmi_function *fn, struct f12_data *f12)
+{
+ struct rmi_2d_sensor *sensor = &f12->sensor;
+
+ sensor->fn = fn;
+ f12->data_addr = fn->fd.data_base_addr;
+
+ /* On quirky devices that don't have a data_reg_desc we hardcode the packet
+ * in rmi_f12_set_hardcoded_desc(). Make sure not to set it to 0 here.
+ */
+ if (!sensor->pkt_size)
+ sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc);
+
+ sensor->axis_align =
+ f12->sensor_pdata.axis_align;
+
+ sensor->x_mm = f12->sensor_pdata.x_mm;
+ sensor->y_mm = f12->sensor_pdata.y_mm;
+ sensor->dribble = f12->sensor_pdata.dribble;
+
+ if (sensor->sensor_type == rmi_sensor_default)
+ sensor->sensor_type =
+ f12->sensor_pdata.sensor_type;
+
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__,
+ sensor->pkt_size);
+
+ sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int rmi_f12_probe(struct rmi_function *fn)
{
struct f12_data *f12;
@@ -351,6 +420,7 @@ static int rmi_f12_probe(struct rmi_function *fn)
struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
u16 data_offset = 0;
int mask_size;
+ bool hardcoded_desc_quirk = false;
rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__);
@@ -365,9 +435,9 @@ static int rmi_f12_probe(struct rmi_function *fn)
++query_addr;
if (!(buf & BIT(0))) {
- dev_err(&fn->dev,
- "Behavior of F12 without register descriptors is undefined.\n");
- return -ENODEV;
+ rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+ "No register descriptors defined for F12, using fallback\n");
+ hardcoded_desc_quirk = true;
}
f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data) + mask_size * 2,
@@ -375,6 +445,8 @@ static int rmi_f12_probe(struct rmi_function *fn)
if (!f12)
return -ENOMEM;
+ dev_set_drvdata(&fn->dev, f12);
+
f12->abs_mask = (unsigned long *)((char *)f12
+ sizeof(struct f12_data));
f12->rel_mask = (unsigned long *)((char *)f12
@@ -393,6 +465,18 @@ static int rmi_f12_probe(struct rmi_function *fn)
f12->sensor_pdata = pdata->sensor_pdata;
}
+ sensor = &f12->sensor;
+
+ if (hardcoded_desc_quirk) {
+ rmi_f12_set_hardcoded_desc(fn, f12);
+
+ ret = rmi_f12_sensor_init(fn, f12);
+ if (ret)
+ return ret;
+
+ goto skip_register_desc;
+ }
+
ret = rmi_read_register_desc(rmi_dev, query_addr,
&f12->query_reg_desc);
if (ret) {
@@ -423,29 +507,9 @@ static int rmi_f12_probe(struct rmi_function *fn)
}
query_addr += 3;
- sensor = &f12->sensor;
- sensor->fn = fn;
- f12->data_addr = fn->fd.data_base_addr;
- sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc);
-
- sensor->axis_align =
- f12->sensor_pdata.axis_align;
-
- sensor->x_mm = f12->sensor_pdata.x_mm;
- sensor->y_mm = f12->sensor_pdata.y_mm;
- sensor->dribble = f12->sensor_pdata.dribble;
-
- if (sensor->sensor_type == rmi_sensor_default)
- sensor->sensor_type =
- f12->sensor_pdata.sensor_type;
-
- rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__,
- sensor->pkt_size);
- sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL);
- if (!sensor->data_pkt)
- return -ENOMEM;
-
- dev_set_drvdata(&fn->dev, f12);
+ ret = rmi_f12_sensor_init(fn, f12);
+ if (ret)
+ return ret;
ret = rmi_f12_read_sensor_tuning(f12);
if (ret)
@@ -543,6 +607,7 @@ static int rmi_f12_probe(struct rmi_function *fn)
data_offset += item->reg_size;
}
+skip_register_desc:
/* allocate the in-kernel tracking buffers */
sensor->tracking_pos = devm_kcalloc(&fn->dev,
sensor->nbr_fingers, sizeof(struct input_mt_pos),
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 0/7] Input: synaptics-rmi4 - add quirks for third party touchscreen controllers
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, Krzysztof Kozlowski,
David Heidelberg, Casey Connolly
With the growing popularity of running upstream Linux on mobile devices,
we're beginning to run into more and more edgecases. The OnePlus 6 is a
fairly well supported 2018 era smartphone, selling over a million units
in it's first 22 days. With this level of popularity, it's almost
inevitable that we get third party replacement displays, and as a
result, replacement touchscreen controllers.
The OnePlus 6 shipped with an extremely usecase specific touchscreen
driver, it implemented only the bare minimum parts of the highly generic
rmi4 protocol, instead hardcoding most of the register addresses.
As a result, the third party touchscreen controllers that are often
found in replacement screens, implement only the registers that the
downstream driver reads from. They additionally have other restrictions
such as heavy penalties on unaligned reads.
This series attempts to implement the necessary workaround to support
some of these chips with the rmi4 driver. Although it's worth noting
that at the time of writing there are other unofficial controllers in
the wild that don't work even with these patches.
We have been shipping these patches in postmarketOS for the last several
years, and they are known to not cause any regressions on the OnePlus
6/6T (with the official Synaptics controller), however I don't own any
other rmi4 hardware to further validate this.
---
Changes in v5:
- Removed -i2c suffix from rmi4-s3706b-i2c (Krzysztof).
- Link to v4: https://lore.kernel.org/r/20250402-synaptics-rmi4-v4-0-1bb95959e564@ixit.cz
Changes in v4:
- Replaced patch "dt-bindings: input: syna,rmi4: document syna,pdt-fallback-desc"
with patch documenting specific touchscreen model used in OnePlus 6 and 6T.
- Fixed zero electrode return code (Dmitry).
- Switched the duplicate detection algo to bitmap (Dmitry).
- Optimized rmi_device_platform_data struct to avoid unnecessary
padding.
- Changed fallback_size from int to unsigned int.
- Changed SoB from nickname and old address (methanal <baclofen@tuta.io>) to
Kaustabh Chakraborty <kauschluss@disroot.org>.
Verified ownership through the sdm845 chatroom on Matrix.
- Link to v3: https://lore.kernel.org/r/20250308-synaptics-rmi4-v3-0-215d3e7289a2@ixit.cz
Changes in v3:
- reworded dt-bindings property description
- fixed the rmi_driver_of_probe definition for non device-tree builds.
- fixed some indentation issues reported by checkpatch
- change rmi_pdt_entry_is_valid() variable to unsigned
- Link to v2: https://lore.kernel.org/all/20230929-caleb-rmi4-quirks-v2-0-b227ac498d88@linaro.org
Changes in v2:
- Improve dt-bindings patch (thanks Rob)
- Add missing cast in patch 5 to fix the pointer arithmetic
- Link to v1: https://lore.kernel.org/r/20230929-caleb-rmi4-quirks-v1-0-cc3c703f022d@linaro.org
---
Caleb Connolly (1):
Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
David Heidelberg (1):
dt-bindings: input: syna,rmi4: Document syna,rmi4-s3706b
Kaustabh Chakraborty (5):
Input: synaptics-rmi4 - f12: use hardcoded values for aftermarket touch ICs
Input: synaptics-rmi4 - f55: handle zero electrode count
Input: synaptics-rmi4 - don't do unaligned reads in IRQ context
Input: synaptics-rmi4 - read product ID on aftermarket touch ICs
Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes
.../devicetree/bindings/input/syna,rmi4.yaml | 11 +-
drivers/input/rmi4/rmi_driver.c | 124 +++++++++++++++++----
drivers/input/rmi4/rmi_driver.h | 10 ++
drivers/input/rmi4/rmi_f01.c | 14 +++
drivers/input/rmi4/rmi_f12.c | 117 ++++++++++++++-----
drivers/input/rmi4/rmi_f55.c | 5 +
include/linux/rmi.h | 3 +
7 files changed, 234 insertions(+), 50 deletions(-)
---
base-commit: f0a16f5363325cc8d9382471cdc7b654c53254c9
change-id: 20250308-synaptics-rmi4-c832b2f73ceb
Best regards,
--
David Heidelberg <david@ixit.cz>
^ permalink raw reply
* [PATCH RESEND v5 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, Casey Connolly,
David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: Casey Connolly <casey.connolly@linaro.org>
Some third party rmi4-compatible ICs don't expose their PDT entries
very well. Add a few checks to skip duplicate entries as well as entries
for unsupported functions.
This is required to support some phones with third party displays.
Validated on a stock OnePlus 6T (original parts):
manufacturer: Synaptics, product: S3706B, fw id: 2852315
Co-developed-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
drivers/input/rmi4/rmi_driver.c | 42 +++++++++++++++++++++++++++++++++++------
drivers/input/rmi4/rmi_driver.h | 8 ++++++++
2 files changed, 44 insertions(+), 6 deletions(-)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 2168b6cd7167334d44553c9c566f870a4e034179..0f6dbe586c652d641178f43ec27df3613126ca66 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -493,12 +493,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
fd->function_version = pdt->function_version;
}
+static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
+ struct pdt_scan_state *state, u8 fn)
+{
+ switch (fn) {
+ case 0x01:
+ case 0x03:
+ case 0x11:
+ case 0x12:
+ case 0x30:
+ case 0x34:
+ case 0x3a:
+ case 0x54:
+ case 0x55:
+ if (state->pdts[fn] == true)
+ return false;
+ break;
+ default:
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
+ "PDT has unknown function number %#02x\n", fn);
+ return false;
+ }
+
+ state->pdts[fn] = true;
+ state->pdt_count++;
+ return true;
+}
+
#define RMI_SCAN_CONTINUE 0
#define RMI_SCAN_DONE 1
static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
int page,
- int *empty_pages,
+ struct pdt_scan_state *state,
void *ctx,
int (*callback)(struct rmi_device *rmi_dev,
void *ctx,
@@ -521,6 +548,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
if (RMI4_END_OF_PDT(pdt_entry.function_number))
break;
+ if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
+ continue;
+
retval = callback(rmi_dev, ctx, &pdt_entry);
if (retval != RMI_SCAN_CONTINUE)
return retval;
@@ -531,11 +561,11 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
* or more is found, stop scanning.
*/
if (addr == pdt_start)
- ++*empty_pages;
+ ++state->empty_pages;
else
- *empty_pages = 0;
+ state->empty_pages = 0;
- return (data->bootloader_mode || *empty_pages >= 2) ?
+ return (data->bootloader_mode || state->empty_pages >= 2) ?
RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
}
@@ -544,11 +574,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
void *ctx, const struct pdt_entry *entry))
{
int page;
- int empty_pages = 0;
+ struct pdt_scan_state state = {0, 0, {0}};
int retval = RMI_SCAN_DONE;
for (page = 0; page <= RMI4_MAX_PAGE; page++) {
- retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
+ retval = rmi_scan_pdt_page(rmi_dev, page, &state,
ctx, callback);
if (retval != RMI_SCAN_CONTINUE)
break;
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 3bfe9013043ef3dff46249095a5b3116c8f7d9a6..cb1cacd013a3f39db96935f705f18018bf15adff 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -46,6 +46,14 @@ struct pdt_entry {
u8 function_number;
};
+#define RMI_PDT_MAX 0x55
+
+struct pdt_scan_state {
+ u8 empty_pages;
+ u8 pdt_count;
+ bool pdts[RMI_PDT_MAX];
+};
+
#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE)
#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE)
--
2.50.1
^ permalink raw reply related
* [PATCH RESEND v5 1/7] dt-bindings: input: syna,rmi4: Document syna,rmi4-s3706b
From: David Heidelberg via B4 Relay @ 2025-07-31 21:06 UTC (permalink / raw)
To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
Matthias Schiffer, Vincent Huang
Cc: linux-input, devicetree, linux-kernel, phone-devel,
~postmarketos/upstreaming, Casey Connolly, Krzysztof Kozlowski,
David Heidelberg
In-Reply-To: <20250731-synaptics-rmi4-v5-0-cd0d87d34afa@ixit.cz>
From: David Heidelberg <david@ixit.cz>
Mostly irrelevant for authentic Synaptics touchscreens, but very important
for applying workarounds to cheap TS knockoffs.
These knockoffs work well with the downstream driver, and since the user
has no way to distinguish them, later in this patch set, we introduce
workarounds to ensure they function as well as possible.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
Documentation/devicetree/bindings/input/syna,rmi4.yaml | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/syna,rmi4.yaml b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
index b522c8d3ce0db719ff379f2fefbdca79e73d027c..d5d0225e3826fa5a0559cbd99115c505ab113aa0 100644
--- a/Documentation/devicetree/bindings/input/syna,rmi4.yaml
+++ b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
@@ -18,9 +18,14 @@ description: |
properties:
compatible:
- enum:
- - syna,rmi4-i2c
- - syna,rmi4-spi
+ oneOf:
+ - enum:
+ - syna,rmi4-i2c
+ - syna,rmi4-spi
+ - items:
+ - enum:
+ - syna,rmi4-s3706b # OnePlus 6/6T
+ - const: syna,rmi4-i2c
reg:
maxItems: 1
--
2.50.1
^ permalink raw reply related
* Re: [PATCH v9 2/6] mfd: pf1550: add core driver
From: Samuel Kayode @ 2025-07-31 20:05 UTC (permalink / raw)
To: Lee Jones
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Liam Girdwood,
Mark Brown, Dmitry Torokhov, Sebastian Reichel, Frank Li, imx,
devicetree, linux-kernel, linux-input, linux-pm, Abel Vesa,
Abel Vesa, Robin Gong, Robin Gong, Enric Balletbo i Serra,
Sean Nyekjaer, Christophe JAILLET
In-Reply-To: <20250731091525.GA1049189@google.com>
On Thu, Jul 31, 2025 at 10:15:25AM +0100, Lee Jones wrote:
> On Wed, 16 Jul 2025, Samuel Kayode via B4 Relay wrote:
>
> > From: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> >
> > Add the core driver for pf1550 PMIC. There are 3 subdevices for which the
> > drivers will be added in subsequent patches.
> >
> > Reviewed-by: Frank Li <Frank.Li@nxp.com>
> > Signed-off-by: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> > ---
> > v9:
> > - Requested by Sean:
> > - Add support for SW1 DVS enable/disable
> > - Use consistent whitespace
> > - Adjust commenting and log messages of the read_otp function
> > v8:
> > - Address Lee's feedback:
> > - Drop `mfd` from driver description and comments
> > - Add module name in Kconfig
> > - Fix license commenting
> > - Drop filenames from comments
> > - Drop unnecessary tabbing
> > - Alphabetical ordering of includes
> > - Remove magic numbers
> > - Add comments for pf1550_read_otp function
> > - Fix log error message in pf1550_read_otp
> > - Drop pf1550_add_child_device function
> > - Start comments with upper case
> > - Rename pf1550_dev to pf1550_ddata
> > - Drop i2c member in struct pf1550_ddata/pf1550_dev
> > - Use more helpful log message when device id not recognized
> > - Fix dvs_enb: when bit is set the DVS is disabled and when bit is clear the
> > DVS is enabled
> > - Verified the PM_OPS suspend and resume do act as expected
> > v7:
> > - Address Frank's feedback:
> > - Ensure reverse christmas tree order for local variable definitions
> > - Drop unnecessary driver data definition in id table
> > v6:
> > - Address Frank's feedback:
> > - Ensure lowercase when defining register addresses
> > - Use GENMASK macro for masking
> > - Hardcode IRQ flags in pf1550_add_child_device
> > - Add dvs_enb variable for SW2 regulator
> > - Drop chip type variable
> > v5:
> > - Use top level interrupt to manage interrupts for the sub-drivers as
> > recommended by Mark Brown. The regmap_irq_sub_irq_map would have been used
> > if not for the irregular charger irq address. For all children, the mask
> > register is directly after the irq register (i.e., 0x08, 0x09) except
> > for the charger: 0x80, 0x82. Meaning .mask_base would be applicable
> > for all but the charger
> > - Fix bad offset for temperature interrupts of regulator
> > v4:
> > - Use struct resource to define irq so platform_get_irq can be used in
> > children as suggested by Dmitry
> > - Let mfd_add_devices create the mappings for the interrupts
> > - ack_base and init_ack_masked defined for charger and regulator irq
> > chips
> > - No need to define driver_data in table id
> > v3:
> > - Address Dmitry's feedback:
> > - Place Table IDs next to each other
> > - Drop of_match_ptr
> > - Replace dev_err with dev_err_probe in probe method
> > - Drop useless log in probe
> > - Map all irqs instead of doing it in the sub-devices as recommended by
> > Dmitry.
> > v2:
> > - Address feedback from Enric Balletbo Serra
> > ---
> > drivers/mfd/Kconfig | 16 ++
> > drivers/mfd/Makefile | 2 +
> > drivers/mfd/pf1550.c | 374 +++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/mfd/pf1550.h | 271 ++++++++++++++++++++++++++++++++
> > 4 files changed, 663 insertions(+)
>
> Mostly pretty good. A few nits left.
>
> > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> > index 96992af22565205716d72db0494c7bf2567b045e..4ac91a556713ba7a867c1d4430c6c0d8bb05d0d7 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -558,6 +558,22 @@ config MFD_MX25_TSADC
> > i.MX25 processors. They consist of a conversion queue for general
> > purpose ADC and a queue for Touchscreens.
> >
> > +config MFD_PF1550
> > + tristate "NXP PF1550 PMIC Support"
> > + depends on I2C=y && OF
> > + select MFD_CORE
> > + select REGMAP_I2C
> > + select REGMAP_IRQ
> > + help
> > + Say yes here to add support for NXP PF1550. This is a companion Power
> > + Management IC with regulators, onkey, and charger control on chip.
> > + This driver provides common support for accessing the device;
> > + additional drivers must be enabled in order to use the functionality
> > + of the device.
> > +
> > + This driver can also be built as a module and if so will be called
> > + pf1550.
> > +
> > config MFD_HI6421_PMIC
> > tristate "HiSilicon Hi6421 PMU/Codec IC"
> > depends on OF
> > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> > index 5e5cc279af6036a6b3ea1f1f0feeddf45b85f15c..7391d1b81d1ee499507b4ac24ff00eb2e344d60b 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -120,6 +120,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
> > obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
> > obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
> >
> > +obj-$(CONFIG_MFD_PF1550) += pf1550.o
> > +
> > obj-$(CONFIG_MFD_CORE) += mfd-core.o
> >
> > ocelot-soc-objs := ocelot-core.o ocelot-spi.o
> > diff --git a/drivers/mfd/pf1550.c b/drivers/mfd/pf1550.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..fd31eff0e5a88c973f7db14b068f1c8e92991d4b
> > --- /dev/null
> > +++ b/drivers/mfd/pf1550.c
> > @@ -0,0 +1,374 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * core driver for the PF1550
>
> Nit: Sentences start with uppercase chars.
>
Will update.
> > + *
> > + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> > + * Robin Gong <yibin.gong@freescale.com>
> > + *
> > + * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
> > + * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/pf1550.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/regmap.h>
> > +
> > +static const struct regmap_config pf1550_regmap_config = {
> > + .reg_bits = 8,
> > + .val_bits = 8,
> > + .max_register = PF1550_PMIC_REG_END,
> > +};
> > +
> > +static const struct regmap_irq pf1550_irqs[] = {
> > + REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG),
> > + REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR),
> > + REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY),
> > +};
> > +
> > +static const struct regmap_irq_chip pf1550_irq_chip = {
> > + .name = "pf1550",
> > + .status_base = PF1550_PMIC_REG_INT_CATEGORY,
> > + .init_ack_masked = 1,
> > + .num_regs = 1,
> > + .irqs = pf1550_irqs,
> > + .num_irqs = ARRAY_SIZE(pf1550_irqs),
> > +};
> > +
> > +static const struct regmap_irq pf1550_regulator_irqs[] = {
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110),
> > + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125),
> > +};
> > +
> > +static const struct regmap_irq_chip pf1550_regulator_irq_chip = {
> > + .name = "pf1550-regulator",
> > + .status_base = PF1550_PMIC_REG_SW_INT_STAT0,
> > + .ack_base = PF1550_PMIC_REG_SW_INT_STAT0,
> > + .mask_base = PF1550_PMIC_REG_SW_INT_MASK0,
> > + .use_ack = 1,
> > + .init_ack_masked = 1,
> > + .num_regs = 25,
> > + .irqs = pf1550_regulator_irqs,
> > + .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs),
> > +};
> > +
> > +static const struct resource regulator_resources[] = {
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110),
> > + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125),
> > +};
> > +
> > +static const struct regmap_irq pf1550_onkey_irqs[] = {
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI),
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI),
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI),
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI),
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI),
> > + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI),
> > +};
> > +
> > +static const struct regmap_irq_chip pf1550_onkey_irq_chip = {
> > + .name = "pf1550-onkey",
> > + .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
> > + .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
> > + .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0,
> > + .use_ack = 1,
> > + .init_ack_masked = 1,
> > + .num_regs = 1,
> > + .irqs = pf1550_onkey_irqs,
> > + .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs),
> > +};
> > +
> > +static const struct resource onkey_resources[] = {
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI),
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI),
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI),
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI),
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI),
> > + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI),
> > +};
> > +
> > +static const struct regmap_irq pf1550_charger_irqs[] = {
> > + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI),
> > + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI),
> > + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI),
> > + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI),
> > + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI),
> > +};
> > +
> > +static const struct regmap_irq_chip pf1550_charger_irq_chip = {
> > + .name = "pf1550-charger",
> > + .status_base = PF1550_CHARG_REG_CHG_INT,
> > + .ack_base = PF1550_CHARG_REG_CHG_INT,
> > + .mask_base = PF1550_CHARG_REG_CHG_INT_MASK,
> > + .use_ack = 1,
> > + .init_ack_masked = 1,
> > + .num_regs = 1,
> > + .irqs = pf1550_charger_irqs,
> > + .num_irqs = ARRAY_SIZE(pf1550_charger_irqs),
> > +};
> > +
> > +static const struct resource charger_resources[] = {
> > + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI),
> > + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI),
> > + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI),
> > + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI),
> > + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI),
> > +};
> > +
> > +static const struct mfd_cell pf1550_regulator_cell = {
> > + .name = "pf1550-regulator",
> > + .num_resources = ARRAY_SIZE(regulator_resources),
> > + .resources = regulator_resources,
> > +};
> > +
> > +static const struct mfd_cell pf1550_onkey_cell = {
> > + .name = "pf1550-onkey",
> > + .num_resources = ARRAY_SIZE(onkey_resources),
> > + .resources = onkey_resources,
> > +};
> > +
> > +static const struct mfd_cell pf1550_charger_cell = {
> > + .name = "pf1550-charger",
> > + .num_resources = ARRAY_SIZE(charger_resources),
> > + .resources = charger_resources,
> > +};
>
> Is there a technical reason why these are all separated out? It would
> normally be preferable to put these in an array and register them all
> with a single all to mfd_add_devices();
>
When registering the devices with devm_mfd_add_devices, I provide the domain of
the interrupt controller, so the mapping to the interrupts are done here instead
of in the sub-drivers as suggested by Dmitry Torokhov (v3).
Due to irregularity in the addresses of the registers serving as interrupt
sources, multiple IRQ controllers(regmap_irq_chip) are used. So, there are
multiple domains that need to be passed to the devm_mfd_add_devices to do these
mappings.
> > +/*
> > + * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a
> > + * configuration of the PMIC in a One-Time Programmable (OTP) memory.
> > + * This memory is accessed indirectly by writing valid keys to specific
> > + * registers of the PMIC. To read the OTP memory after writing the valid keys,
> > + * the OTP register address to be read is written to pf1550 register 0xc4 and
> > + * its value read from pf1550 register 0xc5.
> > + */
> > +static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index,
> > + unsigned int *val)
> > +{
> > + int ret = 0;
> > +
> > + ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY,
> > + PF1550_OTP_PMIC_KEY);
> > + if (ret)
> > + goto read_err;
>
> '\n'
>
> > + ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2,
> > + PF1550_OTP_CHGR_KEY);
> > + if (ret)
> > + goto read_err;
>
> '\n'
>
> > + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3,
> > + PF1550_OTP_TEST_KEY);
> > + if (ret)
> > + goto read_err;
>
> '\n'
>
> > + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index);
> > + if (ret)
> > + goto read_err;
>
> '\n'
>
> > + ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val);
> > + if (ret)
> > + goto read_err;
> > +
> > + return 0;
> > +
> > +read_err:
> > + return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n",
> > + index);
>
> You can use 100-chars everywhere in here to prevent these kinds of wraps.
>
Noted.
> > +}
> > +
> > +static int pf1550_i2c_probe(struct i2c_client *i2c)
> > +{
> > + const struct mfd_cell *regulator = &pf1550_regulator_cell;
> > + const struct mfd_cell *charger = &pf1550_charger_cell;
> > + const struct mfd_cell *onkey = &pf1550_onkey_cell;
> > + unsigned int reg_data = 0, otp_data = 0;
> > + struct pf1550_ddata *pf1550;
> > + struct irq_domain *domain;
> > + int irq, ret = 0;
> > +
> > + pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL);
> > + if (!pf1550)
> > + return -ENOMEM;
> > +
> > + i2c_set_clientdata(i2c, pf1550);
> > + pf1550->dev = &i2c->dev;
> > + pf1550->irq = i2c->irq;
> > +
> > + pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config);
> > + if (IS_ERR(pf1550->regmap))
> > + return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap),
> > + "failed to allocate register map\n");
> > +
> > + ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, ®_data);
> > + if (ret < 0)
> > + return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n");
> > + if (reg_data != PF1550_DEVICE_ID)
> > + return dev_err_probe(pf1550->dev, -ENODEV,
> > + "invalid device ID: 0x%02x\n", reg_data);
> > +
> > + /* Regulator DVS for SW2 */
> > + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data);
> > + if (ret)
> > + return ret;
> > +
> > + /* When clear, DVS should be enabled */
> > + if (!(otp_data & OTP_SW2_DVS_ENB))
> > + pf1550->dvs2_enb = true;
> > +
> > + /* Regulator DVS for SW1 */
> > + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data);
> > + if (ret)
> > + return ret;
> > +
> > + if (!(otp_data & OTP_SW1_DVS_ENB))
> > + pf1550->dvs1_enb = true;
> > +
> > + /* Add top level interrupts */
> > + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq,
> > + IRQF_ONESHOT | IRQF_SHARED |
> > + IRQF_TRIGGER_FALLING,
> > + 0, &pf1550_irq_chip,
> > + &pf1550->irq_data);
> > + if (ret)
> > + return ret;
> > +
> > + /* Add regulator */
> > + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR);
> > + if (irq < 0)
> > + return dev_err_probe(pf1550->dev, irq,
> > + "Failed to get parent vIRQ(%d) for chip %s\n",
> > + PF1550_IRQ_REGULATOR, pf1550_irq_chip.name);
> > +
> > + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> > + IRQF_ONESHOT | IRQF_SHARED |
> > + IRQF_TRIGGER_FALLING, 0,
> > + &pf1550_regulator_irq_chip,
> > + &pf1550->irq_data_regulator);
> > + if (ret)
> > + return dev_err_probe(pf1550->dev, ret,
> > + "Failed to add %s IRQ chip\n",
> > + pf1550_regulator_irq_chip.name);
> > +
> > + domain = regmap_irq_get_domain(pf1550->irq_data_regulator);
> > +
> > + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator,
> > + 1, NULL, 0, domain);
> > + if (ret)
> > + return ret;
> > +
> > + /* Add onkey */
> > + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY);
> > + if (irq < 0)
> > + return dev_err_probe(pf1550->dev, irq,
> > + "Failed to get parent vIRQ(%d) for chip %s\n",
> > + PF1550_IRQ_ONKEY, pf1550_irq_chip.name);
> > +
> > + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> > + IRQF_ONESHOT | IRQF_SHARED |
> > + IRQF_TRIGGER_FALLING, 0,
> > + &pf1550_onkey_irq_chip,
> > + &pf1550->irq_data_onkey);
> > + if (ret)
> > + return dev_err_probe(pf1550->dev, ret,
> > + "Failed to add %s IRQ chip\n",
> > + pf1550_onkey_irq_chip.name);
> > +
> > + domain = regmap_irq_get_domain(pf1550->irq_data_onkey);
> > +
> > + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1,
> > + NULL, 0, domain);
> > + if (ret)
> > + return ret;
> > +
> > + /* Add battery charger */
> > + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG);
> > + if (irq < 0)
> > + return dev_err_probe(pf1550->dev, irq,
> > + "Failed to get parent vIRQ(%d) for chip %s\n",
> > + PF1550_IRQ_CHG, pf1550_irq_chip.name);
> > +
> > + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> > + IRQF_ONESHOT | IRQF_SHARED |
> > + IRQF_TRIGGER_FALLING, 0,
> > + &pf1550_charger_irq_chip,
> > + &pf1550->irq_data_charger);
> > + if (ret)
> > + return dev_err_probe(pf1550->dev, ret,
> > + "Failed to add %s IRQ chip\n",
> > + pf1550_charger_irq_chip.name);
> > +
> > + domain = regmap_irq_get_domain(pf1550->irq_data_charger);
> > +
> > + return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger,
> > + 1, NULL, 0, domain);
> > +}
> > +
> > +static int pf1550_suspend(struct device *dev)
> > +{
> > + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
> > +
> > + if (device_may_wakeup(dev)) {
> > + enable_irq_wake(pf1550->irq);
> > + disable_irq(pf1550->irq);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int pf1550_resume(struct device *dev)
> > +{
> > + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
> > +
> > + if (device_may_wakeup(dev)) {
> > + disable_irq_wake(pf1550->irq);
> > + enable_irq(pf1550->irq);
> > + }
> > +
> > + return 0;
> > +}
> > +static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume);
> > +
> > +static const struct i2c_device_id pf1550_i2c_id[] = {
> > + { "pf1550" },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id);
> > +
> > +static const struct of_device_id pf1550_dt_match[] = {
> > + { .compatible = "nxp,pf1550" },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, pf1550_dt_match);
> > +
> > +static struct i2c_driver pf1550_i2c_driver = {
> > + .driver = {
> > + .name = "pf1550",
> > + .pm = pm_sleep_ptr(&pf1550_pm),
> > + .of_match_table = pf1550_dt_match,
> > + },
> > + .probe = pf1550_i2c_probe,
> > + .id_table = pf1550_i2c_id,
> > +};
> > +module_i2c_driver(pf1550_i2c_driver);
> > +
> > +MODULE_DESCRIPTION("NXP PF1550 core driver");
> > +MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/linux/mfd/pf1550.h b/include/linux/mfd/pf1550.h
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..6099fe670fab13deddbe9e0f834c9b3af5bf4f50
> > --- /dev/null
> > +++ b/include/linux/mfd/pf1550.h
> > @@ -0,0 +1,271 @@
> > +/* SPDX-License-Identifier: GPL-2.0
> > + *
> > + * declarations for the PF1550 PMIC
>
> As above.
>
Noted.
> > + *
> > + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> > + * Robin Gong <yibin.gong@freescale.com>
> > + *
> > + * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
> > + * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> > + */
> > +
> > +#ifndef __LINUX_MFD_PF1550_H
> > +#define __LINUX_MFD_PF1550_H
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/regmap.h>
> > +
> > +enum pf1550_pmic_reg {
> > + /* PMIC regulator part */
> > + PF1550_PMIC_REG_DEVICE_ID = 0x00,
> > + PF1550_PMIC_REG_OTP_FLAVOR = 0x01,
> > + PF1550_PMIC_REG_SILICON_REV = 0x02,
> > +
> > + PF1550_PMIC_REG_INT_CATEGORY = 0x06,
> > + PF1550_PMIC_REG_SW_INT_STAT0 = 0x08,
> > + PF1550_PMIC_REG_SW_INT_MASK0 = 0x09,
> > + PF1550_PMIC_REG_SW_INT_SENSE0 = 0x0a,
> > + PF1550_PMIC_REG_SW_INT_STAT1 = 0x0b,
> > + PF1550_PMIC_REG_SW_INT_MASK1 = 0x0c,
> > + PF1550_PMIC_REG_SW_INT_SENSE1 = 0x0d,
> > + PF1550_PMIC_REG_SW_INT_STAT2 = 0x0e,
> > + PF1550_PMIC_REG_SW_INT_MASK2 = 0x0f,
> > + PF1550_PMIC_REG_SW_INT_SENSE2 = 0x10,
> > + PF1550_PMIC_REG_LDO_INT_STAT0 = 0x18,
> > + PF1550_PMIC_REG_LDO_INT_MASK0 = 0x19,
> > + PF1550_PMIC_REG_LDO_INT_SENSE0 = 0x1a,
> > + PF1550_PMIC_REG_TEMP_INT_STAT0 = 0x20,
> > + PF1550_PMIC_REG_TEMP_INT_MASK0 = 0x21,
> > + PF1550_PMIC_REG_TEMP_INT_SENSE0 = 0x22,
> > + PF1550_PMIC_REG_ONKEY_INT_STAT0 = 0x24,
> > + PF1550_PMIC_REG_ONKEY_INT_MASK0 = 0x25,
> > + PF1550_PMIC_REG_ONKEY_INT_SENSE0 = 0x26,
> > + PF1550_PMIC_REG_MISC_INT_STAT0 = 0x28,
> > + PF1550_PMIC_REG_MISC_INT_MASK0 = 0x29,
> > + PF1550_PMIC_REG_MISC_INT_SENSE0 = 0x2a,
> > +
> > + PF1550_PMIC_REG_COINCELL_CONTROL = 0x30,
> > +
> > + PF1550_PMIC_REG_SW1_VOLT = 0x32,
> > + PF1550_PMIC_REG_SW1_STBY_VOLT = 0x33,
> > + PF1550_PMIC_REG_SW1_SLP_VOLT = 0x34,
> > + PF1550_PMIC_REG_SW1_CTRL = 0x35,
> > + PF1550_PMIC_REG_SW1_CTRL1 = 0x36,
> > + PF1550_PMIC_REG_SW2_VOLT = 0x38,
> > + PF1550_PMIC_REG_SW2_STBY_VOLT = 0x39,
> > + PF1550_PMIC_REG_SW2_SLP_VOLT = 0x3a,
> > + PF1550_PMIC_REG_SW2_CTRL = 0x3b,
> > + PF1550_PMIC_REG_SW2_CTRL1 = 0x3c,
> > + PF1550_PMIC_REG_SW3_VOLT = 0x3e,
> > + PF1550_PMIC_REG_SW3_STBY_VOLT = 0x3f,
> > + PF1550_PMIC_REG_SW3_SLP_VOLT = 0x40,
> > + PF1550_PMIC_REG_SW3_CTRL = 0x41,
> > + PF1550_PMIC_REG_SW3_CTRL1 = 0x42,
> > + PF1550_PMIC_REG_VSNVS_CTRL = 0x48,
> > + PF1550_PMIC_REG_VREFDDR_CTRL = 0x4a,
> > + PF1550_PMIC_REG_LDO1_VOLT = 0x4c,
> > + PF1550_PMIC_REG_LDO1_CTRL = 0x4d,
> > + PF1550_PMIC_REG_LDO2_VOLT = 0x4f,
> > + PF1550_PMIC_REG_LDO2_CTRL = 0x50,
> > + PF1550_PMIC_REG_LDO3_VOLT = 0x52,
> > + PF1550_PMIC_REG_LDO3_CTRL = 0x53,
> > + PF1550_PMIC_REG_PWRCTRL0 = 0x58,
> > + PF1550_PMIC_REG_PWRCTRL1 = 0x59,
> > + PF1550_PMIC_REG_PWRCTRL2 = 0x5a,
> > + PF1550_PMIC_REG_PWRCTRL3 = 0x5b,
> > + PF1550_PMIC_REG_SW1_PWRDN_SEQ = 0x5f,
> > + PF1550_PMIC_REG_SW2_PWRDN_SEQ = 0x60,
> > + PF1550_PMIC_REG_SW3_PWRDN_SEQ = 0x61,
> > + PF1550_PMIC_REG_LDO1_PWRDN_SEQ = 0x62,
> > + PF1550_PMIC_REG_LDO2_PWRDN_SEQ = 0x63,
> > + PF1550_PMIC_REG_LDO3_PWRDN_SEQ = 0x64,
> > + PF1550_PMIC_REG_VREFDDR_PWRDN_SEQ = 0x65,
> > +
> > + PF1550_PMIC_REG_STATE_INFO = 0x67,
> > + PF1550_PMIC_REG_I2C_ADDR = 0x68,
> > + PF1550_PMIC_REG_IO_DRV0 = 0x69,
> > + PF1550_PMIC_REG_IO_DRV1 = 0x6a,
> > + PF1550_PMIC_REG_RC_16MHZ = 0x6b,
> > + PF1550_PMIC_REG_KEY = 0x6f,
> > +
> > + /* Charger part */
> > + PF1550_CHARG_REG_CHG_INT = 0x80,
> > + PF1550_CHARG_REG_CHG_INT_MASK = 0x82,
> > + PF1550_CHARG_REG_CHG_INT_OK = 0x84,
> > + PF1550_CHARG_REG_VBUS_SNS = 0x86,
> > + PF1550_CHARG_REG_CHG_SNS = 0x87,
> > + PF1550_CHARG_REG_BATT_SNS = 0x88,
> > + PF1550_CHARG_REG_CHG_OPER = 0x89,
> > + PF1550_CHARG_REG_CHG_TMR = 0x8a,
> > + PF1550_CHARG_REG_CHG_EOC_CNFG = 0x8d,
> > + PF1550_CHARG_REG_CHG_CURR_CNFG = 0x8e,
> > + PF1550_CHARG_REG_BATT_REG = 0x8f,
> > + PF1550_CHARG_REG_BATFET_CNFG = 0x91,
> > + PF1550_CHARG_REG_THM_REG_CNFG = 0x92,
> > + PF1550_CHARG_REG_VBUS_INLIM_CNFG = 0x94,
> > + PF1550_CHARG_REG_VBUS_LIN_DPM = 0x95,
> > + PF1550_CHARG_REG_USB_PHY_LDO_CNFG = 0x96,
> > + PF1550_CHARG_REG_DBNC_DELAY_TIME = 0x98,
> > + PF1550_CHARG_REG_CHG_INT_CNFG = 0x99,
> > + PF1550_CHARG_REG_THM_ADJ_SETTING = 0x9a,
> > + PF1550_CHARG_REG_VBUS2SYS_CNFG = 0x9b,
> > + PF1550_CHARG_REG_LED_PWM = 0x9c,
> > + PF1550_CHARG_REG_FAULT_BATFET_CNFG = 0x9d,
> > + PF1550_CHARG_REG_LED_CNFG = 0x9e,
> > + PF1550_CHARG_REG_CHGR_KEY2 = 0x9f,
> > +
> > + PF1550_TEST_REG_FMRADDR = 0xc4,
> > + PF1550_TEST_REG_FMRDATA = 0xc5,
> > + PF1550_TEST_REG_KEY3 = 0xdf,
> > +
> > + PF1550_PMIC_REG_END = 0xff,
> > +};
> > +
> > +/* One-Time Programmable(OTP) memory */
> > +enum pf1550_otp_reg {
> > + PF1550_OTP_SW1_SW2 = 0x1e,
> > + PF1550_OTP_SW2_SW3 = 0x1f,
> > +};
> > +
> > +#define PF1550_DEVICE_ID 0x7c
> > +
> > +/* Keys for reading OTP */
> > +#define PF1550_OTP_PMIC_KEY 0x15
> > +#define PF1550_OTP_CHGR_KEY 0x50
> > +#define PF1550_OTP_TEST_KEY 0xab
> > +
> > +/* Supported charger modes */
> > +#define PF1550_CHG_BAT_OFF 1
> > +#define PF1550_CHG_BAT_ON 2
> > +
> > +#define PF1550_CHG_PRECHARGE 0
> > +#define PF1550_CHG_CONSTANT_CURRENT 1
> > +#define PF1550_CHG_CONSTANT_VOL 2
> > +#define PF1550_CHG_EOC 3
> > +#define PF1550_CHG_DONE 4
> > +#define PF1550_CHG_TIMER_FAULT 6
> > +#define PF1550_CHG_SUSPEND 7
> > +#define PF1550_CHG_OFF_INV 8
> > +#define PF1550_CHG_BAT_OVER 9
> > +#define PF1550_CHG_OFF_TEMP 10
> > +#define PF1550_CHG_LINEAR_ONLY 12
> > +#define PF1550_CHG_SNS_MASK 0xf
> > +#define PF1550_CHG_INT_MASK 0x51
> > +
> > +#define PF1550_BAT_NO_VBUS 0
> > +#define PF1550_BAT_LOW_THAN_PRECHARG 1
> > +#define PF1550_BAT_CHARG_FAIL 2
> > +#define PF1550_BAT_HIGH_THAN_PRECHARG 4
> > +#define PF1550_BAT_OVER_VOL 5
> > +#define PF1550_BAT_NO_DETECT 6
> > +#define PF1550_BAT_SNS_MASK 0x7
> > +
> > +#define PF1550_VBUS_UVLO BIT(2)
> > +#define PF1550_VBUS_IN2SYS BIT(3)
> > +#define PF1550_VBUS_OVLO BIT(4)
> > +#define PF1550_VBUS_VALID BIT(5)
> > +
> > +#define PF1550_CHARG_REG_BATT_REG_CHGCV_MASK 0x3f
> > +#define PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT 6
> > +#define PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK GENMASK(7, 6)
> > +#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT 2
> > +#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK GENMASK(3, 2)
> > +
> > +/* DVS ENABLE MASK */
> > +#define OTP_SW1_DVS_ENB BIT(1)
> > +#define OTP_SW2_DVS_ENB BIT(3)
> > +
> > +/* Top level interrupt masks */
> > +#define IRQ_REGULATOR (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(6))
> > +#define IRQ_ONKEY BIT(5)
> > +#define IRQ_CHG BIT(0)
> > +
> > +/* Regulator interrupt masks */
> > +#define PMIC_IRQ_SW1_LS BIT(0)
> > +#define PMIC_IRQ_SW2_LS BIT(1)
> > +#define PMIC_IRQ_SW3_LS BIT(2)
> > +#define PMIC_IRQ_SW1_HS BIT(0)
> > +#define PMIC_IRQ_SW2_HS BIT(1)
> > +#define PMIC_IRQ_SW3_HS BIT(2)
> > +#define PMIC_IRQ_LDO1_FAULT BIT(0)
> > +#define PMIC_IRQ_LDO2_FAULT BIT(1)
> > +#define PMIC_IRQ_LDO3_FAULT BIT(2)
> > +#define PMIC_IRQ_TEMP_110 BIT(0)
> > +#define PMIC_IRQ_TEMP_125 BIT(1)
> > +
> > +/* Onkey interrupt masks */
> > +#define ONKEY_IRQ_PUSHI BIT(0)
> > +#define ONKEY_IRQ_1SI BIT(1)
> > +#define ONKEY_IRQ_2SI BIT(2)
> > +#define ONKEY_IRQ_3SI BIT(3)
> > +#define ONKEY_IRQ_4SI BIT(4)
> > +#define ONKEY_IRQ_8SI BIT(5)
> > +
> > +/* Charger interrupt masks */
> > +#define CHARG_IRQ_BAT2SOCI BIT(1)
> > +#define CHARG_IRQ_BATI BIT(2)
> > +#define CHARG_IRQ_CHGI BIT(3)
> > +#define CHARG_IRQ_VBUSI BIT(5)
> > +#define CHARG_IRQ_DPMI BIT(6)
> > +#define CHARG_IRQ_THMI BIT(7)
> > +
> > +enum pf1550_irq {
> > + PF1550_IRQ_CHG,
> > + PF1550_IRQ_REGULATOR,
> > + PF1550_IRQ_ONKEY,
> > +};
> > +
> > +enum pf1550_pmic_irq {
> > + PF1550_PMIC_IRQ_SW1_LS,
> > + PF1550_PMIC_IRQ_SW2_LS,
> > + PF1550_PMIC_IRQ_SW3_LS,
> > + PF1550_PMIC_IRQ_SW1_HS,
> > + PF1550_PMIC_IRQ_SW2_HS,
> > + PF1550_PMIC_IRQ_SW3_HS,
> > + PF1550_PMIC_IRQ_LDO1_FAULT,
> > + PF1550_PMIC_IRQ_LDO2_FAULT,
> > + PF1550_PMIC_IRQ_LDO3_FAULT,
> > + PF1550_PMIC_IRQ_TEMP_110,
> > + PF1550_PMIC_IRQ_TEMP_125,
> > +};
> > +
> > +enum pf1550_onkey_irq {
> > + PF1550_ONKEY_IRQ_PUSHI,
> > + PF1550_ONKEY_IRQ_1SI,
> > + PF1550_ONKEY_IRQ_2SI,
> > + PF1550_ONKEY_IRQ_3SI,
> > + PF1550_ONKEY_IRQ_4SI,
> > + PF1550_ONKEY_IRQ_8SI,
> > +};
> > +
> > +enum pf1550_charg_irq {
> > + PF1550_CHARG_IRQ_BAT2SOCI,
> > + PF1550_CHARG_IRQ_BATI,
> > + PF1550_CHARG_IRQ_CHGI,
> > + PF1550_CHARG_IRQ_VBUSI,
> > + PF1550_CHARG_IRQ_THMI,
> > +};
> > +
> > +enum pf1550_regulators {
> > + PF1550_SW1,
> > + PF1550_SW2,
> > + PF1550_SW3,
> > + PF1550_VREFDDR,
> > + PF1550_LDO1,
> > + PF1550_LDO2,
> > + PF1550_LDO3,
> > +};
> > +
> > +struct pf1550_ddata {
> > + bool dvs1_enb;
> > + bool dvs2_enb;
>
> Nit: I don't see the benefit of shortening 'enable' here.
>
> Place the small variables at the bottom.
>
Will update.
> > + struct device *dev;
> > + struct regmap *regmap;
> > + struct regmap_irq_chip_data *irq_data_regulator;
> > + struct regmap_irq_chip_data *irq_data_onkey;
> > + struct regmap_irq_chip_data *irq_data_charger;
> > + struct regmap_irq_chip_data *irq_data;
> > + int irq;
> > +};
> > +
> > +#endif /* __LINUX_MFD_PF1550_H */
Thanks,
Sam
^ permalink raw reply
* Re: [PATCH 1/2] drm/panel: Allow powering on panel follower after panel is enabled
From: Doug Anderson @ 2025-07-31 16:38 UTC (permalink / raw)
To: Jani Nikula
Cc: Pin-Yen Lin, Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jiri Kosina, Benjamin Tissoires, linux-kernel, linux-input,
dri-devel, Chen-Yu Tsai, Pin-Yen Lin
In-Reply-To: <1519a7c3872a59b7b5f12e99529237035499bc26@intel.com>
Hi,
On Thu, Jul 31, 2025 at 3:31 AM Jani Nikula <jani.nikula@linux.intel.com> wrote:
>
> On Thu, 31 Jul 2025, Pin-Yen Lin <treapking@chromium.org> wrote:
> > Some touch controllers have to be powered on after the panel's backlight
> > is enabled. To support these controllers, introduce after_panel_enabled
> > flag to the panel follower and power on the device after the panel and
> > its backlight are enabled.
>
> I think it's *very* confusing and error prone to call follower hooks at
> different places depending on a flag. The hook names and documentation
> say *when* they get called, and that should not change.
>
> I think the right approach would be to add .panel_enabled and
> .panel_disabling hooks to struct drm_panel_follower_funcs, and have them
> called after panel (and backlight) have been enabled and before panel
> (and backlight) are disabled, respectively.
>
> In i2c-hid-core.c, you'd then have two copies of struct
> drm_panel_follower_funcs, and use one or the other depending on the
> quirk. You can even reuse the functions.
>
> I think overall it'll be be more consistent, more flexible, and probably
> fewer lines of code too.
Yes, exactly what Jani said. We've wanted to do this before, but I
just never got around to it. There's even a (public) bug for it in the
Google bug tracker and I've just assigned it to you. :-P
https://issuetracker.google.com/305780363
-Doug
^ permalink raw reply
* Re: [PATCH 1/2] drm/panel: Allow powering on panel follower after panel is enabled
From: Jani Nikula @ 2025-07-31 10:30 UTC (permalink / raw)
To: Pin-Yen Lin, Neil Armstrong, Jessica Zhang, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Jiri Kosina, Benjamin Tissoires
Cc: linux-kernel, linux-input, Douglas Anderson, dri-devel,
Chen-Yu Tsai, Pin-Yen Lin
In-Reply-To: <20250731101344.2761757-1-treapking@google.com>
On Thu, 31 Jul 2025, Pin-Yen Lin <treapking@chromium.org> wrote:
> Some touch controllers have to be powered on after the panel's backlight
> is enabled. To support these controllers, introduce after_panel_enabled
> flag to the panel follower and power on the device after the panel and
> its backlight are enabled.
I think it's *very* confusing and error prone to call follower hooks at
different places depending on a flag. The hook names and documentation
say *when* they get called, and that should not change.
I think the right approach would be to add .panel_enabled and
.panel_disabling hooks to struct drm_panel_follower_funcs, and have them
called after panel (and backlight) have been enabled and before panel
(and backlight) are disabled, respectively.
In i2c-hid-core.c, you'd then have two copies of struct
drm_panel_follower_funcs, and use one or the other depending on the
quirk. You can even reuse the functions.
I think overall it'll be be more consistent, more flexible, and probably
fewer lines of code too.
BR,
Jani.
> Signed-off-by: Pin-Yen Lin <treapking@google.com>
PS. Your Signed-off-by doesn't match the patch author.
> ---
>
> drivers/gpu/drm/drm_panel.c | 47 +++++++++++++++++++++++++++++++++----
> include/drm/drm_panel.h | 8 +++++++
> 2 files changed, 51 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
> index 650de4da08537..58125f8418f37 100644
> --- a/drivers/gpu/drm/drm_panel.c
> +++ b/drivers/gpu/drm/drm_panel.c
> @@ -133,6 +133,9 @@ void drm_panel_prepare(struct drm_panel *panel)
> panel->prepared = true;
>
> list_for_each_entry(follower, &panel->followers, list) {
> + if (follower->after_panel_enabled)
> + continue;
> +
> ret = follower->funcs->panel_prepared(follower);
> if (ret < 0)
> dev_info(panel->dev, "%ps failed: %d\n",
> @@ -178,6 +181,9 @@ void drm_panel_unprepare(struct drm_panel *panel)
> mutex_lock(&panel->follower_lock);
>
> list_for_each_entry(follower, &panel->followers, list) {
> + if (follower->after_panel_enabled)
> + continue;
> +
> ret = follower->funcs->panel_unpreparing(follower);
> if (ret < 0)
> dev_info(panel->dev, "%ps failed: %d\n",
> @@ -208,6 +214,7 @@ EXPORT_SYMBOL(drm_panel_unprepare);
> */
> void drm_panel_enable(struct drm_panel *panel)
> {
> + struct drm_panel_follower *follower;
> int ret;
>
> if (!panel)
> @@ -218,10 +225,12 @@ void drm_panel_enable(struct drm_panel *panel)
> return;
> }
>
> + mutex_lock(&panel->follower_lock);
> +
> if (panel->funcs && panel->funcs->enable) {
> ret = panel->funcs->enable(panel);
> if (ret < 0)
> - return;
> + goto exit;
> }
> panel->enabled = true;
>
> @@ -229,6 +238,18 @@ void drm_panel_enable(struct drm_panel *panel)
> if (ret < 0)
> DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
> ret);
> +
> + list_for_each_entry(follower, &panel->followers, list) {
> + if (!follower->after_panel_enabled)
> + continue;
> +
> + ret = follower->funcs->panel_prepared(follower);
> + if (ret < 0)
> + dev_info(panel->dev, "%ps failed: %d\n",
> + follower->funcs->panel_prepared, ret);
> + }
> +exit:
> + mutex_unlock(&panel->follower_lock);
> }
> EXPORT_SYMBOL(drm_panel_enable);
>
> @@ -242,6 +263,7 @@ EXPORT_SYMBOL(drm_panel_enable);
> */
> void drm_panel_disable(struct drm_panel *panel)
> {
> + struct drm_panel_follower *follower;
> int ret;
>
> if (!panel)
> @@ -261,6 +283,18 @@ void drm_panel_disable(struct drm_panel *panel)
> return;
> }
>
> + mutex_lock(&panel->follower_lock);
> +
> + list_for_each_entry(follower, &panel->followers, list) {
> + if (!follower->after_panel_enabled)
> + continue;
> +
> + ret = follower->funcs->panel_unpreparing(follower);
> + if (ret < 0)
> + dev_info(panel->dev, "%ps failed: %d\n",
> + follower->funcs->panel_unpreparing, ret);
> + }
> +
> ret = backlight_disable(panel->backlight);
> if (ret < 0)
> DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
> @@ -269,9 +303,12 @@ void drm_panel_disable(struct drm_panel *panel)
> if (panel->funcs && panel->funcs->disable) {
> ret = panel->funcs->disable(panel);
> if (ret < 0)
> - return;
> + goto exit;
> }
> panel->enabled = false;
> +
> +exit:
> + mutex_unlock(&panel->follower_lock);
> }
> EXPORT_SYMBOL(drm_panel_disable);
>
> @@ -537,7 +574,8 @@ int drm_panel_add_follower(struct device *follower_dev,
> mutex_lock(&panel->follower_lock);
>
> list_add_tail(&follower->list, &panel->followers);
> - if (panel->prepared) {
> + if ((panel->prepared && !follower->after_panel_enabled) ||
> + (panel->enabled && follower->after_panel_enabled)) {
> ret = follower->funcs->panel_prepared(follower);
> if (ret < 0)
> dev_info(panel->dev, "%ps failed: %d\n",
> @@ -566,7 +604,8 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
>
> mutex_lock(&panel->follower_lock);
>
> - if (panel->prepared) {
> + if ((panel->prepared && !follower->after_panel_enabled) ||
> + (panel->enabled && follower->after_panel_enabled)) {
> ret = follower->funcs->panel_unpreparing(follower);
> if (ret < 0)
> dev_info(panel->dev, "%ps failed: %d\n",
> diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
> index 843fb756a2950..aa9b6218702fd 100644
> --- a/include/drm/drm_panel.h
> +++ b/include/drm/drm_panel.h
> @@ -183,6 +183,14 @@ struct drm_panel_follower {
> * The panel we're dependent on; set by drm_panel_add_follower().
> */
> struct drm_panel *panel;
> +
> + /**
> + * @after_panel_enabled
> + *
> + * If true then this panel follower should be powered after the panel
> + * and the backlight are enabled, instead of after panel is prepared.
> + */
> + bool after_panel_enabled;
> };
>
> /**
--
Jani Nikula, Intel
^ permalink raw reply
* [PATCH 2/2] HID: Make elan touch controllers power on after panel is enabled
From: Pin-Yen Lin @ 2025-07-31 10:13 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jiri Kosina,
Benjamin Tissoires
Cc: linux-kernel, linux-input, Douglas Anderson, dri-devel,
Chen-Yu Tsai, Pin-Yen Lin
In-Reply-To: <20250731101344.2761757-1-treapking@google.com>
Introduce a new HID quirk to indicate that this device has to be enabled
after the panel's backlight is enabled, and update the driver data for
the elan devices to enable this quirk. This cannot be a I2C HID quirk
because the kernel needs to acknowledge this before powering up the
device and read the VID/PID.
Signed-off-by: Pin-Yen Lin <treapking@google.com>
---
drivers/hid/i2c-hid/i2c-hid-core.c | 2 ++
drivers/hid/i2c-hid/i2c-hid-of-elan.c | 11 ++++++++++-
include/linux/hid.h | 2 ++
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index d3912e3f2f13a..8dc4d5d56d399 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -1183,6 +1183,8 @@ static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid)
int ret;
ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs;
+ if (ihid->hid->initial_quirks | HID_QUIRK_POWER_ON_AFTER_BACKLIGHT)
+ ihid->panel_follower.after_panel_enabled = true;
/*
* If we're not in control of our own power up/power down then we can't
diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
index 3fcff6daa0d3a..0215f217f6d86 100644
--- a/drivers/hid/i2c-hid/i2c-hid-of-elan.c
+++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
+#include <linux/hid.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -23,6 +24,7 @@ struct elan_i2c_hid_chip_data {
unsigned int post_power_delay_ms;
u16 hid_descriptor_address;
const char *main_supply_name;
+ bool power_after_backlight;
};
struct i2c_hid_of_elan {
@@ -97,6 +99,7 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
{
struct i2c_hid_of_elan *ihid_elan;
int ret;
+ u32 quirks = 0;
ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL);
if (!ihid_elan)
@@ -131,8 +134,12 @@ static int i2c_hid_of_elan_probe(struct i2c_client *client)
}
}
+ if (ihid_elan->chip_data->power_after_backlight)
+ quirks = HID_QUIRK_POWER_ON_AFTER_BACKLIGHT;
+
ret = i2c_hid_core_probe(client, &ihid_elan->ops,
- ihid_elan->chip_data->hid_descriptor_address, 0);
+ ihid_elan->chip_data->hid_descriptor_address,
+ quirks);
if (ret)
goto err_deassert_reset;
@@ -150,6 +157,7 @@ static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = {
.post_gpio_reset_on_delay_ms = 300,
.hid_descriptor_address = 0x0001,
.main_supply_name = "vcc33",
+ .power_after_backlight = true,
};
static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = {
@@ -157,6 +165,7 @@ static const struct elan_i2c_hid_chip_data elan_ekth6a12nay_chip_data = {
.post_gpio_reset_on_delay_ms = 300,
.hid_descriptor_address = 0x0001,
.main_supply_name = "vcc33",
+ .power_after_backlight = true,
};
static const struct elan_i2c_hid_chip_data ilitek_ili9882t_chip_data = {
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 2cc4f1e4ea963..c32425b5d0119 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -364,6 +364,7 @@ struct hid_item {
* | @HID_QUIRK_HAVE_SPECIAL_DRIVER:
* | @HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE:
* | @HID_QUIRK_IGNORE_SPECIAL_DRIVER
+ * | @HID_QUIRK_POWER_ON_AFTER_BACKLIGHT
* | @HID_QUIRK_FULLSPEED_INTERVAL:
* | @HID_QUIRK_NO_INIT_REPORTS:
* | @HID_QUIRK_NO_IGNORE:
@@ -391,6 +392,7 @@ struct hid_item {
#define HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE BIT(20)
#define HID_QUIRK_NOINVERT BIT(21)
#define HID_QUIRK_IGNORE_SPECIAL_DRIVER BIT(22)
+#define HID_QUIRK_POWER_ON_AFTER_BACKLIGHT BIT(23)
#define HID_QUIRK_FULLSPEED_INTERVAL BIT(28)
#define HID_QUIRK_NO_INIT_REPORTS BIT(29)
#define HID_QUIRK_NO_IGNORE BIT(30)
--
2.50.1.552.g942d659e1b-goog
^ permalink raw reply related
* [PATCH 1/2] drm/panel: Allow powering on panel follower after panel is enabled
From: Pin-Yen Lin @ 2025-07-31 10:13 UTC (permalink / raw)
To: Neil Armstrong, Jessica Zhang, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Jiri Kosina,
Benjamin Tissoires
Cc: linux-kernel, linux-input, Douglas Anderson, dri-devel,
Chen-Yu Tsai, Pin-Yen Lin
Some touch controllers have to be powered on after the panel's backlight
is enabled. To support these controllers, introduce after_panel_enabled
flag to the panel follower and power on the device after the panel and
its backlight are enabled.
Signed-off-by: Pin-Yen Lin <treapking@google.com>
---
drivers/gpu/drm/drm_panel.c | 47 +++++++++++++++++++++++++++++++++----
include/drm/drm_panel.h | 8 +++++++
2 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index 650de4da08537..58125f8418f37 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -133,6 +133,9 @@ void drm_panel_prepare(struct drm_panel *panel)
panel->prepared = true;
list_for_each_entry(follower, &panel->followers, list) {
+ if (follower->after_panel_enabled)
+ continue;
+
ret = follower->funcs->panel_prepared(follower);
if (ret < 0)
dev_info(panel->dev, "%ps failed: %d\n",
@@ -178,6 +181,9 @@ void drm_panel_unprepare(struct drm_panel *panel)
mutex_lock(&panel->follower_lock);
list_for_each_entry(follower, &panel->followers, list) {
+ if (follower->after_panel_enabled)
+ continue;
+
ret = follower->funcs->panel_unpreparing(follower);
if (ret < 0)
dev_info(panel->dev, "%ps failed: %d\n",
@@ -208,6 +214,7 @@ EXPORT_SYMBOL(drm_panel_unprepare);
*/
void drm_panel_enable(struct drm_panel *panel)
{
+ struct drm_panel_follower *follower;
int ret;
if (!panel)
@@ -218,10 +225,12 @@ void drm_panel_enable(struct drm_panel *panel)
return;
}
+ mutex_lock(&panel->follower_lock);
+
if (panel->funcs && panel->funcs->enable) {
ret = panel->funcs->enable(panel);
if (ret < 0)
- return;
+ goto exit;
}
panel->enabled = true;
@@ -229,6 +238,18 @@ void drm_panel_enable(struct drm_panel *panel)
if (ret < 0)
DRM_DEV_INFO(panel->dev, "failed to enable backlight: %d\n",
ret);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->after_panel_enabled)
+ continue;
+
+ ret = follower->funcs->panel_prepared(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_prepared, ret);
+ }
+exit:
+ mutex_unlock(&panel->follower_lock);
}
EXPORT_SYMBOL(drm_panel_enable);
@@ -242,6 +263,7 @@ EXPORT_SYMBOL(drm_panel_enable);
*/
void drm_panel_disable(struct drm_panel *panel)
{
+ struct drm_panel_follower *follower;
int ret;
if (!panel)
@@ -261,6 +283,18 @@ void drm_panel_disable(struct drm_panel *panel)
return;
}
+ mutex_lock(&panel->follower_lock);
+
+ list_for_each_entry(follower, &panel->followers, list) {
+ if (!follower->after_panel_enabled)
+ continue;
+
+ ret = follower->funcs->panel_unpreparing(follower);
+ if (ret < 0)
+ dev_info(panel->dev, "%ps failed: %d\n",
+ follower->funcs->panel_unpreparing, ret);
+ }
+
ret = backlight_disable(panel->backlight);
if (ret < 0)
DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n",
@@ -269,9 +303,12 @@ void drm_panel_disable(struct drm_panel *panel)
if (panel->funcs && panel->funcs->disable) {
ret = panel->funcs->disable(panel);
if (ret < 0)
- return;
+ goto exit;
}
panel->enabled = false;
+
+exit:
+ mutex_unlock(&panel->follower_lock);
}
EXPORT_SYMBOL(drm_panel_disable);
@@ -537,7 +574,8 @@ int drm_panel_add_follower(struct device *follower_dev,
mutex_lock(&panel->follower_lock);
list_add_tail(&follower->list, &panel->followers);
- if (panel->prepared) {
+ if ((panel->prepared && !follower->after_panel_enabled) ||
+ (panel->enabled && follower->after_panel_enabled)) {
ret = follower->funcs->panel_prepared(follower);
if (ret < 0)
dev_info(panel->dev, "%ps failed: %d\n",
@@ -566,7 +604,8 @@ void drm_panel_remove_follower(struct drm_panel_follower *follower)
mutex_lock(&panel->follower_lock);
- if (panel->prepared) {
+ if ((panel->prepared && !follower->after_panel_enabled) ||
+ (panel->enabled && follower->after_panel_enabled)) {
ret = follower->funcs->panel_unpreparing(follower);
if (ret < 0)
dev_info(panel->dev, "%ps failed: %d\n",
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 843fb756a2950..aa9b6218702fd 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -183,6 +183,14 @@ struct drm_panel_follower {
* The panel we're dependent on; set by drm_panel_add_follower().
*/
struct drm_panel *panel;
+
+ /**
+ * @after_panel_enabled
+ *
+ * If true then this panel follower should be powered after the panel
+ * and the backlight are enabled, instead of after panel is prepared.
+ */
+ bool after_panel_enabled;
};
/**
--
2.50.1.552.g942d659e1b-goog
^ permalink raw reply related
* Re: [PATCH v12 00/10] Add support for MAX7360
From: Lee Jones @ 2025-07-31 10:02 UTC (permalink / raw)
To: Mathieu Dubois-Briand
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Kamel Bouhara,
Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov,
Uwe Kleine-König, Michael Walle, Mark Brown,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
devicetree, linux-kernel, linux-gpio, linux-input, linux-pwm,
andriy.shevchenko, Grégory Clement, Thomas Petazzoni,
Krzysztof Kozlowski, Andy Shevchenko, Bartosz Golaszewski
In-Reply-To: <20250722-mdb-max7360-support-v12-0-3747721a8d02@bootlin.com>
On Tue, 22 Jul 2025, Mathieu Dubois-Briand wrote:
> This series implements a set of drivers allowing to support the Maxim
> Integrated MAX7360 device.
>
> The MAX7360 is an I2C key-switch and led controller, with following
> functionalities:
> - Keypad controller for a key matrix of up to 8 rows and 8 columns.
> - Rotary encoder support, for a single rotary encoder.
> - Up to 8 PWM outputs.
> - Up to 8 GPIOs with support for interrupts and 6 GPOs.
>
> Chipset pins are shared between all functionalities, so all cannot be
> used at the same time.
>
> Lee Jones suggested the whole series goes through MFD subsystem, once
> all patches got the needed Acks.
MFD looks good.
Let me know when you get all (PWM?) the Acks and I'll merge the set.
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH v9 2/6] mfd: pf1550: add core driver
From: Lee Jones @ 2025-07-31 9:15 UTC (permalink / raw)
To: samuel.kayode
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Liam Girdwood,
Mark Brown, Dmitry Torokhov, Sebastian Reichel, Frank Li, imx,
devicetree, linux-kernel, linux-input, linux-pm, Abel Vesa,
Abel Vesa, Robin Gong, Robin Gong, Enric Balletbo i Serra,
Sean Nyekjaer, Christophe JAILLET
In-Reply-To: <20250716-pf1550-v9-2-502a647f04ef@savoirfairelinux.com>
On Wed, 16 Jul 2025, Samuel Kayode via B4 Relay wrote:
> From: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
>
> Add the core driver for pf1550 PMIC. There are 3 subdevices for which the
> drivers will be added in subsequent patches.
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> ---
> v9:
> - Requested by Sean:
> - Add support for SW1 DVS enable/disable
> - Use consistent whitespace
> - Adjust commenting and log messages of the read_otp function
> v8:
> - Address Lee's feedback:
> - Drop `mfd` from driver description and comments
> - Add module name in Kconfig
> - Fix license commenting
> - Drop filenames from comments
> - Drop unnecessary tabbing
> - Alphabetical ordering of includes
> - Remove magic numbers
> - Add comments for pf1550_read_otp function
> - Fix log error message in pf1550_read_otp
> - Drop pf1550_add_child_device function
> - Start comments with upper case
> - Rename pf1550_dev to pf1550_ddata
> - Drop i2c member in struct pf1550_ddata/pf1550_dev
> - Use more helpful log message when device id not recognized
> - Fix dvs_enb: when bit is set the DVS is disabled and when bit is clear the
> DVS is enabled
> - Verified the PM_OPS suspend and resume do act as expected
> v7:
> - Address Frank's feedback:
> - Ensure reverse christmas tree order for local variable definitions
> - Drop unnecessary driver data definition in id table
> v6:
> - Address Frank's feedback:
> - Ensure lowercase when defining register addresses
> - Use GENMASK macro for masking
> - Hardcode IRQ flags in pf1550_add_child_device
> - Add dvs_enb variable for SW2 regulator
> - Drop chip type variable
> v5:
> - Use top level interrupt to manage interrupts for the sub-drivers as
> recommended by Mark Brown. The regmap_irq_sub_irq_map would have been used
> if not for the irregular charger irq address. For all children, the mask
> register is directly after the irq register (i.e., 0x08, 0x09) except
> for the charger: 0x80, 0x82. Meaning .mask_base would be applicable
> for all but the charger
> - Fix bad offset for temperature interrupts of regulator
> v4:
> - Use struct resource to define irq so platform_get_irq can be used in
> children as suggested by Dmitry
> - Let mfd_add_devices create the mappings for the interrupts
> - ack_base and init_ack_masked defined for charger and regulator irq
> chips
> - No need to define driver_data in table id
> v3:
> - Address Dmitry's feedback:
> - Place Table IDs next to each other
> - Drop of_match_ptr
> - Replace dev_err with dev_err_probe in probe method
> - Drop useless log in probe
> - Map all irqs instead of doing it in the sub-devices as recommended by
> Dmitry.
> v2:
> - Address feedback from Enric Balletbo Serra
> ---
> drivers/mfd/Kconfig | 16 ++
> drivers/mfd/Makefile | 2 +
> drivers/mfd/pf1550.c | 374 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/pf1550.h | 271 ++++++++++++++++++++++++++++++++
> 4 files changed, 663 insertions(+)
Mostly pretty good. A few nits left.
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 96992af22565205716d72db0494c7bf2567b045e..4ac91a556713ba7a867c1d4430c6c0d8bb05d0d7 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -558,6 +558,22 @@ config MFD_MX25_TSADC
> i.MX25 processors. They consist of a conversion queue for general
> purpose ADC and a queue for Touchscreens.
>
> +config MFD_PF1550
> + tristate "NXP PF1550 PMIC Support"
> + depends on I2C=y && OF
> + select MFD_CORE
> + select REGMAP_I2C
> + select REGMAP_IRQ
> + help
> + Say yes here to add support for NXP PF1550. This is a companion Power
> + Management IC with regulators, onkey, and charger control on chip.
> + This driver provides common support for accessing the device;
> + additional drivers must be enabled in order to use the functionality
> + of the device.
> +
> + This driver can also be built as a module and if so will be called
> + pf1550.
> +
> config MFD_HI6421_PMIC
> tristate "HiSilicon Hi6421 PMU/Codec IC"
> depends on OF
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 5e5cc279af6036a6b3ea1f1f0feeddf45b85f15c..7391d1b81d1ee499507b4ac24ff00eb2e344d60b 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -120,6 +120,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
> obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
> obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
>
> +obj-$(CONFIG_MFD_PF1550) += pf1550.o
> +
> obj-$(CONFIG_MFD_CORE) += mfd-core.o
>
> ocelot-soc-objs := ocelot-core.o ocelot-spi.o
> diff --git a/drivers/mfd/pf1550.c b/drivers/mfd/pf1550.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..fd31eff0e5a88c973f7db14b068f1c8e92991d4b
> --- /dev/null
> +++ b/drivers/mfd/pf1550.c
> @@ -0,0 +1,374 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * core driver for the PF1550
Nit: Sentences start with uppercase chars.
> + *
> + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> + * Robin Gong <yibin.gong@freescale.com>
> + *
> + * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
> + * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> + */
> +
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/pf1550.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/regmap.h>
> +
> +static const struct regmap_config pf1550_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .max_register = PF1550_PMIC_REG_END,
> +};
> +
> +static const struct regmap_irq pf1550_irqs[] = {
> + REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG),
> + REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR),
> + REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY),
> +};
> +
> +static const struct regmap_irq_chip pf1550_irq_chip = {
> + .name = "pf1550",
> + .status_base = PF1550_PMIC_REG_INT_CATEGORY,
> + .init_ack_masked = 1,
> + .num_regs = 1,
> + .irqs = pf1550_irqs,
> + .num_irqs = ARRAY_SIZE(pf1550_irqs),
> +};
> +
> +static const struct regmap_irq pf1550_regulator_irqs[] = {
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110),
> + REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125),
> +};
> +
> +static const struct regmap_irq_chip pf1550_regulator_irq_chip = {
> + .name = "pf1550-regulator",
> + .status_base = PF1550_PMIC_REG_SW_INT_STAT0,
> + .ack_base = PF1550_PMIC_REG_SW_INT_STAT0,
> + .mask_base = PF1550_PMIC_REG_SW_INT_MASK0,
> + .use_ack = 1,
> + .init_ack_masked = 1,
> + .num_regs = 25,
> + .irqs = pf1550_regulator_irqs,
> + .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs),
> +};
> +
> +static const struct resource regulator_resources[] = {
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110),
> + DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125),
> +};
> +
> +static const struct regmap_irq pf1550_onkey_irqs[] = {
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI),
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI),
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI),
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI),
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI),
> + REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI),
> +};
> +
> +static const struct regmap_irq_chip pf1550_onkey_irq_chip = {
> + .name = "pf1550-onkey",
> + .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
> + .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
> + .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0,
> + .use_ack = 1,
> + .init_ack_masked = 1,
> + .num_regs = 1,
> + .irqs = pf1550_onkey_irqs,
> + .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs),
> +};
> +
> +static const struct resource onkey_resources[] = {
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI),
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI),
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI),
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI),
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI),
> + DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI),
> +};
> +
> +static const struct regmap_irq pf1550_charger_irqs[] = {
> + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI),
> + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI),
> + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI),
> + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI),
> + REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI),
> +};
> +
> +static const struct regmap_irq_chip pf1550_charger_irq_chip = {
> + .name = "pf1550-charger",
> + .status_base = PF1550_CHARG_REG_CHG_INT,
> + .ack_base = PF1550_CHARG_REG_CHG_INT,
> + .mask_base = PF1550_CHARG_REG_CHG_INT_MASK,
> + .use_ack = 1,
> + .init_ack_masked = 1,
> + .num_regs = 1,
> + .irqs = pf1550_charger_irqs,
> + .num_irqs = ARRAY_SIZE(pf1550_charger_irqs),
> +};
> +
> +static const struct resource charger_resources[] = {
> + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI),
> + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI),
> + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI),
> + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI),
> + DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI),
> +};
> +
> +static const struct mfd_cell pf1550_regulator_cell = {
> + .name = "pf1550-regulator",
> + .num_resources = ARRAY_SIZE(regulator_resources),
> + .resources = regulator_resources,
> +};
> +
> +static const struct mfd_cell pf1550_onkey_cell = {
> + .name = "pf1550-onkey",
> + .num_resources = ARRAY_SIZE(onkey_resources),
> + .resources = onkey_resources,
> +};
> +
> +static const struct mfd_cell pf1550_charger_cell = {
> + .name = "pf1550-charger",
> + .num_resources = ARRAY_SIZE(charger_resources),
> + .resources = charger_resources,
> +};
Is there a technical reason why these are all separated out? It would
normally be preferable to put these in an array and register them all
with a single all to mfd_add_devices();
> +/*
> + * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a
> + * configuration of the PMIC in a One-Time Programmable (OTP) memory.
> + * This memory is accessed indirectly by writing valid keys to specific
> + * registers of the PMIC. To read the OTP memory after writing the valid keys,
> + * the OTP register address to be read is written to pf1550 register 0xc4 and
> + * its value read from pf1550 register 0xc5.
> + */
> +static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index,
> + unsigned int *val)
> +{
> + int ret = 0;
> +
> + ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY,
> + PF1550_OTP_PMIC_KEY);
> + if (ret)
> + goto read_err;
'\n'
> + ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2,
> + PF1550_OTP_CHGR_KEY);
> + if (ret)
> + goto read_err;
'\n'
> + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3,
> + PF1550_OTP_TEST_KEY);
> + if (ret)
> + goto read_err;
'\n'
> + ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index);
> + if (ret)
> + goto read_err;
'\n'
> + ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val);
> + if (ret)
> + goto read_err;
> +
> + return 0;
> +
> +read_err:
> + return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n",
> + index);
You can use 100-chars everywhere in here to prevent these kinds of wraps.
> +}
> +
> +static int pf1550_i2c_probe(struct i2c_client *i2c)
> +{
> + const struct mfd_cell *regulator = &pf1550_regulator_cell;
> + const struct mfd_cell *charger = &pf1550_charger_cell;
> + const struct mfd_cell *onkey = &pf1550_onkey_cell;
> + unsigned int reg_data = 0, otp_data = 0;
> + struct pf1550_ddata *pf1550;
> + struct irq_domain *domain;
> + int irq, ret = 0;
> +
> + pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL);
> + if (!pf1550)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(i2c, pf1550);
> + pf1550->dev = &i2c->dev;
> + pf1550->irq = i2c->irq;
> +
> + pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config);
> + if (IS_ERR(pf1550->regmap))
> + return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap),
> + "failed to allocate register map\n");
> +
> + ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, ®_data);
> + if (ret < 0)
> + return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n");
> + if (reg_data != PF1550_DEVICE_ID)
> + return dev_err_probe(pf1550->dev, -ENODEV,
> + "invalid device ID: 0x%02x\n", reg_data);
> +
> + /* Regulator DVS for SW2 */
> + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data);
> + if (ret)
> + return ret;
> +
> + /* When clear, DVS should be enabled */
> + if (!(otp_data & OTP_SW2_DVS_ENB))
> + pf1550->dvs2_enb = true;
> +
> + /* Regulator DVS for SW1 */
> + ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data);
> + if (ret)
> + return ret;
> +
> + if (!(otp_data & OTP_SW1_DVS_ENB))
> + pf1550->dvs1_enb = true;
> +
> + /* Add top level interrupts */
> + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING,
> + 0, &pf1550_irq_chip,
> + &pf1550->irq_data);
> + if (ret)
> + return ret;
> +
> + /* Add regulator */
> + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR);
> + if (irq < 0)
> + return dev_err_probe(pf1550->dev, irq,
> + "Failed to get parent vIRQ(%d) for chip %s\n",
> + PF1550_IRQ_REGULATOR, pf1550_irq_chip.name);
> +
> + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &pf1550_regulator_irq_chip,
> + &pf1550->irq_data_regulator);
> + if (ret)
> + return dev_err_probe(pf1550->dev, ret,
> + "Failed to add %s IRQ chip\n",
> + pf1550_regulator_irq_chip.name);
> +
> + domain = regmap_irq_get_domain(pf1550->irq_data_regulator);
> +
> + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator,
> + 1, NULL, 0, domain);
> + if (ret)
> + return ret;
> +
> + /* Add onkey */
> + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY);
> + if (irq < 0)
> + return dev_err_probe(pf1550->dev, irq,
> + "Failed to get parent vIRQ(%d) for chip %s\n",
> + PF1550_IRQ_ONKEY, pf1550_irq_chip.name);
> +
> + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &pf1550_onkey_irq_chip,
> + &pf1550->irq_data_onkey);
> + if (ret)
> + return dev_err_probe(pf1550->dev, ret,
> + "Failed to add %s IRQ chip\n",
> + pf1550_onkey_irq_chip.name);
> +
> + domain = regmap_irq_get_domain(pf1550->irq_data_onkey);
> +
> + ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1,
> + NULL, 0, domain);
> + if (ret)
> + return ret;
> +
> + /* Add battery charger */
> + irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG);
> + if (irq < 0)
> + return dev_err_probe(pf1550->dev, irq,
> + "Failed to get parent vIRQ(%d) for chip %s\n",
> + PF1550_IRQ_CHG, pf1550_irq_chip.name);
> +
> + ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
> + IRQF_ONESHOT | IRQF_SHARED |
> + IRQF_TRIGGER_FALLING, 0,
> + &pf1550_charger_irq_chip,
> + &pf1550->irq_data_charger);
> + if (ret)
> + return dev_err_probe(pf1550->dev, ret,
> + "Failed to add %s IRQ chip\n",
> + pf1550_charger_irq_chip.name);
> +
> + domain = regmap_irq_get_domain(pf1550->irq_data_charger);
> +
> + return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger,
> + 1, NULL, 0, domain);
> +}
> +
> +static int pf1550_suspend(struct device *dev)
> +{
> + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev)) {
> + enable_irq_wake(pf1550->irq);
> + disable_irq(pf1550->irq);
> + }
> +
> + return 0;
> +}
> +
> +static int pf1550_resume(struct device *dev)
> +{
> + struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
> +
> + if (device_may_wakeup(dev)) {
> + disable_irq_wake(pf1550->irq);
> + enable_irq(pf1550->irq);
> + }
> +
> + return 0;
> +}
> +static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume);
> +
> +static const struct i2c_device_id pf1550_i2c_id[] = {
> + { "pf1550" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id);
> +
> +static const struct of_device_id pf1550_dt_match[] = {
> + { .compatible = "nxp,pf1550" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, pf1550_dt_match);
> +
> +static struct i2c_driver pf1550_i2c_driver = {
> + .driver = {
> + .name = "pf1550",
> + .pm = pm_sleep_ptr(&pf1550_pm),
> + .of_match_table = pf1550_dt_match,
> + },
> + .probe = pf1550_i2c_probe,
> + .id_table = pf1550_i2c_id,
> +};
> +module_i2c_driver(pf1550_i2c_driver);
> +
> +MODULE_DESCRIPTION("NXP PF1550 core driver");
> +MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/pf1550.h b/include/linux/mfd/pf1550.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..6099fe670fab13deddbe9e0f834c9b3af5bf4f50
> --- /dev/null
> +++ b/include/linux/mfd/pf1550.h
> @@ -0,0 +1,271 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * declarations for the PF1550 PMIC
As above.
> + *
> + * Copyright (C) 2016 Freescale Semiconductor, Inc.
> + * Robin Gong <yibin.gong@freescale.com>
> + *
> + * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
> + * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
> + */
> +
> +#ifndef __LINUX_MFD_PF1550_H
> +#define __LINUX_MFD_PF1550_H
> +
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +
> +enum pf1550_pmic_reg {
> + /* PMIC regulator part */
> + PF1550_PMIC_REG_DEVICE_ID = 0x00,
> + PF1550_PMIC_REG_OTP_FLAVOR = 0x01,
> + PF1550_PMIC_REG_SILICON_REV = 0x02,
> +
> + PF1550_PMIC_REG_INT_CATEGORY = 0x06,
> + PF1550_PMIC_REG_SW_INT_STAT0 = 0x08,
> + PF1550_PMIC_REG_SW_INT_MASK0 = 0x09,
> + PF1550_PMIC_REG_SW_INT_SENSE0 = 0x0a,
> + PF1550_PMIC_REG_SW_INT_STAT1 = 0x0b,
> + PF1550_PMIC_REG_SW_INT_MASK1 = 0x0c,
> + PF1550_PMIC_REG_SW_INT_SENSE1 = 0x0d,
> + PF1550_PMIC_REG_SW_INT_STAT2 = 0x0e,
> + PF1550_PMIC_REG_SW_INT_MASK2 = 0x0f,
> + PF1550_PMIC_REG_SW_INT_SENSE2 = 0x10,
> + PF1550_PMIC_REG_LDO_INT_STAT0 = 0x18,
> + PF1550_PMIC_REG_LDO_INT_MASK0 = 0x19,
> + PF1550_PMIC_REG_LDO_INT_SENSE0 = 0x1a,
> + PF1550_PMIC_REG_TEMP_INT_STAT0 = 0x20,
> + PF1550_PMIC_REG_TEMP_INT_MASK0 = 0x21,
> + PF1550_PMIC_REG_TEMP_INT_SENSE0 = 0x22,
> + PF1550_PMIC_REG_ONKEY_INT_STAT0 = 0x24,
> + PF1550_PMIC_REG_ONKEY_INT_MASK0 = 0x25,
> + PF1550_PMIC_REG_ONKEY_INT_SENSE0 = 0x26,
> + PF1550_PMIC_REG_MISC_INT_STAT0 = 0x28,
> + PF1550_PMIC_REG_MISC_INT_MASK0 = 0x29,
> + PF1550_PMIC_REG_MISC_INT_SENSE0 = 0x2a,
> +
> + PF1550_PMIC_REG_COINCELL_CONTROL = 0x30,
> +
> + PF1550_PMIC_REG_SW1_VOLT = 0x32,
> + PF1550_PMIC_REG_SW1_STBY_VOLT = 0x33,
> + PF1550_PMIC_REG_SW1_SLP_VOLT = 0x34,
> + PF1550_PMIC_REG_SW1_CTRL = 0x35,
> + PF1550_PMIC_REG_SW1_CTRL1 = 0x36,
> + PF1550_PMIC_REG_SW2_VOLT = 0x38,
> + PF1550_PMIC_REG_SW2_STBY_VOLT = 0x39,
> + PF1550_PMIC_REG_SW2_SLP_VOLT = 0x3a,
> + PF1550_PMIC_REG_SW2_CTRL = 0x3b,
> + PF1550_PMIC_REG_SW2_CTRL1 = 0x3c,
> + PF1550_PMIC_REG_SW3_VOLT = 0x3e,
> + PF1550_PMIC_REG_SW3_STBY_VOLT = 0x3f,
> + PF1550_PMIC_REG_SW3_SLP_VOLT = 0x40,
> + PF1550_PMIC_REG_SW3_CTRL = 0x41,
> + PF1550_PMIC_REG_SW3_CTRL1 = 0x42,
> + PF1550_PMIC_REG_VSNVS_CTRL = 0x48,
> + PF1550_PMIC_REG_VREFDDR_CTRL = 0x4a,
> + PF1550_PMIC_REG_LDO1_VOLT = 0x4c,
> + PF1550_PMIC_REG_LDO1_CTRL = 0x4d,
> + PF1550_PMIC_REG_LDO2_VOLT = 0x4f,
> + PF1550_PMIC_REG_LDO2_CTRL = 0x50,
> + PF1550_PMIC_REG_LDO3_VOLT = 0x52,
> + PF1550_PMIC_REG_LDO3_CTRL = 0x53,
> + PF1550_PMIC_REG_PWRCTRL0 = 0x58,
> + PF1550_PMIC_REG_PWRCTRL1 = 0x59,
> + PF1550_PMIC_REG_PWRCTRL2 = 0x5a,
> + PF1550_PMIC_REG_PWRCTRL3 = 0x5b,
> + PF1550_PMIC_REG_SW1_PWRDN_SEQ = 0x5f,
> + PF1550_PMIC_REG_SW2_PWRDN_SEQ = 0x60,
> + PF1550_PMIC_REG_SW3_PWRDN_SEQ = 0x61,
> + PF1550_PMIC_REG_LDO1_PWRDN_SEQ = 0x62,
> + PF1550_PMIC_REG_LDO2_PWRDN_SEQ = 0x63,
> + PF1550_PMIC_REG_LDO3_PWRDN_SEQ = 0x64,
> + PF1550_PMIC_REG_VREFDDR_PWRDN_SEQ = 0x65,
> +
> + PF1550_PMIC_REG_STATE_INFO = 0x67,
> + PF1550_PMIC_REG_I2C_ADDR = 0x68,
> + PF1550_PMIC_REG_IO_DRV0 = 0x69,
> + PF1550_PMIC_REG_IO_DRV1 = 0x6a,
> + PF1550_PMIC_REG_RC_16MHZ = 0x6b,
> + PF1550_PMIC_REG_KEY = 0x6f,
> +
> + /* Charger part */
> + PF1550_CHARG_REG_CHG_INT = 0x80,
> + PF1550_CHARG_REG_CHG_INT_MASK = 0x82,
> + PF1550_CHARG_REG_CHG_INT_OK = 0x84,
> + PF1550_CHARG_REG_VBUS_SNS = 0x86,
> + PF1550_CHARG_REG_CHG_SNS = 0x87,
> + PF1550_CHARG_REG_BATT_SNS = 0x88,
> + PF1550_CHARG_REG_CHG_OPER = 0x89,
> + PF1550_CHARG_REG_CHG_TMR = 0x8a,
> + PF1550_CHARG_REG_CHG_EOC_CNFG = 0x8d,
> + PF1550_CHARG_REG_CHG_CURR_CNFG = 0x8e,
> + PF1550_CHARG_REG_BATT_REG = 0x8f,
> + PF1550_CHARG_REG_BATFET_CNFG = 0x91,
> + PF1550_CHARG_REG_THM_REG_CNFG = 0x92,
> + PF1550_CHARG_REG_VBUS_INLIM_CNFG = 0x94,
> + PF1550_CHARG_REG_VBUS_LIN_DPM = 0x95,
> + PF1550_CHARG_REG_USB_PHY_LDO_CNFG = 0x96,
> + PF1550_CHARG_REG_DBNC_DELAY_TIME = 0x98,
> + PF1550_CHARG_REG_CHG_INT_CNFG = 0x99,
> + PF1550_CHARG_REG_THM_ADJ_SETTING = 0x9a,
> + PF1550_CHARG_REG_VBUS2SYS_CNFG = 0x9b,
> + PF1550_CHARG_REG_LED_PWM = 0x9c,
> + PF1550_CHARG_REG_FAULT_BATFET_CNFG = 0x9d,
> + PF1550_CHARG_REG_LED_CNFG = 0x9e,
> + PF1550_CHARG_REG_CHGR_KEY2 = 0x9f,
> +
> + PF1550_TEST_REG_FMRADDR = 0xc4,
> + PF1550_TEST_REG_FMRDATA = 0xc5,
> + PF1550_TEST_REG_KEY3 = 0xdf,
> +
> + PF1550_PMIC_REG_END = 0xff,
> +};
> +
> +/* One-Time Programmable(OTP) memory */
> +enum pf1550_otp_reg {
> + PF1550_OTP_SW1_SW2 = 0x1e,
> + PF1550_OTP_SW2_SW3 = 0x1f,
> +};
> +
> +#define PF1550_DEVICE_ID 0x7c
> +
> +/* Keys for reading OTP */
> +#define PF1550_OTP_PMIC_KEY 0x15
> +#define PF1550_OTP_CHGR_KEY 0x50
> +#define PF1550_OTP_TEST_KEY 0xab
> +
> +/* Supported charger modes */
> +#define PF1550_CHG_BAT_OFF 1
> +#define PF1550_CHG_BAT_ON 2
> +
> +#define PF1550_CHG_PRECHARGE 0
> +#define PF1550_CHG_CONSTANT_CURRENT 1
> +#define PF1550_CHG_CONSTANT_VOL 2
> +#define PF1550_CHG_EOC 3
> +#define PF1550_CHG_DONE 4
> +#define PF1550_CHG_TIMER_FAULT 6
> +#define PF1550_CHG_SUSPEND 7
> +#define PF1550_CHG_OFF_INV 8
> +#define PF1550_CHG_BAT_OVER 9
> +#define PF1550_CHG_OFF_TEMP 10
> +#define PF1550_CHG_LINEAR_ONLY 12
> +#define PF1550_CHG_SNS_MASK 0xf
> +#define PF1550_CHG_INT_MASK 0x51
> +
> +#define PF1550_BAT_NO_VBUS 0
> +#define PF1550_BAT_LOW_THAN_PRECHARG 1
> +#define PF1550_BAT_CHARG_FAIL 2
> +#define PF1550_BAT_HIGH_THAN_PRECHARG 4
> +#define PF1550_BAT_OVER_VOL 5
> +#define PF1550_BAT_NO_DETECT 6
> +#define PF1550_BAT_SNS_MASK 0x7
> +
> +#define PF1550_VBUS_UVLO BIT(2)
> +#define PF1550_VBUS_IN2SYS BIT(3)
> +#define PF1550_VBUS_OVLO BIT(4)
> +#define PF1550_VBUS_VALID BIT(5)
> +
> +#define PF1550_CHARG_REG_BATT_REG_CHGCV_MASK 0x3f
> +#define PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT 6
> +#define PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK GENMASK(7, 6)
> +#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT 2
> +#define PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK GENMASK(3, 2)
> +
> +/* DVS ENABLE MASK */
> +#define OTP_SW1_DVS_ENB BIT(1)
> +#define OTP_SW2_DVS_ENB BIT(3)
> +
> +/* Top level interrupt masks */
> +#define IRQ_REGULATOR (BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(6))
> +#define IRQ_ONKEY BIT(5)
> +#define IRQ_CHG BIT(0)
> +
> +/* Regulator interrupt masks */
> +#define PMIC_IRQ_SW1_LS BIT(0)
> +#define PMIC_IRQ_SW2_LS BIT(1)
> +#define PMIC_IRQ_SW3_LS BIT(2)
> +#define PMIC_IRQ_SW1_HS BIT(0)
> +#define PMIC_IRQ_SW2_HS BIT(1)
> +#define PMIC_IRQ_SW3_HS BIT(2)
> +#define PMIC_IRQ_LDO1_FAULT BIT(0)
> +#define PMIC_IRQ_LDO2_FAULT BIT(1)
> +#define PMIC_IRQ_LDO3_FAULT BIT(2)
> +#define PMIC_IRQ_TEMP_110 BIT(0)
> +#define PMIC_IRQ_TEMP_125 BIT(1)
> +
> +/* Onkey interrupt masks */
> +#define ONKEY_IRQ_PUSHI BIT(0)
> +#define ONKEY_IRQ_1SI BIT(1)
> +#define ONKEY_IRQ_2SI BIT(2)
> +#define ONKEY_IRQ_3SI BIT(3)
> +#define ONKEY_IRQ_4SI BIT(4)
> +#define ONKEY_IRQ_8SI BIT(5)
> +
> +/* Charger interrupt masks */
> +#define CHARG_IRQ_BAT2SOCI BIT(1)
> +#define CHARG_IRQ_BATI BIT(2)
> +#define CHARG_IRQ_CHGI BIT(3)
> +#define CHARG_IRQ_VBUSI BIT(5)
> +#define CHARG_IRQ_DPMI BIT(6)
> +#define CHARG_IRQ_THMI BIT(7)
> +
> +enum pf1550_irq {
> + PF1550_IRQ_CHG,
> + PF1550_IRQ_REGULATOR,
> + PF1550_IRQ_ONKEY,
> +};
> +
> +enum pf1550_pmic_irq {
> + PF1550_PMIC_IRQ_SW1_LS,
> + PF1550_PMIC_IRQ_SW2_LS,
> + PF1550_PMIC_IRQ_SW3_LS,
> + PF1550_PMIC_IRQ_SW1_HS,
> + PF1550_PMIC_IRQ_SW2_HS,
> + PF1550_PMIC_IRQ_SW3_HS,
> + PF1550_PMIC_IRQ_LDO1_FAULT,
> + PF1550_PMIC_IRQ_LDO2_FAULT,
> + PF1550_PMIC_IRQ_LDO3_FAULT,
> + PF1550_PMIC_IRQ_TEMP_110,
> + PF1550_PMIC_IRQ_TEMP_125,
> +};
> +
> +enum pf1550_onkey_irq {
> + PF1550_ONKEY_IRQ_PUSHI,
> + PF1550_ONKEY_IRQ_1SI,
> + PF1550_ONKEY_IRQ_2SI,
> + PF1550_ONKEY_IRQ_3SI,
> + PF1550_ONKEY_IRQ_4SI,
> + PF1550_ONKEY_IRQ_8SI,
> +};
> +
> +enum pf1550_charg_irq {
> + PF1550_CHARG_IRQ_BAT2SOCI,
> + PF1550_CHARG_IRQ_BATI,
> + PF1550_CHARG_IRQ_CHGI,
> + PF1550_CHARG_IRQ_VBUSI,
> + PF1550_CHARG_IRQ_THMI,
> +};
> +
> +enum pf1550_regulators {
> + PF1550_SW1,
> + PF1550_SW2,
> + PF1550_SW3,
> + PF1550_VREFDDR,
> + PF1550_LDO1,
> + PF1550_LDO2,
> + PF1550_LDO3,
> +};
> +
> +struct pf1550_ddata {
> + bool dvs1_enb;
> + bool dvs2_enb;
Nit: I don't see the benefit of shortening 'enable' here.
Place the small variables at the bottom.
> + struct device *dev;
> + struct regmap *regmap;
> + struct regmap_irq_chip_data *irq_data_regulator;
> + struct regmap_irq_chip_data *irq_data_onkey;
> + struct regmap_irq_chip_data *irq_data_charger;
> + struct regmap_irq_chip_data *irq_data;
> + int irq;
> +};
> +
> +#endif /* __LINUX_MFD_PF1550_H */
>
> --
> 2.50.1
>
>
--
Lee Jones [李琼斯]
^ permalink raw reply
* Re: [PATCH] HID: multitouch: fix integer overflow in set_abs()
From: Jiri Slaby @ 2025-07-31 7:43 UTC (permalink / raw)
To: Qasim Ijaz; +Cc: jikos, bentiss, linux-input, linux-kernel, stable
In-Reply-To: <aIJXqs-U8vDpYv0S@gmail.com>
On 24. 07. 25, 17:56, Qasim Ijaz wrote:
> On Thu, Jul 24, 2025 at 08:58:40AM +0200, Jiri Slaby wrote:
>> On 23. 07. 25, 19:36, Qasim Ijaz wrote:
>>> It is possible for a malicious HID device to trigger a signed integer
>>> overflow (undefined behaviour) in set_abs() in the following expression
>>> by supplying bogus logical maximum and minimum values:
>>>
>>> int fuzz = snratio ? (fmax - fmin) / snratio : 0;
>>>
>>> For example, if the logical_maximum is INT_MAX and logical_minimum is -1
>>> then (fmax - fmin) resolves to INT_MAX + 1, which does not fit in a 32-bit
>>> signed int, so the subtraction overflows.
>>
>> The question is if it matters with -fwrapv?
>
> Ah yea thanks for bringing this up Jiri. I think you might be correct,
> after doing some research it looks like the kernel enables -fno‑strict‑overflow
> which implies -fwrapv which leads to wrap around instead of UB If I undestand
> correctly. So with that in mind this patch probably doesn't do anything
> useful, do you agree?
Yes, it correctly wraps around. But the question remains :). Does it
matter or not?
thanks,
--
js
suse labs
^ permalink raw reply
* Re: [PATCH 11/11] HID: multitouch: add haptic multitouch support
From: Oliver Sang @ 2025-07-31 2:55 UTC (permalink / raw)
To: Jonathan Denose
Cc: Philip Li, kernel test robot, Jiri Kosina, Benjamin Tissoires,
Dmitry Torokhov, Jonathan Corbet, Henrik Rydberg, oe-kbuild-all,
linux-input, linux-kernel, linux-doc, Angela Czubak,
Sean O'Brien, oliver.sang
In-Reply-To: <CAMCVhVNTWKg89MhPJeVvKK5ZhXYy2WCJFBGJo2Hg5=aCUZz32A@mail.gmail.com>
hi, Jonathan,
On Mon, Jul 21, 2025 at 11:06:33AM -0500, Jonathan Denose wrote:
> On Thu, Jul 17, 2025 at 6:59 PM Philip Li <philip.li@intel.com> wrote:
> >
> > On Thu, Jul 17, 2025 at 01:43:28PM -0500, Jonathan Denose wrote:
> > > On Tue, Jul 15, 2025 at 6:36 AM kernel test robot <lkp@intel.com> wrote:
> > > > kernel test robot noticed the following build errors:
> > > >
> > > > [auto build test ERROR on 86731a2a651e58953fc949573895f2fa6d456841]
> > > >
> > > > url: https://github.com/intel-lab-lkp/linux/commits/Jonathan-Denose/HID-add-haptics-page-defines/20250714-231444
> > > > base: 86731a2a651e58953fc949573895f2fa6d456841
> > > > patch link: https://lore.kernel.org/r/20250714-support-forcepads-v1-11-71c7c05748c9%40google.com
> > > > patch subject: [PATCH 11/11] HID: multitouch: add haptic multitouch support
> > > > config: hexagon-randconfig-r112-20250715 (https://download.01.org/0day-ci/archive/20250715/202507151942.94dhYylY-lkp@intel.com/config)
> > > > compiler: clang version 17.0.6 (https://github.com/llvm/llvm-project 6009708b4367171ccdbf4b5905cb6a803753fe18)
> > > > reproduce: (https://download.01.org/0day-ci/archive/20250715/202507151942.94dhYylY-lkp@intel.com/reproduce)
> > >
> > > I'm having trouble reproducing this build error. I tried following the
> >
> > Sorry Jonathan, the reproduce step we provide is wrong, would you mind to give
> > a try similar to the steps in [1]? We will resolve the bug as early as possible.
> >
> > [1] https://download.01.org/0day-ci/archive/20250717/202507170506.Wzz1lR5I-lkp@intel.com/reproduce
> >
> > > steps in the linked reproduce file, but when running:
> > > COMPILER_INSTALL_PATH=$HOME/0day ~/lkp-tests/kbuild/make.cross C=1
> > > CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__ -fmax-errors=unlimited
> > > -fmax-warnings=unlimited' O=build_dir ARCH=hexagon olddefconfig
> > >
> > > I get the errors:
> > > 0day/gcc-4.6.1-nolibc/hexagon-linux/bin/hexagon-linux-gcc: unknown C compiler
> > > scripts/Kconfig.include:45: Sorry, this C compiler is not supported.
> > >
> > > It looks to me like the hexagon-linux-gcc compiler is correctly
> > > installed at $HOME/0day so I'm not sure what to do from here. Can
> > > someone please assist me with this?
> > >
> > > --
> > > Jonathan
> > >
> Great! Thanks for providing the correct reproduce steps Phillip.
>
> I tried them and both of the make.cross steps completed successfully.
> I am not getting the build errors that the test bot is reporting.
sorry for this. just want to confirm one thing, did you follow below steps?
(the link [1] above is just for example, we need do small modification to
reproduce the issue in original report, there are 4 diff in below with [1],
(1) use your commit, (2) 'wget' command to get correct config (3) change to
use clang-17, btw, clang-20 can also reproduce the issue (4) change build
source to 'drivers/hid')
reproduce:
git clone https://github.com/intel/lkp-tests.git ~/lkp-tests
# https://github.com/intel-lab-lkp/linux/commit/4ccef2fdc95970f67857113edb4103d53205ac9c
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Jonathan-Denose/HID-add-haptics-page-defines/20250714-231444
git checkout 4ccef2fdc95970f67857113edb4103d53205ac9c
# save the config file
wget https://download.01.org/0day-ci/archive/20250715/202507151942.94dhYylY-lkp@intel.com/config
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang-17 ~/lkp-tests/kbuild/make.cross W=1 O=build_dir ARCH=hexagon olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang-17 ~/lkp-tests/kbuild/make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/hid/
I can reproduce the issue with above steps, if you still cannot reproduce,
could you give me your full log? below is mine just FYI (this is for clang-20,
and I use some different folders but not important)
xsang@xsang-OptiPlex-9020:~/linux$ COMPILER_INSTALL_PATH=/home/xsang/cross-compiler/ COMPILER=clang-20 /home/xsang/lkp-tests/kbuild/make.cross W=1 O=build_dir ARCH=hexagon olddefconfig
Compiler will be installed in /home/xsang/cross-compiler/
lftpget -c https://cdn.kernel.org/pub/tools/llvm/files/./llvm-20.1.8-x86_64.tar.xz
/home/xsang/linux
tar Jxf /home/xsang/cross-compiler//./llvm-20.1.8-x86_64.tar.xz -C /home/xsang/cross-compiler/
PATH=/home/xsang/cross-compiler//llvm-20.1.8-x86_64/bin:/home/xsang/.local/bin:/home/xsang/bin:/home/xsang/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/xsang/.local/bin
make --keep-going LLVM=1 CROSS_COMPILE=hexagon-linux- --jobs=72 KCFLAGS= -Wno-error=return-type -Wreturn-type -funsigned-char -Wundef W=1 O=build_dir ARCH=hexagon olddefconfig
make[1]: Entering directory '/home/xsang/linux/build_dir'
GEN Makefile
HOSTCC scripts/basic/fixdep
HOSTCC scripts/kconfig/conf.o
HOSTCC scripts/kconfig/confdata.o
HOSTCC scripts/kconfig/expr.o
LEX scripts/kconfig/lexer.lex.c
YACC scripts/kconfig/parser.tab.[ch]
HOSTCC scripts/kconfig/menu.o
HOSTCC scripts/kconfig/preprocess.o
HOSTCC scripts/kconfig/symbol.o
HOSTCC scripts/kconfig/util.o
HOSTCC scripts/kconfig/lexer.lex.o
HOSTCC scripts/kconfig/parser.tab.o
HOSTLD scripts/kconfig/conf
#
# configuration written to .config
#
make[1]: Leaving directory '/home/xsang/linux/build_dir'
xsang@xsang-OptiPlex-9020:~/linux$ COMPILER_INSTALL_PATH=/home/xsang/cross-compiler/ COMPILER=clang-20 /home/xsang/lkp-tests/kbuild/make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/hid/
Compiler will be installed in /home/xsang/cross-compiler/
PATH=/home/xsang/cross-compiler//llvm-20.1.8-x86_64/bin:/home/xsang/.local/bin:/home/xsang/bin:/home/xsang/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/xsang/.local/bin
make --keep-going LLVM=1 CROSS_COMPILE=hexagon-linux- --jobs=72 KCFLAGS= -Wno-error=return-type -Wreturn-type -funsigned-char -Wundef W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash drivers/hid/
make[1]: Entering directory '/home/xsang/linux/build_dir'
GEN Makefile
GENSEED scripts/basic/randstruct.seed
WRAP arch/hexagon/include/generated/uapi/asm/ucontext.h
WRAP arch/hexagon/include/generated/uapi/asm/auxvec.h
WRAP arch/hexagon/include/generated/uapi/asm/bitsperlong.h
WRAP arch/hexagon/include/generated/uapi/asm/bpf_perf_event.h
WRAP arch/hexagon/include/generated/uapi/asm/errno.h
WRAP arch/hexagon/include/generated/uapi/asm/fcntl.h
UPD include/generated/uapi/linux/version.h
WRAP arch/hexagon/include/generated/uapi/asm/ioctl.h
WRAP arch/hexagon/include/generated/uapi/asm/ipcbuf.h
WRAP arch/hexagon/include/generated/uapi/asm/ioctls.h
WRAP arch/hexagon/include/generated/uapi/asm/mman.h
WRAP arch/hexagon/include/generated/uapi/asm/msgbuf.h
WRAP arch/hexagon/include/generated/uapi/asm/poll.h
WRAP arch/hexagon/include/generated/uapi/asm/posix_types.h
WRAP arch/hexagon/include/generated/uapi/asm/resource.h
WRAP arch/hexagon/include/generated/uapi/asm/sembuf.h
WRAP arch/hexagon/include/generated/uapi/asm/shmbuf.h
WRAP arch/hexagon/include/generated/uapi/asm/siginfo.h
WRAP arch/hexagon/include/generated/uapi/asm/sockios.h
WRAP arch/hexagon/include/generated/uapi/asm/socket.h
WRAP arch/hexagon/include/generated/uapi/asm/stat.h
WRAP arch/hexagon/include/generated/uapi/asm/statfs.h
WRAP arch/hexagon/include/generated/uapi/asm/termbits.h
WRAP arch/hexagon/include/generated/uapi/asm/types.h
WRAP arch/hexagon/include/generated/uapi/asm/termios.h
SYSHDR arch/hexagon/include/generated/uapi/asm/unistd_32.h
UPD include/config/kernel.release
UPD include/generated/compile.h
HOSTCC scripts/dtc/dtc.o
HOSTCC scripts/dtc/flattree.o
HOSTCC scripts/dtc/fstree.o
HOSTCC scripts/dtc/data.o
HOSTCC scripts/dtc/livetree.o
HOSTCC scripts/dtc/treesource.o
HOSTCC scripts/dtc/srcpos.o
HOSTCC scripts/dtc/checks.o
HOSTCC scripts/dtc/util.o
LEX scripts/dtc/dtc-lexer.lex.c
YACC scripts/dtc/dtc-parser.tab.[ch]
HOSTCC scripts/dtc/libfdt/fdt.o
HOSTCC scripts/dtc/libfdt/fdt_ro.o
UPD include/generated/utsrelease.h
HOSTCC scripts/dtc/libfdt/fdt_wip.o
HOSTCC scripts/dtc/libfdt/fdt_sw.o
HOSTCC scripts/dtc/libfdt/fdt_rw.o
HOSTCC scripts/dtc/libfdt/fdt_strerror.o
HOSTCC scripts/dtc/libfdt/fdt_empty_tree.o
HOSTCC scripts/dtc/libfdt/fdt_addresses.o
HOSTCC scripts/dtc/libfdt/fdt_overlay.o
HOSTCC scripts/dtc/fdtoverlay.o
WRAP arch/hexagon/include/generated/asm/extable.h
WRAP arch/hexagon/include/generated/asm/iomap.h
WRAP arch/hexagon/include/generated/asm/kvm_para.h
WRAP arch/hexagon/include/generated/asm/mcs_spinlock.h
WRAP arch/hexagon/include/generated/asm/text-patching.h
WRAP arch/hexagon/include/generated/asm/archrandom.h
WRAP arch/hexagon/include/generated/asm/barrier.h
WRAP arch/hexagon/include/generated/asm/bug.h
WRAP arch/hexagon/include/generated/asm/cfi.h
WRAP arch/hexagon/include/generated/asm/compat.h
WRAP arch/hexagon/include/generated/asm/current.h
WRAP arch/hexagon/include/generated/asm/device.h
WRAP arch/hexagon/include/generated/asm/div64.h
WRAP arch/hexagon/include/generated/asm/dma-mapping.h
WRAP arch/hexagon/include/generated/asm/emergency-restart.h
WRAP arch/hexagon/include/generated/asm/ftrace.h
WRAP arch/hexagon/include/generated/asm/hardirq.h
WRAP arch/hexagon/include/generated/asm/hw_irq.h
WRAP arch/hexagon/include/generated/asm/irq_regs.h
WRAP arch/hexagon/include/generated/asm/irq_work.h
WRAP arch/hexagon/include/generated/asm/kdebug.h
WRAP arch/hexagon/include/generated/asm/kmap_size.h
WRAP arch/hexagon/include/generated/asm/kprobes.h
WRAP arch/hexagon/include/generated/asm/local.h
WRAP arch/hexagon/include/generated/asm/local64.h
WRAP arch/hexagon/include/generated/asm/mmiowb.h
WRAP arch/hexagon/include/generated/asm/module.h
WRAP arch/hexagon/include/generated/asm/module.lds.h
WRAP arch/hexagon/include/generated/asm/msi.h
WRAP arch/hexagon/include/generated/asm/pci.h
WRAP arch/hexagon/include/generated/asm/percpu.h
WRAP arch/hexagon/include/generated/asm/preempt.h
WRAP arch/hexagon/include/generated/asm/rqspinlock.h
WRAP arch/hexagon/include/generated/asm/runtime-const.h
WRAP arch/hexagon/include/generated/asm/rwonce.h
WRAP arch/hexagon/include/generated/asm/sections.h
WRAP arch/hexagon/include/generated/asm/serial.h
WRAP arch/hexagon/include/generated/asm/shmparam.h
WRAP arch/hexagon/include/generated/asm/simd.h
WRAP arch/hexagon/include/generated/asm/softirq_stack.h
WRAP arch/hexagon/include/generated/asm/topology.h
WRAP arch/hexagon/include/generated/asm/trace_clock.h
WRAP arch/hexagon/include/generated/asm/vga.h
WRAP arch/hexagon/include/generated/asm/video.h
WRAP arch/hexagon/include/generated/asm/word-at-a-time.h
WRAP arch/hexagon/include/generated/asm/xor.h
SYSTBL arch/hexagon/include/generated/asm/syscall_table_32.h
HOSTCC scripts/dtc/dtc-lexer.lex.o
HOSTCC scripts/dtc/dtc-parser.tab.o
HOSTLD scripts/dtc/fdtoverlay
HOSTLD scripts/dtc/dtc
HOSTCC scripts/kallsyms
HOSTCC scripts/asn1_compiler
HOSTCC scripts/insert-sys-cert
CC scripts/mod/empty.o
HOSTCC scripts/mod/mk_elfconfig
CC scripts/mod/devicetable-offsets.s
UPD scripts/mod/devicetable-offsets.h
MKELF scripts/mod/elfconfig.h
HOSTCC scripts/mod/modpost.o
HOSTCC scripts/mod/file2alias.o
HOSTCC scripts/mod/sumversion.o
HOSTCC scripts/mod/symsearch.o
HOSTLD scripts/mod/modpost
CC kernel/bounds.s
CHKSHA1 ../include/linux/atomic/atomic-arch-fallback.h
CHKSHA1 ../include/linux/atomic/atomic-instrumented.h
CHKSHA1 ../include/linux/atomic/atomic-long.h
UPD include/generated/timeconst.h
UPD include/generated/bounds.h
CC arch/hexagon/kernel/asm-offsets.s
UPD include/generated/asm-offsets.h
CALL ../scripts/checksyscalls.sh
CC drivers/hid/hid-core.o
CC drivers/hid/hid-input.o
CC drivers/hid/hid-quirks.o
CC drivers/hid/hid-debug.o
CC drivers/hid/hid-haptic.o
CC [M] drivers/hid/usbhid/hid-core.o
CC drivers/hid/hidraw.o
CC [M] drivers/hid/usbhid/hiddev.o
CC [M] drivers/hid/usbhid/hid-pidff.o
CC drivers/hid/hid-a4tech.o
CC drivers/hid/hid-alps.o
CC drivers/hid/hid-apple.o
CC drivers/hid/hid-belkin.o
CC drivers/hid/hid-cherry.o
CC drivers/hid/hid-cmedia.o
CC drivers/hid/hid-cougar.o
CC drivers/hid/hid-ezkey.o
CC drivers/hid/hid-icade.o
CC drivers/hid/hid-ite.o
CC drivers/hid/hid-jabra.o
CC drivers/hid/hid-kensington.o
CC drivers/hid/hid-ortek.o
CC drivers/hid/hid-razer.o
CC drivers/hid/hid-rmi.o
CC drivers/hid/hid-saitek.o
CC drivers/hid/hid-sjoy.o
CC drivers/hid/hid-tivo.o
CC drivers/hid/hid-udraw-ps3.o
CC drivers/hid/hid-led.o
CC drivers/hid/hid-wiimote-core.o
CC drivers/hid/hid-wiimote-modules.o
CC drivers/hid/hid-wiimote-debug.o
CC [M] drivers/hid/uhid.o
CC [M] drivers/hid/hid-generic.o
CC [M] drivers/hid/hid-axff.o
CC [M] drivers/hid/hid-appleir.o
CC [M] drivers/hid/hid-asus.o
CC [M] drivers/hid/hid-aureal.o
CC [M] drivers/hid/hid-betopff.o
CC [M] drivers/hid/hid-bigbenff.o
CC [M] drivers/hid/hid-chicony.o
CC [M] drivers/hid/hid-corsair.o
CC [M] drivers/hid/hid-corsair-void.o
CC [M] drivers/hid/hid-cp2112.o
CC [M] drivers/hid/hid-cypress.o
CC [M] drivers/hid/hid-emsff.o
CC [M] drivers/hid/hid-elan.o
CC [M] drivers/hid/hid-elo.o
CC [M] drivers/hid/hid-gembird.o
CC [M] drivers/hid/hid-gfrm.o
CC [M] drivers/hid/hid-vivaldi-common.o
CC [M] drivers/hid/hid-google-stadiaff.o
CC [M] drivers/hid/hid-vivaldi.o
CC [M] drivers/hid/hid-gt683r.o
CC [M] drivers/hid/hid-gyration.o
CC [M] drivers/hid/hid-holtek-kbd.o
CC [M] drivers/hid/hid-holtek-mouse.o
CC [M] drivers/hid/hid-holtekff.o
CC [M] drivers/hid/hid-kye.o
CC [M] drivers/hid/hid-kysona.o
CC [M] drivers/hid/hid-letsketch.o
CC [M] drivers/hid/hid-macally.o
CC [M] drivers/hid/hid-magicmouse.o
CC [M] drivers/hid/hid-mcp2221.o
CC [M] drivers/hid/hid-megaworld.o
CC [M] drivers/hid/hid-microsoft.o
CC [M] drivers/hid/hid-nintendo.o
CC [M] drivers/hid/hid-nti.o
CC [M] drivers/hid/hid-pl.o
CC [M] drivers/hid/hid-penmount.o
CC [M] drivers/hid/hid-picolcd_core.o
CC [M] drivers/hid/hid-picolcd_fb.o
CC [M] drivers/hid/hid-picolcd_backlight.o
CC [M] drivers/hid/hid-picolcd_leds.o
CC [M] drivers/hid/hid-picolcd_cir.o
CC [M] drivers/hid/hid-picolcd_debugfs.o
../drivers/hid/hid-haptic.c:13:6: error: redefinition of 'hid_haptic_feature_mapping'
13 | void hid_haptic_feature_mapping(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.h:83:6: note: previous definition is here
83 | void hid_haptic_feature_mapping(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.c:51:6: error: redefinition of 'hid_haptic_check_pressure_unit'
51 | bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
| ^
../drivers/hid/hid-haptic.h:89:6: note: previous definition is here
89 | bool hid_haptic_check_pressure_unit(struct hid_haptic_device *haptic,
| ^
../drivers/hid/hid-haptic.c:65:5: error: redefinition of 'hid_haptic_input_mapping'
65 | int hid_haptic_input_mapping(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.h:95:5: note: previous definition is here
95 | int hid_haptic_input_mapping(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.c:81:5: error: redefinition of 'hid_haptic_input_configured'
81 | int hid_haptic_input_configured(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.h:104:5: note: previous definition is here
104 | int hid_haptic_input_configured(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.c:403:5: error: redefinition of 'hid_haptic_init'
403 | int hid_haptic_init(struct hid_device *hdev,
| ^
../drivers/hid/hid-haptic.h:114:5: note: previous definition is here
114 | int hid_haptic_init(struct CC [M] drivers/hid/hid-redragon.o
hid_device *hdev, struct hid_haptic_device **haptic_ptr)
| ^
../drivers/hid/hid-haptic.c:569:6: error: redefinition of 'hid_haptic_pressure_reset'
569 | void hid_haptic_pressure_reset(struct hid_haptic_device *haptic)
| ^
../drivers/hid/hid-haptic.h:126:6: note: previous definition is here
126 | void hid_haptic_pressure_reset(struct hid_haptic_device *haptic) {}
| ^
../drivers/hid/hid-haptic.c:575:6: error: redefinition of 'hid_haptic_pressure_increase'
575 | void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
| ^
../drivers/hid/hid-haptic.h:128:6: note: previous definition is here
128 | void hid_haptic_pressure_increase(struct hid_haptic_device *haptic,
| CC [M] drivers/hid/hid-retrode.o
^
7 errors generated.
make[5]: *** [../scripts/Makefile.build:287: drivers/hid/hid-haptic.o] Error 1
CC [M] drivers/hid/hid-roccat.o
CC [M] drivers/hid/hid-roccat-common.o
CC [M] drivers/hid/hid-roccat-arvo.o
CC [M] drivers/hid/hid-roccat-isku.o
CC [M] drivers/hid/hid-roccat-kone.o
CC [M] drivers/hid/hid-roccat-koneplus.o
CC [M] drivers/hid/hid-roccat-konepure.o
CC [M] drivers/hid/hid-roccat-kovaplus.o
CC [M] drivers/hid/hid-roccat-lua.o
CC [M] drivers/hid/hid-roccat-pyra.o
CC [M] drivers/hid/hid-roccat-ryos.o
CC [M] drivers/hid/hid-roccat-savu.o
CC [M] drivers/hid/hid-samsung.o
CC [M] drivers/hid/hid-sony.o
CC [M] drivers/hid/hid-steam.o
CC [M] drivers/hid/hid-sunplus.o
CC [M] drivers/hid/hid-gaff.o
CC [M] drivers/hid/hid-tmff.o
CC [M] drivers/hid/hid-thrustmaster.o
CC [M] drivers/hid/hid-uclogic-core.o
CC [M] drivers/hid/hid-uclogic-rdesc.o
CC [M] drivers/hid/hid-uclogic-params.o
CC [M] drivers/hid/hid-xinmo.o
CC [M] drivers/hid/hid-zpff.o
CC [M] drivers/hid/hid-vrc2.o
CC [M] drivers/hid/wacom_sys.o
CC [M] drivers/hid/wacom_wac.o
CC [M] drivers/hid/hid-waltop.o
CC [M] drivers/hid/hid-winwing.o
CC [M] drivers/hid/hid-uclogic-rdesc-test.o
LD [M] drivers/hid/usbhid/usbhid.o
LD [M] drivers/hid/hid-uclogic-test.o
LD [M] drivers/hid/hid-picolcd.o
LD [M] drivers/hid/hid-uclogic.o
LD [M] drivers/hid/wacom.o
make[5]: Target 'drivers/hid/' not remade because of errors.
make[4]: *** [../scripts/Makefile.build:554: drivers/hid] Error 2
make[4]: Target 'drivers/hid/' not remade because of errors.
make[3]: *** [../scripts/Makefile.build:554: drivers] Error 2
make[3]: Target 'drivers/hid/' not remade because of errors.
make[2]: *** [/home/xsang/linux/Makefile:2003: .] Error 2
make[2]: Target 'drivers/hid/' not remade because of errors.
make[1]: *** [/home/xsang/linux/Makefile:248: __sub-make] Error 2
make[1]: Target 'drivers/hid/' not remade because of errors.
make[1]: Leaving directory '/home/xsang/linux/build_dir'
make: *** [Makefile:248: __sub-make] Error 2
make: Target 'drivers/hid/' not remade because of errors.
> --
> Jonathan
>
^ permalink raw reply
* [PATCH] input: (xpad) Add New Legion Go Controller PID
From: Derek J. Clark @ 2025-07-31 0:39 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: Xino Ni, Zhixin Zhang, Mia Shao, Pierre-Loup A . Griffais,
Derek J . Clark, linux-input, linux-doc, linux-kernel
Adds additional PID for the Lenovo Legion Go detachable controllers.
A recent firmware update for the Lenovo Legion Go Controller MCU enabled
interoperability between it and the upcoming Lenovo Legion Go 2
controllers. As part of this change the PID was changed to 0x61eb. This is
the same PID that will be used by the Legion Go 2 when it ships.
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
drivers/input/joystick/xpad.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index c066a4da7c14..0baceea33a26 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -318,6 +318,7 @@ static const struct xpad_device {
{ 0x1689, 0xfd01, "Razer Onza Classic Edition", 0, XTYPE_XBOX360 },
{ 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
{ 0x17ef, 0x6182, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 },
+ { 0x17ef, 0x61eb, "Lenovo Legion Controller for Windows", 0, XTYPE_XBOX360 },
{ 0x1949, 0x041a, "Amazon Game Controller", 0, XTYPE_XBOX360 },
{ 0x1a86, 0xe310, "Legion Go S", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
--
2.50.1
^ permalink raw reply related
* Re: [PATCH] Input: goodix - add support for polling on devices without IRQ line
From: Marek Vasut @ 2025-07-30 22:14 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Marek Vasut, linux-input, Dave Stevenson, Hans de Goede,
linux-renesas-soc
In-Reply-To: <csjblrulwqcmcjvyzbetng6o3ct4xedff26nvgwlb56fkqphew@rp63nsl7reuk>
On 6/30/25 5:40 PM, Dmitry Torokhov wrote:
> On Mon, Jun 30, 2025 at 11:44:03AM +0200, Marek Vasut wrote:
>> On 6/30/25 3:32 AM, Dmitry Torokhov wrote:
>>> Hi Marek,
>>
>> Hi,
>>
>>> On Tue, Jun 10, 2025 at 02:54:12AM +0200, Marek Vasut wrote:
>>>> Add the capability of polling the touch controller for events every
>>>> 16ms, which is useful on hardware that did integrate this touch
>>>> controller, but did not integrate the IRQ line, like the RasPi .
>>>>
>>>> Make use of the generic input poller code. Factor out the code
>>>> from goodix_ts_irq_handler() into generic goodix_ts_handler(), so
>>>> it can be used both by the IRQ handler and poller callback.
>>>>
>>>> Use of_client->irq to find out whether the interrupt line is present
>>>> or not, independent of whether this is OF or ACPI system. It is not
>>>> possible to register poller in case request_irq() fails, because the
>>>> request_irq() in this driver is deliberately called after the input
>>>> device was registered, and registering the generic poller at that point
>>>> is too late already.
>>>>
>>>> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
>>>
>>> There was another version of this patch that was submitted and reviewed
>>> by Hans, so I merged it.
>>
>> Is it the following patch, with malformed commit message and some odd
>> "LF-15225" subject tag ?
>>
>> https://patchwork.kernel.org/project/linux-input/patch/20250522020418.1963422-1-qijian.guo@nxp.com/
>
> Yes, I cleaned the commit message.
OK. I just tested that patch and it does work on my hardware too.
--
Best regards,
Marek Vasut
^ permalink raw reply
* [PATCH v3] platform/x86: Add WMI driver for Redmibook keyboard.
From: Gladyshev Ilya @ 2025-07-30 18:56 UTC (permalink / raw)
To: foxido
Cc: w_armin, linux-input, nikita.nikita.krasnov, Hans de Goede,
Ilpo Järvinen, linux-kernel, platform-driver-x86
This driver implements support for various Fn keys (like Cut) and Xiaomi
specific AI button.
Signed-off-by: Gladyshev Ilya <foxido@foxido.dev>
---
Changes since v2:
- Fix Kconfig dependencies
- Accept WMI buffers that are bigger than expected
Link to v2: https://lore.kernel.org/platform-driver-x86/20250729190528.8446-1-foxido@foxido.dev/
---
MAINTAINERS | 6 ++
drivers/platform/x86/Kconfig | 12 +++
drivers/platform/x86/Makefile | 1 +
drivers/platform/x86/redmi-wmi.c | 129 +++++++++++++++++++++++++++++++
4 files changed, 148 insertions(+)
create mode 100644 drivers/platform/x86/redmi-wmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c0b444e5fd5a..eb25fb10e751 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20965,6 +20965,12 @@ S: Maintained
T: git https://github.com/pkshih/rtw.git
F: drivers/net/wireless/realtek/rtw89/
+REDMIBOOK WMI DRIVERS
+M: Gladyshev Ilya <foxido@foxido.dev>
+L: platform-driver-x86@vger.kernel.org
+S: Maintained
+F: drivers/platform/x86/redmi-wmi.c
+
REDPINE WIRELESS DRIVER
L: linux-wireless@vger.kernel.org
S: Orphan
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index e5cbd58a99f3..9f98a7042e43 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -109,6 +109,18 @@ config XIAOMI_WMI
To compile this driver as a module, choose M here: the module will
be called xiaomi-wmi.
+config REDMI_WMI
+ tristate "Redmibook WMI key driver"
+ depends on ACPI_WMI
+ depends on INPUT
+ select INPUT_SPARSEKMAP
+ help
+ Say Y here if you want support for WMI-based hotkey events on
+ Xiaomi Redmibook devices.
+
+ To compile this driver as a module, choose M here: the module will
+ be called redmi-wmi.
+
config GIGABYTE_WMI
tristate "Gigabyte WMI temperature driver"
depends on ACPI_WMI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index bea87a85ae75..406dd0807ba7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_HUAWEI_WMI) += huawei-wmi.o
obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o
obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o
+obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o
obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o
# Acer
diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c
new file mode 100644
index 000000000000..3dbf4a40a4ff
--- /dev/null
+++ b/drivers/platform/x86/redmi-wmi.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0
+/* WMI driver for Xiaomi Redmibooks */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/unaligned.h>
+#include <linux/wmi.h>
+
+#include <uapi/linux/input-event-codes.h>
+
+#define WMI_REDMIBOOK_KEYBOARD_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF"
+
+#define AI_KEY_VALUE_MASK 0x00000100
+
+static const struct key_entry redmi_wmi_keymap[] = {
+ {KE_KEY, 0x00000201, {KEY_SELECTIVE_SCREENSHOT}},
+ {KE_KEY, 0x00000301, {KEY_ALL_APPLICATIONS}},
+ {KE_KEY, 0x00001b01, {KEY_SETUP}},
+
+ /* AI button has code for each position */
+ {KE_KEY, 0x00011801, {KEY_ASSISTANT}},
+ {KE_KEY, 0x00011901, {KEY_ASSISTANT}},
+
+ /* Keyboard backlight */
+ {KE_IGNORE, 0x00000501, {}},
+ {KE_IGNORE, 0x00800501, {}},
+ {KE_IGNORE, 0x00050501, {}},
+ {KE_IGNORE, 0x000a0501, {}},
+
+ {KE_END}
+};
+
+struct redmi_wmi {
+ struct input_dev *input_dev;
+ /* Protects the key event sequence */
+ struct mutex key_lock;
+};
+
+static int redmi_wmi_probe(struct wmi_device *wdev, const void *context)
+{
+ struct redmi_wmi *data;
+ int err;
+
+ /* Init dev */
+ data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&wdev->dev, data);
+
+ err = devm_mutex_init(&wdev->dev, &data->key_lock);
+ if (err)
+ return err;
+
+ /* Setup input device */
+ data->input_dev = devm_input_allocate_device(&wdev->dev);
+ if (!data->input_dev)
+ return -ENOMEM;
+ data->input_dev->name = "Redmibook WMI keys";
+ data->input_dev->phys = "wmi/input0";
+
+ err = sparse_keymap_setup(data->input_dev, redmi_wmi_keymap, NULL);
+ if (err)
+ return err;
+
+ return input_register_device(data->input_dev);
+}
+
+static void redmi_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
+{
+ struct redmi_wmi *data = dev_get_drvdata(&wdev->dev);
+ bool autorelease = true;
+ int value = 1;
+
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
+ return;
+ }
+
+ if (obj->buffer.length < 32) {
+ dev_err(&wdev->dev, "Invalid buffer length %u\n", obj->buffer.length);
+ return;
+ }
+
+ /* For linearizability */
+ guard(mutex)(&data->key_lock);
+
+ u32 payload = get_unaligned_le32(obj->buffer.pointer);
+ struct key_entry *entry = sparse_keymap_entry_from_scancode(data->input_dev, payload);
+
+ if (!entry) {
+ dev_dbg(&wdev->dev, "Unknown WMI event with payload %u", payload);
+ return;
+ }
+
+ /* AI key quirk */
+ if (entry->keycode == KEY_ASSISTANT) {
+ value = !(payload & AI_KEY_VALUE_MASK);
+ autorelease = false;
+ }
+
+ sparse_keymap_report_entry(data->input_dev, entry, value, autorelease);
+}
+
+static const struct wmi_device_id redmi_wmi_id_table[] = {
+ { .guid_string = WMI_REDMIBOOK_KEYBOARD_EVENT_GUID },
+ { }
+};
+
+static struct wmi_driver redmi_wmi_driver = {
+ .driver = {
+ .name = "redmi-wmi",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS
+ },
+ .id_table = redmi_wmi_id_table,
+ .probe = redmi_wmi_probe,
+ .notify = redmi_wmi_notify,
+ .no_singleton = true,
+};
+module_wmi_driver(redmi_wmi_driver);
+
+MODULE_DEVICE_TABLE(wmi, redmi_wmi_id_table);
+MODULE_AUTHOR("Gladyshev Ilya <foxido@foxido.dev>");
+MODULE_DESCRIPTION("Redmibook WMI driver");
+MODULE_LICENSE("GPL");
--
2.50.0
^ permalink raw reply related
* Re: [PATCH v2] platform/x86: Add WMI driver for Redmibook keyboard.
From: Armin Wolf @ 2025-07-30 17:52 UTC (permalink / raw)
To: Gladyshev Ilya
Cc: linux-input, nikita.nikita.krasnov, Hans de Goede,
Ilpo Järvinen, linux-kernel, platform-driver-x86
In-Reply-To: <26cdd07b-7b5c-4636-99c2-6c078c6a48bc@foxido.dev>
Am 30.07.25 um 19:37 schrieb Gladyshev Ilya:
> Thx for review
>
> On 7/30/25 19:33, Armin Wolf wrote:
>>> +
>>> + if (obj->type != ACPI_TYPE_BUFFER) {
>>> + dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
>>> + return;
>>> + }
>>> +
>>> + if (obj->buffer.length != 32) {
>>
>> Please also accept oversized buffers.
>>
> Sorry if this is a stupid question, but isn't any size other than 32 a
> sign of a firmware bug?
>
Theoretically yes, practically no because the Windows WMI-ACPI driver does accept oversized buffers :(.
In order to support ACPI implementations that rely on the Windows behavior your driver should also
accept oversized buffers.
>>> +
>>> + /* AI key quirk */
>>> + if (entry->keycode == KEY_ASSISTANT) {
>>> + value = !(payload & AI_KEY_VALUE_MASK);
>>
>> I would rather check the payload for 0x00011901 here.
> Personally I prefer to think about it as "some value encoded in
> payload" rather than "is it AI button release payload?", because in
> the latter case alternatives are more unclear. It's just a preference,
> btw, would fix if you insist.
>
Alright, in this case you can ignore my comment.
Thanks,
Armin Wolf
> --
> Gladyshev Ilya
>
^ permalink raw reply
* Re: [PATCH v2] platform/x86: Add WMI driver for Redmibook keyboard.
From: Gladyshev Ilya @ 2025-07-30 17:37 UTC (permalink / raw)
To: Armin Wolf
Cc: linux-input, nikita.nikita.krasnov, Hans de Goede,
Ilpo Järvinen, linux-kernel, platform-driver-x86
In-Reply-To: <8e7f2cde-f068-4696-8298-f83619dfaf76@gmx.de>
Thx for review
On 7/30/25 19:33, Armin Wolf wrote:
>> +
>> + if (obj->type != ACPI_TYPE_BUFFER) {
>> + dev_err(&wdev->dev, "Bad response type %u\n", obj->type);
>> + return;
>> + }
>> +
>> + if (obj->buffer.length != 32) {
>
> Please also accept oversized buffers.
>
Sorry if this is a stupid question, but isn't any size other than 32 a
sign of a firmware bug?
>> +
>> + /* AI key quirk */
>> + if (entry->keycode == KEY_ASSISTANT) {
>> + value = !(payload & AI_KEY_VALUE_MASK);
>
> I would rather check the payload for 0x00011901 here.
Personally I prefer to think about it as "some value encoded in payload"
rather than "is it AI button release payload?", because in the latter
case alternatives are more unclear. It's just a preference, btw, would
fix if you insist.
--
Gladyshev Ilya
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox