/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
/*
 * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
 */

#ifndef __IR_CORE_MESON_H__
#define __IR_CORE_MESON_H__

#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/kfifo.h>
#include <linux/device.h>
#include <dt-bindings/input/meson_ir.h>
#include <linux/leds.h>
#include "meson_ir_common.h"

#define MULTI_IR_TYPE_MASK(type) (type & 0xff)  /*8bit*/
#define LEGACY_IR_TYPE_MASK(type) ((type >> 8) & 0xff) /*8bit*/
/*bit[7] identify whether software decode or not*/
#define MULTI_IR_SOFTWARE_DECODE(type) ((MULTI_IR_TYPE_MASK(type) >> 7) == 0x1)
#define ENABLE_LEGACY_IR(type) (LEGACY_IR_TYPE_MASK(type) == 0xff)
/*default led blink frequency*/
#define DEFAULT_LED_BLINK_FRQ	100
#define MAX_LEARNED_PULSE	256
#define meson_ir_dbg(dev, format, arg...)           \
do {                                                \
	if ((dev)->debug_enable)                    \
		pr_info(format, ##arg);             \
} while (0)

enum ir_status {
	IR_STATUS_NORMAL		= 0 << 0,
	IR_STATUS_REPEAT		= 1 << 0,
	IR_STATUS_CUSTOM_ERROR		= 1 << 1,
	IR_STATUS_DATA_ERROR		= 1 << 2,
	IR_STATUS_FRAME_ERROR		= 1 << 3,
	IR_STATUS_CHECKSUM_ERROR	= 1 << 4,
	IR_STATUS_CUSTOM_DATA		= 1 << 5
};

enum raw_event_type {
	RAW_SPACE        = (1 << 0),
	RAW_PULSE        = (1 << 1),
	RAW_START_EVENT  = (1 << 2),
	RAW_STOP_EVENT   = (1 << 3),
};

struct pulse_group {
	int len;
	/*bit 30-0 durations, bit31: level*/
	u32 pulse[0];
};

struct meson_ir_raw_handle;
struct meson_ir_dev {
	struct device *dev;
	struct input_dev *input_device;
	struct input_dev *input_device_ots[3];
	struct list_head reg_list;
	struct list_head aml_list;
	struct meson_ir_raw_handle *raw;
	struct led_trigger *led_feedback;
	struct timer_list timer_keyup;
	struct pulse_group *pulses;

	spinlock_t keylock; /*protect runtime data*/

	unsigned long delay_on;
	unsigned long delay_off;
	unsigned long keyup_jiffies;
	unsigned long keyup_delay;

	u32 keypressed;
	u32 last_scancode;
	u32 last_keycode;
	u32 cur_hardcode;
	u32 cur_customcode;
	u32 repeat_time;
	u32 max_frame_time;

	int wait_next_repeat;
	int rc_type;
	int led_blink;
	int max_learned_pulse;
	int protocol;

	void *platform_data;

	u32 (*getkeycode)(struct meson_ir_dev *ir_dev, u32 scancode);
	int (*report_rel)(struct meson_ir_dev *ir_dev, u32 scancode,
			  int status);
	bool (*set_custom_code)(struct meson_ir_dev *ir_dev, u32 scancode);
	bool (*is_valid_custom)(struct meson_ir_dev *ir_dev);
	bool (*is_next_repeat)(struct meson_ir_dev *ir_dev);

	u8 debug_enable;
	u8 enable;
	u8 ir_learning_on;
};

struct meson_ir_raw_handle {
	struct list_head list;
	struct meson_ir_dev *dev;
	struct task_struct *thread;
	struct kfifo_rec_ptr_1 kfifo;/* fifo for the pulse/space durations */

	spinlock_t lock; /*lock used to protect data in soft decode*/

	enum raw_event_type last_type;
	unsigned long jiffies_old;
	unsigned long repeat_time;
	unsigned long max_frame_time;
};

struct meson_ir_map_table {
	u32 scancode;
	u32 keycode;
};

struct meson_ir_map {
	struct meson_ir_map_table *scan;
	int rc_type;
	const char *name;
	u32 size;
};

struct meson_ir_map_list {
	struct list_head list;
	struct meson_ir_map map;
};

struct meson_ir_raw_event {
	u32 duration;
	u32 pulse:1;
	u32 reset:1;
	u32 timeout:1;
};

#define DEFINE_REMOTE_RAW_EVENT(event) \
	struct meson_ir_raw_event event = { \
		.duration = 0, \
		.pulse = 0, \
		.reset = 0, \
		.timeout = 0}

struct meson_ir_raw_handler {
	struct list_head list;

	int protocols;
	void *data;
	int (*decode)(struct meson_ir_dev *dev, struct meson_ir_raw_event event,
		      void *data_dec);
};

/* macros for IR decoders */
static inline bool geq_margin(unsigned int d1, unsigned int d2,
			      unsigned int margin)
{
	return d1 > (d2 - margin);
}

static inline bool eq_margin(unsigned int d1, unsigned int d2,
			     unsigned int margin)
{
	return (d1 > (d2 - margin)) && (d1 < (d2 + margin));
}

static inline bool is_transition(struct meson_ir_raw_event *x,
				 struct meson_ir_raw_event *y)
{
	return x->pulse != y->pulse;
}

static inline void decrease_duration(struct meson_ir_raw_event *ev,
				     unsigned int duration)
{
	if (duration > ev->duration)
		ev->duration = 0;
	else
		ev->duration -= duration;
}

void meson_ir_input_configure(struct meson_ir_dev *dev,
			      struct ir_map_tab *ir_map);
void meson_ir_input_mouse_configure(struct meson_ir_dev *dev);
void meson_ir_keydown(struct meson_ir_dev *dev, int scancode, int status);
int meson_ir_raw_event_store(struct meson_ir_dev *dev,
			     struct meson_ir_raw_event *ev);
int meson_ir_raw_event_register(struct meson_ir_dev *dev);
void meson_ir_raw_event_unregister(struct meson_ir_dev *dev);
int meson_ir_raw_handler_register(struct meson_ir_raw_handler *handler);
void meson_ir_raw_handler_unregister(struct meson_ir_raw_handler *handler);
void meson_ir_raw_event_handle(struct meson_ir_dev *dev);
int meson_ir_raw_event_store_edge(struct meson_ir_dev *dev,
				  enum raw_event_type type, u32 duration);
#endif
