xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/pmag-ba-fb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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