Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH 0/2] HID: appletb-kbd: fix UAF and mutex-in-atomic in inactivity timer
From: Aditya Garg @ 2026-04-28 16:44 UTC (permalink / raw)
  To: Jiri Kosina; +Cc: Sangyun Kim, bentiss, qasdev00, linux-input, linux-kernel
In-Reply-To: <s3qr2s36-n156-4srp-771s-20130s13760r@xreary.bet>



On 28 April 2026 10:03:03 pm IST, Jiri Kosina <jikos@kernel.org> wrote:
>On Mon, 20 Apr 2026, Aditya Garg wrote:
>
>> > This series addresses two defects in hid-appletb-kbd's inactivity
>> > timer subsystem.  The two patches target different bugs and are
>> > logically independent; they are sent together because they touch the
>> > same tear-down code and because the same maintainer will review both.
>> > 
>> > Patch 1 fixes a slab use-after-free with two related tear-down windows
>> > introduced by commit 38224c472a03 ("HID: appletb-kbd: fix slab
>> > use-after-free bug in appletb_kbd_probe"):
>> > 
>> >    A) Within "if (kbd->backlight_dev)" the order was
>> >       put_device() then timer_delete_sync().  A concurrent
>> >       hid_appletb_bl unbind between those two calls can drop the last
>> >       devm reference and free the backlight_device; the still-armed
>> >       inactivity timer softirq then dereferences the freed object
>> >       through backlight_device_set_brightness() -> mutex_lock(&ops_lock).
>> > 
>> >    B) The "if (kbd->backlight_dev)" block ran before
>> >       hid_hw_close()/hid_hw_stop(), so even after window A is closed a
>> >       late ".event" callback from the HID core (USB URB completion on
>> >       real hardware) can arrive between timer_delete_sync() and
>> >       put_device(), reach reset_inactivity_timer(), re-arm the timer
>> >       via mod_timer(), and reopen the same UAF.
>> > 
>> > Both windows produce the same KASAN slab-use-after-free on the object
>> > allocated by devm_backlight_device_register().  Patch 1 closes them
>> > together by moving hid_hw_close()/hid_hw_stop() before the backlight
>> > cleanup and, inside that cleanup block, calling timer_delete_sync()
>> > before put_device().  Shipping both as one commit avoids leaving
>> > stable kernels in a half-fixed state where only window A is closed.
>> > 
>> > Patch 2 fixes a separate "sleeping function called from invalid
>> > context" bug in the same subsystem.  The inactivity timer is a
>> > struct timer_list, so the callback runs in softirq context and calls
>> > backlight_device_set_brightness() -> mutex_lock() from atomic
>> > context; reset_inactivity_timer() has the same issue on the
>> > brightness-restore path (it is called from appletb_kbd_hid_event()
>> > and appletb_kbd_inp_event(), which run in softirq/IRQ context on
>> > real USB hardware).  Convert the inactivity timer to a delayed_work
>> > and defer the brightness-restore call to a dedicated work_struct so
>> > both sleeping calls run in process context.
>> > 
>> > Sangyun Kim (2):
>> >    HID: appletb-kbd: fix UAF in inactivity-timer cleanup path
>> >    HID: appletb-kbd: run inactivity autodim from workqueues
>> > 
>> >   drivers/hid/hid-appletb-kbd.c | 56 ++++++++++++++++++++++-------------
>> >   1 file changed, 36 insertions(+), 20 deletions(-)
>> > 
>> 
>> I had a very weird bug just once. And that was when I pressed fn key, upon
>> releasing, the touchbar mode did not restore to normal.
>
>You mean with this patch applied, correct?

Yes after applying that patch.

>

^ permalink raw reply

* Re: [PATCH] HID: uclogic: Fix regression of input name assignment
From: Jiri Kosina @ 2026-04-28 16:37 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: Benjamin Tissoires, Henry Martin, linux-input, linux-kernel
In-Reply-To: <20260428083321.126674-1-tiwai@suse.de>

On Tue, 28 Apr 2026, Takashi Iwai wrote:

> The previous fix for adding the devm_kasprintf() return check in the
> commit bd07f751208b ("HID: uclogic: Add NULL check in
> uclogic_input_configured()") changed the condition of hi->input->name
> assignment, and it resulted in missing the proper input device name
> when no custom suffix is defined.
> 
> Restore the conditional to the original content to address the
> regression.
> 
> Fixes: bd07f751208b ("HID: uclogic: Add NULL check in uclogic_input_configured()")
> Signed-off-by: Takashi Iwai <tiwai@suse.de>

Applied to hid.git#for-7.1/upstream-fixes, thanks Takashi.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: intel-thc-hid: Intel-quickspi: Fix some error codes
From: Jiri Kosina @ 2026-04-28 16:35 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Even Xu, Xinpeng Sun, Benjamin Tissoires, Mark Pearson,
	Srinivas Pandruvada, linux-input, linux-kernel, kernel-janitors
In-Reply-To: <aenFyk36rTnrD9s3@stanley.mountain>

On Thu, 23 Apr 2026, Dan Carpenter wrote:

> If we have a partial read that is supposed to be treated as failure but
> in this code we forgot to set the error code.  Return -EINVAL.
> 
> Fixes: 9d8d51735a3a ("HID: intel-thc-hid: intel-quickspi: Add HIDSPI protocol implementation")
> Signed-off-by: Dan Carpenter <error27@gmail.com>

Applied to hid.git#for-7.1/upstream-fixes, thanks Dan.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: hid-lenovo-go-s: restore OS_TYPE after resume from s2idle
From: Jiri Kosina @ 2026-04-28 16:34 UTC (permalink / raw)
  To: Matthew Schwartz
  Cc: derekjohn.clark, bentiss, mpearson-lenovo, linux-input,
	linux-kernel
In-Reply-To: <20260420181522.521627-1-matthew.schwartz@linux.dev>

On Mon, 20 Apr 2026, Matthew Schwartz wrote:

> The controller MCU does not persist OS_TYPE across power cycles. During
> s2idle resume, the USB device may be power-cycled, causing the OS_TYPE
> setting to revert to the default Windows value.
> 
> Add a reset_resume callback so that this is correctly restored after
> resume.
> 
> Reviewed-by: Derek J. Clark <derekjohn.clark@gmail.com>
> Signed-off-by: Matthew Schwartz <matthew.schwartz@linux.dev>

Applied, thanks. Next time, please don't forget to add Fixes: tag as well.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH 0/2] HID: appletb-kbd: fix UAF and mutex-in-atomic in inactivity timer
From: Jiri Kosina @ 2026-04-28 16:33 UTC (permalink / raw)
  To: Aditya Garg; +Cc: Sangyun Kim, bentiss, qasdev00, linux-input, linux-kernel
In-Reply-To: <MAUPR01MB115460F44776CC8E5E5EE7DC4B82F2@MAUPR01MB11546.INDPRD01.PROD.OUTLOOK.COM>

On Mon, 20 Apr 2026, Aditya Garg wrote:

> > This series addresses two defects in hid-appletb-kbd's inactivity
> > timer subsystem.  The two patches target different bugs and are
> > logically independent; they are sent together because they touch the
> > same tear-down code and because the same maintainer will review both.
> > 
> > Patch 1 fixes a slab use-after-free with two related tear-down windows
> > introduced by commit 38224c472a03 ("HID: appletb-kbd: fix slab
> > use-after-free bug in appletb_kbd_probe"):
> > 
> >    A) Within "if (kbd->backlight_dev)" the order was
> >       put_device() then timer_delete_sync().  A concurrent
> >       hid_appletb_bl unbind between those two calls can drop the last
> >       devm reference and free the backlight_device; the still-armed
> >       inactivity timer softirq then dereferences the freed object
> >       through backlight_device_set_brightness() -> mutex_lock(&ops_lock).
> > 
> >    B) The "if (kbd->backlight_dev)" block ran before
> >       hid_hw_close()/hid_hw_stop(), so even after window A is closed a
> >       late ".event" callback from the HID core (USB URB completion on
> >       real hardware) can arrive between timer_delete_sync() and
> >       put_device(), reach reset_inactivity_timer(), re-arm the timer
> >       via mod_timer(), and reopen the same UAF.
> > 
> > Both windows produce the same KASAN slab-use-after-free on the object
> > allocated by devm_backlight_device_register().  Patch 1 closes them
> > together by moving hid_hw_close()/hid_hw_stop() before the backlight
> > cleanup and, inside that cleanup block, calling timer_delete_sync()
> > before put_device().  Shipping both as one commit avoids leaving
> > stable kernels in a half-fixed state where only window A is closed.
> > 
> > Patch 2 fixes a separate "sleeping function called from invalid
> > context" bug in the same subsystem.  The inactivity timer is a
> > struct timer_list, so the callback runs in softirq context and calls
> > backlight_device_set_brightness() -> mutex_lock() from atomic
> > context; reset_inactivity_timer() has the same issue on the
> > brightness-restore path (it is called from appletb_kbd_hid_event()
> > and appletb_kbd_inp_event(), which run in softirq/IRQ context on
> > real USB hardware).  Convert the inactivity timer to a delayed_work
> > and defer the brightness-restore call to a dedicated work_struct so
> > both sleeping calls run in process context.
> > 
> > Sangyun Kim (2):
> >    HID: appletb-kbd: fix UAF in inactivity-timer cleanup path
> >    HID: appletb-kbd: run inactivity autodim from workqueues
> > 
> >   drivers/hid/hid-appletb-kbd.c | 56 ++++++++++++++++++++++-------------
> >   1 file changed, 36 insertions(+), 20 deletions(-)
> > 
> 
> I had a very weird bug just once. And that was when I pressed fn key, upon
> releasing, the touchbar mode did not restore to normal.

You mean with this patch applied, correct?

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: elan: Add support for ELAN SB974D touchpad
From: Jiri Kosina @ 2026-04-28 16:30 UTC (permalink / raw)
  To: Damien Dejean
  Cc: Benjamin Tissoires, linux-input, linux-kernel, Kornel Dulęba
In-Reply-To: <20260414133858.3992799-1-damiendejean@google.com>

On Tue, 14 Apr 2026, Damien Dejean wrote:

> Elan SB974D touchpad uses ELAN_MT_I2C format to send HID reports. Add an
> entry to match for the device and parse its vendor specific format.
> 
> Signed-off-by: Damien Dejean <damiendejean@google.com>
> Signed-off-by: Kornel Dulęba <korneld@google.com>
> ---
>  drivers/hid/hid-elan.c | 1 +
>  drivers/hid/hid-ids.h  | 1 +
>  2 files changed, 2 insertions(+)
> 
> diff --git a/drivers/hid/hid-elan.c b/drivers/hid/hid-elan.c
> index 76d93fc48f6a..0190ad567ce4 100644
> --- a/drivers/hid/hid-elan.c
> +++ b/drivers/hid/hid-elan.c
> @@ -513,6 +513,7 @@ static const struct hid_device_id elan_devices[] = {
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_HP_X2_10_COVER),
>  	  .driver_data = ELAN_HAS_LED },
>  	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_TOSHIBA_CLICK_L9W) },
> +	{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_SB974D) },
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(hid, elan_devices);
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 0cf63742315b..8cfec7dced66 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -455,6 +455,7 @@
>  #define USB_DEVICE_ID_EDIFIER_QR30	0xa101	/* EDIFIER Hal0 2.0 SE */
>  
>  #define USB_VENDOR_ID_ELAN		0x04f3
> +#define USB_DEVICE_ID_SB974D            0x0400
>  #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W	0x0401
>  #define USB_DEVICE_ID_HP_X2		0x074d
>  #define USB_DEVICE_ID_HP_X2_10_COVER	0x0755

Applied to hid.git#for-7.1/upstream-fixes, thanks.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: sony: add missing size validation for Rock Band 3 Pro instruments
From: Jiri Kosina @ 2026-04-28 16:29 UTC (permalink / raw)
  To: Rosalie Wanders; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260412011203.8921-1-rosalie@mailbox.org>

On Sun, 12 Apr 2026, Rosalie Wanders wrote:

> This commit adds the missing size validation for Rock Band 3 PS3 Pro
> instruments in sony_raw_event(), this prevents a malicious device from
> allowing hid-sony to read out of bounds of the provided buffer.

