162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ImgTec IR Hardware Decoder found in PowerDown Controller.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2010-2014 Imagination Technologies Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This ties into the input subsystem using the RC-core. Protocol support is
862306a36Sopenharmony_ci * provided in separate modules which provide the parameters and scancode
962306a36Sopenharmony_ci * translation functions to set up the hardware decoder and interpret the
1062306a36Sopenharmony_ci * resulting input.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/bitops.h>
1462306a36Sopenharmony_ci#include <linux/clk.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/timer.h>
1862306a36Sopenharmony_ci#include <media/rc-core.h>
1962306a36Sopenharmony_ci#include "img-ir.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* Decoders lock (only modified to preprocess them) */
2262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(img_ir_decoders_lock);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic bool img_ir_decoders_preprocessed;
2562306a36Sopenharmony_cistatic struct img_ir_decoder *img_ir_decoders[] = {
2662306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_NEC
2762306a36Sopenharmony_ci	&img_ir_nec,
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_JVC
3062306a36Sopenharmony_ci	&img_ir_jvc,
3162306a36Sopenharmony_ci#endif
3262306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_SONY
3362306a36Sopenharmony_ci	&img_ir_sony,
3462306a36Sopenharmony_ci#endif
3562306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_SHARP
3662306a36Sopenharmony_ci	&img_ir_sharp,
3762306a36Sopenharmony_ci#endif
3862306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_SANYO
3962306a36Sopenharmony_ci	&img_ir_sanyo,
4062306a36Sopenharmony_ci#endif
4162306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_RC5
4262306a36Sopenharmony_ci	&img_ir_rc5,
4362306a36Sopenharmony_ci#endif
4462306a36Sopenharmony_ci#ifdef CONFIG_IR_IMG_RC6
4562306a36Sopenharmony_ci	&img_ir_rc6,
4662306a36Sopenharmony_ci#endif
4762306a36Sopenharmony_ci	NULL
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define IMG_IR_F_FILTER		BIT(RC_FILTER_NORMAL)	/* enable filtering */
5162306a36Sopenharmony_ci#define IMG_IR_F_WAKE		BIT(RC_FILTER_WAKEUP)	/* enable waking */
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* code type quirks */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define IMG_IR_QUIRK_CODE_BROKEN	0x1	/* Decode is broken */
5662306a36Sopenharmony_ci#define IMG_IR_QUIRK_CODE_LEN_INCR	0x2	/* Bit length needs increment */
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * The decoder generates rapid interrupts without actually having
5962306a36Sopenharmony_ci * received any new data after an incomplete IR code is decoded.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci#define IMG_IR_QUIRK_CODE_IRQ		0x4
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* functions for preprocessing timings, ensuring max is set */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic void img_ir_timing_preprocess(struct img_ir_timing_range *range,
6662306a36Sopenharmony_ci				     unsigned int unit)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	if (range->max < range->min)
6962306a36Sopenharmony_ci		range->max = range->min;
7062306a36Sopenharmony_ci	if (unit) {
7162306a36Sopenharmony_ci		/* multiply by unit and convert to microseconds */
7262306a36Sopenharmony_ci		range->min = (range->min*unit)/1000;
7362306a36Sopenharmony_ci		range->max = (range->max*unit + 999)/1000; /* round up */
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing,
7862306a36Sopenharmony_ci					    unsigned int unit)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	img_ir_timing_preprocess(&timing->pulse, unit);
8162306a36Sopenharmony_ci	img_ir_timing_preprocess(&timing->space, unit);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void img_ir_timings_preprocess(struct img_ir_timings *timings,
8562306a36Sopenharmony_ci				      unsigned int unit)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	img_ir_symbol_timing_preprocess(&timings->ldr, unit);
8862306a36Sopenharmony_ci	img_ir_symbol_timing_preprocess(&timings->s00, unit);
8962306a36Sopenharmony_ci	img_ir_symbol_timing_preprocess(&timings->s01, unit);
9062306a36Sopenharmony_ci	img_ir_symbol_timing_preprocess(&timings->s10, unit);
9162306a36Sopenharmony_ci	img_ir_symbol_timing_preprocess(&timings->s11, unit);
9262306a36Sopenharmony_ci	/* default s10 and s11 to s00 and s01 if no leader */
9362306a36Sopenharmony_ci	if (unit)
9462306a36Sopenharmony_ci		/* multiply by unit and convert to microseconds (round up) */
9562306a36Sopenharmony_ci		timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* functions for filling empty fields with defaults */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void img_ir_timing_defaults(struct img_ir_timing_range *range,
10162306a36Sopenharmony_ci				   struct img_ir_timing_range *defaults)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	if (!range->min)
10462306a36Sopenharmony_ci		range->min = defaults->min;
10562306a36Sopenharmony_ci	if (!range->max)
10662306a36Sopenharmony_ci		range->max = defaults->max;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing,
11062306a36Sopenharmony_ci					  struct img_ir_symbol_timing *defaults)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	img_ir_timing_defaults(&timing->pulse, &defaults->pulse);
11362306a36Sopenharmony_ci	img_ir_timing_defaults(&timing->space, &defaults->space);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void img_ir_timings_defaults(struct img_ir_timings *timings,
11762306a36Sopenharmony_ci				    struct img_ir_timings *defaults)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr);
12062306a36Sopenharmony_ci	img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00);
12162306a36Sopenharmony_ci	img_ir_symbol_timing_defaults(&timings->s01, &defaults->s01);
12262306a36Sopenharmony_ci	img_ir_symbol_timing_defaults(&timings->s10, &defaults->s10);
12362306a36Sopenharmony_ci	img_ir_symbol_timing_defaults(&timings->s11, &defaults->s11);
12462306a36Sopenharmony_ci	if (!timings->ft.ft_min)
12562306a36Sopenharmony_ci		timings->ft.ft_min = defaults->ft.ft_min;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* functions for converting timings to register values */
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/**
13162306a36Sopenharmony_ci * img_ir_control() - Convert control struct to control register value.
13262306a36Sopenharmony_ci * @control:	Control data
13362306a36Sopenharmony_ci *
13462306a36Sopenharmony_ci * Returns:	The control register value equivalent of @control.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic u32 img_ir_control(const struct img_ir_control *control)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	u32 ctrl = control->code_type << IMG_IR_CODETYPE_SHIFT;
13962306a36Sopenharmony_ci	if (control->decoden)
14062306a36Sopenharmony_ci		ctrl |= IMG_IR_DECODEN;
14162306a36Sopenharmony_ci	if (control->hdrtog)
14262306a36Sopenharmony_ci		ctrl |= IMG_IR_HDRTOG;
14362306a36Sopenharmony_ci	if (control->ldrdec)
14462306a36Sopenharmony_ci		ctrl |= IMG_IR_LDRDEC;
14562306a36Sopenharmony_ci	if (control->decodinpol)
14662306a36Sopenharmony_ci		ctrl |= IMG_IR_DECODINPOL;
14762306a36Sopenharmony_ci	if (control->bitorien)
14862306a36Sopenharmony_ci		ctrl |= IMG_IR_BITORIEN;
14962306a36Sopenharmony_ci	if (control->d1validsel)
15062306a36Sopenharmony_ci		ctrl |= IMG_IR_D1VALIDSEL;
15162306a36Sopenharmony_ci	if (control->bitinv)
15262306a36Sopenharmony_ci		ctrl |= IMG_IR_BITINV;
15362306a36Sopenharmony_ci	if (control->decodend2)
15462306a36Sopenharmony_ci		ctrl |= IMG_IR_DECODEND2;
15562306a36Sopenharmony_ci	if (control->bitoriend2)
15662306a36Sopenharmony_ci		ctrl |= IMG_IR_BITORIEND2;
15762306a36Sopenharmony_ci	if (control->bitinvd2)
15862306a36Sopenharmony_ci		ctrl |= IMG_IR_BITINVD2;
15962306a36Sopenharmony_ci	return ctrl;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/**
16362306a36Sopenharmony_ci * img_ir_timing_range_convert() - Convert microsecond range.
16462306a36Sopenharmony_ci * @out:	Output timing range in clock cycles with a shift.
16562306a36Sopenharmony_ci * @in:		Input timing range in microseconds.
16662306a36Sopenharmony_ci * @tolerance:	Tolerance as a fraction of 128 (roughly percent).
16762306a36Sopenharmony_ci * @clock_hz:	IR clock rate in Hz.
16862306a36Sopenharmony_ci * @shift:	Shift of output units.
16962306a36Sopenharmony_ci *
17062306a36Sopenharmony_ci * Converts min and max from microseconds to IR clock cycles, applies a
17162306a36Sopenharmony_ci * tolerance, and shifts for the register, rounding in the right direction.
17262306a36Sopenharmony_ci * Note that in and out can safely be the same object.
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_cistatic void img_ir_timing_range_convert(struct img_ir_timing_range *out,
17562306a36Sopenharmony_ci					const struct img_ir_timing_range *in,
17662306a36Sopenharmony_ci					unsigned int tolerance,
17762306a36Sopenharmony_ci					unsigned long clock_hz,
17862306a36Sopenharmony_ci					unsigned int shift)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	unsigned int min = in->min;
18162306a36Sopenharmony_ci	unsigned int max = in->max;
18262306a36Sopenharmony_ci	/* add a tolerance */
18362306a36Sopenharmony_ci	min = min - (min*tolerance >> 7);
18462306a36Sopenharmony_ci	max = max + (max*tolerance >> 7);
18562306a36Sopenharmony_ci	/* convert from microseconds into clock cycles */
18662306a36Sopenharmony_ci	min = min*clock_hz / 1000000;
18762306a36Sopenharmony_ci	max = (max*clock_hz + 999999) / 1000000; /* round up */
18862306a36Sopenharmony_ci	/* apply shift and copy to output */
18962306a36Sopenharmony_ci	out->min = min >> shift;
19062306a36Sopenharmony_ci	out->max = (max + ((1 << shift) - 1)) >> shift; /* round up */
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/**
19462306a36Sopenharmony_ci * img_ir_symbol_timing() - Convert symbol timing struct to register value.
19562306a36Sopenharmony_ci * @timing:	Symbol timing data
19662306a36Sopenharmony_ci * @tolerance:	Timing tolerance where 0-128 represents 0-100%
19762306a36Sopenharmony_ci * @clock_hz:	Frequency of source clock in Hz
19862306a36Sopenharmony_ci * @pd_shift:	Shift to apply to symbol period
19962306a36Sopenharmony_ci * @w_shift:	Shift to apply to symbol width
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci * Returns:	Symbol timing register value based on arguments.
20262306a36Sopenharmony_ci */
20362306a36Sopenharmony_cistatic u32 img_ir_symbol_timing(const struct img_ir_symbol_timing *timing,
20462306a36Sopenharmony_ci				unsigned int tolerance,
20562306a36Sopenharmony_ci				unsigned long clock_hz,
20662306a36Sopenharmony_ci				unsigned int pd_shift,
20762306a36Sopenharmony_ci				unsigned int w_shift)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	struct img_ir_timing_range hw_pulse, hw_period;
21062306a36Sopenharmony_ci	/* we calculate period in hw_period, then convert in place */
21162306a36Sopenharmony_ci	hw_period.min = timing->pulse.min + timing->space.min;
21262306a36Sopenharmony_ci	hw_period.max = timing->pulse.max + timing->space.max;
21362306a36Sopenharmony_ci	img_ir_timing_range_convert(&hw_period, &hw_period,
21462306a36Sopenharmony_ci			tolerance, clock_hz, pd_shift);
21562306a36Sopenharmony_ci	img_ir_timing_range_convert(&hw_pulse, &timing->pulse,
21662306a36Sopenharmony_ci			tolerance, clock_hz, w_shift);
21762306a36Sopenharmony_ci	/* construct register value */
21862306a36Sopenharmony_ci	return	(hw_period.max	<< IMG_IR_PD_MAX_SHIFT)	|
21962306a36Sopenharmony_ci		(hw_period.min	<< IMG_IR_PD_MIN_SHIFT)	|
22062306a36Sopenharmony_ci		(hw_pulse.max	<< IMG_IR_W_MAX_SHIFT)	|
22162306a36Sopenharmony_ci		(hw_pulse.min	<< IMG_IR_W_MIN_SHIFT);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci/**
22562306a36Sopenharmony_ci * img_ir_free_timing() - Convert free time timing struct to register value.
22662306a36Sopenharmony_ci * @timing:	Free symbol timing data
22762306a36Sopenharmony_ci * @clock_hz:	Source clock frequency in Hz
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * Returns:	Free symbol timing register value.
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic u32 img_ir_free_timing(const struct img_ir_free_timing *timing,
23262306a36Sopenharmony_ci			      unsigned long clock_hz)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	unsigned int minlen, maxlen, ft_min;
23562306a36Sopenharmony_ci	/* minlen is only 5 bits, and round minlen to multiple of 2 */
23662306a36Sopenharmony_ci	if (timing->minlen < 30)
23762306a36Sopenharmony_ci		minlen = timing->minlen & -2;
23862306a36Sopenharmony_ci	else
23962306a36Sopenharmony_ci		minlen = 30;
24062306a36Sopenharmony_ci	/* maxlen has maximum value of 48, and round maxlen to multiple of 2 */
24162306a36Sopenharmony_ci	if (timing->maxlen < 48)
24262306a36Sopenharmony_ci		maxlen = (timing->maxlen + 1) & -2;
24362306a36Sopenharmony_ci	else
24462306a36Sopenharmony_ci		maxlen = 48;
24562306a36Sopenharmony_ci	/* convert and shift ft_min, rounding upwards */
24662306a36Sopenharmony_ci	ft_min = (timing->ft_min*clock_hz + 999999) / 1000000;
24762306a36Sopenharmony_ci	ft_min = (ft_min + 7) >> 3;
24862306a36Sopenharmony_ci	/* construct register value */
24962306a36Sopenharmony_ci	return	(maxlen << IMG_IR_MAXLEN_SHIFT)	|
25062306a36Sopenharmony_ci		(minlen << IMG_IR_MINLEN_SHIFT)	|
25162306a36Sopenharmony_ci		(ft_min << IMG_IR_FT_MIN_SHIFT);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * img_ir_free_timing_dynamic() - Update free time register value.
25662306a36Sopenharmony_ci * @st_ft:	Static free time register value from img_ir_free_timing.
25762306a36Sopenharmony_ci * @filter:	Current filter which may additionally restrict min/max len.
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Returns:	Updated free time register value based on the current filter.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic u32 img_ir_free_timing_dynamic(u32 st_ft, struct img_ir_filter *filter)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	unsigned int minlen, maxlen, newminlen, newmaxlen;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* round minlen, maxlen to multiple of 2 */
26662306a36Sopenharmony_ci	newminlen = filter->minlen & -2;
26762306a36Sopenharmony_ci	newmaxlen = (filter->maxlen + 1) & -2;
26862306a36Sopenharmony_ci	/* extract min/max len from register */
26962306a36Sopenharmony_ci	minlen = (st_ft & IMG_IR_MINLEN) >> IMG_IR_MINLEN_SHIFT;
27062306a36Sopenharmony_ci	maxlen = (st_ft & IMG_IR_MAXLEN) >> IMG_IR_MAXLEN_SHIFT;
27162306a36Sopenharmony_ci	/* if the new values are more restrictive, update the register value */
27262306a36Sopenharmony_ci	if (newminlen > minlen) {
27362306a36Sopenharmony_ci		st_ft &= ~IMG_IR_MINLEN;
27462306a36Sopenharmony_ci		st_ft |= newminlen << IMG_IR_MINLEN_SHIFT;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	if (newmaxlen < maxlen) {
27762306a36Sopenharmony_ci		st_ft &= ~IMG_IR_MAXLEN;
27862306a36Sopenharmony_ci		st_ft |= newmaxlen << IMG_IR_MAXLEN_SHIFT;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci	return st_ft;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/**
28462306a36Sopenharmony_ci * img_ir_timings_convert() - Convert timings to register values
28562306a36Sopenharmony_ci * @regs:	Output timing register values
28662306a36Sopenharmony_ci * @timings:	Input timing data
28762306a36Sopenharmony_ci * @tolerance:	Timing tolerance where 0-128 represents 0-100%
28862306a36Sopenharmony_ci * @clock_hz:	Source clock frequency in Hz
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_cistatic void img_ir_timings_convert(struct img_ir_timing_regvals *regs,
29162306a36Sopenharmony_ci				   const struct img_ir_timings *timings,
29262306a36Sopenharmony_ci				   unsigned int tolerance,
29362306a36Sopenharmony_ci				   unsigned int clock_hz)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	/* leader symbol timings are divided by 16 */
29662306a36Sopenharmony_ci	regs->ldr = img_ir_symbol_timing(&timings->ldr, tolerance, clock_hz,
29762306a36Sopenharmony_ci			4, 4);
29862306a36Sopenharmony_ci	/* other symbol timings, pd fields only are divided by 2 */
29962306a36Sopenharmony_ci	regs->s00 = img_ir_symbol_timing(&timings->s00, tolerance, clock_hz,
30062306a36Sopenharmony_ci			1, 0);
30162306a36Sopenharmony_ci	regs->s01 = img_ir_symbol_timing(&timings->s01, tolerance, clock_hz,
30262306a36Sopenharmony_ci			1, 0);
30362306a36Sopenharmony_ci	regs->s10 = img_ir_symbol_timing(&timings->s10, tolerance, clock_hz,
30462306a36Sopenharmony_ci			1, 0);
30562306a36Sopenharmony_ci	regs->s11 = img_ir_symbol_timing(&timings->s11, tolerance, clock_hz,
30662306a36Sopenharmony_ci			1, 0);
30762306a36Sopenharmony_ci	regs->ft = img_ir_free_timing(&timings->ft, clock_hz);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/**
31162306a36Sopenharmony_ci * img_ir_decoder_preprocess() - Preprocess timings in decoder.
31262306a36Sopenharmony_ci * @decoder:	Decoder to be preprocessed.
31362306a36Sopenharmony_ci *
31462306a36Sopenharmony_ci * Ensures that the symbol timing ranges are valid with respect to ordering, and
31562306a36Sopenharmony_ci * does some fixed conversion on them.
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistatic void img_ir_decoder_preprocess(struct img_ir_decoder *decoder)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	/* default tolerance */
32062306a36Sopenharmony_ci	if (!decoder->tolerance)
32162306a36Sopenharmony_ci		decoder->tolerance = 10; /* percent */
32262306a36Sopenharmony_ci	/* and convert tolerance to fraction out of 128 */
32362306a36Sopenharmony_ci	decoder->tolerance = decoder->tolerance * 128 / 100;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* fill in implicit fields */
32662306a36Sopenharmony_ci	img_ir_timings_preprocess(&decoder->timings, decoder->unit);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	/* do the same for repeat timings if applicable */
32962306a36Sopenharmony_ci	if (decoder->repeat) {
33062306a36Sopenharmony_ci		img_ir_timings_preprocess(&decoder->rtimings, decoder->unit);
33162306a36Sopenharmony_ci		img_ir_timings_defaults(&decoder->rtimings, &decoder->timings);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci/**
33662306a36Sopenharmony_ci * img_ir_decoder_convert() - Generate internal timings in decoder.
33762306a36Sopenharmony_ci * @decoder:	Decoder to be converted to internal timings.
33862306a36Sopenharmony_ci * @reg_timings: Timing register values.
33962306a36Sopenharmony_ci * @clock_hz:	IR clock rate in Hz.
34062306a36Sopenharmony_ci *
34162306a36Sopenharmony_ci * Fills out the repeat timings and timing register values for a specific clock
34262306a36Sopenharmony_ci * rate.
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_cistatic void img_ir_decoder_convert(const struct img_ir_decoder *decoder,
34562306a36Sopenharmony_ci				   struct img_ir_reg_timings *reg_timings,
34662306a36Sopenharmony_ci				   unsigned int clock_hz)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	/* calculate control value */
34962306a36Sopenharmony_ci	reg_timings->ctrl = img_ir_control(&decoder->control);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* fill in implicit fields and calculate register values */
35262306a36Sopenharmony_ci	img_ir_timings_convert(&reg_timings->timings, &decoder->timings,
35362306a36Sopenharmony_ci			       decoder->tolerance, clock_hz);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* do the same for repeat timings if applicable */
35662306a36Sopenharmony_ci	if (decoder->repeat)
35762306a36Sopenharmony_ci		img_ir_timings_convert(&reg_timings->rtimings,
35862306a36Sopenharmony_ci				       &decoder->rtimings, decoder->tolerance,
35962306a36Sopenharmony_ci				       clock_hz);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/**
36362306a36Sopenharmony_ci * img_ir_write_timings() - Write timings to the hardware now
36462306a36Sopenharmony_ci * @priv:	IR private data
36562306a36Sopenharmony_ci * @regs:	Timing register values to write
36662306a36Sopenharmony_ci * @type:	RC filter type (RC_FILTER_*)
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * Write timing register values @regs to the hardware, taking into account the
36962306a36Sopenharmony_ci * current filter which may impose restrictions on the length of the expected
37062306a36Sopenharmony_ci * data.
37162306a36Sopenharmony_ci */
37262306a36Sopenharmony_cistatic void img_ir_write_timings(struct img_ir_priv *priv,
37362306a36Sopenharmony_ci				 struct img_ir_timing_regvals *regs,
37462306a36Sopenharmony_ci				 enum rc_filter_type type)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* filter may be more restrictive to minlen, maxlen */
37962306a36Sopenharmony_ci	u32 ft = regs->ft;
38062306a36Sopenharmony_ci	if (hw->flags & BIT(type))
38162306a36Sopenharmony_ci		ft = img_ir_free_timing_dynamic(regs->ft, &hw->filters[type]);
38262306a36Sopenharmony_ci	/* write to registers */
38362306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_LEAD_SYMB_TIMING, regs->ldr);
38462306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_S00_SYMB_TIMING, regs->s00);
38562306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_S01_SYMB_TIMING, regs->s01);
38662306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_S10_SYMB_TIMING, regs->s10);
38762306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_S11_SYMB_TIMING, regs->s11);
38862306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_FREE_SYMB_TIMING, ft);
38962306a36Sopenharmony_ci	dev_dbg(priv->dev, "timings: ldr=%#x, s=[%#x, %#x, %#x, %#x], ft=%#x\n",
39062306a36Sopenharmony_ci		regs->ldr, regs->s00, regs->s01, regs->s10, regs->s11, ft);
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic void img_ir_write_filter(struct img_ir_priv *priv,
39462306a36Sopenharmony_ci				struct img_ir_filter *filter)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	if (filter) {
39762306a36Sopenharmony_ci		dev_dbg(priv->dev, "IR filter=%016llx & %016llx\n",
39862306a36Sopenharmony_ci			(unsigned long long)filter->data,
39962306a36Sopenharmony_ci			(unsigned long long)filter->mask);
40062306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_LW, (u32)filter->data);
40162306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_DATA_UP, (u32)(filter->data
40262306a36Sopenharmony_ci									>> 32));
40362306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, (u32)filter->mask);
40462306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, (u32)(filter->mask
40562306a36Sopenharmony_ci									>> 32));
40662306a36Sopenharmony_ci	} else {
40762306a36Sopenharmony_ci		dev_dbg(priv->dev, "IR clearing filter\n");
40862306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_LW, 0);
40962306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_MSG_MASK_UP, 0);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* caller must have lock */
41462306a36Sopenharmony_cistatic void _img_ir_set_filter(struct img_ir_priv *priv,
41562306a36Sopenharmony_ci			       struct img_ir_filter *filter)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
41862306a36Sopenharmony_ci	u32 irq_en, irq_on;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
42162306a36Sopenharmony_ci	if (filter) {
42262306a36Sopenharmony_ci		/* Only use the match interrupt */
42362306a36Sopenharmony_ci		hw->filters[RC_FILTER_NORMAL] = *filter;
42462306a36Sopenharmony_ci		hw->flags |= IMG_IR_F_FILTER;
42562306a36Sopenharmony_ci		irq_on = IMG_IR_IRQ_DATA_MATCH;
42662306a36Sopenharmony_ci		irq_en &= ~(IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID);
42762306a36Sopenharmony_ci	} else {
42862306a36Sopenharmony_ci		/* Only use the valid interrupt */
42962306a36Sopenharmony_ci		hw->flags &= ~IMG_IR_F_FILTER;
43062306a36Sopenharmony_ci		irq_en &= ~IMG_IR_IRQ_DATA_MATCH;
43162306a36Sopenharmony_ci		irq_on = IMG_IR_IRQ_DATA_VALID | IMG_IR_IRQ_DATA2_VALID;
43262306a36Sopenharmony_ci	}
43362306a36Sopenharmony_ci	irq_en |= irq_on;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	img_ir_write_filter(priv, filter);
43662306a36Sopenharmony_ci	/* clear any interrupts we're enabling so we don't handle old ones */
43762306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_on);
43862306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci/* caller must have lock */
44262306a36Sopenharmony_cistatic void _img_ir_set_wake_filter(struct img_ir_priv *priv,
44362306a36Sopenharmony_ci				    struct img_ir_filter *filter)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
44662306a36Sopenharmony_ci	if (filter) {
44762306a36Sopenharmony_ci		/* Enable wake, and copy filter for later */
44862306a36Sopenharmony_ci		hw->filters[RC_FILTER_WAKEUP] = *filter;
44962306a36Sopenharmony_ci		hw->flags |= IMG_IR_F_WAKE;
45062306a36Sopenharmony_ci	} else {
45162306a36Sopenharmony_ci		/* Disable wake */
45262306a36Sopenharmony_ci		hw->flags &= ~IMG_IR_F_WAKE;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/* Callback for setting scancode filter */
45762306a36Sopenharmony_cistatic int img_ir_set_filter(struct rc_dev *dev, enum rc_filter_type type,
45862306a36Sopenharmony_ci			     struct rc_scancode_filter *sc_filter)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct img_ir_priv *priv = dev->priv;
46162306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
46262306a36Sopenharmony_ci	struct img_ir_filter filter, *filter_ptr = &filter;
46362306a36Sopenharmony_ci	int ret = 0;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	dev_dbg(priv->dev, "IR scancode %sfilter=%08x & %08x\n",
46662306a36Sopenharmony_ci		type == RC_FILTER_WAKEUP ? "wake " : "",
46762306a36Sopenharmony_ci		sc_filter->data,
46862306a36Sopenharmony_ci		sc_filter->mask);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	/* filtering can always be disabled */
47362306a36Sopenharmony_ci	if (!sc_filter->mask) {
47462306a36Sopenharmony_ci		filter_ptr = NULL;
47562306a36Sopenharmony_ci		goto set_unlock;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	/* current decoder must support scancode filtering */
47962306a36Sopenharmony_ci	if (!hw->decoder || !hw->decoder->filter) {
48062306a36Sopenharmony_ci		ret = -EINVAL;
48162306a36Sopenharmony_ci		goto unlock;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	/* convert scancode filter to raw filter */
48562306a36Sopenharmony_ci	filter.minlen = 0;
48662306a36Sopenharmony_ci	filter.maxlen = ~0;
48762306a36Sopenharmony_ci	if (type == RC_FILTER_NORMAL) {
48862306a36Sopenharmony_ci		/* guess scancode from protocol */
48962306a36Sopenharmony_ci		ret = hw->decoder->filter(sc_filter, &filter,
49062306a36Sopenharmony_ci					  dev->enabled_protocols);
49162306a36Sopenharmony_ci	} else {
49262306a36Sopenharmony_ci		/* for wakeup user provided exact protocol variant */
49362306a36Sopenharmony_ci		ret = hw->decoder->filter(sc_filter, &filter,
49462306a36Sopenharmony_ci					  1ULL << dev->wakeup_protocol);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci	if (ret)
49762306a36Sopenharmony_ci		goto unlock;
49862306a36Sopenharmony_ci	dev_dbg(priv->dev, "IR raw %sfilter=%016llx & %016llx\n",
49962306a36Sopenharmony_ci		type == RC_FILTER_WAKEUP ? "wake " : "",
50062306a36Sopenharmony_ci		(unsigned long long)filter.data,
50162306a36Sopenharmony_ci		(unsigned long long)filter.mask);
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ciset_unlock:
50462306a36Sopenharmony_ci	/* apply raw filters */
50562306a36Sopenharmony_ci	switch (type) {
50662306a36Sopenharmony_ci	case RC_FILTER_NORMAL:
50762306a36Sopenharmony_ci		_img_ir_set_filter(priv, filter_ptr);
50862306a36Sopenharmony_ci		break;
50962306a36Sopenharmony_ci	case RC_FILTER_WAKEUP:
51062306a36Sopenharmony_ci		_img_ir_set_wake_filter(priv, filter_ptr);
51162306a36Sopenharmony_ci		break;
51262306a36Sopenharmony_ci	default:
51362306a36Sopenharmony_ci		ret = -EINVAL;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ciunlock:
51762306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
51862306a36Sopenharmony_ci	return ret;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int img_ir_set_normal_filter(struct rc_dev *dev,
52262306a36Sopenharmony_ci				    struct rc_scancode_filter *sc_filter)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int img_ir_set_wakeup_filter(struct rc_dev *dev,
52862306a36Sopenharmony_ci				    struct rc_scancode_filter *sc_filter)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	return img_ir_set_filter(dev, RC_FILTER_WAKEUP, sc_filter);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/**
53462306a36Sopenharmony_ci * img_ir_set_decoder() - Set the current decoder.
53562306a36Sopenharmony_ci * @priv:	IR private data.
53662306a36Sopenharmony_ci * @decoder:	Decoder to use with immediate effect.
53762306a36Sopenharmony_ci * @proto:	Protocol bitmap (or 0 to use decoder->type).
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_cistatic void img_ir_set_decoder(struct img_ir_priv *priv,
54062306a36Sopenharmony_ci			       const struct img_ir_decoder *decoder,
54162306a36Sopenharmony_ci			       u64 proto)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
54462306a36Sopenharmony_ci	struct rc_dev *rdev = hw->rdev;
54562306a36Sopenharmony_ci	u32 ir_status, irq_en;
54662306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * First record that the protocol is being stopped so that the end timer
55062306a36Sopenharmony_ci	 * isn't restarted while we're trying to stop it.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	hw->stopping = true;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	/*
55562306a36Sopenharmony_ci	 * Release the lock to stop the end timer, since the end timer handler
55662306a36Sopenharmony_ci	 * acquires the lock and we don't want to deadlock waiting for it.
55762306a36Sopenharmony_ci	 */
55862306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
55962306a36Sopenharmony_ci	del_timer_sync(&hw->end_timer);
56062306a36Sopenharmony_ci	del_timer_sync(&hw->suspend_timer);
56162306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	hw->stopping = false;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* switch off and disable interrupts */
56662306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_CONTROL, 0);
56762306a36Sopenharmony_ci	irq_en = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
56862306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_ENABLE, irq_en & IMG_IR_IRQ_EDGE);
56962306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_IRQ_CLEAR, IMG_IR_IRQ_ALL & ~IMG_IR_IRQ_EDGE);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* ack any data already detected */
57262306a36Sopenharmony_ci	ir_status = img_ir_read(priv, IMG_IR_STATUS);
57362306a36Sopenharmony_ci	if (ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2)) {
57462306a36Sopenharmony_ci		ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
57562306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_STATUS, ir_status);
57662306a36Sopenharmony_ci	}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* always read data to clear buffer if IR wakes the device */
57962306a36Sopenharmony_ci	img_ir_read(priv, IMG_IR_DATA_LW);
58062306a36Sopenharmony_ci	img_ir_read(priv, IMG_IR_DATA_UP);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* switch back to normal mode */
58362306a36Sopenharmony_ci	hw->mode = IMG_IR_M_NORMAL;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/* clear the wakeup scancode filter */
58662306a36Sopenharmony_ci	rdev->scancode_wakeup_filter.data = 0;
58762306a36Sopenharmony_ci	rdev->scancode_wakeup_filter.mask = 0;
58862306a36Sopenharmony_ci	rdev->wakeup_protocol = RC_PROTO_UNKNOWN;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* clear raw filters */
59162306a36Sopenharmony_ci	_img_ir_set_filter(priv, NULL);
59262306a36Sopenharmony_ci	_img_ir_set_wake_filter(priv, NULL);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* clear the enabled protocols */
59562306a36Sopenharmony_ci	hw->enabled_protocols = 0;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* switch decoder */
59862306a36Sopenharmony_ci	hw->decoder = decoder;
59962306a36Sopenharmony_ci	if (!decoder)
60062306a36Sopenharmony_ci		goto unlock;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* set the enabled protocols */
60362306a36Sopenharmony_ci	if (!proto)
60462306a36Sopenharmony_ci		proto = decoder->type;
60562306a36Sopenharmony_ci	hw->enabled_protocols = proto;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	/* write the new timings */
60862306a36Sopenharmony_ci	img_ir_decoder_convert(decoder, &hw->reg_timings, hw->clk_hz);
60962306a36Sopenharmony_ci	img_ir_write_timings(priv, &hw->reg_timings.timings, RC_FILTER_NORMAL);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* set up and enable */
61262306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ciunlock:
61662306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci/**
62062306a36Sopenharmony_ci * img_ir_decoder_compatible() - Find whether a decoder will work with a device.
62162306a36Sopenharmony_ci * @priv:	IR private data.
62262306a36Sopenharmony_ci * @dec:	Decoder to check.
62362306a36Sopenharmony_ci *
62462306a36Sopenharmony_ci * Returns:	true if @dec is compatible with the device @priv refers to.
62562306a36Sopenharmony_ci */
62662306a36Sopenharmony_cistatic bool img_ir_decoder_compatible(struct img_ir_priv *priv,
62762306a36Sopenharmony_ci				      const struct img_ir_decoder *dec)
62862306a36Sopenharmony_ci{
62962306a36Sopenharmony_ci	unsigned int ct;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* don't accept decoders using code types which aren't supported */
63262306a36Sopenharmony_ci	ct = dec->control.code_type;
63362306a36Sopenharmony_ci	if (priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_BROKEN)
63462306a36Sopenharmony_ci		return false;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return true;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * img_ir_allowed_protos() - Get allowed protocols from global decoder list.
64162306a36Sopenharmony_ci * @priv:	IR private data.
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci * Returns:	Mask of protocols supported by the device @priv refers to.
64462306a36Sopenharmony_ci */
64562306a36Sopenharmony_cistatic u64 img_ir_allowed_protos(struct img_ir_priv *priv)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	u64 protos = 0;
64862306a36Sopenharmony_ci	struct img_ir_decoder **decp;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	for (decp = img_ir_decoders; *decp; ++decp) {
65162306a36Sopenharmony_ci		const struct img_ir_decoder *dec = *decp;
65262306a36Sopenharmony_ci		if (img_ir_decoder_compatible(priv, dec))
65362306a36Sopenharmony_ci			protos |= dec->type;
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	return protos;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci/* Callback for changing protocol using sysfs */
65962306a36Sopenharmony_cistatic int img_ir_change_protocol(struct rc_dev *dev, u64 *ir_type)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	struct img_ir_priv *priv = dev->priv;
66262306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
66362306a36Sopenharmony_ci	struct rc_dev *rdev = hw->rdev;
66462306a36Sopenharmony_ci	struct img_ir_decoder **decp;
66562306a36Sopenharmony_ci	u64 wakeup_protocols;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (!*ir_type) {
66862306a36Sopenharmony_ci		/* disable all protocols */
66962306a36Sopenharmony_ci		img_ir_set_decoder(priv, NULL, 0);
67062306a36Sopenharmony_ci		goto success;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci	for (decp = img_ir_decoders; *decp; ++decp) {
67362306a36Sopenharmony_ci		const struct img_ir_decoder *dec = *decp;
67462306a36Sopenharmony_ci		if (!img_ir_decoder_compatible(priv, dec))
67562306a36Sopenharmony_ci			continue;
67662306a36Sopenharmony_ci		if (*ir_type & dec->type) {
67762306a36Sopenharmony_ci			*ir_type &= dec->type;
67862306a36Sopenharmony_ci			img_ir_set_decoder(priv, dec, *ir_type);
67962306a36Sopenharmony_ci			goto success;
68062306a36Sopenharmony_ci		}
68162306a36Sopenharmony_ci	}
68262306a36Sopenharmony_ci	return -EINVAL;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cisuccess:
68562306a36Sopenharmony_ci	/*
68662306a36Sopenharmony_ci	 * Only allow matching wakeup protocols for now, and only if filtering
68762306a36Sopenharmony_ci	 * is supported.
68862306a36Sopenharmony_ci	 */
68962306a36Sopenharmony_ci	wakeup_protocols = *ir_type;
69062306a36Sopenharmony_ci	if (!hw->decoder || !hw->decoder->filter)
69162306a36Sopenharmony_ci		wakeup_protocols = 0;
69262306a36Sopenharmony_ci	rdev->allowed_wakeup_protocols = wakeup_protocols;
69362306a36Sopenharmony_ci	return 0;
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci/* Changes ir-core protocol device attribute */
69762306a36Sopenharmony_cistatic void img_ir_set_protocol(struct img_ir_priv *priv, u64 proto)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct rc_dev *rdev = priv->hw.rdev;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	mutex_lock(&rdev->lock);
70262306a36Sopenharmony_ci	rdev->enabled_protocols = proto;
70362306a36Sopenharmony_ci	rdev->allowed_wakeup_protocols = proto;
70462306a36Sopenharmony_ci	mutex_unlock(&rdev->lock);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/* Set up IR decoders */
70862306a36Sopenharmony_cistatic void img_ir_init_decoders(void)
70962306a36Sopenharmony_ci{
71062306a36Sopenharmony_ci	struct img_ir_decoder **decp;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	spin_lock(&img_ir_decoders_lock);
71362306a36Sopenharmony_ci	if (!img_ir_decoders_preprocessed) {
71462306a36Sopenharmony_ci		for (decp = img_ir_decoders; *decp; ++decp)
71562306a36Sopenharmony_ci			img_ir_decoder_preprocess(*decp);
71662306a36Sopenharmony_ci		img_ir_decoders_preprocessed = true;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci	spin_unlock(&img_ir_decoders_lock);
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
72262306a36Sopenharmony_ci/**
72362306a36Sopenharmony_ci * img_ir_enable_wake() - Switch to wake mode.
72462306a36Sopenharmony_ci * @priv:	IR private data.
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * Returns:	non-zero if the IR can wake the system.
72762306a36Sopenharmony_ci */
72862306a36Sopenharmony_cistatic int img_ir_enable_wake(struct img_ir_priv *priv)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
73162306a36Sopenharmony_ci	int ret = 0;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
73462306a36Sopenharmony_ci	if (hw->flags & IMG_IR_F_WAKE) {
73562306a36Sopenharmony_ci		/* interrupt only on a match */
73662306a36Sopenharmony_ci		hw->suspend_irqen = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
73762306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_ENABLE, IMG_IR_IRQ_DATA_MATCH);
73862306a36Sopenharmony_ci		img_ir_write_filter(priv, &hw->filters[RC_FILTER_WAKEUP]);
73962306a36Sopenharmony_ci		img_ir_write_timings(priv, &hw->reg_timings.timings,
74062306a36Sopenharmony_ci				     RC_FILTER_WAKEUP);
74162306a36Sopenharmony_ci		hw->mode = IMG_IR_M_WAKE;
74262306a36Sopenharmony_ci		ret = 1;
74362306a36Sopenharmony_ci	}
74462306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
74562306a36Sopenharmony_ci	return ret;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci/**
74962306a36Sopenharmony_ci * img_ir_disable_wake() - Switch out of wake mode.
75062306a36Sopenharmony_ci * @priv:	IR private data
75162306a36Sopenharmony_ci *
75262306a36Sopenharmony_ci * Returns:	1 if the hardware should be allowed to wake from a sleep state.
75362306a36Sopenharmony_ci *		0 otherwise.
75462306a36Sopenharmony_ci */
75562306a36Sopenharmony_cistatic int img_ir_disable_wake(struct img_ir_priv *priv)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
75862306a36Sopenharmony_ci	int ret = 0;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
76162306a36Sopenharmony_ci	if (hw->flags & IMG_IR_F_WAKE) {
76262306a36Sopenharmony_ci		/* restore normal filtering */
76362306a36Sopenharmony_ci		if (hw->flags & IMG_IR_F_FILTER) {
76462306a36Sopenharmony_ci			img_ir_write(priv, IMG_IR_IRQ_ENABLE,
76562306a36Sopenharmony_ci				     (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
76662306a36Sopenharmony_ci				     IMG_IR_IRQ_DATA_MATCH);
76762306a36Sopenharmony_ci			img_ir_write_filter(priv,
76862306a36Sopenharmony_ci					    &hw->filters[RC_FILTER_NORMAL]);
76962306a36Sopenharmony_ci		} else {
77062306a36Sopenharmony_ci			img_ir_write(priv, IMG_IR_IRQ_ENABLE,
77162306a36Sopenharmony_ci				     (hw->suspend_irqen & IMG_IR_IRQ_EDGE) |
77262306a36Sopenharmony_ci				     IMG_IR_IRQ_DATA_VALID |
77362306a36Sopenharmony_ci				     IMG_IR_IRQ_DATA2_VALID);
77462306a36Sopenharmony_ci			img_ir_write_filter(priv, NULL);
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci		img_ir_write_timings(priv, &hw->reg_timings.timings,
77762306a36Sopenharmony_ci				     RC_FILTER_NORMAL);
77862306a36Sopenharmony_ci		hw->mode = IMG_IR_M_NORMAL;
77962306a36Sopenharmony_ci		ret = 1;
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
78262306a36Sopenharmony_ci	return ret;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci/* lock must be held */
78762306a36Sopenharmony_cistatic void img_ir_begin_repeat(struct img_ir_priv *priv)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
79062306a36Sopenharmony_ci	if (hw->mode == IMG_IR_M_NORMAL) {
79162306a36Sopenharmony_ci		/* switch to repeat timings */
79262306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_CONTROL, 0);
79362306a36Sopenharmony_ci		hw->mode = IMG_IR_M_REPEATING;
79462306a36Sopenharmony_ci		img_ir_write_timings(priv, &hw->reg_timings.rtimings,
79562306a36Sopenharmony_ci				     RC_FILTER_NORMAL);
79662306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/* lock must be held */
80162306a36Sopenharmony_cistatic void img_ir_end_repeat(struct img_ir_priv *priv)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
80462306a36Sopenharmony_ci	if (hw->mode == IMG_IR_M_REPEATING) {
80562306a36Sopenharmony_ci		/* switch to normal timings */
80662306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_CONTROL, 0);
80762306a36Sopenharmony_ci		hw->mode = IMG_IR_M_NORMAL;
80862306a36Sopenharmony_ci		img_ir_write_timings(priv, &hw->reg_timings.timings,
80962306a36Sopenharmony_ci				     RC_FILTER_NORMAL);
81062306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_CONTROL, hw->reg_timings.ctrl);
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/* lock must be held */
81562306a36Sopenharmony_cistatic void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
81862306a36Sopenharmony_ci	const struct img_ir_decoder *dec = hw->decoder;
81962306a36Sopenharmony_ci	int ret = IMG_IR_SCANCODE;
82062306a36Sopenharmony_ci	struct img_ir_scancode_req request;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	request.protocol = RC_PROTO_UNKNOWN;
82362306a36Sopenharmony_ci	request.toggle   = 0;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (dec->scancode)
82662306a36Sopenharmony_ci		ret = dec->scancode(len, raw, hw->enabled_protocols, &request);
82762306a36Sopenharmony_ci	else if (len >= 32)
82862306a36Sopenharmony_ci		request.scancode = (u32)raw;
82962306a36Sopenharmony_ci	else if (len < 32)
83062306a36Sopenharmony_ci		request.scancode = (u32)raw & ((1 << len)-1);
83162306a36Sopenharmony_ci	dev_dbg(priv->dev, "data (%u bits) = %#llx\n",
83262306a36Sopenharmony_ci		len, (unsigned long long)raw);
83362306a36Sopenharmony_ci	if (ret == IMG_IR_SCANCODE) {
83462306a36Sopenharmony_ci		dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n",
83562306a36Sopenharmony_ci			request.scancode, request.toggle);
83662306a36Sopenharmony_ci		rc_keydown(hw->rdev, request.protocol, request.scancode,
83762306a36Sopenharmony_ci			   request.toggle);
83862306a36Sopenharmony_ci		img_ir_end_repeat(priv);
83962306a36Sopenharmony_ci	} else if (ret == IMG_IR_REPEATCODE) {
84062306a36Sopenharmony_ci		if (hw->mode == IMG_IR_M_REPEATING) {
84162306a36Sopenharmony_ci			dev_dbg(priv->dev, "decoded repeat code\n");
84262306a36Sopenharmony_ci			rc_repeat(hw->rdev);
84362306a36Sopenharmony_ci		} else {
84462306a36Sopenharmony_ci			dev_dbg(priv->dev, "decoded unexpected repeat code, ignoring\n");
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci	} else {
84762306a36Sopenharmony_ci		dev_dbg(priv->dev, "decode failed (%d)\n", ret);
84862306a36Sopenharmony_ci		return;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* we mustn't update the end timer while trying to stop it */
85362306a36Sopenharmony_ci	if (dec->repeat && !hw->stopping) {
85462306a36Sopenharmony_ci		unsigned long interval;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		img_ir_begin_repeat(priv);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci		/* update timer, but allowing for 1/8th tolerance */
85962306a36Sopenharmony_ci		interval = dec->repeat + (dec->repeat >> 3);
86062306a36Sopenharmony_ci		mod_timer(&hw->end_timer,
86162306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(interval));
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci/* timer function to end waiting for repeat. */
86662306a36Sopenharmony_cistatic void img_ir_end_timer(struct timer_list *t)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct img_ir_priv *priv = from_timer(priv, t, hw.end_timer);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
87162306a36Sopenharmony_ci	img_ir_end_repeat(priv);
87262306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci/*
87662306a36Sopenharmony_ci * Timer function to re-enable the current protocol after it had been
87762306a36Sopenharmony_ci * cleared when invalid interrupts were generated due to a quirk in the
87862306a36Sopenharmony_ci * img-ir decoder.
87962306a36Sopenharmony_ci */
88062306a36Sopenharmony_cistatic void img_ir_suspend_timer(struct timer_list *t)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	struct img_ir_priv *priv = from_timer(priv, t, hw.suspend_timer);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
88562306a36Sopenharmony_ci	/*
88662306a36Sopenharmony_ci	 * Don't overwrite enabled valid/match IRQs if they have already been
88762306a36Sopenharmony_ci	 * changed by e.g. a filter change.
88862306a36Sopenharmony_ci	 */
88962306a36Sopenharmony_ci	if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) ==
89062306a36Sopenharmony_ci				img_ir_read(priv, IMG_IR_IRQ_ENABLE))
89162306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
89262306a36Sopenharmony_ci					priv->hw.quirk_suspend_irq);
89362306a36Sopenharmony_ci	/* enable */
89462306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl);
89562306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci#ifdef CONFIG_COMMON_CLK
89962306a36Sopenharmony_cistatic void img_ir_change_frequency(struct img_ir_priv *priv,
90062306a36Sopenharmony_ci				    struct clk_notifier_data *change)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n",
90562306a36Sopenharmony_ci		change->old_rate, change->new_rate);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	spin_lock_irq(&priv->lock);
90862306a36Sopenharmony_ci	if (hw->clk_hz == change->new_rate)
90962306a36Sopenharmony_ci		goto unlock;
91062306a36Sopenharmony_ci	hw->clk_hz = change->new_rate;
91162306a36Sopenharmony_ci	/* refresh current timings */
91262306a36Sopenharmony_ci	if (hw->decoder) {
91362306a36Sopenharmony_ci		img_ir_decoder_convert(hw->decoder, &hw->reg_timings,
91462306a36Sopenharmony_ci				       hw->clk_hz);
91562306a36Sopenharmony_ci		switch (hw->mode) {
91662306a36Sopenharmony_ci		case IMG_IR_M_NORMAL:
91762306a36Sopenharmony_ci			img_ir_write_timings(priv, &hw->reg_timings.timings,
91862306a36Sopenharmony_ci					     RC_FILTER_NORMAL);
91962306a36Sopenharmony_ci			break;
92062306a36Sopenharmony_ci		case IMG_IR_M_REPEATING:
92162306a36Sopenharmony_ci			img_ir_write_timings(priv, &hw->reg_timings.rtimings,
92262306a36Sopenharmony_ci					     RC_FILTER_NORMAL);
92362306a36Sopenharmony_ci			break;
92462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
92562306a36Sopenharmony_ci		case IMG_IR_M_WAKE:
92662306a36Sopenharmony_ci			img_ir_write_timings(priv, &hw->reg_timings.timings,
92762306a36Sopenharmony_ci					     RC_FILTER_WAKEUP);
92862306a36Sopenharmony_ci			break;
92962306a36Sopenharmony_ci#endif
93062306a36Sopenharmony_ci		}
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ciunlock:
93362306a36Sopenharmony_ci	spin_unlock_irq(&priv->lock);
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_cistatic int img_ir_clk_notify(struct notifier_block *self, unsigned long action,
93762306a36Sopenharmony_ci			     void *data)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct img_ir_priv *priv = container_of(self, struct img_ir_priv,
94062306a36Sopenharmony_ci						hw.clk_nb);
94162306a36Sopenharmony_ci	switch (action) {
94262306a36Sopenharmony_ci	case POST_RATE_CHANGE:
94362306a36Sopenharmony_ci		img_ir_change_frequency(priv, data);
94462306a36Sopenharmony_ci		break;
94562306a36Sopenharmony_ci	default:
94662306a36Sopenharmony_ci		break;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci	return NOTIFY_OK;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci#endif /* CONFIG_COMMON_CLK */
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci/* called with priv->lock held */
95362306a36Sopenharmony_civoid img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
95662306a36Sopenharmony_ci	u32 ir_status, len, lw, up;
95762306a36Sopenharmony_ci	unsigned int ct;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	/* use the current decoder */
96062306a36Sopenharmony_ci	if (!hw->decoder)
96162306a36Sopenharmony_ci		return;
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	ct = hw->decoder->control.code_type;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ir_status = img_ir_read(priv, IMG_IR_STATUS);
96662306a36Sopenharmony_ci	if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) {
96762306a36Sopenharmony_ci		if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) ||
96862306a36Sopenharmony_ci				hw->stopping)
96962306a36Sopenharmony_ci			return;
97062306a36Sopenharmony_ci		/*
97162306a36Sopenharmony_ci		 * The below functionality is added as a work around to stop
97262306a36Sopenharmony_ci		 * multiple Interrupts generated when an incomplete IR code is
97362306a36Sopenharmony_ci		 * received by the decoder.
97462306a36Sopenharmony_ci		 * The decoder generates rapid interrupts without actually
97562306a36Sopenharmony_ci		 * having received any new data. After a single interrupt it's
97662306a36Sopenharmony_ci		 * expected to clear up, but instead multiple interrupts are
97762306a36Sopenharmony_ci		 * rapidly generated. only way to get out of this loop is to
97862306a36Sopenharmony_ci		 * reset the control register after a short delay.
97962306a36Sopenharmony_ci		 */
98062306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_CONTROL, 0);
98162306a36Sopenharmony_ci		hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE);
98262306a36Sopenharmony_ci		img_ir_write(priv, IMG_IR_IRQ_ENABLE,
98362306a36Sopenharmony_ci			     hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		/* Timer activated to re-enable the protocol. */
98662306a36Sopenharmony_ci		mod_timer(&hw->suspend_timer,
98762306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(5));
98862306a36Sopenharmony_ci		return;
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci	ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2);
99162306a36Sopenharmony_ci	img_ir_write(priv, IMG_IR_STATUS, ir_status);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT;
99462306a36Sopenharmony_ci	/* some versions report wrong length for certain code types */
99562306a36Sopenharmony_ci	if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR)
99662306a36Sopenharmony_ci		++len;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	lw = img_ir_read(priv, IMG_IR_DATA_LW);
99962306a36Sopenharmony_ci	up = img_ir_read(priv, IMG_IR_DATA_UP);
100062306a36Sopenharmony_ci	img_ir_handle_data(priv, len, (u64)up << 32 | lw);
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_civoid img_ir_setup_hw(struct img_ir_priv *priv)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	struct img_ir_decoder **decp;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	if (!priv->hw.rdev)
100862306a36Sopenharmony_ci		return;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	/* Use the first available decoder (or disable stuff if NULL) */
101162306a36Sopenharmony_ci	for (decp = img_ir_decoders; *decp; ++decp) {
101262306a36Sopenharmony_ci		const struct img_ir_decoder *dec = *decp;
101362306a36Sopenharmony_ci		if (img_ir_decoder_compatible(priv, dec)) {
101462306a36Sopenharmony_ci			img_ir_set_protocol(priv, dec->type);
101562306a36Sopenharmony_ci			img_ir_set_decoder(priv, dec, 0);
101662306a36Sopenharmony_ci			return;
101762306a36Sopenharmony_ci		}
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci	img_ir_set_decoder(priv, NULL, 0);
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci/**
102362306a36Sopenharmony_ci * img_ir_probe_hw_caps() - Probe capabilities of the hardware.
102462306a36Sopenharmony_ci * @priv:	IR private data.
102562306a36Sopenharmony_ci */
102662306a36Sopenharmony_cistatic void img_ir_probe_hw_caps(struct img_ir_priv *priv)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
102962306a36Sopenharmony_ci	/*
103062306a36Sopenharmony_ci	 * When a version of the block becomes available without these quirks,
103162306a36Sopenharmony_ci	 * they'll have to depend on the core revision.
103262306a36Sopenharmony_ci	 */
103362306a36Sopenharmony_ci	hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN]
103462306a36Sopenharmony_ci		|= IMG_IR_QUIRK_CODE_LEN_INCR;
103562306a36Sopenharmony_ci	hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE]
103662306a36Sopenharmony_ci		|= IMG_IR_QUIRK_CODE_IRQ;
103762306a36Sopenharmony_ci	hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS]
103862306a36Sopenharmony_ci		|= IMG_IR_QUIRK_CODE_BROKEN;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ciint img_ir_probe_hw(struct img_ir_priv *priv)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
104462306a36Sopenharmony_ci	struct rc_dev *rdev;
104562306a36Sopenharmony_ci	int error;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	/* Ensure hardware decoders have been preprocessed */
104862306a36Sopenharmony_ci	img_ir_init_decoders();
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/* Probe hardware capabilities */
105162306a36Sopenharmony_ci	img_ir_probe_hw_caps(priv);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* Set up the end timer */
105462306a36Sopenharmony_ci	timer_setup(&hw->end_timer, img_ir_end_timer, 0);
105562306a36Sopenharmony_ci	timer_setup(&hw->suspend_timer, img_ir_suspend_timer, 0);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Register a clock notifier */
105862306a36Sopenharmony_ci	if (!IS_ERR(priv->clk)) {
105962306a36Sopenharmony_ci		hw->clk_hz = clk_get_rate(priv->clk);
106062306a36Sopenharmony_ci#ifdef CONFIG_COMMON_CLK
106162306a36Sopenharmony_ci		hw->clk_nb.notifier_call = img_ir_clk_notify;
106262306a36Sopenharmony_ci		error = clk_notifier_register(priv->clk, &hw->clk_nb);
106362306a36Sopenharmony_ci		if (error)
106462306a36Sopenharmony_ci			dev_warn(priv->dev,
106562306a36Sopenharmony_ci				 "failed to register clock notifier\n");
106662306a36Sopenharmony_ci#endif
106762306a36Sopenharmony_ci	} else {
106862306a36Sopenharmony_ci		hw->clk_hz = 32768;
106962306a36Sopenharmony_ci	}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	/* Allocate hardware decoder */
107262306a36Sopenharmony_ci	hw->rdev = rdev = rc_allocate_device(RC_DRIVER_SCANCODE);
107362306a36Sopenharmony_ci	if (!rdev) {
107462306a36Sopenharmony_ci		dev_err(priv->dev, "cannot allocate input device\n");
107562306a36Sopenharmony_ci		error = -ENOMEM;
107662306a36Sopenharmony_ci		goto err_alloc_rc;
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci	rdev->priv = priv;
107962306a36Sopenharmony_ci	rdev->map_name = RC_MAP_EMPTY;
108062306a36Sopenharmony_ci	rdev->allowed_protocols = img_ir_allowed_protos(priv);
108162306a36Sopenharmony_ci	rdev->device_name = "IMG Infrared Decoder";
108262306a36Sopenharmony_ci	rdev->s_filter = img_ir_set_normal_filter;
108362306a36Sopenharmony_ci	rdev->s_wakeup_filter = img_ir_set_wakeup_filter;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/* Register hardware decoder */
108662306a36Sopenharmony_ci	error = rc_register_device(rdev);
108762306a36Sopenharmony_ci	if (error) {
108862306a36Sopenharmony_ci		dev_err(priv->dev, "failed to register IR input device\n");
108962306a36Sopenharmony_ci		goto err_register_rc;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/*
109362306a36Sopenharmony_ci	 * Set this after rc_register_device as no protocols have been
109462306a36Sopenharmony_ci	 * registered yet.
109562306a36Sopenharmony_ci	 */
109662306a36Sopenharmony_ci	rdev->change_protocol = img_ir_change_protocol;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	device_init_wakeup(priv->dev, 1);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	return 0;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cierr_register_rc:
110362306a36Sopenharmony_ci	img_ir_set_decoder(priv, NULL, 0);
110462306a36Sopenharmony_ci	hw->rdev = NULL;
110562306a36Sopenharmony_ci	rc_free_device(rdev);
110662306a36Sopenharmony_cierr_alloc_rc:
110762306a36Sopenharmony_ci#ifdef CONFIG_COMMON_CLK
110862306a36Sopenharmony_ci	if (!IS_ERR(priv->clk))
110962306a36Sopenharmony_ci		clk_notifier_unregister(priv->clk, &hw->clk_nb);
111062306a36Sopenharmony_ci#endif
111162306a36Sopenharmony_ci	return error;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_civoid img_ir_remove_hw(struct img_ir_priv *priv)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct img_ir_priv_hw *hw = &priv->hw;
111762306a36Sopenharmony_ci	struct rc_dev *rdev = hw->rdev;
111862306a36Sopenharmony_ci	if (!rdev)
111962306a36Sopenharmony_ci		return;
112062306a36Sopenharmony_ci	img_ir_set_decoder(priv, NULL, 0);
112162306a36Sopenharmony_ci	hw->rdev = NULL;
112262306a36Sopenharmony_ci	rc_unregister_device(rdev);
112362306a36Sopenharmony_ci#ifdef CONFIG_COMMON_CLK
112462306a36Sopenharmony_ci	if (!IS_ERR(priv->clk))
112562306a36Sopenharmony_ci		clk_notifier_unregister(priv->clk, &hw->clk_nb);
112662306a36Sopenharmony_ci#endif
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
113062306a36Sopenharmony_ciint img_ir_suspend(struct device *dev)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct img_ir_priv *priv = dev_get_drvdata(dev);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if (device_may_wakeup(dev) && img_ir_enable_wake(priv))
113562306a36Sopenharmony_ci		enable_irq_wake(priv->irq);
113662306a36Sopenharmony_ci	return 0;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ciint img_ir_resume(struct device *dev)
114062306a36Sopenharmony_ci{
114162306a36Sopenharmony_ci	struct img_ir_priv *priv = dev_get_drvdata(dev);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	if (device_may_wakeup(dev) && img_ir_disable_wake(priv))
114462306a36Sopenharmony_ci		disable_irq_wake(priv->irq);
114562306a36Sopenharmony_ci	return 0;
114662306a36Sopenharmony_ci}
114762306a36Sopenharmony_ci#endif	/* CONFIG_PM_SLEEP */
1148