Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] HID: uhid: use strlcpy() instead of strncpy()
From: Jiri Kosina @ 2018-02-16 12:37 UTC (permalink / raw)
  To: Xiongfeng Wang; +Cc: benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <1515500612-191425-1-git-send-email-wangxiongfeng2@huawei.com>

On Tue, 9 Jan 2018, Xiongfeng Wang wrote:

> From: Xiongfeng Wang <xiongfeng.wang@linaro.org>
> 
> gcc-8 reports
> 
> drivers/hid/uhid.c: In function 'uhid_dev_create2':
> ./include/linux/string.h:245:9: warning: '__builtin_strncpy' output may
> be truncated copying 127 bytes from a string of length 127
> [-Wstringop-truncation]
> 
> The compiler require that the input param 'len' of strncpy() should be
> greater than the length of the src string, so that '\0' is copied as
> well. We can just use strlcpy() to avoid this warning.

Applied to for-4.17/upstream.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH V2] HID: corsair: Add K70 Vengeance and K70 RAPIDFIRE to
From: Jiri Kosina @ 2018-02-16 12:39 UTC (permalink / raw)
  To: Aaron Bottegal; +Cc: benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <20171222083814.9090-1-aaronbottegal@gmail.com>

On Fri, 22 Dec 2017, Aaron Bottegal wrote:

> K70 Vengeance sends garbage keypresses when changing  backlight brightness.
> This hooks to the existing corsair driver, which filters out those
> invalid keypresses on similar devices in the input mapping code.

Applied to for-4.16/upstream-fixes.

-- 
Jiri Kosina
SUSE Labs


^ permalink raw reply

* Re: [PATCH 1/2] input: touchscreen: ar1021_i2c: enable touch mode during open
From: Christian Gmeiner @ 2018-02-16 16:38 UTC (permalink / raw)
  To: Martin Kepplinger; +Cc: Dmitry Torokhov, linux-input, LKML
In-Reply-To: <5bfec26e-b09c-2fa0-4eb0-3f9a08d293ed@ginzinger.com>

2018-02-05 11:40 GMT+01:00 Martin Kepplinger <martin.kepplinger@ginzinger.com>:
>
>
>
>
> Martin Kepplinger | Entwicklung Software
>
> GINZINGER ELECTRONIC SYSTEMS GMBH
>
> Tel.: +43 7723 5422 157
> Mail: martin.kepplinger@ginzinger.com
> Web: www.ginzinger.com
>
>
>
>
> On 2018-02-05 11:07, Christian Gmeiner wrote:
>> Hi all.
>>
>> 2017-04-27 14:22 GMT+02:00 Martin Kepplinger <martin.kepplinger@ginzinger.com>:
>>> The device could as well be in command mode, in which this driver cannot
>>> handle the device. When opening the device, let's make sure the device
>>> will be in the mode we expect it to be for this driver.
>>>
>>
>> I run into issues caused by this change. It turns out that the device
>> is non-functional
>> after some warm-reboots and as a result I am not able to use xorg's
>> evdev driver.
>> So I have some questions about this change:
>>
>> * Should we enable irq before calling i2c_master_send(..) as the chip raises an
>>   irq if the command was processed?
>>
>> * Would it be enough to send this command only once during driver
>> lifetime? I can
>>   see that on my system open gets called 3 times during boot-up.
>
> It would. See below for my thought on this change.
>
>>
>> * What are the circumstances the touch device would be in an other state? In the
>>   official kernel driver the userspace can send commands via sysfs.
>> Also the driver
>>   does set the touch enable mode as this patch does.
>
> I did this change as the device was once non-functional unexpectedly
> because it wasn't in touch mode. We can set touch mode during open() or
> probe() but I figured during open() would keep the driver working even
> when others would use the device in command mode.
>
> Does your problem go away when you revert this change or put it into
> probe()?

I needed to postprone further research and reverted this commit locally as
a new software release gets releases soon. The good this that I have
an automated way to run a test to trigger this issue quite easily.

Will have a deeper look after release time.

-- 
greets
--
Christian Gmeiner, MSc

https://christian-gmeiner.info

^ permalink raw reply

* Re: [PATCH 1/2] input: touchscreen: ar1021_i2c: enable touch mode during open
From: Christian Gmeiner @ 2018-02-16 16:46 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Martin Kepplinger, linux-input, LKML
In-Reply-To: <20180206012027.GF46524@dtor-ws>

2018-02-06 2:20 GMT+01:00 Dmitry Torokhov <dmitry.torokhov@gmail.com>:
> On Mon, Feb 05, 2018 at 11:07:08AM +0100, Christian Gmeiner wrote:
>> Hi all.
>>
>> 2017-04-27 14:22 GMT+02:00 Martin Kepplinger <martin.kepplinger@ginzinger.com>:
>> > The device could as well be in command mode, in which this driver cannot
>> > handle the device. When opening the device, let's make sure the device
>> > will be in the mode we expect it to be for this driver.
>> >
>>
>> I run into issues caused by this change. It turns out that the device
>> is non-functional
>> after some warm-reboots and as a result I am not able to use xorg's
>> evdev driver.
>> So I have some questions about this change:
>>
>> * Should we enable irq before calling i2c_master_send(..) as the chip raises an
>>   irq if the command was processed?
>
> Well, we do not care about the response... However, what is your
> interrupt trigger settings? Are you using edge by chance? If so, please
> try switching to level.
>

We may should take care of the response. I have seen strange responses
after the third open of the device.

&i2c2 {
       ar1021@4d {
               compatible = "microchip,ar1021-i2c";
               reg = <0x4d>;
               interrupt-parent = <&gpio3>;
               interrupts = <26 IRQ_TYPE_EDGE_RISING>;
       };
};

I am not sure if this is really the cause of the problem! As without this commit
my device survives one week of a automated touch-press-test done with some
mechanical gear and with the help of an SPS.

>>
>> * Would it be enough to send this command only once during driver
>> lifetime? I can
>>   see that on my system open gets called 3 times during boot-up.
>>
>> * What are the circumstances the touch device would be in an other state? In the
>>   official kernel driver the userspace can send commands via sysfs.
>> Also the driver
>>   does set the touch enable mode as this patch does.
>
> What is "the official kernel driver"?
>

http://ww1.microchip.com/downloads/en/DeviceDoc/AR1020-AR1021-LINUX-SPI-I2C-V102.tar.gz

As it is release time at my company I had to stop finding the root
cause but will look into the issue
again in 2-3 weeks.


-- 
greets
--
Christian Gmeiner, MSc

https://christian-gmeiner.info

^ permalink raw reply

* Re: [PATCH 2/3] HID: steam: add serial number information.
From: Rodrigo Rivas Costa @ 2018-02-16 20:59 UTC (permalink / raw)
  To: Benjamin Tissoires; +Cc: Jiri Kosina, lkml, linux-input
In-Reply-To: <CAO-hwJ+9Jvd8x5dRaerstzeNNgY4_mUar77BB1k2ctxaOCJLtQ@mail.gmail.com>

On Fri, Feb 16, 2018 at 11:38:11AM +0100, Benjamin Tissoires wrote:
> On Fri, Feb 16, 2018 at 10:57 AM, Rodrigo Rivas Costa
> <rodrigorivascosta@gmail.com> wrote:
> > On Fri, Feb 16, 2018 at 10:31:35AM +0100, Benjamin Tissoires wrote:
> >> > Ok, I'll do that. The weird thing, however, is that:
> >> >
> >> >         hid_hw_raw_request(steam->hid_dev, 0x00,
> >> >                 buf, hid_report_len(r), /* 64 */
> >> >                 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> >> >
> >> > fails with EOVERFLOW. I have to use:
> >> >
> >> >         hid_hw_raw_request(steam->hid_dev, 0x00,
> >> >                 buf, 65
> >> >                 HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> >> >
> >> > which just feels wrong to me.
> >>
> >> Indeed
> >>
> 
> Arf, looks like usbhid expects the buffer to start with 0x00 when the
> report is not numbered, thus adding one to the report length.
> 
> I guess that nobody tried to recently set/get reports on a device
> without a report ID. And hidraw matches this behavior too, which means
> it's hard to change.
> 
> One thing I'd like to try to change is the result of hid_report_len.
> If everybody expects the size to be of one more when the report is
> unnumbered, maybe we could simply add this placeholder in the report
> size from the beginning.

I've been trying to make sense of all this numbered/buf++/count-- stuff,
and I admit I don't understand half of it...

But about that +7 in hid_alloc_report_buf(), isn't it to make room for
the implement()/extract() functions? And IIUIC those are not used for
raw_requests... they are instead passed directly to usb_control_msg()
(or whatever the ll driver does). That's the point of being raw, isn't
it?

If I'm right with that, would it make sense to go back to kzalloc(65)?

If I'm wrong, then if you agree, I'll default to:

	hid_hw_raw_request(steam->hid_dev, 0x00,
		buf, hid_report_len(r) + 1, /* count the request number	*/
		HID_FEATURE_REPORT, HID_REQ_GET_REPORT);

Then, if hid_report_len() is ever updated to return the +1, this one
should be removed. And even if it is not, we still have +6 extra bytes
from hid_alloc_report_buf(), so no real harm done.

Regards.
Rodrigo

^ permalink raw reply

* Re: [RFC PATCH] input: Add disable sysfs entry for every input device
From: Pavel Machek @ 2018-02-17 21:19 UTC (permalink / raw)
  To: Peter Hutterer
  Cc: Pali Rohár, Bastien Nocera, Dmitry Torokhov, Ivaylo Dimitrov,
	Sebastian Reichel, Mauro Carvalho Chehab, Chuck Ebbert,
	Henrik Rydberg, linux-input, linux-kernel
In-Reply-To: <20180109020424.GC29886@jelly>

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

Hi!

> > > > So, do you mean to implement this "disable" action as ioctl for
> > > > particular /dev/input/event* device (instead of sysfs entry)?
> > > 
> > > Yes, so the device can be powered down without the device node being
> > > closed and made unavailable. I don't know whether that's something
> > > that's already possible for all cases, but there's already
> > > opportunistic in a lot of drivers and subsystems.
> > > 
> > > This opens up a whole new wave of potential problems, but it's a more
> > > generally useful mechanism, I would think.
> > 
> > Ok. How should API for this ioctl looks like? And do you have an idea
> > for name of that ioctl?
> > 
> > Dmitry, what do you think about it? It is acceptable for you?
> 
> first: sysfs files are pretty terrible because writing to them requires root
> and we don't have the benefit of logind. so for any sysfs toggle expect
> a nicely integrated userspace solution to be less than optimal.

Well, you can chmod / chown sysfs files.

> besides: 99% of the above is figuring out the policy *when* to disable the
> device. disabling it is trivial by just closing the evdev nodes and tbh I
> don't think we (in userspace) should care about whether the device is
> powered down or now, it should be the default assumption that it is powered
> down when not in use.
> 
> for the cases where you must keep the device open but you don't want events, 
> EVIOCSMASK is likely the best solution. improving the kernel so it powers
> down the device when the mask excludes all events (and there are no other
> listeners) could be an interesting task.

But yes, that sounds like an idea.

BTW in the meantime, someone added this to pmos wiki... this should
solve some of my problems.

Best regards,
								Pavel



FILE=~/.screenoff
if [ -f $FILE ]; then
 xinput set-prop 8 "Device Enabled" 1
 xinput set-prop 6 "Device Enabled" 1
 xinput set-prop 9 "Device Enabled" 1
 xset dpms force on
 rm ~/.screenoff
else
 xinput set-prop 8 "Device Enabled" 0
 xinput set-prop 6 "Device Enabled" 0
 xinput set-prop 9 "Device Enabled" 0
 xset dpms force off
 touch ~/.screenoff
fi
					

    
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply

* [PATCH v2] HID: sony: Add touchpad support for NSG-MR5U and NSG-MR7U remotes
From: Todd Kelner @ 2018-02-17 23:47 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Roderick Colenbrander,
	linux-input

Sony's NSG-MR5U and NSG-MR7U remote controls have a full keyboard and a
touchpad.  The keyboard is already supported by the existing Linux
kernel and drivers but the touchpad is not recognized.  This patch adds
the coded needed to bring full functionality to the touchpad.

Note that these remotes use the vendor code for SMK even though they are
Sony branded.

Known limitations
- The built-in accelerometers are not supported by these changes
- When the Drag (Fn) key is used as a mouse button, the button is
automatically released when the key begins repeating.  There are two
  workarounds for this 1) Use the button behind the touchpad instead of
  the Drag (Fn) key or 2) Disable the key repeat functionality or
  increase the key repeat delay.

Signed-off-by: Todd Kelner <tsopdump@gmail.com>
---
Changes in v2:
- Brought code up-to-date with latest changes from git
- Converted relative trackpad coordinates from unsigned to signed
- In sony_register_touchpad, merged two occurrences of
   input_mt_init_slots into one
- Added new parameters to sony_register_trackpad to support touch major
   and minor and orientation settings

 drivers/hid/hid-ids.h    |   3 ++
 drivers/hid/hid-quirks.c |   2 +
 drivers/hid/hid-sony.c   | 131 ++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 43ddcdf..5d56387 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -958,6 +958,9 @@
 
 #define USB_VENDOR_ID_SMK		0x0609
 #define USB_DEVICE_ID_SMK_PS3_BDREMOTE	0x0306
+#define USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE       0x0368
+#define USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE       0x0369
+
 
 #define USB_VENDOR_ID_SONY			0x054c
 #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE	0x024b
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 5f6035a..376cbbc 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -608,6 +608,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
 #if IS_ENABLED(CONFIG_HID_SONY)
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_PS3_BDREMOTE) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_BUZZ_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_WIRELESS_BUZZ_CONTROLLER) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_MOTION_CONTROLLER) },
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index ccdc5f2..e475c50 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -9,6 +9,7 @@
  *  Copyright (c) 2006-2013 Jiri Kosina
  *  Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com>
  *  Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com>
