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 */
48 static 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 };
58 MODULE_DEVICE_TABLE(usb, appledisplay_table);
59
60 /* Structure to hold all of our device specific stuff */
61 struct 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
74 static atomic_t count_displays = ATOMIC_INIT(0);
75
appledisplay_complete(struct urb * urb)76 static 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
122 exit:
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
appledisplay_bl_update_status(struct backlight_device * bd)130 static 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 return retval;
151 }
152
appledisplay_bl_get_brightness(struct backlight_device * bd)153 static int appledisplay_bl_get_brightness(struct backlight_device *bd)
154 {
155 struct appledisplay *pdata = bl_get_data(bd);
156 int retval, brightness;
157
158 mutex_lock(&pdata->sysfslock);
159 retval = usb_control_msg(
160 pdata->udev,
161 usb_rcvctrlpipe(pdata->udev, 0),
162 USB_REQ_GET_REPORT,
163 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
164 ACD_USB_BRIGHTNESS,
165 0,
166 pdata->msgdata, 2,
167 ACD_USB_TIMEOUT);
168 brightness = pdata->msgdata[1];
169 mutex_unlock(&pdata->sysfslock);
170
171 if (retval < 0)
172 return retval;
173 else
174 return brightness;
175 }
176
177 static const struct backlight_ops appledisplay_bl_data = {
178 .get_brightness = appledisplay_bl_get_brightness,
179 .update_status = appledisplay_bl_update_status,
180 };
181
appledisplay_work(struct work_struct * work)182 static void appledisplay_work(struct work_struct *work)
183 {
184 struct appledisplay *pdata =
185 container_of(work, struct appledisplay, work.work);
186 int retval;
187
188 retval = appledisplay_bl_get_brightness(pdata->bd);
189 if (retval >= 0)
190 pdata->bd->props.brightness = retval;
191
192 /* Poll again in about 125ms if there's still a button pressed */
193 if (pdata->button_pressed)
194 schedule_delayed_work(&pdata->work, HZ / 8);
195 }
196
appledisplay_probe(struct usb_interface * iface,const struct usb_device_id * id)197 static int appledisplay_probe(struct usb_interface *iface,
198 const struct usb_device_id *id)
199 {
200 struct backlight_properties props;
201 struct appledisplay *pdata;
202 struct usb_device *udev = interface_to_usbdev(iface);
203 struct usb_endpoint_descriptor *endpoint;
204 int int_in_endpointAddr = 0;
205 int retval, brightness;
206 char bl_name[20];
207
208 /* set up the endpoint information */
209 /* use only the first interrupt-in endpoint */
210 retval = usb_find_int_in_endpoint(iface->cur_altsetting, &endpoint);
211 if (retval) {
212 dev_err(&iface->dev, "Could not find int-in endpoint\n");
213 return retval;
214 }
215
216 int_in_endpointAddr = endpoint->bEndpointAddress;
217
218 /* allocate memory for our device state and initialize it */
219 pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL);
220 if (!pdata) {
221 retval = -ENOMEM;
222 goto error;
223 }
224
225 pdata->udev = udev;
226
227 spin_lock_init(&pdata->lock);
228 INIT_DELAYED_WORK(&pdata->work, appledisplay_work);
229 mutex_init(&pdata->sysfslock);
230
231 /* Allocate buffer for control messages */
232 pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL);
233 if (!pdata->msgdata) {
234 retval = -ENOMEM;
235 goto error;
236 }
237
238 /* Allocate interrupt URB */
239 pdata->urb = usb_alloc_urb(0, GFP_KERNEL);
240 if (!pdata->urb) {
241 retval = -ENOMEM;
242 goto error;
243 }
244
245 /* Allocate buffer for interrupt data */
246 pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
247 GFP_KERNEL, &pdata->urb->transfer_dma);
248 if (!pdata->urbdata) {
249 retval = -ENOMEM;
250 dev_err(&iface->dev, "Allocating URB buffer failed\n");
251 goto error;
252 }
253
254 /* Configure interrupt URB */
255 usb_fill_int_urb(pdata->urb, udev,
256 usb_rcvintpipe(udev, int_in_endpointAddr),
257 pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete,
258 pdata, 1);
259 if (usb_submit_urb(pdata->urb, GFP_KERNEL)) {
260 retval = -EIO;
261 dev_err(&iface->dev, "Submitting URB failed\n");
262 goto error;
263 }
264
265 /* Register backlight device */
266 snprintf(bl_name, sizeof(bl_name), "appledisplay%d",
267 atomic_inc_return(&count_displays) - 1);
268 memset(&props, 0, sizeof(struct backlight_properties));
269 props.type = BACKLIGHT_RAW;
270 props.max_brightness = 0xff;
271 pdata->bd = backlight_device_register(bl_name, NULL, pdata,
272 &appledisplay_bl_data, &props);
273 if (IS_ERR(pdata->bd)) {
274 dev_err(&iface->dev, "Backlight registration failed\n");
275 retval = PTR_ERR(pdata->bd);
276 goto error;
277 }
278
279 /* Try to get brightness */
280 brightness = appledisplay_bl_get_brightness(pdata->bd);
281
282 if (brightness < 0) {
283 retval = brightness;
284 dev_err(&iface->dev,
285 "Error while getting initial brightness: %d\n", retval);
286 goto error;
287 }
288
289 /* Set brightness in backlight device */
290 pdata->bd->props.brightness = brightness;
291
292 /* save our data pointer in the interface device */
293 usb_set_intfdata(iface, pdata);
294
295 printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n");
296
297 return 0;
298
299 error:
300 if (pdata) {
301 if (pdata->urb) {
302 usb_kill_urb(pdata->urb);
303 if (pdata->urbdata)
304 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
305 pdata->urbdata, pdata->urb->transfer_dma);
306 usb_free_urb(pdata->urb);
307 }
308 if (!IS_ERR(pdata->bd))
309 backlight_device_unregister(pdata->bd);
310 kfree(pdata->msgdata);
311 }
312 usb_set_intfdata(iface, NULL);
313 kfree(pdata);
314 return retval;
315 }
316
appledisplay_disconnect(struct usb_interface * iface)317 static void appledisplay_disconnect(struct usb_interface *iface)
318 {
319 struct appledisplay *pdata = usb_get_intfdata(iface);
320
321 if (pdata) {
322 usb_kill_urb(pdata->urb);
323 cancel_delayed_work_sync(&pdata->work);
324 backlight_device_unregister(pdata->bd);
325 usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN,
326 pdata->urbdata, pdata->urb->transfer_dma);
327 usb_free_urb(pdata->urb);
328 kfree(pdata->msgdata);
329 kfree(pdata);
330 }
331
332 printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n");
333 }
334
335 static struct usb_driver appledisplay_driver = {
336 .name = "appledisplay",
337 .probe = appledisplay_probe,
338 .disconnect = appledisplay_disconnect,
339 .id_table = appledisplay_table,
340 };
341
appledisplay_init(void)342 static int __init appledisplay_init(void)
343 {
344 return usb_register(&appledisplay_driver);
345 }
346
appledisplay_exit(void)347 static void __exit appledisplay_exit(void)
348 {
349 usb_deregister(&appledisplay_driver);
350 }
351
352 MODULE_AUTHOR("Michael Hanselmann");
353 MODULE_DESCRIPTION("Apple Cinema Display driver");
354 MODULE_LICENSE("GPL");
355
356 module_init(appledisplay_init);
357 module_exit(appledisplay_exit);
358