274 lines
6.2 KiB
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);
|
|||
|
|
|||
|
//<2F><><EFBFBD><EFBFBD><EFBFBD>ز<EFBFBD>
|
|||
|
void sendcarrier(int unum)
|
|||
|
{
|
|||
|
int i;
|
|||
|
unsigned long flags;
|
|||
|
|
|||
|
spin_lock_irqsave(&carrier, flags); //<2F><>ֹ<EFBFBD>ж<EFBFBD>
|
|||
|
|
|||
|
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");
|