diff mbox series

[1/3] media: rc: meson-ir: support rc driver type RC_DRIVER_SCANCODE

Message ID 20230825115310.39993-2-zelong.dong@amlogic.com
State Accepted
Commit e798f5b2d1cc22c6a01107da504dd195e685339c
Headers show
Series This patchset adds IR controller driver support for | expand

Commit Message

zelong dong Aug. 25, 2023, 11:53 a.m. UTC
From: Zelong Dong <zelong.dong@amlogic.com>

Meson IR Controller supports hardware decoder in Meson-S4 and later
SoC. So far, protocol NEC could be decoded by hardware decoder.

Signed-off-by: Zelong Dong <zelong.dong@amlogic.com>
---
 drivers/media/rc/meson-ir.c | 522 +++++++++++++++++++++++++++++++-----
 1 file changed, 451 insertions(+), 71 deletions(-)

Comments

zelong dong Aug. 31, 2023, 12:13 p.m. UTC | #1
在 2023/8/29 15:39, Sean Young 写道:
> On Fri, Aug 25, 2023 at 07:53:08PM +0800, zelong dong wrote:
>> From: Zelong Dong<zelong.dong@amlogic.com>
>>
>> Meson IR Controller supports hardware decoder in Meson-S4 and later
>> SoC. So far, protocol NEC could be decoded by hardware decoder.
> On Meson-S4, only the hardware decoder for NEC can be used using this
> driver. Does the Meson-S4 hardware support software decoding? If
> software decoding could be used, then any protocol could be supported,
> not just NEC.
> 
> Also, out of interest, is there are documentation available for this
> hardware?
> 
> Thanks,
> 
> Sean
> 
Yes, IR driver still supports SW decoding on Meson-S4. The decode mode 
could be changed by 'support_hw_decoder'.
If IR Controller works in SW decoding, driver will be registered by 
RC_DRIVER_IR_RAW and allows all protocol.
Otherwise, driver will be registered by RC_DRIVER_SCANCODE and only 
allows NEC.

489         if (ir->rc->driver_type == RC_DRIVER_IR_RAW) {
490                 ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
491                 ir->rc->rx_resolution = MESON_RAW_TRATE;
492                 ir->rc->min_timeout = 1;
493                 ir->rc->timeout = IR_DEFAULT_TIMEOUT;
494                 ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
495         } else if (ir->rc->driver_type == RC_DRIVER_SCANCODE) {
496                 ir->rc->allowed_protocols = RC_PROTO_BIT_NEC;
497                 ir->rc->change_protocol = meson_ir_hw_decoder_init;
498         }

Do you get Meson-S4 datasheet? Please refer to chapter 13.5 Infrared Remote.
Neil Armstrong Sept. 6, 2023, 7:17 a.m. UTC | #2
On 06/09/2023 09:11, Zelong Dong wrote:
> 
> 
> 在 2023/8/25 20:19, Neil Armstrong 写道:
>> On 25/08/2023 13:53, zelong dong wrote:
>>> From: Zelong Dong <zelong.dong@amlogic.com>
>>>
>>> Meson IR Controller supports hardware decoder in Meson-S4 and later
>>> SoC. So far, protocol NEC could be decoded by hardware decoder.
>>>
>>> Signed-off-by: Zelong Dong <zelong.dong@amlogic.com>
>>> ---
>>>   drivers/media/rc/meson-ir.c | 522 +++++++++++++++++++++++++++++++-----
>>>   1 file changed, 451 insertions(+), 71 deletions(-)
>>>
>>
>> <snip>
>>
>> It looks pretty good now, so far I'm ok with it:
>>
>> Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
>>
>> I think some test reports would be needed to be sure it doesn't regress existing HW.
>>
>> Thanks,
>> Neil
> 
> Sorry for my late reply, I spent a long time looking for development boards.
> I searched Meson-IR DT Node from all the Amlogic DT, only 4 DTSI as follow:
> arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
> arch/arm64/boot/dts/amlogic/meson-axg.dtsi
> arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> arch/arm/boot/dts/amlogic/meson.dtsi
> 
> So I planned to sample the boards to test:
> arch/arm64/boot/dts/amlogic/meson-g12a-u200.dts
> arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
> arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
> arch/arm/boot/dts/amlogic/meson8b-odroidc1.dts
> 
> Unfortunately, meson-gxl-s905x-p212 & meson8b-odroidc1 are too old to be find.
> there are only 2 boards I got, meson-g12a-u200 & meson-axg-s400, and they worked.
> 
> [    0.943097] IR NEC protocol handler initialized
> [    0.943146] IR RC5(x/sz) protocol handler initialized
> [    0.947029] IR RC6 protocol handler initialized
> [    0.951508] IR RCMM protocol handler initialized
> [    0.956080] IR XMP protocol handler initialized
> [    0.961266] Registered IR keymap rc-empty
> [    0.964674] rc rc0: meson-ir as /devices/platform/soc/ff800000.bus/ff808000.ir/rc/rc0
> [    0.972437] input: meson-ir as /devices/platform/soc/ff800000.bus/ff808000.ir/rc/rc0/input0
> [    0.980983] rc rc0: sw decoder init
> [    0.984089] meson-ir ff808000.ir: receiver initialized
> 
> # cd /sys/devices/platform/soc/ff800000.bus/ff808000.ir/rc/rc0/
> # cat protocols
> rc-5 nec rc-6 rc-5-sz xmp rc-mm
> # echo nec > protocols
> # hexdump /dev/input/event0
> 0000000 0069 0000 3180 0005 0004 0004 011b 0000
> 0000010 0069 0000 3180 0005 0000 0000 0000 0000
> 0000020 0069 0000 fc80 0005 0004 0004 011b 0000
> 0000030 0069 0000 fc80 0005 0000 0000 0000 0000
> 0000040 0069 0000 92db 0007 0004 0004 011b 0000
> 0000050 0069 0000 92db 0007 0000 0000 0000 0000
> 0000060 0069 0000 388b 0009 0004 0004 011b 0000
> 0000070 0069 0000 388b 0009 0000 0000 0000 0000
> 0000080 0069 0000 de72 000a 0004 0004 011b 0000
> 0000090 0069 0000 de72 000a 0000 0000 0000 0000

