#include <linux/acpi.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#include <acpi/video.h>
+#endif
/*
* This driver is needed because a number of Samsung laptops do not hook
struct debugfs_blob_wrapper f0000_wrapper;
struct debugfs_blob_wrapper data_wrapper;
+ struct debugfs_blob_wrapper sdiag_wrapper;
};
struct samsung_laptop;
struct work_struct kbd_led_work;
struct samsung_laptop_debug debug;
+ struct samsung_quirks *quirks;
bool handle_backlight;
bool has_stepping_quirk;
+
+ char sdiag[64];
};
+struct samsung_quirks {
+ bool broken_acpi_video;
+};
+static struct samsung_quirks samsung_unknown = {};
+
+static struct samsung_quirks samsung_broken_acpi_video = {
+ .broken_acpi_video = true,
+};
static bool force;
module_param(force, bool, 0);
if (debug) {
if (in)
- pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ pr_info("SABI command:0x%04x "
+ "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
command, in->d0, in->d1, in->d2, in->d3);
else
- pr_info("SABI 0x%04x", command);
+ pr_info("SABI command:0x%04x", command);
}
/* enable memory to be able to write to it */
/* see if the command actually succeeded */
complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
+
+ /* iface_data = 0xFF happens when a command is not known
+ * so we only add a warning in debug mode since we will
+ * probably issue some unknown command at startup to find
+ * out which features are supported */
+ if (complete != 0xaa || (iface_data == 0xff && debug))
pr_warn("SABI command 0x%04x failed with"
" completion flag 0x%02x and interface data 0x%02x",
command, complete, iface_data);
+
+ if (complete != 0xaa || iface_data == 0xff) {
ret = -EINVAL;
goto exit;
}
}
if (debug && out) {
- pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
out->d0, out->d1, out->d2, out->d3);
}
static int sabi_set_commandb(struct samsung_laptop *samsung,
u16 command, u8 data)
{
- struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 };
+ struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
in.data[0] = data;
return sabi_command(samsung, command, &in, NULL);
static int seclinux_rfkill_set(void *data, bool blocked)
{
- struct samsung_laptop *samsung = data;
+ struct samsung_rfkill *srfkill = data;
+ struct samsung_laptop *samsung = srfkill->samsung;
const struct sabi_commands *commands = &samsung->config->commands;
return sabi_set_commandb(samsung, commands->set_wireless_button,
int ret;
ret = swsmi_wireless_status(samsung, &data);
- if (ret)
+ if (ret) {
+ /* Some swsmi laptops use the old seclinux way to control
+ * wireless devices */
+ if (ret == -EINVAL)
+ ret = samsung_rfkill_init_seclinux(samsung);
return ret;
+ }
/* 0x02 seems to mean that the device is no present/available */
return 0;
}
-static mode_t samsung_sysfs_is_visible(struct kobject *kobj,
+static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = container_of(kobj, struct device, kobj);
samsung->debug.data_wrapper.data = &samsung->debug.data;
samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
+ samsung->debug.sdiag_wrapper.data = samsung->sdiag;
+ samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
+
dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
samsung->debug.root, &samsung->debug.command);
if (!dent)
if (!dent)
goto error_debugfs;
+ dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.sdiag_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
return 0;
error_debugfs:
printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
}
+static void __init samsung_sabi_diag(struct samsung_laptop *samsung)
+{
+ int loca = find_signature(samsung->f0000_segment, "SDiaG@");
+ int i;
+
+ if (loca == 0xffff)
+ return ;
+
+ /* Example:
+ * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A
+ *
+ * Product name: 90X3A
+ * BIOS Version: 07HL
+ */
+ loca += 1;
+ for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) {
+ char temp = readb(samsung->f0000_segment + loca);
+
+ if (isalnum(temp) || temp == '/' || temp == '-')
+ samsung->sdiag[i++] = temp;
+ else
+ break ;
+ }
+
+ if (debug && samsung->sdiag[0])
+ pr_info("sdiag: %s", samsung->sdiag);
+}
+
static int __init samsung_sabi_init(struct samsung_laptop *samsung)
{
const struct sabi_config *config = NULL;
samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
if (!samsung->f0000_segment) {
- pr_err("Can't map the segment at 0xf0000\n");
+ if (debug || force)
+ pr_err("Can't map the segment at 0xf0000\n");
ret = -EINVAL;
goto exit;
}
+ samsung_sabi_diag(samsung);
+
/* Try to find one of the signatures in memory to find the header */
for (i = 0; sabi_configs[i].test_string != 0; ++i) {
samsung->config = &sabi_configs[i];
}
if (loca == 0xffff) {
- pr_err("This computer does not support SABI\n");
+ if (debug || force)
+ pr_err("This computer does not support SABI\n");
ret = -ENODEV;
goto exit;
}
if (samsung->handle_backlight)
check_for_stepping_quirk(samsung);
+ pr_info("detected SABI interface: %s\n",
+ samsung->config->test_string);
+
exit:
if (ret)
samsung_sabi_exit(samsung);
return 0;
}
-static int __init dmi_check_cb(const struct dmi_system_id *id)
+static struct samsung_quirks *quirks;
+
+static int __init samsung_dmi_matched(const struct dmi_system_id *d)
{
- pr_info("found laptop model '%s'\n", id->ident);
- return 1;
+ quirks = d->driver_data;
+ return 0;
}
static struct dmi_system_id __initdata samsung_dmi_table[] = {
{
- .ident = "N128",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
- DMI_MATCH(DMI_BOARD_NAME, "N128"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N130",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
- DMI_MATCH(DMI_BOARD_NAME, "N130"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N510",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
- DMI_MATCH(DMI_BOARD_NAME, "N510"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X125",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
- DMI_MATCH(DMI_BOARD_NAME, "X125"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X120/X170",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
- DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC10",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
- DMI_MATCH(DMI_BOARD_NAME, "NC10"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NP-Q45",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
- DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X360",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R410 Plus",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
- DMI_MATCH(DMI_BOARD_NAME, "R460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R518",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
- DMI_MATCH(DMI_BOARD_NAME, "R518"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
},
- .callback = dmi_check_cb,
},
{
- .ident = "R519/R719",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
- DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
},
- .callback = dmi_check_cb,
},
{
- .ident = "N150/N210/N220",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
},
- .callback = dmi_check_cb,
},
{
- .ident = "N220",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N220"),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
},
- .callback = dmi_check_cb,
},
+ /* Specific DMI ids for laptop with quirks */
{
- .ident = "N150/N210/N220/N230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
+ .callback = samsung_dmi_matched,
+ .ident = "N150P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150P"),
},
- .callback = dmi_check_cb,
+ .driver_data = &samsung_broken_acpi_video,
},
{
- .ident = "N150P/N210P/N220P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
- DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
- DMI_MATCH(DMI_BOARD_NAME, "SR700"),
+ .callback = samsung_dmi_matched,
+ .ident = "N145P/N250P/N260P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
},
- .callback = dmi_check_cb,
+ .driver_data = &samsung_broken_acpi_video,
},
{
- .ident = "R530/R730",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
- DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
+ .callback = samsung_dmi_matched,
+ .ident = "N150/N210/N220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
},
- .callback = dmi_check_cb,
+ .driver_data = &samsung_broken_acpi_video,
},
{
- .ident = "NF110/NF210/NF310",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
- DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
+ .callback = samsung_dmi_matched,
+ .ident = "NF110/NF210/NF310",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
+ DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
},
- .callback = dmi_check_cb,
- },
- {
- .ident = "N145P/N250P/N260P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
- DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R70/R71",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
- DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "P460",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
- DMI_MATCH(DMI_BOARD_NAME, "P460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R528/R728",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
- DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC210/NC110",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
- DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X520",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
- DMI_MATCH(DMI_BOARD_NAME, "X520"),
- },
- .callback = dmi_check_cb,
+ .driver_data = &samsung_broken_acpi_video,
},
{ },
};
struct samsung_laptop *samsung;
int ret;
+ quirks = &samsung_unknown;
if (!force && !dmi_check_system(samsung_dmi_table))
return -ENODEV;
mutex_init(&samsung->sabi_mutex);
samsung->handle_backlight = true;
+ samsung->quirks = quirks;
-#ifdef CONFIG_ACPI
+
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
/* Don't handle backlight here if the acpi video already handle it */
if (acpi_video_backlight_support()) {
- pr_info("Backlight controlled by ACPI video driver\n");
- samsung->handle_backlight = false;
+ if (samsung->quirks->broken_acpi_video) {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
+ } else {
+ samsung->handle_backlight = false;
+ }
}
#endif
if (ret)
goto error_sabi;
+#ifdef CONFIG_ACPI
+ /* Only log that if we are really on a sabi platform */
+ if (acpi_video_backlight_support() &&
+ !samsung->quirks->broken_acpi_video)
+ pr_info("Backlight controlled by ACPI video driver\n");
+#endif
+
ret = samsung_sysfs_init(samsung);
if (ret)
goto error_sysfs;