18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Intel OakTrail Platform support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010-2011 Intel Corporation
68c2ecf20Sopenharmony_ci * Author: Yin Kangkai (kangkai.yin@intel.com)
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * based on Compal driver, Copyright (C) 2008 Cezary Jackiewicz
98c2ecf20Sopenharmony_ci * <cezary.jackiewicz (at) gmail.com>, based on MSI driver
108c2ecf20Sopenharmony_ci * Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This driver does below things:
138c2ecf20Sopenharmony_ci * 1. registers itself in the Linux backlight control in
148c2ecf20Sopenharmony_ci *    /sys/class/backlight/intel_oaktrail/
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * 2. registers in the rfkill subsystem here: /sys/class/rfkill/rfkillX/
178c2ecf20Sopenharmony_ci *    for these components: wifi, bluetooth, wwan (3g), gps
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * This driver might work on other products based on Oaktrail. If you
208c2ecf20Sopenharmony_ci * want to try it you can pass force=1 as argument to the module which
218c2ecf20Sopenharmony_ci * will force it to load even when the DMI data doesn't identify the
228c2ecf20Sopenharmony_ci * product as compatible.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include <linux/acpi.h>
288c2ecf20Sopenharmony_ci#include <linux/backlight.h>
298c2ecf20Sopenharmony_ci#include <linux/dmi.h>
308c2ecf20Sopenharmony_ci#include <linux/err.h>
318c2ecf20Sopenharmony_ci#include <linux/fb.h>
328c2ecf20Sopenharmony_ci#include <linux/i2c.h>
338c2ecf20Sopenharmony_ci#include <linux/kernel.h>
348c2ecf20Sopenharmony_ci#include <linux/module.h>
358c2ecf20Sopenharmony_ci#include <linux/mutex.h>
368c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
378c2ecf20Sopenharmony_ci#include <linux/rfkill.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <acpi/video.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define DRIVER_NAME	"intel_oaktrail"
428c2ecf20Sopenharmony_ci#define DRIVER_VERSION	"0.4ac1"
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/*
458c2ecf20Sopenharmony_ci * This is the devices status address in EC space, and the control bits
468c2ecf20Sopenharmony_ci * definition:
478c2ecf20Sopenharmony_ci *
488c2ecf20Sopenharmony_ci * (1 << 0):	Camera enable/disable, RW.
498c2ecf20Sopenharmony_ci * (1 << 1):	Bluetooth enable/disable, RW.
508c2ecf20Sopenharmony_ci * (1 << 2):	GPS enable/disable, RW.
518c2ecf20Sopenharmony_ci * (1 << 3):	WiFi enable/disable, RW.
528c2ecf20Sopenharmony_ci * (1 << 4):	WWAN (3G) enable/disable, RW.
538c2ecf20Sopenharmony_ci * (1 << 5):	Touchscreen enable/disable, Read Only.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_ci#define OT_EC_DEVICE_STATE_ADDRESS	0xD6
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#define OT_EC_CAMERA_MASK	(1 << 0)
588c2ecf20Sopenharmony_ci#define OT_EC_BT_MASK		(1 << 1)
598c2ecf20Sopenharmony_ci#define OT_EC_GPS_MASK		(1 << 2)
608c2ecf20Sopenharmony_ci#define OT_EC_WIFI_MASK		(1 << 3)
618c2ecf20Sopenharmony_ci#define OT_EC_WWAN_MASK		(1 << 4)
628c2ecf20Sopenharmony_ci#define OT_EC_TS_MASK		(1 << 5)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/*
658c2ecf20Sopenharmony_ci * This is the address in EC space and commands used to control LCD backlight:
668c2ecf20Sopenharmony_ci *
678c2ecf20Sopenharmony_ci * Two steps needed to change the LCD backlight:
688c2ecf20Sopenharmony_ci *   1. write the backlight percentage into OT_EC_BL_BRIGHTNESS_ADDRESS;
698c2ecf20Sopenharmony_ci *   2. write OT_EC_BL_CONTROL_ON_DATA into OT_EC_BL_CONTROL_ADDRESS.
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * To read the LCD back light, just read out the value from
728c2ecf20Sopenharmony_ci * OT_EC_BL_BRIGHTNESS_ADDRESS.
738c2ecf20Sopenharmony_ci *
748c2ecf20Sopenharmony_ci * LCD backlight brightness range: 0 - 100 (OT_EC_BL_BRIGHTNESS_MAX)
758c2ecf20Sopenharmony_ci */
768c2ecf20Sopenharmony_ci#define OT_EC_BL_BRIGHTNESS_ADDRESS	0x44
778c2ecf20Sopenharmony_ci#define OT_EC_BL_BRIGHTNESS_MAX		100
788c2ecf20Sopenharmony_ci#define OT_EC_BL_CONTROL_ADDRESS	0x3A
798c2ecf20Sopenharmony_ci#define OT_EC_BL_CONTROL_ON_DATA	0x1A
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic bool force;
838c2ecf20Sopenharmony_cimodule_param(force, bool, 0);
848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct platform_device *oaktrail_device;
878c2ecf20Sopenharmony_cistatic struct backlight_device *oaktrail_bl_device;
888c2ecf20Sopenharmony_cistatic struct rfkill *bt_rfkill;
898c2ecf20Sopenharmony_cistatic struct rfkill *gps_rfkill;
908c2ecf20Sopenharmony_cistatic struct rfkill *wifi_rfkill;
918c2ecf20Sopenharmony_cistatic struct rfkill *wwan_rfkill;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* rfkill */
958c2ecf20Sopenharmony_cistatic int oaktrail_rfkill_set(void *data, bool blocked)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	u8 value;
988c2ecf20Sopenharmony_ci	u8 result;
998c2ecf20Sopenharmony_ci	unsigned long radio = (unsigned long) data;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	ec_read(OT_EC_DEVICE_STATE_ADDRESS, &result);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!blocked)
1048c2ecf20Sopenharmony_ci		value = (u8) (result | radio);
1058c2ecf20Sopenharmony_ci	else
1068c2ecf20Sopenharmony_ci		value = (u8) (result & ~radio);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ec_write(OT_EC_DEVICE_STATE_ADDRESS, value);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return 0;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic const struct rfkill_ops oaktrail_rfkill_ops = {
1148c2ecf20Sopenharmony_ci	.set_block = oaktrail_rfkill_set,
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct rfkill *oaktrail_rfkill_new(char *name, enum rfkill_type type,
1188c2ecf20Sopenharmony_ci					  unsigned long mask)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct rfkill *rfkill_dev;
1218c2ecf20Sopenharmony_ci	u8 value;
1228c2ecf20Sopenharmony_ci	int err;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	rfkill_dev = rfkill_alloc(name, &oaktrail_device->dev, type,
1258c2ecf20Sopenharmony_ci				  &oaktrail_rfkill_ops, (void *)mask);
1268c2ecf20Sopenharmony_ci	if (!rfkill_dev)
1278c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ec_read(OT_EC_DEVICE_STATE_ADDRESS, &value);
1308c2ecf20Sopenharmony_ci	rfkill_init_sw_state(rfkill_dev, (value & mask) != 1);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	err = rfkill_register(rfkill_dev);
1338c2ecf20Sopenharmony_ci	if (err) {
1348c2ecf20Sopenharmony_ci		rfkill_destroy(rfkill_dev);
1358c2ecf20Sopenharmony_ci		return ERR_PTR(err);
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return rfkill_dev;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic inline void __oaktrail_rfkill_cleanup(struct rfkill *rf)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	if (rf) {
1448c2ecf20Sopenharmony_ci		rfkill_unregister(rf);
1458c2ecf20Sopenharmony_ci		rfkill_destroy(rf);
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void oaktrail_rfkill_cleanup(void)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	__oaktrail_rfkill_cleanup(wifi_rfkill);
1528c2ecf20Sopenharmony_ci	__oaktrail_rfkill_cleanup(bt_rfkill);
1538c2ecf20Sopenharmony_ci	__oaktrail_rfkill_cleanup(gps_rfkill);
1548c2ecf20Sopenharmony_ci	__oaktrail_rfkill_cleanup(wwan_rfkill);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int oaktrail_rfkill_init(void)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int ret;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	wifi_rfkill = oaktrail_rfkill_new("oaktrail-wifi",
1628c2ecf20Sopenharmony_ci					  RFKILL_TYPE_WLAN,
1638c2ecf20Sopenharmony_ci					  OT_EC_WIFI_MASK);
1648c2ecf20Sopenharmony_ci	if (IS_ERR(wifi_rfkill)) {
1658c2ecf20Sopenharmony_ci		ret = PTR_ERR(wifi_rfkill);
1668c2ecf20Sopenharmony_ci		wifi_rfkill = NULL;
1678c2ecf20Sopenharmony_ci		goto cleanup;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	bt_rfkill = oaktrail_rfkill_new("oaktrail-bluetooth",
1718c2ecf20Sopenharmony_ci					RFKILL_TYPE_BLUETOOTH,
1728c2ecf20Sopenharmony_ci					OT_EC_BT_MASK);
1738c2ecf20Sopenharmony_ci	if (IS_ERR(bt_rfkill)) {
1748c2ecf20Sopenharmony_ci		ret = PTR_ERR(bt_rfkill);
1758c2ecf20Sopenharmony_ci		bt_rfkill = NULL;
1768c2ecf20Sopenharmony_ci		goto cleanup;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	gps_rfkill = oaktrail_rfkill_new("oaktrail-gps",
1808c2ecf20Sopenharmony_ci					 RFKILL_TYPE_GPS,
1818c2ecf20Sopenharmony_ci					 OT_EC_GPS_MASK);
1828c2ecf20Sopenharmony_ci	if (IS_ERR(gps_rfkill)) {
1838c2ecf20Sopenharmony_ci		ret = PTR_ERR(gps_rfkill);
1848c2ecf20Sopenharmony_ci		gps_rfkill = NULL;
1858c2ecf20Sopenharmony_ci		goto cleanup;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	wwan_rfkill = oaktrail_rfkill_new("oaktrail-wwan",
1898c2ecf20Sopenharmony_ci					  RFKILL_TYPE_WWAN,
1908c2ecf20Sopenharmony_ci					  OT_EC_WWAN_MASK);
1918c2ecf20Sopenharmony_ci	if (IS_ERR(wwan_rfkill)) {
1928c2ecf20Sopenharmony_ci		ret = PTR_ERR(wwan_rfkill);
1938c2ecf20Sopenharmony_ci		wwan_rfkill = NULL;
1948c2ecf20Sopenharmony_ci		goto cleanup;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cicleanup:
2008c2ecf20Sopenharmony_ci	oaktrail_rfkill_cleanup();
2018c2ecf20Sopenharmony_ci	return ret;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/* backlight */
2068c2ecf20Sopenharmony_cistatic int get_backlight_brightness(struct backlight_device *b)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	u8 value;
2098c2ecf20Sopenharmony_ci	ec_read(OT_EC_BL_BRIGHTNESS_ADDRESS, &value);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return value;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int set_backlight_brightness(struct backlight_device *b)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	u8 percent = (u8) b->props.brightness;
2178c2ecf20Sopenharmony_ci	if (percent < 0 || percent > OT_EC_BL_BRIGHTNESS_MAX)
2188c2ecf20Sopenharmony_ci		return -EINVAL;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ec_write(OT_EC_BL_BRIGHTNESS_ADDRESS, percent);
2218c2ecf20Sopenharmony_ci	ec_write(OT_EC_BL_CONTROL_ADDRESS, OT_EC_BL_CONTROL_ON_DATA);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const struct backlight_ops oaktrail_bl_ops = {
2278c2ecf20Sopenharmony_ci	.get_brightness = get_backlight_brightness,
2288c2ecf20Sopenharmony_ci	.update_status	= set_backlight_brightness,
2298c2ecf20Sopenharmony_ci};
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int oaktrail_backlight_init(void)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct backlight_device *bd;
2348c2ecf20Sopenharmony_ci	struct backlight_properties props;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	memset(&props, 0, sizeof(struct backlight_properties));
2378c2ecf20Sopenharmony_ci	props.type = BACKLIGHT_PLATFORM;
2388c2ecf20Sopenharmony_ci	props.max_brightness = OT_EC_BL_BRIGHTNESS_MAX;
2398c2ecf20Sopenharmony_ci	bd = backlight_device_register(DRIVER_NAME,
2408c2ecf20Sopenharmony_ci				       &oaktrail_device->dev, NULL,
2418c2ecf20Sopenharmony_ci				       &oaktrail_bl_ops,
2428c2ecf20Sopenharmony_ci				       &props);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (IS_ERR(bd)) {
2458c2ecf20Sopenharmony_ci		oaktrail_bl_device = NULL;
2468c2ecf20Sopenharmony_ci		pr_warn("Unable to register backlight device\n");
2478c2ecf20Sopenharmony_ci		return PTR_ERR(bd);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	oaktrail_bl_device = bd;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	bd->props.brightness = get_backlight_brightness(bd);
2538c2ecf20Sopenharmony_ci	bd->props.power = FB_BLANK_UNBLANK;
2548c2ecf20Sopenharmony_ci	backlight_update_status(bd);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void oaktrail_backlight_exit(void)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	backlight_device_unregister(oaktrail_bl_device);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int oaktrail_probe(struct platform_device *pdev)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int oaktrail_remove(struct platform_device *pdev)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic struct platform_driver oaktrail_driver = {
2758c2ecf20Sopenharmony_ci	.driver = {
2768c2ecf20Sopenharmony_ci		.name = DRIVER_NAME,
2778c2ecf20Sopenharmony_ci	},
2788c2ecf20Sopenharmony_ci	.probe	= oaktrail_probe,
2798c2ecf20Sopenharmony_ci	.remove	= oaktrail_remove,
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int dmi_check_cb(const struct dmi_system_id *id)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	pr_info("Identified model '%s'\n", id->ident);
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic const struct dmi_system_id oaktrail_dmi_table[] __initconst = {
2898c2ecf20Sopenharmony_ci	{
2908c2ecf20Sopenharmony_ci		.ident = "OakTrail platform",
2918c2ecf20Sopenharmony_ci		.matches = {
2928c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "OakTrail platform"),
2938c2ecf20Sopenharmony_ci		},
2948c2ecf20Sopenharmony_ci		.callback = dmi_check_cb
2958c2ecf20Sopenharmony_ci	},
2968c2ecf20Sopenharmony_ci	{ }
2978c2ecf20Sopenharmony_ci};
2988c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int __init oaktrail_init(void)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	int ret;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (acpi_disabled) {
3058c2ecf20Sopenharmony_ci		pr_err("ACPI needs to be enabled for this driver to work!\n");
3068c2ecf20Sopenharmony_ci		return -ENODEV;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (!force && !dmi_check_system(oaktrail_dmi_table)) {
3108c2ecf20Sopenharmony_ci		pr_err("Platform not recognized (You could try the module's force-parameter)");
3118c2ecf20Sopenharmony_ci		return -ENODEV;
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ret = platform_driver_register(&oaktrail_driver);
3158c2ecf20Sopenharmony_ci	if (ret) {
3168c2ecf20Sopenharmony_ci		pr_warn("Unable to register platform driver\n");
3178c2ecf20Sopenharmony_ci		goto err_driver_reg;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	oaktrail_device = platform_device_alloc(DRIVER_NAME, -1);
3218c2ecf20Sopenharmony_ci	if (!oaktrail_device) {
3228c2ecf20Sopenharmony_ci		pr_warn("Unable to allocate platform device\n");
3238c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3248c2ecf20Sopenharmony_ci		goto err_device_alloc;
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	ret = platform_device_add(oaktrail_device);
3288c2ecf20Sopenharmony_ci	if (ret) {
3298c2ecf20Sopenharmony_ci		pr_warn("Unable to add platform device\n");
3308c2ecf20Sopenharmony_ci		goto err_device_add;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
3348c2ecf20Sopenharmony_ci		ret = oaktrail_backlight_init();
3358c2ecf20Sopenharmony_ci		if (ret)
3368c2ecf20Sopenharmony_ci			goto err_backlight;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ret = oaktrail_rfkill_init();
3408c2ecf20Sopenharmony_ci	if (ret) {
3418c2ecf20Sopenharmony_ci		pr_warn("Setup rfkill failed\n");
3428c2ecf20Sopenharmony_ci		goto err_rfkill;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pr_info("Driver "DRIVER_VERSION" successfully loaded\n");
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cierr_rfkill:
3498c2ecf20Sopenharmony_ci	oaktrail_backlight_exit();
3508c2ecf20Sopenharmony_cierr_backlight:
3518c2ecf20Sopenharmony_ci	platform_device_del(oaktrail_device);
3528c2ecf20Sopenharmony_cierr_device_add:
3538c2ecf20Sopenharmony_ci	platform_device_put(oaktrail_device);
3548c2ecf20Sopenharmony_cierr_device_alloc:
3558c2ecf20Sopenharmony_ci	platform_driver_unregister(&oaktrail_driver);
3568c2ecf20Sopenharmony_cierr_driver_reg:
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return ret;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void __exit oaktrail_cleanup(void)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	oaktrail_backlight_exit();
3648c2ecf20Sopenharmony_ci	oaktrail_rfkill_cleanup();
3658c2ecf20Sopenharmony_ci	platform_device_unregister(oaktrail_device);
3668c2ecf20Sopenharmony_ci	platform_driver_unregister(&oaktrail_driver);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	pr_info("Driver unloaded\n");
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cimodule_init(oaktrail_init);
3728c2ecf20Sopenharmony_cimodule_exit(oaktrail_cleanup);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
3758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
3768c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
3778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
378