It's fine !

Thanks for testing !!

Neil
zelong dong Sept. 6, 2023, 10:37 a.m. UTC | #3
在 2023/9/1 16:02, Sean Young 写道:
> On Thu, Aug 31, 2023 at 08:13:22PM +0800, Zelong Dong wrote:
>> 在 2023/8/29 15:39, Sean Young 写道:
>>> On Fri, Aug 25, 2023 at 07:53:08PM +0800, zelong dong wrote:
>>>> From: Zelong Dong<zelong.dong@amlogic.com>
>>>>
>>>> Meson IR Controller supports hardware decoder in Meson-S4 and later
>>>> SoC. So far, protocol NEC could be decoded by hardware decoder.
>>> On Meson-S4, only the hardware decoder for NEC can be used using this
>>> driver. Does the Meson-S4 hardware support software decoding? If
>>> software decoding could be used, then any protocol could be supported,
>>> not just NEC.
>>>
>>> Also, out of interest, is there are documentation available for this
>>> hardware?
>>>
>>> Thanks,
>>>
>>> Sean
>>>
>> Yes, IR driver still supports SW decoding on Meson-S4. The decode mode could
>> be changed by 'support_hw_decoder'.
> 
> This requires changing the source code, it cannot be done at runtime.
> 
>> If IR Controller works in SW decoding, driver will be registered by
>> RC_DRIVER_IR_RAW and allows all protocol.
>> Otherwise, driver will be registered by RC_DRIVER_SCANCODE and only allows
>> NEC.
>>
>> 489         if (ir->rc->driver_type == RC_DRIVER_IR_RAW) {
>> 490                 ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
>> 491                 ir->rc->rx_resolution = MESON_RAW_TRATE;
>> 492                 ir->rc->min_timeout = 1;
>> 493                 ir->rc->timeout = IR_DEFAULT_TIMEOUT;
>> 494                 ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
>> 495         } else if (ir->rc->driver_type == RC_DRIVER_SCANCODE) {
>> 496                 ir->rc->allowed_protocols = RC_PROTO_BIT_NEC;
>> 497                 ir->rc->change_protocol = meson_ir_hw_decoder_init;
>> 498         }
> 
> There are other drivers too which can do hardware decoding and software
> decoding. Ideally we should have a mechanism to switch between them at
> runtime, but as-is rc-core does not provide for this.
Anything else I should update for this patchset?
> 
>> Do you get Meson-S4 datasheet? Please refer to chapter 13.5 Infrared Remote.
> 
> I did not get it, unfortunately. Any help would be appreciated, thanks.
Sorry, this document is not public and I don't have permission to share 
with you. Perhaps you could apply to xiaohong.zou@amlogic.com for 
Meson-S4 datasheet.
> 
> Other than that, the driver does look fine. Nothing to hold up merging for.
> I'll apply it when I can.
> 
> Thanks,
> 
> Sean
diff mbox series

Patch

diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
index 70322fab34ac..5303e6da5809 100644
--- a/drivers/media/rc/meson-ir.c
+++ b/drivers/media/rc/meson-ir.c
@@ -20,48 +20,196 @@ 
 
 #define DRIVER_NAME		"meson-ir"
 
-#define IR_DEC_LDR_ACTIVE	0x00
-#define IR_DEC_LDR_IDLE		0x04
-#define IR_DEC_LDR_REPEAT	0x08
-#define IR_DEC_BIT_0		0x0c
-#define IR_DEC_REG0		0x10
-#define IR_DEC_REG0_BASE_TIME	GENMASK(11, 0)
-#define IR_DEC_FRAME		0x14
-#define IR_DEC_STATUS		0x18
-#define IR_DEC_STATUS_PULSE	BIT(8)
-#define IR_DEC_REG1		0x1c
-#define IR_DEC_REG1_TIME_IV	GENMASK(28, 16)
-#define IR_DEC_REG1_ENABLE	BIT(15)
-#define IR_DEC_REG1_MODE	GENMASK(8, 7)
-#define IR_DEC_REG1_IRQSEL	GENMASK(3, 2)
-#define IR_DEC_REG1_RESET	BIT(0)
-/* The following regs are only available on Meson 8b and newer */
-#define IR_DEC_REG2		0x20
-#define IR_DEC_REG2_MODE	GENMASK(3, 0)
-
-#define DEC_MODE_NEC		0x0
-#define DEC_MODE_RAW		0x2
-
-#define IRQSEL_NEC_MODE		0
-#define IRQSEL_RISE_FALL	1
-#define IRQSEL_FALL		2
-#define IRQSEL_RISE		3
-
-#define MESON_RAW_TRATE		10	/* us */
-#define MESON_HW_TRATE		20	/* us */
+#define IR_DEC_LDR_ACTIVE			0x00
+#define IR_DEC_LDR_ACTIVE_MAX			GENMASK(28, 16)
+#define IR_DEC_LDR_ACTIVE_MIN			GENMASK(12, 0)
+#define IR_DEC_LDR_IDLE				0x04
+#define IR_DEC_LDR_IDLE_MAX			GENMASK(28, 16)
+#define IR_DEC_LDR_IDLE_MIN			GENMASK(12, 0)
+#define IR_DEC_LDR_REPEAT			0x08
+#define IR_DEC_LDR_REPEAT_MAX			GENMASK(25, 16)
+#define IR_DEC_LDR_REPEAT_MIN			GENMASK(9, 0)
+#define IR_DEC_BIT_0				0x0c
+#define IR_DEC_BIT_0_MAX			GENMASK(25, 16)
+#define IR_DEC_BIT_0_MIN			GENMASK(9, 0)
+#define IR_DEC_REG0				0x10
+#define IR_DEC_REG0_FILTER			GENMASK(30, 28)
+#define IR_DEC_REG0_FRAME_TIME_MAX		GENMASK(24, 12)
+#define IR_DEC_REG0_BASE_TIME			GENMASK(11, 0)
+#define IR_DEC_FRAME				0x14
+#define IR_DEC_STATUS				0x18
+#define IR_DEC_STATUS_BIT_1_ENABLE		BIT(30)
+#define IR_DEC_STATUS_BIT_1_MAX			GENMASK(29, 20)
+#define IR_DEC_STATUS_BIT_1_MIN			GENMASK(19, 10)
+#define IR_DEC_STATUS_PULSE			BIT(8)
+#define IR_DEC_STATUS_BUSY			BIT(7)
+#define IR_DEC_STATUS_FRAME_STATUS		GENMASK(3, 0)
+#define IR_DEC_REG1				0x1c
+#define IR_DEC_REG1_TIME_IV			GENMASK(28, 16)
+#define IR_DEC_REG1_FRAME_LEN			GENMASK(13, 8)
+#define IR_DEC_REG1_ENABLE			BIT(15)
+#define IR_DEC_REG1_HOLD_CODE			BIT(6)
+#define IR_DEC_REG1_IRQSEL			GENMASK(3, 2)
+#define IR_DEC_REG1_RESET			BIT(0)
+/* Meson 6b uses REG1 to configure IR mode */
+#define IR_DEC_REG1_MODE			GENMASK(8, 7)
+
+/* The following registers are only available on Meson 8b and newer */
+#define IR_DEC_REG2				0x20
+#define IR_DEC_REG2_TICK_MODE			BIT(15)
+#define IR_DEC_REG2_REPEAT_COUNTER		BIT(13)
+#define IR_DEC_REG2_REPEAT_TIME			BIT(12)
+#define IR_DEC_REG2_COMPARE_FRAME		BIT(11)
+#define IR_DEC_REG2_BIT_ORDER			BIT(8)
+/* Meson 8b / GXBB use REG2 to configure IR mode */
+#define IR_DEC_REG2_MODE			GENMASK(3, 0)
+#define IR_DEC_DURATN2				0x24
+#define IR_DEC_DURATN2_MAX			GENMASK(25, 16)
+#define IR_DEC_DURATN2_MIN			GENMASK(9, 0)
+#define IR_DEC_DURATN3				0x28
+#define IR_DEC_DURATN3_MAX			GENMASK(25, 16)
+#define IR_DEC_DURATN3_MIN			GENMASK(9, 0)
+#define IR_DEC_FRAME1				0x2c
+
+#define FRAME_MSB_FIRST				true
+#define FRAME_LSB_FIRST				false
+
+#define DEC_MODE_NEC				0x0
+#define DEC_MODE_RAW				0x2
+#define DEC_MODE_RC6				0x9
+#define DEC_MODE_XMP				0xE
+#define DEC_MODE_UNKNOW				0xFF
+
+#define DEC_STATUS_VALID			BIT(3)
+#define DEC_STATUS_DATA_CODE_ERR		BIT(2)
+#define DEC_STATUS_CUSTOM_CODE_ERR		BIT(1)
+#define DEC_STATUS_REPEAT			BIT(0)
+
+#define IRQSEL_DEC_MODE				0
+#define IRQSEL_RISE_FALL			1
+#define IRQSEL_FALL				2
+#define IRQSEL_RISE				3
+
+#define MESON_RAW_TRATE				10	/* us */
+#define MESON_HW_TRATE				20	/* us */
+
+/**
+ * struct meson_ir_protocol - describe IR Protocol parameter
+ *
+ * @hw_protocol: select IR Protocol from IR Controller
+ * @repeat_counter_enable: enable frame-to-frame time counter, it should work
+ *                         with @repeat_compare_enable to detect the repeat frame
+ * @repeat_check_enable: enable repeat time check for repeat detection
+ * @repeat_compare_enable: enable to compare frame for repeat frame detection.
+ *                         Some IR Protocol send the same data as repeat frame.
+ *                         In this case, it should work with
+ *                         @repeat_counter_enable to detect the repeat frame.
+ * @bit_order: bit order, LSB or MSB
+ * @bit1_match_enable: enable to check bit 1
+ * @hold_code_enable: hold frame code in register IR_DEC_FRAME1, the new one
+ *                    frame code will not be store in IR_DEC_FRAME1.
+ *                    until IR_DEC_FRAME1 has been read
+ * @count_tick_mode: increasing time unit of frame-to-frame time counter.
+ *                   0 = 100us, 1 = 10us
+ * @code_length: length (N-1) of data frame
+ * @frame_time_max: max time for whole frame. Unit: MESON_HW_TRATE
+ * @leader_active_max: max time for NEC/RC6 leader active part. Unit: MESON_HW_TRATE
+ * @leader_active_min: min time for NEC/RC6 leader active part. Unit: MESON_HW_TRATE
+ * @leader_idle_max: max time for NEC/RC6 leader idle part. Unit: MESON_HW_TRATE
+ * @leader_idle_min: min time for NEC/RC6 leader idle part. Unit: MESON_HW_TRATE
+ * @repeat_leader_max: max time for NEC repeat leader idle part. Unit: MESON_HW_TRATE
+ * @repeat_leader_min: min time for NEC repeat leader idle part. Unit: MESON_HW_TRATE
+ * @bit0_max: max time for NEC Logic '0', half of RC6 trailer bit, XMP Logic '00'
+ * @bit0_min: min time for NEC Logic '0', half of RC6 trailer bit, XMP Logic '00'
+ * @bit1_max: max time for NEC Logic '1', whole of RC6 trailer bit, XMP Logic '01'
+ * @bit1_min: min time for NEC Logic '1', whole of RC6 trailer bit, XMP Logic '01'
+ * @duration2_max: max time for half of RC6 normal bit, XMP Logic '10'
+ * @duration2_min: min time for half of RC6 normal bit, XMP Logic '10'
+ * @duration3_max: max time for whole of RC6 normal bit, XMP Logic '11'
+ * @duration3_min: min time for whole of RC6 normal bit, XMP Logic '11'
+ */
+
+struct meson_ir_protocol {
+	u8 hw_protocol;
+	bool repeat_counter_enable;
+	bool repeat_check_enable;
+	bool repeat_compare_enable;
+	bool bit_order;
+	bool bit1_match_enable;
+	bool hold_code_enable;
+	bool count_tick_mode;
+	u8 code_length;
+	u16 frame_time_max;
+	u16 leader_active_max;
+	u16 leader_active_min;
+	u16 leader_idle_max;
+	u16 leader_idle_min;
+	u16 repeat_leader_max;
+	u16 repeat_leader_min;
+	u16 bit0_max;
+	u16 bit0_min;
+	u16 bit1_max;
+	u16 bit1_min;
+	u16 duration2_max;
+	u16 duration2_min;
+	u16 duration3_max;
+	u16 duration3_min;
+};
+
+struct meson_ir_param {
+	bool support_hw_decoder;
+	unsigned int max_register;
+};
 
 struct meson_ir {
+	const struct meson_ir_param *param;
 	struct regmap	*reg;
 	struct rc_dev	*rc;
 	spinlock_t	lock;
 };
 
