disable sstrip when using musl
[lede.git] / target / linux / ubicom32 / files / drivers / video / backlight / ubicom32lcd.c
1 /*
2  * drivers/video/ubicom32lcd.c
3  *      LCD initilization code
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 #include <linux/init.h>
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/delay.h>
28
29 #include <asm/ip5000.h>
30 #include <asm/gpio.h>
31 #include <asm/ubicom32lcd.h>
32
33 #include "ubicom32lcd.h"
34
35 #define DRIVER_NAME                     "ubicom32lcd"
36
37 struct ubicom32lcd_data {
38         const struct ubicom32lcd_panel  *panel;
39
40         int                             pin_cs;
41         int                             pin_rd;
42         int                             pin_rs;
43         int                             pin_wr;
44         int                             pin_reset;
45         struct ubicom32_io_port         *port_data;
46         int                             data_shift;
47 };
48
49 /*
50  * ubicom32lcd_write
51  *      Performs a write cycle on the bus (assumes CS asserted, RD & WR set)
52  */
53 static void ubicom32lcd_write(struct ubicom32lcd_data *ud, int command, u16 data)
54 {
55         if (command) {
56                 UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rs);
57         } else {
58                 UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs);
59         }
60
61         asm volatile (
62                 "or.4   4(%[port]), 4(%[port]), %[mask] \n\t"
63                 "not.4  %[mask], %[mask]                \n\t"
64                 "and.4  8(%[port]), 8(%[port]), %[mask] \n\t"
65                 "or.4   8(%[port]), 8(%[port]), %[cmd]  \n\t"
66                 :
67                 : [port] "a" (ud->port_data),
68                   [mask] "d" (0xFFFF << ud->data_shift),
69                   [cmd] "d" (data << ud->data_shift)
70                 : "cc"
71         );
72
73         UBICOM32_GPIO_SET_PIN_LOW(ud->pin_wr);
74
75         //ndelay(50);
76         udelay(1);
77
78         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr);
79
80         udelay(1);
81         //ndelay(50);
82 }
83
84 /*
85  * ubicom32lcd_read_data
86  *      Performs a read cycle on the bus (assumes CS asserted, RD & WR set)
87  */
88 static u16 ubicom32lcd_read_data(struct ubicom32lcd_data *ud)
89 {
90         u32_t data;
91
92         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs);
93
94         asm volatile (
95                 "and.4  4(%[port]), 4(%[port]), %[mask]\n\t"
96                 :
97                 : [port] "a" (ud->port_data),
98                   [mask] "d" (~(0xFFFF << ud->data_shift))
99                 : "cc"
100         );
101
102         UBICOM32_GPIO_SET_PIN_LOW(ud->pin_rd);
103
104         ndelay(300);
105
106         asm volatile (
107                 "lsr.4  %[data], 12(%[port]), %[shamt]  \n\t"
108                 "and.4  %[data], %[data], %[mask]       \n\t"
109                 : [data] "=d" (data)
110                 : [port] "a" (ud->port_data),
111                   [mask] "d" (0xFFFF),
112                   [shamt] "d" (ud->data_shift)
113                 : "cc"
114         );
115
116         ndelay(200);
117
118         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd);
119
120         ndelay(500);
121
122         return data;
123 }
124
125 /*
126  * ubicom32lcd_execute
127  *      Executes a script for performing operations on the LCD (assumes CS set)
128  */
129 static void ubicom32lcd_execute(struct ubicom32lcd_data *ud, const struct ubicom32lcd_step *script)
130 {
131         while (1) {
132                 switch (script->op) {
133                 case LCD_STEP_CMD:
134                         ubicom32lcd_write(ud, 1, script->cmd);
135                         break;
136
137                 case LCD_STEP_DATA:
138                         ubicom32lcd_write(ud, 0, script->data);
139                         break;
140
141                 case LCD_STEP_CMD_DATA:
142                         ubicom32lcd_write(ud, 1, script->cmd);
143                         ubicom32lcd_write(ud, 0, script->data);
144                         break;
145
146                 case LCD_STEP_SLEEP:
147                         udelay(script->data);
148                         break;
149
150                 case LCD_STEP_DONE:
151                         return;
152                 }
153                 script++;
154         }
155 }
156
157 /*
158  * ubicom32lcd_goto
159  *      Places the gram pointer at a specific X, Y address
160  */
161 static void ubicom32lcd_goto(struct ubicom32lcd_data *ud, int x, int y)
162 {
163         ubicom32lcd_write(ud, 1, ud->panel->horz_reg);
164         ubicom32lcd_write(ud, 0, x);
165         ubicom32lcd_write(ud, 1, ud->panel->vert_reg);
166         ubicom32lcd_write(ud, 0, y);
167         ubicom32lcd_write(ud, 1, ud->panel->gram_reg);
168 }
169
170 /*
171  * ubicom32lcd_panel_init
172  *      Initializes the lcd panel.
173  */
174 static int ubicom32lcd_panel_init(struct ubicom32lcd_data *ud)
175 {
176         u16 id;
177
178         UBICOM32_GPIO_SET_PIN_LOW(ud->pin_reset);
179         UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_reset);
180         UBICOM32_GPIO_ENABLE(ud->pin_reset);
181
182         asm volatile (
183                 "or.4   0x50(%[port]), 0x50(%[port]), %[mask]   \n\t"
184                 "not.4  %[mask], %[mask]                        \n\t"
185                 "and.4  0x04(%[port]), 0x04(%[port]), %[mask]   \n\t"
186                 :
187                 : [port] "a" (ud->port_data),
188                   [mask] "d" (0xFFFF << ud->data_shift)
189                 : "cc"
190         );
191
192         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs);
193         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd);
194         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr);
195         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs);
196
197         UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rs);
198         UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_rd);
199         UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_wr);
200         UBICOM32_GPIO_SET_PIN_OUTPUT(ud->pin_cs);
201
202         UBICOM32_GPIO_ENABLE(ud->pin_rs);
203         UBICOM32_GPIO_ENABLE(ud->pin_rd);
204         UBICOM32_GPIO_ENABLE(ud->pin_wr);
205         UBICOM32_GPIO_ENABLE(ud->pin_cs);
206
207         udelay(20);
208
209         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_reset);
210
211         udelay(20);
212
213         UBICOM32_GPIO_SET_PIN_LOW(ud->pin_cs);
214
215         id = ubicom32lcd_read_data(ud);
216
217         /*
218          * We will try to figure out what kind of panel we have if we were not told.
219          */
220         if (!ud->panel) {
221                 const struct ubicom32lcd_panel **p = ubicom32lcd_panels;
222                 while (*p) {
223                         if ((*p)->id && ((*p)->id == id)) {
224                                 break;
225                         }
226                         p++;
227                 }
228                 if (!*p) {
229                         printk(KERN_WARNING DRIVER_NAME ":Could not find compatible panel, id=%x\n", id);
230                         return -ENODEV;
231                 }
232                 ud->panel = *p;
233         }
234
235         /*
236          * Make sure panel ID matches if we were supplied a panel type
237          */
238         if (ud->panel->id && (ud->panel->id != id)) {
239                 UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs);
240
241                 return -ENODEV;
242         }
243
244         ubicom32lcd_execute(ud, ud->panel->init_seq);
245
246         ubicom32lcd_goto(ud, 0, 0);
247
248         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_cs);
249         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rd);
250         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_wr);
251         UBICOM32_GPIO_SET_PIN_HIGH(ud->pin_rs);
252
253         printk(KERN_INFO DRIVER_NAME ": Initialized panel %s\n", ud->panel->desc);
254
255         return 0;
256 }
257
258 /*
259  * ubicom32lcd_probe
260  */
261 static int ubicom32lcd_probe(struct platform_device *pdev)
262 {
263         const struct ubicom32lcd_platform_data *pdata = pdev->dev.platform_data;
264         struct ubicom32lcd_data *ud;
265         int retval;
266
267         /*
268          * Allocate our private data
269          */
270         ud = kzalloc(sizeof(struct ubicom32lcd_data), GFP_KERNEL);
271         if (!ud) {
272                 return -ENOMEM;
273         }
274
275         if (pdata) {
276                 ud->pin_cs = pdata->pin_cs;
277                 ud->pin_rd = pdata->pin_rd;
278                 ud->pin_wr = pdata->pin_wr;
279                 ud->pin_rs = pdata->pin_rs;
280                 ud->pin_reset = pdata->pin_reset;
281                 ud->port_data = pdata->port_data;
282                 ud->data_shift = pdata->data_shift;
283         } else {
284                 /*
285                  * Defaults
286                  */
287                 ud->pin_cs = GPIO_RD_4;
288                 ud->pin_rd = GPIO_RD_5;
289                 ud->pin_rs = GPIO_RD_3;
290                 ud->pin_wr = GPIO_RD_2;
291                 ud->pin_reset = GPIO_RD_7;
292                 ud->port_data = (struct ubicom32_io_port *)RI;
293                 ud->data_shift = 0;
294         }
295
296         /*
297          * Initialize the display
298          */
299         retval = ubicom32lcd_panel_init(ud);
300         if (retval) {
301                 kfree(ud);
302                 return retval;
303         }
304
305         printk(KERN_INFO DRIVER_NAME ": LCD initialized\n");
306
307         return 0;
308 }
309
310 /*
311  * ubicom32lcd_remove
312  */
313 static int __exit ubicom32lcd_remove(struct platform_device *pdev)
314 {
315         struct ubicom32lcd_data *ud = platform_get_drvdata(pdev);
316
317         kfree(ud);
318
319         return 0;
320 }
321
322 static struct platform_driver ubicom32lcd_driver = {
323         .probe          = ubicom32lcd_probe,
324         .remove         = ubicom32lcd_remove,
325
326         .driver = {
327                 .name = DRIVER_NAME,
328                 .owner = THIS_MODULE,
329         },
330
331         .remove = __exit_p(ubicom32lcd_remove),
332 };
333
334 static struct platform_device *ubicom32lcd_device;
335
336 /*
337  * ubicom32lcd_init
338  */
339 static int __init ubicom32lcd_init(void)
340 {
341         int res;
342
343         res = platform_driver_register(&ubicom32lcd_driver);
344         if (res == 0) {
345                 ubicom32lcd_device = platform_device_alloc(DRIVER_NAME, 0);
346                 if (ubicom32lcd_device) {
347                         res = platform_device_add(ubicom32lcd_device);
348                 } else {
349                         res = -ENOMEM;
350                 }
351                 if (res) {
352                         platform_device_put(ubicom32lcd_device);
353                         platform_driver_unregister(&ubicom32lcd_driver);
354                 }
355         }
356         return res;
357 }
358 module_init(ubicom32lcd_init);
359
360 /*
361  * ubicom32lcd_exit
362  */
363 static void __exit ubicom32lcd_exit(void)
364 {
365         platform_device_unregister(ubicom32lcd_device);
366         platform_driver_unregister(&ubicom32lcd_driver);
367 }
368 module_exit(ubicom32lcd_exit);
369
370 MODULE_AUTHOR("Patrick Tjin <@ubicom.com>");
371 MODULE_DESCRIPTION("Ubicom32 LCD driver");
372 MODULE_LICENSE("GPL");