+ *  Copyright (c) 2018 Todd Kelner
  */
 
 /*
@@ -55,6 +56,8 @@
 #define NAVIGATION_CONTROLLER_BT  BIT(11)
 #define SINO_LITE_CONTROLLER      BIT(12)
 #define FUTUREMAX_DANCE_MAT       BIT(13)
+#define NSG_MR5U_REMOTE_BT        BIT(14)
+#define NSG_MR7U_REMOTE_BT        BIT(15)
 
 #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT)
 #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT)
@@ -72,8 +75,11 @@
 				MOTION_CONTROLLER)
 #define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\
 			MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT)
+#define NSG_MRXU_REMOTE (NSG_MR5U_REMOTE_BT | NSG_MR7U_REMOTE_BT)
 
 #define MAX_LEDS 4
+#define NSG_MRXU_MAX_X 1667
+#define NSG_MRXU_MAX_Y 1868
 
 
 /* PS/3 Motion controller */
@@ -1098,6 +1104,80 @@ static void dualshock4_parse_report(struct sony_sc *sc, u8 *rd, int size)
 	}
 }
 
+static void nsg_mrxu_parse_report(struct sony_sc *sc, u8 *rd, int size)
+{
+	int n, offset, relx, rely;
+	u8 active;
+
+	/*
+	 * The NSG-MRxU multi-touch trackpad data starts at offset 1 and
+	 *   the touch-related data starts at offset 2.
+	 * For the first byte, bit 0 is set when touchpad button is pressed.
+	 * Bit 2 is set when a touch is active and the drag (Fn) key is pressed.
+	 * This drag key is mapped to BTN_LEFT.  It is operational only when a 
+	 *   touch point is active.
+	 * Bit 4 is set when only the first touch point is active.
+	 * Bit 6 is set when only the second touch point is active.
+	 * Bits 5 and 7 are set when both touch points are active.
+	 * The next 3 bytes are two 12 bit X/Y coordinates for the first touch.
+	 * The following byte, offset 5, has the touch width and length.
+	 *   Bits 0-4=X (width), bits 5-7=Y (length).
+	 * A signed relative X coordinate is at offset 6.
+	 * The bytes at offset 7-9 are the second touch X/Y coordinates.
+	 * Offset 10 has the second touch width and length.
+	 * Offset 11 has the relative Y coordinate.
+	 */
+	offset = 1;
+
+	input_report_key(sc->touchpad, BTN_LEFT, rd[offset] & 0x0F);
+	active = (rd[offset] >> 4);
+	relx = (s8) rd[offset+5];
+	rely = ((s8) rd[offset+10]) * -1;
+
+	offset++;
+
+	for (n = 0; n < 2; n++) {
+		u16 x, y;
+		u8 contactx, contacty;
+
+		x = rd[offset] | ((rd[offset+1] & 0x0F) << 8);
+		y = ((rd[offset+1] & 0xF0) >> 4) | (rd[offset+2] << 4);
+
+		input_mt_slot(sc->touchpad, n);
+		input_mt_report_slot_state(sc->touchpad, MT_TOOL_FINGER, active & 0x03);
+
+		if (active & 0x03) {
+			contactx = rd[offset+3] & 0x0F;
+			contacty = rd[offset+3] >> 4;
+			input_report_abs(sc->touchpad, ABS_MT_TOUCH_MAJOR,
+				max(contactx, contacty));
+			input_report_abs(sc->touchpad, ABS_MT_TOUCH_MINOR,
+				min(contactx, contacty));
+			input_report_abs(sc->touchpad, ABS_MT_ORIENTATION,
+				(bool) (contactx > contacty));
+			input_report_abs(sc->touchpad, ABS_MT_POSITION_X, x);
+			input_report_abs(sc->touchpad, ABS_MT_POSITION_Y,
+				NSG_MRXU_MAX_Y - y);
+			/*
+			 * The relative coordinates belong to the first touch
+			 * point, when present, or to the second touch point
+			 * when the first is not active.
+			 */
+			if ((n == 0) || ((n == 1) && (active & 0x01))) {
+				input_report_rel(sc->touchpad, REL_X, relx);
+				input_report_rel(sc->touchpad, REL_Y, rely);
+			}
+		}
+
+		offset += 5;
+		active >>= 2;
+	}
+
+	input_mt_sync_frame(sc->touchpad);
+
+	input_sync(sc->touchpad);
+}
+
 static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		u8 *rd, int size)
 {
@@ -1206,6 +1286,10 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report,
 		}
 
 		dualshock4_parse_report(sc, rd, size);
+
+	} else if ((sc->quirks & NSG_MRXU_REMOTE) && rd[0] == 0x02) {
+		nsg_mrxu_parse_report(sc, rd, size);
+		return 1;
 	}
 
 	if (sc->defer_initialization) {
@@ -1263,7 +1347,7 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
 }
 
 static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
-					int w, int h)
+		int w, int h, int touch_major, int touch_minor, int orientation)
 {
 	size_t name_sz;
 	char *name;
@@ -1294,10 +1378,6 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
 	snprintf(name, name_sz, "%s" DS4_TOUCHPAD_SUFFIX, sc->hdev->name);
 	sc->touchpad->name = name;
 
-	ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
-	if (ret < 0)
-		goto err;
-
 	/* We map the button underneath the touchpad to BTN_LEFT. */
 	__set_bit(EV_KEY, sc->touchpad->evbit);
 	__set_bit(BTN_LEFT, sc->touchpad->keybit);
@@ -1306,6 +1386,25 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
 	input_set_abs_params(sc->touchpad, ABS_MT_POSITION_X, 0, w, 0, 0);
 	input_set_abs_params(sc->touchpad, ABS_MT_POSITION_Y, 0, h, 0, 0);
 
+	if (touch_major > 0) {
+		input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MAJOR, 
+			0, touch_major, 0, 0);
+		if (touch_minor > 0)
+			input_set_abs_params(sc->touchpad, ABS_MT_TOUCH_MINOR, 
+				0, touch_minor, 0, 0);
+		if (orientation > 0)
+			input_set_abs_params(sc->touchpad, ABS_MT_ORIENTATION, 
+				0, orientation, 0, 0);
+	}
+
+	if (sc->quirks & NSG_MRXU_REMOTE) {
+		__set_bit(EV_REL, sc->touchpad->evbit);
+	}
+
+	ret = input_mt_init_slots(sc->touchpad, touch_count, INPUT_MT_POINTER);
+	if (ret < 0)
+		goto err;
+
 	ret = input_register_device(sc->touchpad);
 	if (ret < 0)
 		goto err;
@@ -2690,7 +2789,7 @@ static int sony_input_configured(struct hid_device *hdev,
 		 * The Dualshock 4 touchpad supports 2 touches and has a
 		 * resolution of 1920x942 (44.86 dots/mm).
 		 */
-		ret = sony_register_touchpad(sc, 2, 1920, 942);
+		ret = sony_register_touchpad(sc, 2, 1920, 942, 0, 0, 0);
 		if (ret) {
 			hid_err(sc->hdev,
 			"Unable to initialize multi-touch slots: %d\n",
@@ -2721,6 +2820,20 @@ static int sony_input_configured(struct hid_device *hdev,
 		}
 
 		sony_init_output_report(sc, dualshock4_send_output_report);
+	} else if (sc->quirks & NSG_MRXU_REMOTE) {
+		/*
+		 * The NSG-MRxU touchpad supports 2 touches and has a
+		 * resolution of 1667x1868
+		 */
+		ret = sony_register_touchpad(sc, 2,
+			NSG_MRXU_MAX_X, NSG_MRXU_MAX_Y, 15, 15, 1);
+		if (ret) {
+			hid_err(sc->hdev,
+			"Unable to initialize multi-touch slots: %d\n",
+			ret);
+			goto err_stop;
+		}
+
 	} else if (sc->quirks & MOTION_CONTROLLER) {
 		sony_init_output_report(sc, motion_send_output_report);
 	} else {
@@ -2969,6 +3082,12 @@ static const struct hid_device_id sony_devices[] = {
 	/* Nyko Core Controller for PS3 */
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SINO_LITE, USB_DEVICE_ID_SINO_LITE_CONTROLLER),
 		.driver_data = SIXAXIS_CONTROLLER_USB | SINO_LITE_CONTROLLER },
+	/* SMK-Link NSG-MR5U Remote Control */
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR5U_REMOTE),
+		.driver_data = NSG_MR5U_REMOTE_BT },
+	/* SMK-Link NSG-MR7U Remote Control */
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SMK, USB_DEVICE_ID_SMK_NSG_MR7U_REMOTE),
+		.driver_data = NSG_MR7U_REMOTE_BT },
 	{ }
 };
 MODULE_DEVICE_TABLE(hid, sony_devices);
-- 
2.7.4


^ permalink raw reply related

* [PATCH v3 00/10] extend PWM framework to support PWM modes
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media

Hi all,

Please give feedback on these patches which extends the PWM framework in
order to support multiple PWM modes of operations. This series is a rework
of [1] and [2].

The current patch series add the following PWM modes:
- PWM mode normal
- PWM mode complementary
- PWM mode push-pull

Normal mode - for PWM channels with one output; output waveforms looks like
this:
             __    __    __    __
    PWM   __|  |__|  |__|  |__|  |__
            <--T-->

    Where T is the signal period

Since PWMs with more than one output per channel could be used as one
output PWM the normal mode is the default mode for all PWMs (if not
specified otherwise).

Complementary mode - for PWM channels with two outputs; output waveforms
for a PWM channel in complementary mode looks line this:
             __    __    __    __
    PWMH1 __|  |__|  |__|  |__|  |__
          __    __    __    __    __
    PWML1   |__|  |__|  |__|  |__|
            <--T-->

    Where T is the signal period.

Push-pull mode - for PWM channels with two outputs; output waveforms for a
PWM channel in push-pull mode with normal polarity looks like this:
            __          __
    PWMH __|  |________|  |________
                  __          __
    PWML ________|  |________|  |__
           <--T-->

    If polarity is inversed:
         __    ________    ________
    PWMH   |__|        |__|
         ________    ________    __
    PWML         |__|        |__|
           <--T-->

    Where T is the signal period.

The PWM working modes are per PWM channel registered as PWM's capabilities.
The driver registers itself to PWM core a get_caps() function, in
struct pwm_ops, that will be used by PWM core to retrieve PWM capabilities.
If this function is not registered in driver's probe, a default function
will be used to retrieve PWM capabilities. Currently, the default
capabilities includes only PWM normal mode.

PWM state has been updated to keep PWM mode. PWM mode could be configured
via sysfs or via DT. pwm_apply_state() will do the preliminary validation
for PWM mode to be applied.

In sysfs, user could get PWM modes by reading mode file of PWM device:
root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# ls -l
total 0
-r--r--r-- 1 root root 4096 Oct  9 09:07 capture
lrwxrwxrwx 1 root root    0 Oct  9 09:07 device -> ../../pwmchip0
-rw-r--r-- 1 root root 4096 Oct  9 08:42 duty_cycle
-rw-r--r-- 1 root root 4096 Oct  9 08:44 enable
--w------- 1 root root 4096 Oct  9 09:07 export
-rw-r--r-- 1 root root 4096 Oct  9 08:43 mode
-r--r--r-- 1 root root 4096 Oct  9 09:07 npwm
-rw-r--r-- 1 root root 4096 Oct  9 08:42 period
-rw-r--r-- 1 root root 4096 Oct  9 08:44 polarity
drwxr-xr-x 2 root root    0 Oct  9 09:07 power
lrwxrwxrwx 1 root root    0 Oct  9 09:07 subsystem -> ../../../../../../../../class/pwm
-rw-r--r-- 1 root root 4096 Oct  9 08:42 uevent
--w------- 1 root root 4096 Oct  9 09:07 unexport
root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# cat mode
normal complementary [push-pull]

The mode enclosed in bracket is the currently active mode.

The mode could be set, via sysfs, by writing to mode file one of the modes
displayed at read:
root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# echo normal > mode
root@sama5d2-xplained:/sys/class/pwm/pwmchip0/pwm2# cat mode
[normal] complementary push-pull 

The PWM push-pull mode could be usefull in applications like half bridge
converters.

This series also add support for PWM modes on Atmel/Microchip SoCs.

Thank you,
Claudiu Beznea

[1] https://www.spinics.net/lists/arm-kernel/msg580275.html
[2] https://lkml.org/lkml/2018/1/12/359

Changes in v3:
- removed changes related to only one of_xlate function for all PWM drivers
- switch to PWM capabilities per PWM channel nor per PWM chip
- squash documentation and bindings patches as requeted by reviewer
- introduced PWM_MODE(name) macro and used a bit enum for pwm modes
- related to DT bindings, used flags cell also for PWM modes
- updated of_xlate specific functions with "state->mode = mode;"
  instructions to avoid pwm_apply_state() failures
- use available modes for PWM channel in pwm_config() by first calling
  pwm_get_caps() to get caps.modes
- use loops through available modes in mode_store()/mode_show() and also in
  of_pwm_xlate_with_flags() instead of "if else" instructions; in this way,
  the addition of a new mode is independent of this code sections
- use DTLI=1, DTHI=0 register settings to obtain push-pull mode waveforms
  for Atmel/Microchip PWM controller.

Changes in v2:
- remove of_xlate and of_pwm_n_cells and use generic functions to pharse DT
  inputs; this is done in patches 1, 2, 3, 4, 5, 6, 7 of this series; this will
  make easy the addition of PWM mode support from DT
- add PWM mode normal
- register PWM modes as capabilities of PWM chips at driver probe and, in case
  driver doesn't provide these capabilities use default ones
- change the way PWM mode is pharsed via DT by using a new input for pwms
  binding property


