sdk-hwV1.3/lichee/linux-4.9/drivers/input/keyboard/sunxi-gpio-ir-tx.c

274 lines
6.2 KiB
C

/*
* drivers/input/keyboard/sunxi-gpio-ir-tx.c
*
* Copyright (c) 2013-2018 Allwinnertech Co., Ltd.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
#include <linux/pm.h>
#include <linux/arisc/arisc.h>
#include <asm/irq.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/sunxi-gpio.h>
#include <linux/of_gpio.h>
#include "sunxi-gpio-ir-tx.h"
/*DEBUG_INIT | DEBUG_INT | DEBUG_DATA_INFO | DEBUG_ERR;*/
static u32 debug_mask = 0x13;
#define dprintk(level_mask, fmt, arg...) \
do { \
if (unlikely(debug_mask & level_mask)) \
pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg); \
} while (0)
#define IR_TX_GPIO_NAME "ir_gpio_tx"
#define GPIO_IR_RX_DRIVER_NAME "sunxi-gpio-ir-tx"
#define GPIO_IR_HEADER_CODE0 0x04 /* Custom code 0*/
#define GPIO_IR_HEADER_CODE1 0xFB /* Custom code 1*/
#define CARRIER_1MS (10000/264)
#define CARRIER_9MS (90000/264)
#define CARRIER_560US (5600/264)
struct gpio_ir_tx_info {
unsigned int tx_gpio;
struct kobject *kobj;
};
struct gpio_ir_tx_info *tx_info;
static DEFINE_MUTEX(sysfs_lock);
static DEFINE_SPINLOCK(lock);
static DEFINE_SPINLOCK(carrier);
//·¢ËÍÔØ²¨
void sendcarrier(int unum)
{
int i;
unsigned long flags;
spin_lock_irqsave(&carrier, flags); //½ûÖ¹ÖжÏ
for (i = 0; i < unum; i++) {
gpio_set_value(tx_info->tx_gpio, 1);
udelay(13);
gpio_set_value(tx_info->tx_gpio, 0);
udelay(13);
ndelay(400);
}
gpio_set_value(tx_info->tx_gpio, 0);
spin_unlock_irqrestore(&carrier, flags);
}
void sendIrBit(unsigned char bit)
{
sendcarrier(CARRIER_560US);
udelay(bit == 0x00 ? 560 : 1690);
}
void sendIrByte(unsigned char data)
{
unsigned char uMask = 0x01;
while (uMask) {
/*low bit first*/
sendIrBit(data & uMask);
uMask <<= 1;
}
}
void sendIrInt(unsigned int data)
{
unsigned int uMask = 0x01;
while (uMask) {
sendIrBit(data & uMask);
uMask <<= 1;
}
}
static ssize_t gpio_ir_send_code(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int key, keyOpposite;
unsigned long flags;
dprintk(DEBUG_DATA_INFO, "gpio send buf: %s", buf);
if (sscanf(buf, "%d", &key) != 1) {
dprintk(DEBUG_ERR, "error input key code\n");
return -EINVAL;
}
keyOpposite = ~key;
dprintk(DEBUG_DATA_INFO, "key 0x%x, keyOpp 0x%x\n", key, keyOpposite);
spin_lock_irqsave(&lock, flags);
/*leader code*/
sendcarrier(CARRIER_9MS);
mdelay(4);
udelay(500);
/*addr code*/
sendIrByte(GPIO_IR_HEADER_CODE0);
sendIrByte(GPIO_IR_HEADER_CODE1);
/*data code*/
sendIrByte((unsigned char)key);
sendIrByte((unsigned char)keyOpposite);
sendcarrier(CARRIER_560US);
spin_unlock_irqrestore(&lock, flags);
dprintk(DEBUG_DATA_INFO, "ir code send finished\n");
return size;
}
static const DEVICE_ATTR(ir_code, 0644, NULL, gpio_ir_send_code);
static ssize_t gpio_ir_send_repeat(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int i, num;
unsigned long flags;
dprintk(DEBUG_DATA_INFO, "ir test buf is %s", buf);
if (sscanf(buf, "%d", &num) != 1)
return -EINVAL;
dprintk(DEBUG_DATA_INFO, "repeat num is %d, tx pin %d\n",
num, tx_info->tx_gpio);
/*repeat code*/
for (i = 0; i < num; i++) {
spin_lock_irqsave(&lock, flags);
/*repeat leader code*/
sendcarrier(CARRIER_9MS);
mdelay(2);
udelay(250);
sendcarrier(CARRIER_560US);
spin_unlock_irqrestore(&lock, flags);
msleep(100);
}
return size;
}
static const DEVICE_ATTR(ir_repeat, 0644, NULL, gpio_ir_send_repeat);
static const struct attribute *ir_tx_attrs[] = {
&dev_attr_ir_code.attr,
&dev_attr_ir_repeat.attr,
NULL,
};
static const struct attribute_group tx_info_attrs_group = {
.attrs = (struct attribute **)ir_tx_attrs,
};
static int __init gpio_ir_tx_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpio_config config;
int ret = 0;
dprintk(DEBUG_INIT, "enter gpio ir rx probe\n");
tx_info = devm_kzalloc(dev, sizeof(struct gpio_ir_tx_info), GFP_KERNEL);
if (!tx_info) {
dev_err(dev, "can't allocate gpio ir-tx memory\n");
return -ENOMEM;
}
/*ir gpio tx pin*/
tx_info->tx_gpio = of_get_named_gpio_flags(dev->of_node,
"gpio-tx", 0,
(enum of_gpio_flags *)&config);
dprintk(DEBUG_INIT, "%s, line: %d, gpio ir tx: %d!\n",
__func__, __LINE__, tx_info->tx_gpio);
ret = devm_gpio_request(dev, tx_info->tx_gpio, "ir_tx_gpio");
if (ret < 0) {
dev_err(dev, "error: can't request ir tx gpio %d\n",
tx_info->tx_gpio);
goto err_free_mem;
}
ret = gpio_direction_output(tx_info->tx_gpio, 0);
if (ret < 0) {
dev_err(dev, "error: can't set output direction\n");
goto err_free_gpio;
}
tx_info->kobj = kobject_create_and_add(IR_TX_GPIO_NAME, NULL);
if (!tx_info->kobj)
goto err_free_gpio;
ret = sysfs_create_group(tx_info->kobj, &tx_info_attrs_group);
if (ret)
kobject_put(tx_info->kobj);
dprintk(DEBUG_INIT, "%s, line: %d succeed\n", __func__, __LINE__);
return ret;
err_free_gpio:
gpio_free(tx_info->tx_gpio);
err_free_mem:
devm_kfree(dev, tx_info);
return ret;
}
static int gpio_ir_tx_remove(struct platform_device *pdev)
{
kobject_put(tx_info->kobj);
gpio_free(tx_info->tx_gpio);
return 0;
}
static const struct of_device_id gpio_ir_tx_match[] = {
{ .compatible = "allwinner,gpio-ir-tx", },
{ },
};
MODULE_DEVICE_TABLE(of, gpio_ir_tx_match);
static struct platform_driver gpio_ir_tx_driver = {
.driver = {
.name = GPIO_IR_RX_DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = gpio_ir_tx_match,
},
.probe = gpio_ir_tx_probe,
.remove = gpio_ir_tx_remove,
};
module_platform_driver(gpio_ir_tx_driver);
module_param_named(debug_mask, debug_mask, int, 0644);
MODULE_DESCRIPTION("Remote GPIO IR TX driver");
MODULE_AUTHOR("xudong");
MODULE_LICENSE("GPL");