Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Andre Przywara <andre.przywara@arm.com>
To: Justin Suess <utilityemal77@gmail.com>
Cc: Sean Young <sean@mess.org>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Chen-Yu Tsai <wens@kernel.org>,
	Jernej Skrabec <jernej.skrabec@gmail.com>,
	Samuel Holland <samuel@sholland.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Maxime Ripard <mripard@kernel.org>,
	linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-sunxi@lists.linux.dev, Sashiko <sashiko-bot@kernel.org>
Subject: Re: [PATCH 2/4] media: rc: sunxi-cir: add support for the A523
Date: Fri, 3 Jul 2026 11:11:59 +0200	[thread overview]
Message-ID: <309f6601-2358-4a2d-9696-0849d69ade52@arm.com> (raw)
In-Reply-To: <20260702214750.3428694-3-utilityemal77@gmail.com>

On Thu,  2 Jul 2026 17:47:48 -0400
Justin Suess <utilityemal77@gmail.com> wrote:

Hi Justin,

many thanks for sending this!

> The A523 (sun55i) has a newer revision of the CIR receiver IP. Two
> register fields that do not exist on older SoCs must be programmed
> for reception to work:
> 
>  - CTL bits [7:6] select which pulse polarities are captured into the
>    RX FIFO. The reset value of 0 captures nothing, so program "both
>    pulse" mode, which captures regardless of header polarity.

Are you sure about that? The manual says that *both* the 0b00 (reset
default) and 0b01 values capture both edges, and actually the H6, A133
and H616 have the same bits, and it apparently works there.

I don't see those bits documented in the A64 (and earlier), but haven't 
checked yet whether they exist there regardless or have an effect.
So I think we should force those bits either to 0 or to 1, depending on 
how those bits behave on A64 and before, and how compatible this is with 
H6, A133, H616. I will try to run some experiments on the weekend.

>  - SPLCFG (the sample configuration register) bits [1:0] select the
>    sample clock as a division of the module clock, replacing the
>    fixed module clock / 64 sample rate of the older IP.

That's not fully correct: even the A20(!) has these two bits, actually
there is a third bit, held in bit 24 (because reasons). All those bits
reset to 0, which is encoded as /64, so this is where the rate comes
from. And sunxi_ir_probe() sets the IR clock to 8MHz, which should end
up as 24MHz / 3, on all chips, including the A523.

So what is going on here? Is the manual wrong, about those bits, or the 
clock sources?
Can you point to the BSP sources, if you used those?

>    module clock / 256, which together with the 24 MHz module clock

Why is the A523 mod clock set to 24 MHz? You seem to do this in the DT, 
overriding the 8MHz default? The driver clearly has a clk_set_rate() 
call with that default 8MHz as an argument, and I don't think we should 
deviate from that, unless there are good reasons. The sample clock 
should be more of a driver/subsystem decision, not a a device one.

>    used on the A523 gives a 10.7 μs sample period, close to the 8 μs
>    of the previous 8 MHz / 64 configuration, and keeps the default
>    125 ms idle timeout representable in the 8-bit idle threshold

This is some good info that helps people understand the reasoning behind 
those timing values. Please put this in a comment near the top of the file.
But actually: how does this compute? With an 8us sample clock period, 
the 8-bit ATHR field only covers 2 ms. And I don't see us setting the 
ATHC bit to bump this by 128.

Cheers,
Andre

>    field.
> 
> Parameterize the sample divisor in the resolution/timeout
> calculations, which older SoCs keep at the fixed 64, and add the
> A523 quirks and compatible.
> 
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> ---
>  drivers/media/rc/sunxi-cir.c | 76 ++++++++++++++++++++++++++++++------
>  1 file changed, 63 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
> index cb4c56bf0752..82ada9dc0347 100644
> --- a/drivers/media/rc/sunxi-cir.c
> +++ b/drivers/media/rc/sunxi-cir.c
> @@ -31,6 +31,11 @@
>  /* CIR mode */
>  #define REG_CTL_MD			(BIT(4) | BIT(5))
>  
> +/* Pulse mode selector (bits [7:6]) */
> +#define REG_CTL_PMD(m)			((m) << 6)
> +/* Capture both pulse polarities */
> +#define REG_CTL_PMD_BOTH		REG_CTL_PMD(1)
> +
>  /* Rx Config */
>  #define SUNXI_IR_RXCTL_REG    0x10
>  /* Pulse Polarity Invert flag */
> @@ -66,6 +71,13 @@
>  
>  /* IR Sample Config */
>  #define SUNXI_IR_CIR_REG      0x34
> +/*
> + * Sample clock divider select (bits [1:0]), present on newer IP revisions
> + * (e.g. sun55i). Selects the sample clock as a fraction of the module clock;
> + * must be programmed for the sampler to run. Older SoCs lack the field and
> + * use a fixed module-clock/64 sample rate, so they leave it 0.
> + */
> +#define REG_CIR_SDIV(val)    ((val) & GENMASK(1, 0))
>  /* CIR_REG register noise threshold */
>  #define REG_CIR_NTHR(val)    (((val) << 2) & (GENMASK(7, 2)))
>  /* CIR_REG register idle threshold */
> @@ -73,6 +85,8 @@
>  
>  /* Required frequency for IR0 or IR1 clock in CIR mode (default) */
>  #define SUNXI_IR_BASE_CLK     8000000
> +/* Default sample clock divisor: module clock / 64 (legacy fixed rate) */
> +#define SUNXI_IR_SAMPLE_DIV   64
>  /* Noise threshold in samples  */
>  #define SUNXI_IR_RXNOISE      1
>  
> @@ -81,10 +95,18 @@
>   *
>   * @has_reset: SoC needs reset deasserted.
>   * @fifo_size: size of the fifo.
> + * @both_pulse: program the CTRL pulse-mode field (newer IP revisions).