-static const struct regmap_config meson_ir_regmap_config = {
+static struct regmap_config meson_ir_regmap_config = {
 	.reg_bits = 32,
 	.val_bits = 32,
 	.reg_stride = 4,
 };
 
+static const struct meson_ir_protocol protocol_timings[] = {
+	/* protocol, repeat counter, repeat check, repeat compare, order */
+	{DEC_MODE_NEC, false, false, false, FRAME_LSB_FIRST,
+	/* bit 1 match, hold code, count tick, len, frame time */
+	true, false, false, 32, 4000,
+	/* leader active max/min, leader idle max/min, repeat leader max/min */
+	500, 400, 300, 200, 150, 80,
+	/* bit0 max/min, bit1 max/min, duration2 max/min, duration3 max/min */
+	72, 40, 134, 90, 0, 0, 0, 0}
+};
+
+static void meson_ir_nec_handler(struct meson_ir *ir)
+{
+	u32 code = 0;
+	u32 status = 0;
+	enum rc_proto proto;
+
+	regmap_read(ir->reg, IR_DEC_STATUS, &status);
+
+	if (status & DEC_STATUS_REPEAT) {
+		rc_repeat(ir->rc);
+	} else {
+		regmap_read(ir->reg, IR_DEC_FRAME, &code);
+
+		code = ir_nec_bytes_to_scancode(code, code >> 8,
+						code >> 16, code >> 24, &proto);
+		rc_keydown(ir->rc, proto, code, 0);
+	}
+}
+
+static void meson_ir_hw_handler(struct meson_ir *ir)
+{
+	if (ir->rc->enabled_protocols & RC_PROTO_BIT_NEC)
+		meson_ir_nec_handler(ir);
+}
+
 static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
 {
 	struct meson_ir *ir = dev_id;
@@ -70,22 +218,232 @@  static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
 
 	spin_lock(&ir->lock);
 
-	regmap_read(ir->reg, IR_DEC_REG1, &duration);
-	duration = FIELD_GET(IR_DEC_REG1_TIME_IV, duration);
-	rawir.duration = duration * MESON_RAW_TRATE;
-
 	regmap_read(ir->reg, IR_DEC_STATUS, &status);
-	rawir.pulse = !!(status & IR_DEC_STATUS_PULSE);
 
-	ir_raw_event_store_with_timeout(ir->rc, &rawir);
+	if (ir->rc->driver_type == RC_DRIVER_IR_RAW) {
+		rawir.pulse = !!(status & IR_DEC_STATUS_PULSE);
+
+		regmap_read(ir->reg, IR_DEC_REG1, &duration);
+		duration = FIELD_GET(IR_DEC_REG1_TIME_IV, duration);
+		rawir.duration = duration * MESON_RAW_TRATE;
+
+		ir_raw_event_store_with_timeout(ir->rc, &rawir);
+	} else if (ir->rc->driver_type == RC_DRIVER_SCANCODE) {
+		if (status & DEC_STATUS_VALID)
+			meson_ir_hw_handler(ir);
+	}
 
 	spin_unlock(&ir->lock);
 
 	return IRQ_HANDLED;
 }
 
+static int meson_ir_hw_decoder_init(struct rc_dev *dev, u64 *rc_type)
+{
+	u8 protocol;
+	u32 regval;
+	int i;
+	unsigned long flags;
+	const struct meson_ir_protocol *timings;
+	struct meson_ir *ir = dev->priv;
+
+	if (*rc_type & RC_PROTO_BIT_NEC)
+		protocol = DEC_MODE_NEC;
+	else
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(protocol_timings); i++)
+		if (protocol_timings[i].hw_protocol == protocol)
+			break;
+
+	if (i == ARRAY_SIZE(protocol_timings)) {
+		dev_err(&dev->dev, "hw protocol isn't supported: %d\n",
+			protocol);
+		return -EINVAL;
+	}
+	timings = &protocol_timings[i];
+
+	spin_lock_irqsave(&ir->lock, flags);
+
+	/* Clear controller status */
+	regmap_read(ir->reg, IR_DEC_STATUS, &regval);
+	regmap_read(ir->reg, IR_DEC_FRAME, &regval);
+
+	/* Reset ir decoder and disable decoder */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET,
+			   IR_DEC_REG1_RESET);
+
+	/* Base time resolution, (19+1)*1us=20us */
+	regval = FIELD_PREP(IR_DEC_REG0_BASE_TIME, MESON_HW_TRATE - 1);
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME, regval);
+
+	/* Monitor timing for input filter */
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_FILTER,
+			   FIELD_PREP(IR_DEC_REG0_FILTER, 7));
+
+	/* HW protocol */
+	regval = FIELD_PREP(IR_DEC_REG2_MODE, timings->hw_protocol);
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE, regval);
+
+	/* Hold frame data until register was read */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_HOLD_CODE,
+			   timings->hold_code_enable ?
+			   IR_DEC_REG1_HOLD_CODE : 0);
+
+	/* Bit order */
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_BIT_ORDER,
+			   timings->bit_order ? IR_DEC_REG2_BIT_ORDER : 0);
+
+	/* Select tick mode */
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_TICK_MODE,
+			   timings->count_tick_mode ?
+			   IR_DEC_REG2_TICK_MODE : 0);
+
+	/*
+	 * Some protocols transmit the same data frame as repeat frame
+	 * when the key is pressing. In this case, it could be detected as
+	 * repeat frame if the repeat checker was enabled.
+	 */
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_REPEAT_COUNTER,
+			   timings->repeat_counter_enable ?
+			   IR_DEC_REG2_REPEAT_COUNTER : 0);
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_REPEAT_TIME,
+			   timings->repeat_check_enable ?
+			   IR_DEC_REG2_REPEAT_TIME : 0);
+	regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_COMPARE_FRAME,
+			   timings->repeat_compare_enable ?
+			   IR_DEC_REG2_COMPARE_FRAME : 0);
+
+	/*
+	 * FRAME_TIME_MAX should be larger than the time between
+	 * data frame and repeat frame
+	 */
+	regval = FIELD_PREP(IR_DEC_REG0_FRAME_TIME_MAX,
+			    timings->frame_time_max);
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_FRAME_TIME_MAX,
+			   regval);
+
+	/* Length(N-1) of data frame */
+	regval = FIELD_PREP(IR_DEC_REG1_FRAME_LEN, timings->code_length - 1);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_FRAME_LEN, regval);
+
+	/* Time for leader active part */
+	regval = FIELD_PREP(IR_DEC_LDR_ACTIVE_MAX,
+			    timings->leader_active_max) |
+		 FIELD_PREP(IR_DEC_LDR_ACTIVE_MIN,
+			    timings->leader_active_min);
+	regmap_update_bits(ir->reg, IR_DEC_LDR_ACTIVE, IR_DEC_LDR_ACTIVE_MAX |
+			   IR_DEC_LDR_ACTIVE_MIN, regval);
+
+	/* Time for leader idle part */
+	regval = FIELD_PREP(IR_DEC_LDR_IDLE_MAX, timings->leader_idle_max) |
+		 FIELD_PREP(IR_DEC_LDR_IDLE_MIN, timings->leader_idle_min);
+	regmap_update_bits(ir->reg, IR_DEC_LDR_IDLE,
+			   IR_DEC_LDR_IDLE_MAX | IR_DEC_LDR_IDLE_MIN, regval);
+
+	/* Time for repeat leader idle part */
+	regval = FIELD_PREP(IR_DEC_LDR_REPEAT_MAX, timings->repeat_leader_max) |
+		 FIELD_PREP(IR_DEC_LDR_REPEAT_MIN, timings->repeat_leader_min);
+	regmap_update_bits(ir->reg, IR_DEC_LDR_REPEAT, IR_DEC_LDR_REPEAT_MAX |
+			   IR_DEC_LDR_REPEAT_MIN, regval);
+
+	/*
+	 * NEC: Time for logic '0'
+	 * RC6: Time for half of trailer bit
+	 */
+	regval = FIELD_PREP(IR_DEC_BIT_0_MAX, timings->bit0_max) |
+		 FIELD_PREP(IR_DEC_BIT_0_MIN, timings->bit0_min);
+	regmap_update_bits(ir->reg, IR_DEC_BIT_0,
+			   IR_DEC_BIT_0_MAX | IR_DEC_BIT_0_MIN, regval);
+
+	/*
+	 * NEC: Time for logic '1'
+	 * RC6: Time for whole of trailer bit
+	 */
+	regval = FIELD_PREP(IR_DEC_STATUS_BIT_1_MAX, timings->bit1_max) |
+		 FIELD_PREP(IR_DEC_STATUS_BIT_1_MIN, timings->bit1_min);
+	regmap_update_bits(ir->reg, IR_DEC_STATUS, IR_DEC_STATUS_BIT_1_MAX |
+			   IR_DEC_STATUS_BIT_1_MIN, regval);
+
+	/* Enable to match logic '1' */
+	regmap_update_bits(ir->reg, IR_DEC_STATUS, IR_DEC_STATUS_BIT_1_ENABLE,
+			   timings->bit1_match_enable ?
+			   IR_DEC_STATUS_BIT_1_ENABLE : 0);
+
+	/*
+	 * NEC: Unused
+	 * RC6: Time for halt of logic 0/1
+	 */
+	regval = FIELD_PREP(IR_DEC_DURATN2_MAX, timings->duration2_max) |
+		 FIELD_PREP(IR_DEC_DURATN2_MIN, timings->duration2_min);
+	regmap_update_bits(ir->reg, IR_DEC_DURATN2,
+			   IR_DEC_DURATN2_MAX | IR_DEC_DURATN2_MIN, regval);
+
+	/*
+	 * NEC: Unused
+	 * RC6: Time for whole logic 0/1
+	 */
+	regval = FIELD_PREP(IR_DEC_DURATN3_MAX, timings->duration3_max) |
+		 FIELD_PREP(IR_DEC_DURATN3_MIN, timings->duration3_min);
+	regmap_update_bits(ir->reg, IR_DEC_DURATN3,
+			   IR_DEC_DURATN3_MAX | IR_DEC_DURATN3_MIN, regval);
+
+	/* Reset ir decoder and enable decode */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET,
+			   IR_DEC_REG1_RESET);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET, 0);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE,
+			   IR_DEC_REG1_ENABLE);
+
+	spin_unlock_irqrestore(&ir->lock, flags);
+
+	dev_info(&dev->dev, "hw decoder init, protocol: %d\n", protocol);
+
+	return 0;
+}
+
+static void meson_ir_sw_decoder_init(struct rc_dev *dev)
+{
+	unsigned long flags;
+	struct meson_ir *ir = dev->priv;
+
+	spin_lock_irqsave(&ir->lock, flags);
+
+	/* Reset the decoder */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET,
+			   IR_DEC_REG1_RESET);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET, 0);
+
+	/* Set general operation mode (= raw/software decoding) */
+	if (of_device_is_compatible(dev->dev.of_node, "amlogic,meson6-ir"))
+		regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE,
+				   FIELD_PREP(IR_DEC_REG1_MODE,
+					      DEC_MODE_RAW));
+	else
+		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
+				   FIELD_PREP(IR_DEC_REG2_MODE,
+					      DEC_MODE_RAW));
+
+	/* Set rate */
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME,
+			   FIELD_PREP(IR_DEC_REG0_BASE_TIME,
+				      MESON_RAW_TRATE - 1));
+	/* IRQ on rising and falling edges */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_IRQSEL,
+			   FIELD_PREP(IR_DEC_REG1_IRQSEL, IRQSEL_RISE_FALL));
+	/* Enable the decoder */
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE,
+			   IR_DEC_REG1_ENABLE);
+
+	spin_unlock_irqrestore(&ir->lock, flags);
+
+	dev_info(&dev->dev, "sw decoder init\n");
+}
+
 static int meson_ir_probe(struct platform_device *pdev)
 {
+	const struct meson_ir_param *match_data;
 	struct device *dev = &pdev->dev;
 	struct device_node *node = dev->of_node;
 	void __iomem *res_start;
@@ -97,10 +455,17 @@  static int meson_ir_probe(struct platform_device *pdev)
 	if (!ir)
 		return -ENOMEM;
 
+	match_data = of_device_get_match_data(dev);
+	if (!match_data)
+		return dev_err_probe(dev, -ENODEV, "failed to get match data\n");
+
+	ir->param = match_data;
+
 	res_start = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(res_start))
 		return PTR_ERR(res_start);
 
