diff options
author | Grazvydas Ignotas <notasas@gmail.com> | 2012-03-23 18:02:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-03-23 19:58:33 -0400 |
commit | 35c1682cc069fc1f677012d3170757135e246b39 (patch) | |
tree | 2860a50c2fb51559e46818f794b5b12cb1164bae /drivers/video | |
parent | c8df7428635c02ca3051e39179c83297d8b76fba (diff) |
backlight: add support for Pandora backlight
Add support for pandora (openpandora.org) backlight.
It might look like all this could be done using pwm_bl.c instead, but
there is a need of special programming sequence when turning on the LED
driver chip or else it will misbehave. Doing this using pwm_bl.c would
require to use some register programming and pwm functions from platform
code, and ARM maintainers are allergic to driver-like code in /arch/arm
nowadays. The PMIC PWM driver is currently missing too, so pwm_bl.c
can't be used anyway.
Signed-off-by: Grazvydas Ignotas <notasas@gmail.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/backlight/Kconfig | 7 | ||||
-rw-r--r-- | drivers/video/backlight/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/backlight/pandora_bl.c | 171 |
3 files changed, 179 insertions, 0 deletions
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 248d6c0858dd..ed68fde54454 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig | |||
@@ -348,6 +348,13 @@ config BACKLIGHT_OT200 | |||
348 | To compile this driver as a module, choose M here: the module will be | 348 | To compile this driver as a module, choose M here: the module will be |
349 | called ot200_bl. | 349 | called ot200_bl. |
350 | 350 | ||
351 | config BACKLIGHT_PANDORA | ||
352 | tristate "Backlight driver for Pandora console" | ||
353 | depends on TWL4030_CORE | ||
354 | help | ||
355 | If you have a Pandora console, say Y to enable the | ||
356 | backlight driver. | ||
357 | |||
351 | endif # BACKLIGHT_CLASS_DEVICE | 358 | endif # BACKLIGHT_CLASS_DEVICE |
352 | 359 | ||
353 | endif # BACKLIGHT_LCD_SUPPORT | 360 | endif # BACKLIGHT_LCD_SUPPORT |
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index c71dd793363d..8071eb656147 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile | |||
@@ -24,6 +24,7 @@ obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o | |||
24 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o | 24 | obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o |
25 | obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o | 25 | obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o |
26 | obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o | 26 | obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o |
27 | obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o | ||
27 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o | 28 | obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o |
28 | obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o | 29 | obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o |
29 | obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o | 30 | obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o |
diff --git a/drivers/video/backlight/pandora_bl.c b/drivers/video/backlight/pandora_bl.c new file mode 100644 index 000000000000..4ec30748b447 --- /dev/null +++ b/drivers/video/backlight/pandora_bl.c | |||
@@ -0,0 +1,171 @@ | |||
1 | /* | ||
2 | * Backlight driver for Pandora handheld. | ||
3 | * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight. | ||
4 | * Based on pwm_bl.c | ||
5 | * | ||
6 | * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/fb.h> | ||
18 | #include <linux/backlight.h> | ||
19 | #include <linux/i2c/twl.h> | ||
20 | #include <linux/err.h> | ||
21 | |||
22 | #define TWL_PWM0_ON 0x00 | ||
23 | #define TWL_PWM0_OFF 0x01 | ||
24 | |||
25 | #define TWL_INTBR_GPBR1 0x0c | ||
26 | #define TWL_INTBR_PMBR1 0x0d | ||
27 | |||
28 | #define TWL_PMBR1_PWM0_MUXMASK 0x0c | ||
29 | #define TWL_PMBR1_PWM0 0x04 | ||
30 | #define PWM0_CLK_ENABLE BIT(0) | ||
31 | #define PWM0_ENABLE BIT(2) | ||
32 | |||
33 | /* range accepted by hardware */ | ||
34 | #define MIN_VALUE 9 | ||
35 | #define MAX_VALUE 63 | ||
36 | #define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE) | ||
37 | |||
38 | #define PANDORABL_WAS_OFF BL_CORE_DRIVER1 | ||
39 | |||
40 | static int pandora_backlight_update_status(struct backlight_device *bl) | ||
41 | { | ||
42 | int brightness = bl->props.brightness; | ||
43 | u8 r; | ||
44 | |||
45 | if (bl->props.power != FB_BLANK_UNBLANK) | ||
46 | brightness = 0; | ||
47 | if (bl->props.state & BL_CORE_FBBLANK) | ||
48 | brightness = 0; | ||
49 | if (bl->props.state & BL_CORE_SUSPENDED) | ||
50 | brightness = 0; | ||
51 | |||
52 | if ((unsigned int)brightness > MAX_USER_VALUE) | ||
53 | brightness = MAX_USER_VALUE; | ||
54 | |||
55 | if (brightness == 0) { | ||
56 | if (bl->props.state & PANDORABL_WAS_OFF) | ||
57 | goto done; | ||
58 | |||
59 | /* first disable PWM0 output, then clock */ | ||
60 | twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); | ||
61 | r &= ~PWM0_ENABLE; | ||
62 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); | ||
63 | r &= ~PWM0_CLK_ENABLE; | ||
64 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); | ||
65 | |||
66 | goto done; | ||
67 | } | ||
68 | |||
69 | if (bl->props.state & PANDORABL_WAS_OFF) { | ||
70 | /* | ||
71 | * set PWM duty cycle to max. TPS61161 seems to use this | ||
72 | * to calibrate it's PWM sensitivity when it starts. | ||
73 | */ | ||
74 | twl_i2c_write_u8(TWL4030_MODULE_PWM0, MAX_VALUE, | ||
75 | TWL_PWM0_OFF); | ||
76 | |||
77 | /* first enable clock, then PWM0 out */ | ||
78 | twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1); | ||
79 | r &= ~PWM0_ENABLE; | ||
80 | r |= PWM0_CLK_ENABLE; | ||
81 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); | ||
82 | r |= PWM0_ENABLE; | ||
83 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1); | ||
84 | |||
85 | /* | ||
86 | * TI made it very easy to enable digital control, so easy that | ||
87 | * it often triggers unintentionally and disabes PWM control, | ||
88 | * so wait until 1 wire mode detection window ends. | ||
89 | */ | ||
90 | usleep_range(2000, 10000); | ||
91 | } | ||
92 | |||
93 | twl_i2c_write_u8(TWL4030_MODULE_PWM0, MIN_VALUE + brightness, | ||
94 | TWL_PWM0_OFF); | ||
95 | |||
96 | done: | ||
97 | if (brightness != 0) | ||
98 | bl->props.state &= ~PANDORABL_WAS_OFF; | ||
99 | else | ||
100 | bl->props.state |= PANDORABL_WAS_OFF; | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int pandora_backlight_get_brightness(struct backlight_device *bl) | ||
106 | { | ||
107 | return bl->props.brightness; | ||
108 | } | ||
109 | |||
110 | static const struct backlight_ops pandora_backlight_ops = { | ||
111 | .options = BL_CORE_SUSPENDRESUME, | ||
112 | .update_status = pandora_backlight_update_status, | ||
113 | .get_brightness = pandora_backlight_get_brightness, | ||
114 | }; | ||
115 | |||
116 | static int pandora_backlight_probe(struct platform_device *pdev) | ||
117 | { | ||
118 | struct backlight_properties props; | ||
119 | struct backlight_device *bl; | ||
120 | u8 r; | ||
121 | |||
122 | memset(&props, 0, sizeof(props)); | ||
123 | props.max_brightness = MAX_USER_VALUE; | ||
124 | props.type = BACKLIGHT_RAW; | ||
125 | bl = backlight_device_register(pdev->name, &pdev->dev, | ||
126 | NULL, &pandora_backlight_ops, &props); | ||
127 | if (IS_ERR(bl)) { | ||
128 | dev_err(&pdev->dev, "failed to register backlight\n"); | ||
129 | return PTR_ERR(bl); | ||
130 | } | ||
131 | |||
132 | platform_set_drvdata(pdev, bl); | ||
133 | |||
134 | /* 64 cycle period, ON position 0 */ | ||
135 | twl_i2c_write_u8(TWL4030_MODULE_PWM0, 0x80, TWL_PWM0_ON); | ||
136 | |||
137 | bl->props.state |= PANDORABL_WAS_OFF; | ||
138 | bl->props.brightness = MAX_USER_VALUE; | ||
139 | backlight_update_status(bl); | ||
140 | |||
141 | /* enable PWM function in pin mux */ | ||
142 | twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1); | ||
143 | r &= ~TWL_PMBR1_PWM0_MUXMASK; | ||
144 | r |= TWL_PMBR1_PWM0; | ||
145 | twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1); | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static int pandora_backlight_remove(struct platform_device *pdev) | ||
151 | { | ||
152 | struct backlight_device *bl = platform_get_drvdata(pdev); | ||
153 | backlight_device_unregister(bl); | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static struct platform_driver pandora_backlight_driver = { | ||
158 | .driver = { | ||
159 | .name = "pandora-backlight", | ||
160 | .owner = THIS_MODULE, | ||
161 | }, | ||
162 | .probe = pandora_backlight_probe, | ||
163 | .remove = pandora_backlight_remove, | ||
164 | }; | ||
165 | |||
166 | module_platform_driver(pandora_backlight_driver); | ||
167 | |||
168 | MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); | ||
169 | MODULE_DESCRIPTION("Pandora Backlight Driver"); | ||
170 | MODULE_LICENSE("GPL"); | ||
171 | MODULE_ALIAS("platform:pandora-backlight"); | ||