disable sstrip when using musl
[lede.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");