Claudiu Beznea (10):
  pwm: extend PWM framework with PWM modes
  pwm: clps711x: populate PWM mode in of_xlate function
  pwm: cros-ec: populate PWM mode in of_xlate function
  pwm: pxa: populate PWM mode in of_xlate function
  pwm: add PWM mode to pwm_config()
  pwm: add PWM modes
  pwm: atmel: add pwm capabilities
  pwm: add push-pull mode support
  pwm: add documentation for pwm push-pull mode
  pwm: atmel: add push-pull mode support

 Documentation/devicetree/bindings/pwm/pwm.txt |  11 ++-
 Documentation/pwm.txt                         |  42 ++++++++-
 arch/arm/mach-s3c24xx/mach-rx1950.c           |  11 ++-
 drivers/bus/ts-nbus.c                         |   2 +-
 drivers/clk/clk-pwm.c                         |   3 +-
 drivers/gpu/drm/i915/intel_panel.c            |  17 +++-
 drivers/hwmon/pwm-fan.c                       |   2 +-
 drivers/input/misc/max77693-haptic.c          |   2 +-
 drivers/input/misc/max8997_haptic.c           |   6 +-
 drivers/leds/leds-pwm.c                       |   5 +-
 drivers/media/rc/ir-rx51.c                    |   5 +-
 drivers/media/rc/pwm-ir-tx.c                  |   5 +-
 drivers/pwm/core.c                            |  98 ++++++++++++++++++++-
 drivers/pwm/pwm-atmel.c                       | 118 +++++++++++++++++++-------
 drivers/pwm/pwm-clps711x.c                    |  12 ++-
 drivers/pwm/pwm-cros-ec.c                     |   4 +
 drivers/pwm/pwm-pxa.c                         |   4 +
 drivers/pwm/sysfs.c                           |  56 ++++++++++++
 drivers/video/backlight/lm3630a_bl.c          |   4 +-
 drivers/video/backlight/lp855x_bl.c           |   4 +-
 drivers/video/backlight/lp8788_bl.c           |   5 +-
 drivers/video/backlight/pwm_bl.c              |  11 ++-
 drivers/video/fbdev/ssd1307fb.c               |   3 +-
 include/dt-bindings/pwm/pwm.h                 |   2 +
 include/linux/pwm.h                           | 100 +++++++++++++++++++++-
 25 files changed, 470 insertions(+), 62 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH v3 01/10] pwm: extend PWM framework with PWM modes
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add basic PWM modes: normal and complementary. These modes should
differentiate the single output PWM channels from two outputs PWM
channels. These modes could be set as follow:
1. PWM channels with one output per channel:
- normal mode
2. PWM channels with two outputs per channel:
- normal mode
- complementary mode
Since users could use a PWM channel with two output as one output PWM
channel, the PWM normal mode is allowed to be set for PWM channels with
two outputs; in fact PWM normal mode should be supported by all PWMs.

The PWM capabilities were implemented per PWM channel. Every PWM controller
will register a function to get PWM capabilities. If this is not explicitly
set by the driver a default function will be used to retrieve the the PWM
capabilities (in this case the PWM capabilities will contain only PWM
normal mode). This function is set in pwmchip_add_with_polarity() as a
member of "struct pwm_chip". To retrieve capabilities the pwm_get_caps()
function could be used.

Every PWM channel will have associated a mode in the PWM state. Proper
helper functions were added to get/set PWM mode. The mode could also be set
from DT via flag cells. The valid DT modes could be located in
include/dt-bindings/pwm/pwm.h. Only modes supported by PWM channel could be
set. If nothing is specified for a PWM channel, via DT, the first available
mode will be used (normally, this will be PWM normal mode).

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/core.c  | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 drivers/pwm/sysfs.c | 56 ++++++++++++++++++++++++++++++
 include/linux/pwm.h | 87 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 237 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index 1581f6ab1b1f..16a409d452c0 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -136,6 +136,8 @@ struct pwm_device *
 of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
 {
 	struct pwm_device *pwm;
+	struct pwm_caps caps;
+	int modebit;
 
 	/* check, whether the driver supports a third cell for flags */
 	if (pc->of_pwm_n_cells < 3)
@@ -152,11 +154,23 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args)
 	if (IS_ERR(pwm))
 		return pwm;
 
+	pwm_get_caps(pc, pwm, &caps);
+
 	pwm->args.period = args->args[1];
 	pwm->args.polarity = PWM_POLARITY_NORMAL;
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
+
+	if (args->args_count > 2) {
+		if (args->args[2] & PWM_POLARITY_INVERTED)
+			pwm->args.polarity = PWM_POLARITY_INVERSED;
 
-	if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED)
-		pwm->args.polarity = PWM_POLARITY_INVERSED;
+		for (modebit = PWM_MODE_COMPLEMENTARY_BIT;
+		     modebit < PWM_MODE_CNT; modebit++)
+			if (args->args[2] & BIT(modebit)) {
+				pwm->args.mode = BIT(modebit);
+				break;
+			}
+	}
 
 	return pwm;
 }
@@ -166,6 +180,7 @@ static struct pwm_device *
 of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 {
 	struct pwm_device *pwm;
+	struct pwm_caps caps;
 
 	/* sanity check driver support */
 	if (pc->of_pwm_n_cells < 2)
@@ -182,7 +197,9 @@ of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 	if (IS_ERR(pwm))
 		return pwm;
 
+	pwm_get_caps(pc, pwm, &caps);
 	pwm->args.period = args->args[1];
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
 	return pwm;
 }
@@ -250,6 +267,39 @@ static bool pwm_ops_check(const struct pwm_ops *ops)
 }
 
 /**
+ * pwm_get_caps() - get PWM capabilities
+ * @chip: PWM chip
+ * @pwm: PWM device to get the capabilities for
+ * @caps: returned capabilities
+ *
+ * Retrievers capabilities for PWM device.
+ */
+void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+		  struct pwm_caps *caps)
+{
+	if (!chip || !pwm || !caps)
+		return;
+
+	if (chip->ops && chip->ops->get_caps)
+		pwm->chip->ops->get_caps(chip, pwm, caps);
+	else if (chip->get_default_caps)
+		chip->get_default_caps(caps);
+}
+EXPORT_SYMBOL_GPL(pwm_get_caps);
+
+static void pwmchip_get_default_caps(struct pwm_caps *caps)
+{
+	static const struct pwm_caps default_caps = {
+		.modes = PWM_MODE(NORMAL),
+	};
+
+	if (!caps)
+		return;
+
+	*caps = default_caps;
+}
+
+/**
  * pwmchip_add_with_polarity() - register a new PWM chip
  * @chip: the PWM chip to add
  * @polarity: initial polarity of PWM channels
@@ -264,7 +314,8 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 			      enum pwm_polarity polarity)
 {
 	struct pwm_device *pwm;
-	unsigned int i;
+	struct pwm_caps caps;
+	unsigned int i, j;
 	int ret;
 
 	if (!chip || !chip->dev || !chip->ops || !chip->npwm)
@@ -275,6 +326,8 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 
 	mutex_lock(&pwm_lock);
 
+	chip->get_default_caps = pwmchip_get_default_caps;
+
 	ret = alloc_pwms(chip->base, chip->npwm);
 	if (ret < 0)
 		goto out;
@@ -295,6 +348,16 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 		pwm->hwpwm = i;
 		pwm->state.polarity = polarity;
 
+		pwm_get_caps(chip, pwm, &caps);
+
+		/* Check if modes are supported. */
+		if (!pwm_caps_valid(caps)) {
+			ret = -EINVAL;
+			goto free;
+		}
+
+		pwm->state.mode = BIT(ffs(caps.modes) - 1);
+
 		if (chip->ops->get_state)
 			chip->ops->get_state(chip, pwm, &pwm->state);
 
@@ -316,6 +379,17 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip,
 out:
 	mutex_unlock(&pwm_lock);
 	return ret;
+
+free:
+	for (j = 0; j < i; j++) {
+		pwm = &chip->pwms[j];
+		radix_tree_delete(&pwm_tree, pwm->pwm);
+	}
+	kfree(chip->pwms);
+	chip->pwms = NULL;
+
+	mutex_unlock(&pwm_lock);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(pwmchip_add_with_polarity);
 
@@ -466,10 +540,17 @@ EXPORT_SYMBOL_GPL(pwm_free);
  */
 int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
 {
+	struct pwm_caps caps;
 	int err;
 
 	if (!pwm || !state || !state->period ||
-	    state->duty_cycle > state->period)
+	    state->duty_cycle > state->period ||
+	    !pwm_mode_valid(state->mode))
+		return -EINVAL;
+
+	/* Check if mode is supported by PWM. */
+	pwm_get_caps(pwm->chip, pwm, &caps);
+	if (!(caps.modes & state->mode))
 		return -EINVAL;
 
 	if (!memcmp(state, &pwm->state, sizeof(*state)))
@@ -530,6 +611,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
 
 			pwm->state.enabled = state->enabled;
 		}
+
+		/* No mode support for non-atomic PWM. */
+		pwm->state.mode = state->mode;
 	}
 
 	return 0;
@@ -579,6 +663,8 @@ int pwm_adjust_config(struct pwm_device *pwm)
 	pwm_get_args(pwm, &pargs);
 	pwm_get_state(pwm, &state);
 
+	state.mode = pargs.mode;
+
 	/*
 	 * If the current period is zero it means that either the PWM driver
 	 * does not support initial state retrieval or the PWM has not yet
@@ -767,6 +853,7 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
 	unsigned int best = 0;
 	struct pwm_lookup *p, *chosen = NULL;
 	unsigned int match;
+	struct pwm_caps caps;
 	int err;
 
 	/* look up via DT first */
@@ -848,8 +935,10 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id)
 	if (IS_ERR(pwm))
 		return pwm;
 
+	pwm_get_caps(chip, pwm, &caps);
 	pwm->args.period = chosen->period;
 	pwm->args.polarity = chosen->polarity;
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
 	return pwm;
 }
@@ -999,6 +1088,7 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s)
 		seq_printf(s, " duty: %u ns", state.duty_cycle);
 		seq_printf(s, " polarity: %s",
 			   state.polarity ? "inverse" : "normal");
+		seq_printf(s, " mode: %s", pwm_mode_desc(state.mode));
 
 		seq_puts(s, "\n");
 	}
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 83f2b0b15712..d9eb0eb63d23 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -223,11 +223,66 @@ static ssize_t capture_show(struct device *child,
 	return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t mode_show(struct device *child,
+			 struct device_attribute *attr,
+			 char *buf)
+{
+	struct pwm_device *pwm = child_to_pwm_device(child);
+	struct pwm_state state;
+	struct pwm_caps caps;
+	int modebit, len = 0;
+
+	pwm_get_state(pwm, &state);
+	pwm_get_caps(pwm->chip, pwm, &caps);
+
+	for (modebit = PWM_MODE_NORMAL_BIT; modebit < PWM_MODE_CNT; modebit++)
+		if (caps.modes & BIT(modebit)) {
+			if (state.mode == BIT(modebit))
+				len += scnprintf(buf + len,
+						 PAGE_SIZE - len, "[%s] ",
+						 pwm_mode_desc(BIT(modebit)));
+			else
+				len += scnprintf(buf + len,
+						 PAGE_SIZE - len, "%s ",
+						 pwm_mode_desc(BIT(modebit)));
+		}
+
+	len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+
+	return len;
+}
+
+static ssize_t mode_store(struct device *child,
+			  struct device_attribute *attr,
+			  const char *buf, size_t size)
+{
+	struct pwm_export *export = child_to_pwm_export(child);
+	struct pwm_device *pwm = export->pwm;
+	struct pwm_state state;
+	int modebit, ret;
+
+	for (modebit = PWM_MODE_NORMAL_BIT; modebit < PWM_MODE_CNT; modebit++)
+		if (sysfs_streq(buf, pwm_mode_desc(BIT(modebit))))
+			break;
+
+	if (modebit == PWM_MODE_CNT)
+		return -EINVAL;
+
+	mutex_lock(&export->lock);
+	pwm_get_state(pwm, &state);
+	state.mode = BIT(modebit);
+	ret = pwm_apply_state(pwm, &state);
+	mutex_unlock(&export->lock);
+
+	return ret ? : size;
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RW(mode);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
@@ -235,6 +290,7 @@ static struct attribute *pwm_attrs[] = {
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
 	&dev_attr_capture.attr,
+	&dev_attr_mode.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 56518adc31dd..e62349f48129 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -26,9 +26,32 @@ enum pwm_polarity {
 };
 
 /**
+ * PWM modes
+ * @PWM_MODE_NORMAL_BIT: PWM has one output
+ * @PWM_MODE_COMPLEMENTARY_BIT: PWM has 2 outputs with opposite polarities
+ * @PWM_MODE_CNT: PWM modes count
+ */
+enum {
+	PWM_MODE_NORMAL_BIT,
+	PWM_MODE_COMPLEMENTARY_BIT,
+	PWM_MODE_CNT,
+};
+
+#define PWM_MODE(name)		BIT(PWM_MODE_##name##_BIT)
+
+/**
+ * struct pwm_caps - PWM capabilities
+ * @modes: PWM modes
+ */
+struct pwm_caps {
+	unsigned long modes;
+};
+
+/**
  * struct pwm_args - board-dependent PWM arguments
  * @period: reference period
  * @polarity: reference polarity
+ * @mode: reference mode
  *
  * This structure describes board-dependent arguments attached to a PWM
  * device. These arguments are usually retrieved from the PWM lookup table or
@@ -41,6 +64,7 @@ enum pwm_polarity {
 struct pwm_args {
 	unsigned int period;
 	enum pwm_polarity polarity;
+	unsigned long mode;
 };
 
 enum {
@@ -53,12 +77,14 @@ enum {
  * @period: PWM period (in nanoseconds)
  * @duty_cycle: PWM duty cycle (in nanoseconds)
  * @polarity: PWM polarity
+ * @mode: PWM mode
  * @enabled: PWM enabled status
  */
 struct pwm_state {
 	unsigned int period;
 	unsigned int duty_cycle;
 	enum pwm_polarity polarity;
+	unsigned long mode;
 	bool enabled;
 };
 
@@ -181,6 +207,7 @@ static inline void pwm_init_state(const struct pwm_device *pwm,
 	state->period = args.period;
 	state->polarity = args.polarity;
 	state->duty_cycle = 0;
+	state->mode = args.mode;
 }
 
 /**
@@ -254,6 +281,7 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle,
  * @get_state: get the current PWM state. This function is only
  *	       called once per PWM device when the PWM chip is
  *	       registered.
+ * @get_caps: get PWM capabilities.
  * @dbg_show: optional routine to show contents in debugfs
  * @owner: helps prevent removal of modules exporting active PWMs
  */
@@ -272,6 +300,8 @@ struct pwm_ops {
 		     struct pwm_state *state);
 	void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
 			  struct pwm_state *state);
+	void (*get_caps)(struct pwm_chip *chip, struct pwm_device *pwm,
+			 struct pwm_caps *caps);
 #ifdef CONFIG_DEBUG_FS
 	void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s);
 #endif
@@ -287,6 +317,7 @@ struct pwm_ops {
  * @npwm: number of PWMs controlled by this chip
  * @pwms: array of PWM devices allocated by the framework
  * @of_xlate: request a PWM device given a device tree PWM specifier
+ * @get_default_caps: get default PWM capabilities
  * @of_pwm_n_cells: number of cells expected in the device tree PWM specifier
  */
 struct pwm_chip {
@@ -300,6 +331,7 @@ struct pwm_chip {
 
 	struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
 					const struct of_phandle_args *args);
+	void (*get_default_caps)(struct pwm_caps *caps);
 	unsigned int of_pwm_n_cells;
 };
 
@@ -424,6 +456,37 @@ static inline void pwm_disable(struct pwm_device *pwm)
 	pwm_apply_state(pwm, &state);
 }
 
+static inline bool pwm_mode_valid(unsigned long mode)
+{
+	return (mode &&
+		hweight_long(mode) == 1 &&
+		ffs(mode) - 1 < PWM_MODE_CNT);
+}
+
+static inline bool pwm_caps_valid(struct pwm_caps caps)
+{
+	unsigned int last;
+
+	if (!caps.modes)
+		return false;
+
+	last = fls(caps.modes) - 1;
+	if (last >= PWM_MODE_CNT)
+		return false;
+
+	return true;
+}
+
+static inline const char * const pwm_mode_desc(unsigned long mode)
+{
+	static const char * const modes[] = { "normal", "complementary"	};
+
+	if (!pwm_mode_valid(mode))
+		return "invalid";
+
+	return modes[ffs(mode) - 1];
+}
+
 /* PWM provider APIs */
 int pwm_capture(struct pwm_device *pwm, struct pwm_capture *result,
 		unsigned long timeout);
@@ -438,6 +501,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip,
 					 unsigned int index,
 					 const char *label);
 
