2 * drivers/video/pnx4008/sdum.c
4 * Display Update Master support
6 * Authors: Grigory Tolstolytkin <gtolstolytkin@ru.mvista.com>
7 * Vitaly Wool <vitalywool@gmail.com>
8 * Based on Philips Semiconductors's code
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.
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
22 #include <linux/tty.h>
23 #include <linux/vmalloc.h>
24 #include <linux/delay.h>
25 #include <linux/interrupt.h>
26 #include <linux/platform_device.h>
28 #include <linux/init.h>
29 #include <linux/dma-mapping.h>
30 #include <linux/clk.h>
31 #include <linux/gfp.h>
32 #include <asm/uaccess.h>
39 /* Framebuffers we have */
41 static struct pnx4008_fb_addr
{
47 FB_TYPE_YUV
, 0, 0xB0000
50 FB_TYPE_RGB
, 0xB0000, 0x50000
54 static struct dum_data
{
59 int fb_owning_channel
[MAX_DUM_CHANNELS
];
60 struct dumchannel_uf chan_uf_store
[MAX_DUM_CHANNELS
];
63 /* Different local helper functions */
65 static u32
nof_pixels_dx(struct dum_ch_setup
*ch_setup
)
67 return (ch_setup
->xmax
- ch_setup
->xmin
+ 1);
70 static u32
nof_pixels_dy(struct dum_ch_setup
*ch_setup
)
72 return (ch_setup
->ymax
- ch_setup
->ymin
+ 1);
75 static u32
nof_pixels_dxy(struct dum_ch_setup
*ch_setup
)
77 return (nof_pixels_dx(ch_setup
) * nof_pixels_dy(ch_setup
));
80 static u32
nof_bytes(struct dum_ch_setup
*ch_setup
)
82 u32 r
= nof_pixels_dxy(ch_setup
);
83 switch (ch_setup
->format
) {
96 static u32
build_command(int disp_no
, u32 reg
, u32 val
)
98 return ((disp_no
<< 26) | BIT(25) | (val
<< 16) | (disp_no
<< 10) |
102 static u32
build_double_index(int disp_no
, u32 val
)
104 return ((disp_no
<< 26) | (val
<< 16) | (disp_no
<< 10) | (val
<< 0));
107 static void build_disp_window(struct dum_ch_setup
* ch_setup
, struct disp_window
* dw
)
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;
117 static int put_channel(struct dumchannel chan
)
119 int i
= chan
.channelnr
;
121 if (i
< 0 || i
> MAX_DUM_CHANNELS
)
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
;
133 static void clear_channel(int channr
)
135 struct dumchannel chan
;
137 chan
.channelnr
= channr
;
140 chan
.dum_ch_conf
= 0;
141 chan
.dum_ch_ctrl
= 0;
146 static int put_cmd_string(struct cmdstring cmds
)
148 u16
*cmd_str_virtaddr
;
149 u32
*cmd_ptr0_virtaddr
;
150 u32 cmd_str_physaddr
;
152 int i
= cmds
.channelnr
;
154 if (i
< 0 || i
> MAX_DUM_CHANNELS
)
156 else if ((cmd_ptr0_virtaddr
=
157 (int *)ioremap_nocache(DUM_COM_BASE
,
158 sizeof(int) * MAX_DUM_CHANNELS
)) ==
160 return -EIOREMAPFAILED
;
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
;
170 for (t
= 0; t
< 8; t
++)
171 iowrite16(*((u16
*)&cmds
.prestringlen
+ t
),
172 cmd_str_virtaddr
+ t
);
174 for (t
= 0; t
< cmds
.prestringlen
/ 2; t
++)
175 iowrite16(*((u16
*)&cmds
.precmd
+ t
),
176 cmd_str_virtaddr
+ t
+ 8);
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);
183 iounmap(cmd_ptr0_virtaddr
);
184 iounmap(cmd_str_virtaddr
);
191 static u32
dum_ch_setup(int ch_no
, struct dum_ch_setup
* ch_setup
)
193 struct cmdstring cmds_c
;
194 struct cmdstring
*cmds
= &cmds_c
;
195 struct disp_window dw
;
198 struct dumchannel chan
= { 0 };
201 if ((ch_setup
->xmirror
) || (ch_setup
->ymirror
) || (ch_setup
->rotate
)) {
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);
214 cmds
->channelnr
= ch_no
;
216 /* build command string header */
218 cmds
->prestringlen
= 32;
219 cmds
->poststringlen
= 0;
221 cmds
->prestringlen
= 48;
222 cmds
->poststringlen
= 16;
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);
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;
238 u32 nbytes
= nof_bytes(ch_setup
);
239 cmds
->pixdatlen_low
= (nbytes
& 0xFFFF);
240 cmds
->pixdatlen_high
= (nbytes
>> 16);
243 if (ch_setup
->slave_trans
)
244 cmds
->pixdatlen_high
|= BIT(15);
246 /* build pre-string */
247 build_disp_window(ch_setup
, &dw
);
251 build_command(ch_setup
->disp_no
, DISP_XMIN_L_REG
, 0x99);
253 build_command(ch_setup
->disp_no
, DISP_XMIN_L_REG
,
256 build_command(ch_setup
->disp_no
, DISP_XMIN_H_REG
,
259 build_command(ch_setup
->disp_no
, DISP_YMIN_REG
, dw
.ymin
);
261 build_command(ch_setup
->disp_no
, DISP_XMAX_L_REG
,
264 build_command(ch_setup
->disp_no
, DISP_XMAX_H_REG
,
267 build_command(ch_setup
->disp_no
, DISP_YMAX_REG
, dw
.ymax
);
269 build_double_index(ch_setup
->disp_no
, DISP_PIXEL_REG
);
271 if (dw
.xmin_l
== ch_no
)
273 build_command(ch_setup
->disp_no
, DISP_XMIN_L_REG
,
277 build_command(ch_setup
->disp_no
, DISP_XMIN_L_REG
,
281 build_command(ch_setup
->disp_no
, DISP_XMIN_L_REG
,
284 build_command(ch_setup
->disp_no
, DISP_XMIN_H_REG
,
287 build_command(ch_setup
->disp_no
, DISP_YMIN_REG
, dw
.ymin
);
289 build_command(ch_setup
->disp_no
, DISP_XMAX_L_REG
,
292 build_command(ch_setup
->disp_no
, DISP_XMAX_H_REG
,
295 build_command(ch_setup
->disp_no
, DISP_YMAX_REG
, dw
.ymax
);
297 build_command(ch_setup
->disp_no
, DISP_1_REG
, orientation
);
299 build_double_index(ch_setup
->disp_no
, DISP_PIXEL_REG
);
301 build_double_index(ch_setup
->disp_no
, DISP_PIXEL_REG
);
303 build_double_index(ch_setup
->disp_no
, DISP_PIXEL_REG
);
305 build_double_index(ch_setup
->disp_no
, DISP_PIXEL_REG
);
307 build_command(ch_setup
->disp_no
, DISP_1_REG
, BIT(1));
309 build_command(ch_setup
->disp_no
, DISP_DUMMY1_REG
, 1);
311 build_command(ch_setup
->disp_no
, DISP_DUMMY1_REG
, 2);
313 build_command(ch_setup
->disp_no
, DISP_DUMMY1_REG
, 3);
316 if ((ret
= put_cmd_string(cmds_c
)) != 0) {
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;
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
)
335 struct dum_ch_setup k
;
338 /* keep width & height within display area */
339 if ((xpos
+ w
) > DISP_MAX_X_SIZE
)
340 w
= DISP_MAX_X_SIZE
- xpos
;
342 if ((ypos
+ h
) > DISP_MAX_Y_SIZE
)
343 h
= DISP_MAX_Y_SIZE
- ypos
;
345 /* assume 1 display only */
349 k
.xmax
= xpos
+ (w
- 1);
350 k
.ymax
= ypos
+ (h
- 1);
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;
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;
366 k
.minadr
= (u32
) frame_buffer
;
367 k
.maxadr
= (u32
) frame_buffer
+ (((w
- 1) << 10) | ((h
<< 2) - 2));
369 k
.dirtybuffer
= (u32
) dirty_buffer
;
374 ret
= dum_ch_setup(ch_no
, &k
);
379 static void lcd_reset(void)
381 u32
*dum_pio_base
= (u32
*)IO_ADDRESS(PNX4008_PIO_BASE
);
384 iowrite32(BIT(19), &dum_pio_base
[2]);
386 iowrite32(BIT(19), &dum_pio_base
[1]);
390 static int dum_init(struct platform_device
*pdev
)
394 /* enable DUM clock */
395 clk
= clk_get(&pdev
->dev
, "dum_ck");
397 printk(KERN_ERR
"pnx4008_dum: Unable to access DUM clock\n");
401 clk_set_rate(clk
, 1);
404 DUM_CTRL
= V_DUM_RESET
;
406 /* set priority to "round-robin". All other params to "false" */
410 DUM_WTCFG1
= PNX4008_DUM_WT_CFG
;
411 DUM_RTCFG1
= PNX4008_DUM_RT_CFG
;
412 DUM_TCFG
= PNX4008_DUM_T_CFG
;
417 static void dum_chan_init(void)
424 CMDSTRING_BASEADDR
+ BYTES_PER_CMDSTRING
* NR_OF_CMDSTRINGS
;
427 (u32
*) ioremap_nocache(DUM_COM_BASE
,
428 sizeof(u32
) * NR_OF_CMDSTRINGS
)) == NULL
)
431 for (ch
= 0; ch
< NR_OF_CMDSTRINGS
; ch
++)
432 iowrite32(CMDSTRING_BASEADDR
+ BYTES_PER_CMDSTRING
* ch
,
435 for (ch
= 0; ch
< MAX_DUM_CHANNELS
; ch
++)
438 /* Clear the cmdstrings */
440 (u32
*)ioremap_nocache(*cmdptrs
,
441 BYTES_PER_CMDSTRING
* NR_OF_CMDSTRINGS
);
446 for (i
= 0; i
< NR_OF_CMDSTRINGS
* BYTES_PER_CMDSTRING
/ sizeof(u32
);
448 iowrite32(0, cmdstrings
+ i
);
450 iounmap((u32
*)cmdstrings
);
453 iounmap((u32
*)cmdptrs
);
456 static void lcd_init(void)
460 DUM_OUTP_FORMAT1
= 0; /* RGB666 */
463 iowrite32(V_LCD_STANDBY_OFF
, dum_data
.slave_virt_base
);
465 iowrite32(V_LCD_USE_9BIT_BUS
, dum_data
.slave_virt_base
);
467 iowrite32(V_LCD_SYNC_RISE_L
, dum_data
.slave_virt_base
);
469 iowrite32(V_LCD_SYNC_RISE_H
, dum_data
.slave_virt_base
);
471 iowrite32(V_LCD_SYNC_FALL_L
, dum_data
.slave_virt_base
);
473 iowrite32(V_LCD_SYNC_FALL_H
, dum_data
.slave_virt_base
);
475 iowrite32(V_LCD_SYNC_ENABLE
, dum_data
.slave_virt_base
);
477 iowrite32(V_LCD_DISPLAY_ON
, dum_data
.slave_virt_base
);
481 /* Interface exported to framebuffer drivers */
483 int pnx4008_get_fb_addresses(int fb_type
, void **virt_addr
,
484 dma_addr_t
*phys_addr
, int *fb_length
)
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
);
493 dum_data
.lcd_phys_start
+ fb_addr
[i
].addr_offset
;
494 *fb_length
= fb_addr
[i
].fb_length
;
502 EXPORT_SYMBOL(pnx4008_get_fb_addresses
);
504 int pnx4008_alloc_dum_channel(int dev_id
)
508 while ((i
< MAX_DUM_CHANNELS
) && (dum_data
.fb_owning_channel
[i
] != -1))
511 if (i
== MAX_DUM_CHANNELS
)
512 return -ENORESOURCESLEFT
;
514 dum_data
.fb_owning_channel
[i
] = dev_id
;
519 EXPORT_SYMBOL(pnx4008_alloc_dum_channel
);
521 int pnx4008_free_dum_channel(int channr
, int dev_id
)
523 if (channr
< 0 || channr
> MAX_DUM_CHANNELS
)
525 else if (dum_data
.fb_owning_channel
[channr
] != dev_id
)
528 clear_channel(channr
);
529 dum_data
.fb_owning_channel
[channr
] = -1;
535 EXPORT_SYMBOL(pnx4008_free_dum_channel
);
537 int pnx4008_put_dum_channel_uf(struct dumchannel_uf chan_uf
, int dev_id
)
539 int i
= chan_uf
.channelnr
;
542 if (i
< 0 || i
> MAX_DUM_CHANNELS
)
544 else if (dum_data
.fb_owning_channel
[i
] != dev_id
)
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)
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
;
564 EXPORT_SYMBOL(pnx4008_put_dum_channel_uf
);
566 int pnx4008_set_dum_channel_sync(int channr
, int val
, int dev_id
)
568 if (channr
< 0 || channr
> MAX_DUM_CHANNELS
)
570 else if (dum_data
.fb_owning_channel
[channr
] != dev_id
)
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
;
586 EXPORT_SYMBOL(pnx4008_set_dum_channel_sync
);
588 int pnx4008_set_dum_channel_dirty_detect(int channr
, int val
, int dev_id
)
590 if (channr
< 0 || channr
> MAX_DUM_CHANNELS
)
592 else if (dum_data
.fb_owning_channel
[channr
] != dev_id
)
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
;
606 EXPORT_SYMBOL(pnx4008_set_dum_channel_dirty_detect
);
608 #if 0 /* Functions not used currently, but likely to be used in future */
610 static int get_channel(struct dumchannel
*p_chan
)
612 int i
= p_chan
->channelnr
;
614 if (i
< 0 || i
> MAX_DUM_CHANNELS
)
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 */
627 int pnx4008_get_dum_channel_uf(struct dumchannel_uf
*p_chan_uf
, int dev_id
)
629 int i
= p_chan_uf
->channelnr
;
631 if (i
< 0 || i
> MAX_DUM_CHANNELS
)
633 else if (dum_data
.fb_owning_channel
[i
] != dev_id
)
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
;
647 EXPORT_SYMBOL(pnx4008_get_dum_channel_uf
);
649 int pnx4008_get_dum_channel_config(int channr
, int dev_id
)
652 struct dumchannel chan
;
654 if (channr
< 0 || channr
> MAX_DUM_CHANNELS
)
656 else if (dum_data
.fb_owning_channel
[channr
] != dev_id
)
659 chan
.channelnr
= channr
;
660 if ((ret
= get_channel(&chan
)) != 0)
664 return (chan
.dum_ch_conf
& DUM_CHANNEL_CFG_MASK
);
667 EXPORT_SYMBOL(pnx4008_get_dum_channel_config
);
669 int pnx4008_force_update_dum_channel(int channr
, int dev_id
)
671 if (channr
< 0 || channr
> MAX_DUM_CHANNELS
)
674 else if (dum_data
.fb_owning_channel
[channr
] != dev_id
)
677 DUM_CH_CTRL(channr
) = CTRL_SETDIRTY
;
682 EXPORT_SYMBOL(pnx4008_force_update_dum_channel
);
686 int pnx4008_sdum_mmap(struct fb_info
*info
, struct vm_area_struct
*vma
,
689 unsigned long off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
691 if (off
< info
->fix
.smem_len
) {
693 return dma_mmap_writecombine(dev
, vma
,
694 (void *)dum_data
.lcd_virt_start
,
695 dum_data
.lcd_phys_start
,
701 EXPORT_SYMBOL(pnx4008_sdum_mmap
);
703 int pnx4008_set_dum_exit_notification(int dev_id
)
707 for (i
= 0; i
< MAX_DUM_CHANNELS
; i
++)
708 if (dum_data
.fb_owning_channel
[i
] == dev_id
)
709 return -ERESOURCESNOTFREED
;
714 EXPORT_SYMBOL(pnx4008_set_dum_exit_notification
);
716 /* Platform device driver for DUM */
718 static int sdum_suspend(struct platform_device
*pdev
, pm_message_t state
)
723 clk
= clk_get(0, "dum_ck");
725 clk_set_rate(clk
, 0);
728 retval
= PTR_ERR(clk
);
731 DUM_CTRL
= V_BAC_DISABLE_IDLE
;
733 /* LCD standby & turn off display */
739 static int sdum_resume(struct platform_device
*pdev
)
744 clk
= clk_get(0, "dum_ck");
746 clk_set_rate(clk
, 1);
749 retval
= PTR_ERR(clk
);
751 /* wait for BAC disable */
752 DUM_CTRL
= V_BAC_DISABLE_TRIG
;
754 while (DUM_CTRL
& BAC_ENABLED
)
760 /* enable BAC and reset MUX */
761 DUM_CTRL
= V_BAC_ENABLE
;
763 DUM_CTRL
= V_MUX_RESET
;
767 static int __devinit
sdum_probe(struct platform_device
*pdev
)
771 /* map frame buffer */
772 dum_data
.lcd_virt_start
= (u32
) dma_alloc_writecombine(&pdev
->dev
,
774 &dum_data
.lcd_phys_start
,
777 if (!dum_data
.lcd_virt_start
) {
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
));
787 if (dum_data
.slave_virt_base
== NULL
) {
792 /* initialize DUM and LCD display */
793 ret
= dum_init(pdev
);
800 DUM_CTRL
= V_BAC_ENABLE
;
802 DUM_CTRL
= V_MUX_RESET
;
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
;
808 for (i
= 0; i
< MAX_DUM_CHANNELS
; i
++)
809 dum_data
.fb_owning_channel
[i
] = -1;
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
);
819 iounmap((void *)dum_data
.slave_virt_base
);
821 dma_free_writecombine(&pdev
->dev
, FB_DMA_SIZE
,
822 (void *)dum_data
.lcd_virt_start
,
823 dum_data
.lcd_phys_start
);
828 static int sdum_remove(struct platform_device
*pdev
)
832 start_int_mask(SE_DISP_SYNC_INT
);
834 clk
= clk_get(0, "dum_ck");
836 clk_set_rate(clk
, 0);
840 iounmap((void *)dum_data
.slave_virt_base
);
842 dma_free_writecombine(&pdev
->dev
, FB_DMA_SIZE
,
843 (void *)dum_data
.lcd_virt_start
,
844 dum_data
.lcd_phys_start
);
849 static struct platform_driver sdum_driver
= {
851 .name
= "pnx4008-sdum",
854 .remove
= sdum_remove
,
855 .suspend
= sdum_suspend
,
856 .resume
= sdum_resume
,
859 int __init
sdum_init(void)
861 return platform_driver_register(&sdum_driver
);
864 static void __exit
sdum_exit(void)
866 platform_driver_unregister(&sdum_driver
);
869 module_init(sdum_init
);
870 module_exit(sdum_exit
);
872 MODULE_LICENSE("GPL");