Applied to hid.git#for-7.1/upstream-fixes, thanks.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: sony: add missing size validation for SMK-Link remotes
From: Jiri Kosina @ 2026-04-28 16:27 UTC (permalink / raw)
  To: Rosalie Wanders; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260412010806.7997-2-rosalie@mailbox.org>

On Sun, 12 Apr 2026, Rosalie Wanders wrote:

> This commit adds the missing size validation for SMK-Link remotes in
> sony_raw_event(), this prevents a malicious device from allowing
> hid-sony to read out of bounds of the provided buffer.
> 
> I do not own these devices so the size check only forces that the buffer
> is large enough for nsg_mrxu_parse_report().

Applied to hid.git#for-7.1/upstream-fixes, thanks.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: sony: remove unneeded WARN_ON() in sony_leds_init()
From: Jiri Kosina @ 2026-04-28 16:25 UTC (permalink / raw)
  To: Rosalie Wanders; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260411153247.5920-2-rosalie@mailbox.org>

On Sat, 11 Apr 2026, Rosalie Wanders wrote:

> This commit removes the unneeded WARN_ON() macro usage in
> sony_leds_init(), this is unneeded because the sony_leds_init() function
> call is already gated behind a SONY_LED_SUPPORT check in
> sony_input_configured()
> 
> Signed-off-by: Rosalie Wanders <rosalie@mailbox.org>

Applied to hid.git#for-7.1/upstream-fixes, thanks.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH v2] HID: ft260: validate i2c input report length
From: Jiri Kosina @ 2026-04-28 16:25 UTC (permalink / raw)
  To: Michael Zaidman
  Cc: benjamin.tissoires, linux-input, linux-kernel, sebasjosue84
In-Reply-To: <20260411062437.279838-1-michael.zaidman@gmail.com>

On Sat, 11 Apr 2026, Michael Zaidman wrote:

> Add two checks to ft260_raw_event() to prevent out-of-bounds reads
> from malicious or malfunctioning devices:
> 
> First, reject reports shorter than the 2-byte header (report ID +
> length fields). Without this, even accessing xfer->length on a
> 1-byte report is an OOB read.
> 
> Second, validate xfer->length against the actual data capacity of
> the received HID report. Each I2C data report ID (0xD0 through
> 0xDE) defines a different report size in the HID descriptor, so the
> available payload varies per report. A corrupted length field could
> cause memcpy to read beyond the report buffer.
> 
> Reported-by: Sebastián Josué Alba Vives <sebasjosue84@gmail.com>
> Signed-off-by: Michael Zaidman <michael.zaidman@gmail.com>
> ---
> Changes in v2:
> - Add minimum report size check before accessing header fields to
>   prevent OOB read on truncated reports (size < 2)
> 
> Tested on FT260 with I2C-attached EEPROM (24c02) behind PCA9548
> mux switches. Verified reads of various sizes (1-4 bytes using
> report ID 0xD0, and larger reads using higher report IDs) with
> debug tracing enabled, confirming xfer->length is correctly
> validated against the HID report size for each report ID.

Applied to hid.git#for-7.1/upstream-fixes, thanks Michael.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH] HID: sony: fix incorrect force-feedback check in sony_suspend()
From: Jiri Kosina @ 2026-04-28 16:23 UTC (permalink / raw)
  To: Rosalie Wanders; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20260410195353.4321-2-rosalie@mailbox.org>

On Fri, 10 Apr 2026, Rosalie Wanders wrote:

