.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Fujitsu Lifebook Application Panel button drive |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org> |
---|
5 | 6 | * Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or modify it |
---|
8 | | - * under the terms of the GNU General Public License version 2 as published by |
---|
9 | | - * the Free Software Foundation. |
---|
10 | 7 | * |
---|
11 | 8 | * Many Fujitsu Lifebook laptops have a small panel of buttons that are |
---|
12 | 9 | * accessible via the i2c/smbus interface. This driver polls those |
---|
.. | .. |
---|
20 | 17 | #include <linux/module.h> |
---|
21 | 18 | #include <linux/ioport.h> |
---|
22 | 19 | #include <linux/io.h> |
---|
23 | | -#include <linux/input-polldev.h> |
---|
| 20 | +#include <linux/input.h> |
---|
24 | 21 | #include <linux/i2c.h> |
---|
25 | | -#include <linux/workqueue.h> |
---|
26 | 22 | #include <linux/leds.h> |
---|
27 | 23 | |
---|
28 | 24 | #define APANEL_NAME "Fujitsu Application Panel" |
---|
.. | .. |
---|
55 | 51 | #define MAX_PANEL_KEYS 12 |
---|
56 | 52 | |
---|
57 | 53 | struct apanel { |
---|
58 | | - struct input_polled_dev *ipdev; |
---|
| 54 | + struct input_dev *idev; |
---|
59 | 55 | struct i2c_client *client; |
---|
60 | 56 | unsigned short keymap[MAX_PANEL_KEYS]; |
---|
61 | | - u16 nkeys; |
---|
62 | | - u16 led_bits; |
---|
63 | | - struct work_struct led_work; |
---|
| 57 | + u16 nkeys; |
---|
64 | 58 | struct led_classdev mail_led; |
---|
65 | 59 | }; |
---|
66 | 60 | |
---|
| 61 | +static const unsigned short apanel_keymap[MAX_PANEL_KEYS] = { |
---|
| 62 | + [0] = KEY_MAIL, |
---|
| 63 | + [1] = KEY_WWW, |
---|
| 64 | + [2] = KEY_PROG2, |
---|
| 65 | + [3] = KEY_PROG1, |
---|
67 | 66 | |
---|
68 | | -static int apanel_probe(struct i2c_client *, const struct i2c_device_id *); |
---|
| 67 | + [8] = KEY_FORWARD, |
---|
| 68 | + [9] = KEY_REWIND, |
---|
| 69 | + [10] = KEY_STOPCD, |
---|
| 70 | + [11] = KEY_PLAYPAUSE, |
---|
| 71 | +}; |
---|
69 | 72 | |
---|
70 | 73 | static void report_key(struct input_dev *input, unsigned keycode) |
---|
71 | 74 | { |
---|
72 | | - pr_debug(APANEL ": report key %#x\n", keycode); |
---|
| 75 | + dev_dbg(input->dev.parent, "report key %#x\n", keycode); |
---|
73 | 76 | input_report_key(input, keycode, 1); |
---|
74 | 77 | input_sync(input); |
---|
75 | 78 | |
---|
.. | .. |
---|
85 | 88 | * CD keys: |
---|
86 | 89 | * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800) |
---|
87 | 90 | */ |
---|
88 | | -static void apanel_poll(struct input_polled_dev *ipdev) |
---|
| 91 | +static void apanel_poll(struct input_dev *idev) |
---|
89 | 92 | { |
---|
90 | | - struct apanel *ap = ipdev->private; |
---|
91 | | - struct input_dev *idev = ipdev->input; |
---|
| 93 | + struct apanel *ap = input_get_drvdata(idev); |
---|
92 | 94 | u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; |
---|
93 | 95 | s32 data; |
---|
94 | 96 | int i; |
---|
.. | .. |
---|
109 | 111 | report_key(idev, ap->keymap[i]); |
---|
110 | 112 | } |
---|
111 | 113 | |
---|
112 | | -/* Track state changes of LED */ |
---|
113 | | -static void led_update(struct work_struct *work) |
---|
114 | | -{ |
---|
115 | | - struct apanel *ap = container_of(work, struct apanel, led_work); |
---|
116 | | - |
---|
117 | | - i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits); |
---|
118 | | -} |
---|
119 | | - |
---|
120 | | -static void mail_led_set(struct led_classdev *led, |
---|
| 114 | +static int mail_led_set(struct led_classdev *led, |
---|
121 | 115 | enum led_brightness value) |
---|
122 | 116 | { |
---|
123 | 117 | struct apanel *ap = container_of(led, struct apanel, mail_led); |
---|
| 118 | + u16 led_bits = value != LED_OFF ? 0x8000 : 0x0000; |
---|
124 | 119 | |
---|
125 | | - if (value != LED_OFF) |
---|
126 | | - ap->led_bits |= 0x8000; |
---|
127 | | - else |
---|
128 | | - ap->led_bits &= ~0x8000; |
---|
129 | | - |
---|
130 | | - schedule_work(&ap->led_work); |
---|
| 120 | + return i2c_smbus_write_word_data(ap->client, 0x10, led_bits); |
---|
131 | 121 | } |
---|
132 | 122 | |
---|
133 | | -static int apanel_remove(struct i2c_client *client) |
---|
| 123 | +static int apanel_probe(struct i2c_client *client, |
---|
| 124 | + const struct i2c_device_id *id) |
---|
134 | 125 | { |
---|
135 | | - struct apanel *ap = i2c_get_clientdata(client); |
---|
| 126 | + struct apanel *ap; |
---|
| 127 | + struct input_dev *idev; |
---|
| 128 | + u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; |
---|
| 129 | + int i, err; |
---|
136 | 130 | |
---|
137 | | - if (device_chip[APANEL_DEV_LED] != CHIP_NONE) |
---|
138 | | - led_classdev_unregister(&ap->mail_led); |
---|
| 131 | + ap = devm_kzalloc(&client->dev, sizeof(*ap), GFP_KERNEL); |
---|
| 132 | + if (!ap) |
---|
| 133 | + return -ENOMEM; |
---|
139 | 134 | |
---|
140 | | - input_unregister_polled_device(ap->ipdev); |
---|
141 | | - input_free_polled_device(ap->ipdev); |
---|
| 135 | + idev = devm_input_allocate_device(&client->dev); |
---|
| 136 | + if (!idev) |
---|
| 137 | + return -ENOMEM; |
---|
| 138 | + |
---|
| 139 | + ap->idev = idev; |
---|
| 140 | + ap->client = client; |
---|
| 141 | + |
---|
| 142 | + i2c_set_clientdata(client, ap); |
---|
| 143 | + |
---|
| 144 | + err = i2c_smbus_write_word_data(client, cmd, 0); |
---|
| 145 | + if (err) { |
---|
| 146 | + dev_warn(&client->dev, "smbus write error %d\n", err); |
---|
| 147 | + return err; |
---|
| 148 | + } |
---|
| 149 | + |
---|
| 150 | + input_set_drvdata(idev, ap); |
---|
| 151 | + |
---|
| 152 | + idev->name = APANEL_NAME " buttons"; |
---|
| 153 | + idev->phys = "apanel/input0"; |
---|
| 154 | + idev->id.bustype = BUS_HOST; |
---|
| 155 | + |
---|
| 156 | + memcpy(ap->keymap, apanel_keymap, sizeof(apanel_keymap)); |
---|
| 157 | + idev->keycode = ap->keymap; |
---|
| 158 | + idev->keycodesize = sizeof(ap->keymap[0]); |
---|
| 159 | + idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4; |
---|
| 160 | + |
---|
| 161 | + set_bit(EV_KEY, idev->evbit); |
---|
| 162 | + for (i = 0; i < idev->keycodemax; i++) |
---|
| 163 | + if (ap->keymap[i]) |
---|
| 164 | + set_bit(ap->keymap[i], idev->keybit); |
---|
| 165 | + |
---|
| 166 | + err = input_setup_polling(idev, apanel_poll); |
---|
| 167 | + if (err) |
---|
| 168 | + return err; |
---|
| 169 | + |
---|
| 170 | + input_set_poll_interval(idev, POLL_INTERVAL_DEFAULT); |
---|
| 171 | + |
---|
| 172 | + err = input_register_device(idev); |
---|
| 173 | + if (err) |
---|
| 174 | + return err; |
---|
| 175 | + |
---|
| 176 | + if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { |
---|
| 177 | + ap->mail_led.name = "mail:blue"; |
---|
| 178 | + ap->mail_led.brightness_set_blocking = mail_led_set; |
---|
| 179 | + err = devm_led_classdev_register(&client->dev, &ap->mail_led); |
---|
| 180 | + if (err) |
---|
| 181 | + return err; |
---|
| 182 | + } |
---|
142 | 183 | |
---|
143 | 184 | return 0; |
---|
144 | 185 | } |
---|
145 | 186 | |
---|
146 | 187 | static void apanel_shutdown(struct i2c_client *client) |
---|
147 | 188 | { |
---|
148 | | - apanel_remove(client); |
---|
| 189 | + struct apanel *ap = i2c_get_clientdata(client); |
---|
| 190 | + |
---|
| 191 | + if (device_chip[APANEL_DEV_LED] != CHIP_NONE) |
---|
| 192 | + led_set_brightness(&ap->mail_led, LED_OFF); |
---|
149 | 193 | } |
---|
150 | 194 | |
---|
151 | 195 | static const struct i2c_device_id apanel_id[] = { |
---|
.. | .. |
---|
158 | 202 | .driver = { |
---|
159 | 203 | .name = APANEL, |
---|
160 | 204 | }, |
---|
161 | | - .probe = &apanel_probe, |
---|
162 | | - .remove = &apanel_remove, |
---|
163 | | - .shutdown = &apanel_shutdown, |
---|
| 205 | + .probe = apanel_probe, |
---|
| 206 | + .shutdown = apanel_shutdown, |
---|
164 | 207 | .id_table = apanel_id, |
---|
165 | 208 | }; |
---|
166 | | - |
---|
167 | | -static struct apanel apanel = { |
---|
168 | | - .keymap = { |
---|
169 | | - [0] = KEY_MAIL, |
---|
170 | | - [1] = KEY_WWW, |
---|
171 | | - [2] = KEY_PROG2, |
---|
172 | | - [3] = KEY_PROG1, |
---|
173 | | - |
---|
174 | | - [8] = KEY_FORWARD, |
---|
175 | | - [9] = KEY_REWIND, |
---|
176 | | - [10] = KEY_STOPCD, |
---|
177 | | - [11] = KEY_PLAYPAUSE, |
---|
178 | | - |
---|
179 | | - }, |
---|
180 | | - .mail_led = { |
---|
181 | | - .name = "mail:blue", |
---|
182 | | - .brightness_set = mail_led_set, |
---|
183 | | - }, |
---|
184 | | -}; |
---|
185 | | - |
---|
186 | | -/* NB: Only one panel on the i2c. */ |
---|
187 | | -static int apanel_probe(struct i2c_client *client, |
---|
188 | | - const struct i2c_device_id *id) |
---|
189 | | -{ |
---|
190 | | - struct apanel *ap; |
---|
191 | | - struct input_polled_dev *ipdev; |
---|
192 | | - struct input_dev *idev; |
---|
193 | | - u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8; |
---|
194 | | - int i, err = -ENOMEM; |
---|
195 | | - |
---|
196 | | - ap = &apanel; |
---|
197 | | - |
---|
198 | | - ipdev = input_allocate_polled_device(); |
---|
199 | | - if (!ipdev) |
---|
200 | | - goto out1; |
---|
201 | | - |
---|
202 | | - ap->ipdev = ipdev; |
---|
203 | | - ap->client = client; |
---|
204 | | - |
---|
205 | | - i2c_set_clientdata(client, ap); |
---|
206 | | - |
---|
207 | | - err = i2c_smbus_write_word_data(client, cmd, 0); |
---|
208 | | - if (err) { |
---|
209 | | - dev_warn(&client->dev, APANEL ": smbus write error %d\n", |
---|
210 | | - err); |
---|
211 | | - goto out3; |
---|
212 | | - } |
---|
213 | | - |
---|
214 | | - ipdev->poll = apanel_poll; |
---|
215 | | - ipdev->poll_interval = POLL_INTERVAL_DEFAULT; |
---|
216 | | - ipdev->private = ap; |
---|
217 | | - |
---|
218 | | - idev = ipdev->input; |
---|
219 | | - idev->name = APANEL_NAME " buttons"; |
---|
220 | | - idev->phys = "apanel/input0"; |
---|
221 | | - idev->id.bustype = BUS_HOST; |
---|
222 | | - idev->dev.parent = &client->dev; |
---|
223 | | - |
---|
224 | | - set_bit(EV_KEY, idev->evbit); |
---|
225 | | - |
---|
226 | | - idev->keycode = ap->keymap; |
---|
227 | | - idev->keycodesize = sizeof(ap->keymap[0]); |
---|
228 | | - idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4; |
---|
229 | | - |
---|
230 | | - for (i = 0; i < idev->keycodemax; i++) |
---|
231 | | - if (ap->keymap[i]) |
---|
232 | | - set_bit(ap->keymap[i], idev->keybit); |
---|
233 | | - |
---|
234 | | - err = input_register_polled_device(ipdev); |
---|
235 | | - if (err) |
---|
236 | | - goto out3; |
---|
237 | | - |
---|
238 | | - INIT_WORK(&ap->led_work, led_update); |
---|
239 | | - if (device_chip[APANEL_DEV_LED] != CHIP_NONE) { |
---|
240 | | - err = led_classdev_register(&client->dev, &ap->mail_led); |
---|
241 | | - if (err) |
---|
242 | | - goto out4; |
---|
243 | | - } |
---|
244 | | - |
---|
245 | | - return 0; |
---|
246 | | -out4: |
---|
247 | | - input_unregister_polled_device(ipdev); |
---|
248 | | -out3: |
---|
249 | | - input_free_polled_device(ipdev); |
---|
250 | | -out1: |
---|
251 | | - return err; |
---|
252 | | -} |
---|
253 | 209 | |
---|
254 | 210 | /* Scan the system ROM for the signature "FJKEYINF" */ |
---|
255 | 211 | static __init const void __iomem *bios_signature(const void __iomem *bios) |
---|