All of lore.kernel.org
 help / color / mirror / Atom feed
From: Sean Young <sean@mess.org>
To: "Sean Young" <sean@mess.org>,
	"Mauro Carvalho Chehab" <mchehab@kernel.org>,
	"Thierry Reding" <thierry.reding@gmail.com>,
	"Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
Cc: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>,
	linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-pwm@vger.kernel.org
Subject: [PATCH 2/2] media: pwm-ir-tx: trigger edges from hrtimer interrupt context
Date: Sun,  1 Oct 2023 11:40:30 +0100	[thread overview]
Message-ID: <7efe4229514001b835fa70d51973cd3306dc0b04.1696156485.git.sean@mess.org> (raw)
In-Reply-To: <cover.1696156485.git.sean@mess.org>

The pwm-ir-tx driver has to turn the pwm signal on and off, and suffers
from delays as this is done in process context. Make this work in atomic
context.

This makes the driver much more precise.

Signed-off-by: Sean Young <sean@mess.org>
Cc: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
---
 drivers/media/rc/pwm-ir-tx.c | 79 ++++++++++++++++++++++++++++--------
 1 file changed, 63 insertions(+), 16 deletions(-)

diff --git a/drivers/media/rc/pwm-ir-tx.c b/drivers/media/rc/pwm-ir-tx.c
index c5f37c03af9c..557725a07a67 100644
--- 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,13 @@
 
 struct pwm_ir {
 	struct pwm_device *pwm;
-	unsigned int carrier;
-	unsigned int duty_cycle;
+	struct hrtimer timer;
+	struct completion completion;
+	uint carrier;
+	uint duty_cycle;
+	uint *txbuf;
+	uint txbuf_len;
+	uint txbuf_index;
 };
 
 static const struct of_device_id pwm_ir_of_match[] = {
@@ -55,33 +62,65 @@ 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_state state;
-	int i;
-	ktime_t edge;
-	long delta;
+
+	reinit_completion(&pwm_ir->completion);
 
 	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);
+	state.enabled = false;
 
-	edge = ktime_get();
+	pwm_ir->txbuf = txbuf;
+	pwm_ir->txbuf_len = count;
+	pwm_ir->txbuf_index = 0;
 
-	for (i = 0; i < count; i++) {
-		state.enabled = !(i % 2);
-		pwm_apply_state(pwm, &state);
+	pwm_apply_state(pwm, &state);
 
-		edge = ktime_add_us(edge, txbuf[i]);
-		delta = ktime_us_delta(edge, ktime_get());
-		if (delta > 0)
-			usleep_range(delta, delta + 10);
-	}
+	hrtimer_start(&pwm_ir->timer, 1000, HRTIMER_MODE_REL);
 
-	state.enabled = false;
-	pwm_apply_state(pwm, &state);
+	wait_for_completion(&pwm_ir->completion);
 
 	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;
+
+		if (pwm_ir->txbuf_index >= pwm_ir->txbuf_len) {
+			/* Stop TX here */
+			pwm_disable(pwm_ir->pwm);
+
+			complete(&pwm_ir->completion);
+
+			return HRTIMER_NORESTART;
+		}
+
+		if (pwm_ir->txbuf_index % 2)
+			pwm_disable(pwm_ir->pwm);
+		else
+			pwm_enable(pwm_ir->pwm);
+
+		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;
@@ -96,8 +135,16 @@ static int pwm_ir_probe(struct platform_device *pdev)
 	if (IS_ERR(pwm_ir->pwm))
 		return PTR_ERR(pwm_ir->pwm);
 
+	if (pwm_can_sleep(pwm_ir->pwm)) {
+		dev_err(&pdev->dev, "unsupported pwm device: driver can sleep\n");
+		return -ENODEV;
+	}
+
 	pwm_ir->carrier = 38000;
 	pwm_ir->duty_cycle = 50;
+	init_completion(&pwm_ir->completion);
+	hrtimer_init(&pwm_ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pwm_ir->timer.function = pwm_ir_timer;
 
 	rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
 	if (!rcdev)
-- 
2.42.0


  parent reply	other threads:[~2023-10-01 10:41 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-10-01 10:40 [PATCH 0/2] Improve pwm-ir-tx precision Sean Young
2023-10-01 10:40 ` [PATCH 1/2] pwm: make it possible to apply pwm changes in atomic context Sean Young
2023-10-01 10:40   ` Sean Young
2023-10-01 10:40   ` Sean Young
2023-10-01 14:43   ` kernel test robot
2023-10-01 14:43     ` kernel test robot
2023-10-01 14:43     ` kernel test robot
2023-10-01 16:07   ` kernel test robot
2023-10-01 16:07     ` kernel test robot
2023-10-01 16:07     ` kernel test robot
2023-10-01 17:21     ` Sean Young
2023-10-01 17:21       ` Sean Young
2023-10-01 17:21       ` Sean Young
2023-10-04  9:59   ` Uwe Kleine-König
2023-10-04  9:59     ` Uwe Kleine-König
2023-10-04  9:59     ` Uwe Kleine-König
2023-10-05  8:30     ` Sean Young
2023-10-05  8:30       ` Sean Young
2023-10-05  8:30       ` Sean Young
2023-10-05  9:17       ` Uwe Kleine-König
2023-10-05  9:17         ` Uwe Kleine-König
2023-10-05  9:17         ` Uwe Kleine-König
2023-10-06 10:27     ` Thierry Reding
2023-10-06 10:27       ` Thierry Reding
2023-10-06 10:27       ` Thierry Reding
2023-10-06 14:44       ` Uwe Kleine-König
2023-10-06 14:44         ` Uwe Kleine-König
2023-10-06 14:44         ` Uwe Kleine-König
2023-10-06 10:29   ` Thierry Reding
2023-10-06 10:29     ` Thierry Reding
2023-10-06 10:29     ` Thierry Reding
2023-10-01 10:40 ` Sean Young [this message]
2023-10-02  5:49   ` [PATCH 2/2] media: pwm-ir-tx: trigger edges from hrtimer interrupt context Ivaylo Dimitrov
2023-10-02  8:20     ` Sean Young
2023-10-02  8:59       ` Uwe Kleine-König
2023-10-02  9:52       ` Ivaylo Dimitrov
2023-10-04  7:43         ` Sean Young
2023-10-04  9:35           ` Uwe Kleine-König
2023-10-02  6:16   ` Ivaylo Dimitrov
2023-10-04  8:00     ` Sean Young
2023-10-04 12:54       ` Ivaylo Dimitrov
2023-10-04 14:42         ` Sean Young

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7efe4229514001b835fa70d51973cd3306dc0b04.1696156485.git.sean@mess.org \
    --to=sean@mess.org \
    --cc=ivo.g.dimitrov.75@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-pwm@vger.kernel.org \
    --cc=mchehab@kernel.org \
    --cc=thierry.reding@gmail.com \
    --cc=u.kleine-koenig@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.