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>
33#include <asm/arch/gpio.h>
34
35#include "sdum.h"
36#include "fbcommon.h"
37#include "dum.h"
38
39/* Framebuffers we have */
40
41static 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
54static 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
65static u32 nof_pixels_dx(struct dum_ch_setup *ch_setup)
66{
67	return (ch_setup->xmax - ch_setup->xmin + 1);
68}
69
70static u32 nof_pixels_dy(struct dum_ch_setup *ch_setup)
71{
72	return (ch_setup->ymax - ch_setup->ymin + 1);
73}
74
75static 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
80static 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
96static 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
102static u32 build_double_index(int disp_no, u32 val)
103{
104	return ((disp_no << 26) | (val << 16) | (disp_no << 10) | (val << 0));
105}
106
107static 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
117static 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
133static 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
146static 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
191static 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
331static 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
379static 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
390static 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
417static 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
452out:
453	iounmap((u32 *)cmdptrs);
454}
455
456static 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
483int 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
502EXPORT_SYMBOL(pnx4008_get_fb_addresses);
503
504int 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
519EXPORT_SYMBOL(pnx4008_alloc_dum_channel);
520
521int 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
535EXPORT_SYMBOL(pnx4008_free_dum_channel);
536
537int 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
564EXPORT_SYMBOL(pnx4008_put_dum_channel_uf);
565
566int 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
586EXPORT_SYMBOL(pnx4008_set_dum_channel_sync);
587
588int 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
606EXPORT_SYMBOL(pnx4008_set_dum_channel_dirty_detect);
607
608
609int pnx4008_sdum_mmap(struct fb_info *info, struct vm_area_struct *vma,
610		      struct device *dev)
611{
612	unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
613
614	if (off < info->fix.smem_len) {
615		vma->vm_pgoff += 1;
616		return dma_mmap_writecombine(dev, vma,
617				(void *)dum_data.lcd_virt_start,
618				dum_data.lcd_phys_start,
619				FB_DMA_SIZE);
620	}
621	return -EINVAL;
622}
623
624EXPORT_SYMBOL(pnx4008_sdum_mmap);
625
626int pnx4008_set_dum_exit_notification(int dev_id)
627{
628	int i;
629
630	for (i = 0; i < MAX_DUM_CHANNELS; i++)
631		if (dum_data.fb_owning_channel[i] == dev_id)
632			return -ERESOURCESNOTFREED;
633
634	return 0;
635}
636
637EXPORT_SYMBOL(pnx4008_set_dum_exit_notification);
638
639/* Platform device driver for DUM */
640
641static int sdum_suspend(struct platform_device *pdev, pm_message_t state)
642{
643	int retval = 0;
644	struct clk *clk;
645
646	clk = clk_get(0, "dum_ck");
647	if (!IS_ERR(clk)) {
648		clk_set_rate(clk, 0);
649		clk_put(clk);
650	} else
651		retval = PTR_ERR(clk);
652
653	/* disable BAC */
654	DUM_CTRL = V_BAC_DISABLE_IDLE;
655
656	/* LCD standby & turn off display */
657	lcd_reset();
658
659	return retval;
660}
661
662static int sdum_resume(struct platform_device *pdev)
663{
664	int retval = 0;
665	struct clk *clk;
666
667	clk = clk_get(0, "dum_ck");
668	if (!IS_ERR(clk)) {
669		clk_set_rate(clk, 1);
670		clk_put(clk);
671	} else
672		retval = PTR_ERR(clk);
673
674	/* wait for BAC disable */
675	DUM_CTRL = V_BAC_DISABLE_TRIG;
676
677	while (DUM_CTRL & BAC_ENABLED)
678		udelay(10);
679
680	/* re-init LCD */
681	lcd_init();
682
683	/* enable BAC and reset MUX */
684	DUM_CTRL = V_BAC_ENABLE;
685	udelay(1);
686	DUM_CTRL = V_MUX_RESET;
687	return 0;
688}
689
690static int __devinit sdum_probe(struct platform_device *pdev)
691{
692	int ret = 0, i = 0;
693
694	/* map frame buffer */
695	dum_data.lcd_virt_start = (u32) dma_alloc_writecombine(&pdev->dev,
696						       FB_DMA_SIZE,
697						       &dum_data.lcd_phys_start,
698						       GFP_KERNEL);
699
700	if (!dum_data.lcd_virt_start) {
701		ret = -ENOMEM;
702		goto out_3;
703	}
704
705	/* map slave registers */
706	dum_data.slave_phys_base = PNX4008_DUM_SLAVE_BASE;
707	dum_data.slave_virt_base =
708	    (u32 *) ioremap_nocache(dum_data.slave_phys_base, sizeof(u32));
709
710	if (dum_data.slave_virt_base == NULL) {
711		ret = -ENOMEM;
712		goto out_2;
713	}
714
715	/* initialize DUM and LCD display */
716	ret = dum_init(pdev);
717	if (ret)
718		goto out_1;
719
720	dum_chan_init();
721	lcd_init();
722
723	DUM_CTRL = V_BAC_ENABLE;
724	udelay(1);
725	DUM_CTRL = V_MUX_RESET;
726
727	/* set decode address and sync clock divider */
728	DUM_DECODE = dum_data.lcd_phys_start & DUM_DECODE_MASK;
729	DUM_CLK_DIV = PNX4008_DUM_CLK_DIV;
730
731	for (i = 0; i < MAX_DUM_CHANNELS; i++)
732		dum_data.fb_owning_channel[i] = -1;
733
734	/*setup wakeup interrupt */
735	start_int_set_rising_edge(SE_DISP_SYNC_INT);
736	start_int_ack(SE_DISP_SYNC_INT);
737	start_int_umask(SE_DISP_SYNC_INT);
738
739	return 0;
740
741out_1:
742	iounmap((void *)dum_data.slave_virt_base);
743out_2:
744	dma_free_writecombine(&pdev->dev, FB_DMA_SIZE,
745			(void *)dum_data.lcd_virt_start,
746			dum_data.lcd_phys_start);
747out_3:
748	return ret;
749}
750
751static int sdum_remove(struct platform_device *pdev)
752{
753	struct clk *clk;
754
755	start_int_mask(SE_DISP_SYNC_INT);
756
757	clk = clk_get(0, "dum_ck");
758	if (!IS_ERR(clk)) {
759		clk_set_rate(clk, 0);
760		clk_put(clk);
761	}
762
763	iounmap((void *)dum_data.slave_virt_base);
764
765	dma_free_writecombine(&pdev->dev, FB_DMA_SIZE,
766			(void *)dum_data.lcd_virt_start,
767			dum_data.lcd_phys_start);
768
769	return 0;
770}
771
772static struct platform_driver sdum_driver = {
773	.driver = {
774		.name = "pnx4008-sdum",
775	},
776	.probe = sdum_probe,
777	.remove = sdum_remove,
778	.suspend = sdum_suspend,
779	.resume = sdum_resume,
780};
781
782int __init sdum_init(void)
783{
784	return platform_driver_register(&sdum_driver);
785}
786
787static void __exit sdum_exit(void)
788{
789	platform_driver_unregister(&sdum_driver);
790};
791
792module_init(sdum_init);
793module_exit(sdum_exit);
794
795MODULE_LICENSE("GPL");
796