As mentioned above, those bits exist in earlier IP as well. Typically 
non-implemented bits in Allwinner IP as RES0, so I think we can program 
them unconditionally (and should on H6/A133/H616) and don't need a 
quirks flag.

> + * @sample_div_sel: value for the SPLCFG sample-clock divider field (0 on
> + *		    legacy SoCs that lack the field).

Same here: those bits exist back to the A20, even. And their meaning 
didn't change, if I see this correctly. So no quirk needed, instead we 
should program them explicitly to the value we want (probably 0).

> + * @sample_divisor: module-clock divisor that yields the sample clock; matches
> + *		    @sample_div_sel on newer IP, or the fixed /64 on legacy SoCs.

That looks odd: why do we have that value in the first place? Following 
the things I mention above, the divisor shouldn't be different on the 
A523. And also, I think we should just do the math in the driver, and 
calculate the divisor, based on some timing requirement. Which could be 
something like: aim for a clock period of 8us. Though all the parameters 
seem to be stable: the 24 MHz OSC input, the dividers in the mod clock, 
and the post dividers in register 0x34. So there wouldn't be much of a 
calculation, really. But I still think the driver can figure this out 
itself, and doesn't need explicit telling of a divisor.

So I think we would need a separate patch to fix up driver operation 
before A523. Then the A523 bits should go on top of this. And maybe make 
this two patches, one for the edge sample bits, one for the clock 
calculation.

Cheers,
Andre

