appledisplay.c revision 7d12e780e003f93433d49ce78cfedf4b4c52adc5
1069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton/* 2069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * Apple Cinema Display driver 3069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * 4069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) 5069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * 6069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * Thanks to Caskey L. Dickson for his work with acdctl. 7069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * 8069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * This program is free software; you can redistribute it and/or modify 9069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * it under the terms of the GNU General Public License as published by 10069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * the Free Software Foundation; either version 2 of the License, or 11069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * (at your option) any later version. 12069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * 13069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * This program is distributed in the hope that it will be useful, 14069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * but WITHOUT ANY WARRANTY; without even the implied warranty of 15069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * GNU General Public License for more details. 17069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * 18069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * You should have received a copy of the GNU General Public License 19069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * along with this program; if not, write to the Free Software 20069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 21069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton */ 22069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 23069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/kernel.h> 24069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/errno.h> 25069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/init.h> 26069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/module.h> 27069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/usb.h> 28069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/backlight.h> 29069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/timer.h> 30069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/workqueue.h> 31069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <asm/atomic.h> 32069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <asm/semaphore.h> 33069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 34069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define APPLE_VENDOR_ID 0x05AC 35069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 36069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define USB_REQ_GET_REPORT 0x01 37069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define USB_REQ_SET_REPORT 0x09 38069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 39069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_USB_TIMEOUT 250 40069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 41069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_USB_EDID 0x0302 42069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_USB_BRIGHTNESS 0x0310 43069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 44069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_BTN_NONE 0 45069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_BTN_BRIGHT_UP 3 46069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_BTN_BRIGHT_DOWN 4 47069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 48069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_URB_BUFFER_LEN 2 49069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define ACD_MSG_BUFFER_LEN 2 50069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 51069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#define APPLEDISPLAY_DEVICE(prod) \ 52069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ 53069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DEVICE_ID_MATCH_INT_CLASS | \ 54069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DEVICE_ID_MATCH_INT_PROTOCOL, \ 55069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .idVendor = APPLE_VENDOR_ID, \ 56069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .idProduct = (prod), \ 57069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .bInterfaceClass = USB_CLASS_HID, \ 58069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .bInterfaceProtocol = 0x00 59069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 60069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton/* table of devices that work with this driver */ 61069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct usb_device_id appledisplay_table [] = { 62069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x9218) }, 63069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x9219) }, 64069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x921d) }, 65069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 66069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Terminating entry */ 67069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { } 68069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 69069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_DEVICE_TABLE(usb, appledisplay_table); 70069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 71069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton/* Structure to hold all of our device specific stuff */ 72069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstruct appledisplay { 73069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_device *udev; /* usb device */ 74069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct urb *urb; /* usb request block */ 75069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct backlight_device *bd; /* backlight device */ 76069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton char *urbdata; /* interrupt URB data buffer */ 77069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton char *msgdata; /* control message data buffer */ 78069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 79069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct work_struct work; 80069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int button_pressed; 81069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spinlock_t lock; 82069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 83069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 84069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic atomic_t count_displays = ATOMIC_INIT(0); 85069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct workqueue_struct *wq; 86069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 877d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void appledisplay_complete(struct urb *urb) 88069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 89069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = urb->context; 90069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton unsigned long flags; 91069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 92069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 93069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton switch (urb->status) { 94069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case 0: 95069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* success */ 96069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 97069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -EOVERFLOW: 98069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_ERR "appletouch: OVERFLOW with data " 99069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton "length %d, actual length is %d\n", 100069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_URB_BUFFER_LEN, pdata->urb->actual_length); 101069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ECONNRESET: 102069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ENOENT: 103069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ESHUTDOWN: 104069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* This urb is terminated, clean up */ 105069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton dbg("%s - urb shutting down with status: %d", 106069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton __FUNCTION__, urb->status); 107069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return; 108069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton default: 109069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton dbg("%s - nonzero urb status received: %d", 110069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton __FUNCTION__, urb->status); 111069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto exit; 112069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 113069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 114069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_lock_irqsave(&pdata->lock, flags); 115069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 116069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton switch(pdata->urbdata[1]) { 117069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_BRIGHT_UP: 118069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_BRIGHT_DOWN: 119069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->button_pressed = 1; 120069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton queue_work(wq, &pdata->work); 121069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 122069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_NONE: 123069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton default: 124069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->button_pressed = 0; 125069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 126069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 127069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 128069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_unlock_irqrestore(&pdata->lock, flags); 129069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 130069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonexit: 131069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_submit_urb(pdata->urb, GFP_ATOMIC); 132069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval) { 133069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("%s - usb_submit_urb failed with result %d", 134069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton __FUNCTION__, retval); 135069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 136069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 137069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 138069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int appledisplay_bl_update_status(struct backlight_device *bd) 139069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 140069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = class_get_devdata(&bd->class_dev); 141069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 142069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 143069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata[0] = 0x10; 144069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata[1] = bd->props->brightness; 145069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 146069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_control_msg( 147069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev, 148069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_sndctrlpipe(pdata->udev, 0), 149069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_REQ_SET_REPORT, 150069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 151069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_BRIGHTNESS, 152069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 0, 153069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata, 2, 154069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_TIMEOUT); 155069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 156069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 157069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 158069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 159069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int appledisplay_bl_get_brightness(struct backlight_device *bd) 160069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 161069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = class_get_devdata(&bd->class_dev); 162069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 163069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 164069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_control_msg( 165069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev, 166069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_rcvctrlpipe(pdata->udev, 0), 167069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_REQ_GET_REPORT, 168069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 169069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_BRIGHTNESS, 170069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 0, 171069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata, 2, 172069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_TIMEOUT); 173069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 174069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval < 0) 175069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 176069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton else 177069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return pdata->msgdata[1]; 178069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 179069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 180069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct backlight_properties appledisplay_bl_data = { 181069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .owner = THIS_MODULE, 182069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .get_brightness = appledisplay_bl_get_brightness, 183069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .update_status = appledisplay_bl_update_status, 184069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .max_brightness = 0xFF 185069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 186069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 187069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic void appledisplay_work(void *private) 188069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 189069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = private; 190069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 191069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 192069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton up(&pdata->bd->sem); 193069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = appledisplay_bl_get_brightness(pdata->bd); 194069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval >= 0) 195069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->bd->props->brightness = retval; 196069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton down(&pdata->bd->sem); 197069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 198069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Poll again in about 125ms if there's still a button pressed */ 199069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->button_pressed) 200069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton schedule_delayed_work(&pdata->work, HZ / 8); 201069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 202069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 203069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int appledisplay_probe(struct usb_interface *iface, 204069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton const struct usb_device_id *id) 205069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 206069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata; 207069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_device *udev = interface_to_usbdev(iface); 208069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_host_interface *iface_desc; 209069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_endpoint_descriptor *endpoint; 210069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int int_in_endpointAddr = 0; 211069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int i, retval = -ENOMEM, brightness; 212069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton char bl_name[20]; 213069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 214069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* set up the endpoint information */ 215069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* use only the first interrupt-in endpoint */ 216069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton iface_desc = iface->cur_altsetting; 217069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 218069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton endpoint = &iface_desc->endpoint[i].desc; 219069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!int_in_endpointAddr && 220069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton (endpoint->bEndpointAddress & USB_DIR_IN) && 221069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 222069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_ENDPOINT_XFER_INT)) { 223069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* we found an interrupt in endpoint */ 224069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int_in_endpointAddr = endpoint->bEndpointAddress; 225069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 226069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 227069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 228069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!int_in_endpointAddr) { 229069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("Could not find int-in endpoint"); 230069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return -EIO; 231069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 232069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 233069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* allocate memory for our device state and initialize it */ 234069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL); 235069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata) { 236069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 237069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("Out of memory"); 238069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 239069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 240069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 241069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev = udev; 242069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 243069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_lock_init(&pdata->lock); 244069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton INIT_WORK(&pdata->work, appledisplay_work, pdata); 245069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 246069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate buffer for control messages */ 247069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL); 248069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->msgdata) { 249069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 250069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Allocating buffer for control messages " 251069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton "failed"); 252069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 253069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 254069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 255069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate interrupt URB */ 256069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urb = usb_alloc_urb(0, GFP_KERNEL); 257069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->urb) { 258069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 259069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Allocating URB failed"); 260069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 261069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 262069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 263069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate buffer for interrupt data */ 264069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata = usb_buffer_alloc(pdata->udev, ACD_URB_BUFFER_LEN, 265069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton GFP_KERNEL, &pdata->urb->transfer_dma); 266069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->urbdata) { 267069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 268069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Allocating URB buffer failed"); 269069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 270069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 271069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 272069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Configure interrupt URB */ 273069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_fill_int_urb(pdata->urb, udev, 274069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_rcvintpipe(udev, int_in_endpointAddr), 275069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete, 276069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata, 1); 277069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (usb_submit_urb(pdata->urb, GFP_KERNEL)) { 278069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -EIO; 279069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Submitting URB failed"); 280069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 281069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 282069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 283069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Register backlight device */ 284069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton snprintf(bl_name, sizeof(bl_name), "appledisplay%d", 285069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton atomic_inc_return(&count_displays) - 1); 286069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->bd = backlight_device_register(bl_name, pdata, 287069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton &appledisplay_bl_data); 288069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (IS_ERR(pdata->bd)) { 289069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Backlight registration failed"); 290069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 291069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 292069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 293069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Try to get brightness */ 294069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton up(&pdata->bd->sem); 295069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton brightness = appledisplay_bl_get_brightness(pdata->bd); 296069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton down(&pdata->bd->sem); 297069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 298069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (brightness < 0) { 299069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = brightness; 300069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("appledisplay: Error while getting initial brightness: %d", retval); 301069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 302069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 303069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 304069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Set brightness in backlight device */ 305069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton up(&pdata->bd->sem); 306069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->bd->props->brightness = brightness; 307069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton down(&pdata->bd->sem); 308069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 309069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* save our data pointer in the interface device */ 310069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_set_intfdata(iface, pdata); 311069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 312069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n"); 313069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 314069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return 0; 315069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 316069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonerror: 317069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata) { 318069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->urb) { 319069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_kill_urb(pdata->urb); 320069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->urbdata) 321069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN, 322069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, pdata->urb->transfer_dma); 323069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_free_urb(pdata->urb); 324069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 325069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->bd) 326069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton backlight_device_unregister(pdata->bd); 327069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata->msgdata); 328069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 329069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_set_intfdata(iface, NULL); 330069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata); 331069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 332069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 333069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 334069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic void appledisplay_disconnect(struct usb_interface *iface) 335069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 336069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = usb_get_intfdata(iface); 337069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 338069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata) { 339069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_kill_urb(pdata->urb); 340069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton cancel_delayed_work(&pdata->work); 341069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton backlight_device_unregister(pdata->bd); 342069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_buffer_free(pdata->udev, ACD_URB_BUFFER_LEN, 343069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, pdata->urb->transfer_dma); 344069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_free_urb(pdata->urb); 345069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata->msgdata); 346069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata); 347069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 348069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 349069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n"); 350069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 351069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 352069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct usb_driver appledisplay_driver = { 353069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .name = "appledisplay", 354069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .probe = appledisplay_probe, 355069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .disconnect = appledisplay_disconnect, 356069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .id_table = appledisplay_table, 357069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 358069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 359069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int __init appledisplay_init(void) 360069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 361069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton wq = create_singlethread_workqueue("appledisplay"); 362069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!wq) { 363069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton err("Could not create work queue\n"); 364069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return -ENOMEM; 365069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 366069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 367069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return usb_register(&appledisplay_driver); 368069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 369069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 370069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic void __exit appledisplay_exit(void) 371069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 372069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton flush_workqueue(wq); 373069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton destroy_workqueue(wq); 374069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_deregister(&appledisplay_driver); 375069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 376069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 377069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_AUTHOR("Michael Hanselmann"); 378069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_DESCRIPTION("Apple Cinema Display driver"); 379069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_LICENSE("GPL"); 380069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 381069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonmodule_init(appledisplay_init); 382069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonmodule_exit(appledisplay_exit); 383