root/drivers/platform/x86/intel_oaktrail.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. oaktrail_rfkill_set
  2. oaktrail_rfkill_new
  3. __oaktrail_rfkill_cleanup
  4. oaktrail_rfkill_cleanup
  5. oaktrail_rfkill_init
  6. get_backlight_brightness
  7. set_backlight_brightness
  8. oaktrail_backlight_init
  9. oaktrail_backlight_exit
  10. oaktrail_probe
  11. oaktrail_remove
  12. dmi_check_cb
  13. oaktrail_init
  14. oaktrail_cleanup

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Intel OakTrail Platform support
   4  *
   5  * Copyright (C) 2010-2011 Intel Corporation
   6  * Author: Yin Kangkai (kangkai.yin@intel.com)
   7  *
   8  * based on Compal driver, Copyright (C) 2008 Cezary Jackiewicz
   9  * <cezary.jackiewicz (at) gmail.com>, based on MSI driver
  10  * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
  11  *
  12  * This driver does below things:
  13  * 1. registers itself in the Linux backlight control in
  14  *    /sys/class/backlight/intel_oaktrail/
  15  *
  16  * 2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/
  17  *    for these components: wifi, bluetooth, wwan (3g), gps
  18  *
  19  * This driver might work on other products based on Oaktrail. If you
  20  * want to try it you can pass force=1 as argument to the module which
  21  * will force it to load even when the DMI data doesn't identify the
  22  * product as compatible.
  23  */
  24 
  25 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  26 
  27 #include <linux/acpi.h>
  28 #include <linux/backlight.h>
  29 #include <linux/dmi.h>
  30 #include <linux/err.h>
  31 #include <linux/fb.h>
  32 #include <linux/i2c.h>
  33 #include <linux/kernel.h>
  34 #include <linux/module.h>
  35 #include <linux/mutex.h>
  36 #include <linux/platform_device.h>
  37 #include <linux/rfkill.h>
  38 
  39 #include <acpi/video.h>
  40 
  41 #define DRIVER_NAME     "intel_oaktrail"
  42 #define DRIVER_VERSION  "0.4ac1"
  43 
  44 /*
  45  * This is the devices status address in EC space, and the control bits
  46  * definition:
  47  *
  48  * (1 << 0):    Camera enable/disable, RW.
  49  * (1 << 1):    Bluetooth enable/disable, RW.
  50  * (1 << 2):    GPS enable/disable, RW.
  51  * (1 << 3):    WiFi enable/disable, RW.
  52  * (1 << 4):    WWAN (3G) enable/disable, RW.
  53  * (1 << 5):    Touchscreen enable/disable, Read Only.
  54  */
  55 #define OT_EC_DEVICE_STATE_ADDRESS      0xD6
  56 
  57 #define OT_EC_CAMERA_MASK       (1 << 0)
  58 #define OT_EC_BT_MASK           (1 << 1)
  59 #define OT_EC_GPS_MASK          (1 << 2)
  60 #define OT_EC_WIFI_MASK         (1 << 3)
  61 #define OT_EC_WWAN_MASK         (1 << 4)
  62 #define OT_EC_TS_MASK           (1 << 5)
  63 
  64 /*
  65  * This is the address in EC space and commands used to control LCD backlight:
  66  *
  67  * Two steps needed to change the LCD backlight:
  68  *   1. write the backlight percentage into OT_EC_BL_BRIGHTNESS_ADDRESS;
  69  *   2. write OT_EC_BL_CONTROL_ON_DATA into OT_EC_BL_CONTROL_ADDRESS.
  70  *
  71  * To read the LCD back light, just read out the value from
  72  * OT_EC_BL_BRIGHTNESS_ADDRESS.
  73  *
  74  * LCD backlight brightness range: 0 - 100 (OT_EC_BL_BRIGHTNESS_MAX)
  75  */
  76 #define OT_EC_BL_BRIGHTNESS_ADDRESS     0x44
  77 #define OT_EC_BL_BRIGHTNESS_MAX         100
  78 #define OT_EC_BL_CONTROL_ADDRESS        0x3A
  79 #define OT_EC_BL_CONTROL_ON_DATA        0x1A
  80 
  81 
  82 static bool force;
  83 module_param(force, bool, 0);
  84 MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
  85 
  86 static struct platform_device *oaktrail_device;
  87 static struct backlight_device *oaktrail_bl_device;
  88 static struct rfkill *bt_rfkill;
  89 static struct rfkill *gps_rfkill;
  90 static struct rfkill *wifi_rfkill;
  91 static struct rfkill *wwan_rfkill;
  92 
  93 
  94 /* rfkill */
  95 static int oaktrail_rfkill_set(void *data, bool blocked)
  96 {
  97         u8 value;
  98         u8 result;
  99         unsigned long radio = (unsigned long) data;
 100 
 101         ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result);
 102 
 103         if (!blocked)
 104                 value = (u8) (result | radio);
 105         else
 106                 value = (u8) (result & ~radio);
 107 
 108         ec_write(OT_EC_DEVICE_STATE_ADDRESS, value);
 109 
 110         return 0;
 111 }
 112 
 113 static const struct rfkill_ops oaktrail_rfkill_ops = {
 114         .set_block = oaktrail_rfkill_set,
 115 };
 116 
 117 static struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type,
 118                                           unsigned long mask)
 119 {
 120         struct rfkill *rfkill_dev;
 121         u8 value;
 122         int err;
 123 
 124         rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type,
 125                                   &oaktrail_rfkill_ops, (void *)mask);
 126         if (!rfkill_dev)
 127                 return ERR_PTR(-ENOMEM);
 128 
 129         ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value);
 130         rfkill_init_sw_state(rfkill_dev, (value & mask) != 1);
 131 
 132         err = rfkill_register(rfkill_dev);
 133         if (err) {
 134                 rfkill_destroy(rfkill_dev);
 135                 return ERR_PTR(err);
 136         }
 137 
 138         return rfkill_dev;
 139 }
 140 
 141 static inline void __oaktrail_rfkill_cleanup(struct rfkill *rf)
 142 {
 143         if (rf) {
 144                 rfkill_unregister(rf);
 145                 rfkill_destroy(rf);
 146         }
 147 }
 148 
 149 static void oaktrail_rfkill_cleanup(void)
 150 {
 151         __oaktrail_rfkill_cleanup(wifi_rfkill);
 152         __oaktrail_rfkill_cleanup(bt_rfkill);
 153         __oaktrail_rfkill_cleanup(gps_rfkill);
 154         __oaktrail_rfkill_cleanup(wwan_rfkill);
 155 }
 156 
 157 static int oaktrail_rfkill_init(void)
 158 {
 159         int ret;
 160 
 161         wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi",
 162                                           RFKILL_TYPE_WLAN,
 163                                           OT_EC_WIFI_MASK);
 164         if (IS_ERR(wifi_rfkill)) {
 165                 ret = PTR_ERR(wifi_rfkill);
 166                 wifi_rfkill = NULL;
 167                 goto cleanup;
 168         }
 169 
 170         bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth",
 171                                         RFKILL_TYPE_BLUETOOTH,
 172                                         OT_EC_BT_MASK);
 173         if (IS_ERR(bt_rfkill)) {
 174                 ret = PTR_ERR(bt_rfkill);
 175                 bt_rfkill = NULL;
 176                 goto cleanup;
 177         }
 178 
 179         gps_rfkill = oaktrail_rfkill_new("oaktrail-gps",
 180                                          RFKILL_TYPE_GPS,
 181                                          OT_EC_GPS_MASK);
 182         if (IS_ERR(gps_rfkill)) {
 183                 ret = PTR_ERR(gps_rfkill);
 184                 gps_rfkill = NULL;
 185                 goto cleanup;
 186         }
 187 
 188         wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan",
 189                                           RFKILL_TYPE_WWAN,
 190                                           OT_EC_WWAN_MASK);
 191         if (IS_ERR(wwan_rfkill)) {
 192                 ret = PTR_ERR(wwan_rfkill);
 193                 wwan_rfkill = NULL;
 194                 goto cleanup;
 195         }
 196 
 197         return 0;
 198 
 199 cleanup:
 200         oaktrail_rfkill_cleanup();
 201         return ret;
 202 }
 203 
 204 
 205 /* backlight */
 206 static int get_backlight_brightness(struct backlight_device *b)
 207 {
 208         u8 value;
 209         ec_read(OT_EC_BL_BRIGHTNESS_ADDRESS, &value);
 210 
 211         return value;
 212 }
 213 
 214 static int set_backlight_brightness(struct backlight_device *b)
 215 {
 216         u8 percent = (u8) b->props.brightness;
 217         if (percent < 0 || percent > OT_EC_BL_BRIGHTNESS_MAX)
 218                 return -EINVAL;
 219 
 220         ec_write(OT_EC_BL_BRIGHTNESS_ADDRESS, percent);
 221         ec_write(OT_EC_BL_CONTROL_ADDRESS, OT_EC_BL_CONTROL_ON_DATA);
 222 
 223         return 0;
 224 }
 225 
 226 static const struct backlight_ops oaktrail_bl_ops = {
 227         .get_brightness = get_backlight_brightness,
 228         .update_status  = set_backlight_brightness,
 229 };
 230 
 231 static int oaktrail_backlight_init(void)
 232 {
 233         struct backlight_device *bd;
 234         struct backlight_properties props;
 235 
 236         memset(&props, 0, sizeof(struct backlight_properties));
 237         props.type = BACKLIGHT_PLATFORM;
 238         props.max_brightness = OT_EC_BL_BRIGHTNESS_MAX;
 239         bd = backlight_device_register(DRIVER_NAME,
 240                                        &oaktrail_device->dev, NULL,
 241                                        &oaktrail_bl_ops,
 242                                        &props);
 243 
 244         if (IS_ERR(bd)) {
 245                 oaktrail_bl_device = NULL;
 246                 pr_warning("Unable to register backlight device\n");
 247                 return PTR_ERR(bd);
 248         }
 249 
 250         oaktrail_bl_device = bd;
 251 
 252         bd->props.brightness = get_backlight_brightness(bd);
 253         bd->props.power = FB_BLANK_UNBLANK;
 254         backlight_update_status(bd);
 255 
 256         return 0;
 257 }
 258 
 259 static void oaktrail_backlight_exit(void)
 260 {
 261         backlight_device_unregister(oaktrail_bl_device);
 262 }
 263 
 264 static int oaktrail_probe(struct platform_device *pdev)
 265 {
 266         return 0;
 267 }
 268 
 269 static int oaktrail_remove(struct platform_device *pdev)
 270 {
 271         return 0;
 272 }
 273 
 274 static struct platform_driver oaktrail_driver = {
 275         .driver = {
 276                 .name = DRIVER_NAME,
 277         },
 278         .probe  = oaktrail_probe,
 279         .remove = oaktrail_remove,
 280 };
 281 
 282 static int dmi_check_cb(const struct dmi_system_id *id)
 283 {
 284         pr_info("Identified model '%s'\n", id->ident);
 285         return 0;
 286 }
 287 
 288 static const struct dmi_system_id oaktrail_dmi_table[] __initconst = {
 289         {
 290                 .ident = "OakTrail platform",
 291                 .matches = {
 292                         DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"),
 293                 },
 294                 .callback = dmi_check_cb
 295         },
 296         { }
 297 };
 298 MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table);
 299 
 300 static int __init oaktrail_init(void)
 301 {
 302         int ret;
 303 
 304         if (acpi_disabled) {
 305                 pr_err("ACPI needs to be enabled for this driver to work!\n");
 306                 return -ENODEV;
 307         }
 308 
 309         if (!force && !dmi_check_system(oaktrail_dmi_table)) {
 310                 pr_err("Platform not recognized (You could try the module's force-parameter)");
 311                 return -ENODEV;
 312         }
 313 
 314         ret = platform_driver_register(&oaktrail_driver);
 315         if (ret) {
 316                 pr_warning("Unable to register platform driver\n");
 317                 goto err_driver_reg;
 318         }
 319 
 320         oaktrail_device = platform_device_alloc(DRIVER_NAME, -1);
 321         if (!oaktrail_device) {
 322                 pr_warning("Unable to allocate platform device\n");
 323                 ret = -ENOMEM;
 324                 goto err_device_alloc;
 325         }
 326 
 327         ret = platform_device_add(oaktrail_device);
 328         if (ret) {
 329                 pr_warning("Unable to add platform device\n");
 330                 goto err_device_add;
 331         }
 332 
 333         if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
 334                 ret = oaktrail_backlight_init();
 335                 if (ret)
 336                         goto err_backlight;
 337         }
 338 
 339         ret = oaktrail_rfkill_init();
 340         if (ret) {
 341                 pr_warning("Setup rfkill failed\n");
 342                 goto err_rfkill;
 343         }
 344 
 345         pr_info("Driver "DRIVER_VERSION" successfully loaded\n");
 346         return 0;
 347 
 348 err_rfkill:
 349         oaktrail_backlight_exit();
 350 err_backlight:
 351         platform_device_del(oaktrail_device);
 352 err_device_add:
 353         platform_device_put(oaktrail_device);
 354 err_device_alloc:
 355         platform_driver_unregister(&oaktrail_driver);
 356 err_driver_reg:
 357 
 358         return ret;
 359 }
 360 
 361 static void __exit oaktrail_cleanup(void)
 362 {
 363         oaktrail_backlight_exit();
 364         oaktrail_rfkill_cleanup();
 365         platform_device_unregister(oaktrail_device);
 366         platform_driver_unregister(&oaktrail_driver);
 367 
 368         pr_info("Driver unloaded\n");
 369 }
 370 
 371 module_init(oaktrail_init);
 372 module_exit(oaktrail_cleanup);
 373 
 374 MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
 375 MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
 376 MODULE_VERSION(DRIVER_VERSION);
 377 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */