Commit | Line | Data |
---|---|---|
36c9366e VW |
1 | /* |
2 | * drivers/video/pnx4008/sdum.c | |
3 | * | |
4 | * Display Update Master support | |
5 | * | |
6 | * Authors: Grigory Tolstolytkin <gtolstolytkin@ru.mvista.com> | |
7 | * Vitaly Wool <vitalywool@gmail.com> | |
8 | * Based on Philips Semiconductors's code | |
9 | * | |
10 | * Copyrght (c) 2005-2006 MontaVista Software, Inc. | |
11 | * Copyright (c) 2005 Philips Semiconductors | |
12 | * This file is licensed under the terms of the GNU General Public License | |
13 | * version 2. This program is licensed "as is" without any warranty of any | |
14 | * kind, whether express or implied. | |
15 | */ | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/mm.h> | |
22 | #include <linux/tty.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/vmalloc.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/fb.h> | |
29 | #include <linux/init.h> | |
30 | #include <linux/dma-mapping.h> | |
31 | #include <linux/clk.h> | |
32 | #include <asm/uaccess.h> | |
a09e64fb | 33 | #include <mach/gpio.h> |
36c9366e VW |
34 | |
35 | #include "sdum.h" | |
36 | #include "fbcommon.h" | |
37 | #include "dum.h" | |
38 | ||
39 | /* Framebuffers we have */ | |
40 | ||
41 | static struct pnx4008_fb_addr { | |
42 | int fb_type; | |
43 | long addr_offset; | |
44 | long fb_length; | |
45 | } fb_addr[] = { | |
46 | [0] = { | |
47 | FB_TYPE_YUV, 0, 0xB0000 | |
48 | }, | |
49 | [1] = { | |
50 | FB_TYPE_RGB, 0xB0000, 0x50000 | |
51 | }, | |
52 | }; | |
53 | ||
54 | static struct dum_data { | |
55 | u32 lcd_phys_start; | |
56 | u32 lcd_virt_start; | |
57 | u32 slave_phys_base; | |
58 | u32 *slave_virt_base; | |
59 | int fb_owning_channel[MAX_DUM_CHANNELS]; | |
60 | struct dumchannel_uf chan_uf_store[MAX_DUM_CHANNELS]; | |
61 | } dum_data; | |
62 | ||
63 | /* Different local helper functions */ | |
64 | ||
65 | static u32 nof_pixels_dx(struct dum_ch_setup *ch_setup) | |
66 | { | |
67 | return (ch_setup->xmax - ch_setup->xmin + 1); | |
68 | } | |
69 | ||
70 | static u32 nof_pixels_dy(struct dum_ch_setup *ch_setup) | |
71 | { | |
72 | return (ch_setup->ymax - ch_setup->ymin + 1); | |
73 | } | |
74 | ||
75 | static u32 nof_pixels_dxy(struct dum_ch_setup *ch_setup) | |
76 | { | |
77 | return (nof_pixels_dx(ch_setup) * nof_pixels_dy(ch_setup)); | |
78 | } | |
79 | ||
80 | static u32 nof_bytes(struct dum_ch_setup *ch_setup) | |
81 | { | |
82 | u32 r = nof_pixels_dxy(ch_setup); | |
83 | switch (ch_setup->format) { | |
84 | case RGB888: | |
85 | case RGB666: | |
86 | r *= 4; | |
87 | break; | |
88 | ||
89 | default: | |
90 | r *= 2; | |
91 | break; | |
92 | } | |
93 | return r; | |
94 | } | |
95 | ||
96 | static u32 build_command(int disp_no, u32 reg, u32 val) | |
97 | { | |
98 | return ((disp_no << 26) | BIT(25) | (val << 16) | (disp_no << 10) | | |
99 | (reg << 0)); | |
100 | } | |
101 | ||
102 | static u32 build_double_index(int disp_no, u32 val) | |
103 | { | |
104 | return ((disp_no << 26) | (val << 16) | (disp_no << 10) | (val << 0)); | |
105 | } | |
106 | ||
107 | static void build_disp_window(struct dum_ch_setup * ch_setup, struct disp_window * dw) | |
108 | { | |
109 | dw->ymin = ch_setup->ymin; | |
110 | dw->ymax = ch_setup->ymax; | |
111 | dw->xmin_l = ch_setup->xmin & 0xFF; | |
112 | dw->xmin_h = (ch_setup->xmin & BIT(8)) >> 8; | |
113 | dw->xmax_l = ch_setup->xmax & 0xFF; | |
114 | dw->xmax_h = (ch_setup->xmax & BIT(8)) >> 8; | |
115 | } | |
116 | ||
117 | static int put_channel(struct dumchannel chan) | |
118 | { | |
119 | int i = chan.channelnr; | |
120 | ||
121 | if (i < 0 || i > MAX_DUM_CHANNELS) | |
122 | return -EINVAL; | |
123 | else { | |
124 | DUM_CH_MIN(i) = chan.dum_ch_min; | |
125 | DUM_CH_MAX(i) = chan.dum_ch_max; | |
126 | DUM_CH_CONF(i) = chan.dum_ch_conf; | |
127 | DUM_CH_CTRL(i) = chan.dum_ch_ctrl; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | static void clear_channel(int channr) | |
134 | { | |
135 | struct dumchannel chan; | |
136 | ||
137 | chan.channelnr = channr; | |
138 | chan.dum_ch_min = 0; | |
139 | chan.dum_ch_max = 0; | |
140 | chan.dum_ch_conf = 0; | |
141 | chan.dum_ch_ctrl = 0; | |
142 | ||
143 | put_channel(chan); | |
144 | } | |
145 | ||
146 | static int put_cmd_string(struct cmdstring cmds) | |
147 | { | |
148 | u16 *cmd_str_virtaddr; | |
149 | u32 *cmd_ptr0_virtaddr; | |
150 | u32 cmd_str_physaddr; | |
151 | ||
152 | int i = cmds.channelnr; | |
153 | ||
154 | if (i < 0 || i > MAX_DUM_CHANNELS) | |
155 | return -EINVAL; | |
156 | else if ((cmd_ptr0_virtaddr = | |
157 | (int *)ioremap_nocache(DUM_COM_BASE, | |
158 | sizeof(int) * MAX_DUM_CHANNELS)) == | |
159 | NULL) | |
160 | return -EIOREMAPFAILED; | |
161 | else { | |
162 | cmd_str_physaddr = ioread32(&cmd_ptr0_virtaddr[cmds.channelnr]); | |
163 | if ((cmd_str_virtaddr = | |
164 | (u16 *) ioremap_nocache(cmd_str_physaddr, | |
165 | sizeof(cmds))) == NULL) { | |
166 | iounmap(cmd_ptr0_virtaddr); | |
167 | return -EIOREMAPFAILED; | |
168 | } else { | |
169 | int t; | |
170 | for (t = 0; t < 8; t++) | |
171 | iowrite16(*((u16 *)&cmds.prestringlen + t), | |
172 | cmd_str_virtaddr + t); | |
173 | ||
174 | for (t = 0; t < cmds.prestringlen / 2; t++) | |
175 | iowrite16(*((u16 *)&cmds.precmd + t), | |
176 | cmd_str_virtaddr + t + 8); | |
177 | ||
178 | for (t = 0; t < cmds.poststringlen / 2; t++) | |
179 | iowrite16(*((u16 *)&cmds.postcmd + t), | |
180 | cmd_str_virtaddr + t + 8 + | |
181 | cmds.prestringlen / 2); | |
182 | ||
183 | iounmap(cmd_ptr0_virtaddr); | |
184 | iounmap(cmd_str_virtaddr); | |
185 | } | |
186 | } | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static u32 dum_ch_setup(int ch_no, struct dum_ch_setup * ch_setup) | |
192 | { | |
193 | struct cmdstring cmds_c; | |
194 | struct cmdstring *cmds = &cmds_c; | |
195 | struct disp_window dw; | |
196 | int standard; | |
197 | u32 orientation = 0; | |
198 | struct dumchannel chan = { 0 }; | |
199 | int ret; | |
200 | ||
201 | if ((ch_setup->xmirror) || (ch_setup->ymirror) || (ch_setup->rotate)) { | |
202 | standard = 0; | |
203 | ||
204 | orientation = BIT(1); /* always set 9-bit-bus */ | |
205 | if (ch_setup->xmirror) | |
206 | orientation |= BIT(4); | |
207 | if (ch_setup->ymirror) | |
208 | orientation |= BIT(3); | |
209 | if (ch_setup->rotate) | |
210 | orientation |= BIT(0); | |
211 | } else | |
212 | standard = 1; | |
213 | ||
214 | cmds->channelnr = ch_no; | |
215 | ||
216 | /* build command string header */ | |
217 | if (standard) { | |
218 | cmds->prestringlen = 32; | |
219 | cmds->poststringlen = 0; | |
220 | } else { | |
221 | cmds->prestringlen = 48; | |
222 | cmds->poststringlen = 16; | |
223 | } | |
224 | ||
225 | cmds->format = | |
226 | (u16) ((ch_setup->disp_no << 4) | (BIT(3)) | (ch_setup->format)); | |
227 | cmds->reserved = 0x0; | |
228 | cmds->startaddr_low = (ch_setup->minadr & 0xFFFF); | |
229 | cmds->startaddr_high = (ch_setup->minadr >> 16); | |
230 | ||
231 | if ((ch_setup->minadr == 0) && (ch_setup->maxadr == 0) | |
232 | && (ch_setup->xmin == 0) | |
233 | && (ch_setup->ymin == 0) && (ch_setup->xmax == 0) | |
234 | && (ch_setup->ymax == 0)) { | |
235 | cmds->pixdatlen_low = 0; | |
236 | cmds->pixdatlen_high = 0; | |
237 | } else { | |
238 | u32 nbytes = nof_bytes(ch_setup); | |
239 | cmds->pixdatlen_low = (nbytes & 0xFFFF); | |
240 | cmds->pixdatlen_high = (nbytes >> 16); | |
241 | } | |
242 | ||
243 | if (ch_setup->slave_trans) | |
244 | cmds->pixdatlen_high |= BIT(15); | |
245 | ||
246 | /* build pre-string */ | |
247 | build_disp_window(ch_setup, &dw); | |
248 | ||
249 | if (standard) { | |
250 | cmds->precmd[0] = | |
251 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, 0x99); | |
252 | cmds->precmd[1] = | |
253 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, | |
254 | dw.xmin_l); | |
255 | cmds->precmd[2] = | |
256 | build_command(ch_setup->disp_no, DISP_XMIN_H_REG, | |
257 | dw.xmin_h); | |
258 | cmds->precmd[3] = | |
259 | build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin); | |
260 | cmds->precmd[4] = | |
261 | build_command(ch_setup->disp_no, DISP_XMAX_L_REG, | |
262 | dw.xmax_l); | |
263 | cmds->precmd[5] = | |
264 | build_command(ch_setup->disp_no, DISP_XMAX_H_REG, | |
265 | dw.xmax_h); | |
266 | cmds->precmd[6] = | |
267 | build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax); | |
268 | cmds->precmd[7] = | |
269 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); | |
270 | } else { | |
271 | if (dw.xmin_l == ch_no) | |
272 | cmds->precmd[0] = | |
273 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, | |
274 | 0x99); | |
275 | else | |
276 | cmds->precmd[0] = | |
277 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, | |
278 | ch_no); | |
279 | ||
280 | cmds->precmd[1] = | |
281 | build_command(ch_setup->disp_no, DISP_XMIN_L_REG, | |
282 | dw.xmin_l); | |
283 | cmds->precmd[2] = | |
284 | build_command(ch_setup->disp_no, DISP_XMIN_H_REG, | |
285 | dw.xmin_h); | |
286 | cmds->precmd[3] = | |
287 | build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin); | |
288 | cmds->precmd[4] = | |
289 | build_command(ch_setup->disp_no, DISP_XMAX_L_REG, | |
290 | dw.xmax_l); | |
291 | cmds->precmd[5] = | |
292 | build_command(ch_setup->disp_no, DISP_XMAX_H_REG, | |
293 | dw.xmax_h); | |
294 | cmds->precmd[6] = | |
295 | build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax); | |
296 | cmds->precmd[7] = | |
297 | build_command(ch_setup->disp_no, DISP_1_REG, orientation); | |
298 | cmds->precmd[8] = | |
299 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); | |
300 | cmds->precmd[9] = | |
301 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); | |
302 | cmds->precmd[0xA] = | |
303 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); | |
304 | cmds->precmd[0xB] = | |
305 | build_double_index(ch_setup->disp_no, DISP_PIXEL_REG); | |
306 | cmds->postcmd[0] = | |
307 | build_command(ch_setup->disp_no, DISP_1_REG, BIT(1)); | |
308 | cmds->postcmd[1] = | |
309 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 1); | |
310 | cmds->postcmd[2] = | |
311 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 2); | |
312 | cmds->postcmd[3] = | |
313 | build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 3); | |
314 | } | |
315 | ||
316 | if ((ret = put_cmd_string(cmds_c)) != 0) { | |
317 | return ret; | |
318 | } | |
319 | ||
320 | chan.channelnr = cmds->channelnr; | |
321 | chan.dum_ch_min = ch_setup->dirtybuffer + ch_setup->minadr; | |
322 | chan.dum_ch_max = ch_setup->dirtybuffer + ch_setup->maxadr; | |
323 | chan.dum_ch_conf = 0x002; | |
324 | chan.dum_ch_ctrl = 0x04; | |
325 | ||
326 | put_channel(chan); | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
331 | static u32 display_open(int ch_no, int auto_update, u32 * dirty_buffer, | |
332 | u32 * frame_buffer, u32 xpos, u32 ypos, u32 w, u32 h) | |
333 | { | |
334 | ||
335 | struct dum_ch_setup k; | |
336 | int ret; | |
337 | ||
338 | /* keep width & height within display area */ | |
339 | if ((xpos + w) > DISP_MAX_X_SIZE) | |
340 | w = DISP_MAX_X_SIZE - xpos; | |
341 | ||
342 | if ((ypos + h) > DISP_MAX_Y_SIZE) | |
343 | h = DISP_MAX_Y_SIZE - ypos; | |
344 | ||
345 | /* assume 1 display only */ | |
346 | k.disp_no = 0; | |
347 | k.xmin = xpos; | |
348 | k.ymin = ypos; | |
349 | k.xmax = xpos + (w - 1); | |
350 | k.ymax = ypos + (h - 1); | |
351 | ||
352 | /* adjust min and max values if necessary */ | |
353 | if (k.xmin > DISP_MAX_X_SIZE - 1) | |
354 | k.xmin = DISP_MAX_X_SIZE - 1; | |
355 | if (k.ymin > DISP_MAX_Y_SIZE - 1) | |
356 | k.ymin = DISP_MAX_Y_SIZE - 1; | |
357 | ||
358 | if (k.xmax > DISP_MAX_X_SIZE - 1) | |
359 | k.xmax = DISP_MAX_X_SIZE - 1; | |
360 | if (k.ymax > DISP_MAX_Y_SIZE - 1) | |
361 | k.ymax = DISP_MAX_Y_SIZE - 1; | |
362 | ||
363 | k.xmirror = 0; | |
364 | k.ymirror = 0; | |
365 | k.rotate = 0; | |
366 | k.minadr = (u32) frame_buffer; | |
367 | k.maxadr = (u32) frame_buffer + (((w - 1) << 10) | ((h << 2) - 2)); | |
368 | k.pad = PAD_1024; | |
369 | k.dirtybuffer = (u32) dirty_buffer; | |
370 | k.format = RGB888; | |
371 | k.hwdirty = 0; | |
372 | k.slave_trans = 0; | |
373 | ||
374 | ret = dum_ch_setup(ch_no, &k); | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
379 | static void lcd_reset(void) | |
380 | { | |
381 | u32 *dum_pio_base = (u32 *)IO_ADDRESS(PNX4008_PIO_BASE); | |
382 | ||
383 | udelay(1); | |
384 | iowrite32(BIT(19), &dum_pio_base[2]); | |
385 | udelay(1); | |
386 | iowrite32(BIT(19), &dum_pio_base[1]); | |
387 | udelay(1); | |
388 | } | |
389 | ||
390 | static int dum_init(struct platform_device *pdev) | |
391 | { | |
392 | struct clk *clk; | |
393 | ||
394 | /* enable DUM clock */ | |
395 | clk = clk_get(&pdev->dev, "dum_ck"); | |
396 | if (IS_ERR(clk)) { | |
397 | printk(KERN_ERR "pnx4008_dum: Unable to access DUM clock\n"); | |
398 | return PTR_ERR(clk); | |
399 | } | |
400 | ||
401 | clk_set_rate(clk, 1); | |
402 | clk_put(clk); | |
403 | ||
404 | DUM_CTRL = V_DUM_RESET; | |
405 | ||
406 | /* set priority to "round-robin". All other params to "false" */ | |
407 | DUM_CONF = BIT(9); | |
408 | ||
409 | /* Display 1 */ | |
410 | DUM_WTCFG1 = PNX4008_DUM_WT_CFG; | |
411 | DUM_RTCFG1 = PNX4008_DUM_RT_CFG; | |
412 | DUM_TCFG = PNX4008_DUM_T_CFG; | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
417 | static void dum_chan_init(void) | |
418 | { | |
419 | int i = 0, ch = 0; | |
420 | u32 *cmdptrs; | |
421 | u32 *cmdstrings; | |
422 | ||
423 | DUM_COM_BASE = | |
424 | CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS; | |
425 | ||
426 | if ((cmdptrs = | |
427 | (u32 *) ioremap_nocache(DUM_COM_BASE, | |
428 | sizeof(u32) * NR_OF_CMDSTRINGS)) == NULL) | |
429 | return; | |
430 | ||
431 | for (ch = 0; ch < NR_OF_CMDSTRINGS; ch++) | |
432 | iowrite32(CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * ch, | |
433 | cmdptrs + ch); | |
434 | ||
435 | for (ch = 0; ch < MAX_DUM_CHANNELS; ch++) | |
436 | clear_channel(ch); | |
437 | ||
438 | /* Clear the cmdstrings */ | |
439 | cmdstrings = | |
440 | (u32 *)ioremap_nocache(*cmdptrs, | |
441 | BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS); | |
442 | ||
443 | if (!cmdstrings) | |
444 | goto out; | |
445 | ||
446 | for (i = 0; i < NR_OF_CMDSTRINGS * BYTES_PER_CMDSTRING / sizeof(u32); | |
447 | i++) | |
448 | iowrite32(0, cmdstrings + i); | |
449 | ||
450 | iounmap((u32 *)cmdstrings); | |
451 | ||
452 | out: | |
453 | iounmap((u32 *)cmdptrs); | |
454 | } | |
455 | ||
456 | static void lcd_init(void) | |
457 | { | |
458 | lcd_reset(); | |
459 | ||
460 | DUM_OUTP_FORMAT1 = 0; /* RGB666 */ | |
461 | ||
462 | udelay(1); | |
463 | iowrite32(V_LCD_STANDBY_OFF, dum_data.slave_virt_base); | |
464 | udelay(1); | |
465 | iowrite32(V_LCD_USE_9BIT_BUS, dum_data.slave_virt_base); | |
466 | udelay(1); | |
467 | iowrite32(V_LCD_SYNC_RISE_L, dum_data.slave_virt_base); | |
468 | udelay(1); | |
469 | iowrite32(V_LCD_SYNC_RISE_H, dum_data.slave_virt_base); | |
470 | udelay(1); | |
471 | iowrite32(V_LCD_SYNC_FALL_L, dum_data.slave_virt_base); | |
472 | udelay(1); | |
473 | iowrite32(V_LCD_SYNC_FALL_H, dum_data.slave_virt_base); | |
474 | udelay(1); | |
475 | iowrite32(V_LCD_SYNC_ENABLE, dum_data.slave_virt_base); | |
476 | udelay(1); | |
477 | iowrite32(V_LCD_DISPLAY_ON, dum_data.slave_virt_base); | |
478 | udelay(1); | |
479 | } | |
480 | ||
481 | /* Interface exported to framebuffer drivers */ | |
482 | ||
483 | int pnx4008_get_fb_addresses(int fb_type, void **virt_addr, | |
484 | dma_addr_t *phys_addr, int *fb_length) | |
485 | { | |
486 | int i; | |
487 | int ret = -1; | |
488 | for (i = 0; i < ARRAY_SIZE(fb_addr); i++) | |
489 | if (fb_addr[i].fb_type == fb_type) { | |
490 | *virt_addr = (void *)(dum_data.lcd_virt_start + | |
491 | fb_addr[i].addr_offset); | |
492 | *phys_addr = | |
493 | dum_data.lcd_phys_start + fb_addr[i].addr_offset; | |
494 | *fb_length = fb_addr[i].fb_length; | |
495 | ret = 0; | |
496 | break; | |
497 | } | |
498 | ||
499 | return ret; | |
500 | } | |
501 | ||
502 | EXPORT_SYMBOL(pnx4008_get_fb_addresses); | |
503 | ||
504 | int pnx4008_alloc_dum_channel(int dev_id) | |
505 | { | |
506 | int i = 0; | |
507 | ||
508 | while ((i < MAX_DUM_CHANNELS) && (dum_data.fb_owning_channel[i] != -1)) | |
509 | i++; | |
510 | ||
511 | if (i == MAX_DUM_CHANNELS) | |
512 | return -ENORESOURCESLEFT; | |
513 | else { | |
514 | dum_data.fb_owning_channel[i] = dev_id; | |
515 | return i; | |
516 | } | |
517 | } | |
518 | ||
519 | EXPORT_SYMBOL(pnx4008_alloc_dum_channel); | |
520 | ||
521 | int pnx4008_free_dum_channel(int channr, int dev_id) | |
522 | { | |
523 | if (channr < 0 || channr > MAX_DUM_CHANNELS) | |
524 | return -EINVAL; | |
525 | else if (dum_data.fb_owning_channel[channr] != dev_id) | |
526 | return -EFBNOTOWNER; | |
527 | else { | |
528 | clear_channel(channr); | |
529 | dum_data.fb_owning_channel[channr] = -1; | |
530 | } | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
535 | EXPORT_SYMBOL(pnx4008_free_dum_channel); | |
536 | ||
537 | int pnx4008_put_dum_channel_uf(struct dumchannel_uf chan_uf, int dev_id) | |
538 | { | |
539 | int i = chan_uf.channelnr; | |
540 | int ret; | |
541 | ||
542 | if (i < 0 || i > MAX_DUM_CHANNELS) | |
543 | return -EINVAL; | |
544 | else if (dum_data.fb_owning_channel[i] != dev_id) | |
545 | return -EFBNOTOWNER; | |
546 | else if ((ret = | |
547 | display_open(chan_uf.channelnr, 0, chan_uf.dirty, | |
548 | chan_uf.source, chan_uf.y_offset, | |
549 | chan_uf.x_offset, chan_uf.height, | |
550 | chan_uf.width)) != 0) | |
551 | return ret; | |
552 | else { | |
553 | dum_data.chan_uf_store[i].dirty = chan_uf.dirty; | |
554 | dum_data.chan_uf_store[i].source = chan_uf.source; | |
555 | dum_data.chan_uf_store[i].x_offset = chan_uf.x_offset; | |
556 | dum_data.chan_uf_store[i].y_offset = chan_uf.y_offset; | |
557 | dum_data.chan_uf_store[i].width = chan_uf.width; | |
558 | dum_data.chan_uf_store[i].height = chan_uf.height; | |
559 | } | |
560 | ||
561 | return 0; | |
562 | } | |
563 | ||
564 | EXPORT_SYMBOL(pnx4008_put_dum_channel_uf); | |
565 | ||
566 | int pnx4008_set_dum_channel_sync(int channr, int val, int dev_id) | |
567 | { | |
568 | if (channr < 0 || channr > MAX_DUM_CHANNELS) | |
569 | return -EINVAL; | |
570 | else if (dum_data.fb_owning_channel[channr] != dev_id) | |
571 | return -EFBNOTOWNER; | |
572 | else { | |
573 | if (val == CONF_SYNC_ON) { | |
574 | DUM_CH_CONF(channr) |= CONF_SYNCENABLE; | |
575 | DUM_CH_CONF(channr) |= DUM_CHANNEL_CFG_SYNC_MASK | | |
576 | DUM_CHANNEL_CFG_SYNC_MASK_SET; | |
577 | } else if (val == CONF_SYNC_OFF) | |
578 | DUM_CH_CONF(channr) &= ~CONF_SYNCENABLE; | |
579 | else | |
580 | return -EINVAL; | |
581 | } | |
582 | ||
583 | return 0; | |
584 | } | |
585 | ||
586 | EXPORT_SYMBOL(pnx4008_set_dum_channel_sync); | |
587 | ||
588 | int pnx4008_set_dum_channel_dirty_detect(int channr, int val, int dev_id) | |
589 | { | |
590 | if (channr < 0 || channr > MAX_DUM_CHANNELS) | |
591 | return -EINVAL; | |
592 | else if (dum_data.fb_owning_channel[channr] != dev_id) | |
593 | return -EFBNOTOWNER; | |
594 | else { | |
595 | if (val == CONF_DIRTYDETECTION_ON) | |
596 | DUM_CH_CONF(channr) |= CONF_DIRTYENABLE; | |
597 | else if (val == CONF_DIRTYDETECTION_OFF) | |
598 | DUM_CH_CONF(channr) &= ~CONF_DIRTYENABLE; | |
599 | else | |
600 | return -EINVAL; | |
601 | } | |
602 | ||
603 | return 0; | |
604 | } | |
605 | ||
606 | EXPORT_SYMBOL(pnx4008_set_dum_channel_dirty_detect); | |
607 | ||
608 | #if 0 /* Functions not used currently, but likely to be used in future */ | |
609 | ||
610 | static int get_channel(struct dumchannel *p_chan) | |
611 | { | |
612 | int i = p_chan->channelnr; | |
613 | ||
614 | if (i < 0 || i > MAX_DUM_CHANNELS) | |
615 | return -EINVAL; | |
616 | else { | |
617 | p_chan->dum_ch_min = DUM_CH_MIN(i); | |
618 | p_chan->dum_ch_max = DUM_CH_MAX(i); | |
619 | p_chan->dum_ch_conf = DUM_CH_CONF(i); | |
620 | p_chan->dum_ch_stat = DUM_CH_STAT(i); | |
621 | p_chan->dum_ch_ctrl = 0; /* WriteOnly control register */ | |
622 | } | |
623 | ||
624 | return 0; | |
625 | } | |
626 | ||
627 | int pnx4008_get_dum_channel_uf(struct dumchannel_uf *p_chan_uf, int dev_id) | |
628 | { | |
629 | int i = p_chan_uf->channelnr; | |
630 | ||
631 | if (i < 0 || i > MAX_DUM_CHANNELS) | |
632 | return -EINVAL; | |
633 | else if (dum_data.fb_owning_channel[i] != dev_id) | |
634 | return -EFBNOTOWNER; | |
635 | else { | |
636 | p_chan_uf->dirty = dum_data.chan_uf_store[i].dirty; | |
637 | p_chan_uf->source = dum_data.chan_uf_store[i].source; | |
638 | p_chan_uf->x_offset = dum_data.chan_uf_store[i].x_offset; | |
639 | p_chan_uf->y_offset = dum_data.chan_uf_store[i].y_offset; | |
640 | p_chan_uf->width = dum_data.chan_uf_store[i].width; | |
641 | p_chan_uf->height = dum_data.chan_uf_store[i].height; | |
642 | } | |
643 | ||
644 | return 0; | |
645 | } | |
646 | ||
647 | EXPORT_SYMBOL(pnx4008_get_dum_channel_uf); | |
648 | ||
649 | int pnx4008_get_dum_channel_config(int channr, int dev_id) | |
650 | { | |
651 | int ret; | |
652 | struct dumchannel chan; | |
653 | ||
654 | if (channr < 0 || channr > MAX_DUM_CHANNELS) | |
655 | return -EINVAL; | |
656 | else if (dum_data.fb_owning_channel[channr] != dev_id) | |
657 | return -EFBNOTOWNER; | |
658 | else { | |
659 | chan.channelnr = channr; | |
660 | if ((ret = get_channel(&chan)) != 0) | |
661 | return ret; | |
662 | } | |
663 | ||
664 | return (chan.dum_ch_conf & DUM_CHANNEL_CFG_MASK); | |
665 | } | |
666 | ||
667 | EXPORT_SYMBOL(pnx4008_get_dum_channel_config); | |
668 | ||
669 | int pnx4008_force_update_dum_channel(int channr, int dev_id) | |
670 | { | |
671 | if (channr < 0 || channr > MAX_DUM_CHANNELS) | |
672 | return -EINVAL; | |
673 | ||
674 | else if (dum_data.fb_owning_channel[channr] != dev_id) | |
675 | return -EFBNOTOWNER; | |
676 | else | |
677 | DUM_CH_CTRL(channr) = CTRL_SETDIRTY; | |
678 | ||
679 | return 0; | |
680 | } | |
681 | ||
682 | EXPORT_SYMBOL(pnx4008_force_update_dum_channel); | |
683 | ||
684 | #endif | |
685 | ||
686 | int pnx4008_sdum_mmap(struct fb_info *info, struct vm_area_struct *vma, | |
687 | struct device *dev) | |
688 | { | |
689 | unsigned long off = vma->vm_pgoff << PAGE_SHIFT; | |
690 | ||
691 | if (off < info->fix.smem_len) { | |
692 | vma->vm_pgoff += 1; | |
693 | return dma_mmap_writecombine(dev, vma, | |
694 | (void *)dum_data.lcd_virt_start, | |
695 | dum_data.lcd_phys_start, | |
696 | FB_DMA_SIZE); | |
697 | } | |
698 | return -EINVAL; | |
699 | } | |
700 | ||
701 | EXPORT_SYMBOL(pnx4008_sdum_mmap); | |
702 | ||
703 | int pnx4008_set_dum_exit_notification(int dev_id) | |
704 | { | |
705 | int i; | |
706 | ||
707 | for (i = 0; i < MAX_DUM_CHANNELS; i++) | |
708 | if (dum_data.fb_owning_channel[i] == dev_id) | |
709 | return -ERESOURCESNOTFREED; | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | EXPORT_SYMBOL(pnx4008_set_dum_exit_notification); | |
715 | ||
716 | /* Platform device driver for DUM */ | |
717 | ||
718 | static int sdum_suspend(struct platform_device *pdev, pm_message_t state) | |
719 | { | |
720 | int retval = 0; | |
721 | struct clk *clk; | |
722 | ||
723 | clk = clk_get(0, "dum_ck"); | |
724 | if (!IS_ERR(clk)) { | |
725 | clk_set_rate(clk, 0); | |
726 | clk_put(clk); | |
727 | } else | |
728 | retval = PTR_ERR(clk); | |
729 | ||
730 | /* disable BAC */ | |
731 | DUM_CTRL = V_BAC_DISABLE_IDLE; | |
732 | ||
733 | /* LCD standby & turn off display */ | |
734 | lcd_reset(); | |
735 | ||
736 | return retval; | |
737 | } | |
738 | ||
739 | static int sdum_resume(struct platform_device *pdev) | |
740 | { | |
741 | int retval = 0; | |
742 | struct clk *clk; | |
743 | ||
744 | clk = clk_get(0, "dum_ck"); | |
745 | if (!IS_ERR(clk)) { | |
746 | clk_set_rate(clk, 1); | |
747 | clk_put(clk); | |
748 | } else | |
749 | retval = PTR_ERR(clk); | |
750 | ||
751 | /* wait for BAC disable */ | |
752 | DUM_CTRL = V_BAC_DISABLE_TRIG; | |
753 | ||
754 | while (DUM_CTRL & BAC_ENABLED) | |
755 | udelay(10); | |
756 | ||
757 | /* re-init LCD */ | |
758 | lcd_init(); | |
759 | ||
760 | /* enable BAC and reset MUX */ | |
761 | DUM_CTRL = V_BAC_ENABLE; | |
762 | udelay(1); | |
763 | DUM_CTRL = V_MUX_RESET; | |
764 | return 0; | |
765 | } | |
766 | ||
767 | static int __devinit sdum_probe(struct platform_device *pdev) | |
768 | { | |
769 | int ret = 0, i = 0; | |
770 | ||
771 | /* map frame buffer */ | |
772 | dum_data.lcd_virt_start = (u32) dma_alloc_writecombine(&pdev->dev, | |
773 | FB_DMA_SIZE, | |
774 | &dum_data.lcd_phys_start, | |
775 | GFP_KERNEL); | |
776 | ||
777 | if (!dum_data.lcd_virt_start) { | |
778 | ret = -ENOMEM; | |
779 | goto out_3; | |
780 | } | |
781 | ||
782 | /* map slave registers */ | |
783 | dum_data.slave_phys_base = PNX4008_DUM_SLAVE_BASE; | |
784 | dum_data.slave_virt_base = | |
785 | (u32 *) ioremap_nocache(dum_data.slave_phys_base, sizeof(u32)); | |
786 | ||
787 | if (dum_data.slave_virt_base == NULL) { | |
788 | ret = -ENOMEM; | |
789 | goto out_2; | |
790 | } | |
791 | ||
792 | /* initialize DUM and LCD display */ | |
793 | ret = dum_init(pdev); | |
794 | if (ret) | |
795 | goto out_1; | |
796 | ||
797 | dum_chan_init(); | |
798 | lcd_init(); | |
799 | ||
800 | DUM_CTRL = V_BAC_ENABLE; | |
801 | udelay(1); | |
802 | DUM_CTRL = V_MUX_RESET; | |
803 | ||
804 | /* set decode address and sync clock divider */ | |
805 | DUM_DECODE = dum_data.lcd_phys_start & DUM_DECODE_MASK; | |
806 | DUM_CLK_DIV = PNX4008_DUM_CLK_DIV; | |
807 | ||
808 | for (i = 0; i < MAX_DUM_CHANNELS; i++) | |
809 | dum_data.fb_owning_channel[i] = -1; | |
810 | ||
811 | /*setup wakeup interrupt */ | |
812 | start_int_set_rising_edge(SE_DISP_SYNC_INT); | |
813 | start_int_ack(SE_DISP_SYNC_INT); | |
814 | start_int_umask(SE_DISP_SYNC_INT); | |
815 | ||
816 | return 0; | |
817 | ||
818 | out_1: | |
819 | iounmap((void *)dum_data.slave_virt_base); | |
820 | out_2: | |
821 | dma_free_writecombine(&pdev->dev, FB_DMA_SIZE, | |
822 | (void *)dum_data.lcd_virt_start, | |
823 | dum_data.lcd_phys_start); | |
824 | out_3: | |
825 | return ret; | |
826 | } | |
827 | ||
828 | static int sdum_remove(struct platform_device *pdev) | |
829 | { | |
830 | struct clk *clk; | |
831 | ||
832 | start_int_mask(SE_DISP_SYNC_INT); | |
833 | ||
834 | clk = clk_get(0, "dum_ck"); | |
835 | if (!IS_ERR(clk)) { | |
836 | clk_set_rate(clk, 0); | |
837 | clk_put(clk); | |
838 | } | |
839 | ||
840 | iounmap((void *)dum_data.slave_virt_base); | |
841 | ||
842 | dma_free_writecombine(&pdev->dev, FB_DMA_SIZE, | |
843 | (void *)dum_data.lcd_virt_start, | |
844 | dum_data.lcd_phys_start); | |
845 | ||
846 | return 0; | |
847 | } | |
848 | ||
849 | static struct platform_driver sdum_driver = { | |
850 | .driver = { | |
84a763e3 | 851 | .name = "pnx4008-sdum", |
36c9366e VW |
852 | }, |
853 | .probe = sdum_probe, | |
854 | .remove = sdum_remove, | |
855 | .suspend = sdum_suspend, | |
856 | .resume = sdum_resume, | |
857 | }; | |
858 | ||
859 | int __init sdum_init(void) | |
860 | { | |
861 | return platform_driver_register(&sdum_driver); | |
862 | } | |
863 | ||
864 | static void __exit sdum_exit(void) | |
865 | { | |
866 | platform_driver_unregister(&sdum_driver); | |
867 | }; | |
868 | ||
869 | module_init(sdum_init); | |
870 | module_exit(sdum_exit); | |
871 | ||
872 | MODULE_LICENSE("GPL"); |