aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorAndrei Konovalov <akonovalov@ru.mvista.com>2007-05-08 03:40:18 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:34 -0400
commit147394c8ece44be85d692cc92cc0d047e4d8fb69 (patch)
tree13a69a386a6fbf2fddcb0d88d2ed604cca4b9e99 /drivers/video
parent435d56fcd45cdf32bfb4db5d4e1efe17f3da95b2 (diff)
xilinxfb: xilinx framebuffer device driver
Add support for the video controller IP block included into Xilinx ML300 and ML403 reference designs. Signed-off-by: Andrei Konovalov <akonovalov@ru.mvista.com> Signed-off-by: Antonino Daplas <adaplas@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig11
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/xilinxfb.c381
3 files changed, 393 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 74d764e2e1fa..1132ba5ff391 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1755,6 +1755,17 @@ config FB_PS3_DEFAULT_SIZE_M
1755 The default value can be overridden on the kernel command line 1755 The default value can be overridden on the kernel command line
1756 using the "ps3fb" option (e.g. "ps3fb=9M"); 1756 using the "ps3fb" option (e.g. "ps3fb=9M");
1757 1757
1758config FB_XILINX
1759 tristate "Xilinx frame buffer support"
1760 depends on FB && XILINX_VIRTEX
1761 select FB_CFB_FILLRECT
1762 select FB_CFB_COPYAREA
1763 select FB_CFB_IMAGEBLIT
1764 ---help---
1765 Include support for the Xilinx ML300/ML403 reference design
1766 framebuffer. ML300 carries a 640*480 LCD display on the board,
1767 ML403 uses a standard DB15 VGA connector.
1768
1758config FB_VIRTUAL 1769config FB_VIRTUAL
1759 tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" 1770 tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
1760 depends on FB 1771 depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index a59395d6189d..a916c204274f 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/
109obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o 109obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
110obj-$(CONFIG_FB_PS3) += ps3fb.o 110obj-$(CONFIG_FB_PS3) += ps3fb.o
111obj-$(CONFIG_FB_SM501) += sm501fb.o 111obj-$(CONFIG_FB_SM501) += sm501fb.o
112obj-$(CONFIG_FB_XILINX) += xilinxfb.o
112 113
113# Platform or fallback drivers go here 114# Platform or fallback drivers go here
114obj-$(CONFIG_FB_VESA) += vesafb.o 115obj-$(CONFIG_FB_VESA) += vesafb.o
diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
new file mode 100644
index 000000000000..1d29a89a86b4
--- /dev/null
+++ b/drivers/video/xilinxfb.c
@@ -0,0 +1,381 @@
1/*
2 * xilinxfb.c
3 *
4 * Xilinx TFT LCD frame buffer driver
5 *
6 * Author: MontaVista Software, Inc.
7 * source@mvista.com
8 *
9 * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the
10 * terms of the GNU General Public License version 2. This program is licensed
11 * "as is" without any warranty of any kind, whether express or implied.
12 */
13
14/*
15 * This driver was based on au1100fb.c by MontaVista rewritten for 2.6
16 * by Embedded Alley Solutions <source@embeddedalley.com>, which in turn
17 * was based on skeletonfb.c, Skeleton for a frame buffer device by
18 * Geert Uytterhoeven.
19 */
20
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/version.h>
24#include <linux/errno.h>
25#include <linux/string.h>
26#include <linux/mm.h>
27#include <linux/fb.h>
28#include <linux/init.h>
29#include <linux/dma-mapping.h>
30#include <linux/platform_device.h>
31
32#include <asm/io.h>
33#include <syslib/virtex_devices.h>
34
35#define DRIVER_NAME "xilinxfb"
36#define DRIVER_DESCRIPTION "Xilinx TFT LCD frame buffer driver"
37
38/*
39 * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for
40 * the VGA port on the Xilinx ML40x board. This is a hardware display controller
41 * for a 640x480 resolution TFT or VGA screen.
42 *
43 * The interface to the framebuffer is nice and simple. There are two
44 * control registers. The first tells the LCD interface where in memory
45 * the frame buffer is (only the 11 most significant bits are used, so
46 * don't start thinking about scrolling). The second allows the LCD to
47 * be turned on or off as well as rotated 180 degrees.
48 */
49#define NUM_REGS 2
50#define REG_FB_ADDR 0
51#define REG_CTRL 1
52#define REG_CTRL_ENABLE 0x0001
53#define REG_CTRL_ROTATE 0x0002
54
55/*
56 * The hardware only handles a single mode: 640x480 24 bit true
57 * color. Each pixel gets a word (32 bits) of memory. Within each word,
58 * the 8 most significant bits are ignored, the next 8 bits are the red
59 * level, the next 8 bits are the green level and the 8 least
60 * significant bits are the blue level. Each row of the LCD uses 1024
61 * words, but only the first 640 pixels are displayed with the other 384
62 * words being ignored. There are 480 rows.
63 */
64#define BYTES_PER_PIXEL 4
65#define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8)
66#define XRES 640
67#define YRES 480
68#define XRES_VIRTUAL 1024
69#define YRES_VIRTUAL YRES
70#define LINE_LENGTH (XRES_VIRTUAL * BYTES_PER_PIXEL)
71#define FB_SIZE (YRES_VIRTUAL * LINE_LENGTH)
72
73#define RED_SHIFT 16
74#define GREEN_SHIFT 8
75#define BLUE_SHIFT 0
76
77#define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */
78
79/*
80 * Here are the default fb_fix_screeninfo and fb_var_screeninfo structures
81 */
82static struct fb_fix_screeninfo xilinx_fb_fix __initdata = {
83 .id = "Xilinx",
84 .type = FB_TYPE_PACKED_PIXELS,
85 .visual = FB_VISUAL_TRUECOLOR,
86 .smem_len = FB_SIZE,
87 .line_length = LINE_LENGTH,
88 .accel = FB_ACCEL_NONE
89};
90
91static struct fb_var_screeninfo xilinx_fb_var __initdata = {
92 .xres = XRES,
93 .yres = YRES,
94 .xres_virtual = XRES_VIRTUAL,
95 .yres_virtual = YRES_VIRTUAL,
96
97 .bits_per_pixel = BITS_PER_PIXEL,
98
99 .red = { RED_SHIFT, 8, 0 },
100 .green = { GREEN_SHIFT, 8, 0 },
101 .blue = { BLUE_SHIFT, 8, 0 },
102 .transp = { 0, 0, 0 },
103
104 .activate = FB_ACTIVATE_NOW
105};
106
107struct xilinxfb_drvdata {
108
109 struct fb_info info; /* FB driver info record */
110
111 u32 regs_phys; /* phys. address of the control registers */
112 u32 __iomem *regs; /* virt. address of the control registers */
113
114 unsigned char __iomem *fb_virt; /* virt. address of the frame buffer */
115 dma_addr_t fb_phys; /* phys. address of the frame buffer */
116
117 u32 reg_ctrl_default;
118
119 u32 pseudo_palette[PALETTE_ENTRIES_NO];
120 /* Fake palette of 16 colors */
121};
122
123#define to_xilinxfb_drvdata(_info) \
124 container_of(_info, struct xilinxfb_drvdata, info)
125
126/*
127 * The LCD controller has DCR interface to its registers, but all
128 * the boards and configurations the driver has been tested with
129 * use opb2dcr bridge. So the registers are seen as memory mapped.
130 * This macro is to make it simple to add the direct DCR access
131 * when it's needed.
132 */
133#define xilinx_fb_out_be32(driverdata, offset, val) \
134 out_be32(driverdata->regs + offset, val)
135
136static int
137xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
138 unsigned transp, struct fb_info *fbi)
139{
140 u32 *palette = fbi->pseudo_palette;
141
142 if (regno >= PALETTE_ENTRIES_NO)
143 return -EINVAL;
144
145 if (fbi->var.grayscale) {
146 /* Convert color to grayscale.
147 * grayscale = 0.30*R + 0.59*G + 0.11*B */
148 red = green = blue =
149 (red * 77 + green * 151 + blue * 28 + 127) >> 8;
150 }
151
152 /* fbi->fix.visual is always FB_VISUAL_TRUECOLOR */
153
154 /* We only handle 8 bits of each color. */
155 red >>= 8;
156 green >>= 8;
157 blue >>= 8;
158 palette[regno] = (red << RED_SHIFT) | (green << GREEN_SHIFT) |
159 (blue << BLUE_SHIFT);
160
161 return 0;
162}
163
164static int
165xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
166{
167 struct xilinxfb_drvdata *drvdata = to_xilinxfb_drvdata(fbi);
168
169 switch (blank_mode) {
170 case FB_BLANK_UNBLANK:
171 /* turn on panel */
172 xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
173 break;
174
175 case FB_BLANK_NORMAL:
176 case FB_BLANK_VSYNC_SUSPEND:
177 case FB_BLANK_HSYNC_SUSPEND:
178 case FB_BLANK_POWERDOWN:
179 /* turn off panel */
180 xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
181 default:
182 break;
183
184 }
185 return 0; /* success */
186}
187
188static struct fb_ops xilinxfb_ops =
189{
190 .owner = THIS_MODULE,
191 .fb_setcolreg = xilinx_fb_setcolreg,
192 .fb_blank = xilinx_fb_blank,
193 .fb_fillrect = cfb_fillrect,
194 .fb_copyarea = cfb_copyarea,
195 .fb_imageblit = cfb_imageblit,
196};
197
198/* === The device driver === */
199
200static int
201xilinxfb_drv_probe(struct device *dev)
202{
203 struct platform_device *pdev;
204 struct xilinxfb_platform_data *pdata;
205 struct xilinxfb_drvdata *drvdata;
206 struct resource *regs_res;
207 int retval;
208
209 if (!dev)
210 return -EINVAL;
211
212 pdev = to_platform_device(dev);
213 pdata = pdev->dev.platform_data;
214
215 if (pdata == NULL) {
216 printk(KERN_ERR "Couldn't find platform data.\n");
217 return -EFAULT;
218 }
219
220 drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
221 if (!drvdata) {
222 printk(KERN_ERR "Couldn't allocate device private record\n");
223 return -ENOMEM;
224 }
225 dev_set_drvdata(dev, drvdata);
226
227 /* Map the control registers in */
228 regs_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
229 if (!regs_res || (regs_res->end - regs_res->start + 1 < 8)) {
230 printk(KERN_ERR "Couldn't get registers resource\n");
231 retval = -EFAULT;
232 goto failed1;
233 }
234
235 if (!request_mem_region(regs_res->start, 8, DRIVER_NAME)) {
236 printk(KERN_ERR
237 "Couldn't lock memory region at 0x%08X\n",
238 regs_res->start);
239 retval = -EBUSY;
240 goto failed1;
241 }
242 drvdata->regs = (u32 __iomem*) ioremap(regs_res->start, 8);
243 drvdata->regs_phys = regs_res->start;
244
245 /* Allocate the framebuffer memory */
246 drvdata->fb_virt = dma_alloc_coherent(dev, PAGE_ALIGN(FB_SIZE),
247 &drvdata->fb_phys, GFP_KERNEL);
248 if (!drvdata->fb_virt) {
249 printk(KERN_ERR "Could not allocate frame buffer memory\n");
250 retval = -ENOMEM;
251 goto failed2;
252 }
253
254 /* Clear (turn to black) the framebuffer */
255 memset_io((void *) drvdata->fb_virt, 0, FB_SIZE);
256
257 /* Tell the hardware where the frame buffer is */
258 xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
259
260 /* Turn on the display */
261 if (pdata->rotate_screen) {
262 drvdata->reg_ctrl_default = REG_CTRL_ENABLE | REG_CTRL_ROTATE;
263 } else {
264 drvdata->reg_ctrl_default = REG_CTRL_ENABLE;
265 }
266 xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
267
268 /* Fill struct fb_info */
269 drvdata->info.device = dev;
270 drvdata->info.screen_base = drvdata->fb_virt;
271 drvdata->info.fbops = &xilinxfb_ops;
272 drvdata->info.fix = xilinx_fb_fix;
273 drvdata->info.fix.smem_start = drvdata->fb_phys;
274 drvdata->info.pseudo_palette = drvdata->pseudo_palette;
275
276 if (fb_alloc_cmap(&drvdata->info.cmap, PALETTE_ENTRIES_NO, 0) < 0) {
277 printk(KERN_ERR "Fail to allocate colormap (%d entries)\n",
278 PALETTE_ENTRIES_NO);
279 retval = -EFAULT;
280 goto failed3;
281 }
282
283 drvdata->info.flags = FBINFO_DEFAULT;
284 xilinx_fb_var.height = pdata->screen_height_mm;
285 xilinx_fb_var.width = pdata->screen_width_mm;
286 drvdata->info.var = xilinx_fb_var;
287
288 /* Register new frame buffer */
289 if (register_framebuffer(&drvdata->info) < 0) {
290 printk(KERN_ERR "Could not register frame buffer\n");
291 retval = -EINVAL;
292 goto failed4;
293 }
294
295 return 0; /* success */
296
297failed4:
298 fb_dealloc_cmap(&drvdata->info.cmap);
299
300failed3:
301 dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt,
302 drvdata->fb_phys);
303
304 /* Turn off the display */
305 xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
306 iounmap(drvdata->regs);
307
308failed2:
309 release_mem_region(regs_res->start, 8);
310
311failed1:
312 kfree(drvdata);
313 dev_set_drvdata(dev, NULL);
314
315 return retval;
316}
317
318static int
319xilinxfb_drv_remove(struct device *dev)
320{
321 struct xilinxfb_drvdata *drvdata;
322
323 if (!dev)
324 return -ENODEV;
325
326 drvdata = (struct xilinxfb_drvdata *) dev_get_drvdata(dev);
327
328#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
329 xilinx_fb_blank(VESA_POWERDOWN, &drvdata->info);
330#endif
331
332 unregister_framebuffer(&drvdata->info);
333
334 fb_dealloc_cmap(&drvdata->info.cmap);
335
336 dma_free_coherent(dev, PAGE_ALIGN(FB_SIZE), drvdata->fb_virt,
337 drvdata->fb_phys);
338
339 /* Turn off the display */
340 xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
341 iounmap(drvdata->regs);
342
343 release_mem_region(drvdata->regs_phys, 8);
344
345 kfree(drvdata);
346 dev_set_drvdata(dev, NULL);
347
348 return 0;
349}
350
351
352static struct device_driver xilinxfb_driver = {
353 .name = DRIVER_NAME,
354 .bus = &platform_bus_type,
355
356 .probe = xilinxfb_drv_probe,
357 .remove = xilinxfb_drv_remove
358};
359
360static int __init
361xilinxfb_init(void)
362{
363 /*
364 * No kernel boot options used,
365 * so we just need to register the driver
366 */
367 return driver_register(&xilinxfb_driver);
368}
369
370static void __exit
371xilinxfb_cleanup(void)
372{
373 driver_unregister(&xilinxfb_driver);
374}
375
376module_init(xilinxfb_init);
377module_exit(xilinxfb_cleanup);
378
379MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
380MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
381MODULE_LICENSE("GPL");