Re: ACPI video extensions - ACPI vendor specific drivers vs. video module

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2007-08-24 at 15:09 +0100, Matthew Garrett wrote:
> On Fri, Aug 24, 2007 at 02:10:23PM +0200, Thomas Renninger wrote:
> > On Fri, 2007-08-24 at 12:48 +0100, Matthew Garrett wrote:
> > > I'm not sure there's any especially compelling reason. As long as the 
> > > platform-specific interfaces still work, there's no reason to prefer the 
> > > ACPI interfaces.
> > 
> > There are some reasons:
> >   - If the platform-specific interfaces do not work anymore, it's
> >     already too late. E.g. we see this now for 10.3 with the ThinkPad
> >     driver.
> 
> The driver shouldn't expose functionality that doesn't work.
?!? AFAIK Lenovos BIOS updates add some of those, or similar.
It seems like some Vista compatibility thing and I doubt they add code
in a BIOS update which does not work. I am not sure whether it's the
video functions itself, but Henrique told me about BIOS updates breaking
current ThinkPad brightness control.
The workaround is a bad hack, reading/writing of some kind of CMOS ACPI
device far away of any kind of spec if dmi vendor is Lenovo not IBM. If
the video module is not working there yet, this might be a short term
solution... But the video functions are there... (I could imagine there
are problems I am not aware of, but I also could imagine it's just
because nobody is using the video module on these machines yet. The
video module is not working well, but IMO that is just because it needs
some field testing. The ACPI video extensions exist in a lot of (all?)
current laptop modules and we should start making use of those.)
> 
> >   - Code cleanup: It would be great to rip out some duplicate code.
> >     Especially the code size growth of the ThinkPad module which now
> >     exceeds the 4000 line mark makes me a bit worry how this should
> >     stay maintainable (imagine Henrique not doing all the good work
> >     there anymore...).
> 
> It's not code duplication. Old Thinkpads (and we're talking 2004 here, 
> not amazingly old) don't implement the backlight control section of the 
> video spec, so removing the functionality from thinkpad-acpi would be a 
> regression.
Yes. Sorry, this looks like a mis-understanding...
Of course the ThinkPad, Asus, (don't know much about implementation
details in the other modules) must still support the models they already
do.
I am talking about this change (backported from 2.6.23 to 2.6.22).
The ACPI video spec functions are there and IMO those should get used
instead. I also saw an Asus showing the Asus specific ACPI device and
video functions. Again the video module did not really work (to be
honest I also haven't had a closer look at that, but it should be
done...). If this is really a "The one is for XP and older OS providing
vendor specific applications, the new ones are for Vista and newer OS
already supported by OS" thing, you can count the days until the vendor
specific things are going to vanish. Finally this is a great advantage
for Linux being able to make use of a specified interface. And we should
think about how to let video and vendor specific modules coexist and
bringing the video module into a better shape...

   Thomas

If someone finds something bad in this, it would be great if he/she
could let me know...:

---
 drivers/misc/thinkpad_acpi.c |  190 +++++++++++++++++++++++++++++++++++--------
 drivers/misc/thinkpad_acpi.h |   25 +++++
 include/linux/pci_ids.h      |    2 
 3 files changed, 184 insertions(+), 33 deletions(-)

Index: linux-2.6.22/drivers/misc/thinkpad_acpi.c
===================================================================
--- linux-2.6.22.orig/drivers/misc/thinkpad_acpi.c
+++ linux-2.6.22/drivers/misc/thinkpad_acpi.c
@@ -701,9 +701,19 @@ static int __init thinkpad_acpi_driver_i
 	printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
 	printk(IBM_INFO "%s\n", IBM_URL);
 
-	if (ibm_thinkpad_ec_found)
-		printk(IBM_INFO "ThinkPad EC firmware %s\n",
-		       ibm_thinkpad_ec_found);
+	printk(IBM_INFO "ThinkPad BIOS %s, EC %s\n",
+		(thinkpad_id.bios_version_str) ?
+			thinkpad_id.bios_version_str : "unknown",
+		(thinkpad_id.ec_version_str) ?
+			thinkpad_id.ec_version_str : "unknown");
+
+	if (thinkpad_id.vendor && thinkpad_id.model_str)
+		printk(IBM_INFO "%s %s\n",
+			(thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ?
+				"IBM" : ((thinkpad_id.vendor ==
+						PCI_VENDOR_ID_LENOVO) ?
+					"Lenovo" : "Unknown vendor"),
+			thinkpad_id.model_str);
 
 	return 0;
 }
