/*
* Samsung Laptop driver
*
* Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
* Copyright (C) 2009,2011 Novell Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
/*
* This driver is needed because a number of Samsung laptops do not hook
* their control settings through ACPI. So we have to poke around in the
* BIOS to do things like brightness values, and "special" key controls.
*/
/*
* We have 0 - 8 as valid brightness levels. The specs say that level 0 should
* be reserved by the BIOS (which really doesn't make much sense), we tell
* userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
*/
#define MAX_BRIGHT 0x07
#define SABI_IFACE_MAIN 0x00
#define SABI_IFACE_SUB 0x02
#define SABI_IFACE_COMPLETE 0x04
#define SABI_IFACE_DATA 0x05
/* Structure to get data back to the calling function */
struct sabi_retval {
u8 retval[20];
};
struct sabi_header_offsets {
u8 port;
u8 re_mem;
u8 iface_func;
u8 en_mem;
u8 data_offset;
u8 data_segment;
};
struct sabi_commands {
/*
* Brightness is 0 - 8, as described above.
* Value 0 is for the BIOS to use
*/
u8 get_brightness;
u8 set_brightness;
/*
* first byte:
* 0x00 - wireless is off
* 0x01 - wireless is on
* second byte:
* 0x02 - 3G is off
* 0x03 - 3G is on
* TODO, verify 3G is correct, that doesn't seem right...
*/
u8 get_wireless_button;
u8 set_wireless_button;
/* 0 is off, 1 is on */
u8 get_backlight;
u8 set_backlight;
/*
* 0x80 or 0x00 - no action
* 0x81 - recovery key pressed
*/
u8 get_recovery_mode;
u8 set_recovery_mode;
/*
* on seclinux: 0 is low, 1 is high,
* on swsmi: 0 is normal, 1 is silent, 2 is turbo
*/
u8 get_performance_level;
u8 set_performance_level;
/*
* Tell the BIOS that Linux is running on this machine.
* 81 is on, 80 is off
*/
u8 set_linux;
};
struct sabi_performance_level {
const char *name;
u8 value;
};
struct sabi_config {
const char *test_string;
u16 main_function;
const struct sabi_header_offsets header_offsets;
const struct sabi_commands commands;
const struct sabi_performance_level performance_levels[4];
u8 min_brightness;
u8 max_brightness;
};
static const struct sabi_config sabi_configs[] = {
{
.test_string = "SECLINUX",
.main_function = 0x4c49,
.header_offsets = {
.port = 0x00,
.re_mem = 0x02,
.iface_func = 0x03,
.en_mem = 0x04,
.data_offset = 0x05,
.data_segment = 0x07,
},
.commands = {
.get_brightness = 0x00,
.set_brightness = 0x01,
.get_wireless_button = 0x02,
.set_wireless_button = 0x03,
.get_backlight = 0x04,
.set_backlight = 0x05,
.get_recovery_mode = 0x06,
.set_recovery_mode = 0x07,
.get_performance_level = 0x08,
.set_performance_level = 0x09,
.set_linux = 0x0a,
},
.performance_levels = {
{
.name = "silent",
.value = 0,
},
{
.name = "normal",
.value = 1,
},
{ },
},
.min_brightness = 1,
.max_brightness = 8,
},
{
.test_string = "SwSmi@",
.main_function = 0x5843,
.header_offsets = {
.port = 0x00,
.re_mem = 0x04,
.iface_func = 0x02,
.en_mem = 0x03,
.data_offset = 0x05,
.data_segment = 0x07,
},
.commands = {
.get_brightness = 0x10,
.set_brightness = 0x11,
.get_wireless_button = 0x12,
.set_wireless_button = 0x13,
.get_backlight = 0x2d,
.set_backlight = 0x2e,
.get_recovery_mode = 0xff,
.set_recovery_mode = 0xff,
.get_performance_level = 0x31,
.set_performance_level = 0x32,
.set_linux = 0xff,
},
.performance_levels = {
{
.name = "normal",
.value = 0,
},
{
.name = "silent",
.value = 1,
},
{
.name = "overclock",
.value = 2,
},
{ },
},
.min_brightness = 0,
.max_brightness = 8,
},
{ },
};
static const struct sabi_config *sabi_config;
static void __iomem *sabi;
static void __iomem *sabi_iface;
static void __iomem *f0000_segment;
static struct backlight_device *backlight_device;
static struct mutex sabi_mutex;
static struct platform_device *sdev;
static struct rfkill *rfk