1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * linux/drivers/video/pmag-ba-fb.c
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * PMAG-BA TURBOchannel Color Frame Buffer (CFB) card support,
5*4882a593Smuzhiyun * derived from:
6*4882a593Smuzhiyun * "HP300 Topcat framebuffer support (derived from macfb of all things)
7*4882a593Smuzhiyun * Phil Blundell <philb@gnu.org> 1998", the original code can be
8*4882a593Smuzhiyun * found in the file hpfb.c in the same directory.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Based on digital document:
11*4882a593Smuzhiyun * "PMAG-BA TURBOchannel Color Frame Buffer
12*4882a593Smuzhiyun * Functional Specification", Revision 1.2, August 27, 1990
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * DECstation related code Copyright (C) 1999, 2000, 2001 by
15*4882a593Smuzhiyun * Michael Engel <engel@unix-ag.org>,
16*4882a593Smuzhiyun * Karsten Merker <merker@linuxtag.org> and
17*4882a593Smuzhiyun * Harald Koerfgen.
18*4882a593Smuzhiyun * Copyright (c) 2005, 2006 Maciej W. Rozycki
19*4882a593Smuzhiyun * Copyright (c) 2005 James Simmons
20*4882a593Smuzhiyun *
21*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General
22*4882a593Smuzhiyun * Public License. See the file COPYING in the main directory of this
23*4882a593Smuzhiyun * archive for more details.
24*4882a593Smuzhiyun */
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include <linux/compiler.h>
27*4882a593Smuzhiyun #include <linux/errno.h>
28*4882a593Smuzhiyun #include <linux/fb.h>
29*4882a593Smuzhiyun #include <linux/init.h>
30*4882a593Smuzhiyun #include <linux/kernel.h>
31*4882a593Smuzhiyun #include <linux/module.h>
32*4882a593Smuzhiyun #include <linux/tc.h>
33*4882a593Smuzhiyun #include <linux/types.h>
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #include <asm/io.h>
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #include <video/pmag-ba-fb.h>
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun struct pmagbafb_par {
41*4882a593Smuzhiyun volatile void __iomem *mmio;
42*4882a593Smuzhiyun volatile u32 __iomem *dac;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun static const struct fb_var_screeninfo pmagbafb_defined = {
47*4882a593Smuzhiyun .xres = 1024,
48*4882a593Smuzhiyun .yres = 864,
49*4882a593Smuzhiyun .xres_virtual = 1024,
50*4882a593Smuzhiyun .yres_virtual = 864,
51*4882a593Smuzhiyun .bits_per_pixel = 8,
52*4882a593Smuzhiyun .red.length = 8,
53*4882a593Smuzhiyun .green.length = 8,
54*4882a593Smuzhiyun .blue.length = 8,
55*4882a593Smuzhiyun .activate = FB_ACTIVATE_NOW,
56*4882a593Smuzhiyun .height = -1,
57*4882a593Smuzhiyun .width = -1,
58*4882a593Smuzhiyun .accel_flags = FB_ACCEL_NONE,
59*4882a593Smuzhiyun .pixclock = 14452,
60*4882a593Smuzhiyun .left_margin = 116,
61*4882a593Smuzhiyun .right_margin = 12,
62*4882a593Smuzhiyun .upper_margin = 34,
63*4882a593Smuzhiyun .lower_margin = 0,
64*4882a593Smuzhiyun .hsync_len = 128,
65*4882a593Smuzhiyun .vsync_len = 3,
66*4882a593Smuzhiyun .sync = FB_SYNC_ON_GREEN,
67*4882a593Smuzhiyun .vmode = FB_VMODE_NONINTERLACED,
68*4882a593Smuzhiyun };
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun static const struct fb_fix_screeninfo pmagbafb_fix = {
71*4882a593Smuzhiyun .id = "PMAG-BA",
72*4882a593Smuzhiyun .smem_len = (1024 * 1024),
73*4882a593Smuzhiyun .type = FB_TYPE_PACKED_PIXELS,
74*4882a593Smuzhiyun .visual = FB_VISUAL_PSEUDOCOLOR,
75*4882a593Smuzhiyun .line_length = 1024,
76*4882a593Smuzhiyun .mmio_len = PMAG_BA_SIZE - PMAG_BA_BT459,
77*4882a593Smuzhiyun };
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun
dac_write(struct pmagbafb_par * par,unsigned int reg,u8 v)80*4882a593Smuzhiyun static inline void dac_write(struct pmagbafb_par *par, unsigned int reg, u8 v)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun writeb(v, par->dac + reg / 4);
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun
dac_read(struct pmagbafb_par * par,unsigned int reg)85*4882a593Smuzhiyun static inline u8 dac_read(struct pmagbafb_par *par, unsigned int reg)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun return readb(par->dac + reg / 4);
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun * Set the palette.
93*4882a593Smuzhiyun */
pmagbafb_setcolreg(unsigned int regno,unsigned int red,unsigned int green,unsigned int blue,unsigned int transp,struct fb_info * info)94*4882a593Smuzhiyun static int pmagbafb_setcolreg(unsigned int regno, unsigned int red,
95*4882a593Smuzhiyun unsigned int green, unsigned int blue,
96*4882a593Smuzhiyun unsigned int transp, struct fb_info *info)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun struct pmagbafb_par *par = info->par;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (regno >= info->cmap.len)
101*4882a593Smuzhiyun return 1;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun red >>= 8; /* The cmap fields are 16 bits */
104*4882a593Smuzhiyun green >>= 8; /* wide, but the hardware colormap */
105*4882a593Smuzhiyun blue >>= 8; /* registers are only 8 bits wide */
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun mb();
108*4882a593Smuzhiyun dac_write(par, BT459_ADDR_LO, regno);
109*4882a593Smuzhiyun dac_write(par, BT459_ADDR_HI, 0x00);
110*4882a593Smuzhiyun wmb();
111*4882a593Smuzhiyun dac_write(par, BT459_CMAP, red);
112*4882a593Smuzhiyun wmb();
113*4882a593Smuzhiyun dac_write(par, BT459_CMAP, green);
114*4882a593Smuzhiyun wmb();
115*4882a593Smuzhiyun dac_write(par, BT459_CMAP, blue);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun return 0;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun static const struct fb_ops pmagbafb_ops = {
121*4882a593Smuzhiyun .owner = THIS_MODULE,
122*4882a593Smuzhiyun .fb_setcolreg = pmagbafb_setcolreg,
123*4882a593Smuzhiyun .fb_fillrect = cfb_fillrect,
124*4882a593Smuzhiyun .fb_copyarea = cfb_copyarea,
125*4882a593Smuzhiyun .fb_imageblit = cfb_imageblit,
126*4882a593Smuzhiyun };
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /*
130*4882a593Smuzhiyun * Turn the hardware cursor off.
131*4882a593Smuzhiyun */
pmagbafb_erase_cursor(struct fb_info * info)132*4882a593Smuzhiyun static void pmagbafb_erase_cursor(struct fb_info *info)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun struct pmagbafb_par *par = info->par;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun mb();
137*4882a593Smuzhiyun dac_write(par, BT459_ADDR_LO, 0x00);
138*4882a593Smuzhiyun dac_write(par, BT459_ADDR_HI, 0x03);
139*4882a593Smuzhiyun wmb();
140*4882a593Smuzhiyun dac_write(par, BT459_DATA, 0x00);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun
pmagbafb_probe(struct device * dev)144*4882a593Smuzhiyun static int pmagbafb_probe(struct device *dev)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct tc_dev *tdev = to_tc_dev(dev);
147*4882a593Smuzhiyun resource_size_t start, len;
148*4882a593Smuzhiyun struct fb_info *info;
149*4882a593Smuzhiyun struct pmagbafb_par *par;
150*4882a593Smuzhiyun int err;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun info = framebuffer_alloc(sizeof(struct pmagbafb_par), dev);
153*4882a593Smuzhiyun if (!info)
154*4882a593Smuzhiyun return -ENOMEM;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun par = info->par;
157*4882a593Smuzhiyun dev_set_drvdata(dev, info);
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
160*4882a593Smuzhiyun printk(KERN_ERR "%s: Cannot allocate color map\n",
161*4882a593Smuzhiyun dev_name(dev));
162*4882a593Smuzhiyun err = -ENOMEM;
163*4882a593Smuzhiyun goto err_alloc;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun info->fbops = &pmagbafb_ops;
167*4882a593Smuzhiyun info->fix = pmagbafb_fix;
168*4882a593Smuzhiyun info->var = pmagbafb_defined;
169*4882a593Smuzhiyun info->flags = FBINFO_DEFAULT;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun /* Request the I/O MEM resource. */
172*4882a593Smuzhiyun start = tdev->resource.start;
173*4882a593Smuzhiyun len = tdev->resource.end - start + 1;
174*4882a593Smuzhiyun if (!request_mem_region(start, len, dev_name(dev))) {
175*4882a593Smuzhiyun printk(KERN_ERR "%s: Cannot reserve FB region\n",
176*4882a593Smuzhiyun dev_name(dev));
177*4882a593Smuzhiyun err = -EBUSY;
178*4882a593Smuzhiyun goto err_cmap;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun /* MMIO mapping setup. */
182*4882a593Smuzhiyun info->fix.mmio_start = start;
183*4882a593Smuzhiyun par->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len);
184*4882a593Smuzhiyun if (!par->mmio) {
185*4882a593Smuzhiyun printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev));
186*4882a593Smuzhiyun err = -ENOMEM;
187*4882a593Smuzhiyun goto err_resource;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun par->dac = par->mmio + PMAG_BA_BT459;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /* Frame buffer mapping setup. */
192*4882a593Smuzhiyun info->fix.smem_start = start + PMAG_BA_FBMEM;
193*4882a593Smuzhiyun info->screen_base = ioremap(info->fix.smem_start,
194*4882a593Smuzhiyun info->fix.smem_len);
195*4882a593Smuzhiyun if (!info->screen_base) {
196*4882a593Smuzhiyun printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));
197*4882a593Smuzhiyun err = -ENOMEM;
198*4882a593Smuzhiyun goto err_mmio_map;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun info->screen_size = info->fix.smem_len;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun pmagbafb_erase_cursor(info);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun err = register_framebuffer(info);
205*4882a593Smuzhiyun if (err < 0) {
206*4882a593Smuzhiyun printk(KERN_ERR "%s: Cannot register framebuffer\n",
207*4882a593Smuzhiyun dev_name(dev));
208*4882a593Smuzhiyun goto err_smem_map;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun get_device(dev);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun fb_info(info, "%s frame buffer device at %s\n",
214*4882a593Smuzhiyun info->fix.id, dev_name(dev));
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun return 0;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun err_smem_map:
220*4882a593Smuzhiyun iounmap(info->screen_base);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun err_mmio_map:
223*4882a593Smuzhiyun iounmap(par->mmio);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun err_resource:
226*4882a593Smuzhiyun release_mem_region(start, len);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun err_cmap:
229*4882a593Smuzhiyun fb_dealloc_cmap(&info->cmap);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun err_alloc:
232*4882a593Smuzhiyun framebuffer_release(info);
233*4882a593Smuzhiyun return err;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
pmagbafb_remove(struct device * dev)236*4882a593Smuzhiyun static int pmagbafb_remove(struct device *dev)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun struct tc_dev *tdev = to_tc_dev(dev);
239*4882a593Smuzhiyun struct fb_info *info = dev_get_drvdata(dev);
240*4882a593Smuzhiyun struct pmagbafb_par *par = info->par;
241*4882a593Smuzhiyun resource_size_t start, len;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun put_device(dev);
244*4882a593Smuzhiyun unregister_framebuffer(info);
245*4882a593Smuzhiyun iounmap(info->screen_base);
246*4882a593Smuzhiyun iounmap(par->mmio);
247*4882a593Smuzhiyun start = tdev->resource.start;
248*4882a593Smuzhiyun len = tdev->resource.end - start + 1;
249*4882a593Smuzhiyun release_mem_region(start, len);
250*4882a593Smuzhiyun fb_dealloc_cmap(&info->cmap);
251*4882a593Smuzhiyun framebuffer_release(info);
252*4882a593Smuzhiyun return 0;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun /*
257*4882a593Smuzhiyun * Initialize the framebuffer.
258*4882a593Smuzhiyun */
259*4882a593Smuzhiyun static const struct tc_device_id pmagbafb_tc_table[] = {
260*4882a593Smuzhiyun { "DEC ", "PMAG-BA " },
261*4882a593Smuzhiyun { }
262*4882a593Smuzhiyun };
263*4882a593Smuzhiyun MODULE_DEVICE_TABLE(tc, pmagbafb_tc_table);
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun static struct tc_driver pmagbafb_driver = {
266*4882a593Smuzhiyun .id_table = pmagbafb_tc_table,
267*4882a593Smuzhiyun .driver = {
268*4882a593Smuzhiyun .name = "pmagbafb",
269*4882a593Smuzhiyun .bus = &tc_bus_type,
270*4882a593Smuzhiyun .probe = pmagbafb_probe,
271*4882a593Smuzhiyun .remove = pmagbafb_remove,
272*4882a593Smuzhiyun },
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun
pmagbafb_init(void)275*4882a593Smuzhiyun static int __init pmagbafb_init(void)
276*4882a593Smuzhiyun {
277*4882a593Smuzhiyun #ifndef MODULE
278*4882a593Smuzhiyun if (fb_get_options("pmagbafb", NULL))
279*4882a593Smuzhiyun return -ENXIO;
280*4882a593Smuzhiyun #endif
281*4882a593Smuzhiyun return tc_register_driver(&pmagbafb_driver);
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun
pmagbafb_exit(void)284*4882a593Smuzhiyun static void __exit pmagbafb_exit(void)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun tc_unregister_driver(&pmagbafb_driver);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun module_init(pmagbafb_init);
291*4882a593Smuzhiyun module_exit(pmagbafb_exit);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun MODULE_LICENSE("GPL");
294