linux/drivers/platform/x86/samsung-laptop.c
<<
>>
Prefs
   1/*
   2 * Samsung Laptop driver
   3 *
   4 * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
   5 * Copyright (C) 2009,2011 Novell Inc.
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 */
  12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  13
  14#include <linux/kernel.h>
  15#include <linux/init.h>
  16#include <linux/module.h>
  17#include <linux/delay.h>
  18#include <linux/pci.h>
  19#include <linux/backlight.h>
  20#include <linux/leds.h>
  21#include <linux/fb.h>
  22#include <linux/dmi.h>
  23#include <linux/platform_device.h>
  24#include <linux/rfkill.h>
  25#include <linux/acpi.h>
  26#include <linux/seq_file.h>
  27#include <linux/debugfs.h>
  28#include <linux/ctype.h>
  29#include <acpi/video.h>
  30
  31/*
  32 * This driver is needed because a number of Samsung laptops do not hook
  33 * their control settings through ACPI.  So we have to poke around in the
  34 * BIOS to do things like brightness values, and "special" key controls.
  35 */
  36
  37/*
  38 * We have 0 - 8 as valid brightness levels.  The specs say that level 0 should
  39 * be reserved by the BIOS (which really doesn't make much sense), we tell
  40 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
  41 */
  42#define MAX_BRIGHT      0x07
  43
  44
  45#define SABI_IFACE_MAIN                 0x00
  46#define SABI_IFACE_SUB                  0x02
  47#define SABI_IFACE_COMPLETE             0x04
  48#define SABI_IFACE_DATA                 0x05
  49
  50#define WL_STATUS_WLAN                  0x0
  51#define WL_STATUS_BT                    0x2
  52
  53/* Structure get/set data using sabi */
  54struct sabi_data {
  55        union {
  56                struct {
  57                        u32 d0;
  58                        u32 d1;
  59                        u16 d2;
  60                        u8  d3;
  61                };
  62                u8 data[11];
  63        };
  64};
  65
  66struct sabi_header_offsets {
  67        u8 port;
  68        u8 re_mem;
  69        u8 iface_func;
  70        u8 en_mem;
  71        u8 data_offset;
  72        u8 data_segment;
  73};
  74
  75struct sabi_commands {
  76        /*
  77         * Brightness is 0 - 8, as described above.
  78         * Value 0 is for the BIOS to use
  79         */
  80        u16 get_brightness;
  81        u16 set_brightness;
  82
  83        /*
  84         * first byte:
  85         * 0x00 - wireless is off
  86         * 0x01 - wireless is on
  87         * second byte:
  88         * 0x02 - 3G is off
  89         * 0x03 - 3G is on
  90         * TODO, verify 3G is correct, that doesn't seem right...
  91         */
  92        u16 get_wireless_button;
  93        u16 set_wireless_button;
  94
  95        /* 0 is off, 1 is on */
  96        u16 get_backlight;
  97        u16 set_backlight;
  98
  99        /*
 100         * 0x80 or 0x00 - no action
 101         * 0x81 - recovery key pressed
 102         */
 103        u16 get_recovery_mode;
 104        u16 set_recovery_mode;
 105
 106        /*
 107         * on seclinux: 0 is low, 1 is high,
 108         * on swsmi: 0 is normal, 1 is silent, 2 is turbo
 109         */
 110        u16 get_performance_level;
 111        u16 set_performance_level;
 112
 113        /* 0x80 is off, 0x81 is on */
 114        u16 get_battery_life_extender;
 115        u16 set_battery_life_extender;
 116
 117        /* 0x80 is off, 0x81 is on */
 118        u16 get_usb_charge;
 119        u16 set_usb_charge;
 120
 121        /* the first byte is for bluetooth and the third one is for wlan */
 122        u16 get_wireless_status;
 123        u16 set_wireless_status;
 124
 125        /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
 126        u16 kbd_backlight;
 127
 128        /*
 129         * Tell the BIOS that Linux is running on this machine.
 130         * 81 is on, 80 is off
 131         */
 132        u16 set_linux;
 133};
 134
 135struct sabi_performance_level {
 136        const char *name;
 137        u16 value;
 138};
 139
 140struct sabi_config {
 141        int sabi_version;
 142        const char *test_string;
 143        u16 main_function;
 144        const struct sabi_header_offsets header_offsets;
 145        const struct sabi_commands commands;
 146        const struct sabi_performance_level performance_levels[4];
 147        u8 min_brightness;
 148        u8 max_brightness;
 149};
 150
 151static const struct sabi_config sabi_configs[] = {
 152        {
 153                /* I don't know if it is really 2, but it it is
 154                 * less than 3 anyway */
 155                .sabi_version = 2,
 156
 157                .test_string = "SECLINUX",
 158
 159                .main_function = 0x4c49,
 160
 161                .header_offsets = {
 162                        .port = 0x00,
 163                        .re_mem = 0x02,
 164                        .iface_func = 0x03,
 165                        .en_mem = 0x04,
 166                        .data_offset = 0x05,
 167                        .data_segment = 0x07,
 168                },
 169
 170                .commands = {
 171                        .get_brightness = 0x00,
 172                        .set_brightness = 0x01,
 173
 174                        .get_wireless_button = 0x02,
 175                        .set_wireless_button = 0x03,
 176
 177                        .get_backlight = 0x04,
 178                        .set_backlight = 0x05,
 179
 180                        .get_recovery_mode = 0x06,
 181                        .set_recovery_mode = 0x07,
 182
 183                        .get_performance_level = 0x08,
 184                        .set_performance_level = 0x09,
 185
 186                        .get_battery_life_extender = 0xFFFF,
 187                        .set_battery_life_extender = 0xFFFF,
 188
 189                        .get_usb_charge = 0xFFFF,
 190                        .set_usb_charge = 0xFFFF,
 191
 192                        .get_wireless_status = 0xFFFF,
 193                        .set_wireless_status = 0xFFFF,
 194
 195                        .kbd_backlight = 0xFFFF,
 196
 197                        .set_linux = 0x0a,
 198                },
 199
 200                .performance_levels = {
 201                        {
 202                                .name = "silent",
 203                                .value = 0,
 204                        },
 205                        {
 206                                .name = "normal",
 207                                .value = 1,
 208                        },
 209                        { },
 210                },
 211                .min_brightness = 1,
 212                .max_brightness = 8,
 213        },
 214        {
 215                .sabi_version = 3,
 216
 217                .test_string = "SwSmi@",
 218
 219                .main_function = 0x5843,
 220
 221                .header_offsets = {
 222                        .port = 0x00,
 223                        .re_mem = 0x04,
 224                        .iface_func = 0x02,
 225                        .en_mem = 0x03,
 226                        .data_offset = 0x05,
 227                        .data_segment = 0x07,
 228                },
 229
 230                .commands = {
 231                        .get_brightness = 0x10,
 232                        .set_brightness = 0x11,
 233
 234                        .get_wireless_button = 0x12,
 235                        .set_wireless_button = 0x13,
 236
 237                        .get_backlight = 0x2d,
 238                        .set_backlight = 0x2e,
 239
 240                        .get_recovery_mode = 0xff,
 241                        .set_recovery_mode = 0xff,
 242
 243                        .get_performance_level = 0x31,
 244                        .set_performance_level = 0x32,
 245
 246                        .get_battery_life_extender = 0x65,
 247                        .set_battery_life_extender = 0x66,
 248
 249                        .get_usb_charge = 0x67,
 250                        .set_usb_charge = 0x68,
 251
 252                        .get_wireless_status = 0x69,
 253                        .set_wireless_status = 0x6a,
 254
 255                        .kbd_backlight = 0x78,
 256
 257                        .set_linux = 0xff,
 258                },
 259
 260                .performance_levels = {
 261                        {
 262                                .name = "normal",
 263                                .value = 0,
 264                        },
 265                        {
 266                                .name = "silent",
 267                                .value = 1,
 268                        },
 269                        {
 270                                .name = "overclock",
 271                                .value = 2,
 272                        },
 273                        { },
 274                },
 275                .min_brightness = 0,
 276                .max_brightness = 8,
 277        },
 278        { },
 279};
 280
 281/*
 282 * samsung-laptop/    - debugfs root directory
 283 *   f0000_segment    - dump f0000 segment
 284 *   command          - current command
 285 *   data             - current data
 286 *   d0, d1, d2, d3   - data fields
 287 *   call             - call SABI using command and data
 288 *
 289 * This allow to call arbitrary sabi commands wihout
 290 * modifying the driver at all.
 291 * For example, setting the keyboard backlight brightness to 5
 292 *
 293 *  echo 0x78 > command
 294 *  echo 0x0582 > d0
 295 *  echo 0 > d1
 296 *  echo 0 > d2
 297 *  echo 0 > d3
 298 *  cat call
 299 */
 300
 301struct samsung_laptop_debug {
 302        struct dentry *root;
 303        struct sabi_data data;
 304        u16 command;
 305
 306        struct debugfs_blob_wrapper f0000_wrapper;
 307        struct debugfs_blob_wrapper data_wrapper;
 308        struct debugfs_blob_wrapper sdiag_wrapper;
 309};
 310
 311struct samsung_laptop;
 312
 313struct samsung_rfkill {
 314        struct samsung_laptop *samsung;
 315        struct rfkill *rfkill;
 316        enum rfkill_type type;
 317};
 318
 319struct samsung_laptop {
 320        const struct sabi_config *config;
 321
 322        void __iomem *sabi;
 323        void __iomem *sabi_iface;
 324        void __iomem *f0000_segment;
 325
 326        struct mutex sabi_mutex;
 327
 328        struct platform_device *platform_device;
 329        struct backlight_device *backlight_device;
 330
 331        struct samsung_rfkill wlan;
 332        struct samsung_rfkill bluetooth;
 333
 334        struct led_classdev kbd_led;
 335        int kbd_led_wk;
 336        struct workqueue_struct *led_workqueue;
 337        struct work_struct kbd_led_work;
 338
 339        struct samsung_laptop_debug debug;
 340        struct samsung_quirks *quirks;
 341
 342        bool handle_backlight;
 343        bool has_stepping_quirk;
 344
 345        char sdiag[64];
 346};
 347
 348struct samsung_quirks {
 349        bool broken_acpi_video;
 350};
 351
 352static struct samsung_quirks samsung_unknown = {};
 353
 354static struct samsung_quirks samsung_broken_acpi_video = {
 355        .broken_acpi_video = true,
 356};
 357
 358static bool force;
 359module_param(force, bool, 0);
 360MODULE_PARM_DESC(force,
 361                "Disable the DMI check and forces the driver to be loaded");
 362
 363static bool debug;
 364module_param(debug, bool, S_IRUGO | S_IWUSR);
 365MODULE_PARM_DESC(debug, "Debug enabled or not");
 366
 367static int sabi_command(struct samsung_laptop *samsung, u16 command,
 368                        struct sabi_data *in,
 369                        struct sabi_data *out)
 370{
 371        const struct sabi_config *config = samsung->config;
 372        int ret = 0;
 373        u16 port = readw(samsung->sabi + config->header_offsets.port);
 374        u8 complete, iface_data;
 375
 376        mutex_lock(&samsung->sabi_mutex);
 377
 378        if (debug) {
 379                if (in)
 380                        pr_info("SABI command:0x%04x "
 381                                "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
 382                                command, in->d0, in->d1, in->d2, in->d3);
 383                else
 384                        pr_info("SABI command:0x%04x", command);
 385        }
 386
 387        /* enable memory to be able to write to it */
 388        outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
 389
 390        /* write out the command */
 391        writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
 392        writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
 393        writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
 394        if (in) {
 395                writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
 396                writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
 397                writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
 398                writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
 399        }
 400        outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
 401
 402        /* write protect memory to make it safe */
 403        outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
 404
 405        /* see if the command actually succeeded */
 406        complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
 407        iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
 408
 409        /* iface_data = 0xFF happens when a command is not known
 410         * so we only add a warning in debug mode since we will
 411         * probably issue some unknown command at startup to find
 412         * out which features are supported */
 413        if (complete != 0xaa || (iface_data == 0xff && debug))
 414                pr_warn("SABI command 0x%04x failed with"
 415                        " completion flag 0x%02x and interface data 0x%02x",
 416                        command, complete, iface_data);
 417
 418        if (complete != 0xaa || iface_data == 0xff) {
 419                ret = -EINVAL;
 420                goto exit;
 421        }
 422
 423        if (out) {
 424                out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
 425                out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
 426                out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
 427                out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
 428        }
 429
 430        if (debug && out) {
 431                pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
 432                        out->d0, out->d1, out->d2, out->d3);
 433        }
 434
 435exit:
 436        mutex_unlock(&samsung->sabi_mutex);
 437        return ret;
 438}
 439
 440/* simple wrappers usable with most commands */
 441static int sabi_set_commandb(struct samsung_laptop *samsung,
 442                             u16 command, u8 data)
 443{
 444        struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
 445
 446        in.data[0] = data;
 447        return sabi_command(samsung, command, &in, NULL);
 448}
 449
 450static int read_brightness(struct samsung_laptop *samsung)
 451{
 452        const struct sabi_config *config = samsung->config;
 453        const struct sabi_commands *commands = &samsung->config->commands;
 454        struct sabi_data sretval;
 455        int user_brightness = 0;
 456        int retval;
 457
 458        retval = sabi_command(samsung, commands->get_brightness,
 459                              NULL, &sretval);
 460        if (retval)
 461                return retval;
 462
 463        user_brightness = sretval.data[0];
 464        if (user_brightness > config->min_brightness)
 465                user_brightness -= config->min_brightness;
 466        else
 467                user_brightness = 0;
 468
 469        return user_brightness;
 470}
 471
 472static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
 473{
 474        const struct sabi_config *config = samsung->config;
 475        const struct sabi_commands *commands = &samsung->config->commands;
 476        u8 user_level = user_brightness + config->min_brightness;
 477
 478        if (samsung->has_stepping_quirk && user_level != 0) {
 479                /*
 480                 * short circuit if the specified level is what's already set
 481                 * to prevent the screen from flickering needlessly
 482                 */
 483                if (user_brightness == read_brightness(samsung))
 484                        return;
 485
 486                sabi_set_commandb(samsung, commands->set_brightness, 0);
 487        }
 488
 489        sabi_set_commandb(samsung, commands->set_brightness, user_level);
 490}
 491
 492static int get_brightness(struct backlight_device *bd)
 493{
 494        struct samsung_laptop *samsung = bl_get_data(bd);
 495
 496        return read_brightness(samsung);
 497}
 498
 499static void check_for_stepping_quirk(struct samsung_laptop *samsung)
 500{
 501        int initial_level;
 502        int check_level;
 503        int orig_level = read_brightness(samsung);
 504
 505        /*
 506         * Some laptops exhibit the strange behaviour of stepping toward
 507         * (rather than setting) the brightness except when changing to/from
 508         * brightness level 0. This behaviour is checked for here and worked
 509         * around in set_brightness.
 510         */
 511
 512        if (orig_level == 0)
 513                set_brightness(samsung, 1);
 514
 515        initial_level = read_brightness(samsung);
 516
 517        if (initial_level <= 2)
 518                check_level = initial_level + 2;
 519        else
 520                check_level = initial_level - 2;
 521
 522        samsung->has_stepping_quirk = false;
 523        set_brightness(samsung, check_level);
 524
 525        if (read_brightness(samsung) != check_level) {
 526                samsung->has_stepping_quirk = true;
 527                pr_info("enabled workaround for brightness stepping quirk\n");
 528        }
 529
 530        set_brightness(samsung, orig_level);
 531}
 532
 533static int update_status(struct backlight_device *bd)
 534{
 535        struct samsung_laptop *samsung = bl_get_data(bd);
 536        const struct sabi_commands *commands = &samsung->config->commands;
 537
 538        set_brightness(samsung, bd->props.brightness);
 539
 540        if (bd->props.power == FB_BLANK_UNBLANK)
 541                sabi_set_commandb(samsung, commands->set_backlight, 1);
 542        else
 543                sabi_set_commandb(samsung, commands->set_backlight, 0);
 544
 545        return 0;
 546}
 547
 548static const struct backlight_ops backlight_ops = {
 549        .get_brightness = get_brightness,
 550        .update_status  = update_status,
 551};
 552
 553static int seclinux_rfkill_set(void *data, bool blocked)
 554{
 555        struct samsung_rfkill *srfkill = data;
 556        struct samsung_laptop *samsung = srfkill->samsung;
 557        const struct sabi_commands *commands = &samsung->config->commands;
 558
 559        return sabi_set_commandb(samsung, commands->set_wireless_button,
 560                                 !blocked);
 561}
 562
 563static struct rfkill_ops seclinux_rfkill_ops = {
 564        .set_block = seclinux_rfkill_set,
 565};
 566
 567static int swsmi_wireless_status(struct samsung_laptop *samsung,
 568                                 struct sabi_data *data)
 569{
 570        const struct sabi_commands *commands = &samsung->config->commands;
 571
 572        return sabi_command(samsung, commands->get_wireless_status,
 573                            NULL, data);
 574}
 575
 576static int swsmi_rfkill_set(void *priv, bool blocked)
 577{
 578        struct samsung_rfkill *srfkill = priv;
 579        struct samsung_laptop *samsung = srfkill->samsung;
 580        const struct sabi_commands *commands = &samsung->config->commands;
 581        struct sabi_data data;
 582        int ret, i;
 583
 584        ret = swsmi_wireless_status(samsung, &data);
 585        if (ret)
 586                return ret;
 587
 588        /* Don't set the state for non-present devices */
 589        for (i = 0; i < 4; i++)
 590                if (data.data[i] == 0x02)
 591                        data.data[1] = 0;
 592
 593        if (srfkill->type == RFKILL_TYPE_WLAN)
 594                data.data[WL_STATUS_WLAN] = !blocked;
 595        else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
 596                data.data[WL_STATUS_BT] = !blocked;
 597
 598        return sabi_command(samsung, commands->set_wireless_status,
 599                            &data, &data);
 600}
 601
 602static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
 603{
 604        struct samsung_rfkill *srfkill = priv;
 605        struct samsung_laptop *samsung = srfkill->samsung;
 606        struct sabi_data data;
 607        int ret;
 608
 609        ret = swsmi_wireless_status(samsung, &data);
 610        if (ret)
 611                return ;
 612
 613        if (srfkill->type == RFKILL_TYPE_WLAN)
 614                ret = data.data[WL_STATUS_WLAN];
 615        else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
 616                ret = data.data[WL_STATUS_BT];
 617        else
 618                return ;
 619
 620        rfkill_set_sw_state(rfkill, !ret);
 621}
 622
 623static struct rfkill_ops swsmi_rfkill_ops = {
 624        .set_block = swsmi_rfkill_set,
 625        .query = swsmi_rfkill_query,
 626};
 627
 628static ssize_t get_performance_level(struct device *dev,
 629                                     struct device_attribute *attr, char *buf)
 630{
 631        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 632        const struct sabi_config *config = samsung->config;
 633        const struct sabi_commands *commands = &config->commands;
 634        struct sabi_data sretval;
 635        int retval;
 636        int i;
 637
 638        /* Read the state */
 639        retval = sabi_command(samsung, commands->get_performance_level,
 640                              NULL, &sretval);
 641        if (retval)
 642                return retval;
 643
 644        /* The logic is backwards, yeah, lots of fun... */
 645        for (i = 0; config->performance_levels[i].name; ++i) {
 646                if (sretval.data[0] == config->performance_levels[i].value)
 647                        return sprintf(buf, "%s\n", config->performance_levels[i].name);
 648        }
 649        return sprintf(buf, "%s\n", "unknown");
 650}
 651
 652static ssize_t set_performance_level(struct device *dev,
 653                                struct device_attribute *attr, const char *buf,
 654                                size_t count)
 655{
 656        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 657        const struct sabi_config *config = samsung->config;
 658        const struct sabi_commands *commands = &config->commands;
 659        int i;
 660
 661        if (count < 1)
 662                return count;
 663
 664        for (i = 0; config->performance_levels[i].name; ++i) {
 665                const struct sabi_performance_level *level =
 666                        &config->performance_levels[i];
 667                if (!strncasecmp(level->name, buf, strlen(level->name))) {
 668                        sabi_set_commandb(samsung,
 669                                          commands->set_performance_level,
 670                                          level->value);
 671                        break;
 672                }
 673        }
 674
 675        if (!config->performance_levels[i].name)
 676                return -EINVAL;
 677
 678        return count;
 679}
 680
 681static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
 682                   get_performance_level, set_performance_level);
 683
 684static int read_battery_life_extender(struct samsung_laptop *samsung)
 685{
 686        const struct sabi_commands *commands = &samsung->config->commands;
 687        struct sabi_data data;
 688        int retval;
 689
 690        if (commands->get_battery_life_extender == 0xFFFF)
 691                return -ENODEV;
 692
 693        memset(&data, 0, sizeof(data));
 694        data.data[0] = 0x80;
 695        retval = sabi_command(samsung, commands->get_battery_life_extender,
 696                              &data, &data);
 697
 698        if (retval)
 699                return retval;
 700
 701        if (data.data[0] != 0 && data.data[0] != 1)
 702                return -ENODEV;
 703
 704        return data.data[0];
 705}
 706
 707static int write_battery_life_extender(struct samsung_laptop *samsung,
 708                                       int enabled)
 709{
 710        const struct sabi_commands *commands = &samsung->config->commands;
 711        struct sabi_data data;
 712
 713        memset(&data, 0, sizeof(data));
 714        data.data[0] = 0x80 | enabled;
 715        return sabi_command(samsung, commands->set_battery_life_extender,
 716                            &data, NULL);
 717}
 718
 719static ssize_t get_battery_life_extender(struct device *dev,
 720                                         struct device_attribute *attr,
 721                                         char *buf)
 722{
 723        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 724        int ret;
 725
 726        ret = read_battery_life_extender(samsung);
 727        if (ret < 0)
 728                return ret;
 729
 730        return sprintf(buf, "%d\n", ret);
 731}
 732
 733static ssize_t set_battery_life_extender(struct device *dev,
 734                                        struct device_attribute *attr,
 735                                        const char *buf, size_t count)
 736{
 737        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 738        int ret, value;
 739
 740        if (!count || sscanf(buf, "%i", &value) != 1)
 741                return -EINVAL;
 742
 743        ret = write_battery_life_extender(samsung, !!value);
 744        if (ret < 0)
 745                return ret;
 746
 747        return count;
 748}
 749
 750static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
 751                   get_battery_life_extender, set_battery_life_extender);
 752
 753static int read_usb_charge(struct samsung_laptop *samsung)
 754{
 755        const struct sabi_commands *commands = &samsung->config->commands;
 756        struct sabi_data data;
 757        int retval;
 758
 759        if (commands->get_usb_charge == 0xFFFF)
 760                return -ENODEV;
 761
 762        memset(&data, 0, sizeof(data));
 763        data.data[0] = 0x80;
 764        retval = sabi_command(samsung, commands->get_usb_charge,
 765                              &data, &data);
 766
 767        if (retval)
 768                return retval;
 769
 770        if (data.data[0] != 0 && data.data[0] != 1)
 771                return -ENODEV;
 772
 773        return data.data[0];
 774}
 775
 776static int write_usb_charge(struct samsung_laptop *samsung,
 777                            int enabled)
 778{
 779        const struct sabi_commands *commands = &samsung->config->commands;
 780        struct sabi_data data;
 781
 782        memset(&data, 0, sizeof(data));
 783        data.data[0] = 0x80 | enabled;
 784        return sabi_command(samsung, commands->set_usb_charge,
 785                            &data, NULL);
 786}
 787
 788static ssize_t get_usb_charge(struct device *dev,
 789                              struct device_attribute *attr,
 790                              char *buf)
 791{
 792        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 793        int ret;
 794
 795        ret = read_usb_charge(samsung);
 796        if (ret < 0)
 797                return ret;
 798
 799        return sprintf(buf, "%d\n", ret);
 800}
 801
 802static ssize_t set_usb_charge(struct device *dev,
 803                              struct device_attribute *attr,
 804                              const char *buf, size_t count)
 805{
 806        struct samsung_laptop *samsung = dev_get_drvdata(dev);
 807        int ret, value;
 808
 809        if (!count || sscanf(buf, "%i", &value) != 1)
 810                return -EINVAL;
 811
 812        ret = write_usb_charge(samsung, !!value);
 813        if (ret < 0)
 814                return ret;
 815
 816        return count;
 817}
 818
 819static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
 820                   get_usb_charge, set_usb_charge);
 821
 822static struct attribute *platform_attributes[] = {
 823        &dev_attr_performance_level.attr,
 824        &dev_attr_battery_life_extender.attr,
 825        &dev_attr_usb_charge.attr,
 826        NULL
 827};
 828
 829static int find_signature(void __iomem *memcheck, const char *testStr)
 830{
 831        int i = 0;
 832        int loca;
 833
 834        for (loca = 0; loca < 0xffff; loca++) {
 835                char temp = readb(memcheck + loca);
 836
 837                if (temp == testStr[i]) {
 838                        if (i == strlen(testStr)-1)
 839                                break;
 840                        ++i;
 841                } else {
 842                        i = 0;
 843                }
 844        }
 845        return loca;
 846}
 847
 848static void samsung_rfkill_exit(struct samsung_laptop *samsung)
 849{
 850        if (samsung->wlan.rfkill) {
 851                rfkill_unregister(samsung->wlan.rfkill);
 852                rfkill_destroy(samsung->wlan.rfkill);
 853                samsung->wlan.rfkill = NULL;
 854        }
 855        if (samsung->bluetooth.rfkill) {
 856                rfkill_unregister(samsung->bluetooth.rfkill);
 857                rfkill_destroy(samsung->bluetooth.rfkill);
 858                samsung->bluetooth.rfkill = NULL;
 859        }
 860}
 861
 862static int samsung_new_rfkill(struct samsung_laptop *samsung,
 863                              struct samsung_rfkill *arfkill,
 864                              const char *name, enum rfkill_type type,
 865                              const struct rfkill_ops *ops,
 866                              int blocked)
 867{
 868        struct rfkill **rfkill = &arfkill->rfkill;
 869        int ret;
 870
 871        arfkill->type = type;
 872        arfkill->samsung = samsung;
 873
 874        *rfkill = rfkill_alloc(name, &samsung->platform_device->dev,
 875                               type, ops, arfkill);
 876
 877        if (!*rfkill)
 878                return -EINVAL;
 879
 880        if (blocked != -1)
 881                rfkill_init_sw_state(*rfkill, blocked);
 882
 883        ret = rfkill_register(*rfkill);
 884        if (ret) {
 885                rfkill_destroy(*rfkill);
 886                *rfkill = NULL;
 887                return ret;
 888        }
 889        return 0;
 890}
 891
 892static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung)
 893{
 894        return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan",
 895                                  RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1);
 896}
 897
 898static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung)
 899{
 900        struct sabi_data data;
 901        int ret;
 902
 903        ret = swsmi_wireless_status(samsung, &data);
 904        if (ret) {
 905                /* Some swsmi laptops use the old seclinux way to control
 906                 * wireless devices */
 907                if (ret == -EINVAL)
 908                        ret = samsung_rfkill_init_seclinux(samsung);
 909                return ret;
 910        }
 911
 912        /* 0x02 seems to mean that the device is no present/available */
 913
 914        if (data.data[WL_STATUS_WLAN] != 0x02)
 915                ret = samsung_new_rfkill(samsung, &samsung->wlan,
 916                                         "samsung-wlan",
 917                                         RFKILL_TYPE_WLAN,
 918                                         &swsmi_rfkill_ops,
 919                                         !data.data[WL_STATUS_WLAN]);
 920        if (ret)
 921                goto exit;
 922
 923        if (data.data[WL_STATUS_BT] != 0x02)
 924                ret = samsung_new_rfkill(samsung, &samsung->bluetooth,
 925                                         "samsung-bluetooth",
 926                                         RFKILL_TYPE_BLUETOOTH,
 927                                         &swsmi_rfkill_ops,
 928                                         !data.data[WL_STATUS_BT]);
 929        if (ret)
 930                goto exit;
 931
 932exit:
 933        if (ret)
 934                samsung_rfkill_exit(samsung);
 935
 936        return ret;
 937}
 938
 939static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
 940{
 941        if (samsung->config->sabi_version == 2)
 942                return samsung_rfkill_init_seclinux(samsung);
 943        if (samsung->config->sabi_version == 3)
 944                return samsung_rfkill_init_swsmi(samsung);
 945        return 0;
 946}
 947
 948static int kbd_backlight_enable(struct samsung_laptop *samsung)
 949{
 950        const struct sabi_commands *commands = &samsung->config->commands;
 951        struct sabi_data data;
 952        int retval;
 953
 954        if (commands->kbd_backlight == 0xFFFF)
 955                return -ENODEV;
 956
 957        memset(&data, 0, sizeof(data));
 958        data.d0 = 0xaabb;
 959        retval = sabi_command(samsung, commands->kbd_backlight,
 960                              &data, &data);
 961
 962        if (retval)
 963                return retval;
 964
 965        if (data.d0 != 0xccdd)
 966                return -ENODEV;
 967        return 0;
 968}
 969
 970static int kbd_backlight_read(struct samsung_laptop *samsung)
 971{
 972        const struct sabi_commands *commands = &samsung->config->commands;
 973        struct sabi_data data;
 974        int retval;
 975
 976        memset(&data, 0, sizeof(data));
 977        data.data[0] = 0x81;
 978        retval = sabi_command(samsung, commands->kbd_backlight,
 979                              &data, &data);
 980
 981        if (retval)
 982                return retval;
 983
 984        return data.data[0];
 985}
 986
 987static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
 988{
 989        const struct sabi_commands *commands = &samsung->config->commands;
 990        struct sabi_data data;
 991
 992        memset(&data, 0, sizeof(data));
 993        data.d0 = 0x82 | ((brightness & 0xFF) << 8);
 994        return sabi_command(samsung, commands->kbd_backlight,
 995                            &data, NULL);
 996}
 997
 998static void kbd_led_update(struct work_struct *work)
 999{
1000        struct samsung_laptop *samsung;
1001
1002        samsung = container_of(work, struct samsung_laptop, kbd_led_work);
1003        kbd_backlight_write(samsung, samsung->kbd_led_wk);
1004}
1005
1006static void kbd_led_set(struct led_classdev *led_cdev,
1007                        enum led_brightness value)
1008{
1009        struct samsung_laptop *samsung;
1010
1011        samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
1012
1013        if (value > samsung->kbd_led.max_brightness)
1014                value = samsung->kbd_led.max_brightness;
1015        else if (value < 0)
1016                value = 0;
1017
1018        samsung->kbd_led_wk = value;
1019        queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
1020}
1021
1022static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
1023{
1024        struct samsung_laptop *samsung;
1025
1026        samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
1027        return kbd_backlight_read(samsung);
1028}
1029
1030static void samsung_leds_exit(struct samsung_laptop *samsung)
1031{
1032        if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
1033                led_classdev_unregister(&samsung->kbd_led);
1034        if (samsung->led_workqueue)
1035                destroy_workqueue(samsung->led_workqueue);
1036}
1037
1038static int __init samsung_leds_init(struct samsung_laptop *samsung)
1039{
1040        int ret = 0;
1041
1042        samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
1043        if (!samsung->led_workqueue)
1044                return -ENOMEM;
1045
1046        if (kbd_backlight_enable(samsung) >= 0) {
1047                INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
1048
1049                samsung->kbd_led.name = "samsung::kbd_backlight";
1050                samsung->kbd_led.brightness_set = kbd_led_set;
1051                samsung->kbd_led.brightness_get = kbd_led_get;
1052                samsung->kbd_led.max_brightness = 8;
1053
1054                ret = led_classdev_register(&samsung->platform_device->dev,
1055                                           &samsung->kbd_led);
1056        }
1057
1058        if (ret)
1059                samsung_leds_exit(samsung);
1060
1061        return ret;
1062}
1063
1064static void samsung_backlight_exit(struct samsung_laptop *samsung)
1065{
1066        if (samsung->backlight_device) {
1067                backlight_device_unregister(samsung->backlight_device);
1068                samsung->backlight_device = NULL;
1069        }
1070}
1071
1072static int __init samsung_backlight_init(struct samsung_laptop *samsung)
1073{
1074        struct backlight_device *bd;
1075        struct backlight_properties props;
1076
1077        if (!samsung->handle_backlight)
1078                return 0;
1079
1080        memset(&props, 0, sizeof(struct backlight_properties));
1081        props.type = BACKLIGHT_PLATFORM;
1082        props.max_brightness = samsung->config->max_brightness -
1083                samsung->config->min_brightness;
1084
1085        bd = backlight_device_register("samsung",
1086                                       &samsung->platform_device->dev,
1087                                       samsung, &backlight_ops,
1088                                       &props);
1089        if (IS_ERR(bd))
1090                return PTR_ERR(bd);
1091
1092        samsung->backlight_device = bd;
1093        samsung->backlight_device->props.brightness = read_brightness(samsung);
1094        samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
1095        backlight_update_status(samsung->backlight_device);
1096
1097        return 0;
1098}
1099
1100static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
1101                                       struct attribute *attr, int idx)
1102{
1103        struct device *dev = container_of(kobj, struct device, kobj);
1104        struct platform_device *pdev = to_platform_device(dev);
1105        struct samsung_laptop *samsung = platform_get_drvdata(pdev);
1106        bool ok = true;
1107
1108        if (attr == &dev_attr_performance_level.attr)
1109                ok = !!samsung->config->performance_levels[0].name;
1110        if (attr == &dev_attr_battery_life_extender.attr)
1111                ok = !!(read_battery_life_extender(samsung) >= 0);
1112        if (attr == &dev_attr_usb_charge.attr)
1113                ok = !!(read_usb_charge(samsung) >= 0);
1114
1115        return ok ? attr->mode : 0;
1116}
1117
1118static struct attribute_group platform_attribute_group = {
1119        .is_visible = samsung_sysfs_is_visible,
1120        .attrs = platform_attributes
1121};
1122
1123static void samsung_sysfs_exit(struct samsung_laptop *samsung)
1124{
1125        struct platform_device *device = samsung->platform_device;
1126
1127        sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
1128}
1129
1130static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
1131{
1132        struct platform_device *device = samsung->platform_device;
1133
1134        return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
1135
1136}
1137
1138static int show_call(struct seq_file *m, void *data)
1139{
1140        struct samsung_laptop *samsung = m->private;
1141        struct sabi_data *sdata = &samsung->debug.data;
1142        int ret;
1143
1144        seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1145                   samsung->debug.command,
1146                   sdata->d0, sdata->d1, sdata->d2, sdata->d3);
1147
1148        ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
1149
1150        if (ret) {
1151                seq_printf(m, "SABI command 0x%04x failed\n",
1152                           samsung->debug.command);
1153                return ret;
1154        }
1155
1156        seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
1157                   sdata->d0, sdata->d1, sdata->d2, sdata->d3);
1158        return 0;
1159}
1160
1161static int samsung_debugfs_open(struct inode *inode, struct file *file)
1162{
1163        return single_open(file, show_call, inode->i_private);
1164}
1165
1166static const struct file_operations samsung_laptop_call_io_ops = {
1167        .owner = THIS_MODULE,
1168        .open = samsung_debugfs_open,
1169        .read = seq_read,
1170        .llseek = seq_lseek,
1171        .release = single_release,
1172};
1173
1174static void samsung_debugfs_exit(struct samsung_laptop *samsung)
1175{
1176        debugfs_remove_recursive(samsung->debug.root);
1177}
1178
1179static int samsung_debugfs_init(struct samsung_laptop *samsung)
1180{
1181        struct dentry *dent;
1182
1183        samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
1184        if (!samsung->debug.root) {
1185                pr_err("failed to create debugfs directory");
1186                goto error_debugfs;
1187        }
1188
1189        samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
1190        samsung->debug.f0000_wrapper.size = 0xffff;
1191
1192        samsung->debug.data_wrapper.data = &samsung->debug.data;
1193        samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
1194
1195        samsung->debug.sdiag_wrapper.data = samsung->sdiag;
1196        samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
1197
1198        dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
1199                                  samsung->debug.root, &samsung->debug.command);
1200        if (!dent)
1201                goto error_debugfs;
1202
1203        dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
1204                                  &samsung->debug.data.d0);
1205        if (!dent)
1206                goto error_debugfs;
1207
1208        dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
1209                                  &samsung->debug.data.d1);
1210        if (!dent)
1211                goto error_debugfs;
1212
1213        dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
1214                                  &samsung->debug.data.d2);
1215        if (!dent)
1216                goto error_debugfs;
1217
1218        dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
1219                                 &samsung->debug.data.d3);
1220        if (!dent)
1221                goto error_debugfs;
1222
1223        dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
1224                                   samsung->debug.root,
1225                                   &samsung->debug.data_wrapper);
1226        if (!dent)
1227                goto error_debugfs;
1228
1229        dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
1230                                   samsung->debug.root,
1231                                   &samsung->debug.f0000_wrapper);
1232        if (!dent)
1233                goto error_debugfs;
1234
1235        dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
1236                                   samsung->debug.root, samsung,
1237                                   &samsung_laptop_call_io_ops);
1238        if (!dent)
1239                goto error_debugfs;
1240
1241        dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
1242                                   samsung->debug.root,
1243                                   &samsung->debug.sdiag_wrapper);
1244        if (!dent)
1245                goto error_debugfs;
1246
1247        return 0;
1248
1249error_debugfs:
1250        samsung_debugfs_exit(samsung);
1251        return -ENOMEM;
1252}
1253
1254static void samsung_sabi_exit(struct samsung_laptop *samsung)
1255{
1256        const struct sabi_config *config = samsung->config;
1257
1258        /* Turn off "Linux" mode in the BIOS */
1259        if (config && config->commands.set_linux != 0xff)
1260                sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
1261
1262        if (samsung->sabi_iface) {
1263                iounmap(samsung->sabi_iface);
1264                samsung->sabi_iface = NULL;
1265        }
1266        if (samsung->f0000_segment) {
1267                iounmap(samsung->f0000_segment);
1268                samsung->f0000_segment = NULL;
1269        }
1270
1271        samsung->config = NULL;
1272}
1273
1274static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
1275                                      unsigned int ifaceP)
1276{
1277        const struct sabi_config *config = samsung->config;
1278
1279        printk(KERN_DEBUG "This computer supports SABI==%x\n",
1280               loca + 0xf0000 - 6);
1281
1282        printk(KERN_DEBUG "SABI header:\n");
1283        printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
1284               readw(samsung->sabi + config->header_offsets.port));
1285        printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
1286               readb(samsung->sabi + config->header_offsets.iface_func));
1287        printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
1288               readb(samsung->sabi + config->header_offsets.en_mem));
1289        printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
1290               readb(samsung->sabi + config->header_offsets.re_mem));
1291        printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
1292               readw(samsung->sabi + config->header_offsets.data_offset));
1293        printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
1294               readw(samsung->sabi + config->header_offsets.data_segment));
1295
1296        printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
1297}
1298
1299static void __init samsung_sabi_diag(struct samsung_laptop *samsung)
1300{
1301        int loca = find_signature(samsung->f0000_segment, "SDiaG@");
1302        int i;
1303
1304        if (loca == 0xffff)
1305                return ;
1306
1307        /* Example:
1308         * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A
1309         *
1310         * Product name: 90X3A
1311         * BIOS Version: 07HL
1312         */
1313        loca += 1;
1314        for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) {
1315                char temp = readb(samsung->f0000_segment + loca);
1316
1317                if (isalnum(temp) || temp == '/' || temp == '-')
1318                        samsung->sdiag[i++] = temp;
1319                else
1320                        break ;
1321        }
1322
1323        if (debug && samsung->sdiag[0])
1324                pr_info("sdiag: %s", samsung->sdiag);
1325}
1326
1327static int __init samsung_sabi_init(struct samsung_laptop *samsung)
1328{
1329        const struct sabi_config *config = NULL;
1330        const struct sabi_commands *commands;
1331        unsigned int ifaceP;
1332        int ret = 0;
1333        int i;
1334        int loca;
1335
1336        samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
1337        if (!samsung->f0000_segment) {
1338                if (debug || force)
1339                        pr_err("Can't map the segment at 0xf0000\n");
1340                ret = -EINVAL;
1341                goto exit;
1342        }
1343
1344        samsung_sabi_diag(samsung);
1345
1346        /* Try to find one of the signatures in memory to find the header */
1347        for (i = 0; sabi_configs[i].test_string != 0; ++i) {
1348                samsung->config = &sabi_configs[i];
1349                loca = find_signature(samsung->f0000_segment,
1350                                      samsung->config->test_string);
1351                if (loca != 0xffff)
1352                        break;
1353        }
1354
1355        if (loca == 0xffff) {
1356                if (debug || force)
1357                        pr_err("This computer does not support SABI\n");
1358                ret = -ENODEV;
1359                goto exit;
1360        }
1361
1362        config = samsung->config;
1363        commands = &config->commands;
1364
1365        /* point to the SMI port Number */
1366        loca += 1;
1367        samsung->sabi = (samsung->f0000_segment + loca);
1368
1369        /* Get a pointer to the SABI Interface */
1370        ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
1371        ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
1372
1373        if (debug)
1374                samsung_sabi_infos(samsung, loca, ifaceP);
1375
1376        samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
1377        if (!samsung->sabi_iface) {
1378                pr_err("Can't remap %x\n", ifaceP);
1379                ret = -EINVAL;
1380                goto exit;
1381        }
1382
1383        /* Turn on "Linux" mode in the BIOS */
1384        if (commands->set_linux != 0xff) {
1385                int retval = sabi_set_commandb(samsung,
1386                                               commands->set_linux, 0x81);
1387                if (retval) {
1388                        pr_warn("Linux mode was not set!\n");
1389                        ret = -ENODEV;
1390                        goto exit;
1391                }
1392        }
1393
1394        /* Check for stepping quirk */
1395        if (samsung->handle_backlight)
1396                check_for_stepping_quirk(samsung);
1397
1398        pr_info("detected SABI interface: %s\n",
1399                samsung->config->test_string);
1400
1401exit:
1402        if (ret)
1403                samsung_sabi_exit(samsung);
1404
1405        return ret;
1406}
1407
1408static void samsung_platform_exit(struct samsung_laptop *samsung)
1409{
1410        if (samsung->platform_device) {
1411                platform_device_unregister(samsung->platform_device);
1412                samsung->platform_device = NULL;
1413        }
1414}
1415
1416static int __init samsung_platform_init(struct samsung_laptop *samsung)
1417{
1418        struct platform_device *pdev;
1419
1420        pdev = platform_device_register_simple("samsung", -1, NULL, 0);
1421        if (IS_ERR(pdev))
1422                return PTR_ERR(pdev);
1423
1424        samsung->platform_device = pdev;
1425        platform_set_drvdata(samsung->platform_device, samsung);
1426        return 0;
1427}
1428
1429static struct samsung_quirks *quirks;
1430
1431static int __init samsung_dmi_matched(const struct dmi_system_id *d)
1432{
1433        quirks = d->driver_data;
1434        return 0;
1435}
1436
1437static struct dmi_system_id __initdata samsung_dmi_table[] = {
1438        {
1439                .matches = {
1440                        DMI_MATCH(DMI_SYS_VENDOR,
1441                                        "SAMSUNG ELECTRONICS CO., LTD."),
1442                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
1443                },
1444        },
1445        {
1446                .matches = {
1447                        DMI_MATCH(DMI_SYS_VENDOR,
1448                                        "SAMSUNG ELECTRONICS CO., LTD."),
1449                        DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
1450                },
1451        },
1452        {
1453                .matches = {
1454                        DMI_MATCH(DMI_SYS_VENDOR,
1455                                        "SAMSUNG ELECTRONICS CO., LTD."),
1456                        DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
1457                },
1458        },
1459        {
1460                .matches = {
1461                        DMI_MATCH(DMI_SYS_VENDOR,
1462                                        "SAMSUNG ELECTRONICS CO., LTD."),
1463                        DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
1464                },
1465        },
1466        /* DMI ids for laptops with bad Chassis Type */
1467        {
1468          .ident = "R40/R41",
1469          .matches = {
1470                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1471                DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"),
1472                DMI_MATCH(DMI_BOARD_NAME, "R40/R41"),
1473                },
1474        },
1475        /* Specific DMI ids for laptop with quirks */
1476        {
1477         .callback = samsung_dmi_matched,
1478         .ident = "N150P",
1479         .matches = {
1480                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1481                DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
1482                DMI_MATCH(DMI_BOARD_NAME, "N150P"),
1483                },
1484         .driver_data = &samsung_broken_acpi_video,
1485        },
1486        {
1487         .callback = samsung_dmi_matched,
1488         .ident = "N145P/N250P/N260P",
1489         .matches = {
1490                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1491                DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
1492                DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
1493                },
1494         .driver_data = &samsung_broken_acpi_video,
1495        },
1496        {
1497         .callback = samsung_dmi_matched,
1498         .ident = "N150/N210/N220",
1499         .matches = {
1500                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1501                DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
1502                DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
1503                },
1504         .driver_data = &samsung_broken_acpi_video,
1505        },
1506        {
1507         .callback = samsung_dmi_matched,
1508         .ident = "NF110/NF210/NF310",
1509         .matches = {
1510                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1511                DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
1512                DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
1513                },
1514         .driver_data = &samsung_broken_acpi_video,
1515        },
1516        {
1517         .callback = samsung_dmi_matched,
1518         .ident = "X360",
1519         .matches = {
1520                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1521                DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
1522                DMI_MATCH(DMI_BOARD_NAME, "X360"),
1523                },
1524         .driver_data = &samsung_broken_acpi_video,
1525        },
1526        {
1527         .callback = samsung_dmi_matched,
1528         .ident = "N250P",
1529         .matches = {
1530                DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
1531                DMI_MATCH(DMI_PRODUCT_NAME, "N250P"),
1532                DMI_MATCH(DMI_BOARD_NAME, "N250P"),
1533                },
1534         .driver_data = &samsung_broken_acpi_video,
1535        },
1536        { },
1537};
1538MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
1539
1540static struct platform_device *samsung_platform_device;
1541
1542static int __init samsung_init(void)
1543{
1544        struct samsung_laptop *samsung;
1545        int ret;
1546
1547        quirks = &samsung_unknown;
1548        if (!force && !dmi_check_system(samsung_dmi_table))
1549                return -ENODEV;
1550
1551        samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
1552        if (!samsung)
1553                return -ENOMEM;
1554
1555        mutex_init(&samsung->sabi_mutex);
1556        samsung->handle_backlight = true;
1557        samsung->quirks = quirks;
1558
1559
1560#ifdef CONFIG_ACPI
1561        if (samsung->quirks->broken_acpi_video)
1562                acpi_video_dmi_promote_vendor();
1563
1564        /* Don't handle backlight here if the acpi video already handle it */
1565        if (acpi_video_backlight_support()) {
1566                samsung->handle_backlight = false;
1567        } else if (samsung->quirks->broken_acpi_video) {
1568                pr_info("Disabling ACPI video driver\n");
1569                acpi_video_unregister();
1570        }
1571#endif
1572
1573        ret = samsung_platform_init(samsung);
1574        if (ret)
1575                goto error_platform;
1576
1577        ret = samsung_sabi_init(samsung);
1578        if (ret)
1579                goto error_sabi;
1580
1581#ifdef CONFIG_ACPI
1582        /* Only log that if we are really on a sabi platform */
1583        if (acpi_video_backlight_support())
1584                pr_info("Backlight controlled by ACPI video driver\n");
1585#endif
1586
1587        ret = samsung_sysfs_init(samsung);
1588        if (ret)
1589                goto error_sysfs;
1590
1591        ret = samsung_backlight_init(samsung);
1592        if (ret)
1593                goto error_backlight;
1594
1595        ret = samsung_rfkill_init(samsung);
1596        if (ret)
1597                goto error_rfkill;
1598
1599        ret = samsung_leds_init(samsung);
1600        if (ret)
1601                goto error_leds;
1602
1603        ret = samsung_debugfs_init(samsung);
1604        if (ret)
1605                goto error_debugfs;
1606
1607        samsung_platform_device = samsung->platform_device;
1608        return ret;
1609
1610error_debugfs:
1611        samsung_leds_exit(samsung);
1612error_leds:
1613        samsung_rfkill_exit(samsung);
1614error_rfkill:
1615        samsung_backlight_exit(samsung);
1616error_backlight:
1617        samsung_sysfs_exit(samsung);
1618error_sysfs:
1619        samsung_sabi_exit(samsung);
1620error_sabi:
1621        samsung_platform_exit(samsung);
1622error_platform:
1623        kfree(samsung);
1624        return ret;
1625}
1626
1627static void __exit samsung_exit(void)
1628{
1629        struct samsung_laptop *samsung;
1630
1631        samsung = platform_get_drvdata(samsung_platform_device);
1632
1633        samsung_debugfs_exit(samsung);
1634        samsung_leds_exit(samsung);
1635        samsung_rfkill_exit(samsung);
1636        samsung_backlight_exit(samsung);
1637        samsung_sysfs_exit(samsung);
1638        samsung_sabi_exit(samsung);
1639        samsung_platform_exit(samsung);
1640
1641        kfree(samsung);
1642        samsung_platform_device = NULL;
1643}
1644
1645module_init(samsung_init);
1646module_exit(samsung_exit);
1647
1648MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
1649MODULE_DESCRIPTION("Samsung Backlight driver");
1650MODULE_LICENSE("GPL");
1651
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.