2 * Backlight driver for Marvell Semiconductor 88PM8606
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/platform_device.h>
16 #include <linux/i2c.h>
17 #include <linux/backlight.h>
18 #include <linux/mfd/88pm860x.h>
20 #define MAX_BRIGHTNESS (0xFF)
21 #define MIN_BRIGHTNESS (0)
23 #define CURRENT_MASK (0x1F << 1)
25 struct pm860x_backlight_data
{
26 struct pm860x_chip
*chip
;
27 struct i2c_client
*i2c
;
28 int current_brightness
;
34 static inline int wled_a(int port
)
38 ret
= ((port
- PM8606_BACKLIGHT1
) << 1) + 2;
42 static inline int wled_b(int port
)
46 ret
= ((port
- PM8606_BACKLIGHT1
) << 1) + 3;
50 /* WLED2 & WLED3 share the same IDC */
51 static inline int wled_idc(int port
)
56 case PM8606_BACKLIGHT1
:
57 case PM8606_BACKLIGHT2
:
58 ret
= ((port
- PM8606_BACKLIGHT1
) << 1) + 3;
60 case PM8606_BACKLIGHT3
:
62 ret
= ((port
- PM8606_BACKLIGHT2
) << 1) + 3;
68 static int pm860x_backlight_set(struct backlight_device
*bl
, int brightness
)
70 struct pm860x_backlight_data
*data
= bl_get_data(bl
);
71 struct pm860x_chip
*chip
= data
->chip
;
75 if (brightness
> MAX_BRIGHTNESS
)
76 value
= MAX_BRIGHTNESS
;
80 ret
= pm860x_reg_write(data
->i2c
, wled_a(data
->port
), value
);
84 if ((data
->current_brightness
== 0) && brightness
) {
86 ret
= pm860x_set_bits(data
->i2c
, wled_idc(data
->port
),
87 CURRENT_MASK
, data
->iset
);
92 ret
= pm860x_set_bits(data
->i2c
, PM8606_PWM
,
93 PM8606_PWM_FREQ_MASK
, data
->pwm
);
97 if (brightness
== MAX_BRIGHTNESS
) {
98 /* set WLED_ON bit as 100% */
99 ret
= pm860x_set_bits(data
->i2c
, wled_b(data
->port
),
100 PM8606_WLED_ON
, PM8606_WLED_ON
);
103 if (brightness
== MAX_BRIGHTNESS
) {
104 /* set WLED_ON bit as 100% */
105 ret
= pm860x_set_bits(data
->i2c
, wled_b(data
->port
),
106 PM8606_WLED_ON
, PM8606_WLED_ON
);
108 /* clear WLED_ON bit since it's not 100% */
109 ret
= pm860x_set_bits(data
->i2c
, wled_b(data
->port
),
116 dev_dbg(chip
->dev
, "set brightness %d\n", value
);
117 data
->current_brightness
= value
;
120 dev_dbg(chip
->dev
, "set brightness %d failure with return "
121 "value:%d\n", value
, ret
);
125 static int pm860x_backlight_update_status(struct backlight_device
*bl
)
127 int brightness
= bl
->props
.brightness
;
129 if (bl
->props
.power
!= FB_BLANK_UNBLANK
)
132 if (bl
->props
.fb_blank
!= FB_BLANK_UNBLANK
)
135 if (bl
->props
.state
& BL_CORE_SUSPENDED
)
138 return pm860x_backlight_set(bl
, brightness
);
141 static int pm860x_backlight_get_brightness(struct backlight_device
*bl
)
143 struct pm860x_backlight_data
*data
= bl_get_data(bl
);
144 struct pm860x_chip
*chip
= data
->chip
;
147 ret
= pm860x_reg_read(data
->i2c
, wled_a(data
->port
));
150 data
->current_brightness
= ret
;
151 dev_dbg(chip
->dev
, "get brightness %d\n", data
->current_brightness
);
152 return data
->current_brightness
;
157 static struct backlight_ops pm860x_backlight_ops
= {
158 .options
= BL_CORE_SUSPENDRESUME
,
159 .update_status
= pm860x_backlight_update_status
,
160 .get_brightness
= pm860x_backlight_get_brightness
,
163 static int __check_device(struct pm860x_backlight_pdata
*pdata
, char *name
)
165 struct pm860x_backlight_pdata
*p
= pdata
;
169 if ((p
->id
!= PM8606_ID_BACKLIGHT
) || (p
->flags
< 0))
172 if (!strncmp(name
, pm860x_backlight_name
[p
->flags
],
182 static int pm860x_backlight_probe(struct platform_device
*pdev
)
184 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
185 struct pm860x_platform_data
*pm860x_pdata
;
186 struct pm860x_backlight_pdata
*pdata
= NULL
;
187 struct pm860x_backlight_data
*data
;
188 struct backlight_device
*bl
;
189 struct resource
*res
;
190 struct backlight_properties props
;
192 char name
[MFD_NAME_SIZE
];
195 res
= platform_get_resource(pdev
, IORESOURCE_IO
, 0);
197 dev_err(&pdev
->dev
, "No I/O resource!\n");
201 if (pdev
->dev
.parent
->platform_data
) {
202 pm860x_pdata
= pdev
->dev
.parent
->platform_data
;
203 pdata
= pm860x_pdata
->backlight
;
206 dev_err(&pdev
->dev
, "platform data isn't assigned to "
211 data
= kzalloc(sizeof(struct pm860x_backlight_data
), GFP_KERNEL
);
214 strncpy(name
, res
->name
, MFD_NAME_SIZE
);
216 data
->i2c
= (chip
->id
== CHIP_PM8606
) ? chip
->client \
218 data
->current_brightness
= MAX_BRIGHTNESS
;
219 data
->pwm
= pdata
->pwm
;
220 data
->iset
= pdata
->iset
;
221 data
->port
= __check_device(pdata
, name
);
222 if (data
->port
< 0) {
223 dev_err(&pdev
->dev
, "wrong platform data is assigned");
227 memset(&props
, 0, sizeof(struct backlight_properties
));
228 props
.max_brightness
= MAX_BRIGHTNESS
;
229 bl
= backlight_device_register(name
, &pdev
->dev
, data
,
230 &pm860x_backlight_ops
, &props
);
232 dev_err(&pdev
->dev
, "failed to register backlight\n");
236 bl
->props
.brightness
= MAX_BRIGHTNESS
;
238 platform_set_drvdata(pdev
, bl
);
240 /* Enable reference VSYS */
241 ret
= pm860x_reg_read(data
->i2c
, PM8606_VSYS
);
244 if ((ret
& PM8606_VSYS_EN
) == 0) {
245 value
= ret
| PM8606_VSYS_EN
;
246 ret
= pm860x_reg_write(data
->i2c
, PM8606_VSYS
, value
);
250 /* Enable reference OSC */
251 ret
= pm860x_reg_read(data
->i2c
, PM8606_MISC
);
254 if ((ret
& PM8606_MISC_OSC_EN
) == 0) {
255 value
= ret
| PM8606_MISC_OSC_EN
;
256 ret
= pm860x_reg_write(data
->i2c
, PM8606_MISC
, value
);
260 /* read current backlight */
261 ret
= pm860x_backlight_get_brightness(bl
);
265 backlight_update_status(bl
);
272 static int pm860x_backlight_remove(struct platform_device
*pdev
)
274 struct backlight_device
*bl
= platform_get_drvdata(pdev
);
275 struct pm860x_backlight_data
*data
= bl_get_data(bl
);
277 backlight_device_unregister(bl
);
282 static struct platform_driver pm860x_backlight_driver
= {
284 .name
= "88pm860x-backlight",
285 .owner
= THIS_MODULE
,
287 .probe
= pm860x_backlight_probe
,
288 .remove
= pm860x_backlight_remove
,
291 static int __init
pm860x_backlight_init(void)
293 return platform_driver_register(&pm860x_backlight_driver
);
295 module_init(pm860x_backlight_init
);
297 static void __exit
pm860x_backlight_exit(void)
299 platform_driver_unregister(&pm860x_backlight_driver
);
301 module_exit(pm860x_backlight_exit
);
303 MODULE_DESCRIPTION("Backlight Driver for Marvell Semiconductor 88PM8606");
304 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
305 MODULE_LICENSE("GPL");
306 MODULE_ALIAS("platform:88pm860x-backlight");