blob: 320f410428c59c513e6b704fe5924635d4ca3992 [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/uaccess.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include "meson_ir_main.h"
static ssize_t protocol_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
if (ENABLE_LEGACY_IR(chip->protocol))
return sprintf(buf, "protocol=%s&%s (0x%x)\n",
chip->ir_contr[LEGACY_IR_ID].proto_name,
chip->ir_contr[MULTI_IR_ID].proto_name,
chip->protocol);
return sprintf(buf, "protocol=%s (0x%x)\n",
chip->ir_contr[MULTI_IR_ID].proto_name,
chip->protocol);
}
static ssize_t protocol_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
int ret;
int protocol;
unsigned long flags;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
ret = kstrtoint(buf, 0, &protocol);
if (ret != 0) {
dev_err(chip->dev, "input parameter error\n");
return -EINVAL;
}
spin_lock_irqsave(&chip->slock, flags);
chip->protocol = protocol;
chip->set_register_config(chip, chip->protocol);
spin_unlock_irqrestore(&chip->slock, flags);
if (MULTI_IR_SOFTWARE_DECODE(chip->r_dev->rc_type) &&
!MULTI_IR_SOFTWARE_DECODE(chip->protocol)) {
meson_ir_raw_event_unregister(chip->r_dev); /*raw->no raw*/
dev_info(chip->dev, "meson_ir_raw_event_unregister\n");
} else if (!MULTI_IR_SOFTWARE_DECODE(chip->r_dev->rc_type) &&
MULTI_IR_SOFTWARE_DECODE(chip->protocol)) {
meson_ir_raw_event_register(chip->r_dev); /*no raw->raw*/
}
chip->r_dev->rc_type = chip->protocol;
return count;
}
static ssize_t keymap_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_map_tab_list *map_tab;
unsigned long flags;
int i, len;
spin_lock_irqsave(&chip->slock, flags);
map_tab = meson_ir_seek_map_tab(chip, chip->sys_custom_code);
if (!map_tab) {
dev_err(chip->dev, "please set valid keymap name first\n");
spin_unlock_irqrestore(&chip->slock, flags);
return 0;
}
len = sprintf(buf, "custom_code=0x%x\n", map_tab->tab.custom_code);
len += sprintf(buf + len, "custom_name=%s\n", map_tab->tab.custom_name);
len += sprintf(buf + len, "release_delay=%d\n",
map_tab->tab.release_delay);
len += sprintf(buf + len, "map_size=%d\n", map_tab->tab.map_size);
len += sprintf(buf + len, "fn_key_scancode = 0x%x\n",
map_tab->tab.cursor_code.fn_key_scancode);
len += sprintf(buf + len, "cursor_left_scancode = 0x%x\n",
map_tab->tab.cursor_code.cursor_left_scancode);
len += sprintf(buf + len, "cursor_right_scancode = 0x%x\n",
map_tab->tab.cursor_code.cursor_right_scancode);
len += sprintf(buf + len, "cursor_up_scancode = 0x%x\n",
map_tab->tab.cursor_code.cursor_up_scancode);
len += sprintf(buf + len, "cursor_down_scancode = 0x%x\n",
map_tab->tab.cursor_code.cursor_down_scancode);
len += sprintf(buf + len, "cursor_ok_scancode = 0x%x\n",
map_tab->tab.cursor_code.cursor_ok_scancode);
len += sprintf(buf + len, "keycode scancode\n");
for (i = 0; i < map_tab->tab.map_size; i++) {
len += sprintf(buf + len, "%4d %4d\n",
map_tab->tab.codemap[i].map.keycode,
map_tab->tab.codemap[i].map.scancode);
}
spin_unlock_irqrestore(&chip->slock, flags);
return len;
}
static ssize_t keymap_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
int ret;
int value;
ret = kstrtoint(buf, 0, &value);
if (ret != 0) {
dev_err(chip->dev, "keymap store input err\n");
return -EINVAL;
}
chip->sys_custom_code = value;
return count;
}
static ssize_t debug_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
return sprintf(buf, "%d\n", r_dev->debug_enable);
}
static ssize_t debug_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
int debug_enable;
int ret;
ret = kstrtoint(buf, 0, &debug_enable);
if (ret != 0)
return -EINVAL;
r_dev->debug_enable = debug_enable;
return count;
}
static ssize_t enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
return sprintf(buf, "%d\n", r_dev->enable);
}
static ssize_t enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
unsigned int val;
int enable;
int ret;
int id;
ret = kstrtoint(buf, 0, &enable);
if (ret != 0 || (enable != 0 && enable != 1))
return -EINVAL;
for (id = 0; id < (ENABLE_LEGACY_IR(chip->protocol) ? 2 : 1); id++) {
if (enable) {
regmap_read(chip->ir_contr[id].base, REG_FRAME, &val);
regmap_update_bits(chip->ir_contr[id].base, REG_REG1,
BIT(15), BIT(15));
} else {
regmap_update_bits(chip->ir_contr[id].base, REG_REG1,
BIT(15), 0);
}
}
r_dev->enable = enable;
return count;
}
static ssize_t map_tables_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_map_tab_list *ir_map;
unsigned long flags;
int len = 0;
int cnt = 0;
spin_lock_irqsave(&chip->slock, flags);
list_for_each_entry(ir_map, &chip->map_tab_head, list) {
len += sprintf(buf + len, "%d. 0x%x,%s\n", cnt,
ir_map->tab.custom_code,
ir_map->tab.custom_name);
cnt++;
}
spin_unlock_irqrestore(&chip->slock, flags);
return len;
}
static ssize_t led_blink_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
return sprintf(buf, "%u\n", r_dev->led_blink);
}
static ssize_t led_blink_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
int ret = 0;
int val = 0;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
ret = kstrtoint(buf, 0, &val);
if (ret != 0)
return -EINVAL;
r_dev->led_blink = val;
return count;
}
static ssize_t led_frq_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
return sprintf(buf, "%ld\n", r_dev->delay_on);
}
static ssize_t led_frq_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
int val = 0;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
ret = kstrtoint(buf, 0, &val);
if (ret != 0)
return -EINVAL;
r_dev->delay_off = val;
r_dev->delay_on = val;
return count;
}
static ssize_t ir_learning_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
return sprintf(buf, "%d\n", r_dev->ir_learning_on);
}
static ssize_t ir_learning_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
int val = 0;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
ret = kstrtoint(buf, 0, &val);
if (ret != 0)
return -EINVAL;
if (r_dev->ir_learning_on == !!val)
return count;
disable_irq(chip->irqno);
mutex_lock(&chip->file_lock);
r_dev->ir_learning_on = !!val;
if (!!val) {
chip->set_register_config(chip, REMOTE_TYPE_RAW_NEC);
r_dev->protocol = chip->protocol;
chip->protocol = REMOTE_TYPE_RAW_NEC;
if (meson_ir_pulses_malloc(chip) < 0) {
mutex_unlock(&chip->file_lock);
enable_irq(chip->irqno);
return -ENOMEM;
}
} else {
chip->protocol = r_dev->protocol;
chip->set_register_config(chip, chip->protocol);
meson_ir_pulses_free(chip);
}
mutex_unlock(&chip->file_lock);
enable_irq(chip->irqno);
return count;
}
static ssize_t learned_pulse_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int len = 0;
int i = 0;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
if (!r_dev->pulses)
return len;
disable_irq(chip->irqno);
mutex_lock(&chip->file_lock);
for (i = 0; i < r_dev->pulses->len; i++)
len += sprintf(buf + len, "%lds",
r_dev->pulses->pulse[i] & GENMASK(30, 0));
mutex_unlock(&chip->file_lock);
enable_irq(chip->irqno);
len += sprintf(buf + len, "\n");
return len;
}
static ssize_t learned_pulse_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int len = 0;
struct meson_ir_chip *chip = dev_get_drvdata(dev);
struct meson_ir_dev *r_dev = chip->r_dev;
if (!r_dev->pulses)
return len;
/*clear learned pulse*/
if (buf[0] == 'c')
memset(r_dev->pulses, 0, sizeof(struct pulse_group) +
r_dev->max_learned_pulse * sizeof(u32));
return count;
}
DEVICE_ATTR_RW(learned_pulse);
DEVICE_ATTR_RW(ir_learning);
DEVICE_ATTR_RW(led_frq);
DEVICE_ATTR_RW(led_blink);
DEVICE_ATTR_RW(protocol);
DEVICE_ATTR_RW(keymap);
DEVICE_ATTR_RW(debug_enable);
DEVICE_ATTR_RW(enable);
DEVICE_ATTR_RO(map_tables);
static struct attribute *meson_ir_sysfs_attrs[] = {
&dev_attr_protocol.attr,
&dev_attr_map_tables.attr,
&dev_attr_keymap.attr,
&dev_attr_debug_enable.attr,
&dev_attr_enable.attr,
&dev_attr_led_blink.attr,
&dev_attr_led_frq.attr,
&dev_attr_ir_learning.attr,
&dev_attr_learned_pulse.attr,
NULL,
};
ATTRIBUTE_GROUPS(meson_ir_sysfs);
static struct class meson_ir_class = {
.name = "remote",
.owner = THIS_MODULE,
.dev_groups = meson_ir_sysfs_groups,
};
int meson_ir_sysfs_init(struct meson_ir_chip *chip)
{
struct device *dev;
int err;
err = class_register(&meson_ir_class);
if (unlikely(err))
return err;
dev = device_create(&meson_ir_class, NULL, chip->chr_devno, chip,
chip->dev_name);
if (IS_ERR(dev))
return PTR_ERR(dev);
return 0;
}
EXPORT_SYMBOL_GPL(meson_ir_sysfs_init);
void meson_ir_sysfs_exit(struct meson_ir_chip *chip)
{
device_destroy(&meson_ir_class, chip->chr_devno);
class_unregister(&meson_ir_class);
}
EXPORT_SYMBOL_GPL(meson_ir_sysfs_exit);