+void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+		  struct pwm_caps *caps);
+
 struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc,
 		const struct of_phandle_args *args);
 
@@ -498,6 +564,26 @@ static inline void pwm_disable(struct pwm_device *pwm)
 {
 }
 
+static inline bool pwm_mode_valid(unsigned long mode)
+{
+	return false;
+}
+
+static inline bool pwm_caps_valid(struct pwm_caps caps)
+{
+	return false;
+}
+
+static inline const char * const pwm_mode_desc(unsigned long mode)
+{
+	return NULL;
+}
+
+static inline void pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+				struct pwm_caps *caps)
+{
+}
+
 static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data)
 {
 	return -EINVAL;
@@ -592,6 +678,7 @@ static inline void pwm_apply_args(struct pwm_device *pwm)
 	state.enabled = false;
 	state.polarity = pwm->args.polarity;
 	state.period = pwm->args.period;
+	state.mode = pwm->args.mode;
 
 	pwm_apply_state(pwm, &state);
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 02/10] pwm: clps711x: populate PWM mode in of_xlate function
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/pwm-clps711x.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c
index 26ec24e457b1..2a4d31ab3af0 100644
--- a/drivers/pwm/pwm-clps711x.c
+++ b/drivers/pwm/pwm-clps711x.c
@@ -109,10 +109,20 @@ static const struct pwm_ops clps711x_pwm_ops = {
 static struct pwm_device *clps711x_pwm_xlate(struct pwm_chip *chip,
 					     const struct of_phandle_args *args)
 {
+	struct pwm_device *pwm;
+	struct pwm_caps caps;
+
 	if (args->args[0] >= chip->npwm)
 		return ERR_PTR(-EINVAL);
 
-	return pwm_request_from_chip(chip, args->args[0], NULL);
+	pwm = pwm_request_from_chip(chip, args->args[0], NULL);
+	if (IS_ERR(pwm))
+		return pwm;
+
+	pwm_get_caps(chip, pwm, &caps);
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
+
+	return pwm;
 }
 
 static int clps711x_pwm_probe(struct platform_device *pdev)
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 03/10] pwm: cros-ec: populate PWM mode in of_xlate function
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/pwm-cros-ec.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/pwm/pwm-cros-ec.c b/drivers/pwm/pwm-cros-ec.c
index 9c13694eaa24..e54954c13323 100644
--- a/drivers/pwm/pwm-cros-ec.c
+++ b/drivers/pwm/pwm-cros-ec.c
@@ -137,6 +137,7 @@ static struct pwm_device *
 cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 {
 	struct pwm_device *pwm;
+	struct pwm_caps caps;
 
 	if (args->args[0] >= pc->npwm)
 		return ERR_PTR(-EINVAL);
@@ -145,8 +146,11 @@ cros_ec_pwm_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 	if (IS_ERR(pwm))
 		return pwm;
 
+	pwm_get_caps(pc, pwm, &caps);
+
 	/* The EC won't let us change the period */
 	pwm->args.period = EC_PWM_MAX_DUTY;
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
 	return pwm;
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 04/10] pwm: pxa: populate PWM mode in of_xlate function
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Populate PWM mode in of_xlate function to avoid pwm_apply_state() failure.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/pwm-pxa.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c
index 4143a46684d2..7a035716e054 100644
--- a/drivers/pwm/pwm-pxa.c
+++ b/drivers/pwm/pwm-pxa.c
@@ -155,12 +155,16 @@ static struct pwm_device *
 pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
 {
 	struct pwm_device *pwm;
+	struct pwm_caps caps;
 
 	pwm = pwm_request_from_chip(pc, 0, NULL);
 	if (IS_ERR(pwm))
 		return pwm;
 
+	pwm_get_caps(pc, pwm, &caps);
+
 	pwm->args.period = args->args[0];
+	pwm->args.mode = BIT(ffs(caps.modes) - 1);
 
 	return pwm;
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 05/10] pwm: add PWM mode to pwm_config()
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add PWM mode to pwm_config() function. The drivers which uses pwm_config()
were adapted to this change.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 arch/arm/mach-s3c24xx/mach-rx1950.c  | 11 +++++++++--
 drivers/bus/ts-nbus.c                |  2 +-
 drivers/clk/clk-pwm.c                |  3 ++-
 drivers/gpu/drm/i915/intel_panel.c   | 17 ++++++++++++++---
 drivers/hwmon/pwm-fan.c              |  2 +-
 drivers/input/misc/max77693-haptic.c |  2 +-
 drivers/input/misc/max8997_haptic.c  |  6 +++++-
 drivers/leds/leds-pwm.c              |  5 ++++-
 drivers/media/rc/ir-rx51.c           |  5 ++++-
 drivers/media/rc/pwm-ir-tx.c         |  5 ++++-
 drivers/video/backlight/lm3630a_bl.c |  4 +++-
 drivers/video/backlight/lp855x_bl.c  |  4 +++-
 drivers/video/backlight/lp8788_bl.c  |  5 ++++-
 drivers/video/backlight/pwm_bl.c     | 11 +++++++++--
 drivers/video/fbdev/ssd1307fb.c      |  3 ++-
 include/linux/pwm.h                  |  6 ++++--
 16 files changed, 70 insertions(+), 21 deletions(-)

