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 static unsigned int bl_brightness
= 102;
43 static unsigned int bl_duty
= 21;
44 static unsigned int bl_div
= CLK_DIV1
;
45 static unsigned int bl_frequency
= 32000;
46 static unsigned int div_array
[PWM_DIV_NUM
];
47 struct mt65xx_led_data
*g_leds_data
[MT65XX_LED_TYPE_TOTAL
];
50 /****************************************************************************
52 ***************************************************************************/
53 static int debug_enable_led
= 1;
54 #define LEDS_DRV_DEBUG(format, args...) do { \
55 if (debug_enable_led) \
57 printk(KERN_WARNING format, ##args);\
61 /****************************************************************************
63 ***************************************************************************/
64 #ifndef CONTROL_BL_TEMPERATURE
65 #define CONTROL_BL_TEMPERATURE
68 #define MT_LED_INTERNAL_LEVEL_BIT_CNT 10
70 #ifdef CONTROL_BL_TEMPERATURE
71 int setMaxbrightness(int max_level
, int enable
);
75 /******************************************************************************
76 for DISP backlight High resolution
77 ******************************************************************************/
78 #ifdef LED_INCREASE_LED_LEVEL_MTKPATCH
79 #define LED_INTERNAL_LEVEL_BIT_CNT 10
83 static int mt65xx_led_set_cust(struct cust_mt65xx_led
*cust
, int level
);
85 /****************************************************************************
86 * add API for temperature control
87 ***************************************************************************/
89 #ifdef CONTROL_BL_TEMPERATURE
91 /* define int limit for brightness limitation */
92 static unsigned int limit
= 255;
93 static unsigned int limit_flag
;
94 static unsigned int last_level
;
95 static unsigned int current_level
;
96 static DEFINE_MUTEX(bl_level_limit_mutex
);
97 extern int disp_bls_set_max_backlight(unsigned int level
);
100 /* this API add for control the power and temperature */
101 /* if enabe=1, the value of brightness will smaller than max_level, whatever lightservice transfers to driver */
102 int setMaxbrightness(int max_level
, int enable
)
104 #if !defined(CONFIG_MTK_AAL_SUPPORT)
105 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
106 mutex_lock(&bl_level_limit_mutex
);
110 mutex_unlock(&bl_level_limit_mutex
);
111 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit happen and release lock!!\n"); */
112 /* LEDS_DRV_DEBUG("setMaxbrightness enable:last_level=%d, current_level=%d\n", last_level, current_level); */
113 /* if (limit < last_level){ */
114 if (0 != current_level
) {
115 if (limit
< last_level
) {
117 ("mt65xx_leds_set_cust in setMaxbrightness:value control start! limit=%d\n",
119 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], limit
);
121 /* LEDS_DRV_DEBUG("mt65xx_leds_set_cust in setMaxbrightness:value control start! last_level=%d\n", last_level); */
122 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
],
129 mutex_unlock(&bl_level_limit_mutex
);
130 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit closed and and release lock!!\n"); */
131 /* LEDS_DRV_DEBUG("setMaxbrightness disable:last_level=%d, current_level=%d\n", last_level, current_level); */
133 /* if (last_level != 0){ */
134 if (0 != current_level
) {
135 LEDS_DRV_DEBUG("control temperature close:limit=%d\n", limit
);
136 mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], last_level
);
138 /* printk("mt65xx_leds_set_cust in setMaxbrightness:value control close!\n"); */
142 /* LEDS_DRV_DEBUG("[LED] setMaxbrightness limit_flag = %d, limit=%d, current_level=%d\n",limit_flag, limit, current_level); */
145 LEDS_DRV_DEBUG("setMaxbrightness go through AAL\n");
146 disp_bls_set_max_backlight(max_level
);
147 #endif /* endif CONFIG_MTK_AAL_SUPPORT */
152 /****************************************************************************
154 ***************************************************************************/
155 static void get_div_array(void)
158 unsigned int *temp
= mt_get_div_array();
159 while (i
< PWM_DIV_NUM
) {
160 div_array
[i
] = *temp
++;
161 LEDS_DRV_DEBUG("get_div_array: div_array=%d\n", div_array
[i
]);
166 static int led_set_pwm(int pwm_num
, struct nled_setting
*led
)
169 mt_led_set_pwm(pwm_num
, led
);
173 static int brightness_set_pmic(enum mt65xx_led_pmic pmic_type
, u32 level
, u32 div
)
175 mt_brightness_set_pmic(pmic_type
, level
, div
);
180 static int mt65xx_led_set_cust(struct cust_mt65xx_led
*cust
, int level
)
182 #ifdef CONTROL_BL_TEMPERATURE
183 mutex_lock(&bl_level_limit_mutex
);
184 current_level
= level
;
185 /* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
186 if (0 == limit_flag
) {
188 /* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
190 if (limit
< current_level
) {
192 /* LEDS_DRV_DEBUG("backlight_set_cust: control level=%d\n", level); */
195 mutex_unlock(&bl_level_limit_mutex
);
197 #ifdef LED_INCREASE_LED_LEVEL_MTKPATCH
198 if (MT65XX_LED_MODE_CUST_BLS_PWM
== cust
->mode
) {
199 mt_mt65xx_led_set_cust(cust
,
200 ((((1 << LED_INTERNAL_LEVEL_BIT_CNT
) - 1) * level
+
203 mt_mt65xx_led_set_cust(cust
, level
);
206 mt_mt65xx_led_set_cust(cust
, level
);
212 static void mt65xx_led_set(struct led_classdev
*led_cdev
, enum led_brightness level
)
214 struct mt65xx_led_data
*led_data
= container_of(led_cdev
, struct mt65xx_led_data
, cdev
);
215 if (strcmp(led_data
->cust
.name
, "lcd-backlight") == 0) {
216 #ifdef CONTROL_BL_TEMPERATURE
217 mutex_lock(&bl_level_limit_mutex
);
218 current_level
= level
;
219 /* LEDS_DRV_DEBUG("brightness_set_cust:current_level=%d\n", current_level); */
220 if (0 == limit_flag
) {
222 /* LEDS_DRV_DEBUG("brightness_set_cust:last_level=%d\n", last_level); */
224 if (limit
< current_level
) {
226 LEDS_DRV_DEBUG("backlight_set_cust: control level=%d\n", level
);
229 mutex_unlock(&bl_level_limit_mutex
);
232 mt_mt65xx_led_set(led_cdev
, level
);
235 static int mt65xx_blink_set(struct led_classdev
*led_cdev
,
236 unsigned long *delay_on
, unsigned long *delay_off
)
238 if (mt_mt65xx_blink_set(led_cdev
, delay_on
, delay_off
)) {
245 /****************************************************************************
247 ***************************************************************************/
248 int mt65xx_leds_brightness_set(enum mt65xx_led_type type
, enum led_brightness level
)
250 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
252 LEDS_DRV_DEBUG("[LED]#%d:%d\n", type
, level
);
254 if (type
< 0 || type
>= MT65XX_LED_TYPE_TOTAL
)
257 if (level
> LED_FULL
)
262 return mt65xx_led_set_cust(&cust_led_list
[type
], level
);
265 EXPORT_SYMBOL(mt65xx_leds_brightness_set
);
268 /****************************************************************************
270 ***************************************************************************/
271 int backlight_brightness_set(int level
)
273 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
275 if (level
> ((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT
) - 1) )
276 level
= ((1 << MT_LED_INTERNAL_LEVEL_BIT_CNT
) - 1);
280 if(MT65XX_LED_MODE_CUST_BLS_PWM
== cust_led_list
[MT65XX_LED_TYPE_LCD
].mode
)
282 #ifdef CONTROL_BL_TEMPERATURE
283 mutex_lock(&bl_level_limit_mutex
);
284 current_level
= (level
>> (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)); // 8 bits
286 last_level
= current_level
;
288 if(limit
< current_level
){
289 // extend 8-bit limit to 10 bits
290 level
= (limit
<< (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)) | (limit
>> (16 - MT_LED_INTERNAL_LEVEL_BIT_CNT
));
293 mutex_unlock(&bl_level_limit_mutex
);
296 return mt_mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
], level
);
300 return mt65xx_led_set_cust(&cust_led_list
[MT65XX_LED_TYPE_LCD
],( level
>> (MT_LED_INTERNAL_LEVEL_BIT_CNT
- 8)) );
305 EXPORT_SYMBOL(backlight_brightness_set
);
308 static ssize_t
show_duty(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
310 LEDS_DRV_DEBUG("[LED]get backlight duty value is:%d\n", bl_duty
);
311 return sprintf(buf
, "%u\n", bl_duty
);
314 static ssize_t
store_duty(struct device
*dev
, struct device_attribute
*attr
, const char *buf
,
318 unsigned int level
= 0;
320 bl_div
= mt_get_bl_div();
321 LEDS_DRV_DEBUG("set backlight duty start\n");
322 level
= simple_strtoul(buf
, &pvalue
, 10);
323 count
= pvalue
- buf
;
324 if (*pvalue
&& isspace(*pvalue
))
329 if (bl_setting
->mode
== MT65XX_LED_MODE_PMIC
) {
331 if ((level
>= 0) && (level
<= 15)) {
332 mt_brightness_set_pmic_duty_store((level
* 17), bl_div
);
335 ("duty value is error, please select vaule from [0-15]!\n");
340 else if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
342 mt_led_pwm_disable(bl_setting
->data
);
343 } else if (level
<= 64) {
344 mt_backlight_set_pwm_duty(bl_setting
->data
, level
, bl_div
,
345 &bl_setting
->config_data
);
349 mt_set_bl_duty(level
);
357 static DEVICE_ATTR(duty
, 0664, show_duty
, store_duty
);
360 static ssize_t
show_div(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
362 bl_div
= mt_get_bl_div();
363 LEDS_DRV_DEBUG("get backlight div value is:%d\n", bl_div
);
364 return sprintf(buf
, "%u\n", bl_div
);
367 static ssize_t
store_div(struct device
*dev
, struct device_attribute
*attr
, const char *buf
,
371 unsigned int div
= 0;
374 bl_duty
= mt_get_bl_duty();
375 LEDS_DRV_DEBUG("set backlight div start\n");
376 div
= simple_strtoul(buf
, &pvalue
, 10);
377 count
= pvalue
- buf
;
379 if (*pvalue
&& isspace(*pvalue
))
383 if (div
< 0 || (div
> 7)) {
384 LEDS_DRV_DEBUG("set backlight div parameter error: %d[div:0~7]\n", div
);
388 if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
389 LEDS_DRV_DEBUG("set PWM backlight div OK: div=%d, duty=%d\n", div
, bl_duty
);
390 mt_backlight_set_pwm_div(bl_setting
->data
, bl_duty
, div
,
391 &bl_setting
->config_data
);
394 else if (bl_setting
->mode
== MT65XX_LED_MODE_CUST_LCM
) {
395 bl_brightness
= mt_get_bl_brightness();
396 LEDS_DRV_DEBUG("set cust backlight div OK: div=%d, brightness=%d\n", div
,
398 ((cust_brightness_set
) (bl_setting
->data
)) (bl_brightness
, div
);
407 static DEVICE_ATTR(div
, 0664, show_div
, store_div
);
410 static ssize_t
show_frequency(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
412 bl_div
= mt_get_bl_div();
413 bl_frequency
= mt_get_bl_frequency();
415 if (bl_setting
->mode
== MT65XX_LED_MODE_PWM
) {
416 mt_set_bl_frequency(32000 / div_array
[bl_div
]);
417 } else if (bl_setting
->mode
== MT65XX_LED_MODE_CUST_LCM
) {
418 /* mtkfb_get_backlight_pwm(bl_div, &bl_frequency); */
419 mt_backlight_get_pwm_fsel(bl_div
, &bl_frequency
);
422 LEDS_DRV_DEBUG("[LED]get backlight PWM frequency value is:%d\n", bl_frequency
);
424 return sprintf(buf
, "%u\n", bl_frequency
);
427 static DEVICE_ATTR(frequency
, 0444, show_frequency
, NULL
);
431 static ssize_t
store_pwm_register(struct device
*dev
, struct device_attribute
*attr
,
432 const char *buf
, size_t size
)
435 unsigned int reg_value
= 0;
436 unsigned int reg_address
= 0;
437 if (buf
!= NULL
&& size
!= 0) {
438 //LEDS_DRV_DEBUG("store_pwm_register: size:%d,address:0x%s\n", size, buf);
439 reg_address
= simple_strtoul(buf
, &pvalue
, 16);
441 if (*pvalue
&& (*pvalue
== '#')) {
442 reg_value
= simple_strtoul((pvalue
+ 1), NULL
, 16);
443 LEDS_DRV_DEBUG("set pwm register:[0x%x]= 0x%x\n", reg_address
, reg_value
);
444 /* OUTREG32(reg_address,reg_value); */
445 mt_store_pwm_register(reg_address
, reg_value
);
447 } else if (*pvalue
&& (*pvalue
== '@')) {
448 LEDS_DRV_DEBUG("get pwm register:[0x%x]=0x%x\n", reg_address
,
449 mt_show_pwm_register(reg_address
));
456 static ssize_t
show_pwm_register(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
461 static DEVICE_ATTR(pwm_register
, 0664, show_pwm_register
, store_pwm_register
);
464 /****************************************************************************
466 ***************************************************************************/
467 static int __init
mt65xx_leds_probe(struct platform_device
*pdev
)
471 struct cust_mt65xx_led
*cust_led_list
= mt_get_cust_led_list();
472 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
474 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
475 if (cust_led_list
[i
].mode
== MT65XX_LED_MODE_NONE
) {
476 g_leds_data
[i
] = NULL
;
480 g_leds_data
[i
] = kzalloc(sizeof(struct mt65xx_led_data
), GFP_KERNEL
);
481 if (!g_leds_data
[i
]) {
486 g_leds_data
[i
]->cust
.mode
= cust_led_list
[i
].mode
;
487 g_leds_data
[i
]->cust
.data
= cust_led_list
[i
].data
;
488 g_leds_data
[i
]->cust
.name
= cust_led_list
[i
].name
;
490 g_leds_data
[i
]->cdev
.name
= cust_led_list
[i
].name
;
491 g_leds_data
[i
]->cust
.config_data
= cust_led_list
[i
].config_data
; /* bei add */
493 g_leds_data
[i
]->cdev
.brightness_set
= mt65xx_led_set
;
494 g_leds_data
[i
]->cdev
.blink_set
= mt65xx_blink_set
;
496 INIT_WORK(&g_leds_data
[i
]->work
, mt_mt65xx_led_work
);
498 ret
= led_classdev_register(&pdev
->dev
, &g_leds_data
[i
]->cdev
);
500 if (strcmp(g_leds_data
[i
]->cdev
.name
, "lcd-backlight") == 0) {
501 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_duty
);
503 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
506 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_div
);
508 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
511 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_frequency
);
513 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
516 rc
= device_create_file(g_leds_data
[i
]->cdev
.dev
, &dev_attr_pwm_register
);
518 LEDS_DRV_DEBUG("[LED]device_create_file duty fail!\n");
520 bl_setting
= &g_leds_data
[i
]->cust
;
527 #ifdef CONTROL_BL_TEMPERATURE
534 ("[LED]led probe last_level = %d, limit = %d, limit_flag = %d, current_level = %d\n",
535 last_level
, limit
, limit_flag
, current_level
);
543 for (i
= i
- 1; i
>= 0; i
--) {
546 led_classdev_unregister(&g_leds_data
[i
]->cdev
);
547 cancel_work_sync(&g_leds_data
[i
]->work
);
548 kfree(g_leds_data
[i
]);
549 g_leds_data
[i
] = NULL
;
556 static int mt65xx_leds_remove(struct platform_device
*pdev
)
559 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
562 led_classdev_unregister(&g_leds_data
[i
]->cdev
);
563 cancel_work_sync(&g_leds_data
[i
]->work
);
564 kfree(g_leds_data
[i
]);
565 g_leds_data
[i
] = NULL
;
572 static int mt65xx_leds_suspend(struct platform_device *pdev, pm_message_t state)
578 static void mt65xx_leds_shutdown(struct platform_device
*pdev
)
581 struct nled_setting led_tmp_setting
= { NLED_OFF
, 0, 0 };
583 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
584 LEDS_DRV_DEBUG("[LED]mt65xx_leds_shutdown: turn off backlight\n");
586 for (i
= 0; i
< MT65XX_LED_TYPE_TOTAL
; i
++) {
589 switch (g_leds_data
[i
]->cust
.mode
) {
591 case MT65XX_LED_MODE_PWM
:
592 if (strcmp(g_leds_data
[i
]->cust
.name
, "lcd-backlight") == 0) {
593 /* mt_set_pwm_disable(g_leds_data[i]->cust.data); */
594 /* mt_pwm_power_off (g_leds_data[i]->cust.data); */
595 mt_led_pwm_disable(g_leds_data
[i
]->cust
.data
);
597 led_set_pwm(g_leds_data
[i
]->cust
.data
, &led_tmp_setting
);
601 /* case MT65XX_LED_MODE_GPIO: */
602 /* brightness_set_gpio(g_leds_data[i]->cust.data, 0); */
605 case MT65XX_LED_MODE_PMIC
:
606 brightness_set_pmic(g_leds_data
[i
]->cust
.data
, 0, 0);
608 case MT65XX_LED_MODE_CUST_LCM
:
609 LEDS_DRV_DEBUG("[LED]backlight control through LCM!!1\n");
610 ((cust_brightness_set
) (g_leds_data
[i
]->cust
.data
)) (0, bl_div
);
612 case MT65XX_LED_MODE_CUST_BLS_PWM
:
613 LEDS_DRV_DEBUG("[LED]backlight control through BLS!!1\n");
614 ((cust_set_brightness
) (g_leds_data
[i
]->cust
.data
)) (0);
616 case MT65XX_LED_MODE_NONE
:
624 static struct platform_driver mt65xx_leds_driver
= {
626 .name
= "leds-mt65xx",
627 .owner
= THIS_MODULE
,
629 .probe
= mt65xx_leds_probe
,
630 .remove
= mt65xx_leds_remove
,
631 /* .suspend = mt65xx_leds_suspend, */
632 .shutdown
= mt65xx_leds_shutdown
,
636 static struct platform_device mt65xx_leds_device
= {
637 .name
= "leds-mt65xx",
643 static int __init
mt65xx_leds_init(void)
647 LEDS_DRV_DEBUG("[LED]%s\n", __func__
);
650 ret
= platform_device_register(&mt65xx_leds_device
);
652 printk("[LED]mt65xx_leds_init:dev:E%d\n", ret
);
654 ret
= platform_driver_register(&mt65xx_leds_driver
);
657 LEDS_DRV_DEBUG("[LED]mt65xx_leds_init:drv:E%d\n", ret
);
658 /* platform_device_unregister(&mt65xx_leds_device); */
662 mt_leds_wake_lock_init();
667 static void __exit
mt65xx_leds_exit(void)
669 platform_driver_unregister(&mt65xx_leds_driver
);
670 /* platform_device_unregister(&mt65xx_leds_device); */
673 module_param(debug_enable_led
, int, 0644);
675 module_init(mt65xx_leds_init
);
676 module_exit(mt65xx_leds_exit
);
678 MODULE_AUTHOR("MediaTek Inc.");
679 MODULE_DESCRIPTION("LED driver for MediaTek MT65xx chip");
680 MODULE_LICENSE("GPL");
681 MODULE_ALIAS("leds-mt65xx");