blob: f9d2fb373114b2f7c973f80eedbbd74f149f3988 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/pm_wakeup.h>
#include <linux/pm_wakeirq.h>
#include <linux/amlogic/pm.h>
#include "meson_ir_main.h"
#include <linux/amlogic/gki_module.h>
static void meson_ir_tasklet(unsigned long data);
DECLARE_TASKLET_DISABLED(tasklet, meson_ir_tasklet, 0);
static int disable_ir;
static int get_irenv(char *str)
{
int ret;
ret = kstrtouint(str, 10, &disable_ir);
if (ret)
return -EINVAL;
return 0;
}
__setup("disable_ir=", get_irenv);
int meson_ir_pulses_malloc(struct meson_ir_chip *chip)
{
struct meson_ir_dev *r_dev = chip->r_dev;
int len = r_dev->max_learned_pulse;
int ret = 0;
if (r_dev->pulses) {
dev_info(chip->dev, "ir learning pulse already exists\n");
return ret;
}
r_dev->pulses = kzalloc(sizeof(*r_dev->pulses) +
len * sizeof(unsigned int), GFP_KERNEL);
if (!r_dev->pulses) {
dev_err(chip->dev, "ir learning pulse alloc err\n");
ret = -ENOMEM;
}
return ret;
}
void meson_ir_pulses_free(struct meson_ir_chip *chip)
{
struct meson_ir_dev *r_dev = chip->r_dev;
kfree(r_dev->pulses);
r_dev->pulses = NULL;
}
int meson_ir_scancode_sort(struct ir_map_tab *ir_map)
{
bool is_sorted;
u32 tmp;
int i;
int j;
for (i = 0; i < ir_map->map_size - 1; i++) {
is_sorted = true;
for (j = 0; j < ir_map->map_size - i - 1; j++) {
if (ir_map->codemap[j].map.scancode >
ir_map->codemap[j + 1].map.scancode) {
is_sorted = false;
tmp = ir_map->codemap[j].code;
ir_map->codemap[j].code =
ir_map->codemap[j + 1].code;
ir_map->codemap[j + 1].code = tmp;
}
}
if (is_sorted)
break;
}
return 0;
}
struct meson_ir_map_tab_list *meson_ir_seek_map_tab(struct meson_ir_chip *chip,
int custom_code)
{
struct meson_ir_map_tab_list *ir_map = NULL;
list_for_each_entry(ir_map, &chip->map_tab_head, list) {
if (ir_map->tab.custom_code == custom_code)
return ir_map;
}
return NULL;
}
void meson_ir_map_tab_list_free(struct meson_ir_chip *chip)
{
struct meson_ir_map_tab_list *ir_map, *t_map;
list_for_each_entry_safe(ir_map, t_map, &chip->map_tab_head, list) {
list_del(&ir_map->list);
kfree((void *)ir_map);
}
}
void meson_ir_tab_free(struct meson_ir_map_tab_list *ir_map_list)
{
kfree((void *)ir_map_list);
ir_map_list = NULL;
}
static int meson_ir_lookup_by_scancode(struct ir_map_tab *ir_map,
unsigned int scancode)
{
int start = 0;
int end = ir_map->map_size - 1;
int mid;
while (start <= end) {
mid = (start + end) >> 1;
if (ir_map->codemap[mid].map.scancode < scancode)
start = mid + 1;
else if (ir_map->codemap[mid].map.scancode > scancode)
end = mid - 1;
else
return mid;
}
return -ENODATA;
}
static int meson_ir_report_rel(struct meson_ir_dev *dev, u32 scancode,
int status)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)dev->platform_data;
struct meson_ir_map_tab_list *ct = chip->cur_tab;
static u32 repeat_count;
s32 cursor_value = 0;
u32 valid_scancode;
u16 mouse_code;
int cnt;
s32 move_accelerate[] = CURSOR_MOVE_ACCELERATE;
/*nothing need to do in normal mode*/
if (!ct || ct->ir_dev_mode != MOUSE_MODE)
return -EINVAL;
if (status == IR_STATUS_REPEAT) {
valid_scancode = dev->last_scancode;
repeat_count++;
if (repeat_count > ARRAY_SIZE(move_accelerate) - 1)
repeat_count = ARRAY_SIZE(move_accelerate) - 1;
} else {
valid_scancode = scancode;
dev->last_scancode = scancode;
repeat_count = 0;
}
if (valid_scancode == ct->tab.cursor_code.cursor_left_scancode) {
cursor_value = -(1 + move_accelerate[repeat_count]);
mouse_code = REL_X;
} else if (valid_scancode ==
ct->tab.cursor_code.cursor_right_scancode) {
cursor_value = 1 + move_accelerate[repeat_count];
mouse_code = REL_X;
} else if (valid_scancode ==
ct->tab.cursor_code.cursor_up_scancode) {
cursor_value = -(1 + move_accelerate[repeat_count]);
mouse_code = REL_Y;
} else if (valid_scancode ==
ct->tab.cursor_code.cursor_down_scancode) {
cursor_value = 1 + move_accelerate[repeat_count];
mouse_code = REL_Y;
} else {
return -EINVAL;
}
if (DECIDE_VENDOR_TA_ID) {
for (cnt = 0; cnt <= chip->input_cnt; cnt++) {
if (chip->search_id[cnt] == ct->tab.vendor)
break;
}
if (cnt > chip->input_cnt) {
dev_err(chip->dev, "vdndor ID Configuration error\n");
dev_err(chip->dev, "vendor = %x, product = %x, version = %x\n",
ct->tab.vendor, ct->tab.product, ct->tab.version);
return 0;
}
input_event(chip->r_dev->input_device_ots[cnt], EV_REL,
mouse_code, cursor_value);
input_sync(chip->r_dev->input_device_ots[cnt]);
} else {
input_event(chip->r_dev->input_device, EV_REL,
mouse_code, cursor_value);
input_sync(chip->r_dev->input_device);
}
meson_ir_dbg(chip->r_dev, "mouse cursor be %s moved %d.\n",
mouse_code == REL_X ? "horizontal" : "vertical",
cursor_value);
return 0;
}
static u32 meson_ir_getkeycode(struct meson_ir_dev *dev, u32 scancode)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)dev->platform_data;
struct meson_ir_map_tab_list *ct = chip->cur_tab;
int index;
if (!ct) {
dev_err(chip->dev, "cur_custom is nulll\n");
return KEY_RESERVED;
}
/*return BTN_LEFT in mouse mode*/
if (ct->ir_dev_mode == MOUSE_MODE &&
scancode == ct->tab.cursor_code.cursor_ok_scancode) {
meson_ir_dbg(chip->r_dev, "mouse left button scancode: 0x%x",
BTN_LEFT);
return BTN_LEFT;
}
index = meson_ir_lookup_by_scancode(&ct->tab, scancode);
if (index < 0) {
dev_err(chip->dev, "scancode %d undefined\n", scancode);
return KEY_RESERVED;
}
/*save IR remote-control work mode*/
if (!dev->keypressed &&
scancode == ct->tab.cursor_code.fn_key_scancode) {
if (ct->ir_dev_mode == NORMAL_MODE)
ct->ir_dev_mode = MOUSE_MODE;
else
ct->ir_dev_mode = NORMAL_MODE;
dev_info(chip->dev, "IR remote control[ID: 0x%x] switch to %s\n",
ct->tab.custom_code, ct->ir_dev_mode ?
"mouse mode" : "normal mode");
}
return ct->tab.codemap[index].map.keycode;
}
static bool meson_ir_is_valid_custom(struct meson_ir_dev *dev)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)dev->platform_data;
int custom_code;
if (!chip->ir_contr[chip->ir_work].get_custom_code)
return true;
custom_code = chip->ir_contr[chip->ir_work].get_custom_code(chip);
chip->cur_tab = meson_ir_seek_map_tab(chip, custom_code);
if (chip->cur_tab) {
dev->keyup_delay = chip->cur_tab->tab.release_delay;
return true;
}
return false;
}
static bool meson_ir_is_next_repeat(struct meson_ir_dev *dev)
{
unsigned int val = 0;
unsigned char fbusy = 0;
unsigned char cnt;
struct meson_ir_chip *chip = (struct meson_ir_chip *)dev->platform_data;
for (cnt = 0; cnt < (ENABLE_LEGACY_IR(chip->protocol) ? 2 : 1); cnt++) {
regmap_read(chip->ir_contr[cnt].base, REG_STATUS, &val);
fbusy |= IR_CONTROLLER_BUSY(val);
}
meson_ir_dbg(chip->r_dev, "ir controller busy flag = %d\n", fbusy);
if (!dev->wait_next_repeat && fbusy)
return true;
else
return false;
}
static bool meson_ir_set_custom_code(struct meson_ir_dev *dev, u32 code)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)dev->platform_data;
return chip->ir_contr[chip->ir_work].set_custom_code(chip, code);
}
static void meson_ir_tasklet(unsigned long data)
{
struct meson_ir_chip *chip = (struct meson_ir_chip *)data;
unsigned long flags;
int status = -1;
int scancode = -1;
/**
*need first get_scancode, then get_decode_status, the status
*may was set flag from get_scancode function
*/
spin_lock_irqsave(&chip->slock, flags);
if (chip->ir_contr[chip->ir_work].get_scancode)
scancode = chip->ir_contr[chip->ir_work].get_scancode(chip);
if (chip->ir_contr[chip->ir_work].get_decode_status)
status = chip->ir_contr[chip->ir_work].get_decode_status(chip);
switch (status) {
case IR_STATUS_NORMAL:
meson_ir_dbg(chip->r_dev, "receive scancode=0x%x\n", scancode);
meson_ir_keydown(chip->r_dev, scancode, status);
break;
case IR_STATUS_REPEAT:
meson_ir_dbg(chip->r_dev, "receive repeat\n");
meson_ir_keydown(chip->r_dev, scancode, status);
break;
case IR_STATUS_CUSTOM_DATA:
case IR_STATUS_CHECKSUM_ERROR:
dev_err(chip->dev, "decoder error %d\n", status);
break;
default:
dev_err(chip->dev, "receive error %d\n", status);
break;
}
spin_unlock_irqrestore(&chip->slock, flags);
}
static irqreturn_t meson_ir_interrupt(int irq, void *dev_id)
{
struct meson_ir_chip *rc = (struct meson_ir_chip *)dev_id;
struct meson_ir_dev *r_dev = rc->r_dev;
int contr_status = 0;
int val = 0;
u32 duration;
unsigned char cnt;
enum raw_event_type type = RAW_SPACE;
regmap_read(rc->ir_contr[MULTI_IR_ID].base, REG_REG1, &val);
val = (val & GENMASK(28, 16)) >> 16;
if (r_dev->ir_learning_on) {
if (r_dev->pulses->len >= r_dev->max_learned_pulse)
return IRQ_HANDLED;
/*get pulse durations*/
r_dev->pulses->pulse[r_dev->pulses->len] = val & GENMASK(30, 0);
/*get pulse level*/
regmap_read(rc->ir_contr[MULTI_IR_ID].base, REG_STATUS, &val);
val = !!((val >> 8) & BIT(1));
r_dev->pulses->pulse[r_dev->pulses->len] &= ~BIT(31);
if (val)
r_dev->pulses->pulse[r_dev->pulses->len] |= BIT(31);
r_dev->pulses->len++;
return IRQ_HANDLED;
}
/**
*software decode multiple protocols by using Time Measurement of
*multif-format IR controller
*/
if (MULTI_IR_SOFTWARE_DECODE(rc->protocol)) {
rc->ir_work = MULTI_IR_ID;
duration = val * 10 * 1000;
type = RAW_PULSE;
meson_ir_raw_event_store_edge(rc->r_dev, type, duration);
meson_ir_raw_event_handle(rc->r_dev);
} else {
for (cnt = 0; cnt < (ENABLE_LEGACY_IR(rc->protocol)
? 2 : 1); cnt++) {
regmap_read(rc->ir_contr[cnt].base, REG_STATUS,
&contr_status);
if (IR_DATA_IS_VALID(contr_status)) {
rc->ir_work = cnt;
tasklet_schedule(&tasklet);
return IRQ_HANDLED;
}
}
if (cnt == (ENABLE_LEGACY_IR(rc->protocol) ? 2 : 1)) {
dev_err(rc->dev, "invalid interrupt.\n");
return IRQ_HANDLED;
}
}
return IRQ_HANDLED;
}
static int meson_ir_get_custom_tables(struct device_node *node,
struct meson_ir_chip *chip)
{
const phandle *phandle;
struct device_node *custom_maps, *map;
u32 value;
int ret = -1;
int index;
int cnt = 0;
int cnl;
char *propname;
const char *uname;
unsigned long flags;
struct meson_ir_map_tab_list *ptable;
phandle = of_get_property(node, "map", NULL);
if (!phandle) {
dev_err(chip->dev, "%s:can't find match custom\n", __func__);
return -EINVAL;
}
custom_maps = of_find_node_by_phandle(be32_to_cpup(phandle));
if (!custom_maps) {
dev_err(chip->dev, "can't find device node key\n");
return -ENODATA;
}
ret = of_property_read_u32(custom_maps, "mapnum", &value);
if (ret) {
dev_err(chip->dev, "please config correct mapnum item\n");
return -ENODATA;
}
chip->custom_num = value;
if (chip->custom_num > CUSTOM_NUM_MAX)
chip->custom_num = CUSTOM_NUM_MAX;
for (index = 0; index < chip->custom_num; index++) {
propname = kasprintf(GFP_KERNEL, "map%d", index);
phandle = of_get_property(custom_maps, propname, NULL);
/* propname is never used just use to find phandle once
* To avoid kmemleak warning, should be freed
*/
kfree(propname);
propname = NULL;
if (!phandle) {
dev_err(chip->dev, "can't find match map%d\n", index);
return -ENODATA;
}
map = of_find_node_by_phandle(be32_to_cpup(phandle));
if (!map) {
dev_err(chip->dev, "can't find device node key\n");
return -ENODATA;
}
ret = of_property_read_u32(map, "size", &value);
if (ret || value > MAX_KEYMAP_SIZE) {
dev_err(chip->dev, "no config size item or err\n");
return -ENODATA;
}
/*alloc memory*/
ptable = kzalloc(sizeof(*ptable) +
value * sizeof(union _codemap), GFP_KERNEL);
if (!ptable)
return -ENOMEM;
ptable->tab.map_size = value;
ret = of_property_read_string(map, "mapname", &uname);
if (ret) {
dev_err(chip->dev, "please config mapname item\n");
goto err;
}
snprintf(ptable->tab.custom_name, CUSTOM_NAME_LEN, "%s", uname);
ret = of_property_read_u32(map, "customcode", &value);
if (ret) {
dev_err(chip->dev, "please config customcode item\n");
goto err;
}
ptable->tab.custom_code = value;
ret = of_property_read_u32(map, "release_delay", &value);
if (ret) {
dev_err(chip->dev,
"can't find the node <release_delay>\n");
goto err;
}
ptable->tab.release_delay = value;
ret = of_property_read_u32(map, "vendor", &value);
if (ret)
value = 0;
chip->vendor = value;
ptable->tab.vendor = chip->vendor;
ret = of_property_read_u32(map, "product", &value);
if (ret)
value = 0;
chip->product = value;
ptable->tab.product = chip->product;
ret = of_property_read_u32(map, "version", &value);
if (ret)
value = 0;
chip->version = value;
ptable->tab.version = chip->version;
if (DECIDE_VENDOR_CHIP_ID) {
if (cnt == 0 || cnl != chip->vendor) {
chip->r_dev->input_device_ots[cnt] =
devm_input_allocate_device(chip->dev);
input_set_drvdata(chip->r_dev->input_device_ots[cnt], chip->r_dev);
meson_ir_input_device_ots_init(chip->r_dev->input_device_ots[cnt],
chip->dev, chip, "ir_keypad1", cnt);
meson_ir_input_ots_configure(chip->r_dev, cnt);
chip->input_cnt = cnt;
chip->search_id[cnt] = chip->vendor;
ret = input_register_device(chip->r_dev->input_device_ots[cnt]);
if (ret < 0)
return ret;
}
cnl = chip->vendor;
cnt++;
}
ret = of_property_read_u32_array(map, "keymap",
(u32 *)&ptable->tab.codemap[0],
ptable->tab.map_size);
if (ret) {
dev_err(chip->dev, "please config keymap item\n");
goto err;
}
memset(&ptable->tab.cursor_code, 0xff,
sizeof(struct cursor_codemap));
meson_ir_scancode_sort(&ptable->tab);
/*insert list*/
spin_lock_irqsave(&chip->slock, flags);
list_add_tail(&ptable->list, &chip->map_tab_head);
spin_unlock_irqrestore(&chip->slock, flags);
}
return 0;
err:
meson_ir_tab_free(ptable);
return -ENODATA;
}
static struct regmap_config meson_ir_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int meson_ir_get_devtree_pdata(struct platform_device *pdev)
{
struct resource *res_irq;
struct resource *res_mem;
struct regmap *reg_base;
resource_size_t *res_start[2];
int ret;
int value = 0;
unsigned char i;
struct meson_ir_chip *chip = platform_get_drvdata(pdev);
ret = of_property_read_u32(pdev->dev.of_node,
"protocol", &chip->protocol);
if (ret) {
dev_err(chip->dev, "can't find the node <protocol>\n");
chip->protocol = 1;
}
ret = of_property_read_u32(pdev->dev.of_node,
"led_blink", &chip->r_dev->led_blink);
if (ret)
chip->r_dev->led_blink = 0;
ret = of_property_read_u32(pdev->dev.of_node,
"led_blink_frq", &value);
if (ret) {
chip->r_dev->delay_on = DEFAULT_LED_BLINK_FRQ;
chip->r_dev->delay_off = DEFAULT_LED_BLINK_FRQ;
} else {
chip->r_dev->delay_off = value;
chip->r_dev->delay_on = value;
}
for (i = 0; i < 2; i++) {
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (IS_ERR_OR_NULL(res_mem)) {
dev_err(chip->dev, "get IORESOURCE_MEM error, %ld\n",
PTR_ERR(res_mem));
return PTR_ERR(res_mem);
}
res_start[i] = devm_ioremap_resource(&pdev->dev, res_mem);
chip->ir_contr[i].remote_regs = (void __iomem *)res_start[i];
meson_ir_regmap_config.max_register =
resource_size(res_mem) - 4;
meson_ir_regmap_config.name = devm_kasprintf(&pdev->dev,
GFP_KERNEL,
"%d", i);
reg_base = devm_regmap_init_mmio(&pdev->dev, res_start[i],
&meson_ir_regmap_config);
if (IS_ERR(reg_base)) {
dev_err(chip->dev, "regmap init error, %ld\n",
PTR_ERR(reg_base));
return PTR_ERR(reg_base);
}
chip->ir_contr[i].base = reg_base;
}
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (IS_ERR_OR_NULL(res_irq)) {
dev_err(chip->dev, "get IORESOURCE_IRQ error, %ld\n",
PTR_ERR(res_irq));
return PTR_ERR(res_irq);
}
chip->irqno = res_irq->start;
ret = of_property_read_u32(pdev->dev.of_node,
"max_frame_time", &value);
if (ret) {
dev_err(chip->dev, "can't find the node <max_frame_time>\n");
value = 200; /*default value*/
}
chip->r_dev->max_frame_time = value;
/*create map table */
ret = meson_ir_get_custom_tables(pdev->dev.of_node, chip);
if (ret < 0)
return ret;
return 0;
}
static int meson_ir_hardware_init(struct platform_device *pdev)
{
int ret;
struct meson_ir_chip *chip = platform_get_drvdata(pdev);
if (!pdev->dev.of_node) {
dev_err(chip->dev, "pdev->dev.of_node == NULL!\n");
return -ENODATA;
}
ret = meson_ir_get_devtree_pdata(pdev);
if (ret < 0)
return ret;
chip->set_register_config(chip, chip->protocol);
ret = devm_request_irq(&pdev->dev, chip->irqno, meson_ir_interrupt,
IRQF_SHARED, "meson_ir", (void *)chip);
if (ret < 0) {
dev_err(chip->dev, "request_irq error %d\n", ret);
return -ENODEV;
}
chip->irq_cpumask = 1;
irq_set_affinity_hint(chip->irqno, cpumask_of(chip->irq_cpumask));
tasklet.data = (unsigned long)chip;
tasklet_enable(&tasklet);
return 0;
}
static void meson_ir_input_device_init(struct input_dev *dev,
struct device *parent, const char *name)
{
dev->name = name;
dev->phys = "keypad/input0";
dev->dev.parent = parent;
dev->id.bustype = BUS_ISA;
dev->id.vendor = 0x0001;
dev->id.product = 0x0001;
dev->id.version = 0x0100;
dev->rep[REP_DELAY] = 0xffffffff; /*close input repeat*/
dev->rep[REP_PERIOD] = 0xffffffff; /*close input repeat*/
}
void meson_ir_input_device_ots_init(struct input_dev *dev,
struct device *parent, struct meson_ir_chip *chip, const char *name, int cnt0)
{
chip->r_dev->input_device_ots[cnt0]->name = "ir_keypad1";
chip->r_dev->input_device_ots[cnt0]->phys = "keypad/input0";
chip->r_dev->input_device_ots[cnt0]->dev.parent = chip->dev;
chip->r_dev->input_device_ots[cnt0]->id.bustype = BUS_ISA;
chip->r_dev->input_device_ots[cnt0]->id.vendor = chip->vendor;
chip->r_dev->input_device_ots[cnt0]->id.product = chip->product;
chip->r_dev->input_device_ots[cnt0]->id.version = chip->version;
chip->r_dev->input_device_ots[cnt0]->rep[REP_DELAY] = 0xffffffff;
chip->r_dev->input_device_ots[cnt0]->rep[REP_PERIOD] = 0xffffffff;
}
static int meson_ir_probe(struct platform_device *pdev)
{
struct meson_ir_dev *dev;
struct meson_ir_chip *chip;
int ret;
int id;
chip = devm_kzalloc(&pdev->dev, sizeof(struct meson_ir_chip),
GFP_KERNEL);
if (!chip)
return -ENOMEM;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->input_device = devm_input_allocate_device(&pdev->dev);
if (!dev->input_device)
return -ENOMEM;
input_set_drvdata(dev->input_device, dev);
spin_lock_init(&dev->keylock);
dev->wait_next_repeat = 0;
dev->enable = 1;
mutex_init(&chip->file_lock);
spin_lock_init(&chip->slock);
INIT_LIST_HEAD(&chip->map_tab_head);
chip->r_dev = dev;
chip->dev = &pdev->dev;
chip->rx_count = 0;
chip->r_dev->dev = &pdev->dev;
chip->r_dev->platform_data = (void *)chip;
chip->r_dev->getkeycode = meson_ir_getkeycode;
chip->r_dev->report_rel = meson_ir_report_rel;
chip->r_dev->set_custom_code = meson_ir_set_custom_code;
chip->r_dev->is_valid_custom = meson_ir_is_valid_custom;
chip->r_dev->is_next_repeat = meson_ir_is_next_repeat;
chip->r_dev->max_learned_pulse = MAX_LEARNED_PULSE;
chip->set_register_config = meson_ir_register_default_config;
platform_set_drvdata(pdev, chip);
meson_ir_input_device_init(dev->input_device, &pdev->dev, "ir_keypad");
meson_ir_input_configure(dev);
ret = input_register_device(dev->input_device);
if (ret < 0)
return ret;
ret = meson_ir_hardware_init(pdev);
if (ret < 0)
return ret;
timer_setup(&dev->timer_keyup, meson_ir_timer_keyup, 0);
ret = meson_ir_cdev_init(chip);
if (ret < 0)
return ret;
dev->rc_type = chip->protocol;
device_init_wakeup(&pdev->dev, true);
dev_pm_set_wake_irq(&pdev->dev, chip->irqno);
led_trigger_register_simple("ir_led", &dev->led_feedback);
if (MULTI_IR_SOFTWARE_DECODE(dev->rc_type))
meson_ir_raw_event_register(dev);
if (disable_ir) {
for (id = 0; id < (ENABLE_LEGACY_IR(chip->protocol) ? 2 : 1); id++) {
regmap_update_bits(chip->ir_contr[id].base, REG_REG1,
BIT(15), 0);
}
}
return 0;
}
static int meson_ir_remove(struct platform_device *pdev)
{
struct meson_ir_chip *chip = platform_get_drvdata(pdev);
tasklet_disable(&tasklet);
tasklet_kill(&tasklet);
if (MULTI_IR_SOFTWARE_DECODE(chip->r_dev->rc_type))
meson_ir_raw_event_unregister(chip->r_dev);
led_trigger_unregister_simple(chip->r_dev->led_feedback);
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);
meson_ir_cdev_free(chip);
input_unregister_device(chip->r_dev->input_device);
meson_ir_map_tab_list_free(chip);
irq_set_affinity_hint(chip->irqno, NULL);
return 0;
}
static int meson_ir_resume(struct device *dev)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
unsigned int val;
unsigned long flags;
unsigned char cnt;
/*resume register config*/
spin_lock_irqsave(&chip->slock, flags);
chip->set_register_config(chip, chip->protocol);
/* read REG_STATUS and REG_FRAME to clear status */
for (cnt = 0; cnt < (ENABLE_LEGACY_IR(chip->protocol) ? 2 : 1); cnt++) {
regmap_read(chip->ir_contr[cnt].base, REG_STATUS, &val);
regmap_read(chip->ir_contr[cnt].base, REG_FRAME, &val);
}
spin_unlock_irqrestore(&chip->slock, flags);
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
if (get_resume_method() == REMOTE_WAKEUP) {
input_event(chip->r_dev->input_device,
EV_KEY, KEY_POWER, 1);
input_sync(chip->r_dev->input_device);
input_event(chip->r_dev->input_device,
EV_KEY, KEY_POWER, 0);
input_sync(chip->r_dev->input_device);
}
if (get_resume_method() == REMOTE_CUS_WAKEUP) {
input_event(chip->r_dev->input_device, EV_KEY, 133, 1);
input_sync(chip->r_dev->input_device);
input_event(chip->r_dev->input_device, EV_KEY, 133, 0);
input_sync(chip->r_dev->input_device);
}
#endif
irq_set_affinity_hint(chip->irqno, cpumask_of(chip->irq_cpumask));
enable_irq(chip->irqno);
return 0;
}
static int meson_ir_suspend(struct device *dev)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
disable_irq(chip->irqno);
return 0;
}
static const struct of_device_id meson_ir_dt_match[] = {
{
.compatible = "amlogic, meson-ir",
},
{},
};
#ifdef CONFIG_PM
static const struct dev_pm_ops meson_ir_pm_ops = {
.suspend_late = meson_ir_suspend,
.resume_early = meson_ir_resume,
};
#endif
static struct platform_driver meson_ir_driver = {
.probe = meson_ir_probe,
.remove = meson_ir_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = meson_ir_dt_match,
#ifdef CONFIG_PM
.pm = &meson_ir_pm_ops,
#endif
},
};
static int __init meson_ir_driver_init(void)
{
int ret;
ret = meson_ir_xmp_decode_init();
if (ret)
return ret;
return platform_driver_register(&meson_ir_driver);
}
module_init(meson_ir_driver_init);
static void __exit meson_ir_driver_exit(void)
{
meson_ir_xmp_decode_exit();
platform_driver_unregister(&meson_ir_driver);
}
module_exit(meson_ir_driver_exit);
MODULE_AUTHOR("AMLOGIC");
MODULE_DESCRIPTION("AMLOGIC IR DRIVER");
MODULE_LICENSE("GPL v2");