brintos

brintos / linux-shallow public Read only

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