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(®_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(®_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