cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ep93xx-fb.c (16775B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * linux/drivers/video/ep93xx-fb.c
      4 *
      5 * Framebuffer support for the EP93xx series.
      6 *
      7 * Copyright (C) 2007 Bluewater Systems Ltd
      8 * Author: Ryan Mallon
      9 *
     10 * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com>
     11 *
     12 * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb
     13 * drivers.
     14 */
     15
     16#include <linux/platform_device.h>
     17#include <linux/module.h>
     18#include <linux/dma-mapping.h>
     19#include <linux/slab.h>
     20#include <linux/clk.h>
     21#include <linux/fb.h>
     22#include <linux/io.h>
     23
     24#include <linux/platform_data/video-ep93xx.h>
     25
     26/* Vertical Frame Timing Registers */
     27#define EP93XXFB_VLINES_TOTAL			0x0000	/* SW locked */
     28#define EP93XXFB_VSYNC				0x0004	/* SW locked */
     29#define EP93XXFB_VACTIVE			0x0008	/* SW locked */
     30#define EP93XXFB_VBLANK				0x0228	/* SW locked */
     31#define EP93XXFB_VCLK				0x000c	/* SW locked */
     32
     33/* Horizontal Frame Timing Registers */
     34#define EP93XXFB_HCLKS_TOTAL			0x0010	/* SW locked */
     35#define EP93XXFB_HSYNC				0x0014	/* SW locked */
     36#define EP93XXFB_HACTIVE			0x0018	/* SW locked */
     37#define EP93XXFB_HBLANK				0x022c	/* SW locked */
     38#define EP93XXFB_HCLK				0x001c	/* SW locked */
     39
     40/* Frame Buffer Memory Configuration Registers */
     41#define EP93XXFB_SCREEN_PAGE			0x0028
     42#define EP93XXFB_SCREEN_HPAGE			0x002c
     43#define EP93XXFB_SCREEN_LINES			0x0030
     44#define EP93XXFB_LINE_LENGTH			0x0034
     45#define EP93XXFB_VLINE_STEP			0x0038
     46#define EP93XXFB_LINE_CARRY			0x003c	/* SW locked */
     47#define EP93XXFB_EOL_OFFSET			0x0230
     48
     49/* Other Video Registers */
     50#define EP93XXFB_BRIGHTNESS			0x0020
     51#define EP93XXFB_ATTRIBS			0x0024	/* SW locked */
     52#define EP93XXFB_SWLOCK				0x007c	/* SW locked */
     53#define EP93XXFB_AC_RATE			0x0214
     54#define EP93XXFB_FIFO_LEVEL			0x0234
     55#define EP93XXFB_PIXELMODE			0x0054
     56#define EP93XXFB_PIXELMODE_32BPP		(0x7 << 0)
     57#define EP93XXFB_PIXELMODE_24BPP		(0x6 << 0)
     58#define EP93XXFB_PIXELMODE_16BPP		(0x4 << 0)
     59#define EP93XXFB_PIXELMODE_8BPP			(0x2 << 0)
     60#define EP93XXFB_PIXELMODE_SHIFT_1P_24B		(0x0 << 3)
     61#define EP93XXFB_PIXELMODE_SHIFT_1P_18B		(0x1 << 3)
     62#define EP93XXFB_PIXELMODE_COLOR_LUT		(0x0 << 10)
     63#define EP93XXFB_PIXELMODE_COLOR_888		(0x4 << 10)
     64#define EP93XXFB_PIXELMODE_COLOR_555		(0x5 << 10)
     65#define EP93XXFB_PARL_IF_OUT			0x0058
     66#define EP93XXFB_PARL_IF_IN			0x005c
     67
     68/* Blink Control Registers */
     69#define EP93XXFB_BLINK_RATE			0x0040
     70#define EP93XXFB_BLINK_MASK			0x0044
     71#define EP93XXFB_BLINK_PATTRN			0x0048
     72#define EP93XXFB_PATTRN_MASK			0x004c
     73#define EP93XXFB_BKGRND_OFFSET			0x0050
     74
     75/* Hardware Cursor Registers */
     76#define EP93XXFB_CURSOR_ADR_START		0x0060
     77#define EP93XXFB_CURSOR_ADR_RESET		0x0064
     78#define EP93XXFB_CURSOR_SIZE			0x0068
     79#define EP93XXFB_CURSOR_COLOR1			0x006c
     80#define EP93XXFB_CURSOR_COLOR2			0x0070
     81#define EP93XXFB_CURSOR_BLINK_COLOR1		0x021c
     82#define EP93XXFB_CURSOR_BLINK_COLOR2		0x0220
     83#define EP93XXFB_CURSOR_XY_LOC			0x0074
     84#define EP93XXFB_CURSOR_DSCAN_HY_LOC		0x0078
     85#define EP93XXFB_CURSOR_BLINK_RATE_CTRL		0x0224
     86
     87/* LUT Registers */
     88#define EP93XXFB_GRY_SCL_LUTR			0x0080
     89#define EP93XXFB_GRY_SCL_LUTG			0x0280
     90#define EP93XXFB_GRY_SCL_LUTB			0x0300
     91#define EP93XXFB_LUT_SW_CONTROL			0x0218
     92#define EP93XXFB_LUT_SW_CONTROL_SWTCH		(1 << 0)
     93#define EP93XXFB_LUT_SW_CONTROL_SSTAT		(1 << 1)
     94#define EP93XXFB_COLOR_LUT			0x0400
     95
     96/* Video Signature Registers */
     97#define EP93XXFB_VID_SIG_RSLT_VAL		0x0200
     98#define EP93XXFB_VID_SIG_CTRL			0x0204
     99#define EP93XXFB_VSIG				0x0208
    100#define EP93XXFB_HSIG				0x020c
    101#define EP93XXFB_SIG_CLR_STR			0x0210
    102
    103/* Minimum / Maximum resolutions supported */
    104#define EP93XXFB_MIN_XRES			64
    105#define EP93XXFB_MIN_YRES			64
    106#define EP93XXFB_MAX_XRES			1024
    107#define EP93XXFB_MAX_YRES			768
    108
    109struct ep93xx_fbi {
    110	struct ep93xxfb_mach_info	*mach_info;
    111	struct clk			*clk;
    112	struct resource			*res;
    113	void __iomem			*mmio_base;
    114	unsigned int			pseudo_palette[256];
    115};
    116
    117static int check_screenpage_bug = 1;
    118module_param(check_screenpage_bug, int, 0644);
    119MODULE_PARM_DESC(check_screenpage_bug,
    120		 "Check for bit 27 screen page bug. Default = 1");
    121
    122static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi,
    123					  unsigned int off)
    124{
    125	return __raw_readl(fbi->mmio_base + off);
    126}
    127
    128static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi,
    129				   unsigned int val, unsigned int off)
    130{
    131	__raw_writel(val, fbi->mmio_base + off);
    132}
    133
    134/*
    135 * Write to one of the locked raster registers.
    136 */
    137static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi,
    138				       unsigned int val, unsigned int reg)
    139{
    140	/*
    141	 * We don't need a lock or delay here since the raster register
    142	 * block will remain unlocked until the next access.
    143	 */
    144	ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK);
    145	ep93xxfb_writel(fbi, val, reg);
    146}
    147
    148static void ep93xxfb_set_video_attribs(struct fb_info *info)
    149{
    150	struct ep93xx_fbi *fbi = info->par;
    151	unsigned int attribs;
    152
    153	attribs = EP93XXFB_ENABLE;
    154	attribs |= fbi->mach_info->flags;
    155	ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS);
    156}
    157
    158static int ep93xxfb_set_pixelmode(struct fb_info *info)
    159{
    160	struct ep93xx_fbi *fbi = info->par;
    161	unsigned int val;
    162
    163	info->var.transp.offset = 0;
    164	info->var.transp.length = 0;
    165
    166	switch (info->var.bits_per_pixel) {
    167	case 8:
    168		val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT |
    169			EP93XXFB_PIXELMODE_SHIFT_1P_18B;
    170
    171		info->var.red.offset	= 0;
    172		info->var.red.length	= 8;
    173		info->var.green.offset	= 0;
    174		info->var.green.length	= 8;
    175		info->var.blue.offset	= 0;
    176		info->var.blue.length	= 8;
    177		info->fix.visual 	= FB_VISUAL_PSEUDOCOLOR;
    178		break;
    179
    180	case 16:
    181		val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 |
    182			EP93XXFB_PIXELMODE_SHIFT_1P_18B;
    183
    184		info->var.red.offset	= 11;
    185		info->var.red.length	= 5;
    186		info->var.green.offset	= 5;
    187		info->var.green.length	= 6;
    188		info->var.blue.offset	= 0;
    189		info->var.blue.length	= 5;
    190		info->fix.visual 	= FB_VISUAL_TRUECOLOR;
    191		break;
    192
    193	case 24:
    194		val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 |
    195			EP93XXFB_PIXELMODE_SHIFT_1P_24B;
    196
    197		info->var.red.offset	= 16;
    198		info->var.red.length	= 8;
    199		info->var.green.offset	= 8;
    200		info->var.green.length	= 8;
    201		info->var.blue.offset	= 0;
    202		info->var.blue.length	= 8;
    203		info->fix.visual 	= FB_VISUAL_TRUECOLOR;
    204		break;
    205
    206	case 32:
    207		val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 |
    208			EP93XXFB_PIXELMODE_SHIFT_1P_24B;
    209
    210		info->var.red.offset	= 16;
    211		info->var.red.length	= 8;
    212		info->var.green.offset	= 8;
    213		info->var.green.length	= 8;
    214		info->var.blue.offset	= 0;
    215		info->var.blue.length	= 8;
    216		info->fix.visual 	= FB_VISUAL_TRUECOLOR;
    217		break;
    218
    219	default:
    220		return -EINVAL;
    221	}
    222
    223	ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE);
    224	return 0;
    225}
    226
    227static void ep93xxfb_set_timing(struct fb_info *info)
    228{
    229	struct ep93xx_fbi *fbi = info->par;
    230	unsigned int vlines_total, hclks_total, start, stop;
    231
    232	vlines_total = info->var.yres + info->var.upper_margin +
    233		info->var.lower_margin + info->var.vsync_len - 1;
    234
    235	hclks_total = info->var.xres + info->var.left_margin +
    236		info->var.right_margin + info->var.hsync_len - 1;
    237
    238	ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL);
    239	ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL);
    240
    241	start = vlines_total;
    242	stop = vlines_total - info->var.vsync_len;
    243	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC);
    244
    245	start = vlines_total - info->var.vsync_len - info->var.upper_margin;
    246	stop = info->var.lower_margin - 1;
    247	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK);
    248	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE);
    249
    250	start = vlines_total;
    251	stop = vlines_total + 1;
    252	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK);
    253
    254	start = hclks_total;
    255	stop = hclks_total - info->var.hsync_len;
    256	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC);
    257
    258	start = hclks_total - info->var.hsync_len - info->var.left_margin;
    259	stop = info->var.right_margin - 1;
    260	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK);
    261	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE);
    262
    263	start = hclks_total;
    264	stop = hclks_total;
    265	ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK);
    266
    267	ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY);
    268}
    269
    270static int ep93xxfb_set_par(struct fb_info *info)
    271{
    272	struct ep93xx_fbi *fbi = info->par;
    273
    274	clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock));
    275
    276	ep93xxfb_set_timing(info);
    277
    278	info->fix.line_length = info->var.xres_virtual *
    279		info->var.bits_per_pixel / 8;
    280
    281	ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE);
    282	ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES);
    283	ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel)
    284			      / 32) - 1, EP93XXFB_LINE_LENGTH);
    285	ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP);
    286	ep93xxfb_set_video_attribs(info);
    287	return 0;
    288}
    289
    290static int ep93xxfb_check_var(struct fb_var_screeninfo *var,
    291			      struct fb_info *info)
    292{
    293	int err;
    294
    295	err = ep93xxfb_set_pixelmode(info);
    296	if (err)
    297		return err;
    298
    299	var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES);
    300	var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES);
    301	var->xres_virtual = max(var->xres_virtual, var->xres);
    302
    303	var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES);
    304	var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES);
    305	var->yres_virtual = max(var->yres_virtual, var->yres);
    306
    307	return 0;
    308}
    309
    310static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
    311{
    312	unsigned int offset = vma->vm_pgoff << PAGE_SHIFT;
    313
    314	if (offset < info->fix.smem_len) {
    315		return dma_mmap_wc(info->dev, vma, info->screen_base,
    316				   info->fix.smem_start, info->fix.smem_len);
    317	}
    318
    319	return -EINVAL;
    320}
    321
    322static int ep93xxfb_blank(int blank_mode, struct fb_info *info)
    323{
    324	struct ep93xx_fbi *fbi = info->par;
    325	unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS);
    326
    327	if (blank_mode) {
    328		if (fbi->mach_info->blank)
    329			fbi->mach_info->blank(blank_mode, info);
    330		ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE,
    331				    EP93XXFB_ATTRIBS);
    332		clk_disable(fbi->clk);
    333	} else {
    334		clk_enable(fbi->clk);
    335		ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE,
    336				    EP93XXFB_ATTRIBS);
    337		if (fbi->mach_info->blank)
    338			fbi->mach_info->blank(blank_mode, info);
    339	}
    340
    341	return 0;
    342}
    343
    344static inline int ep93xxfb_convert_color(int val, int width)
    345{
    346	return ((val << width) + 0x7fff - val) >> 16;
    347}
    348
    349static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red,
    350			      unsigned int green, unsigned int blue,
    351			      unsigned int transp, struct fb_info *info)
    352{
    353	struct ep93xx_fbi *fbi = info->par;
    354	unsigned int *pal = info->pseudo_palette;
    355	unsigned int ctrl, i, rgb, lut_current, lut_stat;
    356
    357	switch (info->fix.visual) {
    358	case FB_VISUAL_PSEUDOCOLOR:
    359		if (regno > 255)
    360			return 1;
    361		rgb = ((red & 0xff00) << 8) | (green & 0xff00) |
    362			((blue & 0xff00) >> 8);
    363
    364		pal[regno] = rgb;
    365		ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2)));
    366		ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL);
    367		lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT);
    368		lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH);
    369
    370		if (lut_stat == lut_current) {
    371			for (i = 0; i < 256; i++) {
    372				ep93xxfb_writel(fbi, pal[i],
    373					EP93XXFB_COLOR_LUT + (i << 2));
    374			}
    375
    376			ep93xxfb_writel(fbi,
    377					ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH,
    378					EP93XXFB_LUT_SW_CONTROL);
    379		}
    380		break;
    381
    382	case FB_VISUAL_TRUECOLOR:
    383		if (regno > 16)
    384			return 1;
    385
    386		red = ep93xxfb_convert_color(red, info->var.red.length);
    387		green = ep93xxfb_convert_color(green, info->var.green.length);
    388		blue = ep93xxfb_convert_color(blue, info->var.blue.length);
    389		transp = ep93xxfb_convert_color(transp,
    390						info->var.transp.length);
    391
    392		pal[regno] = (red << info->var.red.offset) |
    393			(green << info->var.green.offset) |
    394			(blue << info->var.blue.offset) |
    395			(transp << info->var.transp.offset);
    396		break;
    397
    398	default:
    399		return 1;
    400	}
    401
    402	return 0;
    403}
    404
    405static const struct fb_ops ep93xxfb_ops = {
    406	.owner		= THIS_MODULE,
    407	.fb_check_var	= ep93xxfb_check_var,
    408	.fb_set_par	= ep93xxfb_set_par,
    409	.fb_blank	= ep93xxfb_blank,
    410	.fb_fillrect	= cfb_fillrect,
    411	.fb_copyarea	= cfb_copyarea,
    412	.fb_imageblit	= cfb_imageblit,
    413	.fb_setcolreg	= ep93xxfb_setcolreg,
    414	.fb_mmap	= ep93xxfb_mmap,
    415};
    416
    417static int ep93xxfb_alloc_videomem(struct fb_info *info)
    418{
    419	char __iomem *virt_addr;
    420	dma_addr_t phys_addr;
    421	unsigned int fb_size;
    422
    423	/* Maximum 16bpp -> used memory is maximum x*y*2 bytes */
    424	fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2;
    425
    426	virt_addr = dma_alloc_wc(info->dev, fb_size, &phys_addr, GFP_KERNEL);
    427	if (!virt_addr)
    428		return -ENOMEM;
    429
    430	/*
    431	 * There is a bug in the ep93xx framebuffer which causes problems
    432	 * if bit 27 of the physical address is set.
    433	 * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2
    434	 * There does not seem to be any official errata for this, but I
    435	 * have confirmed the problem exists on my hardware (ep9315) at
    436	 * least.
    437	 */
    438	if (check_screenpage_bug && phys_addr & (1 << 27)) {
    439		dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) "
    440			"has bit 27 set: cannot init framebuffer\n",
    441			phys_addr);
    442
    443		dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr);
    444		return -ENOMEM;
    445	}
    446
    447	info->fix.smem_start = phys_addr;
    448	info->fix.smem_len = fb_size;
    449	info->screen_base = virt_addr;
    450
    451	return 0;
    452}
    453
    454static void ep93xxfb_dealloc_videomem(struct fb_info *info)
    455{
    456	if (info->screen_base)
    457		dma_free_coherent(info->dev, info->fix.smem_len,
    458				  info->screen_base, info->fix.smem_start);
    459}
    460
    461static int ep93xxfb_probe(struct platform_device *pdev)
    462{
    463	struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev);
    464	struct fb_info *info;
    465	struct ep93xx_fbi *fbi;
    466	struct resource *res;
    467	char *video_mode;
    468	int err;
    469
    470	if (!mach_info)
    471		return -EINVAL;
    472
    473	info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev);
    474	if (!info)
    475		return -ENOMEM;
    476
    477	info->dev = &pdev->dev;
    478	platform_set_drvdata(pdev, info);
    479	fbi = info->par;
    480	fbi->mach_info = mach_info;
    481
    482	err = fb_alloc_cmap(&info->cmap, 256, 0);
    483	if (err)
    484		goto failed_cmap;
    485
    486	err = ep93xxfb_alloc_videomem(info);
    487	if (err)
    488		goto failed_videomem;
    489
    490	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    491	if (!res) {
    492		err = -ENXIO;
    493		goto failed_resource;
    494	}
    495
    496	/*
    497	 * FIXME - We don't do a request_mem_region here because we are
    498	 * sharing the register space with the backlight driver (see
    499	 * drivers/video/backlight/ep93xx_bl.c) and doing so will cause
    500	 * the second loaded driver to return -EBUSY.
    501	 *
    502	 * NOTE: No locking is required; the backlight does not touch
    503	 * any of the framebuffer registers.
    504	 */
    505	fbi->res = res;
    506	fbi->mmio_base = devm_ioremap(&pdev->dev, res->start,
    507				      resource_size(res));
    508	if (!fbi->mmio_base) {
    509		err = -ENXIO;
    510		goto failed_resource;
    511	}
    512
    513	strcpy(info->fix.id, pdev->name);
    514	info->fbops		= &ep93xxfb_ops;
    515	info->fix.type		= FB_TYPE_PACKED_PIXELS;
    516	info->fix.accel		= FB_ACCEL_NONE;
    517	info->var.activate	= FB_ACTIVATE_NOW;
    518	info->var.vmode		= FB_VMODE_NONINTERLACED;
    519	info->flags		= FBINFO_DEFAULT;
    520	info->node		= -1;
    521	info->state		= FBINFO_STATE_RUNNING;
    522	info->pseudo_palette	= &fbi->pseudo_palette;
    523
    524	fb_get_options("ep93xx-fb", &video_mode);
    525	err = fb_find_mode(&info->var, info, video_mode,
    526			   NULL, 0, NULL, 16);
    527	if (err == 0) {
    528		dev_err(info->dev, "No suitable video mode found\n");
    529		err = -EINVAL;
    530		goto failed_resource;
    531	}
    532
    533	if (mach_info->setup) {
    534		err = mach_info->setup(pdev);
    535		if (err)
    536			goto failed_resource;
    537	}
    538
    539	err = ep93xxfb_check_var(&info->var, info);
    540	if (err)
    541		goto failed_check;
    542
    543	fbi->clk = devm_clk_get(&pdev->dev, NULL);
    544	if (IS_ERR(fbi->clk)) {
    545		err = PTR_ERR(fbi->clk);
    546		fbi->clk = NULL;
    547		goto failed_check;
    548	}
    549
    550	ep93xxfb_set_par(info);
    551	clk_prepare_enable(fbi->clk);
    552
    553	err = register_framebuffer(info);
    554	if (err)
    555		goto failed_check;
    556
    557	dev_info(info->dev, "registered. Mode = %dx%d-%d\n",
    558		 info->var.xres, info->var.yres, info->var.bits_per_pixel);
    559	return 0;
    560
    561failed_check:
    562	if (fbi->mach_info->teardown)
    563		fbi->mach_info->teardown(pdev);
    564failed_resource:
    565	ep93xxfb_dealloc_videomem(info);
    566failed_videomem:
    567	fb_dealloc_cmap(&info->cmap);
    568failed_cmap:
    569	kfree(info);
    570
    571	return err;
    572}
    573
    574static int ep93xxfb_remove(struct platform_device *pdev)
    575{
    576	struct fb_info *info = platform_get_drvdata(pdev);
    577	struct ep93xx_fbi *fbi = info->par;
    578
    579	unregister_framebuffer(info);
    580	clk_disable_unprepare(fbi->clk);
    581	ep93xxfb_dealloc_videomem(info);
    582	fb_dealloc_cmap(&info->cmap);
    583
    584	if (fbi->mach_info->teardown)
    585		fbi->mach_info->teardown(pdev);
    586
    587	kfree(info);
    588
    589	return 0;
    590}
    591
    592static struct platform_driver ep93xxfb_driver = {
    593	.probe		= ep93xxfb_probe,
    594	.remove		= ep93xxfb_remove,
    595	.driver = {
    596		.name	= "ep93xx-fb",
    597	},
    598};
    599module_platform_driver(ep93xxfb_driver);
    600
    601MODULE_DESCRIPTION("EP93XX Framebuffer Driver");
    602MODULE_ALIAS("platform:ep93xx-fb");
    603MODULE_AUTHOR("Ryan Mallon, "
    604	      "H Hartley Sweeten <hsweeten@visionengravers.com");
    605MODULE_LICENSE("GPL");