@@ -2424,7 +2434,7 @@ static int __init thermal_init(struct ib
 
 	acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
 
-	if (ibm_thinkpad_ec_found && experimental) {
+	if (thinkpad_id.ec_model && experimental) {
 		/*
 		 * Direct EC access mode: sensors at registers
 		 * 0x78-0x7F, 0xC0-0xC7.  Registers return 0x00 for
@@ -2708,20 +2718,39 @@ static struct ibm_struct ecdump_driver_d
 
 static struct backlight_device *ibm_backlight_device = NULL;
 
+static struct backlight_device *ibm_backlight_device;
+
 static struct backlight_ops ibm_backlight_data = {
         .get_brightness = brightness_get,
         .update_status  = brightness_update_status,
 };
 
+static struct mutex brightness_mutex;
+
 static int __init brightness_init(struct ibm_init_struct *iibm)
 {
 	int b;
 
 	vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
 
+	mutex_init(&brightness_mutex);
+
+	if (!brightness_mode) {
+		if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO)
+			brightness_mode = 2;
+		else
+			brightness_mode = 3;
+
+		dbg_printk(TPACPI_DBG_INIT, "selected brightness_mode=%d\n",
+			brightness_mode);
+	}
+
+	if (brightness_mode > 3)
+		return -EINVAL;
+
 	b = brightness_get(NULL);
 	if (b < 0)
-		return b;
+		return 1;
 
 	ibm_backlight_device = backlight_device_register(
 					TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
@@ -2757,34 +2786,76 @@ static int brightness_update_status(stru
 				bd->props.brightness : 0);
 }
 
+/*
+ * ThinkPads can read brightness from two places: EC 0x31, or
+ * CMOS NVRAM byte 0x5E, bits 0-3.
+ */
 static int brightness_get(struct backlight_device *bd)
 {
-	u8 level;
-	if (!acpi_ec_read(brightness_offset, &level))
-		return -EIO;
-
-	level &= 0x7;
+	u8 lec = 0, lcmos = 0, level = 0;
 
+	if (brightness_mode & 1) {
+		if (!acpi_ec_read(brightness_offset, &lec))
+			return -EIO;
+		lec &= 7;
+		level = lec;
+	};
+	if (brightness_mode & 2) {
+		lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS)
+			 & TP_NVRAM_MASK_LEVEL_BRIGHTNESS)
+			>> TP_NVRAM_POS_LEVEL_BRIGHTNESS;
+		level = lcmos;
+	}
+
+	if (brightness_mode == 3 && lec != lcmos) {
+		printk(IBM_ERR
+			"CMOS NVRAM (%u) and EC (%u) do not agree "
+			"on display brightness level\n",
+			(unsigned int) lcmos,
+			(unsigned int) lec);
+		return -EIO;
+	}
 	return level;
 }
 
 static int brightness_set(int value)
 {
-	int cmos_cmd, inc, i;
-	int current_value = brightness_get(NULL);
+	int cmos_cmd, inc, i, res;
+	int current_value;
 
-	value &= 7;
+	if (value > 7)
+		return -EINVAL;
 
-	cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
+	res = mutex_lock_interruptible(&brightness_mutex);
+	if (res < 0)
+		return res;
+
+	current_value = brightness_get(NULL);
+	if (current_value < 0) {
+		res = current_value;
+		goto errout;
+	}
+
+	cmos_cmd = value > current_value ?
+			TP_CMOS_BRIGHTNESS_UP :
+			TP_CMOS_BRIGHTNESS_DOWN;
 	inc = value > current_value ? 1 : -1;
+	res = 0;
 	for (i = current_value; i != value; i += inc) {
-		if (issue_thinkpad_cmos_command(cmos_cmd))
-			return -EIO;
-		if (!acpi_ec_write(brightness_offset, i + inc))
-			return -EIO;
+		if ((brightness_mode & 2) &&
+		    issue_thinkpad_cmos_command(cmos_cmd)) {
+			res = -EIO;
+			goto errout;
+		}
+		if ((brightness_mode & 1) &&
+		    !acpi_ec_write(brightness_offset, i + inc)) {
+			res = -EIO;
+			goto errout;;
+		}
 	}
-
-	return 0;
+errout:
+	mutex_unlock(&brightness_mutex);
+	return res;
 }
 
 static int brightness_read(char *p)
@@ -3308,20 +3379,19 @@ static int __init fan_init(struct ibm_in
 			 * Enable for TP-1Y (T43), TP-78 (R51e),
 			 * TP-76 (R52), TP-70 (T43, R52), which are known
 			 * to be buggy. */
-			if (fan_control_initial_status == 0x07 &&
-			    ibm_thinkpad_ec_found &&
-			    ((ibm_thinkpad_ec_found[0] == '1' &&
-			      ibm_thinkpad_ec_found[1] == 'Y') ||
-			     (ibm_thinkpad_ec_found[0] == '7' &&
-			      (ibm_thinkpad_ec_found[1] == '6' ||
-			       ibm_thinkpad_ec_found[1] == '8' ||
-			       ibm_thinkpad_ec_found[1] == '0'))
-			    )) {
+			if (fan_control_initial_status == 0x07) {
+				switch (thinkpad_id.ec_model) {
+				case 0x5931: /* TP-1Y */
+				case 0x3837: /* TP-78 */
+				case 0x3637: /* TP-76 */
+				case 0x3037: /* TP-70 */
 				printk(IBM_NOTICE
 				       "fan_init: initial fan status is "
 				       "unknown, assuming it is in auto "
 				       "mode\n");
 				tp_features.fan_ctrl_status_undef = 1;
+				;;
+				}
 			}
 		} else {
 			printk(IBM_ERR
@@ -4081,6 +4151,61 @@ static char* __init check_dmi_for_ec(voi
 	return NULL;
 }
 
+/* Probing */
+
+static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp)
+{
+	struct dmi_device *dev = NULL;
+	char ec_fw_string[18];
+
+	if (!tp)
+		return;
+
+	memset(tp, 0, sizeof(*tp));
+
+	if (dmi_name_in_vendors("IBM"))
+		tp->vendor = PCI_VENDOR_ID_IBM;
+	else if (dmi_name_in_vendors("LENOVO"))
+		tp->vendor = PCI_VENDOR_ID_LENOVO;
+	else
+		return;
+
+	tp->bios_version_str = kstrdup(dmi_get_system_info(DMI_BIOS_VERSION),
+					GFP_KERNEL);
+	if (!tp->bios_version_str)
+		return;
+	tp->bios_model = tp->bios_version_str[0]
+			 | (tp->bios_version_str[1] << 8);
+
+	/*
+	 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
+	 * X32 or newer, all Z series;  Some models must have an
+	 * up-to-date BIOS or they will not be detected.
+	 *
+	 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
+	 */
+	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+		if (sscanf(dev->name,
+			   "IBM ThinkPad Embedded Controller -[%17c",
+			   ec_fw_string) == 1) {
+			ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
+			ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
+
+			tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
+			tp->ec_model = ec_fw_string[0]
+					| (ec_fw_string[1] << 8);
+			break;
+		}
+	}
+
+	tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION),
+					GFP_KERNEL);
+	if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) {
+		kfree(tp->model_str);
+		tp->model_str = NULL;
+	}
+}
+
 static int __init probe_for_thinkpad(void)
 {
 	int is_thinkpad;
@@ -4108,7 +4233,7 @@ static int __init probe_for_thinkpad(voi
 	 * false positives a damn great deal
 	 */
 	if (!is_thinkpad)
-		is_thinkpad = dmi_name_in_vendors("IBM");
+		is_thinkpad = (thinkpad_id.vendor == PCI_VENDOR_ID_IBM);
 
 	if (!is_thinkpad && !force_load)
 		return -ENODEV;
@@ -4251,12 +4376,12 @@ static int __init thinkpad_acpi_module_i
 	int ret, i;
 
 	/* Driver-level probe */
+	get_thinkpad_model_data(&thinkpad_id);
 	ret = probe_for_thinkpad();
 	if (ret)
 		return ret;
 
 	/* Driver initialization */
-	ibm_thinkpad_ec_found = check_dmi_for_ec();
 	IBM_ACPIHANDLE_INIT(ecrd);
 	IBM_ACPIHANDLE_INIT(ecwr);
 
@@ -4344,6 +4469,9 @@ static void thinkpad_acpi_module_exit(vo
 		remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
 
 	kfree(ibm_thinkpad_ec_found);
+	kfree(thinkpad_id.bios_version_str);
+	kfree(thinkpad_id.ec_version_str);
+	kfree(thinkpad_id.model_str);
 }
 
 module_init(thinkpad_acpi_module_init);
Index: linux-2.6.22/drivers/misc/thinkpad_acpi.h
===================================================================
--- linux-2.6.22.orig/drivers/misc/thinkpad_acpi.h
+++ linux-2.6.22/drivers/misc/thinkpad_acpi.h
@@ -32,6 +32,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 
+#include <linux/nvram.h>
 #include <linux/proc_fs.h>
 #include <linux/sysfs.h>
 #include <linux/backlight.h>
@@ -48,6 +49,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/acnamesp.h>
 
+#include <linux/pci_ids.h>
 
 /****************************************************************************
  * Main driver
@@ -78,6 +80,11 @@
 #define TP_CMOS_BRIGHTNESS_UP	4
 #define TP_CMOS_BRIGHTNESS_DOWN	5
 
+/* ThinkPad CMOS NVRAM constants */
+#define TP_NVRAM_ADDR_BRIGHTNESS       0x5e
+#define TP_NVRAM_MASK_LEVEL_BRIGHTNESS 0x07
+#define TP_NVRAM_POS_LEVEL_BRIGHTNESS 0
+
 #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
 #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
 #define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
@@ -168,9 +175,7 @@ static void tpacpi_remove_driver_attribu
 static int experimental;
 static u32 dbg_level;
 static int force_load;
-static char *ibm_thinkpad_ec_found;
 
-static char* check_dmi_for_ec(void);
 static int thinkpad_acpi_module_init(void);
 static void thinkpad_acpi_module_exit(void);
 
@@ -236,6 +241,21 @@ static struct {
 	u16 platform_drv_attrs_registered:1;
 } tp_features;
 
+struct thinkpad_id_data {
+	unsigned int vendor;	/* ThinkPad vendor:
+				 * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */
+
+	char *bios_version_str;	/* Something like 1ZET51WW (1.03z) */
+	char *ec_version_str;	/* Something like 1ZHT51WW-1.04a */
+
+	u16 bios_model;		/* Big Endian, TP-1Y = 0x5931, 0 = unknown */
+	u16 ec_model;
+
+	char *model_str;
+};
+
+static struct thinkpad_id_data thinkpad_id;
+
 static struct list_head tpacpi_all_drivers;
 
 static struct ibm_init_struct ibms_init[];
@@ -302,6 +322,7 @@ static int bluetooth_write(char *buf);
 
 static struct backlight_device *ibm_backlight_device;
 static int brightness_offset = 0x31;
+static int brightness_mode;
 
 static int brightness_init(struct ibm_init_struct *iibm);
 static void brightness_exit(void);
Index: linux-2.6.22/include/linux/pci_ids.h
===================================================================
--- linux-2.6.22.orig/include/linux/pci_ids.h
+++ linux-2.6.22/include/linux/pci_ids.h
@@ -2067,6 +2067,8 @@
 #define PCI_DEVICE_ID_ALTIMA_AC9100	0x03ea
 #define PCI_DEVICE_ID_ALTIMA_AC1003	0x03eb
 
+#define PCI_VENDOR_ID_LENOVO            0x17aa
+
 #define PCI_VENDOR_ID_ARECA		0x17d3
 #define PCI_DEVICE_ID_ARECA_1110	0x1110
 #define PCI_DEVICE_ID_ARECA_1120	0x1120


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

[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux