Merge git://git.kernel.org/pub/scm/linux/kernel/git/holtmann/bluetooth-2.6
[powerpc.git] / drivers / misc / sony-laptop.c
index 09e7539..8ee0321 100644 (file)
@@ -62,7 +62,8 @@
 #include <acpi/acpi_bus.h>
 #include <asm/uaccess.h>
 #include <linux/sonypi.h>
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#include <linux/sony-laptop.h>
+#ifdef CONFIG_SONYPI_COMPAT
 #include <linux/poll.h>
 #include <linux/miscdevice.h>
 #endif
@@ -113,7 +114,7 @@ MODULE_PARM_DESC(camera,
                 "set this to 1 to enable Motion Eye camera controls "
                 "(only use it if you have a C1VE or C1VN model)");
 
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#ifdef CONFIG_SONYPI_COMPAT
 static int minor = -1;
 module_param(minor, int, 0);
 MODULE_PARM_DESC(minor,
@@ -923,11 +924,13 @@ struct sony_pic_dev {
        int                     model;
        u8                      camera_power;
        u8                      bluetooth_power;
+       u8                      wwan_power;
        struct acpi_device      *acpi_dev;
        struct sony_pic_irq     *cur_irq;
        struct sony_pic_ioport  *cur_ioport;
        struct list_head        interrupts;
        struct list_head        ioports;
+       struct mutex            lock;
 };
 
 static struct sony_pic_dev spic_dev = {
@@ -1218,13 +1221,37 @@ static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
 
 /* camera tests and poweron/poweroff */
 #define SONYPI_CAMERA_PICTURE          5
-#define SONYPI_CAMERA_MUTE_MASK                0x40
 #define SONYPI_CAMERA_CONTROL          0x10
-#define SONYPI_CAMERA_STATUS           7
-#define SONYPI_CAMERA_STATUS_READY     0x2
-#define SONYPI_CAMERA_STATUS_POSITION  0x4
 
-static int sony_pic_camera_ready(void)
+#define SONYPI_CAMERA_BRIGHTNESS               0
+#define SONYPI_CAMERA_CONTRAST                 1
+#define SONYPI_CAMERA_HUE                      2
+#define SONYPI_CAMERA_COLOR                    3
+#define SONYPI_CAMERA_SHARPNESS                        4
+
+#define SONYPI_CAMERA_EXPOSURE_MASK            0xC
+#define SONYPI_CAMERA_WHITE_BALANCE_MASK       0x3
+#define SONYPI_CAMERA_PICTURE_MODE_MASK                0x30
+#define SONYPI_CAMERA_MUTE_MASK                        0x40
+
+/* the rest don't need a loop until not 0xff */
+#define SONYPI_CAMERA_AGC                      6
+#define SONYPI_CAMERA_AGC_MASK                 0x30
+#define SONYPI_CAMERA_SHUTTER_MASK             0x7
+
+#define SONYPI_CAMERA_SHUTDOWN_REQUEST         7
+#define SONYPI_CAMERA_CONTROL                  0x10
+
+#define SONYPI_CAMERA_STATUS                   7
+#define SONYPI_CAMERA_STATUS_READY             0x2
+#define SONYPI_CAMERA_STATUS_POSITION          0x4
+
+#define SONYPI_DIRECTION_BACKWARDS             0x4
+
+#define SONYPI_CAMERA_REVISION                         8
+#define SONYPI_CAMERA_ROMVERSION               9
+
+static int __sony_pic_camera_ready(void)
 {
        u8 v;
 
@@ -1232,7 +1259,7 @@ static int sony_pic_camera_ready(void)
        return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
 }
 
-static int sony_pic_camera_off(void)
+static int __sony_pic_camera_off(void)
 {
        if (!camera) {
                printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
@@ -1250,7 +1277,7 @@ static int sony_pic_camera_off(void)
        return 0;
 }
 
-static int sony_pic_camera_on(void)
+static int __sony_pic_camera_on(void)
 {
        int i, j, x;
 
@@ -1269,7 +1296,7 @@ static int sony_pic_camera_on(void)
                sony_pic_call1(0x93);
 
                for (i = 400; i > 0; i--) {
-                       if (sony_pic_camera_ready())
+                       if (__sony_pic_camera_ready())
                                break;
                        msleep(10);
                }
@@ -1290,35 +1317,99 @@ static int sony_pic_camera_on(void)
        return 0;
 }
 
-static ssize_t sony_pic_camerapower_store(struct device *dev,
+/* External camera command (exported to the motion eye v4l driver) */
+int sony_pic_camera_command(int command, u8 value)
+{
+       if (!camera)
+               return -EIO;
+
+       mutex_lock(&spic_dev.lock);
+
+       switch (command) {
+       case SONY_PIC_COMMAND_SETCAMERA:
+               if (value)
+                       __sony_pic_camera_on();
+               else
+                       __sony_pic_camera_off();
+               break;
+       case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACONTRAST:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAHUE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERACOLOR:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERASHARPNESS:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAPICTURE:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value),
+                               ITERATIONS_SHORT);
+               break;
+       case SONY_PIC_COMMAND_SETCAMERAAGC:
+               wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value),
+                               ITERATIONS_SHORT);
+               break;
+       default:
+               printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
+                      command);
+               break;
+       }
+       mutex_unlock(&spic_dev.lock);
+       return 0;
+}
+EXPORT_SYMBOL(sony_pic_camera_command);
+
+/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */
+static void sony_pic_set_wwanpower(u8 state)
+{
+       state = !!state;
+       mutex_lock(&spic_dev.lock);
+       if (spic_dev.wwan_power == state) {
+               mutex_unlock(&spic_dev.lock);
+               return;
+       }
+       sony_pic_call2(0xB0, state);
+       spic_dev.wwan_power = state;
+       mutex_unlock(&spic_dev.lock);
+}
+
+static ssize_t sony_pic_wwanpower_store(struct device *dev,
                struct device_attribute *attr,
                const char *buffer, size_t count)
 {
        unsigned long value;
-       int result;
        if (count > 31)
                return -EINVAL;
 
        value = simple_strtoul(buffer, NULL, 10);
-       if (value)
-               result = sony_pic_camera_on();
-       else
-               result = sony_pic_camera_off();
-
-       if (result)
-               return result;
+       sony_pic_set_wwanpower(value);
 
        return count;
 }
 