+	meson_ir_regmap_config.max_register = ir->param->max_register;
 	ir->reg = devm_regmap_init_mmio(&pdev->dev, res_start,
 					&meson_ir_regmap_config);
 	if (IS_ERR(ir->reg))
@@ -110,23 +475,34 @@  static int meson_ir_probe(struct platform_device *pdev)
 	if (irq < 0)
 		return irq;
 
-	ir->rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW);
+	if (ir->param->support_hw_decoder)
+		ir->rc = devm_rc_allocate_device(&pdev->dev,
+						 RC_DRIVER_SCANCODE);
+	else
+		ir->rc = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW);
+
 	if (!ir->rc) {
 		dev_err(dev, "failed to allocate rc device\n");
 		return -ENOMEM;
 	}
 
+	if (ir->rc->driver_type == RC_DRIVER_IR_RAW) {
+		ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
+		ir->rc->rx_resolution = MESON_RAW_TRATE;
+		ir->rc->min_timeout = 1;
+		ir->rc->timeout = IR_DEFAULT_TIMEOUT;
+		ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
+	} else if (ir->rc->driver_type == RC_DRIVER_SCANCODE) {
+		ir->rc->allowed_protocols = RC_PROTO_BIT_NEC;
+		ir->rc->change_protocol = meson_ir_hw_decoder_init;
+	}
+
 	ir->rc->priv = ir;
 	ir->rc->device_name = DRIVER_NAME;
 	ir->rc->input_phys = DRIVER_NAME "/input0";
 	ir->rc->input_id.bustype = BUS_HOST;
 	map_name = of_get_property(node, "linux,rc-map-name", NULL);
 	ir->rc->map_name = map_name ? map_name : RC_MAP_EMPTY;