diff --git a/arch/arm/mach-s3c24xx/mach-rx1950.c b/arch/arm/mach-s3c24xx/mach-rx1950.c
index e86ad6a68a0b..6feae73dcc73 100644
--- a/arch/arm/mach-s3c24xx/mach-rx1950.c
+++ b/arch/arm/mach-s3c24xx/mach-rx1950.c
@@ -386,8 +386,13 @@ static void rx1950_lcd_power(int enable)
 {
 	int i;
 	static int enabled;
+	struct pwm_caps caps = { };
+
 	if (enabled == enable)
 		return;
+
+	pwm_get_caps(lcd_pwm->chip, lcd_pwm, &caps);
+
 	if (!enable) {
 
 		/* GPC11-GPC15->OUTPUT */
@@ -433,14 +438,16 @@ static void rx1950_lcd_power(int enable)
 
 		/* GPB1->OUTPUT, GPB1->0 */
 		gpio_direction_output(S3C2410_GPB(1), 0);
-		pwm_config(lcd_pwm, 0, LCD_PWM_PERIOD);
+		pwm_config(lcd_pwm, 0, LCD_PWM_PERIOD,
+			   BIT(ffs(caps.modes) - 1));
 		pwm_disable(lcd_pwm);
 
 		/* GPC0->0, GPC10->0 */
 		gpio_direction_output(S3C2410_GPC(0), 0);
 		gpio_direction_output(S3C2410_GPC(10), 0);
 	} else {
-		pwm_config(lcd_pwm, LCD_PWM_DUTY, LCD_PWM_PERIOD);
+		pwm_config(lcd_pwm, LCD_PWM_DUTY, LCD_PWM_PERIOD,
+			   BIT(ffs(caps.modes) - 1));
 		pwm_enable(lcd_pwm);
 
 		gpio_direction_output(S3C2410_GPC(0), 1);
diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c
index 073fd9011154..dcd2ca3bcd99 100644
--- a/drivers/bus/ts-nbus.c
+++ b/drivers/bus/ts-nbus.c
@@ -316,7 +316,7 @@ static int ts_nbus_probe(struct platform_device *pdev)
 	 * the atomic PWM API.
 	 */
 	pwm_apply_args(pwm);
-	ret = pwm_config(pwm, pargs.period, pargs.period);
+	ret = pwm_config(pwm, pargs.period, pargs.period, pargs.mode);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/clk/clk-pwm.c b/drivers/clk/clk-pwm.c
index 8cb9d117fdbf..605a6bffe893 100644
--- a/drivers/clk/clk-pwm.c
+++ b/drivers/clk/clk-pwm.c
@@ -92,7 +92,8 @@ static int clk_pwm_probe(struct platform_device *pdev)
 	 * atomic PWM API.
 	 */
 	pwm_apply_args(pwm);
-	ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period);
+	ret = pwm_config(pwm, (pargs.period + 1) >> 1, pargs.period,
+			 pargs.mode);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index adc51e452e3e..960556261787 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -633,8 +633,12 @@ static void pwm_set_backlight(const struct drm_connector_state *conn_state, u32
 {
 	struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;
 	int duty_ns = DIV_ROUND_UP(level * CRC_PMIC_PWM_PERIOD_NS, 100);
+	struct pwm_caps caps = { };
 
-	pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS);
+	pwm_get_caps(panel->backlight.pwm->chip, panel->backlight.pwm, &caps);
+
+	pwm_config(panel->backlight.pwm, duty_ns, CRC_PMIC_PWM_PERIOD_NS,
+		   BIT(ffs(caps.modes) - 1));
 }
 
 static void
@@ -821,9 +825,13 @@ static void pwm_disable_backlight(const struct drm_connector_state *old_conn_sta
 {
 	struct intel_connector *connector = to_intel_connector(old_conn_state->connector);
 	struct intel_panel *panel = &connector->panel;
+	struct pwm_caps caps = { };
+
+	pwm_get_caps(panel->backlight.pwm->chip, panel->backlight.pwm, &caps);
 
 	/* Disable the backlight */
-	pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS);
+	pwm_config(panel->backlight.pwm, 0, CRC_PMIC_PWM_PERIOD_NS,
+		   BIT(ffs(caps.modes) - 1));
 	usleep_range(2000, 3000);
 	pwm_disable(panel->backlight.pwm);
 }
@@ -1754,6 +1762,7 @@ static int pwm_setup_backlight(struct intel_connector *connector,
 {
 	struct drm_device *dev = connector->base.dev;
 	struct intel_panel *panel = &connector->panel;
+	struct pwm_caps caps = { };
 	int retval;
 
 	/* Get the PWM chip for backlight control */
@@ -1770,8 +1779,10 @@ static int pwm_setup_backlight(struct intel_connector *connector,
 	 */
 	pwm_apply_args(panel->backlight.pwm);
 
+	pwm_get_caps(panel->backlight.pwm->chip, panel->backlight.pwm, &caps);
+
 	retval = pwm_config(panel->backlight.pwm, CRC_PMIC_PWM_PERIOD_NS,
-			    CRC_PMIC_PWM_PERIOD_NS);
+			    CRC_PMIC_PWM_PERIOD_NS, BIT(ffs(caps.modes) - 1));
 	if (retval < 0) {
 		DRM_ERROR("Failed to configure the pwm chip\n");
 		pwm_put(panel->backlight.pwm);
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 70cc0d134f3c..bd05cd81d3d5 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -308,7 +308,7 @@ static int pwm_fan_resume(struct device *dev)
 
 	pwm_get_args(ctx->pwm, &pargs);
 	duty = DIV_ROUND_UP(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
-	ret = pwm_config(ctx->pwm, duty, pargs.period);
+	ret = pwm_config(ctx->pwm, duty, pargs.period, pargs.mode);
 	if (ret)
 		return ret;
 	return pwm_enable(ctx->pwm);
diff --git a/drivers/input/misc/max77693-haptic.c b/drivers/input/misc/max77693-haptic.c
index 46b0f48fbf49..5fe2ff2b408b 100644
--- a/drivers/input/misc/max77693-haptic.c
+++ b/drivers/input/misc/max77693-haptic.c
@@ -76,7 +76,7 @@ static int max77693_haptic_set_duty_cycle(struct max77693_haptic *haptic)
 
 	pwm_get_args(haptic->pwm_dev, &pargs);
 	delta = (pargs.period + haptic->pwm_duty) / 2;
-	error = pwm_config(haptic->pwm_dev, delta, pargs.period);
+	error = pwm_config(haptic->pwm_dev, delta, pargs.period, pargs.mode);
 	if (error) {
 		dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
 		return error;
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
index 99bc762881d5..16de524dc489 100644
--- a/drivers/input/misc/max8997_haptic.c
+++ b/drivers/input/misc/max8997_haptic.c
@@ -73,7 +73,11 @@ static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
 
 	if (chip->mode == MAX8997_EXTERNAL_MODE) {
 		unsigned int duty = chip->pwm_period * chip->level / 100;
-		ret = pwm_config(chip->pwm, duty, chip->pwm_period);
+		struct pwm_caps caps = { };
+
+		pwm_get_caps(chip->pwm->chip, chip->pwm, &caps);
+		ret = pwm_config(chip->pwm, duty, chip->pwm_period,
+				 BIT(ffs(caps.modes) - 1));
 	} else {
 		int i;
 		u8 duty_index = 0;
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index 8d456dc6c5bf..8ba942e96b63 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -39,8 +39,11 @@ struct led_pwm_priv {
 static void __led_pwm_set(struct led_pwm_data *led_dat)
 {
 	int new_duty = led_dat->duty;
+	struct pwm_caps caps = { };
 
-	pwm_config(led_dat->pwm, new_duty, led_dat->period);
+	pwm_get_caps(led_dat->pwm->chip, led_dat->pwm, &caps);
+	pwm_config(led_dat->pwm, new_duty, led_dat->period,
+		   BIT(ffs(caps.modes) - 1));
 
 	if (new_duty == 0)
 		pwm_disable(led_dat->pwm);
diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
index 49265f02e772..a971b02ea021 100644
--- a/drivers/media/rc/ir-rx51.c
+++ b/drivers/media/rc/ir-rx51.c
@@ -55,10 +55,13 @@ static int init_timing_params(struct ir_rx51 *ir_rx51)
 {
 	struct pwm_device *pwm = ir_rx51->pwm;
 	int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq);
+	struct pwm_caps caps = { };
 
 	duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100);
 
-	pwm_config(pwm, duty, period);
+	pwm_get_caps(pwm->chip, pwm, &caps);
+
+	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
 
 	return 0;
 }
diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c
index 27d0f5837a76..c630e1b450a3 100644
--- a/drivers/media/rc/pwm-ir-tx.c
+++ b/drivers/media/rc/pwm-ir-tx.c
@@ -61,6 +61,7 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 {
 	struct pwm_ir *pwm_ir = dev->priv;
 	struct pwm_device *pwm = pwm_ir->pwm;
+	struct pwm_caps caps = { };
 	int i, duty, period;
 	ktime_t edge;
 	long delta;
@@ -68,7 +69,9 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
 	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
 
-	pwm_config(pwm, duty, period);
+	pwm_get_caps(pwm->chip, pwm, &caps);
+
+	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
 
 	edge = ktime_get();
 
diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c
index 2030a6b77a09..696fa25dafd2 100644
--- a/drivers/video/backlight/lm3630a_bl.c
+++ b/drivers/video/backlight/lm3630a_bl.c
@@ -165,8 +165,10 @@ static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
 {
 	unsigned int period = pchip->pdata->pwm_period;
 	unsigned int duty = br * period / br_max;
+	struct pwm_caps caps = { };
 
-	pwm_config(pchip->pwmd, duty, period);
+	pwm_get_caps(pchip->pwmd->chip, pchip->pwmd, &caps);
+	pwm_config(pchip->pwmd, duty, period, BIT(ffs(caps.modes) - 1));
 	if (duty)
 		pwm_enable(pchip->pwmd);
 	else
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
index 939f057836e1..3d274c604862 100644
--- a/drivers/video/backlight/lp855x_bl.c
+++ b/drivers/video/backlight/lp855x_bl.c
@@ -240,6 +240,7 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
 	unsigned int period = lp->pdata->period_ns;
 	unsigned int duty = br * period / max_br;
 	struct pwm_device *pwm;
+	struct pwm_caps caps = { };
 
 	/* request pwm device with the consumer name */
 	if (!lp->pwm) {
@@ -256,7 +257,8 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
 		pwm_apply_args(pwm);
 	}
 
-	pwm_config(lp->pwm, duty, period);
+	pwm_get_caps(lp->pwm->chip, lp->pwm, &caps);
+	pwm_config(lp->pwm, duty, period, BIT(ffs(caps.modes) - 1));
 	if (duty)
 		pwm_enable(lp->pwm);
 	else
diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c
index cf869ec90cce..06de3163650d 100644
--- a/drivers/video/backlight/lp8788_bl.c
+++ b/drivers/video/backlight/lp8788_bl.c
@@ -128,6 +128,7 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
 	unsigned int duty;
 	struct device *dev;
 	struct pwm_device *pwm;
+	struct pwm_caps caps = { };
 
 	if (!bl->pdata)
 		return;
@@ -153,7 +154,9 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
 		pwm_apply_args(pwm);
 	}
 
-	pwm_config(bl->pwm, duty, period);
+	pwm_get_caps(bl->pwm->chip, bl->pwm, &caps);
+
+	pwm_config(bl->pwm, duty, period, BIT(ffs(caps.modes) - 1));
 	if (duty)
 		pwm_enable(bl->pwm);
 	else
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 1c2289ddd555..706a9ab053a7 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -63,10 +63,14 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
 
 static void pwm_backlight_power_off(struct pwm_bl_data *pb)
 {
+	struct pwm_caps caps = { };
+
 	if (!pb->enabled)
 		return;
 
-	pwm_config(pb->pwm, 0, pb->period);
+	pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
+
+	pwm_config(pb->pwm, 0, pb->period, BIT(ffs(caps.modes) - 1));
 	pwm_disable(pb->pwm);
 
 	if (pb->enable_gpio)
@@ -96,6 +100,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 {
 	struct pwm_bl_data *pb = bl_get_data(bl);
 	int brightness = bl->props.brightness;
+	struct pwm_caps caps = { };
 	int duty_cycle;
 
 	if (bl->props.power != FB_BLANK_UNBLANK ||
@@ -108,7 +113,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 
 	if (brightness > 0) {
 		duty_cycle = compute_duty_cycle(pb, brightness);
-		pwm_config(pb->pwm, duty_cycle, pb->period);
+		pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
+		pwm_config(pb->pwm, duty_cycle, pb->period,
+			   BIT(ffs(caps.modes) - 1));
 		pwm_backlight_power_on(pb, brightness);
 	} else
 		pwm_backlight_power_off(pb);
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index f599520374dd..4b57dcb5799a 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -308,7 +308,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
 
 		par->pwm_period = pargs.period;
 		/* Enable the PWM */
-		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period,
+			   pargs.mode);
 		pwm_enable(par->pwm);
 
 		dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index e62349f48129..0ba416ab2772 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -357,11 +357,12 @@ int pwm_adjust_config(struct pwm_device *pwm);
  * @pwm: PWM device
  * @duty_ns: "on" time (in nanoseconds)
  * @period_ns: duration (in nanoseconds) of one cycle
+ * @mode: PWM mode
  *
  * Returns: 0 on success or a negative error code on failure.
  */
 static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
-			     int period_ns)
+			     int period_ns, unsigned long mode)
 {
 	struct pwm_state state;
 
@@ -377,6 +378,7 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
 
 	state.duty_cycle = duty_ns;
 	state.period = period_ns;
+	state.mode = mode;
 	return pwm_apply_state(pwm, &state);
 }
 
@@ -537,7 +539,7 @@ static inline int pwm_adjust_config(struct pwm_device *pwm)
 }
 
 static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
-			     int period_ns)
+			     int period_ns, unsigned long mode)
 {
 	return -EINVAL;
 }
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 06/10] pwm: add PWM modes
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add PWM normal and complementary modes.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 Documentation/devicetree/bindings/pwm/pwm.txt |  9 +++++++--
 Documentation/pwm.txt                         | 26 +++++++++++++++++++++++---
 include/dt-bindings/pwm/pwm.h                 |  1 +
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
index 8556263b8502..c2b4b9ba0e3f 100644
--- a/Documentation/devicetree/bindings/pwm/pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -46,11 +46,16 @@ period in nanoseconds.
 Optionally, the pwm-specifier can encode a number of flags (defined in
 <dt-bindings/pwm/pwm.h>) in a third cell:
 - PWM_POLARITY_INVERTED: invert the PWM signal polarity
+- PWM_DTMODE_COMPLEMENTARY: PWM complementary working mode (for PWM
+channels two outputs); if not specified, the default for PWM channel will
+be used
 
-Example with optional PWM specifier for inverse polarity
+Example with optional PWM specifier for inverse polarity and complementary
+mode:
 
 	bl: backlight {
-		pwms = <&pwm 0 5000000 PWM_POLARITY_INVERTED>;
+		pwms = <&pwm 0 5000000
+			(PWM_DTMODE_COMPLEMENTARY | PWM_POLARITY_INVERTED)>;
 		pwm-names = "backlight";
 	};
 
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 8fbf0aa3ba2d..59592f8cc381 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -61,9 +61,9 @@ In addition to the PWM state, the PWM API also exposes PWM arguments, which
 are the reference PWM config one should use on this PWM.
 PWM arguments are usually platform-specific and allows the PWM user to only
 care about dutycycle relatively to the full period (like, duty = 50% of the
-period). struct pwm_args contains 2 fields (period and polarity) and should
-be used to set the initial PWM config (usually done in the probe function
-of the PWM user). PWM arguments are retrieved with pwm_get_args().
+period). struct pwm_args contains 3 fields (period, polarity and mode) and
+should be used to set the initial PWM config (usually done in the probe
+function of the PWM user). PWM arguments are retrieved with pwm_get_args().
 
 Using PWMs with the sysfs interface
 -----------------------------------
@@ -110,6 +110,26 @@ channel that was exported. The following properties will then be available:
 	- 0 - disabled
 	- 1 - enabled
 
+  mode
+    Get/set PWM channel working mode.
+
+    Normal mode - for PWM channels with one output; this should be the
+        default working mode for every PWM channel; output waveforms looks
+        like this:
+             __    __    __    __
+    PWM   __|  |__|  |__|  |__|  |__
+            <--T-->
+
+    Complementary mode - for PWM channels two outputs; output waveforms
+        looks line this:
+             __    __    __    __
+    PWMH1 __|  |__|  |__|  |__|  |__
+          __    __    __    __    __
+    PWML1   |__|  |__|  |__|  |__|
+            <--T-->
+
+    Where T is the signal period.
+
 Implementing a PWM driver
 -------------------------
 
diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h
index ab9a077e3c7d..b4d61269aced 100644
--- a/include/dt-bindings/pwm/pwm.h
+++ b/include/dt-bindings/pwm/pwm.h
@@ -11,5 +11,6 @@
 #define _DT_BINDINGS_PWM_PWM_H
 
 #define PWM_POLARITY_INVERTED			(1 << 0)
+#define PWM_DTMODE_COMPLEMENTARY		(1 << 1)
 
 #endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 07/10] pwm: atmel: add pwm capabilities
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add pwm capabilities for Atmel/Microchip PWM controllers.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/pwm-atmel.c | 80 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 52 insertions(+), 28 deletions(-)

diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index 530d7dc5f1b5..d2482fe28cfa 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -65,11 +65,16 @@ struct atmel_pwm_registers {
 	u8 duty_upd;
 };
 
+struct atmel_pwm_data {
+	struct atmel_pwm_registers regs;
+	struct pwm_caps caps;
+};
+
 struct atmel_pwm_chip {
 	struct pwm_chip chip;
 	struct clk *clk;
 	void __iomem *base;
-	const struct atmel_pwm_registers *regs;
+	const struct atmel_pwm_data *data;
 
 	unsigned int updated_pwms;
 	/* ISR is cleared when read, ensure only one thread does that */
@@ -150,15 +155,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm,
 	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
 	u32 val;
 
-	if (atmel_pwm->regs->duty_upd ==
-	    atmel_pwm->regs->period_upd) {
+	if (atmel_pwm->data->regs.duty_upd ==
+	    atmel_pwm->data->regs.period_upd) {
 		val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR);
 		val &= ~PWM_CMR_UPD_CDTY;
 		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
 	}
 
 	atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-			    atmel_pwm->regs->duty_upd, cdty);
+			    atmel_pwm->data->regs.duty_upd, cdty);
 }
 
 static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
@@ -168,9 +173,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip,
 	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
 
 	atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-			    atmel_pwm->regs->duty, cdty);
+			    atmel_pwm->data->regs.duty, cdty);
 	atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm,
-			    atmel_pwm->regs->period, cprd);
+			    atmel_pwm->data->regs.period, cprd);
 }
 
 static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
@@ -225,7 +230,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 		    cstate.polarity == state->polarity &&
 		    cstate.period == state->period) {
 			cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
-						  atmel_pwm->regs->period);
+						  atmel_pwm->data->regs.period);
 			atmel_pwm_calculate_cdty(state, cprd, &cdty);
 			atmel_pwm_update_cdty(chip, pwm, cdty);
 			return 0;
