Re: [PATCH] Dell laptop backlight driver

From: jack
Date: Sun Oct 28 2007 - 18:30:55 EST


Fixed style.

jacopo


--- linux-2.6.23.1/Documentation/dell-laptop.txt 1970-01-01 01:00:00.000000000 +0100
+++ b/Documentation/dell-laptop.txt 2007-10-28 23:25:26.000000000 +0100
@@ -0,0 +1,107 @@
+This driver is EXPERIMENTAL, use it at YOUR OWN RISK.
+
+BEFORE TRYING THIS DRIVER OUT:
+You MUST FILL IN your laptop parameters in the source code. This
+driver is not capable of probing these parameters on its own.
+
+The parameters involved are:
+- IO Port address
+- IO command code
+- Location
+
+These parameters depend (hopefully) on the model of your laptop.
+Such information is found in a private Dell DMI table. You may
+use dmidecode or libsmbios' dumpCmos
+(http://linux.dell.com/libsmbios/download/libsmbios/). The
+latter is best since it DECODES COMPLETELY Dell's tokens.
+
+******************************************************* WITH LIBSMBIOS
+root@here:~# dumpCmos | grep 0x007d
+and you will get a line like the following...
+DMI type 0xda Handle 0xda00 CmdIO Port 0x00b2 CmdIO Code 0x0d \
+ Type 0x007d Location 0x0000 value 0000
+
+open dell-laptop.c and write the parameters accordingly under
+the probe_smi_params() function
+
+static int probe_smi_params(void)
+{
+ /*
+ * these should be probed by parsing Dell's CMOS table (type 0xda)
+ */
+ smi_params.cmdio_port = 0x00b2; /* dumpCmos CmdIO Port */
+ smi_params.cmdio_code = 0x0d; /* dumpCmos CmdIO Code */
+ smi_params.backlight_location = 0; /* dumpCmos Location */
+ smi_params.backlight_value = 0; /* dumpCmos Value (unused) */
+
+ return 0;
+}
+**********************************************************************
+
+******************************************************* WITH DMIDECODE
+(this is HAND parsing!!)
+dmidecode & look for table type 218
+
+Handle 0xDA00, DMI type 218, 125 bytes
+OEM-specific Type
+ Header and Data:
+ DA 7D 00 DA B2 00 0D 5F 0F 37 40 7D 00 00 00 00
+ 00 7E 00 02 00 00 00 40 00 04 00 01 00 41 00 04
+ 00 00 00 90 00 05 00 00 00 91 00 05 00 01 00 92
+ 00 05 00 02 00 45 01 45 01 01 00 44 01 44 01 00
+ 00 00 80 00 80 01 00 00 A0 00 A0 01 00 05 80 05
+ 80 01 00 76 01 76 01 01 00 75 01 75 01 01 00 01
+ F0 01 F0 00 00 02 F0 02 F0 00 00 03 F0 03 F0 00
+ 00 04 F0 04 F0 00 00 FF FF 00 00 00 00
+
+now you have:
+1 byte DA (table type)
+1 byte 7D (table size)
+2 bytes 00 DA (handle)
+2 bytes B2 00 (cmdio port address (swapped) )
+1 byte 0D (cmdio code)
+4 bytes 5F 0F 37 40 (supported commands ?)
+
+after that you have triplets terminated by FF FF 00 00 00 00
+in the form (type, location, value)
+
+type | loc | val
+7D 00|00 00|00 00
+7E 00|02 00|00 00
+40 00|04 00|01 00
+41 00|04 00|00 00
+90 00|05 00|00 00
+91 00|05 00|01 00
+92 00|05 00|02 00
+45 01|45 01|01 00
+44 01|44 01|00 00
+00 80|00 80|01 00
+00 A0|00 A0|01 00
+05 80|05 80|01 00
+76 01|76 01|01 00
+75 01|75 01|01 00
+01 F0|01 F0|00 00
+02 F0|02 F0|00 00
+03 F0|03 F0|00 00
+04 F0|04 F0|00 00
+end
+FF FF|00 00|00 00
+
+you can read the Location field in type 0x007d (swapped 7D 00)
+
+then modify dell-laptop.c accordingly (remember to swap the lsb
+with the msb in the port address also!)
+
+static int probe_smi_params(void)
+{
+ /*
+ * these should be probed by parsing Dell's CMOS table (type 0xda)
+ */
+ smi_params.cmdio_port = 0x00b2; /* dumpCmos CmdIO Port */
+ smi_params.cmdio_code = 0x0d; /* dumpCmos CmdIO Code */
+ smi_params.backlight_location = 0; /* dumpCmos Location */
+ smi_params.backlight_value = 0; /* dumpCmos Value (unused) */
+
+ return 0;
+}
+**********************************************************************
--- linux-2.6.23.1/drivers/misc/Makefile 2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/misc/Makefile 2007-10-28 16:42:12.000000000 +0100
@@ -15,3 +15,4 @@ obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
--- linux-2.6.23.1/drivers/misc/dell-laptop.h 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/misc/dell-laptop.h 2007-10-28 21:57:21.000000000 +0100
@@ -0,0 +1,117 @@
+/*
+ * dell-laptop.h - Dell laptop extras
+ *
+ *
+ * Copyright (C) 2007 Jacopo Antonello <jack@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef _DELL_LAPTOP_H_
+#define _DELL_LAPTOP_H_
+
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#define SMI_CMD_MAGIC 0x534D4931 /* "SMI1" */
+#define SMI_CMD_ECX 0x42534931 /* "BSI1" */
+
+#define SMI_CMD_SIZE sizeof(struct smi_cmd)
+#define SMI_BUF_SIZE sizeof(struct smi_cmd_buffer)
+#define SMI_IF_SIZE (SMI_CMD_SIZE + SMI_BUF_SIZE)
+
+#define SMI_CLASS_READ_SETTING 0
+#define SMI_CLASS_WRITE_SETTING 1
+
+#define SMI_SELECT_BATTERY 1
+#define SMI_SELECT_AC 2
+
+#define BRIGHTNESS_BATTERY (SMI_SELECT_BATTERY - 1)
+#define BRIGHTNESS_AC (SMI_SELECT_AC - 1)
+
+#define DELL_TABLE_DA 0xda
+#define DELL_CALLIF_ID_BACKLIGHT 0x007d
+
+#define DRIVER_NAME "dell-laptop"
+#define DRIVER_DESCRIPTION "Dell laptop extras"
+#define DRV_PFX "dell-laptop: "
+
+struct smi_cmd {
+ __u32 magic;
+ __u32 ebx;
+ __u32 ecx;
+ __u16 command_address;
+ __u8 command_code;
+ __u8 reserved;
+ /* __u8 command_buffer[1]; */
+} __attribute__ ((packed));
+
+struct smi_cmd_buffer
+{
+ __u16 smi_class;
+ __u16 smi_select;
+
+ __u32 cbARG1;
+ __u32 cbARG2;
+ __u32 cbARG3;
+ __u32 cbARG4;
+
+ __s32 cbRES1;
+ __s32 cbRES2;
+ __s32 cbRES3;
+ __s32 cbRES4;
+} __attribute__ ((packed));
+
+struct smi_params_t {
+ /* serialize smi calls */
+ struct mutex lock;
+
+ u16 cmdio_port;
+ u8 cmdio_code;
+ u32 supported_cmds;
+
+ u16 backlight_location;
+ u16 backlight_value;
+};
+
+enum backlight_drive_mode {
+ BACKLIGHT_DRIVE_BOTH,
+ BACKLIGHT_DRIVE_CURRENT,
+ BACKLIGHT_DRIVE_BATTERY,
+ BACKLIGHT_DRIVE_AC
+};
+
+static struct platform_driver dell_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct backlight_device *dell_backlight_dev;
+static struct platform_device *dell_platform_dev;
+
+static u8 *smi_if;
+static dma_addr_t smi_handle;
+
+static struct smi_params_t smi_params;
+
+static enum backlight_drive_mode backlight_drive_mode;
+static u32 brightness_vals[2];
+static u32 brightness_min_value;
+
+#endif
--- linux-2.6.23.1/drivers/misc/dell-laptop.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/misc/dell-laptop.c 2007-10-28 22:17:09.000000000 +0100
@@ -0,0 +1,683 @@
+/*
+ * dell-laptop.c - Dell laptop extras
+ *
+ * This driver implements only backlight control for now.
+ * This driver assumes that you are not using bios password
+ * protection and does not work if it is enabled.
+ *
+ * This driver is EXPERIMENTAL and it is not yet complete. In
+ * particular it lacks proper probing for the hardware
+ * it is meant to control.
+ *
+ * WARNING: you MUST fill in your laptop CMOS parameters, by
+ * modifying the default values found in probe_smi_params().
+ * Failing to do so could cause unpredictable effects on your
+ * laptop. In order to do this you may use the dumpCmos utility
+ * found in libsmbios
+ * (http://linux.dell.com/libsmbios/download/libsmbios/)
+ *
+ * Parts of this driver were inspired by dcdbas.c and libsmbios
+ * (http://linux.dell.com/libsmbios/download/libsmbios/)
+ *
+ * Copyright (C) 2007 Jacopo Antonello <jack@xxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#define DRIVER_VERSION "0.1"
+
+/*
+ * Changelog:
+ * 2007-12-22 0.01 initial release
+ * support for backlight on Dell M70 laptops
+ * TODO fix framebuffer support
+ * TODO add cmos token lookup
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+
+#include "dell-laptop.h"
+
+MODULE_DESCRIPTION(DRIVER_DESCRIPTION " (version " DRIVER_VERSION ")");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("jacopo antonello <jack at antonello.org>");
+MODULE_LICENSE("GPL");
+
+/* --------------------------------------------------------------------------
+ * smi
+ * -------------------------------------------------------------------------- */
+
+static int probe_smi_params(void)
+{
+ /*
+ * these should be probed by parsing Dell's CMOS table (dmi type 0xda)
+ */
+ smi_params.cmdio_port = 0x00b2; /* libsmbios' dumpCmos CmdIO Port */
+ smi_params.cmdio_code = 0x0d; /* libsmbios' dumpCmos CmdIO Code */
+ smi_params.backlight_location = 0; /* set to dumpCmos Location */
+ smi_params.backlight_value = 0; /* set to dumpCmos Value (unused) */
+
+ return 0;
+}
+
+static int trigger_smi(u16 ioport, u8 iocode, struct smi_cmd_buffer *parms)
+{
+ struct smi_cmd *cmd = (struct smi_cmd *)smi_if;
+ struct smi_cmd_buffer *buf = (struct smi_cmd_buffer *)
+ (smi_if + SMI_CMD_SIZE);
+ cpumask_t old_mask;
+ int ret = 0;
+
+ mutex_lock(&smi_params.lock);
+
+ cmd->magic = SMI_CMD_MAGIC;
+ cmd->ebx = (u32)virt_to_phys(buf);
+ cmd->ecx = SMI_CMD_ECX;
+ cmd->command_address = ioport; /* libsmbios' dumpCmos CmdIO Port */
+ cmd->command_code = iocode; /* libsmbios' dumpCmos CmdIO Code */
+ cmd->reserved = 0;
+
+ buf->smi_class = parms->smi_class; /* 0 read, 1 write */
+ buf->smi_select = parms->smi_select; /* 1 battery mode, 2 ac mode */
+
+ buf->cbARG1 = parms->cbARG1; /* set to dumpCmos Location */
+ buf->cbARG2 = parms->cbARG2;
+ buf->cbARG3 = parms->cbARG3;
+ buf->cbARG4 = parms->cbARG4;
+
+ buf->cbRES1 = parms->cbRES1; /* set to -1 to tell errors */
+ buf->cbRES2 = parms->cbRES2;
+ buf->cbRES3 = parms->cbRES3;
+ buf->cbRES4 = parms->cbRES4;
+
+ /* dcdbas.c */
+ old_mask = current->cpus_allowed;
+ set_cpus_allowed(current, cpumask_of_cpu(0));
+ if (smp_processor_id() != 0) {
+ dev_dbg(&dell_backlight_dev->dev, "%s: failed to get CPU 0\n",
+ __FUNCTION__);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ asm volatile (
+ "outb %b0,%w1"
+ : /* no output args */
+ : "a" (cmd->command_code),
+ "d" (cmd->command_address),
+ "b" (cmd->ebx),
+ "c" (cmd->ecx)
+ : "memory");
+
+ switch (buf->cbRES1) {
+ case -6: /* output buffer not large enough */
+ case -5: /* output buffer format error */
+ case -3: /* unhandled smi call */
+ case -2: /* unsupported smi call */
+ case -1: /* bios returned error for smi call */
+ ret = -EBUSY;
+ printk(KERN_WARNING DRV_PFX "SMI failed\n");
+ goto out;
+ }
+
+ /* copy back results */
+ parms->smi_class = buf->smi_class;
+ parms->smi_select = buf->smi_select;
+
+ parms->cbARG1 = buf->cbARG1;
+ parms->cbARG2 = buf->cbARG2;
+ parms->cbARG3 = buf->cbARG3;
+ parms->cbARG4 = buf->cbARG4;
+
+ parms->cbRES1 = buf->cbRES1;
+ parms->cbRES2 = buf->cbRES2;
+ parms->cbRES3 = buf->cbRES3;
+ parms->cbRES4 = buf->cbRES4;
+
+out:
+ set_cpus_allowed(current, old_mask);
+
+ mutex_unlock(&smi_params.lock);
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * brightness
+ * -------------------------------------------------------------------------- */
+
+/* use read_brightness(SMI_SELECT_*) */
+static void read_brightness(int select)
+{
+ struct smi_cmd_buffer buf = {
+ .smi_class = SMI_CLASS_READ_SETTING,
+ .smi_select = select,
+
+ .cbARG1 = smi_params.backlight_location,
+ .cbARG2 = 0,
+ .cbARG3 = 0,
+ .cbARG4 = 0,
+
+ .cbRES1 = -1,
+ .cbRES2 = 0,
+ .cbRES3 = 0,
+ .cbRES4 = 0
+ };
+
+ if (trigger_smi(smi_params.cmdio_port, smi_params.cmdio_code, &buf)) {
+ printk(KERN_ERR DRV_PFX "unable to read brightness levels\n");
+ return;
+ }
+
+ brightness_vals[select - 1] = buf.cbRES2;
+ printk(KERN_DEBUG DRV_PFX " brightness_vals[ %d ]: %d\n", select - 1,
+ brightness_vals[select - 1]);
+ brightness_min_value = buf.cbRES3;
+ printk(KERN_DEBUG DRV_PFX " brightness_min_value: %d\n",
+ brightness_min_value);
+ dell_backlight_dev->props.max_brightness = buf.cbRES4;
+ printk(KERN_DEBUG DRV_PFX " props.max_brightness: %d\n",
+ dell_backlight_dev->props.max_brightness);
+}
+
+/* use write_brightness(SMI_SELECT_*) */
+static void write_brightness(int select)
+{
+ struct smi_cmd_buffer buf = {
+ .smi_class = SMI_CLASS_WRITE_SETTING,
+ .smi_select = select,
+
+ .cbARG1 = smi_params.backlight_location,
+ .cbARG2 = brightness_vals[select - 1],
+ .cbARG3 = 0,
+ .cbARG4 = 0,
+
+ .cbRES1 = -1,
+ .cbRES2 = 0,
+ .cbRES3 = 0,
+ .cbRES4 = 0
+ };
+
+ if (trigger_smi(smi_params.cmdio_port, smi_params.cmdio_code, &buf)) {
+ printk(KERN_ERR DRV_PFX "unable to write brightness\n");
+ return;
+ }
+
+ if (buf.cbRES2 != brightness_vals[select - 1])
+ printk(KERN_ERR DRV_PFX "brightness value unchanged\n");
+
+ brightness_vals[select - 1] = buf.cbRES2;
+ printk(KERN_DEBUG DRV_PFX " brightness_vals[ %d ]: %d\n", select - 1,
+ brightness_vals[select - 1]);
+}
+
+static int dell_backlight_update_status(struct backlight_device *dev)
+{
+ dev->props.brightness = dev->props.brightness < brightness_min_value ?
+ brightness_min_value :
+ dev->props.brightness > dev->props.max_brightness ?
+ dev->props.max_brightness : dev->props.brightness;
+
+ switch (backlight_drive_mode) {
+ case BACKLIGHT_DRIVE_BOTH:
+ brightness_vals[BRIGHTNESS_BATTERY] = dev->props.brightness;
+ brightness_vals[BRIGHTNESS_AC] = dev->props.brightness;
+ write_brightness(SMI_SELECT_BATTERY);
+ write_brightness(SMI_SELECT_AC);
+ break;
+ case BACKLIGHT_DRIVE_AC:
+ brightness_vals[BRIGHTNESS_AC] = dev->props.brightness;
+ write_brightness(SMI_SELECT_AC);
+ break;
+ case BACKLIGHT_DRIVE_BATTERY:
+ brightness_vals[BRIGHTNESS_BATTERY] = dev->props.brightness;
+ write_brightness(SMI_SELECT_BATTERY);
+ break;
+ case BACKLIGHT_DRIVE_CURRENT:
+ printk(KERN_ERR DRV_PFX
+ "BACKLIGHT_DRIVER_CURRENT unimplemented\n");
+ default:
+ printk(KERN_ERR DRV_PFX "unknown backlight_drive_mode\n");
+ }
+
+ return dev->props.brightness;
+}
+
+static void update_from_brightness_vals(void)
+{
+ switch (backlight_drive_mode) {
+ case BACKLIGHT_DRIVE_BOTH:
+ dell_backlight_dev->props.brightness =
+ brightness_vals[BRIGHTNESS_BATTERY];
+ if (brightness_vals[BRIGHTNESS_AC] !=
+ brightness_vals[BRIGHTNESS_BATTERY]) {
+ brightness_vals[BRIGHTNESS_AC] =
+ brightness_vals[BRIGHTNESS_BATTERY];
+ /* force ac to the same value */
+ write_brightness(SMI_SELECT_AC);
+ }
+ break;
+ case BACKLIGHT_DRIVE_AC:
+ dell_backlight_dev->props.brightness =
+ brightness_vals[BRIGHTNESS_AC];
+ break;
+ case BACKLIGHT_DRIVE_BATTERY:
+ dell_backlight_dev->props.brightness =
+ brightness_vals[BRIGHTNESS_BATTERY];
+ break;
+ case BACKLIGHT_DRIVE_CURRENT:
+ printk(KERN_ERR DRV_PFX
+ "BACKLIGHT_DRIVER_CURRENT unimplemented\n");
+ default:
+ printk(KERN_ERR DRV_PFX "unknown backlight_drive_mode\n");
+ }
+}
+
+static int dell_backlight_get_brightness(struct backlight_device *dev)
+{
+ /* is locking needed? */
+ mutex_lock(&dell_backlight_dev->update_lock);
+
+ read_brightness(SMI_SELECT_BATTERY);
+ read_brightness(SMI_SELECT_AC);
+
+ update_from_brightness_vals();
+
+ mutex_unlock(&dell_backlight_dev->update_lock);
+
+ return dev->props.brightness;
+}
+
+static struct backlight_ops dell_backlight_ops = {
+ .update_status = dell_backlight_update_status,
+ .get_brightness = dell_backlight_get_brightness
+};
+
+static int dell_backlight_init(struct device *dev)
+{
+ dell_backlight_dev = backlight_device_register(
+ "dell-laptop",
+ NULL,
+ NULL,
+ &dell_backlight_ops);
+
+ if (IS_ERR(dell_backlight_dev)) {
+ dell_backlight_dev = NULL;
+ return PTR_ERR(dell_backlight_dev);
+ }
+
+ backlight_drive_mode = BACKLIGHT_DRIVE_AC;
+
+ dell_backlight_get_brightness(dell_backlight_dev);
+
+ return 0;
+}
+
+/* --------------------------------------------------------------------------
+ * sysfs
+ * -------------------------------------------------------------------------- */
+
+/* show */
+static ssize_t show_bl_drive_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *mode = "unknown";
+
+ switch (backlight_drive_mode) {
+ case BACKLIGHT_DRIVE_BOTH:
+ mode = "BACKLIGHT_DRIVE_BOTH";
+ break;
+ case BACKLIGHT_DRIVE_CURRENT:
+ mode = "BACKLIGHT_DRIVE_CURRENT";
+ break;
+ case BACKLIGHT_DRIVE_BATTERY:
+ mode = "BACKLIGHT_DRIVE_BATTERY";
+ break;
+ case BACKLIGHT_DRIVE_AC:
+ mode = "BACKLIGHT_DRIVE_AC";
+ break;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", mode);
+}
+
+static ssize_t show_bl_drive_mode_list(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int len = 0;
+
+ len = snprintf(buf, PAGE_SIZE,
+ "%d:BACKLIGHT_DRIVE_BOTH\n",
+ BACKLIGHT_DRIVE_BOTH);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%d:BACKLIGHT_DRIVE_CURRENT\n",
+ BACKLIGHT_DRIVE_CURRENT);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%d:BACKLIGHT_DRIVE_BATTERY\n",
+ BACKLIGHT_DRIVE_BATTERY);
+ len += snprintf(buf + len, PAGE_SIZE - len,
+ "%d:BACKLIGHT_DRIVE_AC\n",
+ BACKLIGHT_DRIVE_AC);
+
+ return len;
+}
+
+static ssize_t show_cmdio_port(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n", smi_params.cmdio_port);
+}
+
+static ssize_t show_cmdio_code(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%02x\n", smi_params.cmdio_code);
+}
+
+static ssize_t show_bl_location(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "0x%04x\n",
+ smi_params.backlight_location);
+}
+
+static ssize_t show_bl_battery_val(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ brightness_vals[BRIGHTNESS_BATTERY]);
+}
+
+static ssize_t show_bl_ac_val(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ brightness_vals[BRIGHTNESS_AC]);
+}
+
+static ssize_t show_bl_min_val(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ brightness_min_value);
+}
+
+static ssize_t show_bl_max_val(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ dell_backlight_dev->props.max_brightness);
+}
+
+/* store */
+static ssize_t store_bl_drive_mode(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int mode = simple_strtoul(buf, &endp, 0);
+
+ switch (mode) {
+ case BACKLIGHT_DRIVE_BOTH:
+ case BACKLIGHT_DRIVE_BATTERY:
+ case BACKLIGHT_DRIVE_AC:
+ break;
+ case BACKLIGHT_DRIVE_CURRENT:
+ default:
+ return -ENXIO;
+ }
+
+ mutex_lock(&dell_backlight_dev->update_lock);
+ backlight_drive_mode = mode;
+ mutex_unlock(&dell_backlight_dev->update_lock);
+
+ return count;
+}
+
+static ssize_t store_cmdio_port(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int port = simple_strtoul(buf, &endp, 0);
+
+ if (port <= 0 || port >= 0xffff)
+ return -ENXIO;
+
+ mutex_lock(&smi_params.lock);
+ smi_params.cmdio_port = port;
+ mutex_unlock(&smi_params.lock);
+
+ return count;
+}
+
+static ssize_t store_cmdio_code(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int code = simple_strtoul(buf, &endp, 0);
+
+ if (code <= 0 || code >= 0xff)
+ return -ENXIO;
+
+ mutex_lock(&smi_params.lock);
+ smi_params.cmdio_code = code;
+ mutex_unlock(&smi_params.lock);
+
+ return count;
+}
+
+static ssize_t store_bl_location(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int location = simple_strtoul(buf, &endp, 0);
+
+ if (location <= 0 || location >= 0xffff)
+ return -ENXIO;
+
+ mutex_lock(&dell_backlight_dev->update_lock);
+ smi_params.backlight_location = location;
+ mutex_unlock(&dell_backlight_dev->update_lock);
+
+ return count;
+}
+
+static ssize_t store_bl_battery_val(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int level = simple_strtoul(buf, &endp, 0);
+
+ if (level < brightness_min_value ||
+ level > dell_backlight_dev->props.max_brightness)
+ return -ENXIO;
+
+ mutex_lock(&dell_backlight_dev->update_lock);
+ brightness_vals[BRIGHTNESS_BATTERY] = level;
+ write_brightness(SMI_SELECT_BATTERY);
+ update_from_brightness_vals();
+ mutex_unlock(&dell_backlight_dev->update_lock);
+
+ return count;
+}
+
+static ssize_t store_bl_ac_val(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char *endp;
+ int level = simple_strtoul(buf, &endp, 0);
+
+ if (level < brightness_min_value ||
+ level > dell_backlight_dev->props.max_brightness)
+ return -ENXIO;
+
+ mutex_lock(&dell_backlight_dev->update_lock);
+ brightness_vals[BRIGHTNESS_AC] = level;
+ write_brightness(SMI_SELECT_AC);
+ if (backlight_drive_mode == BACKLIGHT_DRIVE_BOTH) {
+ /* update_from_brightness_vals forces level to battery mode */
+ brightness_vals[BRIGHTNESS_BATTERY] = level;
+ }
+ update_from_brightness_vals();
+ mutex_unlock(&dell_backlight_dev->update_lock);
+
+ return count;
+}
+
+/* static */
+#define DELL_DEVICE_ATTR(_name, _mode, _show, _store) \
+ struct device_attribute dev_attr_##_name = { \
+ .attr = { \
+ .name = __stringify(_name), \
+ .mode = _mode \
+ }, \
+ .show = _show, \
+ .store = _store \
+ }
+
+static DELL_DEVICE_ATTR(backlight_drive_mode, 0666,
+ show_bl_drive_mode, store_bl_drive_mode);
+static DELL_DEVICE_ATTR(backlight_drive_mode_list, 0444,
+ show_bl_drive_mode_list, NULL);
+static DELL_DEVICE_ATTR(cmdio_port, 0600,
+ show_cmdio_port, store_cmdio_port);
+static DELL_DEVICE_ATTR(cmdio_code, 0600,
+ show_cmdio_code, store_cmdio_code);
+static DELL_DEVICE_ATTR(backlight_location, 0600,
+ show_bl_location, store_bl_location);
+static DELL_DEVICE_ATTR(backlight_battery_val, 0666,
+ show_bl_battery_val, store_bl_battery_val);
+static DELL_DEVICE_ATTR(backlight_ac_val, 0666,
+ show_bl_ac_val, store_bl_ac_val);
+static DELL_DEVICE_ATTR(backlight_min_val, 0444,
+ show_bl_min_val, NULL);
+static DELL_DEVICE_ATTR(backlight_max_val, 0444,
+ show_bl_max_val, NULL);
+
+static struct attribute *dell_laptop_attrs[] = {
+ &dev_attr_backlight_drive_mode.attr,
+ &dev_attr_backlight_drive_mode_list.attr,
+ &dev_attr_cmdio_port.attr,
+ &dev_attr_cmdio_code.attr,
+ &dev_attr_backlight_location.attr,
+ &dev_attr_backlight_battery_val.attr,
+ &dev_attr_backlight_ac_val.attr,
+ &dev_attr_backlight_min_val.attr,
+ &dev_attr_backlight_max_val.attr,
+ NULL
+};
+
+static struct attribute_group dell_laptop_attr_group = {
+ .attrs = dell_laptop_attrs
+};
+
+/* --------------------------------------------------------------------------
+ * init / cleanup
+ * -------------------------------------------------------------------------- */
+
+static int __init dell_laptop_init(void)
+{
+ int error;
+
+ error = platform_driver_register(&dell_driver);
+ if (error)
+ return error;
+
+ dell_platform_dev = platform_device_alloc(DRIVER_NAME, -1);
+ if (!dell_platform_dev) {
+ error = -ENOMEM;
+ goto unregister_platform;
+ }
+
+ error = platform_device_add(dell_platform_dev);
+ if (error)
+ goto put_platform;
+
+ /* 32-bit address space */
+ dell_platform_dev->dev.coherent_dma_mask = DMA_32BIT_MASK;
+ dell_platform_dev->dev.dma_mask =
+ &dell_backlight_dev->dev.coherent_dma_mask;
+
+ /* init smi mutex */
+ mutex_init(&smi_params.lock);
+
+ probe_smi_params();
+
+ /* allocate smi buffer */
+ smi_if = dma_alloc_coherent(&dell_platform_dev->dev,
+ SMI_IF_SIZE, &smi_handle, GFP_KERNEL);
+ if (!smi_if) {
+ error = -ENOMEM;
+ goto put_platform;
+ }
+
+ error = dell_backlight_init(&dell_platform_dev->dev);
+ if (error)
+ goto fail_backlight;
+
+ error = sysfs_create_group(&dell_platform_dev->dev.kobj,
+ &dell_laptop_attr_group);
+ if (error) {
+ error = -ENODEV;
+ goto fail_sysfs;
+
+ }
+
+ printk(KERN_INFO DRV_PFX "%s (ver: %s) loaded\n",
+ DRIVER_NAME, DRIVER_VERSION);
+
+ return 0;
+
+
+fail_sysfs:
+ /* */
+fail_backlight:
+ /* */
+put_platform:
+ platform_device_put(dell_platform_dev);
+unregister_platform:
+ platform_driver_unregister(&dell_driver);
+
+ return error;
+}
+
+static void __exit dell_laptop_exit(void)
+{
+ backlight_device_unregister(dell_backlight_dev);
+
+ dma_free_coherent(&dell_platform_dev->dev,
+ SMI_IF_SIZE, smi_if, smi_handle);
+ smi_if = 0;
+ smi_handle = 0;
+
+ sysfs_remove_group(&dell_platform_dev->dev.kobj,
+ &dell_laptop_attr_group);
+
+ platform_device_unregister(dell_platform_dev);
+ platform_driver_unregister(&dell_driver);
+
+ printk(KERN_INFO DRV_PFX "%s (ver: %s) unloaded\n",
+ DRIVER_NAME, DRIVER_VERSION);
+}
+
+module_init(dell_laptop_init);
+module_exit(dell_laptop_exit);
+
--- linux-2.6.23.1/drivers/misc/Kconfig 2007-10-12 18:43:44.000000000 +0200
+++ b/drivers/misc/Kconfig 2007-10-28 16:42:12.000000000 +0100
@@ -130,6 +130,21 @@ config MSI_LAPTOP

If you have an MSI S270 laptop, say Y or M here.

+config DELL_LAPTOP
+ tristate "Dell Laptop Extras (EXPERIMENTAL)"
+ depends on X86
+ depends on EXPERIMENTAL
+ depends on BACKLIGHT_CLASS_DEVICE
+ ---help---
+ This driver implements LCD backlight control for Dell laptops. BEFORE
+ using this driver, please fill in your laptop model parameters as described
+ in dell-laptop.txt. Using this driver without doing this operation
+ may have UNPREDICTABLE effects on your laptop. You have been warned!
+
+ Just read <file:Documentation/dell-laptop.txt> first.
+
+ If you have a Dell laptop, say Y or M here.
+
config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on X86 && ACPI


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/