Commit b3b97473ee07ae8904e9d8fe02914de2188d302c

Authored by Jett.Zhou
Committed by Samuel Ortiz
1 parent 1efc15812d

led: Modified power control of pm860x led

Since several sub modules such as backlight, leds and vibrator depend
on the refernce group and internal oscillator in pm8606, so modified
the power control of led in pm8606 by unified interface.

Signed-off-by: Jett.Zhou <jtzhou@marvell.com>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

Showing 1 changed file with 23 additions and 0 deletions Inline Diff

drivers/leds/leds-88pm860x.c
1 /* 1 /*
2 * LED driver for Marvell 88PM860x 2 * LED driver for Marvell 88PM860x
3 * 3 *
4 * Copyright (C) 2009 Marvell International Ltd. 4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com> 5 * Haojian Zhuang <haojian.zhuang@marvell.com>
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 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 8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
10 * 10 *
11 */ 11 */
12 12
13 #include <linux/kernel.h> 13 #include <linux/kernel.h>
14 #include <linux/init.h> 14 #include <linux/init.h>
15 #include <linux/platform_device.h> 15 #include <linux/platform_device.h>
16 #include <linux/i2c.h> 16 #include <linux/i2c.h>
17 #include <linux/leds.h> 17 #include <linux/leds.h>
18 #include <linux/slab.h> 18 #include <linux/slab.h>
19 #include <linux/workqueue.h> 19 #include <linux/workqueue.h>
20 #include <linux/mfd/88pm860x.h> 20 #include <linux/mfd/88pm860x.h>
21 #include <linux/module.h> 21 #include <linux/module.h>
22 22
23 #define LED_PWM_SHIFT (3) 23 #define LED_PWM_SHIFT (3)
24 #define LED_PWM_MASK (0x1F) 24 #define LED_PWM_MASK (0x1F)
25 #define LED_CURRENT_MASK (0x07 << 5) 25 #define LED_CURRENT_MASK (0x07 << 5)
26 26
27 #define LED_BLINK_ON_MASK (0x07) 27 #define LED_BLINK_ON_MASK (0x07)
28 #define LED_BLINK_MASK (0x7F) 28 #define LED_BLINK_MASK (0x7F)
29 29
30 #define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66) 30 #define LED_BLINK_ON(x) ((x & 0x7) * 66 + 66)
31 #define LED_BLINK_ON_MIN LED_BLINK_ON(0) 31 #define LED_BLINK_ON_MIN LED_BLINK_ON(0)
32 #define LED_BLINK_ON_MAX LED_BLINK_ON(0x7) 32 #define LED_BLINK_ON_MAX LED_BLINK_ON(0x7)
33 #define LED_ON_CONTINUOUS (0x0F << 3) 33 #define LED_ON_CONTINUOUS (0x0F << 3)
34 #define LED_TO_ON(x) ((x - 66) / 66) 34 #define LED_TO_ON(x) ((x - 66) / 66)
35 35
36 #define LED1_BLINK_EN (1 << 1) 36 #define LED1_BLINK_EN (1 << 1)
37 #define LED2_BLINK_EN (1 << 2) 37 #define LED2_BLINK_EN (1 << 2)
38 38
39 struct pm860x_led { 39 struct pm860x_led {
40 struct led_classdev cdev; 40 struct led_classdev cdev;
41 struct i2c_client *i2c; 41 struct i2c_client *i2c;
42 struct work_struct work; 42 struct work_struct work;
43 struct pm860x_chip *chip; 43 struct pm860x_chip *chip;
44 struct mutex lock; 44 struct mutex lock;
45 char name[MFD_NAME_SIZE]; 45 char name[MFD_NAME_SIZE];
46 46
47 int port; 47 int port;
48 int iset; 48 int iset;
49 unsigned char brightness; 49 unsigned char brightness;
50 unsigned char current_brightness; 50 unsigned char current_brightness;
51 51
52 int blink_data; 52 int blink_data;
53 int blink_time; 53 int blink_time;
54 int blink_on; 54 int blink_on;
55 int blink_off; 55 int blink_off;
56 }; 56 };
57 57
58 /* return offset of color register */ 58 /* return offset of color register */
59 static inline int __led_off(int port) 59 static inline int __led_off(int port)
60 { 60 {
61 int ret = -EINVAL; 61 int ret = -EINVAL;
62 62
63 switch (port) { 63 switch (port) {
64 case PM8606_LED1_RED: 64 case PM8606_LED1_RED:
65 case PM8606_LED1_GREEN: 65 case PM8606_LED1_GREEN:
66 case PM8606_LED1_BLUE: 66 case PM8606_LED1_BLUE:
67 ret = port - PM8606_LED1_RED + PM8606_RGB1B; 67 ret = port - PM8606_LED1_RED + PM8606_RGB1B;
68 break; 68 break;
69 case PM8606_LED2_RED: 69 case PM8606_LED2_RED:
70 case PM8606_LED2_GREEN: 70 case PM8606_LED2_GREEN:
71 case PM8606_LED2_BLUE: 71 case PM8606_LED2_BLUE:
72 ret = port - PM8606_LED2_RED + PM8606_RGB2B; 72 ret = port - PM8606_LED2_RED + PM8606_RGB2B;
73 break; 73 break;
74 } 74 }
75 return ret; 75 return ret;
76 } 76 }
77 77
78 /* return offset of blink register */ 78 /* return offset of blink register */
79 static inline int __blink_off(int port) 79 static inline int __blink_off(int port)
80 { 80 {
81 int ret = -EINVAL; 81 int ret = -EINVAL;
82 82
83 switch (port) { 83 switch (port) {
84 case PM8606_LED1_RED: 84 case PM8606_LED1_RED:
85 case PM8606_LED1_GREEN: 85 case PM8606_LED1_GREEN:
86 case PM8606_LED1_BLUE: 86 case PM8606_LED1_BLUE:
87 ret = PM8606_RGB1A; 87 ret = PM8606_RGB1A;
88 break; 88 break;
89 case PM8606_LED2_RED: 89 case PM8606_LED2_RED:
90 case PM8606_LED2_GREEN: 90 case PM8606_LED2_GREEN:
91 case PM8606_LED2_BLUE: 91 case PM8606_LED2_BLUE:
92 ret = PM8606_RGB2A; 92 ret = PM8606_RGB2A;
93 break; 93 break;
94 } 94 }
95 return ret; 95 return ret;
96 } 96 }
97 97
98 static inline int __blink_ctl_mask(int port) 98 static inline int __blink_ctl_mask(int port)
99 { 99 {
100 int ret = -EINVAL; 100 int ret = -EINVAL;
101 101
102 switch (port) { 102 switch (port) {
103 case PM8606_LED1_RED: 103 case PM8606_LED1_RED:
104 case PM8606_LED1_GREEN: 104 case PM8606_LED1_GREEN:
105 case PM8606_LED1_BLUE: 105 case PM8606_LED1_BLUE:
106 ret = LED1_BLINK_EN; 106 ret = LED1_BLINK_EN;
107 break; 107 break;
108 case PM8606_LED2_RED: 108 case PM8606_LED2_RED:
109 case PM8606_LED2_GREEN: 109 case PM8606_LED2_GREEN:
110 case PM8606_LED2_BLUE: 110 case PM8606_LED2_BLUE:
111 ret = LED2_BLINK_EN; 111 ret = LED2_BLINK_EN;
112 break; 112 break;
113 } 113 }
114 return ret; 114 return ret;
115 } 115 }
116 116
117 static int led_power_set(struct pm860x_chip *chip, int port, int on)
118 {
119 int ret = -EINVAL;
120
121 switch (port) {
122 case PM8606_LED1_RED:
123 case PM8606_LED1_GREEN:
124 case PM8606_LED1_BLUE:
125 ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) :
126 pm8606_osc_disable(chip, RGB1_ENABLE);
127 break;
128 case PM8606_LED2_RED:
129 case PM8606_LED2_GREEN:
130 case PM8606_LED2_BLUE:
131 ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) :
132 pm8606_osc_disable(chip, RGB2_ENABLE);
133 break;
134 }
135 return ret;
136 }
137
117 static void pm860x_led_work(struct work_struct *work) 138 static void pm860x_led_work(struct work_struct *work)
118 { 139 {
119 140
120 struct pm860x_led *led; 141 struct pm860x_led *led;
121 struct pm860x_chip *chip; 142 struct pm860x_chip *chip;
122 unsigned char buf[3]; 143 unsigned char buf[3];
123 int mask, ret; 144 int mask, ret;
124 145
125 led = container_of(work, struct pm860x_led, work); 146 led = container_of(work, struct pm860x_led, work);
126 chip = led->chip; 147 chip = led->chip;
127 mutex_lock(&led->lock); 148 mutex_lock(&led->lock);
128 if ((led->current_brightness == 0) && led->brightness) { 149 if ((led->current_brightness == 0) && led->brightness) {
150 led_power_set(chip, led->port, 1);
129 if (led->iset) { 151 if (led->iset) {
130 pm860x_set_bits(led->i2c, __led_off(led->port), 152 pm860x_set_bits(led->i2c, __led_off(led->port),
131 LED_CURRENT_MASK, led->iset); 153 LED_CURRENT_MASK, led->iset);
132 } 154 }
133 pm860x_set_bits(led->i2c, __blink_off(led->port), 155 pm860x_set_bits(led->i2c, __blink_off(led->port),
134 LED_BLINK_MASK, LED_ON_CONTINUOUS); 156 LED_BLINK_MASK, LED_ON_CONTINUOUS);
135 mask = __blink_ctl_mask(led->port); 157 mask = __blink_ctl_mask(led->port);
136 pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask); 158 pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, mask);
137 } 159 }
138 pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK, 160 pm860x_set_bits(led->i2c, __led_off(led->port), LED_PWM_MASK,
139 led->brightness); 161 led->brightness);
140 162
141 if (led->brightness == 0) { 163 if (led->brightness == 0) {
142 pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf); 164 pm860x_bulk_read(led->i2c, __led_off(led->port), 3, buf);
143 ret = buf[0] & LED_PWM_MASK; 165 ret = buf[0] & LED_PWM_MASK;
144 ret |= buf[1] & LED_PWM_MASK; 166 ret |= buf[1] & LED_PWM_MASK;
145 ret |= buf[2] & LED_PWM_MASK; 167 ret |= buf[2] & LED_PWM_MASK;
146 if (ret == 0) { 168 if (ret == 0) {
147 /* unset current since no led is lighting */ 169 /* unset current since no led is lighting */
148 pm860x_set_bits(led->i2c, __led_off(led->port), 170 pm860x_set_bits(led->i2c, __led_off(led->port),
149 LED_CURRENT_MASK, 0); 171 LED_CURRENT_MASK, 0);
150 mask = __blink_ctl_mask(led->port); 172 mask = __blink_ctl_mask(led->port);
151 pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0); 173 pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0);
174 led_power_set(chip, led->port, 0);
152 } 175 }
153 } 176 }
154 led->current_brightness = led->brightness; 177 led->current_brightness = led->brightness;
155 dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", 178 dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n",
156 __led_off(led->port), led->brightness); 179 __led_off(led->port), led->brightness);
157 mutex_unlock(&led->lock); 180 mutex_unlock(&led->lock);
158 } 181 }
159 182
160 static void pm860x_led_set(struct led_classdev *cdev, 183 static void pm860x_led_set(struct led_classdev *cdev,
161 enum led_brightness value) 184 enum led_brightness value)
162 { 185 {
163 struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev); 186 struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev);
164 187
165 data->brightness = value >> 3; 188 data->brightness = value >> 3;
166 schedule_work(&data->work); 189 schedule_work(&data->work);
167 } 190 }
168 191
169 static int pm860x_led_probe(struct platform_device *pdev) 192 static int pm860x_led_probe(struct platform_device *pdev)
170 { 193 {
171 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); 194 struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
172 struct pm860x_led_pdata *pdata; 195 struct pm860x_led_pdata *pdata;
173 struct pm860x_led *data; 196 struct pm860x_led *data;
174 struct resource *res; 197 struct resource *res;
175 int ret; 198 int ret;
176 199
177 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 200 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
178 if (res == NULL) { 201 if (res == NULL) {
179 dev_err(&pdev->dev, "No I/O resource!\n"); 202 dev_err(&pdev->dev, "No I/O resource!\n");
180 return -EINVAL; 203 return -EINVAL;
181 } 204 }
182 205
183 pdata = pdev->dev.platform_data; 206 pdata = pdev->dev.platform_data;
184 if (pdata == NULL) { 207 if (pdata == NULL) {
185 dev_err(&pdev->dev, "No platform data!\n"); 208 dev_err(&pdev->dev, "No platform data!\n");
186 return -EINVAL; 209 return -EINVAL;
187 } 210 }
188 211
189 data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL); 212 data = kzalloc(sizeof(struct pm860x_led), GFP_KERNEL);
190 if (data == NULL) 213 if (data == NULL)
191 return -ENOMEM; 214 return -ENOMEM;
192 strncpy(data->name, res->name, MFD_NAME_SIZE - 1); 215 strncpy(data->name, res->name, MFD_NAME_SIZE - 1);
193 dev_set_drvdata(&pdev->dev, data); 216 dev_set_drvdata(&pdev->dev, data);
194 data->chip = chip; 217 data->chip = chip;
195 data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; 218 data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion;
196 data->iset = pdata->iset; 219 data->iset = pdata->iset;
197 data->port = pdata->flags; 220 data->port = pdata->flags;
198 if (data->port < 0) { 221 if (data->port < 0) {
199 dev_err(&pdev->dev, "check device failed\n"); 222 dev_err(&pdev->dev, "check device failed\n");
200 kfree(data); 223 kfree(data);
201 return -EINVAL; 224 return -EINVAL;
202 } 225 }
203 226
204 data->current_brightness = 0; 227 data->current_brightness = 0;
205 data->cdev.name = data->name; 228 data->cdev.name = data->name;
206 data->cdev.brightness_set = pm860x_led_set; 229 data->cdev.brightness_set = pm860x_led_set;
207 mutex_init(&data->lock); 230 mutex_init(&data->lock);
208 INIT_WORK(&data->work, pm860x_led_work); 231 INIT_WORK(&data->work, pm860x_led_work);
209 232
210 ret = led_classdev_register(chip->dev, &data->cdev); 233 ret = led_classdev_register(chip->dev, &data->cdev);
211 if (ret < 0) { 234 if (ret < 0) {
212 dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); 235 dev_err(&pdev->dev, "Failed to register LED: %d\n", ret);
213 goto out; 236 goto out;
214 } 237 }
215 pm860x_led_set(&data->cdev, 0); 238 pm860x_led_set(&data->cdev, 0);
216 return 0; 239 return 0;
217 out: 240 out:
218 kfree(data); 241 kfree(data);
219 return ret; 242 return ret;
220 } 243 }
221 244
222 static int pm860x_led_remove(struct platform_device *pdev) 245 static int pm860x_led_remove(struct platform_device *pdev)
223 { 246 {
224 struct pm860x_led *data = platform_get_drvdata(pdev); 247 struct pm860x_led *data = platform_get_drvdata(pdev);
225 248
226 led_classdev_unregister(&data->cdev); 249 led_classdev_unregister(&data->cdev);
227 kfree(data); 250 kfree(data);
228 251
229 return 0; 252 return 0;
230 } 253 }
231 254
232 static struct platform_driver pm860x_led_driver = { 255 static struct platform_driver pm860x_led_driver = {
233 .driver = { 256 .driver = {
234 .name = "88pm860x-led", 257 .name = "88pm860x-led",
235 .owner = THIS_MODULE, 258 .owner = THIS_MODULE,
236 }, 259 },
237 .probe = pm860x_led_probe, 260 .probe = pm860x_led_probe,
238 .remove = pm860x_led_remove, 261 .remove = pm860x_led_remove,
239 }; 262 };
240 263
241 module_platform_driver(pm860x_led_driver); 264 module_platform_driver(pm860x_led_driver);
242 265
243 MODULE_DESCRIPTION("LED driver for Marvell PM860x"); 266 MODULE_DESCRIPTION("LED driver for Marvell PM860x");
244 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 267 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
245 MODULE_LICENSE("GPL"); 268 MODULE_LICENSE("GPL");
246 MODULE_ALIAS("platform:88pm860x-led"); 269 MODULE_ALIAS("platform:88pm860x-led");
247 270