diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/leds/leds-sprd-kpled-2723.c | |
Diffstat (limited to 'drivers/leds/leds-sprd-kpled-2723.c')
| -rw-r--r-- | drivers/leds/leds-sprd-kpled-2723.c | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/drivers/leds/leds-sprd-kpled-2723.c b/drivers/leds/leds-sprd-kpled-2723.c new file mode 100644 index 00000000..31b0ca49 --- /dev/null +++ b/drivers/leds/leds-sprd-kpled-2723.c @@ -0,0 +1,347 @@ +/* +* Copyright (C) 2012 Spreadtrum Communications Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <soc/sprd/adi.h> +#include <soc/sprd/adc.h> +#include <soc/sprd/sci_glb_regs.h> +#ifdef CONFIG_HAS_EARLYSUSPEND +#include <linux/earlysuspend.h> +#endif +#include <linux/of.h> +#include <linux/leds-sprd-kpled-2723.h> +//#define SPRD_KPLED_DBG +#ifdef SPRD_KPLED_DBG +#define ENTER printk(KERN_INFO "[SPRD_KPLED_DBG] func: %s line: %04d\n", __func__, __LINE__); +#define PRINT_DBG(x...) printk(KERN_INFO "[SPRD_KPLED_DBG] " x) +#define PRINT_INFO(x...) printk(KERN_INFO "[SPRD_KPLED_INFO] " x) +#define PRINT_WARN(x...) printk(KERN_INFO "[SPRD_KPLED_WARN] " x) +#define PRINT_ERR(format,x...) printk(KERN_ERR "[SPRD_KPLED_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x) +#else +#define ENTER +#define PRINT_DBG(x...) +#define PRINT_INFO(x...) printk(KERN_INFO "[SPRD_KPLED_INFO] " x) +#define PRINT_WARN(x...) printk(KERN_INFO "[SPRD_KPLED_WARN] " x) +#define PRINT_ERR(format,x...) printk(KERN_ERR "[SPRD_KPLED_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x) +#endif +#if 0 +#define SPRD_ANA_BASE (SPRD_MISC_BASE + 0x8800) + +#define ANA_REG_BASE SPRD_ANA_BASE + +#define ANA_LED_CTRL (ANA_REG_BASE + 0xF4) +#endif +#define KPLED_CTL (ANA_REGS_GLB_BASE + 0xf4) + +#define KPLED_V_SHIFT 12 +#define KPLED_V_MSK (0x0F << KPLED_V_SHIFT) +#define LDO_KPLED_PD (1 << 8) +#define LDO_KPLED_V_SHIFT (0x00) +#define LDO_KPLED_V_MSK (0xff) +#define KPLED_PD (1 << 11) +#define KPLED_PULLDOWN_EN (1 << 10) + +/* sprd keypad backlight */ +struct sprd_kpled { + struct platform_device *dev; + struct mutex mutex; + struct work_struct work; + spinlock_t value_lock; + enum led_brightness value; + struct led_classdev cdev; + int enabled; + int brightness_max; + int brightness_min; + int run_mode; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + +}; + +#define to_sprd_led(led_cdev) \ + container_of(led_cdev, struct sprd_kpled, cdev) + +static inline uint32_t kpled_read(uint32_t reg) +{ + return sci_adi_read(reg); +} + +static void sprd_kpled_set_brightness(struct sprd_kpled *led) +{ + unsigned long brightness = led->value; + unsigned long brightness_level; + brightness_level = brightness; + + PRINT_INFO("sprd_kpled_set_brightness:led->run_mode = %d\n",led->run_mode); + if(brightness_level > 255) + brightness_level = 255; + + /*brightness steps = 16*/ + if(led->run_mode == 1){ + sci_adi_write(KPLED_CTL, ((brightness_level << KPLED_V_SHIFT) & KPLED_V_MSK), KPLED_V_MSK); + } + else + sci_adi_write(KPLED_CTL, ((brightness_level << LDO_KPLED_V_SHIFT) & LDO_KPLED_V_MSK), LDO_KPLED_V_MSK); + PRINT_INFO("reg:0x%08X set_val:0x%08X brightness:%ld brightness_level:%ld(0~15)\n", \ + KPLED_CTL, kpled_read(KPLED_CTL), brightness, brightness_level); +} + +static void sprd_kpled_enable(struct sprd_kpled *led) +{ + if(led->run_mode == 1){ + sci_adi_clr(KPLED_CTL, KPLED_PD); + } + else{ + sci_adi_set(KPLED_CTL, KPLED_PULLDOWN_EN); + sci_adi_clr(KPLED_CTL, LDO_KPLED_PD); + } + PRINT_INFO("sprd_kpled_enable\n"); + sprd_kpled_set_brightness(led); + led->enabled = 1; +} + +static void sprd_kpled_disable(struct sprd_kpled *led) +{ + if(led->run_mode == 1){ + sci_adi_set(KPLED_CTL, KPLED_PD); + } + else{ + sci_adi_set(KPLED_CTL, KPLED_PULLDOWN_EN); + sci_adi_set(KPLED_CTL, LDO_KPLED_PD); + } + PRINT_INFO("sprd_kpled_disable\n"); + led->enabled = 0; +} + +static void sprd_kpled_work(struct work_struct *work) +{ + struct sprd_kpled *led = container_of(work, struct sprd_kpled, work); + unsigned long flags; + + mutex_lock(&led->mutex); + spin_lock_irqsave(&led->value_lock, flags); + if (led->value == LED_OFF) { + spin_unlock_irqrestore(&led->value_lock, flags); + sprd_kpled_disable(led); + goto out; + } + spin_unlock_irqrestore(&led->value_lock, flags); + sprd_kpled_enable(led); +out: + mutex_unlock(&led->mutex); +} + +static void sprd_kpled_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct sprd_kpled *led = to_sprd_led(led_cdev); + unsigned long flags; + + PRINT_INFO("sprd_kpled_set!\n"); + spin_lock_irqsave(&led->value_lock, flags); + led->value = value; + spin_unlock_irqrestore(&led->value_lock, flags); + + schedule_work(&led->work); +} + +static void sprd_kpled_shutdown(struct platform_device *dev) +{ + struct sprd_kpled *led = platform_get_drvdata(dev); + + mutex_lock(&led->mutex); + sprd_kpled_disable(led); + mutex_unlock(&led->mutex); +} + +#ifdef CONFIG_EARLYSUSPEND +static void sprd_kpled_early_suspend(struct early_suspend *es) +{ + struct sprd_kpled *led = container_of(es, struct sprd_kpled, early_suspend); + PRINT_INFO("sprd_kpled_early_suspend\n"); +} + +static void sprd_kpled_late_resume(struct early_suspend *es) +{ + struct sprd_kpled *led = container_of(es, struct sprd_kpled, early_suspend); + PRINT_INFO("sprd_kpled_late_resume\n"); +} +#endif + +#ifdef CONFIG_OF +static struct sprd_kpled_2723_platform_data *sprd_kpled_2723_parse_dt(struct platform_device *pdev) +{ + int ret; + struct device_node *np = pdev->dev.of_node; + struct sprd_kpled_2723_platform_data *pdata = NULL; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(pdev, "sprd_kpled Could not allocate pdata"); + return NULL; + } + ret = of_property_read_u32(np, "brightness_max", &pdata->brightness_max); + if(ret){ + dev_err(pdev, "fail to get pdata->brightness_max\n"); + goto fail; + } + ret = of_property_read_u32(np, "brightness_min", &pdata->brightness_min); + if(ret){ + dev_err(pdev, "fail to get pdata->brightness_min\n"); + goto fail; + } + ret = of_property_read_u32(np, "run_mode", &pdata->run_mode); + if(ret){ + dev_err(pdev, "fail to get pdata->run_mode\n"); + goto fail; + } + + return pdata; +fail: + kfree(pdata); + return NULL; +} +#endif + +static int sprd_kpled_probe(struct platform_device *dev) +{ + struct sprd_kpled *led; + int ret; + struct sprd_kpled_2723_platform_data *pdata = NULL; + + #ifdef CONFIG_OF + + struct device_node *np = dev->dev.of_node; + + if(np) { + pdata = sprd_kpled_2723_parse_dt(dev); + if (pdata == NULL) { + PRINT_ERR("get dts data failed!\n"); + return -ENODEV; + } + } else { + PRINT_ERR("dev.of_node is NULL!\n"); + return -ENODEV; + } + #else + pdata = dev->dev.platform_data; + if (!pdata){ + PRINT_ERR("No kpled_platform data!\n"); + return -ENODEV; + } + #endif + + led = kzalloc(sizeof(struct sprd_kpled), GFP_KERNEL); + if (led == NULL) { + dev_err(&dev->dev, "No memory for device\n"); + return -ENOMEM; + } + + led->cdev.brightness_set = sprd_kpled_set; + //led->cdev.default_trigger = "heartbeat"; + led->cdev.default_trigger = "none"; + led->cdev.name = "keyboard-backlight"; + led->cdev.brightness_get = NULL; + led->cdev.flags |= LED_CORE_SUSPENDRESUME; + led->enabled = 0; + led->brightness_max = pdata->brightness_max; + led->brightness_min = pdata->brightness_min; + led->run_mode = pdata->run_mode; + PRINT_INFO("led->run_mode = %d\n",led->run_mode); + PRINT_INFO("led->brightness_max = %d\n",led->brightness_max); + + spin_lock_init(&led->value_lock); + mutex_init(&led->mutex); + INIT_WORK(&led->work, sprd_kpled_work); + led->value = LED_OFF; + platform_set_drvdata(dev, led); + + /* register our new led device */ + + ret = led_classdev_register(&dev->dev, &led->cdev); + if (ret < 0) { + dev_err(&dev->dev, "led_classdev_register failed\n"); + kfree(led); + return ret; + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + led->early_suspend.suspend = sprd_kpled_early_suspend; + led->early_suspend.resume = sprd_kpled_late_resume; + led->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; + register_early_suspend(&led->early_suspend); +#endif + + sprd_kpled_disable(led);//disabled by default + + return 0; +} + +static int sprd_kpled_remove(struct platform_device *dev) +{ + struct sprd_kpled *led = platform_get_drvdata(dev); + + led_classdev_unregister(&led->cdev); + flush_scheduled_work(); + led->value = LED_OFF; + led->enabled = 1; + sprd_kpled_disable(led); + kfree(led); + + return 0; +} + +static const struct of_device_id kpled_2723_of_match[] = { + { .compatible = "sprd,sprd-kpled-2723", }, + { } +}; + +static struct platform_driver sprd_kpled_2723_driver = { + .driver = { + .name = "sprd-kpled-2723", + .owner = THIS_MODULE, + .of_match_table = kpled_2723_of_match, + }, + .probe = sprd_kpled_probe, + .remove = sprd_kpled_remove, + .shutdown = sprd_kpled_shutdown, +}; + +static int __init sprd_kpled_2723_init(void) +{ + PRINT_INFO("Locate in sprd_kpled_2723_init!\n"); + return platform_driver_register(&sprd_kpled_2723_driver); +} + +static void sprd_kpled_2723_exit(void) +{ + platform_driver_unregister(&sprd_kpled_2723_driver); +} + +module_init(sprd_kpled_2723_init); +module_exit(sprd_kpled_2723_exit); + +MODULE_AUTHOR("ya huang <ya.huang@spreadtrum.com>"); +MODULE_DESCRIPTION("Sprd Keyboard backlight driver for 2723s"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:sprd_kpled_2723"); + + + |
