• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/video/
1/*
2 * SH7760/SH7763 LCDC Framebuffer driver.
3 *
4 * (c) 2006-2008 MSC Vertriebsges.m.b.H.,
5 *             Manuel Lauss <mano@roarinelk.homelinux.net>
6 * (c) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
7 *
8 *  This file is subject to the terms and conditions of the GNU General
9 *  Public License.  See the file COPYING in the main directory of this
10 *  archive for more details.
11 *
12 * PLEASE HAVE A LOOK AT Documentation/fb/sh7760fb.txt!
13 *
14 * Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
15 *     for his original source and testing!
16 *
17 * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
18 */
19
20#include <linux/completion.h>
21#include <linux/delay.h>
22#include <linux/dma-mapping.h>
23#include <linux/fb.h>
24#include <linux/interrupt.h>
25#include <linux/io.h>
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/platform_device.h>
29#include <linux/slab.h>
30
31#include <asm/sh7760fb.h>
32
33struct sh7760fb_par {
34	void __iomem *base;
35	int irq;
36
37	struct sh7760fb_platdata *pd;	/* display information */
38
39	dma_addr_t fbdma;	/* physical address */
40
41	int rot;		/* rotation enabled? */
42
43	u32 pseudo_palette[16];
44
45	struct platform_device *dev;
46	struct resource *ioarea;
47	struct completion vsync;	/* vsync irq event */
48};
49
50static irqreturn_t sh7760fb_irq(int irq, void *data)
51{
52	struct completion *c = data;
53
54	complete(c);
55
56	return IRQ_HANDLED;
57}
58
59/* wait_for_lps - wait until power supply has reached a certain state. */
60static int wait_for_lps(struct sh7760fb_par *par, int val)
61{
62	int i = 100;
63	while (--i && ((ioread16(par->base + LDPMMR) & 3) != val))
64		msleep(1);
65
66	if (i <= 0)
67		return -ETIMEDOUT;
68
69	return 0;
70}
71
72/* en/disable the LCDC */
73static int sh7760fb_blank(int blank, struct fb_info *info)
74{
75	struct sh7760fb_par *par = info->par;
76	struct sh7760fb_platdata *pd = par->pd;
77	unsigned short cntr = ioread16(par->base + LDCNTR);
78	unsigned short intr = ioread16(par->base + LDINTR);
79	int lps;
80
81	if (blank == FB_BLANK_UNBLANK) {
82		intr |= VINT_START;
83		cntr = LDCNTR_DON2 | LDCNTR_DON;
84		lps = 3;
85	} else {
86		intr &= ~VINT_START;
87		cntr = LDCNTR_DON2;
88		lps = 0;
89	}
90
91	if (pd->blank)
92		pd->blank(blank);
93
94	iowrite16(intr, par->base + LDINTR);
95	iowrite16(cntr, par->base + LDCNTR);
96
97	return wait_for_lps(par, lps);
98}
99
100static int sh7760_setcolreg (u_int regno,
101	u_int red, u_int green, u_int blue,
102	u_int transp, struct fb_info *info)
103{
104	u32 *palette = info->pseudo_palette;
105
106	if (regno >= 16)
107		return -EINVAL;
108
109	/* only FB_VISUAL_TRUECOLOR supported */
110
111	red >>= 16 - info->var.red.length;
112	green >>= 16 - info->var.green.length;
113	blue >>= 16 - info->var.blue.length;
114	transp >>= 16 - info->var.transp.length;
115
116	palette[regno] = (red << info->var.red.offset) |
117		(green << info->var.green.offset) |
118		(blue << info->var.blue.offset) |
119		(transp << info->var.transp.offset);
120
121	return 0;
122}
123
124static int sh7760fb_get_color_info(struct device *dev,
125				   u16 lddfr, int *bpp, int *gray)
126{
127	int lbpp, lgray;
128
129	lgray = lbpp = 0;
130
131	switch (lddfr & LDDFR_COLOR_MASK) {
132	case LDDFR_1BPP_MONO:
133		lgray = 1;
134		lbpp = 1;
135		break;
136	case LDDFR_2BPP_MONO:
137		lgray = 1;
138		lbpp = 2;
139		break;
140	case LDDFR_4BPP_MONO:
141		lgray = 1;
142	case LDDFR_4BPP:
143		lbpp = 4;
144		break;
145	case LDDFR_6BPP_MONO:
146		lgray = 1;
147	case LDDFR_8BPP:
148		lbpp = 8;
149		break;
150	case LDDFR_16BPP_RGB555:
151	case LDDFR_16BPP_RGB565:
152		lbpp = 16;
153		lgray = 0;
154		break;
155	default:
156		dev_dbg(dev, "unsupported LDDFR bit depth.\n");
157		return -EINVAL;
158	}
159
160	if (bpp)
161		*bpp = lbpp;
162	if (gray)
163		*gray = lgray;
164
165	return 0;
166}
167
168static int sh7760fb_check_var(struct fb_var_screeninfo *var,
169			      struct fb_info *info)
170{
171	struct fb_fix_screeninfo *fix = &info->fix;
172	struct sh7760fb_par *par = info->par;
173	int ret, bpp;
174
175	/* get color info from register value */
176	ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
177	if (ret)
178		return ret;
179
180	var->bits_per_pixel = bpp;
181
182	if ((var->grayscale) && (var->bits_per_pixel == 1))
183		fix->visual = FB_VISUAL_MONO10;
184	else if (var->bits_per_pixel >= 15)
185		fix->visual = FB_VISUAL_TRUECOLOR;
186	else
187		fix->visual = FB_VISUAL_PSEUDOCOLOR;
188
189	/* TODO: add some more validation here */
190	return 0;
191}
192
193/*
194 * sh7760fb_set_par - set videomode.
195 *
196 * NOTE: The rotation, grayscale and DSTN codepaths are
197 *     totally untested!
198 */
199static int sh7760fb_set_par(struct fb_info *info)
200{
201	struct sh7760fb_par *par = info->par;
202	struct fb_videomode *vm = par->pd->def_mode;
203	unsigned long sbase, dstn_off, ldsarl, stride;
204	unsigned short hsynp, hsynw, htcn, hdcn;
205	unsigned short vsynp, vsynw, vtln, vdln;
206	unsigned short lddfr, ldmtr;
207	int ret, bpp, gray;
208
209	par->rot = par->pd->rotate;
210
211	/* rotate only works with xres <= 320 */
212	if (par->rot && (vm->xres > 320)) {
213		dev_dbg(info->dev, "rotation disabled due to display size\n");
214		par->rot = 0;
215	}
216
217	/* calculate LCDC reg vals from display parameters */
218	hsynp = vm->right_margin + vm->xres;
219	hsynw = vm->hsync_len;
220	htcn = vm->left_margin + hsynp + hsynw;
221	hdcn = vm->xres;
222	vsynp = vm->lower_margin + vm->yres;
223	vsynw = vm->vsync_len;
224	vtln = vm->upper_margin + vsynp + vsynw;
225	vdln = vm->yres;
226
227	/* get color info from register value */
228	ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, &gray);
229	if (ret)
230		return ret;
231
232	dev_dbg(info->dev, "%dx%d %dbpp %s (orientation %s)\n", hdcn,
233		vdln, bpp, gray ? "grayscale" : "color",
234		par->rot ? "rotated" : "normal");
235
236#ifdef CONFIG_CPU_LITTLE_ENDIAN
237	lddfr = par->pd->lddfr | (1 << 8);
238#else
239	lddfr = par->pd->lddfr & ~(1 << 8);
240#endif
241
242	ldmtr = par->pd->ldmtr;
243
244	if (!(vm->sync & FB_SYNC_HOR_HIGH_ACT))
245		ldmtr |= LDMTR_CL1POL;
246	if (!(vm->sync & FB_SYNC_VERT_HIGH_ACT))
247		ldmtr |= LDMTR_FLMPOL;
248
249	/* shut down LCDC before changing display parameters */
250	sh7760fb_blank(FB_BLANK_POWERDOWN, info);
251
252	iowrite16(par->pd->ldickr, par->base + LDICKR);	/* pixclock */
253	iowrite16(ldmtr, par->base + LDMTR);	/* polarities */
254	iowrite16(lddfr, par->base + LDDFR);	/* color/depth */
255	iowrite16((par->rot ? 1 << 13 : 0), par->base + LDSMR);	/* rotate */
256	iowrite16(par->pd->ldpmmr, par->base + LDPMMR);	/* Power Management */
257	iowrite16(par->pd->ldpspr, par->base + LDPSPR);	/* Power Supply Ctrl */
258
259	/* display resolution */
260	iowrite16(((htcn >> 3) - 1) | (((hdcn >> 3) - 1) << 8),
261		  par->base + LDHCNR);
262	iowrite16(vdln - 1, par->base + LDVDLNR);
263	iowrite16(vtln - 1, par->base + LDVTLNR);
264	/* h/v sync signals */
265	iowrite16((vsynp - 1) | ((vsynw - 1) << 12), par->base + LDVSYNR);
266	iowrite16(((hsynp >> 3) - 1) | (((hsynw >> 3) - 1) << 12),
267		  par->base + LDHSYNR);
268	/* AC modulation sig */
269	iowrite16(par->pd->ldaclnr, par->base + LDACLNR);
270
271	stride = (par->rot) ? vtln : hdcn;
272	if (!gray)
273		stride *= (bpp + 7) >> 3;
274	else {
275		if (bpp == 1)
276			stride >>= 3;
277		else if (bpp == 2)
278			stride >>= 2;
279		else if (bpp == 4)
280			stride >>= 1;
281		/* 6 bpp == 8 bpp */
282	}
283
284	/* if rotated, stride must be power of 2 */
285	if (par->rot) {
286		unsigned long bit = 1 << 31;
287		while (bit) {
288			if (stride & bit)
289				break;
290			bit >>= 1;
291		}
292		if (stride & ~bit)
293			stride = bit << 1;	/* not P-o-2, round up */
294	}
295	iowrite16(stride, par->base + LDLAOR);
296
297	/* set display mem start address */
298	sbase = (unsigned long)par->fbdma;
299	if (par->rot)
300		sbase += (hdcn - 1) * stride;
301
302	iowrite32(sbase, par->base + LDSARU);
303
304	/*
305	 * for DSTN need to set address for lower half.
306	 * I (mlau) don't know which address to set it to,
307	 * so I guessed at (stride * yres/2).
308	 */
309	if (((ldmtr & 0x003f) >= LDMTR_DSTN_MONO_8) &&
310	    ((ldmtr & 0x003f) <= LDMTR_DSTN_COLOR_16)) {
311
312		dev_dbg(info->dev, " ***** DSTN untested! *****\n");
313
314		dstn_off = stride;
315		if (par->rot)
316			dstn_off *= hdcn >> 1;
317		else
318			dstn_off *= vdln >> 1;
319
320		ldsarl = sbase + dstn_off;
321	} else
322		ldsarl = 0;
323
324	iowrite32(ldsarl, par->base + LDSARL);	/* mem for lower half of DSTN */
325
326	info->fix.line_length = stride;
327
328	sh7760fb_check_var(&info->var, info);
329
330	sh7760fb_blank(FB_BLANK_UNBLANK, info);	/* panel on! */
331
332	dev_dbg(info->dev, "hdcn  : %6d htcn  : %6d\n", hdcn, htcn);
333	dev_dbg(info->dev, "hsynw : %6d hsynp : %6d\n", hsynw, hsynp);
334	dev_dbg(info->dev, "vdln  : %6d vtln  : %6d\n", vdln, vtln);
335	dev_dbg(info->dev, "vsynw : %6d vsynp : %6d\n", vsynw, vsynp);
336	dev_dbg(info->dev, "clksrc: %6d clkdiv: %6d\n",
337		(par->pd->ldickr >> 12) & 3, par->pd->ldickr & 0x1f);
338	dev_dbg(info->dev, "ldpmmr: 0x%04x ldpspr: 0x%04x\n", par->pd->ldpmmr,
339		par->pd->ldpspr);
340	dev_dbg(info->dev, "ldmtr : 0x%04x lddfr : 0x%04x\n", ldmtr, lddfr);
341	dev_dbg(info->dev, "ldlaor: %ld\n", stride);
342	dev_dbg(info->dev, "ldsaru: 0x%08lx ldsarl: 0x%08lx\n", sbase, ldsarl);
343
344	return 0;
345}
346
347static struct fb_ops sh7760fb_ops = {
348	.owner = THIS_MODULE,
349	.fb_blank = sh7760fb_blank,
350	.fb_check_var = sh7760fb_check_var,
351	.fb_setcolreg = sh7760_setcolreg,
352	.fb_set_par = sh7760fb_set_par,
353	.fb_fillrect = cfb_fillrect,
354	.fb_copyarea = cfb_copyarea,
355	.fb_imageblit = cfb_imageblit,
356};
357
358static void sh7760fb_free_mem(struct fb_info *info)
359{
360	struct sh7760fb_par *par = info->par;
361
362	if (!info->screen_base)
363		return;
364
365	dma_free_coherent(info->dev, info->screen_size,
366			  info->screen_base, par->fbdma);
367
368	par->fbdma = 0;
369	info->screen_base = NULL;
370	info->screen_size = 0;
371}
372
373/* allocate the framebuffer memory. This memory must be in Area3,
374 * (dictated by the DMA engine) and contiguous, at a 512 byte boundary.
375 */
376static int sh7760fb_alloc_mem(struct fb_info *info)
377{
378	struct sh7760fb_par *par = info->par;
379	void *fbmem;
380	unsigned long vram;
381	int ret, bpp;
382
383	if (info->screen_base)
384		return 0;
385
386	/* get color info from register value */
387	ret = sh7760fb_get_color_info(info->dev, par->pd->lddfr, &bpp, NULL);
388	if (ret) {
389		printk(KERN_ERR "colinfo\n");
390		return ret;
391	}
392
393	/* min VRAM: xres_min = 16, yres_min = 1, bpp = 1: 2byte -> 1 page
394	   max VRAM: xres_max = 1024, yres_max = 1024, bpp = 16: 2MB */
395
396	vram = info->var.xres * info->var.yres;
397	if (info->var.grayscale) {
398		if (bpp == 1)
399			vram >>= 3;
400		else if (bpp == 2)
401			vram >>= 2;
402		else if (bpp == 4)
403			vram >>= 1;
404	} else if (bpp > 8)
405		vram *= 2;
406	if ((vram < 1) || (vram > 1024 * 2048)) {
407		dev_dbg(info->dev, "too much VRAM required. Check settings\n");
408		return -ENODEV;
409	}
410
411	if (vram < PAGE_SIZE)
412		vram = PAGE_SIZE;
413
414	fbmem = dma_alloc_coherent(info->dev, vram, &par->fbdma, GFP_KERNEL);
415
416	if (!fbmem)
417		return -ENOMEM;
418
419	if ((par->fbdma & SH7760FB_DMA_MASK) != SH7760FB_DMA_MASK) {
420		sh7760fb_free_mem(info);
421		dev_err(info->dev, "kernel gave me memory at 0x%08lx, which is"
422			"unusable for the LCDC\n", (unsigned long)par->fbdma);
423		return -ENOMEM;
424	}
425
426	info->screen_base = fbmem;
427	info->screen_size = vram;
428	info->fix.smem_start = (unsigned long)info->screen_base;
429	info->fix.smem_len = info->screen_size;
430
431	return 0;
432}
433
434static int __devinit sh7760fb_probe(struct platform_device *pdev)
435{
436	struct fb_info *info;
437	struct resource *res;
438	struct sh7760fb_par *par;
439	int ret;
440
441	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
442	if (unlikely(res == NULL)) {
443		dev_err(&pdev->dev, "invalid resource\n");
444		return -EINVAL;
445	}
446
447	info = framebuffer_alloc(sizeof(struct sh7760fb_par), &pdev->dev);
448	if (!info)
449		return -ENOMEM;
450
451	par = info->par;
452	par->dev = pdev;
453
454	par->pd = pdev->dev.platform_data;
455	if (!par->pd) {
456		dev_dbg(info->dev, "no display setup data!\n");
457		ret = -ENODEV;
458		goto out_fb;
459	}
460
461	par->ioarea = request_mem_region(res->start,
462					 (res->end - res->start), pdev->name);
463	if (!par->ioarea) {
464		dev_err(&pdev->dev, "mmio area busy\n");
465		ret = -EBUSY;
466		goto out_fb;
467	}
468
469	par->base = ioremap_nocache(res->start, res->end - res->start + 1);
470	if (!par->base) {
471		dev_err(&pdev->dev, "cannot remap\n");
472		ret = -ENODEV;
473		goto out_res;
474	}
475
476	iowrite16(0, par->base + LDINTR);	/* disable vsync irq */
477	par->irq = platform_get_irq(pdev, 0);
478	if (par->irq >= 0) {
479		ret = request_irq(par->irq, sh7760fb_irq, 0,
480				  "sh7760-lcdc", &par->vsync);
481		if (ret) {
482			dev_err(&pdev->dev, "cannot grab IRQ\n");
483			par->irq = -ENXIO;
484		} else
485			disable_irq_nosync(par->irq);
486	}
487
488	fb_videomode_to_var(&info->var, par->pd->def_mode);
489
490	ret = sh7760fb_alloc_mem(info);
491	if (ret) {
492		dev_dbg(info->dev, "framebuffer memory allocation failed!\n");
493		goto out_unmap;
494	}
495
496	info->pseudo_palette = par->pseudo_palette;
497
498	/* fixup color register bitpositions. These are fixed by hardware */
499	info->var.red.offset = 11;
500	info->var.red.length = 5;
501	info->var.red.msb_right = 0;
502
503	info->var.green.offset = 5;
504	info->var.green.length = 6;
505	info->var.green.msb_right = 0;
506
507	info->var.blue.offset = 0;
508	info->var.blue.length = 5;
509	info->var.blue.msb_right = 0;
510
511	info->var.transp.offset = 0;
512	info->var.transp.length = 0;
513	info->var.transp.msb_right = 0;
514
515	strcpy(info->fix.id, "sh7760-lcdc");
516
517	/* set the DON2 bit now, before cmap allocation, as it will randomize
518	 * palette memory.
519	 */
520	iowrite16(LDCNTR_DON2, par->base + LDCNTR);
521	info->fbops = &sh7760fb_ops;
522
523	ret = fb_alloc_cmap(&info->cmap, 256, 0);
524	if (ret) {
525		dev_dbg(info->dev, "Unable to allocate cmap memory\n");
526		goto out_mem;
527	}
528
529	ret = register_framebuffer(info);
530	if (ret < 0) {
531		dev_dbg(info->dev, "cannot register fb!\n");
532		goto out_cmap;
533	}
534	platform_set_drvdata(pdev, info);
535
536	printk(KERN_INFO "%s: memory at phys 0x%08lx-0x%08lx, size %ld KiB\n",
537	       pdev->name,
538	       (unsigned long)par->fbdma,
539	       (unsigned long)(par->fbdma + info->screen_size - 1),
540	       info->screen_size >> 10);
541
542	return 0;
543
544out_cmap:
545	sh7760fb_blank(FB_BLANK_POWERDOWN, info);
546	fb_dealloc_cmap(&info->cmap);
547out_mem:
548	sh7760fb_free_mem(info);
549out_unmap:
550	if (par->irq >= 0)
551		free_irq(par->irq, &par->vsync);
552	iounmap(par->base);
553out_res:
554	release_resource(par->ioarea);
555	kfree(par->ioarea);
556out_fb:
557	framebuffer_release(info);
558	return ret;
559}
560
561static int __devexit sh7760fb_remove(struct platform_device *dev)
562{
563	struct fb_info *info = platform_get_drvdata(dev);
564	struct sh7760fb_par *par = info->par;
565
566	sh7760fb_blank(FB_BLANK_POWERDOWN, info);
567	unregister_framebuffer(info);
568	fb_dealloc_cmap(&info->cmap);
569	sh7760fb_free_mem(info);
570	if (par->irq >= 0)
571		free_irq(par->irq, par);
572	iounmap(par->base);
573	release_resource(par->ioarea);
574	kfree(par->ioarea);
575	framebuffer_release(info);
576	platform_set_drvdata(dev, NULL);
577
578	return 0;
579}
580
581static struct platform_driver sh7760_lcdc_driver = {
582	.driver = {
583		   .name = "sh7760-lcdc",
584		   .owner = THIS_MODULE,
585		   },
586	.probe = sh7760fb_probe,
587	.remove = __devexit_p(sh7760fb_remove),
588};
589
590static int __init sh7760fb_init(void)
591{
592	return platform_driver_register(&sh7760_lcdc_driver);
593}
594
595static void __exit sh7760fb_exit(void)
596{
597	platform_driver_unregister(&sh7760_lcdc_driver);
598}
599
600module_init(sh7760fb_init);
601module_exit(sh7760fb_exit);
602
603MODULE_AUTHOR("Nobuhiro Iwamatsu, Manuel Lauss");
604MODULE_DESCRIPTION("FBdev for SH7760/63 integrated LCD Controller");
605MODULE_LICENSE("GPL");
606