2 * drivers/leds/leds-mt65xx.c
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file COPYING in the main directory of this archive for
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/delay.h>
15 #include <linux/string.h>
16 #include <linux/ctype.h>
17 #include <linux/leds.h>
18 #include <linux/leds-mt65xx.h>
19 #include <linux/workqueue.h>
20 #include <linux/wakelock.h>
21 #include <linux/slab.h>
22 #include <linux/spinlock.h>
23 /* #include <cust_leds.h> */
24 /* #include <cust_leds_def.h> */
25 #include <mach/mt_pwm.h>
26 /* #include <mach/mt_pwm_hal.h> */
27 /* #include <mach/mt_gpio.h> */
28 #include <mach/upmu_common_sw.h>
29 #include <mach/upmu_hw.h>
30 /* #include <mach/mt_pmic_feature_api.h> */
31 /* #include <mach/mt_boot.h> */
33 /* #include <linux/leds_hal.h> */
38 /****************************************************************************
40 ***************************************************************************/
41 struct cust_mt65xx_led
*bl_setting
= NULL
;
42 /*[PLATFORM]-ADD-BEIGIN by falin.luo 2015.4.17*/
43 /*hall sensor use bl brighrness to detect system status*/
44 #ifdef CONFIG_MTK_HALL
45 unsigned int bl_brightness
= 102;
47 static unsigned int bl_brightness
= 102;
49 /*[PLATFORM]-ADD-END by falin.luo 2015.4.17*/
50 static unsigned int bl_duty
= 21;
51 static unsigned int bl_div
= CLK_DIV1
;
52 static unsigned int bl_frequency
= 32000;
53 static unsigned int div_array
[PWM_DIV_NUM
];
54 struct mt65xx_led_data
*g_leds_data
[MT65XX_LED_TYPE_TOTAL
];
57 /****************************************************************************
59 ***************************************************************************/
60 static int debug_enable_led
= 1;
61 #define LEDS_DRV_DEBUG(format, args...) do { \
62 if (debug_enable_led) \
64 printk(KERN_WARNING format, ##args);\
68 /****************************************************************************
70 ***************************************************************************/
71 #ifndef CONTROL_BL_TEMPERATURE
72 #define CONTROL_BL_TEMPERATURE
75 #define MT_LED_INTERNAL_LEVEL_BIT_CNT 10
77 #ifdef CONTROL_BL_TEMPERATURE
78 int setMaxbrightness(int max_level
, int enable
);
82 /******************************************************************************
83 for DISP backlight High resolution
84 ******************************************************************************/
85 #ifdef LED_INCREASE_LED_LEVEL_MTKPATCH
86 #define LED_INTERNAL_LEVEL_BIT_CNT 10
90 static int mt65xx_led_set_cust(struct cust_mt65xx_led
*cust
, int level
);
92 /****************************************************************************
93 * add API for temperature control
94 ***************************************************************************/
96 #ifdef CONTROL_BL_TEMPERATURE
98 /* define int limit for brightness limitation */
99 static unsigned int limit
= 255;
100 static unsigned int limit_flag
;
101 static unsigned int last_level
;
102 static unsigned int current_level
;
103 static DEFINE_MUTEX(bl_level_limit_mutex
);
104 extern int disp_bls_set_max_backlight(unsigned int level
);
107 /* this API add for control the power and temperature */
108 /* if enabe=1, the value of brightness will smaller than max_level, whatever lightservice transfers to driver */
109 int setMaxbrightness(int max_level
, int enable
)
111 #if !defined(CONFIG_MTK_AAL_SUPPORT)
112 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
113 mutex_lock(&bl_level_limit_mutex
);
117 mutex_unlock(&bl_level_limit_mutex
);
118 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit happen and release lock!!\n"); */
119 /* LEDS_DRV_DEBUG("setMaxbrightness enable:last_level=%d, current_level=%d\n", last_level, current_level); */
120 /* if (limit < last_level){ */
121 if (0 != current_level
) {
122 if (limit
< last_level
) {
124 ("mt65xx_leds_set_cust in setMaxbrightness:value control start! limit=%d\n",
126 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], limit
);
128 /* LEDS_DRV_DEBUG("mt65xx_leds_set_cust in setMaxbrightness:value control start! last_level=%d\n", last_level); */
129 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
],
136 mutex_unlock(&bl_level_limit_mutex
);
137 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit closed and and release lock!!\n"); */
138 /* LEDS_DRV_DEBUG("setMaxbrightness disable:last_level=%d, current_level=%d\n", last_level, current_level); */
140 /* if (last_level != 0){ */
141 if (0 != current_level
) {
142 LEDS_DRV_DEBUG("control temperature close:limit=%d\n", limit
);
143 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], last_level
);
145 /* printk("mt65xx_leds_set_cust in setMaxbrightness:value control close!\n"); */
149 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit_flag = %d, limit=%d, current_level=%d\n",limit_flag, limit, current_level); */
152 LEDS_DRV_DEBUG("setMaxbrightness go through AAL\n");
153 disp_bls_set_max_backlight(max_level
);
154 #endif /* endif CONFIG_MTK_AAL_SUPPORT */
159 /****************************************************************************
161 ***************************************************************************/
162 static void get_div_array(void)
165 unsigned int *temp
= mt_get_div_array();
166 while (i
< PWM_DIV_NUM
) {
167 div_array
[i
] = *temp
++;
168 LEDS_DRV_DEBUG("get_div_array: div_array=%d\n", div_array
[i
]);
173 static int led_set_pwm(int pwm_num
, struct nled_setting
*led
)
176 mt_led_set_pwm(pwm_num
, led
);
180 static int brightness_set_pmic(enum mt65xx_led_pmic pmic_type
, u32 level
, u32 div
)
182 mt_brightness_set_pmic(pmic_type
, level
, div
);
187 static int mt65xx_led_set_cust(struct cust_mt65xx_led
*cust
, int level
)
189 #ifdef CONTROL_BL_TEMPERATURE
190 mutex_lock(&bl_level_limit_mutex
);
191 current_level
= level
;
192 /* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
193 if (0 == limit_flag
) {
195 /* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
197 if (limit
< current_level
) {
199 /* LEDS_DRV_DEBUG("backlight_set_cust: control level=%d\n", level); */
202 mutex_unlock(&bl_level_limit_mutex
);
204 #ifdef LED_INCREASE_LED_LEVEL_MTKPATCH
205 if (MT65XX_LED_MODE_CUST_BLS_PWM
== cust
->mode
) {
206 mt_mt65xx_led_set_cust(cust
,
207 ((((1 << LED_INTERNAL_LEVEL_BIT_CNT
) - 1) * level
+
210 mt_mt65xx_led_set_cust(cust
, level
);
213 mt_mt65xx_led_set_cust(cust
, level
);
219 static void mt65xx_led_set(struct led_classdev
*led_cdev
, enum led_brightness level
)
221 struct mt65xx_led_data
*led_data
= container_of(led_cdev
, struct mt65xx_led_data
, cdev
);
222 if (strcmp(led_data
->cust
.name
, "lcd-backlight") == 0) {
223 #ifdef CONTROL_BL_TEMPERATURE
224 mutex_lock(&bl_level_limit_mutex
);
225 current_level
= level
;
226 /* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
227 if (0 == limit_flag
) {
229 /* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
231 if (limit
< current_level
) {
233 LEDS_DRV_DEBUG("backlight_set_cust: control level=%d\n", level
);
236 mutex_unlock(&bl_level_limit_mutex
);
239 mt_mt65xx_led_set(led_cdev
, level
);
242 static int mt65xx_blink_set(struct led_classdev
*led_cdev
,
243 unsigned long *delay_on
, unsigned long *delay_off
)
245 if (mt_mt65xx_blink_set(led_cdev
, delay_on
, delay_off
)) {
252 /****************************************************************************
254 ***************************************************************************/
255 int mt65xx_leds_brightness_set(enum mt65xx_led_type type
, enum led_brightness level
)
257 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
259 LEDS_DRV_DEBUG("[LED]#%d:%d\n", type
, level
);
261 if (type
< 0 || type
>= MT65XX_LED_TYPE_TOTAL
)
264 if (level
> LED_FULL
)
269 return mt65xx_led_set_cust(&cust_led_list
[type
], level
);
272 EXPORT_SYMBOL(mt65xx_leds_brightness_set
);
275 /****************************************************************************
277 ***************************************************************************/
278 int backlight_brightness_set(int level
)
280 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
282 if (level
> ((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT
) - 1) )
283 level
= ((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT
) - 1);
287 if(MT65XX_LED_MODE_CUST_BLS_PWM
== cust_led_list
[MT65XX_LED_TYPE_LCD
].mode
)
289 #ifdef CONTROL_BL_TEMPERATURE
290 mutex_lock(&bl_level_limit_mutex
);
291 current_level
= (level
>> (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)); // 8 bits
293 last_level
= current_level
;
295 if(limit
< current_level
){
296 // extend 8-bit limit to 10 bits
297 level
= (limit
<< (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)) | (limit
>> (16 - MT_LED_INTERNAL_LEVEL_BIT_CNT
));
300 mutex_unlock(&bl_level_limit_mutex
);
303 return mt_mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], level
);
307 return mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
],( level
>> (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)) );
312 EXPORT_SYMBOL(backlight_brightness_set
);
315 static ssize_t
show_duty(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
317 LEDS_DRV_DEBUG("[LED]get backlight duty value is:%d\n", bl_duty
);
318 return sprintf(buf
, "%u\n", bl_duty
);
321 static ssize_t
store_duty(struct device
*dev
, struct device_attribute
*attr
, const char *buf
,
325 unsigned int level
= 0;
327 bl_div
= mt_get_bl_div();
328 LEDS_DRV_DEBUG("set backlight duty start\n");
329 level
= simple_strtoul(buf
, &pvalue
, 10);
330 count
= pvalue
- buf
;
331 if (*pvalue
&& isspace(*pvalue
))
336 if (bl_setting
->mode
== MT65XX_LED_MODE_PMIC
) {
338 if ((level
>= 0) && (level
<= 15)) {
339 mt_brightness_set_pmic_duty_store((level
* 17), bl_div
);
342 ("duty value is error, please select vaule from [0-15]!\n");
347 else if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
349 mt_led_pwm_disable(bl_setting
->data
);
350 } else if (level
<= 64) {
351 mt_backlight_set_pwm_duty(bl_setting
->data
, level
, bl_div
,
352 &bl_setting
->config_data
);
356 mt_set_bl_duty(level
);
364 static DEVICE_ATTR(duty
, 0664, show_duty
, store_duty
);
367 static ssize_t
show_div(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
369 bl_div
= mt_get_bl_div();
370 LEDS_DRV_DEBUG("get backlight div value is:%d\n", bl_div
);
371 return sprintf(buf
, "%u\n", bl_div
);
374 static ssize_t
store_div(struct device
*dev
, struct device_attribute
*attr
, const char *buf
,
378 unsigned int div
= 0;
381 bl_duty
= mt_get_bl_duty();
382 LEDS_DRV_DEBUG("set backlight div start\n");
383 div
= simple_strtoul(buf
, &pvalue
, 10);
384 count
= pvalue
- buf
;
386 if (*pvalue
&& isspace(*pvalue
))
390 if (div
< 0 || (div
> 7)) {
391 LEDS_DRV_DEBUG("set backlight div parameter error: %d[div:0~7]\n", div
);
395 if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
396 LEDS_DRV_DEBUG("set PWM backlight div OK: div=%d, duty=%d\n", div
, bl_duty
);
397 mt_backlight_set_pwm_div(bl_setting
->data
, bl_duty
, div
,
398 &bl_setting
->config_data
);
401 else if (bl_setting
->mode
== MT65XX_LED_MODE_CUST_LCM
) {
402 bl_brightness
= mt_get_bl_brightness();
403 LEDS_DRV_DEBUG("set cust backlight div OK: div=%d, brightness=%d\n", div
,
405 ((cust_brightness_set
) (bl_setting
->data
)) (bl_brightness
, div
);
414 static DEVICE_ATTR(div
, 0664, show_div
, store_div
);
417 static ssize_t
show_frequency(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
419 bl_div
= mt_get_bl_div();
420 bl_frequency
= mt_get_bl_frequency();
422 if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
423 mt_set_bl_frequency(32000 / div_array
[bl_div
]);
424 } else if (bl_setting
->mode
== MT65XX_LED_MODE_CUST_LCM
) {
425 /* mtkfb_get_backlight_pwm(bl_div, &bl_frequency); */
426 mt_backlight_get_pwm_fsel(bl_div
, &bl_frequency
);
429 LEDS_DRV_DEBUG("[LED]get backlight PWM frequency value is:%d\n", bl_frequency
);
431 return sprintf(buf
, "%u\n", bl_frequency
);
434 static DEVICE_ATTR(frequency
, 0444, show_frequency
, NULL
);
438 static ssize_t
store_pwm_register(struct device
*dev
, struct device_attribute
*attr
,
439 const char *buf
, size_t size
)
442 unsigned int reg_value
= 0;
443 unsigned int reg_address
= 0;
444 if (buf
!= NULL
&& size
!= 0) {
445 //LEDS_DRV_DEBUG("store_pwm_register: size:%d,address:0x%s\n", size, buf);
446 reg_address
= simple_strtoul(buf
, &pvalue
, 16);
448 if (*pvalue
&& (*pvalue
== '#')) {
449 reg_value
= simple_strtoul((pvalue
+ 1), NULL
, 16);
450 LEDS_DRV_DEBUG("set pwm register:[0x%x]= 0x%x\n", reg_address
, reg_value
);
451 /* OUTREG32(reg_address,reg_value); */
452 mt_store_pwm_register(reg_address
, reg_value
);
454 } else if (*pvalue
&& (*pvalue
== '@')) {
455 LEDS_DRV_DEBUG("get pwm register:[0x%x]=0x%x\n", reg_address
,
456 mt_show_pwm_register(reg_address
));
463 static ssize_t
show_pwm_register(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
468 static DEVICE_ATTR(pwm_register
, 0664, show_pwm_register
, store_pwm_register
);
471 /****************************************************************************
473 ***************************************************************************/
474 static int __init
mt65xx_leds_probe(struct platform_device
*pdev
)
478 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
479 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
481 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
482 if (cust_led_list
[i
].mode
== MT65XX_LED_MODE_NONE
) {
483 g_leds_data
[i
] = NULL
;
487 g_leds_data
[i
] = kzalloc(sizeof(struct mt65xx_led_data
), GFP_KERNEL
);
488 if (!g_leds_data
[i
]) {
493 g_leds_data
[i
]->cust
.mode
= cust_led_list
[i
].mode
;
494 g_leds_data
[i
]->cust
.data
= cust_led_list
[i
].data
;
495 g_leds_data
[i
]->cust
.name
= cust_led_list
[i
].name
;
497 g_leds_data
[i
]->cdev
.name
= cust_led_list
[i
].name
;
498 g_leds_data
[i
]->cust
.config_data
= cust_led_list
[i
].config_data
; /* bei add */
500 g_leds_data
[i
]->cdev
.brightness_set
= mt65xx_led_set
;
501 g_leds_data
[i
]->cdev
.blink_set
= mt65xx_blink_set
;
503 INIT_WORK(&g_leds_data
[i
]->work
, mt_mt65xx_led_work
);
505 ret
= led_classdev_register(&pdev
->dev
, &g_leds_data
[i
]->cdev
);
507 if (strcmp(g_leds_data
[i
]->cdev
.name
, "lcd-backlight") == 0) {
508 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_duty
);
510 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
513 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_div
);
515 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
518 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_frequency
);
520 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
523 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_pwm_register
);
525 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
527 bl_setting
= &g_leds_data
[i
]->cust
;
534 #ifdef CONTROL_BL_TEMPERATURE
541 ("[LED]led probe last_level = %d, limit = %d, limit_flag = %d, current_level = %d\n",
542 last_level
, limit
, limit_flag
, current_level
);
550 for (i
= i
- 1; i
>= 0; i
--) {
553 led_classdev_unregister(&g_leds_data
[i
]->cdev
);
554 cancel_work_sync(&g_leds_data
[i
]->work
);
555 kfree(g_leds_data
[i
]);
556 g_leds_data
[i
] = NULL
;
563 static int mt65xx_leds_remove(struct platform_device
*pdev
)
566 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
569 led_classdev_unregister(&g_leds_data
[i
]->cdev
);
570 cancel_work_sync(&g_leds_data
[i
]->work
);
571 kfree(g_leds_data
[i
]);
572 g_leds_data
[i
] = NULL
;
579 static int mt65xx_leds_suspend(struct platform_device *pdev, pm_message_t state)
585 static void mt65xx_leds_shutdown(struct platform_device
*pdev
)
588 struct nled_setting led_tmp_setting
= { NLED_OFF
, 0, 0 };
590 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
591 LEDS_DRV_DEBUG("[LED]mt65xx_leds_shutdown: turn off backlight\n");
593 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
596 switch (g_leds_data
[i
]->cust
.mode
) {
598 case MT65XX_LED_MODE_PWM
:
599 if (strcmp(g_leds_data
[i
]->cust
.name
, "lcd-backlight") == 0) {
600 /* mt_set_pwm_disable(g_leds_data[i]->cust.data); */
601 /* mt_pwm_power_off (g_leds_data[i]->cust.data); */
602 mt_led_pwm_disable(g_leds_data
[i
]->cust
.data
);
604 led_set_pwm(g_leds_data
[i
]->cust
.data
, &led_tmp_setting
);
608 /* case MT65XX_LED_MODE_GPIO: */
609 /* brightness_set_gpio(g_leds_data[i]->cust.data, 0); */
612 case MT65XX_LED_MODE_PMIC
:
613 brightness_set_pmic(g_leds_data
[i
]->cust
.data
, 0, 0);
615 case MT65XX_LED_MODE_CUST_LCM
:
616 LEDS_DRV_DEBUG("[LED]backlight control through LCM!!1\n");
617 ((cust_brightness_set
) (g_leds_data
[i
]->cust
.data
)) (0, bl_div
);
619 case MT65XX_LED_MODE_CUST_BLS_PWM
:
620 LEDS_DRV_DEBUG("[LED]backlight control through BLS!!1\n");
621 ((cust_set_brightness
) (g_leds_data
[i
]->cust
.data
)) (0);
623 case MT65XX_LED_MODE_NONE
:
631 static struct platform_driver mt65xx_leds_driver
= {
633 .name
= "leds-mt65xx",
634 .owner
= THIS_MODULE
,
636 .probe
= mt65xx_leds_probe
,
637 .remove
= mt65xx_leds_remove
,
638 /* .suspend = mt65xx_leds_suspend, */
639 .shutdown
= mt65xx_leds_shutdown
,
643 static struct platform_device mt65xx_leds_device
= {
644 .name
= "leds-mt65xx",
650 static int __init
mt65xx_leds_init(void)
654 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
657 ret
= platform_device_register(&mt65xx_leds_device
);
659 printk("[LED]mt65xx_leds_init:dev:E%d\n", ret
);
661 ret
= platform_driver_register(&mt65xx_leds_driver
);
664 LEDS_DRV_DEBUG("[LED]mt65xx_leds_init:drv:E%d\n", ret
);
665 /* platform_device_unregister(&mt65xx_leds_device); */
669 mt_leds_wake_lock_init();
674 static void __exit
mt65xx_leds_exit(void)
676 platform_driver_unregister(&mt65xx_leds_driver
);
677 /* platform_device_unregister(&mt65xx_leds_device); */
680 module_param(debug_enable_led
, int, 0644);
682 module_init(mt65xx_leds_init
);
683 module_exit(mt65xx_leds_exit
);
685 MODULE_AUTHOR("MediaTek Inc.");
686 MODULE_DESCRIPTION("LED driver for MediaTek MT65xx chip");
687 MODULE_LICENSE("GPL");
688 MODULE_ALIAS("leds-mt65xx");