diff mbox series

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

Message ID 20230302063402.42708-2-zelong.dong@amlogic.com (mailing list archive)
State New, archived
Headers show
Series media: rc: meson-s4: support RC_DRIVER_SCANCODE driver | expand

Commit Message

Zelong Dong March 2, 2023, 6:34 a.m. UTC
From: Zelong Dong <zelong.dong@amlogic.com>

Meson IR Controller supports hardware decoder in Meson-8B and later
SoC. So far, protocol NEC/RC-6/XMP could be decoded in hardware.
DTS property 'amlogic,ir-support-hw-decode' can enable this feature.

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

Comments

Neil Armstrong March 2, 2023, 9:27 a.m. UTC | #1
Hi,

On 02/03/2023 07:34, zelong dong wrote:
> From: Zelong Dong <zelong.dong@amlogic.com>
> 
> Meson IR Controller supports hardware decoder in Meson-8B and later
> SoC. So far, protocol NEC/RC-6/XMP could be decoded in hardware.
> DTS property 'amlogic,ir-support-hw-decode' can enable this feature.

Thamks for your submittion, it's appeciated !

But, The change is too hard to review since you migrate to regmap at the same
time, please separate the migration to regmap first, then add HW decoding
in a separate change.
Same for suspend/resume addition and any other hw-decoding unrelated changes.

Neil

