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> 275a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 28069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/usb.h> 29069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/backlight.h> 30069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/timer.h> 31069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton#include <linux/workqueue.h> 3260063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.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 */ 6133b9e16243fd69493be3ddda7be73226c8be586aNémeth Mártonstatic const struct usb_device_id appledisplay_table[] = { 62069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x9218) }, 63069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x9219) }, 644b372072b554723b508e53249dd1b594ad93fbc2pancho horrillo { APPLEDISPLAY_DEVICE(0x921c) }, 65069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { APPLEDISPLAY_DEVICE(0x921d) }, 66069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 67069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Terminating entry */ 68069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton { } 69069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 70069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_DEVICE_TABLE(usb, appledisplay_table); 71069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 72069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton/* Structure to hold all of our device specific stuff */ 73069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstruct appledisplay { 74069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_device *udev; /* usb device */ 75069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct urb *urb; /* usb request block */ 76069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct backlight_device *bd; /* backlight device */ 7737e9066b2f85480d99d3795373f5ef0b00ac1189pancho horrillo u8 *urbdata; /* interrupt URB data buffer */ 7837e9066b2f85480d99d3795373f5ef0b00ac1189pancho horrillo u8 *msgdata; /* control message data buffer */ 79069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 806d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howells struct delayed_work work; 81069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int button_pressed; 82069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spinlock_t lock; 83069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 84069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 85069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic atomic_t count_displays = ATOMIC_INIT(0); 86069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct workqueue_struct *wq; 87069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 887d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic void appledisplay_complete(struct urb *urb) 89069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 90069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = urb->context; 91069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton unsigned long flags; 9213f9782d8e2e6a20e5fc44dd758d5747fdfd76abGreg Kroah-Hartman int status = urb->status; 93069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 94069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 9513f9782d8e2e6a20e5fc44dd758d5747fdfd76abGreg Kroah-Hartman switch (status) { 96069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case 0: 97069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* success */ 98069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 99069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -EOVERFLOW: 100069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_ERR "appletouch: OVERFLOW with data " 101069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton "length %d, actual length is %d\n", 102069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_URB_BUFFER_LEN, pdata->urb->actual_length); 103069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ECONNRESET: 104069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ENOENT: 105069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case -ESHUTDOWN: 106069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* This urb is terminated, clean up */ 10713f9782d8e2e6a20e5fc44dd758d5747fdfd76abGreg Kroah-Hartman dbg("%s - urb shuttingdown with status: %d", 108441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison __func__, status); 109069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return; 110069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton default: 111069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton dbg("%s - nonzero urb status received: %d", 112441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison __func__, status); 113069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto exit; 114069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 115069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 116069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_lock_irqsave(&pdata->lock, flags); 117069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 118069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton switch(pdata->urbdata[1]) { 119069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_BRIGHT_UP: 120069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_BRIGHT_DOWN: 121069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->button_pressed = 1; 1226d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howells queue_delayed_work(wq, &pdata->work, 0); 123069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 124069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton case ACD_BTN_NONE: 125069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton default: 126069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->button_pressed = 0; 127069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 128069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 129069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 130069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_unlock_irqrestore(&pdata->lock, flags); 131069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 132069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonexit: 133069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_submit_urb(pdata->urb, GFP_ATOMIC); 134069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval) { 135fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&pdata->udev->dev, 136fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman "%s - usb_submit_urb failed with result %d\n", 137441b62c1edb986827154768d89bbac0ba779984fHarvey Harrison __func__, retval); 138069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 139069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 140069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 141069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int appledisplay_bl_update_status(struct backlight_device *bd) 142069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 143655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie struct appledisplay *pdata = bl_get_data(bd); 144069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 145069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 146069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata[0] = 0x10; 147599a52d12629394236d785615808845823875868Richard Purdie pdata->msgdata[1] = bd->props.brightness; 148069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 149069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_control_msg( 150069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev, 151069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_sndctrlpipe(pdata->udev, 0), 152069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_REQ_SET_REPORT, 153069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 154069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_BRIGHTNESS, 155069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 0, 156069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata, 2, 157069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_TIMEOUT); 158069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 159069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 160069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 161069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 162069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int appledisplay_bl_get_brightness(struct backlight_device *bd) 163069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 164655bfd7aebb12481ab9275284d9500bee5ba3e70Richard Purdie struct appledisplay *pdata = bl_get_data(bd); 165069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 166069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 167069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = usb_control_msg( 168069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev, 169069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_rcvctrlpipe(pdata->udev, 0), 170069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_REQ_GET_REPORT, 171069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 172069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_BRIGHTNESS, 173069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 0, 174069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata, 2, 175069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton ACD_USB_TIMEOUT); 176069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 177069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval < 0) 178069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 179069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton else 180069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return pdata->msgdata[1]; 181069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 182069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 183cedf8a78421943441b9011ce7bcdab55f07d2ea6Bruno Prémontstatic const struct backlight_ops appledisplay_bl_data = { 184069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .get_brightness = appledisplay_bl_get_brightness, 185069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .update_status = appledisplay_bl_update_status, 186069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 187069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 1886d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howellsstatic void appledisplay_work(struct work_struct *work) 189069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 1906d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howells struct appledisplay *pdata = 1916d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howells container_of(work, struct appledisplay, work.work); 192069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int retval; 193069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 194069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = appledisplay_bl_get_brightness(pdata->bd); 195069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (retval >= 0) 196599a52d12629394236d785615808845823875868Richard Purdie pdata->bd->props.brightness = retval; 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{ 206a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett struct backlight_properties props; 207069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata; 208069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_device *udev = interface_to_usbdev(iface); 209069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_host_interface *iface_desc; 210069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct usb_endpoint_descriptor *endpoint; 211069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int int_in_endpointAddr = 0; 212069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int i, retval = -ENOMEM, brightness; 213069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton char bl_name[20]; 214069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 215069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* set up the endpoint information */ 216069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* use only the first interrupt-in endpoint */ 217069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton iface_desc = iface->cur_altsetting; 218069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { 219069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton endpoint = &iface_desc->endpoint[i].desc; 22016f96376174bfbcb1b40734f83f5701161a5e63bLuiz Fernando N. Capitulino if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) { 221069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* we found an interrupt in endpoint */ 222069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton int_in_endpointAddr = endpoint->bEndpointAddress; 223069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton break; 224069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 225069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 226069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!int_in_endpointAddr) { 227fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Could not find int-in endpoint\n"); 228069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return -EIO; 229069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 230069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 231069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* allocate memory for our device state and initialize it */ 232069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata = kzalloc(sizeof(struct appledisplay), GFP_KERNEL); 233069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata) { 234069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 235fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Out of memory\n"); 236069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 237069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 238069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 239069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->udev = udev; 240069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 241069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton spin_lock_init(&pdata->lock); 2426d5aefb8eaa38e44b5b8cf60c812aceafc02d924David Howells INIT_DELAYED_WORK(&pdata->work, appledisplay_work); 243069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 244069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate buffer for control messages */ 245069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->msgdata = kmalloc(ACD_MSG_BUFFER_LEN, GFP_KERNEL); 246069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->msgdata) { 247069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 248fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, 249fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman "Allocating buffer for control messages failed\n"); 250069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 251069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 252069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 253069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate interrupt URB */ 254069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urb = usb_alloc_urb(0, GFP_KERNEL); 255069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->urb) { 256069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 257fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Allocating URB failed\n"); 258069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 259069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 260069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 261069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Allocate buffer for interrupt data */ 262997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack pdata->urbdata = usb_alloc_coherent(pdata->udev, ACD_URB_BUFFER_LEN, 263069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton GFP_KERNEL, &pdata->urb->transfer_dma); 264069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!pdata->urbdata) { 265069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -ENOMEM; 266fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Allocating URB buffer failed\n"); 267069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 268069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 269069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 270069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Configure interrupt URB */ 271069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_fill_int_urb(pdata->urb, udev, 272069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_rcvintpipe(udev, int_in_endpointAddr), 273069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, ACD_URB_BUFFER_LEN, appledisplay_complete, 274069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata, 1); 275069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (usb_submit_urb(pdata->urb, GFP_KERNEL)) { 276069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = -EIO; 277fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Submitting URB failed\n"); 278069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 279069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 280069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 281069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Register backlight device */ 282069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton snprintf(bl_name, sizeof(bl_name), "appledisplay%d", 283069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton atomic_inc_return(&count_displays) - 1); 284a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett memset(&props, 0, sizeof(struct backlight_properties)); 285bb7ca747f8d6243b3943c5b133048652020f4a50Matthew Garrett props.type = BACKLIGHT_RAW; 286a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett props.max_brightness = 0xff; 28781450b73dde07f473a4a7208b209b4c8b7251d90Len Brown pdata->bd = backlight_device_register(bl_name, NULL, pdata, 288a19a6ee6cad2b20292a774c2f56ba8039b0fac9cMatthew Garrett &appledisplay_bl_data, &props); 289069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (IS_ERR(pdata->bd)) { 290fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, "Backlight registration failed\n"); 291cedf8a78421943441b9011ce7bcdab55f07d2ea6Bruno Prémont retval = PTR_ERR(pdata->bd); 292069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 293069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 294069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 295069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Try to get brightness */ 296069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton brightness = appledisplay_bl_get_brightness(pdata->bd); 297069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 298069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (brightness < 0) { 299069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton retval = brightness; 300fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman dev_err(&iface->dev, 301fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman "Error while getting initial brightness: %d\n", retval); 302069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton goto error; 303069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 304069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 305069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* Set brightness in backlight device */ 306599a52d12629394236d785615808845823875868Richard Purdie pdata->bd->props.brightness = brightness; 307069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 308069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton /* save our data pointer in the interface device */ 309069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_set_intfdata(iface, pdata); 310069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 311069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_INFO "appledisplay: Apple Cinema Display connected\n"); 312069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 313069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return 0; 314069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 315069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonerror: 316069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata) { 317069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->urb) { 318069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_kill_urb(pdata->urb); 319069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata->urbdata) 320997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN, 321069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, pdata->urb->transfer_dma); 322069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_free_urb(pdata->urb); 323069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 324bedf0883cbe3015d21aec5ed47ddffb429f6cca7Julien Brunel if (pdata->bd && !IS_ERR(pdata->bd)) 325069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton backlight_device_unregister(pdata->bd); 326069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata->msgdata); 327069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 328069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_set_intfdata(iface, NULL); 329069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata); 330069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return retval; 331069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 332069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 333069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic void appledisplay_disconnect(struct usb_interface *iface) 334069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 335069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton struct appledisplay *pdata = usb_get_intfdata(iface); 336069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 337069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (pdata) { 338069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_kill_urb(pdata->urb); 339069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton cancel_delayed_work(&pdata->work); 340069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton backlight_device_unregister(pdata->bd); 341997ea58eb92f9970b8af7aae48800d0ef43b9423Daniel Mack usb_free_coherent(pdata->udev, ACD_URB_BUFFER_LEN, 342069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton pdata->urbdata, pdata->urb->transfer_dma); 343069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_free_urb(pdata->urb); 344069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata->msgdata); 345069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton kfree(pdata); 346069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 347069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 348069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton printk(KERN_INFO "appledisplay: Apple Cinema Display disconnected\n"); 349069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 350069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 351069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic struct usb_driver appledisplay_driver = { 352069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .name = "appledisplay", 353069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .probe = appledisplay_probe, 354069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .disconnect = appledisplay_disconnect, 355069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton .id_table = appledisplay_table, 356069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton}; 357069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 358069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic int __init appledisplay_init(void) 359069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 360069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton wq = create_singlethread_workqueue("appledisplay"); 361069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton if (!wq) { 362fd3f1917e345d852ef9ae36178719f4e639f70aeGreg Kroah-Hartman printk(KERN_ERR "appledisplay: Could not create work queue\n"); 363069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return -ENOMEM; 364069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton } 365069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 366069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton return usb_register(&appledisplay_driver); 367069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 368069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 369069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonstatic void __exit appledisplay_exit(void) 370069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton{ 371069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton flush_workqueue(wq); 372069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton destroy_workqueue(wq); 373069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton usb_deregister(&appledisplay_driver); 374069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton} 375069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 376069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_AUTHOR("Michael Hanselmann"); 377069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_DESCRIPTION("Apple Cinema Display driver"); 378069e8a65cd7970b15672825541be59218d9a8a0fAndrew MortonMODULE_LICENSE("GPL"); 379069e8a65cd7970b15672825541be59218d9a8a0fAndrew Morton 380069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonmodule_init(appledisplay_init); 381069e8a65cd7970b15672825541be59218d9a8a0fAndrew Mortonmodule_exit(appledisplay_exit); 382