>   */
>  struct sunxi_ir_quirks {
>  	bool		has_reset;
>  	int		fifo_size;
> +	bool		both_pulse;
> +	u8		sample_div_sel;
> +	u32		sample_divisor;
>  };
>  
>  struct sunxi_ir {
> @@ -92,6 +114,9 @@ struct sunxi_ir {
>  	void __iomem    *base;
>  	int             irq;
>  	int		fifo_size;
> +	bool		both_pulse;
> +	u8		sample_div_sel;
> +	u32		sample_divisor;
>  	struct clk      *clk;
>  	struct clk      *apb_clk;
>  	struct reset_control *rst;
> @@ -140,17 +165,19 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
>  }
>  
>  /* Convert idle threshold to usec */
> -static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int ithr)
> +static unsigned int sunxi_ithr_to_usec(unsigned int base_clk, unsigned int div,
> +				       unsigned int ithr)
>  {
>  	return DIV_ROUND_CLOSEST(USEC_PER_SEC * (ithr + 1),
> -				 base_clk / (128 * 64));
> +				 base_clk / (128 * div));
>  }
>  
>  /* Convert usec to idle threshold */
> -static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int usec)
> +static unsigned int sunxi_usec_to_ithr(unsigned int base_clk, unsigned int div,
> +				       unsigned int usec)
>  {
>  	/* make sure we don't end up with a timeout less than requested */
> -	return DIV_ROUND_UP((base_clk / (128 * 64)) * usec,  USEC_PER_SEC) - 1;
> +	return DIV_ROUND_UP((base_clk / (128 * div)) * usec,  USEC_PER_SEC) - 1;
>  }
>  
>  static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout)
> @@ -158,15 +185,17 @@ static int sunxi_ir_set_timeout(struct rc_dev *rc_dev, unsigned int timeout)
>  	struct sunxi_ir *ir = rc_dev->priv;
>  	unsigned int base_clk = clk_get_rate(ir->clk);
>  
> -	unsigned int ithr = sunxi_usec_to_ithr(base_clk, timeout);
> +	unsigned int ithr = sunxi_usec_to_ithr(base_clk, ir->sample_divisor,
> +					       timeout);
>  
>  	dev_dbg(rc_dev->dev.parent, "setting idle threshold to %u\n", ithr);
>  
> -	/* Set noise threshold and idle threshold */
> -	writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr),
> +	/* Set sample clock divider, noise threshold and idle threshold */
> +	writel(REG_CIR_SDIV(ir->sample_div_sel) |
> +	       REG_CIR_NTHR(SUNXI_IR_RXNOISE) | REG_CIR_ITHR(ithr),
>  	       ir->base + SUNXI_IR_CIR_REG);
>  
> -	rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ithr);
> +	rc_dev->timeout = sunxi_ithr_to_usec(base_clk, ir->sample_divisor, ithr);
>  
>  	return 0;
>  }
> @@ -193,8 +222,14 @@ static int sunxi_ir_hw_init(struct device *dev)
>  		goto exit_disable_apb_clk;
>  	}
>  
> -	/* Enable CIR Mode */
> -	writel(REG_CTL_MD, ir->base + SUNXI_IR_CTL_REG);
> +	/*
> +	 * Enable CIR Mode. On newer IP revisions the pulse-mode field must
> +	 * also be set, otherwise no pulses are captured into the RX FIFO.
> +	 */
> +	tmp = REG_CTL_MD;
> +	if (ir->both_pulse)
> +		tmp |= REG_CTL_PMD_BOTH;
> +	writel(tmp, ir->base + SUNXI_IR_CTL_REG);
>  
>  	/* Set noise threshold and idle threshold */
>  	sunxi_ir_set_timeout(ir->rc, ir->rc->timeout);
> @@ -271,6 +306,9 @@ static int sunxi_ir_probe(struct platform_device *pdev)
>  	}
>  
>  	ir->fifo_size = quirks->fifo_size;
> +	ir->both_pulse = quirks->both_pulse;
> +	ir->sample_div_sel = quirks->sample_div_sel;
> +	ir->sample_divisor = quirks->sample_divisor ?: SUNXI_IR_SAMPLE_DIV;
>  
>  	/* Clock */
>  	ir->apb_clk = devm_clk_get(dev, "apb");
> @@ -325,10 +363,10 @@ static int sunxi_ir_probe(struct platform_device *pdev)
>  	ir->rc->dev.parent = dev;
>  	ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
>  	/* Frequency after IR internal divider with sample period in us */
> -	ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / 64));
> +	ir->rc->rx_resolution = (USEC_PER_SEC / (b_clk_freq / ir->sample_divisor));
>  	ir->rc->timeout = IR_DEFAULT_TIMEOUT;
> -	ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, 0);
> -	ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, 255);
> +	ir->rc->min_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 0);
> +	ir->rc->max_timeout = sunxi_ithr_to_usec(b_clk_freq, ir->sample_divisor, 255);
>  	ir->rc->s_timeout = sunxi_ir_set_timeout;
>  	ir->rc->driver_name = SUNXI_IR_DEV;
>  
> @@ -395,6 +433,14 @@ static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = {
>  	.fifo_size = 64,
>  };
>  
> +static const struct sunxi_ir_quirks sun55i_a523_ir_quirks = {
> +	.has_reset = true,
> +	.fifo_size = 64,
> +	.both_pulse = true,
> +	.sample_div_sel = 2,	/* sample clock = module clock / 256 */
> +	.sample_divisor = 256,
> +};
> +
>  static const struct of_device_id sunxi_ir_match[] = {
>  	{
>  		.compatible = "allwinner,sun4i-a10-ir",
> @@ -408,6 +454,10 @@ static const struct of_device_id sunxi_ir_match[] = {
>  		.compatible = "allwinner,sun6i-a31-ir",
>  		.data = &sun6i_a31_ir_quirks,
>  	},
> +	{
> +		.compatible = "allwinner,sun55i-a523-ir",
> +		.data = &sun55i_a523_ir_quirks,
> +	},
>  	{}
>  };
>  MODULE_DEVICE_TABLE(of, sunxi_ir_match);



  reply	other threads:[~2026-07-03  9:12 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-02 21:47 [PATCH 0/4] media: rc: sunxi-cir: support the A523/H728/T527 IR receiver Justin Suess
2026-07-02 21:47 ` [PATCH 1/4] media: dt-bindings: allwinner,sun4i-a10-ir: add A523 compatible Justin Suess
2026-07-03 10:56   ` Krzysztof Kozlowski
2026-07-02 21:47 ` [PATCH 2/4] media: rc: sunxi-cir: add support for the A523 Justin Suess
2026-07-03  9:11   ` Andre Przywara [this message]
2026-07-02 21:47 ` [PATCH 3/4] arm64: dts: allwinner: a523: add IR receiver node Justin Suess
2026-07-02 21:47 ` [PATCH 4/4] arm64: dts: allwinner: a523: enable IR receiver on the X96Q Pro+ Justin Suess

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=309f6601-2358-4a2d-9696-0849d69ade52@arm.com \
    --to=andre.przywara@arm.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jernej.skrabec@gmail.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=mchehab@kernel.org \
    --cc=mripard@kernel.org \
    --cc=robh@kernel.org \
    --cc=samuel@sholland.org \
    --cc=sashiko-bot@kernel.org \
    --cc=sean@mess.org \
    --cc=utilityemal77@gmail.com \
    --cc=wens@kernel.org \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox