xilinxfb.c 14.0 KB
Newer Older
1
/*
2
 * Xilinx TFT frame buffer driver
3 4 5 6
 *
 * Author: MontaVista Software, Inc.
 *         source@mvista.com
 *
7 8
 * 2002-2007 (c) MontaVista Software, Inc.
 * 2007 (c) Secret Lab Technologies, Ltd.
9
 * 2009 (c) Xilinx Inc.
10 11 12 13
 *
 * This file is licensed under the terms of the GNU General Public License
 * version 2.  This program is licensed "as is" without any warranty of any
 * kind, whether express or implied.
14 15 16 17 18 19 20 21 22
 */

/*
 * This driver was based on au1100fb.c by MontaVista rewritten for 2.6
 * by Embedded Alley Solutions <source@embeddedalley.com>, which in turn
 * was based on skeletonfb.c, Skeleton for a frame buffer device by
 * Geert Uytterhoeven.
 */

23
#include <linux/device.h>
24 25
#include <linux/module.h>
#include <linux/kernel.h>
26
#include <linux/version.h>
27 28 29 30 31 32
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
33 34
#include <linux/of_device.h>
#include <linux/of_platform.h>
35
#include <linux/of_address.h>
36
#include <linux/io.h>
37
#include <linux/xilinxfb.h>
38
#include <linux/slab.h>
39 40

#ifdef CONFIG_PPC_DCR
41
#include <asm/dcr.h>
42
#endif
43 44

#define DRIVER_NAME		"xilinxfb"
45

46 47 48

/*
 * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for
49 50
 * the VGA port on the Xilinx ML40x board. This is a hardware display
 * controller for a 640x480 resolution TFT or VGA screen.
51 52 53 54 55 56
 *
 * The interface to the framebuffer is nice and simple.  There are two
 * control registers.  The first tells the LCD interface where in memory
 * the frame buffer is (only the 11 most significant bits are used, so
 * don't start thinking about scrolling).  The second allows the LCD to
 * be turned on or off as well as rotated 180 degrees.
57 58 59 60 61 62
 *
 * In case of direct PLB access the second control register will be at
 * an offset of 4 as compared to the DCR access where the offset is 1
 * i.e. REG_CTRL. So this is taken care in the function
 * xilinx_fb_out_be32 where it left shifts the offset 2 times in case of
 * direct PLB access.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
 */
#define NUM_REGS	2
#define REG_FB_ADDR	0
#define REG_CTRL	1
#define REG_CTRL_ENABLE	 0x0001
#define REG_CTRL_ROTATE	 0x0002

/*
 * The hardware only handles a single mode: 640x480 24 bit true
 * color. Each pixel gets a word (32 bits) of memory.  Within each word,
 * the 8 most significant bits are ignored, the next 8 bits are the red
 * level, the next 8 bits are the green level and the 8 least
 * significant bits are the blue level.  Each row of the LCD uses 1024
 * words, but only the first 640 pixels are displayed with the other 384
 * words being ignored.  There are 480 rows.
 */
#define BYTES_PER_PIXEL	4
#define BITS_PER_PIXEL	(BYTES_PER_PIXEL * 8)

#define RED_SHIFT	16
#define GREEN_SHIFT	8
#define BLUE_SHIFT	0

#define PALETTE_ENTRIES_NO	16	/* passed to fb_alloc_cmap() */

88 89 90 91
/*
 * Default xilinxfb configuration
 */
static struct xilinxfb_platform_data xilinx_fb_default_pdata = {
92 93 94
	.xres = 640,
	.yres = 480,
	.xvirt = 1024,
G
Grant Likely 已提交
95
	.yvirt = 480,
96 97
};

98 99 100
/*
 * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures
 */
101
static struct fb_fix_screeninfo xilinx_fb_fix = {
102 103 104 105 106 107
	.id =		"Xilinx",
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_TRUECOLOR,
	.accel =	FB_ACCEL_NONE
};