@@ -272,32 +277,51 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 	return 0;
 }
 
+static void atmel_pwm_get_caps(struct pwm_chip *chip, struct pwm_device *pwm,
+			       struct pwm_caps *caps)
+{
+	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
+
+	*caps = atmel_pwm->data->caps;
+}
+
 static const struct pwm_ops atmel_pwm_ops = {
 	.apply = atmel_pwm_apply,
+	.get_caps = atmel_pwm_get_caps,
 	.owner = THIS_MODULE,
 };
 
-static const struct atmel_pwm_registers atmel_pwm_regs_v1 = {
-	.period		= PWMV1_CPRD,
-	.period_upd	= PWMV1_CUPD,
-	.duty		= PWMV1_CDTY,
-	.duty_upd	= PWMV1_CUPD,
+static const struct atmel_pwm_data atmel_pwm_data_v1 = {
+	.regs = {
+		.period		= PWMV1_CPRD,
+		.period_upd	= PWMV1_CUPD,
+		.duty		= PWMV1_CDTY,
+		.duty_upd	= PWMV1_CUPD,
+	},
+	.caps = {
+		.modes = PWM_MODE(NORMAL),
+	},
 };
 
-static const struct atmel_pwm_registers atmel_pwm_regs_v2 = {
-	.period		= PWMV2_CPRD,
-	.period_upd	= PWMV2_CPRDUPD,
-	.duty		= PWMV2_CDTY,
-	.duty_upd	= PWMV2_CDTYUPD,
+static const struct atmel_pwm_data atmel_pwm_data_v2 = {
+	.regs = {
+		.period		= PWMV2_CPRD,
+		.period_upd	= PWMV2_CPRDUPD,
+		.duty		= PWMV2_CDTY,
+		.duty_upd	= PWMV2_CDTYUPD,
+	},
+	.caps = {
+		.modes = PWM_MODE(NORMAL) | PWM_MODE(COMPLEMENTARY),
+	},
 };
 
 static const struct platform_device_id atmel_pwm_devtypes[] = {
 	{
 		.name = "at91sam9rl-pwm",
-		.driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1,
+		.driver_data = (kernel_ulong_t)&atmel_pwm_data_v1,
 	}, {
 		.name = "sama5d3-pwm",
-		.driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2,
+		.driver_data = (kernel_ulong_t)&atmel_pwm_data_v2,
 	}, {
 		/* sentinel */
 	},
@@ -307,20 +331,20 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
 static const struct of_device_id atmel_pwm_dt_ids[] = {
 	{
 		.compatible = "atmel,at91sam9rl-pwm",
-		.data = &atmel_pwm_regs_v1,
+		.data = &atmel_pwm_data_v1,
 	}, {
 		.compatible = "atmel,sama5d3-pwm",
-		.data = &atmel_pwm_regs_v2,
+		.data = &atmel_pwm_data_v2,
 	}, {
 		.compatible = "atmel,sama5d2-pwm",
-		.data = &atmel_pwm_regs_v2,
+		.data = &atmel_pwm_data_v2,
 	}, {
 		/* sentinel */
 	},
 };
 MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
 
-static inline const struct atmel_pwm_registers *
+static inline const struct atmel_pwm_data *
 atmel_pwm_get_driver_data(struct platform_device *pdev)
 {
 	const struct platform_device_id *id;
@@ -330,18 +354,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev)
 
 	id = platform_get_device_id(pdev);
 
-	return (struct atmel_pwm_registers *)id->driver_data;
+	return (struct atmel_pwm_data *)id->driver_data;
 }
 
 static int atmel_pwm_probe(struct platform_device *pdev)
 {
-	const struct atmel_pwm_registers *regs;
+	const struct atmel_pwm_data *data;
 	struct atmel_pwm_chip *atmel_pwm;
 	struct resource *res;
 	int ret;
 
-	regs = atmel_pwm_get_driver_data(pdev);
-	if (!regs)
+	data = atmel_pwm_get_driver_data(pdev);
+	if (!data)
 		return -ENODEV;
 
 	atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
@@ -373,7 +397,7 @@ static int atmel_pwm_probe(struct platform_device *pdev)
 
 	atmel_pwm->chip.base = -1;
 	atmel_pwm->chip.npwm = 4;
-	atmel_pwm->regs = regs;
+	atmel_pwm->data = data;
 	atmel_pwm->updated_pwms = 0;
 	mutex_init(&atmel_pwm->isr_lock);
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 08/10] pwm: add push-pull mode support
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add push-pull mode support. In push-pull mode the channels' outputs have
same polarities and the edges are complementary delayed for one period.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 include/linux/pwm.h | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 0ba416ab2772..765d760ef82d 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -29,11 +29,14 @@ enum pwm_polarity {
  * PWM modes
  * @PWM_MODE_NORMAL_BIT: PWM has one output
  * @PWM_MODE_COMPLEMENTARY_BIT: PWM has 2 outputs with opposite polarities
+ * @PWM_MODE_PUSH_PULL_BIT: PWM has 2 outputs with same polarities and the edges
+ * are complementary delayed for one period
  * @PWM_MODE_CNT: PWM modes count
  */
 enum {
 	PWM_MODE_NORMAL_BIT,
 	PWM_MODE_COMPLEMENTARY_BIT,
+	PWM_MODE_PUSH_PULL_BIT,
 	PWM_MODE_CNT,
 };
 
@@ -481,7 +484,11 @@ static inline bool pwm_caps_valid(struct pwm_caps caps)
 
 static inline const char * const pwm_mode_desc(unsigned long mode)
 {
-	static const char * const modes[] = { "normal", "complementary"	};
+	static const char * const modes[] = {
+		"normal",
+		"complementary",
+		"push-pull",
+	};
 
 	if (!pwm_mode_valid(mode))
 		return "invalid";
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 09/10] pwm: add documentation for pwm push-pull mode
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add documentation for PWM push-pull mode.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/pwm/pwm.txt |  2 ++
 Documentation/pwm.txt                         | 16 ++++++++++++++++
 include/dt-bindings/pwm/pwm.h                 |  1 +
 3 files changed, 19 insertions(+)

diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt
index c2b4b9ba0e3f..968dd786ad7c 100644
--- a/Documentation/devicetree/bindings/pwm/pwm.txt
+++ b/Documentation/devicetree/bindings/pwm/pwm.txt
@@ -49,6 +49,8 @@ Optionally, the pwm-specifier can encode a number of flags (defined in
 - PWM_DTMODE_COMPLEMENTARY: PWM complementary working mode (for PWM
 channels two outputs); if not specified, the default for PWM channel will
 be used
+- PWM_DTMODE_PUSH_PULL: PWM push-pull working modes (for PWM channels with
+two outputs); if not specified the default for PWM channel will be used
 
 Example with optional PWM specifier for inverse polarity and complementary
 mode:
diff --git a/Documentation/pwm.txt b/Documentation/pwm.txt
index 59592f8cc381..174c371583b6 100644
--- a/Documentation/pwm.txt
+++ b/Documentation/pwm.txt
@@ -128,6 +128,22 @@ channel that was exported. The following properties will then be available:
     PWML1   |__|  |__|  |__|  |__|
             <--T-->
 
+    Push-pull mode - for PWM channels with two outputs; output waveforms
+        for a PWM channel with 2 outputs, in push-pull mode, with normal
+        polarity looks like this:
+            __          __
+    PWMH __|  |________|  |________
+                  __          __
+    PWML ________|  |________|  |__
+           <--T-->
+
+    If polarity is inversed:
+         __    ________    ________
+    PWMH   |__|        |__|
+         ________    ________    __
+    PWML         |__|        |__|
+           <--T-->
+
     Where T is the signal period.
 
 Implementing a PWM driver
diff --git a/include/dt-bindings/pwm/pwm.h b/include/dt-bindings/pwm/pwm.h
index b4d61269aced..674dfdd59595 100644
--- a/include/dt-bindings/pwm/pwm.h
+++ b/include/dt-bindings/pwm/pwm.h
@@ -12,5 +12,6 @@
 
 #define PWM_POLARITY_INVERTED			(1 << 0)
 #define PWM_DTMODE_COMPLEMENTARY		(1 << 1)
+#define PWM_DTMODE_PUSH_PULL			(1 << 2)
 
 #endif
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 10/10] pwm: atmel: add push-pull mode support
From: Claudiu Beznea @ 2018-02-22 12:01 UTC (permalink / raw)
  To: thierry.reding, shc_work, kgene, krzk, linux, mturquette, sboyd,
	jani.nikula, joonas.lahtinen, rodrigo.vivi, airlied, kamil,
	b.zolnierkie, jdelvare, linux, dmitry.torokhov, rpurdie,
	jacek.anaszewski, pavel, mchehab, sean, lee.jones,
	daniel.thompson, jingoohan1, milo.kim, robh+dt, mark.rutland,
	corbet, nicolas.ferre, alexandre.belloni
  Cc: linux-hwmon, linux-pwm, linux-fbdev, linux-samsung-soc,
	devicetree, intel-gfx, linux-doc, linux-kernel, dri-devel,
	Claudiu Beznea, linux-leds, linux-input, linux-clk,
	linux-arm-kernel, linux-media
In-Reply-To: <1519300881-8136-1-git-send-email-claudiu.beznea@microchip.com>

Add support for PWM push-pull mode. This is only supported by SAMA5D2 SoCs.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/pwm/pwm-atmel.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c
index d2482fe28cfa..da4b58c1ecf2 100644
--- a/drivers/pwm/pwm-atmel.c
+++ b/drivers/pwm/pwm-atmel.c
@@ -33,8 +33,11 @@
 
 #define PWM_CMR			0x0
 /* Bit field in CMR */
-#define PWM_CMR_CPOL		(1 << 9)
-#define PWM_CMR_UPD_CDTY	(1 << 10)
+#define PWM_CMR_CPOL		BIT(9)
+#define PWM_CMR_UPD_CDTY	BIT(10)
+#define PWM_CMR_DTHI		BIT(17)
+#define PWM_CMR_DTLI		BIT(18)
+#define PWM_CMR_PPM		BIT(19)
 #define PWM_CMR_CPRE_MSK	0xF
 
 /* The following registers for PWM v1 */
@@ -219,16 +222,19 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 {
 	struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
 	struct pwm_state cstate;
+	struct pwm_caps caps;
 	unsigned long cprd, cdty;
 	u32 pres, val;
 	int ret;
 
 	pwm_get_state(pwm, &cstate);
+	pwm_get_caps(chip, pwm, &caps);
 
 	if (state->enabled) {
 		if (cstate.enabled &&
 		    cstate.polarity == state->polarity &&
-		    cstate.period == state->period) {
+		    cstate.period == state->period &&
+		    cstate.mode == state->mode) {
 			cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm,
 						  atmel_pwm->data->regs.period);
 			atmel_pwm_calculate_cdty(state, cprd, &cdty);
@@ -263,6 +269,18 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			val &= ~PWM_CMR_CPOL;
 		else
 			val |= PWM_CMR_CPOL;
+
+		/* PWM mode. */
+		if (caps.modes & PWM_MODE(PUSH_PULL)) {
+			if (state->mode == PWM_MODE(PUSH_PULL)) {
+				val |= PWM_CMR_PPM | PWM_CMR_DTLI;
+				val &= ~PWM_CMR_DTHI;
+			} else {
+				val &= ~(PWM_CMR_PPM | PWM_CMR_DTLI |
+					 PWM_CMR_DTHI);
+			}
+		}
+
 		atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val);
 		atmel_pwm_set_cprd_cdty(chip, pwm, cprd, cdty);
 		mutex_lock(&atmel_pwm->isr_lock);
@@ -315,6 +333,20 @@ static const struct atmel_pwm_data atmel_pwm_data_v2 = {
 	},
 };
 
+static const struct atmel_pwm_data atmel_pwm_data_v3 = {
+	.regs = {
+		.period		= PWMV2_CPRD,
+		.period_upd	= PWMV2_CPRDUPD,
+		.duty		= PWMV2_CDTY,
+		.duty_upd	= PWMV2_CDTYUPD,
+	},
+	.caps = {
+		.modes = PWM_MODE(NORMAL) |
+			 PWM_MODE(COMPLEMENTARY) |
+			 PWM_MODE(PUSH_PULL),
+	},
+};
+
 static const struct platform_device_id atmel_pwm_devtypes[] = {
 	{
 		.name = "at91sam9rl-pwm",
@@ -337,7 +369,7 @@ static const struct of_device_id atmel_pwm_dt_ids[] = {
 		.data = &atmel_pwm_data_v2,
 	}, {
 		.compatible = "atmel,sama5d2-pwm",
-		.data = &atmel_pwm_data_v2,
+		.data = &atmel_pwm_data_v3,
 	}, {
 		/* sentinel */
 	},
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH v3 05/10] pwm: add PWM mode to pwm_config()
From: Daniel Thompson @ 2018-02-22 12:33 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: mark.rutland, milo.kim, linux-fbdev, sean, devicetree, airlied,
	mturquette, kamil, dri-devel, linux-kernel, alexandre.belloni,
	pavel, lee.jones, linux-clk, linux-leds, linux-samsung-soc,
	shc_work, corbet, linux-doc, linux, krzk, kgene, linux-input,
	linux, linux-media, linux-pwm, jdelvare, b.zolnierkie, intel-gfx,
	robh+dt, jacek.anaszewski, rodrigo.vivi, mchehab,
	linux-arm-kernel, linux-hwmon, jingoohan1, dm
In-Reply-To: <1519300881-8136-6-git-send-email-claudiu.beznea@microchip.com>

On Thu, Feb 22, 2018 at 02:01:16PM +0200, Claudiu Beznea wrote:
> Add PWM mode to pwm_config() function. The drivers which uses pwm_config()
> were adapted to this change.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  arch/arm/mach-s3c24xx/mach-rx1950.c  | 11 +++++++++--
>  drivers/bus/ts-nbus.c                |  2 +-
>  drivers/clk/clk-pwm.c                |  3 ++-
>  drivers/gpu/drm/i915/intel_panel.c   | 17 ++++++++++++++---
>  drivers/hwmon/pwm-fan.c              |  2 +-
>  drivers/input/misc/max77693-haptic.c |  2 +-
>  drivers/input/misc/max8997_haptic.c  |  6 +++++-
>  drivers/leds/leds-pwm.c              |  5 ++++-
>  drivers/media/rc/ir-rx51.c           |  5 ++++-
>  drivers/media/rc/pwm-ir-tx.c         |  5 ++++-
>  drivers/video/backlight/lm3630a_bl.c |  4 +++-
>  drivers/video/backlight/lp855x_bl.c  |  4 +++-
>  drivers/video/backlight/lp8788_bl.c  |  5 ++++-
>  drivers/video/backlight/pwm_bl.c     | 11 +++++++++--
>  drivers/video/fbdev/ssd1307fb.c      |  3 ++-
>  include/linux/pwm.h                  |  6 ++++--
>  16 files changed, 70 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c
> index 2030a6b77a09..696fa25dafd2 100644
> --- a/drivers/video/backlight/lm3630a_bl.c
> +++ b/drivers/video/backlight/lm3630a_bl.c
> @@ -165,8 +165,10 @@ static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
>  {
>  	unsigned int period = pchip->pdata->pwm_period;
>  	unsigned int duty = br * period / br_max;
> +	struct pwm_caps caps = { };
>  
> -	pwm_config(pchip->pwmd, duty, period);
> +	pwm_get_caps(pchip->pwmd->chip, pchip->pwmd, &caps);
> +	pwm_config(pchip->pwmd, duty, period, BIT(ffs(caps.modes) - 1));

Well... I admit I've only really looked at the patches that impact 
backlight but dispersing this really odd looking bit twiddling 
throughout the kernel doesn't strike me a great API design.

IMHO callers should not be required to find the first set bit in
some specially crafted set of capability bits simply to get sane 
default behaviour.


Daniel.




>  	if (duty)
>  		pwm_enable(pchip->pwmd);
>  	else
> diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
> index 939f057836e1..3d274c604862 100644
> --- a/drivers/video/backlight/lp855x_bl.c
> +++ b/drivers/video/backlight/lp855x_bl.c
> @@ -240,6 +240,7 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
>  	unsigned int period = lp->pdata->period_ns;
>  	unsigned int duty = br * period / max_br;
>  	struct pwm_device *pwm;
> +	struct pwm_caps caps = { };
>  
>  	/* request pwm device with the consumer name */
>  	if (!lp->pwm) {
> @@ -256,7 +257,8 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
>  		pwm_apply_args(pwm);
>  	}
>  
> -	pwm_config(lp->pwm, duty, period);
> +	pwm_get_caps(lp->pwm->chip, lp->pwm, &caps);
> +	pwm_config(lp->pwm, duty, period, BIT(ffs(caps.modes) - 1));
>  	if (duty)
>  		pwm_enable(lp->pwm);
>  	else
> diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c
> index cf869ec90cce..06de3163650d 100644
> --- a/drivers/video/backlight/lp8788_bl.c
> +++ b/drivers/video/backlight/lp8788_bl.c
> @@ -128,6 +128,7 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
>  	unsigned int duty;
>  	struct device *dev;
>  	struct pwm_device *pwm;
> +	struct pwm_caps caps = { };
>  
>  	if (!bl->pdata)
>  		return;
> @@ -153,7 +154,9 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
>  		pwm_apply_args(pwm);
>  	}
>  
> -	pwm_config(bl->pwm, duty, period);
> +	pwm_get_caps(bl->pwm->chip, bl->pwm, &caps);
> +
> +	pwm_config(bl->pwm, duty, period, BIT(ffs(caps.modes) - 1));
>  	if (duty)
>  		pwm_enable(bl->pwm);
>  	else
> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
> index 1c2289ddd555..706a9ab053a7 100644
> --- a/drivers/video/backlight/pwm_bl.c
> +++ b/drivers/video/backlight/pwm_bl.c
> @@ -63,10 +63,14 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
>  
>  static void pwm_backlight_power_off(struct pwm_bl_data *pb)
>  {
> +	struct pwm_caps caps = { };
> +
>  	if (!pb->enabled)
>  		return;
>  
> -	pwm_config(pb->pwm, 0, pb->period);
> +	pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
> +
> +	pwm_config(pb->pwm, 0, pb->period, BIT(ffs(caps.modes) - 1));
>  	pwm_disable(pb->pwm);
>  
>  	if (pb->enable_gpio)
> @@ -96,6 +100,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>  {
>  	struct pwm_bl_data *pb = bl_get_data(bl);
>  	int brightness = bl->props.brightness;
> +	struct pwm_caps caps = { };
>  	int duty_cycle;
>  
>  	if (bl->props.power != FB_BLANK_UNBLANK ||
> @@ -108,7 +113,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>  
>  	if (brightness > 0) {
>  		duty_cycle = compute_duty_cycle(pb, brightness);
> -		pwm_config(pb->pwm, duty_cycle, pb->period);
> +		pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
> +		pwm_config(pb->pwm, duty_cycle, pb->period,
> +			   BIT(ffs(caps.modes) - 1));
>  		pwm_backlight_power_on(pb, brightness);
>  	} else
>  		pwm_backlight_power_off(pb);
> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
> index f599520374dd..4b57dcb5799a 100644
> --- a/drivers/video/fbdev/ssd1307fb.c
> +++ b/drivers/video/fbdev/ssd1307fb.c
> @@ -308,7 +308,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
>  
>  		par->pwm_period = pargs.period;
>  		/* Enable the PWM */
> -		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
> +		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period,
> +			   pargs.mode);
>  		pwm_enable(par->pwm);
>  
>  		dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
> index e62349f48129..0ba416ab2772 100644
> --- a/include/linux/pwm.h
> +++ b/include/linux/pwm.h
> @@ -357,11 +357,12 @@ int pwm_adjust_config(struct pwm_device *pwm);
>   * @pwm: PWM device
>   * @duty_ns: "on" time (in nanoseconds)
>   * @period_ns: duration (in nanoseconds) of one cycle
> + * @mode: PWM mode
>   *
>   * Returns: 0 on success or a negative error code on failure.
>   */
>  static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
> -			     int period_ns)
> +			     int period_ns, unsigned long mode)
>  {
>  	struct pwm_state state;
>  
> @@ -377,6 +378,7 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
>  
>  	state.duty_cycle = duty_ns;
>  	state.period = period_ns;
> +	state.mode = mode;
>  	return pwm_apply_state(pwm, &state);
>  }
>  
> @@ -537,7 +539,7 @@ static inline int pwm_adjust_config(struct pwm_device *pwm)
>  }
>  
>  static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
> -			     int period_ns)
> +			     int period_ns, unsigned long mode)
>  {
>  	return -EINVAL;
>  }
> -- 
> 2.7.4
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply

* Re: [PATCH v3 05/10] pwm: add PWM mode to pwm_config()
From: Sean Young @ 2018-02-22 13:01 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: mark.rutland, milo.kim, linux-fbdev, devicetree, airlied,
	mturquette, joonas.lahtinen, kamil, dri-devel, linux-kernel,
	thierry.reding, alexandre.belloni, pavel, lee.jones, linux-clk,
	linux-leds, daniel.thompson, linux-samsung-soc, shc_work, corbet,
	linux-doc, linux, krzk, kgene, linux-input, linux, linux-media,
	linux-pwm, jdelvare, b.zolnierkie, intel-gfx, jani.nikula,
	robh+dt, jacek.anaszewski, rodrigo.vivi, mchehab
In-Reply-To: <1519300881-8136-6-git-send-email-claudiu.beznea@microchip.com>

On Thu, Feb 22, 2018 at 02:01:16PM +0200, Claudiu Beznea wrote:
> Add PWM mode to pwm_config() function. The drivers which uses pwm_config()
> were adapted to this change.
> 
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---

-snip-

> diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
> index 49265f02e772..a971b02ea021 100644
> --- a/drivers/media/rc/ir-rx51.c
> +++ b/drivers/media/rc/ir-rx51.c
> @@ -55,10 +55,13 @@ static int init_timing_params(struct ir_rx51 *ir_rx51)
>  {
>  	struct pwm_device *pwm = ir_rx51->pwm;
>  	int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq);
> +	struct pwm_caps caps = { };
>  
>  	duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100);
>  
> -	pwm_config(pwm, duty, period);
> +	pwm_get_caps(pwm->chip, pwm, &caps);
> +
> +	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
>  
>  	return 0;
>  }
> diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c
> index 27d0f5837a76..c630e1b450a3 100644
> --- a/drivers/media/rc/pwm-ir-tx.c
> +++ b/drivers/media/rc/pwm-ir-tx.c
> @@ -61,6 +61,7 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
>  {
>  	struct pwm_ir *pwm_ir = dev->priv;
>  	struct pwm_device *pwm = pwm_ir->pwm;
> +	struct pwm_caps caps = { };
>  	int i, duty, period;
>  	ktime_t edge;
>  	long delta;
> @@ -68,7 +69,9 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
>  	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
>  	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
>  
> -	pwm_config(pwm, duty, period);
> +	pwm_get_caps(pwm->chip, pwm, &caps);
> +
> +	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
>  
>  	edge = ktime_get();
>  

The two PWM rc-core drivers need PWM_MODE(NORMAL), not the first available
mode that the device supports. If mode normal is not supported, then probe
should fail.


Sean

^ permalink raw reply

* Re: [PATCH v3 05/10] pwm: add PWM mode to pwm_config()
From: Claudiu Beznea @ 2018-02-22 13:21 UTC (permalink / raw)
  To: Daniel Thompson
  Cc: mark.rutland, milo.kim, linux-fbdev, sean, devicetree, airlied,
	mturquette, joonas.lahtinen, kamil, dri-devel, linux-kernel,
	thierry.reding, alexandre.belloni, pavel, lee.jones, linux-clk,
	linux-leds, linux-samsung-soc, shc_work, corbet, linux-doc, linux,
	krzk, kgene, linux-input, linux, linux-media, linux-pwm, jdelvare,
	b.zolnierkie, intel-gfx, jani.nikula, robh+dt, jacek.anaszewski,
	rodrigo.vivi, mchehab
In-Reply-To: <20180222123308.mypx2r7n6o63mj5z@oak.lan>



On 22.02.2018 14:33, Daniel Thompson wrote:
> On Thu, Feb 22, 2018 at 02:01:16PM +0200, Claudiu Beznea wrote:
>> Add PWM mode to pwm_config() function. The drivers which uses pwm_config()
>> were adapted to this change.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  arch/arm/mach-s3c24xx/mach-rx1950.c  | 11 +++++++++--
>>  drivers/bus/ts-nbus.c                |  2 +-
>>  drivers/clk/clk-pwm.c                |  3 ++-
>>  drivers/gpu/drm/i915/intel_panel.c   | 17 ++++++++++++++---
>>  drivers/hwmon/pwm-fan.c              |  2 +-
>>  drivers/input/misc/max77693-haptic.c |  2 +-
>>  drivers/input/misc/max8997_haptic.c  |  6 +++++-
>>  drivers/leds/leds-pwm.c              |  5 ++++-
>>  drivers/media/rc/ir-rx51.c           |  5 ++++-
>>  drivers/media/rc/pwm-ir-tx.c         |  5 ++++-
>>  drivers/video/backlight/lm3630a_bl.c |  4 +++-
>>  drivers/video/backlight/lp855x_bl.c  |  4 +++-
>>  drivers/video/backlight/lp8788_bl.c  |  5 ++++-
>>  drivers/video/backlight/pwm_bl.c     | 11 +++++++++--
>>  drivers/video/fbdev/ssd1307fb.c      |  3 ++-
>>  include/linux/pwm.h                  |  6 ++++--
>>  16 files changed, 70 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/video/backlight/lm3630a_bl.c b/drivers/video/backlight/lm3630a_bl.c
>> index 2030a6b77a09..696fa25dafd2 100644
>> --- a/drivers/video/backlight/lm3630a_bl.c
>> +++ b/drivers/video/backlight/lm3630a_bl.c
>> @@ -165,8 +165,10 @@ static void lm3630a_pwm_ctrl(struct lm3630a_chip *pchip, int br, int br_max)
>>  {
>>  	unsigned int period = pchip->pdata->pwm_period;
>>  	unsigned int duty = br * period / br_max;
>> +	struct pwm_caps caps = { };
>>  
>> -	pwm_config(pchip->pwmd, duty, period);
>> +	pwm_get_caps(pchip->pwmd->chip, pchip->pwmd, &caps);
>> +	pwm_config(pchip->pwmd, duty, period, BIT(ffs(caps.modes) - 1));
> 
> Well... I admit I've only really looked at the patches that impact 
> backlight but dispersing this really odd looking bit twiddling 
> throughout the kernel doesn't strike me a great API design.>
> IMHO callers should not be required to find the first set bit in
> some specially crafted set of capability bits simply to get sane 
> default behaviour.
Thank you for your inputs. I will try to have a fix for this in next version.

The idea with this was to locate first available PWM mode of PWM channel. If the
driver hasn't registered any PWM capabilities related ops the default
capabilities will include only PWM normal mode. In case the PWM channel will
register different capabilities taking the first available mode from caps.modes
and passing it as argument to pwm_config() will ensure the pwm_apply_state()
will not fail.

Thank you,
Claudiu Beznea

> 
> 
> Daniel.
> 
> 
> 
> 
>>  	if (duty)
>>  		pwm_enable(pchip->pwmd);
>>  	else
>> diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
>> index 939f057836e1..3d274c604862 100644
>> --- a/drivers/video/backlight/lp855x_bl.c
>> +++ b/drivers/video/backlight/lp855x_bl.c
>> @@ -240,6 +240,7 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
>>  	unsigned int period = lp->pdata->period_ns;
>>  	unsigned int duty = br * period / max_br;
>>  	struct pwm_device *pwm;
>> +	struct pwm_caps caps = { };
>>  
>>  	/* request pwm device with the consumer name */
>>  	if (!lp->pwm) {
>> @@ -256,7 +257,8 @@ static void lp855x_pwm_ctrl(struct lp855x *lp, int br, int max_br)
>>  		pwm_apply_args(pwm);
>>  	}
>>  
>> -	pwm_config(lp->pwm, duty, period);
>> +	pwm_get_caps(lp->pwm->chip, lp->pwm, &caps);
>> +	pwm_config(lp->pwm, duty, period, BIT(ffs(caps.modes) - 1));
>>  	if (duty)
>>  		pwm_enable(lp->pwm);
>>  	else
>> diff --git a/drivers/video/backlight/lp8788_bl.c b/drivers/video/backlight/lp8788_bl.c
>> index cf869ec90cce..06de3163650d 100644
>> --- a/drivers/video/backlight/lp8788_bl.c
>> +++ b/drivers/video/backlight/lp8788_bl.c
>> @@ -128,6 +128,7 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
>>  	unsigned int duty;
>>  	struct device *dev;
>>  	struct pwm_device *pwm;
>> +	struct pwm_caps caps = { };
>>  
>>  	if (!bl->pdata)
>>  		return;
>> @@ -153,7 +154,9 @@ static void lp8788_pwm_ctrl(struct lp8788_bl *bl, int br, int max_br)
>>  		pwm_apply_args(pwm);
>>  	}
>>  
>> -	pwm_config(bl->pwm, duty, period);
>> +	pwm_get_caps(bl->pwm->chip, bl->pwm, &caps);
>> +
>> +	pwm_config(bl->pwm, duty, period, BIT(ffs(caps.modes) - 1));
>>  	if (duty)
>>  		pwm_enable(bl->pwm);
>>  	else
>> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
>> index 1c2289ddd555..706a9ab053a7 100644
>> --- a/drivers/video/backlight/pwm_bl.c
>> +++ b/drivers/video/backlight/pwm_bl.c
>> @@ -63,10 +63,14 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)
>>  
>>  static void pwm_backlight_power_off(struct pwm_bl_data *pb)
>>  {
>> +	struct pwm_caps caps = { };
>> +
>>  	if (!pb->enabled)
>>  		return;
>>  
>> -	pwm_config(pb->pwm, 0, pb->period);
>> +	pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
>> +
>> +	pwm_config(pb->pwm, 0, pb->period, BIT(ffs(caps.modes) - 1));
>>  	pwm_disable(pb->pwm);
>>  
>>  	if (pb->enable_gpio)
>> @@ -96,6 +100,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>>  {
>>  	struct pwm_bl_data *pb = bl_get_data(bl);
>>  	int brightness = bl->props.brightness;
>> +	struct pwm_caps caps = { };
>>  	int duty_cycle;
>>  
>>  	if (bl->props.power != FB_BLANK_UNBLANK ||
>> @@ -108,7 +113,9 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>>  
>>  	if (brightness > 0) {
>>  		duty_cycle = compute_duty_cycle(pb, brightness);
>> -		pwm_config(pb->pwm, duty_cycle, pb->period);
>> +		pwm_get_caps(pb->pwm->chip, pb->pwm, &caps);
>> +		pwm_config(pb->pwm, duty_cycle, pb->period,
>> +			   BIT(ffs(caps.modes) - 1));
>>  		pwm_backlight_power_on(pb, brightness);
>>  	} else
>>  		pwm_backlight_power_off(pb);
>> diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
>> index f599520374dd..4b57dcb5799a 100644
>> --- a/drivers/video/fbdev/ssd1307fb.c
>> +++ b/drivers/video/fbdev/ssd1307fb.c
>> @@ -308,7 +308,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
>>  
>>  		par->pwm_period = pargs.period;
>>  		/* Enable the PWM */
>> -		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
>> +		pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period,
>> +			   pargs.mode);
>>  		pwm_enable(par->pwm);
>>  
>>  		dev_dbg(&par->client->dev, "Using PWM%d with a %dns period.\n",
>> diff --git a/include/linux/pwm.h b/include/linux/pwm.h
>> index e62349f48129..0ba416ab2772 100644
>> --- a/include/linux/pwm.h
>> +++ b/include/linux/pwm.h
>> @@ -357,11 +357,12 @@ int pwm_adjust_config(struct pwm_device *pwm);
>>   * @pwm: PWM device
>>   * @duty_ns: "on" time (in nanoseconds)
>>   * @period_ns: duration (in nanoseconds) of one cycle
>> + * @mode: PWM mode
>>   *
>>   * Returns: 0 on success or a negative error code on failure.
>>   */
>>  static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
>> -			     int period_ns)
>> +			     int period_ns, unsigned long mode)
>>  {
>>  	struct pwm_state state;
>>  
>> @@ -377,6 +378,7 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
>>  
>>  	state.duty_cycle = duty_ns;
>>  	state.period = period_ns;
>> +	state.mode = mode;
>>  	return pwm_apply_state(pwm, &state);
>>  }
>>  
>> @@ -537,7 +539,7 @@ static inline int pwm_adjust_config(struct pwm_device *pwm)
>>  }
>>  
>>  static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
>> -			     int period_ns)
>> +			     int period_ns, unsigned long mode)
>>  {
>>  	return -EINVAL;
>>  }
>> -- 
>> 2.7.4
>>
> 