-	ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
-	ir->rc->rx_resolution = MESON_RAW_TRATE;
-	ir->rc->min_timeout = 1;
-	ir->rc->timeout = IR_DEFAULT_TIMEOUT;
-	ir->rc->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
 	ir->rc->driver_name = DRIVER_NAME;
 
 	spin_lock_init(&ir->lock);
@@ -138,36 +514,15 @@  static int meson_ir_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	ret = devm_request_irq(dev, irq, meson_ir_irq, 0, NULL, ir);
+	if (ir->rc->driver_type == RC_DRIVER_IR_RAW)
+		meson_ir_sw_decoder_init(ir->rc);
+
+	ret = devm_request_irq(dev, irq, meson_ir_irq, 0, "meson_ir", ir);
 	if (ret) {
 		dev_err(dev, "failed to request irq\n");
 		return ret;
 	}
 
-	/* Reset the decoder */
-	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET,
-			   IR_DEC_REG1_RESET);
-	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_RESET, 0);
-
-	/* Set general operation mode (= raw/software decoding) */
-	if (of_device_is_compatible(node, "amlogic,meson6-ir"))
-		regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE,
-				   FIELD_PREP(IR_DEC_REG1_MODE, DEC_MODE_RAW));
-	else
-		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
-				   FIELD_PREP(IR_DEC_REG2_MODE, DEC_MODE_RAW));
-
-	/* Set rate */
-	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME,
-			   FIELD_PREP(IR_DEC_REG0_BASE_TIME,
-				      MESON_RAW_TRATE - 1));
-	/* IRQ on rising and falling edges */
-	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_IRQSEL,
-			   FIELD_PREP(IR_DEC_REG1_IRQSEL, IRQSEL_RISE_FALL));
-	/* Enable the decoder */
-	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE,
-			   IR_DEC_REG1_ENABLE);
-
 	dev_info(dev, "receiver initialized\n");
 
 	return 0;
