mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	The patches were generated from the RPi repo with the following command: git format-patch v6.6.34..rpi-6.1.y Some patches needed rebasing and, as usual, the applied and reverted, wireless drivers, Github workflows, READMEs and defconfigs patches were removed. Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
		
			
				
	
	
		
			141 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 1db50b3b0222f6f9d89f30bfa8fd64457bd01bef Mon Sep 17 00:00:00 2001
 | 
						|
From: Sean Young <sean@mess.org>
 | 
						|
Date: Tue, 19 Dec 2023 16:30:29 +0000
 | 
						|
Subject: [PATCH 0942/1085] media: pwm-ir-tx: Trigger edges from hrtimer
 | 
						|
 interrupt context
 | 
						|
 | 
						|
commit 363d0e56285e80cda997d41d94c22313b673557d upstream.
 | 
						|
 | 
						|
This makes the generated IR much more precise. Before this change, the
 | 
						|
driver is unreliable and many users opted to use gpio-ir-tx instead.
 | 
						|
 | 
						|
Signed-off-by: Sean Young <sean@mess.org>
 | 
						|
Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
 | 
						|
---
 | 
						|
 drivers/media/rc/pwm-ir-tx.c | 83 +++++++++++++++++++++++++++++++++---
 | 
						|
 1 file changed, 78 insertions(+), 5 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/media/rc/pwm-ir-tx.c
 | 
						|
+++ b/drivers/media/rc/pwm-ir-tx.c
 | 
						|
@@ -10,6 +10,8 @@
 | 
						|
 #include <linux/slab.h>
 | 
						|
 #include <linux/of.h>
 | 
						|
 #include <linux/platform_device.h>
 | 
						|
+#include <linux/hrtimer.h>
 | 
						|
+#include <linux/completion.h>
 | 
						|
 #include <media/rc-core.h>
 | 
						|
 
 | 
						|
 #define DRIVER_NAME	"pwm-ir-tx"
 | 
						|
@@ -17,8 +19,14 @@
 | 
						|
 
 | 
						|
 struct pwm_ir {
 | 
						|
 	struct pwm_device *pwm;
 | 
						|
-	unsigned int carrier;
 | 
						|
-	unsigned int duty_cycle;
 | 
						|
+	struct hrtimer timer;
 | 
						|
+	struct completion tx_done;
 | 
						|
+	struct pwm_state *state;
 | 
						|
+	u32 carrier;
 | 
						|
+	u32 duty_cycle;
 | 
						|
+	const unsigned int *txbuf;
 | 
						|
+	unsigned int txbuf_len;
 | 
						|
+	unsigned int txbuf_index;
 | 
						|
 };
 | 
						|
 
 | 
						|
 static const struct of_device_id pwm_ir_of_match[] = {
 | 
						|
@@ -48,8 +56,8 @@ static int pwm_ir_set_carrier(struct rc_
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 | 
						|
-		     unsigned int count)
 | 
						|
+static int pwm_ir_tx_sleep(struct rc_dev *dev, unsigned int *txbuf,
 | 
						|
+			   unsigned int count)
 | 
						|
 {
 | 
						|
 	struct pwm_ir *pwm_ir = dev->priv;
 | 
						|
 	struct pwm_device *pwm = pwm_ir->pwm;
 | 
						|
@@ -81,6 +89,62 @@ static int pwm_ir_tx(struct rc_dev *dev,
 | 
						|
 	return count;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static int pwm_ir_tx_atomic(struct rc_dev *dev, unsigned int *txbuf,
 | 
						|
+			    unsigned int count)
 | 
						|
+{
 | 
						|
+	struct pwm_ir *pwm_ir = dev->priv;
 | 
						|
+	struct pwm_device *pwm = pwm_ir->pwm;
 | 
						|
+	struct pwm_state state;
 | 
						|
+
 | 
						|
+	pwm_init_state(pwm, &state);
 | 
						|
+
 | 
						|
+	state.period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
 | 
						|
+	pwm_set_relative_duty_cycle(&state, pwm_ir->duty_cycle, 100);
 | 
						|
+
 | 
						|
+	pwm_ir->txbuf = txbuf;
 | 
						|
+	pwm_ir->txbuf_len = count;
 | 
						|
+	pwm_ir->txbuf_index = 0;
 | 
						|
+	pwm_ir->state = &state;
 | 
						|
+
 | 
						|
+	hrtimer_start(&pwm_ir->timer, 0, HRTIMER_MODE_REL);
 | 
						|
+
 | 
						|
+	wait_for_completion(&pwm_ir->tx_done);
 | 
						|
+
 | 
						|
+	return count;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static enum hrtimer_restart pwm_ir_timer(struct hrtimer *timer)
 | 
						|
+{
 | 
						|
+	struct pwm_ir *pwm_ir = container_of(timer, struct pwm_ir, timer);
 | 
						|
+	ktime_t now;
 | 
						|
+
 | 
						|
+	/*
 | 
						|
+	 * If we happen to hit an odd latency spike, loop through the
 | 
						|
+	 * pulses until we catch up.
 | 
						|
+	 */
 | 
						|
+	do {
 | 
						|
+		u64 ns;
 | 
						|
+
 | 
						|
+		pwm_ir->state->enabled = !(pwm_ir->txbuf_index % 2);
 | 
						|
+		pwm_apply_atomic(pwm_ir->pwm, pwm_ir->state);
 | 
						|
+
 | 
						|
+		if (pwm_ir->txbuf_index >= pwm_ir->txbuf_len) {
 | 
						|
+			complete(&pwm_ir->tx_done);
 | 
						|
+
 | 
						|
+			return HRTIMER_NORESTART;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		ns = US_TO_NS(pwm_ir->txbuf[pwm_ir->txbuf_index]);
 | 
						|
+		hrtimer_add_expires_ns(timer, ns);
 | 
						|
+
 | 
						|
+		pwm_ir->txbuf_index++;
 | 
						|
+
 | 
						|
+		now = timer->base->get_time();
 | 
						|
+	} while (hrtimer_get_expires_tv64(timer) < now);
 | 
						|
+
 | 
						|
+	return HRTIMER_RESTART;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int pwm_ir_probe(struct platform_device *pdev)
 | 
						|
 {
 | 
						|
 	struct pwm_ir *pwm_ir;
 | 
						|
@@ -102,10 +166,19 @@ static int pwm_ir_probe(struct platform_
 | 
						|
 	if (!rcdev)
 | 
						|
 		return -ENOMEM;
 | 
						|
 
 | 
						|
+	if (pwm_might_sleep(pwm_ir->pwm)) {
 | 
						|
+		dev_info(&pdev->dev, "TX will not be accurate as PWM device might sleep\n");
 | 
						|
+		rcdev->tx_ir = pwm_ir_tx_sleep;
 | 
						|
+	} else {
 | 
						|
+		init_completion(&pwm_ir->tx_done);
 | 
						|
+		hrtimer_init(&pwm_ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 | 
						|
+		pwm_ir->timer.function = pwm_ir_timer;
 | 
						|
+		rcdev->tx_ir = pwm_ir_tx_atomic;
 | 
						|
+	}
 | 
						|
+
 | 
						|
 	rcdev->priv = pwm_ir;
 | 
						|
 	rcdev->driver_name = DRIVER_NAME;
 | 
						|
 	rcdev->device_name = DEVICE_NAME;
 | 
						|
-	rcdev->tx_ir = pwm_ir_tx;
 | 
						|
 	rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle;
 | 
						|
 	rcdev->s_tx_carrier = pwm_ir_set_carrier;
 | 
						|
 
 |