^ permalink raw reply

* Re: [PATCH v3 05/10] pwm: add PWM mode to pwm_config()
From: Claudiu Beznea @ 2018-02-22 13:23 UTC (permalink / raw)
  To: Sean Young
  Cc: mark.rutland, milo.kim, linux-fbdev, devicetree, airlied,
	mturquette, joonas.lahtinen, kamil, dri-devel, linux-kernel,
	thierry.reding, alexandre.belloni, pavel, lee.jones, linux-clk,
	linux-leds, daniel.thompson, linux-samsung-soc, shc_work, corbet,
	linux-doc, linux, krzk, kgene, linux-input, linux, linux-media,
	linux-pwm, jdelvare, b.zolnierkie, intel-gfx, jani.nikula,
	robh+dt, jacek.anaszewski, rodrigo.vivi, mchehab
In-Reply-To: <20180222130131.46fcuurp4h6dc7wb@gofer.mess.org>



On 22.02.2018 15:01, Sean Young wrote:
> On Thu, Feb 22, 2018 at 02:01:16PM +0200, Claudiu Beznea wrote:
>> Add PWM mode to pwm_config() function. The drivers which uses pwm_config()
>> were adapted to this change.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
> 
> -snip-
> 
>> diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c
>> index 49265f02e772..a971b02ea021 100644
>> --- a/drivers/media/rc/ir-rx51.c
>> +++ b/drivers/media/rc/ir-rx51.c
>> @@ -55,10 +55,13 @@ static int init_timing_params(struct ir_rx51 *ir_rx51)
>>  {
>>  	struct pwm_device *pwm = ir_rx51->pwm;
>>  	int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq);
>> +	struct pwm_caps caps = { };
>>  
>>  	duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100);
>>  
>> -	pwm_config(pwm, duty, period);
>> +	pwm_get_caps(pwm->chip, pwm, &caps);
>> +
>> +	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
>>  
>>  	return 0;
>>  }
>> diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c
>> index 27d0f5837a76..c630e1b450a3 100644
>> --- a/drivers/media/rc/pwm-ir-tx.c
>> +++ b/drivers/media/rc/pwm-ir-tx.c
>> @@ -61,6 +61,7 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
>>  {
>>  	struct pwm_ir *pwm_ir = dev->priv;
>>  	struct pwm_device *pwm = pwm_ir->pwm;
>> +	struct pwm_caps caps = { };
>>  	int i, duty, period;
>>  	ktime_t edge;
>>  	long delta;
>> @@ -68,7 +69,9 @@ static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
>>  	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
>>  	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
>>  
>> -	pwm_config(pwm, duty, period);
>> +	pwm_get_caps(pwm->chip, pwm, &caps);
>> +
>> +	pwm_config(pwm, duty, period, BIT(ffs(caps.modes) - 1));
>>  
>>  	edge = ktime_get();
>>  
> 
> The two PWM rc-core drivers need PWM_MODE(NORMAL), not the first available
> mode that the device supports. If mode normal is not supported, then probe
> should fail.
OK, thank you for your inputs. I will address this in next version.


Thank you,
Claudiu Beznea
> 
> 
> Sean
> 

^ permalink raw reply

* Re: [PATCH v3 06/10] pwm: add PWM modes
From: Andy Shevchenko @ 2018-02-22 17:28 UTC (permalink / raw)
  To: Claudiu Beznea
  Cc: Mark Rutland, milo.kim, linux-fbdev, Sean Young, devicetree,
	David Airlie, Michael Turquette, kamil, dri-devel,
	Linux Kernel Mailing List, Thierry Reding, Alexandre Belloni,
	Pavel Machek, Lee Jones, linux-clk, Linux LED Subsystem,
	Daniel Thompson, linux-samsung-soc, Alexander Shiyan,
	Jonathan Corbet, Linux Documentation List,
	Russell King - ARM Linux, Krzysztof Kozlowski, kgene
In-Reply-To: <1519300881-8136-7-git-send-email-claudiu.beznea@microchip.com>

On Thu, Feb 22, 2018 at 2:01 PM, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
> Add PWM normal and complementary modes.

> +- PWM_DTMODE_COMPLEMENTARY: PWM complementary working mode (for PWM
> +channels two outputs); if not specified, the default for PWM channel will
> +be used

What DT stands for?

-- 
With Best Regards,
Andy Shevchenko
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* Re: [PATCH v3 06/10] pwm: add PWM modes
From: Claudiu Beznea @ 2018-02-22 17:42 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Mark Rutland, milo.kim, linux-fbdev, Sean Young, devicetree,
	David Airlie, Michael Turquette, Joonas Lahtinen, kamil,
	dri-devel, Linux Kernel Mailing List, Thierry Reding,
	Alexandre Belloni, Pavel Machek, Lee Jones, linux-clk,
	Linux LED Subsystem, Daniel Thompson, linux-samsung-soc,
	Alexander Shiyan, Jonathan Corbet, Linux Documentation List,
	Russell King - ARM Linux
In-Reply-To: <CAHp75Ver6GuKSDooAr2DW+eqt=B3p8O+X6+rOSWNgfhHxgVqNQ@mail.gmail.com>



On 22.02.2018 19:28, Andy Shevchenko wrote:
> On Thu, Feb 22, 2018 at 2:01 PM, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>> Add PWM normal and complementary modes.
> 
>> +- PWM_DTMODE_COMPLEMENTARY: PWM complementary working mode (for PWM
>> +channels two outputs); if not specified, the default for PWM channel will
>> +be used
> 
> What DT stands for?
It stands for Device Tree. It remained this way from the previous version. In
the previous version I had modes described in an enum, to be used by PWM core,
as follows:
enum pwm_mode {
	PWM_MODE_NORMAL,
	PWM_MODE_COMPLEMENTARY,
};

and, to avoid conflict b/w these defines and the one from
include/dt-bindings/pwm/pwm.h I introduced this DT in the define names from
dt-bindings. But now the DT might be removed since I've changed the way the PWM
mode is identified in PWM core. I will remove the DT in the next version, if not
requested otherwise.

Thank you,
Claudiu Benea

^ permalink raw reply

* Re: [PATCH v2 0/3] gpio-keys: Add support for specifying wakeup event action
From: JeffyChen @ 2018-02-23  9:43 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Kate Stewart, Mark Rutland, Emil Renner Berthing, stephen lu,
	Catalin Marinas, Will Deacon, Joseph Lo, Brian Norris,
	linux-rockchip, Matthias Kaehlcke, linux-input, Arvind Yadav,
	dtor, devicetree, Arnd Bergmann, dianders, Rob Herring,
	briannorris, Thomas Gleixner, linux-arm-kernel,
	Greg Kroah-Hartman, Dmitry Torokhov, linux-kernel,
	Philippe Ombredanne, Enric Balletbo i Serra <enric.bal>
In-Reply-To: <1541427.8RRpDbxO4f@diego>

Hi Heiko,

Thanks for your reply :)

On 02/14/2018 09:39 PM, Heiko Stübner wrote:
> Hi Jeffy,
>
> Am Samstag, 10. Februar 2018, 12:09:04 CET schrieb Jeffy Chen:
>> On chromebook kevin, we are using gpio-keys for pen insert event. But
>> we only want it to wakeup the system when ejecting the pen.
>>
>> So we may need to change the interrupt trigger type during suspending.
>>
>> Changes in v2:
>> Specify wakeup event action instead of irq trigger type as Brian
>> suggested.
>>
>> Jeffy Chen (3):
>>    Input: gpio-keys - add support for wakeup event action
>>    Input: gpio-keys - allow setting wakeup event action in DT
>>    arm64: dts: rockchip: Avoid wakeup when inserting the pen
>
> The recipient-selection for the mail thread seems to be a bit off and
> I only got the cover-letter and patch 3/3.
> Therefore I won't really see if and when the Input-changes get
> accepted. So please ping me once that has happened so I can pick up
> the rockchip dts change for it.
oops, sorry, i'll add you to the CC list in the next version :)

>
>
> Thanks
> Heiko
>
>
>



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply


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