108
static struct fb_var_screeninfo xilinx_fb_var = {
109 110 111 112 113 114 115 116 117 118
	.bits_per_pixel =	BITS_PER_PIXEL,

	.red =		{ RED_SHIFT, 8, 0 },
	.green =	{ GREEN_SHIFT, 8, 0 },
	.blue =		{ BLUE_SHIFT, 8, 0 },
	.transp =	{ 0, 0, 0 },

	.activate =	FB_ACTIVATE_NOW
};

119 120 121

#define PLB_ACCESS_FLAG	0x1		/* 1 = PLB, 0 = DCR */

122 123 124 125
struct xilinxfb_drvdata {

	struct fb_info	info;		/* FB driver info record */

126 127 128 129
	phys_addr_t	regs_phys;	/* phys. address of the control
						registers */
	void __iomem	*regs;		/* virt. address of the control
						registers */
130
#ifdef CONFIG_PPC_DCR
131 132
	dcr_host_t      dcr_host;
	unsigned int    dcr_len;
133
#endif
G
Grant Likely 已提交
134
	void		*fb_virt;	/* virt. address of the frame buffer */
135
	dma_addr_t	fb_phys;	/* phys. address of the frame buffer */
136
	int		fb_alloced;	/* Flag, was the fb memory alloced? */
137

138 139
	u8 		flags;		/* features of the driver */

140 141 142 143 144 145 146 147 148 149
	u32		reg_ctrl_default;

	u32		pseudo_palette[PALETTE_ENTRIES_NO];
					/* Fake palette of 16 colors */
};

#define to_xilinxfb_drvdata(_info) \
	container_of(_info, struct xilinxfb_drvdata, info)

/*
150 151 152
 * The XPS TFT Controller can be accessed through PLB or DCR interface.
 * To perform the read/write on the registers we need to check on
 * which bus its connected and call the appropriate write API.
153
 */
154 155 156 157 158
static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset,
				u32 val)
{
	if (drvdata->flags & PLB_ACCESS_FLAG)
		out_be32(drvdata->regs + (offset << 2), val);
159
#ifdef CONFIG_PPC_DCR
160 161
	else
		dcr_write(drvdata->dcr_host, offset, val);
162
#endif
163
}
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

static int
xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
	unsigned transp, struct fb_info *fbi)
{
	u32 *palette = fbi->pseudo_palette;

	if (regno >= PALETTE_ENTRIES_NO)
		return -EINVAL;

	if (fbi->var.grayscale) {
		/* Convert color to grayscale.
		 * grayscale = 0.30*R + 0.59*G + 0.11*B */
		red = green = blue =
			(red * 77 + green * 151 + blue * 28 + 127) >> 8;
	}

	/* fbi->fix.visual is always FB_VISUAL_TRUECOLOR */

	/* We only handle 8 bits of each color. */
	red >>= 8;
	green >>= 8;
	blue >>= 8;
	palette[regno] = (red << RED_SHIFT) | (green << GREEN_SHIFT) |
			 (blue << BLUE_SHIFT);

	return 0;
}

static int
xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
{
	struct xilinxfb_drvdata *drvdata = to_xilinxfb_drvdata(fbi);

	switch (blank_mode) {
	case FB_BLANK_UNBLANK:
		/* turn on panel */
		xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
		break;

	case FB_BLANK_NORMAL:
	case FB_BLANK_VSYNC_SUSPEND:
	case FB_BLANK_HSYNC_SUSPEND:
	case FB_BLANK_POWERDOWN:
		/* turn off panel */
		xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
	default:
		break;

	}
	return 0; /* success */
}

static struct fb_ops xilinxfb_ops =
{
	.owner			= THIS_MODULE,
	.fb_setcolreg		= xilinx_fb_setcolreg,
	.fb_blank		= xilinx_fb_blank,
	.fb_fillrect		= cfb_fillrect,
	.fb_copyarea		= cfb_copyarea,
	.fb_imageblit		= cfb_imageblit,
};

