}
- if (sony_pf_add())
+ result = sony_pf_add();
+ if (result)
goto outbacklight;
/* create sony_pf sysfs attributes related to the SNC device */
struct sony_pic_dev {
int model;
+ u8 camera_power;
+ u8 bluetooth_power;
struct acpi_device *acpi_dev;
struct sony_pic_irq *cur_irq;
struct sony_pic_ioport *cur_ioport;
return v1;
}
+static u8 sony_pic_call3(u8 dev, u8 fn, u8 v)
+{
+ u8 v1;
+
+ wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(dev, spic_dev.cur_ioport->io.minimum + 4);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(fn, spic_dev.cur_ioport->io.minimum);
+ wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG);
+ outb(v, spic_dev.cur_ioport->io.minimum);
+ v1 = inb_p(spic_dev.cur_ioport->io.minimum);
+ dprintk("sony_pic_call3: 0x%.4x\n", v1);
+ return v1;
+}
+
+/* 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)
+{
+ u8 v;
+
+ v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS);
+ return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY));
+}
+
+static void sony_pic_camera_off(void)
+{
+ wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE,
+ SONYPI_CAMERA_MUTE_MASK),
+ ITERATIONS_SHORT);
+
+ if (!spic_dev.camera_power)
+ return;
+
+ sony_pic_call2(0x91, 0);
+ spic_dev.camera_power = 0;
+}
+
+static void sony_pic_camera_on(void)
+{
+ int i, j;
+
+ if (spic_dev.camera_power)
+ return;
+
+ for (j = 5; j > 0; j--) {
+
+ while (sony_pic_call2(0x91, 0x1))
+ msleep(10);
+ sony_pic_call1(0x93);
+
+ for (i = 400; i > 0; i--) {
+ if (sony_pic_camera_ready())
+ break;
+ msleep(10);
+ }
+ if (i)
+ break;
+ }
+
+ if (j == 0) {
+ printk(KERN_WARNING "sonypi: failed to power on camera\n");
+ return;
+ }
+
+ wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL,
+ 0x5a),
+ ITERATIONS_SHORT);
+
+ spic_dev.camera_power = 1;
+}
+
+static ssize_t sony_pic_camerapower_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned long value;
+ if (count > 31)
+ return -EINVAL;
+
+ value = simple_strtoul(buffer, NULL, 10);
+ if (value)
+ sony_pic_camera_on();
+ else
+ sony_pic_camera_off();
+
+ return count;
+}
+
+static ssize_t sony_pic_camerapower_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ return snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.camera_power);
+}
+
+/* bluetooth subsystem power state */
+static void sony_pic_set_bluetoothpower(u8 state)
+{
+ state = !!state;
+ if (spic_dev.bluetooth_power == state)
+ return;
+ sony_pic_call2(0x96, state);
+ sony_pic_call1(0x82);
+ spic_dev.bluetooth_power = state;
+}
+
+static ssize_t sony_pic_bluetoothpower_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned long value;
+ if (count > 31)
+ return -EINVAL;
+
+ value = simple_strtoul(buffer, NULL, 10);
+ sony_pic_set_bluetoothpower(value);
+
+ return count;
+}
+
+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);
+}
+
+/* fan speed */
+/* FAN0 information (reverse engineered from ACPI tables) */
+#define SONY_PIC_FAN0_STATUS 0x93
+static ssize_t sony_pic_fanspeed_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned long value;
+ if (count > 31)
+ return -EINVAL;
+
+ value = simple_strtoul(buffer, NULL, 10);
+ if (ec_write(SONY_PIC_FAN0_STATUS, value))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_pic_fanspeed_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ u8 value = 0;
+ if (ec_read(SONY_PIC_FAN0_STATUS, &value))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", value);
+}
+
+#define SPIC_ATTR(_name, _mode) \
+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(fanspeed, 0644);
+
+static struct attribute *spic_attributes[] = {
+ &spic_attr_camerapower.attr,
+ &spic_attr_bluetoothpower.attr,
+ &spic_attr_fanspeed.attr,
+ NULL
+};
+
+static struct attribute_group spic_attribute_group = {
+ .attrs = spic_attributes
+};
+
/*
* ACPI callbacks
*/
sony_laptop_remove_input();
+ /* pf attrs */
+ sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+ sony_pf_remove();
+
list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) {
list_del(&io->list);
kfree(io);
goto err_free_irq;
}
+ spic_dev.bluetooth_power = -1;
+ /* create device attributes */
+ result = sony_pf_add();
+ if (result)
+ goto err_disable_device;
+
+ result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group);
+ if (result)
+ goto err_remove_pf;
+
return 0;
+err_remove_pf:
+ sony_pf_remove();
+
+err_disable_device:
+ sony_pic_disable(device);
+
err_free_irq:
free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev);