Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] rc: add sunxi-ir driver

This patch adds driver for sunxi IR controller.
It is based on Alexsey Shestacov's work based on the original driver
supplied by Allwinner.

Signed-off-by: Alexander Bersenev <bay@hackerdom.ru>
Signed-off-by: Alexsey Shestacov <wingrime@linux-sunxi.org>
[hdegoede@redhat.com: Changed compatible to sun4i-a10-ir]
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>

authored by

Alexander Bersenev and committed by
Mauro Carvalho Chehab
b4e3e59f 22f44249

+329
+10
drivers/media/rc/Kconfig
··· 343 343 344 344 If you're not sure, select N here. 345 345 346 + config IR_SUNXI 347 + tristate "SUNXI IR remote control" 348 + depends on RC_CORE 349 + depends on ARCH_SUNXI 350 + ---help--- 351 + Say Y if you want to use sunXi internal IR Controller 352 + 353 + To compile this driver as a module, choose M here: the module will 354 + be called sunxi-ir. 355 + 346 356 endif #RC_DEVICES
+1
drivers/media/rc/Makefile
··· 32 32 obj-$(CONFIG_IR_IGUANA) += iguanair.o 33 33 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o 34 34 obj-$(CONFIG_RC_ST) += st_rc.o 35 + obj-$(CONFIG_IR_SUNXI) += sunxi-cir.o 35 36 obj-$(CONFIG_IR_IMG) += img-ir/
+318
drivers/media/rc/sunxi-cir.c
··· 1 + /* 2 + * Driver for Allwinner sunXi IR controller 3 + * 4 + * Copyright (C) 2014 Alexsey Shestacov <wingrime@linux-sunxi.org> 5 + * Copyright (C) 2014 Alexander Bersenev <bay@hackerdom.ru> 6 + * 7 + * Based on sun5i-ir.c: 8 + * Copyright (C) 2007-2012 Daniel Wang 9 + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> 10 + * 11 + * This program is free software; you can redistribute it and/or 12 + * modify it under the terms of the GNU General Public License as 13 + * published by the Free Software Foundation; either version 2 of 14 + * the License, or (at your option) any later version. 15 + * 16 + * This program is distributed in the hope that it will be useful, 17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 + * GNU General Public License for more details. 20 + */ 21 + 22 + #include <linux/clk.h> 23 + #include <linux/interrupt.h> 24 + #include <linux/module.h> 25 + #include <linux/of_platform.h> 26 + #include <media/rc-core.h> 27 + 28 + #define SUNXI_IR_DEV "sunxi-ir" 29 + 30 + /* Registers */ 31 + /* IR Control */ 32 + #define SUNXI_IR_CTL_REG 0x00 33 + /* Global Enable */ 34 + #define REG_CTL_GEN BIT(0) 35 + /* RX block enable */ 36 + #define REG_CTL_RXEN BIT(1) 37 + /* CIR mode */ 38 + #define REG_CTL_MD (BIT(4) | BIT(5)) 39 + 40 + /* Rx Config */ 41 + #define SUNXI_IR_RXCTL_REG 0x10 42 + /* Pulse Polarity Invert flag */ 43 + #define REG_RXCTL_RPPI BIT(2) 44 + 45 + /* Rx Data */ 46 + #define SUNXI_IR_RXFIFO_REG 0x20 47 + 48 + /* Rx Interrupt Enable */ 49 + #define SUNXI_IR_RXINT_REG 0x2C 50 + /* Rx FIFO Overflow */ 51 + #define REG_RXINT_ROI_EN BIT(0) 52 + /* Rx Packet End */ 53 + #define REG_RXINT_RPEI_EN BIT(1) 54 + /* Rx FIFO Data Available */ 55 + #define REG_RXINT_RAI_EN BIT(4) 56 + 57 + /* Rx FIFO available byte level */ 58 + #define REG_RXINT_RAL(val) (((val) << 8) & (GENMASK(11, 8))) 59 + 60 + /* Rx Interrupt Status */ 61 + #define SUNXI_IR_RXSTA_REG 0x30 62 + /* RX FIFO Get Available Counter */ 63 + #define REG_RXSTA_GET_AC(val) (((val) >> 8) & (GENMASK(5, 0))) 64 + /* Clear all interrupt status value */ 65 + #define REG_RXSTA_CLEARALL 0xff 66 + 67 + /* IR Sample Config */ 68 + #define SUNXI_IR_CIR_REG 0x34 69 + /* CIR_REG register noise threshold */ 70 + #define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2))) 71 + /* CIR_REG register idle threshold */ 72 + #define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8))) 73 + 74 + /* Hardware supported fifo size */ 75 + #define SUNXI_IR_FIFO_SIZE 16 76 + /* How many messages in FIFO trigger IRQ */ 77 + #define TRIGGER_LEVEL 8 78 + /* Required frequency for IR0 or IR1 clock in CIR mode */ 79 + #define SUNXI_IR_BASE_CLK 8000000 80 + /* Frequency after IR internal divider */ 81 + #define SUNXI_IR_CLK (SUNXI_IR_BASE_CLK / 64) 82 + /* Sample period in ns */ 83 + #define SUNXI_IR_SAMPLE (1000000000ul / SUNXI_IR_CLK) 84 + /* Noise threshold in samples */ 85 + #define SUNXI_IR_RXNOISE 1 86 + /* Idle Threshold in samples */ 87 + #define SUNXI_IR_RXIDLE 20 88 + /* Time after which device stops sending data in ms */ 89 + #define SUNXI_IR_TIMEOUT 120 90 + 91 + struct sunxi_ir { 92 + spinlock_t ir_lock; 93 + struct rc_dev *rc; 94 + void __iomem *base; 95 + int irq; 96 + struct clk *clk; 97 + struct clk *apb_clk; 98 + const char *map_name; 99 + }; 100 + 101 + static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) 102 + { 103 + unsigned long status; 104 + unsigned char dt; 105 + unsigned int cnt, rc; 106 + struct sunxi_ir *ir = dev_id; 107 + DEFINE_IR_RAW_EVENT(rawir); 108 + 109 + spin_lock(&ir->ir_lock); 110 + 111 + status = readl(ir->base + SUNXI_IR_RXSTA_REG); 112 + 113 + /* clean all pending statuses */ 114 + writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 115 + 116 + if (status & REG_RXINT_RAI_EN) { 117 + /* How many messages in fifo */ 118 + rc = REG_RXSTA_GET_AC(status); 119 + /* Sanity check */ 120 + rc = rc > SUNXI_IR_FIFO_SIZE ? SUNXI_IR_FIFO_SIZE : rc; 121 + /* If we have data */ 122 + for (cnt = 0; cnt < rc; cnt++) { 123 + /* for each bit in fifo */ 124 + dt = readb(ir->base + SUNXI_IR_RXFIFO_REG); 125 + rawir.pulse = (dt & 0x80) != 0; 126 + rawir.duration = ((dt & 0x7f) + 1) * SUNXI_IR_SAMPLE; 127 + ir_raw_event_store_with_filter(ir->rc, &rawir); 128 + } 129 + } 130 + 131 + if (status & REG_RXINT_ROI_EN) { 132 + ir_raw_event_reset(ir->rc); 133 + } else if (status & REG_RXINT_RPEI_EN) { 134 + ir_raw_event_set_idle(ir->rc, true); 135 + ir_raw_event_handle(ir->rc); 136 + } 137 + 138 + spin_unlock(&ir->ir_lock); 139 + 140 + return IRQ_HANDLED; 141 + } 142 + 143 + static int sunxi_ir_probe(struct platform_device *pdev) 144 + { 145 + int ret = 0; 146 + unsigned long tmp = 0; 147 + 148 + struct device *dev = &pdev->dev; 149 + struct device_node *dn = dev->of_node; 150 + struct resource *res; 151 + struct sunxi_ir *ir; 152 + 153 + ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL); 154 + if (!ir) 155 + return -ENOMEM; 156 + 157 + /* Clock */ 158 + ir->apb_clk = devm_clk_get(dev, "apb"); 159 + if (IS_ERR(ir->apb_clk)) { 160 + dev_err(dev, "failed to get a apb clock.\n"); 161 + return PTR_ERR(ir->apb_clk); 162 + } 163 + ir->clk = devm_clk_get(dev, "ir"); 164 + if (IS_ERR(ir->clk)) { 165 + dev_err(dev, "failed to get a ir clock.\n"); 166 + return PTR_ERR(ir->clk); 167 + } 168 + 169 + ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK); 170 + if (ret) { 171 + dev_err(dev, "set ir base clock failed!\n"); 172 + return ret; 173 + } 174 + 175 + if (clk_prepare_enable(ir->apb_clk)) { 176 + dev_err(dev, "try to enable apb_ir_clk failed\n"); 177 + return -EINVAL; 178 + } 179 + 180 + if (clk_prepare_enable(ir->clk)) { 181 + dev_err(dev, "try to enable ir_clk failed\n"); 182 + ret = -EINVAL; 183 + goto exit_clkdisable_apb_clk; 184 + } 185 + 186 + /* IO */ 187 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 188 + ir->base = devm_ioremap_resource(dev, res); 189 + if (IS_ERR(ir->base)) { 190 + dev_err(dev, "failed to map registers\n"); 191 + ret = PTR_ERR(ir->base); 192 + goto exit_clkdisable_clk; 193 + } 194 + 195 + ir->rc = rc_allocate_device(); 196 + if (!ir->rc) { 197 + dev_err(dev, "failed to allocate device\n"); 198 + ret = -ENOMEM; 199 + goto exit_clkdisable_clk; 200 + } 201 + 202 + ir->rc->priv = ir; 203 + ir->rc->input_name = SUNXI_IR_DEV; 204 + ir->rc->input_phys = "sunxi-ir/input0"; 205 + ir->rc->input_id.bustype = BUS_HOST; 206 + ir->rc->input_id.vendor = 0x0001; 207 + ir->rc->input_id.product = 0x0001; 208 + ir->rc->input_id.version = 0x0100; 209 + ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL); 210 + ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY; 211 + ir->rc->dev.parent = dev; 212 + ir->rc->driver_type = RC_DRIVER_IR_RAW; 213 + rc_set_allowed_protocols(ir->rc, RC_BIT_ALL); 214 + ir->rc->rx_resolution = SUNXI_IR_SAMPLE; 215 + ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT); 216 + ir->rc->driver_name = SUNXI_IR_DEV; 217 + 218 + ret = rc_register_device(ir->rc); 219 + if (ret) { 220 + dev_err(dev, "failed to register rc device\n"); 221 + goto exit_free_dev; 222 + } 223 + 224 + platform_set_drvdata(pdev, ir); 225 + 226 + /* IRQ */ 227 + ir->irq = platform_get_irq(pdev, 0); 228 + if (ir->irq < 0) { 229 + dev_err(dev, "no irq resource\n"); 230 + ret = ir->irq; 231 + goto exit_free_dev; 232 + } 233 + 234 + ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir); 235 + if (ret) { 236 + dev_err(dev, "failed request irq\n"); 237 + goto exit_free_dev; 238 + } 239 + 240 + /* Enable CIR Mode */ 241 + writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG); 242 + 243 + /* Set noise threshold and idle threshold */ 244 + writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE), 245 + ir->base + SUNXI_IR_CIR_REG); 246 + 247 + /* Invert Input Signal */ 248 + writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG); 249 + 250 + /* Clear All Rx Interrupt Status */ 251 + writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 252 + 253 + /* 254 + * Enable IRQ on overflow, packet end, FIFO available with trigger 255 + * level 256 + */ 257 + writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | 258 + REG_RXINT_RAI_EN | REG_RXINT_RAL(TRIGGER_LEVEL - 1), 259 + ir->base + SUNXI_IR_RXINT_REG); 260 + 261 + /* Enable IR Module */ 262 + tmp = readl(ir->base + SUNXI_IR_CTL_REG); 263 + writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG); 264 + 265 + dev_info(dev, "initialized sunXi IR driver\n"); 266 + return 0; 267 + 268 + exit_free_dev: 269 + rc_free_device(ir->rc); 270 + exit_clkdisable_clk: 271 + clk_disable_unprepare(ir->clk); 272 + exit_clkdisable_apb_clk: 273 + clk_disable_unprepare(ir->apb_clk); 274 + 275 + return ret; 276 + } 277 + 278 + static int sunxi_ir_remove(struct platform_device *pdev) 279 + { 280 + unsigned long flags; 281 + struct sunxi_ir *ir = platform_get_drvdata(pdev); 282 + 283 + clk_disable_unprepare(ir->clk); 284 + clk_disable_unprepare(ir->apb_clk); 285 + 286 + spin_lock_irqsave(&ir->ir_lock, flags); 287 + /* disable IR IRQ */ 288 + writel(0, ir->base + SUNXI_IR_RXINT_REG); 289 + /* clear All Rx Interrupt Status */ 290 + writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); 291 + /* disable IR */ 292 + writel(0, ir->base + SUNXI_IR_CTL_REG); 293 + spin_unlock_irqrestore(&ir->ir_lock, flags); 294 + 295 + rc_unregister_device(ir->rc); 296 + return 0; 297 + } 298 + 299 + static const struct of_device_id sunxi_ir_match[] = { 300 + { .compatible = "allwinner,sun4i-a10-ir", }, 301 + {}, 302 + }; 303 + 304 + static struct platform_driver sunxi_ir_driver = { 305 + .probe = sunxi_ir_probe, 306 + .remove = sunxi_ir_remove, 307 + .driver = { 308 + .name = SUNXI_IR_DEV, 309 + .owner = THIS_MODULE, 310 + .of_match_table = sunxi_ir_match, 311 + }, 312 + }; 313 + 314 + module_platform_driver(sunxi_ir_driver); 315 + 316 + MODULE_DESCRIPTION("Allwinner sunXi IR controller driver"); 317 + MODULE_AUTHOR("Alexsey Shestacov <wingrime@linux-sunxi.org>"); 318 + MODULE_LICENSE("GPL");