227 228 229
/* ---------------------------------------------------------------------
 * Bus independent setup/teardown
 */
230

231 232 233
static int xilinxfb_assign(struct device *dev,
			   struct xilinxfb_drvdata *drvdata,
			   unsigned long physaddr,
234
			   struct xilinxfb_platform_data *pdata)
235
{
236
	int rc;
237
	int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL;
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	if (drvdata->flags & PLB_ACCESS_FLAG) {
		/*
		 * Map the control registers in if the controller
		 * is on direct PLB interface.
		 */
		if (!request_mem_region(physaddr, 8, DRIVER_NAME)) {
			dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
				physaddr);
			rc = -ENODEV;
			goto err_region;
		}

		drvdata->regs_phys = physaddr;
		drvdata->regs = ioremap(physaddr, 8);
		if (!drvdata->regs) {
			dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
				physaddr);
			rc = -ENODEV;
			goto err_map;
		}
259 260 261
	}

	/* Allocate the framebuffer memory */
262 263 264 265 266 267 268 269 270
	if (pdata->fb_phys) {
		drvdata->fb_phys = pdata->fb_phys;
		drvdata->fb_virt = ioremap(pdata->fb_phys, fbsize);
	} else {
		drvdata->fb_alloced = 1;
		drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(fbsize),
					&drvdata->fb_phys, GFP_KERNEL);
	}

271
	if (!drvdata->fb_virt) {
272
		dev_err(dev, "Could not allocate frame buffer memory\n");
273
		rc = -ENOMEM;
274 275 276 277
		if (drvdata->flags & PLB_ACCESS_FLAG)
			goto err_fbmem;
		else
			goto err_region;
278 279 280
	}

	/* Clear (turn to black) the framebuffer */
281
	memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize);
282 283 284 285 286

	/* Tell the hardware where the frame buffer is */
	xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys);

	/* Turn on the display */
287
	drvdata->reg_ctrl_default = REG_CTRL_ENABLE;
288
	if (pdata->rotate_screen)
289
		drvdata->reg_ctrl_default |= REG_CTRL_ROTATE;
290 291
	xilinx_fb_out_be32(drvdata, REG_CTRL,
					drvdata->reg_ctrl_default);
292 293 294

	/* Fill struct fb_info */
	drvdata->info.device = dev;
G
Grant Likely 已提交
295
	drvdata->info.screen_base = (void __iomem *)drvdata->fb_virt;
296 297 298
	drvdata->info.fbops = &xilinxfb_ops;
	drvdata->info.fix = xilinx_fb_fix;
	drvdata->info.fix.smem_start = drvdata->fb_phys;
299 300 301
	drvdata->info.fix.smem_len = fbsize;
	drvdata->info.fix.line_length = pdata->xvirt * BYTES_PER_PIXEL;

302
	drvdata->info.pseudo_palette = drvdata->pseudo_palette;
303 304
	drvdata->info.flags = FBINFO_DEFAULT;
	drvdata->info.var = xilinx_fb_var;
305 306 307 308 309 310
	drvdata->info.var.height = pdata->screen_height_mm;
	drvdata->info.var.width = pdata->screen_width_mm;
	drvdata->info.var.xres = pdata->xres;
	drvdata->info.var.yres = pdata->yres;
	drvdata->info.var.xres_virtual = pdata->xvirt;
	drvdata->info.var.yres_virtual = pdata->yvirt;
311

312 313 314
	/* Allocate a colour map */
	rc = fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0);
	if (rc) {
315
		dev_err(dev, "Fail to allocate colormap (%d entries)\n",
316
			PALETTE_ENTRIES_NO);
317
		goto err_cmap;
318 319 320
	}

	/* Register new frame buffer */
321 322
	rc = register_framebuffer(&drvdata->info);
	if (rc) {
323
		dev_err(dev, "Could not register frame buffer\n");
324
		goto err_regfb;
325 326
	}

