18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Backlight Driver for the KB3886 Backlight 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2007-2008 Claudio Nieder 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on corgi_bl.c by Richard Purdie and kb3886 driver by Robert Woerle 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/fb.h> 168c2ecf20Sopenharmony_ci#include <linux/backlight.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/dmi.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define KB3886_PARENT 0x64 218c2ecf20Sopenharmony_ci#define KB3886_IO 0x60 228c2ecf20Sopenharmony_ci#define KB3886_ADC_DAC_PWM 0xC4 238c2ecf20Sopenharmony_ci#define KB3886_PWM0_WRITE 0x81 248c2ecf20Sopenharmony_ci#define KB3886_PWM0_READ 0x41 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(bl_mutex); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void kb3886_bl_set_intensity(int intensity) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci mutex_lock(&bl_mutex); 318c2ecf20Sopenharmony_ci intensity = intensity&0xff; 328c2ecf20Sopenharmony_ci outb(KB3886_ADC_DAC_PWM, KB3886_PARENT); 338c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 348c2ecf20Sopenharmony_ci outb(KB3886_PWM0_WRITE, KB3886_IO); 358c2ecf20Sopenharmony_ci usleep_range(10000, 11000); 368c2ecf20Sopenharmony_ci outb(intensity, KB3886_IO); 378c2ecf20Sopenharmony_ci mutex_unlock(&bl_mutex); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct kb3886bl_machinfo { 418c2ecf20Sopenharmony_ci int max_intensity; 428c2ecf20Sopenharmony_ci int default_intensity; 438c2ecf20Sopenharmony_ci int limit_mask; 448c2ecf20Sopenharmony_ci void (*set_bl_intensity)(int intensity); 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic struct kb3886bl_machinfo kb3886_bl_machinfo = { 488c2ecf20Sopenharmony_ci .max_intensity = 0xff, 498c2ecf20Sopenharmony_ci .default_intensity = 0xa0, 508c2ecf20Sopenharmony_ci .limit_mask = 0x7f, 518c2ecf20Sopenharmony_ci .set_bl_intensity = kb3886_bl_set_intensity, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct platform_device kb3886bl_device = { 558c2ecf20Sopenharmony_ci .name = "kb3886-bl", 568c2ecf20Sopenharmony_ci .dev = { 578c2ecf20Sopenharmony_ci .platform_data = &kb3886_bl_machinfo, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci .id = -1, 608c2ecf20Sopenharmony_ci}; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct platform_device *devices[] __initdata = { 638c2ecf20Sopenharmony_ci &kb3886bl_device, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * Back to driver 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int kb3886bl_intensity; 718c2ecf20Sopenharmony_cistatic struct backlight_device *kb3886_backlight_device; 728c2ecf20Sopenharmony_cistatic struct kb3886bl_machinfo *bl_machinfo; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic unsigned long kb3886bl_flags; 758c2ecf20Sopenharmony_ci#define KB3886BL_SUSPENDED 0x01 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct dmi_system_id kb3886bl_device_table[] __initconst = { 788c2ecf20Sopenharmony_ci { 798c2ecf20Sopenharmony_ci .ident = "Sahara Touch-iT", 808c2ecf20Sopenharmony_ci .matches = { 818c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SDV"), 828c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "iTouch T201"), 838c2ecf20Sopenharmony_ci }, 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci { } 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int kb3886bl_send_intensity(struct backlight_device *bd) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int intensity = backlight_get_brightness(bd); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (kb3886bl_flags & KB3886BL_SUSPENDED) 938c2ecf20Sopenharmony_ci intensity = 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci bl_machinfo->set_bl_intensity(intensity); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci kb3886bl_intensity = intensity; 988c2ecf20Sopenharmony_ci return 0; 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 1028c2ecf20Sopenharmony_cistatic int kb3886bl_suspend(struct device *dev) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct backlight_device *bd = dev_get_drvdata(dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci kb3886bl_flags |= KB3886BL_SUSPENDED; 1078c2ecf20Sopenharmony_ci backlight_update_status(bd); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic int kb3886bl_resume(struct device *dev) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct backlight_device *bd = dev_get_drvdata(dev); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci kb3886bl_flags &= ~KB3886BL_SUSPENDED; 1168c2ecf20Sopenharmony_ci backlight_update_status(bd); 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci#endif 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(kb3886bl_pm_ops, kb3886bl_suspend, kb3886bl_resume); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int kb3886bl_get_intensity(struct backlight_device *bd) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci return kb3886bl_intensity; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct backlight_ops kb3886bl_ops = { 1298c2ecf20Sopenharmony_ci .get_brightness = kb3886bl_get_intensity, 1308c2ecf20Sopenharmony_ci .update_status = kb3886bl_send_intensity, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int kb3886bl_probe(struct platform_device *pdev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct backlight_properties props; 1368c2ecf20Sopenharmony_ci struct kb3886bl_machinfo *machinfo = dev_get_platdata(&pdev->dev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci bl_machinfo = machinfo; 1398c2ecf20Sopenharmony_ci if (!machinfo->limit_mask) 1408c2ecf20Sopenharmony_ci machinfo->limit_mask = -1; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 1438c2ecf20Sopenharmony_ci props.type = BACKLIGHT_RAW; 1448c2ecf20Sopenharmony_ci props.max_brightness = machinfo->max_intensity; 1458c2ecf20Sopenharmony_ci kb3886_backlight_device = devm_backlight_device_register(&pdev->dev, 1468c2ecf20Sopenharmony_ci "kb3886-bl", &pdev->dev, 1478c2ecf20Sopenharmony_ci NULL, &kb3886bl_ops, 1488c2ecf20Sopenharmony_ci &props); 1498c2ecf20Sopenharmony_ci if (IS_ERR(kb3886_backlight_device)) 1508c2ecf20Sopenharmony_ci return PTR_ERR(kb3886_backlight_device); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, kb3886_backlight_device); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci kb3886_backlight_device->props.power = FB_BLANK_UNBLANK; 1558c2ecf20Sopenharmony_ci kb3886_backlight_device->props.brightness = machinfo->default_intensity; 1568c2ecf20Sopenharmony_ci backlight_update_status(kb3886_backlight_device); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct platform_driver kb3886bl_driver = { 1628c2ecf20Sopenharmony_ci .probe = kb3886bl_probe, 1638c2ecf20Sopenharmony_ci .driver = { 1648c2ecf20Sopenharmony_ci .name = "kb3886-bl", 1658c2ecf20Sopenharmony_ci .pm = &kb3886bl_pm_ops, 1668c2ecf20Sopenharmony_ci }, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int __init kb3886_init(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (!dmi_check_system(kb3886bl_device_table)) 1728c2ecf20Sopenharmony_ci return -ENODEV; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci platform_add_devices(devices, ARRAY_SIZE(devices)); 1758c2ecf20Sopenharmony_ci return platform_driver_register(&kb3886bl_driver); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void __exit kb3886_exit(void) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci platform_driver_unregister(&kb3886bl_driver); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cimodule_init(kb3886_init); 1848c2ecf20Sopenharmony_cimodule_exit(kb3886_exit); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciMODULE_AUTHOR("Claudio Nieder <private@claudio.ch>"); 1878c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tabletkiosk Sahara Touch-iT Backlight Driver"); 1888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1898c2ecf20Sopenharmony_ciMODULE_ALIAS("dmi:*:svnSDV:pniTouchT201:*"); 190