> 
> Signed-off-by: Zelong Dong <zelong.dong@amlogic.com>
> ---
>   drivers/media/rc/meson-ir.c | 713 ++++++++++++++++++++++++++++++++----
>   1 file changed, 632 insertions(+), 81 deletions(-)
> 
> diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
> index 4b769111f78e..1bfdce1c1864 100644
> --- a/drivers/media/rc/meson-ir.c
> +++ b/drivers/media/rc/meson-ir.c
> @@ -14,6 +14,7 @@
>   #include <linux/platform_device.h>
>   #include <linux/spinlock.h>
>   #include <linux/bitfield.h>
> +#include <linux/regmap.h>
>   
>   #include <media/rc-core.h>
>   
> @@ -21,87 +22,598 @@
>   
>   /* valid on all Meson platforms */
>   #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
> -/* only available on Meson 8b and newer */
> +	#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 the 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 the 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 DECODE_MODE_NEC				0x0
> +#define DECODE_MODE_RAW				0x2
> +#define DECODE_MODE_RC6				0x9
> +#define DECODE_MODE_XMP				0xE
> +
> +#define DECODER_STATUS_VALID			BIT(3)
> +#define DECODER_STATUS_DATA_CODE_ERR		BIT(2)
> +#define DECODER_STATUS_CUSTOM_CODE_ERR		BIT(1)
> +#define DECODER_STATUS_REPEAT			BIT(0)
> +
> +#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 MESON_IR_TIMINGS(proto, r_cnt, r_chk, r_comp, b1_e, hc, cnt_tick, ori, \
> +			 flt, len, f_max, la_max, la_min, li_max, li_min,      \
> +			 rl_max, rl_min, b0_max, b0_min, b1_max, b1_min,       \
> +			 d2_max, d2_min, d3_max, d3_min)		\
> +	{								\
> +		.hw_protocol =			proto,			\
> +		.repeat_counter_enable =	r_cnt,			\
> +		.repeat_check_enable =		r_chk,			\
> +		.repeat_compare_enable =	r_comp,			\
> +		.bit1_match_enable =		b1_e,			\
> +		.hold_code_enable =		hc,			\
> +		.count_tick_mode =		cnt_tick,		\
> +		.bit_order =			ori,			\
> +		.filter_cnt =			flt,			\
> +		.code_length =			len,			\
> +		.frame_time_max =		f_max,			\
> +		.leader_active_max =		la_max,			\
> +		.leader_active_min =		la_min,			\
> +		.leader_idle_max =		li_max,			\
> +		.leader_idle_min =		li_min,			\
> +		.repeat_leader_max =		rl_max,			\
> +		.repeat_leader_min =		rl_min,			\
> +		.bit0_max =			b0_max,			\
> +		.bit0_min =			b0_min,			\
> +		.bit1_max =			b1_max,			\
> +		.bit1_min =			b1_min,			\
> +		.duration2_max =		d2_max,			\
> +		.duration2_min =		d2_min,			\
> +		.duration3_max =		d3_max,			\
> +		.duration3_min =		d3_min,			\
> +	}								\
> +
> +/**
> + * struct meson_ir_param - 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.
> + * @filter_cnt: input filter, to filter burr
> + * @code_length: length (N-1) of frame's data part.
> + * @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'.
> + */
>   
> -#define REG0_RATE_MASK		GENMASK(11, 0)
> +struct meson_ir_param {
> +	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		filter_cnt;
> +	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;
> +};
>   
> -#define DECODE_MODE_NEC		0x0
> -#define DECODE_MODE_RAW		0x2
> +struct meson_ir {
> +	struct regmap	*reg;
> +	struct rc_dev	*rc;
> +	spinlock_t	lock;
> +	bool		support_hw_dec;
> +};
>   
> -/* Meson 6b uses REG1 to configure the mode */
> -#define REG1_MODE_MASK		GENMASK(8, 7)
> -#define REG1_MODE_SHIFT		7
> +static struct regmap_config meson_ir_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
>   
> -/* Meson 8b / GXBB use REG2 to configure the mode */
> -#define REG2_MODE_MASK		GENMASK(3, 0)
> -#define REG2_MODE_SHIFT		0
> +static const struct meson_ir_param protocol_timings[] = {
> +	/* protocol, repeat counter, repeat check, repeat compare, bit 1 match */
> +	MESON_IR_TIMINGS(DECODE_MODE_NEC, false, false, false, true,
> +	/* hold code, count tick, order, filter cnt, len, frame time */
> +			 false, false, FRAME_LSB_FIRST, 7, 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),
> +	MESON_IR_TIMINGS(DECODE_MODE_XMP, true, false, true, false,
> +			 false, true, FRAME_MSB_FIRST, 7, 32, 1500,
> +			 0, 0, 0, 0, 0, 0,
> +			 52, 45, 86, 80, 121, 114, 7, 7),
> +	MESON_IR_TIMINGS(DECODE_MODE_RC6, true, false, true, false,
> +			 true, false, FRAME_MSB_FIRST, 7, 37, 4000,
> +			 210, 125, 50, 38, 145, 125,
> +			 51, 38, 94, 82, 28, 16, 51, 38)
> +};
>   
> -#define REG1_TIME_IV_MASK	GENMASK(28, 16)
> +static void meson_ir_rc6_handler(struct meson_ir *ir)
> +{
> +	u32 code0, code1;
>   
> -#define REG1_IRQSEL_MASK	GENMASK(3, 2)
> -#define REG1_IRQSEL_NEC_MODE	0
> -#define REG1_IRQSEL_RISE_FALL	1
> -#define REG1_IRQSEL_FALL	2
> -#define REG1_IRQSEL_RISE	3
> +	regmap_read(ir->reg, IR_DEC_FRAME, &code0);
> +	regmap_read(ir->reg, IR_DEC_FRAME1, &code1);
>   
> -#define REG1_RESET		BIT(0)
> -#define REG1_ENABLE		BIT(15)
> +	rc_keydown(ir->rc, RC_PROTO_RC6_6A_32, code0, code1 & 0x1);
> +}
>   
> -#define STATUS_IR_DEC_IN	BIT(8)
> +static void meson_ir_xmp_handler(struct meson_ir *ir)
> +{
> +	static u32 last_xmp_code;
> +	int i;
> +	u32 code = 0;
> +	u32 scancode, checksum = 0;
> +	u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2;
> +
> +	regmap_read(ir->reg, IR_DEC_FRAME, &code);
> +
> +	for (i = 0; i < 32; i += 4)
> +		checksum += ((code >> i) & 0xf);
> +	checksum = ~(checksum + 0xf - ((code >> 24) & 0xf)) & 0xf;
> +
> +	if (checksum != ((code >> 24) & 0xf)) {
> +		last_xmp_code = 0;
> +		dev_err(&ir->rc->dev, "xmp checksum error, framecode= 0x%x\n",
> +			code);
> +		return;
> +	}
>   
> -#define MESON_TRATE		10	/* us */
> +	subaddr  = (last_xmp_code >> 24 & 0xf0) | (last_xmp_code >> 20 & 0x0f);
> +	subaddr2 = (code >> 24 & 0xf0) | (code >> 16 & 0x0f);
> +	oem      = last_xmp_code >> 8;
> +	addr     = last_xmp_code;
> +	toggle   = code >> 20 & 0xf;
> +	obc1 = code >> 8;
> +	obc2 = code;
> +
> +	if (subaddr != subaddr2) {
> +		last_xmp_code = code;
> +		dev_dbg(&ir->rc->dev, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
> +			subaddr, subaddr2);
> +		return;
> +	}
> +	if (oem != 0x44)
> +		dev_dbg(&ir->rc->dev, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
> +			oem);
>   
> -struct meson_ir {
> -	void __iomem	*reg;
> -	struct rc_dev	*rc;
> -	spinlock_t	lock;
> -};
> +	scancode = addr << 24 | subaddr << 16 | obc1 << 8 | obc2;
> +	dev_dbg(&ir->rc->dev, "XMP scancode 0x%06x\n", scancode);
> +
> +	if (toggle == 0)
> +		rc_keydown(ir->rc, RC_PROTO_XMP, scancode, 0);
> +	else
> +		rc_repeat(ir->rc);
> +
> +	last_xmp_code = code;
> +}
>   
> -static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
> -			      u32 mask, u32 value)
> +static void meson_ir_nec_handler(struct meson_ir *ir)
>   {
> -	u32 data;
> +	u32 code = 0;
> +	u32 status = 0;
> +	enum rc_proto proto;
> +
> +	regmap_read(ir->reg, IR_DEC_STATUS, &status);
> +
> +	if (status & DECODER_STATUS_REPEAT) {
> +		rc_repeat(ir->rc);
> +	} else {
> +		regmap_read(ir->reg, IR_DEC_FRAME, &code);
>   
> -	data = readl(ir->reg + reg);
> -	data &= ~mask;
> -	data |= (value & mask);
> -	writel(data, ir->reg + reg);
> +		code = ir_nec_bytes_to_scancode(code, code >> 8,
> +						code >> 16, code >> 24, &proto);
> +		rc_keydown(ir->rc, proto, code, 0);
> +	}
>   }
>   
>   static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
> +{
> +	struct meson_ir *ir = dev_id;
> +	u32 status = 0;
> +
> +	if (ir->support_hw_dec) {
> +		regmap_read(ir->reg, IR_DEC_STATUS, &status);
> +
> +		if (!(status & DECODER_STATUS_VALID))
> +			return IRQ_NONE;
> +	}
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +
> +static irqreturn_t meson_ir_irq_thread(int irq, void *dev_id)
>   {
>   	struct meson_ir *ir = dev_id;
>   	u32 duration, status;
>   	struct ir_raw_event rawir = {};
>   
> -	spin_lock(&ir->lock);
> +	if (ir->support_hw_dec) {
> +		if (ir->rc->enabled_protocols & RC_PROTO_BIT_NEC)
> +			meson_ir_nec_handler(ir);
> +		else if (ir->rc->enabled_protocols & RC_PROTO_BIT_XMP)
> +			meson_ir_xmp_handler(ir);
> +		else if (ir->rc->enabled_protocols & RC_PROTO_BIT_RC6_6A_32)
> +			meson_ir_rc6_handler(ir);
> +	} else {
> +		spin_lock(&ir->lock);
>   
> -	duration = readl_relaxed(ir->reg + IR_DEC_REG1);
> -	duration = FIELD_GET(REG1_TIME_IV_MASK, duration);
> -	rawir.duration = duration * MESON_TRATE;
> +		regmap_read(ir->reg, IR_DEC_REG1, &duration);
> +		duration = FIELD_GET(IR_DEC_REG1_TIME_IV, duration);
> +		rawir.duration = duration * MESON_RAW_TRATE;
>   
> -	status = readl_relaxed(ir->reg + IR_DEC_STATUS);
> -	rawir.pulse = !!(status & STATUS_IR_DEC_IN);
> +		regmap_read(ir->reg, IR_DEC_STATUS, &status);
> +		rawir.pulse = !!(status & IR_DEC_STATUS_PULSE);
>   
> -	ir_raw_event_store_with_timeout(ir->rc, &rawir);
> +		ir_raw_event_store_with_timeout(ir->rc, &rawir);
>   
> -	spin_unlock(&ir->lock);
> +		spin_unlock(&ir->lock);
> +	}
>   
>   	return IRQ_HANDLED;
>   }
>   
> +static int meson_ir_change_hw_protocol(struct rc_dev *dev, u8 protocol)
> +{
> +	struct meson_ir *ir = dev->priv;
> +	int i;
> +	unsigned long flags;
> +	u32 regval;
> +	const struct meson_ir_param *timings;
> +
> +	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);
> +
> +	/* 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);
> +
> +	/* Monitor timing for input filter */
> +	regval = FIELD_PREP(IR_DEC_REG0_FILTER, timings->filter_cnt);
> +	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_FILTER, 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 IR formats transer 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 check 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 large than the time between
> +	 * data frame and repeat code
> +	 */
> +	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 frame data */
> +	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
> +	 * RC5/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
> +	 * RC5/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);
> +
> +	spin_unlock_irqrestore(&ir->lock, flags);
> +
> +	return 0;
> +}
> +
> +static void meson_ir_hw_decoder_init(struct rc_dev *dev)
> +{
> +	u32 regval;
> +	unsigned long flags;
> +	struct meson_ir *ir = dev->priv;
> +
> +	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);
> +
> +	spin_unlock_irqrestore(&ir->lock, flags);
> +}
> +
> +static int meson_ir_change_protocol(struct rc_dev *dev, u64 *rc_type)
> +{
> +	unsigned long flags;
> +	struct meson_ir *ir = dev->priv;
> +
> +	meson_ir_hw_decoder_init(dev);
> +
> +	if (*rc_type & RC_PROTO_BIT_NEC)
> +		meson_ir_change_hw_protocol(dev, DECODE_MODE_NEC);
> +	else if (*rc_type & RC_PROTO_BIT_XMP)
> +		meson_ir_change_hw_protocol(dev, DECODE_MODE_XMP);
> +	else if (*rc_type & RC_PROTO_BIT_RC6_6A_32)
> +		meson_ir_change_hw_protocol(dev, DECODE_MODE_RC6);
> +
> +	spin_lock_irqsave(&ir->lock, flags);
> +
> +	/* 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);
> +
> +	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,
> +					      DECODE_MODE_RAW));
> +	else
> +		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
> +				   FIELD_PREP(IR_DEC_REG2_MODE,
> +					      DECODE_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);
> +}
> +
> +static int meson_ir_rc_allocate_device(struct platform_device *pdev)
> +{
> +	struct meson_ir *ir = platform_get_drvdata(pdev);
> +
> +	if (ir->support_hw_dec) {
> +		ir->rc = devm_rc_allocate_device(&pdev->dev,
> +						 RC_DRIVER_SCANCODE);
> +		if (!ir->rc) {
> +			dev_err(&pdev->dev, "failed to allocate rc device\n");
> +			return -ENOMEM;
> +		}
> +
> +		ir->rc->allowed_protocols = RC_PROTO_BIT_NEC |
> +					    RC_PROTO_BIT_RC6_6A_32 |
> +					    RC_PROTO_BIT_XMP;
> +		ir->rc->change_protocol = meson_ir_change_protocol;
> +	} else {
> +		ir->rc = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW);
> +		if (!ir->rc) {
> +			dev_err(&pdev->dev, "failed to allocate rc device\n");
> +			return -ENOMEM;
> +		}
> +
> +		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;
> +	}
> +
> +	return 0;
> +}
> +
>   static int meson_ir_probe(struct platform_device *pdev)
>   {
>   	struct device *dev = &pdev->dev;
>   	struct device_node *node = dev->of_node;
> +	struct resource *res;
> +	void __iomem *res_start;
>   	const char *map_name;
>   	struct meson_ir *ir;
>   	int irq, ret;
> @@ -110,7 +622,19 @@ static int meson_ir_probe(struct platform_device *pdev)
>   	if (!ir)
>   		return -ENOMEM;
>   
> -	ir->reg = devm_platform_ioremap_resource(pdev, 0);
> +	platform_set_drvdata(pdev, ir);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (IS_ERR_OR_NULL(res)) {
> +		dev_err(&pdev->dev, "get mem resource error, %ld\n",
> +			PTR_ERR(res));
> +		return PTR_ERR(res);
> +	}
> +
> +	res_start = devm_ioremap_resource(&pdev->dev, res);
> +	meson_ir_regmap_config.max_register = resource_size(res) - 4;
> +	ir->reg = devm_regmap_init_mmio(&pdev->dev, res_start,
> +					&meson_ir_regmap_config);
>   	if (IS_ERR(ir->reg))
>   		return PTR_ERR(ir->reg);
>   
> @@ -118,27 +642,28 @@ 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->rc) {
> -		dev_err(dev, "failed to allocate rc device\n");
> -		return -ENOMEM;
> +	if (of_device_is_compatible(node, "amlogic,meson6-ir")) {
> +		ir->support_hw_dec = false;
> +	} else {
> +		if (of_property_read_bool(node,
> +					  "amlogic,ir-support-hw-decode"))
> +			ir->support_hw_dec = true;
> +		else
> +			ir->support_hw_dec = false;
>   	}
>   
> +	if (meson_ir_rc_allocate_device(pdev))
> +		return -ENOMEM;
> +
>   	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_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);
> -	platform_set_drvdata(pdev, ir);
>   
>   	ret = devm_rc_register_device(dev, ir->rc);
>   	if (ret) {
> @@ -146,33 +671,20 @@ 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->support_hw_dec)
> +		meson_ir_sw_decoder_init(ir->rc);
> +
> +	ret = devm_request_threaded_irq(dev, irq, meson_ir_irq,
> +					meson_ir_irq_thread,
> +					IRQF_SHARED | IRQF_NO_SUSPEND,
> +					"meson_ir", ir);
>   	if (ret) {
>   		dev_err(dev, "failed to request irq\n");
>   		return ret;
>   	}
>   
> -	/* Reset the decoder */
> -	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
> -	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
> -
> -	/* Set general operation mode (= raw/software decoding) */
> -	if (of_device_is_compatible(node, "amlogic,meson6-ir"))
> -		meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
> -				  FIELD_PREP(REG1_MODE_MASK, DECODE_MODE_RAW));
> -	else
> -		meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
> -				  FIELD_PREP(REG2_MODE_MASK, DECODE_MODE_RAW));
> -
> -	/* Set rate */
> -	meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
> -	/* IRQ on rising and falling edges */
> -	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
> -			  FIELD_PREP(REG1_IRQSEL_MASK, REG1_IRQSEL_RISE_FALL));
> -	/* Enable the decoder */
> -	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
> -
> -	dev_info(dev, "receiver initialized\n");
> +	dev_info(dev, "meson ir %s decoder was initialized\n",
> +		 ir->support_hw_dec ? "hw" : "sw");
>   
>   	return 0;
>   }
> @@ -184,7 +696,7 @@ static int meson_ir_remove(struct platform_device *pdev)
>   
>   	/* Disable the decoder */
>   	spin_lock_irqsave(&ir->lock, flags);
> -	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
> +	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
>   	spin_unlock_irqrestore(&ir->lock, flags);
>   
>   	return 0;
> @@ -193,7 +705,6 @@ static int meson_ir_remove(struct platform_device *pdev)
>   static void meson_ir_shutdown(struct platform_device *pdev)
>   {
>   	struct device *dev = &pdev->dev;
> -	struct device_node *node = dev->of_node;
>   	struct meson_ir *ir = platform_get_drvdata(pdev);
>   	unsigned long flags;
>   
> @@ -203,27 +714,64 @@ static void meson_ir_shutdown(struct platform_device *pdev)
>   	 * Set operation mode to NEC/hardware decoding to give
>   	 * bootloader a chance to power the system back on
>   	 */
> -	if (of_device_is_compatible(node, "amlogic,meson6-ir"))
> -		meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
> -				  DECODE_MODE_NEC << REG1_MODE_SHIFT);
> +	if (of_device_is_compatible(dev->of_node, "amlogic,meson6-ir"))
> +		regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE,
> +				   FIELD_PREP(IR_DEC_REG1_MODE,
> +					      DECODE_MODE_NEC));
>   	else
> -		meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
> -				  DECODE_MODE_NEC << REG2_MODE_SHIFT);
> +		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
> +				   FIELD_PREP(IR_DEC_REG2_MODE,
> +					      DECODE_MODE_NEC));
>   
>   	/* Set rate to default value */
> -	meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, 0x13);
> +	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME,
> +			   FIELD_PREP(IR_DEC_REG0_BASE_TIME, MESON_HW_TRATE));
>   
>   	spin_unlock_irqrestore(&ir->lock, flags);
>   }
>   
> +#ifdef CONFIG_PM
> +static int meson_ir_resume(struct device *dev)
> +{
> +	struct meson_ir *ir = dev_get_drvdata(dev);
> +
> +	if (ir->support_hw_dec)
> +		meson_ir_change_protocol(ir->rc, &ir->rc->enabled_protocols);
> +	else
> +		meson_ir_sw_decoder_init(ir->rc);
> +
> +	return 0;
> +}
> +
> +static int meson_ir_suspend(struct device *dev)
> +{
> +	struct meson_ir *ir = dev_get_drvdata(dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ir->lock, flags);
> +	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
> +	spin_unlock_irqrestore(&ir->lock, flags);
> +
> +	return 0;
> +}
> +#endif
> +
>   static const struct of_device_id meson_ir_match[] = {
>   	{ .compatible = "amlogic,meson6-ir" },
>   	{ .compatible = "amlogic,meson8b-ir" },
>   	{ .compatible = "amlogic,meson-gxbb-ir" },
> +	{ .compatible = "amlogic,meson-s4-ir" },
>   	{ },
>   };
>   MODULE_DEVICE_TABLE(of, meson_ir_match);
>   
> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops meson_ir_pm_ops = {
> +	.suspend_late = meson_ir_suspend,
> +	.resume_early = meson_ir_resume,
> +};
> +#endif
> +
>   static struct platform_driver meson_ir_driver = {
>   	.probe		= meson_ir_probe,
>   	.remove		= meson_ir_remove,
> @@ -231,6 +779,9 @@ static struct platform_driver meson_ir_driver = {
>   	.driver = {
>   		.name		= DRIVER_NAME,
>   		.of_match_table	= meson_ir_match,
> +#ifdef CONFIG_PM
> +		.pm = &meson_ir_pm_ops,
> +#endif
>   	},
>   };
>
Dmitry Rokosov March 3, 2023, 1:37 p.m. UTC | #2
Hello Zelong,

On Thu, Mar 02, 2023 at 02:34:00PM +0800, zelong dong wrote:
> From: Zelong Dong <zelong.dong@amlogic.com>
> 
> Meson IR Controller supports hardware decoder in Meson-8B and later
> SoC. So far, protocol NEC/RC-6/XMP could be decoded in hardware.
> DTS property 'amlogic,ir-support-hw-decode' can enable this feature.
> 
> Signed-off-by: Zelong Dong <zelong.dong@amlogic.com>
> ---
>  drivers/media/rc/meson-ir.c | 713 ++++++++++++++++++++++++++++++++----
>  1 file changed, 632 insertions(+), 81 deletions(-)
> 
> diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
> index 4b769111f78e..1bfdce1c1864 100644
> --- a/drivers/media/rc/meson-ir.c
> +++ b/drivers/media/rc/meson-ir.c
> @@ -14,6 +14,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/spinlock.h>
>  #include <linux/bitfield.h>
> +#include <linux/regmap.h>
>  
>  #include <media/rc-core.h>
>  
> @@ -21,87 +22,598 @@
>  
>  /* valid on all Meson platforms */
>  #define IR_DEC_LDR_ACTIVE	0x00
> +	#define	IR_DEC_LDR_ACTIVE_MAX		GENMASK(28, 16)
> +	#define	IR_DEC_LDR_ACTIVE_MIN		GENMASK(12, 0)

Extra tabs before #define statement. The same problem is located in
the whole patchset.

[...]

> +#define MESON_IR_TIMINGS(proto, r_cnt, r_chk, r_comp, b1_e, hc, cnt_tick, ori, \
> +			 flt, len, f_max, la_max, la_min, li_max, li_min,      \
> +			 rl_max, rl_min, b0_max, b0_min, b1_max, b1_min,       \
> +			 d2_max, d2_min, d3_max, d3_min)		\
> +	{								\
> +		.hw_protocol =			proto,			\
> +		.repeat_counter_enable =	r_cnt,			\
> +		.repeat_check_enable =		r_chk,			\
> +		.repeat_compare_enable =	r_comp,			\
> +		.bit1_match_enable =		b1_e,			\
> +		.hold_code_enable =		hc,			\
> +		.count_tick_mode =		cnt_tick,		\
> +		.bit_order =			ori,			\
> +		.filter_cnt =			flt,			\
> +		.code_length =			len,			\
> +		.frame_time_max =		f_max,			\
> +		.leader_active_max =		la_max,			\
> +		.leader_active_min =		la_min,			\
> +		.leader_idle_max =		li_max,			\
> +		.leader_idle_min =		li_min,			\
> +		.repeat_leader_max =		rl_max,			\
> +		.repeat_leader_min =		rl_min,			\
> +		.bit0_max =			b0_max,			\
> +		.bit0_min =			b0_min,			\
> +		.bit1_max =			b1_max,			\
> +		.bit1_min =			b1_min,			\
> +		.duration2_max =		d2_max,			\
> +		.duration2_min =		d2_min,			\
> +		.duration3_max =		d3_max,			\
> +		.duration3_min =		d3_min,			\
> +	}								\