327 328 329 330 331
	if (drvdata->flags & PLB_ACCESS_FLAG) {
		/* Put a banner in the log (for DEBUG) */
		dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr,
					drvdata->regs);
	}
332
	/* Put a banner in the log (for DEBUG) */
333 334
	dev_dbg(dev, "fb: phys=%llx, virt=%p, size=%x\n",
		(unsigned long long)drvdata->fb_phys, drvdata->fb_virt, fbsize);
335

336 337
	return 0;	/* success */

338
err_regfb:
339 340
	fb_dealloc_cmap(&drvdata->info.cmap);

341
err_cmap:
342 343 344
	if (drvdata->fb_alloced)
		dma_free_coherent(dev, PAGE_ALIGN(fbsize), drvdata->fb_virt,
			drvdata->fb_phys);
345 346 347
	else
		iounmap(drvdata->fb_virt);

348 349 350
	/* Turn off the display */
	xilinx_fb_out_be32(drvdata, REG_CTRL, 0);

351
err_fbmem:
352 353
	if (drvdata->flags & PLB_ACCESS_FLAG)
		iounmap(drvdata->regs);
354 355

err_map:
356 357
	if (drvdata->flags & PLB_ACCESS_FLAG)
		release_mem_region(physaddr, 8);
358

359
err_region:
360 361 362
	kfree(drvdata);
	dev_set_drvdata(dev, NULL);

363
	return rc;
364 365
}

366
static int xilinxfb_release(struct device *dev)
367
{
368
	struct xilinxfb_drvdata *drvdata = dev_get_drvdata(dev);
369 370 371 372 373 374 375 376 377

#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
	xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info);
#endif

	unregister_framebuffer(&drvdata->info);

	fb_dealloc_cmap(&drvdata->info.cmap);

378 379 380
	if (drvdata->fb_alloced)
		dma_free_coherent(dev, PAGE_ALIGN(drvdata->info.fix.smem_len),
				  drvdata->fb_virt, drvdata->fb_phys);
381 382
	else
		iounmap(drvdata->fb_virt);
383 384 385 386

	/* Turn off the display */
	xilinx_fb_out_be32(drvdata, REG_CTRL, 0);

387 388 389 390
	/* Release the resources, as allocated based on interface */
	if (drvdata->flags & PLB_ACCESS_FLAG) {
		iounmap(drvdata->regs);
		release_mem_region(drvdata->regs_phys, 8);
391 392 393
	}
#ifdef CONFIG_PPC_DCR
	else
394
		dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
395
#endif
396 397 398 399 400 401 402

	kfree(drvdata);
	dev_set_drvdata(dev, NULL);

	return 0;
}

403 404 405 406 407
/* ---------------------------------------------------------------------
 * OF bus binding
 */

static int __devinit
408
xilinxfb_of_probe(struct platform_device *op, const struct of_device_id *match)
409 410
{
	const u32 *prop;
411 412
	u32 *p;
	u32 tft_access;
413
	struct xilinxfb_platform_data pdata;
414
	struct resource res;
415
	int size, rc;
416
	struct xilinxfb_drvdata *drvdata;
417

418 419 420
	/* Copy with the default pdata (not a ptr reference!) */
	pdata = xilinx_fb_default_pdata;

421 422
	dev_dbg(&op->dev, "xilinxfb_of_probe(%p, %p)\n", op, match);

423 424 425 426 427 428 429
	/* Allocate the driver data region */
	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata) {
		dev_err(&op->dev, "Couldn't allocate device private record\n");
		return -ENOMEM;
	}

430 431 432 433
	/*
	 * To check whether the core is connected directly to DCR or PLB
	 * interface and initialize the tft_access accordingly.
	 */
434
	p = (u32 *)of_get_property(op->dev.of_node, "xlnx,dcr-splb-slave-if", NULL);
435
	tft_access = p ? *p : 0;
436 437 438 439 440 441

	/*
	 * Fill the resource structure if its direct PLB interface
	 * otherwise fill the dcr_host structure.
	 */
	if (tft_access) {
442
		drvdata->flags |= PLB_ACCESS_FLAG;
443
		rc = of_address_to_resource(op->dev.of_node, 0, &res);
444 445
		if (rc) {
			dev_err(&op->dev, "invalid address\n");
446
			goto err;
447
		}
448 449 450 451
	}
#ifdef CONFIG_PPC_DCR
	else {
		int start;
452
		res.start = 0;
453 454 455
		start = dcr_resource_start(op->dev.of_node, 0);
		drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0);
		drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len);
456 457 458
		if (!DCR_MAP_OK(drvdata->dcr_host)) {
			dev_err(&op->dev, "invalid DCR address\n");
			goto err;
459
		}
460
	}
461
#endif
462

463
	prop = of_get_property(op->dev.of_node, "phys-size", &size);
464
	if ((prop) && (size >= sizeof(u32)*2)) {
465 466
		pdata.screen_width_mm = prop[0];
		pdata.screen_height_mm = prop[1];
467 468
	}

469
	prop = of_get_property(op->dev.of_node, "resolution", &size);
470 471 472 473 474
	if ((prop) && (size >= sizeof(u32)*2)) {
		pdata.xres = prop[0];
		pdata.yres = prop[1];
	}

475
	prop = of_get_property(op->dev.of_node, "virtual-resolution", &size);
476 477 478 479 480
	if ((prop) && (size >= sizeof(u32)*2)) {
		pdata.xvirt = prop[0];
		pdata.yvirt = prop[1];
	}

481
	if (of_find_property(op->dev.of_node, "rotate-display", NULL))
482
		pdata.rotate_screen = 1;
483

484
	dev_set_drvdata(&op->dev, drvdata);
485
	return xilinxfb_assign(&op->dev, drvdata, res.start, &pdata);
486

487 488 489
 err:
	kfree(drvdata);
	return -ENODEV;
490 491
}

492
static int __devexit xilinxfb_of_remove(struct platform_device *op)
493 494 495 496 497
{
	return xilinxfb_release(&op->dev);
}

/* Match table for of_platform binding */
498
static struct of_device_id xilinxfb_of_match[] __devinitdata = {
499
	{ .compatible = "xlnx,xps-tft-1.00.a", },
500 501
	{ .compatible = "xlnx,xps-tft-2.00.a", },
	{ .compatible = "xlnx,xps-tft-2.01.a", },
502
	{ .compatible = "xlnx,plb-tft-cntlr-ref-1.00.a", },
503
	{ .compatible = "xlnx,plb-dvi-cntlr-ref-1.00.c", },
504 505 506 507 508 509 510 511 512
	{},
};
MODULE_DEVICE_TABLE(of, xilinxfb_of_match);

static struct of_platform_driver xilinxfb_of_driver = {
	.probe = xilinxfb_of_probe,
	.remove = __devexit_p(xilinxfb_of_remove),
	.driver = {
		.name = DRIVER_NAME,
513 514
		.owner = THIS_MODULE,
		.of_match_table = xilinxfb_of_match,
515 516 517 518 519 520 521 522
	},
};


/* ---------------------------------------------------------------------
 * Module setup and teardown
 */

523 524 525
static int __init
xilinxfb_init(void)
{
526
	return of_register_platform_driver(&xilinxfb_of_driver);
527 528 529 530 531
}

static void __exit
xilinxfb_cleanup(void)
{
532
	of_unregister_platform_driver(&xilinxfb_of_driver);
533 534 535 536 537 538
}

module_init(xilinxfb_init);
module_exit(xilinxfb_cleanup);

MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
539
MODULE_DESCRIPTION("Xilinx TFT frame buffer driver");
540
MODULE_LICENSE("GPL");