/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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");