@@ -212,11 +567,36 @@  static void meson_ir_shutdown(struct platform_device *pdev)
 	spin_unlock_irqrestore(&ir->lock, flags);
 }
 
+static const struct meson_ir_param meson6_ir_param = {
+	.support_hw_decoder = false,
+	.max_register = IR_DEC_REG1,
+};
+
+static const struct meson_ir_param meson8b_ir_param = {
+	.support_hw_decoder = false,
+	.max_register = IR_DEC_REG2,
+};
+
+static const struct meson_ir_param meson_s4_ir_param = {
+	.support_hw_decoder = true,
+	.max_register = IR_DEC_FRAME1,
+};
+
 static const struct of_device_id meson_ir_match[] = {
-	{ .compatible = "amlogic,meson6-ir" },
-	{ .compatible = "amlogic,meson8b-ir" },
-	{ .compatible = "amlogic,meson-gxbb-ir" },
-	{ },
+	{
+		.compatible = "amlogic,meson6-ir",
+		.data = &meson6_ir_param,
+	}, {
+		.compatible = "amlogic,meson8b-ir",
+		.data = &meson8b_ir_param,
+	}, {
+		.compatible = "amlogic,meson-gxbb-ir",
+		.data = &meson8b_ir_param,
+	}, {
+		.compatible = "amlogic,meson-s4-ir",
+		.data = &meson_s4_ir_param,
+	},
+	{},
 };
 MODULE_DEVICE_TABLE(of, meson_ir_match);