diff options
author | Marco Chiappero <marco@absence.it> | 2012-05-19 09:35:54 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2012-05-31 14:33:35 -0400 |
commit | 54535d083f0ae6ee51a43a7a3e17e3ca89774937 (patch) | |
tree | 7b6e9feafe72be44d821d49a62ff083ca59f94e9 /drivers/platform | |
parent | bab7084c745bf4d75b760728387f375fd34dc683 (diff) |
sony-laptop: support automatic resume on lid open
A few models offer the chance to set whether to resume from S3 and/or S4
when opening the lid.
[malattia@linux.it: create three sysfs files for S3/4/5 rather than
using a single one accepting a bitmask. Support S5 since the DSDT
exports it. Use a struct to hold all the related values, caching of the
current status value rather than re-reading all the time in the sysfs
show function.]
Signed-off-by: Marco Chiappero <marco@absence.it>
Signed-off-by: Mattia Dongili <malattia@linux.it>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 2b72e476dfff..bc7f40bcd9cc 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -152,6 +152,9 @@ static int sony_nc_thermal_setup(struct platform_device *pd); | |||
152 | static void sony_nc_thermal_cleanup(struct platform_device *pd); | 152 | static void sony_nc_thermal_cleanup(struct platform_device *pd); |
153 | static void sony_nc_thermal_resume(void); | 153 | static void sony_nc_thermal_resume(void); |
154 | 154 | ||
155 | static int sony_nc_lid_resume_setup(struct platform_device *pd); | ||
156 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); | ||
157 | |||
155 | enum sony_nc_rfkill { | 158 | enum sony_nc_rfkill { |
156 | SONY_WIFI, | 159 | SONY_WIFI, |
157 | SONY_BLUETOOTH, | 160 | SONY_BLUETOOTH, |
@@ -1286,6 +1289,12 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
1286 | pr_err("couldn't set up battery care function (%d)\n", | 1289 | pr_err("couldn't set up battery care function (%d)\n", |
1287 | result); | 1290 | result); |
1288 | break; | 1291 | break; |
1292 | case 0x0119: | ||
1293 | result = sony_nc_lid_resume_setup(pf_device); | ||
1294 | if (result) | ||
1295 | pr_err("couldn't set up lid resume function (%d)\n", | ||
1296 | result); | ||
1297 | break; | ||
1289 | case 0x0122: | 1298 | case 0x0122: |
1290 | result = sony_nc_thermal_setup(pf_device); | 1299 | result = sony_nc_thermal_setup(pf_device); |
1291 | if (result) | 1300 | if (result) |
@@ -1333,6 +1342,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
1333 | case 0x013f: | 1342 | case 0x013f: |
1334 | sony_nc_battery_care_cleanup(pd); | 1343 | sony_nc_battery_care_cleanup(pd); |
1335 | break; | 1344 | break; |
1345 | case 0x0119: | ||
1346 | sony_nc_lid_resume_cleanup(pd); | ||
1347 | break; | ||
1336 | case 0x0122: | 1348 | case 0x0122: |
1337 | sony_nc_thermal_cleanup(pd); | 1349 | sony_nc_thermal_cleanup(pd); |
1338 | break; | 1350 | break; |
@@ -2106,6 +2118,134 @@ static void sony_nc_thermal_resume(void) | |||
2106 | sony_nc_thermal_mode_set(th_handle->mode); | 2118 | sony_nc_thermal_mode_set(th_handle->mode); |
2107 | } | 2119 | } |
2108 | 2120 | ||
2121 | /* resume on LID open */ | ||
2122 | struct snc_lid_resume_control { | ||
2123 | struct device_attribute attrs[3]; | ||
2124 | unsigned int status; | ||
2125 | }; | ||
2126 | static struct snc_lid_resume_control *lid_ctl; | ||
2127 | |||
2128 | static ssize_t sony_nc_lid_resume_store(struct device *dev, | ||
2129 | struct device_attribute *attr, | ||
2130 | const char *buffer, size_t count) | ||
2131 | { | ||
2132 | unsigned int result, pos; | ||
2133 | unsigned long value; | ||
2134 | if (count > 31) | ||
2135 | return -EINVAL; | ||
2136 | |||
2137 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
2138 | return -EINVAL; | ||
2139 | |||
2140 | /* the value we have to write to SNC is a bitmask: | ||
2141 | * +--------------+ | ||
2142 | * | S3 | S4 | S5 | | ||
2143 | * +--------------+ | ||
2144 | * 2 1 0 | ||
2145 | */ | ||
2146 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | ||
2147 | pos = 2; | ||
2148 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | ||
2149 | pos = 1; | ||
2150 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | ||
2151 | pos = 0; | ||
2152 | else | ||
2153 | return -EINVAL; | ||
2154 | |||
2155 | if (value) | ||
2156 | value = lid_ctl->status | (1 << pos); | ||
2157 | else | ||
2158 | value = lid_ctl->status & ~(1 << pos); | ||
2159 | |||
2160 | if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) | ||
2161 | return -EIO; | ||
2162 | |||
2163 | lid_ctl->status = value; | ||
2164 | |||
2165 | return count; | ||
2166 | } | ||
2167 | |||
2168 | static ssize_t sony_nc_lid_resume_show(struct device *dev, | ||
2169 | struct device_attribute *attr, char *buffer) | ||
2170 | { | ||
2171 | unsigned int pos; | ||
2172 | |||
2173 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | ||
2174 | pos = 2; | ||
2175 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | ||
2176 | pos = 1; | ||
2177 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | ||
2178 | pos = 0; | ||
2179 | else | ||
2180 | return -EINVAL; | ||
2181 | |||
2182 | return snprintf(buffer, PAGE_SIZE, "%d\n", | ||
2183 | (lid_ctl->status >> pos) & 0x01); | ||
2184 | } | ||
2185 | |||
2186 | static int sony_nc_lid_resume_setup(struct platform_device *pd) | ||
2187 | { | ||
2188 | unsigned int result; | ||
2189 | int i; | ||
2190 | |||
2191 | if (sony_call_snc_handle(0x0119, 0x0000, &result)) | ||
2192 | return -EIO; | ||
2193 | |||
2194 | lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); | ||
2195 | if (!lid_ctl) | ||
2196 | return -ENOMEM; | ||
2197 | |||
2198 | lid_ctl->status = result & 0x7; | ||
2199 | |||
2200 | sysfs_attr_init(&lid_ctl->attrs[0].attr); | ||
2201 | lid_ctl->attrs[0].attr.name = "lid_resume_S3"; | ||
2202 | lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; | ||
2203 | lid_ctl->attrs[0].show = sony_nc_lid_resume_show; | ||
2204 | lid_ctl->attrs[0].store = sony_nc_lid_resume_store; | ||
2205 | |||
2206 | sysfs_attr_init(&lid_ctl->attrs[1].attr); | ||
2207 | lid_ctl->attrs[1].attr.name = "lid_resume_S4"; | ||
2208 | lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; | ||
2209 | lid_ctl->attrs[1].show = sony_nc_lid_resume_show; | ||
2210 | lid_ctl->attrs[1].store = sony_nc_lid_resume_store; | ||
2211 | |||
2212 | sysfs_attr_init(&lid_ctl->attrs[2].attr); | ||
2213 | lid_ctl->attrs[2].attr.name = "lid_resume_S5"; | ||
2214 | lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; | ||
2215 | lid_ctl->attrs[2].show = sony_nc_lid_resume_show; | ||
2216 | lid_ctl->attrs[2].store = sony_nc_lid_resume_store; | ||
2217 | |||
2218 | for (i = 0; i < 3; i++) { | ||
2219 | result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2220 | if (result) | ||
2221 | goto liderror; | ||
2222 | } | ||
2223 | |||
2224 | return 0; | ||
2225 | |||
2226 | liderror: | ||
2227 | for (; i > 0; i--) | ||
2228 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2229 | |||
2230 | kfree(lid_ctl); | ||
2231 | lid_ctl = NULL; | ||
2232 | |||
2233 | return result; | ||
2234 | } | ||
2235 | |||
2236 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd) | ||
2237 | { | ||
2238 | int i; | ||
2239 | |||
2240 | if (lid_ctl) { | ||
2241 | for (i = 0; i < 3; i++) | ||
2242 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); | ||
2243 | |||
2244 | kfree(lid_ctl); | ||
2245 | lid_ctl = NULL; | ||
2246 | } | ||
2247 | } | ||
2248 | |||
2109 | static void sony_nc_backlight_ng_read_limits(int handle, | 2249 | static void sony_nc_backlight_ng_read_limits(int handle, |
2110 | struct sony_backlight_props *props) | 2250 | struct sony_backlight_props *props) |
2111 | { | 2251 | { |