Commit | Line | Data |
---|---|---|
b429996c SG |
1 | /* |
2 | * Copyright (C) 2017 Sanechips Technology Co., Ltd. | |
3 | * Copyright 2017 Linaro Ltd. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/device.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/io.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/of_platform.h> | |
16 | #include <linux/platform_device.h> | |
17 | ||
18 | #include <media/rc-core.h> | |
19 | ||
20 | #define DRIVER_NAME "zx-irdec" | |
21 | ||
22 | #define ZX_IR_ENABLE 0x04 | |
23 | #define ZX_IREN BIT(0) | |
24 | #define ZX_IR_CTRL 0x08 | |
25 | #define ZX_DEGL_MASK GENMASK(21, 20) | |
26 | #define ZX_DEGL_VALUE(x) (((x) << 20) & ZX_DEGL_MASK) | |
27 | #define ZX_WDBEGIN_MASK GENMASK(18, 8) | |
28 | #define ZX_WDBEGIN_VALUE(x) (((x) << 8) & ZX_WDBEGIN_MASK) | |
29 | #define ZX_IR_INTEN 0x10 | |
30 | #define ZX_IR_INTSTCLR 0x14 | |
31 | #define ZX_IR_CODE 0x30 | |
32 | #define ZX_IR_CNUM 0x34 | |
33 | #define ZX_NECRPT BIT(16) | |
34 | ||
35 | struct zx_irdec { | |
36 | void __iomem *base; | |
37 | struct rc_dev *rcd; | |
38 | }; | |
39 | ||
40 | static void zx_irdec_set_mask(struct zx_irdec *irdec, unsigned int reg, | |
41 | u32 mask, u32 value) | |
42 | { | |
43 | u32 data; | |
44 | ||
45 | data = readl(irdec->base + reg); | |
46 | data &= ~mask; | |
47 | data |= value & mask; | |
48 | writel(data, irdec->base + reg); | |
49 | } | |
50 | ||
51 | static irqreturn_t zx_irdec_irq(int irq, void *dev_id) | |
52 | { | |
53 | struct zx_irdec *irdec = dev_id; | |
54 | u8 address, not_address; | |
55 | u8 command, not_command; | |
56 | u32 rawcode, scancode; | |
6d741bfe | 57 | enum rc_proto rc_proto; |
b429996c SG |
58 | |
59 | /* Clear interrupt */ | |
60 | writel(1, irdec->base + ZX_IR_INTSTCLR); | |
61 | ||
62 | /* Check repeat frame */ | |
63 | if (readl(irdec->base + ZX_IR_CNUM) & ZX_NECRPT) { | |
64 | rc_repeat(irdec->rcd); | |
65 | goto done; | |
66 | } | |
67 | ||
68 | rawcode = readl(irdec->base + ZX_IR_CODE); | |
69 | not_command = (rawcode >> 24) & 0xff; | |
70 | command = (rawcode >> 16) & 0xff; | |
71 | not_address = (rawcode >> 8) & 0xff; | |
72 | address = rawcode & 0xff; | |
73 | ||
74 | scancode = ir_nec_bytes_to_scancode(address, not_address, | |
75 | command, not_command, | |
6d741bfe SY |
76 | &rc_proto); |
77 | rc_keydown(irdec->rcd, rc_proto, scancode, 0); | |
b429996c SG |
78 | |
79 | done: | |
80 | return IRQ_HANDLED; | |
81 | } | |
82 | ||
83 | static int zx_irdec_probe(struct platform_device *pdev) | |
84 | { | |
85 | struct device *dev = &pdev->dev; | |
86 | struct zx_irdec *irdec; | |
87 | struct resource *res; | |
88 | struct rc_dev *rcd; | |
89 | int irq; | |
90 | int ret; | |
91 | ||
92 | irdec = devm_kzalloc(dev, sizeof(*irdec), GFP_KERNEL); | |
93 | if (!irdec) | |
94 | return -ENOMEM; | |
95 | ||
96 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
97 | irdec->base = devm_ioremap_resource(dev, res); | |
98 | if (IS_ERR(irdec->base)) | |
99 | return PTR_ERR(irdec->base); | |
100 | ||
101 | irq = platform_get_irq(pdev, 0); | |
102 | if (irq < 0) | |
103 | return irq; | |
104 | ||
105 | rcd = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE); | |
106 | if (!rcd) { | |
107 | dev_err(dev, "failed to allocate rc device\n"); | |
108 | return -ENOMEM; | |
109 | } | |
110 | ||
111 | irdec->rcd = rcd; | |
112 | ||
113 | rcd->priv = irdec; | |
114 | rcd->input_phys = DRIVER_NAME "/input0"; | |
115 | rcd->input_id.bustype = BUS_HOST; | |
116 | rcd->map_name = RC_MAP_ZX_IRDEC; | |
6d741bfe SY |
117 | rcd->allowed_protocols = RC_PROTO_BIT_NEC | RC_PROTO_BIT_NECX | |
118 | RC_PROTO_BIT_NEC32; | |
b429996c SG |
119 | rcd->driver_name = DRIVER_NAME; |
120 | rcd->device_name = DRIVER_NAME; | |
121 | ||
122 | platform_set_drvdata(pdev, irdec); | |
123 | ||
124 | ret = devm_rc_register_device(dev, rcd); | |
125 | if (ret) { | |
126 | dev_err(dev, "failed to register rc device\n"); | |
127 | return ret; | |
128 | } | |
129 | ||
130 | ret = devm_request_irq(dev, irq, zx_irdec_irq, 0, NULL, irdec); | |
131 | if (ret) { | |
132 | dev_err(dev, "failed to request irq\n"); | |
133 | return ret; | |
134 | } | |
135 | ||
136 | /* | |
137 | * Initialize deglitch level and watchdog counter beginner as | |
138 | * recommended by vendor BSP code. | |
139 | */ | |
140 | zx_irdec_set_mask(irdec, ZX_IR_CTRL, ZX_DEGL_MASK, ZX_DEGL_VALUE(0)); | |
141 | zx_irdec_set_mask(irdec, ZX_IR_CTRL, ZX_WDBEGIN_MASK, | |
142 | ZX_WDBEGIN_VALUE(0x21c)); | |
143 | ||
144 | /* Enable interrupt */ | |
145 | writel(1, irdec->base + ZX_IR_INTEN); | |
146 | ||
147 | /* Enable the decoder */ | |
148 | zx_irdec_set_mask(irdec, ZX_IR_ENABLE, ZX_IREN, ZX_IREN); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static int zx_irdec_remove(struct platform_device *pdev) | |
154 | { | |
155 | struct zx_irdec *irdec = platform_get_drvdata(pdev); | |
156 | ||
157 | /* Disable the decoder */ | |
158 | zx_irdec_set_mask(irdec, ZX_IR_ENABLE, ZX_IREN, 0); | |
159 | ||
160 | /* Disable interrupt */ | |
161 | writel(0, irdec->base + ZX_IR_INTEN); | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | static const struct of_device_id zx_irdec_match[] = { | |
167 | { .compatible = "zte,zx296718-irdec" }, | |
168 | { }, | |
169 | }; | |
170 | MODULE_DEVICE_TABLE(of, zx_irdec_match); | |
171 | ||
172 | static struct platform_driver zx_irdec_driver = { | |
173 | .probe = zx_irdec_probe, | |
174 | .remove = zx_irdec_remove, | |
175 | .driver = { | |
176 | .name = DRIVER_NAME, | |
177 | .of_match_table = zx_irdec_match, | |
178 | }, | |
179 | }; | |
180 | module_platform_driver(zx_irdec_driver); | |
181 | ||
182 | MODULE_DESCRIPTION("ZTE ZX IR remote control driver"); | |
183 | MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); | |
184 | MODULE_LICENSE("GPL v2"); |