Commit 87aae1ea82f93f0f00cb955044ea1db3501cf233
Committed by
Bryan Wu
1 parent
8465b01827
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
leds: use dev_get_platdata()
Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. Signed-off-by: Jingoo Han <jg1.han@samsung.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
Showing 30 changed files with 52 additions and 49 deletions Inline Diff
- drivers/leds/leds-88pm860x.c
- drivers/leds/leds-adp5520.c
- drivers/leds/leds-asic3.c
- drivers/leds/leds-atmel-pwm.c
- drivers/leds/leds-bd2802.c
- drivers/leds/leds-da903x.c
- drivers/leds/leds-da9052.c
- drivers/leds/leds-gpio.c
- drivers/leds/leds-lm3530.c
- drivers/leds/leds-lm3533.c
- drivers/leds/leds-lm355x.c
- drivers/leds/leds-lm3642.c
- drivers/leds/leds-lp3944.c
- drivers/leds/leds-lp5521.c
- drivers/leds/leds-lp5523.c
- drivers/leds/leds-lp5562.c
- drivers/leds/leds-lp8501.c
- drivers/leds/leds-lt3593.c
- drivers/leds/leds-netxbig.c
- drivers/leds/leds-ns2.c
- drivers/leds/leds-pca9532.c
- drivers/leds/leds-pca955x.c
- drivers/leds/leds-pca9633.c
- drivers/leds/leds-pwm.c
- drivers/leds/leds-regulator.c
- drivers/leds/leds-renesas-tpu.c
- drivers/leds/leds-s3c24xx.c
- drivers/leds/leds-tca6507.c
- drivers/leds/leds-wm831x-status.c
- drivers/leds/leds-wm8350.c
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/of.h> | 15 | #include <linux/of.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/i2c.h> | 17 | #include <linux/i2c.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
21 | #include <linux/mfd/88pm860x.h> | 21 | #include <linux/mfd/88pm860x.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | 23 | ||
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_MASK (0x7F) | 27 | #define LED_BLINK_MASK (0x7F) |
28 | 28 | ||
29 | #define LED_ON_CONTINUOUS (0x0F << 3) | 29 | #define LED_ON_CONTINUOUS (0x0F << 3) |
30 | 30 | ||
31 | #define LED1_BLINK_EN (1 << 1) | 31 | #define LED1_BLINK_EN (1 << 1) |
32 | #define LED2_BLINK_EN (1 << 2) | 32 | #define LED2_BLINK_EN (1 << 2) |
33 | 33 | ||
34 | struct pm860x_led { | 34 | struct pm860x_led { |
35 | struct led_classdev cdev; | 35 | struct led_classdev cdev; |
36 | struct i2c_client *i2c; | 36 | struct i2c_client *i2c; |
37 | struct work_struct work; | 37 | struct work_struct work; |
38 | struct pm860x_chip *chip; | 38 | struct pm860x_chip *chip; |
39 | struct mutex lock; | 39 | struct mutex lock; |
40 | char name[MFD_NAME_SIZE]; | 40 | char name[MFD_NAME_SIZE]; |
41 | 41 | ||
42 | int port; | 42 | int port; |
43 | int iset; | 43 | int iset; |
44 | unsigned char brightness; | 44 | unsigned char brightness; |
45 | unsigned char current_brightness; | 45 | unsigned char current_brightness; |
46 | 46 | ||
47 | int reg_control; | 47 | int reg_control; |
48 | int reg_blink; | 48 | int reg_blink; |
49 | int blink_mask; | 49 | int blink_mask; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | static int led_power_set(struct pm860x_chip *chip, int port, int on) | 52 | static int led_power_set(struct pm860x_chip *chip, int port, int on) |
53 | { | 53 | { |
54 | int ret = -EINVAL; | 54 | int ret = -EINVAL; |
55 | 55 | ||
56 | switch (port) { | 56 | switch (port) { |
57 | case 0: | 57 | case 0: |
58 | case 1: | 58 | case 1: |
59 | case 2: | 59 | case 2: |
60 | ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) : | 60 | ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) : |
61 | pm8606_osc_disable(chip, RGB1_ENABLE); | 61 | pm8606_osc_disable(chip, RGB1_ENABLE); |
62 | break; | 62 | break; |
63 | case 3: | 63 | case 3: |
64 | case 4: | 64 | case 4: |
65 | case 5: | 65 | case 5: |
66 | ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) : | 66 | ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) : |
67 | pm8606_osc_disable(chip, RGB2_ENABLE); | 67 | pm8606_osc_disable(chip, RGB2_ENABLE); |
68 | break; | 68 | break; |
69 | } | 69 | } |
70 | return ret; | 70 | return ret; |
71 | } | 71 | } |
72 | 72 | ||
73 | static void pm860x_led_work(struct work_struct *work) | 73 | static void pm860x_led_work(struct work_struct *work) |
74 | { | 74 | { |
75 | 75 | ||
76 | struct pm860x_led *led; | 76 | struct pm860x_led *led; |
77 | struct pm860x_chip *chip; | 77 | struct pm860x_chip *chip; |
78 | unsigned char buf[3]; | 78 | unsigned char buf[3]; |
79 | int ret; | 79 | int ret; |
80 | 80 | ||
81 | led = container_of(work, struct pm860x_led, work); | 81 | led = container_of(work, struct pm860x_led, work); |
82 | chip = led->chip; | 82 | chip = led->chip; |
83 | mutex_lock(&led->lock); | 83 | mutex_lock(&led->lock); |
84 | if ((led->current_brightness == 0) && led->brightness) { | 84 | if ((led->current_brightness == 0) && led->brightness) { |
85 | led_power_set(chip, led->port, 1); | 85 | led_power_set(chip, led->port, 1); |
86 | if (led->iset) { | 86 | if (led->iset) { |
87 | pm860x_set_bits(led->i2c, led->reg_control, | 87 | pm860x_set_bits(led->i2c, led->reg_control, |
88 | LED_CURRENT_MASK, led->iset); | 88 | LED_CURRENT_MASK, led->iset); |
89 | } | 89 | } |
90 | pm860x_set_bits(led->i2c, led->reg_blink, | 90 | pm860x_set_bits(led->i2c, led->reg_blink, |
91 | LED_BLINK_MASK, LED_ON_CONTINUOUS); | 91 | LED_BLINK_MASK, LED_ON_CONTINUOUS); |
92 | pm860x_set_bits(led->i2c, PM8606_WLED3B, led->blink_mask, | 92 | pm860x_set_bits(led->i2c, PM8606_WLED3B, led->blink_mask, |
93 | led->blink_mask); | 93 | led->blink_mask); |
94 | } | 94 | } |
95 | pm860x_set_bits(led->i2c, led->reg_control, LED_PWM_MASK, | 95 | pm860x_set_bits(led->i2c, led->reg_control, LED_PWM_MASK, |
96 | led->brightness); | 96 | led->brightness); |
97 | 97 | ||
98 | if (led->brightness == 0) { | 98 | if (led->brightness == 0) { |
99 | pm860x_bulk_read(led->i2c, led->reg_control, 3, buf); | 99 | pm860x_bulk_read(led->i2c, led->reg_control, 3, buf); |
100 | ret = buf[0] & LED_PWM_MASK; | 100 | ret = buf[0] & LED_PWM_MASK; |
101 | ret |= buf[1] & LED_PWM_MASK; | 101 | ret |= buf[1] & LED_PWM_MASK; |
102 | ret |= buf[2] & LED_PWM_MASK; | 102 | ret |= buf[2] & LED_PWM_MASK; |
103 | if (ret == 0) { | 103 | if (ret == 0) { |
104 | /* unset current since no led is lighting */ | 104 | /* unset current since no led is lighting */ |
105 | pm860x_set_bits(led->i2c, led->reg_control, | 105 | pm860x_set_bits(led->i2c, led->reg_control, |
106 | LED_CURRENT_MASK, 0); | 106 | LED_CURRENT_MASK, 0); |
107 | pm860x_set_bits(led->i2c, PM8606_WLED3B, | 107 | pm860x_set_bits(led->i2c, PM8606_WLED3B, |
108 | led->blink_mask, 0); | 108 | led->blink_mask, 0); |
109 | led_power_set(chip, led->port, 0); | 109 | led_power_set(chip, led->port, 0); |
110 | } | 110 | } |
111 | } | 111 | } |
112 | led->current_brightness = led->brightness; | 112 | led->current_brightness = led->brightness; |
113 | dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", | 113 | dev_dbg(chip->dev, "Update LED. (reg:%d, brightness:%d)\n", |
114 | led->reg_control, led->brightness); | 114 | led->reg_control, led->brightness); |
115 | mutex_unlock(&led->lock); | 115 | mutex_unlock(&led->lock); |
116 | } | 116 | } |
117 | 117 | ||
118 | static void pm860x_led_set(struct led_classdev *cdev, | 118 | static void pm860x_led_set(struct led_classdev *cdev, |
119 | enum led_brightness value) | 119 | enum led_brightness value) |
120 | { | 120 | { |
121 | struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev); | 121 | struct pm860x_led *data = container_of(cdev, struct pm860x_led, cdev); |
122 | 122 | ||
123 | data->brightness = value >> 3; | 123 | data->brightness = value >> 3; |
124 | schedule_work(&data->work); | 124 | schedule_work(&data->work); |
125 | } | 125 | } |
126 | 126 | ||
127 | #ifdef CONFIG_OF | 127 | #ifdef CONFIG_OF |
128 | static int pm860x_led_dt_init(struct platform_device *pdev, | 128 | static int pm860x_led_dt_init(struct platform_device *pdev, |
129 | struct pm860x_led *data) | 129 | struct pm860x_led *data) |
130 | { | 130 | { |
131 | struct device_node *nproot, *np; | 131 | struct device_node *nproot, *np; |
132 | int iset = 0; | 132 | int iset = 0; |
133 | 133 | ||
134 | nproot = of_node_get(pdev->dev.parent->of_node); | 134 | nproot = of_node_get(pdev->dev.parent->of_node); |
135 | if (!nproot) | 135 | if (!nproot) |
136 | return -ENODEV; | 136 | return -ENODEV; |
137 | nproot = of_find_node_by_name(nproot, "leds"); | 137 | nproot = of_find_node_by_name(nproot, "leds"); |
138 | if (!nproot) { | 138 | if (!nproot) { |
139 | dev_err(&pdev->dev, "failed to find leds node\n"); | 139 | dev_err(&pdev->dev, "failed to find leds node\n"); |
140 | return -ENODEV; | 140 | return -ENODEV; |
141 | } | 141 | } |
142 | for_each_child_of_node(nproot, np) { | 142 | for_each_child_of_node(nproot, np) { |
143 | if (!of_node_cmp(np->name, data->name)) { | 143 | if (!of_node_cmp(np->name, data->name)) { |
144 | of_property_read_u32(np, "marvell,88pm860x-iset", | 144 | of_property_read_u32(np, "marvell,88pm860x-iset", |
145 | &iset); | 145 | &iset); |
146 | data->iset = PM8606_LED_CURRENT(iset); | 146 | data->iset = PM8606_LED_CURRENT(iset); |
147 | break; | 147 | break; |
148 | } | 148 | } |
149 | } | 149 | } |
150 | of_node_put(nproot); | 150 | of_node_put(nproot); |
151 | return 0; | 151 | return 0; |
152 | } | 152 | } |
153 | #else | 153 | #else |
154 | #define pm860x_led_dt_init(x, y) (-1) | 154 | #define pm860x_led_dt_init(x, y) (-1) |
155 | #endif | 155 | #endif |
156 | 156 | ||
157 | static int pm860x_led_probe(struct platform_device *pdev) | 157 | static int pm860x_led_probe(struct platform_device *pdev) |
158 | { | 158 | { |
159 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); | 159 | struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); |
160 | struct pm860x_led_pdata *pdata = pdev->dev.platform_data; | 160 | struct pm860x_led_pdata *pdata = dev_get_platdata(&pdev->dev); |
161 | struct pm860x_led *data; | 161 | struct pm860x_led *data; |
162 | struct resource *res; | 162 | struct resource *res; |
163 | int ret = 0; | 163 | int ret = 0; |
164 | 164 | ||
165 | data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL); | 165 | data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_led), GFP_KERNEL); |
166 | if (data == NULL) | 166 | if (data == NULL) |
167 | return -ENOMEM; | 167 | return -ENOMEM; |
168 | res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control"); | 168 | res = platform_get_resource_byname(pdev, IORESOURCE_REG, "control"); |
169 | if (!res) { | 169 | if (!res) { |
170 | dev_err(&pdev->dev, "No REG resource for control\n"); | 170 | dev_err(&pdev->dev, "No REG resource for control\n"); |
171 | return -ENXIO; | 171 | return -ENXIO; |
172 | } | 172 | } |
173 | data->reg_control = res->start; | 173 | data->reg_control = res->start; |
174 | res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink"); | 174 | res = platform_get_resource_byname(pdev, IORESOURCE_REG, "blink"); |
175 | if (!res) { | 175 | if (!res) { |
176 | dev_err(&pdev->dev, "No REG resource for blink\n"); | 176 | dev_err(&pdev->dev, "No REG resource for blink\n"); |
177 | return -ENXIO; | 177 | return -ENXIO; |
178 | } | 178 | } |
179 | data->reg_blink = res->start; | 179 | data->reg_blink = res->start; |
180 | memset(data->name, 0, MFD_NAME_SIZE); | 180 | memset(data->name, 0, MFD_NAME_SIZE); |
181 | switch (pdev->id) { | 181 | switch (pdev->id) { |
182 | case 0: | 182 | case 0: |
183 | data->blink_mask = LED1_BLINK_EN; | 183 | data->blink_mask = LED1_BLINK_EN; |
184 | sprintf(data->name, "led0-red"); | 184 | sprintf(data->name, "led0-red"); |
185 | break; | 185 | break; |
186 | case 1: | 186 | case 1: |
187 | data->blink_mask = LED1_BLINK_EN; | 187 | data->blink_mask = LED1_BLINK_EN; |
188 | sprintf(data->name, "led0-green"); | 188 | sprintf(data->name, "led0-green"); |
189 | break; | 189 | break; |
190 | case 2: | 190 | case 2: |
191 | data->blink_mask = LED1_BLINK_EN; | 191 | data->blink_mask = LED1_BLINK_EN; |
192 | sprintf(data->name, "led0-blue"); | 192 | sprintf(data->name, "led0-blue"); |
193 | break; | 193 | break; |
194 | case 3: | 194 | case 3: |
195 | data->blink_mask = LED2_BLINK_EN; | 195 | data->blink_mask = LED2_BLINK_EN; |
196 | sprintf(data->name, "led1-red"); | 196 | sprintf(data->name, "led1-red"); |
197 | break; | 197 | break; |
198 | case 4: | 198 | case 4: |
199 | data->blink_mask = LED2_BLINK_EN; | 199 | data->blink_mask = LED2_BLINK_EN; |
200 | sprintf(data->name, "led1-green"); | 200 | sprintf(data->name, "led1-green"); |
201 | break; | 201 | break; |
202 | case 5: | 202 | case 5: |
203 | data->blink_mask = LED2_BLINK_EN; | 203 | data->blink_mask = LED2_BLINK_EN; |
204 | sprintf(data->name, "led1-blue"); | 204 | sprintf(data->name, "led1-blue"); |
205 | break; | 205 | break; |
206 | } | 206 | } |
207 | platform_set_drvdata(pdev, data); | 207 | platform_set_drvdata(pdev, data); |
208 | data->chip = chip; | 208 | data->chip = chip; |
209 | data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; | 209 | data->i2c = (chip->id == CHIP_PM8606) ? chip->client : chip->companion; |
210 | data->port = pdev->id; | 210 | data->port = pdev->id; |
211 | if (pm860x_led_dt_init(pdev, data)) | 211 | if (pm860x_led_dt_init(pdev, data)) |
212 | if (pdata) | 212 | if (pdata) |
213 | data->iset = pdata->iset; | 213 | data->iset = pdata->iset; |
214 | 214 | ||
215 | data->current_brightness = 0; | 215 | data->current_brightness = 0; |
216 | data->cdev.name = data->name; | 216 | data->cdev.name = data->name; |
217 | data->cdev.brightness_set = pm860x_led_set; | 217 | data->cdev.brightness_set = pm860x_led_set; |
218 | mutex_init(&data->lock); | 218 | mutex_init(&data->lock); |
219 | INIT_WORK(&data->work, pm860x_led_work); | 219 | INIT_WORK(&data->work, pm860x_led_work); |
220 | 220 | ||
221 | ret = led_classdev_register(chip->dev, &data->cdev); | 221 | ret = led_classdev_register(chip->dev, &data->cdev); |
222 | if (ret < 0) { | 222 | if (ret < 0) { |
223 | dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); | 223 | dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); |
224 | return ret; | 224 | return ret; |
225 | } | 225 | } |
226 | pm860x_led_set(&data->cdev, 0); | 226 | pm860x_led_set(&data->cdev, 0); |
227 | return 0; | 227 | return 0; |
228 | } | 228 | } |
229 | 229 | ||
230 | static int pm860x_led_remove(struct platform_device *pdev) | 230 | static int pm860x_led_remove(struct platform_device *pdev) |
231 | { | 231 | { |
232 | struct pm860x_led *data = platform_get_drvdata(pdev); | 232 | struct pm860x_led *data = platform_get_drvdata(pdev); |
233 | 233 | ||
234 | led_classdev_unregister(&data->cdev); | 234 | led_classdev_unregister(&data->cdev); |
235 | 235 | ||
236 | return 0; | 236 | return 0; |
237 | } | 237 | } |
238 | 238 | ||
239 | static struct platform_driver pm860x_led_driver = { | 239 | static struct platform_driver pm860x_led_driver = { |
240 | .driver = { | 240 | .driver = { |
241 | .name = "88pm860x-led", | 241 | .name = "88pm860x-led", |
242 | .owner = THIS_MODULE, | 242 | .owner = THIS_MODULE, |
243 | }, | 243 | }, |
244 | .probe = pm860x_led_probe, | 244 | .probe = pm860x_led_probe, |
245 | .remove = pm860x_led_remove, | 245 | .remove = pm860x_led_remove, |
246 | }; | 246 | }; |
247 | 247 | ||
248 | module_platform_driver(pm860x_led_driver); | 248 | module_platform_driver(pm860x_led_driver); |
249 | 249 | ||
250 | MODULE_DESCRIPTION("LED driver for Marvell PM860x"); | 250 | MODULE_DESCRIPTION("LED driver for Marvell PM860x"); |
251 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); | 251 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); |
252 | MODULE_LICENSE("GPL"); | 252 | MODULE_LICENSE("GPL"); |
253 | MODULE_ALIAS("platform:88pm860x-led"); | 253 | MODULE_ALIAS("platform:88pm860x-led"); |
254 | 254 |
drivers/leds/leds-adp5520.c
1 | /* | 1 | /* |
2 | * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs | 2 | * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs |
3 | * | 3 | * |
4 | * Copyright 2009 Analog Devices Inc. | 4 | * Copyright 2009 Analog Devices Inc. |
5 | * | 5 | * |
6 | * Loosely derived from leds-da903x: | 6 | * Loosely derived from leds-da903x: |
7 | * Copyright (C) 2008 Compulab, Ltd. | 7 | * Copyright (C) 2008 Compulab, Ltd. |
8 | * Mike Rapoport <mike@compulab.co.il> | 8 | * Mike Rapoport <mike@compulab.co.il> |
9 | * | 9 | * |
10 | * Copyright (C) 2006-2008 Marvell International Ltd. | 10 | * Copyright (C) 2006-2008 Marvell International Ltd. |
11 | * Eric Miao <eric.miao@marvell.com> | 11 | * Eric Miao <eric.miao@marvell.com> |
12 | * | 12 | * |
13 | * Licensed under the GPL-2 or later. | 13 | * Licensed under the GPL-2 or later. |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/leds.h> | 20 | #include <linux/leds.h> |
21 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
22 | #include <linux/mfd/adp5520.h> | 22 | #include <linux/mfd/adp5520.h> |
23 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
24 | 24 | ||
25 | struct adp5520_led { | 25 | struct adp5520_led { |
26 | struct led_classdev cdev; | 26 | struct led_classdev cdev; |
27 | struct work_struct work; | 27 | struct work_struct work; |
28 | struct device *master; | 28 | struct device *master; |
29 | enum led_brightness new_brightness; | 29 | enum led_brightness new_brightness; |
30 | int id; | 30 | int id; |
31 | int flags; | 31 | int flags; |
32 | }; | 32 | }; |
33 | 33 | ||
34 | static void adp5520_led_work(struct work_struct *work) | 34 | static void adp5520_led_work(struct work_struct *work) |
35 | { | 35 | { |
36 | struct adp5520_led *led = container_of(work, struct adp5520_led, work); | 36 | struct adp5520_led *led = container_of(work, struct adp5520_led, work); |
37 | adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1, | 37 | adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1, |
38 | led->new_brightness >> 2); | 38 | led->new_brightness >> 2); |
39 | } | 39 | } |
40 | 40 | ||
41 | static void adp5520_led_set(struct led_classdev *led_cdev, | 41 | static void adp5520_led_set(struct led_classdev *led_cdev, |
42 | enum led_brightness value) | 42 | enum led_brightness value) |
43 | { | 43 | { |
44 | struct adp5520_led *led; | 44 | struct adp5520_led *led; |
45 | 45 | ||
46 | led = container_of(led_cdev, struct adp5520_led, cdev); | 46 | led = container_of(led_cdev, struct adp5520_led, cdev); |
47 | led->new_brightness = value; | 47 | led->new_brightness = value; |
48 | schedule_work(&led->work); | 48 | schedule_work(&led->work); |
49 | } | 49 | } |
50 | 50 | ||
51 | static int adp5520_led_setup(struct adp5520_led *led) | 51 | static int adp5520_led_setup(struct adp5520_led *led) |
52 | { | 52 | { |
53 | struct device *dev = led->master; | 53 | struct device *dev = led->master; |
54 | int flags = led->flags; | 54 | int flags = led->flags; |
55 | int ret = 0; | 55 | int ret = 0; |
56 | 56 | ||
57 | switch (led->id) { | 57 | switch (led->id) { |
58 | case FLAG_ID_ADP5520_LED1_ADP5501_LED0: | 58 | case FLAG_ID_ADP5520_LED1_ADP5501_LED0: |
59 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | 59 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, |
60 | (flags >> ADP5520_FLAG_OFFT_SHIFT) & | 60 | (flags >> ADP5520_FLAG_OFFT_SHIFT) & |
61 | ADP5520_FLAG_OFFT_MASK); | 61 | ADP5520_FLAG_OFFT_MASK); |
62 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | 62 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, |
63 | ADP5520_LED1_EN); | 63 | ADP5520_LED1_EN); |
64 | break; | 64 | break; |
65 | case FLAG_ID_ADP5520_LED2_ADP5501_LED1: | 65 | case FLAG_ID_ADP5520_LED2_ADP5501_LED1: |
66 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | 66 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, |
67 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | 67 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & |
68 | ADP5520_FLAG_OFFT_MASK) << 2); | 68 | ADP5520_FLAG_OFFT_MASK) << 2); |
69 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | 69 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, |
70 | ADP5520_R3_MODE); | 70 | ADP5520_R3_MODE); |
71 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | 71 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, |
72 | ADP5520_LED2_EN); | 72 | ADP5520_LED2_EN); |
73 | break; | 73 | break; |
74 | case FLAG_ID_ADP5520_LED3_ADP5501_LED2: | 74 | case FLAG_ID_ADP5520_LED3_ADP5501_LED2: |
75 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, | 75 | ret |= adp5520_set_bits(dev, ADP5520_LED_TIME, |
76 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & | 76 | ((flags >> ADP5520_FLAG_OFFT_SHIFT) & |
77 | ADP5520_FLAG_OFFT_MASK) << 4); | 77 | ADP5520_FLAG_OFFT_MASK) << 4); |
78 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, | 78 | ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL, |
79 | ADP5520_C3_MODE); | 79 | ADP5520_C3_MODE); |
80 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, | 80 | ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL, |
81 | ADP5520_LED3_EN); | 81 | ADP5520_LED3_EN); |
82 | break; | 82 | break; |
83 | } | 83 | } |
84 | 84 | ||
85 | return ret; | 85 | return ret; |
86 | } | 86 | } |
87 | 87 | ||
88 | static int adp5520_led_prepare(struct platform_device *pdev) | 88 | static int adp5520_led_prepare(struct platform_device *pdev) |
89 | { | 89 | { |
90 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | 90 | struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); |
91 | struct device *dev = pdev->dev.parent; | 91 | struct device *dev = pdev->dev.parent; |
92 | int ret = 0; | 92 | int ret = 0; |
93 | 93 | ||
94 | ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0); | 94 | ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0); |
95 | ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0); | 95 | ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0); |
96 | ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0); | 96 | ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0); |
97 | ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6); | 97 | ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6); |
98 | ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in, | 98 | ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in, |
99 | pdata->fade_out)); | 99 | pdata->fade_out)); |
100 | 100 | ||
101 | return ret; | 101 | return ret; |
102 | } | 102 | } |
103 | 103 | ||
104 | static int adp5520_led_probe(struct platform_device *pdev) | 104 | static int adp5520_led_probe(struct platform_device *pdev) |
105 | { | 105 | { |
106 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | 106 | struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); |
107 | struct adp5520_led *led, *led_dat; | 107 | struct adp5520_led *led, *led_dat; |
108 | struct led_info *cur_led; | 108 | struct led_info *cur_led; |
109 | int ret, i; | 109 | int ret, i; |
110 | 110 | ||
111 | if (pdata == NULL) { | 111 | if (pdata == NULL) { |
112 | dev_err(&pdev->dev, "missing platform data\n"); | 112 | dev_err(&pdev->dev, "missing platform data\n"); |
113 | return -ENODEV; | 113 | return -ENODEV; |
114 | } | 114 | } |
115 | 115 | ||
116 | if (pdata->num_leds > ADP5520_01_MAXLEDS) { | 116 | if (pdata->num_leds > ADP5520_01_MAXLEDS) { |
117 | dev_err(&pdev->dev, "can't handle more than %d LEDS\n", | 117 | dev_err(&pdev->dev, "can't handle more than %d LEDS\n", |
118 | ADP5520_01_MAXLEDS); | 118 | ADP5520_01_MAXLEDS); |
119 | return -EFAULT; | 119 | return -EFAULT; |
120 | } | 120 | } |
121 | 121 | ||
122 | led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds, | 122 | led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds, |
123 | GFP_KERNEL); | 123 | GFP_KERNEL); |
124 | if (led == NULL) { | 124 | if (led == NULL) { |
125 | dev_err(&pdev->dev, "failed to alloc memory\n"); | 125 | dev_err(&pdev->dev, "failed to alloc memory\n"); |
126 | return -ENOMEM; | 126 | return -ENOMEM; |
127 | } | 127 | } |
128 | 128 | ||
129 | ret = adp5520_led_prepare(pdev); | 129 | ret = adp5520_led_prepare(pdev); |
130 | 130 | ||
131 | if (ret) { | 131 | if (ret) { |
132 | dev_err(&pdev->dev, "failed to write\n"); | 132 | dev_err(&pdev->dev, "failed to write\n"); |
133 | return ret; | 133 | return ret; |
134 | } | 134 | } |
135 | 135 | ||
136 | for (i = 0; i < pdata->num_leds; ++i) { | 136 | for (i = 0; i < pdata->num_leds; ++i) { |
137 | cur_led = &pdata->leds[i]; | 137 | cur_led = &pdata->leds[i]; |
138 | led_dat = &led[i]; | 138 | led_dat = &led[i]; |
139 | 139 | ||
140 | led_dat->cdev.name = cur_led->name; | 140 | led_dat->cdev.name = cur_led->name; |
141 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 141 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
142 | led_dat->cdev.brightness_set = adp5520_led_set; | 142 | led_dat->cdev.brightness_set = adp5520_led_set; |
143 | led_dat->cdev.brightness = LED_OFF; | 143 | led_dat->cdev.brightness = LED_OFF; |
144 | 144 | ||
145 | if (cur_led->flags & ADP5520_FLAG_LED_MASK) | 145 | if (cur_led->flags & ADP5520_FLAG_LED_MASK) |
146 | led_dat->flags = cur_led->flags; | 146 | led_dat->flags = cur_led->flags; |
147 | else | 147 | else |
148 | led_dat->flags = i + 1; | 148 | led_dat->flags = i + 1; |
149 | 149 | ||
150 | led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK; | 150 | led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK; |
151 | 151 | ||
152 | led_dat->master = pdev->dev.parent; | 152 | led_dat->master = pdev->dev.parent; |
153 | led_dat->new_brightness = LED_OFF; | 153 | led_dat->new_brightness = LED_OFF; |
154 | 154 | ||
155 | INIT_WORK(&led_dat->work, adp5520_led_work); | 155 | INIT_WORK(&led_dat->work, adp5520_led_work); |
156 | 156 | ||
157 | ret = led_classdev_register(led_dat->master, &led_dat->cdev); | 157 | ret = led_classdev_register(led_dat->master, &led_dat->cdev); |
158 | if (ret) { | 158 | if (ret) { |
159 | dev_err(&pdev->dev, "failed to register LED %d\n", | 159 | dev_err(&pdev->dev, "failed to register LED %d\n", |
160 | led_dat->id); | 160 | led_dat->id); |
161 | goto err; | 161 | goto err; |
162 | } | 162 | } |
163 | 163 | ||
164 | ret = adp5520_led_setup(led_dat); | 164 | ret = adp5520_led_setup(led_dat); |
165 | if (ret) { | 165 | if (ret) { |
166 | dev_err(&pdev->dev, "failed to write\n"); | 166 | dev_err(&pdev->dev, "failed to write\n"); |
167 | i++; | 167 | i++; |
168 | goto err; | 168 | goto err; |
169 | } | 169 | } |
170 | } | 170 | } |
171 | 171 | ||
172 | platform_set_drvdata(pdev, led); | 172 | platform_set_drvdata(pdev, led); |
173 | return 0; | 173 | return 0; |
174 | 174 | ||
175 | err: | 175 | err: |
176 | if (i > 0) { | 176 | if (i > 0) { |
177 | for (i = i - 1; i >= 0; i--) { | 177 | for (i = i - 1; i >= 0; i--) { |
178 | led_classdev_unregister(&led[i].cdev); | 178 | led_classdev_unregister(&led[i].cdev); |
179 | cancel_work_sync(&led[i].work); | 179 | cancel_work_sync(&led[i].work); |
180 | } | 180 | } |
181 | } | 181 | } |
182 | 182 | ||
183 | return ret; | 183 | return ret; |
184 | } | 184 | } |
185 | 185 | ||
186 | static int adp5520_led_remove(struct platform_device *pdev) | 186 | static int adp5520_led_remove(struct platform_device *pdev) |
187 | { | 187 | { |
188 | struct adp5520_leds_platform_data *pdata = pdev->dev.platform_data; | 188 | struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); |
189 | struct adp5520_led *led; | 189 | struct adp5520_led *led; |
190 | int i; | 190 | int i; |
191 | 191 | ||
192 | led = platform_get_drvdata(pdev); | 192 | led = platform_get_drvdata(pdev); |
193 | 193 | ||
194 | adp5520_clr_bits(led->master, ADP5520_LED_CONTROL, | 194 | adp5520_clr_bits(led->master, ADP5520_LED_CONTROL, |
195 | ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN); | 195 | ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN); |
196 | 196 | ||
197 | for (i = 0; i < pdata->num_leds; i++) { | 197 | for (i = 0; i < pdata->num_leds; i++) { |
198 | led_classdev_unregister(&led[i].cdev); | 198 | led_classdev_unregister(&led[i].cdev); |
199 | cancel_work_sync(&led[i].work); | 199 | cancel_work_sync(&led[i].work); |
200 | } | 200 | } |
201 | 201 | ||
202 | return 0; | 202 | return 0; |
203 | } | 203 | } |
204 | 204 | ||
205 | static struct platform_driver adp5520_led_driver = { | 205 | static struct platform_driver adp5520_led_driver = { |
206 | .driver = { | 206 | .driver = { |
207 | .name = "adp5520-led", | 207 | .name = "adp5520-led", |
208 | .owner = THIS_MODULE, | 208 | .owner = THIS_MODULE, |
209 | }, | 209 | }, |
210 | .probe = adp5520_led_probe, | 210 | .probe = adp5520_led_probe, |
211 | .remove = adp5520_led_remove, | 211 | .remove = adp5520_led_remove, |
212 | }; | 212 | }; |
213 | 213 | ||
214 | module_platform_driver(adp5520_led_driver); | 214 | module_platform_driver(adp5520_led_driver); |
215 | 215 | ||
216 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | 216 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
217 | MODULE_DESCRIPTION("LEDS ADP5520(01) Driver"); | 217 | MODULE_DESCRIPTION("LEDS ADP5520(01) Driver"); |
218 | MODULE_LICENSE("GPL"); | 218 | MODULE_LICENSE("GPL"); |
219 | MODULE_ALIAS("platform:adp5520-led"); | 219 | MODULE_ALIAS("platform:adp5520-led"); |
220 | 220 |
drivers/leds/leds-asic3.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.com> | 2 | * Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.com> |
3 | * | 3 | * |
4 | * This program is free software; you can redistribute it and/or modify | 4 | * This program is free software; you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License version 2 as | 5 | * it under the terms of the GNU General Public License version 2 as |
6 | * published by the Free Software Foundation. | 6 | * published by the Free Software Foundation. |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> | 10 | #include <linux/init.h> |
11 | #include <linux/platform_device.h> | 11 | #include <linux/platform_device.h> |
12 | #include <linux/leds.h> | 12 | #include <linux/leds.h> |
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | 14 | ||
15 | #include <linux/mfd/asic3.h> | 15 | #include <linux/mfd/asic3.h> |
16 | #include <linux/mfd/core.h> | 16 | #include <linux/mfd/core.h> |
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | 18 | ||
19 | /* | 19 | /* |
20 | * The HTC ASIC3 LED GPIOs are inputs, not outputs. | 20 | * The HTC ASIC3 LED GPIOs are inputs, not outputs. |
21 | * Hence we turn the LEDs on/off via the TimeBase register. | 21 | * Hence we turn the LEDs on/off via the TimeBase register. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | /* | 24 | /* |
25 | * When TimeBase is 4 the clock resolution is about 32Hz. | 25 | * When TimeBase is 4 the clock resolution is about 32Hz. |
26 | * This driver supports hardware blinking with an on+off | 26 | * This driver supports hardware blinking with an on+off |
27 | * period from 62ms (2 clocks) to 125s (4000 clocks). | 27 | * period from 62ms (2 clocks) to 125s (4000 clocks). |
28 | */ | 28 | */ |
29 | #define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000) | 29 | #define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000) |
30 | #define CLK_TO_MS(clk) (((clk)*32000)/1024) | 30 | #define CLK_TO_MS(clk) (((clk)*32000)/1024) |
31 | #define MAX_CLK 4000 /* Fits into 12-bit Time registers */ | 31 | #define MAX_CLK 4000 /* Fits into 12-bit Time registers */ |
32 | #define MAX_MS CLK_TO_MS(MAX_CLK) | 32 | #define MAX_MS CLK_TO_MS(MAX_CLK) |
33 | 33 | ||
34 | static const unsigned int led_n_base[ASIC3_NUM_LEDS] = { | 34 | static const unsigned int led_n_base[ASIC3_NUM_LEDS] = { |
35 | [0] = ASIC3_LED_0_Base, | 35 | [0] = ASIC3_LED_0_Base, |
36 | [1] = ASIC3_LED_1_Base, | 36 | [1] = ASIC3_LED_1_Base, |
37 | [2] = ASIC3_LED_2_Base, | 37 | [2] = ASIC3_LED_2_Base, |
38 | }; | 38 | }; |
39 | 39 | ||
40 | static void brightness_set(struct led_classdev *cdev, | 40 | static void brightness_set(struct led_classdev *cdev, |
41 | enum led_brightness value) | 41 | enum led_brightness value) |
42 | { | 42 | { |
43 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); | 43 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); |
44 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 44 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
45 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 45 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); |
46 | u32 timebase; | 46 | u32 timebase; |
47 | unsigned int base; | 47 | unsigned int base; |
48 | 48 | ||
49 | timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4); | 49 | timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4); |
50 | 50 | ||
51 | base = led_n_base[cell->id]; | 51 | base = led_n_base[cell->id]; |
52 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32); | 52 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32); |
53 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32); | 53 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32); |
54 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); | 54 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); |
55 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase); | 55 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase); |
56 | } | 56 | } |
57 | 57 | ||
58 | static int blink_set(struct led_classdev *cdev, | 58 | static int blink_set(struct led_classdev *cdev, |
59 | unsigned long *delay_on, | 59 | unsigned long *delay_on, |
60 | unsigned long *delay_off) | 60 | unsigned long *delay_off) |
61 | { | 61 | { |
62 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); | 62 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); |
63 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 63 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
64 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | 64 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); |
65 | u32 on; | 65 | u32 on; |
66 | u32 off; | 66 | u32 off; |
67 | unsigned int base; | 67 | unsigned int base; |
68 | 68 | ||
69 | if (*delay_on > MAX_MS || *delay_off > MAX_MS) | 69 | if (*delay_on > MAX_MS || *delay_off > MAX_MS) |
70 | return -EINVAL; | 70 | return -EINVAL; |
71 | 71 | ||
72 | if (*delay_on == 0 && *delay_off == 0) { | 72 | if (*delay_on == 0 && *delay_off == 0) { |
73 | /* If both are zero then a sensible default should be chosen */ | 73 | /* If both are zero then a sensible default should be chosen */ |
74 | on = MS_TO_CLK(500); | 74 | on = MS_TO_CLK(500); |
75 | off = MS_TO_CLK(500); | 75 | off = MS_TO_CLK(500); |
76 | } else { | 76 | } else { |
77 | on = MS_TO_CLK(*delay_on); | 77 | on = MS_TO_CLK(*delay_on); |
78 | off = MS_TO_CLK(*delay_off); | 78 | off = MS_TO_CLK(*delay_off); |
79 | if ((on + off) > MAX_CLK) | 79 | if ((on + off) > MAX_CLK) |
80 | return -EINVAL; | 80 | return -EINVAL; |
81 | } | 81 | } |
82 | 82 | ||
83 | base = led_n_base[cell->id]; | 83 | base = led_n_base[cell->id]; |
84 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off)); | 84 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off)); |
85 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on); | 85 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on); |
86 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); | 86 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); |
87 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4)); | 87 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4)); |
88 | 88 | ||
89 | *delay_on = CLK_TO_MS(on); | 89 | *delay_on = CLK_TO_MS(on); |
90 | *delay_off = CLK_TO_MS(off); | 90 | *delay_off = CLK_TO_MS(off); |
91 | 91 | ||
92 | return 0; | 92 | return 0; |
93 | } | 93 | } |
94 | 94 | ||
95 | static int asic3_led_probe(struct platform_device *pdev) | 95 | static int asic3_led_probe(struct platform_device *pdev) |
96 | { | 96 | { |
97 | struct asic3_led *led = pdev->dev.platform_data; | 97 | struct asic3_led *led = dev_get_platdata(&pdev->dev); |
98 | int ret; | 98 | int ret; |
99 | 99 | ||
100 | ret = mfd_cell_enable(pdev); | 100 | ret = mfd_cell_enable(pdev); |
101 | if (ret < 0) | 101 | if (ret < 0) |
102 | return ret; | 102 | return ret; |
103 | 103 | ||
104 | led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev), | 104 | led->cdev = devm_kzalloc(&pdev->dev, sizeof(struct led_classdev), |
105 | GFP_KERNEL); | 105 | GFP_KERNEL); |
106 | if (!led->cdev) { | 106 | if (!led->cdev) { |
107 | ret = -ENOMEM; | 107 | ret = -ENOMEM; |
108 | goto out; | 108 | goto out; |
109 | } | 109 | } |
110 | 110 | ||
111 | led->cdev->name = led->name; | 111 | led->cdev->name = led->name; |
112 | led->cdev->flags = LED_CORE_SUSPENDRESUME; | 112 | led->cdev->flags = LED_CORE_SUSPENDRESUME; |
113 | led->cdev->brightness_set = brightness_set; | 113 | led->cdev->brightness_set = brightness_set; |
114 | led->cdev->blink_set = blink_set; | 114 | led->cdev->blink_set = blink_set; |
115 | led->cdev->default_trigger = led->default_trigger; | 115 | led->cdev->default_trigger = led->default_trigger; |
116 | 116 | ||
117 | ret = led_classdev_register(&pdev->dev, led->cdev); | 117 | ret = led_classdev_register(&pdev->dev, led->cdev); |
118 | if (ret < 0) | 118 | if (ret < 0) |
119 | goto out; | 119 | goto out; |
120 | 120 | ||
121 | return 0; | 121 | return 0; |
122 | 122 | ||
123 | out: | 123 | out: |
124 | (void) mfd_cell_disable(pdev); | 124 | (void) mfd_cell_disable(pdev); |
125 | return ret; | 125 | return ret; |
126 | } | 126 | } |
127 | 127 | ||
128 | static int asic3_led_remove(struct platform_device *pdev) | 128 | static int asic3_led_remove(struct platform_device *pdev) |
129 | { | 129 | { |
130 | struct asic3_led *led = pdev->dev.platform_data; | 130 | struct asic3_led *led = dev_get_platdata(&pdev->dev); |
131 | 131 | ||
132 | led_classdev_unregister(led->cdev); | 132 | led_classdev_unregister(led->cdev); |
133 | 133 | ||
134 | return mfd_cell_disable(pdev); | 134 | return mfd_cell_disable(pdev); |
135 | } | 135 | } |
136 | 136 | ||
137 | #ifdef CONFIG_PM_SLEEP | 137 | #ifdef CONFIG_PM_SLEEP |
138 | static int asic3_led_suspend(struct device *dev) | 138 | static int asic3_led_suspend(struct device *dev) |
139 | { | 139 | { |
140 | struct platform_device *pdev = to_platform_device(dev); | 140 | struct platform_device *pdev = to_platform_device(dev); |
141 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 141 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
142 | int ret; | 142 | int ret; |
143 | 143 | ||
144 | ret = 0; | 144 | ret = 0; |
145 | if (cell->suspend) | 145 | if (cell->suspend) |
146 | ret = (*cell->suspend)(pdev); | 146 | ret = (*cell->suspend)(pdev); |
147 | 147 | ||
148 | return ret; | 148 | return ret; |
149 | } | 149 | } |
150 | 150 | ||
151 | static int asic3_led_resume(struct device *dev) | 151 | static int asic3_led_resume(struct device *dev) |
152 | { | 152 | { |
153 | struct platform_device *pdev = to_platform_device(dev); | 153 | struct platform_device *pdev = to_platform_device(dev); |
154 | const struct mfd_cell *cell = mfd_get_cell(pdev); | 154 | const struct mfd_cell *cell = mfd_get_cell(pdev); |
155 | int ret; | 155 | int ret; |
156 | 156 | ||
157 | ret = 0; | 157 | ret = 0; |
158 | if (cell->resume) | 158 | if (cell->resume) |
159 | ret = (*cell->resume)(pdev); | 159 | ret = (*cell->resume)(pdev); |
160 | 160 | ||
161 | return ret; | 161 | return ret; |
162 | } | 162 | } |
163 | #endif | 163 | #endif |
164 | 164 | ||
165 | static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume); | 165 | static SIMPLE_DEV_PM_OPS(asic3_led_pm_ops, asic3_led_suspend, asic3_led_resume); |
166 | 166 | ||
167 | static struct platform_driver asic3_led_driver = { | 167 | static struct platform_driver asic3_led_driver = { |
168 | .probe = asic3_led_probe, | 168 | .probe = asic3_led_probe, |
169 | .remove = asic3_led_remove, | 169 | .remove = asic3_led_remove, |
170 | .driver = { | 170 | .driver = { |
171 | .name = "leds-asic3", | 171 | .name = "leds-asic3", |
172 | .owner = THIS_MODULE, | 172 | .owner = THIS_MODULE, |
173 | .pm = &asic3_led_pm_ops, | 173 | .pm = &asic3_led_pm_ops, |
174 | }, | 174 | }, |
175 | }; | 175 | }; |
176 | 176 | ||
177 | module_platform_driver(asic3_led_driver); | 177 | module_platform_driver(asic3_led_driver); |
178 | 178 | ||
179 | MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); | 179 | MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); |
180 | MODULE_DESCRIPTION("HTC ASIC3 LED driver"); | 180 | MODULE_DESCRIPTION("HTC ASIC3 LED driver"); |
181 | MODULE_LICENSE("GPL"); | 181 | MODULE_LICENSE("GPL"); |
182 | MODULE_ALIAS("platform:leds-asic3"); | 182 | MODULE_ALIAS("platform:leds-asic3"); |
183 | 183 |
drivers/leds/leds-atmel-pwm.c
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <linux/platform_device.h> | 2 | #include <linux/platform_device.h> |
3 | #include <linux/leds.h> | 3 | #include <linux/leds.h> |
4 | #include <linux/io.h> | 4 | #include <linux/io.h> |
5 | #include <linux/atmel_pwm.h> | 5 | #include <linux/atmel_pwm.h> |
6 | #include <linux/slab.h> | 6 | #include <linux/slab.h> |
7 | #include <linux/module.h> | 7 | #include <linux/module.h> |
8 | 8 | ||
9 | 9 | ||
10 | struct pwmled { | 10 | struct pwmled { |
11 | struct led_classdev cdev; | 11 | struct led_classdev cdev; |
12 | struct pwm_channel pwmc; | 12 | struct pwm_channel pwmc; |
13 | struct gpio_led *desc; | 13 | struct gpio_led *desc; |
14 | u32 mult; | 14 | u32 mult; |
15 | u8 active_low; | 15 | u8 active_low; |
16 | }; | 16 | }; |
17 | 17 | ||
18 | 18 | ||
19 | /* | 19 | /* |
20 | * For simplicity, we use "brightness" as if it were a linear function | 20 | * For simplicity, we use "brightness" as if it were a linear function |
21 | * of PWM duty cycle. However, a logarithmic function of duty cycle is | 21 | * of PWM duty cycle. However, a logarithmic function of duty cycle is |
22 | * probably a better match for perceived brightness: two is half as bright | 22 | * probably a better match for perceived brightness: two is half as bright |
23 | * as four, four is half as bright as eight, etc | 23 | * as four, four is half as bright as eight, etc |
24 | */ | 24 | */ |
25 | static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b) | 25 | static void pwmled_brightness(struct led_classdev *cdev, enum led_brightness b) |
26 | { | 26 | { |
27 | struct pwmled *led; | 27 | struct pwmled *led; |
28 | 28 | ||
29 | /* update the duty cycle for the *next* period */ | 29 | /* update the duty cycle for the *next* period */ |
30 | led = container_of(cdev, struct pwmled, cdev); | 30 | led = container_of(cdev, struct pwmled, cdev); |
31 | pwm_channel_writel(&led->pwmc, PWM_CUPD, led->mult * (unsigned) b); | 31 | pwm_channel_writel(&led->pwmc, PWM_CUPD, led->mult * (unsigned) b); |
32 | } | 32 | } |
33 | 33 | ||
34 | /* | 34 | /* |
35 | * NOTE: we reuse the platform_data structure of GPIO leds, | 35 | * NOTE: we reuse the platform_data structure of GPIO leds, |
36 | * but repurpose its "gpio" number as a PWM channel number. | 36 | * but repurpose its "gpio" number as a PWM channel number. |
37 | */ | 37 | */ |
38 | static int pwmled_probe(struct platform_device *pdev) | 38 | static int pwmled_probe(struct platform_device *pdev) |
39 | { | 39 | { |
40 | const struct gpio_led_platform_data *pdata; | 40 | const struct gpio_led_platform_data *pdata; |
41 | struct pwmled *leds; | 41 | struct pwmled *leds; |
42 | int i; | 42 | int i; |
43 | int status; | 43 | int status; |
44 | 44 | ||
45 | pdata = pdev->dev.platform_data; | 45 | pdata = dev_get_platdata(&pdev->dev); |
46 | if (!pdata || pdata->num_leds < 1) | 46 | if (!pdata || pdata->num_leds < 1) |
47 | return -ENODEV; | 47 | return -ENODEV; |
48 | 48 | ||
49 | leds = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*leds), | 49 | leds = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*leds), |
50 | GFP_KERNEL); | 50 | GFP_KERNEL); |
51 | if (!leds) | 51 | if (!leds) |
52 | return -ENOMEM; | 52 | return -ENOMEM; |
53 | 53 | ||
54 | for (i = 0; i < pdata->num_leds; i++) { | 54 | for (i = 0; i < pdata->num_leds; i++) { |
55 | struct pwmled *led = leds + i; | 55 | struct pwmled *led = leds + i; |
56 | const struct gpio_led *dat = pdata->leds + i; | 56 | const struct gpio_led *dat = pdata->leds + i; |
57 | u32 tmp; | 57 | u32 tmp; |
58 | 58 | ||
59 | led->cdev.name = dat->name; | 59 | led->cdev.name = dat->name; |
60 | led->cdev.brightness = LED_OFF; | 60 | led->cdev.brightness = LED_OFF; |
61 | led->cdev.brightness_set = pwmled_brightness; | 61 | led->cdev.brightness_set = pwmled_brightness; |
62 | led->cdev.default_trigger = dat->default_trigger; | 62 | led->cdev.default_trigger = dat->default_trigger; |
63 | 63 | ||
64 | led->active_low = dat->active_low; | 64 | led->active_low = dat->active_low; |
65 | 65 | ||
66 | status = pwm_channel_alloc(dat->gpio, &led->pwmc); | 66 | status = pwm_channel_alloc(dat->gpio, &led->pwmc); |
67 | if (status < 0) | 67 | if (status < 0) |
68 | goto err; | 68 | goto err; |
69 | 69 | ||
70 | /* | 70 | /* |
71 | * Prescale clock by 2^x, so PWM counts in low MHz. | 71 | * Prescale clock by 2^x, so PWM counts in low MHz. |
72 | * Start each cycle with the LED active, so increasing | 72 | * Start each cycle with the LED active, so increasing |
73 | * the duty cycle gives us more time on (== brighter). | 73 | * the duty cycle gives us more time on (== brighter). |
74 | */ | 74 | */ |
75 | tmp = 5; | 75 | tmp = 5; |
76 | if (!led->active_low) | 76 | if (!led->active_low) |
77 | tmp |= PWM_CPR_CPOL; | 77 | tmp |= PWM_CPR_CPOL; |
78 | pwm_channel_writel(&led->pwmc, PWM_CMR, tmp); | 78 | pwm_channel_writel(&led->pwmc, PWM_CMR, tmp); |
79 | 79 | ||
80 | /* | 80 | /* |
81 | * Pick a period so PWM cycles at 100+ Hz; and a multiplier | 81 | * Pick a period so PWM cycles at 100+ Hz; and a multiplier |
82 | * for scaling duty cycle: brightness * mult. | 82 | * for scaling duty cycle: brightness * mult. |
83 | */ | 83 | */ |
84 | tmp = (led->pwmc.mck / (1 << 5)) / 100; | 84 | tmp = (led->pwmc.mck / (1 << 5)) / 100; |
85 | tmp /= 255; | 85 | tmp /= 255; |
86 | led->mult = tmp; | 86 | led->mult = tmp; |
87 | pwm_channel_writel(&led->pwmc, PWM_CDTY, | 87 | pwm_channel_writel(&led->pwmc, PWM_CDTY, |
88 | led->cdev.brightness * 255); | 88 | led->cdev.brightness * 255); |
89 | pwm_channel_writel(&led->pwmc, PWM_CPRD, | 89 | pwm_channel_writel(&led->pwmc, PWM_CPRD, |
90 | LED_FULL * tmp); | 90 | LED_FULL * tmp); |
91 | 91 | ||
92 | pwm_channel_enable(&led->pwmc); | 92 | pwm_channel_enable(&led->pwmc); |
93 | 93 | ||
94 | /* Hand it over to the LED framework */ | 94 | /* Hand it over to the LED framework */ |
95 | status = led_classdev_register(&pdev->dev, &led->cdev); | 95 | status = led_classdev_register(&pdev->dev, &led->cdev); |
96 | if (status < 0) { | 96 | if (status < 0) { |
97 | pwm_channel_free(&led->pwmc); | 97 | pwm_channel_free(&led->pwmc); |
98 | goto err; | 98 | goto err; |
99 | } | 99 | } |
100 | } | 100 | } |
101 | 101 | ||
102 | platform_set_drvdata(pdev, leds); | 102 | platform_set_drvdata(pdev, leds); |
103 | return 0; | 103 | return 0; |
104 | 104 | ||
105 | err: | 105 | err: |
106 | if (i > 0) { | 106 | if (i > 0) { |
107 | for (i = i - 1; i >= 0; i--) { | 107 | for (i = i - 1; i >= 0; i--) { |
108 | led_classdev_unregister(&leds[i].cdev); | 108 | led_classdev_unregister(&leds[i].cdev); |
109 | pwm_channel_free(&leds[i].pwmc); | 109 | pwm_channel_free(&leds[i].pwmc); |
110 | } | 110 | } |
111 | } | 111 | } |
112 | 112 | ||
113 | return status; | 113 | return status; |
114 | } | 114 | } |
115 | 115 | ||
116 | static int pwmled_remove(struct platform_device *pdev) | 116 | static int pwmled_remove(struct platform_device *pdev) |
117 | { | 117 | { |
118 | const struct gpio_led_platform_data *pdata; | 118 | const struct gpio_led_platform_data *pdata; |
119 | struct pwmled *leds; | 119 | struct pwmled *leds; |
120 | unsigned i; | 120 | unsigned i; |
121 | 121 | ||
122 | pdata = pdev->dev.platform_data; | 122 | pdata = dev_get_platdata(&pdev->dev); |
123 | leds = platform_get_drvdata(pdev); | 123 | leds = platform_get_drvdata(pdev); |
124 | 124 | ||
125 | for (i = 0; i < pdata->num_leds; i++) { | 125 | for (i = 0; i < pdata->num_leds; i++) { |
126 | struct pwmled *led = leds + i; | 126 | struct pwmled *led = leds + i; |
127 | 127 | ||
128 | led_classdev_unregister(&led->cdev); | 128 | led_classdev_unregister(&led->cdev); |
129 | pwm_channel_free(&led->pwmc); | 129 | pwm_channel_free(&led->pwmc); |
130 | } | 130 | } |
131 | 131 | ||
132 | return 0; | 132 | return 0; |
133 | } | 133 | } |
134 | 134 | ||
135 | static struct platform_driver pwmled_driver = { | 135 | static struct platform_driver pwmled_driver = { |
136 | .driver = { | 136 | .driver = { |
137 | .name = "leds-atmel-pwm", | 137 | .name = "leds-atmel-pwm", |
138 | .owner = THIS_MODULE, | 138 | .owner = THIS_MODULE, |
139 | }, | 139 | }, |
140 | /* REVISIT add suspend() and resume() methods */ | 140 | /* REVISIT add suspend() and resume() methods */ |
141 | .probe = pwmled_probe, | 141 | .probe = pwmled_probe, |
142 | .remove = pwmled_remove, | 142 | .remove = pwmled_remove, |
143 | }; | 143 | }; |
144 | 144 | ||
145 | module_platform_driver(pwmled_driver); | 145 | module_platform_driver(pwmled_driver); |
146 | 146 | ||
147 | MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness"); | 147 | MODULE_DESCRIPTION("Driver for LEDs with PWM-controlled brightness"); |
148 | MODULE_LICENSE("GPL"); | 148 | MODULE_LICENSE("GPL"); |
149 | MODULE_ALIAS("platform:leds-atmel-pwm"); | 149 | MODULE_ALIAS("platform:leds-atmel-pwm"); |
150 | 150 |
drivers/leds/leds-bd2802.c
1 | /* | 1 | /* |
2 | * leds-bd2802.c - RGB LED Driver | 2 | * leds-bd2802.c - RGB LED Driver |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Samsung Electronics | 4 | * Copyright (C) 2009 Samsung Electronics |
5 | * Kim Kyuwon <q1.kim@samsung.com> | 5 | * Kim Kyuwon <q1.kim@samsung.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 | * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf | 11 | * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/i2c.h> | 16 | #include <linux/i2c.h> |
17 | #include <linux/gpio.h> | 17 | #include <linux/gpio.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/leds.h> | 19 | #include <linux/leds.h> |
20 | #include <linux/leds-bd2802.h> | 20 | #include <linux/leds-bd2802.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/pm.h> | 22 | #include <linux/pm.h> |
23 | 23 | ||
24 | #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0)) | 24 | #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0)) |
25 | 25 | ||
26 | #define BD2802_LED_OFFSET 0xa | 26 | #define BD2802_LED_OFFSET 0xa |
27 | #define BD2802_COLOR_OFFSET 0x3 | 27 | #define BD2802_COLOR_OFFSET 0x3 |
28 | 28 | ||
29 | #define BD2802_REG_CLKSETUP 0x00 | 29 | #define BD2802_REG_CLKSETUP 0x00 |
30 | #define BD2802_REG_CONTROL 0x01 | 30 | #define BD2802_REG_CONTROL 0x01 |
31 | #define BD2802_REG_HOURSETUP 0x02 | 31 | #define BD2802_REG_HOURSETUP 0x02 |
32 | #define BD2802_REG_CURRENT1SETUP 0x03 | 32 | #define BD2802_REG_CURRENT1SETUP 0x03 |
33 | #define BD2802_REG_CURRENT2SETUP 0x04 | 33 | #define BD2802_REG_CURRENT2SETUP 0x04 |
34 | #define BD2802_REG_WAVEPATTERN 0x05 | 34 | #define BD2802_REG_WAVEPATTERN 0x05 |
35 | 35 | ||
36 | #define BD2802_CURRENT_032 0x10 /* 3.2mA */ | 36 | #define BD2802_CURRENT_032 0x10 /* 3.2mA */ |
37 | #define BD2802_CURRENT_000 0x00 /* 0.0mA */ | 37 | #define BD2802_CURRENT_000 0x00 /* 0.0mA */ |
38 | 38 | ||
39 | #define BD2802_PATTERN_FULL 0x07 | 39 | #define BD2802_PATTERN_FULL 0x07 |
40 | #define BD2802_PATTERN_HALF 0x03 | 40 | #define BD2802_PATTERN_HALF 0x03 |
41 | 41 | ||
42 | enum led_ids { | 42 | enum led_ids { |
43 | LED1, | 43 | LED1, |
44 | LED2, | 44 | LED2, |
45 | LED_NUM, | 45 | LED_NUM, |
46 | }; | 46 | }; |
47 | 47 | ||
48 | enum led_colors { | 48 | enum led_colors { |
49 | RED, | 49 | RED, |
50 | GREEN, | 50 | GREEN, |
51 | BLUE, | 51 | BLUE, |
52 | }; | 52 | }; |
53 | 53 | ||
54 | enum led_bits { | 54 | enum led_bits { |
55 | BD2802_OFF, | 55 | BD2802_OFF, |
56 | BD2802_BLINK, | 56 | BD2802_BLINK, |
57 | BD2802_ON, | 57 | BD2802_ON, |
58 | }; | 58 | }; |
59 | 59 | ||
60 | /* | 60 | /* |
61 | * State '0' : 'off' | 61 | * State '0' : 'off' |
62 | * State '1' : 'blink' | 62 | * State '1' : 'blink' |
63 | * State '2' : 'on'. | 63 | * State '2' : 'on'. |
64 | */ | 64 | */ |
65 | struct led_state { | 65 | struct led_state { |
66 | unsigned r:2; | 66 | unsigned r:2; |
67 | unsigned g:2; | 67 | unsigned g:2; |
68 | unsigned b:2; | 68 | unsigned b:2; |
69 | }; | 69 | }; |
70 | 70 | ||
71 | struct bd2802_led { | 71 | struct bd2802_led { |
72 | struct bd2802_led_platform_data *pdata; | 72 | struct bd2802_led_platform_data *pdata; |
73 | struct i2c_client *client; | 73 | struct i2c_client *client; |
74 | struct rw_semaphore rwsem; | 74 | struct rw_semaphore rwsem; |
75 | struct work_struct work; | 75 | struct work_struct work; |
76 | 76 | ||
77 | struct led_state led[2]; | 77 | struct led_state led[2]; |
78 | 78 | ||
79 | /* | 79 | /* |
80 | * Making led_classdev as array is not recommended, because array | 80 | * Making led_classdev as array is not recommended, because array |
81 | * members prevent using 'container_of' macro. So repetitive works | 81 | * members prevent using 'container_of' macro. So repetitive works |
82 | * are needed. | 82 | * are needed. |
83 | */ | 83 | */ |
84 | struct led_classdev cdev_led1r; | 84 | struct led_classdev cdev_led1r; |
85 | struct led_classdev cdev_led1g; | 85 | struct led_classdev cdev_led1g; |
86 | struct led_classdev cdev_led1b; | 86 | struct led_classdev cdev_led1b; |
87 | struct led_classdev cdev_led2r; | 87 | struct led_classdev cdev_led2r; |
88 | struct led_classdev cdev_led2g; | 88 | struct led_classdev cdev_led2g; |
89 | struct led_classdev cdev_led2b; | 89 | struct led_classdev cdev_led2b; |
90 | 90 | ||
91 | /* | 91 | /* |
92 | * Advanced Configuration Function(ADF) mode: | 92 | * Advanced Configuration Function(ADF) mode: |
93 | * In ADF mode, user can set registers of BD2802GU directly, | 93 | * In ADF mode, user can set registers of BD2802GU directly, |
94 | * therefore BD2802GU doesn't enter reset state. | 94 | * therefore BD2802GU doesn't enter reset state. |
95 | */ | 95 | */ |
96 | int adf_on; | 96 | int adf_on; |
97 | 97 | ||
98 | enum led_ids led_id; | 98 | enum led_ids led_id; |
99 | enum led_colors color; | 99 | enum led_colors color; |
100 | enum led_bits state; | 100 | enum led_bits state; |
101 | 101 | ||
102 | /* General attributes of RGB LEDs */ | 102 | /* General attributes of RGB LEDs */ |
103 | int wave_pattern; | 103 | int wave_pattern; |
104 | int rgb_current; | 104 | int rgb_current; |
105 | }; | 105 | }; |
106 | 106 | ||
107 | 107 | ||
108 | /*--------------------------------------------------------------*/ | 108 | /*--------------------------------------------------------------*/ |
109 | /* BD2802GU helper functions */ | 109 | /* BD2802GU helper functions */ |
110 | /*--------------------------------------------------------------*/ | 110 | /*--------------------------------------------------------------*/ |
111 | 111 | ||
112 | static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id, | 112 | static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id, |
113 | enum led_colors color) | 113 | enum led_colors color) |
114 | { | 114 | { |
115 | switch (color) { | 115 | switch (color) { |
116 | case RED: | 116 | case RED: |
117 | return !led->led[id].r; | 117 | return !led->led[id].r; |
118 | case GREEN: | 118 | case GREEN: |
119 | return !led->led[id].g; | 119 | return !led->led[id].g; |
120 | case BLUE: | 120 | case BLUE: |
121 | return !led->led[id].b; | 121 | return !led->led[id].b; |
122 | default: | 122 | default: |
123 | dev_err(&led->client->dev, "%s: Invalid color\n", __func__); | 123 | dev_err(&led->client->dev, "%s: Invalid color\n", __func__); |
124 | return -EINVAL; | 124 | return -EINVAL; |
125 | } | 125 | } |
126 | } | 126 | } |
127 | 127 | ||
128 | static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id) | 128 | static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id) |
129 | { | 129 | { |
130 | if (led->led[id].r || led->led[id].g || led->led[id].b) | 130 | if (led->led[id].r || led->led[id].g || led->led[id].b) |
131 | return 0; | 131 | return 0; |
132 | 132 | ||
133 | return 1; | 133 | return 1; |
134 | } | 134 | } |
135 | 135 | ||
136 | static inline int bd2802_is_all_off(struct bd2802_led *led) | 136 | static inline int bd2802_is_all_off(struct bd2802_led *led) |
137 | { | 137 | { |
138 | int i; | 138 | int i; |
139 | 139 | ||
140 | for (i = 0; i < LED_NUM; i++) | 140 | for (i = 0; i < LED_NUM; i++) |
141 | if (!bd2802_is_led_off(led, i)) | 141 | if (!bd2802_is_led_off(led, i)) |
142 | return 0; | 142 | return 0; |
143 | 143 | ||
144 | return 1; | 144 | return 1; |
145 | } | 145 | } |
146 | 146 | ||
147 | static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color) | 147 | static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color) |
148 | { | 148 | { |
149 | return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET; | 149 | return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET; |
150 | } | 150 | } |
151 | 151 | ||
152 | static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color, | 152 | static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color, |
153 | u8 reg_offset) | 153 | u8 reg_offset) |
154 | { | 154 | { |
155 | return reg_offset + bd2802_get_base_offset(id, color); | 155 | return reg_offset + bd2802_get_base_offset(id, color); |
156 | } | 156 | } |
157 | 157 | ||
158 | 158 | ||
159 | /*--------------------------------------------------------------*/ | 159 | /*--------------------------------------------------------------*/ |
160 | /* BD2802GU core functions */ | 160 | /* BD2802GU core functions */ |
161 | /*--------------------------------------------------------------*/ | 161 | /*--------------------------------------------------------------*/ |
162 | 162 | ||
163 | static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val) | 163 | static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val) |
164 | { | 164 | { |
165 | int ret = i2c_smbus_write_byte_data(client, reg, val); | 165 | int ret = i2c_smbus_write_byte_data(client, reg, val); |
166 | if (ret >= 0) | 166 | if (ret >= 0) |
167 | return 0; | 167 | return 0; |
168 | 168 | ||
169 | dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", | 169 | dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n", |
170 | __func__, reg, val, ret); | 170 | __func__, reg, val, ret); |
171 | 171 | ||
172 | return ret; | 172 | return ret; |
173 | } | 173 | } |
174 | 174 | ||
175 | static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, | 175 | static void bd2802_update_state(struct bd2802_led *led, enum led_ids id, |
176 | enum led_colors color, enum led_bits led_bit) | 176 | enum led_colors color, enum led_bits led_bit) |
177 | { | 177 | { |
178 | int i; | 178 | int i; |
179 | u8 value; | 179 | u8 value; |
180 | 180 | ||
181 | for (i = 0; i < LED_NUM; i++) { | 181 | for (i = 0; i < LED_NUM; i++) { |
182 | if (i == id) { | 182 | if (i == id) { |
183 | switch (color) { | 183 | switch (color) { |
184 | case RED: | 184 | case RED: |
185 | led->led[i].r = led_bit; | 185 | led->led[i].r = led_bit; |
186 | break; | 186 | break; |
187 | case GREEN: | 187 | case GREEN: |
188 | led->led[i].g = led_bit; | 188 | led->led[i].g = led_bit; |
189 | break; | 189 | break; |
190 | case BLUE: | 190 | case BLUE: |
191 | led->led[i].b = led_bit; | 191 | led->led[i].b = led_bit; |
192 | break; | 192 | break; |
193 | default: | 193 | default: |
194 | dev_err(&led->client->dev, | 194 | dev_err(&led->client->dev, |
195 | "%s: Invalid color\n", __func__); | 195 | "%s: Invalid color\n", __func__); |
196 | return; | 196 | return; |
197 | } | 197 | } |
198 | } | 198 | } |
199 | } | 199 | } |
200 | 200 | ||
201 | if (led_bit == BD2802_BLINK || led_bit == BD2802_ON) | 201 | if (led_bit == BD2802_BLINK || led_bit == BD2802_ON) |
202 | return; | 202 | return; |
203 | 203 | ||
204 | if (!bd2802_is_led_off(led, id)) | 204 | if (!bd2802_is_led_off(led, id)) |
205 | return; | 205 | return; |
206 | 206 | ||
207 | if (bd2802_is_all_off(led) && !led->adf_on) { | 207 | if (bd2802_is_all_off(led) && !led->adf_on) { |
208 | gpio_set_value(led->pdata->reset_gpio, 0); | 208 | gpio_set_value(led->pdata->reset_gpio, 0); |
209 | return; | 209 | return; |
210 | } | 210 | } |
211 | 211 | ||
212 | /* | 212 | /* |
213 | * In this case, other led is turned on, and current led is turned | 213 | * In this case, other led is turned on, and current led is turned |
214 | * off. So set RGB LED Control register to stop the current RGB LED | 214 | * off. So set RGB LED Control register to stop the current RGB LED |
215 | */ | 215 | */ |
216 | value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1); | 216 | value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1); |
217 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); | 217 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); |
218 | } | 218 | } |
219 | 219 | ||
220 | static void bd2802_configure(struct bd2802_led *led) | 220 | static void bd2802_configure(struct bd2802_led *led) |
221 | { | 221 | { |
222 | struct bd2802_led_platform_data *pdata = led->pdata; | 222 | struct bd2802_led_platform_data *pdata = led->pdata; |
223 | u8 reg; | 223 | u8 reg; |
224 | 224 | ||
225 | reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP); | 225 | reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP); |
226 | bd2802_write_byte(led->client, reg, pdata->rgb_time); | 226 | bd2802_write_byte(led->client, reg, pdata->rgb_time); |
227 | 227 | ||
228 | reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP); | 228 | reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP); |
229 | bd2802_write_byte(led->client, reg, pdata->rgb_time); | 229 | bd2802_write_byte(led->client, reg, pdata->rgb_time); |
230 | } | 230 | } |
231 | 231 | ||
232 | static void bd2802_reset_cancel(struct bd2802_led *led) | 232 | static void bd2802_reset_cancel(struct bd2802_led *led) |
233 | { | 233 | { |
234 | gpio_set_value(led->pdata->reset_gpio, 1); | 234 | gpio_set_value(led->pdata->reset_gpio, 1); |
235 | udelay(100); | 235 | udelay(100); |
236 | bd2802_configure(led); | 236 | bd2802_configure(led); |
237 | } | 237 | } |
238 | 238 | ||
239 | static void bd2802_enable(struct bd2802_led *led, enum led_ids id) | 239 | static void bd2802_enable(struct bd2802_led *led, enum led_ids id) |
240 | { | 240 | { |
241 | enum led_ids other_led = (id == LED1) ? LED2 : LED1; | 241 | enum led_ids other_led = (id == LED1) ? LED2 : LED1; |
242 | u8 value, other_led_on; | 242 | u8 value, other_led_on; |
243 | 243 | ||
244 | other_led_on = !bd2802_is_led_off(led, other_led); | 244 | other_led_on = !bd2802_is_led_off(led, other_led); |
245 | if (id == LED1) | 245 | if (id == LED1) |
246 | value = LED_CTL(other_led_on, 1); | 246 | value = LED_CTL(other_led_on, 1); |
247 | else | 247 | else |
248 | value = LED_CTL(1 , other_led_on); | 248 | value = LED_CTL(1 , other_led_on); |
249 | 249 | ||
250 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); | 250 | bd2802_write_byte(led->client, BD2802_REG_CONTROL, value); |
251 | } | 251 | } |
252 | 252 | ||
253 | static void bd2802_set_on(struct bd2802_led *led, enum led_ids id, | 253 | static void bd2802_set_on(struct bd2802_led *led, enum led_ids id, |
254 | enum led_colors color) | 254 | enum led_colors color) |
255 | { | 255 | { |
256 | u8 reg; | 256 | u8 reg; |
257 | 257 | ||
258 | if (bd2802_is_all_off(led) && !led->adf_on) | 258 | if (bd2802_is_all_off(led) && !led->adf_on) |
259 | bd2802_reset_cancel(led); | 259 | bd2802_reset_cancel(led); |
260 | 260 | ||
261 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | 261 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); |
262 | bd2802_write_byte(led->client, reg, led->rgb_current); | 262 | bd2802_write_byte(led->client, reg, led->rgb_current); |
263 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | 263 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); |
264 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 264 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
265 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | 265 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); |
266 | bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL); | 266 | bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL); |
267 | 267 | ||
268 | bd2802_enable(led, id); | 268 | bd2802_enable(led, id); |
269 | bd2802_update_state(led, id, color, BD2802_ON); | 269 | bd2802_update_state(led, id, color, BD2802_ON); |
270 | } | 270 | } |
271 | 271 | ||
272 | static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id, | 272 | static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id, |
273 | enum led_colors color) | 273 | enum led_colors color) |
274 | { | 274 | { |
275 | u8 reg; | 275 | u8 reg; |
276 | 276 | ||
277 | if (bd2802_is_all_off(led) && !led->adf_on) | 277 | if (bd2802_is_all_off(led) && !led->adf_on) |
278 | bd2802_reset_cancel(led); | 278 | bd2802_reset_cancel(led); |
279 | 279 | ||
280 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | 280 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); |
281 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 281 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
282 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | 282 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); |
283 | bd2802_write_byte(led->client, reg, led->rgb_current); | 283 | bd2802_write_byte(led->client, reg, led->rgb_current); |
284 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); | 284 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN); |
285 | bd2802_write_byte(led->client, reg, led->wave_pattern); | 285 | bd2802_write_byte(led->client, reg, led->wave_pattern); |
286 | 286 | ||
287 | bd2802_enable(led, id); | 287 | bd2802_enable(led, id); |
288 | bd2802_update_state(led, id, color, BD2802_BLINK); | 288 | bd2802_update_state(led, id, color, BD2802_BLINK); |
289 | } | 289 | } |
290 | 290 | ||
291 | static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id, | 291 | static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id, |
292 | enum led_colors color, enum led_bits led_bit) | 292 | enum led_colors color, enum led_bits led_bit) |
293 | { | 293 | { |
294 | if (led_bit == BD2802_OFF) { | 294 | if (led_bit == BD2802_OFF) { |
295 | dev_err(&led->client->dev, | 295 | dev_err(&led->client->dev, |
296 | "Only 'blink' and 'on' are allowed\n"); | 296 | "Only 'blink' and 'on' are allowed\n"); |
297 | return; | 297 | return; |
298 | } | 298 | } |
299 | 299 | ||
300 | if (led_bit == BD2802_BLINK) | 300 | if (led_bit == BD2802_BLINK) |
301 | bd2802_set_blink(led, id, color); | 301 | bd2802_set_blink(led, id, color); |
302 | else | 302 | else |
303 | bd2802_set_on(led, id, color); | 303 | bd2802_set_on(led, id, color); |
304 | } | 304 | } |
305 | 305 | ||
306 | static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id, | 306 | static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id, |
307 | enum led_colors color) | 307 | enum led_colors color) |
308 | { | 308 | { |
309 | u8 reg; | 309 | u8 reg; |
310 | 310 | ||
311 | if (bd2802_is_rgb_off(led, id, color)) | 311 | if (bd2802_is_rgb_off(led, id, color)) |
312 | return; | 312 | return; |
313 | 313 | ||
314 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); | 314 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP); |
315 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 315 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
316 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); | 316 | reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP); |
317 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); | 317 | bd2802_write_byte(led->client, reg, BD2802_CURRENT_000); |
318 | 318 | ||
319 | bd2802_update_state(led, id, color, BD2802_OFF); | 319 | bd2802_update_state(led, id, color, BD2802_OFF); |
320 | } | 320 | } |
321 | 321 | ||
322 | #define BD2802_SET_REGISTER(reg_addr, reg_name) \ | 322 | #define BD2802_SET_REGISTER(reg_addr, reg_name) \ |
323 | static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \ | 323 | static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \ |
324 | struct device_attribute *attr, const char *buf, size_t count) \ | 324 | struct device_attribute *attr, const char *buf, size_t count) \ |
325 | { \ | 325 | { \ |
326 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | 326 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ |
327 | unsigned long val; \ | 327 | unsigned long val; \ |
328 | int ret; \ | 328 | int ret; \ |
329 | if (!count) \ | 329 | if (!count) \ |
330 | return -EINVAL; \ | 330 | return -EINVAL; \ |
331 | ret = kstrtoul(buf, 16, &val); \ | 331 | ret = kstrtoul(buf, 16, &val); \ |
332 | if (ret) \ | 332 | if (ret) \ |
333 | return ret; \ | 333 | return ret; \ |
334 | down_write(&led->rwsem); \ | 334 | down_write(&led->rwsem); \ |
335 | bd2802_write_byte(led->client, reg_addr, (u8) val); \ | 335 | bd2802_write_byte(led->client, reg_addr, (u8) val); \ |
336 | up_write(&led->rwsem); \ | 336 | up_write(&led->rwsem); \ |
337 | return count; \ | 337 | return count; \ |
338 | } \ | 338 | } \ |
339 | static struct device_attribute bd2802_reg##reg_addr##_attr = { \ | 339 | static struct device_attribute bd2802_reg##reg_addr##_attr = { \ |
340 | .attr = {.name = reg_name, .mode = 0644}, \ | 340 | .attr = {.name = reg_name, .mode = 0644}, \ |
341 | .store = bd2802_store_reg##reg_addr, \ | 341 | .store = bd2802_store_reg##reg_addr, \ |
342 | }; | 342 | }; |
343 | 343 | ||
344 | BD2802_SET_REGISTER(0x00, "0x00"); | 344 | BD2802_SET_REGISTER(0x00, "0x00"); |
345 | BD2802_SET_REGISTER(0x01, "0x01"); | 345 | BD2802_SET_REGISTER(0x01, "0x01"); |
346 | BD2802_SET_REGISTER(0x02, "0x02"); | 346 | BD2802_SET_REGISTER(0x02, "0x02"); |
347 | BD2802_SET_REGISTER(0x03, "0x03"); | 347 | BD2802_SET_REGISTER(0x03, "0x03"); |
348 | BD2802_SET_REGISTER(0x04, "0x04"); | 348 | BD2802_SET_REGISTER(0x04, "0x04"); |
349 | BD2802_SET_REGISTER(0x05, "0x05"); | 349 | BD2802_SET_REGISTER(0x05, "0x05"); |
350 | BD2802_SET_REGISTER(0x06, "0x06"); | 350 | BD2802_SET_REGISTER(0x06, "0x06"); |
351 | BD2802_SET_REGISTER(0x07, "0x07"); | 351 | BD2802_SET_REGISTER(0x07, "0x07"); |
352 | BD2802_SET_REGISTER(0x08, "0x08"); | 352 | BD2802_SET_REGISTER(0x08, "0x08"); |
353 | BD2802_SET_REGISTER(0x09, "0x09"); | 353 | BD2802_SET_REGISTER(0x09, "0x09"); |
354 | BD2802_SET_REGISTER(0x0a, "0x0a"); | 354 | BD2802_SET_REGISTER(0x0a, "0x0a"); |
355 | BD2802_SET_REGISTER(0x0b, "0x0b"); | 355 | BD2802_SET_REGISTER(0x0b, "0x0b"); |
356 | BD2802_SET_REGISTER(0x0c, "0x0c"); | 356 | BD2802_SET_REGISTER(0x0c, "0x0c"); |
357 | BD2802_SET_REGISTER(0x0d, "0x0d"); | 357 | BD2802_SET_REGISTER(0x0d, "0x0d"); |
358 | BD2802_SET_REGISTER(0x0e, "0x0e"); | 358 | BD2802_SET_REGISTER(0x0e, "0x0e"); |
359 | BD2802_SET_REGISTER(0x0f, "0x0f"); | 359 | BD2802_SET_REGISTER(0x0f, "0x0f"); |
360 | BD2802_SET_REGISTER(0x10, "0x10"); | 360 | BD2802_SET_REGISTER(0x10, "0x10"); |
361 | BD2802_SET_REGISTER(0x11, "0x11"); | 361 | BD2802_SET_REGISTER(0x11, "0x11"); |
362 | BD2802_SET_REGISTER(0x12, "0x12"); | 362 | BD2802_SET_REGISTER(0x12, "0x12"); |
363 | BD2802_SET_REGISTER(0x13, "0x13"); | 363 | BD2802_SET_REGISTER(0x13, "0x13"); |
364 | BD2802_SET_REGISTER(0x14, "0x14"); | 364 | BD2802_SET_REGISTER(0x14, "0x14"); |
365 | BD2802_SET_REGISTER(0x15, "0x15"); | 365 | BD2802_SET_REGISTER(0x15, "0x15"); |
366 | 366 | ||
367 | static struct device_attribute *bd2802_addr_attributes[] = { | 367 | static struct device_attribute *bd2802_addr_attributes[] = { |
368 | &bd2802_reg0x00_attr, | 368 | &bd2802_reg0x00_attr, |
369 | &bd2802_reg0x01_attr, | 369 | &bd2802_reg0x01_attr, |
370 | &bd2802_reg0x02_attr, | 370 | &bd2802_reg0x02_attr, |
371 | &bd2802_reg0x03_attr, | 371 | &bd2802_reg0x03_attr, |
372 | &bd2802_reg0x04_attr, | 372 | &bd2802_reg0x04_attr, |
373 | &bd2802_reg0x05_attr, | 373 | &bd2802_reg0x05_attr, |
374 | &bd2802_reg0x06_attr, | 374 | &bd2802_reg0x06_attr, |
375 | &bd2802_reg0x07_attr, | 375 | &bd2802_reg0x07_attr, |
376 | &bd2802_reg0x08_attr, | 376 | &bd2802_reg0x08_attr, |
377 | &bd2802_reg0x09_attr, | 377 | &bd2802_reg0x09_attr, |
378 | &bd2802_reg0x0a_attr, | 378 | &bd2802_reg0x0a_attr, |
379 | &bd2802_reg0x0b_attr, | 379 | &bd2802_reg0x0b_attr, |
380 | &bd2802_reg0x0c_attr, | 380 | &bd2802_reg0x0c_attr, |
381 | &bd2802_reg0x0d_attr, | 381 | &bd2802_reg0x0d_attr, |
382 | &bd2802_reg0x0e_attr, | 382 | &bd2802_reg0x0e_attr, |
383 | &bd2802_reg0x0f_attr, | 383 | &bd2802_reg0x0f_attr, |
384 | &bd2802_reg0x10_attr, | 384 | &bd2802_reg0x10_attr, |
385 | &bd2802_reg0x11_attr, | 385 | &bd2802_reg0x11_attr, |
386 | &bd2802_reg0x12_attr, | 386 | &bd2802_reg0x12_attr, |
387 | &bd2802_reg0x13_attr, | 387 | &bd2802_reg0x13_attr, |
388 | &bd2802_reg0x14_attr, | 388 | &bd2802_reg0x14_attr, |
389 | &bd2802_reg0x15_attr, | 389 | &bd2802_reg0x15_attr, |
390 | }; | 390 | }; |
391 | 391 | ||
392 | static void bd2802_enable_adv_conf(struct bd2802_led *led) | 392 | static void bd2802_enable_adv_conf(struct bd2802_led *led) |
393 | { | 393 | { |
394 | int i, ret; | 394 | int i, ret; |
395 | 395 | ||
396 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) { | 396 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) { |
397 | ret = device_create_file(&led->client->dev, | 397 | ret = device_create_file(&led->client->dev, |
398 | bd2802_addr_attributes[i]); | 398 | bd2802_addr_attributes[i]); |
399 | if (ret) { | 399 | if (ret) { |
400 | dev_err(&led->client->dev, "failed: sysfs file %s\n", | 400 | dev_err(&led->client->dev, "failed: sysfs file %s\n", |
401 | bd2802_addr_attributes[i]->attr.name); | 401 | bd2802_addr_attributes[i]->attr.name); |
402 | goto failed_remove_files; | 402 | goto failed_remove_files; |
403 | } | 403 | } |
404 | } | 404 | } |
405 | 405 | ||
406 | if (bd2802_is_all_off(led)) | 406 | if (bd2802_is_all_off(led)) |
407 | bd2802_reset_cancel(led); | 407 | bd2802_reset_cancel(led); |
408 | 408 | ||
409 | led->adf_on = 1; | 409 | led->adf_on = 1; |
410 | 410 | ||
411 | return; | 411 | return; |
412 | 412 | ||
413 | failed_remove_files: | 413 | failed_remove_files: |
414 | for (i--; i >= 0; i--) | 414 | for (i--; i >= 0; i--) |
415 | device_remove_file(&led->client->dev, | 415 | device_remove_file(&led->client->dev, |
416 | bd2802_addr_attributes[i]); | 416 | bd2802_addr_attributes[i]); |
417 | } | 417 | } |
418 | 418 | ||
419 | static void bd2802_disable_adv_conf(struct bd2802_led *led) | 419 | static void bd2802_disable_adv_conf(struct bd2802_led *led) |
420 | { | 420 | { |
421 | int i; | 421 | int i; |
422 | 422 | ||
423 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) | 423 | for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) |
424 | device_remove_file(&led->client->dev, | 424 | device_remove_file(&led->client->dev, |
425 | bd2802_addr_attributes[i]); | 425 | bd2802_addr_attributes[i]); |
426 | 426 | ||
427 | if (bd2802_is_all_off(led)) | 427 | if (bd2802_is_all_off(led)) |
428 | gpio_set_value(led->pdata->reset_gpio, 0); | 428 | gpio_set_value(led->pdata->reset_gpio, 0); |
429 | 429 | ||
430 | led->adf_on = 0; | 430 | led->adf_on = 0; |
431 | } | 431 | } |
432 | 432 | ||
433 | static ssize_t bd2802_show_adv_conf(struct device *dev, | 433 | static ssize_t bd2802_show_adv_conf(struct device *dev, |
434 | struct device_attribute *attr, char *buf) | 434 | struct device_attribute *attr, char *buf) |
435 | { | 435 | { |
436 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 436 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
437 | ssize_t ret; | 437 | ssize_t ret; |
438 | 438 | ||
439 | down_read(&led->rwsem); | 439 | down_read(&led->rwsem); |
440 | if (led->adf_on) | 440 | if (led->adf_on) |
441 | ret = sprintf(buf, "on\n"); | 441 | ret = sprintf(buf, "on\n"); |
442 | else | 442 | else |
443 | ret = sprintf(buf, "off\n"); | 443 | ret = sprintf(buf, "off\n"); |
444 | up_read(&led->rwsem); | 444 | up_read(&led->rwsem); |
445 | 445 | ||
446 | return ret; | 446 | return ret; |
447 | } | 447 | } |
448 | 448 | ||
449 | static ssize_t bd2802_store_adv_conf(struct device *dev, | 449 | static ssize_t bd2802_store_adv_conf(struct device *dev, |
450 | struct device_attribute *attr, const char *buf, size_t count) | 450 | struct device_attribute *attr, const char *buf, size_t count) |
451 | { | 451 | { |
452 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 452 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
453 | 453 | ||
454 | if (!count) | 454 | if (!count) |
455 | return -EINVAL; | 455 | return -EINVAL; |
456 | 456 | ||
457 | down_write(&led->rwsem); | 457 | down_write(&led->rwsem); |
458 | if (!led->adf_on && !strncmp(buf, "on", 2)) | 458 | if (!led->adf_on && !strncmp(buf, "on", 2)) |
459 | bd2802_enable_adv_conf(led); | 459 | bd2802_enable_adv_conf(led); |
460 | else if (led->adf_on && !strncmp(buf, "off", 3)) | 460 | else if (led->adf_on && !strncmp(buf, "off", 3)) |
461 | bd2802_disable_adv_conf(led); | 461 | bd2802_disable_adv_conf(led); |
462 | up_write(&led->rwsem); | 462 | up_write(&led->rwsem); |
463 | 463 | ||
464 | return count; | 464 | return count; |
465 | } | 465 | } |
466 | 466 | ||
467 | static struct device_attribute bd2802_adv_conf_attr = { | 467 | static struct device_attribute bd2802_adv_conf_attr = { |
468 | .attr = { | 468 | .attr = { |
469 | .name = "advanced_configuration", | 469 | .name = "advanced_configuration", |
470 | .mode = 0644, | 470 | .mode = 0644, |
471 | }, | 471 | }, |
472 | .show = bd2802_show_adv_conf, | 472 | .show = bd2802_show_adv_conf, |
473 | .store = bd2802_store_adv_conf, | 473 | .store = bd2802_store_adv_conf, |
474 | }; | 474 | }; |
475 | 475 | ||
476 | #define BD2802_CONTROL_ATTR(attr_name, name_str) \ | 476 | #define BD2802_CONTROL_ATTR(attr_name, name_str) \ |
477 | static ssize_t bd2802_show_##attr_name(struct device *dev, \ | 477 | static ssize_t bd2802_show_##attr_name(struct device *dev, \ |
478 | struct device_attribute *attr, char *buf) \ | 478 | struct device_attribute *attr, char *buf) \ |
479 | { \ | 479 | { \ |
480 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | 480 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ |
481 | ssize_t ret; \ | 481 | ssize_t ret; \ |
482 | down_read(&led->rwsem); \ | 482 | down_read(&led->rwsem); \ |
483 | ret = sprintf(buf, "0x%02x\n", led->attr_name); \ | 483 | ret = sprintf(buf, "0x%02x\n", led->attr_name); \ |
484 | up_read(&led->rwsem); \ | 484 | up_read(&led->rwsem); \ |
485 | return ret; \ | 485 | return ret; \ |
486 | } \ | 486 | } \ |
487 | static ssize_t bd2802_store_##attr_name(struct device *dev, \ | 487 | static ssize_t bd2802_store_##attr_name(struct device *dev, \ |
488 | struct device_attribute *attr, const char *buf, size_t count) \ | 488 | struct device_attribute *attr, const char *buf, size_t count) \ |
489 | { \ | 489 | { \ |
490 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ | 490 | struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\ |
491 | unsigned long val; \ | 491 | unsigned long val; \ |
492 | int ret; \ | 492 | int ret; \ |
493 | if (!count) \ | 493 | if (!count) \ |
494 | return -EINVAL; \ | 494 | return -EINVAL; \ |
495 | ret = kstrtoul(buf, 16, &val); \ | 495 | ret = kstrtoul(buf, 16, &val); \ |
496 | if (ret) \ | 496 | if (ret) \ |
497 | return ret; \ | 497 | return ret; \ |
498 | down_write(&led->rwsem); \ | 498 | down_write(&led->rwsem); \ |
499 | led->attr_name = val; \ | 499 | led->attr_name = val; \ |
500 | up_write(&led->rwsem); \ | 500 | up_write(&led->rwsem); \ |
501 | return count; \ | 501 | return count; \ |
502 | } \ | 502 | } \ |
503 | static struct device_attribute bd2802_##attr_name##_attr = { \ | 503 | static struct device_attribute bd2802_##attr_name##_attr = { \ |
504 | .attr = { \ | 504 | .attr = { \ |
505 | .name = name_str, \ | 505 | .name = name_str, \ |
506 | .mode = 0644, \ | 506 | .mode = 0644, \ |
507 | }, \ | 507 | }, \ |
508 | .show = bd2802_show_##attr_name, \ | 508 | .show = bd2802_show_##attr_name, \ |
509 | .store = bd2802_store_##attr_name, \ | 509 | .store = bd2802_store_##attr_name, \ |
510 | }; | 510 | }; |
511 | 511 | ||
512 | BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern"); | 512 | BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern"); |
513 | BD2802_CONTROL_ATTR(rgb_current, "rgb_current"); | 513 | BD2802_CONTROL_ATTR(rgb_current, "rgb_current"); |
514 | 514 | ||
515 | static struct device_attribute *bd2802_attributes[] = { | 515 | static struct device_attribute *bd2802_attributes[] = { |
516 | &bd2802_adv_conf_attr, | 516 | &bd2802_adv_conf_attr, |
517 | &bd2802_wave_pattern_attr, | 517 | &bd2802_wave_pattern_attr, |
518 | &bd2802_rgb_current_attr, | 518 | &bd2802_rgb_current_attr, |
519 | }; | 519 | }; |
520 | 520 | ||
521 | static void bd2802_led_work(struct work_struct *work) | 521 | static void bd2802_led_work(struct work_struct *work) |
522 | { | 522 | { |
523 | struct bd2802_led *led = container_of(work, struct bd2802_led, work); | 523 | struct bd2802_led *led = container_of(work, struct bd2802_led, work); |
524 | 524 | ||
525 | if (led->state) | 525 | if (led->state) |
526 | bd2802_turn_on(led, led->led_id, led->color, led->state); | 526 | bd2802_turn_on(led, led->led_id, led->color, led->state); |
527 | else | 527 | else |
528 | bd2802_turn_off(led, led->led_id, led->color); | 528 | bd2802_turn_off(led, led->led_id, led->color); |
529 | } | 529 | } |
530 | 530 | ||
531 | #define BD2802_CONTROL_RGBS(name, id, clr) \ | 531 | #define BD2802_CONTROL_RGBS(name, id, clr) \ |
532 | static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\ | 532 | static void bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\ |
533 | enum led_brightness value) \ | 533 | enum led_brightness value) \ |
534 | { \ | 534 | { \ |
535 | struct bd2802_led *led = \ | 535 | struct bd2802_led *led = \ |
536 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ | 536 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ |
537 | led->led_id = id; \ | 537 | led->led_id = id; \ |
538 | led->color = clr; \ | 538 | led->color = clr; \ |
539 | if (value == LED_OFF) \ | 539 | if (value == LED_OFF) \ |
540 | led->state = BD2802_OFF; \ | 540 | led->state = BD2802_OFF; \ |
541 | else \ | 541 | else \ |
542 | led->state = BD2802_ON; \ | 542 | led->state = BD2802_ON; \ |
543 | schedule_work(&led->work); \ | 543 | schedule_work(&led->work); \ |
544 | } \ | 544 | } \ |
545 | static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \ | 545 | static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \ |
546 | unsigned long *delay_on, unsigned long *delay_off) \ | 546 | unsigned long *delay_on, unsigned long *delay_off) \ |
547 | { \ | 547 | { \ |
548 | struct bd2802_led *led = \ | 548 | struct bd2802_led *led = \ |
549 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ | 549 | container_of(led_cdev, struct bd2802_led, cdev_##name); \ |
550 | if (*delay_on == 0 || *delay_off == 0) \ | 550 | if (*delay_on == 0 || *delay_off == 0) \ |
551 | return -EINVAL; \ | 551 | return -EINVAL; \ |
552 | led->led_id = id; \ | 552 | led->led_id = id; \ |
553 | led->color = clr; \ | 553 | led->color = clr; \ |
554 | led->state = BD2802_BLINK; \ | 554 | led->state = BD2802_BLINK; \ |
555 | schedule_work(&led->work); \ | 555 | schedule_work(&led->work); \ |
556 | return 0; \ | 556 | return 0; \ |
557 | } | 557 | } |
558 | 558 | ||
559 | BD2802_CONTROL_RGBS(led1r, LED1, RED); | 559 | BD2802_CONTROL_RGBS(led1r, LED1, RED); |
560 | BD2802_CONTROL_RGBS(led1g, LED1, GREEN); | 560 | BD2802_CONTROL_RGBS(led1g, LED1, GREEN); |
561 | BD2802_CONTROL_RGBS(led1b, LED1, BLUE); | 561 | BD2802_CONTROL_RGBS(led1b, LED1, BLUE); |
562 | BD2802_CONTROL_RGBS(led2r, LED2, RED); | 562 | BD2802_CONTROL_RGBS(led2r, LED2, RED); |
563 | BD2802_CONTROL_RGBS(led2g, LED2, GREEN); | 563 | BD2802_CONTROL_RGBS(led2g, LED2, GREEN); |
564 | BD2802_CONTROL_RGBS(led2b, LED2, BLUE); | 564 | BD2802_CONTROL_RGBS(led2b, LED2, BLUE); |
565 | 565 | ||
566 | static int bd2802_register_led_classdev(struct bd2802_led *led) | 566 | static int bd2802_register_led_classdev(struct bd2802_led *led) |
567 | { | 567 | { |
568 | int ret; | 568 | int ret; |
569 | 569 | ||
570 | INIT_WORK(&led->work, bd2802_led_work); | 570 | INIT_WORK(&led->work, bd2802_led_work); |
571 | 571 | ||
572 | led->cdev_led1r.name = "led1_R"; | 572 | led->cdev_led1r.name = "led1_R"; |
573 | led->cdev_led1r.brightness = LED_OFF; | 573 | led->cdev_led1r.brightness = LED_OFF; |
574 | led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; | 574 | led->cdev_led1r.brightness_set = bd2802_set_led1r_brightness; |
575 | led->cdev_led1r.blink_set = bd2802_set_led1r_blink; | 575 | led->cdev_led1r.blink_set = bd2802_set_led1r_blink; |
576 | 576 | ||
577 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); | 577 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1r); |
578 | if (ret < 0) { | 578 | if (ret < 0) { |
579 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 579 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
580 | led->cdev_led1r.name); | 580 | led->cdev_led1r.name); |
581 | goto failed_unregister_led1_R; | 581 | goto failed_unregister_led1_R; |
582 | } | 582 | } |
583 | 583 | ||
584 | led->cdev_led1g.name = "led1_G"; | 584 | led->cdev_led1g.name = "led1_G"; |
585 | led->cdev_led1g.brightness = LED_OFF; | 585 | led->cdev_led1g.brightness = LED_OFF; |
586 | led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; | 586 | led->cdev_led1g.brightness_set = bd2802_set_led1g_brightness; |
587 | led->cdev_led1g.blink_set = bd2802_set_led1g_blink; | 587 | led->cdev_led1g.blink_set = bd2802_set_led1g_blink; |
588 | 588 | ||
589 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); | 589 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1g); |
590 | if (ret < 0) { | 590 | if (ret < 0) { |
591 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 591 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
592 | led->cdev_led1g.name); | 592 | led->cdev_led1g.name); |
593 | goto failed_unregister_led1_G; | 593 | goto failed_unregister_led1_G; |
594 | } | 594 | } |
595 | 595 | ||
596 | led->cdev_led1b.name = "led1_B"; | 596 | led->cdev_led1b.name = "led1_B"; |
597 | led->cdev_led1b.brightness = LED_OFF; | 597 | led->cdev_led1b.brightness = LED_OFF; |
598 | led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; | 598 | led->cdev_led1b.brightness_set = bd2802_set_led1b_brightness; |
599 | led->cdev_led1b.blink_set = bd2802_set_led1b_blink; | 599 | led->cdev_led1b.blink_set = bd2802_set_led1b_blink; |
600 | 600 | ||
601 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); | 601 | ret = led_classdev_register(&led->client->dev, &led->cdev_led1b); |
602 | if (ret < 0) { | 602 | if (ret < 0) { |
603 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 603 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
604 | led->cdev_led1b.name); | 604 | led->cdev_led1b.name); |
605 | goto failed_unregister_led1_B; | 605 | goto failed_unregister_led1_B; |
606 | } | 606 | } |
607 | 607 | ||
608 | led->cdev_led2r.name = "led2_R"; | 608 | led->cdev_led2r.name = "led2_R"; |
609 | led->cdev_led2r.brightness = LED_OFF; | 609 | led->cdev_led2r.brightness = LED_OFF; |
610 | led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; | 610 | led->cdev_led2r.brightness_set = bd2802_set_led2r_brightness; |
611 | led->cdev_led2r.blink_set = bd2802_set_led2r_blink; | 611 | led->cdev_led2r.blink_set = bd2802_set_led2r_blink; |
612 | 612 | ||
613 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); | 613 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2r); |
614 | if (ret < 0) { | 614 | if (ret < 0) { |
615 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 615 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
616 | led->cdev_led2r.name); | 616 | led->cdev_led2r.name); |
617 | goto failed_unregister_led2_R; | 617 | goto failed_unregister_led2_R; |
618 | } | 618 | } |
619 | 619 | ||
620 | led->cdev_led2g.name = "led2_G"; | 620 | led->cdev_led2g.name = "led2_G"; |
621 | led->cdev_led2g.brightness = LED_OFF; | 621 | led->cdev_led2g.brightness = LED_OFF; |
622 | led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; | 622 | led->cdev_led2g.brightness_set = bd2802_set_led2g_brightness; |
623 | led->cdev_led2g.blink_set = bd2802_set_led2g_blink; | 623 | led->cdev_led2g.blink_set = bd2802_set_led2g_blink; |
624 | 624 | ||
625 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); | 625 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2g); |
626 | if (ret < 0) { | 626 | if (ret < 0) { |
627 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 627 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
628 | led->cdev_led2g.name); | 628 | led->cdev_led2g.name); |
629 | goto failed_unregister_led2_G; | 629 | goto failed_unregister_led2_G; |
630 | } | 630 | } |
631 | 631 | ||
632 | led->cdev_led2b.name = "led2_B"; | 632 | led->cdev_led2b.name = "led2_B"; |
633 | led->cdev_led2b.brightness = LED_OFF; | 633 | led->cdev_led2b.brightness = LED_OFF; |
634 | led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness; | 634 | led->cdev_led2b.brightness_set = bd2802_set_led2b_brightness; |
635 | led->cdev_led2b.blink_set = bd2802_set_led2b_blink; | 635 | led->cdev_led2b.blink_set = bd2802_set_led2b_blink; |
636 | led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME; | 636 | led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME; |
637 | 637 | ||
638 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2b); | 638 | ret = led_classdev_register(&led->client->dev, &led->cdev_led2b); |
639 | if (ret < 0) { | 639 | if (ret < 0) { |
640 | dev_err(&led->client->dev, "couldn't register LED %s\n", | 640 | dev_err(&led->client->dev, "couldn't register LED %s\n", |
641 | led->cdev_led2b.name); | 641 | led->cdev_led2b.name); |
642 | goto failed_unregister_led2_B; | 642 | goto failed_unregister_led2_B; |
643 | } | 643 | } |
644 | 644 | ||
645 | return 0; | 645 | return 0; |
646 | 646 | ||
647 | failed_unregister_led2_B: | 647 | failed_unregister_led2_B: |
648 | led_classdev_unregister(&led->cdev_led2g); | 648 | led_classdev_unregister(&led->cdev_led2g); |
649 | failed_unregister_led2_G: | 649 | failed_unregister_led2_G: |
650 | led_classdev_unregister(&led->cdev_led2r); | 650 | led_classdev_unregister(&led->cdev_led2r); |
651 | failed_unregister_led2_R: | 651 | failed_unregister_led2_R: |
652 | led_classdev_unregister(&led->cdev_led1b); | 652 | led_classdev_unregister(&led->cdev_led1b); |
653 | failed_unregister_led1_B: | 653 | failed_unregister_led1_B: |
654 | led_classdev_unregister(&led->cdev_led1g); | 654 | led_classdev_unregister(&led->cdev_led1g); |
655 | failed_unregister_led1_G: | 655 | failed_unregister_led1_G: |
656 | led_classdev_unregister(&led->cdev_led1r); | 656 | led_classdev_unregister(&led->cdev_led1r); |
657 | failed_unregister_led1_R: | 657 | failed_unregister_led1_R: |
658 | 658 | ||
659 | return ret; | 659 | return ret; |
660 | } | 660 | } |
661 | 661 | ||
662 | static void bd2802_unregister_led_classdev(struct bd2802_led *led) | 662 | static void bd2802_unregister_led_classdev(struct bd2802_led *led) |
663 | { | 663 | { |
664 | cancel_work_sync(&led->work); | 664 | cancel_work_sync(&led->work); |
665 | led_classdev_unregister(&led->cdev_led2b); | 665 | led_classdev_unregister(&led->cdev_led2b); |
666 | led_classdev_unregister(&led->cdev_led2g); | 666 | led_classdev_unregister(&led->cdev_led2g); |
667 | led_classdev_unregister(&led->cdev_led2r); | 667 | led_classdev_unregister(&led->cdev_led2r); |
668 | led_classdev_unregister(&led->cdev_led1b); | 668 | led_classdev_unregister(&led->cdev_led1b); |
669 | led_classdev_unregister(&led->cdev_led1g); | 669 | led_classdev_unregister(&led->cdev_led1g); |
670 | led_classdev_unregister(&led->cdev_led1r); | 670 | led_classdev_unregister(&led->cdev_led1r); |
671 | } | 671 | } |
672 | 672 | ||
673 | static int bd2802_probe(struct i2c_client *client, | 673 | static int bd2802_probe(struct i2c_client *client, |
674 | const struct i2c_device_id *id) | 674 | const struct i2c_device_id *id) |
675 | { | 675 | { |
676 | struct bd2802_led *led; | 676 | struct bd2802_led *led; |
677 | struct bd2802_led_platform_data *pdata; | 677 | struct bd2802_led_platform_data *pdata; |
678 | int ret, i; | 678 | int ret, i; |
679 | 679 | ||
680 | led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL); | 680 | led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL); |
681 | if (!led) { | 681 | if (!led) { |
682 | dev_err(&client->dev, "failed to allocate driver data\n"); | 682 | dev_err(&client->dev, "failed to allocate driver data\n"); |
683 | return -ENOMEM; | 683 | return -ENOMEM; |
684 | } | 684 | } |
685 | 685 | ||
686 | led->client = client; | 686 | led->client = client; |
687 | pdata = led->pdata = client->dev.platform_data; | 687 | pdata = led->pdata = dev_get_platdata(&client->dev); |
688 | i2c_set_clientdata(client, led); | 688 | i2c_set_clientdata(client, led); |
689 | 689 | ||
690 | /* Configure RESET GPIO (L: RESET, H: RESET cancel) */ | 690 | /* Configure RESET GPIO (L: RESET, H: RESET cancel) */ |
691 | gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, "RGB_RESETB"); | 691 | gpio_request_one(pdata->reset_gpio, GPIOF_OUT_INIT_HIGH, "RGB_RESETB"); |
692 | 692 | ||
693 | /* Tacss = min 0.1ms */ | 693 | /* Tacss = min 0.1ms */ |
694 | udelay(100); | 694 | udelay(100); |
695 | 695 | ||
696 | /* Detect BD2802GU */ | 696 | /* Detect BD2802GU */ |
697 | ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00); | 697 | ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00); |
698 | if (ret < 0) { | 698 | if (ret < 0) { |
699 | dev_err(&client->dev, "failed to detect device\n"); | 699 | dev_err(&client->dev, "failed to detect device\n"); |
700 | return ret; | 700 | return ret; |
701 | } else | 701 | } else |
702 | dev_info(&client->dev, "return 0x%02x\n", ret); | 702 | dev_info(&client->dev, "return 0x%02x\n", ret); |
703 | 703 | ||
704 | /* To save the power, reset BD2802 after detecting */ | 704 | /* To save the power, reset BD2802 after detecting */ |
705 | gpio_set_value(led->pdata->reset_gpio, 0); | 705 | gpio_set_value(led->pdata->reset_gpio, 0); |
706 | 706 | ||
707 | /* Default attributes */ | 707 | /* Default attributes */ |
708 | led->wave_pattern = BD2802_PATTERN_HALF; | 708 | led->wave_pattern = BD2802_PATTERN_HALF; |
709 | led->rgb_current = BD2802_CURRENT_032; | 709 | led->rgb_current = BD2802_CURRENT_032; |
710 | 710 | ||
711 | init_rwsem(&led->rwsem); | 711 | init_rwsem(&led->rwsem); |
712 | 712 | ||
713 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) { | 713 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) { |
714 | ret = device_create_file(&led->client->dev, | 714 | ret = device_create_file(&led->client->dev, |
715 | bd2802_attributes[i]); | 715 | bd2802_attributes[i]); |
716 | if (ret) { | 716 | if (ret) { |
717 | dev_err(&led->client->dev, "failed: sysfs file %s\n", | 717 | dev_err(&led->client->dev, "failed: sysfs file %s\n", |
718 | bd2802_attributes[i]->attr.name); | 718 | bd2802_attributes[i]->attr.name); |
719 | goto failed_unregister_dev_file; | 719 | goto failed_unregister_dev_file; |
720 | } | 720 | } |
721 | } | 721 | } |
722 | 722 | ||
723 | ret = bd2802_register_led_classdev(led); | 723 | ret = bd2802_register_led_classdev(led); |
724 | if (ret < 0) | 724 | if (ret < 0) |
725 | goto failed_unregister_dev_file; | 725 | goto failed_unregister_dev_file; |
726 | 726 | ||
727 | return 0; | 727 | return 0; |
728 | 728 | ||
729 | failed_unregister_dev_file: | 729 | failed_unregister_dev_file: |
730 | for (i--; i >= 0; i--) | 730 | for (i--; i >= 0; i--) |
731 | device_remove_file(&led->client->dev, bd2802_attributes[i]); | 731 | device_remove_file(&led->client->dev, bd2802_attributes[i]); |
732 | return ret; | 732 | return ret; |
733 | } | 733 | } |
734 | 734 | ||
735 | static int bd2802_remove(struct i2c_client *client) | 735 | static int bd2802_remove(struct i2c_client *client) |
736 | { | 736 | { |
737 | struct bd2802_led *led = i2c_get_clientdata(client); | 737 | struct bd2802_led *led = i2c_get_clientdata(client); |
738 | int i; | 738 | int i; |
739 | 739 | ||
740 | gpio_set_value(led->pdata->reset_gpio, 0); | 740 | gpio_set_value(led->pdata->reset_gpio, 0); |
741 | bd2802_unregister_led_classdev(led); | 741 | bd2802_unregister_led_classdev(led); |
742 | if (led->adf_on) | 742 | if (led->adf_on) |
743 | bd2802_disable_adv_conf(led); | 743 | bd2802_disable_adv_conf(led); |
744 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) | 744 | for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) |
745 | device_remove_file(&led->client->dev, bd2802_attributes[i]); | 745 | device_remove_file(&led->client->dev, bd2802_attributes[i]); |
746 | 746 | ||
747 | return 0; | 747 | return 0; |
748 | } | 748 | } |
749 | 749 | ||
750 | #ifdef CONFIG_PM_SLEEP | 750 | #ifdef CONFIG_PM_SLEEP |
751 | static void bd2802_restore_state(struct bd2802_led *led) | 751 | static void bd2802_restore_state(struct bd2802_led *led) |
752 | { | 752 | { |
753 | int i; | 753 | int i; |
754 | 754 | ||
755 | for (i = 0; i < LED_NUM; i++) { | 755 | for (i = 0; i < LED_NUM; i++) { |
756 | if (led->led[i].r) | 756 | if (led->led[i].r) |
757 | bd2802_turn_on(led, i, RED, led->led[i].r); | 757 | bd2802_turn_on(led, i, RED, led->led[i].r); |
758 | if (led->led[i].g) | 758 | if (led->led[i].g) |
759 | bd2802_turn_on(led, i, GREEN, led->led[i].g); | 759 | bd2802_turn_on(led, i, GREEN, led->led[i].g); |
760 | if (led->led[i].b) | 760 | if (led->led[i].b) |
761 | bd2802_turn_on(led, i, BLUE, led->led[i].b); | 761 | bd2802_turn_on(led, i, BLUE, led->led[i].b); |
762 | } | 762 | } |
763 | } | 763 | } |
764 | 764 | ||
765 | static int bd2802_suspend(struct device *dev) | 765 | static int bd2802_suspend(struct device *dev) |
766 | { | 766 | { |
767 | struct i2c_client *client = to_i2c_client(dev); | 767 | struct i2c_client *client = to_i2c_client(dev); |
768 | struct bd2802_led *led = i2c_get_clientdata(client); | 768 | struct bd2802_led *led = i2c_get_clientdata(client); |
769 | 769 | ||
770 | gpio_set_value(led->pdata->reset_gpio, 0); | 770 | gpio_set_value(led->pdata->reset_gpio, 0); |
771 | 771 | ||
772 | return 0; | 772 | return 0; |
773 | } | 773 | } |
774 | 774 | ||
775 | static int bd2802_resume(struct device *dev) | 775 | static int bd2802_resume(struct device *dev) |
776 | { | 776 | { |
777 | struct i2c_client *client = to_i2c_client(dev); | 777 | struct i2c_client *client = to_i2c_client(dev); |
778 | struct bd2802_led *led = i2c_get_clientdata(client); | 778 | struct bd2802_led *led = i2c_get_clientdata(client); |
779 | 779 | ||
780 | if (!bd2802_is_all_off(led) || led->adf_on) { | 780 | if (!bd2802_is_all_off(led) || led->adf_on) { |
781 | bd2802_reset_cancel(led); | 781 | bd2802_reset_cancel(led); |
782 | bd2802_restore_state(led); | 782 | bd2802_restore_state(led); |
783 | } | 783 | } |
784 | 784 | ||
785 | return 0; | 785 | return 0; |
786 | } | 786 | } |
787 | #endif | 787 | #endif |
788 | 788 | ||
789 | static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume); | 789 | static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume); |
790 | 790 | ||
791 | static const struct i2c_device_id bd2802_id[] = { | 791 | static const struct i2c_device_id bd2802_id[] = { |
792 | { "BD2802", 0 }, | 792 | { "BD2802", 0 }, |
793 | { } | 793 | { } |
794 | }; | 794 | }; |
795 | MODULE_DEVICE_TABLE(i2c, bd2802_id); | 795 | MODULE_DEVICE_TABLE(i2c, bd2802_id); |
796 | 796 | ||
797 | static struct i2c_driver bd2802_i2c_driver = { | 797 | static struct i2c_driver bd2802_i2c_driver = { |
798 | .driver = { | 798 | .driver = { |
799 | .name = "BD2802", | 799 | .name = "BD2802", |
800 | .pm = &bd2802_pm, | 800 | .pm = &bd2802_pm, |
801 | }, | 801 | }, |
802 | .probe = bd2802_probe, | 802 | .probe = bd2802_probe, |
803 | .remove = bd2802_remove, | 803 | .remove = bd2802_remove, |
804 | .id_table = bd2802_id, | 804 | .id_table = bd2802_id, |
805 | }; | 805 | }; |
806 | 806 | ||
807 | module_i2c_driver(bd2802_i2c_driver); | 807 | module_i2c_driver(bd2802_i2c_driver); |
808 | 808 | ||
809 | MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); | 809 | MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>"); |
810 | MODULE_DESCRIPTION("BD2802 LED driver"); | 810 | MODULE_DESCRIPTION("BD2802 LED driver"); |
811 | MODULE_LICENSE("GPL v2"); | 811 | MODULE_LICENSE("GPL v2"); |
812 | 812 |
drivers/leds/leds-da903x.c
1 | /* | 1 | /* |
2 | * LEDs driver for Dialog Semiconductor DA9030/DA9034 | 2 | * LEDs driver for Dialog Semiconductor DA9030/DA9034 |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Compulab, Ltd. | 4 | * Copyright (C) 2008 Compulab, Ltd. |
5 | * Mike Rapoport <mike@compulab.co.il> | 5 | * Mike Rapoport <mike@compulab.co.il> |
6 | * | 6 | * |
7 | * Copyright (C) 2006-2008 Marvell International Ltd. | 7 | * Copyright (C) 2006-2008 Marvell International Ltd. |
8 | * Eric Miao <eric.miao@marvell.com> | 8 | * Eric Miao <eric.miao@marvell.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/leds.h> | 19 | #include <linux/leds.h> |
20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
21 | #include <linux/mfd/da903x.h> | 21 | #include <linux/mfd/da903x.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | 23 | ||
24 | #define DA9030_LED1_CONTROL 0x20 | 24 | #define DA9030_LED1_CONTROL 0x20 |
25 | #define DA9030_LED2_CONTROL 0x21 | 25 | #define DA9030_LED2_CONTROL 0x21 |
26 | #define DA9030_LED3_CONTROL 0x22 | 26 | #define DA9030_LED3_CONTROL 0x22 |
27 | #define DA9030_LED4_CONTROL 0x23 | 27 | #define DA9030_LED4_CONTROL 0x23 |
28 | #define DA9030_LEDPC_CONTROL 0x24 | 28 | #define DA9030_LEDPC_CONTROL 0x24 |
29 | #define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */ | 29 | #define DA9030_MISC_CONTROL_A 0x26 /* Vibrator Control */ |
30 | 30 | ||
31 | #define DA9034_LED1_CONTROL 0x35 | 31 | #define DA9034_LED1_CONTROL 0x35 |
32 | #define DA9034_LED2_CONTROL 0x36 | 32 | #define DA9034_LED2_CONTROL 0x36 |
33 | #define DA9034_VIBRA 0x40 | 33 | #define DA9034_VIBRA 0x40 |
34 | 34 | ||
35 | struct da903x_led { | 35 | struct da903x_led { |
36 | struct led_classdev cdev; | 36 | struct led_classdev cdev; |
37 | struct work_struct work; | 37 | struct work_struct work; |
38 | struct device *master; | 38 | struct device *master; |
39 | enum led_brightness new_brightness; | 39 | enum led_brightness new_brightness; |
40 | int id; | 40 | int id; |
41 | int flags; | 41 | int flags; |
42 | }; | 42 | }; |
43 | 43 | ||
44 | #define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1) | 44 | #define DA9030_LED_OFFSET(id) ((id) - DA9030_ID_LED_1) |
45 | #define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1) | 45 | #define DA9034_LED_OFFSET(id) ((id) - DA9034_ID_LED_1) |
46 | 46 | ||
47 | static void da903x_led_work(struct work_struct *work) | 47 | static void da903x_led_work(struct work_struct *work) |
48 | { | 48 | { |
49 | struct da903x_led *led = container_of(work, struct da903x_led, work); | 49 | struct da903x_led *led = container_of(work, struct da903x_led, work); |
50 | uint8_t val; | 50 | uint8_t val; |
51 | int offset; | 51 | int offset; |
52 | 52 | ||
53 | switch (led->id) { | 53 | switch (led->id) { |
54 | case DA9030_ID_LED_1: | 54 | case DA9030_ID_LED_1: |
55 | case DA9030_ID_LED_2: | 55 | case DA9030_ID_LED_2: |
56 | case DA9030_ID_LED_3: | 56 | case DA9030_ID_LED_3: |
57 | case DA9030_ID_LED_4: | 57 | case DA9030_ID_LED_4: |
58 | case DA9030_ID_LED_PC: | 58 | case DA9030_ID_LED_PC: |
59 | offset = DA9030_LED_OFFSET(led->id); | 59 | offset = DA9030_LED_OFFSET(led->id); |
60 | val = led->flags & ~0x87; | 60 | val = led->flags & ~0x87; |
61 | val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ | 61 | val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ |
62 | val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */ | 62 | val |= (0x7 - (led->new_brightness >> 5)) & 0x7; /* PWM<2:0> */ |
63 | da903x_write(led->master, DA9030_LED1_CONTROL + offset, val); | 63 | da903x_write(led->master, DA9030_LED1_CONTROL + offset, val); |
64 | break; | 64 | break; |
65 | case DA9030_ID_VIBRA: | 65 | case DA9030_ID_VIBRA: |
66 | val = led->flags & ~0x80; | 66 | val = led->flags & ~0x80; |
67 | val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ | 67 | val |= (led->new_brightness) ? 0x80 : 0; /* EN bit */ |
68 | da903x_write(led->master, DA9030_MISC_CONTROL_A, val); | 68 | da903x_write(led->master, DA9030_MISC_CONTROL_A, val); |
69 | break; | 69 | break; |
70 | case DA9034_ID_LED_1: | 70 | case DA9034_ID_LED_1: |
71 | case DA9034_ID_LED_2: | 71 | case DA9034_ID_LED_2: |
72 | offset = DA9034_LED_OFFSET(led->id); | 72 | offset = DA9034_LED_OFFSET(led->id); |
73 | val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f; | 73 | val = (led->new_brightness * 0x5f / LED_FULL) & 0x7f; |
74 | val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0; | 74 | val |= (led->flags & DA9034_LED_RAMP) ? 0x80 : 0; |
75 | da903x_write(led->master, DA9034_LED1_CONTROL + offset, val); | 75 | da903x_write(led->master, DA9034_LED1_CONTROL + offset, val); |
76 | break; | 76 | break; |
77 | case DA9034_ID_VIBRA: | 77 | case DA9034_ID_VIBRA: |
78 | val = led->new_brightness & 0xfe; | 78 | val = led->new_brightness & 0xfe; |
79 | da903x_write(led->master, DA9034_VIBRA, val); | 79 | da903x_write(led->master, DA9034_VIBRA, val); |
80 | break; | 80 | break; |
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | static void da903x_led_set(struct led_classdev *led_cdev, | 84 | static void da903x_led_set(struct led_classdev *led_cdev, |
85 | enum led_brightness value) | 85 | enum led_brightness value) |
86 | { | 86 | { |
87 | struct da903x_led *led; | 87 | struct da903x_led *led; |
88 | 88 | ||
89 | led = container_of(led_cdev, struct da903x_led, cdev); | 89 | led = container_of(led_cdev, struct da903x_led, cdev); |
90 | led->new_brightness = value; | 90 | led->new_brightness = value; |
91 | schedule_work(&led->work); | 91 | schedule_work(&led->work); |
92 | } | 92 | } |
93 | 93 | ||
94 | static int da903x_led_probe(struct platform_device *pdev) | 94 | static int da903x_led_probe(struct platform_device *pdev) |
95 | { | 95 | { |
96 | struct led_info *pdata = pdev->dev.platform_data; | 96 | struct led_info *pdata = dev_get_platdata(&pdev->dev); |
97 | struct da903x_led *led; | 97 | struct da903x_led *led; |
98 | int id, ret; | 98 | int id, ret; |
99 | 99 | ||
100 | if (pdata == NULL) | 100 | if (pdata == NULL) |
101 | return 0; | 101 | return 0; |
102 | 102 | ||
103 | id = pdev->id; | 103 | id = pdev->id; |
104 | 104 | ||
105 | if (!((id >= DA9030_ID_LED_1 && id <= DA9030_ID_VIBRA) || | 105 | if (!((id >= DA9030_ID_LED_1 && id <= DA9030_ID_VIBRA) || |
106 | (id >= DA9034_ID_LED_1 && id <= DA9034_ID_VIBRA))) { | 106 | (id >= DA9034_ID_LED_1 && id <= DA9034_ID_VIBRA))) { |
107 | dev_err(&pdev->dev, "invalid LED ID (%d) specified\n", id); | 107 | dev_err(&pdev->dev, "invalid LED ID (%d) specified\n", id); |
108 | return -EINVAL; | 108 | return -EINVAL; |
109 | } | 109 | } |
110 | 110 | ||
111 | led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL); | 111 | led = devm_kzalloc(&pdev->dev, sizeof(struct da903x_led), GFP_KERNEL); |
112 | if (led == NULL) { | 112 | if (led == NULL) { |
113 | dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id); | 113 | dev_err(&pdev->dev, "failed to alloc memory for LED%d\n", id); |
114 | return -ENOMEM; | 114 | return -ENOMEM; |
115 | } | 115 | } |
116 | 116 | ||
117 | led->cdev.name = pdata->name; | 117 | led->cdev.name = pdata->name; |
118 | led->cdev.default_trigger = pdata->default_trigger; | 118 | led->cdev.default_trigger = pdata->default_trigger; |
119 | led->cdev.brightness_set = da903x_led_set; | 119 | led->cdev.brightness_set = da903x_led_set; |
120 | led->cdev.brightness = LED_OFF; | 120 | led->cdev.brightness = LED_OFF; |
121 | 121 | ||
122 | led->id = id; | 122 | led->id = id; |
123 | led->flags = pdata->flags; | 123 | led->flags = pdata->flags; |
124 | led->master = pdev->dev.parent; | 124 | led->master = pdev->dev.parent; |
125 | led->new_brightness = LED_OFF; | 125 | led->new_brightness = LED_OFF; |
126 | 126 | ||
127 | INIT_WORK(&led->work, da903x_led_work); | 127 | INIT_WORK(&led->work, da903x_led_work); |
128 | 128 | ||
129 | ret = led_classdev_register(led->master, &led->cdev); | 129 | ret = led_classdev_register(led->master, &led->cdev); |
130 | if (ret) { | 130 | if (ret) { |
131 | dev_err(&pdev->dev, "failed to register LED %d\n", id); | 131 | dev_err(&pdev->dev, "failed to register LED %d\n", id); |
132 | return ret; | 132 | return ret; |
133 | } | 133 | } |
134 | 134 | ||
135 | platform_set_drvdata(pdev, led); | 135 | platform_set_drvdata(pdev, led); |
136 | return 0; | 136 | return 0; |
137 | } | 137 | } |
138 | 138 | ||
139 | static int da903x_led_remove(struct platform_device *pdev) | 139 | static int da903x_led_remove(struct platform_device *pdev) |
140 | { | 140 | { |
141 | struct da903x_led *led = platform_get_drvdata(pdev); | 141 | struct da903x_led *led = platform_get_drvdata(pdev); |
142 | 142 | ||
143 | led_classdev_unregister(&led->cdev); | 143 | led_classdev_unregister(&led->cdev); |
144 | return 0; | 144 | return 0; |
145 | } | 145 | } |
146 | 146 | ||
147 | static struct platform_driver da903x_led_driver = { | 147 | static struct platform_driver da903x_led_driver = { |
148 | .driver = { | 148 | .driver = { |
149 | .name = "da903x-led", | 149 | .name = "da903x-led", |
150 | .owner = THIS_MODULE, | 150 | .owner = THIS_MODULE, |
151 | }, | 151 | }, |
152 | .probe = da903x_led_probe, | 152 | .probe = da903x_led_probe, |
153 | .remove = da903x_led_remove, | 153 | .remove = da903x_led_remove, |
154 | }; | 154 | }; |
155 | 155 | ||
156 | module_platform_driver(da903x_led_driver); | 156 | module_platform_driver(da903x_led_driver); |
157 | 157 | ||
158 | MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034"); | 158 | MODULE_DESCRIPTION("LEDs driver for Dialog Semiconductor DA9030/DA9034"); |
159 | MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); | 159 | MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); |
160 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); | 160 | MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); |
161 | MODULE_LICENSE("GPL"); | 161 | MODULE_LICENSE("GPL"); |
162 | MODULE_ALIAS("platform:da903x-led"); | 162 | MODULE_ALIAS("platform:da903x-led"); |
163 | 163 |
drivers/leds/leds-da9052.c
1 | /* | 1 | /* |
2 | * LED Driver for Dialog DA9052 PMICs. | 2 | * LED Driver for Dialog DA9052 PMICs. |
3 | * | 3 | * |
4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. | 4 | * Copyright(c) 2012 Dialog Semiconductor Ltd. |
5 | * | 5 | * |
6 | * Author: David Dajun Chen <dchen@diasemi.com> | 6 | * Author: David Dajun Chen <dchen@diasemi.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your | 10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. | 11 | * option) any later version. |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/leds.h> | 19 | #include <linux/leds.h> |
20 | #include <linux/workqueue.h> | 20 | #include <linux/workqueue.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | 22 | ||
23 | #include <linux/mfd/da9052/reg.h> | 23 | #include <linux/mfd/da9052/reg.h> |
24 | #include <linux/mfd/da9052/da9052.h> | 24 | #include <linux/mfd/da9052/da9052.h> |
25 | #include <linux/mfd/da9052/pdata.h> | 25 | #include <linux/mfd/da9052/pdata.h> |
26 | 26 | ||
27 | #define DA9052_OPENDRAIN_OUTPUT 2 | 27 | #define DA9052_OPENDRAIN_OUTPUT 2 |
28 | #define DA9052_SET_HIGH_LVL_OUTPUT (1 << 3) | 28 | #define DA9052_SET_HIGH_LVL_OUTPUT (1 << 3) |
29 | #define DA9052_MASK_UPPER_NIBBLE 0xF0 | 29 | #define DA9052_MASK_UPPER_NIBBLE 0xF0 |
30 | #define DA9052_MASK_LOWER_NIBBLE 0x0F | 30 | #define DA9052_MASK_LOWER_NIBBLE 0x0F |
31 | #define DA9052_NIBBLE_SHIFT 4 | 31 | #define DA9052_NIBBLE_SHIFT 4 |
32 | #define DA9052_MAX_BRIGHTNESS 0x5f | 32 | #define DA9052_MAX_BRIGHTNESS 0x5f |
33 | 33 | ||
34 | struct da9052_led { | 34 | struct da9052_led { |
35 | struct led_classdev cdev; | 35 | struct led_classdev cdev; |
36 | struct work_struct work; | 36 | struct work_struct work; |
37 | struct da9052 *da9052; | 37 | struct da9052 *da9052; |
38 | unsigned char led_index; | 38 | unsigned char led_index; |
39 | unsigned char id; | 39 | unsigned char id; |
40 | int brightness; | 40 | int brightness; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static unsigned char led_reg[] = { | 43 | static unsigned char led_reg[] = { |
44 | DA9052_LED_CONT_4_REG, | 44 | DA9052_LED_CONT_4_REG, |
45 | DA9052_LED_CONT_5_REG, | 45 | DA9052_LED_CONT_5_REG, |
46 | }; | 46 | }; |
47 | 47 | ||
48 | static int da9052_set_led_brightness(struct da9052_led *led) | 48 | static int da9052_set_led_brightness(struct da9052_led *led) |
49 | { | 49 | { |
50 | u8 val; | 50 | u8 val; |
51 | int error; | 51 | int error; |
52 | 52 | ||
53 | val = (led->brightness & 0x7f) | DA9052_LED_CONT_DIM; | 53 | val = (led->brightness & 0x7f) | DA9052_LED_CONT_DIM; |
54 | 54 | ||
55 | error = da9052_reg_write(led->da9052, led_reg[led->led_index], val); | 55 | error = da9052_reg_write(led->da9052, led_reg[led->led_index], val); |
56 | if (error < 0) | 56 | if (error < 0) |
57 | dev_err(led->da9052->dev, "Failed to set led brightness, %d\n", | 57 | dev_err(led->da9052->dev, "Failed to set led brightness, %d\n", |
58 | error); | 58 | error); |
59 | return error; | 59 | return error; |
60 | } | 60 | } |
61 | 61 | ||
62 | static void da9052_led_work(struct work_struct *work) | 62 | static void da9052_led_work(struct work_struct *work) |
63 | { | 63 | { |
64 | struct da9052_led *led = container_of(work, struct da9052_led, work); | 64 | struct da9052_led *led = container_of(work, struct da9052_led, work); |
65 | 65 | ||
66 | da9052_set_led_brightness(led); | 66 | da9052_set_led_brightness(led); |
67 | } | 67 | } |
68 | 68 | ||
69 | static void da9052_led_set(struct led_classdev *led_cdev, | 69 | static void da9052_led_set(struct led_classdev *led_cdev, |
70 | enum led_brightness value) | 70 | enum led_brightness value) |
71 | { | 71 | { |
72 | struct da9052_led *led; | 72 | struct da9052_led *led; |
73 | 73 | ||
74 | led = container_of(led_cdev, struct da9052_led, cdev); | 74 | led = container_of(led_cdev, struct da9052_led, cdev); |
75 | led->brightness = value; | 75 | led->brightness = value; |
76 | schedule_work(&led->work); | 76 | schedule_work(&led->work); |
77 | } | 77 | } |
78 | 78 | ||
79 | static int da9052_configure_leds(struct da9052 *da9052) | 79 | static int da9052_configure_leds(struct da9052 *da9052) |
80 | { | 80 | { |
81 | int error; | 81 | int error; |
82 | unsigned char register_value = DA9052_OPENDRAIN_OUTPUT | 82 | unsigned char register_value = DA9052_OPENDRAIN_OUTPUT |
83 | | DA9052_SET_HIGH_LVL_OUTPUT; | 83 | | DA9052_SET_HIGH_LVL_OUTPUT; |
84 | 84 | ||
85 | error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG, | 85 | error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG, |
86 | DA9052_MASK_LOWER_NIBBLE, | 86 | DA9052_MASK_LOWER_NIBBLE, |
87 | register_value); | 87 | register_value); |
88 | 88 | ||
89 | if (error < 0) { | 89 | if (error < 0) { |
90 | dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n", | 90 | dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n", |
91 | error); | 91 | error); |
92 | return error; | 92 | return error; |
93 | } | 93 | } |
94 | 94 | ||
95 | error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG, | 95 | error = da9052_reg_update(da9052, DA9052_GPIO_14_15_REG, |
96 | DA9052_MASK_UPPER_NIBBLE, | 96 | DA9052_MASK_UPPER_NIBBLE, |
97 | register_value << DA9052_NIBBLE_SHIFT); | 97 | register_value << DA9052_NIBBLE_SHIFT); |
98 | if (error < 0) | 98 | if (error < 0) |
99 | dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n", | 99 | dev_err(da9052->dev, "Failed to write GPIO 14-15 reg, %d\n", |
100 | error); | 100 | error); |
101 | 101 | ||
102 | return error; | 102 | return error; |
103 | } | 103 | } |
104 | 104 | ||
105 | static int da9052_led_probe(struct platform_device *pdev) | 105 | static int da9052_led_probe(struct platform_device *pdev) |
106 | { | 106 | { |
107 | struct da9052_pdata *pdata; | 107 | struct da9052_pdata *pdata; |
108 | struct da9052 *da9052; | 108 | struct da9052 *da9052; |
109 | struct led_platform_data *pled; | 109 | struct led_platform_data *pled; |
110 | struct da9052_led *led = NULL; | 110 | struct da9052_led *led = NULL; |
111 | int error = -ENODEV; | 111 | int error = -ENODEV; |
112 | int i; | 112 | int i; |
113 | 113 | ||
114 | da9052 = dev_get_drvdata(pdev->dev.parent); | 114 | da9052 = dev_get_drvdata(pdev->dev.parent); |
115 | pdata = da9052->dev->platform_data; | 115 | pdata = dev_get_platdata(da9052->dev); |
116 | if (pdata == NULL) { | 116 | if (pdata == NULL) { |
117 | dev_err(&pdev->dev, "No platform data\n"); | 117 | dev_err(&pdev->dev, "No platform data\n"); |
118 | goto err; | 118 | goto err; |
119 | } | 119 | } |
120 | 120 | ||
121 | pled = pdata->pled; | 121 | pled = pdata->pled; |
122 | if (pled == NULL) { | 122 | if (pled == NULL) { |
123 | dev_err(&pdev->dev, "No platform data for LED\n"); | 123 | dev_err(&pdev->dev, "No platform data for LED\n"); |
124 | goto err; | 124 | goto err; |
125 | } | 125 | } |
126 | 126 | ||
127 | led = devm_kzalloc(&pdev->dev, | 127 | led = devm_kzalloc(&pdev->dev, |
128 | sizeof(struct da9052_led) * pled->num_leds, | 128 | sizeof(struct da9052_led) * pled->num_leds, |
129 | GFP_KERNEL); | 129 | GFP_KERNEL); |
130 | if (led == NULL) { | 130 | if (led == NULL) { |
131 | dev_err(&pdev->dev, "Failed to alloc memory\n"); | 131 | dev_err(&pdev->dev, "Failed to alloc memory\n"); |
132 | error = -ENOMEM; | 132 | error = -ENOMEM; |
133 | goto err; | 133 | goto err; |
134 | } | 134 | } |
135 | 135 | ||
136 | for (i = 0; i < pled->num_leds; i++) { | 136 | for (i = 0; i < pled->num_leds; i++) { |
137 | led[i].cdev.name = pled->leds[i].name; | 137 | led[i].cdev.name = pled->leds[i].name; |
138 | led[i].cdev.brightness_set = da9052_led_set; | 138 | led[i].cdev.brightness_set = da9052_led_set; |
139 | led[i].cdev.brightness = LED_OFF; | 139 | led[i].cdev.brightness = LED_OFF; |
140 | led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS; | 140 | led[i].cdev.max_brightness = DA9052_MAX_BRIGHTNESS; |
141 | led[i].brightness = LED_OFF; | 141 | led[i].brightness = LED_OFF; |
142 | led[i].led_index = pled->leds[i].flags; | 142 | led[i].led_index = pled->leds[i].flags; |
143 | led[i].da9052 = dev_get_drvdata(pdev->dev.parent); | 143 | led[i].da9052 = dev_get_drvdata(pdev->dev.parent); |
144 | INIT_WORK(&led[i].work, da9052_led_work); | 144 | INIT_WORK(&led[i].work, da9052_led_work); |
145 | 145 | ||
146 | error = led_classdev_register(pdev->dev.parent, &led[i].cdev); | 146 | error = led_classdev_register(pdev->dev.parent, &led[i].cdev); |
147 | if (error) { | 147 | if (error) { |
148 | dev_err(&pdev->dev, "Failed to register led %d\n", | 148 | dev_err(&pdev->dev, "Failed to register led %d\n", |
149 | led[i].led_index); | 149 | led[i].led_index); |
150 | goto err_register; | 150 | goto err_register; |
151 | } | 151 | } |
152 | 152 | ||
153 | error = da9052_set_led_brightness(&led[i]); | 153 | error = da9052_set_led_brightness(&led[i]); |
154 | if (error) { | 154 | if (error) { |
155 | dev_err(&pdev->dev, "Unable to init led %d\n", | 155 | dev_err(&pdev->dev, "Unable to init led %d\n", |
156 | led[i].led_index); | 156 | led[i].led_index); |
157 | continue; | 157 | continue; |
158 | } | 158 | } |
159 | } | 159 | } |
160 | error = da9052_configure_leds(led->da9052); | 160 | error = da9052_configure_leds(led->da9052); |
161 | if (error) { | 161 | if (error) { |
162 | dev_err(&pdev->dev, "Failed to configure GPIO LED%d\n", error); | 162 | dev_err(&pdev->dev, "Failed to configure GPIO LED%d\n", error); |
163 | goto err_register; | 163 | goto err_register; |
164 | } | 164 | } |
165 | 165 | ||
166 | platform_set_drvdata(pdev, led); | 166 | platform_set_drvdata(pdev, led); |
167 | 167 | ||
168 | return 0; | 168 | return 0; |
169 | 169 | ||
170 | err_register: | 170 | err_register: |
171 | for (i = i - 1; i >= 0; i--) { | 171 | for (i = i - 1; i >= 0; i--) { |
172 | led_classdev_unregister(&led[i].cdev); | 172 | led_classdev_unregister(&led[i].cdev); |
173 | cancel_work_sync(&led[i].work); | 173 | cancel_work_sync(&led[i].work); |
174 | } | 174 | } |
175 | err: | 175 | err: |
176 | return error; | 176 | return error; |
177 | } | 177 | } |
178 | 178 | ||
179 | static int da9052_led_remove(struct platform_device *pdev) | 179 | static int da9052_led_remove(struct platform_device *pdev) |
180 | { | 180 | { |
181 | struct da9052_led *led = platform_get_drvdata(pdev); | 181 | struct da9052_led *led = platform_get_drvdata(pdev); |
182 | struct da9052_pdata *pdata; | 182 | struct da9052_pdata *pdata; |
183 | struct da9052 *da9052; | 183 | struct da9052 *da9052; |
184 | struct led_platform_data *pled; | 184 | struct led_platform_data *pled; |
185 | int i; | 185 | int i; |
186 | 186 | ||
187 | da9052 = dev_get_drvdata(pdev->dev.parent); | 187 | da9052 = dev_get_drvdata(pdev->dev.parent); |
188 | pdata = da9052->dev->platform_data; | 188 | pdata = dev_get_platdata(da9052->dev); |
189 | pled = pdata->pled; | 189 | pled = pdata->pled; |
190 | 190 | ||
191 | for (i = 0; i < pled->num_leds; i++) { | 191 | for (i = 0; i < pled->num_leds; i++) { |
192 | led[i].brightness = 0; | 192 | led[i].brightness = 0; |
193 | da9052_set_led_brightness(&led[i]); | 193 | da9052_set_led_brightness(&led[i]); |
194 | led_classdev_unregister(&led[i].cdev); | 194 | led_classdev_unregister(&led[i].cdev); |
195 | cancel_work_sync(&led[i].work); | 195 | cancel_work_sync(&led[i].work); |
196 | } | 196 | } |
197 | 197 | ||
198 | return 0; | 198 | return 0; |
199 | } | 199 | } |
200 | 200 | ||
201 | static struct platform_driver da9052_led_driver = { | 201 | static struct platform_driver da9052_led_driver = { |
202 | .driver = { | 202 | .driver = { |
203 | .name = "da9052-leds", | 203 | .name = "da9052-leds", |
204 | .owner = THIS_MODULE, | 204 | .owner = THIS_MODULE, |
205 | }, | 205 | }, |
206 | .probe = da9052_led_probe, | 206 | .probe = da9052_led_probe, |
207 | .remove = da9052_led_remove, | 207 | .remove = da9052_led_remove, |
208 | }; | 208 | }; |
209 | 209 | ||
210 | module_platform_driver(da9052_led_driver); | 210 | module_platform_driver(da9052_led_driver); |
211 | 211 | ||
212 | MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>"); | 212 | MODULE_AUTHOR("Dialog Semiconductor Ltd <dchen@diasemi.com>"); |
213 | MODULE_DESCRIPTION("LED driver for Dialog DA9052 PMIC"); | 213 | MODULE_DESCRIPTION("LED driver for Dialog DA9052 PMIC"); |
214 | MODULE_LICENSE("GPL"); | 214 | MODULE_LICENSE("GPL"); |
215 | 215 |
drivers/leds/leds-gpio.c
1 | /* | 1 | /* |
2 | * LEDs driver for GPIOs | 2 | * LEDs driver for GPIOs |
3 | * | 3 | * |
4 | * Copyright (C) 2007 8D Technologies inc. | 4 | * Copyright (C) 2007 8D Technologies inc. |
5 | * Raphael Assenat <raph@8d.com> | 5 | * Raphael Assenat <raph@8d.com> |
6 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | 6 | * Copyright (C) 2008 Freescale Semiconductor, Inc. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 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 | 9 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
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/gpio.h> | 16 | #include <linux/gpio.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/of_platform.h> | 18 | #include <linux/of_platform.h> |
19 | #include <linux/of_gpio.h> | 19 | #include <linux/of_gpio.h> |
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/err.h> | 23 | #include <linux/err.h> |
24 | 24 | ||
25 | struct gpio_led_data { | 25 | struct gpio_led_data { |
26 | struct led_classdev cdev; | 26 | struct led_classdev cdev; |
27 | unsigned gpio; | 27 | unsigned gpio; |
28 | struct work_struct work; | 28 | struct work_struct work; |
29 | u8 new_level; | 29 | u8 new_level; |
30 | u8 can_sleep; | 30 | u8 can_sleep; |
31 | u8 active_low; | 31 | u8 active_low; |
32 | u8 blinking; | 32 | u8 blinking; |
33 | int (*platform_gpio_blink_set)(unsigned gpio, int state, | 33 | int (*platform_gpio_blink_set)(unsigned gpio, int state, |
34 | unsigned long *delay_on, unsigned long *delay_off); | 34 | unsigned long *delay_on, unsigned long *delay_off); |
35 | }; | 35 | }; |
36 | 36 | ||
37 | static void gpio_led_work(struct work_struct *work) | 37 | static void gpio_led_work(struct work_struct *work) |
38 | { | 38 | { |
39 | struct gpio_led_data *led_dat = | 39 | struct gpio_led_data *led_dat = |
40 | container_of(work, struct gpio_led_data, work); | 40 | container_of(work, struct gpio_led_data, work); |
41 | 41 | ||
42 | if (led_dat->blinking) { | 42 | if (led_dat->blinking) { |
43 | led_dat->platform_gpio_blink_set(led_dat->gpio, | 43 | led_dat->platform_gpio_blink_set(led_dat->gpio, |
44 | led_dat->new_level, | 44 | led_dat->new_level, |
45 | NULL, NULL); | 45 | NULL, NULL); |
46 | led_dat->blinking = 0; | 46 | led_dat->blinking = 0; |
47 | } else | 47 | } else |
48 | gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); | 48 | gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level); |
49 | } | 49 | } |
50 | 50 | ||
51 | static void gpio_led_set(struct led_classdev *led_cdev, | 51 | static void gpio_led_set(struct led_classdev *led_cdev, |
52 | enum led_brightness value) | 52 | enum led_brightness value) |
53 | { | 53 | { |
54 | struct gpio_led_data *led_dat = | 54 | struct gpio_led_data *led_dat = |
55 | container_of(led_cdev, struct gpio_led_data, cdev); | 55 | container_of(led_cdev, struct gpio_led_data, cdev); |
56 | int level; | 56 | int level; |
57 | 57 | ||
58 | if (value == LED_OFF) | 58 | if (value == LED_OFF) |
59 | level = 0; | 59 | level = 0; |
60 | else | 60 | else |
61 | level = 1; | 61 | level = 1; |
62 | 62 | ||
63 | if (led_dat->active_low) | 63 | if (led_dat->active_low) |
64 | level = !level; | 64 | level = !level; |
65 | 65 | ||
66 | /* Setting GPIOs with I2C/etc requires a task context, and we don't | 66 | /* Setting GPIOs with I2C/etc requires a task context, and we don't |
67 | * seem to have a reliable way to know if we're already in one; so | 67 | * seem to have a reliable way to know if we're already in one; so |
68 | * let's just assume the worst. | 68 | * let's just assume the worst. |
69 | */ | 69 | */ |
70 | if (led_dat->can_sleep) { | 70 | if (led_dat->can_sleep) { |
71 | led_dat->new_level = level; | 71 | led_dat->new_level = level; |
72 | schedule_work(&led_dat->work); | 72 | schedule_work(&led_dat->work); |
73 | } else { | 73 | } else { |
74 | if (led_dat->blinking) { | 74 | if (led_dat->blinking) { |
75 | led_dat->platform_gpio_blink_set(led_dat->gpio, level, | 75 | led_dat->platform_gpio_blink_set(led_dat->gpio, level, |
76 | NULL, NULL); | 76 | NULL, NULL); |
77 | led_dat->blinking = 0; | 77 | led_dat->blinking = 0; |
78 | } else | 78 | } else |
79 | gpio_set_value(led_dat->gpio, level); | 79 | gpio_set_value(led_dat->gpio, level); |
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | static int gpio_blink_set(struct led_classdev *led_cdev, | 83 | static int gpio_blink_set(struct led_classdev *led_cdev, |
84 | unsigned long *delay_on, unsigned long *delay_off) | 84 | unsigned long *delay_on, unsigned long *delay_off) |
85 | { | 85 | { |
86 | struct gpio_led_data *led_dat = | 86 | struct gpio_led_data *led_dat = |
87 | container_of(led_cdev, struct gpio_led_data, cdev); | 87 | container_of(led_cdev, struct gpio_led_data, cdev); |
88 | 88 | ||
89 | led_dat->blinking = 1; | 89 | led_dat->blinking = 1; |
90 | return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, | 90 | return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK, |
91 | delay_on, delay_off); | 91 | delay_on, delay_off); |
92 | } | 92 | } |
93 | 93 | ||
94 | static int create_gpio_led(const struct gpio_led *template, | 94 | static int create_gpio_led(const struct gpio_led *template, |
95 | struct gpio_led_data *led_dat, struct device *parent, | 95 | struct gpio_led_data *led_dat, struct device *parent, |
96 | int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) | 96 | int (*blink_set)(unsigned, int, unsigned long *, unsigned long *)) |
97 | { | 97 | { |
98 | int ret, state; | 98 | int ret, state; |
99 | 99 | ||
100 | led_dat->gpio = -1; | 100 | led_dat->gpio = -1; |
101 | 101 | ||
102 | /* skip leds that aren't available */ | 102 | /* skip leds that aren't available */ |
103 | if (!gpio_is_valid(template->gpio)) { | 103 | if (!gpio_is_valid(template->gpio)) { |
104 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", | 104 | dev_info(parent, "Skipping unavailable LED gpio %d (%s)\n", |
105 | template->gpio, template->name); | 105 | template->gpio, template->name); |
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | 108 | ||
109 | ret = devm_gpio_request(parent, template->gpio, template->name); | 109 | ret = devm_gpio_request(parent, template->gpio, template->name); |
110 | if (ret < 0) | 110 | if (ret < 0) |
111 | return ret; | 111 | return ret; |
112 | 112 | ||
113 | led_dat->cdev.name = template->name; | 113 | led_dat->cdev.name = template->name; |
114 | led_dat->cdev.default_trigger = template->default_trigger; | 114 | led_dat->cdev.default_trigger = template->default_trigger; |
115 | led_dat->gpio = template->gpio; | 115 | led_dat->gpio = template->gpio; |
116 | led_dat->can_sleep = gpio_cansleep(template->gpio); | 116 | led_dat->can_sleep = gpio_cansleep(template->gpio); |
117 | led_dat->active_low = template->active_low; | 117 | led_dat->active_low = template->active_low; |
118 | led_dat->blinking = 0; | 118 | led_dat->blinking = 0; |
119 | if (blink_set) { | 119 | if (blink_set) { |
120 | led_dat->platform_gpio_blink_set = blink_set; | 120 | led_dat->platform_gpio_blink_set = blink_set; |
121 | led_dat->cdev.blink_set = gpio_blink_set; | 121 | led_dat->cdev.blink_set = gpio_blink_set; |
122 | } | 122 | } |
123 | led_dat->cdev.brightness_set = gpio_led_set; | 123 | led_dat->cdev.brightness_set = gpio_led_set; |
124 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) | 124 | if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) |
125 | state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; | 125 | state = !!gpio_get_value_cansleep(led_dat->gpio) ^ led_dat->active_low; |
126 | else | 126 | else |
127 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | 127 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
128 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | 128 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; |
129 | if (!template->retain_state_suspended) | 129 | if (!template->retain_state_suspended) |
130 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 130 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
131 | 131 | ||
132 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); | 132 | ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state); |
133 | if (ret < 0) | 133 | if (ret < 0) |
134 | return ret; | 134 | return ret; |
135 | 135 | ||
136 | INIT_WORK(&led_dat->work, gpio_led_work); | 136 | INIT_WORK(&led_dat->work, gpio_led_work); |
137 | 137 | ||
138 | ret = led_classdev_register(parent, &led_dat->cdev); | 138 | ret = led_classdev_register(parent, &led_dat->cdev); |
139 | if (ret < 0) | 139 | if (ret < 0) |
140 | return ret; | 140 | return ret; |
141 | 141 | ||
142 | return 0; | 142 | return 0; |
143 | } | 143 | } |
144 | 144 | ||
145 | static void delete_gpio_led(struct gpio_led_data *led) | 145 | static void delete_gpio_led(struct gpio_led_data *led) |
146 | { | 146 | { |
147 | if (!gpio_is_valid(led->gpio)) | 147 | if (!gpio_is_valid(led->gpio)) |
148 | return; | 148 | return; |
149 | led_classdev_unregister(&led->cdev); | 149 | led_classdev_unregister(&led->cdev); |
150 | cancel_work_sync(&led->work); | 150 | cancel_work_sync(&led->work); |
151 | } | 151 | } |
152 | 152 | ||
153 | struct gpio_leds_priv { | 153 | struct gpio_leds_priv { |
154 | int num_leds; | 154 | int num_leds; |
155 | struct gpio_led_data leds[]; | 155 | struct gpio_led_data leds[]; |
156 | }; | 156 | }; |
157 | 157 | ||
158 | static inline int sizeof_gpio_leds_priv(int num_leds) | 158 | static inline int sizeof_gpio_leds_priv(int num_leds) |
159 | { | 159 | { |
160 | return sizeof(struct gpio_leds_priv) + | 160 | return sizeof(struct gpio_leds_priv) + |
161 | (sizeof(struct gpio_led_data) * num_leds); | 161 | (sizeof(struct gpio_led_data) * num_leds); |
162 | } | 162 | } |
163 | 163 | ||
164 | /* Code to create from OpenFirmware platform devices */ | 164 | /* Code to create from OpenFirmware platform devices */ |
165 | #ifdef CONFIG_OF_GPIO | 165 | #ifdef CONFIG_OF_GPIO |
166 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | 166 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) |
167 | { | 167 | { |
168 | struct device_node *np = pdev->dev.of_node, *child; | 168 | struct device_node *np = pdev->dev.of_node, *child; |
169 | struct gpio_leds_priv *priv; | 169 | struct gpio_leds_priv *priv; |
170 | int count, ret; | 170 | int count, ret; |
171 | 171 | ||
172 | /* count LEDs in this device, so we know how much to allocate */ | 172 | /* count LEDs in this device, so we know how much to allocate */ |
173 | count = of_get_child_count(np); | 173 | count = of_get_child_count(np); |
174 | if (!count) | 174 | if (!count) |
175 | return ERR_PTR(-ENODEV); | 175 | return ERR_PTR(-ENODEV); |
176 | 176 | ||
177 | for_each_child_of_node(np, child) | 177 | for_each_child_of_node(np, child) |
178 | if (of_get_gpio(child, 0) == -EPROBE_DEFER) | 178 | if (of_get_gpio(child, 0) == -EPROBE_DEFER) |
179 | return ERR_PTR(-EPROBE_DEFER); | 179 | return ERR_PTR(-EPROBE_DEFER); |
180 | 180 | ||
181 | priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count), | 181 | priv = devm_kzalloc(&pdev->dev, sizeof_gpio_leds_priv(count), |
182 | GFP_KERNEL); | 182 | GFP_KERNEL); |
183 | if (!priv) | 183 | if (!priv) |
184 | return ERR_PTR(-ENOMEM); | 184 | return ERR_PTR(-ENOMEM); |
185 | 185 | ||
186 | for_each_child_of_node(np, child) { | 186 | for_each_child_of_node(np, child) { |
187 | struct gpio_led led = {}; | 187 | struct gpio_led led = {}; |
188 | enum of_gpio_flags flags; | 188 | enum of_gpio_flags flags; |
189 | const char *state; | 189 | const char *state; |
190 | 190 | ||
191 | led.gpio = of_get_gpio_flags(child, 0, &flags); | 191 | led.gpio = of_get_gpio_flags(child, 0, &flags); |
192 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; | 192 | led.active_low = flags & OF_GPIO_ACTIVE_LOW; |
193 | led.name = of_get_property(child, "label", NULL) ? : child->name; | 193 | led.name = of_get_property(child, "label", NULL) ? : child->name; |
194 | led.default_trigger = | 194 | led.default_trigger = |
195 | of_get_property(child, "linux,default-trigger", NULL); | 195 | of_get_property(child, "linux,default-trigger", NULL); |
196 | state = of_get_property(child, "default-state", NULL); | 196 | state = of_get_property(child, "default-state", NULL); |
197 | if (state) { | 197 | if (state) { |
198 | if (!strcmp(state, "keep")) | 198 | if (!strcmp(state, "keep")) |
199 | led.default_state = LEDS_GPIO_DEFSTATE_KEEP; | 199 | led.default_state = LEDS_GPIO_DEFSTATE_KEEP; |
200 | else if (!strcmp(state, "on")) | 200 | else if (!strcmp(state, "on")) |
201 | led.default_state = LEDS_GPIO_DEFSTATE_ON; | 201 | led.default_state = LEDS_GPIO_DEFSTATE_ON; |
202 | else | 202 | else |
203 | led.default_state = LEDS_GPIO_DEFSTATE_OFF; | 203 | led.default_state = LEDS_GPIO_DEFSTATE_OFF; |
204 | } | 204 | } |
205 | 205 | ||
206 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], | 206 | ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], |
207 | &pdev->dev, NULL); | 207 | &pdev->dev, NULL); |
208 | if (ret < 0) { | 208 | if (ret < 0) { |
209 | of_node_put(child); | 209 | of_node_put(child); |
210 | goto err; | 210 | goto err; |
211 | } | 211 | } |
212 | } | 212 | } |
213 | 213 | ||
214 | return priv; | 214 | return priv; |
215 | 215 | ||
216 | err: | 216 | err: |
217 | for (count = priv->num_leds - 2; count >= 0; count--) | 217 | for (count = priv->num_leds - 2; count >= 0; count--) |
218 | delete_gpio_led(&priv->leds[count]); | 218 | delete_gpio_led(&priv->leds[count]); |
219 | return ERR_PTR(-ENODEV); | 219 | return ERR_PTR(-ENODEV); |
220 | } | 220 | } |
221 | 221 | ||
222 | static const struct of_device_id of_gpio_leds_match[] = { | 222 | static const struct of_device_id of_gpio_leds_match[] = { |
223 | { .compatible = "gpio-leds", }, | 223 | { .compatible = "gpio-leds", }, |
224 | {}, | 224 | {}, |
225 | }; | 225 | }; |
226 | #else /* CONFIG_OF_GPIO */ | 226 | #else /* CONFIG_OF_GPIO */ |
227 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) | 227 | static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) |
228 | { | 228 | { |
229 | return ERR_PTR(-ENODEV); | 229 | return ERR_PTR(-ENODEV); |
230 | } | 230 | } |
231 | #endif /* CONFIG_OF_GPIO */ | 231 | #endif /* CONFIG_OF_GPIO */ |
232 | 232 | ||
233 | 233 | ||
234 | static int gpio_led_probe(struct platform_device *pdev) | 234 | static int gpio_led_probe(struct platform_device *pdev) |
235 | { | 235 | { |
236 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 236 | struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
237 | struct gpio_leds_priv *priv; | 237 | struct gpio_leds_priv *priv; |
238 | int i, ret = 0; | 238 | int i, ret = 0; |
239 | 239 | ||
240 | 240 | ||
241 | if (pdata && pdata->num_leds) { | 241 | if (pdata && pdata->num_leds) { |
242 | priv = devm_kzalloc(&pdev->dev, | 242 | priv = devm_kzalloc(&pdev->dev, |
243 | sizeof_gpio_leds_priv(pdata->num_leds), | 243 | sizeof_gpio_leds_priv(pdata->num_leds), |
244 | GFP_KERNEL); | 244 | GFP_KERNEL); |
245 | if (!priv) | 245 | if (!priv) |
246 | return -ENOMEM; | 246 | return -ENOMEM; |
247 | 247 | ||
248 | priv->num_leds = pdata->num_leds; | 248 | priv->num_leds = pdata->num_leds; |
249 | for (i = 0; i < priv->num_leds; i++) { | 249 | for (i = 0; i < priv->num_leds; i++) { |
250 | ret = create_gpio_led(&pdata->leds[i], | 250 | ret = create_gpio_led(&pdata->leds[i], |
251 | &priv->leds[i], | 251 | &priv->leds[i], |
252 | &pdev->dev, pdata->gpio_blink_set); | 252 | &pdev->dev, pdata->gpio_blink_set); |
253 | if (ret < 0) { | 253 | if (ret < 0) { |
254 | /* On failure: unwind the led creations */ | 254 | /* On failure: unwind the led creations */ |
255 | for (i = i - 1; i >= 0; i--) | 255 | for (i = i - 1; i >= 0; i--) |
256 | delete_gpio_led(&priv->leds[i]); | 256 | delete_gpio_led(&priv->leds[i]); |
257 | return ret; | 257 | return ret; |
258 | } | 258 | } |
259 | } | 259 | } |
260 | } else { | 260 | } else { |
261 | priv = gpio_leds_create_of(pdev); | 261 | priv = gpio_leds_create_of(pdev); |
262 | if (IS_ERR(priv)) | 262 | if (IS_ERR(priv)) |
263 | return PTR_ERR(priv); | 263 | return PTR_ERR(priv); |
264 | } | 264 | } |
265 | 265 | ||
266 | platform_set_drvdata(pdev, priv); | 266 | platform_set_drvdata(pdev, priv); |
267 | 267 | ||
268 | return 0; | 268 | return 0; |
269 | } | 269 | } |
270 | 270 | ||
271 | static int gpio_led_remove(struct platform_device *pdev) | 271 | static int gpio_led_remove(struct platform_device *pdev) |
272 | { | 272 | { |
273 | struct gpio_leds_priv *priv = platform_get_drvdata(pdev); | 273 | struct gpio_leds_priv *priv = platform_get_drvdata(pdev); |
274 | int i; | 274 | int i; |
275 | 275 | ||
276 | for (i = 0; i < priv->num_leds; i++) | 276 | for (i = 0; i < priv->num_leds; i++) |
277 | delete_gpio_led(&priv->leds[i]); | 277 | delete_gpio_led(&priv->leds[i]); |
278 | 278 | ||
279 | return 0; | 279 | return 0; |
280 | } | 280 | } |
281 | 281 | ||
282 | static struct platform_driver gpio_led_driver = { | 282 | static struct platform_driver gpio_led_driver = { |
283 | .probe = gpio_led_probe, | 283 | .probe = gpio_led_probe, |
284 | .remove = gpio_led_remove, | 284 | .remove = gpio_led_remove, |
285 | .driver = { | 285 | .driver = { |
286 | .name = "leds-gpio", | 286 | .name = "leds-gpio", |
287 | .owner = THIS_MODULE, | 287 | .owner = THIS_MODULE, |
288 | .of_match_table = of_match_ptr(of_gpio_leds_match), | 288 | .of_match_table = of_match_ptr(of_gpio_leds_match), |
289 | }, | 289 | }, |
290 | }; | 290 | }; |
291 | 291 | ||
292 | module_platform_driver(gpio_led_driver); | 292 | module_platform_driver(gpio_led_driver); |
293 | 293 | ||
294 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>"); | 294 | MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>"); |
295 | MODULE_DESCRIPTION("GPIO LED driver"); | 295 | MODULE_DESCRIPTION("GPIO LED driver"); |
296 | MODULE_LICENSE("GPL"); | 296 | MODULE_LICENSE("GPL"); |
297 | MODULE_ALIAS("platform:leds-gpio"); | 297 | MODULE_ALIAS("platform:leds-gpio"); |
298 | 298 |
drivers/leds/leds-lm3530.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2011 ST-Ericsson SA. | 2 | * Copyright (C) 2011 ST-Ericsson SA. |
3 | * Copyright (C) 2009 Motorola, Inc. | 3 | * Copyright (C) 2009 Motorola, Inc. |
4 | * | 4 | * |
5 | * License Terms: GNU General Public License v2 | 5 | * License Terms: GNU General Public License v2 |
6 | * | 6 | * |
7 | * Simple driver for National Semiconductor LM3530 Backlight driver chip | 7 | * Simple driver for National Semiconductor LM3530 Backlight driver chip |
8 | * | 8 | * |
9 | * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com> | 9 | * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com> |
10 | * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com> | 10 | * based on leds-lm3530.c by Dan Murphy <D.Murphy@motorola.com> |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/i2c.h> | 13 | #include <linux/i2c.h> |
14 | #include <linux/leds.h> | 14 | #include <linux/leds.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/input.h> | 17 | #include <linux/input.h> |
18 | #include <linux/led-lm3530.h> | 18 | #include <linux/led-lm3530.h> |
19 | #include <linux/types.h> | 19 | #include <linux/types.h> |
20 | #include <linux/regulator/consumer.h> | 20 | #include <linux/regulator/consumer.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | 22 | ||
23 | #define LM3530_LED_DEV "lcd-backlight" | 23 | #define LM3530_LED_DEV "lcd-backlight" |
24 | #define LM3530_NAME "lm3530-led" | 24 | #define LM3530_NAME "lm3530-led" |
25 | 25 | ||
26 | #define LM3530_GEN_CONFIG 0x10 | 26 | #define LM3530_GEN_CONFIG 0x10 |
27 | #define LM3530_ALS_CONFIG 0x20 | 27 | #define LM3530_ALS_CONFIG 0x20 |
28 | #define LM3530_BRT_RAMP_RATE 0x30 | 28 | #define LM3530_BRT_RAMP_RATE 0x30 |
29 | #define LM3530_ALS_IMP_SELECT 0x41 | 29 | #define LM3530_ALS_IMP_SELECT 0x41 |
30 | #define LM3530_BRT_CTRL_REG 0xA0 | 30 | #define LM3530_BRT_CTRL_REG 0xA0 |
31 | #define LM3530_ALS_ZB0_REG 0x60 | 31 | #define LM3530_ALS_ZB0_REG 0x60 |
32 | #define LM3530_ALS_ZB1_REG 0x61 | 32 | #define LM3530_ALS_ZB1_REG 0x61 |
33 | #define LM3530_ALS_ZB2_REG 0x62 | 33 | #define LM3530_ALS_ZB2_REG 0x62 |
34 | #define LM3530_ALS_ZB3_REG 0x63 | 34 | #define LM3530_ALS_ZB3_REG 0x63 |
35 | #define LM3530_ALS_Z0T_REG 0x70 | 35 | #define LM3530_ALS_Z0T_REG 0x70 |
36 | #define LM3530_ALS_Z1T_REG 0x71 | 36 | #define LM3530_ALS_Z1T_REG 0x71 |
37 | #define LM3530_ALS_Z2T_REG 0x72 | 37 | #define LM3530_ALS_Z2T_REG 0x72 |
38 | #define LM3530_ALS_Z3T_REG 0x73 | 38 | #define LM3530_ALS_Z3T_REG 0x73 |
39 | #define LM3530_ALS_Z4T_REG 0x74 | 39 | #define LM3530_ALS_Z4T_REG 0x74 |
40 | #define LM3530_REG_MAX 14 | 40 | #define LM3530_REG_MAX 14 |
41 | 41 | ||
42 | /* General Control Register */ | 42 | /* General Control Register */ |
43 | #define LM3530_EN_I2C_SHIFT (0) | 43 | #define LM3530_EN_I2C_SHIFT (0) |
44 | #define LM3530_RAMP_LAW_SHIFT (1) | 44 | #define LM3530_RAMP_LAW_SHIFT (1) |
45 | #define LM3530_MAX_CURR_SHIFT (2) | 45 | #define LM3530_MAX_CURR_SHIFT (2) |
46 | #define LM3530_EN_PWM_SHIFT (5) | 46 | #define LM3530_EN_PWM_SHIFT (5) |
47 | #define LM3530_PWM_POL_SHIFT (6) | 47 | #define LM3530_PWM_POL_SHIFT (6) |
48 | #define LM3530_EN_PWM_SIMPLE_SHIFT (7) | 48 | #define LM3530_EN_PWM_SIMPLE_SHIFT (7) |
49 | 49 | ||
50 | #define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT) | 50 | #define LM3530_ENABLE_I2C (1 << LM3530_EN_I2C_SHIFT) |
51 | #define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT) | 51 | #define LM3530_ENABLE_PWM (1 << LM3530_EN_PWM_SHIFT) |
52 | #define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT) | 52 | #define LM3530_POL_LOW (1 << LM3530_PWM_POL_SHIFT) |
53 | #define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT) | 53 | #define LM3530_ENABLE_PWM_SIMPLE (1 << LM3530_EN_PWM_SIMPLE_SHIFT) |
54 | 54 | ||
55 | /* ALS Config Register Options */ | 55 | /* ALS Config Register Options */ |
56 | #define LM3530_ALS_AVG_TIME_SHIFT (0) | 56 | #define LM3530_ALS_AVG_TIME_SHIFT (0) |
57 | #define LM3530_EN_ALS_SHIFT (3) | 57 | #define LM3530_EN_ALS_SHIFT (3) |
58 | #define LM3530_ALS_SEL_SHIFT (5) | 58 | #define LM3530_ALS_SEL_SHIFT (5) |
59 | 59 | ||
60 | #define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT) | 60 | #define LM3530_ENABLE_ALS (3 << LM3530_EN_ALS_SHIFT) |
61 | 61 | ||
62 | /* Brightness Ramp Rate Register */ | 62 | /* Brightness Ramp Rate Register */ |
63 | #define LM3530_BRT_RAMP_FALL_SHIFT (0) | 63 | #define LM3530_BRT_RAMP_FALL_SHIFT (0) |
64 | #define LM3530_BRT_RAMP_RISE_SHIFT (3) | 64 | #define LM3530_BRT_RAMP_RISE_SHIFT (3) |
65 | 65 | ||
66 | /* ALS Resistor Select */ | 66 | /* ALS Resistor Select */ |
67 | #define LM3530_ALS1_IMP_SHIFT (0) | 67 | #define LM3530_ALS1_IMP_SHIFT (0) |
68 | #define LM3530_ALS2_IMP_SHIFT (4) | 68 | #define LM3530_ALS2_IMP_SHIFT (4) |
69 | 69 | ||
70 | /* Zone Boundary Register defaults */ | 70 | /* Zone Boundary Register defaults */ |
71 | #define LM3530_ALS_ZB_MAX (4) | 71 | #define LM3530_ALS_ZB_MAX (4) |
72 | #define LM3530_ALS_WINDOW_mV (1000) | 72 | #define LM3530_ALS_WINDOW_mV (1000) |
73 | #define LM3530_ALS_OFFSET_mV (4) | 73 | #define LM3530_ALS_OFFSET_mV (4) |
74 | 74 | ||
75 | /* Zone Target Register defaults */ | 75 | /* Zone Target Register defaults */ |
76 | #define LM3530_DEF_ZT_0 (0x7F) | 76 | #define LM3530_DEF_ZT_0 (0x7F) |
77 | #define LM3530_DEF_ZT_1 (0x66) | 77 | #define LM3530_DEF_ZT_1 (0x66) |
78 | #define LM3530_DEF_ZT_2 (0x4C) | 78 | #define LM3530_DEF_ZT_2 (0x4C) |
79 | #define LM3530_DEF_ZT_3 (0x33) | 79 | #define LM3530_DEF_ZT_3 (0x33) |
80 | #define LM3530_DEF_ZT_4 (0x19) | 80 | #define LM3530_DEF_ZT_4 (0x19) |
81 | 81 | ||
82 | /* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */ | 82 | /* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */ |
83 | #define MAX_BRIGHTNESS (127) | 83 | #define MAX_BRIGHTNESS (127) |
84 | 84 | ||
85 | struct lm3530_mode_map { | 85 | struct lm3530_mode_map { |
86 | const char *mode; | 86 | const char *mode; |
87 | enum lm3530_mode mode_val; | 87 | enum lm3530_mode mode_val; |
88 | }; | 88 | }; |
89 | 89 | ||
90 | static struct lm3530_mode_map mode_map[] = { | 90 | static struct lm3530_mode_map mode_map[] = { |
91 | { "man", LM3530_BL_MODE_MANUAL }, | 91 | { "man", LM3530_BL_MODE_MANUAL }, |
92 | { "als", LM3530_BL_MODE_ALS }, | 92 | { "als", LM3530_BL_MODE_ALS }, |
93 | { "pwm", LM3530_BL_MODE_PWM }, | 93 | { "pwm", LM3530_BL_MODE_PWM }, |
94 | }; | 94 | }; |
95 | 95 | ||
96 | /** | 96 | /** |
97 | * struct lm3530_data | 97 | * struct lm3530_data |
98 | * @led_dev: led class device | 98 | * @led_dev: led class device |
99 | * @client: i2c client | 99 | * @client: i2c client |
100 | * @pdata: LM3530 platform data | 100 | * @pdata: LM3530 platform data |
101 | * @mode: mode of operation - manual, ALS, PWM | 101 | * @mode: mode of operation - manual, ALS, PWM |
102 | * @regulator: regulator | 102 | * @regulator: regulator |
103 | * @brighness: previous brightness value | 103 | * @brighness: previous brightness value |
104 | * @enable: regulator is enabled | 104 | * @enable: regulator is enabled |
105 | */ | 105 | */ |
106 | struct lm3530_data { | 106 | struct lm3530_data { |
107 | struct led_classdev led_dev; | 107 | struct led_classdev led_dev; |
108 | struct i2c_client *client; | 108 | struct i2c_client *client; |
109 | struct lm3530_platform_data *pdata; | 109 | struct lm3530_platform_data *pdata; |
110 | enum lm3530_mode mode; | 110 | enum lm3530_mode mode; |
111 | struct regulator *regulator; | 111 | struct regulator *regulator; |
112 | enum led_brightness brightness; | 112 | enum led_brightness brightness; |
113 | bool enable; | 113 | bool enable; |
114 | }; | 114 | }; |
115 | 115 | ||
116 | /* | 116 | /* |
117 | * struct lm3530_als_data | 117 | * struct lm3530_als_data |
118 | * @config : value of ALS configuration register | 118 | * @config : value of ALS configuration register |
119 | * @imp_sel : value of ALS resistor select register | 119 | * @imp_sel : value of ALS resistor select register |
120 | * @zone : values of ALS ZB(Zone Boundary) registers | 120 | * @zone : values of ALS ZB(Zone Boundary) registers |
121 | */ | 121 | */ |
122 | struct lm3530_als_data { | 122 | struct lm3530_als_data { |
123 | u8 config; | 123 | u8 config; |
124 | u8 imp_sel; | 124 | u8 imp_sel; |
125 | u8 zones[LM3530_ALS_ZB_MAX]; | 125 | u8 zones[LM3530_ALS_ZB_MAX]; |
126 | }; | 126 | }; |
127 | 127 | ||
128 | static const u8 lm3530_reg[LM3530_REG_MAX] = { | 128 | static const u8 lm3530_reg[LM3530_REG_MAX] = { |
129 | LM3530_GEN_CONFIG, | 129 | LM3530_GEN_CONFIG, |
130 | LM3530_ALS_CONFIG, | 130 | LM3530_ALS_CONFIG, |
131 | LM3530_BRT_RAMP_RATE, | 131 | LM3530_BRT_RAMP_RATE, |
132 | LM3530_ALS_IMP_SELECT, | 132 | LM3530_ALS_IMP_SELECT, |
133 | LM3530_BRT_CTRL_REG, | 133 | LM3530_BRT_CTRL_REG, |
134 | LM3530_ALS_ZB0_REG, | 134 | LM3530_ALS_ZB0_REG, |
135 | LM3530_ALS_ZB1_REG, | 135 | LM3530_ALS_ZB1_REG, |
136 | LM3530_ALS_ZB2_REG, | 136 | LM3530_ALS_ZB2_REG, |
137 | LM3530_ALS_ZB3_REG, | 137 | LM3530_ALS_ZB3_REG, |
138 | LM3530_ALS_Z0T_REG, | 138 | LM3530_ALS_Z0T_REG, |
139 | LM3530_ALS_Z1T_REG, | 139 | LM3530_ALS_Z1T_REG, |
140 | LM3530_ALS_Z2T_REG, | 140 | LM3530_ALS_Z2T_REG, |
141 | LM3530_ALS_Z3T_REG, | 141 | LM3530_ALS_Z3T_REG, |
142 | LM3530_ALS_Z4T_REG, | 142 | LM3530_ALS_Z4T_REG, |
143 | }; | 143 | }; |
144 | 144 | ||
145 | static int lm3530_get_mode_from_str(const char *str) | 145 | static int lm3530_get_mode_from_str(const char *str) |
146 | { | 146 | { |
147 | int i; | 147 | int i; |
148 | 148 | ||
149 | for (i = 0; i < ARRAY_SIZE(mode_map); i++) | 149 | for (i = 0; i < ARRAY_SIZE(mode_map); i++) |
150 | if (sysfs_streq(str, mode_map[i].mode)) | 150 | if (sysfs_streq(str, mode_map[i].mode)) |
151 | return mode_map[i].mode_val; | 151 | return mode_map[i].mode_val; |
152 | 152 | ||
153 | return -EINVAL; | 153 | return -EINVAL; |
154 | } | 154 | } |
155 | 155 | ||
156 | static void lm3530_als_configure(struct lm3530_platform_data *pdata, | 156 | static void lm3530_als_configure(struct lm3530_platform_data *pdata, |
157 | struct lm3530_als_data *als) | 157 | struct lm3530_als_data *als) |
158 | { | 158 | { |
159 | int i; | 159 | int i; |
160 | u32 als_vmin, als_vmax, als_vstep; | 160 | u32 als_vmin, als_vmax, als_vstep; |
161 | 161 | ||
162 | if (pdata->als_vmax == 0) { | 162 | if (pdata->als_vmax == 0) { |
163 | pdata->als_vmin = 0; | 163 | pdata->als_vmin = 0; |
164 | pdata->als_vmax = LM3530_ALS_WINDOW_mV; | 164 | pdata->als_vmax = LM3530_ALS_WINDOW_mV; |
165 | } | 165 | } |
166 | 166 | ||
167 | als_vmin = pdata->als_vmin; | 167 | als_vmin = pdata->als_vmin; |
168 | als_vmax = pdata->als_vmax; | 168 | als_vmax = pdata->als_vmax; |
169 | 169 | ||
170 | if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV) | 170 | if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV) |
171 | pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV; | 171 | pdata->als_vmax = als_vmax = als_vmin + LM3530_ALS_WINDOW_mV; |
172 | 172 | ||
173 | /* n zone boundary makes n+1 zones */ | 173 | /* n zone boundary makes n+1 zones */ |
174 | als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1); | 174 | als_vstep = (als_vmax - als_vmin) / (LM3530_ALS_ZB_MAX + 1); |
175 | 175 | ||
176 | for (i = 0; i < LM3530_ALS_ZB_MAX; i++) | 176 | for (i = 0; i < LM3530_ALS_ZB_MAX; i++) |
177 | als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) + | 177 | als->zones[i] = (((als_vmin + LM3530_ALS_OFFSET_mV) + |
178 | als_vstep + (i * als_vstep)) * LED_FULL) / 1000; | 178 | als_vstep + (i * als_vstep)) * LED_FULL) / 1000; |
179 | 179 | ||
180 | als->config = | 180 | als->config = |
181 | (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) | | 181 | (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) | |
182 | (LM3530_ENABLE_ALS) | | 182 | (LM3530_ENABLE_ALS) | |
183 | (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT); | 183 | (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT); |
184 | 184 | ||
185 | als->imp_sel = | 185 | als->imp_sel = |
186 | (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) | | 186 | (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) | |
187 | (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); | 187 | (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT); |
188 | } | 188 | } |
189 | 189 | ||
190 | static int lm3530_led_enable(struct lm3530_data *drvdata) | 190 | static int lm3530_led_enable(struct lm3530_data *drvdata) |
191 | { | 191 | { |
192 | int ret; | 192 | int ret; |
193 | 193 | ||
194 | if (drvdata->enable) | 194 | if (drvdata->enable) |
195 | return 0; | 195 | return 0; |
196 | 196 | ||
197 | ret = regulator_enable(drvdata->regulator); | 197 | ret = regulator_enable(drvdata->regulator); |
198 | if (ret) { | 198 | if (ret) { |
199 | dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret); | 199 | dev_err(drvdata->led_dev.dev, "Failed to enable vin:%d\n", ret); |
200 | return ret; | 200 | return ret; |
201 | } | 201 | } |
202 | 202 | ||
203 | drvdata->enable = true; | 203 | drvdata->enable = true; |
204 | return 0; | 204 | return 0; |
205 | } | 205 | } |
206 | 206 | ||
207 | static void lm3530_led_disable(struct lm3530_data *drvdata) | 207 | static void lm3530_led_disable(struct lm3530_data *drvdata) |
208 | { | 208 | { |
209 | int ret; | 209 | int ret; |
210 | 210 | ||
211 | if (!drvdata->enable) | 211 | if (!drvdata->enable) |
212 | return; | 212 | return; |
213 | 213 | ||
214 | ret = regulator_disable(drvdata->regulator); | 214 | ret = regulator_disable(drvdata->regulator); |
215 | if (ret) { | 215 | if (ret) { |
216 | dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n", | 216 | dev_err(drvdata->led_dev.dev, "Failed to disable vin:%d\n", |
217 | ret); | 217 | ret); |
218 | return; | 218 | return; |
219 | } | 219 | } |
220 | 220 | ||
221 | drvdata->enable = false; | 221 | drvdata->enable = false; |
222 | } | 222 | } |
223 | 223 | ||
224 | static int lm3530_init_registers(struct lm3530_data *drvdata) | 224 | static int lm3530_init_registers(struct lm3530_data *drvdata) |
225 | { | 225 | { |
226 | int ret = 0; | 226 | int ret = 0; |
227 | int i; | 227 | int i; |
228 | u8 gen_config; | 228 | u8 gen_config; |
229 | u8 brt_ramp; | 229 | u8 brt_ramp; |
230 | u8 brightness; | 230 | u8 brightness; |
231 | u8 reg_val[LM3530_REG_MAX]; | 231 | u8 reg_val[LM3530_REG_MAX]; |
232 | struct lm3530_platform_data *pdata = drvdata->pdata; | 232 | struct lm3530_platform_data *pdata = drvdata->pdata; |
233 | struct i2c_client *client = drvdata->client; | 233 | struct i2c_client *client = drvdata->client; |
234 | struct lm3530_pwm_data *pwm = &pdata->pwm_data; | 234 | struct lm3530_pwm_data *pwm = &pdata->pwm_data; |
235 | struct lm3530_als_data als; | 235 | struct lm3530_als_data als; |
236 | 236 | ||
237 | memset(&als, 0, sizeof(struct lm3530_als_data)); | 237 | memset(&als, 0, sizeof(struct lm3530_als_data)); |
238 | 238 | ||
239 | gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | | 239 | gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) | |
240 | ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT); | 240 | ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT); |
241 | 241 | ||
242 | switch (drvdata->mode) { | 242 | switch (drvdata->mode) { |
243 | case LM3530_BL_MODE_MANUAL: | 243 | case LM3530_BL_MODE_MANUAL: |
244 | gen_config |= LM3530_ENABLE_I2C; | 244 | gen_config |= LM3530_ENABLE_I2C; |
245 | break; | 245 | break; |
246 | case LM3530_BL_MODE_ALS: | 246 | case LM3530_BL_MODE_ALS: |
247 | gen_config |= LM3530_ENABLE_I2C; | 247 | gen_config |= LM3530_ENABLE_I2C; |
248 | lm3530_als_configure(pdata, &als); | 248 | lm3530_als_configure(pdata, &als); |
249 | break; | 249 | break; |
250 | case LM3530_BL_MODE_PWM: | 250 | case LM3530_BL_MODE_PWM: |
251 | gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE | | 251 | gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE | |
252 | (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT); | 252 | (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT); |
253 | break; | 253 | break; |
254 | } | 254 | } |
255 | 255 | ||
256 | brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | | 256 | brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) | |
257 | (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); | 257 | (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT); |
258 | 258 | ||
259 | if (drvdata->brightness) | 259 | if (drvdata->brightness) |
260 | brightness = drvdata->brightness; | 260 | brightness = drvdata->brightness; |
261 | else | 261 | else |
262 | brightness = drvdata->brightness = pdata->brt_val; | 262 | brightness = drvdata->brightness = pdata->brt_val; |
263 | 263 | ||
264 | if (brightness > drvdata->led_dev.max_brightness) | 264 | if (brightness > drvdata->led_dev.max_brightness) |
265 | brightness = drvdata->led_dev.max_brightness; | 265 | brightness = drvdata->led_dev.max_brightness; |
266 | 266 | ||
267 | reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */ | 267 | reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */ |
268 | reg_val[1] = als.config; /* LM3530_ALS_CONFIG */ | 268 | reg_val[1] = als.config; /* LM3530_ALS_CONFIG */ |
269 | reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */ | 269 | reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */ |
270 | reg_val[3] = als.imp_sel; /* LM3530_ALS_IMP_SELECT */ | 270 | reg_val[3] = als.imp_sel; /* LM3530_ALS_IMP_SELECT */ |
271 | reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */ | 271 | reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */ |
272 | reg_val[5] = als.zones[0]; /* LM3530_ALS_ZB0_REG */ | 272 | reg_val[5] = als.zones[0]; /* LM3530_ALS_ZB0_REG */ |
273 | reg_val[6] = als.zones[1]; /* LM3530_ALS_ZB1_REG */ | 273 | reg_val[6] = als.zones[1]; /* LM3530_ALS_ZB1_REG */ |
274 | reg_val[7] = als.zones[2]; /* LM3530_ALS_ZB2_REG */ | 274 | reg_val[7] = als.zones[2]; /* LM3530_ALS_ZB2_REG */ |
275 | reg_val[8] = als.zones[3]; /* LM3530_ALS_ZB3_REG */ | 275 | reg_val[8] = als.zones[3]; /* LM3530_ALS_ZB3_REG */ |
276 | reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */ | 276 | reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */ |
277 | reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */ | 277 | reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */ |
278 | reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */ | 278 | reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */ |
279 | reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ | 279 | reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */ |
280 | reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ | 280 | reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */ |
281 | 281 | ||
282 | ret = lm3530_led_enable(drvdata); | 282 | ret = lm3530_led_enable(drvdata); |
283 | if (ret) | 283 | if (ret) |
284 | return ret; | 284 | return ret; |
285 | 285 | ||
286 | for (i = 0; i < LM3530_REG_MAX; i++) { | 286 | for (i = 0; i < LM3530_REG_MAX; i++) { |
287 | /* do not update brightness register when pwm mode */ | 287 | /* do not update brightness register when pwm mode */ |
288 | if (lm3530_reg[i] == LM3530_BRT_CTRL_REG && | 288 | if (lm3530_reg[i] == LM3530_BRT_CTRL_REG && |
289 | drvdata->mode == LM3530_BL_MODE_PWM) { | 289 | drvdata->mode == LM3530_BL_MODE_PWM) { |
290 | if (pwm->pwm_set_intensity) | 290 | if (pwm->pwm_set_intensity) |
291 | pwm->pwm_set_intensity(reg_val[i], | 291 | pwm->pwm_set_intensity(reg_val[i], |
292 | drvdata->led_dev.max_brightness); | 292 | drvdata->led_dev.max_brightness); |
293 | continue; | 293 | continue; |
294 | } | 294 | } |
295 | 295 | ||
296 | ret = i2c_smbus_write_byte_data(client, | 296 | ret = i2c_smbus_write_byte_data(client, |
297 | lm3530_reg[i], reg_val[i]); | 297 | lm3530_reg[i], reg_val[i]); |
298 | if (ret) | 298 | if (ret) |
299 | break; | 299 | break; |
300 | } | 300 | } |
301 | 301 | ||
302 | return ret; | 302 | return ret; |
303 | } | 303 | } |
304 | 304 | ||
305 | static void lm3530_brightness_set(struct led_classdev *led_cdev, | 305 | static void lm3530_brightness_set(struct led_classdev *led_cdev, |
306 | enum led_brightness brt_val) | 306 | enum led_brightness brt_val) |
307 | { | 307 | { |
308 | int err; | 308 | int err; |
309 | struct lm3530_data *drvdata = | 309 | struct lm3530_data *drvdata = |
310 | container_of(led_cdev, struct lm3530_data, led_dev); | 310 | container_of(led_cdev, struct lm3530_data, led_dev); |
311 | struct lm3530_platform_data *pdata = drvdata->pdata; | 311 | struct lm3530_platform_data *pdata = drvdata->pdata; |
312 | struct lm3530_pwm_data *pwm = &pdata->pwm_data; | 312 | struct lm3530_pwm_data *pwm = &pdata->pwm_data; |
313 | u8 max_brightness = led_cdev->max_brightness; | 313 | u8 max_brightness = led_cdev->max_brightness; |
314 | 314 | ||
315 | switch (drvdata->mode) { | 315 | switch (drvdata->mode) { |
316 | case LM3530_BL_MODE_MANUAL: | 316 | case LM3530_BL_MODE_MANUAL: |
317 | 317 | ||
318 | if (!drvdata->enable) { | 318 | if (!drvdata->enable) { |
319 | err = lm3530_init_registers(drvdata); | 319 | err = lm3530_init_registers(drvdata); |
320 | if (err) { | 320 | if (err) { |
321 | dev_err(&drvdata->client->dev, | 321 | dev_err(&drvdata->client->dev, |
322 | "Register Init failed: %d\n", err); | 322 | "Register Init failed: %d\n", err); |
323 | break; | 323 | break; |
324 | } | 324 | } |
325 | } | 325 | } |
326 | 326 | ||
327 | /* set the brightness in brightness control register*/ | 327 | /* set the brightness in brightness control register*/ |
328 | err = i2c_smbus_write_byte_data(drvdata->client, | 328 | err = i2c_smbus_write_byte_data(drvdata->client, |
329 | LM3530_BRT_CTRL_REG, brt_val); | 329 | LM3530_BRT_CTRL_REG, brt_val); |
330 | if (err) | 330 | if (err) |
331 | dev_err(&drvdata->client->dev, | 331 | dev_err(&drvdata->client->dev, |
332 | "Unable to set brightness: %d\n", err); | 332 | "Unable to set brightness: %d\n", err); |
333 | else | 333 | else |
334 | drvdata->brightness = brt_val; | 334 | drvdata->brightness = brt_val; |
335 | 335 | ||
336 | if (brt_val == 0) | 336 | if (brt_val == 0) |
337 | lm3530_led_disable(drvdata); | 337 | lm3530_led_disable(drvdata); |
338 | break; | 338 | break; |
339 | case LM3530_BL_MODE_ALS: | 339 | case LM3530_BL_MODE_ALS: |
340 | break; | 340 | break; |
341 | case LM3530_BL_MODE_PWM: | 341 | case LM3530_BL_MODE_PWM: |
342 | if (pwm->pwm_set_intensity) | 342 | if (pwm->pwm_set_intensity) |
343 | pwm->pwm_set_intensity(brt_val, max_brightness); | 343 | pwm->pwm_set_intensity(brt_val, max_brightness); |
344 | break; | 344 | break; |
345 | default: | 345 | default: |
346 | break; | 346 | break; |
347 | } | 347 | } |
348 | } | 348 | } |
349 | 349 | ||
350 | static ssize_t lm3530_mode_get(struct device *dev, | 350 | static ssize_t lm3530_mode_get(struct device *dev, |
351 | struct device_attribute *attr, char *buf) | 351 | struct device_attribute *attr, char *buf) |
352 | { | 352 | { |
353 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 353 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
354 | struct lm3530_data *drvdata; | 354 | struct lm3530_data *drvdata; |
355 | int i, len = 0; | 355 | int i, len = 0; |
356 | 356 | ||
357 | drvdata = container_of(led_cdev, struct lm3530_data, led_dev); | 357 | drvdata = container_of(led_cdev, struct lm3530_data, led_dev); |
358 | for (i = 0; i < ARRAY_SIZE(mode_map); i++) | 358 | for (i = 0; i < ARRAY_SIZE(mode_map); i++) |
359 | if (drvdata->mode == mode_map[i].mode_val) | 359 | if (drvdata->mode == mode_map[i].mode_val) |
360 | len += sprintf(buf + len, "[%s] ", mode_map[i].mode); | 360 | len += sprintf(buf + len, "[%s] ", mode_map[i].mode); |
361 | else | 361 | else |
362 | len += sprintf(buf + len, "%s ", mode_map[i].mode); | 362 | len += sprintf(buf + len, "%s ", mode_map[i].mode); |
363 | 363 | ||
364 | len += sprintf(buf + len, "\n"); | 364 | len += sprintf(buf + len, "\n"); |
365 | 365 | ||
366 | return len; | 366 | return len; |
367 | } | 367 | } |
368 | 368 | ||
369 | static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute | 369 | static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute |
370 | *attr, const char *buf, size_t size) | 370 | *attr, const char *buf, size_t size) |
371 | { | 371 | { |
372 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 372 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
373 | struct lm3530_data *drvdata; | 373 | struct lm3530_data *drvdata; |
374 | struct lm3530_pwm_data *pwm; | 374 | struct lm3530_pwm_data *pwm; |
375 | u8 max_brightness; | 375 | u8 max_brightness; |
376 | int mode, err; | 376 | int mode, err; |
377 | 377 | ||
378 | drvdata = container_of(led_cdev, struct lm3530_data, led_dev); | 378 | drvdata = container_of(led_cdev, struct lm3530_data, led_dev); |
379 | pwm = &drvdata->pdata->pwm_data; | 379 | pwm = &drvdata->pdata->pwm_data; |
380 | max_brightness = led_cdev->max_brightness; | 380 | max_brightness = led_cdev->max_brightness; |
381 | mode = lm3530_get_mode_from_str(buf); | 381 | mode = lm3530_get_mode_from_str(buf); |
382 | if (mode < 0) { | 382 | if (mode < 0) { |
383 | dev_err(dev, "Invalid mode\n"); | 383 | dev_err(dev, "Invalid mode\n"); |
384 | return mode; | 384 | return mode; |
385 | } | 385 | } |
386 | 386 | ||
387 | drvdata->mode = mode; | 387 | drvdata->mode = mode; |
388 | 388 | ||
389 | /* set pwm to low if unnecessary */ | 389 | /* set pwm to low if unnecessary */ |
390 | if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity) | 390 | if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity) |
391 | pwm->pwm_set_intensity(0, max_brightness); | 391 | pwm->pwm_set_intensity(0, max_brightness); |
392 | 392 | ||
393 | err = lm3530_init_registers(drvdata); | 393 | err = lm3530_init_registers(drvdata); |
394 | if (err) { | 394 | if (err) { |
395 | dev_err(dev, "Setting %s Mode failed :%d\n", buf, err); | 395 | dev_err(dev, "Setting %s Mode failed :%d\n", buf, err); |
396 | return err; | 396 | return err; |
397 | } | 397 | } |
398 | 398 | ||
399 | return sizeof(drvdata->mode); | 399 | return sizeof(drvdata->mode); |
400 | } | 400 | } |
401 | static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set); | 401 | static DEVICE_ATTR(mode, 0644, lm3530_mode_get, lm3530_mode_set); |
402 | 402 | ||
403 | static int lm3530_probe(struct i2c_client *client, | 403 | static int lm3530_probe(struct i2c_client *client, |
404 | const struct i2c_device_id *id) | 404 | const struct i2c_device_id *id) |
405 | { | 405 | { |
406 | struct lm3530_platform_data *pdata = client->dev.platform_data; | 406 | struct lm3530_platform_data *pdata = dev_get_platdata(&client->dev); |
407 | struct lm3530_data *drvdata; | 407 | struct lm3530_data *drvdata; |
408 | int err = 0; | 408 | int err = 0; |
409 | 409 | ||
410 | if (pdata == NULL) { | 410 | if (pdata == NULL) { |
411 | dev_err(&client->dev, "platform data required\n"); | 411 | dev_err(&client->dev, "platform data required\n"); |
412 | return -ENODEV; | 412 | return -ENODEV; |
413 | } | 413 | } |
414 | 414 | ||
415 | /* BL mode */ | 415 | /* BL mode */ |
416 | if (pdata->mode > LM3530_BL_MODE_PWM) { | 416 | if (pdata->mode > LM3530_BL_MODE_PWM) { |
417 | dev_err(&client->dev, "Illegal Mode request\n"); | 417 | dev_err(&client->dev, "Illegal Mode request\n"); |
418 | return -EINVAL; | 418 | return -EINVAL; |
419 | } | 419 | } |
420 | 420 | ||
421 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | 421 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
422 | dev_err(&client->dev, "I2C_FUNC_I2C not supported\n"); | 422 | dev_err(&client->dev, "I2C_FUNC_I2C not supported\n"); |
423 | return -EIO; | 423 | return -EIO; |
424 | } | 424 | } |
425 | 425 | ||
426 | drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data), | 426 | drvdata = devm_kzalloc(&client->dev, sizeof(struct lm3530_data), |
427 | GFP_KERNEL); | 427 | GFP_KERNEL); |
428 | if (drvdata == NULL) | 428 | if (drvdata == NULL) |
429 | return -ENOMEM; | 429 | return -ENOMEM; |
430 | 430 | ||
431 | drvdata->mode = pdata->mode; | 431 | drvdata->mode = pdata->mode; |
432 | drvdata->client = client; | 432 | drvdata->client = client; |
433 | drvdata->pdata = pdata; | 433 | drvdata->pdata = pdata; |
434 | drvdata->brightness = LED_OFF; | 434 | drvdata->brightness = LED_OFF; |
435 | drvdata->enable = false; | 435 | drvdata->enable = false; |
436 | drvdata->led_dev.name = LM3530_LED_DEV; | 436 | drvdata->led_dev.name = LM3530_LED_DEV; |
437 | drvdata->led_dev.brightness_set = lm3530_brightness_set; | 437 | drvdata->led_dev.brightness_set = lm3530_brightness_set; |
438 | drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; | 438 | drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; |
439 | 439 | ||
440 | i2c_set_clientdata(client, drvdata); | 440 | i2c_set_clientdata(client, drvdata); |
441 | 441 | ||
442 | drvdata->regulator = devm_regulator_get(&client->dev, "vin"); | 442 | drvdata->regulator = devm_regulator_get(&client->dev, "vin"); |
443 | if (IS_ERR(drvdata->regulator)) { | 443 | if (IS_ERR(drvdata->regulator)) { |
444 | dev_err(&client->dev, "regulator get failed\n"); | 444 | dev_err(&client->dev, "regulator get failed\n"); |
445 | err = PTR_ERR(drvdata->regulator); | 445 | err = PTR_ERR(drvdata->regulator); |
446 | drvdata->regulator = NULL; | 446 | drvdata->regulator = NULL; |
447 | return err; | 447 | return err; |
448 | } | 448 | } |
449 | 449 | ||
450 | if (drvdata->pdata->brt_val) { | 450 | if (drvdata->pdata->brt_val) { |
451 | err = lm3530_init_registers(drvdata); | 451 | err = lm3530_init_registers(drvdata); |
452 | if (err < 0) { | 452 | if (err < 0) { |
453 | dev_err(&client->dev, | 453 | dev_err(&client->dev, |
454 | "Register Init failed: %d\n", err); | 454 | "Register Init failed: %d\n", err); |
455 | return err; | 455 | return err; |
456 | } | 456 | } |
457 | } | 457 | } |
458 | err = led_classdev_register(&client->dev, &drvdata->led_dev); | 458 | err = led_classdev_register(&client->dev, &drvdata->led_dev); |
459 | if (err < 0) { | 459 | if (err < 0) { |
460 | dev_err(&client->dev, "Register led class failed: %d\n", err); | 460 | dev_err(&client->dev, "Register led class failed: %d\n", err); |
461 | return err; | 461 | return err; |
462 | } | 462 | } |
463 | 463 | ||
464 | err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); | 464 | err = device_create_file(drvdata->led_dev.dev, &dev_attr_mode); |
465 | if (err < 0) { | 465 | if (err < 0) { |
466 | dev_err(&client->dev, "File device creation failed: %d\n", err); | 466 | dev_err(&client->dev, "File device creation failed: %d\n", err); |
467 | err = -ENODEV; | 467 | err = -ENODEV; |
468 | goto err_create_file; | 468 | goto err_create_file; |
469 | } | 469 | } |
470 | 470 | ||
471 | return 0; | 471 | return 0; |
472 | 472 | ||
473 | err_create_file: | 473 | err_create_file: |
474 | led_classdev_unregister(&drvdata->led_dev); | 474 | led_classdev_unregister(&drvdata->led_dev); |
475 | return err; | 475 | return err; |
476 | } | 476 | } |
477 | 477 | ||
478 | static int lm3530_remove(struct i2c_client *client) | 478 | static int lm3530_remove(struct i2c_client *client) |
479 | { | 479 | { |
480 | struct lm3530_data *drvdata = i2c_get_clientdata(client); | 480 | struct lm3530_data *drvdata = i2c_get_clientdata(client); |
481 | 481 | ||
482 | device_remove_file(drvdata->led_dev.dev, &dev_attr_mode); | 482 | device_remove_file(drvdata->led_dev.dev, &dev_attr_mode); |
483 | 483 | ||
484 | lm3530_led_disable(drvdata); | 484 | lm3530_led_disable(drvdata); |
485 | led_classdev_unregister(&drvdata->led_dev); | 485 | led_classdev_unregister(&drvdata->led_dev); |
486 | return 0; | 486 | return 0; |
487 | } | 487 | } |
488 | 488 | ||
489 | static const struct i2c_device_id lm3530_id[] = { | 489 | static const struct i2c_device_id lm3530_id[] = { |
490 | {LM3530_NAME, 0}, | 490 | {LM3530_NAME, 0}, |
491 | {} | 491 | {} |
492 | }; | 492 | }; |
493 | MODULE_DEVICE_TABLE(i2c, lm3530_id); | 493 | MODULE_DEVICE_TABLE(i2c, lm3530_id); |
494 | 494 | ||
495 | static struct i2c_driver lm3530_i2c_driver = { | 495 | static struct i2c_driver lm3530_i2c_driver = { |
496 | .probe = lm3530_probe, | 496 | .probe = lm3530_probe, |
497 | .remove = lm3530_remove, | 497 | .remove = lm3530_remove, |
498 | .id_table = lm3530_id, | 498 | .id_table = lm3530_id, |
499 | .driver = { | 499 | .driver = { |
500 | .name = LM3530_NAME, | 500 | .name = LM3530_NAME, |
501 | .owner = THIS_MODULE, | 501 | .owner = THIS_MODULE, |
502 | }, | 502 | }, |
503 | }; | 503 | }; |
504 | 504 | ||
505 | module_i2c_driver(lm3530_i2c_driver); | 505 | module_i2c_driver(lm3530_i2c_driver); |
506 | 506 | ||
507 | MODULE_DESCRIPTION("Back Light driver for LM3530"); | 507 | MODULE_DESCRIPTION("Back Light driver for LM3530"); |
508 | MODULE_LICENSE("GPL v2"); | 508 | MODULE_LICENSE("GPL v2"); |
509 | MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>"); | 509 | MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>"); |
510 | 510 |
drivers/leds/leds-lm3533.c
1 | /* | 1 | /* |
2 | * leds-lm3533.c -- LM3533 LED driver | 2 | * leds-lm3533.c -- LM3533 LED driver |
3 | * | 3 | * |
4 | * Copyright (C) 2011-2012 Texas Instruments | 4 | * Copyright (C) 2011-2012 Texas Instruments |
5 | * | 5 | * |
6 | * Author: Johan Hovold <jhovold@gmail.com> | 6 | * Author: Johan Hovold <jhovold@gmail.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your | 10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. | 11 | * option) any later version. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/leds.h> | 16 | #include <linux/leds.h> |
17 | #include <linux/mfd/core.h> | 17 | #include <linux/mfd/core.h> |
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
22 | 22 | ||
23 | #include <linux/mfd/lm3533.h> | 23 | #include <linux/mfd/lm3533.h> |
24 | 24 | ||
25 | 25 | ||
26 | #define LM3533_LVCTRLBANK_MIN 2 | 26 | #define LM3533_LVCTRLBANK_MIN 2 |
27 | #define LM3533_LVCTRLBANK_MAX 5 | 27 | #define LM3533_LVCTRLBANK_MAX 5 |
28 | #define LM3533_LVCTRLBANK_COUNT 4 | 28 | #define LM3533_LVCTRLBANK_COUNT 4 |
29 | #define LM3533_RISEFALLTIME_MAX 7 | 29 | #define LM3533_RISEFALLTIME_MAX 7 |
30 | #define LM3533_ALS_CHANNEL_LV_MIN 1 | 30 | #define LM3533_ALS_CHANNEL_LV_MIN 1 |
31 | #define LM3533_ALS_CHANNEL_LV_MAX 2 | 31 | #define LM3533_ALS_CHANNEL_LV_MAX 2 |
32 | 32 | ||
33 | #define LM3533_REG_CTRLBANK_BCONF_BASE 0x1b | 33 | #define LM3533_REG_CTRLBANK_BCONF_BASE 0x1b |
34 | #define LM3533_REG_PATTERN_ENABLE 0x28 | 34 | #define LM3533_REG_PATTERN_ENABLE 0x28 |
35 | #define LM3533_REG_PATTERN_LOW_TIME_BASE 0x71 | 35 | #define LM3533_REG_PATTERN_LOW_TIME_BASE 0x71 |
36 | #define LM3533_REG_PATTERN_HIGH_TIME_BASE 0x72 | 36 | #define LM3533_REG_PATTERN_HIGH_TIME_BASE 0x72 |
37 | #define LM3533_REG_PATTERN_RISETIME_BASE 0x74 | 37 | #define LM3533_REG_PATTERN_RISETIME_BASE 0x74 |
38 | #define LM3533_REG_PATTERN_FALLTIME_BASE 0x75 | 38 | #define LM3533_REG_PATTERN_FALLTIME_BASE 0x75 |
39 | 39 | ||
40 | #define LM3533_REG_PATTERN_STEP 0x10 | 40 | #define LM3533_REG_PATTERN_STEP 0x10 |
41 | 41 | ||
42 | #define LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK 0x04 | 42 | #define LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK 0x04 |
43 | #define LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK 0x02 | 43 | #define LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK 0x02 |
44 | #define LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK 0x01 | 44 | #define LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK 0x01 |
45 | 45 | ||
46 | #define LM3533_LED_FLAG_PATTERN_ENABLE 1 | 46 | #define LM3533_LED_FLAG_PATTERN_ENABLE 1 |
47 | 47 | ||
48 | 48 | ||
49 | struct lm3533_led { | 49 | struct lm3533_led { |
50 | struct lm3533 *lm3533; | 50 | struct lm3533 *lm3533; |
51 | struct lm3533_ctrlbank cb; | 51 | struct lm3533_ctrlbank cb; |
52 | struct led_classdev cdev; | 52 | struct led_classdev cdev; |
53 | int id; | 53 | int id; |
54 | 54 | ||
55 | struct mutex mutex; | 55 | struct mutex mutex; |
56 | unsigned long flags; | 56 | unsigned long flags; |
57 | 57 | ||
58 | struct work_struct work; | 58 | struct work_struct work; |
59 | u8 new_brightness; | 59 | u8 new_brightness; |
60 | }; | 60 | }; |
61 | 61 | ||
62 | 62 | ||
63 | static inline struct lm3533_led *to_lm3533_led(struct led_classdev *cdev) | 63 | static inline struct lm3533_led *to_lm3533_led(struct led_classdev *cdev) |
64 | { | 64 | { |
65 | return container_of(cdev, struct lm3533_led, cdev); | 65 | return container_of(cdev, struct lm3533_led, cdev); |
66 | } | 66 | } |
67 | 67 | ||
68 | static inline int lm3533_led_get_ctrlbank_id(struct lm3533_led *led) | 68 | static inline int lm3533_led_get_ctrlbank_id(struct lm3533_led *led) |
69 | { | 69 | { |
70 | return led->id + 2; | 70 | return led->id + 2; |
71 | } | 71 | } |
72 | 72 | ||
73 | static inline u8 lm3533_led_get_lv_reg(struct lm3533_led *led, u8 base) | 73 | static inline u8 lm3533_led_get_lv_reg(struct lm3533_led *led, u8 base) |
74 | { | 74 | { |
75 | return base + led->id; | 75 | return base + led->id; |
76 | } | 76 | } |
77 | 77 | ||
78 | static inline u8 lm3533_led_get_pattern(struct lm3533_led *led) | 78 | static inline u8 lm3533_led_get_pattern(struct lm3533_led *led) |
79 | { | 79 | { |
80 | return led->id; | 80 | return led->id; |
81 | } | 81 | } |
82 | 82 | ||
83 | static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led, | 83 | static inline u8 lm3533_led_get_pattern_reg(struct lm3533_led *led, |
84 | u8 base) | 84 | u8 base) |
85 | { | 85 | { |
86 | return base + lm3533_led_get_pattern(led) * LM3533_REG_PATTERN_STEP; | 86 | return base + lm3533_led_get_pattern(led) * LM3533_REG_PATTERN_STEP; |
87 | } | 87 | } |
88 | 88 | ||
89 | static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) | 89 | static int lm3533_led_pattern_enable(struct lm3533_led *led, int enable) |
90 | { | 90 | { |
91 | u8 mask; | 91 | u8 mask; |
92 | u8 val; | 92 | u8 val; |
93 | int pattern; | 93 | int pattern; |
94 | int state; | 94 | int state; |
95 | int ret = 0; | 95 | int ret = 0; |
96 | 96 | ||
97 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, enable); | 97 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, enable); |
98 | 98 | ||
99 | mutex_lock(&led->mutex); | 99 | mutex_lock(&led->mutex); |
100 | 100 | ||
101 | state = test_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); | 101 | state = test_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); |
102 | if ((enable && state) || (!enable && !state)) | 102 | if ((enable && state) || (!enable && !state)) |
103 | goto out; | 103 | goto out; |
104 | 104 | ||
105 | pattern = lm3533_led_get_pattern(led); | 105 | pattern = lm3533_led_get_pattern(led); |
106 | mask = 1 << (2 * pattern); | 106 | mask = 1 << (2 * pattern); |
107 | 107 | ||
108 | if (enable) | 108 | if (enable) |
109 | val = mask; | 109 | val = mask; |
110 | else | 110 | else |
111 | val = 0; | 111 | val = 0; |
112 | 112 | ||
113 | ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask); | 113 | ret = lm3533_update(led->lm3533, LM3533_REG_PATTERN_ENABLE, val, mask); |
114 | if (ret) { | 114 | if (ret) { |
115 | dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n", | 115 | dev_err(led->cdev.dev, "failed to enable pattern %d (%d)\n", |
116 | pattern, enable); | 116 | pattern, enable); |
117 | goto out; | 117 | goto out; |
118 | } | 118 | } |
119 | 119 | ||
120 | __change_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); | 120 | __change_bit(LM3533_LED_FLAG_PATTERN_ENABLE, &led->flags); |
121 | out: | 121 | out: |
122 | mutex_unlock(&led->mutex); | 122 | mutex_unlock(&led->mutex); |
123 | 123 | ||
124 | return ret; | 124 | return ret; |
125 | } | 125 | } |
126 | 126 | ||
127 | static void lm3533_led_work(struct work_struct *work) | 127 | static void lm3533_led_work(struct work_struct *work) |
128 | { | 128 | { |
129 | struct lm3533_led *led = container_of(work, struct lm3533_led, work); | 129 | struct lm3533_led *led = container_of(work, struct lm3533_led, work); |
130 | 130 | ||
131 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness); | 131 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, led->new_brightness); |
132 | 132 | ||
133 | if (led->new_brightness == 0) | 133 | if (led->new_brightness == 0) |
134 | lm3533_led_pattern_enable(led, 0); /* disable blink */ | 134 | lm3533_led_pattern_enable(led, 0); /* disable blink */ |
135 | 135 | ||
136 | lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness); | 136 | lm3533_ctrlbank_set_brightness(&led->cb, led->new_brightness); |
137 | } | 137 | } |
138 | 138 | ||
139 | static void lm3533_led_set(struct led_classdev *cdev, | 139 | static void lm3533_led_set(struct led_classdev *cdev, |
140 | enum led_brightness value) | 140 | enum led_brightness value) |
141 | { | 141 | { |
142 | struct lm3533_led *led = to_lm3533_led(cdev); | 142 | struct lm3533_led *led = to_lm3533_led(cdev); |
143 | 143 | ||
144 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value); | 144 | dev_dbg(led->cdev.dev, "%s - %d\n", __func__, value); |
145 | 145 | ||
146 | led->new_brightness = value; | 146 | led->new_brightness = value; |
147 | schedule_work(&led->work); | 147 | schedule_work(&led->work); |
148 | } | 148 | } |
149 | 149 | ||
150 | static enum led_brightness lm3533_led_get(struct led_classdev *cdev) | 150 | static enum led_brightness lm3533_led_get(struct led_classdev *cdev) |
151 | { | 151 | { |
152 | struct lm3533_led *led = to_lm3533_led(cdev); | 152 | struct lm3533_led *led = to_lm3533_led(cdev); |
153 | u8 val; | 153 | u8 val; |
154 | int ret; | 154 | int ret; |
155 | 155 | ||
156 | ret = lm3533_ctrlbank_get_brightness(&led->cb, &val); | 156 | ret = lm3533_ctrlbank_get_brightness(&led->cb, &val); |
157 | if (ret) | 157 | if (ret) |
158 | return ret; | 158 | return ret; |
159 | 159 | ||
160 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, val); | 160 | dev_dbg(led->cdev.dev, "%s - %u\n", __func__, val); |
161 | 161 | ||
162 | return val; | 162 | return val; |
163 | } | 163 | } |
164 | 164 | ||
165 | /* Pattern generator defines (delays in us). */ | 165 | /* Pattern generator defines (delays in us). */ |
166 | #define LM3533_LED_DELAY1_VMIN 0x00 | 166 | #define LM3533_LED_DELAY1_VMIN 0x00 |
167 | #define LM3533_LED_DELAY2_VMIN 0x3d | 167 | #define LM3533_LED_DELAY2_VMIN 0x3d |
168 | #define LM3533_LED_DELAY3_VMIN 0x80 | 168 | #define LM3533_LED_DELAY3_VMIN 0x80 |
169 | 169 | ||
170 | #define LM3533_LED_DELAY1_VMAX (LM3533_LED_DELAY2_VMIN - 1) | 170 | #define LM3533_LED_DELAY1_VMAX (LM3533_LED_DELAY2_VMIN - 1) |
171 | #define LM3533_LED_DELAY2_VMAX (LM3533_LED_DELAY3_VMIN - 1) | 171 | #define LM3533_LED_DELAY2_VMAX (LM3533_LED_DELAY3_VMIN - 1) |
172 | #define LM3533_LED_DELAY3_VMAX 0xff | 172 | #define LM3533_LED_DELAY3_VMAX 0xff |
173 | 173 | ||
174 | #define LM3533_LED_DELAY1_TMIN 16384U | 174 | #define LM3533_LED_DELAY1_TMIN 16384U |
175 | #define LM3533_LED_DELAY2_TMIN 1130496U | 175 | #define LM3533_LED_DELAY2_TMIN 1130496U |
176 | #define LM3533_LED_DELAY3_TMIN 10305536U | 176 | #define LM3533_LED_DELAY3_TMIN 10305536U |
177 | 177 | ||
178 | #define LM3533_LED_DELAY1_TMAX 999424U | 178 | #define LM3533_LED_DELAY1_TMAX 999424U |
179 | #define LM3533_LED_DELAY2_TMAX 9781248U | 179 | #define LM3533_LED_DELAY2_TMAX 9781248U |
180 | #define LM3533_LED_DELAY3_TMAX 76890112U | 180 | #define LM3533_LED_DELAY3_TMAX 76890112U |
181 | 181 | ||
182 | /* t_step = (t_max - t_min) / (v_max - v_min) */ | 182 | /* t_step = (t_max - t_min) / (v_max - v_min) */ |
183 | #define LM3533_LED_DELAY1_TSTEP 16384 | 183 | #define LM3533_LED_DELAY1_TSTEP 16384 |
184 | #define LM3533_LED_DELAY2_TSTEP 131072 | 184 | #define LM3533_LED_DELAY2_TSTEP 131072 |
185 | #define LM3533_LED_DELAY3_TSTEP 524288 | 185 | #define LM3533_LED_DELAY3_TSTEP 524288 |
186 | 186 | ||
187 | /* Delay limits for hardware accelerated blinking (in ms). */ | 187 | /* Delay limits for hardware accelerated blinking (in ms). */ |
188 | #define LM3533_LED_DELAY_ON_MAX \ | 188 | #define LM3533_LED_DELAY_ON_MAX \ |
189 | ((LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY2_TSTEP / 2) / 1000) | 189 | ((LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY2_TSTEP / 2) / 1000) |
190 | #define LM3533_LED_DELAY_OFF_MAX \ | 190 | #define LM3533_LED_DELAY_OFF_MAX \ |
191 | ((LM3533_LED_DELAY3_TMAX + LM3533_LED_DELAY3_TSTEP / 2) / 1000) | 191 | ((LM3533_LED_DELAY3_TMAX + LM3533_LED_DELAY3_TSTEP / 2) / 1000) |
192 | 192 | ||
193 | /* | 193 | /* |
194 | * Returns linear map of *t from [t_min,t_max] to [v_min,v_max] with a step | 194 | * Returns linear map of *t from [t_min,t_max] to [v_min,v_max] with a step |
195 | * size of t_step, where | 195 | * size of t_step, where |
196 | * | 196 | * |
197 | * t_step = (t_max - t_min) / (v_max - v_min) | 197 | * t_step = (t_max - t_min) / (v_max - v_min) |
198 | * | 198 | * |
199 | * and updates *t to reflect the mapped value. | 199 | * and updates *t to reflect the mapped value. |
200 | */ | 200 | */ |
201 | static u8 time_to_val(unsigned *t, unsigned t_min, unsigned t_step, | 201 | static u8 time_to_val(unsigned *t, unsigned t_min, unsigned t_step, |
202 | u8 v_min, u8 v_max) | 202 | u8 v_min, u8 v_max) |
203 | { | 203 | { |
204 | unsigned val; | 204 | unsigned val; |
205 | 205 | ||
206 | val = (*t + t_step / 2 - t_min) / t_step + v_min; | 206 | val = (*t + t_step / 2 - t_min) / t_step + v_min; |
207 | 207 | ||
208 | *t = t_step * (val - v_min) + t_min; | 208 | *t = t_step * (val - v_min) + t_min; |
209 | 209 | ||
210 | return (u8)val; | 210 | return (u8)val; |
211 | } | 211 | } |
212 | 212 | ||
213 | /* | 213 | /* |
214 | * Returns time code corresponding to *delay (in ms) and updates *delay to | 214 | * Returns time code corresponding to *delay (in ms) and updates *delay to |
215 | * reflect actual hardware delay. | 215 | * reflect actual hardware delay. |
216 | * | 216 | * |
217 | * Hardware supports 256 discrete delay times, divided into three groups with | 217 | * Hardware supports 256 discrete delay times, divided into three groups with |
218 | * the following ranges and step-sizes: | 218 | * the following ranges and step-sizes: |
219 | * | 219 | * |
220 | * [ 16, 999] [0x00, 0x3e] step 16 ms | 220 | * [ 16, 999] [0x00, 0x3e] step 16 ms |
221 | * [ 1130, 9781] [0x3d, 0x7f] step 131 ms | 221 | * [ 1130, 9781] [0x3d, 0x7f] step 131 ms |
222 | * [10306, 76890] [0x80, 0xff] step 524 ms | 222 | * [10306, 76890] [0x80, 0xff] step 524 ms |
223 | * | 223 | * |
224 | * Note that delay group 3 is only available for delay_off. | 224 | * Note that delay group 3 is only available for delay_off. |
225 | */ | 225 | */ |
226 | static u8 lm3533_led_get_hw_delay(unsigned *delay) | 226 | static u8 lm3533_led_get_hw_delay(unsigned *delay) |
227 | { | 227 | { |
228 | unsigned t; | 228 | unsigned t; |
229 | u8 val; | 229 | u8 val; |
230 | 230 | ||
231 | t = *delay * 1000; | 231 | t = *delay * 1000; |
232 | 232 | ||
233 | if (t >= (LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY3_TMIN) / 2) { | 233 | if (t >= (LM3533_LED_DELAY2_TMAX + LM3533_LED_DELAY3_TMIN) / 2) { |
234 | t = clamp(t, LM3533_LED_DELAY3_TMIN, LM3533_LED_DELAY3_TMAX); | 234 | t = clamp(t, LM3533_LED_DELAY3_TMIN, LM3533_LED_DELAY3_TMAX); |
235 | val = time_to_val(&t, LM3533_LED_DELAY3_TMIN, | 235 | val = time_to_val(&t, LM3533_LED_DELAY3_TMIN, |
236 | LM3533_LED_DELAY3_TSTEP, | 236 | LM3533_LED_DELAY3_TSTEP, |
237 | LM3533_LED_DELAY3_VMIN, | 237 | LM3533_LED_DELAY3_VMIN, |
238 | LM3533_LED_DELAY3_VMAX); | 238 | LM3533_LED_DELAY3_VMAX); |
239 | } else if (t >= (LM3533_LED_DELAY1_TMAX + LM3533_LED_DELAY2_TMIN) / 2) { | 239 | } else if (t >= (LM3533_LED_DELAY1_TMAX + LM3533_LED_DELAY2_TMIN) / 2) { |
240 | t = clamp(t, LM3533_LED_DELAY2_TMIN, LM3533_LED_DELAY2_TMAX); | 240 | t = clamp(t, LM3533_LED_DELAY2_TMIN, LM3533_LED_DELAY2_TMAX); |
241 | val = time_to_val(&t, LM3533_LED_DELAY2_TMIN, | 241 | val = time_to_val(&t, LM3533_LED_DELAY2_TMIN, |
242 | LM3533_LED_DELAY2_TSTEP, | 242 | LM3533_LED_DELAY2_TSTEP, |
243 | LM3533_LED_DELAY2_VMIN, | 243 | LM3533_LED_DELAY2_VMIN, |
244 | LM3533_LED_DELAY2_VMAX); | 244 | LM3533_LED_DELAY2_VMAX); |
245 | } else { | 245 | } else { |
246 | t = clamp(t, LM3533_LED_DELAY1_TMIN, LM3533_LED_DELAY1_TMAX); | 246 | t = clamp(t, LM3533_LED_DELAY1_TMIN, LM3533_LED_DELAY1_TMAX); |
247 | val = time_to_val(&t, LM3533_LED_DELAY1_TMIN, | 247 | val = time_to_val(&t, LM3533_LED_DELAY1_TMIN, |
248 | LM3533_LED_DELAY1_TSTEP, | 248 | LM3533_LED_DELAY1_TSTEP, |
249 | LM3533_LED_DELAY1_VMIN, | 249 | LM3533_LED_DELAY1_VMIN, |
250 | LM3533_LED_DELAY1_VMAX); | 250 | LM3533_LED_DELAY1_VMAX); |
251 | } | 251 | } |
252 | 252 | ||
253 | *delay = (t + 500) / 1000; | 253 | *delay = (t + 500) / 1000; |
254 | 254 | ||
255 | return val; | 255 | return val; |
256 | } | 256 | } |
257 | 257 | ||
258 | /* | 258 | /* |
259 | * Set delay register base to *delay (in ms) and update *delay to reflect | 259 | * Set delay register base to *delay (in ms) and update *delay to reflect |
260 | * actual hardware delay used. | 260 | * actual hardware delay used. |
261 | */ | 261 | */ |
262 | static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base, | 262 | static u8 lm3533_led_delay_set(struct lm3533_led *led, u8 base, |
263 | unsigned long *delay) | 263 | unsigned long *delay) |
264 | { | 264 | { |
265 | unsigned t; | 265 | unsigned t; |
266 | u8 val; | 266 | u8 val; |
267 | u8 reg; | 267 | u8 reg; |
268 | int ret; | 268 | int ret; |
269 | 269 | ||
270 | t = (unsigned)*delay; | 270 | t = (unsigned)*delay; |
271 | 271 | ||
272 | /* Delay group 3 is only available for low time (delay off). */ | 272 | /* Delay group 3 is only available for low time (delay off). */ |
273 | if (base != LM3533_REG_PATTERN_LOW_TIME_BASE) | 273 | if (base != LM3533_REG_PATTERN_LOW_TIME_BASE) |
274 | t = min(t, LM3533_LED_DELAY2_TMAX / 1000); | 274 | t = min(t, LM3533_LED_DELAY2_TMAX / 1000); |
275 | 275 | ||
276 | val = lm3533_led_get_hw_delay(&t); | 276 | val = lm3533_led_get_hw_delay(&t); |
277 | 277 | ||
278 | dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__, | 278 | dev_dbg(led->cdev.dev, "%s - %lu: %u (0x%02x)\n", __func__, |
279 | *delay, t, val); | 279 | *delay, t, val); |
280 | reg = lm3533_led_get_pattern_reg(led, base); | 280 | reg = lm3533_led_get_pattern_reg(led, base); |
281 | ret = lm3533_write(led->lm3533, reg, val); | 281 | ret = lm3533_write(led->lm3533, reg, val); |
282 | if (ret) | 282 | if (ret) |
283 | dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg); | 283 | dev_err(led->cdev.dev, "failed to set delay (%02x)\n", reg); |
284 | 284 | ||
285 | *delay = t; | 285 | *delay = t; |
286 | 286 | ||
287 | return ret; | 287 | return ret; |
288 | } | 288 | } |
289 | 289 | ||
290 | static int lm3533_led_delay_on_set(struct lm3533_led *led, unsigned long *t) | 290 | static int lm3533_led_delay_on_set(struct lm3533_led *led, unsigned long *t) |
291 | { | 291 | { |
292 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_HIGH_TIME_BASE, t); | 292 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_HIGH_TIME_BASE, t); |
293 | } | 293 | } |
294 | 294 | ||
295 | static int lm3533_led_delay_off_set(struct lm3533_led *led, unsigned long *t) | 295 | static int lm3533_led_delay_off_set(struct lm3533_led *led, unsigned long *t) |
296 | { | 296 | { |
297 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_LOW_TIME_BASE, t); | 297 | return lm3533_led_delay_set(led, LM3533_REG_PATTERN_LOW_TIME_BASE, t); |
298 | } | 298 | } |
299 | 299 | ||
300 | static int lm3533_led_blink_set(struct led_classdev *cdev, | 300 | static int lm3533_led_blink_set(struct led_classdev *cdev, |
301 | unsigned long *delay_on, | 301 | unsigned long *delay_on, |
302 | unsigned long *delay_off) | 302 | unsigned long *delay_off) |
303 | { | 303 | { |
304 | struct lm3533_led *led = to_lm3533_led(cdev); | 304 | struct lm3533_led *led = to_lm3533_led(cdev); |
305 | int ret; | 305 | int ret; |
306 | 306 | ||
307 | dev_dbg(led->cdev.dev, "%s - on = %lu, off = %lu\n", __func__, | 307 | dev_dbg(led->cdev.dev, "%s - on = %lu, off = %lu\n", __func__, |
308 | *delay_on, *delay_off); | 308 | *delay_on, *delay_off); |
309 | 309 | ||
310 | if (*delay_on > LM3533_LED_DELAY_ON_MAX || | 310 | if (*delay_on > LM3533_LED_DELAY_ON_MAX || |
311 | *delay_off > LM3533_LED_DELAY_OFF_MAX) | 311 | *delay_off > LM3533_LED_DELAY_OFF_MAX) |
312 | return -EINVAL; | 312 | return -EINVAL; |
313 | 313 | ||
314 | if (*delay_on == 0 && *delay_off == 0) { | 314 | if (*delay_on == 0 && *delay_off == 0) { |
315 | *delay_on = 500; | 315 | *delay_on = 500; |
316 | *delay_off = 500; | 316 | *delay_off = 500; |
317 | } | 317 | } |
318 | 318 | ||
319 | ret = lm3533_led_delay_on_set(led, delay_on); | 319 | ret = lm3533_led_delay_on_set(led, delay_on); |
320 | if (ret) | 320 | if (ret) |
321 | return ret; | 321 | return ret; |
322 | 322 | ||
323 | ret = lm3533_led_delay_off_set(led, delay_off); | 323 | ret = lm3533_led_delay_off_set(led, delay_off); |
324 | if (ret) | 324 | if (ret) |
325 | return ret; | 325 | return ret; |
326 | 326 | ||
327 | return lm3533_led_pattern_enable(led, 1); | 327 | return lm3533_led_pattern_enable(led, 1); |
328 | } | 328 | } |
329 | 329 | ||
330 | static ssize_t show_id(struct device *dev, | 330 | static ssize_t show_id(struct device *dev, |
331 | struct device_attribute *attr, char *buf) | 331 | struct device_attribute *attr, char *buf) |
332 | { | 332 | { |
333 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 333 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
334 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 334 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
335 | 335 | ||
336 | return scnprintf(buf, PAGE_SIZE, "%d\n", led->id); | 336 | return scnprintf(buf, PAGE_SIZE, "%d\n", led->id); |
337 | } | 337 | } |
338 | 338 | ||
339 | /* | 339 | /* |
340 | * Pattern generator rise/fall times: | 340 | * Pattern generator rise/fall times: |
341 | * | 341 | * |
342 | * 0 - 2048 us (default) | 342 | * 0 - 2048 us (default) |
343 | * 1 - 262 ms | 343 | * 1 - 262 ms |
344 | * 2 - 524 ms | 344 | * 2 - 524 ms |
345 | * 3 - 1.049 s | 345 | * 3 - 1.049 s |
346 | * 4 - 2.097 s | 346 | * 4 - 2.097 s |
347 | * 5 - 4.194 s | 347 | * 5 - 4.194 s |
348 | * 6 - 8.389 s | 348 | * 6 - 8.389 s |
349 | * 7 - 16.78 s | 349 | * 7 - 16.78 s |
350 | */ | 350 | */ |
351 | static ssize_t show_risefalltime(struct device *dev, | 351 | static ssize_t show_risefalltime(struct device *dev, |
352 | struct device_attribute *attr, | 352 | struct device_attribute *attr, |
353 | char *buf, u8 base) | 353 | char *buf, u8 base) |
354 | { | 354 | { |
355 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 355 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
356 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 356 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
357 | ssize_t ret; | 357 | ssize_t ret; |
358 | u8 reg; | 358 | u8 reg; |
359 | u8 val; | 359 | u8 val; |
360 | 360 | ||
361 | reg = lm3533_led_get_pattern_reg(led, base); | 361 | reg = lm3533_led_get_pattern_reg(led, base); |
362 | ret = lm3533_read(led->lm3533, reg, &val); | 362 | ret = lm3533_read(led->lm3533, reg, &val); |
363 | if (ret) | 363 | if (ret) |
364 | return ret; | 364 | return ret; |
365 | 365 | ||
366 | return scnprintf(buf, PAGE_SIZE, "%x\n", val); | 366 | return scnprintf(buf, PAGE_SIZE, "%x\n", val); |
367 | } | 367 | } |
368 | 368 | ||
369 | static ssize_t show_risetime(struct device *dev, | 369 | static ssize_t show_risetime(struct device *dev, |
370 | struct device_attribute *attr, char *buf) | 370 | struct device_attribute *attr, char *buf) |
371 | { | 371 | { |
372 | return show_risefalltime(dev, attr, buf, | 372 | return show_risefalltime(dev, attr, buf, |
373 | LM3533_REG_PATTERN_RISETIME_BASE); | 373 | LM3533_REG_PATTERN_RISETIME_BASE); |
374 | } | 374 | } |
375 | 375 | ||
376 | static ssize_t show_falltime(struct device *dev, | 376 | static ssize_t show_falltime(struct device *dev, |
377 | struct device_attribute *attr, char *buf) | 377 | struct device_attribute *attr, char *buf) |
378 | { | 378 | { |
379 | return show_risefalltime(dev, attr, buf, | 379 | return show_risefalltime(dev, attr, buf, |
380 | LM3533_REG_PATTERN_FALLTIME_BASE); | 380 | LM3533_REG_PATTERN_FALLTIME_BASE); |
381 | } | 381 | } |
382 | 382 | ||
383 | static ssize_t store_risefalltime(struct device *dev, | 383 | static ssize_t store_risefalltime(struct device *dev, |
384 | struct device_attribute *attr, | 384 | struct device_attribute *attr, |
385 | const char *buf, size_t len, u8 base) | 385 | const char *buf, size_t len, u8 base) |
386 | { | 386 | { |
387 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 387 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
388 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 388 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
389 | u8 val; | 389 | u8 val; |
390 | u8 reg; | 390 | u8 reg; |
391 | int ret; | 391 | int ret; |
392 | 392 | ||
393 | if (kstrtou8(buf, 0, &val) || val > LM3533_RISEFALLTIME_MAX) | 393 | if (kstrtou8(buf, 0, &val) || val > LM3533_RISEFALLTIME_MAX) |
394 | return -EINVAL; | 394 | return -EINVAL; |
395 | 395 | ||
396 | reg = lm3533_led_get_pattern_reg(led, base); | 396 | reg = lm3533_led_get_pattern_reg(led, base); |
397 | ret = lm3533_write(led->lm3533, reg, val); | 397 | ret = lm3533_write(led->lm3533, reg, val); |
398 | if (ret) | 398 | if (ret) |
399 | return ret; | 399 | return ret; |
400 | 400 | ||
401 | return len; | 401 | return len; |
402 | } | 402 | } |
403 | 403 | ||
404 | static ssize_t store_risetime(struct device *dev, | 404 | static ssize_t store_risetime(struct device *dev, |
405 | struct device_attribute *attr, | 405 | struct device_attribute *attr, |
406 | const char *buf, size_t len) | 406 | const char *buf, size_t len) |
407 | { | 407 | { |
408 | return store_risefalltime(dev, attr, buf, len, | 408 | return store_risefalltime(dev, attr, buf, len, |
409 | LM3533_REG_PATTERN_RISETIME_BASE); | 409 | LM3533_REG_PATTERN_RISETIME_BASE); |
410 | } | 410 | } |
411 | 411 | ||
412 | static ssize_t store_falltime(struct device *dev, | 412 | static ssize_t store_falltime(struct device *dev, |
413 | struct device_attribute *attr, | 413 | struct device_attribute *attr, |
414 | const char *buf, size_t len) | 414 | const char *buf, size_t len) |
415 | { | 415 | { |
416 | return store_risefalltime(dev, attr, buf, len, | 416 | return store_risefalltime(dev, attr, buf, len, |
417 | LM3533_REG_PATTERN_FALLTIME_BASE); | 417 | LM3533_REG_PATTERN_FALLTIME_BASE); |
418 | } | 418 | } |
419 | 419 | ||
420 | static ssize_t show_als_channel(struct device *dev, | 420 | static ssize_t show_als_channel(struct device *dev, |
421 | struct device_attribute *attr, char *buf) | 421 | struct device_attribute *attr, char *buf) |
422 | { | 422 | { |
423 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 423 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
424 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 424 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
425 | unsigned channel; | 425 | unsigned channel; |
426 | u8 reg; | 426 | u8 reg; |
427 | u8 val; | 427 | u8 val; |
428 | int ret; | 428 | int ret; |
429 | 429 | ||
430 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 430 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
431 | ret = lm3533_read(led->lm3533, reg, &val); | 431 | ret = lm3533_read(led->lm3533, reg, &val); |
432 | if (ret) | 432 | if (ret) |
433 | return ret; | 433 | return ret; |
434 | 434 | ||
435 | channel = (val & LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK) + 1; | 435 | channel = (val & LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK) + 1; |
436 | 436 | ||
437 | return scnprintf(buf, PAGE_SIZE, "%u\n", channel); | 437 | return scnprintf(buf, PAGE_SIZE, "%u\n", channel); |
438 | } | 438 | } |
439 | 439 | ||
440 | static ssize_t store_als_channel(struct device *dev, | 440 | static ssize_t store_als_channel(struct device *dev, |
441 | struct device_attribute *attr, | 441 | struct device_attribute *attr, |
442 | const char *buf, size_t len) | 442 | const char *buf, size_t len) |
443 | { | 443 | { |
444 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 444 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
445 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 445 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
446 | unsigned channel; | 446 | unsigned channel; |
447 | u8 reg; | 447 | u8 reg; |
448 | u8 val; | 448 | u8 val; |
449 | u8 mask; | 449 | u8 mask; |
450 | int ret; | 450 | int ret; |
451 | 451 | ||
452 | if (kstrtouint(buf, 0, &channel)) | 452 | if (kstrtouint(buf, 0, &channel)) |
453 | return -EINVAL; | 453 | return -EINVAL; |
454 | 454 | ||
455 | if (channel < LM3533_ALS_CHANNEL_LV_MIN || | 455 | if (channel < LM3533_ALS_CHANNEL_LV_MIN || |
456 | channel > LM3533_ALS_CHANNEL_LV_MAX) | 456 | channel > LM3533_ALS_CHANNEL_LV_MAX) |
457 | return -EINVAL; | 457 | return -EINVAL; |
458 | 458 | ||
459 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 459 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
460 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK; | 460 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_CHANNEL_MASK; |
461 | val = channel - 1; | 461 | val = channel - 1; |
462 | 462 | ||
463 | ret = lm3533_update(led->lm3533, reg, val, mask); | 463 | ret = lm3533_update(led->lm3533, reg, val, mask); |
464 | if (ret) | 464 | if (ret) |
465 | return ret; | 465 | return ret; |
466 | 466 | ||
467 | return len; | 467 | return len; |
468 | } | 468 | } |
469 | 469 | ||
470 | static ssize_t show_als_en(struct device *dev, | 470 | static ssize_t show_als_en(struct device *dev, |
471 | struct device_attribute *attr, char *buf) | 471 | struct device_attribute *attr, char *buf) |
472 | { | 472 | { |
473 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 473 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
474 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 474 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
475 | bool enable; | 475 | bool enable; |
476 | u8 reg; | 476 | u8 reg; |
477 | u8 val; | 477 | u8 val; |
478 | int ret; | 478 | int ret; |
479 | 479 | ||
480 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 480 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
481 | ret = lm3533_read(led->lm3533, reg, &val); | 481 | ret = lm3533_read(led->lm3533, reg, &val); |
482 | if (ret) | 482 | if (ret) |
483 | return ret; | 483 | return ret; |
484 | 484 | ||
485 | enable = val & LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; | 485 | enable = val & LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; |
486 | 486 | ||
487 | return scnprintf(buf, PAGE_SIZE, "%d\n", enable); | 487 | return scnprintf(buf, PAGE_SIZE, "%d\n", enable); |
488 | } | 488 | } |
489 | 489 | ||
490 | static ssize_t store_als_en(struct device *dev, | 490 | static ssize_t store_als_en(struct device *dev, |
491 | struct device_attribute *attr, | 491 | struct device_attribute *attr, |
492 | const char *buf, size_t len) | 492 | const char *buf, size_t len) |
493 | { | 493 | { |
494 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 494 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
495 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 495 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
496 | unsigned enable; | 496 | unsigned enable; |
497 | u8 reg; | 497 | u8 reg; |
498 | u8 mask; | 498 | u8 mask; |
499 | u8 val; | 499 | u8 val; |
500 | int ret; | 500 | int ret; |
501 | 501 | ||
502 | if (kstrtouint(buf, 0, &enable)) | 502 | if (kstrtouint(buf, 0, &enable)) |
503 | return -EINVAL; | 503 | return -EINVAL; |
504 | 504 | ||
505 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 505 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
506 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; | 506 | mask = LM3533_REG_CTRLBANK_BCONF_ALS_EN_MASK; |
507 | 507 | ||
508 | if (enable) | 508 | if (enable) |
509 | val = mask; | 509 | val = mask; |
510 | else | 510 | else |
511 | val = 0; | 511 | val = 0; |
512 | 512 | ||
513 | ret = lm3533_update(led->lm3533, reg, val, mask); | 513 | ret = lm3533_update(led->lm3533, reg, val, mask); |
514 | if (ret) | 514 | if (ret) |
515 | return ret; | 515 | return ret; |
516 | 516 | ||
517 | return len; | 517 | return len; |
518 | } | 518 | } |
519 | 519 | ||
520 | static ssize_t show_linear(struct device *dev, | 520 | static ssize_t show_linear(struct device *dev, |
521 | struct device_attribute *attr, char *buf) | 521 | struct device_attribute *attr, char *buf) |
522 | { | 522 | { |
523 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 523 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
524 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 524 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
525 | u8 reg; | 525 | u8 reg; |
526 | u8 val; | 526 | u8 val; |
527 | int linear; | 527 | int linear; |
528 | int ret; | 528 | int ret; |
529 | 529 | ||
530 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 530 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
531 | ret = lm3533_read(led->lm3533, reg, &val); | 531 | ret = lm3533_read(led->lm3533, reg, &val); |
532 | if (ret) | 532 | if (ret) |
533 | return ret; | 533 | return ret; |
534 | 534 | ||
535 | if (val & LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK) | 535 | if (val & LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK) |
536 | linear = 1; | 536 | linear = 1; |
537 | else | 537 | else |
538 | linear = 0; | 538 | linear = 0; |
539 | 539 | ||
540 | return scnprintf(buf, PAGE_SIZE, "%x\n", linear); | 540 | return scnprintf(buf, PAGE_SIZE, "%x\n", linear); |
541 | } | 541 | } |
542 | 542 | ||
543 | static ssize_t store_linear(struct device *dev, | 543 | static ssize_t store_linear(struct device *dev, |
544 | struct device_attribute *attr, | 544 | struct device_attribute *attr, |
545 | const char *buf, size_t len) | 545 | const char *buf, size_t len) |
546 | { | 546 | { |
547 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 547 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
548 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 548 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
549 | unsigned long linear; | 549 | unsigned long linear; |
550 | u8 reg; | 550 | u8 reg; |
551 | u8 mask; | 551 | u8 mask; |
552 | u8 val; | 552 | u8 val; |
553 | int ret; | 553 | int ret; |
554 | 554 | ||
555 | if (kstrtoul(buf, 0, &linear)) | 555 | if (kstrtoul(buf, 0, &linear)) |
556 | return -EINVAL; | 556 | return -EINVAL; |
557 | 557 | ||
558 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); | 558 | reg = lm3533_led_get_lv_reg(led, LM3533_REG_CTRLBANK_BCONF_BASE); |
559 | mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK; | 559 | mask = LM3533_REG_CTRLBANK_BCONF_MAPPING_MASK; |
560 | 560 | ||
561 | if (linear) | 561 | if (linear) |
562 | val = mask; | 562 | val = mask; |
563 | else | 563 | else |
564 | val = 0; | 564 | val = 0; |
565 | 565 | ||
566 | ret = lm3533_update(led->lm3533, reg, val, mask); | 566 | ret = lm3533_update(led->lm3533, reg, val, mask); |
567 | if (ret) | 567 | if (ret) |
568 | return ret; | 568 | return ret; |
569 | 569 | ||
570 | return len; | 570 | return len; |
571 | } | 571 | } |
572 | 572 | ||
573 | static ssize_t show_pwm(struct device *dev, | 573 | static ssize_t show_pwm(struct device *dev, |
574 | struct device_attribute *attr, | 574 | struct device_attribute *attr, |
575 | char *buf) | 575 | char *buf) |
576 | { | 576 | { |
577 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 577 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
578 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 578 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
579 | u8 val; | 579 | u8 val; |
580 | int ret; | 580 | int ret; |
581 | 581 | ||
582 | ret = lm3533_ctrlbank_get_pwm(&led->cb, &val); | 582 | ret = lm3533_ctrlbank_get_pwm(&led->cb, &val); |
583 | if (ret) | 583 | if (ret) |
584 | return ret; | 584 | return ret; |
585 | 585 | ||
586 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); | 586 | return scnprintf(buf, PAGE_SIZE, "%u\n", val); |
587 | } | 587 | } |
588 | 588 | ||
589 | static ssize_t store_pwm(struct device *dev, | 589 | static ssize_t store_pwm(struct device *dev, |
590 | struct device_attribute *attr, | 590 | struct device_attribute *attr, |
591 | const char *buf, size_t len) | 591 | const char *buf, size_t len) |
592 | { | 592 | { |
593 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 593 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
594 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 594 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
595 | u8 val; | 595 | u8 val; |
596 | int ret; | 596 | int ret; |
597 | 597 | ||
598 | if (kstrtou8(buf, 0, &val)) | 598 | if (kstrtou8(buf, 0, &val)) |
599 | return -EINVAL; | 599 | return -EINVAL; |
600 | 600 | ||
601 | ret = lm3533_ctrlbank_set_pwm(&led->cb, val); | 601 | ret = lm3533_ctrlbank_set_pwm(&led->cb, val); |
602 | if (ret) | 602 | if (ret) |
603 | return ret; | 603 | return ret; |
604 | 604 | ||
605 | return len; | 605 | return len; |
606 | } | 606 | } |
607 | 607 | ||
608 | static LM3533_ATTR_RW(als_channel); | 608 | static LM3533_ATTR_RW(als_channel); |
609 | static LM3533_ATTR_RW(als_en); | 609 | static LM3533_ATTR_RW(als_en); |
610 | static LM3533_ATTR_RW(falltime); | 610 | static LM3533_ATTR_RW(falltime); |
611 | static LM3533_ATTR_RO(id); | 611 | static LM3533_ATTR_RO(id); |
612 | static LM3533_ATTR_RW(linear); | 612 | static LM3533_ATTR_RW(linear); |
613 | static LM3533_ATTR_RW(pwm); | 613 | static LM3533_ATTR_RW(pwm); |
614 | static LM3533_ATTR_RW(risetime); | 614 | static LM3533_ATTR_RW(risetime); |
615 | 615 | ||
616 | static struct attribute *lm3533_led_attributes[] = { | 616 | static struct attribute *lm3533_led_attributes[] = { |
617 | &dev_attr_als_channel.attr, | 617 | &dev_attr_als_channel.attr, |
618 | &dev_attr_als_en.attr, | 618 | &dev_attr_als_en.attr, |
619 | &dev_attr_falltime.attr, | 619 | &dev_attr_falltime.attr, |
620 | &dev_attr_id.attr, | 620 | &dev_attr_id.attr, |
621 | &dev_attr_linear.attr, | 621 | &dev_attr_linear.attr, |
622 | &dev_attr_pwm.attr, | 622 | &dev_attr_pwm.attr, |
623 | &dev_attr_risetime.attr, | 623 | &dev_attr_risetime.attr, |
624 | NULL, | 624 | NULL, |
625 | }; | 625 | }; |
626 | 626 | ||
627 | static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, | 627 | static umode_t lm3533_led_attr_is_visible(struct kobject *kobj, |
628 | struct attribute *attr, int n) | 628 | struct attribute *attr, int n) |
629 | { | 629 | { |
630 | struct device *dev = container_of(kobj, struct device, kobj); | 630 | struct device *dev = container_of(kobj, struct device, kobj); |
631 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 631 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
632 | struct lm3533_led *led = to_lm3533_led(led_cdev); | 632 | struct lm3533_led *led = to_lm3533_led(led_cdev); |
633 | umode_t mode = attr->mode; | 633 | umode_t mode = attr->mode; |
634 | 634 | ||
635 | if (attr == &dev_attr_als_channel.attr || | 635 | if (attr == &dev_attr_als_channel.attr || |
636 | attr == &dev_attr_als_en.attr) { | 636 | attr == &dev_attr_als_en.attr) { |
637 | if (!led->lm3533->have_als) | 637 | if (!led->lm3533->have_als) |
638 | mode = 0; | 638 | mode = 0; |
639 | } | 639 | } |
640 | 640 | ||
641 | return mode; | 641 | return mode; |
642 | }; | 642 | }; |
643 | 643 | ||
644 | static struct attribute_group lm3533_led_attribute_group = { | 644 | static struct attribute_group lm3533_led_attribute_group = { |
645 | .is_visible = lm3533_led_attr_is_visible, | 645 | .is_visible = lm3533_led_attr_is_visible, |
646 | .attrs = lm3533_led_attributes | 646 | .attrs = lm3533_led_attributes |
647 | }; | 647 | }; |
648 | 648 | ||
649 | static int lm3533_led_setup(struct lm3533_led *led, | 649 | static int lm3533_led_setup(struct lm3533_led *led, |
650 | struct lm3533_led_platform_data *pdata) | 650 | struct lm3533_led_platform_data *pdata) |
651 | { | 651 | { |
652 | int ret; | 652 | int ret; |
653 | 653 | ||
654 | ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current); | 654 | ret = lm3533_ctrlbank_set_max_current(&led->cb, pdata->max_current); |
655 | if (ret) | 655 | if (ret) |
656 | return ret; | 656 | return ret; |
657 | 657 | ||
658 | return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm); | 658 | return lm3533_ctrlbank_set_pwm(&led->cb, pdata->pwm); |
659 | } | 659 | } |
660 | 660 | ||
661 | static int lm3533_led_probe(struct platform_device *pdev) | 661 | static int lm3533_led_probe(struct platform_device *pdev) |
662 | { | 662 | { |
663 | struct lm3533 *lm3533; | 663 | struct lm3533 *lm3533; |
664 | struct lm3533_led_platform_data *pdata; | 664 | struct lm3533_led_platform_data *pdata; |
665 | struct lm3533_led *led; | 665 | struct lm3533_led *led; |
666 | int ret; | 666 | int ret; |
667 | 667 | ||
668 | dev_dbg(&pdev->dev, "%s\n", __func__); | 668 | dev_dbg(&pdev->dev, "%s\n", __func__); |
669 | 669 | ||
670 | lm3533 = dev_get_drvdata(pdev->dev.parent); | 670 | lm3533 = dev_get_drvdata(pdev->dev.parent); |
671 | if (!lm3533) | 671 | if (!lm3533) |
672 | return -EINVAL; | 672 | return -EINVAL; |
673 | 673 | ||
674 | pdata = pdev->dev.platform_data; | 674 | pdata = dev_get_platdata(&pdev->dev); |
675 | if (!pdata) { | 675 | if (!pdata) { |
676 | dev_err(&pdev->dev, "no platform data\n"); | 676 | dev_err(&pdev->dev, "no platform data\n"); |
677 | return -EINVAL; | 677 | return -EINVAL; |
678 | } | 678 | } |
679 | 679 | ||
680 | if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) { | 680 | if (pdev->id < 0 || pdev->id >= LM3533_LVCTRLBANK_COUNT) { |
681 | dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id); | 681 | dev_err(&pdev->dev, "illegal LED id %d\n", pdev->id); |
682 | return -EINVAL; | 682 | return -EINVAL; |
683 | } | 683 | } |
684 | 684 | ||
685 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | 685 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
686 | if (!led) | 686 | if (!led) |
687 | return -ENOMEM; | 687 | return -ENOMEM; |
688 | 688 | ||
689 | led->lm3533 = lm3533; | 689 | led->lm3533 = lm3533; |
690 | led->cdev.name = pdata->name; | 690 | led->cdev.name = pdata->name; |
691 | led->cdev.default_trigger = pdata->default_trigger; | 691 | led->cdev.default_trigger = pdata->default_trigger; |
692 | led->cdev.brightness_set = lm3533_led_set; | 692 | led->cdev.brightness_set = lm3533_led_set; |
693 | led->cdev.brightness_get = lm3533_led_get; | 693 | led->cdev.brightness_get = lm3533_led_get; |
694 | led->cdev.blink_set = lm3533_led_blink_set; | 694 | led->cdev.blink_set = lm3533_led_blink_set; |
695 | led->cdev.brightness = LED_OFF; | 695 | led->cdev.brightness = LED_OFF; |
696 | led->id = pdev->id; | 696 | led->id = pdev->id; |
697 | 697 | ||
698 | mutex_init(&led->mutex); | 698 | mutex_init(&led->mutex); |
699 | INIT_WORK(&led->work, lm3533_led_work); | 699 | INIT_WORK(&led->work, lm3533_led_work); |
700 | 700 | ||
701 | /* The class framework makes a callback to get brightness during | 701 | /* The class framework makes a callback to get brightness during |
702 | * registration so use parent device (for error reporting) until | 702 | * registration so use parent device (for error reporting) until |
703 | * registered. | 703 | * registered. |
704 | */ | 704 | */ |
705 | led->cb.lm3533 = lm3533; | 705 | led->cb.lm3533 = lm3533; |
706 | led->cb.id = lm3533_led_get_ctrlbank_id(led); | 706 | led->cb.id = lm3533_led_get_ctrlbank_id(led); |
707 | led->cb.dev = lm3533->dev; | 707 | led->cb.dev = lm3533->dev; |
708 | 708 | ||
709 | platform_set_drvdata(pdev, led); | 709 | platform_set_drvdata(pdev, led); |
710 | 710 | ||
711 | ret = led_classdev_register(pdev->dev.parent, &led->cdev); | 711 | ret = led_classdev_register(pdev->dev.parent, &led->cdev); |
712 | if (ret) { | 712 | if (ret) { |
713 | dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id); | 713 | dev_err(&pdev->dev, "failed to register LED %d\n", pdev->id); |
714 | return ret; | 714 | return ret; |
715 | } | 715 | } |
716 | 716 | ||
717 | led->cb.dev = led->cdev.dev; | 717 | led->cb.dev = led->cdev.dev; |
718 | 718 | ||
719 | ret = sysfs_create_group(&led->cdev.dev->kobj, | 719 | ret = sysfs_create_group(&led->cdev.dev->kobj, |
720 | &lm3533_led_attribute_group); | 720 | &lm3533_led_attribute_group); |
721 | if (ret < 0) { | 721 | if (ret < 0) { |
722 | dev_err(&pdev->dev, "failed to create sysfs attributes\n"); | 722 | dev_err(&pdev->dev, "failed to create sysfs attributes\n"); |
723 | goto err_unregister; | 723 | goto err_unregister; |
724 | } | 724 | } |
725 | 725 | ||
726 | ret = lm3533_led_setup(led, pdata); | 726 | ret = lm3533_led_setup(led, pdata); |
727 | if (ret) | 727 | if (ret) |
728 | goto err_sysfs_remove; | 728 | goto err_sysfs_remove; |
729 | 729 | ||
730 | ret = lm3533_ctrlbank_enable(&led->cb); | 730 | ret = lm3533_ctrlbank_enable(&led->cb); |
731 | if (ret) | 731 | if (ret) |
732 | goto err_sysfs_remove; | 732 | goto err_sysfs_remove; |
733 | 733 | ||
734 | return 0; | 734 | return 0; |
735 | 735 | ||
736 | err_sysfs_remove: | 736 | err_sysfs_remove: |
737 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); | 737 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); |
738 | err_unregister: | 738 | err_unregister: |
739 | led_classdev_unregister(&led->cdev); | 739 | led_classdev_unregister(&led->cdev); |
740 | flush_work(&led->work); | 740 | flush_work(&led->work); |
741 | 741 | ||
742 | return ret; | 742 | return ret; |
743 | } | 743 | } |
744 | 744 | ||
745 | static int lm3533_led_remove(struct platform_device *pdev) | 745 | static int lm3533_led_remove(struct platform_device *pdev) |
746 | { | 746 | { |
747 | struct lm3533_led *led = platform_get_drvdata(pdev); | 747 | struct lm3533_led *led = platform_get_drvdata(pdev); |
748 | 748 | ||
749 | dev_dbg(&pdev->dev, "%s\n", __func__); | 749 | dev_dbg(&pdev->dev, "%s\n", __func__); |
750 | 750 | ||
751 | lm3533_ctrlbank_disable(&led->cb); | 751 | lm3533_ctrlbank_disable(&led->cb); |
752 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); | 752 | sysfs_remove_group(&led->cdev.dev->kobj, &lm3533_led_attribute_group); |
753 | led_classdev_unregister(&led->cdev); | 753 | led_classdev_unregister(&led->cdev); |
754 | flush_work(&led->work); | 754 | flush_work(&led->work); |
755 | 755 | ||
756 | return 0; | 756 | return 0; |
757 | } | 757 | } |
758 | 758 | ||
759 | static void lm3533_led_shutdown(struct platform_device *pdev) | 759 | static void lm3533_led_shutdown(struct platform_device *pdev) |
760 | { | 760 | { |
761 | 761 | ||
762 | struct lm3533_led *led = platform_get_drvdata(pdev); | 762 | struct lm3533_led *led = platform_get_drvdata(pdev); |
763 | 763 | ||
764 | dev_dbg(&pdev->dev, "%s\n", __func__); | 764 | dev_dbg(&pdev->dev, "%s\n", __func__); |
765 | 765 | ||
766 | lm3533_ctrlbank_disable(&led->cb); | 766 | lm3533_ctrlbank_disable(&led->cb); |
767 | lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ | 767 | lm3533_led_set(&led->cdev, LED_OFF); /* disable blink */ |
768 | flush_work(&led->work); | 768 | flush_work(&led->work); |
769 | } | 769 | } |
770 | 770 | ||
771 | static struct platform_driver lm3533_led_driver = { | 771 | static struct platform_driver lm3533_led_driver = { |
772 | .driver = { | 772 | .driver = { |
773 | .name = "lm3533-leds", | 773 | .name = "lm3533-leds", |
774 | .owner = THIS_MODULE, | 774 | .owner = THIS_MODULE, |
775 | }, | 775 | }, |
776 | .probe = lm3533_led_probe, | 776 | .probe = lm3533_led_probe, |
777 | .remove = lm3533_led_remove, | 777 | .remove = lm3533_led_remove, |
778 | .shutdown = lm3533_led_shutdown, | 778 | .shutdown = lm3533_led_shutdown, |
779 | }; | 779 | }; |
780 | module_platform_driver(lm3533_led_driver); | 780 | module_platform_driver(lm3533_led_driver); |
781 | 781 | ||
782 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); | 782 | MODULE_AUTHOR("Johan Hovold <jhovold@gmail.com>"); |
783 | MODULE_DESCRIPTION("LM3533 LED driver"); | 783 | MODULE_DESCRIPTION("LM3533 LED driver"); |
784 | MODULE_LICENSE("GPL"); | 784 | MODULE_LICENSE("GPL"); |
785 | MODULE_ALIAS("platform:lm3533-leds"); | 785 | MODULE_ALIAS("platform:lm3533-leds"); |
786 | 786 |
drivers/leds/leds-lm355x.c
1 | /* | 1 | /* |
2 | * Simple driver for Texas Instruments LM355x LED Flash driver chip | 2 | * Simple driver for Texas Instruments LM355x LED Flash driver chip |
3 | * Copyright (C) 2012 Texas Instruments | 3 | * Copyright (C) 2012 Texas Instruments |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
12 | #include <linux/i2c.h> | 12 | #include <linux/i2c.h> |
13 | #include <linux/gpio.h> | 13 | #include <linux/gpio.h> |
14 | #include <linux/leds.h> | 14 | #include <linux/leds.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/regmap.h> | 18 | #include <linux/regmap.h> |
19 | #include <linux/workqueue.h> | 19 | #include <linux/workqueue.h> |
20 | #include <linux/platform_data/leds-lm355x.h> | 20 | #include <linux/platform_data/leds-lm355x.h> |
21 | 21 | ||
22 | enum lm355x_type { | 22 | enum lm355x_type { |
23 | CHIP_LM3554 = 0, | 23 | CHIP_LM3554 = 0, |
24 | CHIP_LM3556, | 24 | CHIP_LM3556, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | enum lm355x_regs { | 27 | enum lm355x_regs { |
28 | REG_FLAG = 0, | 28 | REG_FLAG = 0, |
29 | REG_TORCH_CFG, | 29 | REG_TORCH_CFG, |
30 | REG_TORCH_CTRL, | 30 | REG_TORCH_CTRL, |
31 | REG_STROBE_CFG, | 31 | REG_STROBE_CFG, |
32 | REG_FLASH_CTRL, | 32 | REG_FLASH_CTRL, |
33 | REG_INDI_CFG, | 33 | REG_INDI_CFG, |
34 | REG_INDI_CTRL, | 34 | REG_INDI_CTRL, |
35 | REG_OPMODE, | 35 | REG_OPMODE, |
36 | REG_MAX, | 36 | REG_MAX, |
37 | }; | 37 | }; |
38 | 38 | ||
39 | /* operation mode */ | 39 | /* operation mode */ |
40 | enum lm355x_mode { | 40 | enum lm355x_mode { |
41 | MODE_SHDN = 0, | 41 | MODE_SHDN = 0, |
42 | MODE_INDIC, | 42 | MODE_INDIC, |
43 | MODE_TORCH, | 43 | MODE_TORCH, |
44 | MODE_FLASH | 44 | MODE_FLASH |
45 | }; | 45 | }; |
46 | 46 | ||
47 | /* register map info. */ | 47 | /* register map info. */ |
48 | struct lm355x_reg_data { | 48 | struct lm355x_reg_data { |
49 | u8 regno; | 49 | u8 regno; |
50 | u8 mask; | 50 | u8 mask; |
51 | u8 shift; | 51 | u8 shift; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | struct lm355x_chip_data { | 54 | struct lm355x_chip_data { |
55 | struct device *dev; | 55 | struct device *dev; |
56 | enum lm355x_type type; | 56 | enum lm355x_type type; |
57 | 57 | ||
58 | struct led_classdev cdev_flash; | 58 | struct led_classdev cdev_flash; |
59 | struct led_classdev cdev_torch; | 59 | struct led_classdev cdev_torch; |
60 | struct led_classdev cdev_indicator; | 60 | struct led_classdev cdev_indicator; |
61 | 61 | ||
62 | struct work_struct work_flash; | 62 | struct work_struct work_flash; |
63 | struct work_struct work_torch; | 63 | struct work_struct work_torch; |
64 | struct work_struct work_indicator; | 64 | struct work_struct work_indicator; |
65 | 65 | ||
66 | u8 br_flash; | 66 | u8 br_flash; |
67 | u8 br_torch; | 67 | u8 br_torch; |
68 | u8 br_indicator; | 68 | u8 br_indicator; |
69 | 69 | ||
70 | struct lm355x_platform_data *pdata; | 70 | struct lm355x_platform_data *pdata; |
71 | struct regmap *regmap; | 71 | struct regmap *regmap; |
72 | struct mutex lock; | 72 | struct mutex lock; |
73 | 73 | ||
74 | unsigned int last_flag; | 74 | unsigned int last_flag; |
75 | struct lm355x_reg_data *regs; | 75 | struct lm355x_reg_data *regs; |
76 | }; | 76 | }; |
77 | 77 | ||
78 | /* specific indicator function for lm3556 */ | 78 | /* specific indicator function for lm3556 */ |
79 | enum lm3556_indic_pulse_time { | 79 | enum lm3556_indic_pulse_time { |
80 | PULSE_TIME_0_MS = 0, | 80 | PULSE_TIME_0_MS = 0, |
81 | PULSE_TIME_32_MS, | 81 | PULSE_TIME_32_MS, |
82 | PULSE_TIME_64_MS, | 82 | PULSE_TIME_64_MS, |
83 | PULSE_TIME_92_MS, | 83 | PULSE_TIME_92_MS, |
84 | PULSE_TIME_128_MS, | 84 | PULSE_TIME_128_MS, |
85 | PULSE_TIME_160_MS, | 85 | PULSE_TIME_160_MS, |
86 | PULSE_TIME_196_MS, | 86 | PULSE_TIME_196_MS, |
87 | PULSE_TIME_224_MS, | 87 | PULSE_TIME_224_MS, |
88 | PULSE_TIME_256_MS, | 88 | PULSE_TIME_256_MS, |
89 | PULSE_TIME_288_MS, | 89 | PULSE_TIME_288_MS, |
90 | PULSE_TIME_320_MS, | 90 | PULSE_TIME_320_MS, |
91 | PULSE_TIME_352_MS, | 91 | PULSE_TIME_352_MS, |
92 | PULSE_TIME_384_MS, | 92 | PULSE_TIME_384_MS, |
93 | PULSE_TIME_416_MS, | 93 | PULSE_TIME_416_MS, |
94 | PULSE_TIME_448_MS, | 94 | PULSE_TIME_448_MS, |
95 | PULSE_TIME_480_MS, | 95 | PULSE_TIME_480_MS, |
96 | }; | 96 | }; |
97 | 97 | ||
98 | enum lm3556_indic_n_blank { | 98 | enum lm3556_indic_n_blank { |
99 | INDIC_N_BLANK_0 = 0, | 99 | INDIC_N_BLANK_0 = 0, |
100 | INDIC_N_BLANK_1, | 100 | INDIC_N_BLANK_1, |
101 | INDIC_N_BLANK_2, | 101 | INDIC_N_BLANK_2, |
102 | INDIC_N_BLANK_3, | 102 | INDIC_N_BLANK_3, |
103 | INDIC_N_BLANK_4, | 103 | INDIC_N_BLANK_4, |
104 | INDIC_N_BLANK_5, | 104 | INDIC_N_BLANK_5, |
105 | INDIC_N_BLANK_6, | 105 | INDIC_N_BLANK_6, |
106 | INDIC_N_BLANK_7, | 106 | INDIC_N_BLANK_7, |
107 | INDIC_N_BLANK_8, | 107 | INDIC_N_BLANK_8, |
108 | INDIC_N_BLANK_9, | 108 | INDIC_N_BLANK_9, |
109 | INDIC_N_BLANK_10, | 109 | INDIC_N_BLANK_10, |
110 | INDIC_N_BLANK_11, | 110 | INDIC_N_BLANK_11, |
111 | INDIC_N_BLANK_12, | 111 | INDIC_N_BLANK_12, |
112 | INDIC_N_BLANK_13, | 112 | INDIC_N_BLANK_13, |
113 | INDIC_N_BLANK_14, | 113 | INDIC_N_BLANK_14, |
114 | INDIC_N_BLANK_15, | 114 | INDIC_N_BLANK_15, |
115 | }; | 115 | }; |
116 | 116 | ||
117 | enum lm3556_indic_period { | 117 | enum lm3556_indic_period { |
118 | INDIC_PERIOD_0 = 0, | 118 | INDIC_PERIOD_0 = 0, |
119 | INDIC_PERIOD_1, | 119 | INDIC_PERIOD_1, |
120 | INDIC_PERIOD_2, | 120 | INDIC_PERIOD_2, |
121 | INDIC_PERIOD_3, | 121 | INDIC_PERIOD_3, |
122 | INDIC_PERIOD_4, | 122 | INDIC_PERIOD_4, |
123 | INDIC_PERIOD_5, | 123 | INDIC_PERIOD_5, |
124 | INDIC_PERIOD_6, | 124 | INDIC_PERIOD_6, |
125 | INDIC_PERIOD_7, | 125 | INDIC_PERIOD_7, |
126 | }; | 126 | }; |
127 | 127 | ||
128 | #define INDIC_PATTERN_SIZE 4 | 128 | #define INDIC_PATTERN_SIZE 4 |
129 | 129 | ||
130 | struct indicator { | 130 | struct indicator { |
131 | u8 blinking; | 131 | u8 blinking; |
132 | u8 period_cnt; | 132 | u8 period_cnt; |
133 | }; | 133 | }; |
134 | 134 | ||
135 | /* indicator pattern data only for lm3556 */ | 135 | /* indicator pattern data only for lm3556 */ |
136 | static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { | 136 | static struct indicator indicator_pattern[INDIC_PATTERN_SIZE] = { |
137 | [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, | 137 | [0] = {(INDIC_N_BLANK_1 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_1}, |
138 | [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, | 138 | [1] = {(INDIC_N_BLANK_15 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_2}, |
139 | [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, | 139 | [2] = {(INDIC_N_BLANK_10 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_4}, |
140 | [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, | 140 | [3] = {(INDIC_N_BLANK_5 << 4) | PULSE_TIME_32_MS, INDIC_PERIOD_7}, |
141 | }; | 141 | }; |
142 | 142 | ||
143 | static struct lm355x_reg_data lm3554_regs[REG_MAX] = { | 143 | static struct lm355x_reg_data lm3554_regs[REG_MAX] = { |
144 | [REG_FLAG] = {0xD0, 0xBF, 0}, | 144 | [REG_FLAG] = {0xD0, 0xBF, 0}, |
145 | [REG_TORCH_CFG] = {0xE0, 0x80, 7}, | 145 | [REG_TORCH_CFG] = {0xE0, 0x80, 7}, |
146 | [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, | 146 | [REG_TORCH_CTRL] = {0xA0, 0x38, 3}, |
147 | [REG_STROBE_CFG] = {0xE0, 0x04, 2}, | 147 | [REG_STROBE_CFG] = {0xE0, 0x04, 2}, |
148 | [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, | 148 | [REG_FLASH_CTRL] = {0xB0, 0x78, 3}, |
149 | [REG_INDI_CFG] = {0xE0, 0x08, 3}, | 149 | [REG_INDI_CFG] = {0xE0, 0x08, 3}, |
150 | [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, | 150 | [REG_INDI_CTRL] = {0xA0, 0xC0, 6}, |
151 | [REG_OPMODE] = {0xA0, 0x03, 0}, | 151 | [REG_OPMODE] = {0xA0, 0x03, 0}, |
152 | }; | 152 | }; |
153 | 153 | ||
154 | static struct lm355x_reg_data lm3556_regs[REG_MAX] = { | 154 | static struct lm355x_reg_data lm3556_regs[REG_MAX] = { |
155 | [REG_FLAG] = {0x0B, 0xFF, 0}, | 155 | [REG_FLAG] = {0x0B, 0xFF, 0}, |
156 | [REG_TORCH_CFG] = {0x0A, 0x10, 4}, | 156 | [REG_TORCH_CFG] = {0x0A, 0x10, 4}, |
157 | [REG_TORCH_CTRL] = {0x09, 0x70, 4}, | 157 | [REG_TORCH_CTRL] = {0x09, 0x70, 4}, |
158 | [REG_STROBE_CFG] = {0x0A, 0x20, 5}, | 158 | [REG_STROBE_CFG] = {0x0A, 0x20, 5}, |
159 | [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, | 159 | [REG_FLASH_CTRL] = {0x09, 0x0F, 0}, |
160 | [REG_INDI_CFG] = {0xFF, 0xFF, 0}, | 160 | [REG_INDI_CFG] = {0xFF, 0xFF, 0}, |
161 | [REG_INDI_CTRL] = {0x09, 0x70, 4}, | 161 | [REG_INDI_CTRL] = {0x09, 0x70, 4}, |
162 | [REG_OPMODE] = {0x0A, 0x03, 0}, | 162 | [REG_OPMODE] = {0x0A, 0x03, 0}, |
163 | }; | 163 | }; |
164 | 164 | ||
165 | static char lm355x_name[][I2C_NAME_SIZE] = { | 165 | static char lm355x_name[][I2C_NAME_SIZE] = { |
166 | [CHIP_LM3554] = LM3554_NAME, | 166 | [CHIP_LM3554] = LM3554_NAME, |
167 | [CHIP_LM3556] = LM3556_NAME, | 167 | [CHIP_LM3556] = LM3556_NAME, |
168 | }; | 168 | }; |
169 | 169 | ||
170 | /* chip initialize */ | 170 | /* chip initialize */ |
171 | static int lm355x_chip_init(struct lm355x_chip_data *chip) | 171 | static int lm355x_chip_init(struct lm355x_chip_data *chip) |
172 | { | 172 | { |
173 | int ret; | 173 | int ret; |
174 | unsigned int reg_val; | 174 | unsigned int reg_val; |
175 | struct lm355x_platform_data *pdata = chip->pdata; | 175 | struct lm355x_platform_data *pdata = chip->pdata; |
176 | 176 | ||
177 | /* input and output pins configuration */ | 177 | /* input and output pins configuration */ |
178 | switch (chip->type) { | 178 | switch (chip->type) { |
179 | case CHIP_LM3554: | 179 | case CHIP_LM3554: |
180 | reg_val = pdata->pin_tx2 | pdata->ntc_pin; | 180 | reg_val = pdata->pin_tx2 | pdata->ntc_pin; |
181 | ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); | 181 | ret = regmap_update_bits(chip->regmap, 0xE0, 0x28, reg_val); |
182 | if (ret < 0) | 182 | if (ret < 0) |
183 | goto out; | 183 | goto out; |
184 | reg_val = pdata->pass_mode; | 184 | reg_val = pdata->pass_mode; |
185 | ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); | 185 | ret = regmap_update_bits(chip->regmap, 0xA0, 0x04, reg_val); |
186 | if (ret < 0) | 186 | if (ret < 0) |
187 | goto out; | 187 | goto out; |
188 | break; | 188 | break; |
189 | 189 | ||
190 | case CHIP_LM3556: | 190 | case CHIP_LM3556: |
191 | reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; | 191 | reg_val = pdata->pin_tx2 | pdata->ntc_pin | pdata->pass_mode; |
192 | ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); | 192 | ret = regmap_update_bits(chip->regmap, 0x0A, 0xC4, reg_val); |
193 | if (ret < 0) | 193 | if (ret < 0) |
194 | goto out; | 194 | goto out; |
195 | break; | 195 | break; |
196 | default: | 196 | default: |
197 | return -ENODATA; | 197 | return -ENODATA; |
198 | } | 198 | } |
199 | 199 | ||
200 | return ret; | 200 | return ret; |
201 | out: | 201 | out: |
202 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | 202 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); |
203 | return ret; | 203 | return ret; |
204 | } | 204 | } |
205 | 205 | ||
206 | /* chip control */ | 206 | /* chip control */ |
207 | static void lm355x_control(struct lm355x_chip_data *chip, | 207 | static void lm355x_control(struct lm355x_chip_data *chip, |
208 | u8 brightness, enum lm355x_mode opmode) | 208 | u8 brightness, enum lm355x_mode opmode) |
209 | { | 209 | { |
210 | int ret; | 210 | int ret; |
211 | unsigned int reg_val; | 211 | unsigned int reg_val; |
212 | struct lm355x_platform_data *pdata = chip->pdata; | 212 | struct lm355x_platform_data *pdata = chip->pdata; |
213 | struct lm355x_reg_data *preg = chip->regs; | 213 | struct lm355x_reg_data *preg = chip->regs; |
214 | 214 | ||
215 | ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); | 215 | ret = regmap_read(chip->regmap, preg[REG_FLAG].regno, &chip->last_flag); |
216 | if (ret < 0) | 216 | if (ret < 0) |
217 | goto out; | 217 | goto out; |
218 | if (chip->last_flag & preg[REG_FLAG].mask) | 218 | if (chip->last_flag & preg[REG_FLAG].mask) |
219 | dev_info(chip->dev, "%s Last FLAG is 0x%x\n", | 219 | dev_info(chip->dev, "%s Last FLAG is 0x%x\n", |
220 | lm355x_name[chip->type], | 220 | lm355x_name[chip->type], |
221 | chip->last_flag & preg[REG_FLAG].mask); | 221 | chip->last_flag & preg[REG_FLAG].mask); |
222 | /* brightness 0 means shutdown */ | 222 | /* brightness 0 means shutdown */ |
223 | if (!brightness) | 223 | if (!brightness) |
224 | opmode = MODE_SHDN; | 224 | opmode = MODE_SHDN; |
225 | 225 | ||
226 | switch (opmode) { | 226 | switch (opmode) { |
227 | case MODE_TORCH: | 227 | case MODE_TORCH: |
228 | ret = | 228 | ret = |
229 | regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, | 229 | regmap_update_bits(chip->regmap, preg[REG_TORCH_CTRL].regno, |
230 | preg[REG_TORCH_CTRL].mask, | 230 | preg[REG_TORCH_CTRL].mask, |
231 | (brightness - 1) | 231 | (brightness - 1) |
232 | << preg[REG_TORCH_CTRL].shift); | 232 | << preg[REG_TORCH_CTRL].shift); |
233 | if (ret < 0) | 233 | if (ret < 0) |
234 | goto out; | 234 | goto out; |
235 | 235 | ||
236 | if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { | 236 | if (pdata->pin_tx1 != LM355x_PIN_TORCH_DISABLE) { |
237 | ret = | 237 | ret = |
238 | regmap_update_bits(chip->regmap, | 238 | regmap_update_bits(chip->regmap, |
239 | preg[REG_TORCH_CFG].regno, | 239 | preg[REG_TORCH_CFG].regno, |
240 | preg[REG_TORCH_CFG].mask, | 240 | preg[REG_TORCH_CFG].mask, |
241 | 0x01 << | 241 | 0x01 << |
242 | preg[REG_TORCH_CFG].shift); | 242 | preg[REG_TORCH_CFG].shift); |
243 | if (ret < 0) | 243 | if (ret < 0) |
244 | goto out; | 244 | goto out; |
245 | opmode = MODE_SHDN; | 245 | opmode = MODE_SHDN; |
246 | dev_info(chip->dev, | 246 | dev_info(chip->dev, |
247 | "torch brt is set - ext. torch pin mode\n"); | 247 | "torch brt is set - ext. torch pin mode\n"); |
248 | } | 248 | } |
249 | break; | 249 | break; |
250 | 250 | ||
251 | case MODE_FLASH: | 251 | case MODE_FLASH: |
252 | 252 | ||
253 | ret = | 253 | ret = |
254 | regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, | 254 | regmap_update_bits(chip->regmap, preg[REG_FLASH_CTRL].regno, |
255 | preg[REG_FLASH_CTRL].mask, | 255 | preg[REG_FLASH_CTRL].mask, |
256 | (brightness - 1) | 256 | (brightness - 1) |
257 | << preg[REG_FLASH_CTRL].shift); | 257 | << preg[REG_FLASH_CTRL].shift); |
258 | if (ret < 0) | 258 | if (ret < 0) |
259 | goto out; | 259 | goto out; |
260 | 260 | ||
261 | if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { | 261 | if (pdata->pin_strobe != LM355x_PIN_STROBE_DISABLE) { |
262 | if (chip->type == CHIP_LM3554) | 262 | if (chip->type == CHIP_LM3554) |
263 | reg_val = 0x00; | 263 | reg_val = 0x00; |
264 | else | 264 | else |
265 | reg_val = 0x01; | 265 | reg_val = 0x01; |
266 | ret = | 266 | ret = |
267 | regmap_update_bits(chip->regmap, | 267 | regmap_update_bits(chip->regmap, |
268 | preg[REG_STROBE_CFG].regno, | 268 | preg[REG_STROBE_CFG].regno, |
269 | preg[REG_STROBE_CFG].mask, | 269 | preg[REG_STROBE_CFG].mask, |
270 | reg_val << | 270 | reg_val << |
271 | preg[REG_STROBE_CFG].shift); | 271 | preg[REG_STROBE_CFG].shift); |
272 | if (ret < 0) | 272 | if (ret < 0) |
273 | goto out; | 273 | goto out; |
274 | opmode = MODE_SHDN; | 274 | opmode = MODE_SHDN; |
275 | dev_info(chip->dev, | 275 | dev_info(chip->dev, |
276 | "flash brt is set - ext. strobe pin mode\n"); | 276 | "flash brt is set - ext. strobe pin mode\n"); |
277 | } | 277 | } |
278 | break; | 278 | break; |
279 | 279 | ||
280 | case MODE_INDIC: | 280 | case MODE_INDIC: |
281 | ret = | 281 | ret = |
282 | regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, | 282 | regmap_update_bits(chip->regmap, preg[REG_INDI_CTRL].regno, |
283 | preg[REG_INDI_CTRL].mask, | 283 | preg[REG_INDI_CTRL].mask, |
284 | (brightness - 1) | 284 | (brightness - 1) |
285 | << preg[REG_INDI_CTRL].shift); | 285 | << preg[REG_INDI_CTRL].shift); |
286 | if (ret < 0) | 286 | if (ret < 0) |
287 | goto out; | 287 | goto out; |
288 | 288 | ||
289 | if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { | 289 | if (pdata->pin_tx2 != LM355x_PIN_TX_DISABLE) { |
290 | ret = | 290 | ret = |
291 | regmap_update_bits(chip->regmap, | 291 | regmap_update_bits(chip->regmap, |
292 | preg[REG_INDI_CFG].regno, | 292 | preg[REG_INDI_CFG].regno, |
293 | preg[REG_INDI_CFG].mask, | 293 | preg[REG_INDI_CFG].mask, |
294 | 0x01 << | 294 | 0x01 << |
295 | preg[REG_INDI_CFG].shift); | 295 | preg[REG_INDI_CFG].shift); |
296 | if (ret < 0) | 296 | if (ret < 0) |
297 | goto out; | 297 | goto out; |
298 | opmode = MODE_SHDN; | 298 | opmode = MODE_SHDN; |
299 | } | 299 | } |
300 | break; | 300 | break; |
301 | case MODE_SHDN: | 301 | case MODE_SHDN: |
302 | break; | 302 | break; |
303 | default: | 303 | default: |
304 | return; | 304 | return; |
305 | } | 305 | } |
306 | /* operation mode control */ | 306 | /* operation mode control */ |
307 | ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, | 307 | ret = regmap_update_bits(chip->regmap, preg[REG_OPMODE].regno, |
308 | preg[REG_OPMODE].mask, | 308 | preg[REG_OPMODE].mask, |
309 | opmode << preg[REG_OPMODE].shift); | 309 | opmode << preg[REG_OPMODE].shift); |
310 | if (ret < 0) | 310 | if (ret < 0) |
311 | goto out; | 311 | goto out; |
312 | return; | 312 | return; |
313 | out: | 313 | out: |
314 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | 314 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); |
315 | return; | 315 | return; |
316 | } | 316 | } |
317 | 317 | ||
318 | /* torch */ | 318 | /* torch */ |
319 | static void lm355x_deferred_torch_brightness_set(struct work_struct *work) | 319 | static void lm355x_deferred_torch_brightness_set(struct work_struct *work) |
320 | { | 320 | { |
321 | struct lm355x_chip_data *chip = | 321 | struct lm355x_chip_data *chip = |
322 | container_of(work, struct lm355x_chip_data, work_torch); | 322 | container_of(work, struct lm355x_chip_data, work_torch); |
323 | 323 | ||
324 | mutex_lock(&chip->lock); | 324 | mutex_lock(&chip->lock); |
325 | lm355x_control(chip, chip->br_torch, MODE_TORCH); | 325 | lm355x_control(chip, chip->br_torch, MODE_TORCH); |
326 | mutex_unlock(&chip->lock); | 326 | mutex_unlock(&chip->lock); |
327 | } | 327 | } |
328 | 328 | ||
329 | static void lm355x_torch_brightness_set(struct led_classdev *cdev, | 329 | static void lm355x_torch_brightness_set(struct led_classdev *cdev, |
330 | enum led_brightness brightness) | 330 | enum led_brightness brightness) |
331 | { | 331 | { |
332 | struct lm355x_chip_data *chip = | 332 | struct lm355x_chip_data *chip = |
333 | container_of(cdev, struct lm355x_chip_data, cdev_torch); | 333 | container_of(cdev, struct lm355x_chip_data, cdev_torch); |
334 | 334 | ||
335 | chip->br_torch = brightness; | 335 | chip->br_torch = brightness; |
336 | schedule_work(&chip->work_torch); | 336 | schedule_work(&chip->work_torch); |
337 | } | 337 | } |
338 | 338 | ||
339 | /* flash */ | 339 | /* flash */ |
340 | static void lm355x_deferred_strobe_brightness_set(struct work_struct *work) | 340 | static void lm355x_deferred_strobe_brightness_set(struct work_struct *work) |
341 | { | 341 | { |
342 | struct lm355x_chip_data *chip = | 342 | struct lm355x_chip_data *chip = |
343 | container_of(work, struct lm355x_chip_data, work_flash); | 343 | container_of(work, struct lm355x_chip_data, work_flash); |
344 | 344 | ||
345 | mutex_lock(&chip->lock); | 345 | mutex_lock(&chip->lock); |
346 | lm355x_control(chip, chip->br_flash, MODE_FLASH); | 346 | lm355x_control(chip, chip->br_flash, MODE_FLASH); |
347 | mutex_unlock(&chip->lock); | 347 | mutex_unlock(&chip->lock); |
348 | } | 348 | } |
349 | 349 | ||
350 | static void lm355x_strobe_brightness_set(struct led_classdev *cdev, | 350 | static void lm355x_strobe_brightness_set(struct led_classdev *cdev, |
351 | enum led_brightness brightness) | 351 | enum led_brightness brightness) |
352 | { | 352 | { |
353 | struct lm355x_chip_data *chip = | 353 | struct lm355x_chip_data *chip = |
354 | container_of(cdev, struct lm355x_chip_data, cdev_flash); | 354 | container_of(cdev, struct lm355x_chip_data, cdev_flash); |
355 | 355 | ||
356 | chip->br_flash = brightness; | 356 | chip->br_flash = brightness; |
357 | schedule_work(&chip->work_flash); | 357 | schedule_work(&chip->work_flash); |
358 | } | 358 | } |
359 | 359 | ||
360 | /* indicator */ | 360 | /* indicator */ |
361 | static void lm355x_deferred_indicator_brightness_set(struct work_struct *work) | 361 | static void lm355x_deferred_indicator_brightness_set(struct work_struct *work) |
362 | { | 362 | { |
363 | struct lm355x_chip_data *chip = | 363 | struct lm355x_chip_data *chip = |
364 | container_of(work, struct lm355x_chip_data, work_indicator); | 364 | container_of(work, struct lm355x_chip_data, work_indicator); |
365 | 365 | ||
366 | mutex_lock(&chip->lock); | 366 | mutex_lock(&chip->lock); |
367 | lm355x_control(chip, chip->br_indicator, MODE_INDIC); | 367 | lm355x_control(chip, chip->br_indicator, MODE_INDIC); |
368 | mutex_unlock(&chip->lock); | 368 | mutex_unlock(&chip->lock); |
369 | } | 369 | } |
370 | 370 | ||
371 | static void lm355x_indicator_brightness_set(struct led_classdev *cdev, | 371 | static void lm355x_indicator_brightness_set(struct led_classdev *cdev, |
372 | enum led_brightness brightness) | 372 | enum led_brightness brightness) |
373 | { | 373 | { |
374 | struct lm355x_chip_data *chip = | 374 | struct lm355x_chip_data *chip = |
375 | container_of(cdev, struct lm355x_chip_data, cdev_indicator); | 375 | container_of(cdev, struct lm355x_chip_data, cdev_indicator); |
376 | 376 | ||
377 | chip->br_indicator = brightness; | 377 | chip->br_indicator = brightness; |
378 | schedule_work(&chip->work_indicator); | 378 | schedule_work(&chip->work_indicator); |
379 | } | 379 | } |
380 | 380 | ||
381 | /* indicator pattern only for lm3556*/ | 381 | /* indicator pattern only for lm3556*/ |
382 | static ssize_t lm3556_indicator_pattern_store(struct device *dev, | 382 | static ssize_t lm3556_indicator_pattern_store(struct device *dev, |
383 | struct device_attribute *attr, | 383 | struct device_attribute *attr, |
384 | const char *buf, size_t size) | 384 | const char *buf, size_t size) |
385 | { | 385 | { |
386 | ssize_t ret; | 386 | ssize_t ret; |
387 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 387 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
388 | struct lm355x_chip_data *chip = | 388 | struct lm355x_chip_data *chip = |
389 | container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); | 389 | container_of(led_cdev, struct lm355x_chip_data, cdev_indicator); |
390 | unsigned int state; | 390 | unsigned int state; |
391 | 391 | ||
392 | ret = kstrtouint(buf, 10, &state); | 392 | ret = kstrtouint(buf, 10, &state); |
393 | if (ret) | 393 | if (ret) |
394 | goto out; | 394 | goto out; |
395 | if (state > INDIC_PATTERN_SIZE - 1) | 395 | if (state > INDIC_PATTERN_SIZE - 1) |
396 | state = INDIC_PATTERN_SIZE - 1; | 396 | state = INDIC_PATTERN_SIZE - 1; |
397 | 397 | ||
398 | ret = regmap_write(chip->regmap, 0x04, | 398 | ret = regmap_write(chip->regmap, 0x04, |
399 | indicator_pattern[state].blinking); | 399 | indicator_pattern[state].blinking); |
400 | if (ret < 0) | 400 | if (ret < 0) |
401 | goto out; | 401 | goto out; |
402 | 402 | ||
403 | ret = regmap_write(chip->regmap, 0x05, | 403 | ret = regmap_write(chip->regmap, 0x05, |
404 | indicator_pattern[state].period_cnt); | 404 | indicator_pattern[state].period_cnt); |
405 | if (ret < 0) | 405 | if (ret < 0) |
406 | goto out; | 406 | goto out; |
407 | 407 | ||
408 | return size; | 408 | return size; |
409 | out: | 409 | out: |
410 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | 410 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); |
411 | return ret; | 411 | return ret; |
412 | } | 412 | } |
413 | 413 | ||
414 | static DEVICE_ATTR(pattern, S_IWUSR, NULL, lm3556_indicator_pattern_store); | 414 | static DEVICE_ATTR(pattern, S_IWUSR, NULL, lm3556_indicator_pattern_store); |
415 | 415 | ||
416 | static const struct regmap_config lm355x_regmap = { | 416 | static const struct regmap_config lm355x_regmap = { |
417 | .reg_bits = 8, | 417 | .reg_bits = 8, |
418 | .val_bits = 8, | 418 | .val_bits = 8, |
419 | .max_register = 0xFF, | 419 | .max_register = 0xFF, |
420 | }; | 420 | }; |
421 | 421 | ||
422 | /* module initialize */ | 422 | /* module initialize */ |
423 | static int lm355x_probe(struct i2c_client *client, | 423 | static int lm355x_probe(struct i2c_client *client, |
424 | const struct i2c_device_id *id) | 424 | const struct i2c_device_id *id) |
425 | { | 425 | { |
426 | struct lm355x_platform_data *pdata = client->dev.platform_data; | 426 | struct lm355x_platform_data *pdata = dev_get_platdata(&client->dev); |
427 | struct lm355x_chip_data *chip; | 427 | struct lm355x_chip_data *chip; |
428 | 428 | ||
429 | int err; | 429 | int err; |
430 | 430 | ||
431 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | 431 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
432 | dev_err(&client->dev, "i2c functionality check fail.\n"); | 432 | dev_err(&client->dev, "i2c functionality check fail.\n"); |
433 | return -EOPNOTSUPP; | 433 | return -EOPNOTSUPP; |
434 | } | 434 | } |
435 | 435 | ||
436 | if (pdata == NULL) { | 436 | if (pdata == NULL) { |
437 | dev_err(&client->dev, "needs Platform Data.\n"); | 437 | dev_err(&client->dev, "needs Platform Data.\n"); |
438 | return -ENODATA; | 438 | return -ENODATA; |
439 | } | 439 | } |
440 | 440 | ||
441 | chip = devm_kzalloc(&client->dev, | 441 | chip = devm_kzalloc(&client->dev, |
442 | sizeof(struct lm355x_chip_data), GFP_KERNEL); | 442 | sizeof(struct lm355x_chip_data), GFP_KERNEL); |
443 | if (!chip) | 443 | if (!chip) |
444 | return -ENOMEM; | 444 | return -ENOMEM; |
445 | 445 | ||
446 | chip->dev = &client->dev; | 446 | chip->dev = &client->dev; |
447 | chip->type = id->driver_data; | 447 | chip->type = id->driver_data; |
448 | switch (id->driver_data) { | 448 | switch (id->driver_data) { |
449 | case CHIP_LM3554: | 449 | case CHIP_LM3554: |
450 | chip->regs = lm3554_regs; | 450 | chip->regs = lm3554_regs; |
451 | break; | 451 | break; |
452 | case CHIP_LM3556: | 452 | case CHIP_LM3556: |
453 | chip->regs = lm3556_regs; | 453 | chip->regs = lm3556_regs; |
454 | break; | 454 | break; |
455 | default: | 455 | default: |
456 | return -ENOSYS; | 456 | return -ENOSYS; |
457 | } | 457 | } |
458 | chip->pdata = pdata; | 458 | chip->pdata = pdata; |
459 | 459 | ||
460 | chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); | 460 | chip->regmap = devm_regmap_init_i2c(client, &lm355x_regmap); |
461 | if (IS_ERR(chip->regmap)) { | 461 | if (IS_ERR(chip->regmap)) { |
462 | err = PTR_ERR(chip->regmap); | 462 | err = PTR_ERR(chip->regmap); |
463 | dev_err(&client->dev, | 463 | dev_err(&client->dev, |
464 | "Failed to allocate register map: %d\n", err); | 464 | "Failed to allocate register map: %d\n", err); |
465 | return err; | 465 | return err; |
466 | } | 466 | } |
467 | 467 | ||
468 | mutex_init(&chip->lock); | 468 | mutex_init(&chip->lock); |
469 | i2c_set_clientdata(client, chip); | 469 | i2c_set_clientdata(client, chip); |
470 | 470 | ||
471 | err = lm355x_chip_init(chip); | 471 | err = lm355x_chip_init(chip); |
472 | if (err < 0) | 472 | if (err < 0) |
473 | goto err_out; | 473 | goto err_out; |
474 | 474 | ||
475 | /* flash */ | 475 | /* flash */ |
476 | INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set); | 476 | INIT_WORK(&chip->work_flash, lm355x_deferred_strobe_brightness_set); |
477 | chip->cdev_flash.name = "flash"; | 477 | chip->cdev_flash.name = "flash"; |
478 | chip->cdev_flash.max_brightness = 16; | 478 | chip->cdev_flash.max_brightness = 16; |
479 | chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set; | 479 | chip->cdev_flash.brightness_set = lm355x_strobe_brightness_set; |
480 | chip->cdev_flash.default_trigger = "flash"; | 480 | chip->cdev_flash.default_trigger = "flash"; |
481 | err = led_classdev_register((struct device *) | 481 | err = led_classdev_register((struct device *) |
482 | &client->dev, &chip->cdev_flash); | 482 | &client->dev, &chip->cdev_flash); |
483 | if (err < 0) | 483 | if (err < 0) |
484 | goto err_out; | 484 | goto err_out; |
485 | /* torch */ | 485 | /* torch */ |
486 | INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set); | 486 | INIT_WORK(&chip->work_torch, lm355x_deferred_torch_brightness_set); |
487 | chip->cdev_torch.name = "torch"; | 487 | chip->cdev_torch.name = "torch"; |
488 | chip->cdev_torch.max_brightness = 8; | 488 | chip->cdev_torch.max_brightness = 8; |
489 | chip->cdev_torch.brightness_set = lm355x_torch_brightness_set; | 489 | chip->cdev_torch.brightness_set = lm355x_torch_brightness_set; |
490 | chip->cdev_torch.default_trigger = "torch"; | 490 | chip->cdev_torch.default_trigger = "torch"; |
491 | err = led_classdev_register((struct device *) | 491 | err = led_classdev_register((struct device *) |
492 | &client->dev, &chip->cdev_torch); | 492 | &client->dev, &chip->cdev_torch); |
493 | if (err < 0) | 493 | if (err < 0) |
494 | goto err_create_torch_file; | 494 | goto err_create_torch_file; |
495 | /* indicator */ | 495 | /* indicator */ |
496 | INIT_WORK(&chip->work_indicator, | 496 | INIT_WORK(&chip->work_indicator, |
497 | lm355x_deferred_indicator_brightness_set); | 497 | lm355x_deferred_indicator_brightness_set); |
498 | chip->cdev_indicator.name = "indicator"; | 498 | chip->cdev_indicator.name = "indicator"; |
499 | if (id->driver_data == CHIP_LM3554) | 499 | if (id->driver_data == CHIP_LM3554) |
500 | chip->cdev_indicator.max_brightness = 4; | 500 | chip->cdev_indicator.max_brightness = 4; |
501 | else | 501 | else |
502 | chip->cdev_indicator.max_brightness = 8; | 502 | chip->cdev_indicator.max_brightness = 8; |
503 | chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; | 503 | chip->cdev_indicator.brightness_set = lm355x_indicator_brightness_set; |
504 | err = led_classdev_register((struct device *) | 504 | err = led_classdev_register((struct device *) |
505 | &client->dev, &chip->cdev_indicator); | 505 | &client->dev, &chip->cdev_indicator); |
506 | if (err < 0) | 506 | if (err < 0) |
507 | goto err_create_indicator_file; | 507 | goto err_create_indicator_file; |
508 | /* indicator pattern control only for LM3554 */ | 508 | /* indicator pattern control only for LM3554 */ |
509 | if (id->driver_data == CHIP_LM3556) { | 509 | if (id->driver_data == CHIP_LM3556) { |
510 | err = | 510 | err = |
511 | device_create_file(chip->cdev_indicator.dev, | 511 | device_create_file(chip->cdev_indicator.dev, |
512 | &dev_attr_pattern); | 512 | &dev_attr_pattern); |
513 | if (err < 0) | 513 | if (err < 0) |
514 | goto err_create_pattern_file; | 514 | goto err_create_pattern_file; |
515 | } | 515 | } |
516 | 516 | ||
517 | dev_info(&client->dev, "%s is initialized\n", | 517 | dev_info(&client->dev, "%s is initialized\n", |
518 | lm355x_name[id->driver_data]); | 518 | lm355x_name[id->driver_data]); |
519 | return 0; | 519 | return 0; |
520 | 520 | ||
521 | err_create_pattern_file: | 521 | err_create_pattern_file: |
522 | led_classdev_unregister(&chip->cdev_indicator); | 522 | led_classdev_unregister(&chip->cdev_indicator); |
523 | err_create_indicator_file: | 523 | err_create_indicator_file: |
524 | led_classdev_unregister(&chip->cdev_torch); | 524 | led_classdev_unregister(&chip->cdev_torch); |
525 | err_create_torch_file: | 525 | err_create_torch_file: |
526 | led_classdev_unregister(&chip->cdev_flash); | 526 | led_classdev_unregister(&chip->cdev_flash); |
527 | err_out: | 527 | err_out: |
528 | return err; | 528 | return err; |
529 | } | 529 | } |
530 | 530 | ||
531 | static int lm355x_remove(struct i2c_client *client) | 531 | static int lm355x_remove(struct i2c_client *client) |
532 | { | 532 | { |
533 | struct lm355x_chip_data *chip = i2c_get_clientdata(client); | 533 | struct lm355x_chip_data *chip = i2c_get_clientdata(client); |
534 | struct lm355x_reg_data *preg = chip->regs; | 534 | struct lm355x_reg_data *preg = chip->regs; |
535 | 535 | ||
536 | regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); | 536 | regmap_write(chip->regmap, preg[REG_OPMODE].regno, 0); |
537 | if (chip->type == CHIP_LM3556) | 537 | if (chip->type == CHIP_LM3556) |
538 | device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); | 538 | device_remove_file(chip->cdev_indicator.dev, &dev_attr_pattern); |
539 | led_classdev_unregister(&chip->cdev_indicator); | 539 | led_classdev_unregister(&chip->cdev_indicator); |
540 | flush_work(&chip->work_indicator); | 540 | flush_work(&chip->work_indicator); |
541 | led_classdev_unregister(&chip->cdev_torch); | 541 | led_classdev_unregister(&chip->cdev_torch); |
542 | flush_work(&chip->work_torch); | 542 | flush_work(&chip->work_torch); |
543 | led_classdev_unregister(&chip->cdev_flash); | 543 | led_classdev_unregister(&chip->cdev_flash); |
544 | flush_work(&chip->work_flash); | 544 | flush_work(&chip->work_flash); |
545 | dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); | 545 | dev_info(&client->dev, "%s is removed\n", lm355x_name[chip->type]); |
546 | 546 | ||
547 | return 0; | 547 | return 0; |
548 | } | 548 | } |
549 | 549 | ||
550 | static const struct i2c_device_id lm355x_id[] = { | 550 | static const struct i2c_device_id lm355x_id[] = { |
551 | {LM3554_NAME, CHIP_LM3554}, | 551 | {LM3554_NAME, CHIP_LM3554}, |
552 | {LM3556_NAME, CHIP_LM3556}, | 552 | {LM3556_NAME, CHIP_LM3556}, |
553 | {} | 553 | {} |
554 | }; | 554 | }; |
555 | 555 | ||
556 | MODULE_DEVICE_TABLE(i2c, lm355x_id); | 556 | MODULE_DEVICE_TABLE(i2c, lm355x_id); |
557 | 557 | ||
558 | static struct i2c_driver lm355x_i2c_driver = { | 558 | static struct i2c_driver lm355x_i2c_driver = { |
559 | .driver = { | 559 | .driver = { |
560 | .name = LM355x_NAME, | 560 | .name = LM355x_NAME, |
561 | .owner = THIS_MODULE, | 561 | .owner = THIS_MODULE, |
562 | .pm = NULL, | 562 | .pm = NULL, |
563 | }, | 563 | }, |
564 | .probe = lm355x_probe, | 564 | .probe = lm355x_probe, |
565 | .remove = lm355x_remove, | 565 | .remove = lm355x_remove, |
566 | .id_table = lm355x_id, | 566 | .id_table = lm355x_id, |
567 | }; | 567 | }; |
568 | 568 | ||
569 | module_i2c_driver(lm355x_i2c_driver); | 569 | module_i2c_driver(lm355x_i2c_driver); |
570 | 570 | ||
571 | MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); | 571 | MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM355x"); |
572 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); | 572 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); |
573 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); | 573 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); |
574 | MODULE_LICENSE("GPL v2"); | 574 | MODULE_LICENSE("GPL v2"); |
575 | 575 |
drivers/leds/leds-lm3642.c
1 | /* | 1 | /* |
2 | * Simple driver for Texas Instruments LM3642 LED Flash driver chip | 2 | * Simple driver for Texas Instruments LM3642 LED Flash driver chip |
3 | * Copyright (C) 2012 Texas Instruments | 3 | * Copyright (C) 2012 Texas Instruments |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation. | 7 | * published by the Free Software Foundation. |
8 | * | 8 | * |
9 | */ | 9 | */ |
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
12 | #include <linux/i2c.h> | 12 | #include <linux/i2c.h> |
13 | #include <linux/leds.h> | 13 | #include <linux/leds.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
16 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
17 | #include <linux/regmap.h> | 17 | #include <linux/regmap.h> |
18 | #include <linux/workqueue.h> | 18 | #include <linux/workqueue.h> |
19 | #include <linux/platform_data/leds-lm3642.h> | 19 | #include <linux/platform_data/leds-lm3642.h> |
20 | 20 | ||
21 | #define REG_FILT_TIME (0x0) | 21 | #define REG_FILT_TIME (0x0) |
22 | #define REG_IVFM_MODE (0x1) | 22 | #define REG_IVFM_MODE (0x1) |
23 | #define REG_TORCH_TIME (0x6) | 23 | #define REG_TORCH_TIME (0x6) |
24 | #define REG_FLASH (0x8) | 24 | #define REG_FLASH (0x8) |
25 | #define REG_I_CTRL (0x9) | 25 | #define REG_I_CTRL (0x9) |
26 | #define REG_ENABLE (0xA) | 26 | #define REG_ENABLE (0xA) |
27 | #define REG_FLAG (0xB) | 27 | #define REG_FLAG (0xB) |
28 | #define REG_MAX (0xB) | 28 | #define REG_MAX (0xB) |
29 | 29 | ||
30 | #define UVLO_EN_SHIFT (7) | 30 | #define UVLO_EN_SHIFT (7) |
31 | #define IVM_D_TH_SHIFT (2) | 31 | #define IVM_D_TH_SHIFT (2) |
32 | #define TORCH_RAMP_UP_TIME_SHIFT (3) | 32 | #define TORCH_RAMP_UP_TIME_SHIFT (3) |
33 | #define TORCH_RAMP_DN_TIME_SHIFT (0) | 33 | #define TORCH_RAMP_DN_TIME_SHIFT (0) |
34 | #define INDUCTOR_I_LIMIT_SHIFT (6) | 34 | #define INDUCTOR_I_LIMIT_SHIFT (6) |
35 | #define FLASH_RAMP_TIME_SHIFT (3) | 35 | #define FLASH_RAMP_TIME_SHIFT (3) |
36 | #define FLASH_TOUT_TIME_SHIFT (0) | 36 | #define FLASH_TOUT_TIME_SHIFT (0) |
37 | #define TORCH_I_SHIFT (4) | 37 | #define TORCH_I_SHIFT (4) |
38 | #define FLASH_I_SHIFT (0) | 38 | #define FLASH_I_SHIFT (0) |
39 | #define IVFM_SHIFT (7) | 39 | #define IVFM_SHIFT (7) |
40 | #define TX_PIN_EN_SHIFT (6) | 40 | #define TX_PIN_EN_SHIFT (6) |
41 | #define STROBE_PIN_EN_SHIFT (5) | 41 | #define STROBE_PIN_EN_SHIFT (5) |
42 | #define TORCH_PIN_EN_SHIFT (4) | 42 | #define TORCH_PIN_EN_SHIFT (4) |
43 | #define MODE_BITS_SHIFT (0) | 43 | #define MODE_BITS_SHIFT (0) |
44 | 44 | ||
45 | #define UVLO_EN_MASK (0x1) | 45 | #define UVLO_EN_MASK (0x1) |
46 | #define IVM_D_TH_MASK (0x7) | 46 | #define IVM_D_TH_MASK (0x7) |
47 | #define TORCH_RAMP_UP_TIME_MASK (0x7) | 47 | #define TORCH_RAMP_UP_TIME_MASK (0x7) |
48 | #define TORCH_RAMP_DN_TIME_MASK (0x7) | 48 | #define TORCH_RAMP_DN_TIME_MASK (0x7) |
49 | #define INDUCTOR_I_LIMIT_MASK (0x1) | 49 | #define INDUCTOR_I_LIMIT_MASK (0x1) |
50 | #define FLASH_RAMP_TIME_MASK (0x7) | 50 | #define FLASH_RAMP_TIME_MASK (0x7) |
51 | #define FLASH_TOUT_TIME_MASK (0x7) | 51 | #define FLASH_TOUT_TIME_MASK (0x7) |
52 | #define TORCH_I_MASK (0x7) | 52 | #define TORCH_I_MASK (0x7) |
53 | #define FLASH_I_MASK (0xF) | 53 | #define FLASH_I_MASK (0xF) |
54 | #define IVFM_MASK (0x1) | 54 | #define IVFM_MASK (0x1) |
55 | #define TX_PIN_EN_MASK (0x1) | 55 | #define TX_PIN_EN_MASK (0x1) |
56 | #define STROBE_PIN_EN_MASK (0x1) | 56 | #define STROBE_PIN_EN_MASK (0x1) |
57 | #define TORCH_PIN_EN_MASK (0x1) | 57 | #define TORCH_PIN_EN_MASK (0x1) |
58 | #define MODE_BITS_MASK (0x73) | 58 | #define MODE_BITS_MASK (0x73) |
59 | #define EX_PIN_CONTROL_MASK (0x71) | 59 | #define EX_PIN_CONTROL_MASK (0x71) |
60 | #define EX_PIN_ENABLE_MASK (0x70) | 60 | #define EX_PIN_ENABLE_MASK (0x70) |
61 | 61 | ||
62 | enum lm3642_mode { | 62 | enum lm3642_mode { |
63 | MODES_STASNDBY = 0, | 63 | MODES_STASNDBY = 0, |
64 | MODES_INDIC, | 64 | MODES_INDIC, |
65 | MODES_TORCH, | 65 | MODES_TORCH, |
66 | MODES_FLASH | 66 | MODES_FLASH |
67 | }; | 67 | }; |
68 | 68 | ||
69 | struct lm3642_chip_data { | 69 | struct lm3642_chip_data { |
70 | struct device *dev; | 70 | struct device *dev; |
71 | 71 | ||
72 | struct led_classdev cdev_flash; | 72 | struct led_classdev cdev_flash; |
73 | struct led_classdev cdev_torch; | 73 | struct led_classdev cdev_torch; |
74 | struct led_classdev cdev_indicator; | 74 | struct led_classdev cdev_indicator; |
75 | 75 | ||
76 | struct work_struct work_flash; | 76 | struct work_struct work_flash; |
77 | struct work_struct work_torch; | 77 | struct work_struct work_torch; |
78 | struct work_struct work_indicator; | 78 | struct work_struct work_indicator; |
79 | 79 | ||
80 | u8 br_flash; | 80 | u8 br_flash; |
81 | u8 br_torch; | 81 | u8 br_torch; |
82 | u8 br_indicator; | 82 | u8 br_indicator; |
83 | 83 | ||
84 | enum lm3642_torch_pin_enable torch_pin; | 84 | enum lm3642_torch_pin_enable torch_pin; |
85 | enum lm3642_strobe_pin_enable strobe_pin; | 85 | enum lm3642_strobe_pin_enable strobe_pin; |
86 | enum lm3642_tx_pin_enable tx_pin; | 86 | enum lm3642_tx_pin_enable tx_pin; |
87 | 87 | ||
88 | struct lm3642_platform_data *pdata; | 88 | struct lm3642_platform_data *pdata; |
89 | struct regmap *regmap; | 89 | struct regmap *regmap; |
90 | struct mutex lock; | 90 | struct mutex lock; |
91 | 91 | ||
92 | unsigned int last_flag; | 92 | unsigned int last_flag; |
93 | }; | 93 | }; |
94 | 94 | ||
95 | /* chip initialize */ | 95 | /* chip initialize */ |
96 | static int lm3642_chip_init(struct lm3642_chip_data *chip) | 96 | static int lm3642_chip_init(struct lm3642_chip_data *chip) |
97 | { | 97 | { |
98 | int ret; | 98 | int ret; |
99 | struct lm3642_platform_data *pdata = chip->pdata; | 99 | struct lm3642_platform_data *pdata = chip->pdata; |
100 | 100 | ||
101 | /* set enable register */ | 101 | /* set enable register */ |
102 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, EX_PIN_ENABLE_MASK, | 102 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, EX_PIN_ENABLE_MASK, |
103 | pdata->tx_pin); | 103 | pdata->tx_pin); |
104 | if (ret < 0) | 104 | if (ret < 0) |
105 | dev_err(chip->dev, "Failed to update REG_ENABLE Register\n"); | 105 | dev_err(chip->dev, "Failed to update REG_ENABLE Register\n"); |
106 | return ret; | 106 | return ret; |
107 | } | 107 | } |
108 | 108 | ||
109 | /* chip control */ | 109 | /* chip control */ |
110 | static int lm3642_control(struct lm3642_chip_data *chip, | 110 | static int lm3642_control(struct lm3642_chip_data *chip, |
111 | u8 brightness, enum lm3642_mode opmode) | 111 | u8 brightness, enum lm3642_mode opmode) |
112 | { | 112 | { |
113 | int ret; | 113 | int ret; |
114 | 114 | ||
115 | ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); | 115 | ret = regmap_read(chip->regmap, REG_FLAG, &chip->last_flag); |
116 | if (ret < 0) { | 116 | if (ret < 0) { |
117 | dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); | 117 | dev_err(chip->dev, "Failed to read REG_FLAG Register\n"); |
118 | goto out; | 118 | goto out; |
119 | } | 119 | } |
120 | 120 | ||
121 | if (chip->last_flag) | 121 | if (chip->last_flag) |
122 | dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); | 122 | dev_info(chip->dev, "Last FLAG is 0x%x\n", chip->last_flag); |
123 | 123 | ||
124 | /* brightness 0 means off-state */ | 124 | /* brightness 0 means off-state */ |
125 | if (!brightness) | 125 | if (!brightness) |
126 | opmode = MODES_STASNDBY; | 126 | opmode = MODES_STASNDBY; |
127 | 127 | ||
128 | switch (opmode) { | 128 | switch (opmode) { |
129 | case MODES_TORCH: | 129 | case MODES_TORCH: |
130 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, | 130 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, |
131 | TORCH_I_MASK << TORCH_I_SHIFT, | 131 | TORCH_I_MASK << TORCH_I_SHIFT, |
132 | (brightness - 1) << TORCH_I_SHIFT); | 132 | (brightness - 1) << TORCH_I_SHIFT); |
133 | 133 | ||
134 | if (chip->torch_pin) | 134 | if (chip->torch_pin) |
135 | opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); | 135 | opmode |= (TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT); |
136 | break; | 136 | break; |
137 | 137 | ||
138 | case MODES_FLASH: | 138 | case MODES_FLASH: |
139 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, | 139 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, |
140 | FLASH_I_MASK << FLASH_I_SHIFT, | 140 | FLASH_I_MASK << FLASH_I_SHIFT, |
141 | (brightness - 1) << FLASH_I_SHIFT); | 141 | (brightness - 1) << FLASH_I_SHIFT); |
142 | 142 | ||
143 | if (chip->strobe_pin) | 143 | if (chip->strobe_pin) |
144 | opmode |= (STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT); | 144 | opmode |= (STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT); |
145 | break; | 145 | break; |
146 | 146 | ||
147 | case MODES_INDIC: | 147 | case MODES_INDIC: |
148 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, | 148 | ret = regmap_update_bits(chip->regmap, REG_I_CTRL, |
149 | TORCH_I_MASK << TORCH_I_SHIFT, | 149 | TORCH_I_MASK << TORCH_I_SHIFT, |
150 | (brightness - 1) << TORCH_I_SHIFT); | 150 | (brightness - 1) << TORCH_I_SHIFT); |
151 | break; | 151 | break; |
152 | 152 | ||
153 | case MODES_STASNDBY: | 153 | case MODES_STASNDBY: |
154 | 154 | ||
155 | break; | 155 | break; |
156 | 156 | ||
157 | default: | 157 | default: |
158 | return ret; | 158 | return ret; |
159 | } | 159 | } |
160 | if (ret < 0) { | 160 | if (ret < 0) { |
161 | dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); | 161 | dev_err(chip->dev, "Failed to write REG_I_CTRL Register\n"); |
162 | goto out; | 162 | goto out; |
163 | } | 163 | } |
164 | 164 | ||
165 | if (chip->tx_pin) | 165 | if (chip->tx_pin) |
166 | opmode |= (TX_PIN_EN_MASK << TX_PIN_EN_SHIFT); | 166 | opmode |= (TX_PIN_EN_MASK << TX_PIN_EN_SHIFT); |
167 | 167 | ||
168 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, | 168 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, |
169 | MODE_BITS_MASK << MODE_BITS_SHIFT, | 169 | MODE_BITS_MASK << MODE_BITS_SHIFT, |
170 | opmode << MODE_BITS_SHIFT); | 170 | opmode << MODE_BITS_SHIFT); |
171 | out: | 171 | out: |
172 | return ret; | 172 | return ret; |
173 | } | 173 | } |
174 | 174 | ||
175 | /* torch */ | 175 | /* torch */ |
176 | 176 | ||
177 | /* torch pin config for lm3642*/ | 177 | /* torch pin config for lm3642*/ |
178 | static ssize_t lm3642_torch_pin_store(struct device *dev, | 178 | static ssize_t lm3642_torch_pin_store(struct device *dev, |
179 | struct device_attribute *attr, | 179 | struct device_attribute *attr, |
180 | const char *buf, size_t size) | 180 | const char *buf, size_t size) |
181 | { | 181 | { |
182 | ssize_t ret; | 182 | ssize_t ret; |
183 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 183 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
184 | struct lm3642_chip_data *chip = | 184 | struct lm3642_chip_data *chip = |
185 | container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); | 185 | container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); |
186 | unsigned int state; | 186 | unsigned int state; |
187 | 187 | ||
188 | ret = kstrtouint(buf, 10, &state); | 188 | ret = kstrtouint(buf, 10, &state); |
189 | if (ret) | 189 | if (ret) |
190 | goto out_strtoint; | 190 | goto out_strtoint; |
191 | if (state != 0) | 191 | if (state != 0) |
192 | state = 0x01 << TORCH_PIN_EN_SHIFT; | 192 | state = 0x01 << TORCH_PIN_EN_SHIFT; |
193 | 193 | ||
194 | chip->torch_pin = state; | 194 | chip->torch_pin = state; |
195 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, | 195 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, |
196 | TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, | 196 | TORCH_PIN_EN_MASK << TORCH_PIN_EN_SHIFT, |
197 | state); | 197 | state); |
198 | if (ret < 0) | 198 | if (ret < 0) |
199 | goto out; | 199 | goto out; |
200 | 200 | ||
201 | return size; | 201 | return size; |
202 | out: | 202 | out: |
203 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | 203 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); |
204 | return ret; | 204 | return ret; |
205 | out_strtoint: | 205 | out_strtoint: |
206 | dev_err(chip->dev, "%s: fail to change str to int\n", __func__); | 206 | dev_err(chip->dev, "%s: fail to change str to int\n", __func__); |
207 | return ret; | 207 | return ret; |
208 | } | 208 | } |
209 | 209 | ||
210 | static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); | 210 | static DEVICE_ATTR(torch_pin, S_IWUSR, NULL, lm3642_torch_pin_store); |
211 | 211 | ||
212 | static void lm3642_deferred_torch_brightness_set(struct work_struct *work) | 212 | static void lm3642_deferred_torch_brightness_set(struct work_struct *work) |
213 | { | 213 | { |
214 | struct lm3642_chip_data *chip = | 214 | struct lm3642_chip_data *chip = |
215 | container_of(work, struct lm3642_chip_data, work_torch); | 215 | container_of(work, struct lm3642_chip_data, work_torch); |
216 | 216 | ||
217 | mutex_lock(&chip->lock); | 217 | mutex_lock(&chip->lock); |
218 | lm3642_control(chip, chip->br_torch, MODES_TORCH); | 218 | lm3642_control(chip, chip->br_torch, MODES_TORCH); |
219 | mutex_unlock(&chip->lock); | 219 | mutex_unlock(&chip->lock); |
220 | } | 220 | } |
221 | 221 | ||
222 | static void lm3642_torch_brightness_set(struct led_classdev *cdev, | 222 | static void lm3642_torch_brightness_set(struct led_classdev *cdev, |
223 | enum led_brightness brightness) | 223 | enum led_brightness brightness) |
224 | { | 224 | { |
225 | struct lm3642_chip_data *chip = | 225 | struct lm3642_chip_data *chip = |
226 | container_of(cdev, struct lm3642_chip_data, cdev_torch); | 226 | container_of(cdev, struct lm3642_chip_data, cdev_torch); |
227 | 227 | ||
228 | chip->br_torch = brightness; | 228 | chip->br_torch = brightness; |
229 | schedule_work(&chip->work_torch); | 229 | schedule_work(&chip->work_torch); |
230 | } | 230 | } |
231 | 231 | ||
232 | /* flash */ | 232 | /* flash */ |
233 | 233 | ||
234 | /* strobe pin config for lm3642*/ | 234 | /* strobe pin config for lm3642*/ |
235 | static ssize_t lm3642_strobe_pin_store(struct device *dev, | 235 | static ssize_t lm3642_strobe_pin_store(struct device *dev, |
236 | struct device_attribute *attr, | 236 | struct device_attribute *attr, |
237 | const char *buf, size_t size) | 237 | const char *buf, size_t size) |
238 | { | 238 | { |
239 | ssize_t ret; | 239 | ssize_t ret; |
240 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 240 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
241 | struct lm3642_chip_data *chip = | 241 | struct lm3642_chip_data *chip = |
242 | container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); | 242 | container_of(led_cdev, struct lm3642_chip_data, cdev_indicator); |
243 | unsigned int state; | 243 | unsigned int state; |
244 | 244 | ||
245 | ret = kstrtouint(buf, 10, &state); | 245 | ret = kstrtouint(buf, 10, &state); |
246 | if (ret) | 246 | if (ret) |
247 | goto out_strtoint; | 247 | goto out_strtoint; |
248 | if (state != 0) | 248 | if (state != 0) |
249 | state = 0x01 << STROBE_PIN_EN_SHIFT; | 249 | state = 0x01 << STROBE_PIN_EN_SHIFT; |
250 | 250 | ||
251 | chip->strobe_pin = state; | 251 | chip->strobe_pin = state; |
252 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, | 252 | ret = regmap_update_bits(chip->regmap, REG_ENABLE, |
253 | STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, | 253 | STROBE_PIN_EN_MASK << STROBE_PIN_EN_SHIFT, |
254 | state); | 254 | state); |
255 | if (ret < 0) | 255 | if (ret < 0) |
256 | goto out; | 256 | goto out; |
257 | 257 | ||
258 | return size; | 258 | return size; |
259 | out: | 259 | out: |
260 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); | 260 | dev_err(chip->dev, "%s:i2c access fail to register\n", __func__); |
261 | return ret; | 261 | return ret; |
262 | out_strtoint: | 262 | out_strtoint: |
263 | dev_err(chip->dev, "%s: fail to change str to int\n", __func__); | 263 | dev_err(chip->dev, "%s: fail to change str to int\n", __func__); |
264 | return ret; | 264 | return ret; |
265 | } | 265 | } |
266 | 266 | ||
267 | static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); | 267 | static DEVICE_ATTR(strobe_pin, S_IWUSR, NULL, lm3642_strobe_pin_store); |
268 | 268 | ||
269 | static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) | 269 | static void lm3642_deferred_strobe_brightness_set(struct work_struct *work) |
270 | { | 270 | { |
271 | struct lm3642_chip_data *chip = | 271 | struct lm3642_chip_data *chip = |
272 | container_of(work, struct lm3642_chip_data, work_flash); | 272 | container_of(work, struct lm3642_chip_data, work_flash); |
273 | 273 | ||
274 | mutex_lock(&chip->lock); | 274 | mutex_lock(&chip->lock); |
275 | lm3642_control(chip, chip->br_flash, MODES_FLASH); | 275 | lm3642_control(chip, chip->br_flash, MODES_FLASH); |
276 | mutex_unlock(&chip->lock); | 276 | mutex_unlock(&chip->lock); |
277 | } | 277 | } |
278 | 278 | ||
279 | static void lm3642_strobe_brightness_set(struct led_classdev *cdev, | 279 | static void lm3642_strobe_brightness_set(struct led_classdev *cdev, |
280 | enum led_brightness brightness) | 280 | enum led_brightness brightness) |
281 | { | 281 | { |
282 | struct lm3642_chip_data *chip = | 282 | struct lm3642_chip_data *chip = |
283 | container_of(cdev, struct lm3642_chip_data, cdev_flash); | 283 | container_of(cdev, struct lm3642_chip_data, cdev_flash); |
284 | 284 | ||
285 | chip->br_flash = brightness; | 285 | chip->br_flash = brightness; |
286 | schedule_work(&chip->work_flash); | 286 | schedule_work(&chip->work_flash); |
287 | } | 287 | } |
288 | 288 | ||
289 | /* indicator */ | 289 | /* indicator */ |
290 | static void lm3642_deferred_indicator_brightness_set(struct work_struct *work) | 290 | static void lm3642_deferred_indicator_brightness_set(struct work_struct *work) |
291 | { | 291 | { |
292 | struct lm3642_chip_data *chip = | 292 | struct lm3642_chip_data *chip = |
293 | container_of(work, struct lm3642_chip_data, work_indicator); | 293 | container_of(work, struct lm3642_chip_data, work_indicator); |
294 | 294 | ||
295 | mutex_lock(&chip->lock); | 295 | mutex_lock(&chip->lock); |
296 | lm3642_control(chip, chip->br_indicator, MODES_INDIC); | 296 | lm3642_control(chip, chip->br_indicator, MODES_INDIC); |
297 | mutex_unlock(&chip->lock); | 297 | mutex_unlock(&chip->lock); |
298 | } | 298 | } |
299 | 299 | ||
300 | static void lm3642_indicator_brightness_set(struct led_classdev *cdev, | 300 | static void lm3642_indicator_brightness_set(struct led_classdev *cdev, |
301 | enum led_brightness brightness) | 301 | enum led_brightness brightness) |
302 | { | 302 | { |
303 | struct lm3642_chip_data *chip = | 303 | struct lm3642_chip_data *chip = |
304 | container_of(cdev, struct lm3642_chip_data, cdev_indicator); | 304 | container_of(cdev, struct lm3642_chip_data, cdev_indicator); |
305 | 305 | ||
306 | chip->br_indicator = brightness; | 306 | chip->br_indicator = brightness; |
307 | schedule_work(&chip->work_indicator); | 307 | schedule_work(&chip->work_indicator); |
308 | } | 308 | } |
309 | 309 | ||
310 | static const struct regmap_config lm3642_regmap = { | 310 | static const struct regmap_config lm3642_regmap = { |
311 | .reg_bits = 8, | 311 | .reg_bits = 8, |
312 | .val_bits = 8, | 312 | .val_bits = 8, |
313 | .max_register = REG_MAX, | 313 | .max_register = REG_MAX, |
314 | }; | 314 | }; |
315 | 315 | ||
316 | static int lm3642_probe(struct i2c_client *client, | 316 | static int lm3642_probe(struct i2c_client *client, |
317 | const struct i2c_device_id *id) | 317 | const struct i2c_device_id *id) |
318 | { | 318 | { |
319 | struct lm3642_platform_data *pdata = client->dev.platform_data; | 319 | struct lm3642_platform_data *pdata = dev_get_platdata(&client->dev); |
320 | struct lm3642_chip_data *chip; | 320 | struct lm3642_chip_data *chip; |
321 | 321 | ||
322 | int err; | 322 | int err; |
323 | 323 | ||
324 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | 324 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
325 | dev_err(&client->dev, "i2c functionality check fail.\n"); | 325 | dev_err(&client->dev, "i2c functionality check fail.\n"); |
326 | return -EOPNOTSUPP; | 326 | return -EOPNOTSUPP; |
327 | } | 327 | } |
328 | 328 | ||
329 | if (pdata == NULL) { | 329 | if (pdata == NULL) { |
330 | dev_err(&client->dev, "needs Platform Data.\n"); | 330 | dev_err(&client->dev, "needs Platform Data.\n"); |
331 | return -ENODATA; | 331 | return -ENODATA; |
332 | } | 332 | } |
333 | 333 | ||
334 | chip = devm_kzalloc(&client->dev, | 334 | chip = devm_kzalloc(&client->dev, |
335 | sizeof(struct lm3642_chip_data), GFP_KERNEL); | 335 | sizeof(struct lm3642_chip_data), GFP_KERNEL); |
336 | if (!chip) | 336 | if (!chip) |
337 | return -ENOMEM; | 337 | return -ENOMEM; |
338 | 338 | ||
339 | chip->dev = &client->dev; | 339 | chip->dev = &client->dev; |
340 | chip->pdata = pdata; | 340 | chip->pdata = pdata; |
341 | 341 | ||
342 | chip->tx_pin = pdata->tx_pin; | 342 | chip->tx_pin = pdata->tx_pin; |
343 | chip->torch_pin = pdata->torch_pin; | 343 | chip->torch_pin = pdata->torch_pin; |
344 | chip->strobe_pin = pdata->strobe_pin; | 344 | chip->strobe_pin = pdata->strobe_pin; |
345 | 345 | ||
346 | chip->regmap = devm_regmap_init_i2c(client, &lm3642_regmap); | 346 | chip->regmap = devm_regmap_init_i2c(client, &lm3642_regmap); |
347 | if (IS_ERR(chip->regmap)) { | 347 | if (IS_ERR(chip->regmap)) { |
348 | err = PTR_ERR(chip->regmap); | 348 | err = PTR_ERR(chip->regmap); |
349 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | 349 | dev_err(&client->dev, "Failed to allocate register map: %d\n", |
350 | err); | 350 | err); |
351 | return err; | 351 | return err; |
352 | } | 352 | } |
353 | 353 | ||
354 | mutex_init(&chip->lock); | 354 | mutex_init(&chip->lock); |
355 | i2c_set_clientdata(client, chip); | 355 | i2c_set_clientdata(client, chip); |
356 | 356 | ||
357 | err = lm3642_chip_init(chip); | 357 | err = lm3642_chip_init(chip); |
358 | if (err < 0) | 358 | if (err < 0) |
359 | goto err_out; | 359 | goto err_out; |
360 | 360 | ||
361 | /* flash */ | 361 | /* flash */ |
362 | INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set); | 362 | INIT_WORK(&chip->work_flash, lm3642_deferred_strobe_brightness_set); |
363 | chip->cdev_flash.name = "flash"; | 363 | chip->cdev_flash.name = "flash"; |
364 | chip->cdev_flash.max_brightness = 16; | 364 | chip->cdev_flash.max_brightness = 16; |
365 | chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; | 365 | chip->cdev_flash.brightness_set = lm3642_strobe_brightness_set; |
366 | chip->cdev_flash.default_trigger = "flash"; | 366 | chip->cdev_flash.default_trigger = "flash"; |
367 | err = led_classdev_register((struct device *) | 367 | err = led_classdev_register((struct device *) |
368 | &client->dev, &chip->cdev_flash); | 368 | &client->dev, &chip->cdev_flash); |
369 | if (err < 0) { | 369 | if (err < 0) { |
370 | dev_err(chip->dev, "failed to register flash\n"); | 370 | dev_err(chip->dev, "failed to register flash\n"); |
371 | goto err_out; | 371 | goto err_out; |
372 | } | 372 | } |
373 | err = device_create_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); | 373 | err = device_create_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); |
374 | if (err < 0) { | 374 | if (err < 0) { |
375 | dev_err(chip->dev, "failed to create strobe-pin file\n"); | 375 | dev_err(chip->dev, "failed to create strobe-pin file\n"); |
376 | goto err_create_flash_pin_file; | 376 | goto err_create_flash_pin_file; |
377 | } | 377 | } |
378 | 378 | ||
379 | /* torch */ | 379 | /* torch */ |
380 | INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set); | 380 | INIT_WORK(&chip->work_torch, lm3642_deferred_torch_brightness_set); |
381 | chip->cdev_torch.name = "torch"; | 381 | chip->cdev_torch.name = "torch"; |
382 | chip->cdev_torch.max_brightness = 8; | 382 | chip->cdev_torch.max_brightness = 8; |
383 | chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; | 383 | chip->cdev_torch.brightness_set = lm3642_torch_brightness_set; |
384 | chip->cdev_torch.default_trigger = "torch"; | 384 | chip->cdev_torch.default_trigger = "torch"; |
385 | err = led_classdev_register((struct device *) | 385 | err = led_classdev_register((struct device *) |
386 | &client->dev, &chip->cdev_torch); | 386 | &client->dev, &chip->cdev_torch); |
387 | if (err < 0) { | 387 | if (err < 0) { |
388 | dev_err(chip->dev, "failed to register torch\n"); | 388 | dev_err(chip->dev, "failed to register torch\n"); |
389 | goto err_create_torch_file; | 389 | goto err_create_torch_file; |
390 | } | 390 | } |
391 | err = device_create_file(chip->cdev_torch.dev, &dev_attr_torch_pin); | 391 | err = device_create_file(chip->cdev_torch.dev, &dev_attr_torch_pin); |
392 | if (err < 0) { | 392 | if (err < 0) { |
393 | dev_err(chip->dev, "failed to create torch-pin file\n"); | 393 | dev_err(chip->dev, "failed to create torch-pin file\n"); |
394 | goto err_create_torch_pin_file; | 394 | goto err_create_torch_pin_file; |
395 | } | 395 | } |
396 | 396 | ||
397 | /* indicator */ | 397 | /* indicator */ |
398 | INIT_WORK(&chip->work_indicator, | 398 | INIT_WORK(&chip->work_indicator, |
399 | lm3642_deferred_indicator_brightness_set); | 399 | lm3642_deferred_indicator_brightness_set); |
400 | chip->cdev_indicator.name = "indicator"; | 400 | chip->cdev_indicator.name = "indicator"; |
401 | chip->cdev_indicator.max_brightness = 8; | 401 | chip->cdev_indicator.max_brightness = 8; |
402 | chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set; | 402 | chip->cdev_indicator.brightness_set = lm3642_indicator_brightness_set; |
403 | err = led_classdev_register((struct device *) | 403 | err = led_classdev_register((struct device *) |
404 | &client->dev, &chip->cdev_indicator); | 404 | &client->dev, &chip->cdev_indicator); |
405 | if (err < 0) { | 405 | if (err < 0) { |
406 | dev_err(chip->dev, "failed to register indicator\n"); | 406 | dev_err(chip->dev, "failed to register indicator\n"); |
407 | goto err_create_indicator_file; | 407 | goto err_create_indicator_file; |
408 | } | 408 | } |
409 | 409 | ||
410 | dev_info(&client->dev, "LM3642 is initialized\n"); | 410 | dev_info(&client->dev, "LM3642 is initialized\n"); |
411 | return 0; | 411 | return 0; |
412 | 412 | ||
413 | err_create_indicator_file: | 413 | err_create_indicator_file: |
414 | device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); | 414 | device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); |
415 | err_create_torch_pin_file: | 415 | err_create_torch_pin_file: |
416 | led_classdev_unregister(&chip->cdev_torch); | 416 | led_classdev_unregister(&chip->cdev_torch); |
417 | err_create_torch_file: | 417 | err_create_torch_file: |
418 | device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); | 418 | device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); |
419 | err_create_flash_pin_file: | 419 | err_create_flash_pin_file: |
420 | led_classdev_unregister(&chip->cdev_flash); | 420 | led_classdev_unregister(&chip->cdev_flash); |
421 | err_out: | 421 | err_out: |
422 | return err; | 422 | return err; |
423 | } | 423 | } |
424 | 424 | ||
425 | static int lm3642_remove(struct i2c_client *client) | 425 | static int lm3642_remove(struct i2c_client *client) |
426 | { | 426 | { |
427 | struct lm3642_chip_data *chip = i2c_get_clientdata(client); | 427 | struct lm3642_chip_data *chip = i2c_get_clientdata(client); |
428 | 428 | ||
429 | led_classdev_unregister(&chip->cdev_indicator); | 429 | led_classdev_unregister(&chip->cdev_indicator); |
430 | flush_work(&chip->work_indicator); | 430 | flush_work(&chip->work_indicator); |
431 | device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); | 431 | device_remove_file(chip->cdev_torch.dev, &dev_attr_torch_pin); |
432 | led_classdev_unregister(&chip->cdev_torch); | 432 | led_classdev_unregister(&chip->cdev_torch); |
433 | flush_work(&chip->work_torch); | 433 | flush_work(&chip->work_torch); |
434 | device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); | 434 | device_remove_file(chip->cdev_flash.dev, &dev_attr_strobe_pin); |
435 | led_classdev_unregister(&chip->cdev_flash); | 435 | led_classdev_unregister(&chip->cdev_flash); |
436 | flush_work(&chip->work_flash); | 436 | flush_work(&chip->work_flash); |
437 | regmap_write(chip->regmap, REG_ENABLE, 0); | 437 | regmap_write(chip->regmap, REG_ENABLE, 0); |
438 | return 0; | 438 | return 0; |
439 | } | 439 | } |
440 | 440 | ||
441 | static const struct i2c_device_id lm3642_id[] = { | 441 | static const struct i2c_device_id lm3642_id[] = { |
442 | {LM3642_NAME, 0}, | 442 | {LM3642_NAME, 0}, |
443 | {} | 443 | {} |
444 | }; | 444 | }; |
445 | 445 | ||
446 | MODULE_DEVICE_TABLE(i2c, lm3642_id); | 446 | MODULE_DEVICE_TABLE(i2c, lm3642_id); |
447 | 447 | ||
448 | static struct i2c_driver lm3642_i2c_driver = { | 448 | static struct i2c_driver lm3642_i2c_driver = { |
449 | .driver = { | 449 | .driver = { |
450 | .name = LM3642_NAME, | 450 | .name = LM3642_NAME, |
451 | .owner = THIS_MODULE, | 451 | .owner = THIS_MODULE, |
452 | .pm = NULL, | 452 | .pm = NULL, |
453 | }, | 453 | }, |
454 | .probe = lm3642_probe, | 454 | .probe = lm3642_probe, |
455 | .remove = lm3642_remove, | 455 | .remove = lm3642_remove, |
456 | .id_table = lm3642_id, | 456 | .id_table = lm3642_id, |
457 | }; | 457 | }; |
458 | 458 | ||
459 | module_i2c_driver(lm3642_i2c_driver); | 459 | module_i2c_driver(lm3642_i2c_driver); |
460 | 460 | ||
461 | MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3642"); | 461 | MODULE_DESCRIPTION("Texas Instruments Flash Lighting driver for LM3642"); |
462 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); | 462 | MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); |
463 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); | 463 | MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); |
464 | MODULE_LICENSE("GPL v2"); | 464 | MODULE_LICENSE("GPL v2"); |
465 | 465 |
drivers/leds/leds-lp3944.c
1 | /* | 1 | /* |
2 | * leds-lp3944.c - driver for National Semiconductor LP3944 Funlight Chip | 2 | * leds-lp3944.c - driver for National Semiconductor LP3944 Funlight Chip |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | 4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | 11 | ||
12 | /* | 12 | /* |
13 | * I2C driver for National Semiconductor LP3944 Funlight Chip | 13 | * I2C driver for National Semiconductor LP3944 Funlight Chip |
14 | * http://www.national.com/pf/LP/LP3944.html | 14 | * http://www.national.com/pf/LP/LP3944.html |
15 | * | 15 | * |
16 | * This helper chip can drive up to 8 leds, with two programmable DIM modes; | 16 | * This helper chip can drive up to 8 leds, with two programmable DIM modes; |
17 | * it could even be used as a gpio expander but this driver assumes it is used | 17 | * it could even be used as a gpio expander but this driver assumes it is used |
18 | * as a led controller. | 18 | * as a led controller. |
19 | * | 19 | * |
20 | * The DIM modes are used to set _blink_ patterns for leds, the pattern is | 20 | * The DIM modes are used to set _blink_ patterns for leds, the pattern is |
21 | * specified supplying two parameters: | 21 | * specified supplying two parameters: |
22 | * - period: from 0s to 1.6s | 22 | * - period: from 0s to 1.6s |
23 | * - duty cycle: percentage of the period the led is on, from 0 to 100 | 23 | * - duty cycle: percentage of the period the led is on, from 0 to 100 |
24 | * | 24 | * |
25 | * LP3944 can be found on Motorola A910 smartphone, where it drives the rgb | 25 | * LP3944 can be found on Motorola A910 smartphone, where it drives the rgb |
26 | * leds, the camera flash light and the displays backlights. | 26 | * leds, the camera flash light and the displays backlights. |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #include <linux/module.h> | 29 | #include <linux/module.h> |
30 | #include <linux/i2c.h> | 30 | #include <linux/i2c.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/leds.h> | 32 | #include <linux/leds.h> |
33 | #include <linux/mutex.h> | 33 | #include <linux/mutex.h> |
34 | #include <linux/workqueue.h> | 34 | #include <linux/workqueue.h> |
35 | #include <linux/leds-lp3944.h> | 35 | #include <linux/leds-lp3944.h> |
36 | 36 | ||
37 | /* Read Only Registers */ | 37 | /* Read Only Registers */ |
38 | #define LP3944_REG_INPUT1 0x00 /* LEDs 0-7 InputRegister (Read Only) */ | 38 | #define LP3944_REG_INPUT1 0x00 /* LEDs 0-7 InputRegister (Read Only) */ |
39 | #define LP3944_REG_REGISTER1 0x01 /* None (Read Only) */ | 39 | #define LP3944_REG_REGISTER1 0x01 /* None (Read Only) */ |
40 | 40 | ||
41 | #define LP3944_REG_PSC0 0x02 /* Frequency Prescaler 0 (R/W) */ | 41 | #define LP3944_REG_PSC0 0x02 /* Frequency Prescaler 0 (R/W) */ |
42 | #define LP3944_REG_PWM0 0x03 /* PWM Register 0 (R/W) */ | 42 | #define LP3944_REG_PWM0 0x03 /* PWM Register 0 (R/W) */ |
43 | #define LP3944_REG_PSC1 0x04 /* Frequency Prescaler 1 (R/W) */ | 43 | #define LP3944_REG_PSC1 0x04 /* Frequency Prescaler 1 (R/W) */ |
44 | #define LP3944_REG_PWM1 0x05 /* PWM Register 1 (R/W) */ | 44 | #define LP3944_REG_PWM1 0x05 /* PWM Register 1 (R/W) */ |
45 | #define LP3944_REG_LS0 0x06 /* LEDs 0-3 Selector (R/W) */ | 45 | #define LP3944_REG_LS0 0x06 /* LEDs 0-3 Selector (R/W) */ |
46 | #define LP3944_REG_LS1 0x07 /* LEDs 4-7 Selector (R/W) */ | 46 | #define LP3944_REG_LS1 0x07 /* LEDs 4-7 Selector (R/W) */ |
47 | 47 | ||
48 | /* These registers are not used to control leds in LP3944, they can store | 48 | /* These registers are not used to control leds in LP3944, they can store |
49 | * arbitrary values which the chip will ignore. | 49 | * arbitrary values which the chip will ignore. |
50 | */ | 50 | */ |
51 | #define LP3944_REG_REGISTER8 0x08 | 51 | #define LP3944_REG_REGISTER8 0x08 |
52 | #define LP3944_REG_REGISTER9 0x09 | 52 | #define LP3944_REG_REGISTER9 0x09 |
53 | 53 | ||
54 | #define LP3944_DIM0 0 | 54 | #define LP3944_DIM0 0 |
55 | #define LP3944_DIM1 1 | 55 | #define LP3944_DIM1 1 |
56 | 56 | ||
57 | /* period in ms */ | 57 | /* period in ms */ |
58 | #define LP3944_PERIOD_MIN 0 | 58 | #define LP3944_PERIOD_MIN 0 |
59 | #define LP3944_PERIOD_MAX 1600 | 59 | #define LP3944_PERIOD_MAX 1600 |
60 | 60 | ||
61 | /* duty cycle is a percentage */ | 61 | /* duty cycle is a percentage */ |
62 | #define LP3944_DUTY_CYCLE_MIN 0 | 62 | #define LP3944_DUTY_CYCLE_MIN 0 |
63 | #define LP3944_DUTY_CYCLE_MAX 100 | 63 | #define LP3944_DUTY_CYCLE_MAX 100 |
64 | 64 | ||
65 | #define ldev_to_led(c) container_of(c, struct lp3944_led_data, ldev) | 65 | #define ldev_to_led(c) container_of(c, struct lp3944_led_data, ldev) |
66 | 66 | ||
67 | /* Saved data */ | 67 | /* Saved data */ |
68 | struct lp3944_led_data { | 68 | struct lp3944_led_data { |
69 | u8 id; | 69 | u8 id; |
70 | enum lp3944_type type; | 70 | enum lp3944_type type; |
71 | enum lp3944_status status; | 71 | enum lp3944_status status; |
72 | struct led_classdev ldev; | 72 | struct led_classdev ldev; |
73 | struct i2c_client *client; | 73 | struct i2c_client *client; |
74 | struct work_struct work; | 74 | struct work_struct work; |
75 | }; | 75 | }; |
76 | 76 | ||
77 | struct lp3944_data { | 77 | struct lp3944_data { |
78 | struct mutex lock; | 78 | struct mutex lock; |
79 | struct i2c_client *client; | 79 | struct i2c_client *client; |
80 | struct lp3944_led_data leds[LP3944_LEDS_MAX]; | 80 | struct lp3944_led_data leds[LP3944_LEDS_MAX]; |
81 | }; | 81 | }; |
82 | 82 | ||
83 | static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value) | 83 | static int lp3944_reg_read(struct i2c_client *client, u8 reg, u8 *value) |
84 | { | 84 | { |
85 | int tmp; | 85 | int tmp; |
86 | 86 | ||
87 | tmp = i2c_smbus_read_byte_data(client, reg); | 87 | tmp = i2c_smbus_read_byte_data(client, reg); |
88 | if (tmp < 0) | 88 | if (tmp < 0) |
89 | return tmp; | 89 | return tmp; |
90 | 90 | ||
91 | *value = tmp; | 91 | *value = tmp; |
92 | 92 | ||
93 | return 0; | 93 | return 0; |
94 | } | 94 | } |
95 | 95 | ||
96 | static int lp3944_reg_write(struct i2c_client *client, u8 reg, u8 value) | 96 | static int lp3944_reg_write(struct i2c_client *client, u8 reg, u8 value) |
97 | { | 97 | { |
98 | return i2c_smbus_write_byte_data(client, reg, value); | 98 | return i2c_smbus_write_byte_data(client, reg, value); |
99 | } | 99 | } |
100 | 100 | ||
101 | /** | 101 | /** |
102 | * Set the period for DIM status | 102 | * Set the period for DIM status |
103 | * | 103 | * |
104 | * @client: the i2c client | 104 | * @client: the i2c client |
105 | * @dim: either LP3944_DIM0 or LP3944_DIM1 | 105 | * @dim: either LP3944_DIM0 or LP3944_DIM1 |
106 | * @period: period of a blink, that is a on/off cycle, expressed in ms. | 106 | * @period: period of a blink, that is a on/off cycle, expressed in ms. |
107 | */ | 107 | */ |
108 | static int lp3944_dim_set_period(struct i2c_client *client, u8 dim, u16 period) | 108 | static int lp3944_dim_set_period(struct i2c_client *client, u8 dim, u16 period) |
109 | { | 109 | { |
110 | u8 psc_reg; | 110 | u8 psc_reg; |
111 | u8 psc_value; | 111 | u8 psc_value; |
112 | int err; | 112 | int err; |
113 | 113 | ||
114 | if (dim == LP3944_DIM0) | 114 | if (dim == LP3944_DIM0) |
115 | psc_reg = LP3944_REG_PSC0; | 115 | psc_reg = LP3944_REG_PSC0; |
116 | else if (dim == LP3944_DIM1) | 116 | else if (dim == LP3944_DIM1) |
117 | psc_reg = LP3944_REG_PSC1; | 117 | psc_reg = LP3944_REG_PSC1; |
118 | else | 118 | else |
119 | return -EINVAL; | 119 | return -EINVAL; |
120 | 120 | ||
121 | /* Convert period to Prescaler value */ | 121 | /* Convert period to Prescaler value */ |
122 | if (period > LP3944_PERIOD_MAX) | 122 | if (period > LP3944_PERIOD_MAX) |
123 | return -EINVAL; | 123 | return -EINVAL; |
124 | 124 | ||
125 | psc_value = (period * 255) / LP3944_PERIOD_MAX; | 125 | psc_value = (period * 255) / LP3944_PERIOD_MAX; |
126 | 126 | ||
127 | err = lp3944_reg_write(client, psc_reg, psc_value); | 127 | err = lp3944_reg_write(client, psc_reg, psc_value); |
128 | 128 | ||
129 | return err; | 129 | return err; |
130 | } | 130 | } |
131 | 131 | ||
132 | /** | 132 | /** |
133 | * Set the duty cycle for DIM status | 133 | * Set the duty cycle for DIM status |
134 | * | 134 | * |
135 | * @client: the i2c client | 135 | * @client: the i2c client |
136 | * @dim: either LP3944_DIM0 or LP3944_DIM1 | 136 | * @dim: either LP3944_DIM0 or LP3944_DIM1 |
137 | * @duty_cycle: percentage of a period during which a led is ON | 137 | * @duty_cycle: percentage of a period during which a led is ON |
138 | */ | 138 | */ |
139 | static int lp3944_dim_set_dutycycle(struct i2c_client *client, u8 dim, | 139 | static int lp3944_dim_set_dutycycle(struct i2c_client *client, u8 dim, |
140 | u8 duty_cycle) | 140 | u8 duty_cycle) |
141 | { | 141 | { |
142 | u8 pwm_reg; | 142 | u8 pwm_reg; |
143 | u8 pwm_value; | 143 | u8 pwm_value; |
144 | int err; | 144 | int err; |
145 | 145 | ||
146 | if (dim == LP3944_DIM0) | 146 | if (dim == LP3944_DIM0) |
147 | pwm_reg = LP3944_REG_PWM0; | 147 | pwm_reg = LP3944_REG_PWM0; |
148 | else if (dim == LP3944_DIM1) | 148 | else if (dim == LP3944_DIM1) |
149 | pwm_reg = LP3944_REG_PWM1; | 149 | pwm_reg = LP3944_REG_PWM1; |
150 | else | 150 | else |
151 | return -EINVAL; | 151 | return -EINVAL; |
152 | 152 | ||
153 | /* Convert duty cycle to PWM value */ | 153 | /* Convert duty cycle to PWM value */ |
154 | if (duty_cycle > LP3944_DUTY_CYCLE_MAX) | 154 | if (duty_cycle > LP3944_DUTY_CYCLE_MAX) |
155 | return -EINVAL; | 155 | return -EINVAL; |
156 | 156 | ||
157 | pwm_value = (duty_cycle * 255) / LP3944_DUTY_CYCLE_MAX; | 157 | pwm_value = (duty_cycle * 255) / LP3944_DUTY_CYCLE_MAX; |
158 | 158 | ||
159 | err = lp3944_reg_write(client, pwm_reg, pwm_value); | 159 | err = lp3944_reg_write(client, pwm_reg, pwm_value); |
160 | 160 | ||
161 | return err; | 161 | return err; |
162 | } | 162 | } |
163 | 163 | ||
164 | /** | 164 | /** |
165 | * Set the led status | 165 | * Set the led status |
166 | * | 166 | * |
167 | * @led: a lp3944_led_data structure | 167 | * @led: a lp3944_led_data structure |
168 | * @status: one of LP3944_LED_STATUS_OFF | 168 | * @status: one of LP3944_LED_STATUS_OFF |
169 | * LP3944_LED_STATUS_ON | 169 | * LP3944_LED_STATUS_ON |
170 | * LP3944_LED_STATUS_DIM0 | 170 | * LP3944_LED_STATUS_DIM0 |
171 | * LP3944_LED_STATUS_DIM1 | 171 | * LP3944_LED_STATUS_DIM1 |
172 | */ | 172 | */ |
173 | static int lp3944_led_set(struct lp3944_led_data *led, u8 status) | 173 | static int lp3944_led_set(struct lp3944_led_data *led, u8 status) |
174 | { | 174 | { |
175 | struct lp3944_data *data = i2c_get_clientdata(led->client); | 175 | struct lp3944_data *data = i2c_get_clientdata(led->client); |
176 | u8 id = led->id; | 176 | u8 id = led->id; |
177 | u8 reg; | 177 | u8 reg; |
178 | u8 val = 0; | 178 | u8 val = 0; |
179 | int err; | 179 | int err; |
180 | 180 | ||
181 | dev_dbg(&led->client->dev, "%s: %s, status before normalization:%d\n", | 181 | dev_dbg(&led->client->dev, "%s: %s, status before normalization:%d\n", |
182 | __func__, led->ldev.name, status); | 182 | __func__, led->ldev.name, status); |
183 | 183 | ||
184 | switch (id) { | 184 | switch (id) { |
185 | case LP3944_LED0: | 185 | case LP3944_LED0: |
186 | case LP3944_LED1: | 186 | case LP3944_LED1: |
187 | case LP3944_LED2: | 187 | case LP3944_LED2: |
188 | case LP3944_LED3: | 188 | case LP3944_LED3: |
189 | reg = LP3944_REG_LS0; | 189 | reg = LP3944_REG_LS0; |
190 | break; | 190 | break; |
191 | case LP3944_LED4: | 191 | case LP3944_LED4: |
192 | case LP3944_LED5: | 192 | case LP3944_LED5: |
193 | case LP3944_LED6: | 193 | case LP3944_LED6: |
194 | case LP3944_LED7: | 194 | case LP3944_LED7: |
195 | id -= LP3944_LED4; | 195 | id -= LP3944_LED4; |
196 | reg = LP3944_REG_LS1; | 196 | reg = LP3944_REG_LS1; |
197 | break; | 197 | break; |
198 | default: | 198 | default: |
199 | return -EINVAL; | 199 | return -EINVAL; |
200 | } | 200 | } |
201 | 201 | ||
202 | if (status > LP3944_LED_STATUS_DIM1) | 202 | if (status > LP3944_LED_STATUS_DIM1) |
203 | return -EINVAL; | 203 | return -EINVAL; |
204 | 204 | ||
205 | /* invert only 0 and 1, leave unchanged the other values, | 205 | /* invert only 0 and 1, leave unchanged the other values, |
206 | * remember we are abusing status to set blink patterns | 206 | * remember we are abusing status to set blink patterns |
207 | */ | 207 | */ |
208 | if (led->type == LP3944_LED_TYPE_LED_INVERTED && status < 2) | 208 | if (led->type == LP3944_LED_TYPE_LED_INVERTED && status < 2) |
209 | status = 1 - status; | 209 | status = 1 - status; |
210 | 210 | ||
211 | mutex_lock(&data->lock); | 211 | mutex_lock(&data->lock); |
212 | lp3944_reg_read(led->client, reg, &val); | 212 | lp3944_reg_read(led->client, reg, &val); |
213 | 213 | ||
214 | val &= ~(LP3944_LED_STATUS_MASK << (id << 1)); | 214 | val &= ~(LP3944_LED_STATUS_MASK << (id << 1)); |
215 | val |= (status << (id << 1)); | 215 | val |= (status << (id << 1)); |
216 | 216 | ||
217 | dev_dbg(&led->client->dev, "%s: %s, reg:%d id:%d status:%d val:%#x\n", | 217 | dev_dbg(&led->client->dev, "%s: %s, reg:%d id:%d status:%d val:%#x\n", |
218 | __func__, led->ldev.name, reg, id, status, val); | 218 | __func__, led->ldev.name, reg, id, status, val); |
219 | 219 | ||
220 | /* set led status */ | 220 | /* set led status */ |
221 | err = lp3944_reg_write(led->client, reg, val); | 221 | err = lp3944_reg_write(led->client, reg, val); |
222 | mutex_unlock(&data->lock); | 222 | mutex_unlock(&data->lock); |
223 | 223 | ||
224 | return err; | 224 | return err; |
225 | } | 225 | } |
226 | 226 | ||
227 | static int lp3944_led_set_blink(struct led_classdev *led_cdev, | 227 | static int lp3944_led_set_blink(struct led_classdev *led_cdev, |
228 | unsigned long *delay_on, | 228 | unsigned long *delay_on, |
229 | unsigned long *delay_off) | 229 | unsigned long *delay_off) |
230 | { | 230 | { |
231 | struct lp3944_led_data *led = ldev_to_led(led_cdev); | 231 | struct lp3944_led_data *led = ldev_to_led(led_cdev); |
232 | u16 period; | 232 | u16 period; |
233 | u8 duty_cycle; | 233 | u8 duty_cycle; |
234 | int err; | 234 | int err; |
235 | 235 | ||
236 | /* units are in ms */ | 236 | /* units are in ms */ |
237 | if (*delay_on + *delay_off > LP3944_PERIOD_MAX) | 237 | if (*delay_on + *delay_off > LP3944_PERIOD_MAX) |
238 | return -EINVAL; | 238 | return -EINVAL; |
239 | 239 | ||
240 | if (*delay_on == 0 && *delay_off == 0) { | 240 | if (*delay_on == 0 && *delay_off == 0) { |
241 | /* Special case: the leds subsystem requires a default user | 241 | /* Special case: the leds subsystem requires a default user |
242 | * friendly blink pattern for the LED. Let's blink the led | 242 | * friendly blink pattern for the LED. Let's blink the led |
243 | * slowly (1Hz). | 243 | * slowly (1Hz). |
244 | */ | 244 | */ |
245 | *delay_on = 500; | 245 | *delay_on = 500; |
246 | *delay_off = 500; | 246 | *delay_off = 500; |
247 | } | 247 | } |
248 | 248 | ||
249 | period = (*delay_on) + (*delay_off); | 249 | period = (*delay_on) + (*delay_off); |
250 | 250 | ||
251 | /* duty_cycle is the percentage of period during which the led is ON */ | 251 | /* duty_cycle is the percentage of period during which the led is ON */ |
252 | duty_cycle = 100 * (*delay_on) / period; | 252 | duty_cycle = 100 * (*delay_on) / period; |
253 | 253 | ||
254 | /* invert duty cycle for inverted leds, this has the same effect of | 254 | /* invert duty cycle for inverted leds, this has the same effect of |
255 | * swapping delay_on and delay_off | 255 | * swapping delay_on and delay_off |
256 | */ | 256 | */ |
257 | if (led->type == LP3944_LED_TYPE_LED_INVERTED) | 257 | if (led->type == LP3944_LED_TYPE_LED_INVERTED) |
258 | duty_cycle = 100 - duty_cycle; | 258 | duty_cycle = 100 - duty_cycle; |
259 | 259 | ||
260 | /* NOTE: using always the first DIM mode, this means that all leds | 260 | /* NOTE: using always the first DIM mode, this means that all leds |
261 | * will have the same blinking pattern. | 261 | * will have the same blinking pattern. |
262 | * | 262 | * |
263 | * We could find a way later to have two leds blinking in hardware | 263 | * We could find a way later to have two leds blinking in hardware |
264 | * with different patterns at the same time, falling back to software | 264 | * with different patterns at the same time, falling back to software |
265 | * control for the other ones. | 265 | * control for the other ones. |
266 | */ | 266 | */ |
267 | err = lp3944_dim_set_period(led->client, LP3944_DIM0, period); | 267 | err = lp3944_dim_set_period(led->client, LP3944_DIM0, period); |
268 | if (err) | 268 | if (err) |
269 | return err; | 269 | return err; |
270 | 270 | ||
271 | err = lp3944_dim_set_dutycycle(led->client, LP3944_DIM0, duty_cycle); | 271 | err = lp3944_dim_set_dutycycle(led->client, LP3944_DIM0, duty_cycle); |
272 | if (err) | 272 | if (err) |
273 | return err; | 273 | return err; |
274 | 274 | ||
275 | dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n", | 275 | dev_dbg(&led->client->dev, "%s: OK hardware accelerated blink!\n", |
276 | __func__); | 276 | __func__); |
277 | 277 | ||
278 | led->status = LP3944_LED_STATUS_DIM0; | 278 | led->status = LP3944_LED_STATUS_DIM0; |
279 | schedule_work(&led->work); | 279 | schedule_work(&led->work); |
280 | 280 | ||
281 | return 0; | 281 | return 0; |
282 | } | 282 | } |
283 | 283 | ||
284 | static void lp3944_led_set_brightness(struct led_classdev *led_cdev, | 284 | static void lp3944_led_set_brightness(struct led_classdev *led_cdev, |
285 | enum led_brightness brightness) | 285 | enum led_brightness brightness) |
286 | { | 286 | { |
287 | struct lp3944_led_data *led = ldev_to_led(led_cdev); | 287 | struct lp3944_led_data *led = ldev_to_led(led_cdev); |
288 | 288 | ||
289 | dev_dbg(&led->client->dev, "%s: %s, %d\n", | 289 | dev_dbg(&led->client->dev, "%s: %s, %d\n", |
290 | __func__, led_cdev->name, brightness); | 290 | __func__, led_cdev->name, brightness); |
291 | 291 | ||
292 | led->status = brightness; | 292 | led->status = brightness; |
293 | schedule_work(&led->work); | 293 | schedule_work(&led->work); |
294 | } | 294 | } |
295 | 295 | ||
296 | static void lp3944_led_work(struct work_struct *work) | 296 | static void lp3944_led_work(struct work_struct *work) |
297 | { | 297 | { |
298 | struct lp3944_led_data *led; | 298 | struct lp3944_led_data *led; |
299 | 299 | ||
300 | led = container_of(work, struct lp3944_led_data, work); | 300 | led = container_of(work, struct lp3944_led_data, work); |
301 | lp3944_led_set(led, led->status); | 301 | lp3944_led_set(led, led->status); |
302 | } | 302 | } |
303 | 303 | ||
304 | static int lp3944_configure(struct i2c_client *client, | 304 | static int lp3944_configure(struct i2c_client *client, |
305 | struct lp3944_data *data, | 305 | struct lp3944_data *data, |
306 | struct lp3944_platform_data *pdata) | 306 | struct lp3944_platform_data *pdata) |
307 | { | 307 | { |
308 | int i, err = 0; | 308 | int i, err = 0; |
309 | 309 | ||
310 | for (i = 0; i < pdata->leds_size; i++) { | 310 | for (i = 0; i < pdata->leds_size; i++) { |
311 | struct lp3944_led *pled = &pdata->leds[i]; | 311 | struct lp3944_led *pled = &pdata->leds[i]; |
312 | struct lp3944_led_data *led = &data->leds[i]; | 312 | struct lp3944_led_data *led = &data->leds[i]; |
313 | led->client = client; | 313 | led->client = client; |
314 | led->id = i; | 314 | led->id = i; |
315 | 315 | ||
316 | switch (pled->type) { | 316 | switch (pled->type) { |
317 | 317 | ||
318 | case LP3944_LED_TYPE_LED: | 318 | case LP3944_LED_TYPE_LED: |
319 | case LP3944_LED_TYPE_LED_INVERTED: | 319 | case LP3944_LED_TYPE_LED_INVERTED: |
320 | led->type = pled->type; | 320 | led->type = pled->type; |
321 | led->status = pled->status; | 321 | led->status = pled->status; |
322 | led->ldev.name = pled->name; | 322 | led->ldev.name = pled->name; |
323 | led->ldev.max_brightness = 1; | 323 | led->ldev.max_brightness = 1; |
324 | led->ldev.brightness_set = lp3944_led_set_brightness; | 324 | led->ldev.brightness_set = lp3944_led_set_brightness; |
325 | led->ldev.blink_set = lp3944_led_set_blink; | 325 | led->ldev.blink_set = lp3944_led_set_blink; |
326 | led->ldev.flags = LED_CORE_SUSPENDRESUME; | 326 | led->ldev.flags = LED_CORE_SUSPENDRESUME; |
327 | 327 | ||
328 | INIT_WORK(&led->work, lp3944_led_work); | 328 | INIT_WORK(&led->work, lp3944_led_work); |
329 | err = led_classdev_register(&client->dev, &led->ldev); | 329 | err = led_classdev_register(&client->dev, &led->ldev); |
330 | if (err < 0) { | 330 | if (err < 0) { |
331 | dev_err(&client->dev, | 331 | dev_err(&client->dev, |
332 | "couldn't register LED %s\n", | 332 | "couldn't register LED %s\n", |
333 | led->ldev.name); | 333 | led->ldev.name); |
334 | goto exit; | 334 | goto exit; |
335 | } | 335 | } |
336 | 336 | ||
337 | /* to expose the default value to userspace */ | 337 | /* to expose the default value to userspace */ |
338 | led->ldev.brightness = led->status; | 338 | led->ldev.brightness = led->status; |
339 | 339 | ||
340 | /* Set the default led status */ | 340 | /* Set the default led status */ |
341 | err = lp3944_led_set(led, led->status); | 341 | err = lp3944_led_set(led, led->status); |
342 | if (err < 0) { | 342 | if (err < 0) { |
343 | dev_err(&client->dev, | 343 | dev_err(&client->dev, |
344 | "%s couldn't set STATUS %d\n", | 344 | "%s couldn't set STATUS %d\n", |
345 | led->ldev.name, led->status); | 345 | led->ldev.name, led->status); |
346 | goto exit; | 346 | goto exit; |
347 | } | 347 | } |
348 | break; | 348 | break; |
349 | 349 | ||
350 | case LP3944_LED_TYPE_NONE: | 350 | case LP3944_LED_TYPE_NONE: |
351 | default: | 351 | default: |
352 | break; | 352 | break; |
353 | 353 | ||
354 | } | 354 | } |
355 | } | 355 | } |
356 | return 0; | 356 | return 0; |
357 | 357 | ||
358 | exit: | 358 | exit: |
359 | if (i > 0) | 359 | if (i > 0) |
360 | for (i = i - 1; i >= 0; i--) | 360 | for (i = i - 1; i >= 0; i--) |
361 | switch (pdata->leds[i].type) { | 361 | switch (pdata->leds[i].type) { |
362 | 362 | ||
363 | case LP3944_LED_TYPE_LED: | 363 | case LP3944_LED_TYPE_LED: |
364 | case LP3944_LED_TYPE_LED_INVERTED: | 364 | case LP3944_LED_TYPE_LED_INVERTED: |
365 | led_classdev_unregister(&data->leds[i].ldev); | 365 | led_classdev_unregister(&data->leds[i].ldev); |
366 | cancel_work_sync(&data->leds[i].work); | 366 | cancel_work_sync(&data->leds[i].work); |
367 | break; | 367 | break; |
368 | 368 | ||
369 | case LP3944_LED_TYPE_NONE: | 369 | case LP3944_LED_TYPE_NONE: |
370 | default: | 370 | default: |
371 | break; | 371 | break; |
372 | } | 372 | } |
373 | 373 | ||
374 | return err; | 374 | return err; |
375 | } | 375 | } |
376 | 376 | ||
377 | static int lp3944_probe(struct i2c_client *client, | 377 | static int lp3944_probe(struct i2c_client *client, |
378 | const struct i2c_device_id *id) | 378 | const struct i2c_device_id *id) |
379 | { | 379 | { |
380 | struct lp3944_platform_data *lp3944_pdata = client->dev.platform_data; | 380 | struct lp3944_platform_data *lp3944_pdata = |
381 | dev_get_platdata(&client->dev); | ||
381 | struct lp3944_data *data; | 382 | struct lp3944_data *data; |
382 | int err; | 383 | int err; |
383 | 384 | ||
384 | if (lp3944_pdata == NULL) { | 385 | if (lp3944_pdata == NULL) { |
385 | dev_err(&client->dev, "no platform data\n"); | 386 | dev_err(&client->dev, "no platform data\n"); |
386 | return -EINVAL; | 387 | return -EINVAL; |
387 | } | 388 | } |
388 | 389 | ||
389 | /* Let's see whether this adapter can support what we need. */ | 390 | /* Let's see whether this adapter can support what we need. */ |
390 | if (!i2c_check_functionality(client->adapter, | 391 | if (!i2c_check_functionality(client->adapter, |
391 | I2C_FUNC_SMBUS_BYTE_DATA)) { | 392 | I2C_FUNC_SMBUS_BYTE_DATA)) { |
392 | dev_err(&client->dev, "insufficient functionality!\n"); | 393 | dev_err(&client->dev, "insufficient functionality!\n"); |
393 | return -ENODEV; | 394 | return -ENODEV; |
394 | } | 395 | } |
395 | 396 | ||
396 | data = devm_kzalloc(&client->dev, sizeof(struct lp3944_data), | 397 | data = devm_kzalloc(&client->dev, sizeof(struct lp3944_data), |
397 | GFP_KERNEL); | 398 | GFP_KERNEL); |
398 | if (!data) | 399 | if (!data) |
399 | return -ENOMEM; | 400 | return -ENOMEM; |
400 | 401 | ||
401 | data->client = client; | 402 | data->client = client; |
402 | i2c_set_clientdata(client, data); | 403 | i2c_set_clientdata(client, data); |
403 | 404 | ||
404 | mutex_init(&data->lock); | 405 | mutex_init(&data->lock); |
405 | 406 | ||
406 | err = lp3944_configure(client, data, lp3944_pdata); | 407 | err = lp3944_configure(client, data, lp3944_pdata); |
407 | if (err < 0) | 408 | if (err < 0) |
408 | return err; | 409 | return err; |
409 | 410 | ||
410 | dev_info(&client->dev, "lp3944 enabled\n"); | 411 | dev_info(&client->dev, "lp3944 enabled\n"); |
411 | return 0; | 412 | return 0; |
412 | } | 413 | } |
413 | 414 | ||
414 | static int lp3944_remove(struct i2c_client *client) | 415 | static int lp3944_remove(struct i2c_client *client) |
415 | { | 416 | { |
416 | struct lp3944_platform_data *pdata = client->dev.platform_data; | 417 | struct lp3944_platform_data *pdata = dev_get_platdata(&client->dev); |
417 | struct lp3944_data *data = i2c_get_clientdata(client); | 418 | struct lp3944_data *data = i2c_get_clientdata(client); |
418 | int i; | 419 | int i; |
419 | 420 | ||
420 | for (i = 0; i < pdata->leds_size; i++) | 421 | for (i = 0; i < pdata->leds_size; i++) |
421 | switch (data->leds[i].type) { | 422 | switch (data->leds[i].type) { |
422 | case LP3944_LED_TYPE_LED: | 423 | case LP3944_LED_TYPE_LED: |
423 | case LP3944_LED_TYPE_LED_INVERTED: | 424 | case LP3944_LED_TYPE_LED_INVERTED: |
424 | led_classdev_unregister(&data->leds[i].ldev); | 425 | led_classdev_unregister(&data->leds[i].ldev); |
425 | cancel_work_sync(&data->leds[i].work); | 426 | cancel_work_sync(&data->leds[i].work); |
426 | break; | 427 | break; |
427 | 428 | ||
428 | case LP3944_LED_TYPE_NONE: | 429 | case LP3944_LED_TYPE_NONE: |
429 | default: | 430 | default: |
430 | break; | 431 | break; |
431 | } | 432 | } |
432 | 433 | ||
433 | return 0; | 434 | return 0; |
434 | } | 435 | } |
435 | 436 | ||
436 | /* lp3944 i2c driver struct */ | 437 | /* lp3944 i2c driver struct */ |
437 | static const struct i2c_device_id lp3944_id[] = { | 438 | static const struct i2c_device_id lp3944_id[] = { |
438 | {"lp3944", 0}, | 439 | {"lp3944", 0}, |
439 | {} | 440 | {} |
440 | }; | 441 | }; |
441 | 442 | ||
442 | MODULE_DEVICE_TABLE(i2c, lp3944_id); | 443 | MODULE_DEVICE_TABLE(i2c, lp3944_id); |
443 | 444 | ||
444 | static struct i2c_driver lp3944_driver = { | 445 | static struct i2c_driver lp3944_driver = { |
445 | .driver = { | 446 | .driver = { |
446 | .name = "lp3944", | 447 | .name = "lp3944", |
447 | }, | 448 | }, |
448 | .probe = lp3944_probe, | 449 | .probe = lp3944_probe, |
449 | .remove = lp3944_remove, | 450 | .remove = lp3944_remove, |
450 | .id_table = lp3944_id, | 451 | .id_table = lp3944_id, |
451 | }; | 452 | }; |
452 | 453 | ||
453 | module_i2c_driver(lp3944_driver); | 454 | module_i2c_driver(lp3944_driver); |
454 | 455 | ||
455 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | 456 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); |
456 | MODULE_DESCRIPTION("LP3944 Fun Light Chip"); | 457 | MODULE_DESCRIPTION("LP3944 Fun Light Chip"); |
457 | MODULE_LICENSE("GPL"); | 458 | MODULE_LICENSE("GPL"); |
458 | 459 |
drivers/leds/leds-lp5521.c
1 | /* | 1 | /* |
2 | * LP5521 LED chip driver. | 2 | * LP5521 LED chip driver. |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Nokia Corporation | 4 | * Copyright (C) 2010 Nokia Corporation |
5 | * Copyright (C) 2012 Texas Instruments | 5 | * Copyright (C) 2012 Texas Instruments |
6 | * | 6 | * |
7 | * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> | 7 | * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> |
8 | * Milo(Woogyom) Kim <milo.kim@ti.com> | 8 | * Milo(Woogyom) Kim <milo.kim@ti.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or | 10 | * This program is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU General Public License | 11 | * modify it under the terms of the GNU General Public License |
12 | * version 2 as published by the Free Software Foundation. | 12 | * version 2 as published by the Free Software Foundation. |
13 | * | 13 | * |
14 | * This program is distributed in the hope that it will be useful, but | 14 | * This program is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * General Public License for more details. | 17 | * General Public License for more details. |
18 | * | 18 | * |
19 | * You should have received a copy of the GNU General Public License | 19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software | 20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
22 | * 02110-1301 USA | 22 | * 02110-1301 USA |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/firmware.h> | 26 | #include <linux/firmware.h> |
27 | #include <linux/i2c.h> | 27 | #include <linux/i2c.h> |
28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
29 | #include <linux/leds.h> | 29 | #include <linux/leds.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/mutex.h> | 31 | #include <linux/mutex.h> |
32 | #include <linux/platform_data/leds-lp55xx.h> | 32 | #include <linux/platform_data/leds-lp55xx.h> |
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/of.h> | 34 | #include <linux/of.h> |
35 | 35 | ||
36 | #include "leds-lp55xx-common.h" | 36 | #include "leds-lp55xx-common.h" |
37 | 37 | ||
38 | #define LP5521_PROGRAM_LENGTH 32 | 38 | #define LP5521_PROGRAM_LENGTH 32 |
39 | #define LP5521_MAX_LEDS 3 | 39 | #define LP5521_MAX_LEDS 3 |
40 | #define LP5521_CMD_DIRECT 0x3F | 40 | #define LP5521_CMD_DIRECT 0x3F |
41 | 41 | ||
42 | /* Registers */ | 42 | /* Registers */ |
43 | #define LP5521_REG_ENABLE 0x00 | 43 | #define LP5521_REG_ENABLE 0x00 |
44 | #define LP5521_REG_OP_MODE 0x01 | 44 | #define LP5521_REG_OP_MODE 0x01 |
45 | #define LP5521_REG_R_PWM 0x02 | 45 | #define LP5521_REG_R_PWM 0x02 |
46 | #define LP5521_REG_G_PWM 0x03 | 46 | #define LP5521_REG_G_PWM 0x03 |
47 | #define LP5521_REG_B_PWM 0x04 | 47 | #define LP5521_REG_B_PWM 0x04 |
48 | #define LP5521_REG_R_CURRENT 0x05 | 48 | #define LP5521_REG_R_CURRENT 0x05 |
49 | #define LP5521_REG_G_CURRENT 0x06 | 49 | #define LP5521_REG_G_CURRENT 0x06 |
50 | #define LP5521_REG_B_CURRENT 0x07 | 50 | #define LP5521_REG_B_CURRENT 0x07 |
51 | #define LP5521_REG_CONFIG 0x08 | 51 | #define LP5521_REG_CONFIG 0x08 |
52 | #define LP5521_REG_STATUS 0x0C | 52 | #define LP5521_REG_STATUS 0x0C |
53 | #define LP5521_REG_RESET 0x0D | 53 | #define LP5521_REG_RESET 0x0D |
54 | #define LP5521_REG_R_PROG_MEM 0x10 | 54 | #define LP5521_REG_R_PROG_MEM 0x10 |
55 | #define LP5521_REG_G_PROG_MEM 0x30 | 55 | #define LP5521_REG_G_PROG_MEM 0x30 |
56 | #define LP5521_REG_B_PROG_MEM 0x50 | 56 | #define LP5521_REG_B_PROG_MEM 0x50 |
57 | 57 | ||
58 | /* Base register to set LED current */ | 58 | /* Base register to set LED current */ |
59 | #define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT | 59 | #define LP5521_REG_LED_CURRENT_BASE LP5521_REG_R_CURRENT |
60 | /* Base register to set the brightness */ | 60 | /* Base register to set the brightness */ |
61 | #define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM | 61 | #define LP5521_REG_LED_PWM_BASE LP5521_REG_R_PWM |
62 | 62 | ||
63 | /* Bits in ENABLE register */ | 63 | /* Bits in ENABLE register */ |
64 | #define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ | 64 | #define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */ |
65 | #define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ | 65 | #define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ |
66 | #define LP5521_EXEC_RUN 0x2A | 66 | #define LP5521_EXEC_RUN 0x2A |
67 | #define LP5521_ENABLE_DEFAULT \ | 67 | #define LP5521_ENABLE_DEFAULT \ |
68 | (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM) | 68 | (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM) |
69 | #define LP5521_ENABLE_RUN_PROGRAM \ | 69 | #define LP5521_ENABLE_RUN_PROGRAM \ |
70 | (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN) | 70 | (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN) |
71 | 71 | ||
72 | /* CONFIG register */ | 72 | /* CONFIG register */ |
73 | #define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ | 73 | #define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */ |
74 | #define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ | 74 | #define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */ |
75 | #define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ | 75 | #define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */ |
76 | #define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ | 76 | #define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */ |
77 | #define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ | 77 | #define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */ |
78 | #define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ | 78 | #define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */ |
79 | #define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */ | 79 | #define LP5521_R_TO_BATT 0x04 /* R out: 0 = CP, 1 = Vbat */ |
80 | #define LP5521_CLK_INT 0x01 /* Internal clock */ | 80 | #define LP5521_CLK_INT 0x01 /* Internal clock */ |
81 | #define LP5521_DEFAULT_CFG \ | 81 | #define LP5521_DEFAULT_CFG \ |
82 | (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO) | 82 | (LP5521_PWM_HF | LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO) |
83 | 83 | ||
84 | /* Status */ | 84 | /* Status */ |
85 | #define LP5521_EXT_CLK_USED 0x08 | 85 | #define LP5521_EXT_CLK_USED 0x08 |
86 | 86 | ||
87 | /* default R channel current register value */ | 87 | /* default R channel current register value */ |
88 | #define LP5521_REG_R_CURR_DEFAULT 0xAF | 88 | #define LP5521_REG_R_CURR_DEFAULT 0xAF |
89 | 89 | ||
90 | /* Reset register value */ | 90 | /* Reset register value */ |
91 | #define LP5521_RESET 0xFF | 91 | #define LP5521_RESET 0xFF |
92 | 92 | ||
93 | /* Program Memory Operations */ | 93 | /* Program Memory Operations */ |
94 | #define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ | 94 | #define LP5521_MODE_R_M 0x30 /* Operation Mode Register */ |
95 | #define LP5521_MODE_G_M 0x0C | 95 | #define LP5521_MODE_G_M 0x0C |
96 | #define LP5521_MODE_B_M 0x03 | 96 | #define LP5521_MODE_B_M 0x03 |
97 | #define LP5521_LOAD_R 0x10 | 97 | #define LP5521_LOAD_R 0x10 |
98 | #define LP5521_LOAD_G 0x04 | 98 | #define LP5521_LOAD_G 0x04 |
99 | #define LP5521_LOAD_B 0x01 | 99 | #define LP5521_LOAD_B 0x01 |
100 | 100 | ||
101 | #define LP5521_R_IS_LOADING(mode) \ | 101 | #define LP5521_R_IS_LOADING(mode) \ |
102 | ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) | 102 | ((mode & LP5521_MODE_R_M) == LP5521_LOAD_R) |
103 | #define LP5521_G_IS_LOADING(mode) \ | 103 | #define LP5521_G_IS_LOADING(mode) \ |
104 | ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) | 104 | ((mode & LP5521_MODE_G_M) == LP5521_LOAD_G) |
105 | #define LP5521_B_IS_LOADING(mode) \ | 105 | #define LP5521_B_IS_LOADING(mode) \ |
106 | ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) | 106 | ((mode & LP5521_MODE_B_M) == LP5521_LOAD_B) |
107 | 107 | ||
108 | #define LP5521_EXEC_R_M 0x30 /* Enable Register */ | 108 | #define LP5521_EXEC_R_M 0x30 /* Enable Register */ |
109 | #define LP5521_EXEC_G_M 0x0C | 109 | #define LP5521_EXEC_G_M 0x0C |
110 | #define LP5521_EXEC_B_M 0x03 | 110 | #define LP5521_EXEC_B_M 0x03 |
111 | #define LP5521_EXEC_M 0x3F | 111 | #define LP5521_EXEC_M 0x3F |
112 | #define LP5521_RUN_R 0x20 | 112 | #define LP5521_RUN_R 0x20 |
113 | #define LP5521_RUN_G 0x08 | 113 | #define LP5521_RUN_G 0x08 |
114 | #define LP5521_RUN_B 0x02 | 114 | #define LP5521_RUN_B 0x02 |
115 | 115 | ||
116 | static inline void lp5521_wait_opmode_done(void) | 116 | static inline void lp5521_wait_opmode_done(void) |
117 | { | 117 | { |
118 | /* operation mode change needs to be longer than 153 us */ | 118 | /* operation mode change needs to be longer than 153 us */ |
119 | usleep_range(200, 300); | 119 | usleep_range(200, 300); |
120 | } | 120 | } |
121 | 121 | ||
122 | static inline void lp5521_wait_enable_done(void) | 122 | static inline void lp5521_wait_enable_done(void) |
123 | { | 123 | { |
124 | /* it takes more 488 us to update ENABLE register */ | 124 | /* it takes more 488 us to update ENABLE register */ |
125 | usleep_range(500, 600); | 125 | usleep_range(500, 600); |
126 | } | 126 | } |
127 | 127 | ||
128 | static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) | 128 | static void lp5521_set_led_current(struct lp55xx_led *led, u8 led_current) |
129 | { | 129 | { |
130 | led->led_current = led_current; | 130 | led->led_current = led_current; |
131 | lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, | 131 | lp55xx_write(led->chip, LP5521_REG_LED_CURRENT_BASE + led->chan_nr, |
132 | led_current); | 132 | led_current); |
133 | } | 133 | } |
134 | 134 | ||
135 | static void lp5521_load_engine(struct lp55xx_chip *chip) | 135 | static void lp5521_load_engine(struct lp55xx_chip *chip) |
136 | { | 136 | { |
137 | enum lp55xx_engine_index idx = chip->engine_idx; | 137 | enum lp55xx_engine_index idx = chip->engine_idx; |
138 | u8 mask[] = { | 138 | u8 mask[] = { |
139 | [LP55XX_ENGINE_1] = LP5521_MODE_R_M, | 139 | [LP55XX_ENGINE_1] = LP5521_MODE_R_M, |
140 | [LP55XX_ENGINE_2] = LP5521_MODE_G_M, | 140 | [LP55XX_ENGINE_2] = LP5521_MODE_G_M, |
141 | [LP55XX_ENGINE_3] = LP5521_MODE_B_M, | 141 | [LP55XX_ENGINE_3] = LP5521_MODE_B_M, |
142 | }; | 142 | }; |
143 | 143 | ||
144 | u8 val[] = { | 144 | u8 val[] = { |
145 | [LP55XX_ENGINE_1] = LP5521_LOAD_R, | 145 | [LP55XX_ENGINE_1] = LP5521_LOAD_R, |
146 | [LP55XX_ENGINE_2] = LP5521_LOAD_G, | 146 | [LP55XX_ENGINE_2] = LP5521_LOAD_G, |
147 | [LP55XX_ENGINE_3] = LP5521_LOAD_B, | 147 | [LP55XX_ENGINE_3] = LP5521_LOAD_B, |
148 | }; | 148 | }; |
149 | 149 | ||
150 | lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); | 150 | lp55xx_update_bits(chip, LP5521_REG_OP_MODE, mask[idx], val[idx]); |
151 | 151 | ||
152 | lp5521_wait_opmode_done(); | 152 | lp5521_wait_opmode_done(); |
153 | } | 153 | } |
154 | 154 | ||
155 | static void lp5521_stop_engine(struct lp55xx_chip *chip) | 155 | static void lp5521_stop_engine(struct lp55xx_chip *chip) |
156 | { | 156 | { |
157 | lp55xx_write(chip, LP5521_REG_OP_MODE, 0); | 157 | lp55xx_write(chip, LP5521_REG_OP_MODE, 0); |
158 | lp5521_wait_opmode_done(); | 158 | lp5521_wait_opmode_done(); |
159 | } | 159 | } |
160 | 160 | ||
161 | static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) | 161 | static void lp5521_run_engine(struct lp55xx_chip *chip, bool start) |
162 | { | 162 | { |
163 | int ret; | 163 | int ret; |
164 | u8 mode; | 164 | u8 mode; |
165 | u8 exec; | 165 | u8 exec; |
166 | 166 | ||
167 | /* stop engine */ | 167 | /* stop engine */ |
168 | if (!start) { | 168 | if (!start) { |
169 | lp5521_stop_engine(chip); | 169 | lp5521_stop_engine(chip); |
170 | lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); | 170 | lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); |
171 | lp5521_wait_opmode_done(); | 171 | lp5521_wait_opmode_done(); |
172 | return; | 172 | return; |
173 | } | 173 | } |
174 | 174 | ||
175 | /* | 175 | /* |
176 | * To run the engine, | 176 | * To run the engine, |
177 | * operation mode and enable register should updated at the same time | 177 | * operation mode and enable register should updated at the same time |
178 | */ | 178 | */ |
179 | 179 | ||
180 | ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); | 180 | ret = lp55xx_read(chip, LP5521_REG_OP_MODE, &mode); |
181 | if (ret) | 181 | if (ret) |
182 | return; | 182 | return; |
183 | 183 | ||
184 | ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); | 184 | ret = lp55xx_read(chip, LP5521_REG_ENABLE, &exec); |
185 | if (ret) | 185 | if (ret) |
186 | return; | 186 | return; |
187 | 187 | ||
188 | /* change operation mode to RUN only when each engine is loading */ | 188 | /* change operation mode to RUN only when each engine is loading */ |
189 | if (LP5521_R_IS_LOADING(mode)) { | 189 | if (LP5521_R_IS_LOADING(mode)) { |
190 | mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; | 190 | mode = (mode & ~LP5521_MODE_R_M) | LP5521_RUN_R; |
191 | exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; | 191 | exec = (exec & ~LP5521_EXEC_R_M) | LP5521_RUN_R; |
192 | } | 192 | } |
193 | 193 | ||
194 | if (LP5521_G_IS_LOADING(mode)) { | 194 | if (LP5521_G_IS_LOADING(mode)) { |
195 | mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; | 195 | mode = (mode & ~LP5521_MODE_G_M) | LP5521_RUN_G; |
196 | exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; | 196 | exec = (exec & ~LP5521_EXEC_G_M) | LP5521_RUN_G; |
197 | } | 197 | } |
198 | 198 | ||
199 | if (LP5521_B_IS_LOADING(mode)) { | 199 | if (LP5521_B_IS_LOADING(mode)) { |
200 | mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; | 200 | mode = (mode & ~LP5521_MODE_B_M) | LP5521_RUN_B; |
201 | exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; | 201 | exec = (exec & ~LP5521_EXEC_B_M) | LP5521_RUN_B; |
202 | } | 202 | } |
203 | 203 | ||
204 | lp55xx_write(chip, LP5521_REG_OP_MODE, mode); | 204 | lp55xx_write(chip, LP5521_REG_OP_MODE, mode); |
205 | lp5521_wait_opmode_done(); | 205 | lp5521_wait_opmode_done(); |
206 | 206 | ||
207 | lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); | 207 | lp55xx_update_bits(chip, LP5521_REG_ENABLE, LP5521_EXEC_M, exec); |
208 | lp5521_wait_enable_done(); | 208 | lp5521_wait_enable_done(); |
209 | } | 209 | } |
210 | 210 | ||
211 | static int lp5521_update_program_memory(struct lp55xx_chip *chip, | 211 | static int lp5521_update_program_memory(struct lp55xx_chip *chip, |
212 | const u8 *data, size_t size) | 212 | const u8 *data, size_t size) |
213 | { | 213 | { |
214 | enum lp55xx_engine_index idx = chip->engine_idx; | 214 | enum lp55xx_engine_index idx = chip->engine_idx; |
215 | u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; | 215 | u8 pattern[LP5521_PROGRAM_LENGTH] = {0}; |
216 | u8 addr[] = { | 216 | u8 addr[] = { |
217 | [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, | 217 | [LP55XX_ENGINE_1] = LP5521_REG_R_PROG_MEM, |
218 | [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, | 218 | [LP55XX_ENGINE_2] = LP5521_REG_G_PROG_MEM, |
219 | [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, | 219 | [LP55XX_ENGINE_3] = LP5521_REG_B_PROG_MEM, |
220 | }; | 220 | }; |
221 | unsigned cmd; | 221 | unsigned cmd; |
222 | char c[3]; | 222 | char c[3]; |
223 | int program_size; | 223 | int program_size; |
224 | int nrchars; | 224 | int nrchars; |
225 | int offset = 0; | 225 | int offset = 0; |
226 | int ret; | 226 | int ret; |
227 | int i; | 227 | int i; |
228 | 228 | ||
229 | /* clear program memory before updating */ | 229 | /* clear program memory before updating */ |
230 | for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) | 230 | for (i = 0; i < LP5521_PROGRAM_LENGTH; i++) |
231 | lp55xx_write(chip, addr[idx] + i, 0); | 231 | lp55xx_write(chip, addr[idx] + i, 0); |
232 | 232 | ||
233 | i = 0; | 233 | i = 0; |
234 | while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { | 234 | while ((offset < size - 1) && (i < LP5521_PROGRAM_LENGTH)) { |
235 | /* separate sscanfs because length is working only for %s */ | 235 | /* separate sscanfs because length is working only for %s */ |
236 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); | 236 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); |
237 | if (ret != 1) | 237 | if (ret != 1) |
238 | goto err; | 238 | goto err; |
239 | 239 | ||
240 | ret = sscanf(c, "%2x", &cmd); | 240 | ret = sscanf(c, "%2x", &cmd); |
241 | if (ret != 1) | 241 | if (ret != 1) |
242 | goto err; | 242 | goto err; |
243 | 243 | ||
244 | pattern[i] = (u8)cmd; | 244 | pattern[i] = (u8)cmd; |
245 | offset += nrchars; | 245 | offset += nrchars; |
246 | i++; | 246 | i++; |
247 | } | 247 | } |
248 | 248 | ||
249 | /* Each instruction is 16bit long. Check that length is even */ | 249 | /* Each instruction is 16bit long. Check that length is even */ |
250 | if (i % 2) | 250 | if (i % 2) |
251 | goto err; | 251 | goto err; |
252 | 252 | ||
253 | program_size = i; | 253 | program_size = i; |
254 | for (i = 0; i < program_size; i++) | 254 | for (i = 0; i < program_size; i++) |
255 | lp55xx_write(chip, addr[idx] + i, pattern[i]); | 255 | lp55xx_write(chip, addr[idx] + i, pattern[i]); |
256 | 256 | ||
257 | return 0; | 257 | return 0; |
258 | 258 | ||
259 | err: | 259 | err: |
260 | dev_err(&chip->cl->dev, "wrong pattern format\n"); | 260 | dev_err(&chip->cl->dev, "wrong pattern format\n"); |
261 | return -EINVAL; | 261 | return -EINVAL; |
262 | } | 262 | } |
263 | 263 | ||
264 | static void lp5521_firmware_loaded(struct lp55xx_chip *chip) | 264 | static void lp5521_firmware_loaded(struct lp55xx_chip *chip) |
265 | { | 265 | { |
266 | const struct firmware *fw = chip->fw; | 266 | const struct firmware *fw = chip->fw; |
267 | 267 | ||
268 | if (fw->size > LP5521_PROGRAM_LENGTH) { | 268 | if (fw->size > LP5521_PROGRAM_LENGTH) { |
269 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", | 269 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", |
270 | fw->size); | 270 | fw->size); |
271 | return; | 271 | return; |
272 | } | 272 | } |
273 | 273 | ||
274 | /* | 274 | /* |
275 | * Program momery sequence | 275 | * Program momery sequence |
276 | * 1) set engine mode to "LOAD" | 276 | * 1) set engine mode to "LOAD" |
277 | * 2) write firmware data into program memory | 277 | * 2) write firmware data into program memory |
278 | */ | 278 | */ |
279 | 279 | ||
280 | lp5521_load_engine(chip); | 280 | lp5521_load_engine(chip); |
281 | lp5521_update_program_memory(chip, fw->data, fw->size); | 281 | lp5521_update_program_memory(chip, fw->data, fw->size); |
282 | } | 282 | } |
283 | 283 | ||
284 | static int lp5521_post_init_device(struct lp55xx_chip *chip) | 284 | static int lp5521_post_init_device(struct lp55xx_chip *chip) |
285 | { | 285 | { |
286 | int ret; | 286 | int ret; |
287 | u8 val; | 287 | u8 val; |
288 | 288 | ||
289 | /* | 289 | /* |
290 | * Make sure that the chip is reset by reading back the r channel | 290 | * Make sure that the chip is reset by reading back the r channel |
291 | * current reg. This is dummy read is required on some platforms - | 291 | * current reg. This is dummy read is required on some platforms - |
292 | * otherwise further access to the R G B channels in the | 292 | * otherwise further access to the R G B channels in the |
293 | * LP5521_REG_ENABLE register will not have any effect - strange! | 293 | * LP5521_REG_ENABLE register will not have any effect - strange! |
294 | */ | 294 | */ |
295 | ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); | 295 | ret = lp55xx_read(chip, LP5521_REG_R_CURRENT, &val); |
296 | if (ret) { | 296 | if (ret) { |
297 | dev_err(&chip->cl->dev, "error in resetting chip\n"); | 297 | dev_err(&chip->cl->dev, "error in resetting chip\n"); |
298 | return ret; | 298 | return ret; |
299 | } | 299 | } |
300 | if (val != LP5521_REG_R_CURR_DEFAULT) { | 300 | if (val != LP5521_REG_R_CURR_DEFAULT) { |
301 | dev_err(&chip->cl->dev, | 301 | dev_err(&chip->cl->dev, |
302 | "unexpected data in register (expected 0x%x got 0x%x)\n", | 302 | "unexpected data in register (expected 0x%x got 0x%x)\n", |
303 | LP5521_REG_R_CURR_DEFAULT, val); | 303 | LP5521_REG_R_CURR_DEFAULT, val); |
304 | ret = -EINVAL; | 304 | ret = -EINVAL; |
305 | return ret; | 305 | return ret; |
306 | } | 306 | } |
307 | usleep_range(10000, 20000); | 307 | usleep_range(10000, 20000); |
308 | 308 | ||
309 | /* Set all PWMs to direct control mode */ | 309 | /* Set all PWMs to direct control mode */ |
310 | ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); | 310 | ret = lp55xx_write(chip, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); |
311 | 311 | ||
312 | /* Update configuration for the clock setting */ | 312 | /* Update configuration for the clock setting */ |
313 | val = LP5521_DEFAULT_CFG; | 313 | val = LP5521_DEFAULT_CFG; |
314 | if (!lp55xx_is_extclk_used(chip)) | 314 | if (!lp55xx_is_extclk_used(chip)) |
315 | val |= LP5521_CLK_INT; | 315 | val |= LP5521_CLK_INT; |
316 | 316 | ||
317 | ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); | 317 | ret = lp55xx_write(chip, LP5521_REG_CONFIG, val); |
318 | if (ret) | 318 | if (ret) |
319 | return ret; | 319 | return ret; |
320 | 320 | ||
321 | /* Initialize all channels PWM to zero -> leds off */ | 321 | /* Initialize all channels PWM to zero -> leds off */ |
322 | lp55xx_write(chip, LP5521_REG_R_PWM, 0); | 322 | lp55xx_write(chip, LP5521_REG_R_PWM, 0); |
323 | lp55xx_write(chip, LP5521_REG_G_PWM, 0); | 323 | lp55xx_write(chip, LP5521_REG_G_PWM, 0); |
324 | lp55xx_write(chip, LP5521_REG_B_PWM, 0); | 324 | lp55xx_write(chip, LP5521_REG_B_PWM, 0); |
325 | 325 | ||
326 | /* Set engines are set to run state when OP_MODE enables engines */ | 326 | /* Set engines are set to run state when OP_MODE enables engines */ |
327 | ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); | 327 | ret = lp55xx_write(chip, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM); |
328 | if (ret) | 328 | if (ret) |
329 | return ret; | 329 | return ret; |
330 | 330 | ||
331 | lp5521_wait_enable_done(); | 331 | lp5521_wait_enable_done(); |
332 | 332 | ||
333 | return 0; | 333 | return 0; |
334 | } | 334 | } |
335 | 335 | ||
336 | static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) | 336 | static int lp5521_run_selftest(struct lp55xx_chip *chip, char *buf) |
337 | { | 337 | { |
338 | struct lp55xx_platform_data *pdata = chip->pdata; | 338 | struct lp55xx_platform_data *pdata = chip->pdata; |
339 | int ret; | 339 | int ret; |
340 | u8 status; | 340 | u8 status; |
341 | 341 | ||
342 | ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); | 342 | ret = lp55xx_read(chip, LP5521_REG_STATUS, &status); |
343 | if (ret < 0) | 343 | if (ret < 0) |
344 | return ret; | 344 | return ret; |
345 | 345 | ||
346 | if (pdata->clock_mode != LP55XX_CLOCK_EXT) | 346 | if (pdata->clock_mode != LP55XX_CLOCK_EXT) |
347 | return 0; | 347 | return 0; |
348 | 348 | ||
349 | /* Check that ext clock is really in use if requested */ | 349 | /* Check that ext clock is really in use if requested */ |
350 | if ((status & LP5521_EXT_CLK_USED) == 0) | 350 | if ((status & LP5521_EXT_CLK_USED) == 0) |
351 | return -EIO; | 351 | return -EIO; |
352 | 352 | ||
353 | return 0; | 353 | return 0; |
354 | } | 354 | } |
355 | 355 | ||
356 | static void lp5521_led_brightness_work(struct work_struct *work) | 356 | static void lp5521_led_brightness_work(struct work_struct *work) |
357 | { | 357 | { |
358 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, | 358 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, |
359 | brightness_work); | 359 | brightness_work); |
360 | struct lp55xx_chip *chip = led->chip; | 360 | struct lp55xx_chip *chip = led->chip; |
361 | 361 | ||
362 | mutex_lock(&chip->lock); | 362 | mutex_lock(&chip->lock); |
363 | lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, | 363 | lp55xx_write(chip, LP5521_REG_LED_PWM_BASE + led->chan_nr, |
364 | led->brightness); | 364 | led->brightness); |
365 | mutex_unlock(&chip->lock); | 365 | mutex_unlock(&chip->lock); |
366 | } | 366 | } |
367 | 367 | ||
368 | static ssize_t lp5521_selftest(struct device *dev, | 368 | static ssize_t lp5521_selftest(struct device *dev, |
369 | struct device_attribute *attr, | 369 | struct device_attribute *attr, |
370 | char *buf) | 370 | char *buf) |
371 | { | 371 | { |
372 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 372 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
373 | struct lp55xx_chip *chip = led->chip; | 373 | struct lp55xx_chip *chip = led->chip; |
374 | int ret; | 374 | int ret; |
375 | 375 | ||
376 | mutex_lock(&chip->lock); | 376 | mutex_lock(&chip->lock); |
377 | ret = lp5521_run_selftest(chip, buf); | 377 | ret = lp5521_run_selftest(chip, buf); |
378 | mutex_unlock(&chip->lock); | 378 | mutex_unlock(&chip->lock); |
379 | 379 | ||
380 | return scnprintf(buf, PAGE_SIZE, "%s\n", ret ? "FAIL" : "OK"); | 380 | return scnprintf(buf, PAGE_SIZE, "%s\n", ret ? "FAIL" : "OK"); |
381 | } | 381 | } |
382 | 382 | ||
383 | /* device attributes */ | 383 | /* device attributes */ |
384 | static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); | 384 | static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); |
385 | 385 | ||
386 | static struct attribute *lp5521_attributes[] = { | 386 | static struct attribute *lp5521_attributes[] = { |
387 | &dev_attr_selftest.attr, | 387 | &dev_attr_selftest.attr, |
388 | NULL | 388 | NULL |
389 | }; | 389 | }; |
390 | 390 | ||
391 | static const struct attribute_group lp5521_group = { | 391 | static const struct attribute_group lp5521_group = { |
392 | .attrs = lp5521_attributes, | 392 | .attrs = lp5521_attributes, |
393 | }; | 393 | }; |
394 | 394 | ||
395 | /* Chip specific configurations */ | 395 | /* Chip specific configurations */ |
396 | static struct lp55xx_device_config lp5521_cfg = { | 396 | static struct lp55xx_device_config lp5521_cfg = { |
397 | .reset = { | 397 | .reset = { |
398 | .addr = LP5521_REG_RESET, | 398 | .addr = LP5521_REG_RESET, |
399 | .val = LP5521_RESET, | 399 | .val = LP5521_RESET, |
400 | }, | 400 | }, |
401 | .enable = { | 401 | .enable = { |
402 | .addr = LP5521_REG_ENABLE, | 402 | .addr = LP5521_REG_ENABLE, |
403 | .val = LP5521_ENABLE_DEFAULT, | 403 | .val = LP5521_ENABLE_DEFAULT, |
404 | }, | 404 | }, |
405 | .max_channel = LP5521_MAX_LEDS, | 405 | .max_channel = LP5521_MAX_LEDS, |
406 | .post_init_device = lp5521_post_init_device, | 406 | .post_init_device = lp5521_post_init_device, |
407 | .brightness_work_fn = lp5521_led_brightness_work, | 407 | .brightness_work_fn = lp5521_led_brightness_work, |
408 | .set_led_current = lp5521_set_led_current, | 408 | .set_led_current = lp5521_set_led_current, |
409 | .firmware_cb = lp5521_firmware_loaded, | 409 | .firmware_cb = lp5521_firmware_loaded, |
410 | .run_engine = lp5521_run_engine, | 410 | .run_engine = lp5521_run_engine, |
411 | .dev_attr_group = &lp5521_group, | 411 | .dev_attr_group = &lp5521_group, |
412 | }; | 412 | }; |
413 | 413 | ||
414 | static int lp5521_probe(struct i2c_client *client, | 414 | static int lp5521_probe(struct i2c_client *client, |
415 | const struct i2c_device_id *id) | 415 | const struct i2c_device_id *id) |
416 | { | 416 | { |
417 | int ret; | 417 | int ret; |
418 | struct lp55xx_chip *chip; | 418 | struct lp55xx_chip *chip; |
419 | struct lp55xx_led *led; | 419 | struct lp55xx_led *led; |
420 | struct lp55xx_platform_data *pdata; | 420 | struct lp55xx_platform_data *pdata; |
421 | struct device_node *np = client->dev.of_node; | 421 | struct device_node *np = client->dev.of_node; |
422 | 422 | ||
423 | if (!client->dev.platform_data) { | 423 | if (!dev_get_platdata(&client->dev)) { |
424 | if (np) { | 424 | if (np) { |
425 | ret = lp55xx_of_populate_pdata(&client->dev, np); | 425 | ret = lp55xx_of_populate_pdata(&client->dev, np); |
426 | if (ret < 0) | 426 | if (ret < 0) |
427 | return ret; | 427 | return ret; |
428 | } else { | 428 | } else { |
429 | dev_err(&client->dev, "no platform data\n"); | 429 | dev_err(&client->dev, "no platform data\n"); |
430 | return -EINVAL; | 430 | return -EINVAL; |
431 | } | 431 | } |
432 | } | 432 | } |
433 | pdata = client->dev.platform_data; | 433 | pdata = dev_get_platdata(&client->dev); |
434 | 434 | ||
435 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | 435 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
436 | if (!chip) | 436 | if (!chip) |
437 | return -ENOMEM; | 437 | return -ENOMEM; |
438 | 438 | ||
439 | led = devm_kzalloc(&client->dev, | 439 | led = devm_kzalloc(&client->dev, |
440 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); | 440 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); |
441 | if (!led) | 441 | if (!led) |
442 | return -ENOMEM; | 442 | return -ENOMEM; |
443 | 443 | ||
444 | chip->cl = client; | 444 | chip->cl = client; |
445 | chip->pdata = pdata; | 445 | chip->pdata = pdata; |
446 | chip->cfg = &lp5521_cfg; | 446 | chip->cfg = &lp5521_cfg; |
447 | 447 | ||
448 | mutex_init(&chip->lock); | 448 | mutex_init(&chip->lock); |
449 | 449 | ||
450 | i2c_set_clientdata(client, led); | 450 | i2c_set_clientdata(client, led); |
451 | 451 | ||
452 | ret = lp55xx_init_device(chip); | 452 | ret = lp55xx_init_device(chip); |
453 | if (ret) | 453 | if (ret) |
454 | goto err_init; | 454 | goto err_init; |
455 | 455 | ||
456 | dev_info(&client->dev, "%s programmable led chip found\n", id->name); | 456 | dev_info(&client->dev, "%s programmable led chip found\n", id->name); |
457 | 457 | ||
458 | ret = lp55xx_register_leds(led, chip); | 458 | ret = lp55xx_register_leds(led, chip); |
459 | if (ret) | 459 | if (ret) |
460 | goto err_register_leds; | 460 | goto err_register_leds; |
461 | 461 | ||
462 | ret = lp55xx_register_sysfs(chip); | 462 | ret = lp55xx_register_sysfs(chip); |
463 | if (ret) { | 463 | if (ret) { |
464 | dev_err(&client->dev, "registering sysfs failed\n"); | 464 | dev_err(&client->dev, "registering sysfs failed\n"); |
465 | goto err_register_sysfs; | 465 | goto err_register_sysfs; |
466 | } | 466 | } |
467 | 467 | ||
468 | return 0; | 468 | return 0; |
469 | 469 | ||
470 | err_register_sysfs: | 470 | err_register_sysfs: |
471 | lp55xx_unregister_leds(led, chip); | 471 | lp55xx_unregister_leds(led, chip); |
472 | err_register_leds: | 472 | err_register_leds: |
473 | lp55xx_deinit_device(chip); | 473 | lp55xx_deinit_device(chip); |
474 | err_init: | 474 | err_init: |
475 | return ret; | 475 | return ret; |
476 | } | 476 | } |
477 | 477 | ||
478 | static int lp5521_remove(struct i2c_client *client) | 478 | static int lp5521_remove(struct i2c_client *client) |
479 | { | 479 | { |
480 | struct lp55xx_led *led = i2c_get_clientdata(client); | 480 | struct lp55xx_led *led = i2c_get_clientdata(client); |
481 | struct lp55xx_chip *chip = led->chip; | 481 | struct lp55xx_chip *chip = led->chip; |
482 | 482 | ||
483 | lp5521_stop_engine(chip); | 483 | lp5521_stop_engine(chip); |
484 | lp55xx_unregister_sysfs(chip); | 484 | lp55xx_unregister_sysfs(chip); |
485 | lp55xx_unregister_leds(led, chip); | 485 | lp55xx_unregister_leds(led, chip); |
486 | lp55xx_deinit_device(chip); | 486 | lp55xx_deinit_device(chip); |
487 | 487 | ||
488 | return 0; | 488 | return 0; |
489 | } | 489 | } |
490 | 490 | ||
491 | static const struct i2c_device_id lp5521_id[] = { | 491 | static const struct i2c_device_id lp5521_id[] = { |
492 | { "lp5521", 0 }, /* Three channel chip */ | 492 | { "lp5521", 0 }, /* Three channel chip */ |
493 | { } | 493 | { } |
494 | }; | 494 | }; |
495 | MODULE_DEVICE_TABLE(i2c, lp5521_id); | 495 | MODULE_DEVICE_TABLE(i2c, lp5521_id); |
496 | 496 | ||
497 | #ifdef CONFIG_OF | 497 | #ifdef CONFIG_OF |
498 | static const struct of_device_id of_lp5521_leds_match[] = { | 498 | static const struct of_device_id of_lp5521_leds_match[] = { |
499 | { .compatible = "national,lp5521", }, | 499 | { .compatible = "national,lp5521", }, |
500 | {}, | 500 | {}, |
501 | }; | 501 | }; |
502 | 502 | ||
503 | MODULE_DEVICE_TABLE(of, of_lp5521_leds_match); | 503 | MODULE_DEVICE_TABLE(of, of_lp5521_leds_match); |
504 | #endif | 504 | #endif |
505 | static struct i2c_driver lp5521_driver = { | 505 | static struct i2c_driver lp5521_driver = { |
506 | .driver = { | 506 | .driver = { |
507 | .name = "lp5521", | 507 | .name = "lp5521", |
508 | .of_match_table = of_match_ptr(of_lp5521_leds_match), | 508 | .of_match_table = of_match_ptr(of_lp5521_leds_match), |
509 | }, | 509 | }, |
510 | .probe = lp5521_probe, | 510 | .probe = lp5521_probe, |
511 | .remove = lp5521_remove, | 511 | .remove = lp5521_remove, |
512 | .id_table = lp5521_id, | 512 | .id_table = lp5521_id, |
513 | }; | 513 | }; |
514 | 514 | ||
515 | module_i2c_driver(lp5521_driver); | 515 | module_i2c_driver(lp5521_driver); |
516 | 516 | ||
517 | MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); | 517 | MODULE_AUTHOR("Mathias Nyman, Yuri Zaporozhets, Samu Onkalo"); |
518 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); | 518 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); |
519 | MODULE_DESCRIPTION("LP5521 LED engine"); | 519 | MODULE_DESCRIPTION("LP5521 LED engine"); |
520 | MODULE_LICENSE("GPL v2"); | 520 | MODULE_LICENSE("GPL v2"); |
521 | 521 |
drivers/leds/leds-lp5523.c
1 | /* | 1 | /* |
2 | * lp5523.c - LP5523 LED Driver | 2 | * lp5523.c - LP5523 LED Driver |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Nokia Corporation | 4 | * Copyright (C) 2010 Nokia Corporation |
5 | * Copyright (C) 2012 Texas Instruments | 5 | * Copyright (C) 2012 Texas Instruments |
6 | * | 6 | * |
7 | * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> | 7 | * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> |
8 | * Milo(Woogyom) Kim <milo.kim@ti.com> | 8 | * Milo(Woogyom) Kim <milo.kim@ti.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or | 10 | * This program is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU General Public License | 11 | * modify it under the terms of the GNU General Public License |
12 | * version 2 as published by the Free Software Foundation. | 12 | * version 2 as published by the Free Software Foundation. |
13 | * | 13 | * |
14 | * This program is distributed in the hope that it will be useful, but | 14 | * This program is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * General Public License for more details. | 17 | * General Public License for more details. |
18 | * | 18 | * |
19 | * You should have received a copy of the GNU General Public License | 19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software | 20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | 21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
22 | * 02110-1301 USA | 22 | * 02110-1301 USA |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/firmware.h> | 26 | #include <linux/firmware.h> |
27 | #include <linux/i2c.h> | 27 | #include <linux/i2c.h> |
28 | #include <linux/init.h> | 28 | #include <linux/init.h> |
29 | #include <linux/leds.h> | 29 | #include <linux/leds.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/mutex.h> | 31 | #include <linux/mutex.h> |
32 | #include <linux/platform_data/leds-lp55xx.h> | 32 | #include <linux/platform_data/leds-lp55xx.h> |
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | 34 | ||
35 | #include "leds-lp55xx-common.h" | 35 | #include "leds-lp55xx-common.h" |
36 | 36 | ||
37 | #define LP5523_PROGRAM_LENGTH 32 | 37 | #define LP5523_PROGRAM_LENGTH 32 |
38 | #define LP5523_MAX_LEDS 9 | 38 | #define LP5523_MAX_LEDS 9 |
39 | 39 | ||
40 | /* Registers */ | 40 | /* Registers */ |
41 | #define LP5523_REG_ENABLE 0x00 | 41 | #define LP5523_REG_ENABLE 0x00 |
42 | #define LP5523_REG_OP_MODE 0x01 | 42 | #define LP5523_REG_OP_MODE 0x01 |
43 | #define LP5523_REG_ENABLE_LEDS_MSB 0x04 | 43 | #define LP5523_REG_ENABLE_LEDS_MSB 0x04 |
44 | #define LP5523_REG_ENABLE_LEDS_LSB 0x05 | 44 | #define LP5523_REG_ENABLE_LEDS_LSB 0x05 |
45 | #define LP5523_REG_LED_PWM_BASE 0x16 | 45 | #define LP5523_REG_LED_PWM_BASE 0x16 |
46 | #define LP5523_REG_LED_CURRENT_BASE 0x26 | 46 | #define LP5523_REG_LED_CURRENT_BASE 0x26 |
47 | #define LP5523_REG_CONFIG 0x36 | 47 | #define LP5523_REG_CONFIG 0x36 |
48 | #define LP5523_REG_STATUS 0x3A | 48 | #define LP5523_REG_STATUS 0x3A |
49 | #define LP5523_REG_RESET 0x3D | 49 | #define LP5523_REG_RESET 0x3D |
50 | #define LP5523_REG_LED_TEST_CTRL 0x41 | 50 | #define LP5523_REG_LED_TEST_CTRL 0x41 |
51 | #define LP5523_REG_LED_TEST_ADC 0x42 | 51 | #define LP5523_REG_LED_TEST_ADC 0x42 |
52 | #define LP5523_REG_PROG_PAGE_SEL 0x4F | 52 | #define LP5523_REG_PROG_PAGE_SEL 0x4F |
53 | #define LP5523_REG_PROG_MEM 0x50 | 53 | #define LP5523_REG_PROG_MEM 0x50 |
54 | 54 | ||
55 | /* Bit description in registers */ | 55 | /* Bit description in registers */ |
56 | #define LP5523_ENABLE 0x40 | 56 | #define LP5523_ENABLE 0x40 |
57 | #define LP5523_AUTO_INC 0x40 | 57 | #define LP5523_AUTO_INC 0x40 |
58 | #define LP5523_PWR_SAVE 0x20 | 58 | #define LP5523_PWR_SAVE 0x20 |
59 | #define LP5523_PWM_PWR_SAVE 0x04 | 59 | #define LP5523_PWM_PWR_SAVE 0x04 |
60 | #define LP5523_CP_AUTO 0x18 | 60 | #define LP5523_CP_AUTO 0x18 |
61 | #define LP5523_AUTO_CLK 0x02 | 61 | #define LP5523_AUTO_CLK 0x02 |
62 | 62 | ||
63 | #define LP5523_EN_LEDTEST 0x80 | 63 | #define LP5523_EN_LEDTEST 0x80 |
64 | #define LP5523_LEDTEST_DONE 0x80 | 64 | #define LP5523_LEDTEST_DONE 0x80 |
65 | #define LP5523_RESET 0xFF | 65 | #define LP5523_RESET 0xFF |
66 | #define LP5523_ADC_SHORTCIRC_LIM 80 | 66 | #define LP5523_ADC_SHORTCIRC_LIM 80 |
67 | #define LP5523_EXT_CLK_USED 0x08 | 67 | #define LP5523_EXT_CLK_USED 0x08 |
68 | 68 | ||
69 | /* Memory Page Selection */ | 69 | /* Memory Page Selection */ |
70 | #define LP5523_PAGE_ENG1 0 | 70 | #define LP5523_PAGE_ENG1 0 |
71 | #define LP5523_PAGE_ENG2 1 | 71 | #define LP5523_PAGE_ENG2 1 |
72 | #define LP5523_PAGE_ENG3 2 | 72 | #define LP5523_PAGE_ENG3 2 |
73 | 73 | ||
74 | /* Program Memory Operations */ | 74 | /* Program Memory Operations */ |
75 | #define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */ | 75 | #define LP5523_MODE_ENG1_M 0x30 /* Operation Mode Register */ |
76 | #define LP5523_MODE_ENG2_M 0x0C | 76 | #define LP5523_MODE_ENG2_M 0x0C |
77 | #define LP5523_MODE_ENG3_M 0x03 | 77 | #define LP5523_MODE_ENG3_M 0x03 |
78 | #define LP5523_LOAD_ENG1 0x10 | 78 | #define LP5523_LOAD_ENG1 0x10 |
79 | #define LP5523_LOAD_ENG2 0x04 | 79 | #define LP5523_LOAD_ENG2 0x04 |
80 | #define LP5523_LOAD_ENG3 0x01 | 80 | #define LP5523_LOAD_ENG3 0x01 |
81 | 81 | ||
82 | #define LP5523_ENG1_IS_LOADING(mode) \ | 82 | #define LP5523_ENG1_IS_LOADING(mode) \ |
83 | ((mode & LP5523_MODE_ENG1_M) == LP5523_LOAD_ENG1) | 83 | ((mode & LP5523_MODE_ENG1_M) == LP5523_LOAD_ENG1) |
84 | #define LP5523_ENG2_IS_LOADING(mode) \ | 84 | #define LP5523_ENG2_IS_LOADING(mode) \ |
85 | ((mode & LP5523_MODE_ENG2_M) == LP5523_LOAD_ENG2) | 85 | ((mode & LP5523_MODE_ENG2_M) == LP5523_LOAD_ENG2) |
86 | #define LP5523_ENG3_IS_LOADING(mode) \ | 86 | #define LP5523_ENG3_IS_LOADING(mode) \ |
87 | ((mode & LP5523_MODE_ENG3_M) == LP5523_LOAD_ENG3) | 87 | ((mode & LP5523_MODE_ENG3_M) == LP5523_LOAD_ENG3) |
88 | 88 | ||
89 | #define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */ | 89 | #define LP5523_EXEC_ENG1_M 0x30 /* Enable Register */ |
90 | #define LP5523_EXEC_ENG2_M 0x0C | 90 | #define LP5523_EXEC_ENG2_M 0x0C |
91 | #define LP5523_EXEC_ENG3_M 0x03 | 91 | #define LP5523_EXEC_ENG3_M 0x03 |
92 | #define LP5523_EXEC_M 0x3F | 92 | #define LP5523_EXEC_M 0x3F |
93 | #define LP5523_RUN_ENG1 0x20 | 93 | #define LP5523_RUN_ENG1 0x20 |
94 | #define LP5523_RUN_ENG2 0x08 | 94 | #define LP5523_RUN_ENG2 0x08 |
95 | #define LP5523_RUN_ENG3 0x02 | 95 | #define LP5523_RUN_ENG3 0x02 |
96 | 96 | ||
97 | enum lp5523_chip_id { | 97 | enum lp5523_chip_id { |
98 | LP5523, | 98 | LP5523, |
99 | LP55231, | 99 | LP55231, |
100 | }; | 100 | }; |
101 | 101 | ||
102 | static inline void lp5523_wait_opmode_done(void) | 102 | static inline void lp5523_wait_opmode_done(void) |
103 | { | 103 | { |
104 | usleep_range(1000, 2000); | 104 | usleep_range(1000, 2000); |
105 | } | 105 | } |
106 | 106 | ||
107 | static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) | 107 | static void lp5523_set_led_current(struct lp55xx_led *led, u8 led_current) |
108 | { | 108 | { |
109 | led->led_current = led_current; | 109 | led->led_current = led_current; |
110 | lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, | 110 | lp55xx_write(led->chip, LP5523_REG_LED_CURRENT_BASE + led->chan_nr, |
111 | led_current); | 111 | led_current); |
112 | } | 112 | } |
113 | 113 | ||
114 | static int lp5523_post_init_device(struct lp55xx_chip *chip) | 114 | static int lp5523_post_init_device(struct lp55xx_chip *chip) |
115 | { | 115 | { |
116 | int ret; | 116 | int ret; |
117 | 117 | ||
118 | ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE); | 118 | ret = lp55xx_write(chip, LP5523_REG_ENABLE, LP5523_ENABLE); |
119 | if (ret) | 119 | if (ret) |
120 | return ret; | 120 | return ret; |
121 | 121 | ||
122 | /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ | 122 | /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ |
123 | usleep_range(1000, 2000); | 123 | usleep_range(1000, 2000); |
124 | 124 | ||
125 | ret = lp55xx_write(chip, LP5523_REG_CONFIG, | 125 | ret = lp55xx_write(chip, LP5523_REG_CONFIG, |
126 | LP5523_AUTO_INC | LP5523_PWR_SAVE | | 126 | LP5523_AUTO_INC | LP5523_PWR_SAVE | |
127 | LP5523_CP_AUTO | LP5523_AUTO_CLK | | 127 | LP5523_CP_AUTO | LP5523_AUTO_CLK | |
128 | LP5523_PWM_PWR_SAVE); | 128 | LP5523_PWM_PWR_SAVE); |
129 | if (ret) | 129 | if (ret) |
130 | return ret; | 130 | return ret; |
131 | 131 | ||
132 | /* turn on all leds */ | 132 | /* turn on all leds */ |
133 | ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); | 133 | ret = lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_MSB, 0x01); |
134 | if (ret) | 134 | if (ret) |
135 | return ret; | 135 | return ret; |
136 | 136 | ||
137 | return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); | 137 | return lp55xx_write(chip, LP5523_REG_ENABLE_LEDS_LSB, 0xff); |
138 | } | 138 | } |
139 | 139 | ||
140 | static void lp5523_load_engine(struct lp55xx_chip *chip) | 140 | static void lp5523_load_engine(struct lp55xx_chip *chip) |
141 | { | 141 | { |
142 | enum lp55xx_engine_index idx = chip->engine_idx; | 142 | enum lp55xx_engine_index idx = chip->engine_idx; |
143 | u8 mask[] = { | 143 | u8 mask[] = { |
144 | [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M, | 144 | [LP55XX_ENGINE_1] = LP5523_MODE_ENG1_M, |
145 | [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M, | 145 | [LP55XX_ENGINE_2] = LP5523_MODE_ENG2_M, |
146 | [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M, | 146 | [LP55XX_ENGINE_3] = LP5523_MODE_ENG3_M, |
147 | }; | 147 | }; |
148 | 148 | ||
149 | u8 val[] = { | 149 | u8 val[] = { |
150 | [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, | 150 | [LP55XX_ENGINE_1] = LP5523_LOAD_ENG1, |
151 | [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, | 151 | [LP55XX_ENGINE_2] = LP5523_LOAD_ENG2, |
152 | [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, | 152 | [LP55XX_ENGINE_3] = LP5523_LOAD_ENG3, |
153 | }; | 153 | }; |
154 | 154 | ||
155 | u8 page_sel[] = { | 155 | u8 page_sel[] = { |
156 | [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, | 156 | [LP55XX_ENGINE_1] = LP5523_PAGE_ENG1, |
157 | [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, | 157 | [LP55XX_ENGINE_2] = LP5523_PAGE_ENG2, |
158 | [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, | 158 | [LP55XX_ENGINE_3] = LP5523_PAGE_ENG3, |
159 | }; | 159 | }; |
160 | 160 | ||
161 | lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); | 161 | lp55xx_update_bits(chip, LP5523_REG_OP_MODE, mask[idx], val[idx]); |
162 | 162 | ||
163 | lp5523_wait_opmode_done(); | 163 | lp5523_wait_opmode_done(); |
164 | 164 | ||
165 | lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); | 165 | lp55xx_write(chip, LP5523_REG_PROG_PAGE_SEL, page_sel[idx]); |
166 | } | 166 | } |
167 | 167 | ||
168 | static void lp5523_stop_engine(struct lp55xx_chip *chip) | 168 | static void lp5523_stop_engine(struct lp55xx_chip *chip) |
169 | { | 169 | { |
170 | lp55xx_write(chip, LP5523_REG_OP_MODE, 0); | 170 | lp55xx_write(chip, LP5523_REG_OP_MODE, 0); |
171 | lp5523_wait_opmode_done(); | 171 | lp5523_wait_opmode_done(); |
172 | } | 172 | } |
173 | 173 | ||
174 | static void lp5523_turn_off_channels(struct lp55xx_chip *chip) | 174 | static void lp5523_turn_off_channels(struct lp55xx_chip *chip) |
175 | { | 175 | { |
176 | int i; | 176 | int i; |
177 | 177 | ||
178 | for (i = 0; i < LP5523_MAX_LEDS; i++) | 178 | for (i = 0; i < LP5523_MAX_LEDS; i++) |
179 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0); | 179 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0); |
180 | } | 180 | } |
181 | 181 | ||
182 | static void lp5523_run_engine(struct lp55xx_chip *chip, bool start) | 182 | static void lp5523_run_engine(struct lp55xx_chip *chip, bool start) |
183 | { | 183 | { |
184 | int ret; | 184 | int ret; |
185 | u8 mode; | 185 | u8 mode; |
186 | u8 exec; | 186 | u8 exec; |
187 | 187 | ||
188 | /* stop engine */ | 188 | /* stop engine */ |
189 | if (!start) { | 189 | if (!start) { |
190 | lp5523_stop_engine(chip); | 190 | lp5523_stop_engine(chip); |
191 | lp5523_turn_off_channels(chip); | 191 | lp5523_turn_off_channels(chip); |
192 | return; | 192 | return; |
193 | } | 193 | } |
194 | 194 | ||
195 | /* | 195 | /* |
196 | * To run the engine, | 196 | * To run the engine, |
197 | * operation mode and enable register should updated at the same time | 197 | * operation mode and enable register should updated at the same time |
198 | */ | 198 | */ |
199 | 199 | ||
200 | ret = lp55xx_read(chip, LP5523_REG_OP_MODE, &mode); | 200 | ret = lp55xx_read(chip, LP5523_REG_OP_MODE, &mode); |
201 | if (ret) | 201 | if (ret) |
202 | return; | 202 | return; |
203 | 203 | ||
204 | ret = lp55xx_read(chip, LP5523_REG_ENABLE, &exec); | 204 | ret = lp55xx_read(chip, LP5523_REG_ENABLE, &exec); |
205 | if (ret) | 205 | if (ret) |
206 | return; | 206 | return; |
207 | 207 | ||
208 | /* change operation mode to RUN only when each engine is loading */ | 208 | /* change operation mode to RUN only when each engine is loading */ |
209 | if (LP5523_ENG1_IS_LOADING(mode)) { | 209 | if (LP5523_ENG1_IS_LOADING(mode)) { |
210 | mode = (mode & ~LP5523_MODE_ENG1_M) | LP5523_RUN_ENG1; | 210 | mode = (mode & ~LP5523_MODE_ENG1_M) | LP5523_RUN_ENG1; |
211 | exec = (exec & ~LP5523_EXEC_ENG1_M) | LP5523_RUN_ENG1; | 211 | exec = (exec & ~LP5523_EXEC_ENG1_M) | LP5523_RUN_ENG1; |
212 | } | 212 | } |
213 | 213 | ||
214 | if (LP5523_ENG2_IS_LOADING(mode)) { | 214 | if (LP5523_ENG2_IS_LOADING(mode)) { |
215 | mode = (mode & ~LP5523_MODE_ENG2_M) | LP5523_RUN_ENG2; | 215 | mode = (mode & ~LP5523_MODE_ENG2_M) | LP5523_RUN_ENG2; |
216 | exec = (exec & ~LP5523_EXEC_ENG2_M) | LP5523_RUN_ENG2; | 216 | exec = (exec & ~LP5523_EXEC_ENG2_M) | LP5523_RUN_ENG2; |
217 | } | 217 | } |
218 | 218 | ||
219 | if (LP5523_ENG3_IS_LOADING(mode)) { | 219 | if (LP5523_ENG3_IS_LOADING(mode)) { |
220 | mode = (mode & ~LP5523_MODE_ENG3_M) | LP5523_RUN_ENG3; | 220 | mode = (mode & ~LP5523_MODE_ENG3_M) | LP5523_RUN_ENG3; |
221 | exec = (exec & ~LP5523_EXEC_ENG3_M) | LP5523_RUN_ENG3; | 221 | exec = (exec & ~LP5523_EXEC_ENG3_M) | LP5523_RUN_ENG3; |
222 | } | 222 | } |
223 | 223 | ||
224 | lp55xx_write(chip, LP5523_REG_OP_MODE, mode); | 224 | lp55xx_write(chip, LP5523_REG_OP_MODE, mode); |
225 | lp5523_wait_opmode_done(); | 225 | lp5523_wait_opmode_done(); |
226 | 226 | ||
227 | lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec); | 227 | lp55xx_update_bits(chip, LP5523_REG_ENABLE, LP5523_EXEC_M, exec); |
228 | } | 228 | } |
229 | 229 | ||
230 | static int lp5523_update_program_memory(struct lp55xx_chip *chip, | 230 | static int lp5523_update_program_memory(struct lp55xx_chip *chip, |
231 | const u8 *data, size_t size) | 231 | const u8 *data, size_t size) |
232 | { | 232 | { |
233 | u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; | 233 | u8 pattern[LP5523_PROGRAM_LENGTH] = {0}; |
234 | unsigned cmd; | 234 | unsigned cmd; |
235 | char c[3]; | 235 | char c[3]; |
236 | int update_size; | 236 | int update_size; |
237 | int nrchars; | 237 | int nrchars; |
238 | int offset = 0; | 238 | int offset = 0; |
239 | int ret; | 239 | int ret; |
240 | int i; | 240 | int i; |
241 | 241 | ||
242 | /* clear program memory before updating */ | 242 | /* clear program memory before updating */ |
243 | for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) | 243 | for (i = 0; i < LP5523_PROGRAM_LENGTH; i++) |
244 | lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0); | 244 | lp55xx_write(chip, LP5523_REG_PROG_MEM + i, 0); |
245 | 245 | ||
246 | i = 0; | 246 | i = 0; |
247 | while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) { | 247 | while ((offset < size - 1) && (i < LP5523_PROGRAM_LENGTH)) { |
248 | /* separate sscanfs because length is working only for %s */ | 248 | /* separate sscanfs because length is working only for %s */ |
249 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); | 249 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); |
250 | if (ret != 1) | 250 | if (ret != 1) |
251 | goto err; | 251 | goto err; |
252 | 252 | ||
253 | ret = sscanf(c, "%2x", &cmd); | 253 | ret = sscanf(c, "%2x", &cmd); |
254 | if (ret != 1) | 254 | if (ret != 1) |
255 | goto err; | 255 | goto err; |
256 | 256 | ||
257 | pattern[i] = (u8)cmd; | 257 | pattern[i] = (u8)cmd; |
258 | offset += nrchars; | 258 | offset += nrchars; |
259 | i++; | 259 | i++; |
260 | } | 260 | } |
261 | 261 | ||
262 | /* Each instruction is 16bit long. Check that length is even */ | 262 | /* Each instruction is 16bit long. Check that length is even */ |
263 | if (i % 2) | 263 | if (i % 2) |
264 | goto err; | 264 | goto err; |
265 | 265 | ||
266 | update_size = i; | 266 | update_size = i; |
267 | for (i = 0; i < update_size; i++) | 267 | for (i = 0; i < update_size; i++) |
268 | lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]); | 268 | lp55xx_write(chip, LP5523_REG_PROG_MEM + i, pattern[i]); |
269 | 269 | ||
270 | return 0; | 270 | return 0; |
271 | 271 | ||
272 | err: | 272 | err: |
273 | dev_err(&chip->cl->dev, "wrong pattern format\n"); | 273 | dev_err(&chip->cl->dev, "wrong pattern format\n"); |
274 | return -EINVAL; | 274 | return -EINVAL; |
275 | } | 275 | } |
276 | 276 | ||
277 | static void lp5523_firmware_loaded(struct lp55xx_chip *chip) | 277 | static void lp5523_firmware_loaded(struct lp55xx_chip *chip) |
278 | { | 278 | { |
279 | const struct firmware *fw = chip->fw; | 279 | const struct firmware *fw = chip->fw; |
280 | 280 | ||
281 | if (fw->size > LP5523_PROGRAM_LENGTH) { | 281 | if (fw->size > LP5523_PROGRAM_LENGTH) { |
282 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", | 282 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", |
283 | fw->size); | 283 | fw->size); |
284 | return; | 284 | return; |
285 | } | 285 | } |
286 | 286 | ||
287 | /* | 287 | /* |
288 | * Program momery sequence | 288 | * Program momery sequence |
289 | * 1) set engine mode to "LOAD" | 289 | * 1) set engine mode to "LOAD" |
290 | * 2) write firmware data into program memory | 290 | * 2) write firmware data into program memory |
291 | */ | 291 | */ |
292 | 292 | ||
293 | lp5523_load_engine(chip); | 293 | lp5523_load_engine(chip); |
294 | lp5523_update_program_memory(chip, fw->data, fw->size); | 294 | lp5523_update_program_memory(chip, fw->data, fw->size); |
295 | } | 295 | } |
296 | 296 | ||
297 | static ssize_t lp5523_selftest(struct device *dev, | 297 | static ssize_t lp5523_selftest(struct device *dev, |
298 | struct device_attribute *attr, | 298 | struct device_attribute *attr, |
299 | char *buf) | 299 | char *buf) |
300 | { | 300 | { |
301 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 301 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
302 | struct lp55xx_chip *chip = led->chip; | 302 | struct lp55xx_chip *chip = led->chip; |
303 | struct lp55xx_platform_data *pdata = chip->pdata; | 303 | struct lp55xx_platform_data *pdata = chip->pdata; |
304 | int i, ret, pos = 0; | 304 | int i, ret, pos = 0; |
305 | u8 status, adc, vdd; | 305 | u8 status, adc, vdd; |
306 | 306 | ||
307 | mutex_lock(&chip->lock); | 307 | mutex_lock(&chip->lock); |
308 | 308 | ||
309 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); | 309 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); |
310 | if (ret < 0) | 310 | if (ret < 0) |
311 | goto fail; | 311 | goto fail; |
312 | 312 | ||
313 | /* Check that ext clock is really in use if requested */ | 313 | /* Check that ext clock is really in use if requested */ |
314 | if (pdata->clock_mode == LP55XX_CLOCK_EXT) { | 314 | if (pdata->clock_mode == LP55XX_CLOCK_EXT) { |
315 | if ((status & LP5523_EXT_CLK_USED) == 0) | 315 | if ((status & LP5523_EXT_CLK_USED) == 0) |
316 | goto fail; | 316 | goto fail; |
317 | } | 317 | } |
318 | 318 | ||
319 | /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ | 319 | /* Measure VDD (i.e. VBAT) first (channel 16 corresponds to VDD) */ |
320 | lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); | 320 | lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, LP5523_EN_LEDTEST | 16); |
321 | usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ | 321 | usleep_range(3000, 6000); /* ADC conversion time is typically 2.7 ms */ |
322 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); | 322 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); |
323 | if (ret < 0) | 323 | if (ret < 0) |
324 | goto fail; | 324 | goto fail; |
325 | 325 | ||
326 | if (!(status & LP5523_LEDTEST_DONE)) | 326 | if (!(status & LP5523_LEDTEST_DONE)) |
327 | usleep_range(3000, 6000); /* Was not ready. Wait little bit */ | 327 | usleep_range(3000, 6000); /* Was not ready. Wait little bit */ |
328 | 328 | ||
329 | ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd); | 329 | ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &vdd); |
330 | if (ret < 0) | 330 | if (ret < 0) |
331 | goto fail; | 331 | goto fail; |
332 | 332 | ||
333 | vdd--; /* There may be some fluctuation in measurement */ | 333 | vdd--; /* There may be some fluctuation in measurement */ |
334 | 334 | ||
335 | for (i = 0; i < LP5523_MAX_LEDS; i++) { | 335 | for (i = 0; i < LP5523_MAX_LEDS; i++) { |
336 | /* Skip non-existing channels */ | 336 | /* Skip non-existing channels */ |
337 | if (pdata->led_config[i].led_current == 0) | 337 | if (pdata->led_config[i].led_current == 0) |
338 | continue; | 338 | continue; |
339 | 339 | ||
340 | /* Set default current */ | 340 | /* Set default current */ |
341 | lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, | 341 | lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, |
342 | pdata->led_config[i].led_current); | 342 | pdata->led_config[i].led_current); |
343 | 343 | ||
344 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0xff); | 344 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0xff); |
345 | /* let current stabilize 2 - 4ms before measurements start */ | 345 | /* let current stabilize 2 - 4ms before measurements start */ |
346 | usleep_range(2000, 4000); | 346 | usleep_range(2000, 4000); |
347 | lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, | 347 | lp55xx_write(chip, LP5523_REG_LED_TEST_CTRL, |
348 | LP5523_EN_LEDTEST | i); | 348 | LP5523_EN_LEDTEST | i); |
349 | /* ADC conversion time is 2.7 ms typically */ | 349 | /* ADC conversion time is 2.7 ms typically */ |
350 | usleep_range(3000, 6000); | 350 | usleep_range(3000, 6000); |
351 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); | 351 | ret = lp55xx_read(chip, LP5523_REG_STATUS, &status); |
352 | if (ret < 0) | 352 | if (ret < 0) |
353 | goto fail; | 353 | goto fail; |
354 | 354 | ||
355 | if (!(status & LP5523_LEDTEST_DONE)) | 355 | if (!(status & LP5523_LEDTEST_DONE)) |
356 | usleep_range(3000, 6000);/* Was not ready. Wait. */ | 356 | usleep_range(3000, 6000);/* Was not ready. Wait. */ |
357 | 357 | ||
358 | ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc); | 358 | ret = lp55xx_read(chip, LP5523_REG_LED_TEST_ADC, &adc); |
359 | if (ret < 0) | 359 | if (ret < 0) |
360 | goto fail; | 360 | goto fail; |
361 | 361 | ||
362 | if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) | 362 | if (adc >= vdd || adc < LP5523_ADC_SHORTCIRC_LIM) |
363 | pos += sprintf(buf + pos, "LED %d FAIL\n", i); | 363 | pos += sprintf(buf + pos, "LED %d FAIL\n", i); |
364 | 364 | ||
365 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0x00); | 365 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + i, 0x00); |
366 | 366 | ||
367 | /* Restore current */ | 367 | /* Restore current */ |
368 | lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, | 368 | lp55xx_write(chip, LP5523_REG_LED_CURRENT_BASE + i, |
369 | led->led_current); | 369 | led->led_current); |
370 | led++; | 370 | led++; |
371 | } | 371 | } |
372 | if (pos == 0) | 372 | if (pos == 0) |
373 | pos = sprintf(buf, "OK\n"); | 373 | pos = sprintf(buf, "OK\n"); |
374 | goto release_lock; | 374 | goto release_lock; |
375 | fail: | 375 | fail: |
376 | pos = sprintf(buf, "FAIL\n"); | 376 | pos = sprintf(buf, "FAIL\n"); |
377 | 377 | ||
378 | release_lock: | 378 | release_lock: |
379 | mutex_unlock(&chip->lock); | 379 | mutex_unlock(&chip->lock); |
380 | 380 | ||
381 | return pos; | 381 | return pos; |
382 | } | 382 | } |
383 | 383 | ||
384 | static void lp5523_led_brightness_work(struct work_struct *work) | 384 | static void lp5523_led_brightness_work(struct work_struct *work) |
385 | { | 385 | { |
386 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, | 386 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, |
387 | brightness_work); | 387 | brightness_work); |
388 | struct lp55xx_chip *chip = led->chip; | 388 | struct lp55xx_chip *chip = led->chip; |
389 | 389 | ||
390 | mutex_lock(&chip->lock); | 390 | mutex_lock(&chip->lock); |
391 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, | 391 | lp55xx_write(chip, LP5523_REG_LED_PWM_BASE + led->chan_nr, |
392 | led->brightness); | 392 | led->brightness); |
393 | mutex_unlock(&chip->lock); | 393 | mutex_unlock(&chip->lock); |
394 | } | 394 | } |
395 | 395 | ||
396 | static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); | 396 | static DEVICE_ATTR(selftest, S_IRUGO, lp5523_selftest, NULL); |
397 | 397 | ||
398 | static struct attribute *lp5523_attributes[] = { | 398 | static struct attribute *lp5523_attributes[] = { |
399 | &dev_attr_selftest.attr, | 399 | &dev_attr_selftest.attr, |
400 | NULL, | 400 | NULL, |
401 | }; | 401 | }; |
402 | 402 | ||
403 | static const struct attribute_group lp5523_group = { | 403 | static const struct attribute_group lp5523_group = { |
404 | .attrs = lp5523_attributes, | 404 | .attrs = lp5523_attributes, |
405 | }; | 405 | }; |
406 | 406 | ||
407 | /* Chip specific configurations */ | 407 | /* Chip specific configurations */ |
408 | static struct lp55xx_device_config lp5523_cfg = { | 408 | static struct lp55xx_device_config lp5523_cfg = { |
409 | .reset = { | 409 | .reset = { |
410 | .addr = LP5523_REG_RESET, | 410 | .addr = LP5523_REG_RESET, |
411 | .val = LP5523_RESET, | 411 | .val = LP5523_RESET, |
412 | }, | 412 | }, |
413 | .enable = { | 413 | .enable = { |
414 | .addr = LP5523_REG_ENABLE, | 414 | .addr = LP5523_REG_ENABLE, |
415 | .val = LP5523_ENABLE, | 415 | .val = LP5523_ENABLE, |
416 | }, | 416 | }, |
417 | .max_channel = LP5523_MAX_LEDS, | 417 | .max_channel = LP5523_MAX_LEDS, |
418 | .post_init_device = lp5523_post_init_device, | 418 | .post_init_device = lp5523_post_init_device, |
419 | .brightness_work_fn = lp5523_led_brightness_work, | 419 | .brightness_work_fn = lp5523_led_brightness_work, |
420 | .set_led_current = lp5523_set_led_current, | 420 | .set_led_current = lp5523_set_led_current, |
421 | .firmware_cb = lp5523_firmware_loaded, | 421 | .firmware_cb = lp5523_firmware_loaded, |
422 | .run_engine = lp5523_run_engine, | 422 | .run_engine = lp5523_run_engine, |
423 | .dev_attr_group = &lp5523_group, | 423 | .dev_attr_group = &lp5523_group, |
424 | }; | 424 | }; |
425 | 425 | ||
426 | static int lp5523_probe(struct i2c_client *client, | 426 | static int lp5523_probe(struct i2c_client *client, |
427 | const struct i2c_device_id *id) | 427 | const struct i2c_device_id *id) |
428 | { | 428 | { |
429 | int ret; | 429 | int ret; |
430 | struct lp55xx_chip *chip; | 430 | struct lp55xx_chip *chip; |
431 | struct lp55xx_led *led; | 431 | struct lp55xx_led *led; |
432 | struct lp55xx_platform_data *pdata; | 432 | struct lp55xx_platform_data *pdata; |
433 | struct device_node *np = client->dev.of_node; | 433 | struct device_node *np = client->dev.of_node; |
434 | 434 | ||
435 | if (!client->dev.platform_data) { | 435 | if (!dev_get_platdata(&client->dev)) { |
436 | if (np) { | 436 | if (np) { |
437 | ret = lp55xx_of_populate_pdata(&client->dev, np); | 437 | ret = lp55xx_of_populate_pdata(&client->dev, np); |
438 | if (ret < 0) | 438 | if (ret < 0) |
439 | return ret; | 439 | return ret; |
440 | } else { | 440 | } else { |
441 | dev_err(&client->dev, "no platform data\n"); | 441 | dev_err(&client->dev, "no platform data\n"); |
442 | return -EINVAL; | 442 | return -EINVAL; |
443 | } | 443 | } |
444 | } | 444 | } |
445 | pdata = client->dev.platform_data; | 445 | pdata = dev_get_platdata(&client->dev); |
446 | 446 | ||
447 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | 447 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
448 | if (!chip) | 448 | if (!chip) |
449 | return -ENOMEM; | 449 | return -ENOMEM; |
450 | 450 | ||
451 | led = devm_kzalloc(&client->dev, | 451 | led = devm_kzalloc(&client->dev, |
452 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); | 452 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); |
453 | if (!led) | 453 | if (!led) |
454 | return -ENOMEM; | 454 | return -ENOMEM; |
455 | 455 | ||
456 | chip->cl = client; | 456 | chip->cl = client; |
457 | chip->pdata = pdata; | 457 | chip->pdata = pdata; |
458 | chip->cfg = &lp5523_cfg; | 458 | chip->cfg = &lp5523_cfg; |
459 | 459 | ||
460 | mutex_init(&chip->lock); | 460 | mutex_init(&chip->lock); |
461 | 461 | ||
462 | i2c_set_clientdata(client, led); | 462 | i2c_set_clientdata(client, led); |
463 | 463 | ||
464 | ret = lp55xx_init_device(chip); | 464 | ret = lp55xx_init_device(chip); |
465 | if (ret) | 465 | if (ret) |
466 | goto err_init; | 466 | goto err_init; |
467 | 467 | ||
468 | dev_info(&client->dev, "%s Programmable led chip found\n", id->name); | 468 | dev_info(&client->dev, "%s Programmable led chip found\n", id->name); |
469 | 469 | ||
470 | ret = lp55xx_register_leds(led, chip); | 470 | ret = lp55xx_register_leds(led, chip); |
471 | if (ret) | 471 | if (ret) |
472 | goto err_register_leds; | 472 | goto err_register_leds; |
473 | 473 | ||
474 | ret = lp55xx_register_sysfs(chip); | 474 | ret = lp55xx_register_sysfs(chip); |
475 | if (ret) { | 475 | if (ret) { |
476 | dev_err(&client->dev, "registering sysfs failed\n"); | 476 | dev_err(&client->dev, "registering sysfs failed\n"); |
477 | goto err_register_sysfs; | 477 | goto err_register_sysfs; |
478 | } | 478 | } |
479 | 479 | ||
480 | return 0; | 480 | return 0; |
481 | 481 | ||
482 | err_register_sysfs: | 482 | err_register_sysfs: |
483 | lp55xx_unregister_leds(led, chip); | 483 | lp55xx_unregister_leds(led, chip); |
484 | err_register_leds: | 484 | err_register_leds: |
485 | lp55xx_deinit_device(chip); | 485 | lp55xx_deinit_device(chip); |
486 | err_init: | 486 | err_init: |
487 | return ret; | 487 | return ret; |
488 | } | 488 | } |
489 | 489 | ||
490 | static int lp5523_remove(struct i2c_client *client) | 490 | static int lp5523_remove(struct i2c_client *client) |
491 | { | 491 | { |
492 | struct lp55xx_led *led = i2c_get_clientdata(client); | 492 | struct lp55xx_led *led = i2c_get_clientdata(client); |
493 | struct lp55xx_chip *chip = led->chip; | 493 | struct lp55xx_chip *chip = led->chip; |
494 | 494 | ||
495 | lp5523_stop_engine(chip); | 495 | lp5523_stop_engine(chip); |
496 | lp55xx_unregister_sysfs(chip); | 496 | lp55xx_unregister_sysfs(chip); |
497 | lp55xx_unregister_leds(led, chip); | 497 | lp55xx_unregister_leds(led, chip); |
498 | lp55xx_deinit_device(chip); | 498 | lp55xx_deinit_device(chip); |
499 | 499 | ||
500 | return 0; | 500 | return 0; |
501 | } | 501 | } |
502 | 502 | ||
503 | static const struct i2c_device_id lp5523_id[] = { | 503 | static const struct i2c_device_id lp5523_id[] = { |
504 | { "lp5523", LP5523 }, | 504 | { "lp5523", LP5523 }, |
505 | { "lp55231", LP55231 }, | 505 | { "lp55231", LP55231 }, |
506 | { } | 506 | { } |
507 | }; | 507 | }; |
508 | 508 | ||
509 | MODULE_DEVICE_TABLE(i2c, lp5523_id); | 509 | MODULE_DEVICE_TABLE(i2c, lp5523_id); |
510 | 510 | ||
511 | #ifdef CONFIG_OF | 511 | #ifdef CONFIG_OF |
512 | static const struct of_device_id of_lp5523_leds_match[] = { | 512 | static const struct of_device_id of_lp5523_leds_match[] = { |
513 | { .compatible = "national,lp5523", }, | 513 | { .compatible = "national,lp5523", }, |
514 | {}, | 514 | {}, |
515 | }; | 515 | }; |
516 | 516 | ||
517 | MODULE_DEVICE_TABLE(of, of_lp5523_leds_match); | 517 | MODULE_DEVICE_TABLE(of, of_lp5523_leds_match); |
518 | #endif | 518 | #endif |
519 | 519 | ||
520 | static struct i2c_driver lp5523_driver = { | 520 | static struct i2c_driver lp5523_driver = { |
521 | .driver = { | 521 | .driver = { |
522 | .name = "lp5523x", | 522 | .name = "lp5523x", |
523 | .of_match_table = of_match_ptr(of_lp5523_leds_match), | 523 | .of_match_table = of_match_ptr(of_lp5523_leds_match), |
524 | }, | 524 | }, |
525 | .probe = lp5523_probe, | 525 | .probe = lp5523_probe, |
526 | .remove = lp5523_remove, | 526 | .remove = lp5523_remove, |
527 | .id_table = lp5523_id, | 527 | .id_table = lp5523_id, |
528 | }; | 528 | }; |
529 | 529 | ||
530 | module_i2c_driver(lp5523_driver); | 530 | module_i2c_driver(lp5523_driver); |
531 | 531 | ||
532 | MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>"); | 532 | MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>"); |
533 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); | 533 | MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>"); |
534 | MODULE_DESCRIPTION("LP5523 LED engine"); | 534 | MODULE_DESCRIPTION("LP5523 LED engine"); |
535 | MODULE_LICENSE("GPL"); | 535 | MODULE_LICENSE("GPL"); |
536 | 536 |
drivers/leds/leds-lp5562.c
1 | /* | 1 | /* |
2 | * LP5562 LED driver | 2 | * LP5562 LED driver |
3 | * | 3 | * |
4 | * Copyright (C) 2013 Texas Instruments | 4 | * Copyright (C) 2013 Texas Instruments |
5 | * | 5 | * |
6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> | 6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 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 | 9 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
14 | #include <linux/firmware.h> | 14 | #include <linux/firmware.h> |
15 | #include <linux/i2c.h> | 15 | #include <linux/i2c.h> |
16 | #include <linux/init.h> | 16 | #include <linux/init.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
20 | #include <linux/platform_data/leds-lp55xx.h> | 20 | #include <linux/platform_data/leds-lp55xx.h> |
21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | 22 | ||
23 | #include "leds-lp55xx-common.h" | 23 | #include "leds-lp55xx-common.h" |
24 | 24 | ||
25 | #define LP5562_PROGRAM_LENGTH 32 | 25 | #define LP5562_PROGRAM_LENGTH 32 |
26 | #define LP5562_MAX_LEDS 4 | 26 | #define LP5562_MAX_LEDS 4 |
27 | 27 | ||
28 | /* ENABLE Register 00h */ | 28 | /* ENABLE Register 00h */ |
29 | #define LP5562_REG_ENABLE 0x00 | 29 | #define LP5562_REG_ENABLE 0x00 |
30 | #define LP5562_EXEC_ENG1_M 0x30 | 30 | #define LP5562_EXEC_ENG1_M 0x30 |
31 | #define LP5562_EXEC_ENG2_M 0x0C | 31 | #define LP5562_EXEC_ENG2_M 0x0C |
32 | #define LP5562_EXEC_ENG3_M 0x03 | 32 | #define LP5562_EXEC_ENG3_M 0x03 |
33 | #define LP5562_EXEC_M 0x3F | 33 | #define LP5562_EXEC_M 0x3F |
34 | #define LP5562_MASTER_ENABLE 0x40 /* Chip master enable */ | 34 | #define LP5562_MASTER_ENABLE 0x40 /* Chip master enable */ |
35 | #define LP5562_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ | 35 | #define LP5562_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */ |
36 | #define LP5562_EXEC_RUN 0x2A | 36 | #define LP5562_EXEC_RUN 0x2A |
37 | #define LP5562_ENABLE_DEFAULT \ | 37 | #define LP5562_ENABLE_DEFAULT \ |
38 | (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM) | 38 | (LP5562_MASTER_ENABLE | LP5562_LOGARITHMIC_PWM) |
39 | #define LP5562_ENABLE_RUN_PROGRAM \ | 39 | #define LP5562_ENABLE_RUN_PROGRAM \ |
40 | (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN) | 40 | (LP5562_ENABLE_DEFAULT | LP5562_EXEC_RUN) |
41 | 41 | ||
42 | /* OPMODE Register 01h */ | 42 | /* OPMODE Register 01h */ |
43 | #define LP5562_REG_OP_MODE 0x01 | 43 | #define LP5562_REG_OP_MODE 0x01 |
44 | #define LP5562_MODE_ENG1_M 0x30 | 44 | #define LP5562_MODE_ENG1_M 0x30 |
45 | #define LP5562_MODE_ENG2_M 0x0C | 45 | #define LP5562_MODE_ENG2_M 0x0C |
46 | #define LP5562_MODE_ENG3_M 0x03 | 46 | #define LP5562_MODE_ENG3_M 0x03 |
47 | #define LP5562_LOAD_ENG1 0x10 | 47 | #define LP5562_LOAD_ENG1 0x10 |
48 | #define LP5562_LOAD_ENG2 0x04 | 48 | #define LP5562_LOAD_ENG2 0x04 |
49 | #define LP5562_LOAD_ENG3 0x01 | 49 | #define LP5562_LOAD_ENG3 0x01 |
50 | #define LP5562_RUN_ENG1 0x20 | 50 | #define LP5562_RUN_ENG1 0x20 |
51 | #define LP5562_RUN_ENG2 0x08 | 51 | #define LP5562_RUN_ENG2 0x08 |
52 | #define LP5562_RUN_ENG3 0x02 | 52 | #define LP5562_RUN_ENG3 0x02 |
53 | #define LP5562_ENG1_IS_LOADING(mode) \ | 53 | #define LP5562_ENG1_IS_LOADING(mode) \ |
54 | ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1) | 54 | ((mode & LP5562_MODE_ENG1_M) == LP5562_LOAD_ENG1) |
55 | #define LP5562_ENG2_IS_LOADING(mode) \ | 55 | #define LP5562_ENG2_IS_LOADING(mode) \ |
56 | ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2) | 56 | ((mode & LP5562_MODE_ENG2_M) == LP5562_LOAD_ENG2) |
57 | #define LP5562_ENG3_IS_LOADING(mode) \ | 57 | #define LP5562_ENG3_IS_LOADING(mode) \ |
58 | ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3) | 58 | ((mode & LP5562_MODE_ENG3_M) == LP5562_LOAD_ENG3) |
59 | 59 | ||
60 | /* BRIGHTNESS Registers */ | 60 | /* BRIGHTNESS Registers */ |
61 | #define LP5562_REG_R_PWM 0x04 | 61 | #define LP5562_REG_R_PWM 0x04 |
62 | #define LP5562_REG_G_PWM 0x03 | 62 | #define LP5562_REG_G_PWM 0x03 |
63 | #define LP5562_REG_B_PWM 0x02 | 63 | #define LP5562_REG_B_PWM 0x02 |
64 | #define LP5562_REG_W_PWM 0x0E | 64 | #define LP5562_REG_W_PWM 0x0E |
65 | 65 | ||
66 | /* CURRENT Registers */ | 66 | /* CURRENT Registers */ |
67 | #define LP5562_REG_R_CURRENT 0x07 | 67 | #define LP5562_REG_R_CURRENT 0x07 |
68 | #define LP5562_REG_G_CURRENT 0x06 | 68 | #define LP5562_REG_G_CURRENT 0x06 |
69 | #define LP5562_REG_B_CURRENT 0x05 | 69 | #define LP5562_REG_B_CURRENT 0x05 |
70 | #define LP5562_REG_W_CURRENT 0x0F | 70 | #define LP5562_REG_W_CURRENT 0x0F |
71 | 71 | ||
72 | /* CONFIG Register 08h */ | 72 | /* CONFIG Register 08h */ |
73 | #define LP5562_REG_CONFIG 0x08 | 73 | #define LP5562_REG_CONFIG 0x08 |
74 | #define LP5562_PWM_HF 0x40 | 74 | #define LP5562_PWM_HF 0x40 |
75 | #define LP5562_PWRSAVE_EN 0x20 | 75 | #define LP5562_PWRSAVE_EN 0x20 |
76 | #define LP5562_CLK_INT 0x01 /* Internal clock */ | 76 | #define LP5562_CLK_INT 0x01 /* Internal clock */ |
77 | #define LP5562_DEFAULT_CFG (LP5562_PWM_HF | LP5562_PWRSAVE_EN) | 77 | #define LP5562_DEFAULT_CFG (LP5562_PWM_HF | LP5562_PWRSAVE_EN) |
78 | 78 | ||
79 | /* RESET Register 0Dh */ | 79 | /* RESET Register 0Dh */ |
80 | #define LP5562_REG_RESET 0x0D | 80 | #define LP5562_REG_RESET 0x0D |
81 | #define LP5562_RESET 0xFF | 81 | #define LP5562_RESET 0xFF |
82 | 82 | ||
83 | /* PROGRAM ENGINE Registers */ | 83 | /* PROGRAM ENGINE Registers */ |
84 | #define LP5562_REG_PROG_MEM_ENG1 0x10 | 84 | #define LP5562_REG_PROG_MEM_ENG1 0x10 |
85 | #define LP5562_REG_PROG_MEM_ENG2 0x30 | 85 | #define LP5562_REG_PROG_MEM_ENG2 0x30 |
86 | #define LP5562_REG_PROG_MEM_ENG3 0x50 | 86 | #define LP5562_REG_PROG_MEM_ENG3 0x50 |
87 | 87 | ||
88 | /* LEDMAP Register 70h */ | 88 | /* LEDMAP Register 70h */ |
89 | #define LP5562_REG_ENG_SEL 0x70 | 89 | #define LP5562_REG_ENG_SEL 0x70 |
90 | #define LP5562_ENG_SEL_PWM 0 | 90 | #define LP5562_ENG_SEL_PWM 0 |
91 | #define LP5562_ENG_FOR_RGB_M 0x3F | 91 | #define LP5562_ENG_FOR_RGB_M 0x3F |
92 | #define LP5562_ENG_SEL_RGB 0x1B /* R:ENG1, G:ENG2, B:ENG3 */ | 92 | #define LP5562_ENG_SEL_RGB 0x1B /* R:ENG1, G:ENG2, B:ENG3 */ |
93 | #define LP5562_ENG_FOR_W_M 0xC0 | 93 | #define LP5562_ENG_FOR_W_M 0xC0 |
94 | #define LP5562_ENG1_FOR_W 0x40 /* W:ENG1 */ | 94 | #define LP5562_ENG1_FOR_W 0x40 /* W:ENG1 */ |
95 | #define LP5562_ENG2_FOR_W 0x80 /* W:ENG2 */ | 95 | #define LP5562_ENG2_FOR_W 0x80 /* W:ENG2 */ |
96 | #define LP5562_ENG3_FOR_W 0xC0 /* W:ENG3 */ | 96 | #define LP5562_ENG3_FOR_W 0xC0 /* W:ENG3 */ |
97 | 97 | ||
98 | /* Program Commands */ | 98 | /* Program Commands */ |
99 | #define LP5562_CMD_DISABLE 0x00 | 99 | #define LP5562_CMD_DISABLE 0x00 |
100 | #define LP5562_CMD_LOAD 0x15 | 100 | #define LP5562_CMD_LOAD 0x15 |
101 | #define LP5562_CMD_RUN 0x2A | 101 | #define LP5562_CMD_RUN 0x2A |
102 | #define LP5562_CMD_DIRECT 0x3F | 102 | #define LP5562_CMD_DIRECT 0x3F |
103 | #define LP5562_PATTERN_OFF 0 | 103 | #define LP5562_PATTERN_OFF 0 |
104 | 104 | ||
105 | static inline void lp5562_wait_opmode_done(void) | 105 | static inline void lp5562_wait_opmode_done(void) |
106 | { | 106 | { |
107 | /* operation mode change needs to be longer than 153 us */ | 107 | /* operation mode change needs to be longer than 153 us */ |
108 | usleep_range(200, 300); | 108 | usleep_range(200, 300); |
109 | } | 109 | } |
110 | 110 | ||
111 | static inline void lp5562_wait_enable_done(void) | 111 | static inline void lp5562_wait_enable_done(void) |
112 | { | 112 | { |
113 | /* it takes more 488 us to update ENABLE register */ | 113 | /* it takes more 488 us to update ENABLE register */ |
114 | usleep_range(500, 600); | 114 | usleep_range(500, 600); |
115 | } | 115 | } |
116 | 116 | ||
117 | static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current) | 117 | static void lp5562_set_led_current(struct lp55xx_led *led, u8 led_current) |
118 | { | 118 | { |
119 | u8 addr[] = { | 119 | u8 addr[] = { |
120 | LP5562_REG_R_CURRENT, | 120 | LP5562_REG_R_CURRENT, |
121 | LP5562_REG_G_CURRENT, | 121 | LP5562_REG_G_CURRENT, |
122 | LP5562_REG_B_CURRENT, | 122 | LP5562_REG_B_CURRENT, |
123 | LP5562_REG_W_CURRENT, | 123 | LP5562_REG_W_CURRENT, |
124 | }; | 124 | }; |
125 | 125 | ||
126 | led->led_current = led_current; | 126 | led->led_current = led_current; |
127 | lp55xx_write(led->chip, addr[led->chan_nr], led_current); | 127 | lp55xx_write(led->chip, addr[led->chan_nr], led_current); |
128 | } | 128 | } |
129 | 129 | ||
130 | static void lp5562_load_engine(struct lp55xx_chip *chip) | 130 | static void lp5562_load_engine(struct lp55xx_chip *chip) |
131 | { | 131 | { |
132 | enum lp55xx_engine_index idx = chip->engine_idx; | 132 | enum lp55xx_engine_index idx = chip->engine_idx; |
133 | u8 mask[] = { | 133 | u8 mask[] = { |
134 | [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M, | 134 | [LP55XX_ENGINE_1] = LP5562_MODE_ENG1_M, |
135 | [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M, | 135 | [LP55XX_ENGINE_2] = LP5562_MODE_ENG2_M, |
136 | [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M, | 136 | [LP55XX_ENGINE_3] = LP5562_MODE_ENG3_M, |
137 | }; | 137 | }; |
138 | 138 | ||
139 | u8 val[] = { | 139 | u8 val[] = { |
140 | [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1, | 140 | [LP55XX_ENGINE_1] = LP5562_LOAD_ENG1, |
141 | [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2, | 141 | [LP55XX_ENGINE_2] = LP5562_LOAD_ENG2, |
142 | [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3, | 142 | [LP55XX_ENGINE_3] = LP5562_LOAD_ENG3, |
143 | }; | 143 | }; |
144 | 144 | ||
145 | lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]); | 145 | lp55xx_update_bits(chip, LP5562_REG_OP_MODE, mask[idx], val[idx]); |
146 | 146 | ||
147 | lp5562_wait_opmode_done(); | 147 | lp5562_wait_opmode_done(); |
148 | } | 148 | } |
149 | 149 | ||
150 | static void lp5562_stop_engine(struct lp55xx_chip *chip) | 150 | static void lp5562_stop_engine(struct lp55xx_chip *chip) |
151 | { | 151 | { |
152 | lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE); | 152 | lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DISABLE); |
153 | lp5562_wait_opmode_done(); | 153 | lp5562_wait_opmode_done(); |
154 | } | 154 | } |
155 | 155 | ||
156 | static void lp5562_run_engine(struct lp55xx_chip *chip, bool start) | 156 | static void lp5562_run_engine(struct lp55xx_chip *chip, bool start) |
157 | { | 157 | { |
158 | int ret; | 158 | int ret; |
159 | u8 mode; | 159 | u8 mode; |
160 | u8 exec; | 160 | u8 exec; |
161 | 161 | ||
162 | /* stop engine */ | 162 | /* stop engine */ |
163 | if (!start) { | 163 | if (!start) { |
164 | lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT); | 164 | lp55xx_write(chip, LP5562_REG_ENABLE, LP5562_ENABLE_DEFAULT); |
165 | lp5562_wait_enable_done(); | 165 | lp5562_wait_enable_done(); |
166 | lp5562_stop_engine(chip); | 166 | lp5562_stop_engine(chip); |
167 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM); | 167 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM); |
168 | lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT); | 168 | lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT); |
169 | lp5562_wait_opmode_done(); | 169 | lp5562_wait_opmode_done(); |
170 | return; | 170 | return; |
171 | } | 171 | } |
172 | 172 | ||
173 | /* | 173 | /* |
174 | * To run the engine, | 174 | * To run the engine, |
175 | * operation mode and enable register should updated at the same time | 175 | * operation mode and enable register should updated at the same time |
176 | */ | 176 | */ |
177 | 177 | ||
178 | ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode); | 178 | ret = lp55xx_read(chip, LP5562_REG_OP_MODE, &mode); |
179 | if (ret) | 179 | if (ret) |
180 | return; | 180 | return; |
181 | 181 | ||
182 | ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec); | 182 | ret = lp55xx_read(chip, LP5562_REG_ENABLE, &exec); |
183 | if (ret) | 183 | if (ret) |
184 | return; | 184 | return; |
185 | 185 | ||
186 | /* change operation mode to RUN only when each engine is loading */ | 186 | /* change operation mode to RUN only when each engine is loading */ |
187 | if (LP5562_ENG1_IS_LOADING(mode)) { | 187 | if (LP5562_ENG1_IS_LOADING(mode)) { |
188 | mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1; | 188 | mode = (mode & ~LP5562_MODE_ENG1_M) | LP5562_RUN_ENG1; |
189 | exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1; | 189 | exec = (exec & ~LP5562_EXEC_ENG1_M) | LP5562_RUN_ENG1; |
190 | } | 190 | } |
191 | 191 | ||
192 | if (LP5562_ENG2_IS_LOADING(mode)) { | 192 | if (LP5562_ENG2_IS_LOADING(mode)) { |
193 | mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2; | 193 | mode = (mode & ~LP5562_MODE_ENG2_M) | LP5562_RUN_ENG2; |
194 | exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2; | 194 | exec = (exec & ~LP5562_EXEC_ENG2_M) | LP5562_RUN_ENG2; |
195 | } | 195 | } |
196 | 196 | ||
197 | if (LP5562_ENG3_IS_LOADING(mode)) { | 197 | if (LP5562_ENG3_IS_LOADING(mode)) { |
198 | mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3; | 198 | mode = (mode & ~LP5562_MODE_ENG3_M) | LP5562_RUN_ENG3; |
199 | exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3; | 199 | exec = (exec & ~LP5562_EXEC_ENG3_M) | LP5562_RUN_ENG3; |
200 | } | 200 | } |
201 | 201 | ||
202 | lp55xx_write(chip, LP5562_REG_OP_MODE, mode); | 202 | lp55xx_write(chip, LP5562_REG_OP_MODE, mode); |
203 | lp5562_wait_opmode_done(); | 203 | lp5562_wait_opmode_done(); |
204 | 204 | ||
205 | lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec); | 205 | lp55xx_update_bits(chip, LP5562_REG_ENABLE, LP5562_EXEC_M, exec); |
206 | lp5562_wait_enable_done(); | 206 | lp5562_wait_enable_done(); |
207 | } | 207 | } |
208 | 208 | ||
209 | static int lp5562_update_firmware(struct lp55xx_chip *chip, | 209 | static int lp5562_update_firmware(struct lp55xx_chip *chip, |
210 | const u8 *data, size_t size) | 210 | const u8 *data, size_t size) |
211 | { | 211 | { |
212 | enum lp55xx_engine_index idx = chip->engine_idx; | 212 | enum lp55xx_engine_index idx = chip->engine_idx; |
213 | u8 pattern[LP5562_PROGRAM_LENGTH] = {0}; | 213 | u8 pattern[LP5562_PROGRAM_LENGTH] = {0}; |
214 | u8 addr[] = { | 214 | u8 addr[] = { |
215 | [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1, | 215 | [LP55XX_ENGINE_1] = LP5562_REG_PROG_MEM_ENG1, |
216 | [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2, | 216 | [LP55XX_ENGINE_2] = LP5562_REG_PROG_MEM_ENG2, |
217 | [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3, | 217 | [LP55XX_ENGINE_3] = LP5562_REG_PROG_MEM_ENG3, |
218 | }; | 218 | }; |
219 | unsigned cmd; | 219 | unsigned cmd; |
220 | char c[3]; | 220 | char c[3]; |
221 | int program_size; | 221 | int program_size; |
222 | int nrchars; | 222 | int nrchars; |
223 | int offset = 0; | 223 | int offset = 0; |
224 | int ret; | 224 | int ret; |
225 | int i; | 225 | int i; |
226 | 226 | ||
227 | /* clear program memory before updating */ | 227 | /* clear program memory before updating */ |
228 | for (i = 0; i < LP5562_PROGRAM_LENGTH; i++) | 228 | for (i = 0; i < LP5562_PROGRAM_LENGTH; i++) |
229 | lp55xx_write(chip, addr[idx] + i, 0); | 229 | lp55xx_write(chip, addr[idx] + i, 0); |
230 | 230 | ||
231 | i = 0; | 231 | i = 0; |
232 | while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) { | 232 | while ((offset < size - 1) && (i < LP5562_PROGRAM_LENGTH)) { |
233 | /* separate sscanfs because length is working only for %s */ | 233 | /* separate sscanfs because length is working only for %s */ |
234 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); | 234 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); |
235 | if (ret != 1) | 235 | if (ret != 1) |
236 | goto err; | 236 | goto err; |
237 | 237 | ||
238 | ret = sscanf(c, "%2x", &cmd); | 238 | ret = sscanf(c, "%2x", &cmd); |
239 | if (ret != 1) | 239 | if (ret != 1) |
240 | goto err; | 240 | goto err; |
241 | 241 | ||
242 | pattern[i] = (u8)cmd; | 242 | pattern[i] = (u8)cmd; |
243 | offset += nrchars; | 243 | offset += nrchars; |
244 | i++; | 244 | i++; |
245 | } | 245 | } |
246 | 246 | ||
247 | /* Each instruction is 16bit long. Check that length is even */ | 247 | /* Each instruction is 16bit long. Check that length is even */ |
248 | if (i % 2) | 248 | if (i % 2) |
249 | goto err; | 249 | goto err; |
250 | 250 | ||
251 | program_size = i; | 251 | program_size = i; |
252 | for (i = 0; i < program_size; i++) | 252 | for (i = 0; i < program_size; i++) |
253 | lp55xx_write(chip, addr[idx] + i, pattern[i]); | 253 | lp55xx_write(chip, addr[idx] + i, pattern[i]); |
254 | 254 | ||
255 | return 0; | 255 | return 0; |
256 | 256 | ||
257 | err: | 257 | err: |
258 | dev_err(&chip->cl->dev, "wrong pattern format\n"); | 258 | dev_err(&chip->cl->dev, "wrong pattern format\n"); |
259 | return -EINVAL; | 259 | return -EINVAL; |
260 | } | 260 | } |
261 | 261 | ||
262 | static void lp5562_firmware_loaded(struct lp55xx_chip *chip) | 262 | static void lp5562_firmware_loaded(struct lp55xx_chip *chip) |
263 | { | 263 | { |
264 | const struct firmware *fw = chip->fw; | 264 | const struct firmware *fw = chip->fw; |
265 | 265 | ||
266 | if (fw->size > LP5562_PROGRAM_LENGTH) { | 266 | if (fw->size > LP5562_PROGRAM_LENGTH) { |
267 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", | 267 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", |
268 | fw->size); | 268 | fw->size); |
269 | return; | 269 | return; |
270 | } | 270 | } |
271 | 271 | ||
272 | /* | 272 | /* |
273 | * Program momery sequence | 273 | * Program momery sequence |
274 | * 1) set engine mode to "LOAD" | 274 | * 1) set engine mode to "LOAD" |
275 | * 2) write firmware data into program memory | 275 | * 2) write firmware data into program memory |
276 | */ | 276 | */ |
277 | 277 | ||
278 | lp5562_load_engine(chip); | 278 | lp5562_load_engine(chip); |
279 | lp5562_update_firmware(chip, fw->data, fw->size); | 279 | lp5562_update_firmware(chip, fw->data, fw->size); |
280 | } | 280 | } |
281 | 281 | ||
282 | static int lp5562_post_init_device(struct lp55xx_chip *chip) | 282 | static int lp5562_post_init_device(struct lp55xx_chip *chip) |
283 | { | 283 | { |
284 | int ret; | 284 | int ret; |
285 | u8 cfg = LP5562_DEFAULT_CFG; | 285 | u8 cfg = LP5562_DEFAULT_CFG; |
286 | 286 | ||
287 | /* Set all PWMs to direct control mode */ | 287 | /* Set all PWMs to direct control mode */ |
288 | ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT); | 288 | ret = lp55xx_write(chip, LP5562_REG_OP_MODE, LP5562_CMD_DIRECT); |
289 | if (ret) | 289 | if (ret) |
290 | return ret; | 290 | return ret; |
291 | 291 | ||
292 | lp5562_wait_opmode_done(); | 292 | lp5562_wait_opmode_done(); |
293 | 293 | ||
294 | /* Update configuration for the clock setting */ | 294 | /* Update configuration for the clock setting */ |
295 | if (!lp55xx_is_extclk_used(chip)) | 295 | if (!lp55xx_is_extclk_used(chip)) |
296 | cfg |= LP5562_CLK_INT; | 296 | cfg |= LP5562_CLK_INT; |
297 | 297 | ||
298 | ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg); | 298 | ret = lp55xx_write(chip, LP5562_REG_CONFIG, cfg); |
299 | if (ret) | 299 | if (ret) |
300 | return ret; | 300 | return ret; |
301 | 301 | ||
302 | /* Initialize all channels PWM to zero -> leds off */ | 302 | /* Initialize all channels PWM to zero -> leds off */ |
303 | lp55xx_write(chip, LP5562_REG_R_PWM, 0); | 303 | lp55xx_write(chip, LP5562_REG_R_PWM, 0); |
304 | lp55xx_write(chip, LP5562_REG_G_PWM, 0); | 304 | lp55xx_write(chip, LP5562_REG_G_PWM, 0); |
305 | lp55xx_write(chip, LP5562_REG_B_PWM, 0); | 305 | lp55xx_write(chip, LP5562_REG_B_PWM, 0); |
306 | lp55xx_write(chip, LP5562_REG_W_PWM, 0); | 306 | lp55xx_write(chip, LP5562_REG_W_PWM, 0); |
307 | 307 | ||
308 | /* Set LED map as register PWM by default */ | 308 | /* Set LED map as register PWM by default */ |
309 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM); | 309 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_PWM); |
310 | 310 | ||
311 | return 0; | 311 | return 0; |
312 | } | 312 | } |
313 | 313 | ||
314 | static void lp5562_led_brightness_work(struct work_struct *work) | 314 | static void lp5562_led_brightness_work(struct work_struct *work) |
315 | { | 315 | { |
316 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, | 316 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, |
317 | brightness_work); | 317 | brightness_work); |
318 | struct lp55xx_chip *chip = led->chip; | 318 | struct lp55xx_chip *chip = led->chip; |
319 | u8 addr[] = { | 319 | u8 addr[] = { |
320 | LP5562_REG_R_PWM, | 320 | LP5562_REG_R_PWM, |
321 | LP5562_REG_G_PWM, | 321 | LP5562_REG_G_PWM, |
322 | LP5562_REG_B_PWM, | 322 | LP5562_REG_B_PWM, |
323 | LP5562_REG_W_PWM, | 323 | LP5562_REG_W_PWM, |
324 | }; | 324 | }; |
325 | 325 | ||
326 | mutex_lock(&chip->lock); | 326 | mutex_lock(&chip->lock); |
327 | lp55xx_write(chip, addr[led->chan_nr], led->brightness); | 327 | lp55xx_write(chip, addr[led->chan_nr], led->brightness); |
328 | mutex_unlock(&chip->lock); | 328 | mutex_unlock(&chip->lock); |
329 | } | 329 | } |
330 | 330 | ||
331 | static void lp5562_write_program_memory(struct lp55xx_chip *chip, | 331 | static void lp5562_write_program_memory(struct lp55xx_chip *chip, |
332 | u8 base, const u8 *rgb, int size) | 332 | u8 base, const u8 *rgb, int size) |
333 | { | 333 | { |
334 | int i; | 334 | int i; |
335 | 335 | ||
336 | if (!rgb || size <= 0) | 336 | if (!rgb || size <= 0) |
337 | return; | 337 | return; |
338 | 338 | ||
339 | for (i = 0; i < size; i++) | 339 | for (i = 0; i < size; i++) |
340 | lp55xx_write(chip, base + i, *(rgb + i)); | 340 | lp55xx_write(chip, base + i, *(rgb + i)); |
341 | 341 | ||
342 | lp55xx_write(chip, base + i, 0); | 342 | lp55xx_write(chip, base + i, 0); |
343 | lp55xx_write(chip, base + i + 1, 0); | 343 | lp55xx_write(chip, base + i + 1, 0); |
344 | } | 344 | } |
345 | 345 | ||
346 | /* check the size of program count */ | 346 | /* check the size of program count */ |
347 | static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn) | 347 | static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn) |
348 | { | 348 | { |
349 | return (ptn->size_r >= LP5562_PROGRAM_LENGTH || | 349 | return (ptn->size_r >= LP5562_PROGRAM_LENGTH || |
350 | ptn->size_g >= LP5562_PROGRAM_LENGTH || | 350 | ptn->size_g >= LP5562_PROGRAM_LENGTH || |
351 | ptn->size_b >= LP5562_PROGRAM_LENGTH); | 351 | ptn->size_b >= LP5562_PROGRAM_LENGTH); |
352 | } | 352 | } |
353 | 353 | ||
354 | static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode) | 354 | static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode) |
355 | { | 355 | { |
356 | struct lp55xx_predef_pattern *ptn; | 356 | struct lp55xx_predef_pattern *ptn; |
357 | int i; | 357 | int i; |
358 | 358 | ||
359 | if (mode == LP5562_PATTERN_OFF) { | 359 | if (mode == LP5562_PATTERN_OFF) { |
360 | lp5562_run_engine(chip, false); | 360 | lp5562_run_engine(chip, false); |
361 | return 0; | 361 | return 0; |
362 | } | 362 | } |
363 | 363 | ||
364 | ptn = chip->pdata->patterns + (mode - 1); | 364 | ptn = chip->pdata->patterns + (mode - 1); |
365 | if (!ptn || _is_pc_overflow(ptn)) { | 365 | if (!ptn || _is_pc_overflow(ptn)) { |
366 | dev_err(&chip->cl->dev, "invalid pattern data\n"); | 366 | dev_err(&chip->cl->dev, "invalid pattern data\n"); |
367 | return -EINVAL; | 367 | return -EINVAL; |
368 | } | 368 | } |
369 | 369 | ||
370 | lp5562_stop_engine(chip); | 370 | lp5562_stop_engine(chip); |
371 | 371 | ||
372 | /* Set LED map as RGB */ | 372 | /* Set LED map as RGB */ |
373 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB); | 373 | lp55xx_write(chip, LP5562_REG_ENG_SEL, LP5562_ENG_SEL_RGB); |
374 | 374 | ||
375 | /* Load engines */ | 375 | /* Load engines */ |
376 | for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) { | 376 | for (i = LP55XX_ENGINE_1; i <= LP55XX_ENGINE_3; i++) { |
377 | chip->engine_idx = i; | 377 | chip->engine_idx = i; |
378 | lp5562_load_engine(chip); | 378 | lp5562_load_engine(chip); |
379 | } | 379 | } |
380 | 380 | ||
381 | /* Clear program registers */ | 381 | /* Clear program registers */ |
382 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0); | 382 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1, 0); |
383 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0); | 383 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG1 + 1, 0); |
384 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0); | 384 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2, 0); |
385 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0); | 385 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG2 + 1, 0); |
386 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0); | 386 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3, 0); |
387 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0); | 387 | lp55xx_write(chip, LP5562_REG_PROG_MEM_ENG3 + 1, 0); |
388 | 388 | ||
389 | /* Program engines */ | 389 | /* Program engines */ |
390 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1, | 390 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG1, |
391 | ptn->r, ptn->size_r); | 391 | ptn->r, ptn->size_r); |
392 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2, | 392 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG2, |
393 | ptn->g, ptn->size_g); | 393 | ptn->g, ptn->size_g); |
394 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3, | 394 | lp5562_write_program_memory(chip, LP5562_REG_PROG_MEM_ENG3, |
395 | ptn->b, ptn->size_b); | 395 | ptn->b, ptn->size_b); |
396 | 396 | ||
397 | /* Run engines */ | 397 | /* Run engines */ |
398 | lp5562_run_engine(chip, true); | 398 | lp5562_run_engine(chip, true); |
399 | 399 | ||
400 | return 0; | 400 | return 0; |
401 | } | 401 | } |
402 | 402 | ||
403 | static ssize_t lp5562_store_pattern(struct device *dev, | 403 | static ssize_t lp5562_store_pattern(struct device *dev, |
404 | struct device_attribute *attr, | 404 | struct device_attribute *attr, |
405 | const char *buf, size_t len) | 405 | const char *buf, size_t len) |
406 | { | 406 | { |
407 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 407 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
408 | struct lp55xx_chip *chip = led->chip; | 408 | struct lp55xx_chip *chip = led->chip; |
409 | struct lp55xx_predef_pattern *ptn = chip->pdata->patterns; | 409 | struct lp55xx_predef_pattern *ptn = chip->pdata->patterns; |
410 | int num_patterns = chip->pdata->num_patterns; | 410 | int num_patterns = chip->pdata->num_patterns; |
411 | unsigned long mode; | 411 | unsigned long mode; |
412 | int ret; | 412 | int ret; |
413 | 413 | ||
414 | ret = kstrtoul(buf, 0, &mode); | 414 | ret = kstrtoul(buf, 0, &mode); |
415 | if (ret) | 415 | if (ret) |
416 | return ret; | 416 | return ret; |
417 | 417 | ||
418 | if (mode > num_patterns || !ptn) | 418 | if (mode > num_patterns || !ptn) |
419 | return -EINVAL; | 419 | return -EINVAL; |
420 | 420 | ||
421 | mutex_lock(&chip->lock); | 421 | mutex_lock(&chip->lock); |
422 | ret = lp5562_run_predef_led_pattern(chip, mode); | 422 | ret = lp5562_run_predef_led_pattern(chip, mode); |
423 | mutex_unlock(&chip->lock); | 423 | mutex_unlock(&chip->lock); |
424 | 424 | ||
425 | if (ret) | 425 | if (ret) |
426 | return ret; | 426 | return ret; |
427 | 427 | ||
428 | return len; | 428 | return len; |
429 | } | 429 | } |
430 | 430 | ||
431 | static ssize_t lp5562_store_engine_mux(struct device *dev, | 431 | static ssize_t lp5562_store_engine_mux(struct device *dev, |
432 | struct device_attribute *attr, | 432 | struct device_attribute *attr, |
433 | const char *buf, size_t len) | 433 | const char *buf, size_t len) |
434 | { | 434 | { |
435 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); | 435 | struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev)); |
436 | struct lp55xx_chip *chip = led->chip; | 436 | struct lp55xx_chip *chip = led->chip; |
437 | u8 mask; | 437 | u8 mask; |
438 | u8 val; | 438 | u8 val; |
439 | 439 | ||
440 | /* LED map | 440 | /* LED map |
441 | * R ... Engine 1 (fixed) | 441 | * R ... Engine 1 (fixed) |
442 | * G ... Engine 2 (fixed) | 442 | * G ... Engine 2 (fixed) |
443 | * B ... Engine 3 (fixed) | 443 | * B ... Engine 3 (fixed) |
444 | * W ... Engine 1 or 2 or 3 | 444 | * W ... Engine 1 or 2 or 3 |
445 | */ | 445 | */ |
446 | 446 | ||
447 | if (sysfs_streq(buf, "RGB")) { | 447 | if (sysfs_streq(buf, "RGB")) { |
448 | mask = LP5562_ENG_FOR_RGB_M; | 448 | mask = LP5562_ENG_FOR_RGB_M; |
449 | val = LP5562_ENG_SEL_RGB; | 449 | val = LP5562_ENG_SEL_RGB; |
450 | } else if (sysfs_streq(buf, "W")) { | 450 | } else if (sysfs_streq(buf, "W")) { |
451 | enum lp55xx_engine_index idx = chip->engine_idx; | 451 | enum lp55xx_engine_index idx = chip->engine_idx; |
452 | 452 | ||
453 | mask = LP5562_ENG_FOR_W_M; | 453 | mask = LP5562_ENG_FOR_W_M; |
454 | switch (idx) { | 454 | switch (idx) { |
455 | case LP55XX_ENGINE_1: | 455 | case LP55XX_ENGINE_1: |
456 | val = LP5562_ENG1_FOR_W; | 456 | val = LP5562_ENG1_FOR_W; |
457 | break; | 457 | break; |
458 | case LP55XX_ENGINE_2: | 458 | case LP55XX_ENGINE_2: |
459 | val = LP5562_ENG2_FOR_W; | 459 | val = LP5562_ENG2_FOR_W; |
460 | break; | 460 | break; |
461 | case LP55XX_ENGINE_3: | 461 | case LP55XX_ENGINE_3: |
462 | val = LP5562_ENG3_FOR_W; | 462 | val = LP5562_ENG3_FOR_W; |
463 | break; | 463 | break; |
464 | default: | 464 | default: |
465 | return -EINVAL; | 465 | return -EINVAL; |
466 | } | 466 | } |
467 | 467 | ||
468 | } else { | 468 | } else { |
469 | dev_err(dev, "choose RGB or W\n"); | 469 | dev_err(dev, "choose RGB or W\n"); |
470 | return -EINVAL; | 470 | return -EINVAL; |
471 | } | 471 | } |
472 | 472 | ||
473 | mutex_lock(&chip->lock); | 473 | mutex_lock(&chip->lock); |
474 | lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val); | 474 | lp55xx_update_bits(chip, LP5562_REG_ENG_SEL, mask, val); |
475 | mutex_unlock(&chip->lock); | 475 | mutex_unlock(&chip->lock); |
476 | 476 | ||
477 | return len; | 477 | return len; |
478 | } | 478 | } |
479 | 479 | ||
480 | static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern); | 480 | static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, lp5562_store_pattern); |
481 | static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux); | 481 | static DEVICE_ATTR(engine_mux, S_IWUSR, NULL, lp5562_store_engine_mux); |
482 | 482 | ||
483 | static struct attribute *lp5562_attributes[] = { | 483 | static struct attribute *lp5562_attributes[] = { |
484 | &dev_attr_led_pattern.attr, | 484 | &dev_attr_led_pattern.attr, |
485 | &dev_attr_engine_mux.attr, | 485 | &dev_attr_engine_mux.attr, |
486 | NULL, | 486 | NULL, |
487 | }; | 487 | }; |
488 | 488 | ||
489 | static const struct attribute_group lp5562_group = { | 489 | static const struct attribute_group lp5562_group = { |
490 | .attrs = lp5562_attributes, | 490 | .attrs = lp5562_attributes, |
491 | }; | 491 | }; |
492 | 492 | ||
493 | /* Chip specific configurations */ | 493 | /* Chip specific configurations */ |
494 | static struct lp55xx_device_config lp5562_cfg = { | 494 | static struct lp55xx_device_config lp5562_cfg = { |
495 | .max_channel = LP5562_MAX_LEDS, | 495 | .max_channel = LP5562_MAX_LEDS, |
496 | .reset = { | 496 | .reset = { |
497 | .addr = LP5562_REG_RESET, | 497 | .addr = LP5562_REG_RESET, |
498 | .val = LP5562_RESET, | 498 | .val = LP5562_RESET, |
499 | }, | 499 | }, |
500 | .enable = { | 500 | .enable = { |
501 | .addr = LP5562_REG_ENABLE, | 501 | .addr = LP5562_REG_ENABLE, |
502 | .val = LP5562_ENABLE_DEFAULT, | 502 | .val = LP5562_ENABLE_DEFAULT, |
503 | }, | 503 | }, |
504 | .post_init_device = lp5562_post_init_device, | 504 | .post_init_device = lp5562_post_init_device, |
505 | .set_led_current = lp5562_set_led_current, | 505 | .set_led_current = lp5562_set_led_current, |
506 | .brightness_work_fn = lp5562_led_brightness_work, | 506 | .brightness_work_fn = lp5562_led_brightness_work, |
507 | .run_engine = lp5562_run_engine, | 507 | .run_engine = lp5562_run_engine, |
508 | .firmware_cb = lp5562_firmware_loaded, | 508 | .firmware_cb = lp5562_firmware_loaded, |
509 | .dev_attr_group = &lp5562_group, | 509 | .dev_attr_group = &lp5562_group, |
510 | }; | 510 | }; |
511 | 511 | ||
512 | static int lp5562_probe(struct i2c_client *client, | 512 | static int lp5562_probe(struct i2c_client *client, |
513 | const struct i2c_device_id *id) | 513 | const struct i2c_device_id *id) |
514 | { | 514 | { |
515 | int ret; | 515 | int ret; |
516 | struct lp55xx_chip *chip; | 516 | struct lp55xx_chip *chip; |
517 | struct lp55xx_led *led; | 517 | struct lp55xx_led *led; |
518 | struct lp55xx_platform_data *pdata; | 518 | struct lp55xx_platform_data *pdata; |
519 | struct device_node *np = client->dev.of_node; | 519 | struct device_node *np = client->dev.of_node; |
520 | 520 | ||
521 | if (!client->dev.platform_data) { | 521 | if (!dev_get_platdata(&client->dev)) { |
522 | if (np) { | 522 | if (np) { |
523 | ret = lp55xx_of_populate_pdata(&client->dev, np); | 523 | ret = lp55xx_of_populate_pdata(&client->dev, np); |
524 | if (ret < 0) | 524 | if (ret < 0) |
525 | return ret; | 525 | return ret; |
526 | } else { | 526 | } else { |
527 | dev_err(&client->dev, "no platform data\n"); | 527 | dev_err(&client->dev, "no platform data\n"); |
528 | return -EINVAL; | 528 | return -EINVAL; |
529 | } | 529 | } |
530 | } | 530 | } |
531 | pdata = client->dev.platform_data; | 531 | pdata = dev_get_platdata(&client->dev); |
532 | 532 | ||
533 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | 533 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
534 | if (!chip) | 534 | if (!chip) |
535 | return -ENOMEM; | 535 | return -ENOMEM; |
536 | 536 | ||
537 | led = devm_kzalloc(&client->dev, | 537 | led = devm_kzalloc(&client->dev, |
538 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); | 538 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); |
539 | if (!led) | 539 | if (!led) |
540 | return -ENOMEM; | 540 | return -ENOMEM; |
541 | 541 | ||
542 | chip->cl = client; | 542 | chip->cl = client; |
543 | chip->pdata = pdata; | 543 | chip->pdata = pdata; |
544 | chip->cfg = &lp5562_cfg; | 544 | chip->cfg = &lp5562_cfg; |
545 | 545 | ||
546 | mutex_init(&chip->lock); | 546 | mutex_init(&chip->lock); |
547 | 547 | ||
548 | i2c_set_clientdata(client, led); | 548 | i2c_set_clientdata(client, led); |
549 | 549 | ||
550 | ret = lp55xx_init_device(chip); | 550 | ret = lp55xx_init_device(chip); |
551 | if (ret) | 551 | if (ret) |
552 | goto err_init; | 552 | goto err_init; |
553 | 553 | ||
554 | ret = lp55xx_register_leds(led, chip); | 554 | ret = lp55xx_register_leds(led, chip); |
555 | if (ret) | 555 | if (ret) |
556 | goto err_register_leds; | 556 | goto err_register_leds; |
557 | 557 | ||
558 | ret = lp55xx_register_sysfs(chip); | 558 | ret = lp55xx_register_sysfs(chip); |
559 | if (ret) { | 559 | if (ret) { |
560 | dev_err(&client->dev, "registering sysfs failed\n"); | 560 | dev_err(&client->dev, "registering sysfs failed\n"); |
561 | goto err_register_sysfs; | 561 | goto err_register_sysfs; |
562 | } | 562 | } |
563 | 563 | ||
564 | return 0; | 564 | return 0; |
565 | 565 | ||
566 | err_register_sysfs: | 566 | err_register_sysfs: |
567 | lp55xx_unregister_leds(led, chip); | 567 | lp55xx_unregister_leds(led, chip); |
568 | err_register_leds: | 568 | err_register_leds: |
569 | lp55xx_deinit_device(chip); | 569 | lp55xx_deinit_device(chip); |
570 | err_init: | 570 | err_init: |
571 | return ret; | 571 | return ret; |
572 | } | 572 | } |
573 | 573 | ||
574 | static int lp5562_remove(struct i2c_client *client) | 574 | static int lp5562_remove(struct i2c_client *client) |
575 | { | 575 | { |
576 | struct lp55xx_led *led = i2c_get_clientdata(client); | 576 | struct lp55xx_led *led = i2c_get_clientdata(client); |
577 | struct lp55xx_chip *chip = led->chip; | 577 | struct lp55xx_chip *chip = led->chip; |
578 | 578 | ||
579 | lp5562_stop_engine(chip); | 579 | lp5562_stop_engine(chip); |
580 | 580 | ||
581 | lp55xx_unregister_sysfs(chip); | 581 | lp55xx_unregister_sysfs(chip); |
582 | lp55xx_unregister_leds(led, chip); | 582 | lp55xx_unregister_leds(led, chip); |
583 | lp55xx_deinit_device(chip); | 583 | lp55xx_deinit_device(chip); |
584 | 584 | ||
585 | return 0; | 585 | return 0; |
586 | } | 586 | } |
587 | 587 | ||
588 | static const struct i2c_device_id lp5562_id[] = { | 588 | static const struct i2c_device_id lp5562_id[] = { |
589 | { "lp5562", 0 }, | 589 | { "lp5562", 0 }, |
590 | { } | 590 | { } |
591 | }; | 591 | }; |
592 | MODULE_DEVICE_TABLE(i2c, lp5562_id); | 592 | MODULE_DEVICE_TABLE(i2c, lp5562_id); |
593 | 593 | ||
594 | #ifdef CONFIG_OF | 594 | #ifdef CONFIG_OF |
595 | static const struct of_device_id of_lp5562_leds_match[] = { | 595 | static const struct of_device_id of_lp5562_leds_match[] = { |
596 | { .compatible = "ti,lp5562", }, | 596 | { .compatible = "ti,lp5562", }, |
597 | {}, | 597 | {}, |
598 | }; | 598 | }; |
599 | 599 | ||
600 | MODULE_DEVICE_TABLE(of, of_lp5562_leds_match); | 600 | MODULE_DEVICE_TABLE(of, of_lp5562_leds_match); |
601 | #endif | 601 | #endif |
602 | 602 | ||
603 | static struct i2c_driver lp5562_driver = { | 603 | static struct i2c_driver lp5562_driver = { |
604 | .driver = { | 604 | .driver = { |
605 | .name = "lp5562", | 605 | .name = "lp5562", |
606 | .of_match_table = of_match_ptr(of_lp5562_leds_match), | 606 | .of_match_table = of_match_ptr(of_lp5562_leds_match), |
607 | }, | 607 | }, |
608 | .probe = lp5562_probe, | 608 | .probe = lp5562_probe, |
609 | .remove = lp5562_remove, | 609 | .remove = lp5562_remove, |
610 | .id_table = lp5562_id, | 610 | .id_table = lp5562_id, |
611 | }; | 611 | }; |
612 | 612 | ||
613 | module_i2c_driver(lp5562_driver); | 613 | module_i2c_driver(lp5562_driver); |
614 | 614 | ||
615 | MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver"); | 615 | MODULE_DESCRIPTION("Texas Instruments LP5562 LED Driver"); |
616 | MODULE_AUTHOR("Milo Kim"); | 616 | MODULE_AUTHOR("Milo Kim"); |
617 | MODULE_LICENSE("GPL"); | 617 | MODULE_LICENSE("GPL"); |
618 | 618 |
drivers/leds/leds-lp8501.c
1 | /* | 1 | /* |
2 | * TI LP8501 9 channel LED Driver | 2 | * TI LP8501 9 channel LED Driver |
3 | * | 3 | * |
4 | * Copyright (C) 2013 Texas Instruments | 4 | * Copyright (C) 2013 Texas Instruments |
5 | * | 5 | * |
6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> | 6 | * Author: Milo(Woogyom) Kim <milo.kim@ti.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or | 8 | * This program is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU General Public License | 9 | * modify it under the terms of the GNU General Public License |
10 | * version 2 as published by the Free Software Foundation. | 10 | * version 2 as published by the Free Software Foundation. |
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/firmware.h> | 15 | #include <linux/firmware.h> |
16 | #include <linux/i2c.h> | 16 | #include <linux/i2c.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/mutex.h> | 20 | #include <linux/mutex.h> |
21 | #include <linux/platform_data/leds-lp55xx.h> | 21 | #include <linux/platform_data/leds-lp55xx.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | 23 | ||
24 | #include "leds-lp55xx-common.h" | 24 | #include "leds-lp55xx-common.h" |
25 | 25 | ||
26 | #define LP8501_PROGRAM_LENGTH 32 | 26 | #define LP8501_PROGRAM_LENGTH 32 |
27 | #define LP8501_MAX_LEDS 9 | 27 | #define LP8501_MAX_LEDS 9 |
28 | 28 | ||
29 | /* Registers */ | 29 | /* Registers */ |
30 | #define LP8501_REG_ENABLE 0x00 | 30 | #define LP8501_REG_ENABLE 0x00 |
31 | #define LP8501_ENABLE BIT(6) | 31 | #define LP8501_ENABLE BIT(6) |
32 | #define LP8501_EXEC_M 0x3F | 32 | #define LP8501_EXEC_M 0x3F |
33 | #define LP8501_EXEC_ENG1_M 0x30 | 33 | #define LP8501_EXEC_ENG1_M 0x30 |
34 | #define LP8501_EXEC_ENG2_M 0x0C | 34 | #define LP8501_EXEC_ENG2_M 0x0C |
35 | #define LP8501_EXEC_ENG3_M 0x03 | 35 | #define LP8501_EXEC_ENG3_M 0x03 |
36 | #define LP8501_RUN_ENG1 0x20 | 36 | #define LP8501_RUN_ENG1 0x20 |
37 | #define LP8501_RUN_ENG2 0x08 | 37 | #define LP8501_RUN_ENG2 0x08 |
38 | #define LP8501_RUN_ENG3 0x02 | 38 | #define LP8501_RUN_ENG3 0x02 |
39 | 39 | ||
40 | #define LP8501_REG_OP_MODE 0x01 | 40 | #define LP8501_REG_OP_MODE 0x01 |
41 | #define LP8501_MODE_ENG1_M 0x30 | 41 | #define LP8501_MODE_ENG1_M 0x30 |
42 | #define LP8501_MODE_ENG2_M 0x0C | 42 | #define LP8501_MODE_ENG2_M 0x0C |
43 | #define LP8501_MODE_ENG3_M 0x03 | 43 | #define LP8501_MODE_ENG3_M 0x03 |
44 | #define LP8501_LOAD_ENG1 0x10 | 44 | #define LP8501_LOAD_ENG1 0x10 |
45 | #define LP8501_LOAD_ENG2 0x04 | 45 | #define LP8501_LOAD_ENG2 0x04 |
46 | #define LP8501_LOAD_ENG3 0x01 | 46 | #define LP8501_LOAD_ENG3 0x01 |
47 | 47 | ||
48 | #define LP8501_REG_PWR_CONFIG 0x05 | 48 | #define LP8501_REG_PWR_CONFIG 0x05 |
49 | #define LP8501_PWR_CONFIG_M 0x03 | 49 | #define LP8501_PWR_CONFIG_M 0x03 |
50 | 50 | ||
51 | #define LP8501_REG_LED_PWM_BASE 0x16 | 51 | #define LP8501_REG_LED_PWM_BASE 0x16 |
52 | 52 | ||
53 | #define LP8501_REG_LED_CURRENT_BASE 0x26 | 53 | #define LP8501_REG_LED_CURRENT_BASE 0x26 |
54 | 54 | ||
55 | #define LP8501_REG_CONFIG 0x36 | 55 | #define LP8501_REG_CONFIG 0x36 |
56 | #define LP8501_PWM_PSAVE BIT(7) | 56 | #define LP8501_PWM_PSAVE BIT(7) |
57 | #define LP8501_AUTO_INC BIT(6) | 57 | #define LP8501_AUTO_INC BIT(6) |
58 | #define LP8501_PWR_SAVE BIT(5) | 58 | #define LP8501_PWR_SAVE BIT(5) |
59 | #define LP8501_CP_AUTO 0x18 | 59 | #define LP8501_CP_AUTO 0x18 |
60 | #define LP8501_INT_CLK BIT(0) | 60 | #define LP8501_INT_CLK BIT(0) |
61 | #define LP8501_DEFAULT_CFG \ | 61 | #define LP8501_DEFAULT_CFG \ |
62 | (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO) | 62 | (LP8501_PWM_PSAVE | LP8501_AUTO_INC | LP8501_PWR_SAVE | LP8501_CP_AUTO) |
63 | 63 | ||
64 | #define LP8501_REG_RESET 0x3D | 64 | #define LP8501_REG_RESET 0x3D |
65 | #define LP8501_RESET 0xFF | 65 | #define LP8501_RESET 0xFF |
66 | 66 | ||
67 | #define LP8501_REG_PROG_PAGE_SEL 0x4F | 67 | #define LP8501_REG_PROG_PAGE_SEL 0x4F |
68 | #define LP8501_PAGE_ENG1 0 | 68 | #define LP8501_PAGE_ENG1 0 |
69 | #define LP8501_PAGE_ENG2 1 | 69 | #define LP8501_PAGE_ENG2 1 |
70 | #define LP8501_PAGE_ENG3 2 | 70 | #define LP8501_PAGE_ENG3 2 |
71 | 71 | ||
72 | #define LP8501_REG_PROG_MEM 0x50 | 72 | #define LP8501_REG_PROG_MEM 0x50 |
73 | 73 | ||
74 | #define LP8501_ENG1_IS_LOADING(mode) \ | 74 | #define LP8501_ENG1_IS_LOADING(mode) \ |
75 | ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1) | 75 | ((mode & LP8501_MODE_ENG1_M) == LP8501_LOAD_ENG1) |
76 | #define LP8501_ENG2_IS_LOADING(mode) \ | 76 | #define LP8501_ENG2_IS_LOADING(mode) \ |
77 | ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2) | 77 | ((mode & LP8501_MODE_ENG2_M) == LP8501_LOAD_ENG2) |
78 | #define LP8501_ENG3_IS_LOADING(mode) \ | 78 | #define LP8501_ENG3_IS_LOADING(mode) \ |
79 | ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3) | 79 | ((mode & LP8501_MODE_ENG3_M) == LP8501_LOAD_ENG3) |
80 | 80 | ||
81 | static inline void lp8501_wait_opmode_done(void) | 81 | static inline void lp8501_wait_opmode_done(void) |
82 | { | 82 | { |
83 | usleep_range(1000, 2000); | 83 | usleep_range(1000, 2000); |
84 | } | 84 | } |
85 | 85 | ||
86 | static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current) | 86 | static void lp8501_set_led_current(struct lp55xx_led *led, u8 led_current) |
87 | { | 87 | { |
88 | led->led_current = led_current; | 88 | led->led_current = led_current; |
89 | lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr, | 89 | lp55xx_write(led->chip, LP8501_REG_LED_CURRENT_BASE + led->chan_nr, |
90 | led_current); | 90 | led_current); |
91 | } | 91 | } |
92 | 92 | ||
93 | static int lp8501_post_init_device(struct lp55xx_chip *chip) | 93 | static int lp8501_post_init_device(struct lp55xx_chip *chip) |
94 | { | 94 | { |
95 | int ret; | 95 | int ret; |
96 | u8 val = LP8501_DEFAULT_CFG; | 96 | u8 val = LP8501_DEFAULT_CFG; |
97 | 97 | ||
98 | ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE); | 98 | ret = lp55xx_write(chip, LP8501_REG_ENABLE, LP8501_ENABLE); |
99 | if (ret) | 99 | if (ret) |
100 | return ret; | 100 | return ret; |
101 | 101 | ||
102 | /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ | 102 | /* Chip startup time is 500 us, 1 - 2 ms gives some margin */ |
103 | usleep_range(1000, 2000); | 103 | usleep_range(1000, 2000); |
104 | 104 | ||
105 | if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT) | 105 | if (chip->pdata->clock_mode != LP55XX_CLOCK_EXT) |
106 | val |= LP8501_INT_CLK; | 106 | val |= LP8501_INT_CLK; |
107 | 107 | ||
108 | ret = lp55xx_write(chip, LP8501_REG_CONFIG, val); | 108 | ret = lp55xx_write(chip, LP8501_REG_CONFIG, val); |
109 | if (ret) | 109 | if (ret) |
110 | return ret; | 110 | return ret; |
111 | 111 | ||
112 | /* Power selection for each output */ | 112 | /* Power selection for each output */ |
113 | return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG, | 113 | return lp55xx_update_bits(chip, LP8501_REG_PWR_CONFIG, |
114 | LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel); | 114 | LP8501_PWR_CONFIG_M, chip->pdata->pwr_sel); |
115 | } | 115 | } |
116 | 116 | ||
117 | static void lp8501_load_engine(struct lp55xx_chip *chip) | 117 | static void lp8501_load_engine(struct lp55xx_chip *chip) |
118 | { | 118 | { |
119 | enum lp55xx_engine_index idx = chip->engine_idx; | 119 | enum lp55xx_engine_index idx = chip->engine_idx; |
120 | u8 mask[] = { | 120 | u8 mask[] = { |
121 | [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, | 121 | [LP55XX_ENGINE_1] = LP8501_MODE_ENG1_M, |
122 | [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, | 122 | [LP55XX_ENGINE_2] = LP8501_MODE_ENG2_M, |
123 | [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, | 123 | [LP55XX_ENGINE_3] = LP8501_MODE_ENG3_M, |
124 | }; | 124 | }; |
125 | 125 | ||
126 | u8 val[] = { | 126 | u8 val[] = { |
127 | [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, | 127 | [LP55XX_ENGINE_1] = LP8501_LOAD_ENG1, |
128 | [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, | 128 | [LP55XX_ENGINE_2] = LP8501_LOAD_ENG2, |
129 | [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, | 129 | [LP55XX_ENGINE_3] = LP8501_LOAD_ENG3, |
130 | }; | 130 | }; |
131 | 131 | ||
132 | u8 page_sel[] = { | 132 | u8 page_sel[] = { |
133 | [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, | 133 | [LP55XX_ENGINE_1] = LP8501_PAGE_ENG1, |
134 | [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, | 134 | [LP55XX_ENGINE_2] = LP8501_PAGE_ENG2, |
135 | [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, | 135 | [LP55XX_ENGINE_3] = LP8501_PAGE_ENG3, |
136 | }; | 136 | }; |
137 | 137 | ||
138 | lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]); | 138 | lp55xx_update_bits(chip, LP8501_REG_OP_MODE, mask[idx], val[idx]); |
139 | 139 | ||
140 | lp8501_wait_opmode_done(); | 140 | lp8501_wait_opmode_done(); |
141 | 141 | ||
142 | lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]); | 142 | lp55xx_write(chip, LP8501_REG_PROG_PAGE_SEL, page_sel[idx]); |
143 | } | 143 | } |
144 | 144 | ||
145 | static void lp8501_stop_engine(struct lp55xx_chip *chip) | 145 | static void lp8501_stop_engine(struct lp55xx_chip *chip) |
146 | { | 146 | { |
147 | lp55xx_write(chip, LP8501_REG_OP_MODE, 0); | 147 | lp55xx_write(chip, LP8501_REG_OP_MODE, 0); |
148 | lp8501_wait_opmode_done(); | 148 | lp8501_wait_opmode_done(); |
149 | } | 149 | } |
150 | 150 | ||
151 | static void lp8501_turn_off_channels(struct lp55xx_chip *chip) | 151 | static void lp8501_turn_off_channels(struct lp55xx_chip *chip) |
152 | { | 152 | { |
153 | int i; | 153 | int i; |
154 | 154 | ||
155 | for (i = 0; i < LP8501_MAX_LEDS; i++) | 155 | for (i = 0; i < LP8501_MAX_LEDS; i++) |
156 | lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0); | 156 | lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + i, 0); |
157 | } | 157 | } |
158 | 158 | ||
159 | static void lp8501_run_engine(struct lp55xx_chip *chip, bool start) | 159 | static void lp8501_run_engine(struct lp55xx_chip *chip, bool start) |
160 | { | 160 | { |
161 | int ret; | 161 | int ret; |
162 | u8 mode; | 162 | u8 mode; |
163 | u8 exec; | 163 | u8 exec; |
164 | 164 | ||
165 | /* stop engine */ | 165 | /* stop engine */ |
166 | if (!start) { | 166 | if (!start) { |
167 | lp8501_stop_engine(chip); | 167 | lp8501_stop_engine(chip); |
168 | lp8501_turn_off_channels(chip); | 168 | lp8501_turn_off_channels(chip); |
169 | return; | 169 | return; |
170 | } | 170 | } |
171 | 171 | ||
172 | /* | 172 | /* |
173 | * To run the engine, | 173 | * To run the engine, |
174 | * operation mode and enable register should updated at the same time | 174 | * operation mode and enable register should updated at the same time |
175 | */ | 175 | */ |
176 | 176 | ||
177 | ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode); | 177 | ret = lp55xx_read(chip, LP8501_REG_OP_MODE, &mode); |
178 | if (ret) | 178 | if (ret) |
179 | return; | 179 | return; |
180 | 180 | ||
181 | ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec); | 181 | ret = lp55xx_read(chip, LP8501_REG_ENABLE, &exec); |
182 | if (ret) | 182 | if (ret) |
183 | return; | 183 | return; |
184 | 184 | ||
185 | /* change operation mode to RUN only when each engine is loading */ | 185 | /* change operation mode to RUN only when each engine is loading */ |
186 | if (LP8501_ENG1_IS_LOADING(mode)) { | 186 | if (LP8501_ENG1_IS_LOADING(mode)) { |
187 | mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1; | 187 | mode = (mode & ~LP8501_MODE_ENG1_M) | LP8501_RUN_ENG1; |
188 | exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1; | 188 | exec = (exec & ~LP8501_EXEC_ENG1_M) | LP8501_RUN_ENG1; |
189 | } | 189 | } |
190 | 190 | ||
191 | if (LP8501_ENG2_IS_LOADING(mode)) { | 191 | if (LP8501_ENG2_IS_LOADING(mode)) { |
192 | mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2; | 192 | mode = (mode & ~LP8501_MODE_ENG2_M) | LP8501_RUN_ENG2; |
193 | exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2; | 193 | exec = (exec & ~LP8501_EXEC_ENG2_M) | LP8501_RUN_ENG2; |
194 | } | 194 | } |
195 | 195 | ||
196 | if (LP8501_ENG3_IS_LOADING(mode)) { | 196 | if (LP8501_ENG3_IS_LOADING(mode)) { |
197 | mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3; | 197 | mode = (mode & ~LP8501_MODE_ENG3_M) | LP8501_RUN_ENG3; |
198 | exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3; | 198 | exec = (exec & ~LP8501_EXEC_ENG3_M) | LP8501_RUN_ENG3; |
199 | } | 199 | } |
200 | 200 | ||
201 | lp55xx_write(chip, LP8501_REG_OP_MODE, mode); | 201 | lp55xx_write(chip, LP8501_REG_OP_MODE, mode); |
202 | lp8501_wait_opmode_done(); | 202 | lp8501_wait_opmode_done(); |
203 | 203 | ||
204 | lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec); | 204 | lp55xx_update_bits(chip, LP8501_REG_ENABLE, LP8501_EXEC_M, exec); |
205 | } | 205 | } |
206 | 206 | ||
207 | static int lp8501_update_program_memory(struct lp55xx_chip *chip, | 207 | static int lp8501_update_program_memory(struct lp55xx_chip *chip, |
208 | const u8 *data, size_t size) | 208 | const u8 *data, size_t size) |
209 | { | 209 | { |
210 | u8 pattern[LP8501_PROGRAM_LENGTH] = {0}; | 210 | u8 pattern[LP8501_PROGRAM_LENGTH] = {0}; |
211 | unsigned cmd; | 211 | unsigned cmd; |
212 | char c[3]; | 212 | char c[3]; |
213 | int update_size; | 213 | int update_size; |
214 | int nrchars; | 214 | int nrchars; |
215 | int offset = 0; | 215 | int offset = 0; |
216 | int ret; | 216 | int ret; |
217 | int i; | 217 | int i; |
218 | 218 | ||
219 | /* clear program memory before updating */ | 219 | /* clear program memory before updating */ |
220 | for (i = 0; i < LP8501_PROGRAM_LENGTH; i++) | 220 | for (i = 0; i < LP8501_PROGRAM_LENGTH; i++) |
221 | lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0); | 221 | lp55xx_write(chip, LP8501_REG_PROG_MEM + i, 0); |
222 | 222 | ||
223 | i = 0; | 223 | i = 0; |
224 | while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) { | 224 | while ((offset < size - 1) && (i < LP8501_PROGRAM_LENGTH)) { |
225 | /* separate sscanfs because length is working only for %s */ | 225 | /* separate sscanfs because length is working only for %s */ |
226 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); | 226 | ret = sscanf(data + offset, "%2s%n ", c, &nrchars); |
227 | if (ret != 1) | 227 | if (ret != 1) |
228 | goto err; | 228 | goto err; |
229 | 229 | ||
230 | ret = sscanf(c, "%2x", &cmd); | 230 | ret = sscanf(c, "%2x", &cmd); |
231 | if (ret != 1) | 231 | if (ret != 1) |
232 | goto err; | 232 | goto err; |
233 | 233 | ||
234 | pattern[i] = (u8)cmd; | 234 | pattern[i] = (u8)cmd; |
235 | offset += nrchars; | 235 | offset += nrchars; |
236 | i++; | 236 | i++; |
237 | } | 237 | } |
238 | 238 | ||
239 | /* Each instruction is 16bit long. Check that length is even */ | 239 | /* Each instruction is 16bit long. Check that length is even */ |
240 | if (i % 2) | 240 | if (i % 2) |
241 | goto err; | 241 | goto err; |
242 | 242 | ||
243 | update_size = i; | 243 | update_size = i; |
244 | for (i = 0; i < update_size; i++) | 244 | for (i = 0; i < update_size; i++) |
245 | lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]); | 245 | lp55xx_write(chip, LP8501_REG_PROG_MEM + i, pattern[i]); |
246 | 246 | ||
247 | return 0; | 247 | return 0; |
248 | 248 | ||
249 | err: | 249 | err: |
250 | dev_err(&chip->cl->dev, "wrong pattern format\n"); | 250 | dev_err(&chip->cl->dev, "wrong pattern format\n"); |
251 | return -EINVAL; | 251 | return -EINVAL; |
252 | } | 252 | } |
253 | 253 | ||
254 | static void lp8501_firmware_loaded(struct lp55xx_chip *chip) | 254 | static void lp8501_firmware_loaded(struct lp55xx_chip *chip) |
255 | { | 255 | { |
256 | const struct firmware *fw = chip->fw; | 256 | const struct firmware *fw = chip->fw; |
257 | 257 | ||
258 | if (fw->size > LP8501_PROGRAM_LENGTH) { | 258 | if (fw->size > LP8501_PROGRAM_LENGTH) { |
259 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", | 259 | dev_err(&chip->cl->dev, "firmware data size overflow: %zu\n", |
260 | fw->size); | 260 | fw->size); |
261 | return; | 261 | return; |
262 | } | 262 | } |
263 | 263 | ||
264 | /* | 264 | /* |
265 | * Program momery sequence | 265 | * Program momery sequence |
266 | * 1) set engine mode to "LOAD" | 266 | * 1) set engine mode to "LOAD" |
267 | * 2) write firmware data into program memory | 267 | * 2) write firmware data into program memory |
268 | */ | 268 | */ |
269 | 269 | ||
270 | lp8501_load_engine(chip); | 270 | lp8501_load_engine(chip); |
271 | lp8501_update_program_memory(chip, fw->data, fw->size); | 271 | lp8501_update_program_memory(chip, fw->data, fw->size); |
272 | } | 272 | } |
273 | 273 | ||
274 | static void lp8501_led_brightness_work(struct work_struct *work) | 274 | static void lp8501_led_brightness_work(struct work_struct *work) |
275 | { | 275 | { |
276 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, | 276 | struct lp55xx_led *led = container_of(work, struct lp55xx_led, |
277 | brightness_work); | 277 | brightness_work); |
278 | struct lp55xx_chip *chip = led->chip; | 278 | struct lp55xx_chip *chip = led->chip; |
279 | 279 | ||
280 | mutex_lock(&chip->lock); | 280 | mutex_lock(&chip->lock); |
281 | lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr, | 281 | lp55xx_write(chip, LP8501_REG_LED_PWM_BASE + led->chan_nr, |
282 | led->brightness); | 282 | led->brightness); |
283 | mutex_unlock(&chip->lock); | 283 | mutex_unlock(&chip->lock); |
284 | } | 284 | } |
285 | 285 | ||
286 | /* Chip specific configurations */ | 286 | /* Chip specific configurations */ |
287 | static struct lp55xx_device_config lp8501_cfg = { | 287 | static struct lp55xx_device_config lp8501_cfg = { |
288 | .reset = { | 288 | .reset = { |
289 | .addr = LP8501_REG_RESET, | 289 | .addr = LP8501_REG_RESET, |
290 | .val = LP8501_RESET, | 290 | .val = LP8501_RESET, |
291 | }, | 291 | }, |
292 | .enable = { | 292 | .enable = { |
293 | .addr = LP8501_REG_ENABLE, | 293 | .addr = LP8501_REG_ENABLE, |
294 | .val = LP8501_ENABLE, | 294 | .val = LP8501_ENABLE, |
295 | }, | 295 | }, |
296 | .max_channel = LP8501_MAX_LEDS, | 296 | .max_channel = LP8501_MAX_LEDS, |
297 | .post_init_device = lp8501_post_init_device, | 297 | .post_init_device = lp8501_post_init_device, |
298 | .brightness_work_fn = lp8501_led_brightness_work, | 298 | .brightness_work_fn = lp8501_led_brightness_work, |
299 | .set_led_current = lp8501_set_led_current, | 299 | .set_led_current = lp8501_set_led_current, |
300 | .firmware_cb = lp8501_firmware_loaded, | 300 | .firmware_cb = lp8501_firmware_loaded, |
301 | .run_engine = lp8501_run_engine, | 301 | .run_engine = lp8501_run_engine, |
302 | }; | 302 | }; |
303 | 303 | ||
304 | static int lp8501_probe(struct i2c_client *client, | 304 | static int lp8501_probe(struct i2c_client *client, |
305 | const struct i2c_device_id *id) | 305 | const struct i2c_device_id *id) |
306 | { | 306 | { |
307 | int ret; | 307 | int ret; |
308 | struct lp55xx_chip *chip; | 308 | struct lp55xx_chip *chip; |
309 | struct lp55xx_led *led; | 309 | struct lp55xx_led *led; |
310 | struct lp55xx_platform_data *pdata; | 310 | struct lp55xx_platform_data *pdata; |
311 | struct device_node *np = client->dev.of_node; | 311 | struct device_node *np = client->dev.of_node; |
312 | 312 | ||
313 | if (!client->dev.platform_data) { | 313 | if (!dev_get_platdata(&client->dev)) { |
314 | if (np) { | 314 | if (np) { |
315 | ret = lp55xx_of_populate_pdata(&client->dev, np); | 315 | ret = lp55xx_of_populate_pdata(&client->dev, np); |
316 | if (ret < 0) | 316 | if (ret < 0) |
317 | return ret; | 317 | return ret; |
318 | } else { | 318 | } else { |
319 | dev_err(&client->dev, "no platform data\n"); | 319 | dev_err(&client->dev, "no platform data\n"); |
320 | return -EINVAL; | 320 | return -EINVAL; |
321 | } | 321 | } |
322 | } | 322 | } |
323 | pdata = client->dev.platform_data; | 323 | pdata = dev_get_platdata(&client->dev); |
324 | 324 | ||
325 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); | 325 | chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); |
326 | if (!chip) | 326 | if (!chip) |
327 | return -ENOMEM; | 327 | return -ENOMEM; |
328 | 328 | ||
329 | led = devm_kzalloc(&client->dev, | 329 | led = devm_kzalloc(&client->dev, |
330 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); | 330 | sizeof(*led) * pdata->num_channels, GFP_KERNEL); |
331 | if (!led) | 331 | if (!led) |
332 | return -ENOMEM; | 332 | return -ENOMEM; |
333 | 333 | ||
334 | chip->cl = client; | 334 | chip->cl = client; |
335 | chip->pdata = pdata; | 335 | chip->pdata = pdata; |
336 | chip->cfg = &lp8501_cfg; | 336 | chip->cfg = &lp8501_cfg; |
337 | 337 | ||
338 | mutex_init(&chip->lock); | 338 | mutex_init(&chip->lock); |
339 | 339 | ||
340 | i2c_set_clientdata(client, led); | 340 | i2c_set_clientdata(client, led); |
341 | 341 | ||
342 | ret = lp55xx_init_device(chip); | 342 | ret = lp55xx_init_device(chip); |
343 | if (ret) | 343 | if (ret) |
344 | goto err_init; | 344 | goto err_init; |
345 | 345 | ||
346 | dev_info(&client->dev, "%s Programmable led chip found\n", id->name); | 346 | dev_info(&client->dev, "%s Programmable led chip found\n", id->name); |
347 | 347 | ||
348 | ret = lp55xx_register_leds(led, chip); | 348 | ret = lp55xx_register_leds(led, chip); |
349 | if (ret) | 349 | if (ret) |
350 | goto err_register_leds; | 350 | goto err_register_leds; |
351 | 351 | ||
352 | ret = lp55xx_register_sysfs(chip); | 352 | ret = lp55xx_register_sysfs(chip); |
353 | if (ret) { | 353 | if (ret) { |
354 | dev_err(&client->dev, "registering sysfs failed\n"); | 354 | dev_err(&client->dev, "registering sysfs failed\n"); |
355 | goto err_register_sysfs; | 355 | goto err_register_sysfs; |
356 | } | 356 | } |
357 | 357 | ||
358 | return 0; | 358 | return 0; |
359 | 359 | ||
360 | err_register_sysfs: | 360 | err_register_sysfs: |
361 | lp55xx_unregister_leds(led, chip); | 361 | lp55xx_unregister_leds(led, chip); |
362 | err_register_leds: | 362 | err_register_leds: |
363 | lp55xx_deinit_device(chip); | 363 | lp55xx_deinit_device(chip); |
364 | err_init: | 364 | err_init: |
365 | return ret; | 365 | return ret; |
366 | } | 366 | } |
367 | 367 | ||
368 | static int lp8501_remove(struct i2c_client *client) | 368 | static int lp8501_remove(struct i2c_client *client) |
369 | { | 369 | { |
370 | struct lp55xx_led *led = i2c_get_clientdata(client); | 370 | struct lp55xx_led *led = i2c_get_clientdata(client); |
371 | struct lp55xx_chip *chip = led->chip; | 371 | struct lp55xx_chip *chip = led->chip; |
372 | 372 | ||
373 | lp8501_stop_engine(chip); | 373 | lp8501_stop_engine(chip); |
374 | lp55xx_unregister_sysfs(chip); | 374 | lp55xx_unregister_sysfs(chip); |
375 | lp55xx_unregister_leds(led, chip); | 375 | lp55xx_unregister_leds(led, chip); |
376 | lp55xx_deinit_device(chip); | 376 | lp55xx_deinit_device(chip); |
377 | 377 | ||
378 | return 0; | 378 | return 0; |
379 | } | 379 | } |
380 | 380 | ||
381 | static const struct i2c_device_id lp8501_id[] = { | 381 | static const struct i2c_device_id lp8501_id[] = { |
382 | { "lp8501", 0 }, | 382 | { "lp8501", 0 }, |
383 | { } | 383 | { } |
384 | }; | 384 | }; |
385 | MODULE_DEVICE_TABLE(i2c, lp8501_id); | 385 | MODULE_DEVICE_TABLE(i2c, lp8501_id); |
386 | 386 | ||
387 | #ifdef CONFIG_OF | 387 | #ifdef CONFIG_OF |
388 | static const struct of_device_id of_lp8501_leds_match[] = { | 388 | static const struct of_device_id of_lp8501_leds_match[] = { |
389 | { .compatible = "ti,lp8501", }, | 389 | { .compatible = "ti,lp8501", }, |
390 | {}, | 390 | {}, |
391 | }; | 391 | }; |
392 | 392 | ||
393 | MODULE_DEVICE_TABLE(of, of_lp8501_leds_match); | 393 | MODULE_DEVICE_TABLE(of, of_lp8501_leds_match); |
394 | #endif | 394 | #endif |
395 | 395 | ||
396 | static struct i2c_driver lp8501_driver = { | 396 | static struct i2c_driver lp8501_driver = { |
397 | .driver = { | 397 | .driver = { |
398 | .name = "lp8501", | 398 | .name = "lp8501", |
399 | .of_match_table = of_match_ptr(of_lp8501_leds_match), | 399 | .of_match_table = of_match_ptr(of_lp8501_leds_match), |
400 | }, | 400 | }, |
401 | .probe = lp8501_probe, | 401 | .probe = lp8501_probe, |
402 | .remove = lp8501_remove, | 402 | .remove = lp8501_remove, |
403 | .id_table = lp8501_id, | 403 | .id_table = lp8501_id, |
404 | }; | 404 | }; |
405 | 405 | ||
406 | module_i2c_driver(lp8501_driver); | 406 | module_i2c_driver(lp8501_driver); |
407 | 407 | ||
408 | MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier"); | 408 | MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier"); |
409 | MODULE_AUTHOR("Milo Kim"); | 409 | MODULE_AUTHOR("Milo Kim"); |
410 | MODULE_LICENSE("GPL"); | 410 | MODULE_LICENSE("GPL"); |
411 | 411 |
drivers/leds/leds-lt3593.c
1 | /* | 1 | /* |
2 | * LEDs driver for LT3593 controllers | 2 | * LEDs driver for LT3593 controllers |
3 | * | 3 | * |
4 | * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf | 4 | * See the datasheet at http://cds.linear.com/docs/Datasheet/3593f.pdf |
5 | * | 5 | * |
6 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> | 6 | * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> |
7 | * | 7 | * |
8 | * Based on leds-gpio.c, | 8 | * Based on leds-gpio.c, |
9 | * | 9 | * |
10 | * Copyright (C) 2007 8D Technologies inc. | 10 | * Copyright (C) 2007 8D Technologies inc. |
11 | * Raphael Assenat <raph@8d.com> | 11 | * Raphael Assenat <raph@8d.com> |
12 | * Copyright (C) 2008 Freescale Semiconductor, Inc. | 12 | * Copyright (C) 2008 Freescale Semiconductor, Inc. |
13 | * | 13 | * |
14 | * This program is free software; you can redistribute it and/or modify | 14 | * This program is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License version 2 as | 15 | * it under the terms of the GNU General Public License version 2 as |
16 | * published by the Free Software Foundation. | 16 | * published by the Free Software Foundation. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
22 | #include <linux/leds.h> | 22 | #include <linux/leds.h> |
23 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/gpio.h> | 25 | #include <linux/gpio.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | 28 | ||
29 | struct lt3593_led_data { | 29 | struct lt3593_led_data { |
30 | struct led_classdev cdev; | 30 | struct led_classdev cdev; |
31 | unsigned gpio; | 31 | unsigned gpio; |
32 | struct work_struct work; | 32 | struct work_struct work; |
33 | u8 new_level; | 33 | u8 new_level; |
34 | }; | 34 | }; |
35 | 35 | ||
36 | static void lt3593_led_work(struct work_struct *work) | 36 | static void lt3593_led_work(struct work_struct *work) |
37 | { | 37 | { |
38 | int pulses; | 38 | int pulses; |
39 | struct lt3593_led_data *led_dat = | 39 | struct lt3593_led_data *led_dat = |
40 | container_of(work, struct lt3593_led_data, work); | 40 | container_of(work, struct lt3593_led_data, work); |
41 | 41 | ||
42 | /* | 42 | /* |
43 | * The LT3593 resets its internal current level register to the maximum | 43 | * The LT3593 resets its internal current level register to the maximum |
44 | * level on the first falling edge on the control pin. Each following | 44 | * level on the first falling edge on the control pin. Each following |
45 | * falling edge decreases the current level by 625uA. Up to 32 pulses | 45 | * falling edge decreases the current level by 625uA. Up to 32 pulses |
46 | * can be sent, so the maximum power reduction is 20mA. | 46 | * can be sent, so the maximum power reduction is 20mA. |
47 | * After a timeout of 128us, the value is taken from the register and | 47 | * After a timeout of 128us, the value is taken from the register and |
48 | * applied is to the output driver. | 48 | * applied is to the output driver. |
49 | */ | 49 | */ |
50 | 50 | ||
51 | if (led_dat->new_level == 0) { | 51 | if (led_dat->new_level == 0) { |
52 | gpio_set_value_cansleep(led_dat->gpio, 0); | 52 | gpio_set_value_cansleep(led_dat->gpio, 0); |
53 | return; | 53 | return; |
54 | } | 54 | } |
55 | 55 | ||
56 | pulses = 32 - (led_dat->new_level * 32) / 255; | 56 | pulses = 32 - (led_dat->new_level * 32) / 255; |
57 | 57 | ||
58 | if (pulses == 0) { | 58 | if (pulses == 0) { |
59 | gpio_set_value_cansleep(led_dat->gpio, 0); | 59 | gpio_set_value_cansleep(led_dat->gpio, 0); |
60 | mdelay(1); | 60 | mdelay(1); |
61 | gpio_set_value_cansleep(led_dat->gpio, 1); | 61 | gpio_set_value_cansleep(led_dat->gpio, 1); |
62 | return; | 62 | return; |
63 | } | 63 | } |
64 | 64 | ||
65 | gpio_set_value_cansleep(led_dat->gpio, 1); | 65 | gpio_set_value_cansleep(led_dat->gpio, 1); |
66 | 66 | ||
67 | while (pulses--) { | 67 | while (pulses--) { |
68 | gpio_set_value_cansleep(led_dat->gpio, 0); | 68 | gpio_set_value_cansleep(led_dat->gpio, 0); |
69 | udelay(1); | 69 | udelay(1); |
70 | gpio_set_value_cansleep(led_dat->gpio, 1); | 70 | gpio_set_value_cansleep(led_dat->gpio, 1); |
71 | udelay(1); | 71 | udelay(1); |
72 | } | 72 | } |
73 | } | 73 | } |
74 | 74 | ||
75 | static void lt3593_led_set(struct led_classdev *led_cdev, | 75 | static void lt3593_led_set(struct led_classdev *led_cdev, |
76 | enum led_brightness value) | 76 | enum led_brightness value) |
77 | { | 77 | { |
78 | struct lt3593_led_data *led_dat = | 78 | struct lt3593_led_data *led_dat = |
79 | container_of(led_cdev, struct lt3593_led_data, cdev); | 79 | container_of(led_cdev, struct lt3593_led_data, cdev); |
80 | 80 | ||
81 | led_dat->new_level = value; | 81 | led_dat->new_level = value; |
82 | schedule_work(&led_dat->work); | 82 | schedule_work(&led_dat->work); |
83 | } | 83 | } |
84 | 84 | ||
85 | static int create_lt3593_led(const struct gpio_led *template, | 85 | static int create_lt3593_led(const struct gpio_led *template, |
86 | struct lt3593_led_data *led_dat, struct device *parent) | 86 | struct lt3593_led_data *led_dat, struct device *parent) |
87 | { | 87 | { |
88 | int ret, state; | 88 | int ret, state; |
89 | 89 | ||
90 | /* skip leds on GPIOs that aren't available */ | 90 | /* skip leds on GPIOs that aren't available */ |
91 | if (!gpio_is_valid(template->gpio)) { | 91 | if (!gpio_is_valid(template->gpio)) { |
92 | dev_info(parent, "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", | 92 | dev_info(parent, "%s: skipping unavailable LT3593 LED at gpio %d (%s)\n", |
93 | KBUILD_MODNAME, template->gpio, template->name); | 93 | KBUILD_MODNAME, template->gpio, template->name); |
94 | return 0; | 94 | return 0; |
95 | } | 95 | } |
96 | 96 | ||
97 | led_dat->cdev.name = template->name; | 97 | led_dat->cdev.name = template->name; |
98 | led_dat->cdev.default_trigger = template->default_trigger; | 98 | led_dat->cdev.default_trigger = template->default_trigger; |
99 | led_dat->gpio = template->gpio; | 99 | led_dat->gpio = template->gpio; |
100 | 100 | ||
101 | led_dat->cdev.brightness_set = lt3593_led_set; | 101 | led_dat->cdev.brightness_set = lt3593_led_set; |
102 | 102 | ||
103 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); | 103 | state = (template->default_state == LEDS_GPIO_DEFSTATE_ON); |
104 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; | 104 | led_dat->cdev.brightness = state ? LED_FULL : LED_OFF; |
105 | 105 | ||
106 | if (!template->retain_state_suspended) | 106 | if (!template->retain_state_suspended) |
107 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 107 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
108 | 108 | ||
109 | ret = devm_gpio_request_one(parent, template->gpio, state ? | 109 | ret = devm_gpio_request_one(parent, template->gpio, state ? |
110 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, | 110 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, |
111 | template->name); | 111 | template->name); |
112 | if (ret < 0) | 112 | if (ret < 0) |
113 | return ret; | 113 | return ret; |
114 | 114 | ||
115 | INIT_WORK(&led_dat->work, lt3593_led_work); | 115 | INIT_WORK(&led_dat->work, lt3593_led_work); |
116 | 116 | ||
117 | ret = led_classdev_register(parent, &led_dat->cdev); | 117 | ret = led_classdev_register(parent, &led_dat->cdev); |
118 | if (ret < 0) | 118 | if (ret < 0) |
119 | return ret; | 119 | return ret; |
120 | 120 | ||
121 | dev_info(parent, "%s: registered LT3593 LED '%s' at GPIO %d\n", | 121 | dev_info(parent, "%s: registered LT3593 LED '%s' at GPIO %d\n", |
122 | KBUILD_MODNAME, template->name, template->gpio); | 122 | KBUILD_MODNAME, template->name, template->gpio); |
123 | 123 | ||
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | static void delete_lt3593_led(struct lt3593_led_data *led) | 127 | static void delete_lt3593_led(struct lt3593_led_data *led) |
128 | { | 128 | { |
129 | if (!gpio_is_valid(led->gpio)) | 129 | if (!gpio_is_valid(led->gpio)) |
130 | return; | 130 | return; |
131 | 131 | ||
132 | led_classdev_unregister(&led->cdev); | 132 | led_classdev_unregister(&led->cdev); |
133 | cancel_work_sync(&led->work); | 133 | cancel_work_sync(&led->work); |
134 | } | 134 | } |
135 | 135 | ||
136 | static int lt3593_led_probe(struct platform_device *pdev) | 136 | static int lt3593_led_probe(struct platform_device *pdev) |
137 | { | 137 | { |
138 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 138 | struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
139 | struct lt3593_led_data *leds_data; | 139 | struct lt3593_led_data *leds_data; |
140 | int i, ret = 0; | 140 | int i, ret = 0; |
141 | 141 | ||
142 | if (!pdata) | 142 | if (!pdata) |
143 | return -EBUSY; | 143 | return -EBUSY; |
144 | 144 | ||
145 | leds_data = devm_kzalloc(&pdev->dev, | 145 | leds_data = devm_kzalloc(&pdev->dev, |
146 | sizeof(struct lt3593_led_data) * pdata->num_leds, | 146 | sizeof(struct lt3593_led_data) * pdata->num_leds, |
147 | GFP_KERNEL); | 147 | GFP_KERNEL); |
148 | if (!leds_data) | 148 | if (!leds_data) |
149 | return -ENOMEM; | 149 | return -ENOMEM; |
150 | 150 | ||
151 | for (i = 0; i < pdata->num_leds; i++) { | 151 | for (i = 0; i < pdata->num_leds; i++) { |
152 | ret = create_lt3593_led(&pdata->leds[i], &leds_data[i], | 152 | ret = create_lt3593_led(&pdata->leds[i], &leds_data[i], |
153 | &pdev->dev); | 153 | &pdev->dev); |
154 | if (ret < 0) | 154 | if (ret < 0) |
155 | goto err; | 155 | goto err; |
156 | } | 156 | } |
157 | 157 | ||
158 | platform_set_drvdata(pdev, leds_data); | 158 | platform_set_drvdata(pdev, leds_data); |
159 | 159 | ||
160 | return 0; | 160 | return 0; |
161 | 161 | ||
162 | err: | 162 | err: |
163 | for (i = i - 1; i >= 0; i--) | 163 | for (i = i - 1; i >= 0; i--) |
164 | delete_lt3593_led(&leds_data[i]); | 164 | delete_lt3593_led(&leds_data[i]); |
165 | 165 | ||
166 | return ret; | 166 | return ret; |
167 | } | 167 | } |
168 | 168 | ||
169 | static int lt3593_led_remove(struct platform_device *pdev) | 169 | static int lt3593_led_remove(struct platform_device *pdev) |
170 | { | 170 | { |
171 | int i; | 171 | int i; |
172 | struct gpio_led_platform_data *pdata = pdev->dev.platform_data; | 172 | struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
173 | struct lt3593_led_data *leds_data; | 173 | struct lt3593_led_data *leds_data; |
174 | 174 | ||
175 | leds_data = platform_get_drvdata(pdev); | 175 | leds_data = platform_get_drvdata(pdev); |
176 | 176 | ||
177 | for (i = 0; i < pdata->num_leds; i++) | 177 | for (i = 0; i < pdata->num_leds; i++) |
178 | delete_lt3593_led(&leds_data[i]); | 178 | delete_lt3593_led(&leds_data[i]); |
179 | 179 | ||
180 | return 0; | 180 | return 0; |
181 | } | 181 | } |
182 | 182 | ||
183 | static struct platform_driver lt3593_led_driver = { | 183 | static struct platform_driver lt3593_led_driver = { |
184 | .probe = lt3593_led_probe, | 184 | .probe = lt3593_led_probe, |
185 | .remove = lt3593_led_remove, | 185 | .remove = lt3593_led_remove, |
186 | .driver = { | 186 | .driver = { |
187 | .name = "leds-lt3593", | 187 | .name = "leds-lt3593", |
188 | .owner = THIS_MODULE, | 188 | .owner = THIS_MODULE, |
189 | }, | 189 | }, |
190 | }; | 190 | }; |
191 | 191 | ||
192 | module_platform_driver(lt3593_led_driver); | 192 | module_platform_driver(lt3593_led_driver); |
193 | 193 | ||
194 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | 194 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); |
195 | MODULE_DESCRIPTION("LED driver for LT3593 controllers"); | 195 | MODULE_DESCRIPTION("LED driver for LT3593 controllers"); |
196 | MODULE_LICENSE("GPL"); | 196 | MODULE_LICENSE("GPL"); |
197 | MODULE_ALIAS("platform:leds-lt3593"); | 197 | MODULE_ALIAS("platform:leds-lt3593"); |
198 | 198 |
drivers/leds/leds-netxbig.c
1 | /* | 1 | /* |
2 | * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs | 2 | * leds-netxbig.c - Driver for the 2Big and 5Big Network series LEDs |
3 | * | 3 | * |
4 | * Copyright (C) 2010 LaCie | 4 | * Copyright (C) 2010 LaCie |
5 | * | 5 | * |
6 | * Author: Simon Guinot <sguinot@lacie.com> | 6 | * Author: Simon Guinot <sguinot@lacie.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
12 | * | 12 | * |
13 | * This program is distributed in the hope that it will be useful, | 13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. | 16 | * GNU General Public License for more details. |
17 | * | 17 | * |
18 | * You should have received a copy of the GNU General Public License | 18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | 19 | * along with this program; if not, write to the Free Software |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
25 | #include <linux/irq.h> | 25 | #include <linux/irq.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/spinlock.h> | 27 | #include <linux/spinlock.h> |
28 | #include <linux/platform_device.h> | 28 | #include <linux/platform_device.h> |
29 | #include <linux/gpio.h> | 29 | #include <linux/gpio.h> |
30 | #include <linux/leds.h> | 30 | #include <linux/leds.h> |
31 | #include <linux/platform_data/leds-kirkwood-netxbig.h> | 31 | #include <linux/platform_data/leds-kirkwood-netxbig.h> |
32 | 32 | ||
33 | /* | 33 | /* |
34 | * GPIO extension bus. | 34 | * GPIO extension bus. |
35 | */ | 35 | */ |
36 | 36 | ||
37 | static DEFINE_SPINLOCK(gpio_ext_lock); | 37 | static DEFINE_SPINLOCK(gpio_ext_lock); |
38 | 38 | ||
39 | static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr) | 39 | static void gpio_ext_set_addr(struct netxbig_gpio_ext *gpio_ext, int addr) |
40 | { | 40 | { |
41 | int pin; | 41 | int pin; |
42 | 42 | ||
43 | for (pin = 0; pin < gpio_ext->num_addr; pin++) | 43 | for (pin = 0; pin < gpio_ext->num_addr; pin++) |
44 | gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1); | 44 | gpio_set_value(gpio_ext->addr[pin], (addr >> pin) & 1); |
45 | } | 45 | } |
46 | 46 | ||
47 | static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data) | 47 | static void gpio_ext_set_data(struct netxbig_gpio_ext *gpio_ext, int data) |
48 | { | 48 | { |
49 | int pin; | 49 | int pin; |
50 | 50 | ||
51 | for (pin = 0; pin < gpio_ext->num_data; pin++) | 51 | for (pin = 0; pin < gpio_ext->num_data; pin++) |
52 | gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1); | 52 | gpio_set_value(gpio_ext->data[pin], (data >> pin) & 1); |
53 | } | 53 | } |
54 | 54 | ||
55 | static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext) | 55 | static void gpio_ext_enable_select(struct netxbig_gpio_ext *gpio_ext) |
56 | { | 56 | { |
57 | /* Enable select is done on the raising edge. */ | 57 | /* Enable select is done on the raising edge. */ |
58 | gpio_set_value(gpio_ext->enable, 0); | 58 | gpio_set_value(gpio_ext->enable, 0); |
59 | gpio_set_value(gpio_ext->enable, 1); | 59 | gpio_set_value(gpio_ext->enable, 1); |
60 | } | 60 | } |
61 | 61 | ||
62 | static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, | 62 | static void gpio_ext_set_value(struct netxbig_gpio_ext *gpio_ext, |
63 | int addr, int value) | 63 | int addr, int value) |
64 | { | 64 | { |
65 | unsigned long flags; | 65 | unsigned long flags; |
66 | 66 | ||
67 | spin_lock_irqsave(&gpio_ext_lock, flags); | 67 | spin_lock_irqsave(&gpio_ext_lock, flags); |
68 | gpio_ext_set_addr(gpio_ext, addr); | 68 | gpio_ext_set_addr(gpio_ext, addr); |
69 | gpio_ext_set_data(gpio_ext, value); | 69 | gpio_ext_set_data(gpio_ext, value); |
70 | gpio_ext_enable_select(gpio_ext); | 70 | gpio_ext_enable_select(gpio_ext); |
71 | spin_unlock_irqrestore(&gpio_ext_lock, flags); | 71 | spin_unlock_irqrestore(&gpio_ext_lock, flags); |
72 | } | 72 | } |
73 | 73 | ||
74 | static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext) | 74 | static int gpio_ext_init(struct netxbig_gpio_ext *gpio_ext) |
75 | { | 75 | { |
76 | int err; | 76 | int err; |
77 | int i; | 77 | int i; |
78 | 78 | ||
79 | if (unlikely(!gpio_ext)) | 79 | if (unlikely(!gpio_ext)) |
80 | return -EINVAL; | 80 | return -EINVAL; |
81 | 81 | ||
82 | /* Configure address GPIOs. */ | 82 | /* Configure address GPIOs. */ |
83 | for (i = 0; i < gpio_ext->num_addr; i++) { | 83 | for (i = 0; i < gpio_ext->num_addr; i++) { |
84 | err = gpio_request_one(gpio_ext->addr[i], GPIOF_OUT_INIT_LOW, | 84 | err = gpio_request_one(gpio_ext->addr[i], GPIOF_OUT_INIT_LOW, |
85 | "GPIO extension addr"); | 85 | "GPIO extension addr"); |
86 | if (err) | 86 | if (err) |
87 | goto err_free_addr; | 87 | goto err_free_addr; |
88 | } | 88 | } |
89 | /* Configure data GPIOs. */ | 89 | /* Configure data GPIOs. */ |
90 | for (i = 0; i < gpio_ext->num_data; i++) { | 90 | for (i = 0; i < gpio_ext->num_data; i++) { |
91 | err = gpio_request_one(gpio_ext->data[i], GPIOF_OUT_INIT_LOW, | 91 | err = gpio_request_one(gpio_ext->data[i], GPIOF_OUT_INIT_LOW, |
92 | "GPIO extension data"); | 92 | "GPIO extension data"); |
93 | if (err) | 93 | if (err) |
94 | goto err_free_data; | 94 | goto err_free_data; |
95 | } | 95 | } |
96 | /* Configure "enable select" GPIO. */ | 96 | /* Configure "enable select" GPIO. */ |
97 | err = gpio_request_one(gpio_ext->enable, GPIOF_OUT_INIT_LOW, | 97 | err = gpio_request_one(gpio_ext->enable, GPIOF_OUT_INIT_LOW, |
98 | "GPIO extension enable"); | 98 | "GPIO extension enable"); |
99 | if (err) | 99 | if (err) |
100 | goto err_free_data; | 100 | goto err_free_data; |
101 | 101 | ||
102 | return 0; | 102 | return 0; |
103 | 103 | ||
104 | err_free_data: | 104 | err_free_data: |
105 | for (i = i - 1; i >= 0; i--) | 105 | for (i = i - 1; i >= 0; i--) |
106 | gpio_free(gpio_ext->data[i]); | 106 | gpio_free(gpio_ext->data[i]); |
107 | i = gpio_ext->num_addr; | 107 | i = gpio_ext->num_addr; |
108 | err_free_addr: | 108 | err_free_addr: |
109 | for (i = i - 1; i >= 0; i--) | 109 | for (i = i - 1; i >= 0; i--) |
110 | gpio_free(gpio_ext->addr[i]); | 110 | gpio_free(gpio_ext->addr[i]); |
111 | 111 | ||
112 | return err; | 112 | return err; |
113 | } | 113 | } |
114 | 114 | ||
115 | static void gpio_ext_free(struct netxbig_gpio_ext *gpio_ext) | 115 | static void gpio_ext_free(struct netxbig_gpio_ext *gpio_ext) |
116 | { | 116 | { |
117 | int i; | 117 | int i; |
118 | 118 | ||
119 | gpio_free(gpio_ext->enable); | 119 | gpio_free(gpio_ext->enable); |
120 | for (i = gpio_ext->num_addr - 1; i >= 0; i--) | 120 | for (i = gpio_ext->num_addr - 1; i >= 0; i--) |
121 | gpio_free(gpio_ext->addr[i]); | 121 | gpio_free(gpio_ext->addr[i]); |
122 | for (i = gpio_ext->num_data - 1; i >= 0; i--) | 122 | for (i = gpio_ext->num_data - 1; i >= 0; i--) |
123 | gpio_free(gpio_ext->data[i]); | 123 | gpio_free(gpio_ext->data[i]); |
124 | } | 124 | } |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * Class LED driver. | 127 | * Class LED driver. |
128 | */ | 128 | */ |
129 | 129 | ||
130 | struct netxbig_led_data { | 130 | struct netxbig_led_data { |
131 | struct netxbig_gpio_ext *gpio_ext; | 131 | struct netxbig_gpio_ext *gpio_ext; |
132 | struct led_classdev cdev; | 132 | struct led_classdev cdev; |
133 | int mode_addr; | 133 | int mode_addr; |
134 | int *mode_val; | 134 | int *mode_val; |
135 | int bright_addr; | 135 | int bright_addr; |
136 | int bright_max; | 136 | int bright_max; |
137 | struct netxbig_led_timer *timer; | 137 | struct netxbig_led_timer *timer; |
138 | int num_timer; | 138 | int num_timer; |
139 | enum netxbig_led_mode mode; | 139 | enum netxbig_led_mode mode; |
140 | int sata; | 140 | int sata; |
141 | spinlock_t lock; | 141 | spinlock_t lock; |
142 | }; | 142 | }; |
143 | 143 | ||
144 | static int netxbig_led_get_timer_mode(enum netxbig_led_mode *mode, | 144 | static int netxbig_led_get_timer_mode(enum netxbig_led_mode *mode, |
145 | unsigned long delay_on, | 145 | unsigned long delay_on, |
146 | unsigned long delay_off, | 146 | unsigned long delay_off, |
147 | struct netxbig_led_timer *timer, | 147 | struct netxbig_led_timer *timer, |
148 | int num_timer) | 148 | int num_timer) |
149 | { | 149 | { |
150 | int i; | 150 | int i; |
151 | 151 | ||
152 | for (i = 0; i < num_timer; i++) { | 152 | for (i = 0; i < num_timer; i++) { |
153 | if (timer[i].delay_on == delay_on && | 153 | if (timer[i].delay_on == delay_on && |
154 | timer[i].delay_off == delay_off) { | 154 | timer[i].delay_off == delay_off) { |
155 | *mode = timer[i].mode; | 155 | *mode = timer[i].mode; |
156 | return 0; | 156 | return 0; |
157 | } | 157 | } |
158 | } | 158 | } |
159 | return -EINVAL; | 159 | return -EINVAL; |
160 | } | 160 | } |
161 | 161 | ||
162 | static int netxbig_led_blink_set(struct led_classdev *led_cdev, | 162 | static int netxbig_led_blink_set(struct led_classdev *led_cdev, |
163 | unsigned long *delay_on, | 163 | unsigned long *delay_on, |
164 | unsigned long *delay_off) | 164 | unsigned long *delay_off) |
165 | { | 165 | { |
166 | struct netxbig_led_data *led_dat = | 166 | struct netxbig_led_data *led_dat = |
167 | container_of(led_cdev, struct netxbig_led_data, cdev); | 167 | container_of(led_cdev, struct netxbig_led_data, cdev); |
168 | enum netxbig_led_mode mode; | 168 | enum netxbig_led_mode mode; |
169 | int mode_val; | 169 | int mode_val; |
170 | int ret; | 170 | int ret; |
171 | 171 | ||
172 | /* Look for a LED mode with the requested timer frequency. */ | 172 | /* Look for a LED mode with the requested timer frequency. */ |
173 | ret = netxbig_led_get_timer_mode(&mode, *delay_on, *delay_off, | 173 | ret = netxbig_led_get_timer_mode(&mode, *delay_on, *delay_off, |
174 | led_dat->timer, led_dat->num_timer); | 174 | led_dat->timer, led_dat->num_timer); |
175 | if (ret < 0) | 175 | if (ret < 0) |
176 | return ret; | 176 | return ret; |
177 | 177 | ||
178 | mode_val = led_dat->mode_val[mode]; | 178 | mode_val = led_dat->mode_val[mode]; |
179 | if (mode_val == NETXBIG_LED_INVALID_MODE) | 179 | if (mode_val == NETXBIG_LED_INVALID_MODE) |
180 | return -EINVAL; | 180 | return -EINVAL; |
181 | 181 | ||
182 | spin_lock_irq(&led_dat->lock); | 182 | spin_lock_irq(&led_dat->lock); |
183 | 183 | ||
184 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); | 184 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); |
185 | led_dat->mode = mode; | 185 | led_dat->mode = mode; |
186 | 186 | ||
187 | spin_unlock_irq(&led_dat->lock); | 187 | spin_unlock_irq(&led_dat->lock); |
188 | 188 | ||
189 | return 0; | 189 | return 0; |
190 | } | 190 | } |
191 | 191 | ||
192 | static void netxbig_led_set(struct led_classdev *led_cdev, | 192 | static void netxbig_led_set(struct led_classdev *led_cdev, |
193 | enum led_brightness value) | 193 | enum led_brightness value) |
194 | { | 194 | { |
195 | struct netxbig_led_data *led_dat = | 195 | struct netxbig_led_data *led_dat = |
196 | container_of(led_cdev, struct netxbig_led_data, cdev); | 196 | container_of(led_cdev, struct netxbig_led_data, cdev); |
197 | enum netxbig_led_mode mode; | 197 | enum netxbig_led_mode mode; |
198 | int mode_val, bright_val; | 198 | int mode_val, bright_val; |
199 | int set_brightness = 1; | 199 | int set_brightness = 1; |
200 | unsigned long flags; | 200 | unsigned long flags; |
201 | 201 | ||
202 | spin_lock_irqsave(&led_dat->lock, flags); | 202 | spin_lock_irqsave(&led_dat->lock, flags); |
203 | 203 | ||
204 | if (value == LED_OFF) { | 204 | if (value == LED_OFF) { |
205 | mode = NETXBIG_LED_OFF; | 205 | mode = NETXBIG_LED_OFF; |
206 | set_brightness = 0; | 206 | set_brightness = 0; |
207 | } else { | 207 | } else { |
208 | if (led_dat->sata) | 208 | if (led_dat->sata) |
209 | mode = NETXBIG_LED_SATA; | 209 | mode = NETXBIG_LED_SATA; |
210 | else if (led_dat->mode == NETXBIG_LED_OFF) | 210 | else if (led_dat->mode == NETXBIG_LED_OFF) |
211 | mode = NETXBIG_LED_ON; | 211 | mode = NETXBIG_LED_ON; |
212 | else /* Keep 'timer' mode. */ | 212 | else /* Keep 'timer' mode. */ |
213 | mode = led_dat->mode; | 213 | mode = led_dat->mode; |
214 | } | 214 | } |
215 | mode_val = led_dat->mode_val[mode]; | 215 | mode_val = led_dat->mode_val[mode]; |
216 | 216 | ||
217 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); | 217 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); |
218 | led_dat->mode = mode; | 218 | led_dat->mode = mode; |
219 | /* | 219 | /* |
220 | * Note that the brightness register is shared between all the | 220 | * Note that the brightness register is shared between all the |
221 | * SATA LEDs. So, change the brightness setting for a single | 221 | * SATA LEDs. So, change the brightness setting for a single |
222 | * SATA LED will affect all the others. | 222 | * SATA LED will affect all the others. |
223 | */ | 223 | */ |
224 | if (set_brightness) { | 224 | if (set_brightness) { |
225 | bright_val = DIV_ROUND_UP(value * led_dat->bright_max, | 225 | bright_val = DIV_ROUND_UP(value * led_dat->bright_max, |
226 | LED_FULL); | 226 | LED_FULL); |
227 | gpio_ext_set_value(led_dat->gpio_ext, | 227 | gpio_ext_set_value(led_dat->gpio_ext, |
228 | led_dat->bright_addr, bright_val); | 228 | led_dat->bright_addr, bright_val); |
229 | } | 229 | } |
230 | 230 | ||
231 | spin_unlock_irqrestore(&led_dat->lock, flags); | 231 | spin_unlock_irqrestore(&led_dat->lock, flags); |
232 | } | 232 | } |
233 | 233 | ||
234 | static ssize_t netxbig_led_sata_store(struct device *dev, | 234 | static ssize_t netxbig_led_sata_store(struct device *dev, |
235 | struct device_attribute *attr, | 235 | struct device_attribute *attr, |
236 | const char *buff, size_t count) | 236 | const char *buff, size_t count) |
237 | { | 237 | { |
238 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 238 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
239 | struct netxbig_led_data *led_dat = | 239 | struct netxbig_led_data *led_dat = |
240 | container_of(led_cdev, struct netxbig_led_data, cdev); | 240 | container_of(led_cdev, struct netxbig_led_data, cdev); |
241 | unsigned long enable; | 241 | unsigned long enable; |
242 | enum netxbig_led_mode mode; | 242 | enum netxbig_led_mode mode; |
243 | int mode_val; | 243 | int mode_val; |
244 | int ret; | 244 | int ret; |
245 | 245 | ||
246 | ret = kstrtoul(buff, 10, &enable); | 246 | ret = kstrtoul(buff, 10, &enable); |
247 | if (ret < 0) | 247 | if (ret < 0) |
248 | return ret; | 248 | return ret; |
249 | 249 | ||
250 | enable = !!enable; | 250 | enable = !!enable; |
251 | 251 | ||
252 | spin_lock_irq(&led_dat->lock); | 252 | spin_lock_irq(&led_dat->lock); |
253 | 253 | ||
254 | if (led_dat->sata == enable) { | 254 | if (led_dat->sata == enable) { |
255 | ret = count; | 255 | ret = count; |
256 | goto exit_unlock; | 256 | goto exit_unlock; |
257 | } | 257 | } |
258 | 258 | ||
259 | if (led_dat->mode != NETXBIG_LED_ON && | 259 | if (led_dat->mode != NETXBIG_LED_ON && |
260 | led_dat->mode != NETXBIG_LED_SATA) | 260 | led_dat->mode != NETXBIG_LED_SATA) |
261 | mode = led_dat->mode; /* Keep modes 'off' and 'timer'. */ | 261 | mode = led_dat->mode; /* Keep modes 'off' and 'timer'. */ |
262 | else if (enable) | 262 | else if (enable) |
263 | mode = NETXBIG_LED_SATA; | 263 | mode = NETXBIG_LED_SATA; |
264 | else | 264 | else |
265 | mode = NETXBIG_LED_ON; | 265 | mode = NETXBIG_LED_ON; |
266 | 266 | ||
267 | mode_val = led_dat->mode_val[mode]; | 267 | mode_val = led_dat->mode_val[mode]; |
268 | if (mode_val == NETXBIG_LED_INVALID_MODE) { | 268 | if (mode_val == NETXBIG_LED_INVALID_MODE) { |
269 | ret = -EINVAL; | 269 | ret = -EINVAL; |
270 | goto exit_unlock; | 270 | goto exit_unlock; |
271 | } | 271 | } |
272 | 272 | ||
273 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); | 273 | gpio_ext_set_value(led_dat->gpio_ext, led_dat->mode_addr, mode_val); |
274 | led_dat->mode = mode; | 274 | led_dat->mode = mode; |
275 | led_dat->sata = enable; | 275 | led_dat->sata = enable; |
276 | 276 | ||
277 | ret = count; | 277 | ret = count; |
278 | 278 | ||
279 | exit_unlock: | 279 | exit_unlock: |
280 | spin_unlock_irq(&led_dat->lock); | 280 | spin_unlock_irq(&led_dat->lock); |
281 | 281 | ||
282 | return ret; | 282 | return ret; |
283 | } | 283 | } |
284 | 284 | ||
285 | static ssize_t netxbig_led_sata_show(struct device *dev, | 285 | static ssize_t netxbig_led_sata_show(struct device *dev, |
286 | struct device_attribute *attr, char *buf) | 286 | struct device_attribute *attr, char *buf) |
287 | { | 287 | { |
288 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 288 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
289 | struct netxbig_led_data *led_dat = | 289 | struct netxbig_led_data *led_dat = |
290 | container_of(led_cdev, struct netxbig_led_data, cdev); | 290 | container_of(led_cdev, struct netxbig_led_data, cdev); |
291 | 291 | ||
292 | return sprintf(buf, "%d\n", led_dat->sata); | 292 | return sprintf(buf, "%d\n", led_dat->sata); |
293 | } | 293 | } |
294 | 294 | ||
295 | static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); | 295 | static DEVICE_ATTR(sata, 0644, netxbig_led_sata_show, netxbig_led_sata_store); |
296 | 296 | ||
297 | static void delete_netxbig_led(struct netxbig_led_data *led_dat) | 297 | static void delete_netxbig_led(struct netxbig_led_data *led_dat) |
298 | { | 298 | { |
299 | if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) | 299 | if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) |
300 | device_remove_file(led_dat->cdev.dev, &dev_attr_sata); | 300 | device_remove_file(led_dat->cdev.dev, &dev_attr_sata); |
301 | led_classdev_unregister(&led_dat->cdev); | 301 | led_classdev_unregister(&led_dat->cdev); |
302 | } | 302 | } |
303 | 303 | ||
304 | static int | 304 | static int |
305 | create_netxbig_led(struct platform_device *pdev, | 305 | create_netxbig_led(struct platform_device *pdev, |
306 | struct netxbig_led_data *led_dat, | 306 | struct netxbig_led_data *led_dat, |
307 | const struct netxbig_led *template) | 307 | const struct netxbig_led *template) |
308 | { | 308 | { |
309 | struct netxbig_led_platform_data *pdata = pdev->dev.platform_data; | 309 | struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
310 | int ret; | 310 | int ret; |
311 | 311 | ||
312 | spin_lock_init(&led_dat->lock); | 312 | spin_lock_init(&led_dat->lock); |
313 | led_dat->gpio_ext = pdata->gpio_ext; | 313 | led_dat->gpio_ext = pdata->gpio_ext; |
314 | led_dat->cdev.name = template->name; | 314 | led_dat->cdev.name = template->name; |
315 | led_dat->cdev.default_trigger = template->default_trigger; | 315 | led_dat->cdev.default_trigger = template->default_trigger; |
316 | led_dat->cdev.blink_set = netxbig_led_blink_set; | 316 | led_dat->cdev.blink_set = netxbig_led_blink_set; |
317 | led_dat->cdev.brightness_set = netxbig_led_set; | 317 | led_dat->cdev.brightness_set = netxbig_led_set; |
318 | /* | 318 | /* |
319 | * Because the GPIO extension bus don't allow to read registers | 319 | * Because the GPIO extension bus don't allow to read registers |
320 | * value, there is no way to probe the LED initial state. | 320 | * value, there is no way to probe the LED initial state. |
321 | * So, the initial sysfs LED value for the "brightness" and "sata" | 321 | * So, the initial sysfs LED value for the "brightness" and "sata" |
322 | * attributes are inconsistent. | 322 | * attributes are inconsistent. |
323 | * | 323 | * |
324 | * Note that the initial LED state can't be reconfigured. | 324 | * Note that the initial LED state can't be reconfigured. |
325 | * The reason is that the LED behaviour must stay uniform during | 325 | * The reason is that the LED behaviour must stay uniform during |
326 | * the whole boot process (bootloader+linux). | 326 | * the whole boot process (bootloader+linux). |
327 | */ | 327 | */ |
328 | led_dat->sata = 0; | 328 | led_dat->sata = 0; |
329 | led_dat->cdev.brightness = LED_OFF; | 329 | led_dat->cdev.brightness = LED_OFF; |
330 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 330 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
331 | led_dat->mode_addr = template->mode_addr; | 331 | led_dat->mode_addr = template->mode_addr; |
332 | led_dat->mode_val = template->mode_val; | 332 | led_dat->mode_val = template->mode_val; |
333 | led_dat->bright_addr = template->bright_addr; | 333 | led_dat->bright_addr = template->bright_addr; |
334 | led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1; | 334 | led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1; |
335 | led_dat->timer = pdata->timer; | 335 | led_dat->timer = pdata->timer; |
336 | led_dat->num_timer = pdata->num_timer; | 336 | led_dat->num_timer = pdata->num_timer; |
337 | 337 | ||
338 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 338 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
339 | if (ret < 0) | 339 | if (ret < 0) |
340 | return ret; | 340 | return ret; |
341 | 341 | ||
342 | /* | 342 | /* |
343 | * If available, expose the SATA activity blink capability through | 343 | * If available, expose the SATA activity blink capability through |
344 | * a "sata" sysfs attribute. | 344 | * a "sata" sysfs attribute. |
345 | */ | 345 | */ |
346 | if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) { | 346 | if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) { |
347 | ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); | 347 | ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); |
348 | if (ret) | 348 | if (ret) |
349 | led_classdev_unregister(&led_dat->cdev); | 349 | led_classdev_unregister(&led_dat->cdev); |
350 | } | 350 | } |
351 | 351 | ||
352 | return ret; | 352 | return ret; |
353 | } | 353 | } |
354 | 354 | ||
355 | static int netxbig_led_probe(struct platform_device *pdev) | 355 | static int netxbig_led_probe(struct platform_device *pdev) |
356 | { | 356 | { |
357 | struct netxbig_led_platform_data *pdata = pdev->dev.platform_data; | 357 | struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
358 | struct netxbig_led_data *leds_data; | 358 | struct netxbig_led_data *leds_data; |
359 | int i; | 359 | int i; |
360 | int ret; | 360 | int ret; |
361 | 361 | ||
362 | if (!pdata) | 362 | if (!pdata) |
363 | return -EINVAL; | 363 | return -EINVAL; |
364 | 364 | ||
365 | leds_data = devm_kzalloc(&pdev->dev, | 365 | leds_data = devm_kzalloc(&pdev->dev, |
366 | sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL); | 366 | sizeof(struct netxbig_led_data) * pdata->num_leds, GFP_KERNEL); |
367 | if (!leds_data) | 367 | if (!leds_data) |
368 | return -ENOMEM; | 368 | return -ENOMEM; |
369 | 369 | ||
370 | ret = gpio_ext_init(pdata->gpio_ext); | 370 | ret = gpio_ext_init(pdata->gpio_ext); |
371 | if (ret < 0) | 371 | if (ret < 0) |
372 | return ret; | 372 | return ret; |
373 | 373 | ||
374 | for (i = 0; i < pdata->num_leds; i++) { | 374 | for (i = 0; i < pdata->num_leds; i++) { |
375 | ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]); | 375 | ret = create_netxbig_led(pdev, &leds_data[i], &pdata->leds[i]); |
376 | if (ret < 0) | 376 | if (ret < 0) |
377 | goto err_free_leds; | 377 | goto err_free_leds; |
378 | } | 378 | } |
379 | 379 | ||
380 | platform_set_drvdata(pdev, leds_data); | 380 | platform_set_drvdata(pdev, leds_data); |
381 | 381 | ||
382 | return 0; | 382 | return 0; |
383 | 383 | ||
384 | err_free_leds: | 384 | err_free_leds: |
385 | for (i = i - 1; i >= 0; i--) | 385 | for (i = i - 1; i >= 0; i--) |
386 | delete_netxbig_led(&leds_data[i]); | 386 | delete_netxbig_led(&leds_data[i]); |
387 | 387 | ||
388 | gpio_ext_free(pdata->gpio_ext); | 388 | gpio_ext_free(pdata->gpio_ext); |
389 | return ret; | 389 | return ret; |
390 | } | 390 | } |
391 | 391 | ||
392 | static int netxbig_led_remove(struct platform_device *pdev) | 392 | static int netxbig_led_remove(struct platform_device *pdev) |
393 | { | 393 | { |
394 | struct netxbig_led_platform_data *pdata = pdev->dev.platform_data; | 394 | struct netxbig_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
395 | struct netxbig_led_data *leds_data; | 395 | struct netxbig_led_data *leds_data; |
396 | int i; | 396 | int i; |
397 | 397 | ||
398 | leds_data = platform_get_drvdata(pdev); | 398 | leds_data = platform_get_drvdata(pdev); |
399 | 399 | ||
400 | for (i = 0; i < pdata->num_leds; i++) | 400 | for (i = 0; i < pdata->num_leds; i++) |
401 | delete_netxbig_led(&leds_data[i]); | 401 | delete_netxbig_led(&leds_data[i]); |
402 | 402 | ||
403 | gpio_ext_free(pdata->gpio_ext); | 403 | gpio_ext_free(pdata->gpio_ext); |
404 | 404 | ||
405 | return 0; | 405 | return 0; |
406 | } | 406 | } |
407 | 407 | ||
408 | static struct platform_driver netxbig_led_driver = { | 408 | static struct platform_driver netxbig_led_driver = { |
409 | .probe = netxbig_led_probe, | 409 | .probe = netxbig_led_probe, |
410 | .remove = netxbig_led_remove, | 410 | .remove = netxbig_led_remove, |
411 | .driver = { | 411 | .driver = { |
412 | .name = "leds-netxbig", | 412 | .name = "leds-netxbig", |
413 | .owner = THIS_MODULE, | 413 | .owner = THIS_MODULE, |
414 | }, | 414 | }, |
415 | }; | 415 | }; |
416 | 416 | ||
417 | module_platform_driver(netxbig_led_driver); | 417 | module_platform_driver(netxbig_led_driver); |
418 | 418 | ||
419 | MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); | 419 | MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); |
420 | MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards"); | 420 | MODULE_DESCRIPTION("LED driver for LaCie xBig Network boards"); |
421 | MODULE_LICENSE("GPL"); | 421 | MODULE_LICENSE("GPL"); |
422 | MODULE_ALIAS("platform:leds-netxbig"); | 422 | MODULE_ALIAS("platform:leds-netxbig"); |
423 | 423 |
drivers/leds/leds-ns2.c
1 | /* | 1 | /* |
2 | * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED | 2 | * leds-ns2.c - Driver for the Network Space v2 (and parents) dual-GPIO LED |
3 | * | 3 | * |
4 | * Copyright (C) 2010 LaCie | 4 | * Copyright (C) 2010 LaCie |
5 | * | 5 | * |
6 | * Author: Simon Guinot <sguinot@lacie.com> | 6 | * Author: Simon Guinot <sguinot@lacie.com> |
7 | * | 7 | * |
8 | * Based on leds-gpio.c by Raphael Assenat <raph@8d.com> | 8 | * Based on leds-gpio.c by Raphael Assenat <raph@8d.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or | 12 | * the Free Software Foundation; either version 2 of the License, or |
13 | * (at your option) any later version. | 13 | * (at your option) any later version. |
14 | * | 14 | * |
15 | * This program is distributed in the hope that it will be useful, | 15 | * This program is distributed in the hope that it will be useful, |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | * GNU General Public License for more details. | 18 | * GNU General Public License for more details. |
19 | * | 19 | * |
20 | * You should have received a copy of the GNU General Public License | 20 | * You should have received a copy of the GNU General Public License |
21 | * along with this program; if not, write to the Free Software | 21 | * along with this program; if not, write to the Free Software |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/gpio.h> | 29 | #include <linux/gpio.h> |
30 | #include <linux/leds.h> | 30 | #include <linux/leds.h> |
31 | #include <linux/module.h> | 31 | #include <linux/module.h> |
32 | #include <linux/platform_data/leds-kirkwood-ns2.h> | 32 | #include <linux/platform_data/leds-kirkwood-ns2.h> |
33 | #include <linux/of_gpio.h> | 33 | #include <linux/of_gpio.h> |
34 | 34 | ||
35 | /* | 35 | /* |
36 | * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in | 36 | * The Network Space v2 dual-GPIO LED is wired to a CPLD and can blink in |
37 | * relation with the SATA activity. This capability is exposed through the | 37 | * relation with the SATA activity. This capability is exposed through the |
38 | * "sata" sysfs attribute. | 38 | * "sata" sysfs attribute. |
39 | * | 39 | * |
40 | * The following array detail the different LED registers and the combination | 40 | * The following array detail the different LED registers and the combination |
41 | * of their possible values: | 41 | * of their possible values: |
42 | * | 42 | * |
43 | * cmd_led | slow_led | /SATA active | LED state | 43 | * cmd_led | slow_led | /SATA active | LED state |
44 | * | | | | 44 | * | | | |
45 | * 1 | 0 | x | off | 45 | * 1 | 0 | x | off |
46 | * - | 1 | x | on | 46 | * - | 1 | x | on |
47 | * 0 | 0 | 1 | on | 47 | * 0 | 0 | 1 | on |
48 | * 0 | 0 | 0 | blink (rate 300ms) | 48 | * 0 | 0 | 0 | blink (rate 300ms) |
49 | */ | 49 | */ |
50 | 50 | ||
51 | enum ns2_led_modes { | 51 | enum ns2_led_modes { |
52 | NS_V2_LED_OFF, | 52 | NS_V2_LED_OFF, |
53 | NS_V2_LED_ON, | 53 | NS_V2_LED_ON, |
54 | NS_V2_LED_SATA, | 54 | NS_V2_LED_SATA, |
55 | }; | 55 | }; |
56 | 56 | ||
57 | struct ns2_led_mode_value { | 57 | struct ns2_led_mode_value { |
58 | enum ns2_led_modes mode; | 58 | enum ns2_led_modes mode; |
59 | int cmd_level; | 59 | int cmd_level; |
60 | int slow_level; | 60 | int slow_level; |
61 | }; | 61 | }; |
62 | 62 | ||
63 | static struct ns2_led_mode_value ns2_led_modval[] = { | 63 | static struct ns2_led_mode_value ns2_led_modval[] = { |
64 | { NS_V2_LED_OFF , 1, 0 }, | 64 | { NS_V2_LED_OFF , 1, 0 }, |
65 | { NS_V2_LED_ON , 0, 1 }, | 65 | { NS_V2_LED_ON , 0, 1 }, |
66 | { NS_V2_LED_ON , 1, 1 }, | 66 | { NS_V2_LED_ON , 1, 1 }, |
67 | { NS_V2_LED_SATA, 0, 0 }, | 67 | { NS_V2_LED_SATA, 0, 0 }, |
68 | }; | 68 | }; |
69 | 69 | ||
70 | struct ns2_led_data { | 70 | struct ns2_led_data { |
71 | struct led_classdev cdev; | 71 | struct led_classdev cdev; |
72 | unsigned cmd; | 72 | unsigned cmd; |
73 | unsigned slow; | 73 | unsigned slow; |
74 | unsigned char sata; /* True when SATA mode active. */ | 74 | unsigned char sata; /* True when SATA mode active. */ |
75 | rwlock_t rw_lock; /* Lock GPIOs. */ | 75 | rwlock_t rw_lock; /* Lock GPIOs. */ |
76 | }; | 76 | }; |
77 | 77 | ||
78 | static int ns2_led_get_mode(struct ns2_led_data *led_dat, | 78 | static int ns2_led_get_mode(struct ns2_led_data *led_dat, |
79 | enum ns2_led_modes *mode) | 79 | enum ns2_led_modes *mode) |
80 | { | 80 | { |
81 | int i; | 81 | int i; |
82 | int ret = -EINVAL; | 82 | int ret = -EINVAL; |
83 | int cmd_level; | 83 | int cmd_level; |
84 | int slow_level; | 84 | int slow_level; |
85 | 85 | ||
86 | read_lock_irq(&led_dat->rw_lock); | 86 | read_lock_irq(&led_dat->rw_lock); |
87 | 87 | ||
88 | cmd_level = gpio_get_value(led_dat->cmd); | 88 | cmd_level = gpio_get_value(led_dat->cmd); |
89 | slow_level = gpio_get_value(led_dat->slow); | 89 | slow_level = gpio_get_value(led_dat->slow); |
90 | 90 | ||
91 | for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { | 91 | for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { |
92 | if (cmd_level == ns2_led_modval[i].cmd_level && | 92 | if (cmd_level == ns2_led_modval[i].cmd_level && |
93 | slow_level == ns2_led_modval[i].slow_level) { | 93 | slow_level == ns2_led_modval[i].slow_level) { |
94 | *mode = ns2_led_modval[i].mode; | 94 | *mode = ns2_led_modval[i].mode; |
95 | ret = 0; | 95 | ret = 0; |
96 | break; | 96 | break; |
97 | } | 97 | } |
98 | } | 98 | } |
99 | 99 | ||
100 | read_unlock_irq(&led_dat->rw_lock); | 100 | read_unlock_irq(&led_dat->rw_lock); |
101 | 101 | ||
102 | return ret; | 102 | return ret; |
103 | } | 103 | } |
104 | 104 | ||
105 | static void ns2_led_set_mode(struct ns2_led_data *led_dat, | 105 | static void ns2_led_set_mode(struct ns2_led_data *led_dat, |
106 | enum ns2_led_modes mode) | 106 | enum ns2_led_modes mode) |
107 | { | 107 | { |
108 | int i; | 108 | int i; |
109 | unsigned long flags; | 109 | unsigned long flags; |
110 | 110 | ||
111 | write_lock_irqsave(&led_dat->rw_lock, flags); | 111 | write_lock_irqsave(&led_dat->rw_lock, flags); |
112 | 112 | ||
113 | for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { | 113 | for (i = 0; i < ARRAY_SIZE(ns2_led_modval); i++) { |
114 | if (mode == ns2_led_modval[i].mode) { | 114 | if (mode == ns2_led_modval[i].mode) { |
115 | gpio_set_value(led_dat->cmd, | 115 | gpio_set_value(led_dat->cmd, |
116 | ns2_led_modval[i].cmd_level); | 116 | ns2_led_modval[i].cmd_level); |
117 | gpio_set_value(led_dat->slow, | 117 | gpio_set_value(led_dat->slow, |
118 | ns2_led_modval[i].slow_level); | 118 | ns2_led_modval[i].slow_level); |
119 | } | 119 | } |
120 | } | 120 | } |
121 | 121 | ||
122 | write_unlock_irqrestore(&led_dat->rw_lock, flags); | 122 | write_unlock_irqrestore(&led_dat->rw_lock, flags); |
123 | } | 123 | } |
124 | 124 | ||
125 | static void ns2_led_set(struct led_classdev *led_cdev, | 125 | static void ns2_led_set(struct led_classdev *led_cdev, |
126 | enum led_brightness value) | 126 | enum led_brightness value) |
127 | { | 127 | { |
128 | struct ns2_led_data *led_dat = | 128 | struct ns2_led_data *led_dat = |
129 | container_of(led_cdev, struct ns2_led_data, cdev); | 129 | container_of(led_cdev, struct ns2_led_data, cdev); |
130 | enum ns2_led_modes mode; | 130 | enum ns2_led_modes mode; |
131 | 131 | ||
132 | if (value == LED_OFF) | 132 | if (value == LED_OFF) |
133 | mode = NS_V2_LED_OFF; | 133 | mode = NS_V2_LED_OFF; |
134 | else if (led_dat->sata) | 134 | else if (led_dat->sata) |
135 | mode = NS_V2_LED_SATA; | 135 | mode = NS_V2_LED_SATA; |
136 | else | 136 | else |
137 | mode = NS_V2_LED_ON; | 137 | mode = NS_V2_LED_ON; |
138 | 138 | ||
139 | ns2_led_set_mode(led_dat, mode); | 139 | ns2_led_set_mode(led_dat, mode); |
140 | } | 140 | } |
141 | 141 | ||
142 | static ssize_t ns2_led_sata_store(struct device *dev, | 142 | static ssize_t ns2_led_sata_store(struct device *dev, |
143 | struct device_attribute *attr, | 143 | struct device_attribute *attr, |
144 | const char *buff, size_t count) | 144 | const char *buff, size_t count) |
145 | { | 145 | { |
146 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 146 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
147 | struct ns2_led_data *led_dat = | 147 | struct ns2_led_data *led_dat = |
148 | container_of(led_cdev, struct ns2_led_data, cdev); | 148 | container_of(led_cdev, struct ns2_led_data, cdev); |
149 | int ret; | 149 | int ret; |
150 | unsigned long enable; | 150 | unsigned long enable; |
151 | enum ns2_led_modes mode; | 151 | enum ns2_led_modes mode; |
152 | 152 | ||
153 | ret = kstrtoul(buff, 10, &enable); | 153 | ret = kstrtoul(buff, 10, &enable); |
154 | if (ret < 0) | 154 | if (ret < 0) |
155 | return ret; | 155 | return ret; |
156 | 156 | ||
157 | enable = !!enable; | 157 | enable = !!enable; |
158 | 158 | ||
159 | if (led_dat->sata == enable) | 159 | if (led_dat->sata == enable) |
160 | return count; | 160 | return count; |
161 | 161 | ||
162 | ret = ns2_led_get_mode(led_dat, &mode); | 162 | ret = ns2_led_get_mode(led_dat, &mode); |
163 | if (ret < 0) | 163 | if (ret < 0) |
164 | return ret; | 164 | return ret; |
165 | 165 | ||
166 | if (enable && mode == NS_V2_LED_ON) | 166 | if (enable && mode == NS_V2_LED_ON) |
167 | ns2_led_set_mode(led_dat, NS_V2_LED_SATA); | 167 | ns2_led_set_mode(led_dat, NS_V2_LED_SATA); |
168 | if (!enable && mode == NS_V2_LED_SATA) | 168 | if (!enable && mode == NS_V2_LED_SATA) |
169 | ns2_led_set_mode(led_dat, NS_V2_LED_ON); | 169 | ns2_led_set_mode(led_dat, NS_V2_LED_ON); |
170 | 170 | ||
171 | led_dat->sata = enable; | 171 | led_dat->sata = enable; |
172 | 172 | ||
173 | return count; | 173 | return count; |
174 | } | 174 | } |
175 | 175 | ||
176 | static ssize_t ns2_led_sata_show(struct device *dev, | 176 | static ssize_t ns2_led_sata_show(struct device *dev, |
177 | struct device_attribute *attr, char *buf) | 177 | struct device_attribute *attr, char *buf) |
178 | { | 178 | { |
179 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 179 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
180 | struct ns2_led_data *led_dat = | 180 | struct ns2_led_data *led_dat = |
181 | container_of(led_cdev, struct ns2_led_data, cdev); | 181 | container_of(led_cdev, struct ns2_led_data, cdev); |
182 | 182 | ||
183 | return sprintf(buf, "%d\n", led_dat->sata); | 183 | return sprintf(buf, "%d\n", led_dat->sata); |
184 | } | 184 | } |
185 | 185 | ||
186 | static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); | 186 | static DEVICE_ATTR(sata, 0644, ns2_led_sata_show, ns2_led_sata_store); |
187 | 187 | ||
188 | static int | 188 | static int |
189 | create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, | 189 | create_ns2_led(struct platform_device *pdev, struct ns2_led_data *led_dat, |
190 | const struct ns2_led *template) | 190 | const struct ns2_led *template) |
191 | { | 191 | { |
192 | int ret; | 192 | int ret; |
193 | enum ns2_led_modes mode; | 193 | enum ns2_led_modes mode; |
194 | 194 | ||
195 | ret = devm_gpio_request_one(&pdev->dev, template->cmd, | 195 | ret = devm_gpio_request_one(&pdev->dev, template->cmd, |
196 | gpio_get_value(template->cmd) ? | 196 | gpio_get_value(template->cmd) ? |
197 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, | 197 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, |
198 | template->name); | 198 | template->name); |
199 | if (ret) { | 199 | if (ret) { |
200 | dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", | 200 | dev_err(&pdev->dev, "%s: failed to setup command GPIO\n", |
201 | template->name); | 201 | template->name); |
202 | return ret; | 202 | return ret; |
203 | } | 203 | } |
204 | 204 | ||
205 | ret = devm_gpio_request_one(&pdev->dev, template->slow, | 205 | ret = devm_gpio_request_one(&pdev->dev, template->slow, |
206 | gpio_get_value(template->slow) ? | 206 | gpio_get_value(template->slow) ? |
207 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, | 207 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, |
208 | template->name); | 208 | template->name); |
209 | if (ret) { | 209 | if (ret) { |
210 | dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", | 210 | dev_err(&pdev->dev, "%s: failed to setup slow GPIO\n", |
211 | template->name); | 211 | template->name); |
212 | return ret; | 212 | return ret; |
213 | } | 213 | } |
214 | 214 | ||
215 | rwlock_init(&led_dat->rw_lock); | 215 | rwlock_init(&led_dat->rw_lock); |
216 | 216 | ||
217 | led_dat->cdev.name = template->name; | 217 | led_dat->cdev.name = template->name; |
218 | led_dat->cdev.default_trigger = template->default_trigger; | 218 | led_dat->cdev.default_trigger = template->default_trigger; |
219 | led_dat->cdev.blink_set = NULL; | 219 | led_dat->cdev.blink_set = NULL; |
220 | led_dat->cdev.brightness_set = ns2_led_set; | 220 | led_dat->cdev.brightness_set = ns2_led_set; |
221 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 221 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
222 | led_dat->cmd = template->cmd; | 222 | led_dat->cmd = template->cmd; |
223 | led_dat->slow = template->slow; | 223 | led_dat->slow = template->slow; |
224 | 224 | ||
225 | ret = ns2_led_get_mode(led_dat, &mode); | 225 | ret = ns2_led_get_mode(led_dat, &mode); |
226 | if (ret < 0) | 226 | if (ret < 0) |
227 | return ret; | 227 | return ret; |
228 | 228 | ||
229 | /* Set LED initial state. */ | 229 | /* Set LED initial state. */ |
230 | led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; | 230 | led_dat->sata = (mode == NS_V2_LED_SATA) ? 1 : 0; |
231 | led_dat->cdev.brightness = | 231 | led_dat->cdev.brightness = |
232 | (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; | 232 | (mode == NS_V2_LED_OFF) ? LED_OFF : LED_FULL; |
233 | 233 | ||
234 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 234 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
235 | if (ret < 0) | 235 | if (ret < 0) |
236 | return ret; | 236 | return ret; |
237 | 237 | ||
238 | ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); | 238 | ret = device_create_file(led_dat->cdev.dev, &dev_attr_sata); |
239 | if (ret < 0) | 239 | if (ret < 0) |
240 | goto err_free_cdev; | 240 | goto err_free_cdev; |
241 | 241 | ||
242 | return 0; | 242 | return 0; |
243 | 243 | ||
244 | err_free_cdev: | 244 | err_free_cdev: |
245 | led_classdev_unregister(&led_dat->cdev); | 245 | led_classdev_unregister(&led_dat->cdev); |
246 | return ret; | 246 | return ret; |
247 | } | 247 | } |
248 | 248 | ||
249 | static void delete_ns2_led(struct ns2_led_data *led_dat) | 249 | static void delete_ns2_led(struct ns2_led_data *led_dat) |
250 | { | 250 | { |
251 | device_remove_file(led_dat->cdev.dev, &dev_attr_sata); | 251 | device_remove_file(led_dat->cdev.dev, &dev_attr_sata); |
252 | led_classdev_unregister(&led_dat->cdev); | 252 | led_classdev_unregister(&led_dat->cdev); |
253 | } | 253 | } |
254 | 254 | ||
255 | #ifdef CONFIG_OF_GPIO | 255 | #ifdef CONFIG_OF_GPIO |
256 | /* | 256 | /* |
257 | * Translate OpenFirmware node properties into platform_data. | 257 | * Translate OpenFirmware node properties into platform_data. |
258 | */ | 258 | */ |
259 | static int | 259 | static int |
260 | ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) | 260 | ns2_leds_get_of_pdata(struct device *dev, struct ns2_led_platform_data *pdata) |
261 | { | 261 | { |
262 | struct device_node *np = dev->of_node; | 262 | struct device_node *np = dev->of_node; |
263 | struct device_node *child; | 263 | struct device_node *child; |
264 | struct ns2_led *leds; | 264 | struct ns2_led *leds; |
265 | int num_leds = 0; | 265 | int num_leds = 0; |
266 | int i = 0; | 266 | int i = 0; |
267 | 267 | ||
268 | num_leds = of_get_child_count(np); | 268 | num_leds = of_get_child_count(np); |
269 | if (!num_leds) | 269 | if (!num_leds) |
270 | return -ENODEV; | 270 | return -ENODEV; |
271 | 271 | ||
272 | leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), | 272 | leds = devm_kzalloc(dev, num_leds * sizeof(struct ns2_led), |
273 | GFP_KERNEL); | 273 | GFP_KERNEL); |
274 | if (!leds) | 274 | if (!leds) |
275 | return -ENOMEM; | 275 | return -ENOMEM; |
276 | 276 | ||
277 | for_each_child_of_node(np, child) { | 277 | for_each_child_of_node(np, child) { |
278 | const char *string; | 278 | const char *string; |
279 | int ret; | 279 | int ret; |
280 | 280 | ||
281 | ret = of_get_named_gpio(child, "cmd-gpio", 0); | 281 | ret = of_get_named_gpio(child, "cmd-gpio", 0); |
282 | if (ret < 0) | 282 | if (ret < 0) |
283 | return ret; | 283 | return ret; |
284 | leds[i].cmd = ret; | 284 | leds[i].cmd = ret; |
285 | ret = of_get_named_gpio(child, "slow-gpio", 0); | 285 | ret = of_get_named_gpio(child, "slow-gpio", 0); |
286 | if (ret < 0) | 286 | if (ret < 0) |
287 | return ret; | 287 | return ret; |
288 | leds[i].slow = ret; | 288 | leds[i].slow = ret; |
289 | ret = of_property_read_string(child, "label", &string); | 289 | ret = of_property_read_string(child, "label", &string); |
290 | leds[i].name = (ret == 0) ? string : child->name; | 290 | leds[i].name = (ret == 0) ? string : child->name; |
291 | ret = of_property_read_string(child, "linux,default-trigger", | 291 | ret = of_property_read_string(child, "linux,default-trigger", |
292 | &string); | 292 | &string); |
293 | if (ret == 0) | 293 | if (ret == 0) |
294 | leds[i].default_trigger = string; | 294 | leds[i].default_trigger = string; |
295 | 295 | ||
296 | i++; | 296 | i++; |
297 | } | 297 | } |
298 | 298 | ||
299 | pdata->leds = leds; | 299 | pdata->leds = leds; |
300 | pdata->num_leds = num_leds; | 300 | pdata->num_leds = num_leds; |
301 | 301 | ||
302 | return 0; | 302 | return 0; |
303 | } | 303 | } |
304 | 304 | ||
305 | static const struct of_device_id of_ns2_leds_match[] = { | 305 | static const struct of_device_id of_ns2_leds_match[] = { |
306 | { .compatible = "lacie,ns2-leds", }, | 306 | { .compatible = "lacie,ns2-leds", }, |
307 | {}, | 307 | {}, |
308 | }; | 308 | }; |
309 | #endif /* CONFIG_OF_GPIO */ | 309 | #endif /* CONFIG_OF_GPIO */ |
310 | 310 | ||
311 | struct ns2_led_priv { | 311 | struct ns2_led_priv { |
312 | int num_leds; | 312 | int num_leds; |
313 | struct ns2_led_data leds_data[]; | 313 | struct ns2_led_data leds_data[]; |
314 | }; | 314 | }; |
315 | 315 | ||
316 | static inline int sizeof_ns2_led_priv(int num_leds) | 316 | static inline int sizeof_ns2_led_priv(int num_leds) |
317 | { | 317 | { |
318 | return sizeof(struct ns2_led_priv) + | 318 | return sizeof(struct ns2_led_priv) + |
319 | (sizeof(struct ns2_led_data) * num_leds); | 319 | (sizeof(struct ns2_led_data) * num_leds); |
320 | } | 320 | } |
321 | 321 | ||
322 | static int ns2_led_probe(struct platform_device *pdev) | 322 | static int ns2_led_probe(struct platform_device *pdev) |
323 | { | 323 | { |
324 | struct ns2_led_platform_data *pdata = pdev->dev.platform_data; | 324 | struct ns2_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
325 | struct ns2_led_priv *priv; | 325 | struct ns2_led_priv *priv; |
326 | int i; | 326 | int i; |
327 | int ret; | 327 | int ret; |
328 | 328 | ||
329 | #ifdef CONFIG_OF_GPIO | 329 | #ifdef CONFIG_OF_GPIO |
330 | if (!pdata) { | 330 | if (!pdata) { |
331 | pdata = devm_kzalloc(&pdev->dev, | 331 | pdata = devm_kzalloc(&pdev->dev, |
332 | sizeof(struct ns2_led_platform_data), | 332 | sizeof(struct ns2_led_platform_data), |
333 | GFP_KERNEL); | 333 | GFP_KERNEL); |
334 | if (!pdata) | 334 | if (!pdata) |
335 | return -ENOMEM; | 335 | return -ENOMEM; |
336 | 336 | ||
337 | ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); | 337 | ret = ns2_leds_get_of_pdata(&pdev->dev, pdata); |
338 | if (ret) | 338 | if (ret) |
339 | return ret; | 339 | return ret; |
340 | } | 340 | } |
341 | #else | 341 | #else |
342 | if (!pdata) | 342 | if (!pdata) |
343 | return -EINVAL; | 343 | return -EINVAL; |
344 | #endif /* CONFIG_OF_GPIO */ | 344 | #endif /* CONFIG_OF_GPIO */ |
345 | 345 | ||
346 | priv = devm_kzalloc(&pdev->dev, | 346 | priv = devm_kzalloc(&pdev->dev, |
347 | sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); | 347 | sizeof_ns2_led_priv(pdata->num_leds), GFP_KERNEL); |
348 | if (!priv) | 348 | if (!priv) |
349 | return -ENOMEM; | 349 | return -ENOMEM; |
350 | priv->num_leds = pdata->num_leds; | 350 | priv->num_leds = pdata->num_leds; |
351 | 351 | ||
352 | for (i = 0; i < priv->num_leds; i++) { | 352 | for (i = 0; i < priv->num_leds; i++) { |
353 | ret = create_ns2_led(pdev, &priv->leds_data[i], | 353 | ret = create_ns2_led(pdev, &priv->leds_data[i], |
354 | &pdata->leds[i]); | 354 | &pdata->leds[i]); |
355 | if (ret < 0) { | 355 | if (ret < 0) { |
356 | for (i = i - 1; i >= 0; i--) | 356 | for (i = i - 1; i >= 0; i--) |
357 | delete_ns2_led(&priv->leds_data[i]); | 357 | delete_ns2_led(&priv->leds_data[i]); |
358 | return ret; | 358 | return ret; |
359 | } | 359 | } |
360 | } | 360 | } |
361 | 361 | ||
362 | platform_set_drvdata(pdev, priv); | 362 | platform_set_drvdata(pdev, priv); |
363 | 363 | ||
364 | return 0; | 364 | return 0; |
365 | } | 365 | } |
366 | 366 | ||
367 | static int ns2_led_remove(struct platform_device *pdev) | 367 | static int ns2_led_remove(struct platform_device *pdev) |
368 | { | 368 | { |
369 | int i; | 369 | int i; |
370 | struct ns2_led_priv *priv; | 370 | struct ns2_led_priv *priv; |
371 | 371 | ||
372 | priv = platform_get_drvdata(pdev); | 372 | priv = platform_get_drvdata(pdev); |
373 | 373 | ||
374 | for (i = 0; i < priv->num_leds; i++) | 374 | for (i = 0; i < priv->num_leds; i++) |
375 | delete_ns2_led(&priv->leds_data[i]); | 375 | delete_ns2_led(&priv->leds_data[i]); |
376 | 376 | ||
377 | return 0; | 377 | return 0; |
378 | } | 378 | } |
379 | 379 | ||
380 | static struct platform_driver ns2_led_driver = { | 380 | static struct platform_driver ns2_led_driver = { |
381 | .probe = ns2_led_probe, | 381 | .probe = ns2_led_probe, |
382 | .remove = ns2_led_remove, | 382 | .remove = ns2_led_remove, |
383 | .driver = { | 383 | .driver = { |
384 | .name = "leds-ns2", | 384 | .name = "leds-ns2", |
385 | .owner = THIS_MODULE, | 385 | .owner = THIS_MODULE, |
386 | .of_match_table = of_match_ptr(of_ns2_leds_match), | 386 | .of_match_table = of_match_ptr(of_ns2_leds_match), |
387 | }, | 387 | }, |
388 | }; | 388 | }; |
389 | 389 | ||
390 | module_platform_driver(ns2_led_driver); | 390 | module_platform_driver(ns2_led_driver); |
391 | 391 | ||
392 | MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); | 392 | MODULE_AUTHOR("Simon Guinot <sguinot@lacie.com>"); |
393 | MODULE_DESCRIPTION("Network Space v2 LED driver"); | 393 | MODULE_DESCRIPTION("Network Space v2 LED driver"); |
394 | MODULE_LICENSE("GPL"); | 394 | MODULE_LICENSE("GPL"); |
395 | MODULE_ALIAS("platform:leds-ns2"); | 395 | MODULE_ALIAS("platform:leds-ns2"); |
396 | 396 |
drivers/leds/leds-pca9532.c
1 | /* | 1 | /* |
2 | * pca9532.c - 16-bit Led dimmer | 2 | * pca9532.c - 16-bit Led dimmer |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Jan Weitzel | 4 | * Copyright (C) 2011 Jan Weitzel |
5 | * Copyright (C) 2008 Riku Voipio | 5 | * Copyright (C) 2008 Riku Voipio |
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 as published by | 8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; version 2 of the License. | 9 | * the Free Software Foundation; version 2 of the License. |
10 | * | 10 | * |
11 | * Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf | 11 | * Datasheet: http://www.nxp.com/documents/data_sheet/PCA9532.pdf |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/i2c.h> | 16 | #include <linux/i2c.h> |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | #include <linux/input.h> | 19 | #include <linux/input.h> |
20 | #include <linux/mutex.h> | 20 | #include <linux/mutex.h> |
21 | #include <linux/workqueue.h> | 21 | #include <linux/workqueue.h> |
22 | #include <linux/leds-pca9532.h> | 22 | #include <linux/leds-pca9532.h> |
23 | #include <linux/gpio.h> | 23 | #include <linux/gpio.h> |
24 | 24 | ||
25 | /* m = num_leds*/ | 25 | /* m = num_leds*/ |
26 | #define PCA9532_REG_INPUT(i) ((i) >> 3) | 26 | #define PCA9532_REG_INPUT(i) ((i) >> 3) |
27 | #define PCA9532_REG_OFFSET(m) ((m) >> 4) | 27 | #define PCA9532_REG_OFFSET(m) ((m) >> 4) |
28 | #define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2) | 28 | #define PCA9532_REG_PSC(m, i) (PCA9532_REG_OFFSET(m) + 0x1 + (i) * 2) |
29 | #define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2) | 29 | #define PCA9532_REG_PWM(m, i) (PCA9532_REG_OFFSET(m) + 0x2 + (i) * 2) |
30 | #define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2)) | 30 | #define LED_REG(m, led) (PCA9532_REG_OFFSET(m) + 0x5 + (led >> 2)) |
31 | #define LED_NUM(led) (led & 0x3) | 31 | #define LED_NUM(led) (led & 0x3) |
32 | 32 | ||
33 | #define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) | 33 | #define ldev_to_led(c) container_of(c, struct pca9532_led, ldev) |
34 | 34 | ||
35 | struct pca9532_chip_info { | 35 | struct pca9532_chip_info { |
36 | u8 num_leds; | 36 | u8 num_leds; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | struct pca9532_data { | 39 | struct pca9532_data { |
40 | struct i2c_client *client; | 40 | struct i2c_client *client; |
41 | struct pca9532_led leds[16]; | 41 | struct pca9532_led leds[16]; |
42 | struct mutex update_lock; | 42 | struct mutex update_lock; |
43 | struct input_dev *idev; | 43 | struct input_dev *idev; |
44 | struct work_struct work; | 44 | struct work_struct work; |
45 | #ifdef CONFIG_LEDS_PCA9532_GPIO | 45 | #ifdef CONFIG_LEDS_PCA9532_GPIO |
46 | struct gpio_chip gpio; | 46 | struct gpio_chip gpio; |
47 | #endif | 47 | #endif |
48 | const struct pca9532_chip_info *chip_info; | 48 | const struct pca9532_chip_info *chip_info; |
49 | u8 pwm[2]; | 49 | u8 pwm[2]; |
50 | u8 psc[2]; | 50 | u8 psc[2]; |
51 | }; | 51 | }; |
52 | 52 | ||
53 | static int pca9532_probe(struct i2c_client *client, | 53 | static int pca9532_probe(struct i2c_client *client, |
54 | const struct i2c_device_id *id); | 54 | const struct i2c_device_id *id); |
55 | static int pca9532_remove(struct i2c_client *client); | 55 | static int pca9532_remove(struct i2c_client *client); |
56 | 56 | ||
57 | enum { | 57 | enum { |
58 | pca9530, | 58 | pca9530, |
59 | pca9531, | 59 | pca9531, |
60 | pca9532, | 60 | pca9532, |
61 | pca9533, | 61 | pca9533, |
62 | }; | 62 | }; |
63 | 63 | ||
64 | static const struct i2c_device_id pca9532_id[] = { | 64 | static const struct i2c_device_id pca9532_id[] = { |
65 | { "pca9530", pca9530 }, | 65 | { "pca9530", pca9530 }, |
66 | { "pca9531", pca9531 }, | 66 | { "pca9531", pca9531 }, |
67 | { "pca9532", pca9532 }, | 67 | { "pca9532", pca9532 }, |
68 | { "pca9533", pca9533 }, | 68 | { "pca9533", pca9533 }, |
69 | { } | 69 | { } |
70 | }; | 70 | }; |
71 | 71 | ||
72 | MODULE_DEVICE_TABLE(i2c, pca9532_id); | 72 | MODULE_DEVICE_TABLE(i2c, pca9532_id); |
73 | 73 | ||
74 | static const struct pca9532_chip_info pca9532_chip_info_tbl[] = { | 74 | static const struct pca9532_chip_info pca9532_chip_info_tbl[] = { |
75 | [pca9530] = { | 75 | [pca9530] = { |
76 | .num_leds = 2, | 76 | .num_leds = 2, |
77 | }, | 77 | }, |
78 | [pca9531] = { | 78 | [pca9531] = { |
79 | .num_leds = 8, | 79 | .num_leds = 8, |
80 | }, | 80 | }, |
81 | [pca9532] = { | 81 | [pca9532] = { |
82 | .num_leds = 16, | 82 | .num_leds = 16, |
83 | }, | 83 | }, |
84 | [pca9533] = { | 84 | [pca9533] = { |
85 | .num_leds = 4, | 85 | .num_leds = 4, |
86 | }, | 86 | }, |
87 | }; | 87 | }; |
88 | 88 | ||
89 | static struct i2c_driver pca9532_driver = { | 89 | static struct i2c_driver pca9532_driver = { |
90 | .driver = { | 90 | .driver = { |
91 | .name = "leds-pca953x", | 91 | .name = "leds-pca953x", |
92 | }, | 92 | }, |
93 | .probe = pca9532_probe, | 93 | .probe = pca9532_probe, |
94 | .remove = pca9532_remove, | 94 | .remove = pca9532_remove, |
95 | .id_table = pca9532_id, | 95 | .id_table = pca9532_id, |
96 | }; | 96 | }; |
97 | 97 | ||
98 | /* We have two pwm/blinkers, but 16 possible leds to drive. Additionally, | 98 | /* We have two pwm/blinkers, but 16 possible leds to drive. Additionally, |
99 | * the clever Thecus people are using one pwm to drive the beeper. So, | 99 | * the clever Thecus people are using one pwm to drive the beeper. So, |
100 | * as a compromise we average one pwm to the values requested by all | 100 | * as a compromise we average one pwm to the values requested by all |
101 | * leds that are not ON/OFF. | 101 | * leds that are not ON/OFF. |
102 | * */ | 102 | * */ |
103 | static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, | 103 | static int pca9532_calcpwm(struct i2c_client *client, int pwm, int blink, |
104 | enum led_brightness value) | 104 | enum led_brightness value) |
105 | { | 105 | { |
106 | int a = 0, b = 0, i = 0; | 106 | int a = 0, b = 0, i = 0; |
107 | struct pca9532_data *data = i2c_get_clientdata(client); | 107 | struct pca9532_data *data = i2c_get_clientdata(client); |
108 | for (i = 0; i < data->chip_info->num_leds; i++) { | 108 | for (i = 0; i < data->chip_info->num_leds; i++) { |
109 | if (data->leds[i].type == PCA9532_TYPE_LED && | 109 | if (data->leds[i].type == PCA9532_TYPE_LED && |
110 | data->leds[i].state == PCA9532_PWM0+pwm) { | 110 | data->leds[i].state == PCA9532_PWM0+pwm) { |
111 | a++; | 111 | a++; |
112 | b += data->leds[i].ldev.brightness; | 112 | b += data->leds[i].ldev.brightness; |
113 | } | 113 | } |
114 | } | 114 | } |
115 | if (a == 0) { | 115 | if (a == 0) { |
116 | dev_err(&client->dev, | 116 | dev_err(&client->dev, |
117 | "fear of division by zero %d/%d, wanted %d\n", | 117 | "fear of division by zero %d/%d, wanted %d\n", |
118 | b, a, value); | 118 | b, a, value); |
119 | return -EINVAL; | 119 | return -EINVAL; |
120 | } | 120 | } |
121 | b = b/a; | 121 | b = b/a; |
122 | if (b > 0xFF) | 122 | if (b > 0xFF) |
123 | return -EINVAL; | 123 | return -EINVAL; |
124 | data->pwm[pwm] = b; | 124 | data->pwm[pwm] = b; |
125 | data->psc[pwm] = blink; | 125 | data->psc[pwm] = blink; |
126 | return 0; | 126 | return 0; |
127 | } | 127 | } |
128 | 128 | ||
129 | static int pca9532_setpwm(struct i2c_client *client, int pwm) | 129 | static int pca9532_setpwm(struct i2c_client *client, int pwm) |
130 | { | 130 | { |
131 | struct pca9532_data *data = i2c_get_clientdata(client); | 131 | struct pca9532_data *data = i2c_get_clientdata(client); |
132 | u8 maxleds = data->chip_info->num_leds; | 132 | u8 maxleds = data->chip_info->num_leds; |
133 | 133 | ||
134 | mutex_lock(&data->update_lock); | 134 | mutex_lock(&data->update_lock); |
135 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm), | 135 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, pwm), |
136 | data->pwm[pwm]); | 136 | data->pwm[pwm]); |
137 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm), | 137 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, pwm), |
138 | data->psc[pwm]); | 138 | data->psc[pwm]); |
139 | mutex_unlock(&data->update_lock); | 139 | mutex_unlock(&data->update_lock); |
140 | return 0; | 140 | return 0; |
141 | } | 141 | } |
142 | 142 | ||
143 | /* Set LED routing */ | 143 | /* Set LED routing */ |
144 | static void pca9532_setled(struct pca9532_led *led) | 144 | static void pca9532_setled(struct pca9532_led *led) |
145 | { | 145 | { |
146 | struct i2c_client *client = led->client; | 146 | struct i2c_client *client = led->client; |
147 | struct pca9532_data *data = i2c_get_clientdata(client); | 147 | struct pca9532_data *data = i2c_get_clientdata(client); |
148 | u8 maxleds = data->chip_info->num_leds; | 148 | u8 maxleds = data->chip_info->num_leds; |
149 | char reg; | 149 | char reg; |
150 | 150 | ||
151 | mutex_lock(&data->update_lock); | 151 | mutex_lock(&data->update_lock); |
152 | reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id)); | 152 | reg = i2c_smbus_read_byte_data(client, LED_REG(maxleds, led->id)); |
153 | /* zero led bits */ | 153 | /* zero led bits */ |
154 | reg = reg & ~(0x3<<LED_NUM(led->id)*2); | 154 | reg = reg & ~(0x3<<LED_NUM(led->id)*2); |
155 | /* set the new value */ | 155 | /* set the new value */ |
156 | reg = reg | (led->state << LED_NUM(led->id)*2); | 156 | reg = reg | (led->state << LED_NUM(led->id)*2); |
157 | i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg); | 157 | i2c_smbus_write_byte_data(client, LED_REG(maxleds, led->id), reg); |
158 | mutex_unlock(&data->update_lock); | 158 | mutex_unlock(&data->update_lock); |
159 | } | 159 | } |
160 | 160 | ||
161 | static void pca9532_set_brightness(struct led_classdev *led_cdev, | 161 | static void pca9532_set_brightness(struct led_classdev *led_cdev, |
162 | enum led_brightness value) | 162 | enum led_brightness value) |
163 | { | 163 | { |
164 | int err = 0; | 164 | int err = 0; |
165 | struct pca9532_led *led = ldev_to_led(led_cdev); | 165 | struct pca9532_led *led = ldev_to_led(led_cdev); |
166 | 166 | ||
167 | if (value == LED_OFF) | 167 | if (value == LED_OFF) |
168 | led->state = PCA9532_OFF; | 168 | led->state = PCA9532_OFF; |
169 | else if (value == LED_FULL) | 169 | else if (value == LED_FULL) |
170 | led->state = PCA9532_ON; | 170 | led->state = PCA9532_ON; |
171 | else { | 171 | else { |
172 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ | 172 | led->state = PCA9532_PWM0; /* Thecus: hardcode one pwm */ |
173 | err = pca9532_calcpwm(led->client, 0, 0, value); | 173 | err = pca9532_calcpwm(led->client, 0, 0, value); |
174 | if (err) | 174 | if (err) |
175 | return; /* XXX: led api doesn't allow error code? */ | 175 | return; /* XXX: led api doesn't allow error code? */ |
176 | } | 176 | } |
177 | schedule_work(&led->work); | 177 | schedule_work(&led->work); |
178 | } | 178 | } |
179 | 179 | ||
180 | static int pca9532_set_blink(struct led_classdev *led_cdev, | 180 | static int pca9532_set_blink(struct led_classdev *led_cdev, |
181 | unsigned long *delay_on, unsigned long *delay_off) | 181 | unsigned long *delay_on, unsigned long *delay_off) |
182 | { | 182 | { |
183 | struct pca9532_led *led = ldev_to_led(led_cdev); | 183 | struct pca9532_led *led = ldev_to_led(led_cdev); |
184 | struct i2c_client *client = led->client; | 184 | struct i2c_client *client = led->client; |
185 | int psc; | 185 | int psc; |
186 | int err = 0; | 186 | int err = 0; |
187 | 187 | ||
188 | if (*delay_on == 0 && *delay_off == 0) { | 188 | if (*delay_on == 0 && *delay_off == 0) { |
189 | /* led subsystem ask us for a blink rate */ | 189 | /* led subsystem ask us for a blink rate */ |
190 | *delay_on = 1000; | 190 | *delay_on = 1000; |
191 | *delay_off = 1000; | 191 | *delay_off = 1000; |
192 | } | 192 | } |
193 | if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) | 193 | if (*delay_on != *delay_off || *delay_on > 1690 || *delay_on < 6) |
194 | return -EINVAL; | 194 | return -EINVAL; |
195 | 195 | ||
196 | /* Thecus specific: only use PSC/PWM 0 */ | 196 | /* Thecus specific: only use PSC/PWM 0 */ |
197 | psc = (*delay_on * 152-1)/1000; | 197 | psc = (*delay_on * 152-1)/1000; |
198 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); | 198 | err = pca9532_calcpwm(client, 0, psc, led_cdev->brightness); |
199 | if (err) | 199 | if (err) |
200 | return err; | 200 | return err; |
201 | schedule_work(&led->work); | 201 | schedule_work(&led->work); |
202 | return 0; | 202 | return 0; |
203 | } | 203 | } |
204 | 204 | ||
205 | static int pca9532_event(struct input_dev *dev, unsigned int type, | 205 | static int pca9532_event(struct input_dev *dev, unsigned int type, |
206 | unsigned int code, int value) | 206 | unsigned int code, int value) |
207 | { | 207 | { |
208 | struct pca9532_data *data = input_get_drvdata(dev); | 208 | struct pca9532_data *data = input_get_drvdata(dev); |
209 | 209 | ||
210 | if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE))) | 210 | if (!(type == EV_SND && (code == SND_BELL || code == SND_TONE))) |
211 | return -1; | 211 | return -1; |
212 | 212 | ||
213 | /* XXX: allow different kind of beeps with psc/pwm modifications */ | 213 | /* XXX: allow different kind of beeps with psc/pwm modifications */ |
214 | if (value > 1 && value < 32767) | 214 | if (value > 1 && value < 32767) |
215 | data->pwm[1] = 127; | 215 | data->pwm[1] = 127; |
216 | else | 216 | else |
217 | data->pwm[1] = 0; | 217 | data->pwm[1] = 0; |
218 | 218 | ||
219 | schedule_work(&data->work); | 219 | schedule_work(&data->work); |
220 | 220 | ||
221 | return 0; | 221 | return 0; |
222 | } | 222 | } |
223 | 223 | ||
224 | static void pca9532_input_work(struct work_struct *work) | 224 | static void pca9532_input_work(struct work_struct *work) |
225 | { | 225 | { |
226 | struct pca9532_data *data = | 226 | struct pca9532_data *data = |
227 | container_of(work, struct pca9532_data, work); | 227 | container_of(work, struct pca9532_data, work); |
228 | u8 maxleds = data->chip_info->num_leds; | 228 | u8 maxleds = data->chip_info->num_leds; |
229 | 229 | ||
230 | mutex_lock(&data->update_lock); | 230 | mutex_lock(&data->update_lock); |
231 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1), | 231 | i2c_smbus_write_byte_data(data->client, PCA9532_REG_PWM(maxleds, 1), |
232 | data->pwm[1]); | 232 | data->pwm[1]); |
233 | mutex_unlock(&data->update_lock); | 233 | mutex_unlock(&data->update_lock); |
234 | } | 234 | } |
235 | 235 | ||
236 | static void pca9532_led_work(struct work_struct *work) | 236 | static void pca9532_led_work(struct work_struct *work) |
237 | { | 237 | { |
238 | struct pca9532_led *led; | 238 | struct pca9532_led *led; |
239 | led = container_of(work, struct pca9532_led, work); | 239 | led = container_of(work, struct pca9532_led, work); |
240 | if (led->state == PCA9532_PWM0) | 240 | if (led->state == PCA9532_PWM0) |
241 | pca9532_setpwm(led->client, 0); | 241 | pca9532_setpwm(led->client, 0); |
242 | pca9532_setled(led); | 242 | pca9532_setled(led); |
243 | } | 243 | } |
244 | 244 | ||
245 | #ifdef CONFIG_LEDS_PCA9532_GPIO | 245 | #ifdef CONFIG_LEDS_PCA9532_GPIO |
246 | static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset) | 246 | static int pca9532_gpio_request_pin(struct gpio_chip *gc, unsigned offset) |
247 | { | 247 | { |
248 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); | 248 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); |
249 | struct pca9532_led *led = &data->leds[offset]; | 249 | struct pca9532_led *led = &data->leds[offset]; |
250 | 250 | ||
251 | if (led->type == PCA9532_TYPE_GPIO) | 251 | if (led->type == PCA9532_TYPE_GPIO) |
252 | return 0; | 252 | return 0; |
253 | 253 | ||
254 | return -EBUSY; | 254 | return -EBUSY; |
255 | } | 255 | } |
256 | 256 | ||
257 | static void pca9532_gpio_set_value(struct gpio_chip *gc, unsigned offset, int val) | 257 | static void pca9532_gpio_set_value(struct gpio_chip *gc, unsigned offset, int val) |
258 | { | 258 | { |
259 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); | 259 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); |
260 | struct pca9532_led *led = &data->leds[offset]; | 260 | struct pca9532_led *led = &data->leds[offset]; |
261 | 261 | ||
262 | if (val) | 262 | if (val) |
263 | led->state = PCA9532_ON; | 263 | led->state = PCA9532_ON; |
264 | else | 264 | else |
265 | led->state = PCA9532_OFF; | 265 | led->state = PCA9532_OFF; |
266 | 266 | ||
267 | pca9532_setled(led); | 267 | pca9532_setled(led); |
268 | } | 268 | } |
269 | 269 | ||
270 | static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset) | 270 | static int pca9532_gpio_get_value(struct gpio_chip *gc, unsigned offset) |
271 | { | 271 | { |
272 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); | 272 | struct pca9532_data *data = container_of(gc, struct pca9532_data, gpio); |
273 | unsigned char reg; | 273 | unsigned char reg; |
274 | 274 | ||
275 | reg = i2c_smbus_read_byte_data(data->client, PCA9532_REG_INPUT(offset)); | 275 | reg = i2c_smbus_read_byte_data(data->client, PCA9532_REG_INPUT(offset)); |
276 | 276 | ||
277 | return !!(reg & (1 << (offset % 8))); | 277 | return !!(reg & (1 << (offset % 8))); |
278 | } | 278 | } |
279 | 279 | ||
280 | static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset) | 280 | static int pca9532_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
281 | { | 281 | { |
282 | /* To use as input ensure pin is not driven */ | 282 | /* To use as input ensure pin is not driven */ |
283 | pca9532_gpio_set_value(gc, offset, 0); | 283 | pca9532_gpio_set_value(gc, offset, 0); |
284 | 284 | ||
285 | return 0; | 285 | return 0; |
286 | } | 286 | } |
287 | 287 | ||
288 | static int pca9532_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int val) | 288 | static int pca9532_gpio_direction_output(struct gpio_chip *gc, unsigned offset, int val) |
289 | { | 289 | { |
290 | pca9532_gpio_set_value(gc, offset, val); | 290 | pca9532_gpio_set_value(gc, offset, val); |
291 | 291 | ||
292 | return 0; | 292 | return 0; |
293 | } | 293 | } |
294 | #endif /* CONFIG_LEDS_PCA9532_GPIO */ | 294 | #endif /* CONFIG_LEDS_PCA9532_GPIO */ |
295 | 295 | ||
296 | static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs) | 296 | static int pca9532_destroy_devices(struct pca9532_data *data, int n_devs) |
297 | { | 297 | { |
298 | int i = n_devs; | 298 | int i = n_devs; |
299 | 299 | ||
300 | if (!data) | 300 | if (!data) |
301 | return -EINVAL; | 301 | return -EINVAL; |
302 | 302 | ||
303 | while (--i >= 0) { | 303 | while (--i >= 0) { |
304 | switch (data->leds[i].type) { | 304 | switch (data->leds[i].type) { |
305 | case PCA9532_TYPE_NONE: | 305 | case PCA9532_TYPE_NONE: |
306 | case PCA9532_TYPE_GPIO: | 306 | case PCA9532_TYPE_GPIO: |
307 | break; | 307 | break; |
308 | case PCA9532_TYPE_LED: | 308 | case PCA9532_TYPE_LED: |
309 | led_classdev_unregister(&data->leds[i].ldev); | 309 | led_classdev_unregister(&data->leds[i].ldev); |
310 | cancel_work_sync(&data->leds[i].work); | 310 | cancel_work_sync(&data->leds[i].work); |
311 | break; | 311 | break; |
312 | case PCA9532_TYPE_N2100_BEEP: | 312 | case PCA9532_TYPE_N2100_BEEP: |
313 | if (data->idev != NULL) { | 313 | if (data->idev != NULL) { |
314 | cancel_work_sync(&data->work); | 314 | cancel_work_sync(&data->work); |
315 | data->idev = NULL; | 315 | data->idev = NULL; |
316 | } | 316 | } |
317 | break; | 317 | break; |
318 | } | 318 | } |
319 | } | 319 | } |
320 | 320 | ||
321 | #ifdef CONFIG_LEDS_PCA9532_GPIO | 321 | #ifdef CONFIG_LEDS_PCA9532_GPIO |
322 | if (data->gpio.dev) { | 322 | if (data->gpio.dev) { |
323 | int err = gpiochip_remove(&data->gpio); | 323 | int err = gpiochip_remove(&data->gpio); |
324 | if (err) { | 324 | if (err) { |
325 | dev_err(&data->client->dev, "%s failed, %d\n", | 325 | dev_err(&data->client->dev, "%s failed, %d\n", |
326 | "gpiochip_remove()", err); | 326 | "gpiochip_remove()", err); |
327 | return err; | 327 | return err; |
328 | } | 328 | } |
329 | } | 329 | } |
330 | #endif | 330 | #endif |
331 | 331 | ||
332 | return 0; | 332 | return 0; |
333 | } | 333 | } |
334 | 334 | ||
335 | static int pca9532_configure(struct i2c_client *client, | 335 | static int pca9532_configure(struct i2c_client *client, |
336 | struct pca9532_data *data, struct pca9532_platform_data *pdata) | 336 | struct pca9532_data *data, struct pca9532_platform_data *pdata) |
337 | { | 337 | { |
338 | int i, err = 0; | 338 | int i, err = 0; |
339 | int gpios = 0; | 339 | int gpios = 0; |
340 | u8 maxleds = data->chip_info->num_leds; | 340 | u8 maxleds = data->chip_info->num_leds; |
341 | 341 | ||
342 | for (i = 0; i < 2; i++) { | 342 | for (i = 0; i < 2; i++) { |
343 | data->pwm[i] = pdata->pwm[i]; | 343 | data->pwm[i] = pdata->pwm[i]; |
344 | data->psc[i] = pdata->psc[i]; | 344 | data->psc[i] = pdata->psc[i]; |
345 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i), | 345 | i2c_smbus_write_byte_data(client, PCA9532_REG_PWM(maxleds, i), |
346 | data->pwm[i]); | 346 | data->pwm[i]); |
347 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i), | 347 | i2c_smbus_write_byte_data(client, PCA9532_REG_PSC(maxleds, i), |
348 | data->psc[i]); | 348 | data->psc[i]); |
349 | } | 349 | } |
350 | 350 | ||
351 | for (i = 0; i < data->chip_info->num_leds; i++) { | 351 | for (i = 0; i < data->chip_info->num_leds; i++) { |
352 | struct pca9532_led *led = &data->leds[i]; | 352 | struct pca9532_led *led = &data->leds[i]; |
353 | struct pca9532_led *pled = &pdata->leds[i]; | 353 | struct pca9532_led *pled = &pdata->leds[i]; |
354 | led->client = client; | 354 | led->client = client; |
355 | led->id = i; | 355 | led->id = i; |
356 | led->type = pled->type; | 356 | led->type = pled->type; |
357 | switch (led->type) { | 357 | switch (led->type) { |
358 | case PCA9532_TYPE_NONE: | 358 | case PCA9532_TYPE_NONE: |
359 | break; | 359 | break; |
360 | case PCA9532_TYPE_GPIO: | 360 | case PCA9532_TYPE_GPIO: |
361 | gpios++; | 361 | gpios++; |
362 | break; | 362 | break; |
363 | case PCA9532_TYPE_LED: | 363 | case PCA9532_TYPE_LED: |
364 | led->state = pled->state; | 364 | led->state = pled->state; |
365 | led->name = pled->name; | 365 | led->name = pled->name; |
366 | led->ldev.name = led->name; | 366 | led->ldev.name = led->name; |
367 | led->ldev.brightness = LED_OFF; | 367 | led->ldev.brightness = LED_OFF; |
368 | led->ldev.brightness_set = pca9532_set_brightness; | 368 | led->ldev.brightness_set = pca9532_set_brightness; |
369 | led->ldev.blink_set = pca9532_set_blink; | 369 | led->ldev.blink_set = pca9532_set_blink; |
370 | INIT_WORK(&led->work, pca9532_led_work); | 370 | INIT_WORK(&led->work, pca9532_led_work); |
371 | err = led_classdev_register(&client->dev, &led->ldev); | 371 | err = led_classdev_register(&client->dev, &led->ldev); |
372 | if (err < 0) { | 372 | if (err < 0) { |
373 | dev_err(&client->dev, | 373 | dev_err(&client->dev, |
374 | "couldn't register LED %s\n", | 374 | "couldn't register LED %s\n", |
375 | led->name); | 375 | led->name); |
376 | goto exit; | 376 | goto exit; |
377 | } | 377 | } |
378 | pca9532_setled(led); | 378 | pca9532_setled(led); |
379 | break; | 379 | break; |
380 | case PCA9532_TYPE_N2100_BEEP: | 380 | case PCA9532_TYPE_N2100_BEEP: |
381 | BUG_ON(data->idev); | 381 | BUG_ON(data->idev); |
382 | led->state = PCA9532_PWM1; | 382 | led->state = PCA9532_PWM1; |
383 | pca9532_setled(led); | 383 | pca9532_setled(led); |
384 | data->idev = devm_input_allocate_device(&client->dev); | 384 | data->idev = devm_input_allocate_device(&client->dev); |
385 | if (data->idev == NULL) { | 385 | if (data->idev == NULL) { |
386 | err = -ENOMEM; | 386 | err = -ENOMEM; |
387 | goto exit; | 387 | goto exit; |
388 | } | 388 | } |
389 | data->idev->name = pled->name; | 389 | data->idev->name = pled->name; |
390 | data->idev->phys = "i2c/pca9532"; | 390 | data->idev->phys = "i2c/pca9532"; |
391 | data->idev->id.bustype = BUS_HOST; | 391 | data->idev->id.bustype = BUS_HOST; |
392 | data->idev->id.vendor = 0x001f; | 392 | data->idev->id.vendor = 0x001f; |
393 | data->idev->id.product = 0x0001; | 393 | data->idev->id.product = 0x0001; |
394 | data->idev->id.version = 0x0100; | 394 | data->idev->id.version = 0x0100; |
395 | data->idev->evbit[0] = BIT_MASK(EV_SND); | 395 | data->idev->evbit[0] = BIT_MASK(EV_SND); |
396 | data->idev->sndbit[0] = BIT_MASK(SND_BELL) | | 396 | data->idev->sndbit[0] = BIT_MASK(SND_BELL) | |
397 | BIT_MASK(SND_TONE); | 397 | BIT_MASK(SND_TONE); |
398 | data->idev->event = pca9532_event; | 398 | data->idev->event = pca9532_event; |
399 | input_set_drvdata(data->idev, data); | 399 | input_set_drvdata(data->idev, data); |
400 | INIT_WORK(&data->work, pca9532_input_work); | 400 | INIT_WORK(&data->work, pca9532_input_work); |
401 | err = input_register_device(data->idev); | 401 | err = input_register_device(data->idev); |
402 | if (err) { | 402 | if (err) { |
403 | cancel_work_sync(&data->work); | 403 | cancel_work_sync(&data->work); |
404 | data->idev = NULL; | 404 | data->idev = NULL; |
405 | goto exit; | 405 | goto exit; |
406 | } | 406 | } |
407 | break; | 407 | break; |
408 | } | 408 | } |
409 | } | 409 | } |
410 | 410 | ||
411 | #ifdef CONFIG_LEDS_PCA9532_GPIO | 411 | #ifdef CONFIG_LEDS_PCA9532_GPIO |
412 | if (gpios) { | 412 | if (gpios) { |
413 | data->gpio.label = "gpio-pca9532"; | 413 | data->gpio.label = "gpio-pca9532"; |
414 | data->gpio.direction_input = pca9532_gpio_direction_input; | 414 | data->gpio.direction_input = pca9532_gpio_direction_input; |
415 | data->gpio.direction_output = pca9532_gpio_direction_output; | 415 | data->gpio.direction_output = pca9532_gpio_direction_output; |
416 | data->gpio.set = pca9532_gpio_set_value; | 416 | data->gpio.set = pca9532_gpio_set_value; |
417 | data->gpio.get = pca9532_gpio_get_value; | 417 | data->gpio.get = pca9532_gpio_get_value; |
418 | data->gpio.request = pca9532_gpio_request_pin; | 418 | data->gpio.request = pca9532_gpio_request_pin; |
419 | data->gpio.can_sleep = 1; | 419 | data->gpio.can_sleep = 1; |
420 | data->gpio.base = pdata->gpio_base; | 420 | data->gpio.base = pdata->gpio_base; |
421 | data->gpio.ngpio = data->chip_info->num_leds; | 421 | data->gpio.ngpio = data->chip_info->num_leds; |
422 | data->gpio.dev = &client->dev; | 422 | data->gpio.dev = &client->dev; |
423 | data->gpio.owner = THIS_MODULE; | 423 | data->gpio.owner = THIS_MODULE; |
424 | 424 | ||
425 | err = gpiochip_add(&data->gpio); | 425 | err = gpiochip_add(&data->gpio); |
426 | if (err) { | 426 | if (err) { |
427 | /* Use data->gpio.dev as a flag for freeing gpiochip */ | 427 | /* Use data->gpio.dev as a flag for freeing gpiochip */ |
428 | data->gpio.dev = NULL; | 428 | data->gpio.dev = NULL; |
429 | dev_warn(&client->dev, "could not add gpiochip\n"); | 429 | dev_warn(&client->dev, "could not add gpiochip\n"); |
430 | } else { | 430 | } else { |
431 | dev_info(&client->dev, "gpios %i...%i\n", | 431 | dev_info(&client->dev, "gpios %i...%i\n", |
432 | data->gpio.base, data->gpio.base + | 432 | data->gpio.base, data->gpio.base + |
433 | data->gpio.ngpio - 1); | 433 | data->gpio.ngpio - 1); |
434 | } | 434 | } |
435 | } | 435 | } |
436 | #endif | 436 | #endif |
437 | 437 | ||
438 | return 0; | 438 | return 0; |
439 | 439 | ||
440 | exit: | 440 | exit: |
441 | pca9532_destroy_devices(data, i); | 441 | pca9532_destroy_devices(data, i); |
442 | return err; | 442 | return err; |
443 | } | 443 | } |
444 | 444 | ||
445 | static int pca9532_probe(struct i2c_client *client, | 445 | static int pca9532_probe(struct i2c_client *client, |
446 | const struct i2c_device_id *id) | 446 | const struct i2c_device_id *id) |
447 | { | 447 | { |
448 | struct pca9532_data *data = i2c_get_clientdata(client); | 448 | struct pca9532_data *data = i2c_get_clientdata(client); |
449 | struct pca9532_platform_data *pca9532_pdata = client->dev.platform_data; | 449 | struct pca9532_platform_data *pca9532_pdata = |
450 | dev_get_platdata(&client->dev); | ||
450 | 451 | ||
451 | if (!pca9532_pdata) | 452 | if (!pca9532_pdata) |
452 | return -EIO; | 453 | return -EIO; |
453 | 454 | ||
454 | if (!i2c_check_functionality(client->adapter, | 455 | if (!i2c_check_functionality(client->adapter, |
455 | I2C_FUNC_SMBUS_BYTE_DATA)) | 456 | I2C_FUNC_SMBUS_BYTE_DATA)) |
456 | return -EIO; | 457 | return -EIO; |
457 | 458 | ||
458 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); | 459 | data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); |
459 | if (!data) | 460 | if (!data) |
460 | return -ENOMEM; | 461 | return -ENOMEM; |
461 | 462 | ||
462 | data->chip_info = &pca9532_chip_info_tbl[id->driver_data]; | 463 | data->chip_info = &pca9532_chip_info_tbl[id->driver_data]; |
463 | 464 | ||
464 | dev_info(&client->dev, "setting platform data\n"); | 465 | dev_info(&client->dev, "setting platform data\n"); |
465 | i2c_set_clientdata(client, data); | 466 | i2c_set_clientdata(client, data); |
466 | data->client = client; | 467 | data->client = client; |
467 | mutex_init(&data->update_lock); | 468 | mutex_init(&data->update_lock); |
468 | 469 | ||
469 | return pca9532_configure(client, data, pca9532_pdata); | 470 | return pca9532_configure(client, data, pca9532_pdata); |
470 | } | 471 | } |
471 | 472 | ||
472 | static int pca9532_remove(struct i2c_client *client) | 473 | static int pca9532_remove(struct i2c_client *client) |
473 | { | 474 | { |
474 | struct pca9532_data *data = i2c_get_clientdata(client); | 475 | struct pca9532_data *data = i2c_get_clientdata(client); |
475 | int err; | 476 | int err; |
476 | 477 | ||
477 | err = pca9532_destroy_devices(data, data->chip_info->num_leds); | 478 | err = pca9532_destroy_devices(data, data->chip_info->num_leds); |
478 | if (err) | 479 | if (err) |
479 | return err; | 480 | return err; |
480 | 481 | ||
481 | return 0; | 482 | return 0; |
482 | } | 483 | } |
483 | 484 | ||
484 | module_i2c_driver(pca9532_driver); | 485 | module_i2c_driver(pca9532_driver); |
485 | 486 | ||
486 | MODULE_AUTHOR("Riku Voipio"); | 487 | MODULE_AUTHOR("Riku Voipio"); |
487 | MODULE_LICENSE("GPL"); | 488 | MODULE_LICENSE("GPL"); |
488 | MODULE_DESCRIPTION("PCA 9532 LED dimmer"); | 489 | MODULE_DESCRIPTION("PCA 9532 LED dimmer"); |
489 | 490 |
drivers/leds/leds-pca955x.c
1 | /* | 1 | /* |
2 | * Copyright 2007-2008 Extreme Engineering Solutions, Inc. | 2 | * Copyright 2007-2008 Extreme Engineering Solutions, Inc. |
3 | * | 3 | * |
4 | * Author: Nate Case <ncase@xes-inc.com> | 4 | * Author: Nate Case <ncase@xes-inc.com> |
5 | * | 5 | * |
6 | * This file is subject to the terms and conditions of version 2 of | 6 | * This file is subject to the terms and conditions of version 2 of |
7 | * the GNU General Public License. See the file COPYING in the main | 7 | * the GNU General Public License. See the file COPYING in the main |
8 | * directory of this archive for more details. | 8 | * directory of this archive for more details. |
9 | * | 9 | * |
10 | * LED driver for various PCA955x I2C LED drivers | 10 | * LED driver for various PCA955x I2C LED drivers |
11 | * | 11 | * |
12 | * Supported devices: | 12 | * Supported devices: |
13 | * | 13 | * |
14 | * Device Description 7-bit slave address | 14 | * Device Description 7-bit slave address |
15 | * ------ ----------- ------------------- | 15 | * ------ ----------- ------------------- |
16 | * PCA9550 2-bit driver 0x60 .. 0x61 | 16 | * PCA9550 2-bit driver 0x60 .. 0x61 |
17 | * PCA9551 8-bit driver 0x60 .. 0x67 | 17 | * PCA9551 8-bit driver 0x60 .. 0x67 |
18 | * PCA9552 16-bit driver 0x60 .. 0x67 | 18 | * PCA9552 16-bit driver 0x60 .. 0x67 |
19 | * PCA9553/01 4-bit driver 0x62 | 19 | * PCA9553/01 4-bit driver 0x62 |
20 | * PCA9553/02 4-bit driver 0x63 | 20 | * PCA9553/02 4-bit driver 0x63 |
21 | * | 21 | * |
22 | * Philips PCA955x LED driver chips follow a register map as shown below: | 22 | * Philips PCA955x LED driver chips follow a register map as shown below: |
23 | * | 23 | * |
24 | * Control Register Description | 24 | * Control Register Description |
25 | * ---------------- ----------- | 25 | * ---------------- ----------- |
26 | * 0x0 Input register 0 | 26 | * 0x0 Input register 0 |
27 | * .. | 27 | * .. |
28 | * NUM_INPUT_REGS - 1 Last Input register X | 28 | * NUM_INPUT_REGS - 1 Last Input register X |
29 | * | 29 | * |
30 | * NUM_INPUT_REGS Frequency prescaler 0 | 30 | * NUM_INPUT_REGS Frequency prescaler 0 |
31 | * NUM_INPUT_REGS + 1 PWM register 0 | 31 | * NUM_INPUT_REGS + 1 PWM register 0 |
32 | * NUM_INPUT_REGS + 2 Frequency prescaler 1 | 32 | * NUM_INPUT_REGS + 2 Frequency prescaler 1 |
33 | * NUM_INPUT_REGS + 3 PWM register 1 | 33 | * NUM_INPUT_REGS + 3 PWM register 1 |
34 | * | 34 | * |
35 | * NUM_INPUT_REGS + 4 LED selector 0 | 35 | * NUM_INPUT_REGS + 4 LED selector 0 |
36 | * NUM_INPUT_REGS + 4 | 36 | * NUM_INPUT_REGS + 4 |
37 | * + NUM_LED_REGS - 1 Last LED selector | 37 | * + NUM_LED_REGS - 1 Last LED selector |
38 | * | 38 | * |
39 | * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many | 39 | * where NUM_INPUT_REGS and NUM_LED_REGS vary depending on how many |
40 | * bits the chip supports. | 40 | * bits the chip supports. |
41 | */ | 41 | */ |
42 | 42 | ||
43 | #include <linux/module.h> | 43 | #include <linux/module.h> |
44 | #include <linux/delay.h> | 44 | #include <linux/delay.h> |
45 | #include <linux/string.h> | 45 | #include <linux/string.h> |
46 | #include <linux/ctype.h> | 46 | #include <linux/ctype.h> |
47 | #include <linux/leds.h> | 47 | #include <linux/leds.h> |
48 | #include <linux/err.h> | 48 | #include <linux/err.h> |
49 | #include <linux/i2c.h> | 49 | #include <linux/i2c.h> |
50 | #include <linux/workqueue.h> | 50 | #include <linux/workqueue.h> |
51 | #include <linux/slab.h> | 51 | #include <linux/slab.h> |
52 | 52 | ||
53 | /* LED select registers determine the source that drives LED outputs */ | 53 | /* LED select registers determine the source that drives LED outputs */ |
54 | #define PCA955X_LS_LED_ON 0x0 /* Output LOW */ | 54 | #define PCA955X_LS_LED_ON 0x0 /* Output LOW */ |
55 | #define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ | 55 | #define PCA955X_LS_LED_OFF 0x1 /* Output HI-Z */ |
56 | #define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ | 56 | #define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */ |
57 | #define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ | 57 | #define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */ |
58 | 58 | ||
59 | enum pca955x_type { | 59 | enum pca955x_type { |
60 | pca9550, | 60 | pca9550, |
61 | pca9551, | 61 | pca9551, |
62 | pca9552, | 62 | pca9552, |
63 | pca9553, | 63 | pca9553, |
64 | }; | 64 | }; |
65 | 65 | ||
66 | struct pca955x_chipdef { | 66 | struct pca955x_chipdef { |
67 | int bits; | 67 | int bits; |
68 | u8 slv_addr; /* 7-bit slave address mask */ | 68 | u8 slv_addr; /* 7-bit slave address mask */ |
69 | int slv_addr_shift; /* Number of bits to ignore */ | 69 | int slv_addr_shift; /* Number of bits to ignore */ |
70 | }; | 70 | }; |
71 | 71 | ||
72 | static struct pca955x_chipdef pca955x_chipdefs[] = { | 72 | static struct pca955x_chipdef pca955x_chipdefs[] = { |
73 | [pca9550] = { | 73 | [pca9550] = { |
74 | .bits = 2, | 74 | .bits = 2, |
75 | .slv_addr = /* 110000x */ 0x60, | 75 | .slv_addr = /* 110000x */ 0x60, |
76 | .slv_addr_shift = 1, | 76 | .slv_addr_shift = 1, |
77 | }, | 77 | }, |
78 | [pca9551] = { | 78 | [pca9551] = { |
79 | .bits = 8, | 79 | .bits = 8, |
80 | .slv_addr = /* 1100xxx */ 0x60, | 80 | .slv_addr = /* 1100xxx */ 0x60, |
81 | .slv_addr_shift = 3, | 81 | .slv_addr_shift = 3, |
82 | }, | 82 | }, |
83 | [pca9552] = { | 83 | [pca9552] = { |
84 | .bits = 16, | 84 | .bits = 16, |
85 | .slv_addr = /* 1100xxx */ 0x60, | 85 | .slv_addr = /* 1100xxx */ 0x60, |
86 | .slv_addr_shift = 3, | 86 | .slv_addr_shift = 3, |
87 | }, | 87 | }, |
88 | [pca9553] = { | 88 | [pca9553] = { |
89 | .bits = 4, | 89 | .bits = 4, |
90 | .slv_addr = /* 110001x */ 0x62, | 90 | .slv_addr = /* 110001x */ 0x62, |
91 | .slv_addr_shift = 1, | 91 | .slv_addr_shift = 1, |
92 | }, | 92 | }, |
93 | }; | 93 | }; |
94 | 94 | ||
95 | static const struct i2c_device_id pca955x_id[] = { | 95 | static const struct i2c_device_id pca955x_id[] = { |
96 | { "pca9550", pca9550 }, | 96 | { "pca9550", pca9550 }, |
97 | { "pca9551", pca9551 }, | 97 | { "pca9551", pca9551 }, |
98 | { "pca9552", pca9552 }, | 98 | { "pca9552", pca9552 }, |
99 | { "pca9553", pca9553 }, | 99 | { "pca9553", pca9553 }, |
100 | { } | 100 | { } |
101 | }; | 101 | }; |
102 | MODULE_DEVICE_TABLE(i2c, pca955x_id); | 102 | MODULE_DEVICE_TABLE(i2c, pca955x_id); |
103 | 103 | ||
104 | struct pca955x { | 104 | struct pca955x { |
105 | struct mutex lock; | 105 | struct mutex lock; |
106 | struct pca955x_led *leds; | 106 | struct pca955x_led *leds; |
107 | struct pca955x_chipdef *chipdef; | 107 | struct pca955x_chipdef *chipdef; |
108 | struct i2c_client *client; | 108 | struct i2c_client *client; |
109 | }; | 109 | }; |
110 | 110 | ||
111 | struct pca955x_led { | 111 | struct pca955x_led { |
112 | struct pca955x *pca955x; | 112 | struct pca955x *pca955x; |
113 | struct work_struct work; | 113 | struct work_struct work; |
114 | enum led_brightness brightness; | 114 | enum led_brightness brightness; |
115 | struct led_classdev led_cdev; | 115 | struct led_classdev led_cdev; |
116 | int led_num; /* 0 .. 15 potentially */ | 116 | int led_num; /* 0 .. 15 potentially */ |
117 | char name[32]; | 117 | char name[32]; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | /* 8 bits per input register */ | 120 | /* 8 bits per input register */ |
121 | static inline int pca95xx_num_input_regs(int bits) | 121 | static inline int pca95xx_num_input_regs(int bits) |
122 | { | 122 | { |
123 | return (bits + 7) / 8; | 123 | return (bits + 7) / 8; |
124 | } | 124 | } |
125 | 125 | ||
126 | /* 4 bits per LED selector register */ | 126 | /* 4 bits per LED selector register */ |
127 | static inline int pca95xx_num_led_regs(int bits) | 127 | static inline int pca95xx_num_led_regs(int bits) |
128 | { | 128 | { |
129 | return (bits + 3) / 4; | 129 | return (bits + 3) / 4; |
130 | } | 130 | } |
131 | 131 | ||
132 | /* | 132 | /* |
133 | * Return an LED selector register value based on an existing one, with | 133 | * Return an LED selector register value based on an existing one, with |
134 | * the appropriate 2-bit state value set for the given LED number (0-3). | 134 | * the appropriate 2-bit state value set for the given LED number (0-3). |
135 | */ | 135 | */ |
136 | static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) | 136 | static inline u8 pca955x_ledsel(u8 oldval, int led_num, int state) |
137 | { | 137 | { |
138 | return (oldval & (~(0x3 << (led_num << 1)))) | | 138 | return (oldval & (~(0x3 << (led_num << 1)))) | |
139 | ((state & 0x3) << (led_num << 1)); | 139 | ((state & 0x3) << (led_num << 1)); |
140 | } | 140 | } |
141 | 141 | ||
142 | /* | 142 | /* |
143 | * Write to frequency prescaler register, used to program the | 143 | * Write to frequency prescaler register, used to program the |
144 | * period of the PWM output. period = (PSCx + 1) / 38 | 144 | * period of the PWM output. period = (PSCx + 1) / 38 |
145 | */ | 145 | */ |
146 | static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) | 146 | static void pca955x_write_psc(struct i2c_client *client, int n, u8 val) |
147 | { | 147 | { |
148 | struct pca955x *pca955x = i2c_get_clientdata(client); | 148 | struct pca955x *pca955x = i2c_get_clientdata(client); |
149 | 149 | ||
150 | i2c_smbus_write_byte_data(client, | 150 | i2c_smbus_write_byte_data(client, |
151 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, | 151 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 2*n, |
152 | val); | 152 | val); |
153 | } | 153 | } |
154 | 154 | ||
155 | /* | 155 | /* |
156 | * Write to PWM register, which determines the duty cycle of the | 156 | * Write to PWM register, which determines the duty cycle of the |
157 | * output. LED is OFF when the count is less than the value of this | 157 | * output. LED is OFF when the count is less than the value of this |
158 | * register, and ON when it is greater. If PWMx == 0, LED is always OFF. | 158 | * register, and ON when it is greater. If PWMx == 0, LED is always OFF. |
159 | * | 159 | * |
160 | * Duty cycle is (256 - PWMx) / 256 | 160 | * Duty cycle is (256 - PWMx) / 256 |
161 | */ | 161 | */ |
162 | static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) | 162 | static void pca955x_write_pwm(struct i2c_client *client, int n, u8 val) |
163 | { | 163 | { |
164 | struct pca955x *pca955x = i2c_get_clientdata(client); | 164 | struct pca955x *pca955x = i2c_get_clientdata(client); |
165 | 165 | ||
166 | i2c_smbus_write_byte_data(client, | 166 | i2c_smbus_write_byte_data(client, |
167 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, | 167 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 1 + 2*n, |
168 | val); | 168 | val); |
169 | } | 169 | } |
170 | 170 | ||
171 | /* | 171 | /* |
172 | * Write to LED selector register, which determines the source that | 172 | * Write to LED selector register, which determines the source that |
173 | * drives the LED output. | 173 | * drives the LED output. |
174 | */ | 174 | */ |
175 | static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) | 175 | static void pca955x_write_ls(struct i2c_client *client, int n, u8 val) |
176 | { | 176 | { |
177 | struct pca955x *pca955x = i2c_get_clientdata(client); | 177 | struct pca955x *pca955x = i2c_get_clientdata(client); |
178 | 178 | ||
179 | i2c_smbus_write_byte_data(client, | 179 | i2c_smbus_write_byte_data(client, |
180 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, | 180 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n, |
181 | val); | 181 | val); |
182 | } | 182 | } |
183 | 183 | ||
184 | /* | 184 | /* |
185 | * Read the LED selector register, which determines the source that | 185 | * Read the LED selector register, which determines the source that |
186 | * drives the LED output. | 186 | * drives the LED output. |
187 | */ | 187 | */ |
188 | static u8 pca955x_read_ls(struct i2c_client *client, int n) | 188 | static u8 pca955x_read_ls(struct i2c_client *client, int n) |
189 | { | 189 | { |
190 | struct pca955x *pca955x = i2c_get_clientdata(client); | 190 | struct pca955x *pca955x = i2c_get_clientdata(client); |
191 | 191 | ||
192 | return (u8) i2c_smbus_read_byte_data(client, | 192 | return (u8) i2c_smbus_read_byte_data(client, |
193 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); | 193 | pca95xx_num_input_regs(pca955x->chipdef->bits) + 4 + n); |
194 | } | 194 | } |
195 | 195 | ||
196 | static void pca955x_led_work(struct work_struct *work) | 196 | static void pca955x_led_work(struct work_struct *work) |
197 | { | 197 | { |
198 | struct pca955x_led *pca955x_led; | 198 | struct pca955x_led *pca955x_led; |
199 | struct pca955x *pca955x; | 199 | struct pca955x *pca955x; |
200 | u8 ls; | 200 | u8 ls; |
201 | int chip_ls; /* which LSx to use (0-3 potentially) */ | 201 | int chip_ls; /* which LSx to use (0-3 potentially) */ |
202 | int ls_led; /* which set of bits within LSx to use (0-3) */ | 202 | int ls_led; /* which set of bits within LSx to use (0-3) */ |
203 | 203 | ||
204 | pca955x_led = container_of(work, struct pca955x_led, work); | 204 | pca955x_led = container_of(work, struct pca955x_led, work); |
205 | pca955x = pca955x_led->pca955x; | 205 | pca955x = pca955x_led->pca955x; |
206 | 206 | ||
207 | chip_ls = pca955x_led->led_num / 4; | 207 | chip_ls = pca955x_led->led_num / 4; |
208 | ls_led = pca955x_led->led_num % 4; | 208 | ls_led = pca955x_led->led_num % 4; |
209 | 209 | ||
210 | mutex_lock(&pca955x->lock); | 210 | mutex_lock(&pca955x->lock); |
211 | 211 | ||
212 | ls = pca955x_read_ls(pca955x->client, chip_ls); | 212 | ls = pca955x_read_ls(pca955x->client, chip_ls); |
213 | 213 | ||
214 | switch (pca955x_led->brightness) { | 214 | switch (pca955x_led->brightness) { |
215 | case LED_FULL: | 215 | case LED_FULL: |
216 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); | 216 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON); |
217 | break; | 217 | break; |
218 | case LED_OFF: | 218 | case LED_OFF: |
219 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); | 219 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_OFF); |
220 | break; | 220 | break; |
221 | case LED_HALF: | 221 | case LED_HALF: |
222 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); | 222 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK0); |
223 | break; | 223 | break; |
224 | default: | 224 | default: |
225 | /* | 225 | /* |
226 | * Use PWM1 for all other values. This has the unwanted | 226 | * Use PWM1 for all other values. This has the unwanted |
227 | * side effect of making all LEDs on the chip share the | 227 | * side effect of making all LEDs on the chip share the |
228 | * same brightness level if set to a value other than | 228 | * same brightness level if set to a value other than |
229 | * OFF, HALF, or FULL. But, this is probably better than | 229 | * OFF, HALF, or FULL. But, this is probably better than |
230 | * just turning off for all other values. | 230 | * just turning off for all other values. |
231 | */ | 231 | */ |
232 | pca955x_write_pwm(pca955x->client, 1, | 232 | pca955x_write_pwm(pca955x->client, 1, |
233 | 255 - pca955x_led->brightness); | 233 | 255 - pca955x_led->brightness); |
234 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); | 234 | ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_BLINK1); |
235 | break; | 235 | break; |
236 | } | 236 | } |
237 | 237 | ||
238 | pca955x_write_ls(pca955x->client, chip_ls, ls); | 238 | pca955x_write_ls(pca955x->client, chip_ls, ls); |
239 | 239 | ||
240 | mutex_unlock(&pca955x->lock); | 240 | mutex_unlock(&pca955x->lock); |
241 | } | 241 | } |
242 | 242 | ||
243 | static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) | 243 | static void pca955x_led_set(struct led_classdev *led_cdev, enum led_brightness value) |
244 | { | 244 | { |
245 | struct pca955x_led *pca955x; | 245 | struct pca955x_led *pca955x; |
246 | 246 | ||
247 | pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); | 247 | pca955x = container_of(led_cdev, struct pca955x_led, led_cdev); |
248 | 248 | ||
249 | pca955x->brightness = value; | 249 | pca955x->brightness = value; |
250 | 250 | ||
251 | /* | 251 | /* |
252 | * Must use workqueue for the actual I/O since I2C operations | 252 | * Must use workqueue for the actual I/O since I2C operations |
253 | * can sleep. | 253 | * can sleep. |
254 | */ | 254 | */ |
255 | schedule_work(&pca955x->work); | 255 | schedule_work(&pca955x->work); |
256 | } | 256 | } |
257 | 257 | ||
258 | static int pca955x_probe(struct i2c_client *client, | 258 | static int pca955x_probe(struct i2c_client *client, |
259 | const struct i2c_device_id *id) | 259 | const struct i2c_device_id *id) |
260 | { | 260 | { |
261 | struct pca955x *pca955x; | 261 | struct pca955x *pca955x; |
262 | struct pca955x_led *pca955x_led; | 262 | struct pca955x_led *pca955x_led; |
263 | struct pca955x_chipdef *chip; | 263 | struct pca955x_chipdef *chip; |
264 | struct i2c_adapter *adapter; | 264 | struct i2c_adapter *adapter; |
265 | struct led_platform_data *pdata; | 265 | struct led_platform_data *pdata; |
266 | int i, err; | 266 | int i, err; |
267 | 267 | ||
268 | chip = &pca955x_chipdefs[id->driver_data]; | 268 | chip = &pca955x_chipdefs[id->driver_data]; |
269 | adapter = to_i2c_adapter(client->dev.parent); | 269 | adapter = to_i2c_adapter(client->dev.parent); |
270 | pdata = client->dev.platform_data; | 270 | pdata = dev_get_platdata(&client->dev); |
271 | 271 | ||
272 | /* Make sure the slave address / chip type combo given is possible */ | 272 | /* Make sure the slave address / chip type combo given is possible */ |
273 | if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != | 273 | if ((client->addr & ~((1 << chip->slv_addr_shift) - 1)) != |
274 | chip->slv_addr) { | 274 | chip->slv_addr) { |
275 | dev_err(&client->dev, "invalid slave address %02x\n", | 275 | dev_err(&client->dev, "invalid slave address %02x\n", |
276 | client->addr); | 276 | client->addr); |
277 | return -ENODEV; | 277 | return -ENODEV; |
278 | } | 278 | } |
279 | 279 | ||
280 | dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " | 280 | dev_info(&client->dev, "leds-pca955x: Using %s %d-bit LED driver at " |
281 | "slave address 0x%02x\n", | 281 | "slave address 0x%02x\n", |
282 | id->name, chip->bits, client->addr); | 282 | id->name, chip->bits, client->addr); |
283 | 283 | ||
284 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) | 284 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) |
285 | return -EIO; | 285 | return -EIO; |
286 | 286 | ||
287 | if (pdata) { | 287 | if (pdata) { |
288 | if (pdata->num_leds != chip->bits) { | 288 | if (pdata->num_leds != chip->bits) { |
289 | dev_err(&client->dev, "board info claims %d LEDs" | 289 | dev_err(&client->dev, "board info claims %d LEDs" |
290 | " on a %d-bit chip\n", | 290 | " on a %d-bit chip\n", |
291 | pdata->num_leds, chip->bits); | 291 | pdata->num_leds, chip->bits); |
292 | return -ENODEV; | 292 | return -ENODEV; |
293 | } | 293 | } |
294 | } | 294 | } |
295 | 295 | ||
296 | pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL); | 296 | pca955x = devm_kzalloc(&client->dev, sizeof(*pca955x), GFP_KERNEL); |
297 | if (!pca955x) | 297 | if (!pca955x) |
298 | return -ENOMEM; | 298 | return -ENOMEM; |
299 | 299 | ||
300 | pca955x->leds = devm_kzalloc(&client->dev, | 300 | pca955x->leds = devm_kzalloc(&client->dev, |
301 | sizeof(*pca955x_led) * chip->bits, GFP_KERNEL); | 301 | sizeof(*pca955x_led) * chip->bits, GFP_KERNEL); |
302 | if (!pca955x->leds) | 302 | if (!pca955x->leds) |
303 | return -ENOMEM; | 303 | return -ENOMEM; |
304 | 304 | ||
305 | i2c_set_clientdata(client, pca955x); | 305 | i2c_set_clientdata(client, pca955x); |
306 | 306 | ||
307 | mutex_init(&pca955x->lock); | 307 | mutex_init(&pca955x->lock); |
308 | pca955x->client = client; | 308 | pca955x->client = client; |
309 | pca955x->chipdef = chip; | 309 | pca955x->chipdef = chip; |
310 | 310 | ||
311 | for (i = 0; i < chip->bits; i++) { | 311 | for (i = 0; i < chip->bits; i++) { |
312 | pca955x_led = &pca955x->leds[i]; | 312 | pca955x_led = &pca955x->leds[i]; |
313 | pca955x_led->led_num = i; | 313 | pca955x_led->led_num = i; |
314 | pca955x_led->pca955x = pca955x; | 314 | pca955x_led->pca955x = pca955x; |
315 | 315 | ||
316 | /* Platform data can specify LED names and default triggers */ | 316 | /* Platform data can specify LED names and default triggers */ |
317 | if (pdata) { | 317 | if (pdata) { |
318 | if (pdata->leds[i].name) | 318 | if (pdata->leds[i].name) |
319 | snprintf(pca955x_led->name, | 319 | snprintf(pca955x_led->name, |
320 | sizeof(pca955x_led->name), "pca955x:%s", | 320 | sizeof(pca955x_led->name), "pca955x:%s", |
321 | pdata->leds[i].name); | 321 | pdata->leds[i].name); |
322 | if (pdata->leds[i].default_trigger) | 322 | if (pdata->leds[i].default_trigger) |
323 | pca955x_led->led_cdev.default_trigger = | 323 | pca955x_led->led_cdev.default_trigger = |
324 | pdata->leds[i].default_trigger; | 324 | pdata->leds[i].default_trigger; |
325 | } else { | 325 | } else { |
326 | snprintf(pca955x_led->name, sizeof(pca955x_led->name), | 326 | snprintf(pca955x_led->name, sizeof(pca955x_led->name), |
327 | "pca955x:%d", i); | 327 | "pca955x:%d", i); |
328 | } | 328 | } |
329 | 329 | ||
330 | pca955x_led->led_cdev.name = pca955x_led->name; | 330 | pca955x_led->led_cdev.name = pca955x_led->name; |
331 | pca955x_led->led_cdev.brightness_set = pca955x_led_set; | 331 | pca955x_led->led_cdev.brightness_set = pca955x_led_set; |
332 | 332 | ||
333 | INIT_WORK(&pca955x_led->work, pca955x_led_work); | 333 | INIT_WORK(&pca955x_led->work, pca955x_led_work); |
334 | 334 | ||
335 | err = led_classdev_register(&client->dev, | 335 | err = led_classdev_register(&client->dev, |
336 | &pca955x_led->led_cdev); | 336 | &pca955x_led->led_cdev); |
337 | if (err < 0) | 337 | if (err < 0) |
338 | goto exit; | 338 | goto exit; |
339 | } | 339 | } |
340 | 340 | ||
341 | /* Turn off LEDs */ | 341 | /* Turn off LEDs */ |
342 | for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) | 342 | for (i = 0; i < pca95xx_num_led_regs(chip->bits); i++) |
343 | pca955x_write_ls(client, i, 0x55); | 343 | pca955x_write_ls(client, i, 0x55); |
344 | 344 | ||
345 | /* PWM0 is used for half brightness or 50% duty cycle */ | 345 | /* PWM0 is used for half brightness or 50% duty cycle */ |
346 | pca955x_write_pwm(client, 0, 255-LED_HALF); | 346 | pca955x_write_pwm(client, 0, 255-LED_HALF); |
347 | 347 | ||
348 | /* PWM1 is used for variable brightness, default to OFF */ | 348 | /* PWM1 is used for variable brightness, default to OFF */ |
349 | pca955x_write_pwm(client, 1, 0); | 349 | pca955x_write_pwm(client, 1, 0); |
350 | 350 | ||
351 | /* Set to fast frequency so we do not see flashing */ | 351 | /* Set to fast frequency so we do not see flashing */ |
352 | pca955x_write_psc(client, 0, 0); | 352 | pca955x_write_psc(client, 0, 0); |
353 | pca955x_write_psc(client, 1, 0); | 353 | pca955x_write_psc(client, 1, 0); |
354 | 354 | ||
355 | return 0; | 355 | return 0; |
356 | 356 | ||
357 | exit: | 357 | exit: |
358 | while (i--) { | 358 | while (i--) { |
359 | led_classdev_unregister(&pca955x->leds[i].led_cdev); | 359 | led_classdev_unregister(&pca955x->leds[i].led_cdev); |
360 | cancel_work_sync(&pca955x->leds[i].work); | 360 | cancel_work_sync(&pca955x->leds[i].work); |
361 | } | 361 | } |
362 | 362 | ||
363 | return err; | 363 | return err; |
364 | } | 364 | } |
365 | 365 | ||
366 | static int pca955x_remove(struct i2c_client *client) | 366 | static int pca955x_remove(struct i2c_client *client) |
367 | { | 367 | { |
368 | struct pca955x *pca955x = i2c_get_clientdata(client); | 368 | struct pca955x *pca955x = i2c_get_clientdata(client); |
369 | int i; | 369 | int i; |
370 | 370 | ||
371 | for (i = 0; i < pca955x->chipdef->bits; i++) { | 371 | for (i = 0; i < pca955x->chipdef->bits; i++) { |
372 | led_classdev_unregister(&pca955x->leds[i].led_cdev); | 372 | led_classdev_unregister(&pca955x->leds[i].led_cdev); |
373 | cancel_work_sync(&pca955x->leds[i].work); | 373 | cancel_work_sync(&pca955x->leds[i].work); |
374 | } | 374 | } |
375 | 375 | ||
376 | return 0; | 376 | return 0; |
377 | } | 377 | } |
378 | 378 | ||
379 | static struct i2c_driver pca955x_driver = { | 379 | static struct i2c_driver pca955x_driver = { |
380 | .driver = { | 380 | .driver = { |
381 | .name = "leds-pca955x", | 381 | .name = "leds-pca955x", |
382 | .owner = THIS_MODULE, | 382 | .owner = THIS_MODULE, |
383 | }, | 383 | }, |
384 | .probe = pca955x_probe, | 384 | .probe = pca955x_probe, |
385 | .remove = pca955x_remove, | 385 | .remove = pca955x_remove, |
386 | .id_table = pca955x_id, | 386 | .id_table = pca955x_id, |
387 | }; | 387 | }; |
388 | 388 | ||
389 | module_i2c_driver(pca955x_driver); | 389 | module_i2c_driver(pca955x_driver); |
390 | 390 | ||
391 | MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>"); | 391 | MODULE_AUTHOR("Nate Case <ncase@xes-inc.com>"); |
392 | MODULE_DESCRIPTION("PCA955x LED driver"); | 392 | MODULE_DESCRIPTION("PCA955x LED driver"); |
393 | MODULE_LICENSE("GPL v2"); | 393 | MODULE_LICENSE("GPL v2"); |
394 | 394 |
drivers/leds/leds-pca9633.c
1 | /* | 1 | /* |
2 | * Copyright 2011 bct electronic GmbH | 2 | * Copyright 2011 bct electronic GmbH |
3 | * | 3 | * |
4 | * Author: Peter Meerwald <p.meerwald@bct-electronic.com> | 4 | * Author: Peter Meerwald <p.meerwald@bct-electronic.com> |
5 | * | 5 | * |
6 | * Based on leds-pca955x.c | 6 | * Based on leds-pca955x.c |
7 | * | 7 | * |
8 | * This file is subject to the terms and conditions of version 2 of | 8 | * This file is subject to the terms and conditions of version 2 of |
9 | * the GNU General Public License. See the file COPYING in the main | 9 | * the GNU General Public License. See the file COPYING in the main |
10 | * directory of this archive for more details. | 10 | * directory of this archive for more details. |
11 | * | 11 | * |
12 | * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) | 12 | * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) |
13 | * | 13 | * |
14 | * Note that hardware blinking violates the leds infrastructure driver | 14 | * Note that hardware blinking violates the leds infrastructure driver |
15 | * interface since the hardware only supports blinking all LEDs with the | 15 | * interface since the hardware only supports blinking all LEDs with the |
16 | * same delay_on/delay_off rates. That is, only the LEDs that are set to | 16 | * same delay_on/delay_off rates. That is, only the LEDs that are set to |
17 | * blink will actually blink but all LEDs that are set to blink will blink | 17 | * blink will actually blink but all LEDs that are set to blink will blink |
18 | * in identical fashion. The delay_on/delay_off values of the last LED | 18 | * in identical fashion. The delay_on/delay_off values of the last LED |
19 | * that is set to blink will be used for all of the blinking LEDs. | 19 | * that is set to blink will be used for all of the blinking LEDs. |
20 | * Hardware blinking is disabled by default but can be enabled by setting | 20 | * Hardware blinking is disabled by default but can be enabled by setting |
21 | * the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' | 21 | * the 'blink_type' member in the platform_data struct to 'PCA9633_HW_BLINK' |
22 | * or by adding the 'nxp,hw-blink' property to the DTS. | 22 | * or by adding the 'nxp,hw-blink' property to the DTS. |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/delay.h> | 26 | #include <linux/delay.h> |
27 | #include <linux/string.h> | 27 | #include <linux/string.h> |
28 | #include <linux/ctype.h> | 28 | #include <linux/ctype.h> |
29 | #include <linux/leds.h> | 29 | #include <linux/leds.h> |
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
32 | #include <linux/workqueue.h> | 32 | #include <linux/workqueue.h> |
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/of.h> | 34 | #include <linux/of.h> |
35 | #include <linux/platform_data/leds-pca9633.h> | 35 | #include <linux/platform_data/leds-pca9633.h> |
36 | 36 | ||
37 | /* LED select registers determine the source that drives LED outputs */ | 37 | /* LED select registers determine the source that drives LED outputs */ |
38 | #define PCA9633_LED_OFF 0x0 /* LED driver off */ | 38 | #define PCA9633_LED_OFF 0x0 /* LED driver off */ |
39 | #define PCA9633_LED_ON 0x1 /* LED driver on */ | 39 | #define PCA9633_LED_ON 0x1 /* LED driver on */ |
40 | #define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ | 40 | #define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ |
41 | #define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ | 41 | #define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ |
42 | 42 | ||
43 | #define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */ | 43 | #define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */ |
44 | 44 | ||
45 | #define PCA9633_MODE1 0x00 | 45 | #define PCA9633_MODE1 0x00 |
46 | #define PCA9633_MODE2 0x01 | 46 | #define PCA9633_MODE2 0x01 |
47 | #define PCA9633_PWM_BASE 0x02 | 47 | #define PCA9633_PWM_BASE 0x02 |
48 | #define PCA9633_GRPPWM 0x06 | 48 | #define PCA9633_GRPPWM 0x06 |
49 | #define PCA9633_GRPFREQ 0x07 | 49 | #define PCA9633_GRPFREQ 0x07 |
50 | #define PCA9633_LEDOUT 0x08 | 50 | #define PCA9633_LEDOUT 0x08 |
51 | 51 | ||
52 | /* Total blink period in milliseconds */ | 52 | /* Total blink period in milliseconds */ |
53 | #define PCA9632_BLINK_PERIOD_MIN 42 | 53 | #define PCA9632_BLINK_PERIOD_MIN 42 |
54 | #define PCA9632_BLINK_PERIOD_MAX 10667 | 54 | #define PCA9632_BLINK_PERIOD_MAX 10667 |
55 | 55 | ||
56 | static const struct i2c_device_id pca9633_id[] = { | 56 | static const struct i2c_device_id pca9633_id[] = { |
57 | { "pca9633", 0 }, | 57 | { "pca9633", 0 }, |
58 | { } | 58 | { } |
59 | }; | 59 | }; |
60 | MODULE_DEVICE_TABLE(i2c, pca9633_id); | 60 | MODULE_DEVICE_TABLE(i2c, pca9633_id); |
61 | 61 | ||
62 | enum pca9633_cmd { | 62 | enum pca9633_cmd { |
63 | BRIGHTNESS_SET, | 63 | BRIGHTNESS_SET, |
64 | BLINK_SET, | 64 | BLINK_SET, |
65 | }; | 65 | }; |
66 | 66 | ||
67 | struct pca9633_led { | 67 | struct pca9633_led { |
68 | struct i2c_client *client; | 68 | struct i2c_client *client; |
69 | struct work_struct work; | 69 | struct work_struct work; |
70 | enum led_brightness brightness; | 70 | enum led_brightness brightness; |
71 | struct led_classdev led_cdev; | 71 | struct led_classdev led_cdev; |
72 | int led_num; /* 0 .. 3 potentially */ | 72 | int led_num; /* 0 .. 3 potentially */ |
73 | enum pca9633_cmd cmd; | 73 | enum pca9633_cmd cmd; |
74 | char name[32]; | 74 | char name[32]; |
75 | u8 gdc; | 75 | u8 gdc; |
76 | u8 gfrq; | 76 | u8 gfrq; |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static void pca9633_brightness_work(struct pca9633_led *pca9633) | 79 | static void pca9633_brightness_work(struct pca9633_led *pca9633) |
80 | { | 80 | { |
81 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); | 81 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); |
82 | int shift = 2 * pca9633->led_num; | 82 | int shift = 2 * pca9633->led_num; |
83 | u8 mask = 0x3 << shift; | 83 | u8 mask = 0x3 << shift; |
84 | 84 | ||
85 | switch (pca9633->brightness) { | 85 | switch (pca9633->brightness) { |
86 | case LED_FULL: | 86 | case LED_FULL: |
87 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | 87 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, |
88 | (ledout & ~mask) | (PCA9633_LED_ON << shift)); | 88 | (ledout & ~mask) | (PCA9633_LED_ON << shift)); |
89 | break; | 89 | break; |
90 | case LED_OFF: | 90 | case LED_OFF: |
91 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | 91 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, |
92 | ledout & ~mask); | 92 | ledout & ~mask); |
93 | break; | 93 | break; |
94 | default: | 94 | default: |
95 | i2c_smbus_write_byte_data(pca9633->client, | 95 | i2c_smbus_write_byte_data(pca9633->client, |
96 | PCA9633_PWM_BASE + pca9633->led_num, | 96 | PCA9633_PWM_BASE + pca9633->led_num, |
97 | pca9633->brightness); | 97 | pca9633->brightness); |
98 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | 98 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, |
99 | (ledout & ~mask) | (PCA9633_LED_PWM << shift)); | 99 | (ledout & ~mask) | (PCA9633_LED_PWM << shift)); |
100 | break; | 100 | break; |
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | static void pca9633_blink_work(struct pca9633_led *pca9633) | 104 | static void pca9633_blink_work(struct pca9633_led *pca9633) |
105 | { | 105 | { |
106 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); | 106 | u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); |
107 | u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2); | 107 | u8 mode2 = i2c_smbus_read_byte_data(pca9633->client, PCA9633_MODE2); |
108 | int shift = 2 * pca9633->led_num; | 108 | int shift = 2 * pca9633->led_num; |
109 | u8 mask = 0x3 << shift; | 109 | u8 mask = 0x3 << shift; |
110 | 110 | ||
111 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPPWM, | 111 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPPWM, |
112 | pca9633->gdc); | 112 | pca9633->gdc); |
113 | 113 | ||
114 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPFREQ, | 114 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_GRPFREQ, |
115 | pca9633->gfrq); | 115 | pca9633->gfrq); |
116 | 116 | ||
117 | if (!(mode2 & PCA9633_MODE2_DMBLNK)) | 117 | if (!(mode2 & PCA9633_MODE2_DMBLNK)) |
118 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2, | 118 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_MODE2, |
119 | mode2 | PCA9633_MODE2_DMBLNK); | 119 | mode2 | PCA9633_MODE2_DMBLNK); |
120 | 120 | ||
121 | if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) | 121 | if ((ledout & mask) != (PCA9633_LED_GRP_PWM << shift)) |
122 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, | 122 | i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, |
123 | (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); | 123 | (ledout & ~mask) | (PCA9633_LED_GRP_PWM << shift)); |
124 | } | 124 | } |
125 | 125 | ||
126 | static void pca9633_work(struct work_struct *work) | 126 | static void pca9633_work(struct work_struct *work) |
127 | { | 127 | { |
128 | struct pca9633_led *pca9633 = container_of(work, | 128 | struct pca9633_led *pca9633 = container_of(work, |
129 | struct pca9633_led, work); | 129 | struct pca9633_led, work); |
130 | 130 | ||
131 | switch (pca9633->cmd) { | 131 | switch (pca9633->cmd) { |
132 | case BRIGHTNESS_SET: | 132 | case BRIGHTNESS_SET: |
133 | pca9633_brightness_work(pca9633); | 133 | pca9633_brightness_work(pca9633); |
134 | break; | 134 | break; |
135 | case BLINK_SET: | 135 | case BLINK_SET: |
136 | pca9633_blink_work(pca9633); | 136 | pca9633_blink_work(pca9633); |
137 | break; | 137 | break; |
138 | } | 138 | } |
139 | } | 139 | } |
140 | 140 | ||
141 | static void pca9633_led_set(struct led_classdev *led_cdev, | 141 | static void pca9633_led_set(struct led_classdev *led_cdev, |
142 | enum led_brightness value) | 142 | enum led_brightness value) |
143 | { | 143 | { |
144 | struct pca9633_led *pca9633; | 144 | struct pca9633_led *pca9633; |
145 | 145 | ||
146 | pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); | 146 | pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); |
147 | 147 | ||
148 | pca9633->cmd = BRIGHTNESS_SET; | 148 | pca9633->cmd = BRIGHTNESS_SET; |
149 | pca9633->brightness = value; | 149 | pca9633->brightness = value; |
150 | 150 | ||
151 | /* | 151 | /* |
152 | * Must use workqueue for the actual I/O since I2C operations | 152 | * Must use workqueue for the actual I/O since I2C operations |
153 | * can sleep. | 153 | * can sleep. |
154 | */ | 154 | */ |
155 | schedule_work(&pca9633->work); | 155 | schedule_work(&pca9633->work); |
156 | } | 156 | } |
157 | 157 | ||
158 | static int pca9633_blink_set(struct led_classdev *led_cdev, | 158 | static int pca9633_blink_set(struct led_classdev *led_cdev, |
159 | unsigned long *delay_on, unsigned long *delay_off) | 159 | unsigned long *delay_on, unsigned long *delay_off) |
160 | { | 160 | { |
161 | struct pca9633_led *pca9633; | 161 | struct pca9633_led *pca9633; |
162 | unsigned long time_on, time_off, period; | 162 | unsigned long time_on, time_off, period; |
163 | u8 gdc, gfrq; | 163 | u8 gdc, gfrq; |
164 | 164 | ||
165 | pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); | 165 | pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); |
166 | 166 | ||
167 | time_on = *delay_on; | 167 | time_on = *delay_on; |
168 | time_off = *delay_off; | 168 | time_off = *delay_off; |
169 | 169 | ||
170 | /* If both zero, pick reasonable defaults of 500ms each */ | 170 | /* If both zero, pick reasonable defaults of 500ms each */ |
171 | if (!time_on && !time_off) { | 171 | if (!time_on && !time_off) { |
172 | time_on = 500; | 172 | time_on = 500; |
173 | time_off = 500; | 173 | time_off = 500; |
174 | } | 174 | } |
175 | 175 | ||
176 | period = time_on + time_off; | 176 | period = time_on + time_off; |
177 | 177 | ||
178 | /* If period not supported by hardware, default to someting sane. */ | 178 | /* If period not supported by hardware, default to someting sane. */ |
179 | if ((period < PCA9632_BLINK_PERIOD_MIN) || | 179 | if ((period < PCA9632_BLINK_PERIOD_MIN) || |
180 | (period > PCA9632_BLINK_PERIOD_MAX)) { | 180 | (period > PCA9632_BLINK_PERIOD_MAX)) { |
181 | time_on = 500; | 181 | time_on = 500; |
182 | time_off = 500; | 182 | time_off = 500; |
183 | period = time_on + time_off; | 183 | period = time_on + time_off; |
184 | } | 184 | } |
185 | 185 | ||
186 | /* | 186 | /* |
187 | * From manual: duty cycle = (GDC / 256) -> | 187 | * From manual: duty cycle = (GDC / 256) -> |
188 | * (time_on / period) = (GDC / 256) -> | 188 | * (time_on / period) = (GDC / 256) -> |
189 | * GDC = ((time_on * 256) / period) | 189 | * GDC = ((time_on * 256) / period) |
190 | */ | 190 | */ |
191 | gdc = (time_on * 256) / period; | 191 | gdc = (time_on * 256) / period; |
192 | 192 | ||
193 | /* | 193 | /* |
194 | * From manual: period = ((GFRQ + 1) / 24) in seconds. | 194 | * From manual: period = ((GFRQ + 1) / 24) in seconds. |
195 | * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) -> | 195 | * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) -> |
196 | * GFRQ = ((period * 24 / 1000) - 1) | 196 | * GFRQ = ((period * 24 / 1000) - 1) |
197 | */ | 197 | */ |
198 | gfrq = (period * 24 / 1000) - 1; | 198 | gfrq = (period * 24 / 1000) - 1; |
199 | 199 | ||
200 | pca9633->cmd = BLINK_SET; | 200 | pca9633->cmd = BLINK_SET; |
201 | pca9633->gdc = gdc; | 201 | pca9633->gdc = gdc; |
202 | pca9633->gfrq = gfrq; | 202 | pca9633->gfrq = gfrq; |
203 | 203 | ||
204 | /* | 204 | /* |
205 | * Must use workqueue for the actual I/O since I2C operations | 205 | * Must use workqueue for the actual I/O since I2C operations |
206 | * can sleep. | 206 | * can sleep. |
207 | */ | 207 | */ |
208 | schedule_work(&pca9633->work); | 208 | schedule_work(&pca9633->work); |
209 | 209 | ||
210 | *delay_on = time_on; | 210 | *delay_on = time_on; |
211 | *delay_off = time_off; | 211 | *delay_off = time_off; |
212 | 212 | ||
213 | return 0; | 213 | return 0; |
214 | } | 214 | } |
215 | 215 | ||
216 | #if IS_ENABLED(CONFIG_OF) | 216 | #if IS_ENABLED(CONFIG_OF) |
217 | static struct pca9633_platform_data * | 217 | static struct pca9633_platform_data * |
218 | pca9633_dt_init(struct i2c_client *client) | 218 | pca9633_dt_init(struct i2c_client *client) |
219 | { | 219 | { |
220 | struct device_node *np = client->dev.of_node, *child; | 220 | struct device_node *np = client->dev.of_node, *child; |
221 | struct pca9633_platform_data *pdata; | 221 | struct pca9633_platform_data *pdata; |
222 | struct led_info *pca9633_leds; | 222 | struct led_info *pca9633_leds; |
223 | int count; | 223 | int count; |
224 | 224 | ||
225 | count = of_get_child_count(np); | 225 | count = of_get_child_count(np); |
226 | if (!count || count > 4) | 226 | if (!count || count > 4) |
227 | return ERR_PTR(-ENODEV); | 227 | return ERR_PTR(-ENODEV); |
228 | 228 | ||
229 | pca9633_leds = devm_kzalloc(&client->dev, | 229 | pca9633_leds = devm_kzalloc(&client->dev, |
230 | sizeof(struct led_info) * count, GFP_KERNEL); | 230 | sizeof(struct led_info) * count, GFP_KERNEL); |
231 | if (!pca9633_leds) | 231 | if (!pca9633_leds) |
232 | return ERR_PTR(-ENOMEM); | 232 | return ERR_PTR(-ENOMEM); |
233 | 233 | ||
234 | for_each_child_of_node(np, child) { | 234 | for_each_child_of_node(np, child) { |
235 | struct led_info led; | 235 | struct led_info led; |
236 | u32 reg; | 236 | u32 reg; |
237 | int res; | 237 | int res; |
238 | 238 | ||
239 | led.name = | 239 | led.name = |
240 | of_get_property(child, "label", NULL) ? : child->name; | 240 | of_get_property(child, "label", NULL) ? : child->name; |
241 | led.default_trigger = | 241 | led.default_trigger = |
242 | of_get_property(child, "linux,default-trigger", NULL); | 242 | of_get_property(child, "linux,default-trigger", NULL); |
243 | res = of_property_read_u32(child, "reg", ®); | 243 | res = of_property_read_u32(child, "reg", ®); |
244 | if (res != 0) | 244 | if (res != 0) |
245 | continue; | 245 | continue; |
246 | pca9633_leds[reg] = led; | 246 | pca9633_leds[reg] = led; |
247 | } | 247 | } |
248 | pdata = devm_kzalloc(&client->dev, | 248 | pdata = devm_kzalloc(&client->dev, |
249 | sizeof(struct pca9633_platform_data), GFP_KERNEL); | 249 | sizeof(struct pca9633_platform_data), GFP_KERNEL); |
250 | if (!pdata) | 250 | if (!pdata) |
251 | return ERR_PTR(-ENOMEM); | 251 | return ERR_PTR(-ENOMEM); |
252 | 252 | ||
253 | pdata->leds.leds = pca9633_leds; | 253 | pdata->leds.leds = pca9633_leds; |
254 | pdata->leds.num_leds = count; | 254 | pdata->leds.num_leds = count; |
255 | 255 | ||
256 | /* default to open-drain unless totem pole (push-pull) is specified */ | 256 | /* default to open-drain unless totem pole (push-pull) is specified */ |
257 | if (of_property_read_bool(np, "nxp,totem-pole")) | 257 | if (of_property_read_bool(np, "nxp,totem-pole")) |
258 | pdata->outdrv = PCA9633_TOTEM_POLE; | 258 | pdata->outdrv = PCA9633_TOTEM_POLE; |
259 | else | 259 | else |
260 | pdata->outdrv = PCA9633_OPEN_DRAIN; | 260 | pdata->outdrv = PCA9633_OPEN_DRAIN; |
261 | 261 | ||
262 | /* default to software blinking unless hardware blinking is specified */ | 262 | /* default to software blinking unless hardware blinking is specified */ |
263 | if (of_property_read_bool(np, "nxp,hw-blink")) | 263 | if (of_property_read_bool(np, "nxp,hw-blink")) |
264 | pdata->blink_type = PCA9633_HW_BLINK; | 264 | pdata->blink_type = PCA9633_HW_BLINK; |
265 | else | 265 | else |
266 | pdata->blink_type = PCA9633_SW_BLINK; | 266 | pdata->blink_type = PCA9633_SW_BLINK; |
267 | 267 | ||
268 | return pdata; | 268 | return pdata; |
269 | } | 269 | } |
270 | 270 | ||
271 | static const struct of_device_id of_pca9633_match[] = { | 271 | static const struct of_device_id of_pca9633_match[] = { |
272 | { .compatible = "nxp,pca963x", }, | 272 | { .compatible = "nxp,pca963x", }, |
273 | {}, | 273 | {}, |
274 | }; | 274 | }; |
275 | #else | 275 | #else |
276 | static struct pca9633_platform_data * | 276 | static struct pca9633_platform_data * |
277 | pca9633_dt_init(struct i2c_client *client) | 277 | pca9633_dt_init(struct i2c_client *client) |
278 | { | 278 | { |
279 | return ERR_PTR(-ENODEV); | 279 | return ERR_PTR(-ENODEV); |
280 | } | 280 | } |
281 | #endif | 281 | #endif |
282 | 282 | ||
283 | static int pca9633_probe(struct i2c_client *client, | 283 | static int pca9633_probe(struct i2c_client *client, |
284 | const struct i2c_device_id *id) | 284 | const struct i2c_device_id *id) |
285 | { | 285 | { |
286 | struct pca9633_led *pca9633; | 286 | struct pca9633_led *pca9633; |
287 | struct pca9633_platform_data *pdata; | 287 | struct pca9633_platform_data *pdata; |
288 | int i, err; | 288 | int i, err; |
289 | 289 | ||
290 | pdata = client->dev.platform_data; | 290 | pdata = dev_get_platdata(&client->dev); |
291 | 291 | ||
292 | if (!pdata) { | 292 | if (!pdata) { |
293 | pdata = pca9633_dt_init(client); | 293 | pdata = pca9633_dt_init(client); |
294 | if (IS_ERR(pdata)) { | 294 | if (IS_ERR(pdata)) { |
295 | dev_warn(&client->dev, "could not parse configuration\n"); | 295 | dev_warn(&client->dev, "could not parse configuration\n"); |
296 | pdata = NULL; | 296 | pdata = NULL; |
297 | } | 297 | } |
298 | } | 298 | } |
299 | 299 | ||
300 | if (pdata) { | 300 | if (pdata) { |
301 | if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) { | 301 | if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) { |
302 | dev_err(&client->dev, "board info must claim at most 4 LEDs"); | 302 | dev_err(&client->dev, "board info must claim at most 4 LEDs"); |
303 | return -EINVAL; | 303 | return -EINVAL; |
304 | } | 304 | } |
305 | } | 305 | } |
306 | 306 | ||
307 | pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL); | 307 | pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL); |
308 | if (!pca9633) | 308 | if (!pca9633) |
309 | return -ENOMEM; | 309 | return -ENOMEM; |
310 | 310 | ||
311 | i2c_set_clientdata(client, pca9633); | 311 | i2c_set_clientdata(client, pca9633); |
312 | 312 | ||
313 | for (i = 0; i < 4; i++) { | 313 | for (i = 0; i < 4; i++) { |
314 | pca9633[i].client = client; | 314 | pca9633[i].client = client; |
315 | pca9633[i].led_num = i; | 315 | pca9633[i].led_num = i; |
316 | 316 | ||
317 | /* Platform data can specify LED names and default triggers */ | 317 | /* Platform data can specify LED names and default triggers */ |
318 | if (pdata && i < pdata->leds.num_leds) { | 318 | if (pdata && i < pdata->leds.num_leds) { |
319 | if (pdata->leds.leds[i].name) | 319 | if (pdata->leds.leds[i].name) |
320 | snprintf(pca9633[i].name, | 320 | snprintf(pca9633[i].name, |
321 | sizeof(pca9633[i].name), "pca9633:%s", | 321 | sizeof(pca9633[i].name), "pca9633:%s", |
322 | pdata->leds.leds[i].name); | 322 | pdata->leds.leds[i].name); |
323 | if (pdata->leds.leds[i].default_trigger) | 323 | if (pdata->leds.leds[i].default_trigger) |
324 | pca9633[i].led_cdev.default_trigger = | 324 | pca9633[i].led_cdev.default_trigger = |
325 | pdata->leds.leds[i].default_trigger; | 325 | pdata->leds.leds[i].default_trigger; |
326 | } else { | 326 | } else { |
327 | snprintf(pca9633[i].name, sizeof(pca9633[i].name), | 327 | snprintf(pca9633[i].name, sizeof(pca9633[i].name), |
328 | "pca9633:%d", i); | 328 | "pca9633:%d", i); |
329 | } | 329 | } |
330 | 330 | ||
331 | pca9633[i].led_cdev.name = pca9633[i].name; | 331 | pca9633[i].led_cdev.name = pca9633[i].name; |
332 | pca9633[i].led_cdev.brightness_set = pca9633_led_set; | 332 | pca9633[i].led_cdev.brightness_set = pca9633_led_set; |
333 | 333 | ||
334 | if (pdata && pdata->blink_type == PCA9633_HW_BLINK) | 334 | if (pdata && pdata->blink_type == PCA9633_HW_BLINK) |
335 | pca9633[i].led_cdev.blink_set = pca9633_blink_set; | 335 | pca9633[i].led_cdev.blink_set = pca9633_blink_set; |
336 | 336 | ||
337 | INIT_WORK(&pca9633[i].work, pca9633_work); | 337 | INIT_WORK(&pca9633[i].work, pca9633_work); |
338 | 338 | ||
339 | err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); | 339 | err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); |
340 | if (err < 0) | 340 | if (err < 0) |
341 | goto exit; | 341 | goto exit; |
342 | } | 342 | } |
343 | 343 | ||
344 | /* Disable LED all-call address and set normal mode */ | 344 | /* Disable LED all-call address and set normal mode */ |
345 | i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); | 345 | i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); |
346 | 346 | ||
347 | /* Configure output: open-drain or totem pole (push-pull) */ | 347 | /* Configure output: open-drain or totem pole (push-pull) */ |
348 | if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) | 348 | if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) |
349 | i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); | 349 | i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); |
350 | 350 | ||
351 | /* Turn off LEDs */ | 351 | /* Turn off LEDs */ |
352 | i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00); | 352 | i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00); |
353 | 353 | ||
354 | return 0; | 354 | return 0; |
355 | 355 | ||
356 | exit: | 356 | exit: |
357 | while (i--) { | 357 | while (i--) { |
358 | led_classdev_unregister(&pca9633[i].led_cdev); | 358 | led_classdev_unregister(&pca9633[i].led_cdev); |
359 | cancel_work_sync(&pca9633[i].work); | 359 | cancel_work_sync(&pca9633[i].work); |
360 | } | 360 | } |
361 | 361 | ||
362 | return err; | 362 | return err; |
363 | } | 363 | } |
364 | 364 | ||
365 | static int pca9633_remove(struct i2c_client *client) | 365 | static int pca9633_remove(struct i2c_client *client) |
366 | { | 366 | { |
367 | struct pca9633_led *pca9633 = i2c_get_clientdata(client); | 367 | struct pca9633_led *pca9633 = i2c_get_clientdata(client); |
368 | int i; | 368 | int i; |
369 | 369 | ||
370 | for (i = 0; i < 4; i++) { | 370 | for (i = 0; i < 4; i++) { |
371 | led_classdev_unregister(&pca9633[i].led_cdev); | 371 | led_classdev_unregister(&pca9633[i].led_cdev); |
372 | cancel_work_sync(&pca9633[i].work); | 372 | cancel_work_sync(&pca9633[i].work); |
373 | } | 373 | } |
374 | 374 | ||
375 | return 0; | 375 | return 0; |
376 | } | 376 | } |
377 | 377 | ||
378 | static struct i2c_driver pca9633_driver = { | 378 | static struct i2c_driver pca9633_driver = { |
379 | .driver = { | 379 | .driver = { |
380 | .name = "leds-pca9633", | 380 | .name = "leds-pca9633", |
381 | .owner = THIS_MODULE, | 381 | .owner = THIS_MODULE, |
382 | .of_match_table = of_match_ptr(of_pca9633_match), | 382 | .of_match_table = of_match_ptr(of_pca9633_match), |
383 | }, | 383 | }, |
384 | .probe = pca9633_probe, | 384 | .probe = pca9633_probe, |
385 | .remove = pca9633_remove, | 385 | .remove = pca9633_remove, |
386 | .id_table = pca9633_id, | 386 | .id_table = pca9633_id, |
387 | }; | 387 | }; |
388 | 388 | ||
389 | module_i2c_driver(pca9633_driver); | 389 | module_i2c_driver(pca9633_driver); |
390 | 390 | ||
391 | MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>"); | 391 | MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>"); |
392 | MODULE_DESCRIPTION("PCA9633 LED driver"); | 392 | MODULE_DESCRIPTION("PCA9633 LED driver"); |
393 | MODULE_LICENSE("GPL v2"); | 393 | MODULE_LICENSE("GPL v2"); |
394 | 394 |
drivers/leds/leds-pwm.c
1 | /* | 1 | /* |
2 | * linux/drivers/leds-pwm.c | 2 | * linux/drivers/leds-pwm.c |
3 | * | 3 | * |
4 | * simple PWM based LED control | 4 | * simple PWM based LED control |
5 | * | 5 | * |
6 | * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de) | 6 | * Copyright 2009 Luotao Fu @ Pengutronix (l.fu@pengutronix.de) |
7 | * | 7 | * |
8 | * based on leds-gpio.c by Raphael Assenat <raph@8d.com> | 8 | * based on leds-gpio.c by Raphael Assenat <raph@8d.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> | 16 | #include <linux/kernel.h> |
17 | #include <linux/init.h> | 17 | #include <linux/init.h> |
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/of_platform.h> | 19 | #include <linux/of_platform.h> |
20 | #include <linux/fb.h> | 20 | #include <linux/fb.h> |
21 | #include <linux/leds.h> | 21 | #include <linux/leds.h> |
22 | #include <linux/err.h> | 22 | #include <linux/err.h> |
23 | #include <linux/pwm.h> | 23 | #include <linux/pwm.h> |
24 | #include <linux/leds_pwm.h> | 24 | #include <linux/leds_pwm.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/workqueue.h> | 26 | #include <linux/workqueue.h> |
27 | 27 | ||
28 | struct led_pwm_data { | 28 | struct led_pwm_data { |
29 | struct led_classdev cdev; | 29 | struct led_classdev cdev; |
30 | struct pwm_device *pwm; | 30 | struct pwm_device *pwm; |
31 | struct work_struct work; | 31 | struct work_struct work; |
32 | unsigned int active_low; | 32 | unsigned int active_low; |
33 | unsigned int period; | 33 | unsigned int period; |
34 | int duty; | 34 | int duty; |
35 | bool can_sleep; | 35 | bool can_sleep; |
36 | }; | 36 | }; |
37 | 37 | ||
38 | struct led_pwm_priv { | 38 | struct led_pwm_priv { |
39 | int num_leds; | 39 | int num_leds; |
40 | struct led_pwm_data leds[0]; | 40 | struct led_pwm_data leds[0]; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static void __led_pwm_set(struct led_pwm_data *led_dat) | 43 | static void __led_pwm_set(struct led_pwm_data *led_dat) |
44 | { | 44 | { |
45 | int new_duty = led_dat->duty; | 45 | int new_duty = led_dat->duty; |
46 | 46 | ||
47 | pwm_config(led_dat->pwm, new_duty, led_dat->period); | 47 | pwm_config(led_dat->pwm, new_duty, led_dat->period); |
48 | 48 | ||
49 | if (new_duty == 0) | 49 | if (new_duty == 0) |
50 | pwm_disable(led_dat->pwm); | 50 | pwm_disable(led_dat->pwm); |
51 | else | 51 | else |
52 | pwm_enable(led_dat->pwm); | 52 | pwm_enable(led_dat->pwm); |
53 | } | 53 | } |
54 | 54 | ||
55 | static void led_pwm_work(struct work_struct *work) | 55 | static void led_pwm_work(struct work_struct *work) |
56 | { | 56 | { |
57 | struct led_pwm_data *led_dat = | 57 | struct led_pwm_data *led_dat = |
58 | container_of(work, struct led_pwm_data, work); | 58 | container_of(work, struct led_pwm_data, work); |
59 | 59 | ||
60 | __led_pwm_set(led_dat); | 60 | __led_pwm_set(led_dat); |
61 | } | 61 | } |
62 | 62 | ||
63 | static void led_pwm_set(struct led_classdev *led_cdev, | 63 | static void led_pwm_set(struct led_classdev *led_cdev, |
64 | enum led_brightness brightness) | 64 | enum led_brightness brightness) |
65 | { | 65 | { |
66 | struct led_pwm_data *led_dat = | 66 | struct led_pwm_data *led_dat = |
67 | container_of(led_cdev, struct led_pwm_data, cdev); | 67 | container_of(led_cdev, struct led_pwm_data, cdev); |
68 | unsigned int max = led_dat->cdev.max_brightness; | 68 | unsigned int max = led_dat->cdev.max_brightness; |
69 | unsigned int period = led_dat->period; | 69 | unsigned int period = led_dat->period; |
70 | 70 | ||
71 | led_dat->duty = brightness * period / max; | 71 | led_dat->duty = brightness * period / max; |
72 | 72 | ||
73 | if (led_dat->can_sleep) | 73 | if (led_dat->can_sleep) |
74 | schedule_work(&led_dat->work); | 74 | schedule_work(&led_dat->work); |
75 | else | 75 | else |
76 | __led_pwm_set(led_dat); | 76 | __led_pwm_set(led_dat); |
77 | } | 77 | } |
78 | 78 | ||
79 | static inline size_t sizeof_pwm_leds_priv(int num_leds) | 79 | static inline size_t sizeof_pwm_leds_priv(int num_leds) |
80 | { | 80 | { |
81 | return sizeof(struct led_pwm_priv) + | 81 | return sizeof(struct led_pwm_priv) + |
82 | (sizeof(struct led_pwm_data) * num_leds); | 82 | (sizeof(struct led_pwm_data) * num_leds); |
83 | } | 83 | } |
84 | 84 | ||
85 | static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) | 85 | static struct led_pwm_priv *led_pwm_create_of(struct platform_device *pdev) |
86 | { | 86 | { |
87 | struct device_node *node = pdev->dev.of_node; | 87 | struct device_node *node = pdev->dev.of_node; |
88 | struct device_node *child; | 88 | struct device_node *child; |
89 | struct led_pwm_priv *priv; | 89 | struct led_pwm_priv *priv; |
90 | int count, ret; | 90 | int count, ret; |
91 | 91 | ||
92 | /* count LEDs in this device, so we know how much to allocate */ | 92 | /* count LEDs in this device, so we know how much to allocate */ |
93 | count = of_get_child_count(node); | 93 | count = of_get_child_count(node); |
94 | if (!count) | 94 | if (!count) |
95 | return NULL; | 95 | return NULL; |
96 | 96 | ||
97 | priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count), | 97 | priv = devm_kzalloc(&pdev->dev, sizeof_pwm_leds_priv(count), |
98 | GFP_KERNEL); | 98 | GFP_KERNEL); |
99 | if (!priv) | 99 | if (!priv) |
100 | return NULL; | 100 | return NULL; |
101 | 101 | ||
102 | for_each_child_of_node(node, child) { | 102 | for_each_child_of_node(node, child) { |
103 | struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; | 103 | struct led_pwm_data *led_dat = &priv->leds[priv->num_leds]; |
104 | 104 | ||
105 | led_dat->cdev.name = of_get_property(child, "label", | 105 | led_dat->cdev.name = of_get_property(child, "label", |
106 | NULL) ? : child->name; | 106 | NULL) ? : child->name; |
107 | 107 | ||
108 | led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL); | 108 | led_dat->pwm = devm_of_pwm_get(&pdev->dev, child, NULL); |
109 | if (IS_ERR(led_dat->pwm)) { | 109 | if (IS_ERR(led_dat->pwm)) { |
110 | dev_err(&pdev->dev, "unable to request PWM for %s\n", | 110 | dev_err(&pdev->dev, "unable to request PWM for %s\n", |
111 | led_dat->cdev.name); | 111 | led_dat->cdev.name); |
112 | goto err; | 112 | goto err; |
113 | } | 113 | } |
114 | /* Get the period from PWM core when n*/ | 114 | /* Get the period from PWM core when n*/ |
115 | led_dat->period = pwm_get_period(led_dat->pwm); | 115 | led_dat->period = pwm_get_period(led_dat->pwm); |
116 | 116 | ||
117 | led_dat->cdev.default_trigger = of_get_property(child, | 117 | led_dat->cdev.default_trigger = of_get_property(child, |
118 | "linux,default-trigger", NULL); | 118 | "linux,default-trigger", NULL); |
119 | of_property_read_u32(child, "max-brightness", | 119 | of_property_read_u32(child, "max-brightness", |
120 | &led_dat->cdev.max_brightness); | 120 | &led_dat->cdev.max_brightness); |
121 | 121 | ||
122 | led_dat->cdev.brightness_set = led_pwm_set; | 122 | led_dat->cdev.brightness_set = led_pwm_set; |
123 | led_dat->cdev.brightness = LED_OFF; | 123 | led_dat->cdev.brightness = LED_OFF; |
124 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 124 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
125 | 125 | ||
126 | led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); | 126 | led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); |
127 | if (led_dat->can_sleep) | 127 | if (led_dat->can_sleep) |
128 | INIT_WORK(&led_dat->work, led_pwm_work); | 128 | INIT_WORK(&led_dat->work, led_pwm_work); |
129 | 129 | ||
130 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 130 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
131 | if (ret < 0) { | 131 | if (ret < 0) { |
132 | dev_err(&pdev->dev, "failed to register for %s\n", | 132 | dev_err(&pdev->dev, "failed to register for %s\n", |
133 | led_dat->cdev.name); | 133 | led_dat->cdev.name); |
134 | of_node_put(child); | 134 | of_node_put(child); |
135 | goto err; | 135 | goto err; |
136 | } | 136 | } |
137 | priv->num_leds++; | 137 | priv->num_leds++; |
138 | } | 138 | } |
139 | 139 | ||
140 | return priv; | 140 | return priv; |
141 | err: | 141 | err: |
142 | while (priv->num_leds--) | 142 | while (priv->num_leds--) |
143 | led_classdev_unregister(&priv->leds[priv->num_leds].cdev); | 143 | led_classdev_unregister(&priv->leds[priv->num_leds].cdev); |
144 | 144 | ||
145 | return NULL; | 145 | return NULL; |
146 | } | 146 | } |
147 | 147 | ||
148 | static int led_pwm_probe(struct platform_device *pdev) | 148 | static int led_pwm_probe(struct platform_device *pdev) |
149 | { | 149 | { |
150 | struct led_pwm_platform_data *pdata = pdev->dev.platform_data; | 150 | struct led_pwm_platform_data *pdata = dev_get_platdata(&pdev->dev); |
151 | struct led_pwm_priv *priv; | 151 | struct led_pwm_priv *priv; |
152 | int i, ret = 0; | 152 | int i, ret = 0; |
153 | 153 | ||
154 | if (pdata && pdata->num_leds) { | 154 | if (pdata && pdata->num_leds) { |
155 | priv = devm_kzalloc(&pdev->dev, | 155 | priv = devm_kzalloc(&pdev->dev, |
156 | sizeof_pwm_leds_priv(pdata->num_leds), | 156 | sizeof_pwm_leds_priv(pdata->num_leds), |
157 | GFP_KERNEL); | 157 | GFP_KERNEL); |
158 | if (!priv) | 158 | if (!priv) |
159 | return -ENOMEM; | 159 | return -ENOMEM; |
160 | 160 | ||
161 | for (i = 0; i < pdata->num_leds; i++) { | 161 | for (i = 0; i < pdata->num_leds; i++) { |
162 | struct led_pwm *cur_led = &pdata->leds[i]; | 162 | struct led_pwm *cur_led = &pdata->leds[i]; |
163 | struct led_pwm_data *led_dat = &priv->leds[i]; | 163 | struct led_pwm_data *led_dat = &priv->leds[i]; |
164 | 164 | ||
165 | led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); | 165 | led_dat->pwm = devm_pwm_get(&pdev->dev, cur_led->name); |
166 | if (IS_ERR(led_dat->pwm)) { | 166 | if (IS_ERR(led_dat->pwm)) { |
167 | ret = PTR_ERR(led_dat->pwm); | 167 | ret = PTR_ERR(led_dat->pwm); |
168 | dev_err(&pdev->dev, | 168 | dev_err(&pdev->dev, |
169 | "unable to request PWM for %s\n", | 169 | "unable to request PWM for %s\n", |
170 | cur_led->name); | 170 | cur_led->name); |
171 | goto err; | 171 | goto err; |
172 | } | 172 | } |
173 | 173 | ||
174 | led_dat->cdev.name = cur_led->name; | 174 | led_dat->cdev.name = cur_led->name; |
175 | led_dat->cdev.default_trigger = cur_led->default_trigger; | 175 | led_dat->cdev.default_trigger = cur_led->default_trigger; |
176 | led_dat->active_low = cur_led->active_low; | 176 | led_dat->active_low = cur_led->active_low; |
177 | led_dat->period = cur_led->pwm_period_ns; | 177 | led_dat->period = cur_led->pwm_period_ns; |
178 | led_dat->cdev.brightness_set = led_pwm_set; | 178 | led_dat->cdev.brightness_set = led_pwm_set; |
179 | led_dat->cdev.brightness = LED_OFF; | 179 | led_dat->cdev.brightness = LED_OFF; |
180 | led_dat->cdev.max_brightness = cur_led->max_brightness; | 180 | led_dat->cdev.max_brightness = cur_led->max_brightness; |
181 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; | 181 | led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; |
182 | 182 | ||
183 | led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); | 183 | led_dat->can_sleep = pwm_can_sleep(led_dat->pwm); |
184 | if (led_dat->can_sleep) | 184 | if (led_dat->can_sleep) |
185 | INIT_WORK(&led_dat->work, led_pwm_work); | 185 | INIT_WORK(&led_dat->work, led_pwm_work); |
186 | 186 | ||
187 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); | 187 | ret = led_classdev_register(&pdev->dev, &led_dat->cdev); |
188 | if (ret < 0) | 188 | if (ret < 0) |
189 | goto err; | 189 | goto err; |
190 | } | 190 | } |
191 | priv->num_leds = pdata->num_leds; | 191 | priv->num_leds = pdata->num_leds; |
192 | } else { | 192 | } else { |
193 | priv = led_pwm_create_of(pdev); | 193 | priv = led_pwm_create_of(pdev); |
194 | if (!priv) | 194 | if (!priv) |
195 | return -ENODEV; | 195 | return -ENODEV; |
196 | } | 196 | } |
197 | 197 | ||
198 | platform_set_drvdata(pdev, priv); | 198 | platform_set_drvdata(pdev, priv); |
199 | 199 | ||
200 | return 0; | 200 | return 0; |
201 | 201 | ||
202 | err: | 202 | err: |
203 | while (i--) | 203 | while (i--) |
204 | led_classdev_unregister(&priv->leds[i].cdev); | 204 | led_classdev_unregister(&priv->leds[i].cdev); |
205 | 205 | ||
206 | return ret; | 206 | return ret; |
207 | } | 207 | } |
208 | 208 | ||
209 | static int led_pwm_remove(struct platform_device *pdev) | 209 | static int led_pwm_remove(struct platform_device *pdev) |
210 | { | 210 | { |
211 | struct led_pwm_priv *priv = platform_get_drvdata(pdev); | 211 | struct led_pwm_priv *priv = platform_get_drvdata(pdev); |
212 | int i; | 212 | int i; |
213 | 213 | ||
214 | for (i = 0; i < priv->num_leds; i++) { | 214 | for (i = 0; i < priv->num_leds; i++) { |
215 | led_classdev_unregister(&priv->leds[i].cdev); | 215 | led_classdev_unregister(&priv->leds[i].cdev); |
216 | if (priv->leds[i].can_sleep) | 216 | if (priv->leds[i].can_sleep) |
217 | cancel_work_sync(&priv->leds[i].work); | 217 | cancel_work_sync(&priv->leds[i].work); |
218 | } | 218 | } |
219 | 219 | ||
220 | return 0; | 220 | return 0; |
221 | } | 221 | } |
222 | 222 | ||
223 | static const struct of_device_id of_pwm_leds_match[] = { | 223 | static const struct of_device_id of_pwm_leds_match[] = { |
224 | { .compatible = "pwm-leds", }, | 224 | { .compatible = "pwm-leds", }, |
225 | {}, | 225 | {}, |
226 | }; | 226 | }; |
227 | MODULE_DEVICE_TABLE(of, of_pwm_leds_match); | 227 | MODULE_DEVICE_TABLE(of, of_pwm_leds_match); |
228 | 228 | ||
229 | static struct platform_driver led_pwm_driver = { | 229 | static struct platform_driver led_pwm_driver = { |
230 | .probe = led_pwm_probe, | 230 | .probe = led_pwm_probe, |
231 | .remove = led_pwm_remove, | 231 | .remove = led_pwm_remove, |
232 | .driver = { | 232 | .driver = { |
233 | .name = "leds_pwm", | 233 | .name = "leds_pwm", |
234 | .owner = THIS_MODULE, | 234 | .owner = THIS_MODULE, |
235 | .of_match_table = of_match_ptr(of_pwm_leds_match), | 235 | .of_match_table = of_match_ptr(of_pwm_leds_match), |
236 | }, | 236 | }, |
237 | }; | 237 | }; |
238 | 238 | ||
239 | module_platform_driver(led_pwm_driver); | 239 | module_platform_driver(led_pwm_driver); |
240 | 240 | ||
241 | MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); | 241 | MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>"); |
242 | MODULE_DESCRIPTION("PWM LED driver for PXA"); | 242 | MODULE_DESCRIPTION("PWM LED driver for PXA"); |
243 | MODULE_LICENSE("GPL"); | 243 | MODULE_LICENSE("GPL"); |
244 | MODULE_ALIAS("platform:leds-pwm"); | 244 | MODULE_ALIAS("platform:leds-pwm"); |
245 | 245 |
drivers/leds/leds-regulator.c
1 | /* | 1 | /* |
2 | * leds-regulator.c - LED class driver for regulator driven LEDs. | 2 | * leds-regulator.c - LED class driver for regulator driven LEDs. |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> | 4 | * Copyright (C) 2009 Antonio Ospite <ospite@studenti.unina.it> |
5 | * | 5 | * |
6 | * Inspired by leds-wm8350 driver. | 6 | * Inspired by leds-wm8350 driver. |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 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 | 9 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
11 | * | 11 | * |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/workqueue.h> | 17 | #include <linux/workqueue.h> |
18 | #include <linux/leds.h> | 18 | #include <linux/leds.h> |
19 | #include <linux/leds-regulator.h> | 19 | #include <linux/leds-regulator.h> |
20 | #include <linux/platform_device.h> | 20 | #include <linux/platform_device.h> |
21 | #include <linux/regulator/consumer.h> | 21 | #include <linux/regulator/consumer.h> |
22 | 22 | ||
23 | #define to_regulator_led(led_cdev) \ | 23 | #define to_regulator_led(led_cdev) \ |
24 | container_of(led_cdev, struct regulator_led, cdev) | 24 | container_of(led_cdev, struct regulator_led, cdev) |
25 | 25 | ||
26 | struct regulator_led { | 26 | struct regulator_led { |
27 | struct led_classdev cdev; | 27 | struct led_classdev cdev; |
28 | enum led_brightness value; | 28 | enum led_brightness value; |
29 | int enabled; | 29 | int enabled; |
30 | struct mutex mutex; | 30 | struct mutex mutex; |
31 | struct work_struct work; | 31 | struct work_struct work; |
32 | 32 | ||
33 | struct regulator *vcc; | 33 | struct regulator *vcc; |
34 | }; | 34 | }; |
35 | 35 | ||
36 | static inline int led_regulator_get_max_brightness(struct regulator *supply) | 36 | static inline int led_regulator_get_max_brightness(struct regulator *supply) |
37 | { | 37 | { |
38 | int ret; | 38 | int ret; |
39 | int voltage = regulator_list_voltage(supply, 0); | 39 | int voltage = regulator_list_voltage(supply, 0); |
40 | 40 | ||
41 | if (voltage <= 0) | 41 | if (voltage <= 0) |
42 | return 1; | 42 | return 1; |
43 | 43 | ||
44 | /* even if regulator can't change voltages, | 44 | /* even if regulator can't change voltages, |
45 | * we still assume it can change status | 45 | * we still assume it can change status |
46 | * and the LED can be turned on and off. | 46 | * and the LED can be turned on and off. |
47 | */ | 47 | */ |
48 | ret = regulator_set_voltage(supply, voltage, voltage); | 48 | ret = regulator_set_voltage(supply, voltage, voltage); |
49 | if (ret < 0) | 49 | if (ret < 0) |
50 | return 1; | 50 | return 1; |
51 | 51 | ||
52 | return regulator_count_voltages(supply); | 52 | return regulator_count_voltages(supply); |
53 | } | 53 | } |
54 | 54 | ||
55 | static int led_regulator_get_voltage(struct regulator *supply, | 55 | static int led_regulator_get_voltage(struct regulator *supply, |
56 | enum led_brightness brightness) | 56 | enum led_brightness brightness) |
57 | { | 57 | { |
58 | if (brightness == 0) | 58 | if (brightness == 0) |
59 | return -EINVAL; | 59 | return -EINVAL; |
60 | 60 | ||
61 | return regulator_list_voltage(supply, brightness - 1); | 61 | return regulator_list_voltage(supply, brightness - 1); |
62 | } | 62 | } |
63 | 63 | ||
64 | 64 | ||
65 | static void regulator_led_enable(struct regulator_led *led) | 65 | static void regulator_led_enable(struct regulator_led *led) |
66 | { | 66 | { |
67 | int ret; | 67 | int ret; |
68 | 68 | ||
69 | if (led->enabled) | 69 | if (led->enabled) |
70 | return; | 70 | return; |
71 | 71 | ||
72 | ret = regulator_enable(led->vcc); | 72 | ret = regulator_enable(led->vcc); |
73 | if (ret != 0) { | 73 | if (ret != 0) { |
74 | dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); | 74 | dev_err(led->cdev.dev, "Failed to enable vcc: %d\n", ret); |
75 | return; | 75 | return; |
76 | } | 76 | } |
77 | 77 | ||
78 | led->enabled = 1; | 78 | led->enabled = 1; |
79 | } | 79 | } |
80 | 80 | ||
81 | static void regulator_led_disable(struct regulator_led *led) | 81 | static void regulator_led_disable(struct regulator_led *led) |
82 | { | 82 | { |
83 | int ret; | 83 | int ret; |
84 | 84 | ||
85 | if (!led->enabled) | 85 | if (!led->enabled) |
86 | return; | 86 | return; |
87 | 87 | ||
88 | ret = regulator_disable(led->vcc); | 88 | ret = regulator_disable(led->vcc); |
89 | if (ret != 0) { | 89 | if (ret != 0) { |
90 | dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); | 90 | dev_err(led->cdev.dev, "Failed to disable vcc: %d\n", ret); |
91 | return; | 91 | return; |
92 | } | 92 | } |
93 | 93 | ||
94 | led->enabled = 0; | 94 | led->enabled = 0; |
95 | } | 95 | } |
96 | 96 | ||
97 | static void regulator_led_set_value(struct regulator_led *led) | 97 | static void regulator_led_set_value(struct regulator_led *led) |
98 | { | 98 | { |
99 | int voltage; | 99 | int voltage; |
100 | int ret; | 100 | int ret; |
101 | 101 | ||
102 | mutex_lock(&led->mutex); | 102 | mutex_lock(&led->mutex); |
103 | 103 | ||
104 | if (led->value == LED_OFF) { | 104 | if (led->value == LED_OFF) { |
105 | regulator_led_disable(led); | 105 | regulator_led_disable(led); |
106 | goto out; | 106 | goto out; |
107 | } | 107 | } |
108 | 108 | ||
109 | if (led->cdev.max_brightness > 1) { | 109 | if (led->cdev.max_brightness > 1) { |
110 | voltage = led_regulator_get_voltage(led->vcc, led->value); | 110 | voltage = led_regulator_get_voltage(led->vcc, led->value); |
111 | dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", | 111 | dev_dbg(led->cdev.dev, "brightness: %d voltage: %d\n", |
112 | led->value, voltage); | 112 | led->value, voltage); |
113 | 113 | ||
114 | ret = regulator_set_voltage(led->vcc, voltage, voltage); | 114 | ret = regulator_set_voltage(led->vcc, voltage, voltage); |
115 | if (ret != 0) | 115 | if (ret != 0) |
116 | dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n", | 116 | dev_err(led->cdev.dev, "Failed to set voltage %d: %d\n", |
117 | voltage, ret); | 117 | voltage, ret); |
118 | } | 118 | } |
119 | 119 | ||
120 | regulator_led_enable(led); | 120 | regulator_led_enable(led); |
121 | 121 | ||
122 | out: | 122 | out: |
123 | mutex_unlock(&led->mutex); | 123 | mutex_unlock(&led->mutex); |
124 | } | 124 | } |
125 | 125 | ||
126 | static void led_work(struct work_struct *work) | 126 | static void led_work(struct work_struct *work) |
127 | { | 127 | { |
128 | struct regulator_led *led; | 128 | struct regulator_led *led; |
129 | 129 | ||
130 | led = container_of(work, struct regulator_led, work); | 130 | led = container_of(work, struct regulator_led, work); |
131 | regulator_led_set_value(led); | 131 | regulator_led_set_value(led); |
132 | } | 132 | } |
133 | 133 | ||
134 | static void regulator_led_brightness_set(struct led_classdev *led_cdev, | 134 | static void regulator_led_brightness_set(struct led_classdev *led_cdev, |
135 | enum led_brightness value) | 135 | enum led_brightness value) |
136 | { | 136 | { |
137 | struct regulator_led *led = to_regulator_led(led_cdev); | 137 | struct regulator_led *led = to_regulator_led(led_cdev); |
138 | 138 | ||
139 | led->value = value; | 139 | led->value = value; |
140 | schedule_work(&led->work); | 140 | schedule_work(&led->work); |
141 | } | 141 | } |
142 | 142 | ||
143 | static int regulator_led_probe(struct platform_device *pdev) | 143 | static int regulator_led_probe(struct platform_device *pdev) |
144 | { | 144 | { |
145 | struct led_regulator_platform_data *pdata = pdev->dev.platform_data; | 145 | struct led_regulator_platform_data *pdata = |
146 | dev_get_platdata(&pdev->dev); | ||
146 | struct regulator_led *led; | 147 | struct regulator_led *led; |
147 | struct regulator *vcc; | 148 | struct regulator *vcc; |
148 | int ret = 0; | 149 | int ret = 0; |
149 | 150 | ||
150 | if (pdata == NULL) { | 151 | if (pdata == NULL) { |
151 | dev_err(&pdev->dev, "no platform data\n"); | 152 | dev_err(&pdev->dev, "no platform data\n"); |
152 | return -ENODEV; | 153 | return -ENODEV; |
153 | } | 154 | } |
154 | 155 | ||
155 | vcc = regulator_get_exclusive(&pdev->dev, "vled"); | 156 | vcc = regulator_get_exclusive(&pdev->dev, "vled"); |
156 | if (IS_ERR(vcc)) { | 157 | if (IS_ERR(vcc)) { |
157 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); | 158 | dev_err(&pdev->dev, "Cannot get vcc for %s\n", pdata->name); |
158 | return PTR_ERR(vcc); | 159 | return PTR_ERR(vcc); |
159 | } | 160 | } |
160 | 161 | ||
161 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | 162 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
162 | if (led == NULL) { | 163 | if (led == NULL) { |
163 | ret = -ENOMEM; | 164 | ret = -ENOMEM; |
164 | goto err_vcc; | 165 | goto err_vcc; |
165 | } | 166 | } |
166 | 167 | ||
167 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); | 168 | led->cdev.max_brightness = led_regulator_get_max_brightness(vcc); |
168 | if (pdata->brightness > led->cdev.max_brightness) { | 169 | if (pdata->brightness > led->cdev.max_brightness) { |
169 | dev_err(&pdev->dev, "Invalid default brightness %d\n", | 170 | dev_err(&pdev->dev, "Invalid default brightness %d\n", |
170 | pdata->brightness); | 171 | pdata->brightness); |
171 | ret = -EINVAL; | 172 | ret = -EINVAL; |
172 | goto err_vcc; | 173 | goto err_vcc; |
173 | } | 174 | } |
174 | led->value = pdata->brightness; | 175 | led->value = pdata->brightness; |
175 | 176 | ||
176 | led->cdev.brightness_set = regulator_led_brightness_set; | 177 | led->cdev.brightness_set = regulator_led_brightness_set; |
177 | led->cdev.name = pdata->name; | 178 | led->cdev.name = pdata->name; |
178 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | 179 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; |
179 | led->vcc = vcc; | 180 | led->vcc = vcc; |
180 | 181 | ||
181 | /* to handle correctly an already enabled regulator */ | 182 | /* to handle correctly an already enabled regulator */ |
182 | if (regulator_is_enabled(led->vcc)) | 183 | if (regulator_is_enabled(led->vcc)) |
183 | led->enabled = 1; | 184 | led->enabled = 1; |
184 | 185 | ||
185 | mutex_init(&led->mutex); | 186 | mutex_init(&led->mutex); |
186 | INIT_WORK(&led->work, led_work); | 187 | INIT_WORK(&led->work, led_work); |
187 | 188 | ||
188 | platform_set_drvdata(pdev, led); | 189 | platform_set_drvdata(pdev, led); |
189 | 190 | ||
190 | ret = led_classdev_register(&pdev->dev, &led->cdev); | 191 | ret = led_classdev_register(&pdev->dev, &led->cdev); |
191 | if (ret < 0) { | 192 | if (ret < 0) { |
192 | cancel_work_sync(&led->work); | 193 | cancel_work_sync(&led->work); |
193 | goto err_vcc; | 194 | goto err_vcc; |
194 | } | 195 | } |
195 | 196 | ||
196 | /* to expose the default value to userspace */ | 197 | /* to expose the default value to userspace */ |
197 | led->cdev.brightness = led->value; | 198 | led->cdev.brightness = led->value; |
198 | 199 | ||
199 | /* Set the default led status */ | 200 | /* Set the default led status */ |
200 | regulator_led_set_value(led); | 201 | regulator_led_set_value(led); |
201 | 202 | ||
202 | return 0; | 203 | return 0; |
203 | 204 | ||
204 | err_vcc: | 205 | err_vcc: |
205 | regulator_put(vcc); | 206 | regulator_put(vcc); |
206 | return ret; | 207 | return ret; |
207 | } | 208 | } |
208 | 209 | ||
209 | static int regulator_led_remove(struct platform_device *pdev) | 210 | static int regulator_led_remove(struct platform_device *pdev) |
210 | { | 211 | { |
211 | struct regulator_led *led = platform_get_drvdata(pdev); | 212 | struct regulator_led *led = platform_get_drvdata(pdev); |
212 | 213 | ||
213 | led_classdev_unregister(&led->cdev); | 214 | led_classdev_unregister(&led->cdev); |
214 | cancel_work_sync(&led->work); | 215 | cancel_work_sync(&led->work); |
215 | regulator_led_disable(led); | 216 | regulator_led_disable(led); |
216 | regulator_put(led->vcc); | 217 | regulator_put(led->vcc); |
217 | return 0; | 218 | return 0; |
218 | } | 219 | } |
219 | 220 | ||
220 | static struct platform_driver regulator_led_driver = { | 221 | static struct platform_driver regulator_led_driver = { |
221 | .driver = { | 222 | .driver = { |
222 | .name = "leds-regulator", | 223 | .name = "leds-regulator", |
223 | .owner = THIS_MODULE, | 224 | .owner = THIS_MODULE, |
224 | }, | 225 | }, |
225 | .probe = regulator_led_probe, | 226 | .probe = regulator_led_probe, |
226 | .remove = regulator_led_remove, | 227 | .remove = regulator_led_remove, |
227 | }; | 228 | }; |
228 | 229 | ||
229 | module_platform_driver(regulator_led_driver); | 230 | module_platform_driver(regulator_led_driver); |
230 | 231 | ||
231 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | 232 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); |
232 | MODULE_DESCRIPTION("Regulator driven LED driver"); | 233 | MODULE_DESCRIPTION("Regulator driven LED driver"); |
233 | MODULE_LICENSE("GPL"); | 234 | MODULE_LICENSE("GPL"); |
234 | MODULE_ALIAS("platform:leds-regulator"); | 235 | MODULE_ALIAS("platform:leds-regulator"); |
235 | 236 |
drivers/leds/leds-renesas-tpu.c
1 | /* | 1 | /* |
2 | * LED control using Renesas TPU | 2 | * LED control using Renesas TPU |
3 | * | 3 | * |
4 | * Copyright (C) 2011 Magnus Damm | 4 | * Copyright (C) 2011 Magnus Damm |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License | 8 | * the Free Software Foundation; either version 2 of the License |
9 | * | 9 | * |
10 | * This program is distributed in the hope that it will be useful, | 10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU General Public License | 15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, write to the Free Software | 16 | * along with this program; if not, write to the Free Software |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #include <linux/spinlock.h> | 23 | #include <linux/spinlock.h> |
24 | #include <linux/printk.h> | 24 | #include <linux/printk.h> |
25 | #include <linux/ioport.h> | 25 | #include <linux/ioport.h> |
26 | #include <linux/io.h> | 26 | #include <linux/io.h> |
27 | #include <linux/clk.h> | 27 | #include <linux/clk.h> |
28 | #include <linux/leds.h> | 28 | #include <linux/leds.h> |
29 | #include <linux/platform_data/leds-renesas-tpu.h> | 29 | #include <linux/platform_data/leds-renesas-tpu.h> |
30 | #include <linux/gpio.h> | 30 | #include <linux/gpio.h> |
31 | #include <linux/err.h> | 31 | #include <linux/err.h> |
32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/pm_runtime.h> | 33 | #include <linux/pm_runtime.h> |
34 | #include <linux/workqueue.h> | 34 | #include <linux/workqueue.h> |
35 | 35 | ||
36 | enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN }; | 36 | enum r_tpu_pin { R_TPU_PIN_UNUSED, R_TPU_PIN_GPIO, R_TPU_PIN_GPIO_FN }; |
37 | enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON }; | 37 | enum r_tpu_timer { R_TPU_TIMER_UNUSED, R_TPU_TIMER_ON }; |
38 | 38 | ||
39 | struct r_tpu_priv { | 39 | struct r_tpu_priv { |
40 | struct led_classdev ldev; | 40 | struct led_classdev ldev; |
41 | void __iomem *mapbase; | 41 | void __iomem *mapbase; |
42 | struct clk *clk; | 42 | struct clk *clk; |
43 | struct platform_device *pdev; | 43 | struct platform_device *pdev; |
44 | enum r_tpu_pin pin_state; | 44 | enum r_tpu_pin pin_state; |
45 | enum r_tpu_timer timer_state; | 45 | enum r_tpu_timer timer_state; |
46 | unsigned long min_rate; | 46 | unsigned long min_rate; |
47 | unsigned int refresh_rate; | 47 | unsigned int refresh_rate; |
48 | struct work_struct work; | 48 | struct work_struct work; |
49 | enum led_brightness new_brightness; | 49 | enum led_brightness new_brightness; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | static DEFINE_SPINLOCK(r_tpu_lock); | 52 | static DEFINE_SPINLOCK(r_tpu_lock); |
53 | 53 | ||
54 | #define TSTR -1 /* Timer start register (shared register) */ | 54 | #define TSTR -1 /* Timer start register (shared register) */ |
55 | #define TCR 0 /* Timer control register (+0x00) */ | 55 | #define TCR 0 /* Timer control register (+0x00) */ |
56 | #define TMDR 1 /* Timer mode register (+0x04) */ | 56 | #define TMDR 1 /* Timer mode register (+0x04) */ |
57 | #define TIOR 2 /* Timer I/O control register (+0x08) */ | 57 | #define TIOR 2 /* Timer I/O control register (+0x08) */ |
58 | #define TIER 3 /* Timer interrupt enable register (+0x0c) */ | 58 | #define TIER 3 /* Timer interrupt enable register (+0x0c) */ |
59 | #define TSR 4 /* Timer status register (+0x10) */ | 59 | #define TSR 4 /* Timer status register (+0x10) */ |
60 | #define TCNT 5 /* Timer counter (+0x14) */ | 60 | #define TCNT 5 /* Timer counter (+0x14) */ |
61 | #define TGRA 6 /* Timer general register A (+0x18) */ | 61 | #define TGRA 6 /* Timer general register A (+0x18) */ |
62 | #define TGRB 7 /* Timer general register B (+0x1c) */ | 62 | #define TGRB 7 /* Timer general register B (+0x1c) */ |
63 | #define TGRC 8 /* Timer general register C (+0x20) */ | 63 | #define TGRC 8 /* Timer general register C (+0x20) */ |
64 | #define TGRD 9 /* Timer general register D (+0x24) */ | 64 | #define TGRD 9 /* Timer general register D (+0x24) */ |
65 | 65 | ||
66 | static inline u16 r_tpu_read(struct r_tpu_priv *p, int reg_nr) | 66 | static inline u16 r_tpu_read(struct r_tpu_priv *p, int reg_nr) |
67 | { | 67 | { |
68 | struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; | 68 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&p->pdev->dev); |
69 | void __iomem *base = p->mapbase; | 69 | void __iomem *base = p->mapbase; |
70 | unsigned long offs = reg_nr << 2; | 70 | unsigned long offs = reg_nr << 2; |
71 | 71 | ||
72 | if (reg_nr == TSTR) | 72 | if (reg_nr == TSTR) |
73 | return ioread16(base - cfg->channel_offset); | 73 | return ioread16(base - cfg->channel_offset); |
74 | 74 | ||
75 | return ioread16(base + offs); | 75 | return ioread16(base + offs); |
76 | } | 76 | } |
77 | 77 | ||
78 | static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr, u16 value) | 78 | static inline void r_tpu_write(struct r_tpu_priv *p, int reg_nr, u16 value) |
79 | { | 79 | { |
80 | struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; | 80 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&p->pdev->dev); |
81 | void __iomem *base = p->mapbase; | 81 | void __iomem *base = p->mapbase; |
82 | unsigned long offs = reg_nr << 2; | 82 | unsigned long offs = reg_nr << 2; |
83 | 83 | ||
84 | if (reg_nr == TSTR) { | 84 | if (reg_nr == TSTR) { |
85 | iowrite16(value, base - cfg->channel_offset); | 85 | iowrite16(value, base - cfg->channel_offset); |
86 | return; | 86 | return; |
87 | } | 87 | } |
88 | 88 | ||
89 | iowrite16(value, base + offs); | 89 | iowrite16(value, base + offs); |
90 | } | 90 | } |
91 | 91 | ||
92 | static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start) | 92 | static void r_tpu_start_stop_ch(struct r_tpu_priv *p, int start) |
93 | { | 93 | { |
94 | struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; | 94 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&p->pdev->dev); |
95 | unsigned long flags; | 95 | unsigned long flags; |
96 | u16 value; | 96 | u16 value; |
97 | 97 | ||
98 | /* start stop register shared by multiple timer channels */ | 98 | /* start stop register shared by multiple timer channels */ |
99 | spin_lock_irqsave(&r_tpu_lock, flags); | 99 | spin_lock_irqsave(&r_tpu_lock, flags); |
100 | value = r_tpu_read(p, TSTR); | 100 | value = r_tpu_read(p, TSTR); |
101 | 101 | ||
102 | if (start) | 102 | if (start) |
103 | value |= 1 << cfg->timer_bit; | 103 | value |= 1 << cfg->timer_bit; |
104 | else | 104 | else |
105 | value &= ~(1 << cfg->timer_bit); | 105 | value &= ~(1 << cfg->timer_bit); |
106 | 106 | ||
107 | r_tpu_write(p, TSTR, value); | 107 | r_tpu_write(p, TSTR, value); |
108 | spin_unlock_irqrestore(&r_tpu_lock, flags); | 108 | spin_unlock_irqrestore(&r_tpu_lock, flags); |
109 | } | 109 | } |
110 | 110 | ||
111 | static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness) | 111 | static int r_tpu_enable(struct r_tpu_priv *p, enum led_brightness brightness) |
112 | { | 112 | { |
113 | struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; | 113 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&p->pdev->dev); |
114 | int prescaler[] = { 1, 4, 16, 64 }; | 114 | int prescaler[] = { 1, 4, 16, 64 }; |
115 | int k, ret; | 115 | int k, ret; |
116 | unsigned long rate, tmp; | 116 | unsigned long rate, tmp; |
117 | 117 | ||
118 | if (p->timer_state == R_TPU_TIMER_ON) | 118 | if (p->timer_state == R_TPU_TIMER_ON) |
119 | return 0; | 119 | return 0; |
120 | 120 | ||
121 | /* wake up device and enable clock */ | 121 | /* wake up device and enable clock */ |
122 | pm_runtime_get_sync(&p->pdev->dev); | 122 | pm_runtime_get_sync(&p->pdev->dev); |
123 | ret = clk_enable(p->clk); | 123 | ret = clk_enable(p->clk); |
124 | if (ret) { | 124 | if (ret) { |
125 | dev_err(&p->pdev->dev, "cannot enable clock\n"); | 125 | dev_err(&p->pdev->dev, "cannot enable clock\n"); |
126 | return ret; | 126 | return ret; |
127 | } | 127 | } |
128 | 128 | ||
129 | /* make sure channel is disabled */ | 129 | /* make sure channel is disabled */ |
130 | r_tpu_start_stop_ch(p, 0); | 130 | r_tpu_start_stop_ch(p, 0); |
131 | 131 | ||
132 | /* get clock rate after enabling it */ | 132 | /* get clock rate after enabling it */ |
133 | rate = clk_get_rate(p->clk); | 133 | rate = clk_get_rate(p->clk); |
134 | 134 | ||
135 | /* pick the lowest acceptable rate */ | 135 | /* pick the lowest acceptable rate */ |
136 | for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--) | 136 | for (k = ARRAY_SIZE(prescaler) - 1; k >= 0; k--) |
137 | if ((rate / prescaler[k]) >= p->min_rate) | 137 | if ((rate / prescaler[k]) >= p->min_rate) |
138 | break; | 138 | break; |
139 | 139 | ||
140 | if (k < 0) { | 140 | if (k < 0) { |
141 | dev_err(&p->pdev->dev, "clock rate mismatch\n"); | 141 | dev_err(&p->pdev->dev, "clock rate mismatch\n"); |
142 | goto err0; | 142 | goto err0; |
143 | } | 143 | } |
144 | dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n", | 144 | dev_dbg(&p->pdev->dev, "rate = %lu, prescaler %u\n", |
145 | rate, prescaler[k]); | 145 | rate, prescaler[k]); |
146 | 146 | ||
147 | /* clear TCNT on TGRB match, count on rising edge, set prescaler */ | 147 | /* clear TCNT on TGRB match, count on rising edge, set prescaler */ |
148 | r_tpu_write(p, TCR, 0x0040 | k); | 148 | r_tpu_write(p, TCR, 0x0040 | k); |
149 | 149 | ||
150 | /* output 0 until TGRA, output 1 until TGRB */ | 150 | /* output 0 until TGRA, output 1 until TGRB */ |
151 | r_tpu_write(p, TIOR, 0x0002); | 151 | r_tpu_write(p, TIOR, 0x0002); |
152 | 152 | ||
153 | rate /= prescaler[k] * p->refresh_rate; | 153 | rate /= prescaler[k] * p->refresh_rate; |
154 | r_tpu_write(p, TGRB, rate); | 154 | r_tpu_write(p, TGRB, rate); |
155 | dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate); | 155 | dev_dbg(&p->pdev->dev, "TRGB = 0x%04lx\n", rate); |
156 | 156 | ||
157 | tmp = (cfg->max_brightness - brightness) * rate; | 157 | tmp = (cfg->max_brightness - brightness) * rate; |
158 | r_tpu_write(p, TGRA, tmp / cfg->max_brightness); | 158 | r_tpu_write(p, TGRA, tmp / cfg->max_brightness); |
159 | dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness); | 159 | dev_dbg(&p->pdev->dev, "TRGA = 0x%04lx\n", tmp / cfg->max_brightness); |
160 | 160 | ||
161 | /* PWM mode */ | 161 | /* PWM mode */ |
162 | r_tpu_write(p, TMDR, 0x0002); | 162 | r_tpu_write(p, TMDR, 0x0002); |
163 | 163 | ||
164 | /* enable channel */ | 164 | /* enable channel */ |
165 | r_tpu_start_stop_ch(p, 1); | 165 | r_tpu_start_stop_ch(p, 1); |
166 | 166 | ||
167 | p->timer_state = R_TPU_TIMER_ON; | 167 | p->timer_state = R_TPU_TIMER_ON; |
168 | return 0; | 168 | return 0; |
169 | err0: | 169 | err0: |
170 | clk_disable(p->clk); | 170 | clk_disable(p->clk); |
171 | pm_runtime_put_sync(&p->pdev->dev); | 171 | pm_runtime_put_sync(&p->pdev->dev); |
172 | return -ENOTSUPP; | 172 | return -ENOTSUPP; |
173 | } | 173 | } |
174 | 174 | ||
175 | static void r_tpu_disable(struct r_tpu_priv *p) | 175 | static void r_tpu_disable(struct r_tpu_priv *p) |
176 | { | 176 | { |
177 | if (p->timer_state == R_TPU_TIMER_UNUSED) | 177 | if (p->timer_state == R_TPU_TIMER_UNUSED) |
178 | return; | 178 | return; |
179 | 179 | ||
180 | /* disable channel */ | 180 | /* disable channel */ |
181 | r_tpu_start_stop_ch(p, 0); | 181 | r_tpu_start_stop_ch(p, 0); |
182 | 182 | ||
183 | /* stop clock and mark device as idle */ | 183 | /* stop clock and mark device as idle */ |
184 | clk_disable(p->clk); | 184 | clk_disable(p->clk); |
185 | pm_runtime_put_sync(&p->pdev->dev); | 185 | pm_runtime_put_sync(&p->pdev->dev); |
186 | 186 | ||
187 | p->timer_state = R_TPU_TIMER_UNUSED; | 187 | p->timer_state = R_TPU_TIMER_UNUSED; |
188 | } | 188 | } |
189 | 189 | ||
190 | static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state, | 190 | static void r_tpu_set_pin(struct r_tpu_priv *p, enum r_tpu_pin new_state, |
191 | enum led_brightness brightness) | 191 | enum led_brightness brightness) |
192 | { | 192 | { |
193 | struct led_renesas_tpu_config *cfg = p->pdev->dev.platform_data; | 193 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&p->pdev->dev); |
194 | 194 | ||
195 | if (p->pin_state == new_state) { | 195 | if (p->pin_state == new_state) { |
196 | if (p->pin_state == R_TPU_PIN_GPIO) | 196 | if (p->pin_state == R_TPU_PIN_GPIO) |
197 | gpio_set_value(cfg->pin_gpio, brightness); | 197 | gpio_set_value(cfg->pin_gpio, brightness); |
198 | return; | 198 | return; |
199 | } | 199 | } |
200 | 200 | ||
201 | if (p->pin_state == R_TPU_PIN_GPIO) | 201 | if (p->pin_state == R_TPU_PIN_GPIO) |
202 | gpio_free(cfg->pin_gpio); | 202 | gpio_free(cfg->pin_gpio); |
203 | 203 | ||
204 | if (p->pin_state == R_TPU_PIN_GPIO_FN) | 204 | if (p->pin_state == R_TPU_PIN_GPIO_FN) |
205 | gpio_free(cfg->pin_gpio_fn); | 205 | gpio_free(cfg->pin_gpio_fn); |
206 | 206 | ||
207 | if (new_state == R_TPU_PIN_GPIO) | 207 | if (new_state == R_TPU_PIN_GPIO) |
208 | gpio_request_one(cfg->pin_gpio, !!brightness ? | 208 | gpio_request_one(cfg->pin_gpio, !!brightness ? |
209 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, | 209 | GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, |
210 | cfg->name); | 210 | cfg->name); |
211 | 211 | ||
212 | if (new_state == R_TPU_PIN_GPIO_FN) | 212 | if (new_state == R_TPU_PIN_GPIO_FN) |
213 | gpio_request(cfg->pin_gpio_fn, cfg->name); | 213 | gpio_request(cfg->pin_gpio_fn, cfg->name); |
214 | 214 | ||
215 | p->pin_state = new_state; | 215 | p->pin_state = new_state; |
216 | } | 216 | } |
217 | 217 | ||
218 | static void r_tpu_work(struct work_struct *work) | 218 | static void r_tpu_work(struct work_struct *work) |
219 | { | 219 | { |
220 | struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work); | 220 | struct r_tpu_priv *p = container_of(work, struct r_tpu_priv, work); |
221 | enum led_brightness brightness = p->new_brightness; | 221 | enum led_brightness brightness = p->new_brightness; |
222 | 222 | ||
223 | r_tpu_disable(p); | 223 | r_tpu_disable(p); |
224 | 224 | ||
225 | /* off and maximum are handled as GPIO pins, in between PWM */ | 225 | /* off and maximum are handled as GPIO pins, in between PWM */ |
226 | if ((brightness == 0) || (brightness == p->ldev.max_brightness)) | 226 | if ((brightness == 0) || (brightness == p->ldev.max_brightness)) |
227 | r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness); | 227 | r_tpu_set_pin(p, R_TPU_PIN_GPIO, brightness); |
228 | else { | 228 | else { |
229 | r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0); | 229 | r_tpu_set_pin(p, R_TPU_PIN_GPIO_FN, 0); |
230 | r_tpu_enable(p, brightness); | 230 | r_tpu_enable(p, brightness); |
231 | } | 231 | } |
232 | } | 232 | } |
233 | 233 | ||
234 | static void r_tpu_set_brightness(struct led_classdev *ldev, | 234 | static void r_tpu_set_brightness(struct led_classdev *ldev, |
235 | enum led_brightness brightness) | 235 | enum led_brightness brightness) |
236 | { | 236 | { |
237 | struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev); | 237 | struct r_tpu_priv *p = container_of(ldev, struct r_tpu_priv, ldev); |
238 | p->new_brightness = brightness; | 238 | p->new_brightness = brightness; |
239 | schedule_work(&p->work); | 239 | schedule_work(&p->work); |
240 | } | 240 | } |
241 | 241 | ||
242 | static int r_tpu_probe(struct platform_device *pdev) | 242 | static int r_tpu_probe(struct platform_device *pdev) |
243 | { | 243 | { |
244 | struct led_renesas_tpu_config *cfg = pdev->dev.platform_data; | 244 | struct led_renesas_tpu_config *cfg = dev_get_platdata(&pdev->dev); |
245 | struct r_tpu_priv *p; | 245 | struct r_tpu_priv *p; |
246 | struct resource *res; | 246 | struct resource *res; |
247 | int ret; | 247 | int ret; |
248 | 248 | ||
249 | if (!cfg) { | 249 | if (!cfg) { |
250 | dev_err(&pdev->dev, "missing platform data\n"); | 250 | dev_err(&pdev->dev, "missing platform data\n"); |
251 | return -ENODEV; | 251 | return -ENODEV; |
252 | } | 252 | } |
253 | 253 | ||
254 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); | 254 | p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL); |
255 | if (p == NULL) { | 255 | if (p == NULL) { |
256 | dev_err(&pdev->dev, "failed to allocate driver data\n"); | 256 | dev_err(&pdev->dev, "failed to allocate driver data\n"); |
257 | return -ENOMEM; | 257 | return -ENOMEM; |
258 | } | 258 | } |
259 | 259 | ||
260 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 260 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
261 | if (!res) { | 261 | if (!res) { |
262 | dev_err(&pdev->dev, "failed to get I/O memory\n"); | 262 | dev_err(&pdev->dev, "failed to get I/O memory\n"); |
263 | return -ENXIO; | 263 | return -ENXIO; |
264 | } | 264 | } |
265 | 265 | ||
266 | /* map memory, let mapbase point to our channel */ | 266 | /* map memory, let mapbase point to our channel */ |
267 | p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start, | 267 | p->mapbase = devm_ioremap_nocache(&pdev->dev, res->start, |
268 | resource_size(res)); | 268 | resource_size(res)); |
269 | if (p->mapbase == NULL) { | 269 | if (p->mapbase == NULL) { |
270 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); | 270 | dev_err(&pdev->dev, "failed to remap I/O memory\n"); |
271 | return -ENXIO; | 271 | return -ENXIO; |
272 | } | 272 | } |
273 | 273 | ||
274 | /* get hold of clock */ | 274 | /* get hold of clock */ |
275 | p->clk = devm_clk_get(&pdev->dev, NULL); | 275 | p->clk = devm_clk_get(&pdev->dev, NULL); |
276 | if (IS_ERR(p->clk)) { | 276 | if (IS_ERR(p->clk)) { |
277 | dev_err(&pdev->dev, "cannot get clock\n"); | 277 | dev_err(&pdev->dev, "cannot get clock\n"); |
278 | return PTR_ERR(p->clk); | 278 | return PTR_ERR(p->clk); |
279 | } | 279 | } |
280 | 280 | ||
281 | p->pdev = pdev; | 281 | p->pdev = pdev; |
282 | p->pin_state = R_TPU_PIN_UNUSED; | 282 | p->pin_state = R_TPU_PIN_UNUSED; |
283 | p->timer_state = R_TPU_TIMER_UNUSED; | 283 | p->timer_state = R_TPU_TIMER_UNUSED; |
284 | p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100; | 284 | p->refresh_rate = cfg->refresh_rate ? cfg->refresh_rate : 100; |
285 | r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF); | 285 | r_tpu_set_pin(p, R_TPU_PIN_GPIO, LED_OFF); |
286 | platform_set_drvdata(pdev, p); | 286 | platform_set_drvdata(pdev, p); |
287 | 287 | ||
288 | INIT_WORK(&p->work, r_tpu_work); | 288 | INIT_WORK(&p->work, r_tpu_work); |
289 | 289 | ||
290 | p->ldev.name = cfg->name; | 290 | p->ldev.name = cfg->name; |
291 | p->ldev.brightness = LED_OFF; | 291 | p->ldev.brightness = LED_OFF; |
292 | p->ldev.max_brightness = cfg->max_brightness; | 292 | p->ldev.max_brightness = cfg->max_brightness; |
293 | p->ldev.brightness_set = r_tpu_set_brightness; | 293 | p->ldev.brightness_set = r_tpu_set_brightness; |
294 | p->ldev.flags |= LED_CORE_SUSPENDRESUME; | 294 | p->ldev.flags |= LED_CORE_SUSPENDRESUME; |
295 | ret = led_classdev_register(&pdev->dev, &p->ldev); | 295 | ret = led_classdev_register(&pdev->dev, &p->ldev); |
296 | if (ret < 0) | 296 | if (ret < 0) |
297 | goto err0; | 297 | goto err0; |
298 | 298 | ||
299 | /* max_brightness may be updated by the LED core code */ | 299 | /* max_brightness may be updated by the LED core code */ |
300 | p->min_rate = p->ldev.max_brightness * p->refresh_rate; | 300 | p->min_rate = p->ldev.max_brightness * p->refresh_rate; |
301 | 301 | ||
302 | pm_runtime_enable(&pdev->dev); | 302 | pm_runtime_enable(&pdev->dev); |
303 | return 0; | 303 | return 0; |
304 | 304 | ||
305 | err0: | 305 | err0: |
306 | r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); | 306 | r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); |
307 | return ret; | 307 | return ret; |
308 | } | 308 | } |
309 | 309 | ||
310 | static int r_tpu_remove(struct platform_device *pdev) | 310 | static int r_tpu_remove(struct platform_device *pdev) |
311 | { | 311 | { |
312 | struct r_tpu_priv *p = platform_get_drvdata(pdev); | 312 | struct r_tpu_priv *p = platform_get_drvdata(pdev); |
313 | 313 | ||
314 | r_tpu_set_brightness(&p->ldev, LED_OFF); | 314 | r_tpu_set_brightness(&p->ldev, LED_OFF); |
315 | led_classdev_unregister(&p->ldev); | 315 | led_classdev_unregister(&p->ldev); |
316 | cancel_work_sync(&p->work); | 316 | cancel_work_sync(&p->work); |
317 | r_tpu_disable(p); | 317 | r_tpu_disable(p); |
318 | r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); | 318 | r_tpu_set_pin(p, R_TPU_PIN_UNUSED, LED_OFF); |
319 | 319 | ||
320 | pm_runtime_disable(&pdev->dev); | 320 | pm_runtime_disable(&pdev->dev); |
321 | 321 | ||
322 | return 0; | 322 | return 0; |
323 | } | 323 | } |
324 | 324 | ||
325 | static struct platform_driver r_tpu_device_driver = { | 325 | static struct platform_driver r_tpu_device_driver = { |
326 | .probe = r_tpu_probe, | 326 | .probe = r_tpu_probe, |
327 | .remove = r_tpu_remove, | 327 | .remove = r_tpu_remove, |
328 | .driver = { | 328 | .driver = { |
329 | .name = "leds-renesas-tpu", | 329 | .name = "leds-renesas-tpu", |
330 | } | 330 | } |
331 | }; | 331 | }; |
332 | 332 | ||
333 | module_platform_driver(r_tpu_device_driver); | 333 | module_platform_driver(r_tpu_device_driver); |
334 | 334 | ||
335 | MODULE_AUTHOR("Magnus Damm"); | 335 | MODULE_AUTHOR("Magnus Damm"); |
336 | MODULE_DESCRIPTION("Renesas TPU LED Driver"); | 336 | MODULE_DESCRIPTION("Renesas TPU LED Driver"); |
337 | MODULE_LICENSE("GPL v2"); | 337 | MODULE_LICENSE("GPL v2"); |
338 | 338 |
drivers/leds/leds-s3c24xx.c
1 | /* drivers/leds/leds-s3c24xx.c | 1 | /* drivers/leds/leds-s3c24xx.c |
2 | * | 2 | * |
3 | * (c) 2006 Simtec Electronics | 3 | * (c) 2006 Simtec Electronics |
4 | * http://armlinux.simtec.co.uk/ | 4 | * http://armlinux.simtec.co.uk/ |
5 | * Ben Dooks <ben@simtec.co.uk> | 5 | * Ben Dooks <ben@simtec.co.uk> |
6 | * | 6 | * |
7 | * S3C24XX - LEDs GPIO driver | 7 | * S3C24XX - LEDs GPIO driver |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/leds.h> | 17 | #include <linux/leds.h> |
18 | #include <linux/gpio.h> | 18 | #include <linux/gpio.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | 21 | ||
22 | #include <mach/hardware.h> | 22 | #include <mach/hardware.h> |
23 | #include <mach/regs-gpio.h> | 23 | #include <mach/regs-gpio.h> |
24 | #include <linux/platform_data/leds-s3c24xx.h> | 24 | #include <linux/platform_data/leds-s3c24xx.h> |
25 | 25 | ||
26 | /* our context */ | 26 | /* our context */ |
27 | 27 | ||
28 | struct s3c24xx_gpio_led { | 28 | struct s3c24xx_gpio_led { |
29 | struct led_classdev cdev; | 29 | struct led_classdev cdev; |
30 | struct s3c24xx_led_platdata *pdata; | 30 | struct s3c24xx_led_platdata *pdata; |
31 | }; | 31 | }; |
32 | 32 | ||
33 | static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev) | 33 | static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev) |
34 | { | 34 | { |
35 | return platform_get_drvdata(dev); | 35 | return platform_get_drvdata(dev); |
36 | } | 36 | } |
37 | 37 | ||
38 | static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev) | 38 | static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev) |
39 | { | 39 | { |
40 | return container_of(led_cdev, struct s3c24xx_gpio_led, cdev); | 40 | return container_of(led_cdev, struct s3c24xx_gpio_led, cdev); |
41 | } | 41 | } |
42 | 42 | ||
43 | static void s3c24xx_led_set(struct led_classdev *led_cdev, | 43 | static void s3c24xx_led_set(struct led_classdev *led_cdev, |
44 | enum led_brightness value) | 44 | enum led_brightness value) |
45 | { | 45 | { |
46 | struct s3c24xx_gpio_led *led = to_gpio(led_cdev); | 46 | struct s3c24xx_gpio_led *led = to_gpio(led_cdev); |
47 | struct s3c24xx_led_platdata *pd = led->pdata; | 47 | struct s3c24xx_led_platdata *pd = led->pdata; |
48 | int state = (value ? 1 : 0) ^ (pd->flags & S3C24XX_LEDF_ACTLOW); | 48 | int state = (value ? 1 : 0) ^ (pd->flags & S3C24XX_LEDF_ACTLOW); |
49 | 49 | ||
50 | /* there will be a short delay between setting the output and | 50 | /* there will be a short delay between setting the output and |
51 | * going from output to input when using tristate. */ | 51 | * going from output to input when using tristate. */ |
52 | 52 | ||
53 | gpio_set_value(pd->gpio, state); | 53 | gpio_set_value(pd->gpio, state); |
54 | 54 | ||
55 | if (pd->flags & S3C24XX_LEDF_TRISTATE) { | 55 | if (pd->flags & S3C24XX_LEDF_TRISTATE) { |
56 | if (value) | 56 | if (value) |
57 | gpio_direction_output(pd->gpio, state); | 57 | gpio_direction_output(pd->gpio, state); |
58 | else | 58 | else |
59 | gpio_direction_input(pd->gpio); | 59 | gpio_direction_input(pd->gpio); |
60 | } | 60 | } |
61 | } | 61 | } |
62 | 62 | ||
63 | static int s3c24xx_led_remove(struct platform_device *dev) | 63 | static int s3c24xx_led_remove(struct platform_device *dev) |
64 | { | 64 | { |
65 | struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); | 65 | struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); |
66 | 66 | ||
67 | led_classdev_unregister(&led->cdev); | 67 | led_classdev_unregister(&led->cdev); |
68 | 68 | ||
69 | return 0; | 69 | return 0; |
70 | } | 70 | } |
71 | 71 | ||
72 | static int s3c24xx_led_probe(struct platform_device *dev) | 72 | static int s3c24xx_led_probe(struct platform_device *dev) |
73 | { | 73 | { |
74 | struct s3c24xx_led_platdata *pdata = dev->dev.platform_data; | 74 | struct s3c24xx_led_platdata *pdata = dev_get_platdata(&dev->dev); |
75 | struct s3c24xx_gpio_led *led; | 75 | struct s3c24xx_gpio_led *led; |
76 | int ret; | 76 | int ret; |
77 | 77 | ||
78 | led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led), | 78 | led = devm_kzalloc(&dev->dev, sizeof(struct s3c24xx_gpio_led), |
79 | GFP_KERNEL); | 79 | GFP_KERNEL); |
80 | if (led == NULL) { | 80 | if (led == NULL) { |
81 | dev_err(&dev->dev, "No memory for device\n"); | 81 | dev_err(&dev->dev, "No memory for device\n"); |
82 | return -ENOMEM; | 82 | return -ENOMEM; |
83 | } | 83 | } |
84 | 84 | ||
85 | platform_set_drvdata(dev, led); | 85 | platform_set_drvdata(dev, led); |
86 | 86 | ||
87 | led->cdev.brightness_set = s3c24xx_led_set; | 87 | led->cdev.brightness_set = s3c24xx_led_set; |
88 | led->cdev.default_trigger = pdata->def_trigger; | 88 | led->cdev.default_trigger = pdata->def_trigger; |
89 | led->cdev.name = pdata->name; | 89 | led->cdev.name = pdata->name; |
90 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | 90 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; |
91 | 91 | ||
92 | led->pdata = pdata; | 92 | led->pdata = pdata; |
93 | 93 | ||
94 | ret = devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED"); | 94 | ret = devm_gpio_request(&dev->dev, pdata->gpio, "S3C24XX_LED"); |
95 | if (ret < 0) | 95 | if (ret < 0) |
96 | return ret; | 96 | return ret; |
97 | 97 | ||
98 | /* no point in having a pull-up if we are always driving */ | 98 | /* no point in having a pull-up if we are always driving */ |
99 | 99 | ||
100 | s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE); | 100 | s3c_gpio_setpull(pdata->gpio, S3C_GPIO_PULL_NONE); |
101 | 101 | ||
102 | if (pdata->flags & S3C24XX_LEDF_TRISTATE) | 102 | if (pdata->flags & S3C24XX_LEDF_TRISTATE) |
103 | gpio_direction_input(pdata->gpio); | 103 | gpio_direction_input(pdata->gpio); |
104 | else | 104 | else |
105 | gpio_direction_output(pdata->gpio, | 105 | gpio_direction_output(pdata->gpio, |
106 | pdata->flags & S3C24XX_LEDF_ACTLOW ? 1 : 0); | 106 | pdata->flags & S3C24XX_LEDF_ACTLOW ? 1 : 0); |
107 | 107 | ||
108 | /* register our new led device */ | 108 | /* register our new led device */ |
109 | 109 | ||
110 | ret = led_classdev_register(&dev->dev, &led->cdev); | 110 | ret = led_classdev_register(&dev->dev, &led->cdev); |
111 | if (ret < 0) | 111 | if (ret < 0) |
112 | dev_err(&dev->dev, "led_classdev_register failed\n"); | 112 | dev_err(&dev->dev, "led_classdev_register failed\n"); |
113 | 113 | ||
114 | return ret; | 114 | return ret; |
115 | } | 115 | } |
116 | 116 | ||
117 | static struct platform_driver s3c24xx_led_driver = { | 117 | static struct platform_driver s3c24xx_led_driver = { |
118 | .probe = s3c24xx_led_probe, | 118 | .probe = s3c24xx_led_probe, |
119 | .remove = s3c24xx_led_remove, | 119 | .remove = s3c24xx_led_remove, |
120 | .driver = { | 120 | .driver = { |
121 | .name = "s3c24xx_led", | 121 | .name = "s3c24xx_led", |
122 | .owner = THIS_MODULE, | 122 | .owner = THIS_MODULE, |
123 | }, | 123 | }, |
124 | }; | 124 | }; |
125 | 125 | ||
126 | module_platform_driver(s3c24xx_led_driver); | 126 | module_platform_driver(s3c24xx_led_driver); |
127 | 127 | ||
128 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); | 128 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); |
129 | MODULE_DESCRIPTION("S3C24XX LED driver"); | 129 | MODULE_DESCRIPTION("S3C24XX LED driver"); |
130 | MODULE_LICENSE("GPL"); | 130 | MODULE_LICENSE("GPL"); |
131 | MODULE_ALIAS("platform:s3c24xx_led"); | 131 | MODULE_ALIAS("platform:s3c24xx_led"); |
132 | 132 |
drivers/leds/leds-tca6507.c
1 | /* | 1 | /* |
2 | * leds-tca6507 | 2 | * leds-tca6507 |
3 | * | 3 | * |
4 | * The TCA6507 is a programmable LED controller that can drive 7 | 4 | * The TCA6507 is a programmable LED controller that can drive 7 |
5 | * separate lines either by holding them low, or by pulsing them | 5 | * separate lines either by holding them low, or by pulsing them |
6 | * with modulated width. | 6 | * with modulated width. |
7 | * The modulation can be varied in a simple pattern to produce a blink or | 7 | * The modulation can be varied in a simple pattern to produce a blink or |
8 | * double-blink. | 8 | * double-blink. |
9 | * | 9 | * |
10 | * This driver can configure each line either as a 'GPIO' which is out-only | 10 | * This driver can configure each line either as a 'GPIO' which is out-only |
11 | * (no pull-up) or as an LED with variable brightness and hardware-assisted | 11 | * (no pull-up) or as an LED with variable brightness and hardware-assisted |
12 | * blinking. | 12 | * blinking. |
13 | * | 13 | * |
14 | * Apart from OFF and ON there are three programmable brightness levels which | 14 | * Apart from OFF and ON there are three programmable brightness levels which |
15 | * can be programmed from 0 to 15 and indicate how many 500usec intervals in | 15 | * can be programmed from 0 to 15 and indicate how many 500usec intervals in |
16 | * each 8msec that the led is 'on'. The levels are named MASTER, BANK0 and | 16 | * each 8msec that the led is 'on'. The levels are named MASTER, BANK0 and |
17 | * BANK1. | 17 | * BANK1. |
18 | * | 18 | * |
19 | * There are two different blink rates that can be programmed, each with | 19 | * There are two different blink rates that can be programmed, each with |
20 | * separate time for rise, on, fall, off and second-off. Thus if 3 or more | 20 | * separate time for rise, on, fall, off and second-off. Thus if 3 or more |
21 | * different non-trivial rates are required, software must be used for the extra | 21 | * different non-trivial rates are required, software must be used for the extra |
22 | * rates. The two different blink rates must align with the two levels BANK0 and | 22 | * rates. The two different blink rates must align with the two levels BANK0 and |
23 | * BANK1. | 23 | * BANK1. |
24 | * This driver does not support double-blink so 'second-off' always matches | 24 | * This driver does not support double-blink so 'second-off' always matches |
25 | * 'off'. | 25 | * 'off'. |
26 | * | 26 | * |
27 | * Only 16 different times can be programmed in a roughly logarithmic scale from | 27 | * Only 16 different times can be programmed in a roughly logarithmic scale from |
28 | * 64ms to 16320ms. To be precise the possible times are: | 28 | * 64ms to 16320ms. To be precise the possible times are: |
29 | * 0, 64, 128, 192, 256, 384, 512, 768, | 29 | * 0, 64, 128, 192, 256, 384, 512, 768, |
30 | * 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320 | 30 | * 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320 |
31 | * | 31 | * |
32 | * Times that cannot be closely matched with these must be | 32 | * Times that cannot be closely matched with these must be |
33 | * handled in software. This driver allows 12.5% error in matching. | 33 | * handled in software. This driver allows 12.5% error in matching. |
34 | * | 34 | * |
35 | * This driver does not allow rise/fall rates to be set explicitly. When trying | 35 | * This driver does not allow rise/fall rates to be set explicitly. When trying |
36 | * to match a given 'on' or 'off' period, an appropriate pair of 'change' and | 36 | * to match a given 'on' or 'off' period, an appropriate pair of 'change' and |
37 | * 'hold' times are chosen to get a close match. If the target delay is even, | 37 | * 'hold' times are chosen to get a close match. If the target delay is even, |
38 | * the 'change' number will be the smaller; if odd, the 'hold' number will be | 38 | * the 'change' number will be the smaller; if odd, the 'hold' number will be |
39 | * the smaller. | 39 | * the smaller. |
40 | 40 | ||
41 | * Choosing pairs of delays with 12.5% errors allows us to match delays in the | 41 | * Choosing pairs of delays with 12.5% errors allows us to match delays in the |
42 | * ranges: 56-72, 112-144, 168-216, 224-27504, 28560-36720. | 42 | * ranges: 56-72, 112-144, 168-216, 224-27504, 28560-36720. |
43 | * 26% of the achievable sums can be matched by multiple pairings. For example | 43 | * 26% of the achievable sums can be matched by multiple pairings. For example |
44 | * 1536 == 1536+0, 1024+512, or 768+768. This driver will always choose the | 44 | * 1536 == 1536+0, 1024+512, or 768+768. This driver will always choose the |
45 | * pairing with the least maximum - 768+768 in this case. Other pairings are | 45 | * pairing with the least maximum - 768+768 in this case. Other pairings are |
46 | * not available. | 46 | * not available. |
47 | * | 47 | * |
48 | * Access to the 3 levels and 2 blinks are on a first-come, first-served basis. | 48 | * Access to the 3 levels and 2 blinks are on a first-come, first-served basis. |
49 | * Access can be shared by multiple leds if they have the same level and | 49 | * Access can be shared by multiple leds if they have the same level and |
50 | * either same blink rates, or some don't blink. | 50 | * either same blink rates, or some don't blink. |
51 | * When a led changes, it relinquishes access and tries again, so it might | 51 | * When a led changes, it relinquishes access and tries again, so it might |
52 | * lose access to hardware blink. | 52 | * lose access to hardware blink. |
53 | * If a blink engine cannot be allocated, software blink is used. | 53 | * If a blink engine cannot be allocated, software blink is used. |
54 | * If the desired brightness cannot be allocated, the closest available non-zero | 54 | * If the desired brightness cannot be allocated, the closest available non-zero |
55 | * brightness is used. As 'full' is always available, the worst case would be | 55 | * brightness is used. As 'full' is always available, the worst case would be |
56 | * to have two different blink rates at '1', with Max at '2', then other leds | 56 | * to have two different blink rates at '1', with Max at '2', then other leds |
57 | * will have to choose between '2' and '16'. Hopefully this is not likely. | 57 | * will have to choose between '2' and '16'. Hopefully this is not likely. |
58 | * | 58 | * |
59 | * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the brightness | 59 | * Each bank (BANK0 and BANK1) has two usage counts - LEDs using the brightness |
60 | * and LEDs using the blink. It can only be reprogrammed when the appropriate | 60 | * and LEDs using the blink. It can only be reprogrammed when the appropriate |
61 | * counter is zero. The MASTER level has a single usage count. | 61 | * counter is zero. The MASTER level has a single usage count. |
62 | * | 62 | * |
63 | * Each Led has programmable 'on' and 'off' time as milliseconds. With each | 63 | * Each Led has programmable 'on' and 'off' time as milliseconds. With each |
64 | * there is a flag saying if it was explicitly requested or defaulted. | 64 | * there is a flag saying if it was explicitly requested or defaulted. |
65 | * Similarly the banks know if each time was explicit or a default. Defaults | 65 | * Similarly the banks know if each time was explicit or a default. Defaults |
66 | * are permitted to be changed freely - they are not recognised when matching. | 66 | * are permitted to be changed freely - they are not recognised when matching. |
67 | * | 67 | * |
68 | * | 68 | * |
69 | * An led-tca6507 device must be provided with platform data. This data | 69 | * An led-tca6507 device must be provided with platform data. This data |
70 | * lists for each output: the name, default trigger, and whether the signal | 70 | * lists for each output: the name, default trigger, and whether the signal |
71 | * is being used as a GPiO rather than an led. 'struct led_plaform_data' | 71 | * is being used as a GPiO rather than an led. 'struct led_plaform_data' |
72 | * is used for this. If 'name' is NULL, the output isn't used. If 'flags' | 72 | * is used for this. If 'name' is NULL, the output isn't used. If 'flags' |
73 | * is TCA6507_MAKE_CPIO, the output is a GPO. | 73 | * is TCA6507_MAKE_CPIO, the output is a GPO. |
74 | * The "struct led_platform_data" can be embedded in a | 74 | * The "struct led_platform_data" can be embedded in a |
75 | * "struct tca6507_platform_data" which adds a 'gpio_base' for the GPiOs, | 75 | * "struct tca6507_platform_data" which adds a 'gpio_base' for the GPiOs, |
76 | * and a 'setup' callback which is called once the GPiOs are available. | 76 | * and a 'setup' callback which is called once the GPiOs are available. |
77 | * | 77 | * |
78 | */ | 78 | */ |
79 | 79 | ||
80 | #include <linux/module.h> | 80 | #include <linux/module.h> |
81 | #include <linux/slab.h> | 81 | #include <linux/slab.h> |
82 | #include <linux/leds.h> | 82 | #include <linux/leds.h> |
83 | #include <linux/err.h> | 83 | #include <linux/err.h> |
84 | #include <linux/i2c.h> | 84 | #include <linux/i2c.h> |
85 | #include <linux/gpio.h> | 85 | #include <linux/gpio.h> |
86 | #include <linux/workqueue.h> | 86 | #include <linux/workqueue.h> |
87 | #include <linux/leds-tca6507.h> | 87 | #include <linux/leds-tca6507.h> |
88 | #include <linux/of.h> | 88 | #include <linux/of.h> |
89 | 89 | ||
90 | /* LED select registers determine the source that drives LED outputs */ | 90 | /* LED select registers determine the source that drives LED outputs */ |
91 | #define TCA6507_LS_LED_OFF 0x0 /* Output HI-Z (off) */ | 91 | #define TCA6507_LS_LED_OFF 0x0 /* Output HI-Z (off) */ |
92 | #define TCA6507_LS_LED_OFF1 0x1 /* Output HI-Z (off) - not used */ | 92 | #define TCA6507_LS_LED_OFF1 0x1 /* Output HI-Z (off) - not used */ |
93 | #define TCA6507_LS_LED_PWM0 0x2 /* Output LOW with Bank0 rate */ | 93 | #define TCA6507_LS_LED_PWM0 0x2 /* Output LOW with Bank0 rate */ |
94 | #define TCA6507_LS_LED_PWM1 0x3 /* Output LOW with Bank1 rate */ | 94 | #define TCA6507_LS_LED_PWM1 0x3 /* Output LOW with Bank1 rate */ |
95 | #define TCA6507_LS_LED_ON 0x4 /* Output LOW (on) */ | 95 | #define TCA6507_LS_LED_ON 0x4 /* Output LOW (on) */ |
96 | #define TCA6507_LS_LED_MIR 0x5 /* Output LOW with Master Intensity */ | 96 | #define TCA6507_LS_LED_MIR 0x5 /* Output LOW with Master Intensity */ |
97 | #define TCA6507_LS_BLINK0 0x6 /* Blink at Bank0 rate */ | 97 | #define TCA6507_LS_BLINK0 0x6 /* Blink at Bank0 rate */ |
98 | #define TCA6507_LS_BLINK1 0x7 /* Blink at Bank1 rate */ | 98 | #define TCA6507_LS_BLINK1 0x7 /* Blink at Bank1 rate */ |
99 | 99 | ||
100 | enum { | 100 | enum { |
101 | BANK0, | 101 | BANK0, |
102 | BANK1, | 102 | BANK1, |
103 | MASTER, | 103 | MASTER, |
104 | }; | 104 | }; |
105 | static int bank_source[3] = { | 105 | static int bank_source[3] = { |
106 | TCA6507_LS_LED_PWM0, | 106 | TCA6507_LS_LED_PWM0, |
107 | TCA6507_LS_LED_PWM1, | 107 | TCA6507_LS_LED_PWM1, |
108 | TCA6507_LS_LED_MIR, | 108 | TCA6507_LS_LED_MIR, |
109 | }; | 109 | }; |
110 | static int blink_source[2] = { | 110 | static int blink_source[2] = { |
111 | TCA6507_LS_BLINK0, | 111 | TCA6507_LS_BLINK0, |
112 | TCA6507_LS_BLINK1, | 112 | TCA6507_LS_BLINK1, |
113 | }; | 113 | }; |
114 | 114 | ||
115 | /* PWM registers */ | 115 | /* PWM registers */ |
116 | #define TCA6507_REG_CNT 11 | 116 | #define TCA6507_REG_CNT 11 |
117 | 117 | ||
118 | /* | 118 | /* |
119 | * 0x00, 0x01, 0x02 encode the TCA6507_LS_* values, each output | 119 | * 0x00, 0x01, 0x02 encode the TCA6507_LS_* values, each output |
120 | * owns one bit in each register | 120 | * owns one bit in each register |
121 | */ | 121 | */ |
122 | #define TCA6507_FADE_ON 0x03 | 122 | #define TCA6507_FADE_ON 0x03 |
123 | #define TCA6507_FULL_ON 0x04 | 123 | #define TCA6507_FULL_ON 0x04 |
124 | #define TCA6507_FADE_OFF 0x05 | 124 | #define TCA6507_FADE_OFF 0x05 |
125 | #define TCA6507_FIRST_OFF 0x06 | 125 | #define TCA6507_FIRST_OFF 0x06 |
126 | #define TCA6507_SECOND_OFF 0x07 | 126 | #define TCA6507_SECOND_OFF 0x07 |
127 | #define TCA6507_MAX_INTENSITY 0x08 | 127 | #define TCA6507_MAX_INTENSITY 0x08 |
128 | #define TCA6507_MASTER_INTENSITY 0x09 | 128 | #define TCA6507_MASTER_INTENSITY 0x09 |
129 | #define TCA6507_INITIALIZE 0x0A | 129 | #define TCA6507_INITIALIZE 0x0A |
130 | 130 | ||
131 | #define INIT_CODE 0x8 | 131 | #define INIT_CODE 0x8 |
132 | 132 | ||
133 | #define TIMECODES 16 | 133 | #define TIMECODES 16 |
134 | static int time_codes[TIMECODES] = { | 134 | static int time_codes[TIMECODES] = { |
135 | 0, 64, 128, 192, 256, 384, 512, 768, | 135 | 0, 64, 128, 192, 256, 384, 512, 768, |
136 | 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320 | 136 | 1024, 1536, 2048, 3072, 4096, 5760, 8128, 16320 |
137 | }; | 137 | }; |
138 | 138 | ||
139 | /* Convert an led.brightness level (0..255) to a TCA6507 level (0..15) */ | 139 | /* Convert an led.brightness level (0..255) to a TCA6507 level (0..15) */ |
140 | static inline int TO_LEVEL(int brightness) | 140 | static inline int TO_LEVEL(int brightness) |
141 | { | 141 | { |
142 | return brightness >> 4; | 142 | return brightness >> 4; |
143 | } | 143 | } |
144 | 144 | ||
145 | /* ...and convert back */ | 145 | /* ...and convert back */ |
146 | static inline int TO_BRIGHT(int level) | 146 | static inline int TO_BRIGHT(int level) |
147 | { | 147 | { |
148 | if (level) | 148 | if (level) |
149 | return (level << 4) | 0xf; | 149 | return (level << 4) | 0xf; |
150 | return 0; | 150 | return 0; |
151 | } | 151 | } |
152 | 152 | ||
153 | #define NUM_LEDS 7 | 153 | #define NUM_LEDS 7 |
154 | struct tca6507_chip { | 154 | struct tca6507_chip { |
155 | int reg_set; /* One bit per register where | 155 | int reg_set; /* One bit per register where |
156 | * a '1' means the register | 156 | * a '1' means the register |
157 | * should be written */ | 157 | * should be written */ |
158 | u8 reg_file[TCA6507_REG_CNT]; | 158 | u8 reg_file[TCA6507_REG_CNT]; |
159 | /* Bank 2 is Master Intensity and doesn't use times */ | 159 | /* Bank 2 is Master Intensity and doesn't use times */ |
160 | struct bank { | 160 | struct bank { |
161 | int level; | 161 | int level; |
162 | int ontime, offtime; | 162 | int ontime, offtime; |
163 | int on_dflt, off_dflt; | 163 | int on_dflt, off_dflt; |
164 | int time_use, level_use; | 164 | int time_use, level_use; |
165 | } bank[3]; | 165 | } bank[3]; |
166 | struct i2c_client *client; | 166 | struct i2c_client *client; |
167 | struct work_struct work; | 167 | struct work_struct work; |
168 | spinlock_t lock; | 168 | spinlock_t lock; |
169 | 169 | ||
170 | struct tca6507_led { | 170 | struct tca6507_led { |
171 | struct tca6507_chip *chip; | 171 | struct tca6507_chip *chip; |
172 | struct led_classdev led_cdev; | 172 | struct led_classdev led_cdev; |
173 | int num; | 173 | int num; |
174 | int ontime, offtime; | 174 | int ontime, offtime; |
175 | int on_dflt, off_dflt; | 175 | int on_dflt, off_dflt; |
176 | int bank; /* Bank used, or -1 */ | 176 | int bank; /* Bank used, or -1 */ |
177 | int blink; /* Set if hardware-blinking */ | 177 | int blink; /* Set if hardware-blinking */ |
178 | } leds[NUM_LEDS]; | 178 | } leds[NUM_LEDS]; |
179 | #ifdef CONFIG_GPIOLIB | 179 | #ifdef CONFIG_GPIOLIB |
180 | struct gpio_chip gpio; | 180 | struct gpio_chip gpio; |
181 | const char *gpio_name[NUM_LEDS]; | 181 | const char *gpio_name[NUM_LEDS]; |
182 | int gpio_map[NUM_LEDS]; | 182 | int gpio_map[NUM_LEDS]; |
183 | #endif | 183 | #endif |
184 | }; | 184 | }; |
185 | 185 | ||
186 | static const struct i2c_device_id tca6507_id[] = { | 186 | static const struct i2c_device_id tca6507_id[] = { |
187 | { "tca6507" }, | 187 | { "tca6507" }, |
188 | { } | 188 | { } |
189 | }; | 189 | }; |
190 | MODULE_DEVICE_TABLE(i2c, tca6507_id); | 190 | MODULE_DEVICE_TABLE(i2c, tca6507_id); |
191 | 191 | ||
192 | static int choose_times(int msec, int *c1p, int *c2p) | 192 | static int choose_times(int msec, int *c1p, int *c2p) |
193 | { | 193 | { |
194 | /* | 194 | /* |
195 | * Choose two timecodes which add to 'msec' as near as possible. | 195 | * Choose two timecodes which add to 'msec' as near as possible. |
196 | * The first returned is the 'on' or 'off' time. The second is to be | 196 | * The first returned is the 'on' or 'off' time. The second is to be |
197 | * used as a 'fade-on' or 'fade-off' time. If 'msec' is even, | 197 | * used as a 'fade-on' or 'fade-off' time. If 'msec' is even, |
198 | * the first will not be smaller than the second. If 'msec' is odd, | 198 | * the first will not be smaller than the second. If 'msec' is odd, |
199 | * the first will not be larger than the second. | 199 | * the first will not be larger than the second. |
200 | * If we cannot get a sum within 1/8 of 'msec' fail with -EINVAL, | 200 | * If we cannot get a sum within 1/8 of 'msec' fail with -EINVAL, |
201 | * otherwise return the sum that was achieved, plus 1 if the first is | 201 | * otherwise return the sum that was achieved, plus 1 if the first is |
202 | * smaller. | 202 | * smaller. |
203 | * If two possibilities are equally good (e.g. 512+0, 256+256), choose | 203 | * If two possibilities are equally good (e.g. 512+0, 256+256), choose |
204 | * the first pair so there is more change-time visible (i.e. it is | 204 | * the first pair so there is more change-time visible (i.e. it is |
205 | * softer). | 205 | * softer). |
206 | */ | 206 | */ |
207 | int c1, c2; | 207 | int c1, c2; |
208 | int tmax = msec * 9 / 8; | 208 | int tmax = msec * 9 / 8; |
209 | int tmin = msec * 7 / 8; | 209 | int tmin = msec * 7 / 8; |
210 | int diff = 65536; | 210 | int diff = 65536; |
211 | 211 | ||
212 | /* We start at '1' to ensure we never even think of choosing a | 212 | /* We start at '1' to ensure we never even think of choosing a |
213 | * total time of '0'. | 213 | * total time of '0'. |
214 | */ | 214 | */ |
215 | for (c1 = 1; c1 < TIMECODES; c1++) { | 215 | for (c1 = 1; c1 < TIMECODES; c1++) { |
216 | int t = time_codes[c1]; | 216 | int t = time_codes[c1]; |
217 | if (t*2 < tmin) | 217 | if (t*2 < tmin) |
218 | continue; | 218 | continue; |
219 | if (t > tmax) | 219 | if (t > tmax) |
220 | break; | 220 | break; |
221 | for (c2 = 0; c2 <= c1; c2++) { | 221 | for (c2 = 0; c2 <= c1; c2++) { |
222 | int tt = t + time_codes[c2]; | 222 | int tt = t + time_codes[c2]; |
223 | int d; | 223 | int d; |
224 | if (tt < tmin) | 224 | if (tt < tmin) |
225 | continue; | 225 | continue; |
226 | if (tt > tmax) | 226 | if (tt > tmax) |
227 | break; | 227 | break; |
228 | /* This works! */ | 228 | /* This works! */ |
229 | d = abs(msec - tt); | 229 | d = abs(msec - tt); |
230 | if (d >= diff) | 230 | if (d >= diff) |
231 | continue; | 231 | continue; |
232 | /* Best yet */ | 232 | /* Best yet */ |
233 | *c1p = c1; | 233 | *c1p = c1; |
234 | *c2p = c2; | 234 | *c2p = c2; |
235 | diff = d; | 235 | diff = d; |
236 | if (d == 0) | 236 | if (d == 0) |
237 | return msec; | 237 | return msec; |
238 | } | 238 | } |
239 | } | 239 | } |
240 | if (diff < 65536) { | 240 | if (diff < 65536) { |
241 | int actual; | 241 | int actual; |
242 | if (msec & 1) { | 242 | if (msec & 1) { |
243 | c1 = *c2p; | 243 | c1 = *c2p; |
244 | *c2p = *c1p; | 244 | *c2p = *c1p; |
245 | *c1p = c1; | 245 | *c1p = c1; |
246 | } | 246 | } |
247 | actual = time_codes[*c1p] + time_codes[*c2p]; | 247 | actual = time_codes[*c1p] + time_codes[*c2p]; |
248 | if (*c1p < *c2p) | 248 | if (*c1p < *c2p) |
249 | return actual + 1; | 249 | return actual + 1; |
250 | else | 250 | else |
251 | return actual; | 251 | return actual; |
252 | } | 252 | } |
253 | /* No close match */ | 253 | /* No close match */ |
254 | return -EINVAL; | 254 | return -EINVAL; |
255 | } | 255 | } |
256 | 256 | ||
257 | /* | 257 | /* |
258 | * Update the register file with the appropriate 3-bit state for | 258 | * Update the register file with the appropriate 3-bit state for |
259 | * the given led. | 259 | * the given led. |
260 | */ | 260 | */ |
261 | static void set_select(struct tca6507_chip *tca, int led, int val) | 261 | static void set_select(struct tca6507_chip *tca, int led, int val) |
262 | { | 262 | { |
263 | int mask = (1 << led); | 263 | int mask = (1 << led); |
264 | int bit; | 264 | int bit; |
265 | 265 | ||
266 | for (bit = 0; bit < 3; bit++) { | 266 | for (bit = 0; bit < 3; bit++) { |
267 | int n = tca->reg_file[bit] & ~mask; | 267 | int n = tca->reg_file[bit] & ~mask; |
268 | if (val & (1 << bit)) | 268 | if (val & (1 << bit)) |
269 | n |= mask; | 269 | n |= mask; |
270 | if (tca->reg_file[bit] != n) { | 270 | if (tca->reg_file[bit] != n) { |
271 | tca->reg_file[bit] = n; | 271 | tca->reg_file[bit] = n; |
272 | tca->reg_set |= (1 << bit); | 272 | tca->reg_set |= (1 << bit); |
273 | } | 273 | } |
274 | } | 274 | } |
275 | } | 275 | } |
276 | 276 | ||
277 | /* Update the register file with the appropriate 4-bit code for | 277 | /* Update the register file with the appropriate 4-bit code for |
278 | * one bank or other. This can be used for timers, for levels, or | 278 | * one bank or other. This can be used for timers, for levels, or |
279 | * for initialisation. | 279 | * for initialisation. |
280 | */ | 280 | */ |
281 | static void set_code(struct tca6507_chip *tca, int reg, int bank, int new) | 281 | static void set_code(struct tca6507_chip *tca, int reg, int bank, int new) |
282 | { | 282 | { |
283 | int mask = 0xF; | 283 | int mask = 0xF; |
284 | int n; | 284 | int n; |
285 | if (bank) { | 285 | if (bank) { |
286 | mask <<= 4; | 286 | mask <<= 4; |
287 | new <<= 4; | 287 | new <<= 4; |
288 | } | 288 | } |
289 | n = tca->reg_file[reg] & ~mask; | 289 | n = tca->reg_file[reg] & ~mask; |
290 | n |= new; | 290 | n |= new; |
291 | if (tca->reg_file[reg] != n) { | 291 | if (tca->reg_file[reg] != n) { |
292 | tca->reg_file[reg] = n; | 292 | tca->reg_file[reg] = n; |
293 | tca->reg_set |= 1 << reg; | 293 | tca->reg_set |= 1 << reg; |
294 | } | 294 | } |
295 | } | 295 | } |
296 | 296 | ||
297 | /* Update brightness level. */ | 297 | /* Update brightness level. */ |
298 | static void set_level(struct tca6507_chip *tca, int bank, int level) | 298 | static void set_level(struct tca6507_chip *tca, int bank, int level) |
299 | { | 299 | { |
300 | switch (bank) { | 300 | switch (bank) { |
301 | case BANK0: | 301 | case BANK0: |
302 | case BANK1: | 302 | case BANK1: |
303 | set_code(tca, TCA6507_MAX_INTENSITY, bank, level); | 303 | set_code(tca, TCA6507_MAX_INTENSITY, bank, level); |
304 | break; | 304 | break; |
305 | case MASTER: | 305 | case MASTER: |
306 | set_code(tca, TCA6507_MASTER_INTENSITY, 0, level); | 306 | set_code(tca, TCA6507_MASTER_INTENSITY, 0, level); |
307 | break; | 307 | break; |
308 | } | 308 | } |
309 | tca->bank[bank].level = level; | 309 | tca->bank[bank].level = level; |
310 | } | 310 | } |
311 | 311 | ||
312 | /* Record all relevant time code for a given bank */ | 312 | /* Record all relevant time code for a given bank */ |
313 | static void set_times(struct tca6507_chip *tca, int bank) | 313 | static void set_times(struct tca6507_chip *tca, int bank) |
314 | { | 314 | { |
315 | int c1, c2; | 315 | int c1, c2; |
316 | int result; | 316 | int result; |
317 | 317 | ||
318 | result = choose_times(tca->bank[bank].ontime, &c1, &c2); | 318 | result = choose_times(tca->bank[bank].ontime, &c1, &c2); |
319 | dev_dbg(&tca->client->dev, | 319 | dev_dbg(&tca->client->dev, |
320 | "Chose on times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], | 320 | "Chose on times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], |
321 | c2, time_codes[c2], tca->bank[bank].ontime); | 321 | c2, time_codes[c2], tca->bank[bank].ontime); |
322 | set_code(tca, TCA6507_FADE_ON, bank, c2); | 322 | set_code(tca, TCA6507_FADE_ON, bank, c2); |
323 | set_code(tca, TCA6507_FULL_ON, bank, c1); | 323 | set_code(tca, TCA6507_FULL_ON, bank, c1); |
324 | tca->bank[bank].ontime = result; | 324 | tca->bank[bank].ontime = result; |
325 | 325 | ||
326 | result = choose_times(tca->bank[bank].offtime, &c1, &c2); | 326 | result = choose_times(tca->bank[bank].offtime, &c1, &c2); |
327 | dev_dbg(&tca->client->dev, | 327 | dev_dbg(&tca->client->dev, |
328 | "Chose off times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], | 328 | "Chose off times %d(%d) %d(%d) for %dms\n", c1, time_codes[c1], |
329 | c2, time_codes[c2], tca->bank[bank].offtime); | 329 | c2, time_codes[c2], tca->bank[bank].offtime); |
330 | set_code(tca, TCA6507_FADE_OFF, bank, c2); | 330 | set_code(tca, TCA6507_FADE_OFF, bank, c2); |
331 | set_code(tca, TCA6507_FIRST_OFF, bank, c1); | 331 | set_code(tca, TCA6507_FIRST_OFF, bank, c1); |
332 | set_code(tca, TCA6507_SECOND_OFF, bank, c1); | 332 | set_code(tca, TCA6507_SECOND_OFF, bank, c1); |
333 | tca->bank[bank].offtime = result; | 333 | tca->bank[bank].offtime = result; |
334 | 334 | ||
335 | set_code(tca, TCA6507_INITIALIZE, bank, INIT_CODE); | 335 | set_code(tca, TCA6507_INITIALIZE, bank, INIT_CODE); |
336 | } | 336 | } |
337 | 337 | ||
338 | /* Write all needed register of tca6507 */ | 338 | /* Write all needed register of tca6507 */ |
339 | 339 | ||
340 | static void tca6507_work(struct work_struct *work) | 340 | static void tca6507_work(struct work_struct *work) |
341 | { | 341 | { |
342 | struct tca6507_chip *tca = container_of(work, struct tca6507_chip, | 342 | struct tca6507_chip *tca = container_of(work, struct tca6507_chip, |
343 | work); | 343 | work); |
344 | struct i2c_client *cl = tca->client; | 344 | struct i2c_client *cl = tca->client; |
345 | int set; | 345 | int set; |
346 | u8 file[TCA6507_REG_CNT]; | 346 | u8 file[TCA6507_REG_CNT]; |
347 | int r; | 347 | int r; |
348 | 348 | ||
349 | spin_lock_irq(&tca->lock); | 349 | spin_lock_irq(&tca->lock); |
350 | set = tca->reg_set; | 350 | set = tca->reg_set; |
351 | memcpy(file, tca->reg_file, TCA6507_REG_CNT); | 351 | memcpy(file, tca->reg_file, TCA6507_REG_CNT); |
352 | tca->reg_set = 0; | 352 | tca->reg_set = 0; |
353 | spin_unlock_irq(&tca->lock); | 353 | spin_unlock_irq(&tca->lock); |
354 | 354 | ||
355 | for (r = 0; r < TCA6507_REG_CNT; r++) | 355 | for (r = 0; r < TCA6507_REG_CNT; r++) |
356 | if (set & (1<<r)) | 356 | if (set & (1<<r)) |
357 | i2c_smbus_write_byte_data(cl, r, file[r]); | 357 | i2c_smbus_write_byte_data(cl, r, file[r]); |
358 | } | 358 | } |
359 | 359 | ||
360 | static void led_release(struct tca6507_led *led) | 360 | static void led_release(struct tca6507_led *led) |
361 | { | 361 | { |
362 | /* If led owns any resource, release it. */ | 362 | /* If led owns any resource, release it. */ |
363 | struct tca6507_chip *tca = led->chip; | 363 | struct tca6507_chip *tca = led->chip; |
364 | if (led->bank >= 0) { | 364 | if (led->bank >= 0) { |
365 | struct bank *b = tca->bank + led->bank; | 365 | struct bank *b = tca->bank + led->bank; |
366 | if (led->blink) | 366 | if (led->blink) |
367 | b->time_use--; | 367 | b->time_use--; |
368 | b->level_use--; | 368 | b->level_use--; |
369 | } | 369 | } |
370 | led->blink = 0; | 370 | led->blink = 0; |
371 | led->bank = -1; | 371 | led->bank = -1; |
372 | } | 372 | } |
373 | 373 | ||
374 | static int led_prepare(struct tca6507_led *led) | 374 | static int led_prepare(struct tca6507_led *led) |
375 | { | 375 | { |
376 | /* Assign this led to a bank, configuring that bank if necessary. */ | 376 | /* Assign this led to a bank, configuring that bank if necessary. */ |
377 | int level = TO_LEVEL(led->led_cdev.brightness); | 377 | int level = TO_LEVEL(led->led_cdev.brightness); |
378 | struct tca6507_chip *tca = led->chip; | 378 | struct tca6507_chip *tca = led->chip; |
379 | int c1, c2; | 379 | int c1, c2; |
380 | int i; | 380 | int i; |
381 | struct bank *b; | 381 | struct bank *b; |
382 | int need_init = 0; | 382 | int need_init = 0; |
383 | 383 | ||
384 | led->led_cdev.brightness = TO_BRIGHT(level); | 384 | led->led_cdev.brightness = TO_BRIGHT(level); |
385 | if (level == 0) { | 385 | if (level == 0) { |
386 | set_select(tca, led->num, TCA6507_LS_LED_OFF); | 386 | set_select(tca, led->num, TCA6507_LS_LED_OFF); |
387 | return 0; | 387 | return 0; |
388 | } | 388 | } |
389 | 389 | ||
390 | if (led->ontime == 0 || led->offtime == 0) { | 390 | if (led->ontime == 0 || led->offtime == 0) { |
391 | /* | 391 | /* |
392 | * Just set the brightness, choosing first usable bank. | 392 | * Just set the brightness, choosing first usable bank. |
393 | * If none perfect, choose best. | 393 | * If none perfect, choose best. |
394 | * Count backwards so we check MASTER bank first | 394 | * Count backwards so we check MASTER bank first |
395 | * to avoid wasting a timer. | 395 | * to avoid wasting a timer. |
396 | */ | 396 | */ |
397 | int best = -1;/* full-on */ | 397 | int best = -1;/* full-on */ |
398 | int diff = 15-level; | 398 | int diff = 15-level; |
399 | 399 | ||
400 | if (level == 15) { | 400 | if (level == 15) { |
401 | set_select(tca, led->num, TCA6507_LS_LED_ON); | 401 | set_select(tca, led->num, TCA6507_LS_LED_ON); |
402 | return 0; | 402 | return 0; |
403 | } | 403 | } |
404 | 404 | ||
405 | for (i = MASTER; i >= BANK0; i--) { | 405 | for (i = MASTER; i >= BANK0; i--) { |
406 | int d; | 406 | int d; |
407 | if (tca->bank[i].level == level || | 407 | if (tca->bank[i].level == level || |
408 | tca->bank[i].level_use == 0) { | 408 | tca->bank[i].level_use == 0) { |
409 | best = i; | 409 | best = i; |
410 | break; | 410 | break; |
411 | } | 411 | } |
412 | d = abs(level - tca->bank[i].level); | 412 | d = abs(level - tca->bank[i].level); |
413 | if (d < diff) { | 413 | if (d < diff) { |
414 | diff = d; | 414 | diff = d; |
415 | best = i; | 415 | best = i; |
416 | } | 416 | } |
417 | } | 417 | } |
418 | if (best == -1) { | 418 | if (best == -1) { |
419 | /* Best brightness is full-on */ | 419 | /* Best brightness is full-on */ |
420 | set_select(tca, led->num, TCA6507_LS_LED_ON); | 420 | set_select(tca, led->num, TCA6507_LS_LED_ON); |
421 | led->led_cdev.brightness = LED_FULL; | 421 | led->led_cdev.brightness = LED_FULL; |
422 | return 0; | 422 | return 0; |
423 | } | 423 | } |
424 | 424 | ||
425 | if (!tca->bank[best].level_use) | 425 | if (!tca->bank[best].level_use) |
426 | set_level(tca, best, level); | 426 | set_level(tca, best, level); |
427 | 427 | ||
428 | tca->bank[best].level_use++; | 428 | tca->bank[best].level_use++; |
429 | led->bank = best; | 429 | led->bank = best; |
430 | set_select(tca, led->num, bank_source[best]); | 430 | set_select(tca, led->num, bank_source[best]); |
431 | led->led_cdev.brightness = TO_BRIGHT(tca->bank[best].level); | 431 | led->led_cdev.brightness = TO_BRIGHT(tca->bank[best].level); |
432 | return 0; | 432 | return 0; |
433 | } | 433 | } |
434 | 434 | ||
435 | /* | 435 | /* |
436 | * We have on/off time so we need to try to allocate a timing bank. | 436 | * We have on/off time so we need to try to allocate a timing bank. |
437 | * First check if times are compatible with hardware and give up if | 437 | * First check if times are compatible with hardware and give up if |
438 | * not. | 438 | * not. |
439 | */ | 439 | */ |
440 | if (choose_times(led->ontime, &c1, &c2) < 0) | 440 | if (choose_times(led->ontime, &c1, &c2) < 0) |
441 | return -EINVAL; | 441 | return -EINVAL; |
442 | if (choose_times(led->offtime, &c1, &c2) < 0) | 442 | if (choose_times(led->offtime, &c1, &c2) < 0) |
443 | return -EINVAL; | 443 | return -EINVAL; |
444 | 444 | ||
445 | for (i = BANK0; i <= BANK1; i++) { | 445 | for (i = BANK0; i <= BANK1; i++) { |
446 | if (tca->bank[i].level_use == 0) | 446 | if (tca->bank[i].level_use == 0) |
447 | /* not in use - it is ours! */ | 447 | /* not in use - it is ours! */ |
448 | break; | 448 | break; |
449 | if (tca->bank[i].level != level) | 449 | if (tca->bank[i].level != level) |
450 | /* Incompatible level - skip */ | 450 | /* Incompatible level - skip */ |
451 | /* FIX: if timer matches we maybe should consider | 451 | /* FIX: if timer matches we maybe should consider |
452 | * this anyway... | 452 | * this anyway... |
453 | */ | 453 | */ |
454 | continue; | 454 | continue; |
455 | 455 | ||
456 | if (tca->bank[i].time_use == 0) | 456 | if (tca->bank[i].time_use == 0) |
457 | /* Timer not in use, and level matches - use it */ | 457 | /* Timer not in use, and level matches - use it */ |
458 | break; | 458 | break; |
459 | 459 | ||
460 | if (!(tca->bank[i].on_dflt || | 460 | if (!(tca->bank[i].on_dflt || |
461 | led->on_dflt || | 461 | led->on_dflt || |
462 | tca->bank[i].ontime == led->ontime)) | 462 | tca->bank[i].ontime == led->ontime)) |
463 | /* on time is incompatible */ | 463 | /* on time is incompatible */ |
464 | continue; | 464 | continue; |
465 | 465 | ||
466 | if (!(tca->bank[i].off_dflt || | 466 | if (!(tca->bank[i].off_dflt || |
467 | led->off_dflt || | 467 | led->off_dflt || |
468 | tca->bank[i].offtime == led->offtime)) | 468 | tca->bank[i].offtime == led->offtime)) |
469 | /* off time is incompatible */ | 469 | /* off time is incompatible */ |
470 | continue; | 470 | continue; |
471 | 471 | ||
472 | /* looks like a suitable match */ | 472 | /* looks like a suitable match */ |
473 | break; | 473 | break; |
474 | } | 474 | } |
475 | 475 | ||
476 | if (i > BANK1) | 476 | if (i > BANK1) |
477 | /* Nothing matches - how sad */ | 477 | /* Nothing matches - how sad */ |
478 | return -EINVAL; | 478 | return -EINVAL; |
479 | 479 | ||
480 | b = &tca->bank[i]; | 480 | b = &tca->bank[i]; |
481 | if (b->level_use == 0) | 481 | if (b->level_use == 0) |
482 | set_level(tca, i, level); | 482 | set_level(tca, i, level); |
483 | b->level_use++; | 483 | b->level_use++; |
484 | led->bank = i; | 484 | led->bank = i; |
485 | 485 | ||
486 | if (b->on_dflt || | 486 | if (b->on_dflt || |
487 | !led->on_dflt || | 487 | !led->on_dflt || |
488 | b->time_use == 0) { | 488 | b->time_use == 0) { |
489 | b->ontime = led->ontime; | 489 | b->ontime = led->ontime; |
490 | b->on_dflt = led->on_dflt; | 490 | b->on_dflt = led->on_dflt; |
491 | need_init = 1; | 491 | need_init = 1; |
492 | } | 492 | } |
493 | 493 | ||
494 | if (b->off_dflt || | 494 | if (b->off_dflt || |
495 | !led->off_dflt || | 495 | !led->off_dflt || |
496 | b->time_use == 0) { | 496 | b->time_use == 0) { |
497 | b->offtime = led->offtime; | 497 | b->offtime = led->offtime; |
498 | b->off_dflt = led->off_dflt; | 498 | b->off_dflt = led->off_dflt; |
499 | need_init = 1; | 499 | need_init = 1; |
500 | } | 500 | } |
501 | 501 | ||
502 | if (need_init) | 502 | if (need_init) |
503 | set_times(tca, i); | 503 | set_times(tca, i); |
504 | 504 | ||
505 | led->ontime = b->ontime; | 505 | led->ontime = b->ontime; |
506 | led->offtime = b->offtime; | 506 | led->offtime = b->offtime; |
507 | 507 | ||
508 | b->time_use++; | 508 | b->time_use++; |
509 | led->blink = 1; | 509 | led->blink = 1; |
510 | led->led_cdev.brightness = TO_BRIGHT(b->level); | 510 | led->led_cdev.brightness = TO_BRIGHT(b->level); |
511 | set_select(tca, led->num, blink_source[i]); | 511 | set_select(tca, led->num, blink_source[i]); |
512 | return 0; | 512 | return 0; |
513 | } | 513 | } |
514 | 514 | ||
515 | static int led_assign(struct tca6507_led *led) | 515 | static int led_assign(struct tca6507_led *led) |
516 | { | 516 | { |
517 | struct tca6507_chip *tca = led->chip; | 517 | struct tca6507_chip *tca = led->chip; |
518 | int err; | 518 | int err; |
519 | unsigned long flags; | 519 | unsigned long flags; |
520 | 520 | ||
521 | spin_lock_irqsave(&tca->lock, flags); | 521 | spin_lock_irqsave(&tca->lock, flags); |
522 | led_release(led); | 522 | led_release(led); |
523 | err = led_prepare(led); | 523 | err = led_prepare(led); |
524 | if (err) { | 524 | if (err) { |
525 | /* | 525 | /* |
526 | * Can only fail on timer setup. In that case we need to | 526 | * Can only fail on timer setup. In that case we need to |
527 | * re-establish as steady level. | 527 | * re-establish as steady level. |
528 | */ | 528 | */ |
529 | led->ontime = 0; | 529 | led->ontime = 0; |
530 | led->offtime = 0; | 530 | led->offtime = 0; |
531 | led_prepare(led); | 531 | led_prepare(led); |
532 | } | 532 | } |
533 | spin_unlock_irqrestore(&tca->lock, flags); | 533 | spin_unlock_irqrestore(&tca->lock, flags); |
534 | 534 | ||
535 | if (tca->reg_set) | 535 | if (tca->reg_set) |
536 | schedule_work(&tca->work); | 536 | schedule_work(&tca->work); |
537 | return err; | 537 | return err; |
538 | } | 538 | } |
539 | 539 | ||
540 | static void tca6507_brightness_set(struct led_classdev *led_cdev, | 540 | static void tca6507_brightness_set(struct led_classdev *led_cdev, |
541 | enum led_brightness brightness) | 541 | enum led_brightness brightness) |
542 | { | 542 | { |
543 | struct tca6507_led *led = container_of(led_cdev, struct tca6507_led, | 543 | struct tca6507_led *led = container_of(led_cdev, struct tca6507_led, |
544 | led_cdev); | 544 | led_cdev); |
545 | led->led_cdev.brightness = brightness; | 545 | led->led_cdev.brightness = brightness; |
546 | led->ontime = 0; | 546 | led->ontime = 0; |
547 | led->offtime = 0; | 547 | led->offtime = 0; |
548 | led_assign(led); | 548 | led_assign(led); |
549 | } | 549 | } |
550 | 550 | ||
551 | static int tca6507_blink_set(struct led_classdev *led_cdev, | 551 | static int tca6507_blink_set(struct led_classdev *led_cdev, |
552 | unsigned long *delay_on, | 552 | unsigned long *delay_on, |
553 | unsigned long *delay_off) | 553 | unsigned long *delay_off) |
554 | { | 554 | { |
555 | struct tca6507_led *led = container_of(led_cdev, struct tca6507_led, | 555 | struct tca6507_led *led = container_of(led_cdev, struct tca6507_led, |
556 | led_cdev); | 556 | led_cdev); |
557 | 557 | ||
558 | if (*delay_on == 0) | 558 | if (*delay_on == 0) |
559 | led->on_dflt = 1; | 559 | led->on_dflt = 1; |
560 | else if (delay_on != &led_cdev->blink_delay_on) | 560 | else if (delay_on != &led_cdev->blink_delay_on) |
561 | led->on_dflt = 0; | 561 | led->on_dflt = 0; |
562 | led->ontime = *delay_on; | 562 | led->ontime = *delay_on; |
563 | 563 | ||
564 | if (*delay_off == 0) | 564 | if (*delay_off == 0) |
565 | led->off_dflt = 1; | 565 | led->off_dflt = 1; |
566 | else if (delay_off != &led_cdev->blink_delay_off) | 566 | else if (delay_off != &led_cdev->blink_delay_off) |
567 | led->off_dflt = 0; | 567 | led->off_dflt = 0; |
568 | led->offtime = *delay_off; | 568 | led->offtime = *delay_off; |
569 | 569 | ||
570 | if (led->ontime == 0) | 570 | if (led->ontime == 0) |
571 | led->ontime = 512; | 571 | led->ontime = 512; |
572 | if (led->offtime == 0) | 572 | if (led->offtime == 0) |
573 | led->offtime = 512; | 573 | led->offtime = 512; |
574 | 574 | ||
575 | if (led->led_cdev.brightness == LED_OFF) | 575 | if (led->led_cdev.brightness == LED_OFF) |
576 | led->led_cdev.brightness = LED_FULL; | 576 | led->led_cdev.brightness = LED_FULL; |
577 | if (led_assign(led) < 0) { | 577 | if (led_assign(led) < 0) { |
578 | led->ontime = 0; | 578 | led->ontime = 0; |
579 | led->offtime = 0; | 579 | led->offtime = 0; |
580 | led->led_cdev.brightness = LED_OFF; | 580 | led->led_cdev.brightness = LED_OFF; |
581 | return -EINVAL; | 581 | return -EINVAL; |
582 | } | 582 | } |
583 | *delay_on = led->ontime; | 583 | *delay_on = led->ontime; |
584 | *delay_off = led->offtime; | 584 | *delay_off = led->offtime; |
585 | return 0; | 585 | return 0; |
586 | } | 586 | } |
587 | 587 | ||
588 | #ifdef CONFIG_GPIOLIB | 588 | #ifdef CONFIG_GPIOLIB |
589 | static void tca6507_gpio_set_value(struct gpio_chip *gc, | 589 | static void tca6507_gpio_set_value(struct gpio_chip *gc, |
590 | unsigned offset, int val) | 590 | unsigned offset, int val) |
591 | { | 591 | { |
592 | struct tca6507_chip *tca = container_of(gc, struct tca6507_chip, gpio); | 592 | struct tca6507_chip *tca = container_of(gc, struct tca6507_chip, gpio); |
593 | unsigned long flags; | 593 | unsigned long flags; |
594 | 594 | ||
595 | spin_lock_irqsave(&tca->lock, flags); | 595 | spin_lock_irqsave(&tca->lock, flags); |
596 | /* | 596 | /* |
597 | * 'OFF' is floating high, and 'ON' is pulled down, so it has the | 597 | * 'OFF' is floating high, and 'ON' is pulled down, so it has the |
598 | * inverse sense of 'val'. | 598 | * inverse sense of 'val'. |
599 | */ | 599 | */ |
600 | set_select(tca, tca->gpio_map[offset], | 600 | set_select(tca, tca->gpio_map[offset], |
601 | val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON); | 601 | val ? TCA6507_LS_LED_OFF : TCA6507_LS_LED_ON); |
602 | spin_unlock_irqrestore(&tca->lock, flags); | 602 | spin_unlock_irqrestore(&tca->lock, flags); |
603 | if (tca->reg_set) | 603 | if (tca->reg_set) |
604 | schedule_work(&tca->work); | 604 | schedule_work(&tca->work); |
605 | } | 605 | } |
606 | 606 | ||
607 | static int tca6507_gpio_direction_output(struct gpio_chip *gc, | 607 | static int tca6507_gpio_direction_output(struct gpio_chip *gc, |
608 | unsigned offset, int val) | 608 | unsigned offset, int val) |
609 | { | 609 | { |
610 | tca6507_gpio_set_value(gc, offset, val); | 610 | tca6507_gpio_set_value(gc, offset, val); |
611 | return 0; | 611 | return 0; |
612 | } | 612 | } |
613 | 613 | ||
614 | static int tca6507_probe_gpios(struct i2c_client *client, | 614 | static int tca6507_probe_gpios(struct i2c_client *client, |
615 | struct tca6507_chip *tca, | 615 | struct tca6507_chip *tca, |
616 | struct tca6507_platform_data *pdata) | 616 | struct tca6507_platform_data *pdata) |
617 | { | 617 | { |
618 | int err; | 618 | int err; |
619 | int i = 0; | 619 | int i = 0; |
620 | int gpios = 0; | 620 | int gpios = 0; |
621 | 621 | ||
622 | for (i = 0; i < NUM_LEDS; i++) | 622 | for (i = 0; i < NUM_LEDS; i++) |
623 | if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) { | 623 | if (pdata->leds.leds[i].name && pdata->leds.leds[i].flags) { |
624 | /* Configure as a gpio */ | 624 | /* Configure as a gpio */ |
625 | tca->gpio_name[gpios] = pdata->leds.leds[i].name; | 625 | tca->gpio_name[gpios] = pdata->leds.leds[i].name; |
626 | tca->gpio_map[gpios] = i; | 626 | tca->gpio_map[gpios] = i; |
627 | gpios++; | 627 | gpios++; |
628 | } | 628 | } |
629 | 629 | ||
630 | if (!gpios) | 630 | if (!gpios) |
631 | return 0; | 631 | return 0; |
632 | 632 | ||
633 | tca->gpio.label = "gpio-tca6507"; | 633 | tca->gpio.label = "gpio-tca6507"; |
634 | tca->gpio.names = tca->gpio_name; | 634 | tca->gpio.names = tca->gpio_name; |
635 | tca->gpio.ngpio = gpios; | 635 | tca->gpio.ngpio = gpios; |
636 | tca->gpio.base = pdata->gpio_base; | 636 | tca->gpio.base = pdata->gpio_base; |
637 | tca->gpio.owner = THIS_MODULE; | 637 | tca->gpio.owner = THIS_MODULE; |
638 | tca->gpio.direction_output = tca6507_gpio_direction_output; | 638 | tca->gpio.direction_output = tca6507_gpio_direction_output; |
639 | tca->gpio.set = tca6507_gpio_set_value; | 639 | tca->gpio.set = tca6507_gpio_set_value; |
640 | tca->gpio.dev = &client->dev; | 640 | tca->gpio.dev = &client->dev; |
641 | err = gpiochip_add(&tca->gpio); | 641 | err = gpiochip_add(&tca->gpio); |
642 | if (err) { | 642 | if (err) { |
643 | tca->gpio.ngpio = 0; | 643 | tca->gpio.ngpio = 0; |
644 | return err; | 644 | return err; |
645 | } | 645 | } |
646 | if (pdata->setup) | 646 | if (pdata->setup) |
647 | pdata->setup(tca->gpio.base, tca->gpio.ngpio); | 647 | pdata->setup(tca->gpio.base, tca->gpio.ngpio); |
648 | return 0; | 648 | return 0; |
649 | } | 649 | } |
650 | 650 | ||
651 | static void tca6507_remove_gpio(struct tca6507_chip *tca) | 651 | static void tca6507_remove_gpio(struct tca6507_chip *tca) |
652 | { | 652 | { |
653 | if (tca->gpio.ngpio) { | 653 | if (tca->gpio.ngpio) { |
654 | int err = gpiochip_remove(&tca->gpio); | 654 | int err = gpiochip_remove(&tca->gpio); |
655 | dev_err(&tca->client->dev, "%s failed, %d\n", | 655 | dev_err(&tca->client->dev, "%s failed, %d\n", |
656 | "gpiochip_remove()", err); | 656 | "gpiochip_remove()", err); |
657 | } | 657 | } |
658 | } | 658 | } |
659 | #else /* CONFIG_GPIOLIB */ | 659 | #else /* CONFIG_GPIOLIB */ |
660 | static int tca6507_probe_gpios(struct i2c_client *client, | 660 | static int tca6507_probe_gpios(struct i2c_client *client, |
661 | struct tca6507_chip *tca, | 661 | struct tca6507_chip *tca, |
662 | struct tca6507_platform_data *pdata) | 662 | struct tca6507_platform_data *pdata) |
663 | { | 663 | { |
664 | return 0; | 664 | return 0; |
665 | } | 665 | } |
666 | static void tca6507_remove_gpio(struct tca6507_chip *tca) | 666 | static void tca6507_remove_gpio(struct tca6507_chip *tca) |
667 | { | 667 | { |
668 | } | 668 | } |
669 | #endif /* CONFIG_GPIOLIB */ | 669 | #endif /* CONFIG_GPIOLIB */ |
670 | 670 | ||
671 | #ifdef CONFIG_OF | 671 | #ifdef CONFIG_OF |
672 | static struct tca6507_platform_data * | 672 | static struct tca6507_platform_data * |
673 | tca6507_led_dt_init(struct i2c_client *client) | 673 | tca6507_led_dt_init(struct i2c_client *client) |
674 | { | 674 | { |
675 | struct device_node *np = client->dev.of_node, *child; | 675 | struct device_node *np = client->dev.of_node, *child; |
676 | struct tca6507_platform_data *pdata; | 676 | struct tca6507_platform_data *pdata; |
677 | struct led_info *tca_leds; | 677 | struct led_info *tca_leds; |
678 | int count; | 678 | int count; |
679 | 679 | ||
680 | count = of_get_child_count(np); | 680 | count = of_get_child_count(np); |
681 | if (!count || count > NUM_LEDS) | 681 | if (!count || count > NUM_LEDS) |
682 | return ERR_PTR(-ENODEV); | 682 | return ERR_PTR(-ENODEV); |
683 | 683 | ||
684 | tca_leds = devm_kzalloc(&client->dev, | 684 | tca_leds = devm_kzalloc(&client->dev, |
685 | sizeof(struct led_info) * count, GFP_KERNEL); | 685 | sizeof(struct led_info) * count, GFP_KERNEL); |
686 | if (!tca_leds) | 686 | if (!tca_leds) |
687 | return ERR_PTR(-ENOMEM); | 687 | return ERR_PTR(-ENOMEM); |
688 | 688 | ||
689 | for_each_child_of_node(np, child) { | 689 | for_each_child_of_node(np, child) { |
690 | struct led_info led; | 690 | struct led_info led; |
691 | u32 reg; | 691 | u32 reg; |
692 | int ret; | 692 | int ret; |
693 | 693 | ||
694 | led.name = | 694 | led.name = |
695 | of_get_property(child, "label", NULL) ? : child->name; | 695 | of_get_property(child, "label", NULL) ? : child->name; |
696 | led.default_trigger = | 696 | led.default_trigger = |
697 | of_get_property(child, "linux,default-trigger", NULL); | 697 | of_get_property(child, "linux,default-trigger", NULL); |
698 | 698 | ||
699 | ret = of_property_read_u32(child, "reg", ®); | 699 | ret = of_property_read_u32(child, "reg", ®); |
700 | if (ret != 0) | 700 | if (ret != 0) |
701 | continue; | 701 | continue; |
702 | 702 | ||
703 | tca_leds[reg] = led; | 703 | tca_leds[reg] = led; |
704 | } | 704 | } |
705 | pdata = devm_kzalloc(&client->dev, | 705 | pdata = devm_kzalloc(&client->dev, |
706 | sizeof(struct tca6507_platform_data), GFP_KERNEL); | 706 | sizeof(struct tca6507_platform_data), GFP_KERNEL); |
707 | if (!pdata) | 707 | if (!pdata) |
708 | return ERR_PTR(-ENOMEM); | 708 | return ERR_PTR(-ENOMEM); |
709 | 709 | ||
710 | pdata->leds.leds = tca_leds; | 710 | pdata->leds.leds = tca_leds; |
711 | pdata->leds.num_leds = count; | 711 | pdata->leds.num_leds = count; |
712 | 712 | ||
713 | return pdata; | 713 | return pdata; |
714 | } | 714 | } |
715 | 715 | ||
716 | static const struct of_device_id of_tca6507_leds_match[] = { | 716 | static const struct of_device_id of_tca6507_leds_match[] = { |
717 | { .compatible = "ti,tca6507", }, | 717 | { .compatible = "ti,tca6507", }, |
718 | {}, | 718 | {}, |
719 | }; | 719 | }; |
720 | 720 | ||
721 | #else | 721 | #else |
722 | static struct tca6507_platform_data * | 722 | static struct tca6507_platform_data * |
723 | tca6507_led_dt_init(struct i2c_client *client) | 723 | tca6507_led_dt_init(struct i2c_client *client) |
724 | { | 724 | { |
725 | return ERR_PTR(-ENODEV); | 725 | return ERR_PTR(-ENODEV); |
726 | } | 726 | } |
727 | 727 | ||
728 | #endif | 728 | #endif |
729 | 729 | ||
730 | static int tca6507_probe(struct i2c_client *client, | 730 | static int tca6507_probe(struct i2c_client *client, |
731 | const struct i2c_device_id *id) | 731 | const struct i2c_device_id *id) |
732 | { | 732 | { |
733 | struct tca6507_chip *tca; | 733 | struct tca6507_chip *tca; |
734 | struct i2c_adapter *adapter; | 734 | struct i2c_adapter *adapter; |
735 | struct tca6507_platform_data *pdata; | 735 | struct tca6507_platform_data *pdata; |
736 | int err; | 736 | int err; |
737 | int i = 0; | 737 | int i = 0; |
738 | 738 | ||
739 | adapter = to_i2c_adapter(client->dev.parent); | 739 | adapter = to_i2c_adapter(client->dev.parent); |
740 | pdata = client->dev.platform_data; | 740 | pdata = dev_get_platdata(&client->dev); |
741 | 741 | ||
742 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) | 742 | if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) |
743 | return -EIO; | 743 | return -EIO; |
744 | 744 | ||
745 | if (!pdata || pdata->leds.num_leds != NUM_LEDS) { | 745 | if (!pdata || pdata->leds.num_leds != NUM_LEDS) { |
746 | pdata = tca6507_led_dt_init(client); | 746 | pdata = tca6507_led_dt_init(client); |
747 | if (IS_ERR(pdata)) { | 747 | if (IS_ERR(pdata)) { |
748 | dev_err(&client->dev, "Need %d entries in platform-data list\n", | 748 | dev_err(&client->dev, "Need %d entries in platform-data list\n", |
749 | NUM_LEDS); | 749 | NUM_LEDS); |
750 | return PTR_ERR(pdata); | 750 | return PTR_ERR(pdata); |
751 | } | 751 | } |
752 | } | 752 | } |
753 | tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL); | 753 | tca = devm_kzalloc(&client->dev, sizeof(*tca), GFP_KERNEL); |
754 | if (!tca) | 754 | if (!tca) |
755 | return -ENOMEM; | 755 | return -ENOMEM; |
756 | 756 | ||
757 | tca->client = client; | 757 | tca->client = client; |
758 | INIT_WORK(&tca->work, tca6507_work); | 758 | INIT_WORK(&tca->work, tca6507_work); |
759 | spin_lock_init(&tca->lock); | 759 | spin_lock_init(&tca->lock); |
760 | i2c_set_clientdata(client, tca); | 760 | i2c_set_clientdata(client, tca); |
761 | 761 | ||
762 | for (i = 0; i < NUM_LEDS; i++) { | 762 | for (i = 0; i < NUM_LEDS; i++) { |
763 | struct tca6507_led *l = tca->leds + i; | 763 | struct tca6507_led *l = tca->leds + i; |
764 | 764 | ||
765 | l->chip = tca; | 765 | l->chip = tca; |
766 | l->num = i; | 766 | l->num = i; |
767 | if (pdata->leds.leds[i].name && !pdata->leds.leds[i].flags) { | 767 | if (pdata->leds.leds[i].name && !pdata->leds.leds[i].flags) { |
768 | l->led_cdev.name = pdata->leds.leds[i].name; | 768 | l->led_cdev.name = pdata->leds.leds[i].name; |
769 | l->led_cdev.default_trigger | 769 | l->led_cdev.default_trigger |
770 | = pdata->leds.leds[i].default_trigger; | 770 | = pdata->leds.leds[i].default_trigger; |
771 | l->led_cdev.brightness_set = tca6507_brightness_set; | 771 | l->led_cdev.brightness_set = tca6507_brightness_set; |
772 | l->led_cdev.blink_set = tca6507_blink_set; | 772 | l->led_cdev.blink_set = tca6507_blink_set; |
773 | l->bank = -1; | 773 | l->bank = -1; |
774 | err = led_classdev_register(&client->dev, | 774 | err = led_classdev_register(&client->dev, |
775 | &l->led_cdev); | 775 | &l->led_cdev); |
776 | if (err < 0) | 776 | if (err < 0) |
777 | goto exit; | 777 | goto exit; |
778 | } | 778 | } |
779 | } | 779 | } |
780 | err = tca6507_probe_gpios(client, tca, pdata); | 780 | err = tca6507_probe_gpios(client, tca, pdata); |
781 | if (err) | 781 | if (err) |
782 | goto exit; | 782 | goto exit; |
783 | /* set all registers to known state - zero */ | 783 | /* set all registers to known state - zero */ |
784 | tca->reg_set = 0x7f; | 784 | tca->reg_set = 0x7f; |
785 | schedule_work(&tca->work); | 785 | schedule_work(&tca->work); |
786 | 786 | ||
787 | return 0; | 787 | return 0; |
788 | exit: | 788 | exit: |
789 | while (i--) { | 789 | while (i--) { |
790 | if (tca->leds[i].led_cdev.name) | 790 | if (tca->leds[i].led_cdev.name) |
791 | led_classdev_unregister(&tca->leds[i].led_cdev); | 791 | led_classdev_unregister(&tca->leds[i].led_cdev); |
792 | } | 792 | } |
793 | return err; | 793 | return err; |
794 | } | 794 | } |
795 | 795 | ||
796 | static int tca6507_remove(struct i2c_client *client) | 796 | static int tca6507_remove(struct i2c_client *client) |
797 | { | 797 | { |
798 | int i; | 798 | int i; |
799 | struct tca6507_chip *tca = i2c_get_clientdata(client); | 799 | struct tca6507_chip *tca = i2c_get_clientdata(client); |
800 | struct tca6507_led *tca_leds = tca->leds; | 800 | struct tca6507_led *tca_leds = tca->leds; |
801 | 801 | ||
802 | for (i = 0; i < NUM_LEDS; i++) { | 802 | for (i = 0; i < NUM_LEDS; i++) { |
803 | if (tca_leds[i].led_cdev.name) | 803 | if (tca_leds[i].led_cdev.name) |
804 | led_classdev_unregister(&tca_leds[i].led_cdev); | 804 | led_classdev_unregister(&tca_leds[i].led_cdev); |
805 | } | 805 | } |
806 | tca6507_remove_gpio(tca); | 806 | tca6507_remove_gpio(tca); |
807 | cancel_work_sync(&tca->work); | 807 | cancel_work_sync(&tca->work); |
808 | 808 | ||
809 | return 0; | 809 | return 0; |
810 | } | 810 | } |
811 | 811 | ||
812 | static struct i2c_driver tca6507_driver = { | 812 | static struct i2c_driver tca6507_driver = { |
813 | .driver = { | 813 | .driver = { |
814 | .name = "leds-tca6507", | 814 | .name = "leds-tca6507", |
815 | .owner = THIS_MODULE, | 815 | .owner = THIS_MODULE, |
816 | .of_match_table = of_match_ptr(of_tca6507_leds_match), | 816 | .of_match_table = of_match_ptr(of_tca6507_leds_match), |
817 | }, | 817 | }, |
818 | .probe = tca6507_probe, | 818 | .probe = tca6507_probe, |
819 | .remove = tca6507_remove, | 819 | .remove = tca6507_remove, |
820 | .id_table = tca6507_id, | 820 | .id_table = tca6507_id, |
821 | }; | 821 | }; |
822 | 822 | ||
823 | module_i2c_driver(tca6507_driver); | 823 | module_i2c_driver(tca6507_driver); |
824 | 824 | ||
825 | MODULE_AUTHOR("NeilBrown <neilb@suse.de>"); | 825 | MODULE_AUTHOR("NeilBrown <neilb@suse.de>"); |
826 | MODULE_DESCRIPTION("TCA6507 LED/GPO driver"); | 826 | MODULE_DESCRIPTION("TCA6507 LED/GPO driver"); |
827 | MODULE_LICENSE("GPL v2"); | 827 | MODULE_LICENSE("GPL v2"); |
828 | 828 |
drivers/leds/leds-wm831x-status.c
1 | /* | 1 | /* |
2 | * LED driver for WM831x status LEDs | 2 | * LED driver for WM831x status LEDs |
3 | * | 3 | * |
4 | * Copyright(C) 2009 Wolfson Microelectronics PLC. | 4 | * Copyright(C) 2009 Wolfson Microelectronics PLC. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/leds.h> | 16 | #include <linux/leds.h> |
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/mfd/wm831x/core.h> | 18 | #include <linux/mfd/wm831x/core.h> |
19 | #include <linux/mfd/wm831x/pdata.h> | 19 | #include <linux/mfd/wm831x/pdata.h> |
20 | #include <linux/mfd/wm831x/status.h> | 20 | #include <linux/mfd/wm831x/status.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | 22 | ||
23 | 23 | ||
24 | struct wm831x_status { | 24 | struct wm831x_status { |
25 | struct led_classdev cdev; | 25 | struct led_classdev cdev; |
26 | struct wm831x *wm831x; | 26 | struct wm831x *wm831x; |
27 | struct work_struct work; | 27 | struct work_struct work; |
28 | struct mutex mutex; | 28 | struct mutex mutex; |
29 | 29 | ||
30 | spinlock_t value_lock; | 30 | spinlock_t value_lock; |
31 | int reg; /* Control register */ | 31 | int reg; /* Control register */ |
32 | int reg_val; /* Control register value */ | 32 | int reg_val; /* Control register value */ |
33 | 33 | ||
34 | int blink; | 34 | int blink; |
35 | int blink_time; | 35 | int blink_time; |
36 | int blink_cyc; | 36 | int blink_cyc; |
37 | int src; | 37 | int src; |
38 | enum led_brightness brightness; | 38 | enum led_brightness brightness; |
39 | }; | 39 | }; |
40 | 40 | ||
41 | #define to_wm831x_status(led_cdev) \ | 41 | #define to_wm831x_status(led_cdev) \ |
42 | container_of(led_cdev, struct wm831x_status, cdev) | 42 | container_of(led_cdev, struct wm831x_status, cdev) |
43 | 43 | ||
44 | static void wm831x_status_work(struct work_struct *work) | 44 | static void wm831x_status_work(struct work_struct *work) |
45 | { | 45 | { |
46 | struct wm831x_status *led = container_of(work, struct wm831x_status, | 46 | struct wm831x_status *led = container_of(work, struct wm831x_status, |
47 | work); | 47 | work); |
48 | unsigned long flags; | 48 | unsigned long flags; |
49 | 49 | ||
50 | mutex_lock(&led->mutex); | 50 | mutex_lock(&led->mutex); |
51 | 51 | ||
52 | led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK | | 52 | led->reg_val &= ~(WM831X_LED_SRC_MASK | WM831X_LED_MODE_MASK | |
53 | WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK); | 53 | WM831X_LED_DUTY_CYC_MASK | WM831X_LED_DUR_MASK); |
54 | 54 | ||
55 | spin_lock_irqsave(&led->value_lock, flags); | 55 | spin_lock_irqsave(&led->value_lock, flags); |
56 | 56 | ||
57 | led->reg_val |= led->src << WM831X_LED_SRC_SHIFT; | 57 | led->reg_val |= led->src << WM831X_LED_SRC_SHIFT; |
58 | if (led->blink) { | 58 | if (led->blink) { |
59 | led->reg_val |= 2 << WM831X_LED_MODE_SHIFT; | 59 | led->reg_val |= 2 << WM831X_LED_MODE_SHIFT; |
60 | led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT; | 60 | led->reg_val |= led->blink_time << WM831X_LED_DUR_SHIFT; |
61 | led->reg_val |= led->blink_cyc; | 61 | led->reg_val |= led->blink_cyc; |
62 | } else { | 62 | } else { |
63 | if (led->brightness != LED_OFF) | 63 | if (led->brightness != LED_OFF) |
64 | led->reg_val |= 1 << WM831X_LED_MODE_SHIFT; | 64 | led->reg_val |= 1 << WM831X_LED_MODE_SHIFT; |
65 | } | 65 | } |
66 | 66 | ||
67 | spin_unlock_irqrestore(&led->value_lock, flags); | 67 | spin_unlock_irqrestore(&led->value_lock, flags); |
68 | 68 | ||
69 | wm831x_reg_write(led->wm831x, led->reg, led->reg_val); | 69 | wm831x_reg_write(led->wm831x, led->reg, led->reg_val); |
70 | 70 | ||
71 | mutex_unlock(&led->mutex); | 71 | mutex_unlock(&led->mutex); |
72 | } | 72 | } |
73 | 73 | ||
74 | static void wm831x_status_set(struct led_classdev *led_cdev, | 74 | static void wm831x_status_set(struct led_classdev *led_cdev, |
75 | enum led_brightness value) | 75 | enum led_brightness value) |
76 | { | 76 | { |
77 | struct wm831x_status *led = to_wm831x_status(led_cdev); | 77 | struct wm831x_status *led = to_wm831x_status(led_cdev); |
78 | unsigned long flags; | 78 | unsigned long flags; |
79 | 79 | ||
80 | spin_lock_irqsave(&led->value_lock, flags); | 80 | spin_lock_irqsave(&led->value_lock, flags); |
81 | led->brightness = value; | 81 | led->brightness = value; |
82 | if (value == LED_OFF) | 82 | if (value == LED_OFF) |
83 | led->blink = 0; | 83 | led->blink = 0; |
84 | schedule_work(&led->work); | 84 | schedule_work(&led->work); |
85 | spin_unlock_irqrestore(&led->value_lock, flags); | 85 | spin_unlock_irqrestore(&led->value_lock, flags); |
86 | } | 86 | } |
87 | 87 | ||
88 | static int wm831x_status_blink_set(struct led_classdev *led_cdev, | 88 | static int wm831x_status_blink_set(struct led_classdev *led_cdev, |
89 | unsigned long *delay_on, | 89 | unsigned long *delay_on, |
90 | unsigned long *delay_off) | 90 | unsigned long *delay_off) |
91 | { | 91 | { |
92 | struct wm831x_status *led = to_wm831x_status(led_cdev); | 92 | struct wm831x_status *led = to_wm831x_status(led_cdev); |
93 | unsigned long flags; | 93 | unsigned long flags; |
94 | int ret = 0; | 94 | int ret = 0; |
95 | 95 | ||
96 | /* Pick some defaults if we've not been given times */ | 96 | /* Pick some defaults if we've not been given times */ |
97 | if (*delay_on == 0 && *delay_off == 0) { | 97 | if (*delay_on == 0 && *delay_off == 0) { |
98 | *delay_on = 250; | 98 | *delay_on = 250; |
99 | *delay_off = 250; | 99 | *delay_off = 250; |
100 | } | 100 | } |
101 | 101 | ||
102 | spin_lock_irqsave(&led->value_lock, flags); | 102 | spin_lock_irqsave(&led->value_lock, flags); |
103 | 103 | ||
104 | /* We only have a limited selection of settings, see if we can | 104 | /* We only have a limited selection of settings, see if we can |
105 | * support the configuration we're being given */ | 105 | * support the configuration we're being given */ |
106 | switch (*delay_on) { | 106 | switch (*delay_on) { |
107 | case 1000: | 107 | case 1000: |
108 | led->blink_time = 0; | 108 | led->blink_time = 0; |
109 | break; | 109 | break; |
110 | case 250: | 110 | case 250: |
111 | led->blink_time = 1; | 111 | led->blink_time = 1; |
112 | break; | 112 | break; |
113 | case 125: | 113 | case 125: |
114 | led->blink_time = 2; | 114 | led->blink_time = 2; |
115 | break; | 115 | break; |
116 | case 62: | 116 | case 62: |
117 | case 63: | 117 | case 63: |
118 | /* Actually 62.5ms */ | 118 | /* Actually 62.5ms */ |
119 | led->blink_time = 3; | 119 | led->blink_time = 3; |
120 | break; | 120 | break; |
121 | default: | 121 | default: |
122 | ret = -EINVAL; | 122 | ret = -EINVAL; |
123 | break; | 123 | break; |
124 | } | 124 | } |
125 | 125 | ||
126 | if (ret == 0) { | 126 | if (ret == 0) { |
127 | switch (*delay_off / *delay_on) { | 127 | switch (*delay_off / *delay_on) { |
128 | case 1: | 128 | case 1: |
129 | led->blink_cyc = 0; | 129 | led->blink_cyc = 0; |
130 | break; | 130 | break; |
131 | case 3: | 131 | case 3: |
132 | led->blink_cyc = 1; | 132 | led->blink_cyc = 1; |
133 | break; | 133 | break; |
134 | case 4: | 134 | case 4: |
135 | led->blink_cyc = 2; | 135 | led->blink_cyc = 2; |
136 | break; | 136 | break; |
137 | case 8: | 137 | case 8: |
138 | led->blink_cyc = 3; | 138 | led->blink_cyc = 3; |
139 | break; | 139 | break; |
140 | default: | 140 | default: |
141 | ret = -EINVAL; | 141 | ret = -EINVAL; |
142 | break; | 142 | break; |
143 | } | 143 | } |
144 | } | 144 | } |
145 | 145 | ||
146 | if (ret == 0) | 146 | if (ret == 0) |
147 | led->blink = 1; | 147 | led->blink = 1; |
148 | else | 148 | else |
149 | led->blink = 0; | 149 | led->blink = 0; |
150 | 150 | ||
151 | /* Always update; if we fail turn off blinking since we expect | 151 | /* Always update; if we fail turn off blinking since we expect |
152 | * a software fallback. */ | 152 | * a software fallback. */ |
153 | schedule_work(&led->work); | 153 | schedule_work(&led->work); |
154 | 154 | ||
155 | spin_unlock_irqrestore(&led->value_lock, flags); | 155 | spin_unlock_irqrestore(&led->value_lock, flags); |
156 | 156 | ||
157 | return ret; | 157 | return ret; |
158 | } | 158 | } |
159 | 159 | ||
160 | static const char * const led_src_texts[] = { | 160 | static const char * const led_src_texts[] = { |
161 | "otp", | 161 | "otp", |
162 | "power", | 162 | "power", |
163 | "charger", | 163 | "charger", |
164 | "soft", | 164 | "soft", |
165 | }; | 165 | }; |
166 | 166 | ||
167 | static ssize_t wm831x_status_src_show(struct device *dev, | 167 | static ssize_t wm831x_status_src_show(struct device *dev, |
168 | struct device_attribute *attr, char *buf) | 168 | struct device_attribute *attr, char *buf) |
169 | { | 169 | { |
170 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 170 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
171 | struct wm831x_status *led = to_wm831x_status(led_cdev); | 171 | struct wm831x_status *led = to_wm831x_status(led_cdev); |
172 | int i; | 172 | int i; |
173 | ssize_t ret = 0; | 173 | ssize_t ret = 0; |
174 | 174 | ||
175 | mutex_lock(&led->mutex); | 175 | mutex_lock(&led->mutex); |
176 | 176 | ||
177 | for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) | 177 | for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) |
178 | if (i == led->src) | 178 | if (i == led->src) |
179 | ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]); | 179 | ret += sprintf(&buf[ret], "[%s] ", led_src_texts[i]); |
180 | else | 180 | else |
181 | ret += sprintf(&buf[ret], "%s ", led_src_texts[i]); | 181 | ret += sprintf(&buf[ret], "%s ", led_src_texts[i]); |
182 | 182 | ||
183 | mutex_unlock(&led->mutex); | 183 | mutex_unlock(&led->mutex); |
184 | 184 | ||
185 | ret += sprintf(&buf[ret], "\n"); | 185 | ret += sprintf(&buf[ret], "\n"); |
186 | 186 | ||
187 | return ret; | 187 | return ret; |
188 | } | 188 | } |
189 | 189 | ||
190 | static ssize_t wm831x_status_src_store(struct device *dev, | 190 | static ssize_t wm831x_status_src_store(struct device *dev, |
191 | struct device_attribute *attr, | 191 | struct device_attribute *attr, |
192 | const char *buf, size_t size) | 192 | const char *buf, size_t size) |
193 | { | 193 | { |
194 | struct led_classdev *led_cdev = dev_get_drvdata(dev); | 194 | struct led_classdev *led_cdev = dev_get_drvdata(dev); |
195 | struct wm831x_status *led = to_wm831x_status(led_cdev); | 195 | struct wm831x_status *led = to_wm831x_status(led_cdev); |
196 | char name[20]; | 196 | char name[20]; |
197 | int i; | 197 | int i; |
198 | size_t len; | 198 | size_t len; |
199 | 199 | ||
200 | name[sizeof(name) - 1] = '\0'; | 200 | name[sizeof(name) - 1] = '\0'; |
201 | strncpy(name, buf, sizeof(name) - 1); | 201 | strncpy(name, buf, sizeof(name) - 1); |
202 | len = strlen(name); | 202 | len = strlen(name); |
203 | 203 | ||
204 | if (len && name[len - 1] == '\n') | 204 | if (len && name[len - 1] == '\n') |
205 | name[len - 1] = '\0'; | 205 | name[len - 1] = '\0'; |
206 | 206 | ||
207 | for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) { | 207 | for (i = 0; i < ARRAY_SIZE(led_src_texts); i++) { |
208 | if (!strcmp(name, led_src_texts[i])) { | 208 | if (!strcmp(name, led_src_texts[i])) { |
209 | mutex_lock(&led->mutex); | 209 | mutex_lock(&led->mutex); |
210 | 210 | ||
211 | led->src = i; | 211 | led->src = i; |
212 | schedule_work(&led->work); | 212 | schedule_work(&led->work); |
213 | 213 | ||
214 | mutex_unlock(&led->mutex); | 214 | mutex_unlock(&led->mutex); |
215 | } | 215 | } |
216 | } | 216 | } |
217 | 217 | ||
218 | return size; | 218 | return size; |
219 | } | 219 | } |
220 | 220 | ||
221 | static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store); | 221 | static DEVICE_ATTR(src, 0644, wm831x_status_src_show, wm831x_status_src_store); |
222 | 222 | ||
223 | static int wm831x_status_probe(struct platform_device *pdev) | 223 | static int wm831x_status_probe(struct platform_device *pdev) |
224 | { | 224 | { |
225 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | 225 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); |
226 | struct wm831x_pdata *chip_pdata; | 226 | struct wm831x_pdata *chip_pdata; |
227 | struct wm831x_status_pdata pdata; | 227 | struct wm831x_status_pdata pdata; |
228 | struct wm831x_status *drvdata; | 228 | struct wm831x_status *drvdata; |
229 | struct resource *res; | 229 | struct resource *res; |
230 | int id = pdev->id % ARRAY_SIZE(chip_pdata->status); | 230 | int id = pdev->id % ARRAY_SIZE(chip_pdata->status); |
231 | int ret; | 231 | int ret; |
232 | 232 | ||
233 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 233 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
234 | if (res == NULL) { | 234 | if (res == NULL) { |
235 | dev_err(&pdev->dev, "No I/O resource\n"); | 235 | dev_err(&pdev->dev, "No I/O resource\n"); |
236 | ret = -EINVAL; | 236 | ret = -EINVAL; |
237 | goto err; | 237 | goto err; |
238 | } | 238 | } |
239 | 239 | ||
240 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_status), | 240 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_status), |
241 | GFP_KERNEL); | 241 | GFP_KERNEL); |
242 | if (!drvdata) | 242 | if (!drvdata) |
243 | return -ENOMEM; | 243 | return -ENOMEM; |
244 | platform_set_drvdata(pdev, drvdata); | 244 | platform_set_drvdata(pdev, drvdata); |
245 | 245 | ||
246 | drvdata->wm831x = wm831x; | 246 | drvdata->wm831x = wm831x; |
247 | drvdata->reg = res->start; | 247 | drvdata->reg = res->start; |
248 | 248 | ||
249 | if (wm831x->dev->platform_data) | 249 | if (dev_get_platdata(wm831x->dev)) |
250 | chip_pdata = wm831x->dev->platform_data; | 250 | chip_pdata = dev_get_platdata(wm831x->dev); |
251 | else | 251 | else |
252 | chip_pdata = NULL; | 252 | chip_pdata = NULL; |
253 | 253 | ||
254 | memset(&pdata, 0, sizeof(pdata)); | 254 | memset(&pdata, 0, sizeof(pdata)); |
255 | if (chip_pdata && chip_pdata->status[id]) | 255 | if (chip_pdata && chip_pdata->status[id]) |
256 | memcpy(&pdata, chip_pdata->status[id], sizeof(pdata)); | 256 | memcpy(&pdata, chip_pdata->status[id], sizeof(pdata)); |
257 | else | 257 | else |
258 | pdata.name = dev_name(&pdev->dev); | 258 | pdata.name = dev_name(&pdev->dev); |
259 | 259 | ||
260 | mutex_init(&drvdata->mutex); | 260 | mutex_init(&drvdata->mutex); |
261 | INIT_WORK(&drvdata->work, wm831x_status_work); | 261 | INIT_WORK(&drvdata->work, wm831x_status_work); |
262 | spin_lock_init(&drvdata->value_lock); | 262 | spin_lock_init(&drvdata->value_lock); |
263 | 263 | ||
264 | /* We cache the configuration register and read startup values | 264 | /* We cache the configuration register and read startup values |
265 | * from it. */ | 265 | * from it. */ |
266 | drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg); | 266 | drvdata->reg_val = wm831x_reg_read(wm831x, drvdata->reg); |
267 | 267 | ||
268 | if (drvdata->reg_val & WM831X_LED_MODE_MASK) | 268 | if (drvdata->reg_val & WM831X_LED_MODE_MASK) |
269 | drvdata->brightness = LED_FULL; | 269 | drvdata->brightness = LED_FULL; |
270 | else | 270 | else |
271 | drvdata->brightness = LED_OFF; | 271 | drvdata->brightness = LED_OFF; |
272 | 272 | ||
273 | /* Set a default source if configured, otherwise leave the | 273 | /* Set a default source if configured, otherwise leave the |
274 | * current hardware setting. | 274 | * current hardware setting. |
275 | */ | 275 | */ |
276 | if (pdata.default_src == WM831X_STATUS_PRESERVE) { | 276 | if (pdata.default_src == WM831X_STATUS_PRESERVE) { |
277 | drvdata->src = drvdata->reg_val; | 277 | drvdata->src = drvdata->reg_val; |
278 | drvdata->src &= WM831X_LED_SRC_MASK; | 278 | drvdata->src &= WM831X_LED_SRC_MASK; |
279 | drvdata->src >>= WM831X_LED_SRC_SHIFT; | 279 | drvdata->src >>= WM831X_LED_SRC_SHIFT; |
280 | } else { | 280 | } else { |
281 | drvdata->src = pdata.default_src - 1; | 281 | drvdata->src = pdata.default_src - 1; |
282 | } | 282 | } |
283 | 283 | ||
284 | drvdata->cdev.name = pdata.name; | 284 | drvdata->cdev.name = pdata.name; |
285 | drvdata->cdev.default_trigger = pdata.default_trigger; | 285 | drvdata->cdev.default_trigger = pdata.default_trigger; |
286 | drvdata->cdev.brightness_set = wm831x_status_set; | 286 | drvdata->cdev.brightness_set = wm831x_status_set; |
287 | drvdata->cdev.blink_set = wm831x_status_blink_set; | 287 | drvdata->cdev.blink_set = wm831x_status_blink_set; |
288 | 288 | ||
289 | ret = led_classdev_register(wm831x->dev, &drvdata->cdev); | 289 | ret = led_classdev_register(wm831x->dev, &drvdata->cdev); |
290 | if (ret < 0) { | 290 | if (ret < 0) { |
291 | dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); | 291 | dev_err(&pdev->dev, "Failed to register LED: %d\n", ret); |
292 | goto err_led; | 292 | goto err_led; |
293 | } | 293 | } |
294 | 294 | ||
295 | ret = device_create_file(drvdata->cdev.dev, &dev_attr_src); | 295 | ret = device_create_file(drvdata->cdev.dev, &dev_attr_src); |
296 | if (ret != 0) | 296 | if (ret != 0) |
297 | dev_err(&pdev->dev, | 297 | dev_err(&pdev->dev, |
298 | "No source control for LED: %d\n", ret); | 298 | "No source control for LED: %d\n", ret); |
299 | 299 | ||
300 | return 0; | 300 | return 0; |
301 | 301 | ||
302 | err_led: | 302 | err_led: |
303 | led_classdev_unregister(&drvdata->cdev); | 303 | led_classdev_unregister(&drvdata->cdev); |
304 | err: | 304 | err: |
305 | return ret; | 305 | return ret; |
306 | } | 306 | } |
307 | 307 | ||
308 | static int wm831x_status_remove(struct platform_device *pdev) | 308 | static int wm831x_status_remove(struct platform_device *pdev) |
309 | { | 309 | { |
310 | struct wm831x_status *drvdata = platform_get_drvdata(pdev); | 310 | struct wm831x_status *drvdata = platform_get_drvdata(pdev); |
311 | 311 | ||
312 | device_remove_file(drvdata->cdev.dev, &dev_attr_src); | 312 | device_remove_file(drvdata->cdev.dev, &dev_attr_src); |
313 | led_classdev_unregister(&drvdata->cdev); | 313 | led_classdev_unregister(&drvdata->cdev); |
314 | 314 | ||
315 | return 0; | 315 | return 0; |
316 | } | 316 | } |
317 | 317 | ||
318 | static struct platform_driver wm831x_status_driver = { | 318 | static struct platform_driver wm831x_status_driver = { |
319 | .driver = { | 319 | .driver = { |
320 | .name = "wm831x-status", | 320 | .name = "wm831x-status", |
321 | .owner = THIS_MODULE, | 321 | .owner = THIS_MODULE, |
322 | }, | 322 | }, |
323 | .probe = wm831x_status_probe, | 323 | .probe = wm831x_status_probe, |
324 | .remove = wm831x_status_remove, | 324 | .remove = wm831x_status_remove, |
325 | }; | 325 | }; |
326 | 326 | ||
327 | module_platform_driver(wm831x_status_driver); | 327 | module_platform_driver(wm831x_status_driver); |
328 | 328 | ||
329 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | 329 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |
330 | MODULE_DESCRIPTION("WM831x status LED driver"); | 330 | MODULE_DESCRIPTION("WM831x status LED driver"); |
331 | MODULE_LICENSE("GPL"); | 331 | MODULE_LICENSE("GPL"); |
332 | MODULE_ALIAS("platform:wm831x-status"); | 332 | MODULE_ALIAS("platform:wm831x-status"); |
333 | 333 |
drivers/leds/leds-wm8350.c
1 | /* | 1 | /* |
2 | * LED driver for WM8350 driven LEDS. | 2 | * LED driver for WM8350 driven LEDS. |
3 | * | 3 | * |
4 | * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC. | 4 | * Copyright(C) 2007, 2008 Wolfson Microelectronics PLC. |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
15 | #include <linux/leds.h> | 15 | #include <linux/leds.h> |
16 | #include <linux/err.h> | 16 | #include <linux/err.h> |
17 | #include <linux/mfd/wm8350/pmic.h> | 17 | #include <linux/mfd/wm8350/pmic.h> |
18 | #include <linux/regulator/consumer.h> | 18 | #include <linux/regulator/consumer.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | 21 | ||
22 | /* Microamps */ | 22 | /* Microamps */ |
23 | static const int isink_cur[] = { | 23 | static const int isink_cur[] = { |
24 | 4, | 24 | 4, |
25 | 5, | 25 | 5, |
26 | 6, | 26 | 6, |
27 | 7, | 27 | 7, |
28 | 8, | 28 | 8, |
29 | 10, | 29 | 10, |
30 | 11, | 30 | 11, |
31 | 14, | 31 | 14, |
32 | 16, | 32 | 16, |
33 | 19, | 33 | 19, |
34 | 23, | 34 | 23, |
35 | 27, | 35 | 27, |
36 | 32, | 36 | 32, |
37 | 39, | 37 | 39, |
38 | 46, | 38 | 46, |
39 | 54, | 39 | 54, |
40 | 65, | 40 | 65, |
41 | 77, | 41 | 77, |
42 | 92, | 42 | 92, |
43 | 109, | 43 | 109, |
44 | 130, | 44 | 130, |
45 | 154, | 45 | 154, |
46 | 183, | 46 | 183, |
47 | 218, | 47 | 218, |
48 | 259, | 48 | 259, |
49 | 308, | 49 | 308, |
50 | 367, | 50 | 367, |
51 | 436, | 51 | 436, |
52 | 518, | 52 | 518, |
53 | 616, | 53 | 616, |
54 | 733, | 54 | 733, |
55 | 872, | 55 | 872, |
56 | 1037, | 56 | 1037, |
57 | 1233, | 57 | 1233, |
58 | 1466, | 58 | 1466, |
59 | 1744, | 59 | 1744, |
60 | 2073, | 60 | 2073, |
61 | 2466, | 61 | 2466, |
62 | 2933, | 62 | 2933, |
63 | 3487, | 63 | 3487, |
64 | 4147, | 64 | 4147, |
65 | 4932, | 65 | 4932, |
66 | 5865, | 66 | 5865, |
67 | 6975, | 67 | 6975, |
68 | 8294, | 68 | 8294, |
69 | 9864, | 69 | 9864, |
70 | 11730, | 70 | 11730, |
71 | 13949, | 71 | 13949, |
72 | 16589, | 72 | 16589, |
73 | 19728, | 73 | 19728, |
74 | 23460, | 74 | 23460, |
75 | 27899, | 75 | 27899, |
76 | 33178, | 76 | 33178, |
77 | 39455, | 77 | 39455, |
78 | 46920, | 78 | 46920, |
79 | 55798, | 79 | 55798, |
80 | 66355, | 80 | 66355, |
81 | 78910, | 81 | 78910, |
82 | 93840, | 82 | 93840, |
83 | 111596, | 83 | 111596, |
84 | 132710, | 84 | 132710, |
85 | 157820, | 85 | 157820, |
86 | 187681, | 86 | 187681, |
87 | 223191 | 87 | 223191 |
88 | }; | 88 | }; |
89 | 89 | ||
90 | #define to_wm8350_led(led_cdev) \ | 90 | #define to_wm8350_led(led_cdev) \ |
91 | container_of(led_cdev, struct wm8350_led, cdev) | 91 | container_of(led_cdev, struct wm8350_led, cdev) |
92 | 92 | ||
93 | static void wm8350_led_enable(struct wm8350_led *led) | 93 | static void wm8350_led_enable(struct wm8350_led *led) |
94 | { | 94 | { |
95 | int ret; | 95 | int ret; |
96 | 96 | ||
97 | if (led->enabled) | 97 | if (led->enabled) |
98 | return; | 98 | return; |
99 | 99 | ||
100 | ret = regulator_enable(led->isink); | 100 | ret = regulator_enable(led->isink); |
101 | if (ret != 0) { | 101 | if (ret != 0) { |
102 | dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret); | 102 | dev_err(led->cdev.dev, "Failed to enable ISINK: %d\n", ret); |
103 | return; | 103 | return; |
104 | } | 104 | } |
105 | 105 | ||
106 | ret = regulator_enable(led->dcdc); | 106 | ret = regulator_enable(led->dcdc); |
107 | if (ret != 0) { | 107 | if (ret != 0) { |
108 | dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret); | 108 | dev_err(led->cdev.dev, "Failed to enable DCDC: %d\n", ret); |
109 | regulator_disable(led->isink); | 109 | regulator_disable(led->isink); |
110 | return; | 110 | return; |
111 | } | 111 | } |
112 | 112 | ||
113 | led->enabled = 1; | 113 | led->enabled = 1; |
114 | } | 114 | } |
115 | 115 | ||
116 | static void wm8350_led_disable(struct wm8350_led *led) | 116 | static void wm8350_led_disable(struct wm8350_led *led) |
117 | { | 117 | { |
118 | int ret; | 118 | int ret; |
119 | 119 | ||
120 | if (!led->enabled) | 120 | if (!led->enabled) |
121 | return; | 121 | return; |
122 | 122 | ||
123 | ret = regulator_disable(led->dcdc); | 123 | ret = regulator_disable(led->dcdc); |
124 | if (ret != 0) { | 124 | if (ret != 0) { |
125 | dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret); | 125 | dev_err(led->cdev.dev, "Failed to disable DCDC: %d\n", ret); |
126 | return; | 126 | return; |
127 | } | 127 | } |
128 | 128 | ||
129 | ret = regulator_disable(led->isink); | 129 | ret = regulator_disable(led->isink); |
130 | if (ret != 0) { | 130 | if (ret != 0) { |
131 | dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret); | 131 | dev_err(led->cdev.dev, "Failed to disable ISINK: %d\n", ret); |
132 | ret = regulator_enable(led->dcdc); | 132 | ret = regulator_enable(led->dcdc); |
133 | if (ret != 0) | 133 | if (ret != 0) |
134 | dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n", | 134 | dev_err(led->cdev.dev, "Failed to reenable DCDC: %d\n", |
135 | ret); | 135 | ret); |
136 | return; | 136 | return; |
137 | } | 137 | } |
138 | 138 | ||
139 | led->enabled = 0; | 139 | led->enabled = 0; |
140 | } | 140 | } |
141 | 141 | ||
142 | static void led_work(struct work_struct *work) | 142 | static void led_work(struct work_struct *work) |
143 | { | 143 | { |
144 | struct wm8350_led *led = container_of(work, struct wm8350_led, work); | 144 | struct wm8350_led *led = container_of(work, struct wm8350_led, work); |
145 | int ret; | 145 | int ret; |
146 | int uA; | 146 | int uA; |
147 | unsigned long flags; | 147 | unsigned long flags; |
148 | 148 | ||
149 | mutex_lock(&led->mutex); | 149 | mutex_lock(&led->mutex); |
150 | 150 | ||
151 | spin_lock_irqsave(&led->value_lock, flags); | 151 | spin_lock_irqsave(&led->value_lock, flags); |
152 | 152 | ||
153 | if (led->value == LED_OFF) { | 153 | if (led->value == LED_OFF) { |
154 | spin_unlock_irqrestore(&led->value_lock, flags); | 154 | spin_unlock_irqrestore(&led->value_lock, flags); |
155 | wm8350_led_disable(led); | 155 | wm8350_led_disable(led); |
156 | goto out; | 156 | goto out; |
157 | } | 157 | } |
158 | 158 | ||
159 | /* This scales linearly into the index of valid current | 159 | /* This scales linearly into the index of valid current |
160 | * settings which results in a linear scaling of perceived | 160 | * settings which results in a linear scaling of perceived |
161 | * brightness due to the non-linear current settings provided | 161 | * brightness due to the non-linear current settings provided |
162 | * by the hardware. | 162 | * by the hardware. |
163 | */ | 163 | */ |
164 | uA = (led->max_uA_index * led->value) / LED_FULL; | 164 | uA = (led->max_uA_index * led->value) / LED_FULL; |
165 | spin_unlock_irqrestore(&led->value_lock, flags); | 165 | spin_unlock_irqrestore(&led->value_lock, flags); |
166 | BUG_ON(uA >= ARRAY_SIZE(isink_cur)); | 166 | BUG_ON(uA >= ARRAY_SIZE(isink_cur)); |
167 | 167 | ||
168 | ret = regulator_set_current_limit(led->isink, isink_cur[uA], | 168 | ret = regulator_set_current_limit(led->isink, isink_cur[uA], |
169 | isink_cur[uA]); | 169 | isink_cur[uA]); |
170 | if (ret != 0) | 170 | if (ret != 0) |
171 | dev_err(led->cdev.dev, "Failed to set %duA: %d\n", | 171 | dev_err(led->cdev.dev, "Failed to set %duA: %d\n", |
172 | isink_cur[uA], ret); | 172 | isink_cur[uA], ret); |
173 | 173 | ||
174 | wm8350_led_enable(led); | 174 | wm8350_led_enable(led); |
175 | 175 | ||
176 | out: | 176 | out: |
177 | mutex_unlock(&led->mutex); | 177 | mutex_unlock(&led->mutex); |
178 | } | 178 | } |
179 | 179 | ||
180 | static void wm8350_led_set(struct led_classdev *led_cdev, | 180 | static void wm8350_led_set(struct led_classdev *led_cdev, |
181 | enum led_brightness value) | 181 | enum led_brightness value) |
182 | { | 182 | { |
183 | struct wm8350_led *led = to_wm8350_led(led_cdev); | 183 | struct wm8350_led *led = to_wm8350_led(led_cdev); |
184 | unsigned long flags; | 184 | unsigned long flags; |
185 | 185 | ||
186 | spin_lock_irqsave(&led->value_lock, flags); | 186 | spin_lock_irqsave(&led->value_lock, flags); |
187 | led->value = value; | 187 | led->value = value; |
188 | schedule_work(&led->work); | 188 | schedule_work(&led->work); |
189 | spin_unlock_irqrestore(&led->value_lock, flags); | 189 | spin_unlock_irqrestore(&led->value_lock, flags); |
190 | } | 190 | } |
191 | 191 | ||
192 | static void wm8350_led_shutdown(struct platform_device *pdev) | 192 | static void wm8350_led_shutdown(struct platform_device *pdev) |
193 | { | 193 | { |
194 | struct wm8350_led *led = platform_get_drvdata(pdev); | 194 | struct wm8350_led *led = platform_get_drvdata(pdev); |
195 | 195 | ||
196 | mutex_lock(&led->mutex); | 196 | mutex_lock(&led->mutex); |
197 | led->value = LED_OFF; | 197 | led->value = LED_OFF; |
198 | wm8350_led_disable(led); | 198 | wm8350_led_disable(led); |
199 | mutex_unlock(&led->mutex); | 199 | mutex_unlock(&led->mutex); |
200 | } | 200 | } |
201 | 201 | ||
202 | static int wm8350_led_probe(struct platform_device *pdev) | 202 | static int wm8350_led_probe(struct platform_device *pdev) |
203 | { | 203 | { |
204 | struct regulator *isink, *dcdc; | 204 | struct regulator *isink, *dcdc; |
205 | struct wm8350_led *led; | 205 | struct wm8350_led *led; |
206 | struct wm8350_led_platform_data *pdata = pdev->dev.platform_data; | 206 | struct wm8350_led_platform_data *pdata = dev_get_platdata(&pdev->dev); |
207 | int i; | 207 | int i; |
208 | 208 | ||
209 | if (pdata == NULL) { | 209 | if (pdata == NULL) { |
210 | dev_err(&pdev->dev, "no platform data\n"); | 210 | dev_err(&pdev->dev, "no platform data\n"); |
211 | return -ENODEV; | 211 | return -ENODEV; |
212 | } | 212 | } |
213 | 213 | ||
214 | if (pdata->max_uA < isink_cur[0]) { | 214 | if (pdata->max_uA < isink_cur[0]) { |
215 | dev_err(&pdev->dev, "Invalid maximum current %duA\n", | 215 | dev_err(&pdev->dev, "Invalid maximum current %duA\n", |
216 | pdata->max_uA); | 216 | pdata->max_uA); |
217 | return -EINVAL; | 217 | return -EINVAL; |
218 | } | 218 | } |
219 | 219 | ||
220 | isink = devm_regulator_get(&pdev->dev, "led_isink"); | 220 | isink = devm_regulator_get(&pdev->dev, "led_isink"); |
221 | if (IS_ERR(isink)) { | 221 | if (IS_ERR(isink)) { |
222 | dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__); | 222 | dev_err(&pdev->dev, "%s: can't get ISINK\n", __func__); |
223 | return PTR_ERR(isink); | 223 | return PTR_ERR(isink); |
224 | } | 224 | } |
225 | 225 | ||
226 | dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); | 226 | dcdc = devm_regulator_get(&pdev->dev, "led_vcc"); |
227 | if (IS_ERR(dcdc)) { | 227 | if (IS_ERR(dcdc)) { |
228 | dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__); | 228 | dev_err(&pdev->dev, "%s: can't get DCDC\n", __func__); |
229 | return PTR_ERR(dcdc); | 229 | return PTR_ERR(dcdc); |
230 | } | 230 | } |
231 | 231 | ||
232 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); | 232 | led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL); |
233 | if (led == NULL) | 233 | if (led == NULL) |
234 | return -ENOMEM; | 234 | return -ENOMEM; |
235 | 235 | ||
236 | led->cdev.brightness_set = wm8350_led_set; | 236 | led->cdev.brightness_set = wm8350_led_set; |
237 | led->cdev.default_trigger = pdata->default_trigger; | 237 | led->cdev.default_trigger = pdata->default_trigger; |
238 | led->cdev.name = pdata->name; | 238 | led->cdev.name = pdata->name; |
239 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; | 239 | led->cdev.flags |= LED_CORE_SUSPENDRESUME; |
240 | led->enabled = regulator_is_enabled(isink); | 240 | led->enabled = regulator_is_enabled(isink); |
241 | led->isink = isink; | 241 | led->isink = isink; |
242 | led->dcdc = dcdc; | 242 | led->dcdc = dcdc; |
243 | 243 | ||
244 | for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) | 244 | for (i = 0; i < ARRAY_SIZE(isink_cur) - 1; i++) |
245 | if (isink_cur[i] >= pdata->max_uA) | 245 | if (isink_cur[i] >= pdata->max_uA) |
246 | break; | 246 | break; |
247 | led->max_uA_index = i; | 247 | led->max_uA_index = i; |
248 | if (pdata->max_uA != isink_cur[i]) | 248 | if (pdata->max_uA != isink_cur[i]) |
249 | dev_warn(&pdev->dev, | 249 | dev_warn(&pdev->dev, |
250 | "Maximum current %duA is not directly supported," | 250 | "Maximum current %duA is not directly supported," |
251 | " check platform data\n", | 251 | " check platform data\n", |
252 | pdata->max_uA); | 252 | pdata->max_uA); |
253 | 253 | ||
254 | spin_lock_init(&led->value_lock); | 254 | spin_lock_init(&led->value_lock); |
255 | mutex_init(&led->mutex); | 255 | mutex_init(&led->mutex); |
256 | INIT_WORK(&led->work, led_work); | 256 | INIT_WORK(&led->work, led_work); |
257 | led->value = LED_OFF; | 257 | led->value = LED_OFF; |
258 | platform_set_drvdata(pdev, led); | 258 | platform_set_drvdata(pdev, led); |
259 | 259 | ||
260 | return led_classdev_register(&pdev->dev, &led->cdev); | 260 | return led_classdev_register(&pdev->dev, &led->cdev); |
261 | } | 261 | } |
262 | 262 | ||
263 | static int wm8350_led_remove(struct platform_device *pdev) | 263 | static int wm8350_led_remove(struct platform_device *pdev) |
264 | { | 264 | { |
265 | struct wm8350_led *led = platform_get_drvdata(pdev); | 265 | struct wm8350_led *led = platform_get_drvdata(pdev); |
266 | 266 | ||
267 | led_classdev_unregister(&led->cdev); | 267 | led_classdev_unregister(&led->cdev); |
268 | flush_work(&led->work); | 268 | flush_work(&led->work); |
269 | wm8350_led_disable(led); | 269 | wm8350_led_disable(led); |
270 | return 0; | 270 | return 0; |
271 | } | 271 | } |
272 | 272 | ||
273 | static struct platform_driver wm8350_led_driver = { | 273 | static struct platform_driver wm8350_led_driver = { |
274 | .driver = { | 274 | .driver = { |
275 | .name = "wm8350-led", | 275 | .name = "wm8350-led", |
276 | .owner = THIS_MODULE, | 276 | .owner = THIS_MODULE, |
277 | }, | 277 | }, |
278 | .probe = wm8350_led_probe, | 278 | .probe = wm8350_led_probe, |
279 | .remove = wm8350_led_remove, | 279 | .remove = wm8350_led_remove, |
280 | .shutdown = wm8350_led_shutdown, | 280 | .shutdown = wm8350_led_shutdown, |
281 | }; | 281 | }; |
282 | 282 | ||
283 | module_platform_driver(wm8350_led_driver); | 283 | module_platform_driver(wm8350_led_driver); |
284 | 284 | ||
285 | MODULE_AUTHOR("Mark Brown"); | 285 | MODULE_AUTHOR("Mark Brown"); |
286 | MODULE_DESCRIPTION("WM8350 LED driver"); | 286 | MODULE_DESCRIPTION("WM8350 LED driver"); |
287 | MODULE_LICENSE("GPL"); | 287 | MODULE_LICENSE("GPL"); |
288 | MODULE_ALIAS("platform:wm8350-led"); | 288 | MODULE_ALIAS("platform:wm8350-led"); |
289 | 289 |