0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0026
0027 #include <linux/acpi.h>
0028 #include <linux/backlight.h>
0029 #include <linux/dmi.h>
0030 #include <linux/err.h>
0031 #include <linux/fb.h>
0032 #include <linux/i2c.h>
0033 #include <linux/kernel.h>
0034 #include <linux/module.h>
0035 #include <linux/mutex.h>
0036 #include <linux/platform_device.h>
0037 #include <linux/rfkill.h>
0038
0039 #include <acpi/video.h>
0040
0041 #define DRIVER_NAME "intel_oaktrail"
0042 #define DRIVER_VERSION "0.4ac1"
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 #define OT_EC_DEVICE_STATE_ADDRESS 0xD6
0056
0057 #define OT_EC_CAMERA_MASK (1 << 0)
0058 #define OT_EC_BT_MASK (1 << 1)
0059 #define OT_EC_GPS_MASK (1 << 2)
0060 #define OT_EC_WIFI_MASK (1 << 3)
0061 #define OT_EC_WWAN_MASK (1 << 4)
0062 #define OT_EC_TS_MASK (1 << 5)
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076 #define OT_EC_BL_BRIGHTNESS_ADDRESS 0x44
0077 #define OT_EC_BL_BRIGHTNESS_MAX 100
0078 #define OT_EC_BL_CONTROL_ADDRESS 0x3A
0079 #define OT_EC_BL_CONTROL_ON_DATA 0x1A
0080
0081
0082 static bool force;
0083 module_param(force, bool, 0);
0084 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
0085
0086 static struct platform_device *oaktrail_device;
0087 static struct backlight_device *oaktrail_bl_device;
0088 static struct rfkill *bt_rfkill;
0089 static struct rfkill *gps_rfkill;
0090 static struct rfkill *wifi_rfkill;
0091 static struct rfkill *wwan_rfkill;
0092
0093
0094
0095 static int oaktrail_rfkill_set(void *data, bool blocked)
0096 {
0097 u8 value;
0098 u8 result;
0099 unsigned long radio = (unsigned long) data;
0100
0101 ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result);
0102
0103 if (!blocked)
0104 value = (u8) (result | radio);
0105 else
0106 value = (u8) (result & ~radio);
0107
0108 ec_write(OT_EC_DEVICE_STATE_ADDRESS, value);
0109
0110 return 0;
0111 }
0112
0113 static const struct rfkill_ops oaktrail_rfkill_ops = {
0114 .set_block = oaktrail_rfkill_set,
0115 };
0116
0117 static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type,
0118 unsigned long mask)
0119 {
0120 struct rfkill *rfkill_dev;
0121 u8 value;
0122 int err;
0123
0124 rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type,
0125 &oaktrail_rfkill_ops, (void *)mask);
0126 if (!rfkill_dev)
0127 return ERR_PTR(-ENOMEM);
0128
0129 ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value);
0130 rfkill_init_sw_state(rfkill_dev, (value & mask) != 1);
0131
0132 err = rfkill_register(rfkill_dev);
0133 if (err) {
0134 rfkill_destroy(rfkill_dev);
0135 return ERR_PTR(err);
0136 }
0137
0138 return rfkill_dev;
0139 }
0140
0141 static inline void __oaktrail_rfkill_cleanup(struct rfkill *rf)
0142 {
0143 if (rf) {
0144 rfkill_unregister(rf);
0145 rfkill_destroy(rf);
0146 }
0147 }
0148
0149 static void oaktrail_rfkill_cleanup(void)
0150 {
0151 __oaktrail_rfkill_cleanup(wifi_rfkill);
0152 __oaktrail_rfkill_cleanup(bt_rfkill);
0153 __oaktrail_rfkill_cleanup(gps_rfkill);
0154 __oaktrail_rfkill_cleanup(wwan_rfkill);
0155 }
0156
0157 static int oaktrail_rfkill_init(void)
0158 {
0159 int ret;
0160
0161 wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi",
0162 RFKILL_TYPE_WLAN,
0163 OT_EC_WIFI_MASK);
0164 if (IS_ERR(wifi_rfkill)) {
0165 ret = PTR_ERR(wifi_rfkill);
0166 wifi_rfkill = NULL;
0167 goto cleanup;
0168 }
0169
0170 bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth",
0171 RFKILL_TYPE_BLUETOOTH,
0172 OT_EC_BT_MASK);
0173 if (IS_ERR(bt_rfkill)) {
0174 ret = PTR_ERR(bt_rfkill);
0175 bt_rfkill = NULL;
0176 goto cleanup;
0177 }
0178
0179 gps_rfkill = oaktrail_rfkill_new("oaktrail-gps",
0180 RFKILL_TYPE_GPS,
0181 OT_EC_GPS_MASK);
0182 if (IS_ERR(gps_rfkill)) {
0183 ret = PTR_ERR(gps_rfkill);
0184 gps_rfkill = NULL;
0185 goto cleanup;
0186 }
0187
0188 wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan",
0189 RFKILL_TYPE_WWAN,
0190 OT_EC_WWAN_MASK);
0191 if (IS_ERR(wwan_rfkill)) {
0192 ret = PTR_ERR(wwan_rfkill);
0193 wwan_rfkill = NULL;
0194 goto cleanup;
0195 }
0196
0197 return 0;
0198
0199 cleanup:
0200 oaktrail_rfkill_cleanup();
0201 return ret;
0202 }
0203
0204
0205
0206 static int get_backlight_brightness(struct backlight_device *b)
0207 {
0208 u8 value;
0209 ec_read(OT_EC_BL_BRIGHTNESS_ADDRESS, &value);
0210
0211 return value;
0212 }
0213
0214 static int set_backlight_brightness(struct backlight_device *b)
0215 {
0216 u8 percent = (u8) b->props.brightness;
0217 if (percent < 0 || percent > OT_EC_BL_BRIGHTNESS_MAX)
0218 return -EINVAL;
0219
0220 ec_write(OT_EC_BL_BRIGHTNESS_ADDRESS, percent);
0221 ec_write(OT_EC_BL_CONTROL_ADDRESS, OT_EC_BL_CONTROL_ON_DATA);
0222
0223 return 0;
0224 }
0225
0226 static const struct backlight_ops oaktrail_bl_ops = {
0227 .get_brightness = get_backlight_brightness,
0228 .update_status = set_backlight_brightness,
0229 };
0230
0231 static int oaktrail_backlight_init(void)
0232 {
0233 struct backlight_device *bd;
0234 struct backlight_properties props;
0235
0236 memset(&props, 0, sizeof(struct backlight_properties));
0237 props.type = BACKLIGHT_PLATFORM;
0238 props.max_brightness = OT_EC_BL_BRIGHTNESS_MAX;
0239 bd = backlight_device_register(DRIVER_NAME,
0240 &oaktrail_device->dev, NULL,
0241 &oaktrail_bl_ops,
0242 &props);
0243
0244 if (IS_ERR(bd)) {
0245 oaktrail_bl_device = NULL;
0246 pr_warn("Unable to register backlight device\n");
0247 return PTR_ERR(bd);
0248 }
0249
0250 oaktrail_bl_device = bd;
0251
0252 bd->props.brightness = get_backlight_brightness(bd);
0253 bd->props.power = FB_BLANK_UNBLANK;
0254 backlight_update_status(bd);
0255
0256 return 0;
0257 }
0258
0259 static void oaktrail_backlight_exit(void)
0260 {
0261 backlight_device_unregister(oaktrail_bl_device);
0262 }
0263
0264 static int oaktrail_probe(struct platform_device *pdev)
0265 {
0266 return 0;
0267 }
0268
0269 static int oaktrail_remove(struct platform_device *pdev)
0270 {
0271 return 0;
0272 }
0273
0274 static struct platform_driver oaktrail_driver = {
0275 .driver = {
0276 .name = DRIVER_NAME,
0277 },
0278 .probe = oaktrail_probe,
0279 .remove = oaktrail_remove,
0280 };
0281
0282 static int dmi_check_cb(const struct dmi_system_id *id)
0283 {
0284 pr_info("Identified model '%s'\n", id->ident);
0285 return 0;
0286 }
0287
0288 static const struct dmi_system_id oaktrail_dmi_table[] __initconst = {
0289 {
0290 .ident = "OakTrail platform",
0291 .matches = {
0292 DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"),
0293 },
0294 .callback = dmi_check_cb
0295 },
0296 { }
0297 };
0298 MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table);
0299
0300 static int __init oaktrail_init(void)
0301 {
0302 int ret;
0303
0304 if (acpi_disabled) {
0305 pr_err("ACPI needs to be enabled for this driver to work!\n");
0306 return -ENODEV;
0307 }
0308
0309 if (!force && !dmi_check_system(oaktrail_dmi_table)) {
0310 pr_err("Platform not recognized (You could try the module's force-parameter)");
0311 return -ENODEV;
0312 }
0313
0314 ret = platform_driver_register(&oaktrail_driver);
0315 if (ret) {
0316 pr_warn("Unable to register platform driver\n");
0317 goto err_driver_reg;
0318 }
0319
0320 oaktrail_device = platform_device_alloc(DRIVER_NAME, -1);
0321 if (!oaktrail_device) {
0322 pr_warn("Unable to allocate platform device\n");
0323 ret = -ENOMEM;
0324 goto err_device_alloc;
0325 }
0326
0327 ret = platform_device_add(oaktrail_device);
0328 if (ret) {
0329 pr_warn("Unable to add platform device\n");
0330 goto err_device_add;
0331 }
0332
0333 if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
0334 ret = oaktrail_backlight_init();
0335 if (ret)
0336 goto err_backlight;
0337 }
0338
0339 ret = oaktrail_rfkill_init();
0340 if (ret) {
0341 pr_warn("Setup rfkill failed\n");
0342 goto err_rfkill;
0343 }
0344
0345 pr_info("Driver "DRIVER_VERSION" successfully loaded\n");
0346 return 0;
0347
0348 err_rfkill:
0349 oaktrail_backlight_exit();
0350 err_backlight:
0351 platform_device_del(oaktrail_device);
0352 err_device_add:
0353 platform_device_put(oaktrail_device);
0354 err_device_alloc:
0355 platform_driver_unregister(&oaktrail_driver);
0356 err_driver_reg:
0357
0358 return ret;
0359 }
0360
0361 static void __exit oaktrail_cleanup(void)
0362 {
0363 oaktrail_backlight_exit();
0364 oaktrail_rfkill_cleanup();
0365 platform_device_unregister(oaktrail_device);
0366 platform_driver_unregister(&oaktrail_driver);
0367
0368 pr_info("Driver unloaded\n");
0369 }
0370
0371 module_init(oaktrail_init);
0372 module_exit(oaktrail_cleanup);
0373
0374 MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
0375 MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
0376 MODULE_VERSION(DRIVER_VERSION);
0377 MODULE_LICENSE("GPL");