PageRenderTime 142ms CodeModel.GetById 13ms app.highlight 71ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/video/pnx4008/sdum.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 872 lines | 679 code | 155 blank | 38 comment | 137 complexity | 3b961d86fa481c7ed93ade02f3f15503 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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/vmalloc.h>
 24#include <linux/delay.h>
 25#include <linux/interrupt.h>
 26#include <linux/platform_device.h>
 27#include <linux/fb.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>
 33#include <mach/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#if 0 /* Functions not used currently, but likely to be used in future */
609
610static 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
627int 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
647EXPORT_SYMBOL(pnx4008_get_dum_channel_uf);
648
649int 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
667EXPORT_SYMBOL(pnx4008_get_dum_channel_config);
668
669int 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
682EXPORT_SYMBOL(pnx4008_force_update_dum_channel);
683
684#endif
685
686int 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
701EXPORT_SYMBOL(pnx4008_sdum_mmap);
702
703int 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
714EXPORT_SYMBOL(pnx4008_set_dum_exit_notification);
715
716/* Platform device driver for DUM */
717
718static 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
739static 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
767static 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
818out_1:
819	iounmap((void *)dum_data.slave_virt_base);
820out_2:
821	dma_free_writecombine(&pdev->dev, FB_DMA_SIZE,
822			(void *)dum_data.lcd_virt_start,
823			dum_data.lcd_phys_start);
824out_3:
825	return ret;
826}
827
828static 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
849static struct platform_driver sdum_driver = {
850	.driver = {
851		.name = "pnx4008-sdum",
852	},
853	.probe = sdum_probe,
854	.remove = sdum_remove,
855	.suspend = sdum_suspend,
856	.resume = sdum_resume,
857};
858
859int __init sdum_init(void)
860{
861	return platform_driver_register(&sdum_driver);
862}
863
864static void __exit sdum_exit(void)
865{
866	platform_driver_unregister(&sdum_driver);
867};
868
869module_init(sdum_init);
870module_exit(sdum_exit);
871
872MODULE_LICENSE("GPL");