Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Intel OakTrail Platform support
0004  *
0005  * Copyright (C) 2010-2011 Intel Corporation
0006  * Author: Yin Kangkai (kangkai.yin@intel.com)
0007  *
0008  * based on Compal driver, Copyright (C) 2008 Cezary Jackiewicz
0009  * <cezary.jackiewicz (at) gmail.com>, based on MSI driver
0010  * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
0011  *
0012  * This driver does below things:
0013  * 1. registers itself in the Linux backlight control in
0014  *    /sys/class/backlight/intel_oaktrail/
0015  *
0016  * 2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/
0017  *    for these components: wifi, bluetooth, wwan (3g), gps
0018  *
0019  * This driver might work on other products based on Oaktrail. If you
0020  * want to try it you can pass force=1 as argument to the module which
0021  * will force it to load even when the DMI data doesn't identify the
0022  * product as compatible.
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  * This is the devices status address in EC space, and the control bits
0046  * definition:
0047  *
0048  * (1 << 0):    Camera enable/disable, RW.
0049  * (1 << 1):    Bluetooth enable/disable, RW.
0050  * (1 << 2):    GPS enable/disable, RW.
0051  * (1 << 3):    WiFi enable/disable, RW.
0052  * (1 << 4):    WWAN (3G) enable/disable, RW.
0053  * (1 << 5):    Touchscreen enable/disable, Read Only.
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  * This is the address in EC space and commands used to control LCD backlight:
0066  *
0067  * Two steps needed to change the LCD backlight:
0068  *   1. write the backlight percentage into OT_EC_BL_BRIGHTNESS_ADDRESS;
0069  *   2. write OT_EC_BL_CONTROL_ON_DATA into OT_EC_BL_CONTROL_ADDRESS.
0070  *
0071  * To read the LCD back light, just read out the value from
0072  * OT_EC_BL_BRIGHTNESS_ADDRESS.
0073  *
0074  * LCD backlight brightness range: 0 - 100 (OT_EC_BL_BRIGHTNESS_MAX)
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 /* rfkill */
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 /* backlight */
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");