/*
* ImgTec IR Hardware Decoder found in PowerDown Controller.
*
* Copyright 2010-2014 Imagination Technologies Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This ties into the input subsystem using the RC-core. Protocol support is
* provided in separate modules which provide the parameters and scancode
* translation functions to set up the hardware decoder and interpret the
* resulting input.
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <media/rc-core.h>
#include "img-ir.h"
/* Decoders lock (only modified to preprocess them) */
static DEFINE_SPINLOCK(img_ir_decoders_lock);
static bool img_ir_decoders_preprocessed;
static struct img_ir_decoder *img_ir_decoders[] = {
#ifdef CONFIG_IR_IMG_NEC
&img_ir_nec,
#endif
#ifdef CONFIG_IR_IMG_JVC
&img_ir_jvc,
#endif
#ifdef CONFIG_IR_IMG_SONY
&img_ir_sony,
#endif
#ifdef CONFIG_IR_IMG_SHARP
&img_ir_sharp,
#endif
#ifdef CONFIG_IR_IMG_SANYO
&img_ir_sanyo,
#endif
#ifdef CONFIG_IR_IMG_RC5
&img_ir_rc5,
#endif
#ifdef CONFIG_IR_IMG_RC6
&img_ir_rc6,
#endif
NULL
};
#define IMG_IR_F_FILTER BIT(RC_FILTER_NORMAL) /* enable filtering */
#define IMG_IR_F_WAKE BIT(RC_FILTER_WAKEUP) /* enable waking */
/* code type quirks */
#define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */
#define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */
/*
* The decoder generates rapid interrupts without actually having
* received any new data after an incomplete IR code is decoded.
*/
#define IMG_IR_QUIRK_CODE_IRQ 0x4
/* functions for preprocessing timings, ensuring max is set */
static void img_ir_timing_preprocess(struct img_ir_timing_range *range,
unsigned int unit)
{
if (range->max < range->min)
range->max = range->min;
if (unit) {
/* multiply by unit and convert to microseconds */
range->min = (range->min*unit)/1000;
range->max = (range->max*unit + 999)/1000; /* round up */
}
}
static void img_ir_symbol_timing_preprocess(struct img_ir_symbol_timing *timing,
unsigned int unit)
{
img_ir_timing_preprocess(&timing->pulse, unit);
img_ir_timing_preprocess(&timing->space, unit);
}
static void img_ir_timings_preprocess(struct img_ir_timings *timings,
unsigned int unit)
{
img_ir_symbol_timing_preprocess(&timings->ldr, unit);
img_ir_symbol_timing_preprocess(&timings->s00, unit);
img_ir_symbol_timing_preprocess(&timings->s01, unit);
img_ir_symbol_timing_preprocess(&timings->s10, unit);
img_ir_symbol_timing_preprocess(&timings->s11, unit);
/* default s10 and s11 to s00 and s01 if no leader */
if (unit)
/* multiply by unit and convert to microseconds (round up) */
timings->ft.ft_min = (timings->ft.ft_min*unit + 999)/1000;
}
/* functions for filling empty fields with defaults */
static void img_ir_timing_defaults(struct img_ir_timing_range *range,
struct img_ir_timing_range *defaults)
{
if (!range->min)
range->min = defaults->min;
if (!range->max)
range->max = defaults->max;
}
static void img_ir_symbol_timing_defaults(struct img_ir_symbol_timing *timing,
struct img_ir_symbol_timing *defaults)
{
img_ir_timing_defaults(&timing->pulse, &defaults->pulse);
img_ir_timing_defaults(&timing->space, &defaults->space);
}
static void img_ir_timings_defaults(struct img_ir_timings *timings,
struct img_ir_timings *defaults)
{
img_ir_symbol_timing_defaults(&timings->ldr, &defaults->ldr);
img_ir_symbol_timing_defaults(&timings->s00, &defaults->s00);
img_ir_symbol_timin