> This commit fixes the incorrect force-feedback check in sony_suspend(),
> without this the check will always be true due to checking a constant
> define that is never 0.
> 
> Signed-off-by: Rosalie Wanders <rosalie@mailbox.org>
> ---
>  drivers/hid/hid-sony.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index 83e82a0a3327..9cfea6f40ec2 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -2456,11 +2456,10 @@ static void sony_remove(struct hid_device *hdev)
>  static int sony_suspend(struct hid_device *hdev, pm_message_t message)
>  {
>  #ifdef CONFIG_SONY_FF
> +	struct sony_sc *sc = hid_get_drvdata(hdev);
>  
>  	/* On suspend stop any running force-feedback events */
> -	if (SONY_FF_SUPPORT) {
> -		struct sony_sc *sc = hid_get_drvdata(hdev);
> -
> +	if (sc->quirks & SONY_FF_SUPPORT) {
>  		sc->left = sc->right = 0;
>  		sony_send_output_report(sc);

Applied, thanks.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH 02/10] iio: orientation: adapt to hid_sensor_remove_trigger() API change
From: Jonathan Cameron @ 2026-04-28 16:12 UTC (permalink / raw)
  To: Sanjay Chitroda
  Cc: Andy Shevchenko, jikos, srinivas.pandruvada, dlechner, nuno.sa,
	andy, sakari.ailus, linux-input, linux-iio, linux-kernel
In-Reply-To: <39108D2A-3C3F-40B5-9A9B-5B3572A63922@gmail.com>

On Tue, 28 Apr 2026 16:32:02 +0530
Sanjay Chitroda <sanjayembeddedse@gmail.com> wrote:

> On 28 April 2026 1:52:48 pm IST, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> >On Tue, Apr 28, 2026 at 12:46:05PM +0530, Sanjay Chitroda wrote:
> >  
> >> Update the driver to match the updated hid_sensor_remove_trigger()
> >> prototype, which no longer requires struct iio_dev.  
> >
> >You haven't compiled the previous patch, right?
> >This is not the way how all this should be done.
> >
> >Also NAK to the patch 1 as even unused parameter is there for the sake of
> >consistency. The prototype to allocate and other in the similar group all
> >have it.
> >  
> Hi Andy,
> 
> Thank for the review comment.
> I agree your point for consistency.
> 
> However primary object of this pre-series is to prepare the drivers for devm_ conversation.
> 
> I would prepare devm_ wrapper for the hid_sensor_setup_trigger() and respective resource release hid_sensor_remove_trigger().
> 
> ...  devm_hid_sensor_setup_trigger( ... ) 
> {
>        ...
>        .. hid_sensor_setup_trigger();
>        ....
>        devm_add_action_or_release(dev, hid_sensor_remove_trigger, attrb)
>        ......
> }
> 
> I observed that many HID IIO drivers are not covered fully with managed API.
> 
> This devm_* sensor setup trigger would use across multiple HID IIO sensors and will go step forward for managed API support.
> 


Show us what that looks like with this series squashed into a single patch
as a percursor. It might be worth doing as part of that larger work - on its
own it's not justified.

A series must be applicable 1 patch at a time without breaking anything.

Jonathan


^ permalink raw reply

* Re: [PATCH v2 1/4] HID: pass the buffer size to hid_report_raw_event
From: Jiri Kosina @ 2026-04-28 16:02 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Icenowy Zheng, Filipe Laíns, Bastien Nocera, Ping Cheng,
	Jason Gerecke, Viresh Kumar, Johan Hovold, Alex Elder,
	Greg Kroah-Hartman, Lee Jones, linux-input, linux-kernel,
	greybus-dev, linux-staging, linux-usb, stable
In-Reply-To: <aeXdKFJe8JyatqLR@beelink>

On Mon, 20 Apr 2026, Benjamin Tissoires wrote:


> > Oops, "ghid" is misspelled here...
> 
> Damn, you're correct. Sorry.
> 
> Jiri, do you want me to send v3? Or can you fix it while applying?

Normally I'd fix it manually while applying, but kernel test robot found 
newly added compiler warnings in the meantime, so please include that in 
the followup v3.

Thanks,

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* [PATCH v4 6/6 RESEND] mfd: motorola-cpcap: add support for Mot CPCAP composition
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

Add a MFD subdevice composition used in Tegra20 based Mot board
(Motorola Atrix 4G and Droid X2).

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/mfd/motorola-cpcap.c | 50 ++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
index 516d1e33affa..fdec92f5c6b0 100644
--- a/drivers/mfd/motorola-cpcap.c
+++ b/drivers/mfd/motorola-cpcap.c
@@ -335,6 +335,54 @@ static const struct cpcap_chip_data cpcap_mapphone_data = {
 	.num_devices = ARRAY_SIZE(cpcap_mapphone_mfd_devices),
 };
 
+/*
+ * The Mot board features a USB-PHY and charger similar to the ones in
+ * Mapphone; however, because Mot is based on Tegra20, it is incompatible
+ * with the existing implementation, which is tightly interconnected with
+ * the OMAP USB PHY.
+ */
+static const struct mfd_cell cpcap_mot_mfd_devices[] = {
+	{
+		.name          = "cpcap_adc",
+		.of_compatible = "motorola,mot-cpcap-adc",
+	}, {
+		.name          = "cpcap_battery",
+		.of_compatible = "motorola,cpcap-battery",
+	}, {
+		.name          = "cpcap-regulator",
+		.of_compatible = "motorola,mot-cpcap-regulator",
+	}, {
+		.name          = "cpcap-rtc",
+		.of_compatible = "motorola,cpcap-rtc",
+	}, {
+		.name          = "cpcap-pwrbutton",
+		.of_compatible = "motorola,cpcap-pwrbutton",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 0,
+		.of_compatible = "motorola,cpcap-led-red",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 1,
+		.of_compatible = "motorola,cpcap-led-green",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 2,
+		.of_compatible = "motorola,cpcap-led-blue",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 3,
+		.of_compatible = "motorola,cpcap-led-adl",
+	}, {
+		.name          = "cpcap-codec",
+	},
+};
+
+static const struct cpcap_chip_data cpcap_mot_data = {
+	.mfd_devices = cpcap_mot_mfd_devices,
+	.num_devices = ARRAY_SIZE(cpcap_mot_mfd_devices),
+};
+
 static int cpcap_probe(struct spi_device *spi)
 {
 	struct cpcap_ddata *cpcap;
@@ -389,6 +437,7 @@ static int cpcap_probe(struct spi_device *spi)
 static const struct of_device_id cpcap_of_match[] = {
 	{ .compatible = "motorola,cpcap", .data = &cpcap_default_data },
 	{ .compatible = "motorola,mapphone-cpcap", .data = &cpcap_mapphone_data	},
+	{ .compatible = "motorola,mot-cpcap", .data = &cpcap_mot_data },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, cpcap_of_match);
@@ -396,6 +445,7 @@ MODULE_DEVICE_TABLE(of, cpcap_of_match);
 static const struct spi_device_id cpcap_spi_ids[] = {
 	{ .name = "cpcap", .driver_data = (kernel_ulong_t)&cpcap_default_data },
 	{ .name = "mapphone-cpcap", .driver_data = (kernel_ulong_t)&cpcap_mapphone_data },
+	{ .name = "mot-cpcap", .driver_data = (kernel_ulong_t)&cpcap_mot_data },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(spi, cpcap_spi_ids);
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 5/6 RESEND] mfd: motorola-cpcap: diverge configuration per-board
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

MFD have rigid subdevice structure which does not allow flexible dynamic
subdevice linking. Address this by diverging CPCAP subdevice composition
to take into account board specific configuration.

Create a common default subdevice composition, rename existing subdevice
composition into cpcap_mapphone_mfd_devices since it targets mainly
Mapphone board.

Removed st,6556002 as it is no longer applicable to all cases and
duplicates motorola,cpcap, which is used as the default composition.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/mfd/motorola-cpcap.c | 101 ++++++++++++++++++++++++++++-------
 1 file changed, 83 insertions(+), 18 deletions(-)

diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
index d8243b956f87..516d1e33affa 100644
--- a/drivers/mfd/motorola-cpcap.c
+++ b/drivers/mfd/motorola-cpcap.c
@@ -12,6 +12,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
+#include <linux/property.h>
 #include <linux/regmap.h>
 #include <linux/sysfs.h>
 
@@ -24,10 +25,16 @@
 #define CPCAP_REGISTER_SIZE	4
 #define CPCAP_REGISTER_BITS	16
 
+struct cpcap_chip_data {
+	const struct mfd_cell *mfd_devices;
+	unsigned int num_devices;
+};
+
 struct cpcap_ddata {
 	struct spi_device *spi;
 	struct regmap_irq *irqs;
 	struct regmap_irq_chip_data *irqdata[CPCAP_NR_IRQ_CHIPS];
+	const struct cpcap_chip_data *cdata;
 	const struct regmap_config *regmap_conf;
 	struct regmap *regmap;
 };
@@ -195,20 +202,6 @@ static int cpcap_init_irq(struct cpcap_ddata *cpcap)
 	return 0;
 }
 
-static const struct of_device_id cpcap_of_match[] = {
-	{ .compatible = "motorola,cpcap", },
-	{ .compatible = "st,6556002", },
-	{},
-};
-MODULE_DEVICE_TABLE(of, cpcap_of_match);
-
-static const struct spi_device_id cpcap_spi_ids[] = {
-	{ .name = "cpcap", },
-	{ .name = "6556002", },
-	{},
-};
-MODULE_DEVICE_TABLE(spi, cpcap_spi_ids);
-
 static const struct regmap_config cpcap_regmap_config = {
 	.reg_bits = 16,
 	.reg_stride = 4,
@@ -241,7 +234,56 @@ static int cpcap_resume(struct device *dev)
 
 static DEFINE_SIMPLE_DEV_PM_OPS(cpcap_pm, cpcap_suspend, cpcap_resume);
 
-static const struct mfd_cell cpcap_mfd_devices[] = {
+static const struct mfd_cell cpcap_default_mfd_devices[] = {
+	{
+		.name          = "cpcap_adc",
+		.of_compatible = "motorola,cpcap-adc",
+	}, {
+		.name          = "cpcap_battery",
+		.of_compatible = "motorola,cpcap-battery",
+	}, {
+		.name          = "cpcap-regulator",
+		.of_compatible = "motorola,cpcap-regulator",
+	}, {
+		.name          = "cpcap-rtc",
+		.of_compatible = "motorola,cpcap-rtc",
+	}, {
+		.name          = "cpcap-pwrbutton",
+		.of_compatible = "motorola,cpcap-pwrbutton",
+	}, {
+		.name          = "cpcap-usb-phy",
+		.of_compatible = "motorola,cpcap-usb-phy",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 0,
+		.of_compatible = "motorola,cpcap-led-red",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 1,
+		.of_compatible = "motorola,cpcap-led-green",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 2,
+		.of_compatible = "motorola,cpcap-led-blue",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 3,
+		.of_compatible = "motorola,cpcap-led-adl",
+	}, {
+		.name          = "cpcap-led",
+		.id            = 4,
+		.of_compatible = "motorola,cpcap-led-cp",
+	}, {
+		.name          = "cpcap-codec",
+	},
+};
+
+static const struct cpcap_chip_data cpcap_default_data = {
+	.mfd_devices = cpcap_default_mfd_devices,
+	.num_devices = ARRAY_SIZE(cpcap_default_mfd_devices),
+};
+
+static const struct mfd_cell cpcap_mapphone_mfd_devices[] = {
 	{
 		.name          = "cpcap_adc",
 		.of_compatible = "motorola,mapphone-cpcap-adc",
@@ -285,7 +327,12 @@ static const struct mfd_cell cpcap_mfd_devices[] = {
 		.of_compatible = "motorola,cpcap-led-cp",
 	}, {
 		.name          = "cpcap-codec",
-	}
+	},
+};
+
+static const struct cpcap_chip_data cpcap_mapphone_data = {
+	.mfd_devices = cpcap_mapphone_mfd_devices,
+	.num_devices = ARRAY_SIZE(cpcap_mapphone_mfd_devices),
 };
 
 static int cpcap_probe(struct spi_device *spi)
@@ -297,6 +344,10 @@ static int cpcap_probe(struct spi_device *spi)
 	if (!cpcap)
 		return -ENOMEM;
 
+	cpcap->cdata = device_get_match_data(&spi->dev);
+	if (!cpcap->cdata)
+		return -ENODEV;
+
 	cpcap->spi = spi;
 	spi_set_drvdata(spi, cpcap);
 
@@ -331,10 +382,24 @@ static int cpcap_probe(struct spi_device *spi)
 	spi->dev.coherent_dma_mask = 0;
 	spi->dev.dma_mask = &spi->dev.coherent_dma_mask;
 
-	return devm_mfd_add_devices(&spi->dev, 0, cpcap_mfd_devices,
-				    ARRAY_SIZE(cpcap_mfd_devices), NULL, 0, NULL);
+	return devm_mfd_add_devices(&spi->dev, 0, cpcap->cdata->mfd_devices,
+				    cpcap->cdata->num_devices, NULL, 0, NULL);
 }
 
+static const struct of_device_id cpcap_of_match[] = {
+	{ .compatible = "motorola,cpcap", .data = &cpcap_default_data },
+	{ .compatible = "motorola,mapphone-cpcap", .data = &cpcap_mapphone_data	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cpcap_of_match);
+
+static const struct spi_device_id cpcap_spi_ids[] = {
+	{ .name = "cpcap", .driver_data = (kernel_ulong_t)&cpcap_default_data },
+	{ .name = "mapphone-cpcap", .driver_data = (kernel_ulong_t)&cpcap_mapphone_data },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, cpcap_spi_ids);
+
 static struct spi_driver cpcap_driver = {
 	.driver = {
 		.name = "cpcap-core",
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 4/6 RESEND] dt-bindings: mfd: motorola-cpcap: document Mapphone and Mot CPCAP
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

Add compatibles for Mapphone and Mot CPCAP subdevice compositions. Both
variations cannot use st,6556002 fallback since they may be based on
different controllers.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../devicetree/bindings/mfd/motorola,cpcap.yaml       | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
index 7f257f3a1a5a..542d149d2b39 100644
--- a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
+++ b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
@@ -14,9 +14,14 @@ allOf:
 
 properties:
   compatible:
-    items:
-      - const: motorola,cpcap
-      - const: st,6556002
+    oneOf:
+      - enum:
+          - motorola,mapphone-cpcap
+          - motorola,mot-cpcap
+
+      - items:
+          - const: motorola,cpcap
+          - const: st,6556002
 
   reg:
     maxItems: 1
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 3/6 RESEND] dt-bindings: mfd: motorola-cpcap: convert to DT schema
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

Convert devicetree bindings for the Motorola CPCAP MFD from TXT to YAML.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../bindings/mfd/motorola,cpcap.yaml          | 414 ++++++++++++++++++
 .../bindings/mfd/motorola-cpcap.txt           |  78 ----
 2 files changed, 414 insertions(+), 78 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
 delete mode 100644 Documentation/devicetree/bindings/mfd/motorola-cpcap.txt

diff --git a/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
new file mode 100644
index 000000000000..7f257f3a1a5a
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
@@ -0,0 +1,414 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/motorola,cpcap.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Motorola CPCAP PMIC MFD
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+properties:
+  compatible:
+    items:
+      - const: motorola,cpcap
+      - const: st,6556002
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  spi-max-frequency:
+    maximum: 9600000
+
+  spi-cs-high: true
+  spi-cpol: true
+  spi-cpha: true
+
+  adc:
+    $ref: /schemas/iio/adc/motorola,cpcap-adc.yaml#
+
+  audio-codec:
+    type: object
+    additionalProperties: false
+
+    properties:
+      interrupts:
+        items:
+          - description: headset detect interrupt
+          - description: microphone bias 2 detect interrupt
+
+      interrupt-names:
+        items:
+          - const: hs
+          - const: mb2
+
+      "#sound-dai-cells":
+        const: 1
+
+      VAUDIO-supply:
+        description:
+          Codec power supply, usually VAUDIO regulator of CPCAP.
+
+      ports:
+        $ref: /schemas/graph.yaml#/properties/ports
+
+        properties:
+          port@0:
+            $ref: /schemas/graph.yaml#/properties/port
+            description: port connected to the Stereo HiFi DAC
+
+          port@1:
+            $ref: /schemas/graph.yaml#/properties/port
+            description: port connected to the Voice DAC
+
+        required:
+          - port@0
+          - port@1
+
+    required:
+      - interrupts
+      - interrupt-names
+      - "#sound-dai-cells"
+
+  battery:
+    $ref: /schemas/power/supply/cpcap-battery.yaml#
+
+  charger:
+    $ref: /schemas/power/supply/cpcap-charger.yaml#
+
+  key-power:
+    $ref: /schemas/input/motorola,cpcap-pwrbutton.yaml#
+
+  phy:
+    $ref: /schemas/phy/motorola,cpcap-usb-phy.yaml#
+
+  regulator:
+    $ref: /schemas/regulator/motorola,cpcap-regulator.yaml#
+
+  rtc:
+    $ref: /schemas/rtc/motorola,cpcap-rtc.yaml#
+
+patternProperties:
+  "^led(-[a-z]+)?$":
+    $ref: /schemas/leds/motorola,cpcap-leds.yaml#
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-controller
+  - "#interrupt-cells"
+  - spi-max-frequency
+  - "#address-cells"
+  - "#size-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/input/linux-event-codes.h>
+
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        cpcap: pmic@0 {
+            compatible = "motorola,cpcap", "st,6556002";
+            reg = <0>; /* cs0 */
+
+            interrupt-parent = <&gpio1>;
+            interrupts = <7 IRQ_TYPE_EDGE_RISING>;
+
+            interrupt-controller;
+            #interrupt-cells = <2>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            spi-max-frequency = <3000000>;
+            spi-cs-high;
+
+            spi-cpol;
+            spi-cpha;
+
+            cpcap_adc: adc {
+                compatible = "motorola,cpcap-adc";
+
+                interrupt-parent = <&cpcap>;
+                interrupts = <8 IRQ_TYPE_NONE>;
+                interrupt-names = "adcdone";
+
+                #io-channel-cells = <1>;
+            };
+
+            cpcap_audio: audio-codec {
+                interrupt-parent = <&cpcap>;
+                interrupts = <9 IRQ_TYPE_NONE>, <10 IRQ_TYPE_NONE>;
+                interrupt-names = "hs", "mb2";
+
+                VAUDIO-supply = <&vdd_audio>;
+
+                #sound-dai-cells = <1>;
+
+                ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    /* HiFi */
+                    port@0 {
+                        reg = <0>;
+
+                        cpcap_audio_codec0: endpoint {
+                        };
+                    };
+
+                    /* Voice */
+                    port@1 {
+                        reg = <1>;
+
+                        cpcap_audio_codec1: endpoint {
+                        };
+                    };
+                };
+            };
+
+            cpcap_battery: battery {
+                compatible = "motorola,cpcap-battery";
+
+                interrupt-parent = <&cpcap>;
+                interrupts = <6 IRQ_TYPE_NONE>, <5 IRQ_TYPE_NONE>,
+                             <3 IRQ_TYPE_NONE>, <20 IRQ_TYPE_NONE>,
+                             <54 IRQ_TYPE_NONE>, <57 IRQ_TYPE_NONE>;
+                interrupt-names = "eol", "lowbph", "lowbpl",
+                                  "chrgcurr1", "battdetb", "cccal";
+
+                io-channels = <&cpcap_adc 0>, <&cpcap_adc 1>,
+                              <&cpcap_adc 5>, <&cpcap_adc 6>;
+                io-channel-names = "battdetb", "battp",
+                                   "chg_isense", "batti";
+                power-supplies = <&cpcap_charger>;
+            };
+
+            cpcap_charger: charger {
+                compatible = "motorola,mapphone-cpcap-charger";
+
+                interrupt-parent = <&cpcap>;
+                interrupts = <13 IRQ_TYPE_NONE>, <12 IRQ_TYPE_NONE>,
+                             <29 IRQ_TYPE_NONE>, <28 IRQ_TYPE_NONE>,
+                             <22 IRQ_TYPE_NONE>, <21 IRQ_TYPE_NONE>,
+                             <20 IRQ_TYPE_NONE>, <19 IRQ_TYPE_NONE>,
+                             <54 IRQ_TYPE_NONE>;
+                interrupt-names = "chrg_det", "rvrs_chrg", "chrg_se1b",
+                                  "se0conn", "rvrs_mode", "chrgcurr2",
+                                  "chrgcurr1", "vbusvld", "battdetb";
+
+                mode-gpios = <&gpio3 29 GPIO_ACTIVE_LOW>,
+                             <&gpio3 23 GPIO_ACTIVE_LOW>;
+
+                io-channels = <&cpcap_adc 0>, <&cpcap_adc 1>,
+                              <&cpcap_adc 2>, <&cpcap_adc 5>,
+                              <&cpcap_adc 6>;
+                io-channel-names = "battdetb", "battp",
+                                   "vbus", "chg_isense",
+                                   "batti";
+            };
+
+            key-power {
+                compatible = "motorola,cpcap-pwrbutton";
+
+                interrupt-parent = <&cpcap>;
+                interrupts = <23 IRQ_TYPE_NONE>;
+            };
+
+            led-red {
+                compatible = "motorola,cpcap-led-red";
+                vdd-supply = <&vdd_led>;
+                label = "status-led::red";
+            };
+
+            led-green {
+                compatible = "motorola,cpcap-led-green";
+                vdd-supply = <&vdd_led>;
+                label = "status-led::green";
+            };
+
+            led-blue {
+                compatible = "motorola,cpcap-led-blue";
+                vdd-supply = <&vdd_led>;
+                label = "status-led::blue";
+            };
+
+            cpcap_usb2_phy: phy {
+                compatible = "motorola,cpcap-usb-phy";
+
+                pinctrl-0 = <&usb_gpio_mux_sel1>, <&usb_gpio_mux_sel2>;
+                pinctrl-1 = <&usb_ulpi_pins>;
+                pinctrl-2 = <&usb_utmi_pins>;
+                pinctrl-3 = <&uart3_pins>;
+                pinctrl-names = "default", "ulpi", "utmi", "uart";
+                #phy-cells = <0>;
+
+                interrupts-extended =
+                    <&cpcap 15 IRQ_TYPE_NONE>, <&cpcap 14 IRQ_TYPE_NONE>,
+                    <&cpcap 28 IRQ_TYPE_NONE>, <&cpcap 19 IRQ_TYPE_NONE>,
+                    <&cpcap 18 IRQ_TYPE_NONE>, <&cpcap 17 IRQ_TYPE_NONE>,
+                    <&cpcap 16 IRQ_TYPE_NONE>, <&cpcap 49 IRQ_TYPE_NONE>,
+                    <&cpcap 48 IRQ_TYPE_NONE>;
+                interrupt-names = "id_ground", "id_float", "se0conn",
+                                  "vbusvld", "sessvld", "sessend",
+                                  "se1", "dm", "dp";
+
+                mode-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>,
+                             <&gpio1 0 GPIO_ACTIVE_HIGH>;
+
+                io-channels = <&cpcap_adc 2>, <&cpcap_adc 7>;
+                io-channel-names = "vbus", "id";
+
+                vusb-supply = <&avdd_usb>;
+            };
+
+            regulator {
+                compatible = "motorola,cpcap-regulator";
+
+                regulators {
+                    vdd_cpu: SW1 {
+                        regulator-name = "vdd_cpu";
+                        regulator-min-microvolt = <750000>;
+                        regulator-max-microvolt = <1125000>;
+                        regulator-enable-ramp-delay = <1500>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    vdd_core: SW2 {
+                        regulator-name = "vdd_core";
+                        regulator-min-microvolt = <950000>;
+                        regulator-max-microvolt = <1300000>;
+                        regulator-enable-ramp-delay = <1500>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    vdd_1v8_vio: SW3 {
+                        regulator-name = "vdd_1v8_vio";
+                        regulator-min-microvolt = <1800000>;
+                        regulator-max-microvolt = <1800000>;
+                        regulator-enable-ramp-delay = <0>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    vdd_aon: SW4 {
+                        regulator-name = "vdd_aon";
+                        regulator-min-microvolt = <950000>;
+                        regulator-max-microvolt = <1300000>;
+                        regulator-enable-ramp-delay = <1500>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    vdd_led: SW5 {
+                        regulator-name = "vdd_led";
+                        regulator-min-microvolt = <5050000>;
+                        regulator-max-microvolt = <5050000>;
+                        regulator-enable-ramp-delay = <1500>;
+                        regulator-boot-on;
+                    };
+
+                    vdd_hvio: VHVIO {
+                        regulator-name = "vdd_hvio";
+                        regulator-min-microvolt = <2775000>;
+                        regulator-max-microvolt = <2775000>;
+                        regulator-enable-ramp-delay = <1000>;
+                    };
+
+                    vcore_emmc: VSDIO {
+                        regulator-name = "vcore_emmc";
+                        regulator-min-microvolt = <1500000>;
+                        regulator-max-microvolt = <3000000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    avdd_dsi_csi: VCSI {
+                        regulator-name = "avdd_dsi_csi";
+                        regulator-min-microvolt = <1200000>;
+                        regulator-max-microvolt = <1200000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-boot-on;
+                    };
+
+                    avdd_3v3_periph: VWLAN2 {
+                        regulator-name = "avdd_3v3_periph";
+                        regulator-min-microvolt = <2775000>;
+                        regulator-max-microvolt = <3300000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-boot-on;
+                    };
+
+                    vddio_usd: VSIMCARD {
+                        regulator-name = "vddio_usd";
+                        regulator-min-microvolt = <1800000>;
+                        regulator-max-microvolt = <2900000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-boot-on;
+                    };
+
+                    vdd_haptic: VVIB {
+                        regulator-name = "vdd_haptic";
+                        regulator-min-microvolt = <1300000>;
+                        regulator-max-microvolt = <3000000>;
+                        regulator-enable-ramp-delay = <1000>;
+                    };
+
+                    avdd_usb: VUSB {
+                        regulator-name = "avdd_usb";
+                        regulator-min-microvolt = <3300000>;
+                        regulator-max-microvolt = <3300000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+
+                    vdd_audio: VAUDIO {
+                        regulator-name = "vdd_audio";
+                        regulator-min-microvolt = <2775000>;
+                        regulator-max-microvolt = <2775000>;
+                        regulator-enable-ramp-delay = <1000>;
+                        regulator-always-on;
+                        regulator-boot-on;
+                    };
+                };
+            };
+
+            cpcap_rtc: rtc {
+                compatible = "motorola,cpcap-rtc";
+
+                interrupt-parent = <&cpcap>;
+                interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
+            };
+        };
+    };
+
+...
diff --git a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
deleted file mode 100644
index 18c3fc26ca93..000000000000
--- a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-Motorola CPCAP PMIC device tree binding
-
-Required properties:
-- compatible		: One or both of "motorola,cpcap" or "ste,6556002"
-- reg			: SPI chip select
-- interrupts		: The interrupt line the device is connected to
-- interrupt-controller	: Marks the device node as an interrupt controller
-- #interrupt-cells	: The number of cells to describe an IRQ, should be 2
-- #address-cells	: Child device offset number of cells, should be 1
-- #size-cells		: Child device size number of cells, should be 0
-- spi-max-frequency	: Typically set to 3000000
-- spi-cs-high		: SPI chip select direction
-
-Optional subnodes:
-
-The sub-functions of CPCAP get their own node with their own compatible values,
-which are described in the following files:
-
-- Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml
-- Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml
-- Documentation/devicetree/bindings/regulator/cpcap-regulator.txt
-- Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
-- Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
-- Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
-- Documentation/devicetree/bindings/leds/leds-cpcap.txt
-- Documentation/devicetree/bindings/iio/adc/motorola,cpcap-adc.yaml
-
-The only exception is the audio codec. Instead of a compatible value its
-node must be named "audio-codec".
-
-Required properties for the audio-codec subnode:
-
-- #sound-dai-cells = <1>;
-- interrupts		: should contain jack detection interrupts, with headset
-			  detect interrupt matching "hs" and microphone bias 2
-			  detect interrupt matching "mb2" in interrupt-names.
-- interrupt-names	: Contains "hs", "mb2"
-
-The audio-codec provides two DAIs. The first one is connected to the
-Stereo HiFi DAC and the second one is connected to the Voice DAC.
-
-Example:
-
-&mcspi1 {
-	cpcap: pmic@0 {
-		compatible = "motorola,cpcap", "ste,6556002";
-		reg = <0>;	/* cs0 */
-		interrupt-parent = <&gpio1>;
-		interrupts = <7 IRQ_TYPE_EDGE_RISING>;
-		interrupt-controller;
-		#interrupt-cells = <2>;
-		#address-cells = <1>;
-		#size-cells = <0>;
-		spi-max-frequency = <3000000>;
-		spi-cs-high;
-
-		audio-codec {
-			#sound-dai-cells = <1>;
-			interrupts-extended = <&cpcap 9 0>, <&cpcap 10 0>;
-			interrupt-names = "hs", "mb2";
-
-			/* HiFi */
-			port@0 {
-				endpoint {
-					remote-endpoint = <&cpu_dai1>;
-				};
-			};
-
-			/* Voice */
-			port@1 {
-				endpoint {
-					remote-endpoint = <&cpu_dai2>;
-				};
-			};
-		};
-	};
-};
-
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 2/6 RESEND] dt-bindings: input: cpcap-pwrbutton: convert to DT schema
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

Convert power button devicetree bindings for the Motorola CPCAP MFD from
TXT to YAML format. This patch does not change any functionality; the
bindings remain the same.

Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../bindings/input/cpcap-pwrbutton.txt        | 20 ------------
 .../input/motorola,cpcap-pwrbutton.yaml       | 32 +++++++++++++++++++
 2 files changed, 32 insertions(+), 20 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
 create mode 100644 Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml

diff --git a/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt b/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
deleted file mode 100644
index 0dd0076daf71..000000000000
--- a/Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Motorola CPCAP on key
-
-This module is part of the CPCAP. For more details about the whole
-chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
-
-This module provides a simple power button event via an Interrupt.
-
-Required properties:
-- compatible: should be one of the following
-   - "motorola,cpcap-pwrbutton"
-- interrupts: irq specifier for CPCAP's ON IRQ
-
-Example:
-
-&cpcap {
-	cpcap_pwrbutton: pwrbutton {
-		compatible = "motorola,cpcap-pwrbutton";
-		interrupts = <23 IRQ_TYPE_NONE>;
-	};
-};
diff --git a/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml b/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml
new file mode 100644
index 000000000000..77a3e5a47d1a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml
@@ -0,0 +1,32 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/motorola,cpcap-pwrbutton.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Motorola CPCAP PMIC power key
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description:
+  This module is part of the Motorola CPCAP MFD device. For more details
+  see Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml. The
+  power key is represented as a sub-node of the PMIC node on the device
+  tree.
+
+properties:
+  compatible:
+    const: motorola,cpcap-pwrbutton
+
+  interrupts:
+    items:
+      - description: CPCAP's ON interrupt
+
+required:
+  - compatible
+  - interrupts
+
+additionalProperties: false
+
+...
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 1/6 RESEND] dt-bindings: leds: leds-cpcap: convert to DT schema
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds
In-Reply-To: <20260428153611.142816-1-clamor95@gmail.com>

Convert LEDs devicetree bindings for the Motorola CPCAP MFD from TXT to
YAML format. This patch does not change any functionality; the bindings
remain the same.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
 .../devicetree/bindings/leds/leds-cpcap.txt   | 29 -------------
 .../bindings/leds/motorola,cpcap-leds.yaml    | 42 +++++++++++++++++++
 2 files changed, 42 insertions(+), 29 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/leds/leds-cpcap.txt
 create mode 100644 Documentation/devicetree/bindings/leds/motorola,cpcap-leds.yaml

diff --git a/Documentation/devicetree/bindings/leds/leds-cpcap.txt b/Documentation/devicetree/bindings/leds/leds-cpcap.txt
deleted file mode 100644
index ebf7cdc7f70c..000000000000
--- a/Documentation/devicetree/bindings/leds/leds-cpcap.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-Motorola CPCAP PMIC LEDs
-------------------------
-
-This module is part of the CPCAP. For more details about the whole
-chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
-
-Requires node properties:
-- compatible: should be one of
-   * "motorola,cpcap-led-mdl"		(Main Display Lighting)
-   * "motorola,cpcap-led-kl"		(Keyboard Lighting)
-   * "motorola,cpcap-led-adl"		(Aux Display Lighting)
-   * "motorola,cpcap-led-red"		(Red Triode)
-   * "motorola,cpcap-led-green"		(Green Triode)
-   * "motorola,cpcap-led-blue"		(Blue Triode)
-   * "motorola,cpcap-led-cf"		(Camera Flash)
-   * "motorola,cpcap-led-bt"		(Bluetooth)
-   * "motorola,cpcap-led-cp"		(Camera Privacy LED)
-- label: see Documentation/devicetree/bindings/leds/common.txt
-- vdd-supply: A phandle to the regulator powering the LED
-
-Example:
-
-&cpcap {
-	cpcap_led_red: red-led {
-		compatible = "motorola,cpcap-led-red";
-		label = "cpcap:red";
-		vdd-supply = <&sw5>;
-	};
-};
diff --git a/Documentation/devicetree/bindings/leds/motorola,cpcap-leds.yaml b/Documentation/devicetree/bindings/leds/motorola,cpcap-leds.yaml
new file mode 100644
index 000000000000..c8e7b88a05cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/motorola,cpcap-leds.yaml
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/motorola,cpcap-leds.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Motorola CPCAP PMIC LEDs
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description:
+  This module is part of the Motorola CPCAP MFD device. For more details
+  see Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml. LEDs are
+  represented as sub-nodes of the PMIC node on the device tree.
+
+allOf:
+  - $ref: /schemas/leds/common.yaml#
+
+properties:
+  compatible:
+    enum:
+      - motorola,cpcap-led-adl # Display Lighting
+      - motorola,cpcap-led-blue # Blue Triode
+      - motorola,cpcap-led-bt # Bluetooth
+      - motorola,cpcap-led-cf # Camera Flash
+      - motorola,cpcap-led-cp # Camera Privacy LED
+      - motorola,cpcap-led-green # Green Triode
+      - motorola,cpcap-led-kl # Keyboard Lighting
+      - motorola,cpcap-led-mdl # Main Display Lighting
+      - motorola,cpcap-led-red # Red Triode
+
+  vdd-supply: true
+
+required:
+  - compatible
+  - label
+  - vdd-supply
+
+unevaluatedProperties: false
+
+...
-- 
2.51.0


^ permalink raw reply related

* [PATCH v4 0/6 RESEND] mfd: cpcap: convert documentation to schema and add Mot board support
From: Svyatoslav Ryhel @ 2026-04-28 15:36 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Lee Jones, Pavel Machek, Svyatoslav Ryhel, David Lechner,
	Tony Lindgren
  Cc: linux-input, devicetree, linux-kernel, linux-leds

The initial goal was only to add support for the CPCAP used in the Mot
Tegra20 board; however, since the documentation was already partially
converted, I decided to complete the conversion to schema too.

The CPCAP regulator, leds, rtc, pwrbutton and core files were converted
from TXT to YAML while preserving the original structure. Mot board
compatibility was added to the regulator and core schema. Since these
were one-line patches, they were not separated into dedicated commits;
however, the commit message notes this for both cases.

Finally, the CPCAP MFD was slightly refactored to improve support for
multiple subcell compositions.

---
Changes in v2:
- fixed code style
- rtc conversion was picked, so patch dropped
- added audio ports description into mfd schema
- splitted schema conversion and compatible addition
- minor style improvements and typo fixes

Changes in v3:
- added regulator node names list into pattern
- filled spi_device_id with driver data
- ADC patches were picked, so changes dropped

Changes in v4:
- dropped regulator patches (applied)
---

Svyatoslav Ryhel (6):
  dt-bindings: leds: leds-cpcap: convert to DT schema
  dt-bindings: input: cpcap-pwrbutton: convert to DT schema
  dt-bindings: mfd: motorola-cpcap: convert to DT schema
  dt-bindings: mfd: motorola-cpcap: document Mapphone and Mot CPCAP
  mfd: motorola-cpcap: diverge configuration per-board
  mfd: motorola-cpcap: add support for Mot CPCAP composition

 .../bindings/input/cpcap-pwrbutton.txt        |  20 -
 .../input/motorola,cpcap-pwrbutton.yaml       |  32 ++
 .../devicetree/bindings/leds/leds-cpcap.txt   |  29 --
 .../bindings/leds/motorola,cpcap-leds.yaml    |  42 ++
 .../bindings/mfd/motorola,cpcap.yaml          | 419 ++++++++++++++++++
 .../bindings/mfd/motorola-cpcap.txt           |  78 ----
 drivers/mfd/motorola-cpcap.c                  | 151 ++++++-
 7 files changed, 626 insertions(+), 145 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
 create mode 100644 Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml
 delete mode 100644 Documentation/devicetree/bindings/leds/leds-cpcap.txt
 create mode 100644 Documentation/devicetree/bindings/leds/motorola,cpcap-leds.yaml
 create mode 100644 Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
 delete mode 100644 Documentation/devicetree/bindings/mfd/motorola-cpcap.txt

-- 
2.51.0


^ permalink raw reply

* Re: [bug report] Potential order bug in 'drivers/input/misc', particularly 'ati_remote2.c', 'cm109.c', 'keyspan_remote.c'
From: Dmitry Torokhov @ 2026-04-28 15:01 UTC (permalink / raw)
  To: Ginger; +Cc: linux-input
In-Reply-To: <CAGp+u1aFTivN-EC5QQaQ=j21GaGHktunHEzWQ9e2uASsE40svQ@mail.gmail.com>

Hi Ginger,

On Tue, Apr 28, 2026 at 12:18:46PM +0800, Ginger wrote:
> Dear Linux kernel maintainers,
> 
> My research-based static analyzer found a potential order bug within
> the ' drivers/input/misc' subsystem. I will use the potential bug
> found in 'drivers/input/misc/ati_remote2.c' as the typical example.
> The similar potential bug patterns are also observed in 'cm109.c' and
> 'keyspan_remote.c'.
> 
> Kernel version: long-term kernel v6.18.9
> 
> Potential issue:
> T0:
> ati_remote2_disconnect
>     --> ar2 = usb_get_intfdata(interface);
>     --> usb_set_intfdata(interface, NULL);
>     --> input_unregister_device(ar2->idev);
>     --> usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
>     --> kfree(ar2)
> 
> T1:
> ati_remote2_store_channel_mask (registered as the device attribute
> function and exposed via the sysfs)
>     --> struct ati_remote2 *ar2 = usb_get_intfdata(intf);
>     --> ar2->mode_mask = mask;
> 
> In T0, the interface is nullified before its get deregistered. Thus,
> it is possible for T1 to still get the usb dev and access it via the
> interface, which, however, has been already nullified or even freed.
> This similar pattern is also observed in 'cm109_usb_disconnect()' of
> file 'cm109.c' and 'keyspan_disconnect()' of file 'keyspan_remote.c'.

Driver core is supposed to remove sysfs attributes before calling
remove() or disconnect() (see drivers/base/dd.c::device_remove()) and
sysfs ensures that all threads leave sysfs show() and store() methods
before the call to delete the attributes returns.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] platform/x86/lenovo: Add Yoga Book 9 keyboard dock detection driver
From: Ilpo Järvinen @ 2026-04-28 14:39 UTC (permalink / raw)
  To: Dave Carey, Hans de Goede, Pit Henrich
  Cc: platform-driver-x86, LKML, linux-input
In-Reply-To: <20260425132323.82809-1-carvsdriver@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 14388 bytes --]

On Sat, 25 Apr 2026, Dave Carey wrote:

> The Lenovo Yoga Book 9 14IAH10 ships with a detachable Bluetooth keyboard
> that magnetically attaches to the bottom (secondary) screen in one of two
> positions.

> The Embedded Controller tracks the attachment state in a 2-bit
> field called BKBD and signals changes via WMI event GUID
> 806BD2A2-177B-481D-BFB5-3BA0BB4A2285 (notify ID 0xEB on the WM10 ACPI
> device).

Please put this in depth explanation in own paragraph.

> The current BKBD state is read via WMI query GUID
> E7F300FA-21CD-4003-ADAC-2696135982E6 (WQAF method), 

This seems mostly duplicate of what was said previously.

>which returns an
> 8-byte buffer: bytes [0..3] hold the LFID constant 0x00060000 and bytes
> [4..7] hold the BKBD value.
>
> BKBD encoding:
>   0 = keyboard detached
>   1 = keyboard docked on top half of bottom screen
>   2 = keyboard docked on bottom half of bottom screen
>   3 = reserved (not observed in practice)

These two can be combined with the in depth explanation paragraph.

> This driver:
>   - Registers as a WMI driver on the event GUID.

Unnecessary / obvious.

>   - Queries BKBD state on probe and on each WMI notification.
>   - Reports SW_TABLET_MODE=1 when detached, SW_TABLET_MODE=0 when docked
>     in either position (a physical keyboard is present in both cases).
>   - Exposes the raw BKBD value via a read-only sysfs attribute
>     "keyboard_position" for use by userspace (e.g. to distinguish between
>     the two docked positions for different UI layouts).

Please write this without bullet points. Bullet points usually break 
relationships between sentences.

> Tested on: Lenovo Yoga Book 9 14IAH10 (model 83KJ), kernel 6.19.
> 
> Signed-off-by: Dave Carey <carvsdriver@gmail.com>
> ---
>  .../testing/sysfs-driver-lenovo-yb9-kbdock    |  21 ++
>  MAINTAINERS                                   |   6 +
>  drivers/platform/x86/lenovo/Kconfig           |  14 ++
>  drivers/platform/x86/lenovo/Makefile          |   1 +
>  drivers/platform/x86/lenovo/yb9-kbdock.c      | 216 ++++++++++++++++++
>  5 files changed, 258 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
>  create mode 100644 MAINTAINERS
>  create mode 100644 drivers/platform/x86/lenovo/yb9-kbdock.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
> new file mode 100644
> index 0000000..bb57690
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
> @@ -0,0 +1,21 @@
> +What:		/sys/bus/wmi/drivers/lenovo-yb9-kbdock/<guid>/keyboard_position
> +Date:		April 2026
> +KernelVersion:	6.10
> +Contact:	Dave Carey <carvsdriver@gmail.com>
> +Description:
> +		Read-only attribute reporting the current keyboard dock position
> +		as reported by the Embedded Controller on the Lenovo Yoga Book 9
> +		14IAH10.
> +
> +		Possible values:
> +
> +		==  ============================================================
> +		0   detached  — keyboard is not docked to any screen
> +		1   top-half  — keyboard docked on the top half of the bottom screen
> +		2   bottom-half — keyboard docked on the bottom half of the bottom screen
> +		==  ============================================================

Interesting, I wonder if this is similar physically to what is being added 
here:

https://lore.kernel.org/all/20260419102724.91451-1-pithenrich2d@gmail.com/

?

If yes, we may have to take another look at how to create the interface 
for this.

You didn't document unknown but return it (maybe it should return some 
-Exx code instead?).

> +		The value is formatted as "<n> (<name>)\n", e.g. "1 (top-half)\n".
> +
> +		SW_TABLET_MODE input events are also emitted: 0 when the keyboard
> +		is docked (either position), 1 when detached.
> diff --git a/MAINTAINERS b/MAINTAINERS
> new file mode 100644
> index 0000000..cb765b4
> --- /dev/null
> +++ b/MAINTAINERS
> @@ -0,0 +1,6 @@
> +LENOVO YOGA BOOK 9 KEYBOARD DOCK DRIVER
> +M:	Dave Carey <carvsdriver@gmail.com>
> +L:	platform-driver-x86@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-driver-lenovo-yb9-kbdock
> +F:	drivers/platform/x86/lenovo/yb9-kbdock.c
> diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
> index 9c48487..938b361 100644
> --- a/drivers/platform/x86/lenovo/Kconfig
> +++ b/drivers/platform/x86/lenovo/Kconfig
> @@ -43,6 +43,20 @@ config LENOVO_WMI_CAMERA
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called lenovo-wmi-camera.
>  
> +config LENOVO_YB9_KBDOCK
> +	tristate "Lenovo Yoga Book 9 keyboard dock detection"
> +	depends on ACPI_WMI
> +	depends on DMI
> +	depends on INPUT
> +	help
> +	  Say Y here to enable keyboard dock detection on the Lenovo Yoga Book 9
> +	  14IAH10.  The detachable Bluetooth keyboard magnetically attaches to
> +	  either screen; this driver reports SW_TABLET_MODE input events based
> +	  on the attachment state and exposes the raw position in sysfs.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called lenovo-yb9-kbdock.
> +
>  config LENOVO_YMC
>  	tristate "Lenovo Yoga Tablet Mode Control"
>  	depends on ACPI_WMI
> diff --git a/drivers/platform/x86/lenovo/Makefile b/drivers/platform/x86/lenovo/Makefile
> index 7b2128e..2842d7d 100644
> --- a/drivers/platform/x86/lenovo/Makefile
> +++ b/drivers/platform/x86/lenovo/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_THINKPAD_LMI)	+= think-lmi.o
>  obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
>  
>  lenovo-target-$(CONFIG_LENOVO_WMI_HOTKEY_UTILITIES)	+= wmi-hotkey-utilities.o
> +lenovo-target-$(CONFIG_LENOVO_YB9_KBDOCK)	+= yb9-kbdock.o
>  lenovo-target-$(CONFIG_LENOVO_YMC)	+= ymc.o
>  lenovo-target-$(CONFIG_YOGABOOK)	+= yogabook.o
>  lenovo-target-$(CONFIG_YT2_1380)	+= yoga-tab2-pro-1380-fastcharger.o
> diff --git a/drivers/platform/x86/lenovo/yb9-kbdock.c b/drivers/platform/x86/lenovo/yb9-kbdock.c
> new file mode 100644
> index 0000000..693e287
> --- /dev/null
> +++ b/drivers/platform/x86/lenovo/yb9-kbdock.c
> @@ -0,0 +1,216 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Lenovo Yoga Book 9 keyboard-dock detection
> + *
> + * The Yoga Book 9 ships with a detachable Bluetooth keyboard that magnetically
> + * attaches to the bottom screen in one of two positions.  The EC tracks
> + * attachment state in a 2-bit field called BKBD and signals changes via WMI
> + * event 0xEB on the WM10 ACPI device.
> + *
> + * BKBD values:
> + *   0 = keyboard detached
> + *   1 = keyboard docked on the top half of the bottom screen
> + *   2 = keyboard docked on the bottom half of the bottom screen
> + *   3 = reserved / not observed
>
> + * This driver registers for the WMI event GUID, queries BKBD on probe and on
> + * each event, reports SW_TABLET_MODE=0 when the keyboard is docked (either
> + * position) and SW_TABLET_MODE=1 when detached, and exposes the raw BKBD
> + * value in sysfs as "keyboard_position".

I don't think the functional description on this level is warranted in the 
top comment (may place it below where you define things but if naming is 
obvious, some comments may not even be necessary).

> + * Copyright (C) 2026 Dave Carey <carvsdriver@gmail.com>
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/wmi.h>
> +
> +/*
> + * WM10 ACPI device (_UID "GMZN"):
> + *   Event GUID — notify ID 0xEB fires on keyboard attachment change.
> + *   Query GUID — object "AF", maps to WQAF(); returns 8-byte buffer
> + *                {LFID=0x00060000, BKBD[31:0]}.
> + */
> +#define YB9_KBDOCK_EVENT_GUID	"806BD2A2-177B-481D-BFB5-3BA0BB4A2285"
> +#define YB9_KBDOCK_QUERY_GUID	"E7F300FA-21CD-4003-ADAC-2696135982E6"
> +
> +#define YB9_KBDOCK_QUERY_INSTANCE	0
> +
> +/* BKBD encoding — keyboard always docks on the bottom screen */
> +#define BKBD_DETACHED		0
> +#define BKBD_TOP_HALF		1	/* docked on top half of bottom screen */
> +#define BKBD_BOTTOM_HALF	2	/* docked on bottom half of bottom screen */
> +
> +static const struct dmi_system_id yb9_kbdock_dmi_table[] = {
> +	{
> +		/* Lenovo Yoga Book 9 14IAH10 */
> +		.matches = {
> +			DMI_MATCH(DMI_SYS_VENDOR,   "LENOVO"),
> +			DMI_MATCH(DMI_PRODUCT_NAME, "83KJ"),
> +		},
> +	},
> +	{ }
> +};

Normally these appear towards the end of the file.

> +
> +struct yb9_kbdock_priv {
> +	struct input_dev *input_dev;
> +	unsigned int bkbd;	/* last read BKBD value (0-3) */
> +};
> +
> +/* Read current BKBD state via WQAF.  Returns 0-3 or -errno. */
> +static int yb9_kbdock_query(struct wmi_device *wdev)
> +{
> +	struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
> +	union acpi_object *obj;
> +	acpi_status status;
> +	u32 bkbd;
> +
> +	status = wmi_query_block(YB9_KBDOCK_QUERY_GUID,
> +				 YB9_KBDOCK_QUERY_INSTANCE, &out);

This interface has been deprecated.

> +	if (ACPI_FAILURE(status)) {
> +		dev_warn(&wdev->dev, "WQAF query failed: %s\n",
> +			 acpi_format_exception(status));
> +		return -EIO;
> +	}
> +
> +	obj = out.pointer;
> +	if (!obj) {
> +		dev_warn(&wdev->dev, "WQAF returned NULL\n");
> +		return -EIO;
> +	}
> +
> +	/*
> +	 * WQAF returns an 8-byte buffer: bytes [0..3] = LFID (0x00060000),
> +	 * bytes [4..7] = BKBD value.  Guard against short buffers.

One space is enough.

> +	 */
> +	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length >= 8) {
> +		memcpy(&bkbd, obj->buffer.pointer + 4, sizeof(bkbd));
> +		bkbd &= 0x3;

Can this literal be named with a define? Should it use FIELD_GET() 
(don't forget the header if you start to use FIELD_GET())?

> +	} else if (obj->type == ACPI_TYPE_INTEGER) {
> +		bkbd = obj->integer.value & 0x3;

Same question here.

> +	} else {
> +		dev_warn(&wdev->dev,
> +			 "WQAF: unexpected result type %d len %u\n",
> +			 obj->type,
> +			 obj->type == ACPI_TYPE_BUFFER
> +				? obj->buffer.length : 0);

Put the last two lines to one line.

> +		kfree(obj);
> +		return -EIO;
> +	}
> +
> +	kfree(obj);

Please use __free() instead of duplicating kfree()s.

When converting to __free(), don't use ... = NULL; pattern, instead place 
the variable declaration mid-function as instructed in the long comment in 
cleanup.h.

> +	return (int)bkbd;

Unnecessary cast. And your types are a major mess between int and 
unsigned types.

> +}
> +
> +static void yb9_kbdock_update(struct wmi_device *wdev)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(&wdev->dev);
> +	int bkbd;
> +	int tablet_mode;

Please use reverse-xmas tree order where there are no internal 
dependencies between local variables that prevent usinbg it.

> +
> +	bkbd = yb9_kbdock_query(wdev);
> +	if (bkbd < 0)
> +		return;
> +
> +	priv->bkbd = bkbd;
> +
> +	/*
> +	 * Report tablet mode only when the keyboard is fully detached.
> +	 * Both docked positions (top-half and bottom-half of the bottom screen)
> +	 * indicate a physical keyboard is present — report laptop mode.
> +	 */
> +	tablet_mode = (bkbd == BKBD_DETACHED) ? 1 : 0;
> +
> +	input_report_switch(priv->input_dev, SW_TABLET_MODE, tablet_mode);
> +	input_sync(priv->input_dev);
> +
> +	dev_dbg(&wdev->dev, "BKBD=%u tablet_mode=%d\n", bkbd, tablet_mode);

Missing include.

> +}
> +
> +static void yb9_kbdock_notify(struct wmi_device *wdev, union acpi_object *data)
> +{
> +	yb9_kbdock_update(wdev);
> +}
> +
> +/* sysfs: keyboard_position — exposes raw BKBD value */
> +static ssize_t keyboard_position_show(struct device *dev,
> +				      struct device_attribute *attr, char *buf)
> +{
> +	struct yb9_kbdock_priv *priv = dev_get_drvdata(dev);
> +	static const char * const names[] = {
> +		"detached", "top-half", "bottom-half", "unknown"

Use comma for all non-terminating entries.

> +	};
> +	unsigned int bkbd = priv->bkbd;
> +
> +	if (bkbd > 3)

How can this happen without it being bug in the driver? Didn't you mask it 
when reading the value?

So maybe

	if (WARN_ON_ONCE(bkbd > 3))
		return -EINVAL;

> +		bkbd = 3;
> +	return sysfs_emit(buf, "%u (%s)\n", bkbd, names[bkbd]);
> +}
> +static DEVICE_ATTR_RO(keyboard_position);
> +
> +static struct attribute *yb9_kbdock_attrs[] = {
> +	&dev_attr_keyboard_position.attr,
> +	NULL,
> +};
> +ATTRIBUTE_GROUPS(yb9_kbdock);
> +
> +static int yb9_kbdock_probe(struct wmi_device *wdev, const void *ctx)
> +{
> +	struct yb9_kbdock_priv *priv;
> +	struct input_dev *input_dev;
> +	int err;
> +
> +	if (!dmi_check_system(yb9_kbdock_dmi_table)) {
> +		dev_dbg(&wdev->dev, "not a Yoga Book 9, skipping\n");
> +		return -ENODEV;
> +	}
> +
> +	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	input_dev = devm_input_allocate_device(&wdev->dev);
> +	if (!input_dev)
> +		return -ENOMEM;
> +
> +	input_dev->name = "Lenovo Yoga Book 9 keyboard dock switch";
> +	input_dev->phys = YB9_KBDOCK_EVENT_GUID "/input0";
> +	input_dev->id.bustype = BUS_HOST;
> +	input_set_capability(input_dev, EV_SW, SW_TABLET_MODE);
> +
> +	err = input_register_device(input_dev);
> +	if (err) {
> +		dev_err(&wdev->dev, "failed to register input device: %d\n", err);
> +		return err;
> +	}
> +
> +	priv->input_dev = input_dev;
> +	dev_set_drvdata(&wdev->dev, priv);
> +
> +	/* Report initial state */
> +	yb9_kbdock_update(wdev);
> +	return 0;
> +}
> +
> +static const struct wmi_device_id yb9_kbdock_wmi_id_table[] = {
> +	{ .guid_string = YB9_KBDOCK_EVENT_GUID },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(wmi, yb9_kbdock_wmi_id_table);
> +
> +static struct wmi_driver yb9_kbdock_driver = {
> +	.driver = {
> +		.name = "lenovo-yb9-kbdock",
> +		.dev_groups = yb9_kbdock_groups,
> +	},
> +	.id_table = yb9_kbdock_wmi_id_table,
> +	.probe    = yb9_kbdock_probe,
> +	.notify   = yb9_kbdock_notify,
> +};
> +module_wmi_driver(yb9_kbdock_driver);
> +
> +MODULE_AUTHOR("Dave Carey <carvsdriver@gmail.com>");
> +MODULE_DESCRIPTION("Lenovo Yoga Book 9 keyboard dock detection");
> +MODULE_LICENSE("GPL");
> 

-- 
 i.

^ permalink raw reply

* Re: [PATCH 02/10] iio: orientation: adapt to hid_sensor_remove_trigger() API change
From: Sanjay Chitroda @ 2026-04-28 12:06 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: jikos, jic23, srinivas.pandruvada, dlechner, nuno.sa, andy,
	sakari.ailus, linux-input, linux-iio, linux-kernel
In-Reply-To: <afBuWAElmx7t7FcB@ashevche-desk.local>



On 28 April 2026 1:52:48 pm IST, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
>On Tue, Apr 28, 2026 at 12:46:05PM +0530, Sanjay Chitroda wrote:
>
>> Update the driver to match the updated hid_sensor_remove_trigger()
>> prototype, which no longer requires struct iio_dev.
>
>You haven't compiled the previous patch, right?
>This is not the way how all this should be done.
>
>Also NAK to the patch 1 as even unused parameter is there for the sake of
>consistency. The prototype to allocate and other in the similar group all
>have it.
Hi Andy,

Thank for the review comment.
I agree your point for consistency.

However primary object of this pre-series is to prepare the drivers for devm_ conversation.

I would prepare devm_ wrapper for the hid_sensor_setup_trigger() and respective resource release hid_sensor_remove_trigger().

...  devm_hid_sensor_setup_trigger( ... ) 
{
       ...
       .. hid_sensor_setup_trigger();
       ....
       devm_add_action_or_release(dev, hid_sensor_remove_trigger, attrb)
       ......
}

I observed that many HID IIO drivers are not covered fully with managed API.

This devm_* sensor setup trigger would use across multiple HID IIO sensors and will go step forward for managed API support.


>

^ permalink raw reply

* [PATCH v2 2/2] Input: isa1200 - new driver for Imagis ISA1200
From: Svyatoslav Ryhel @ 2026-04-28 11:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Linus Walleij, Svyatoslav Ryhel
  Cc: linux-input, devicetree, linux-kernel
In-Reply-To: <20260428114308.113253-1-clamor95@gmail.com>

From: Linus Walleij <linusw@kernel.org>

The ISA1200 is a haptic feedback unit from Imagis Technology using two
motors for haptic feedback in mobile phones. Used in many mobile devices
c. 2012 including Samsung Galxy S Advance GT-I9070 (Janice), Samsung Beam
GT-I8350 (Gavini), LG Optimus 4X P880 and LG Optimus Vu P895.

The exact datasheet for the ISA1200 is not available; all data was modeled
based on available downstream kernel sources for various devices and
fragments of information scattered across the internet.

Signed-off-by: Linus Walleij <linusw@kernel.org>
Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/input/misc/Kconfig   |  11 +
 drivers/input/misc/Makefile  |   1 +
 drivers/input/misc/isa1200.c | 507 +++++++++++++++++++++++++++++++++++
 3 files changed, 519 insertions(+)
 create mode 100644 drivers/input/misc/isa1200.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 94a753fcb64f..5e5d46f44b91 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -852,6 +852,17 @@ config INPUT_IQS7222
 	  To compile this driver as a module, choose M here: the
 	  module will be called iqs7222.
 
+config INPUT_ISA1200_HAPTIC
+	tristate "Imagis ISA1200 haptic feedback unit"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y to enable support for the Imagis ISA1200 haptic
+	  feedback unit.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called isa1200.
+
 config INPUT_CMA3000
 	tristate "VTI CMA3000 Tri-axis accelerometer"
 	help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 415fc4e2918b..d62bf2e9d85f 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -49,6 +49,7 @@ obj-$(CONFIG_INPUT_IMS_PCU)		+= ims-pcu.o
 obj-$(CONFIG_INPUT_IQS269A)		+= iqs269a.o
 obj-$(CONFIG_INPUT_IQS626A)		+= iqs626a.o
 obj-$(CONFIG_INPUT_IQS7222)		+= iqs7222.o
+obj-$(CONFIG_INPUT_ISA1200_HAPTIC)	+= isa1200.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)	+= keyspan_remote.o
 obj-$(CONFIG_INPUT_KXTJ9)		+= kxtj9.o
 obj-$(CONFIG_INPUT_M68K_BEEP)		+= m68kspkr.o
diff --git a/drivers/input/misc/isa1200.c b/drivers/input/misc/isa1200.c
new file mode 100644
index 000000000000..9a9d980ae47f
--- /dev/null
+++ b/drivers/input/misc/isa1200.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/array_size.h>
+#include <linux/bitmap.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/devm-helpers.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+/*
+ * System control (LDO regulator)
+ *
+ * LDO voltage to register mapping is linear, but it is split in two parts:
+ * 2.3V - 3.0V map to 0x08 - 0x0f; 3.1V - 3.8V map to 0x00 - 0x7
+ */
+
+#define ISA1200_SCTRL			0x00
+#define ISA1200_LDO_VOLTAGE_BASE	0x08
+#define ISA1200_LDO_VOLTAGE_STEP	100000
+#define ISA1200_LDO_VOLTAGE_2V3		23
+#define ISA1200_LDO_VOLTAGE_3V1		31
+
+/*
+ * The output frequency is calculated with this formula:
+ *
+ *                 base clock frequency
+ * fout = -----------------------------------------
+ *        (128 - PWM_FREQ) * 2 * PLLDIV * PWM_PERIOD
+ *
+ * The base clock frequency is the clock frequency provided on the
+ * clock input to the chip, divided by the value in HCTRL0
+ *
+ * PWM_FREQ is configured in register HCTRL4, it is common to set this
+ * to 0 to get only two variables to calculate.
+ *
+ * PLLDIV is configured in register HCTRL3 (bits 7..4, so 0..15)
+ * PWM_PERIOD is configured in register HCTRL6
+ * Further the duty cycle can be configured in HCTRL5
+ */
+
+/*
+ * HCTRL0 configures clock or PWM input and selects the divider for
+ * the clock input.
+ */
+#define ISA1200_HCTRL0			0x30
+#define ISA1200_HCTRL0_HAP_ENABLE	BIT(7)
+#define ISA1200_HCTRL0_PWM_GEN_MODE	BIT(4)
+#define ISA1200_HCTRL0_PWM_INPUT_MODE	BIT(3)
+#define ISA1200_HCTRL0_CLKDIV_128	128
+
+/*
+ * HCTRL1 configures the motor type and clock sourse
+ */
+#define ISA1200_HCTRL1			0x31
+#define ISA1200_HCTRL1_EXT_CLOCK	BIT(7)
+#define ISA1200_HCTRL1_DAC_INVERT	BIT(6)
+#define ISA1200_HCTRL1_MODE(n)		((n) << 5)
+
+/* HCTRL2 controls software reset of the chip */
+#define ISA1200_HCTRL2			0x32
+#define ISA1200_HCTRL2_SW_RESET		BIT(0)
+
+/*
+ * HCTRL3 controls the PLL divisor
+ *
+ * Bits [0,1] are always set to 1 (we don't know what they are
+ * used for) and bit 4 and upward control the PLL divisor.
+ */
+#define ISA1200_HCTRL3			0x33
+#define ISA1200_HCTRL3_DEFAULT		0x03
+#define ISA1200_HCTRL3_PLLDIV(n)	(((n) & 0xf) << 4)
+
+/* HCTRL4 controls the PWM frequency of external channel */
+#define ISA1200_HCTRL4			0x34
+
+/* HCTRL5 controls the PWM high duty cycle of internal channel */
+#define ISA1200_HCTRL5			0x35
+
+/* HCTRL6 controls the PWM period of internal channel */
+#define ISA1200_HCTRL6			0x36
+#define ISA1200_HCTRL6_PERIOD_SCALE	100
+
+/* The use for these registers is unknown but they exist */
+#define ISA1200_HCTRL7			0x37
+#define ISA1200_HCTRL8			0x38
+#define ISA1200_HCTRL9			0x39
+#define ISA1200_HCTRLA			0x3a
+#define ISA1200_HCTRLB			0x3b
+#define ISA1200_HCTRLC			0x3c
+#define ISA1200_HCTRLD			0x3d
+
+#define ISA1200_EN_PINS_MAX		2
+
+struct isa1200_config {
+	u32 ldo_voltage;
+	u32 mode;
+	u32 clkdiv;
+	u32 plldiv;
+	u32 freq;
+	u32 period;
+	u32 duty;
+};
+
+struct isa1200 {
+	struct input_dev *input;
+	struct regmap *map;
+
+	struct clk *clk;
+	struct pwm_device *pwm;
+	struct gpio_descs *enable_gpios;
+
+	struct work_struct play_work;
+	struct isa1200_config config;
+
+	int level;
+};
+
+static const struct regmap_config isa1200_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = ISA1200_HCTRLD,
+};
+
+static void isa1200_start(struct isa1200 *isa)
+{
+	struct isa1200_config *config = &isa->config;
+	struct pwm_state state;
+	u8 hctrl0 = 0, hctrl1 = 0;
+	DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
+
+	clk_prepare_enable(isa->clk);
+
+	bitmap_fill(values, ISA1200_EN_PINS_MAX);
+	gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
+
+	usleep_range(200, 300);
+
+	regmap_write(isa->map, ISA1200_SCTRL, config->ldo_voltage);
+
+	if (isa->clk) {
+		hctrl0 = ISA1200_HCTRL0_PWM_GEN_MODE;
+		hctrl1 = ISA1200_HCTRL1_EXT_CLOCK;
+	}
+
+	if (isa->pwm) {
+		hctrl0 = ISA1200_HCTRL0_PWM_INPUT_MODE;
+		hctrl1 = 0;
+	}
+
+	hctrl0 |= __ffs(config->clkdiv / ISA1200_HCTRL0_CLKDIV_128);
+	hctrl1 |= ISA1200_HCTRL1_DAC_INVERT;
+	hctrl1 |= ISA1200_HCTRL1_MODE(config->mode);
+
+	regmap_write(isa->map, ISA1200_HCTRL0, hctrl0);
+	regmap_write(isa->map, ISA1200_HCTRL1, hctrl1);
+
+	/* Make sure to de-assert software reset */
+	regmap_write(isa->map, ISA1200_HCTRL2, 0x00);
+
+	/* PLL divisor */
+	regmap_write(isa->map, ISA1200_HCTRL3,
+		     ISA1200_HCTRL3_PLLDIV(config->plldiv) |
+		     ISA1200_HCTRL3_DEFAULT);
+
+	/* Frequency */
+	regmap_write(isa->map, ISA1200_HCTRL4, config->freq);
+	/* Duty cycle */
+	regmap_write(isa->map, ISA1200_HCTRL5, config->period >> 1);
+	/* Period */
+	regmap_write(isa->map, ISA1200_HCTRL6, config->period);
+
+	hctrl0 |= ISA1200_HCTRL0_HAP_ENABLE;
+	regmap_write(isa->map, ISA1200_HCTRL0, hctrl0);
+
+	if (isa->clk)
+		regmap_write(isa->map, ISA1200_HCTRL5, config->duty);
+
+	if (isa->pwm) {
+		pwm_get_state(isa->pwm, &state);
+		state.duty_cycle = config->duty;
+		state.enabled = true;
+		pwm_apply_might_sleep(isa->pwm, &state);
+	}
+}
+
+static void isa1200_power_off(void *data)
+{
+	struct isa1200 *isa = data;
+
+	DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
+
+	bitmap_zero(values, ISA1200_EN_PINS_MAX);
+	gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
+
+	clk_disable_unprepare(isa->clk);
+}
+
+static void isa1200_stop(struct isa1200 *isa)
+{
+	struct pwm_state state;
+
+	if (isa->pwm) {
+		pwm_get_state(isa->pwm, &state);
+		state.duty_cycle = 0;
+		state.enabled = false;
+		pwm_apply_might_sleep(isa->pwm, &state);
+	}
+
+	regmap_write(isa->map, ISA1200_HCTRL0, 0x00);
+	isa1200_power_off(isa);
+}
+
+static void isa1200_play_work(struct work_struct *work)
+{
+	struct isa1200 *isa =
+		container_of(work, struct isa1200, play_work);
+
+	if (isa->level)
+		isa1200_start(isa);
+	else
+		isa1200_stop(isa);
+}
+
+static int isa1200_vibrator_play_effect(struct input_dev *input, void *data,
+					struct ff_effect *effect)
+{
+	struct isa1200 *isa = input_get_drvdata(input);
+	int level;
+
+	/*
+	 * TODO: we currently only support rumble.
+	 * The ISA1200 can control two motors and some devices
+	 * also have two motors mounted.
+	 */
+	level = effect->u.rumble.strong_magnitude;
+	if (!level)
+		level = effect->u.rumble.weak_magnitude;
+
+	dev_dbg(&input->dev, "FF effect type %d level %d\n",
+		effect->type, level);
+
+	if (isa->level != level) {
+		isa->level = level;
+		schedule_work(&isa->play_work);
+	}
+
+	return 0;
+}
+
+static void isa1200_vibrator_close(struct input_dev *input)
+{
+	struct isa1200 *isa = input_get_drvdata(input);
+
+	cancel_work_sync(&isa->play_work);
+
+	if (isa->level)
+		isa1200_stop(isa);
+
+	isa->level = 0;
+}
+
+static int isa1200_of_probe(struct i2c_client *client)
+{
+	static const char * const isa1200_supplies[] = { "vdd", "vddp" };
+	struct isa1200 *isa = i2c_get_clientdata(client);
+	struct isa1200_config *config = &isa->config;
+	struct device *dev = &client->dev;
+	struct fwnode_handle *ldo_node;
+	int ret;
+
+	isa->clk = devm_clk_get_optional(dev, NULL);
+	if (IS_ERR(isa->clk))
+		return dev_err_probe(dev, PTR_ERR(isa->clk),
+				     "failed to get clock\n");
+
+	isa->pwm = devm_pwm_get(dev, NULL);
+	if (IS_ERR(isa->pwm)) {
+		ret = PTR_ERR(isa->pwm);
+		if (ret == -ENODEV || ret == -EINVAL)
+			isa->pwm = NULL;
+		else
+			return dev_err_probe(dev, ret, "getting PWM\n");
+	}
+
+	if (!isa->clk && !isa->pwm)
+		return dev_err_probe(dev, -EINVAL,
+				     "clock or PWM are required, none were provided\n");
+
+	ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(isa1200_supplies),
+					     isa1200_supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to set up supplies\n");
+
+	isa->enable_gpios = devm_gpiod_get_array_optional(dev, "control",
+							  GPIOD_OUT_LOW);
+	if (IS_ERR(isa->enable_gpios))
+		return dev_err_probe(dev, PTR_ERR(isa->enable_gpios),
+				     "failed to get enable gpios\n");
+
+	ldo_node = device_get_named_child_node(dev, "ldo");
+	if (!ldo_node)
+		return dev_err_probe(dev, -ENODEV,
+				     "failed to get embedded LDO node\n");
+
+	config->ldo_voltage = 2300000;
+	ret = fwnode_property_read_u32(ldo_node, "regulator-min-microvolt",
+				       &config->ldo_voltage);
+	fwnode_handle_put(ldo_node);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to get ldo voltage\n");
+
+	/* Device supports only 2.3V - 3.8V range */
+	if (config->ldo_voltage < 2300000 || config->ldo_voltage > 3800000)
+		return dev_err_probe(dev, -EINVAL,
+				     "voltage is out of 2.3V - 3.8V range\n");
+
+	config->ldo_voltage /= ISA1200_LDO_VOLTAGE_STEP;
+	if (config->ldo_voltage < ISA1200_LDO_VOLTAGE_3V1)
+		config->ldo_voltage = config->ldo_voltage -
+				      ISA1200_LDO_VOLTAGE_2V3 +
+				      ISA1200_LDO_VOLTAGE_BASE;
+	else
+		config->ldo_voltage -= ISA1200_LDO_VOLTAGE_3V1;
+
+	config->mode = 0; /* LRA_MODE */
+	device_property_read_u32(dev, "imagis,mode", &config->mode);
+
+	config->clkdiv = ISA1200_HCTRL0_CLKDIV_128;
+	device_property_read_u32(dev, "imagis,clk-div", &config->clkdiv);
+	if (!config->clkdiv)
+		return dev_err_probe(dev, -EINVAL, "clk-div cannot be zero\n");
+
+	config->plldiv = 1;
+	device_property_read_u32(dev, "imagis,pll-div", &config->plldiv);
+
+	config->period = 0;
+	config->freq = 0;
+	config->duty = 0;
+
+	if (isa->clk) {
+		ret = device_property_read_u32(dev, "imagis,period-ns",
+					       &config->period);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to get period\n");
+
+		config->period /= ISA1200_HCTRL6_PERIOD_SCALE;
+		config->duty = config->period >> 1;
+	}
+
+	if (isa->pwm) {
+		struct pwm_state state;
+
+		pwm_init_state(isa->pwm, &state);
+
+		if (!state.period)
+			return dev_err_probe(dev, -EINVAL,
+					     "PWM period cannot be zero\n");
+
+		config->freq = div64_u64(NANO, state.period * config->clkdiv);
+		config->duty = state.period >> 1;
+
+		ret = pwm_apply_might_sleep(isa->pwm, &state);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to apply initial PWM state\n");
+	}
+
+	device_property_read_u32(dev, "imagis,duty-cycle-ns", &config->duty);
+
+	return 0;
+}
+
+static int isa1200_probe(struct i2c_client *client)
+{
+	struct isa1200 *isa;
+	struct device *dev = &client->dev;
+	DECLARE_BITMAP(values, ISA1200_EN_PINS_MAX);
+	u32 val;
+	int ret;
+
+	isa = devm_kzalloc(dev, sizeof(*isa), GFP_KERNEL);
+	if (!isa)
+		return -ENOMEM;
+
+	isa->input = devm_input_allocate_device(dev);
+	if (!isa->input)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, isa);
+
+	ret = isa1200_of_probe(client);
+	if (ret)
+		return ret;
+
+	isa->map = devm_regmap_init_i2c(client, &isa1200_regmap_config);
+	if (IS_ERR(isa->map))
+		return dev_err_probe(dev, PTR_ERR(isa->map),
+				     "failed to initialize register map\n");
+
+	clk_prepare_enable(isa->clk);
+
+	bitmap_fill(values, ISA1200_EN_PINS_MAX);
+	gpiod_multi_set_value_cansleep(isa->enable_gpios, values);
+
+	ret = devm_add_action_or_reset(dev, isa1200_power_off, isa);
+	if (ret)
+		return ret;
+
+	usleep_range(200, 300);
+
+	/* Read a register so we know that regmap and I2C transport works */
+	ret = regmap_read(isa->map, ISA1200_SCTRL, &val);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to read SCTRL\n");
+
+	ret = devm_work_autocancel(dev, &isa->play_work, isa1200_play_work);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to init work\n");
+
+	isa->input->name = "isa1200-haptic";
+	isa->input->id.bustype = BUS_HOST;
+	isa->input->dev.parent = dev;
+	isa->input->close = isa1200_vibrator_close;
+
+	input_set_drvdata(isa->input, isa);
+
+	/* TODO: this hardware can likely support more than rumble */
+	input_set_capability(isa->input, EV_FF, FF_RUMBLE);
+
+	ret = input_ff_create_memless(isa->input, NULL,
+				      isa1200_vibrator_play_effect);
+	if (ret)
+		return dev_err_probe(dev, ret, "couldn't create FF dev\n");
+
+	ret = input_register_device(isa->input);
+	if (ret)
+		return dev_err_probe(dev, ret, "couldn't register input dev\n");
+
+	return ret;
+}
+
+static int isa1200_suspend(struct device *dev)
+{
+	struct isa1200 *isa = dev_get_drvdata(dev);
+	struct input_dev *input_dev = isa->input;
+
+	guard(mutex)(&input_dev->mutex);
+
+	if (input_device_enabled(input_dev)) {
+		cancel_work_sync(&isa->play_work);
+		if (isa->level)
+			isa1200_stop(isa);
+	}
+
+	return 0;
+}
+
+static int isa1200_resume(struct device *dev)
+{
+	struct isa1200 *isa = dev_get_drvdata(dev);
+	struct input_dev *input_dev = isa->input;
+
+	guard(mutex)(&input_dev->mutex);
+
+	if (input_device_enabled(input_dev))
+		if (isa->level)
+			isa1200_start(isa);
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(isa1200_pm_ops, isa1200_suspend, isa1200_resume);
+
+static const struct of_device_id isa1200_of_match[] = {
+	{ .compatible = "imagis,isa1200" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, isa1200_of_match);
+
+static struct i2c_driver isa1200_i2c_driver = {
+	.driver = {
+		.name = "isa1200",
+		.of_match_table = isa1200_of_match,
+		.pm = pm_sleep_ptr(&isa1200_pm_ops),
+	},
+	.probe = isa1200_probe,
+};
+module_i2c_driver(isa1200_i2c_driver);
+
+MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Imagis ISA1200 haptic feedback unit");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 1/2] dt-bindings: input: Document Imagis ISA1200 haptic motor driver
From: Svyatoslav Ryhel @ 2026-04-28 11:43 UTC (permalink / raw)
  To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Linus Walleij, Svyatoslav Ryhel
  Cc: linux-input, devicetree, linux-kernel
In-Reply-To: <20260428114308.113253-1-clamor95@gmail.com>

Document the Imagis ISA1200 haptic motor driver, used primarily in mobile
handheld devices and capable of supporting up to two motors.

The exact datasheet for the ISA1200 is not available; all data was modeled
based on available downstream kernel sources for various devices and
fragments of information scattered across the internet.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../bindings/input/imagis,isa1200.yaml        | 140 ++++++++++++++++++
 1 file changed, 140 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/imagis,isa1200.yaml

diff --git a/Documentation/devicetree/bindings/input/imagis,isa1200.yaml b/Documentation/devicetree/bindings/input/imagis,isa1200.yaml
new file mode 100644
index 000000000000..40a4c7fd78bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/imagis,isa1200.yaml
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/input/imagis,isa1200.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Imagis ISA1200 haptic motor driver
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+  - Linus Walleij <linusw@kernel.org>
+
+description:
+  The ISA1200 is a high-performance enhanced haptic motor driver designed
+  for mobile hand-held devices. It supports various voltages for both ERM
+  (Eccentric Rotating Mass) and LRA (Linear Resonant Actuator) type
+  actuators. Thanks to an embedded LDO, battery power can be used directly
+  in handheld applications.
+
+properties:
+  compatible:
+    const: imagis,isa1200
+
+  reg:
+    maxItems: 1
+
+  control-gpios:
+    description:
+      One or two GPIOs flagged as active high linked to HEN and LEN pins
+    maxItems: 2
+
+  clocks:
+    maxItems: 1
+
+  pwms:
+    maxItems: 1
+
+  vdd-supply:
+    description:
+      Regulator for 2.4V - 5.5V power supply
+
+  vddp-supply:
+    description:
+      Regulator for 2.4V - 3.6V IO power supply
+
+  imagis,clk-div:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Divider for the external input clock/PWM
+    enum: [128, 256, 512, 1024]
+    default: 128
+
+  imagis,pll-div:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Divider for the internal PLL clock
+    minimum: 1
+    maximum: 15
+    default: 1
+
+  imagis,mode:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: |
+      Defines the motor type isa1200 drives
+      0 - LRA (Linear Resonant Actuator)
+      1 - ERM (Eccentric Rotating Mass)
+    enum: [0, 1]
+    default: 0
+
+  imagis,period-ns:
+    description:
+      Period of the internal PWM channel in nanoseconds.
+    minimum: 12800
+    maximum: 25400
+
+  imagis,duty-cycle-ns:
+    description:
+      Duty cycle of the external/internal PWM channel in nanoseconds,
+      defaults to 50% of the channel's period
+
+  ldo:
+    $ref: /schemas/regulator/regulator.yaml#
+    type: object
+    description:
+      Embedded LDO regulator with voltage range 2.3V - 3.8V
+    unevaluatedProperties: false
+
+    required:
+      - regulator-min-microvolt
+      - regulator-max-microvolt
+
+required:
+  - compatible
+  - reg
+  - ldo
+
+anyOf:
+  - required:
+      - clocks
+      - imagis,period-ns
+  - required:
+      - pwms
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        haptic-engine@49 {
+            compatible = "imagis,isa1200";
+            reg = <0x49>;
+
+            clocks = <&isa1200_refclk>;
+
+            control-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>,
+                            <&gpio 23 GPIO_ACTIVE_HIGH>;
+
+            vdd-supply = <&vdd_3v3_vbat>;
+            vddp-supply = <&vdd_2v8_vvib>;
+
+            imagis,clk-div = <256>;
+            imagis,pll-div = <2>;
+
+            imagis,mode = <0>; /* LRA_MODE */
+
+            imagis,period-ns = <13400>;
+            imagis,duty-cycle-ns = <100>;
+
+            ldo {
+                regulator-name = "vdd_vib";
+                regulator-min-microvolt = <2300000>;
+                regulator-max-microvolt = <2300000>;
+            };
+        };
+    };
-- 
2.51.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox