File Index
Files and directories
Sign in
FILE

ep93xx-fb.c

File preview with syntax highlighting.

linux-6.19.14/drivers/video/fbdev/ep93xx-fb.c C
  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	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
315
316	if (offset < info->fix.smem_len) {
317		return dma_mmap_wc(info->device, vma, info->screen_base,
318				   info->fix.smem_start, info->fix.smem_len);
319	}
320
321	return -EINVAL;
322}
323
324static int ep93xxfb_blank(int blank_mode, struct fb_info *info)
325{
326	struct ep93xx_fbi *fbi = info->par;
327	unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS);
328
329	if (blank_mode) {
330		if (fbi->mach_info->blank)
331			fbi->mach_info->blank(blank_mode, info);
332		ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE,
333				    EP93XXFB_ATTRIBS);
334		clk_disable(fbi->clk);
335	} else {
336		clk_enable(fbi->clk);
337		ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE,
338				    EP93XXFB_ATTRIBS);
339		if (fbi->mach_info->blank)
340			fbi->mach_info->blank(blank_mode, info);
341	}
342
343	return 0;
344}
345
346static inline int ep93xxfb_convert_color(int val, int width)
347{
348	return ((val << width) + 0x7fff - val) >> 16;
349}
350
351static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red,
352			      unsigned int green, unsigned int blue,
353			      unsigned int transp, struct fb_info *info)
354{
355	struct ep93xx_fbi *fbi = info->par;
356	unsigned int *pal = info->pseudo_palette;
357	unsigned int ctrl, i, rgb, lut_current, lut_stat;
358
359	switch (info->fix.visual) {
360	case FB_VISUAL_PSEUDOCOLOR:
361		if (regno > 255)
362			return 1;
363		rgb = ((red & 0xff00) << 8) | (green & 0xff00) |
364			((blue & 0xff00) >> 8);
365
366		pal[regno] = rgb;
367		ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2)));
368		ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL);
369		lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT);
370		lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH);
371
372		if (lut_stat == lut_current) {
373			for (i = 0; i < 256; i++) {
374				ep93xxfb_writel(fbi, pal[i],
375					EP93XXFB_COLOR_LUT + (i << 2));
376			}
377
378			ep93xxfb_writel(fbi,
379					ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH,
380					EP93XXFB_LUT_SW_CONTROL);
381		}
382		break;
383
384	case FB_VISUAL_TRUECOLOR:
385		if (regno > 16)
386			return 1;
387
388		red = ep93xxfb_convert_color(red, info->var.red.length);
389		green = ep93xxfb_convert_color(green, info->var.green.length);
390		blue = ep93xxfb_convert_color(blue, info->var.blue.length);
391		transp = ep93xxfb_convert_color(transp,
392						info->var.transp.length);
393
394		pal[regno] = (red << info->var.red.offset) |
395			(green << info->var.green.offset) |
396			(blue << info->var.blue.offset) |
397			(transp << info->var.transp.offset);
398		break;
399
400	default:
401		return 1;
402	}
403
404	return 0;
405}
406
407static const struct fb_ops ep93xxfb_ops = {
408	.owner		= THIS_MODULE,
409	__FB_DEFAULT_IOMEM_OPS_RDWR,
410	.fb_check_var	= ep93xxfb_check_var,
411	.fb_set_par	= ep93xxfb_set_par,
412	.fb_blank	= ep93xxfb_blank,
413	__FB_DEFAULT_IOMEM_OPS_DRAW,
414	.fb_setcolreg	= ep93xxfb_setcolreg,
415	.fb_mmap	= ep93xxfb_mmap,
416};
417
418static int ep93xxfb_alloc_videomem(struct fb_info *info)
419{
420	char __iomem *virt_addr;
421	dma_addr_t phys_addr;
422	unsigned int fb_size;
423
424	/* Maximum 16bpp -> used memory is maximum x*y*2 bytes */
425	fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2;
426
427	virt_addr = dma_alloc_wc(info->device, fb_size, &phys_addr, GFP_KERNEL);
428	if (!virt_addr)
429		return -ENOMEM;
430
431	/*
432	 * There is a bug in the ep93xx framebuffer which causes problems
433	 * if bit 27 of the physical address is set.
434	 * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2
435	 * There does not seem to be any official errata for this, but I
436	 * have confirmed the problem exists on my hardware (ep9315) at
437	 * least.
438	 */
439	if (check_screenpage_bug && phys_addr & (1 << 27)) {
440		fb_err(info, "ep93xx framebuffer bug. phys addr (0x%x) "
441		       "has bit 27 set: cannot init framebuffer\n",
442		       phys_addr);
443
444		dma_free_coherent(info->device, fb_size, virt_addr, phys_addr);
445		return -ENOMEM;
446	}
447
448	info->fix.smem_start = phys_addr;
449	info->fix.smem_len = fb_size;
450	info->screen_base = virt_addr;
451
452	return 0;
453}
454
455static void ep93xxfb_dealloc_videomem(struct fb_info *info)
456{
457	if (info->screen_base)
458		dma_free_coherent(info->device, info->fix.smem_len,
459				  info->screen_base, info->fix.smem_start);
460}
461
462static int ep93xxfb_probe(struct platform_device *pdev)
463{
464	struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev);
465	struct fb_info *info;
466	struct ep93xx_fbi *fbi;
467	struct resource *res;
468	char *video_mode;
469	int err;
470
471	if (!mach_info)
472		return -EINVAL;
473
474	info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev);
475	if (!info)
476		return -ENOMEM;
477
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->node		= -1;
520	info->state		= FBINFO_STATE_RUNNING;
521	info->pseudo_palette	= &fbi->pseudo_palette;
522
523	fb_get_options("ep93xx-fb", &video_mode);
524	err = fb_find_mode(&info->var, info, video_mode,
525			   NULL, 0, NULL, 16);
526	if (err == 0) {
527		fb_err(info, "No suitable video mode found\n");
528		err = -EINVAL;
529		goto failed_resource;
530	}
531
532	if (mach_info->setup) {
533		err = mach_info->setup(pdev);
534		if (err)
535			goto failed_resource;
536	}
537
538	err = ep93xxfb_check_var(&info->var, info);
539	if (err)
540		goto failed_check;
541
542	fbi->clk = devm_clk_get(&pdev->dev, NULL);
543	if (IS_ERR(fbi->clk)) {
544		err = PTR_ERR(fbi->clk);
545		fbi->clk = NULL;
546		goto failed_check;
547	}
548
549	ep93xxfb_set_par(info);
550	err = clk_prepare_enable(fbi->clk);
551	if (err)
552		goto failed_check;
553
554	err = register_framebuffer(info);
555	if (err)
556		goto failed_framebuffer;
557
558	fb_info(info, "registered. Mode = %dx%d-%d\n",
559		info->var.xres, info->var.yres, info->var.bits_per_pixel);
560	return 0;
561
562failed_framebuffer:
563	clk_disable_unprepare(fbi->clk);
564failed_check:
565	if (fbi->mach_info->teardown)
566		fbi->mach_info->teardown(pdev);
567failed_resource:
568	ep93xxfb_dealloc_videomem(info);
569failed_videomem:
570	fb_dealloc_cmap(&info->cmap);
571failed_cmap:
572	kfree(info);
573
574	return err;
575}
576
577static void ep93xxfb_remove(struct platform_device *pdev)
578{
579	struct fb_info *info = platform_get_drvdata(pdev);
580	struct ep93xx_fbi *fbi = info->par;
581
582	unregister_framebuffer(info);
583	clk_disable_unprepare(fbi->clk);
584	ep93xxfb_dealloc_videomem(info);
585	fb_dealloc_cmap(&info->cmap);
586
587	if (fbi->mach_info->teardown)
588		fbi->mach_info->teardown(pdev);
589
590	kfree(info);
591}
592
593static struct platform_driver ep93xxfb_driver = {
594	.probe		= ep93xxfb_probe,
595	.remove		= ep93xxfb_remove,
596	.driver = {
597		.name	= "ep93xx-fb",
598	},
599};
600module_platform_driver(ep93xxfb_driver);
601
602MODULE_DESCRIPTION("EP93XX Framebuffer Driver");
603MODULE_ALIAS("platform:ep93xx-fb");
604MODULE_AUTHOR("Ryan Mallon, "
605	      "H Hartley Sweeten <hsweeten@visionengravers.com");
606MODULE_LICENSE("GPL");
~/ Read-only file preview.