Extra tabs for back slash alignment

> +
> +/**
> + * struct meson_ir_param - 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.
> + * @filter_cnt: input filter, to filter burr
> + * @code_length: length (N-1) of frame's data part.
> + * @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'.
> + */
>  

Did you run checkpatch and kernel-doc checker for above struct
documentation?

> -#define REG0_RATE_MASK		GENMASK(11, 0)
> +struct meson_ir_param {
> +	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		filter_cnt;
> +	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;
> +};

Why do you need tab alignment between type and variable?

[...]

> +#ifdef CONFIG_PM
> +static int meson_ir_resume(struct device *dev)
> +{
> +	struct meson_ir *ir = dev_get_drvdata(dev);
> +
> +	if (ir->support_hw_dec)
> +		meson_ir_change_protocol(ir->rc, &ir->rc->enabled_protocols);
> +	else
> +		meson_ir_sw_decoder_init(ir->rc);
> +
> +	return 0;
> +}
> +
> +static int meson_ir_suspend(struct device *dev)
> +{
> +	struct meson_ir *ir = dev_get_drvdata(dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ir->lock, flags);
> +	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
> +	spin_unlock_irqrestore(&ir->lock, flags);
> +
> +	return 0;
> +}
> +#endif
> +
>  static const struct of_device_id meson_ir_match[] = {
>  	{ .compatible = "amlogic,meson6-ir" },
>  	{ .compatible = "amlogic,meson8b-ir" },
>  	{ .compatible = "amlogic,meson-gxbb-ir" },
> +	{ .compatible = "amlogic,meson-s4-ir" },
>  	{ },
>  };
>  MODULE_DEVICE_TABLE(of, meson_ir_match);
>  
> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops meson_ir_pm_ops = {
> +	.suspend_late = meson_ir_suspend,
> +	.resume_early = meson_ir_resume,
> +};
> +#endif
> +
>  static struct platform_driver meson_ir_driver = {
>  	.probe		= meson_ir_probe,
>  	.remove		= meson_ir_remove,
> @@ -231,6 +779,9 @@ static struct platform_driver meson_ir_driver = {
>  	.driver = {
>  		.name		= DRIVER_NAME,
>  		.of_match_table	= meson_ir_match,
> +#ifdef CONFIG_PM
> +		.pm = &meson_ir_pm_ops,
> +#endif

You can use pm_ptr() and DEFINE_SIMPLE_DEV_PM_OPS instead of checking of
CONFIG_PM definition.

[...]
diff mbox series

Patch

diff --git a/drivers/media/rc/meson-ir.c b/drivers/media/rc/meson-ir.c
index 4b769111f78e..1bfdce1c1864 100644
--- a/drivers/media/rc/meson-ir.c
+++ b/drivers/media/rc/meson-ir.c
@@ -14,6 +14,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/bitfield.h>
+#include <linux/regmap.h>
 
 #include <media/rc-core.h>
 
@@ -21,87 +22,598 @@ 
 
 /* valid on all Meson platforms */
 #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
-/* only available on Meson 8b and newer */
+	#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 the 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 the 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 DECODE_MODE_NEC				0x0
+#define DECODE_MODE_RAW				0x2
+#define DECODE_MODE_RC6				0x9
+#define DECODE_MODE_XMP				0xE
+
+#define DECODER_STATUS_VALID			BIT(3)
+#define DECODER_STATUS_DATA_CODE_ERR		BIT(2)
+#define DECODER_STATUS_CUSTOM_CODE_ERR		BIT(1)
+#define DECODER_STATUS_REPEAT			BIT(0)
+
+#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 MESON_IR_TIMINGS(proto, r_cnt, r_chk, r_comp, b1_e, hc, cnt_tick, ori, \
+			 flt, len, f_max, la_max, la_min, li_max, li_min,      \
+			 rl_max, rl_min, b0_max, b0_min, b1_max, b1_min,       \
+			 d2_max, d2_min, d3_max, d3_min)		\
+	{								\
+		.hw_protocol =			proto,			\
+		.repeat_counter_enable =	r_cnt,			\
+		.repeat_check_enable =		r_chk,			\
+		.repeat_compare_enable =	r_comp,			\
+		.bit1_match_enable =		b1_e,			\
+		.hold_code_enable =		hc,			\
+		.count_tick_mode =		cnt_tick,		\
+		.bit_order =			ori,			\
+		.filter_cnt =			flt,			\
+		.code_length =			len,			\
+		.frame_time_max =		f_max,			\
+		.leader_active_max =		la_max,			\
+		.leader_active_min =		la_min,			\
+		.leader_idle_max =		li_max,			\
+		.leader_idle_min =		li_min,			\
+		.repeat_leader_max =		rl_max,			\
+		.repeat_leader_min =		rl_min,			\
+		.bit0_max =			b0_max,			\
+		.bit0_min =			b0_min,			\
+		.bit1_max =			b1_max,			\
+		.bit1_min =			b1_min,			\
+		.duration2_max =		d2_max,			\
+		.duration2_min =		d2_min,			\
+		.duration3_max =		d3_max,			\
+		.duration3_min =		d3_min,			\
+	}								\
+
+/**
+ * struct meson_ir_param - 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.
+ * @filter_cnt: input filter, to filter burr
+ * @code_length: length (N-1) of frame's data part.
+ * @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'.
+ */
 
-#define REG0_RATE_MASK		GENMASK(11, 0)
+struct meson_ir_param {
+	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		filter_cnt;
+	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;
+};
 
-#define DECODE_MODE_NEC		0x0
-#define DECODE_MODE_RAW		0x2
+struct meson_ir {
+	struct regmap	*reg;
+	struct rc_dev	*rc;
+	spinlock_t	lock;
+	bool		support_hw_dec;
+};
 
-/* Meson 6b uses REG1 to configure the mode */
-#define REG1_MODE_MASK		GENMASK(8, 7)
-#define REG1_MODE_SHIFT		7
+static struct regmap_config meson_ir_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
 
-/* Meson 8b / GXBB use REG2 to configure the mode */
-#define REG2_MODE_MASK		GENMASK(3, 0)
-#define REG2_MODE_SHIFT		0
+static const struct meson_ir_param protocol_timings[] = {
+	/* protocol, repeat counter, repeat check, repeat compare, bit 1 match */
+	MESON_IR_TIMINGS(DECODE_MODE_NEC, false, false, false, true,
+	/* hold code, count tick, order, filter cnt, len, frame time */
+			 false, false, FRAME_LSB_FIRST, 7, 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),
+	MESON_IR_TIMINGS(DECODE_MODE_XMP, true, false, true, false,
+			 false, true, FRAME_MSB_FIRST, 7, 32, 1500,
+			 0, 0, 0, 0, 0, 0,
+			 52, 45, 86, 80, 121, 114, 7, 7),
+	MESON_IR_TIMINGS(DECODE_MODE_RC6, true, false, true, false,
+			 true, false, FRAME_MSB_FIRST, 7, 37, 4000,
+			 210, 125, 50, 38, 145, 125,
+			 51, 38, 94, 82, 28, 16, 51, 38)
+};
 
-#define REG1_TIME_IV_MASK	GENMASK(28, 16)
+static void meson_ir_rc6_handler(struct meson_ir *ir)
+{
+	u32 code0, code1;
 
-#define REG1_IRQSEL_MASK	GENMASK(3, 2)
-#define REG1_IRQSEL_NEC_MODE	0
-#define REG1_IRQSEL_RISE_FALL	1
-#define REG1_IRQSEL_FALL	2
-#define REG1_IRQSEL_RISE	3
+	regmap_read(ir->reg, IR_DEC_FRAME, &code0);
+	regmap_read(ir->reg, IR_DEC_FRAME1, &code1);
 
-#define REG1_RESET		BIT(0)
-#define REG1_ENABLE		BIT(15)
+	rc_keydown(ir->rc, RC_PROTO_RC6_6A_32, code0, code1 & 0x1);
+}
 
-#define STATUS_IR_DEC_IN	BIT(8)
+static void meson_ir_xmp_handler(struct meson_ir *ir)
+{
+	static u32 last_xmp_code;
+	int i;
+	u32 code = 0;
+	u32 scancode, checksum = 0;
+	u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2;
+
+	regmap_read(ir->reg, IR_DEC_FRAME, &code);
+
+	for (i = 0; i < 32; i += 4)
+		checksum += ((code >> i) & 0xf);
+	checksum = ~(checksum + 0xf - ((code >> 24) & 0xf)) & 0xf;
+
+	if (checksum != ((code >> 24) & 0xf)) {
+		last_xmp_code = 0;
+		dev_err(&ir->rc->dev, "xmp checksum error, framecode= 0x%x\n",
+			code);
+		return;
+	}
 
-#define MESON_TRATE		10	/* us */
+	subaddr  = (last_xmp_code >> 24 & 0xf0) | (last_xmp_code >> 20 & 0x0f);
+	subaddr2 = (code >> 24 & 0xf0) | (code >> 16 & 0x0f);
+	oem      = last_xmp_code >> 8;
+	addr     = last_xmp_code;
+	toggle   = code >> 20 & 0xf;
+	obc1 = code >> 8;
+	obc2 = code;
+
+	if (subaddr != subaddr2) {
+		last_xmp_code = code;
+		dev_dbg(&ir->rc->dev, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
+			subaddr, subaddr2);
+		return;
+	}
+	if (oem != 0x44)
+		dev_dbg(&ir->rc->dev, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
+			oem);
 
-struct meson_ir {
-	void __iomem	*reg;
-	struct rc_dev	*rc;
-	spinlock_t	lock;
-};
+	scancode = addr << 24 | subaddr << 16 | obc1 << 8 | obc2;
+	dev_dbg(&ir->rc->dev, "XMP scancode 0x%06x\n", scancode);
+
+	if (toggle == 0)
+		rc_keydown(ir->rc, RC_PROTO_XMP, scancode, 0);
+	else
+		rc_repeat(ir->rc);
+
+	last_xmp_code = code;
+}
 
-static void meson_ir_set_mask(struct meson_ir *ir, unsigned int reg,
-			      u32 mask, u32 value)
+static void meson_ir_nec_handler(struct meson_ir *ir)
 {
-	u32 data;
+	u32 code = 0;
+	u32 status = 0;
+	enum rc_proto proto;
+
+	regmap_read(ir->reg, IR_DEC_STATUS, &status);
+
+	if (status & DECODER_STATUS_REPEAT) {
+		rc_repeat(ir->rc);
+	} else {
+		regmap_read(ir->reg, IR_DEC_FRAME, &code);
 
-	data = readl(ir->reg + reg);
-	data &= ~mask;
-	data |= (value & mask);
-	writel(data, ir->reg + reg);
+		code = ir_nec_bytes_to_scancode(code, code >> 8,
+						code >> 16, code >> 24, &proto);
+		rc_keydown(ir->rc, proto, code, 0);
+	}
 }
 
 static irqreturn_t meson_ir_irq(int irqno, void *dev_id)
+{
+	struct meson_ir *ir = dev_id;
+	u32 status = 0;
+
+	if (ir->support_hw_dec) {
+		regmap_read(ir->reg, IR_DEC_STATUS, &status);
+
+		if (!(status & DECODER_STATUS_VALID))
+			return IRQ_NONE;
+	}
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t meson_ir_irq_thread(int irq, void *dev_id)
 {
 	struct meson_ir *ir = dev_id;
 	u32 duration, status;
 	struct ir_raw_event rawir = {};
 
-	spin_lock(&ir->lock);
+	if (ir->support_hw_dec) {
+		if (ir->rc->enabled_protocols & RC_PROTO_BIT_NEC)
+			meson_ir_nec_handler(ir);
+		else if (ir->rc->enabled_protocols & RC_PROTO_BIT_XMP)
+			meson_ir_xmp_handler(ir);
+		else if (ir->rc->enabled_protocols & RC_PROTO_BIT_RC6_6A_32)
+			meson_ir_rc6_handler(ir);
+	} else {
+		spin_lock(&ir->lock);
 
-	duration = readl_relaxed(ir->reg + IR_DEC_REG1);
-	duration = FIELD_GET(REG1_TIME_IV_MASK, duration);
-	rawir.duration = duration * MESON_TRATE;
+		regmap_read(ir->reg, IR_DEC_REG1, &duration);
+		duration = FIELD_GET(IR_DEC_REG1_TIME_IV, duration);
+		rawir.duration = duration * MESON_RAW_TRATE;
 
-	status = readl_relaxed(ir->reg + IR_DEC_STATUS);
-	rawir.pulse = !!(status & STATUS_IR_DEC_IN);
+		regmap_read(ir->reg, IR_DEC_STATUS, &status);
+		rawir.pulse = !!(status & IR_DEC_STATUS_PULSE);
 
-	ir_raw_event_store_with_timeout(ir->rc, &rawir);
+		ir_raw_event_store_with_timeout(ir->rc, &rawir);
 
-	spin_unlock(&ir->lock);
+		spin_unlock(&ir->lock);
+	}
 
 	return IRQ_HANDLED;
 }
 
+static int meson_ir_change_hw_protocol(struct rc_dev *dev, u8 protocol)
+{
+	struct meson_ir *ir = dev->priv;
+	int i;
+	unsigned long flags;
+	u32 regval;
+	const struct meson_ir_param *timings;
+
+	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);
+
+	/* 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);
+
+	/* Monitor timing for input filter */
+	regval = FIELD_PREP(IR_DEC_REG0_FILTER, timings->filter_cnt);
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_FILTER, 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 IR formats transer 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 check 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 large than the time between
+	 * data frame and repeat code
+	 */
+	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 frame data */
+	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
+	 * RC5/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
+	 * RC5/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);
+
+	spin_unlock_irqrestore(&ir->lock, flags);
+
+	return 0;
+}
+
+static void meson_ir_hw_decoder_init(struct rc_dev *dev)
+{
+	u32 regval;
+	unsigned long flags;
+	struct meson_ir *ir = dev->priv;
+
+	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);
+
+	spin_unlock_irqrestore(&ir->lock, flags);
+}
+
+static int meson_ir_change_protocol(struct rc_dev *dev, u64 *rc_type)
+{
+	unsigned long flags;
+	struct meson_ir *ir = dev->priv;
+
+	meson_ir_hw_decoder_init(dev);
+
+	if (*rc_type & RC_PROTO_BIT_NEC)
+		meson_ir_change_hw_protocol(dev, DECODE_MODE_NEC);
+	else if (*rc_type & RC_PROTO_BIT_XMP)
+		meson_ir_change_hw_protocol(dev, DECODE_MODE_XMP);
+	else if (*rc_type & RC_PROTO_BIT_RC6_6A_32)
+		meson_ir_change_hw_protocol(dev, DECODE_MODE_RC6);
+
+	spin_lock_irqsave(&ir->lock, flags);
+
+	/* 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);
+
+	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,
+					      DECODE_MODE_RAW));
+	else
+		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
+				   FIELD_PREP(IR_DEC_REG2_MODE,
+					      DECODE_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);
+}
+
+static int meson_ir_rc_allocate_device(struct platform_device *pdev)
+{
+	struct meson_ir *ir = platform_get_drvdata(pdev);
+
+	if (ir->support_hw_dec) {
+		ir->rc = devm_rc_allocate_device(&pdev->dev,
+						 RC_DRIVER_SCANCODE);
+		if (!ir->rc) {
+			dev_err(&pdev->dev, "failed to allocate rc device\n");
+			return -ENOMEM;
+		}
+
+		ir->rc->allowed_protocols = RC_PROTO_BIT_NEC |
+					    RC_PROTO_BIT_RC6_6A_32 |
+					    RC_PROTO_BIT_XMP;
+		ir->rc->change_protocol = meson_ir_change_protocol;
+	} else {
+		ir->rc = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW);
+		if (!ir->rc) {
+			dev_err(&pdev->dev, "failed to allocate rc device\n");
+			return -ENOMEM;
+		}
+
+		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;
+	}
+
+	return 0;
+}
+
 static int meson_ir_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct device_node *node = dev->of_node;
+	struct resource *res;
+	void __iomem *res_start;
 	const char *map_name;
 	struct meson_ir *ir;
 	int irq, ret;
@@ -110,7 +622,19 @@  static int meson_ir_probe(struct platform_device *pdev)
 	if (!ir)
 		return -ENOMEM;
 
-	ir->reg = devm_platform_ioremap_resource(pdev, 0);
+	platform_set_drvdata(pdev, ir);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (IS_ERR_OR_NULL(res)) {
+		dev_err(&pdev->dev, "get mem resource error, %ld\n",
+			PTR_ERR(res));
+		return PTR_ERR(res);
+	}
+
+	res_start = devm_ioremap_resource(&pdev->dev, res);
+	meson_ir_regmap_config.max_register = resource_size(res) - 4;
+	ir->reg = devm_regmap_init_mmio(&pdev->dev, res_start,
+					&meson_ir_regmap_config);
 	if (IS_ERR(ir->reg))
 		return PTR_ERR(ir->reg);
 
@@ -118,27 +642,28 @@  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->rc) {
-		dev_err(dev, "failed to allocate rc device\n");
-		return -ENOMEM;
+	if (of_device_is_compatible(node, "amlogic,meson6-ir")) {
+		ir->support_hw_dec = false;
+	} else {
+		if (of_property_read_bool(node,
+					  "amlogic,ir-support-hw-decode"))
+			ir->support_hw_dec = true;
+		else
+			ir->support_hw_dec = false;
 	}
 
+	if (meson_ir_rc_allocate_device(pdev))
+		return -ENOMEM;
+
 	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_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);
-	platform_set_drvdata(pdev, ir);
 
 	ret = devm_rc_register_device(dev, ir->rc);
 	if (ret) {
@@ -146,33 +671,20 @@  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->support_hw_dec)
+		meson_ir_sw_decoder_init(ir->rc);
+
+	ret = devm_request_threaded_irq(dev, irq, meson_ir_irq,
+					meson_ir_irq_thread,
+					IRQF_SHARED | IRQF_NO_SUSPEND,
+					"meson_ir", ir);
 	if (ret) {
 		dev_err(dev, "failed to request irq\n");
 		return ret;
 	}
 
-	/* Reset the decoder */
-	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, REG1_RESET);
-	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_RESET, 0);
-
-	/* Set general operation mode (= raw/software decoding) */
-	if (of_device_is_compatible(node, "amlogic,meson6-ir"))
-		meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
-				  FIELD_PREP(REG1_MODE_MASK, DECODE_MODE_RAW));
-	else
-		meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
-				  FIELD_PREP(REG2_MODE_MASK, DECODE_MODE_RAW));
-
-	/* Set rate */
-	meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, MESON_TRATE - 1);
-	/* IRQ on rising and falling edges */
-	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_IRQSEL_MASK,
-			  FIELD_PREP(REG1_IRQSEL_MASK, REG1_IRQSEL_RISE_FALL));
-	/* Enable the decoder */
-	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, REG1_ENABLE);
-
-	dev_info(dev, "receiver initialized\n");
+	dev_info(dev, "meson ir %s decoder was initialized\n",
+		 ir->support_hw_dec ? "hw" : "sw");
 
 	return 0;
 }
@@ -184,7 +696,7 @@  static int meson_ir_remove(struct platform_device *pdev)
 
 	/* Disable the decoder */
 	spin_lock_irqsave(&ir->lock, flags);
-	meson_ir_set_mask(ir, IR_DEC_REG1, REG1_ENABLE, 0);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
 	spin_unlock_irqrestore(&ir->lock, flags);
 
 	return 0;
@@ -193,7 +705,6 @@  static int meson_ir_remove(struct platform_device *pdev)
 static void meson_ir_shutdown(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct device_node *node = dev->of_node;
 	struct meson_ir *ir = platform_get_drvdata(pdev);
 	unsigned long flags;
 
@@ -203,27 +714,64 @@  static void meson_ir_shutdown(struct platform_device *pdev)
 	 * Set operation mode to NEC/hardware decoding to give
 	 * bootloader a chance to power the system back on
 	 */
-	if (of_device_is_compatible(node, "amlogic,meson6-ir"))
-		meson_ir_set_mask(ir, IR_DEC_REG1, REG1_MODE_MASK,
-				  DECODE_MODE_NEC << REG1_MODE_SHIFT);
+	if (of_device_is_compatible(dev->of_node, "amlogic,meson6-ir"))
+		regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_MODE,
+				   FIELD_PREP(IR_DEC_REG1_MODE,
+					      DECODE_MODE_NEC));
 	else
