Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Apple Cinema Display driver
4 *
5 * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch)
6 *
7 * Thanks to Caskey L. Dickson for his work with acdctl.
8 */
9
10#include <linux/kernel.h>
11#include <linux/errno.h>
12#include <linux/init.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/usb.h>
16#include <linux/backlight.h>
17#include <linux/timer.h>
18#include <linux/workqueue.h>
19#include <linux/atomic.h>
20
21#define APPLE_VENDOR_ID 0x05AC
22
23#define USB_REQ_GET_REPORT 0x01
24#define USB_REQ_SET_REPORT 0x09
25
26#define ACD_USB_TIMEOUT 250
27
28#define ACD_USB_EDID 0x0302
29#define ACD_USB_BRIGHTNESS 0x0310
30
31#define ACD_BTN_NONE 0
32#define ACD_BTN_BRIGHT_UP 3
33#define ACD_BTN_BRIGHT_DOWN 4
34
35#define ACD_URB_BUFFER_LEN 2
36#define ACD_MSG_BUFFER_LEN 2
37
38#define APPLEDISPLAY_DEVICE(prod) \
39 .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
40 USB_DEVICE_ID_MATCH_INT_CLASS | \
41 USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
42 .idVendor = APPLE_VENDOR_ID, \
43 .idProduct = (prod), \
44 .bInterfaceClass = USB_CLASS_HID, \
45 .bInterfaceProtocol = 0x00
46
47/* table of devices that work with this driver */
48static const struct usb_device_id appledisplay_table[] = {
49 { APPLEDISPLAY_DEVICE(0x9218) },
50 { APPLEDISPLAY_DEVICE(0x9219) },
51 { APPLEDISPLAY_DEVICE(0x921c) },
52 { APPLEDISPLAY_DEVICE(0x921d) },
53 { APPLEDISPLAY_DEVICE(0x9236) },
54
55 /* Terminating entry */
56 { }
57};
58MODULE_DEVICE_TABLE(usb, appledisplay_table);
59
60/* Structure to hold all of our device specific stuff */
61struct appledisplay {
62 struct usb_device *udev; /* usb device */
63 struct urb *urb; /* usb request block */
64 struct backlight_device *bd; /* backlight device */
65 u8 *urbdata; /* interrupt URB data buffer */
66 u8 *msgdata; /* control message data buffer */
67
68 struct delayed_work work;
69 int button_pressed;
70 spinlock_t lock;
71 struct mutex sysfslock; /* concurrent read and write */
72};
73
74static atomic_t count_displays = ATOMIC_INIT(0);
75
76static void appledisplay_complete(struct urb *urb)
77{
78 struct appledisplay *pdata = urb->context;
79 struct device *dev = &pdata->udev->dev;
80 unsigned long flags;
81 int status = urb->status;
82 int retval;
83
84 switch (status) {
85 case 0:
86 /* success */
87 break;
88 case -EOVERFLOW:
89 dev_err(dev,
90 "OVERFLOW with data length %d, actual length is %d\n",
91 ACD_URB_BUFFER_LEN, pdata->urb->actual_length);
92 /* fall through */
93 case -ECONNRESET:
94 case -ENOENT:
95 case -ESHUTDOWN:
96 /* This urb is terminated, clean up */
97 dev_dbg(dev, "%s - urb shuttingdown with status: %d\n",
98 __func__, status);
99 return;
100 default:
101 dev_dbg(dev, "%s - nonzero urb status received: %d\n",
102 __func__, status);
103 goto exit;
104 }
105
106 spin_lock_irqsave(&pdata->lock, flags);
107
108 switch(pdata->urbdata[1]) {
109 case ACD_BTN_BRIGHT_UP:
110 case ACD_BTN_BRIGHT_DOWN:
111 pdata->button_pressed = 1;
112 schedule_delayed_work(&pdata->work, 0);
113 break;
114 case ACD_BTN_NONE:
115 default:
116 pdata->button_pressed = 0;
117 break;
118 }
119
120 spin_unlock_irqrestore(&pdata->lock, flags);
121
122exit:
123 retval = usb_submit_urb(pdata->urb, GFP_ATOMIC);
124 if (retval) {
125 dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
126 __func__, retval);
127 }
128}
129
130static int appledisplay_bl_update_status(struct backlight_device *bd)
131{
132 struct appledisplay *pdata = bl_get_data(bd);
133 int retval;
134
135 mutex_lock(&pdata->sysfslock);
136 pdata->msgdata[0] = 0x10;
137 pdata->msgdata[1] = bd->props.brightness;
138
139 retval = usb_control_msg(
140 pdata->udev,
141 usb_sndctrlpipe(pdata->udev, 0),
142 USB_REQ_SET_REPORT,
143 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
144 ACD_USB_BRIGHTNESS,
145 0,
146 pdata->msgdata, 2,
147 ACD_USB_TIMEOUT);
148 mutex_unlock(&pdata->sysfslock);
149
150 if (retval < 0)
151 return retval;
152 else
153 return 0;
154}
155
156static int appledisplay_bl_get_brightness(struct backlight_device *bd)
157{
158 struct appledisplay *pdata = bl_get_data(bd);
159 int retval, brightness;
160
161 mutex_lock(&pdata->sysfslock);
162 retval = usb_control_msg(
163 pdata->udev,
164 usb_rcvctrlpipe(pdata->udev, 0),
165 USB_REQ_GET_REPORT,
166 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
167 ACD_USB_BRIGHTNESS,
168 0,
169 pdata->msgdata, 2,
170 ACD_USB_TIMEOUT);
171 brightness = pdata->msgdata[1];
172 mutex_unlock(&pdata->sysfslock);
173
174 if (retval < 0)
175 return retval;
176 else
177 return brightness;
178}
179
180static const struct backlight_ops appledisplay_bl_data = {
181 .get_brightness = appledisplay_bl_get_brightness,
182 .update_status = appledisplay_bl_update_status,
183};
184
185static void appledisplay_work(struct work_struct *work)
186{
187 struct appledisplay *pdata =
188 container_of(work, struct appledisplay, work.work);
189 int retval;
190
191 retval = appledisplay_bl_get_brightness(pdata->bd);
192 if (retval >= 0)
193 pdata->bd->props.brightness = retval;
194
195 /* Poll again in about 125ms if there's still a button pressed */
196 if (pdata->button_pressed)
197 schedule_delayed_work(&pdata->work, HZ / 8);
198}
199
200static int appledisplay_probe(struct usb_interface *iface,
201 const struct usb_device_id *id)
202{
203 struct backlight_properties props;
204 struct appledisplay *pdata;
205 struct usb_device *udev = interface_to_usbdev(iface);
206 struct usb_endpoint_descriptor *endpoint;
207 int int_in_endpointAddr = 0;
208 int retval, brightness;
209 char bl_name[20];
210
211 /* set up the endpoint information */
212 /* use only the first interrupt-in endpoint */
213 retval = usb_find_int_in_endpoint(iface->cur_altsetting, &endpoint);
214 if (retval) {
215 dev_err(&iface->dev, "Could not find int-in endpoint\n");
216 return retval;
217 }
218
219 int_in_endpointAddr = endpoint->bEndpointAddress;
220
221 /* allocate memory for our device state and initialize it */
222 pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
223 if (!pdata) {
224 retval = -ENOMEM;
225 goto error;
226 }
227
228 pdata->udev = udev;
229
230 spin_lock_init(&pdata->lock);
231 INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
232 mutex_init(&pdata->sysfslock);
233
234 /* Allocate buffer for control messages */
235 pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
236 if (!pdata->msgdata) {
237 retval = -ENOMEM;
238 goto error;
239 }
240
241 /* Allocate interrupt URB */
242 pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
243 if (!pdata->urb) {
244 retval = -ENOMEM;
245 goto error;
246 }
247
248 /* Allocate buffer for interrupt data */
249 pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
250 GFP_KERNEL, &pdata->urb->transfer_dma);
251 if (!pdata->urbdata) {
252 retval = -ENOMEM;
253 dev_err(&iface->dev, "Allocating URB buffer failed\n");
254 goto error;
255 }
256
257 /* Configure interrupt URB */
258 usb_fill_int_urb(pdata->urb, udev,
259 usb_rcvintpipe(udev, int_in_endpointAddr),
260 pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
261 pdata, 1);
262 if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
263 retval = -EIO;
264 dev_err(&iface->dev, "Submitting URB failed\n");
265 goto error;
266 }
267
268 /* Register backlight device */
269 snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
270 atomic_inc_return(&count_displays) - 1);
271 memset(&props, 0, sizeof(struct backlight_properties));
272 props.type = BACKLIGHT_RAW;
273 props.max_brightness = 0xff;
274 pdata->bd = backlight_device_register(bl_name, NULL, pdata,
275 &appledisplay_bl_data, &props);
276 if (IS_ERR(pdata->bd)) {
277 dev_err(&iface->dev, "Backlight registration failed\n");
278 retval = PTR_ERR(pdata->bd);
279 goto error;
280 }
281
282 /* Try to get brightness */
283 brightness = appledisplay_bl_get_brightness(pdata->bd);
284
285 if (brightness < 0) {
286 retval = brightness;
287 dev_err(&iface->dev,
288 "Error while getting initial brightness: %d\n", retval);
289 goto error;
290 }
291
292 /* Set brightness in backlight device */
293 pdata->bd->props.brightness = brightness;
294
295 /* save our data pointer in the interface device */
296 usb_set_intfdata(iface, pdata);
297
298 printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
299
300 return 0;
301
302error:
303 if (pdata) {
304 if (pdata->urb) {
305 usb_kill_urb(pdata->urb);
306 if (pdata->urbdata)
307 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
308 pdata->urbdata, pdata->urb->transfer_dma);
309 usb_free_urb(pdata->urb);
310 }
311 if (!IS_ERR(pdata->bd))
312 backlight_device_unregister(pdata->bd);
313 kfree(pdata->msgdata);
314 }
315 usb_set_intfdata(iface, NULL);
316 kfree(pdata);
317 return retval;
318}
319
320static void appledisplay_disconnect(struct usb_interface *iface)
321{
322 struct appledisplay *pdata = usb_get_intfdata(iface);
323
324 if (pdata) {
325 usb_kill_urb(pdata->urb);
326 cancel_delayed_work_sync(&pdata->work);
327 backlight_device_unregister(pdata->bd);
328 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
329 pdata->urbdata, pdata->urb->transfer_dma);
330 usb_free_urb(pdata->urb);
331 kfree(pdata->msgdata);
332 kfree(pdata);
333 }
334
335 printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
336}
337
338static struct usb_driver appledisplay_driver = {
339 .name = "appledisplay",
340 .probe = appledisplay_probe,
341 .disconnect = appledisplay_disconnect,
342 .id_table = appledisplay_table,
343};
344
345static int __init appledisplay_init(void)
346{
347 return usb_register(&appledisplay_driver);
348}
349
350static void __exit appledisplay_exit(void)
351{
352 usb_deregister(&appledisplay_driver);
353}
354
355MODULE_AUTHOR("Michael Hanselmann");
356MODULE_DESCRIPTION("Apple Cinema Display driver");
357MODULE_LICENSE("GPL");
358
359module_init(appledisplay_init);
360module_exit(appledisplay_exit);