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