-		meson_ir_set_mask(ir, IR_DEC_REG2, REG2_MODE_MASK,
-				  DECODE_MODE_NEC << REG2_MODE_SHIFT);
+		regmap_update_bits(ir->reg, IR_DEC_REG2, IR_DEC_REG2_MODE,
+				   FIELD_PREP(IR_DEC_REG2_MODE,
+					      DECODE_MODE_NEC));
 
 	/* Set rate to default value */
-	meson_ir_set_mask(ir, IR_DEC_REG0, REG0_RATE_MASK, 0x13);
+	regmap_update_bits(ir->reg, IR_DEC_REG0, IR_DEC_REG0_BASE_TIME,
+			   FIELD_PREP(IR_DEC_REG0_BASE_TIME, MESON_HW_TRATE));
 
 	spin_unlock_irqrestore(&ir->lock, flags);
 }
 
+#ifdef CONFIG_PM
+static int meson_ir_resume(struct device *dev)
+{
+	struct meson_ir *ir = dev_get_drvdata(dev);
+
+	if (ir->support_hw_dec)
+		meson_ir_change_protocol(ir->rc, &ir->rc->enabled_protocols);
+	else
+		meson_ir_sw_decoder_init(ir->rc);
+
+	return 0;
+}
+
+static int meson_ir_suspend(struct device *dev)
+{
+	struct meson_ir *ir = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ir->lock, flags);
+	regmap_update_bits(ir->reg, IR_DEC_REG1, IR_DEC_REG1_ENABLE, 0);
+	spin_unlock_irqrestore(&ir->lock, flags);
+
+	return 0;
+}
+#endif
+
 static const struct of_device_id meson_ir_match[] = {
 	{ .compatible = "amlogic,meson6-ir" },
 	{ .compatible = "amlogic,meson8b-ir" },
 	{ .compatible = "amlogic,meson-gxbb-ir" },
+	{ .compatible = "amlogic,meson-s4-ir" },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, meson_ir_match);
 
+#ifdef CONFIG_PM
+static const struct dev_pm_ops meson_ir_pm_ops = {
+	.suspend_late = meson_ir_suspend,
+	.resume_early = meson_ir_resume,
+};
+#endif
+
 static struct platform_driver meson_ir_driver = {
 	.probe		= meson_ir_probe,
 	.remove		= meson_ir_remove,
@@ -231,6 +779,9 @@  static struct platform_driver meson_ir_driver = {
 	.driver = {
 		.name		= DRIVER_NAME,
 		.of_match_table	= meson_ir_match,
+#ifdef CONFIG_PM
+		.pm = &meson_ir_pm_ops,
+#endif
 	},
 };