-static ssize_t sony_pic_camerapower_show(struct device *dev,
+static ssize_t sony_pic_wwanpower_show(struct device *dev,
                struct device_attribute *attr, char *buffer)
 {
-       return snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.camera_power);
+       ssize_t count;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
 }
 
 /* bluetooth subsystem power state */
-static void sony_pic_set_bluetoothpower(u8 state)
+static void __sony_pic_set_bluetoothpower(u8 state)
 {
        state = !!state;
        if (spic_dev.bluetooth_power == state)
@@ -1337,7 +1428,9 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
                return -EINVAL;
 
        value = simple_strtoul(buffer, NULL, 10);
-       sony_pic_set_bluetoothpower(value);
+       mutex_lock(&spic_dev.lock);
+       __sony_pic_set_bluetoothpower(value);
+       mutex_unlock(&spic_dev.lock);
 
        return count;
 }
@@ -1345,7 +1438,11 @@ static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
 static ssize_t sony_pic_bluetoothpower_show(struct device *dev,
                struct device_attribute *attr, char *buffer)
 {
-       return snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
+       ssize_t count = 0;
+       mutex_lock(&spic_dev.lock);
+       count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power);
+       mutex_unlock(&spic_dev.lock);
+       return count;
 }
 
 /* fan speed */
@@ -1391,13 +1488,13 @@ struct device_attribute spic_attr_##_name = __ATTR(_name,       \
                _mode, sony_pic_## _name ##_show,               \
                sony_pic_## _name ##_store)
 
-static SPIC_ATTR(camerapower, 0644);
 static SPIC_ATTR(bluetoothpower, 0644);
+static SPIC_ATTR(wwanpower, 0644);
 static SPIC_ATTR(fanspeed, 0644);
 
 static struct attribute *spic_attributes[] = {
-       &spic_attr_camerapower.attr,
        &spic_attr_bluetoothpower.attr,
+       &spic_attr_wwanpower.attr,
        &spic_attr_fanspeed.attr,
        NULL
 };
@@ -1407,7 +1504,7 @@ static struct attribute_group spic_attribute_group = {
 };
 
 /******** SONYPI compatibility **********/
-#ifdef CONFIG_SONY_LAPTOP_OLD
+#ifdef CONFIG_SONYPI_COMPAT
 
 /* battery / brightness / temperature  addresses */
 #define SONYPI_BAT_FLAGS       0x81
@@ -1518,7 +1615,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
        u16 val16;
        int value;
 
-       /*down(&sonypi_device.lock);*/
+       mutex_lock(&spic_dev.lock);
        switch (cmd) {
        case SONYPI_IOCGBRT:
                if (sony_backlight_device == NULL) {
@@ -1602,7 +1699,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
                        ret = -EFAULT;
                        break;
                }
-               sony_pic_set_bluetoothpower(val8);
+               __sony_pic_set_bluetoothpower(val8);
                break;
        /* FAN Controls */
        case SONYPI_IOCGFAN:
@@ -1633,7 +1730,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp,
        default:
                ret = -EINVAL;
        }
-       /*up(&sonypi_device.lock);*/
+       mutex_unlock(&spic_dev.lock);
        return ret;
 }
 
@@ -1673,7 +1770,6 @@ static int sonypi_compat_init(void)
        }
 
        init_waitqueue_head(&sonypi_compat.fifo_proc_list);
-       /*init_MUTEX(&sonypi_device.lock);*/
 
        if (minor != -1)
                sonypi_misc_device.minor = minor;
@@ -1702,7 +1798,7 @@ static void sonypi_compat_exit(void)
 static int sonypi_compat_init(void) { return 0; }
 static void sonypi_compat_exit(void) { }
 static void sonypi_compat_report_event(u8 event) { }
-#endif /* CONFIG_SONY_LAPTOP_OLD */
+#endif /* CONFIG_SONYPI_COMPAT */
 
 /*
  * ACPI callbacks
@@ -1742,7 +1838,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
                                if (!interrupt)
                                        return AE_ERROR;
 
-                               list_add(&interrupt->list, &dev->interrupts);
+                               list_add_tail(&interrupt->list, &dev->interrupts);
                                interrupt->irq.triggering = p->triggering;
                                interrupt->irq.polarity = p->polarity;
                                interrupt->irq.sharable = p->sharable;
@@ -1764,7 +1860,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
                        if (!ioport)
                                return AE_ERROR;
 
-                       list_add(&ioport->list, &dev->ioports);
+                       list_add_tail(&ioport->list, &dev->ioports);
                        memcpy(&ioport->io, io, sizeof(*io));
                        return AE_OK;
                }
@@ -2004,6 +2100,7 @@ static int sony_pic_add(struct acpi_device *device)
        spic_dev.acpi_dev = device;
        strcpy(acpi_device_class(device), "sony/hotkey");
        spic_dev.model = sony_pic_detect_device_type();
+       mutex_init(&spic_dev.lock);
 
        /* read _PRS resources */
        result = sony_pic_possible_resources(device);