disable sstrip when using musl
[openwrt/openwrt.git] / target / linux / ubicom32 / files / drivers / video / backlight / ubicom32bl.c
1 /*
2 * drivers/video/backlight/ubicom32bl.c
3 * Backlight driver for the Ubicom32 platform
4 *
5 * (C) Copyright 2009, Ubicom, Inc.
6 *
7 * This file is part of the Ubicom32 Linux Kernel Port.
8 *
9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
10 * it and/or modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation, either version 2 of the
12 * License, or (at your option) any later version.
13 *
14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with the Ubicom32 Linux Kernel Port. If not,
21 * see <http://www.gnu.org/licenses/>.
22 *
23 * Ubicom32 implementation derived from (with many thanks):
24 * arch/m68knommu
25 * arch/blackfin
26 * arch/parisc
27 */
28 #include <linux/init.h>
29 #include <linux/kernel.h>
30 #include <linux/module.h>
31 #include <linux/platform_device.h>
32 #include <linux/backlight.h>
33 #include <linux/fb.h>
34
35 #include <asm/ubicom32bl.h>
36 #include <asm/ip5000.h>
37
38 #define DRIVER_NAME "ubicom32bl"
39 #define UBICOM32BL_MAX_BRIGHTNESS 255
40
41 struct ubicom32bl_data {
42 /*
43 * Pointer to the platform data structure. Keep this around since we need values
44 * from it to set the backlight intensity.
45 */
46 const struct ubicom32bl_platform_data *pdata;
47
48 /*
49 * Backlight device, we have to save this for use when we remove ourselves.
50 */
51 struct backlight_device *bldev;
52
53 /*
54 * Current intensity, used for get_intensity.
55 */
56 int cur_intensity;
57
58 /*
59 * Init function for PWM
60 */
61 int (*init_fn)(struct ubicom32bl_data *);
62
63 /*
64 * Set intensity function depending on the backlight type
65 */
66 int (*set_intensity_fn)(struct ubicom32bl_data *, int);
67 };
68
69 /*
70 * ubicom32bl_set_intensity_gpio
71 */
72 static int ubicom32bl_set_intensity_gpio(struct ubicom32bl_data *ud, int intensity)
73 {
74 ud->cur_intensity = intensity ? 255 : 0;
75
76 if (intensity) {
77 // set gpio
78 return 0;
79 }
80
81 // clear gpio
82 return 0;
83 }
84
85 /*
86 * ubicom32bl_set_intensity_hw
87 */
88 static int ubicom32bl_set_intensity_hw(struct ubicom32bl_data *ud, int intensity)
89 {
90 u16_t period = ud->pdata->pwm_period;
91 u16_t duty;
92
93 /*
94 * Calculate the new duty cycle
95 */
96 duty = (period * intensity) / (UBICOM32BL_MAX_BRIGHTNESS + 1);
97
98 /*
99 * Set the new duty cycle
100 */
101 switch (ud->pdata->pwm_channel) {
102 case 0:
103 /*
104 * Channel 0 is in the lower half of PORT C ctl0 and ctl1
105 */
106 UBICOM32_IO_PORT(RC)->ctl1 = (ud->pdata->pwm_period << 16) | duty;
107 break;
108
109 case 1:
110 /*
111 * Channel 1 is in the upper half of PORT C ctl0 and ctl2
112 */
113 UBICOM32_IO_PORT(RC)->ctl2 = (ud->pdata->pwm_period << 16) | duty;
114 break;
115
116 case 2:
117 /*
118 * Channel 2 is in PORT H ctl0 and ctl1
119 */
120 UBICOM32_IO_PORT(RH)->ctl1 = (ud->pdata->pwm_period << 16) | duty;
121 break;
122 }
123
124 ud->cur_intensity = intensity;
125
126 return 0;
127 }
128
129 /*
130 * ubicom32bl_set_intensity
131 */
132 static int ubicom32bl_set_intensity(struct backlight_device *bd)
133 {
134 struct ubicom32bl_data *ud = (struct ubicom32bl_data *)bl_get_data(bd);
135 int intensity = bd->props.brightness;
136
137 /*
138 * If we're blanked the the intensity doesn't matter.
139 */
140 if ((bd->props.power != FB_BLANK_UNBLANK) || (bd->props.fb_blank != FB_BLANK_UNBLANK)) {
141 intensity = 0;
142 }
143
144 /*
145 * Check for inverted backlight.
146 */
147 if (ud->pdata->invert) {
148 intensity = UBICOM32BL_MAX_BRIGHTNESS - intensity;
149 }
150
151 if (ud->set_intensity_fn) {
152 return ud->set_intensity_fn(ud, intensity);
153 }
154
155 return -ENXIO;
156 }
157
158 /*
159 * ubicom32bl_get_intensity
160 * Return the current intensity of the backlight.
161 */
162 static int ubicom32bl_get_intensity(struct backlight_device *bd)
163 {
164 struct ubicom32bl_data *ud = (struct ubicom32bl_data *)bl_get_data(bd);
165
166 return ud->cur_intensity;
167 }
168
169 /*
170 * ubicom32bl_init_hw_pwm
171 * Set the appropriate PWM registers
172 */
173 static int ubicom32bl_init_hw_pwm(struct ubicom32bl_data *ud)
174 {
175 /*
176 * bit 13: enable
177 */
178 u16_t pwm_cfg = (1 << 13) | (ud->pdata->pwm_prescale << 8) ;
179
180 switch (ud->pdata->pwm_channel) {
181 case 0:
182 /*
183 * Channel 0 is in the lower half of PORT C ctl0 and ctl1 (PA5)
184 */
185 UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF;
186 UBICOM32_IO_PORT(RC)->ctl0 |= pwm_cfg;
187 UBICOM32_IO_PORT(RC)->ctl1 = ud->pdata->pwm_period << 16;
188
189 /*
190 * If the port function is not set, set it to GPIO/PWM
191 */
192 if (!UBICOM32_IO_PORT(RA)->function) {
193 UBICOM32_IO_PORT(RA)->function = 3;
194 }
195 break;
196
197 case 1:
198 /*
199 * Channel 1 is in the upper half of PORT C ctl0 and ctl2 (PE4)
200 */
201 UBICOM32_IO_PORT(RC)->ctl0 &= ~0xFFFF0000;
202 UBICOM32_IO_PORT(RC)->ctl0 |= (pwm_cfg << 16);
203 UBICOM32_IO_PORT(RC)->ctl2 = ud->pdata->pwm_period << 16;
204
205 /*
206 * If the port function is not set, set it to GPIO/ExtIOInt
207 */
208 if (!UBICOM32_IO_PORT(RE)->function) {
209 UBICOM32_IO_PORT(RE)->function = 3;
210 }
211 break;
212
213 case 2:
214 /*
215 * Channel 2 is in PORT H ctl0 and ctl1 (PD0)
216 */
217 UBICOM32_IO_PORT(RH)->ctl0 &= ~0xFFFF0000;
218 UBICOM32_IO_PORT(RH)->ctl0 = pwm_cfg;
219 UBICOM32_IO_PORT(RH)->ctl1 = ud->pdata->pwm_period << 16;
220
221 /*
222 * If the port function is not set, set it to GPIO
223 */
224 if (!UBICOM32_IO_PORT(RD)->function) {
225 UBICOM32_IO_PORT(RD)->function = 3;
226 }
227 break;
228 }
229
230 return 0;
231 }
232
233 /*
234 * ubicom32bl_init_gpio
235 * Allocate the appropriate GPIO
236 */
237 static int ubicom32bl_init_gpio(struct ubicom32bl_data *ud)
238 {
239 return 0;
240 }
241
242 static struct backlight_ops ubicom32bl_ops = {
243 .get_brightness = ubicom32bl_get_intensity,
244 .update_status = ubicom32bl_set_intensity,
245 };
246
247 /*
248 * ubicom32bl_probe
249 */
250 static int ubicom32bl_probe(struct platform_device *pdev)
251 {
252 const struct ubicom32bl_platform_data *pdata = pdev->dev.platform_data;
253 struct ubicom32bl_data *ud;
254 struct backlight_device *bldev;
255 int retval;
256
257 /*
258 * Check to see if we have any platform data, if we don't then the backlight is not
259 * configured on this device.
260 */
261 if (!pdata) {
262 return -ENODEV;
263 }
264
265 /*
266 * Allocate our private data
267 */
268 ud = kzalloc(sizeof(struct ubicom32bl_data), GFP_KERNEL);
269 if (!ud) {
270 return -ENOMEM;
271 }
272
273 ud->pdata = pdata;
274
275 /*
276 * Check to see that the platform data is valid for this driver
277 */
278 switch (pdata->type) {
279 case UBICOM32BL_TYPE_PWM:
280 {
281 /*
282 * Make sure we have a PWM peripheral
283 */
284 u32_t chipid;
285 asm volatile (
286 "move.4 %0, CHIP_ID \n\t"
287 : "=r" (chipid)
288 );
289 if (chipid != 0x00030001) {
290 retval = -ENODEV;
291 goto fail;
292 }
293
294 if (pdata->pwm_channel > 3) {
295 retval = -ENODEV;
296 goto fail;
297 }
298 if (pdata->pwm_prescale > 16) {
299 retval = -EINVAL;
300 goto fail;
301 }
302
303 ud->init_fn = ubicom32bl_init_hw_pwm;
304 ud->set_intensity_fn = ubicom32bl_set_intensity_hw;
305 break;
306 }
307
308 case UBICOM32BL_TYPE_PWM_HRT:
309 // For now, PWM HRT devices are treated as binary lights.
310
311 case UBICOM32BL_TYPE_BINARY:
312 ud->init_fn = ubicom32bl_init_gpio;
313 ud->set_intensity_fn = ubicom32bl_set_intensity_gpio;
314 break;
315 }
316
317 /*
318 * Register our backlight device
319 */
320 bldev = backlight_device_register(DRIVER_NAME, &pdev->dev, ud, &ubicom32bl_ops);
321 if (IS_ERR(bldev)) {
322 retval = PTR_ERR(bldev);
323 goto fail;
324 }
325
326 ud->bldev = bldev;
327 ud->cur_intensity = pdata->default_intensity;
328 platform_set_drvdata(pdev, ud);
329
330 /*
331 * Start up the backlight at the prescribed default intensity
332 */
333 bldev->props.power = FB_BLANK_UNBLANK;
334 bldev->props.max_brightness = UBICOM32BL_MAX_BRIGHTNESS;
335 bldev->props.brightness = pdata->default_intensity;
336
337 if (ud->init_fn) {
338 if (ud->init_fn(ud) != 0) {
339 retval = -ENODEV;
340 backlight_device_unregister(ud->bldev);
341 goto fail;
342 }
343 }
344 ubicom32bl_set_intensity(bldev);
345
346 printk(KERN_INFO DRIVER_NAME ": Backlight driver started\n");
347
348 return 0;
349
350 fail:
351 platform_set_drvdata(pdev, NULL);
352 kfree(ud);
353 return retval;
354 }
355
356 /*
357 * ubicom32bl_remove
358 */
359 static int __exit ubicom32bl_remove(struct platform_device *pdev)
360 {
361 struct ubicom32bl_data *ud = platform_get_drvdata(pdev);
362
363 backlight_device_unregister(ud->bldev);
364 platform_set_drvdata(pdev, NULL);
365 kfree(ud);
366
367 return 0;
368 }
369
370 static struct platform_driver ubicom32bl_driver = {
371 .driver = {
372 .name = DRIVER_NAME,
373 .owner = THIS_MODULE,
374 },
375
376 .remove = __exit_p(ubicom32bl_remove),
377 };
378
379 /*
380 * ubicom32bl_init
381 */
382 static int __init ubicom32bl_init(void)
383 {
384 return platform_driver_probe(&ubicom32bl_driver, ubicom32bl_probe);
385 }
386 module_init(ubicom32bl_init);
387
388 /*
389 * ubicom32bl_exit
390 */
391 static void __exit ubicom32bl_exit(void)
392 {
393 platform_driver_unregister(&ubicom32bl_driver);
394 }
395 module_exit(ubicom32bl_exit);
396
397 MODULE_AUTHOR("Patrick Tjin <@ubicom.com>");
398 MODULE_DESCRIPTION("Ubicom32 backlight driver");
399 MODULE_LICENSE("GPL");