2 * linux/drivers/video/pmagb-b-fb.c
4 * PMAGB-B TURBOchannel Smart Frame Buffer (SFB) card support,
6 * "HP300 Topcat framebuffer support (derived from macfb of all things)
7 * Phil Blundell <philb@gnu.org> 1998", the original code can be
8 * found in the file hpfb.c in the same directory.
10 * DECstation related code Copyright (C) 1999, 2000, 2001 by
11 * Michael Engel <engel@unix-ag.org>,
12 * Karsten Merker <merker@linuxtag.org> and
14 * Copyright (c) 2005, 2006 Maciej W. Rozycki
16 * This file is subject to the terms and conditions of the GNU General
17 * Public License. See the file COPYING in the main directory of this
18 * archive for more details.
21 #include <linux/compiler.h>
22 #include <linux/delay.h>
23 #include <linux/errno.h>
25 #include <linux/init.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
29 #include <linux/types.h>
33 #include <video/pmagb-b-fb.h>
37 volatile void __iomem
*mmio
;
38 volatile void __iomem
*smem
;
39 volatile u32 __iomem
*sfb
;
40 volatile u32 __iomem
*dac
;
47 static const struct fb_var_screeninfo pmagbbfb_defined
= {
52 .activate
= FB_ACTIVATE_NOW
,
55 .accel_flags
= FB_ACCEL_NONE
,
56 .sync
= FB_SYNC_ON_GREEN
,
57 .vmode
= FB_VMODE_NONINTERLACED
,
60 static const struct fb_fix_screeninfo pmagbbfb_fix
= {
62 .smem_len
= (2048 * 1024),
63 .type
= FB_TYPE_PACKED_PIXELS
,
64 .visual
= FB_VISUAL_PSEUDOCOLOR
,
65 .mmio_len
= PMAGB_B_FBMEM
,
69 static inline void sfb_write(struct pmagbbfb_par
*par
, unsigned int reg
, u32 v
)
71 writel(v
, par
->sfb
+ reg
/ 4);
74 static inline u32
sfb_read(struct pmagbbfb_par
*par
, unsigned int reg
)
76 return readl(par
->sfb
+ reg
/ 4);
79 static inline void dac_write(struct pmagbbfb_par
*par
, unsigned int reg
, u8 v
)
81 writeb(v
, par
->dac
+ reg
/ 4);
84 static inline u8
dac_read(struct pmagbbfb_par
*par
, unsigned int reg
)
86 return readb(par
->dac
+ reg
/ 4);
89 static inline void gp0_write(struct pmagbbfb_par
*par
, u32 v
)
91 writel(v
, par
->mmio
+ PMAGB_B_GP0
);
98 static int pmagbbfb_setcolreg(unsigned int regno
, unsigned int red
,
99 unsigned int green
, unsigned int blue
,
100 unsigned int transp
, struct fb_info
*info
)
102 struct pmagbbfb_par
*par
= info
->par
;
104 if (regno
>= info
->cmap
.len
)
107 red
>>= 8; /* The cmap fields are 16 bits */
108 green
>>= 8; /* wide, but the hardware colormap */
109 blue
>>= 8; /* registers are only 8 bits wide */
112 dac_write(par
, BT459_ADDR_LO
, regno
);
113 dac_write(par
, BT459_ADDR_HI
, 0x00);
115 dac_write(par
, BT459_CMAP
, red
);
117 dac_write(par
, BT459_CMAP
, green
);
119 dac_write(par
, BT459_CMAP
, blue
);
124 static const struct fb_ops pmagbbfb_ops
= {
125 .owner
= THIS_MODULE
,
126 FB_DEFAULT_IOMEM_OPS
,
127 .fb_setcolreg
= pmagbbfb_setcolreg
,
132 * Turn the hardware cursor off.
134 static void pmagbbfb_erase_cursor(struct fb_info
*info
)
136 struct pmagbbfb_par
*par
= info
->par
;
139 dac_write(par
, BT459_ADDR_LO
, 0x00);
140 dac_write(par
, BT459_ADDR_HI
, 0x03);
142 dac_write(par
, BT459_DATA
, 0x00);
146 * Set up screen parameters.
148 static void pmagbbfb_screen_setup(struct fb_info
*info
)
150 struct pmagbbfb_par
*par
= info
->par
;
152 info
->var
.xres
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
153 SFB_VID_HOR_PIX_SHIFT
) & SFB_VID_HOR_PIX_MASK
) * 4;
154 info
->var
.xres_virtual
= info
->var
.xres
;
155 info
->var
.yres
= (sfb_read(par
, SFB_REG_VID_VER
) >>
156 SFB_VID_VER_SL_SHIFT
) & SFB_VID_VER_SL_MASK
;
157 info
->var
.yres_virtual
= info
->var
.yres
;
158 info
->var
.left_margin
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
159 SFB_VID_HOR_BP_SHIFT
) &
160 SFB_VID_HOR_BP_MASK
) * 4;
161 info
->var
.right_margin
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
162 SFB_VID_HOR_FP_SHIFT
) &
163 SFB_VID_HOR_FP_MASK
) * 4;
164 info
->var
.upper_margin
= (sfb_read(par
, SFB_REG_VID_VER
) >>
165 SFB_VID_VER_BP_SHIFT
) & SFB_VID_VER_BP_MASK
;
166 info
->var
.lower_margin
= (sfb_read(par
, SFB_REG_VID_VER
) >>
167 SFB_VID_VER_FP_SHIFT
) & SFB_VID_VER_FP_MASK
;
168 info
->var
.hsync_len
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
169 SFB_VID_HOR_SYN_SHIFT
) &
170 SFB_VID_HOR_SYN_MASK
) * 4;
171 info
->var
.vsync_len
= (sfb_read(par
, SFB_REG_VID_VER
) >>
172 SFB_VID_VER_SYN_SHIFT
) & SFB_VID_VER_SYN_MASK
;
174 info
->fix
.line_length
= info
->var
.xres
;
178 * Determine oscillator configuration.
180 static void pmagbbfb_osc_setup(struct fb_info
*info
)
182 static unsigned int pmagbbfb_freqs
[] = {
183 130808, 119843, 104000, 92980, 74370, 72800,
184 69197, 66000, 65000, 50350, 36000, 32000, 25175
186 struct pmagbbfb_par
*par
= info
->par
;
187 struct tc_bus
*tbus
= to_tc_dev(info
->device
)->bus
;
188 u32 count0
= 8, count1
= 8, counttc
= 16 * 256 + 8;
189 u32 freq0
, freq1
, freqtc
= tc_get_speed(tbus
) / 250;
192 gp0_write(par
, 0); /* select Osc0 */
193 for (j
= 0; j
< 16; j
++) {
195 sfb_write(par
, SFB_REG_TCCLK_COUNT
, 0);
197 for (i
= 0; i
< 100; i
++) { /* nominally max. 20.5us */
198 if (sfb_read(par
, SFB_REG_TCCLK_COUNT
) == 0)
202 count0
+= sfb_read(par
, SFB_REG_VIDCLK_COUNT
);
205 gp0_write(par
, 1); /* select Osc1 */
206 for (j
= 0; j
< 16; j
++) {
208 sfb_write(par
, SFB_REG_TCCLK_COUNT
, 0);
210 for (i
= 0; i
< 100; i
++) { /* nominally max. 20.5us */
211 if (sfb_read(par
, SFB_REG_TCCLK_COUNT
) == 0)
215 count1
+= sfb_read(par
, SFB_REG_VIDCLK_COUNT
);
218 freq0
= (freqtc
* count0
+ counttc
/ 2) / counttc
;
220 if (freq0
>= pmagbbfb_freqs
[0] - (pmagbbfb_freqs
[0] + 32) / 64 &&
221 freq0
<= pmagbbfb_freqs
[0] + (pmagbbfb_freqs
[0] + 32) / 64)
222 par
->osc0
= pmagbbfb_freqs
[0];
224 freq1
= (par
->osc0
* count1
+ count0
/ 2) / count0
;
226 for (i
= 0; i
< ARRAY_SIZE(pmagbbfb_freqs
); i
++)
227 if (freq1
>= pmagbbfb_freqs
[i
] -
228 (pmagbbfb_freqs
[i
] + 128) / 256 &&
229 freq1
<= pmagbbfb_freqs
[i
] +
230 (pmagbbfb_freqs
[i
] + 128) / 256) {
231 par
->osc1
= pmagbbfb_freqs
[i
];
235 if (par
->osc0
- par
->osc1
<= (par
->osc0
+ par
->osc1
+ 256) / 512 ||
236 par
->osc1
- par
->osc0
<= (par
->osc0
+ par
->osc1
+ 256) / 512)
239 gp0_write(par
, par
->osc1
!= 0); /* reselect OscX */
241 info
->var
.pixclock
= par
->osc1
?
242 (1000000000 + par
->osc1
/ 2) / par
->osc1
:
243 (1000000000 + par
->osc0
/ 2) / par
->osc0
;
247 static int pmagbbfb_probe(struct device
*dev
)
249 struct tc_dev
*tdev
= to_tc_dev(dev
);
250 resource_size_t start
, len
;
251 struct fb_info
*info
;
252 struct pmagbbfb_par
*par
;
253 char freq0
[12], freq1
[12];
257 info
= framebuffer_alloc(sizeof(struct pmagbbfb_par
), dev
);
262 dev_set_drvdata(dev
, info
);
264 if (fb_alloc_cmap(&info
->cmap
, 256, 0) < 0) {
265 printk(KERN_ERR
"%s: Cannot allocate color map\n",
271 info
->fbops
= &pmagbbfb_ops
;
272 info
->fix
= pmagbbfb_fix
;
273 info
->var
= pmagbbfb_defined
;
275 /* Request the I/O MEM resource. */
276 start
= tdev
->resource
.start
;
277 len
= tdev
->resource
.end
- start
+ 1;
278 if (!request_mem_region(start
, len
, dev_name(dev
))) {
279 printk(KERN_ERR
"%s: Cannot reserve FB region\n",
285 /* MMIO mapping setup. */
286 info
->fix
.mmio_start
= start
;
287 par
->mmio
= ioremap(info
->fix
.mmio_start
, info
->fix
.mmio_len
);
289 printk(KERN_ERR
"%s: Cannot map MMIO\n", dev_name(dev
));
293 par
->sfb
= par
->mmio
+ PMAGB_B_SFB
;
294 par
->dac
= par
->mmio
+ PMAGB_B_BT459
;
296 /* Frame buffer mapping setup. */
297 info
->fix
.smem_start
= start
+ PMAGB_B_FBMEM
;
298 par
->smem
= ioremap(info
->fix
.smem_start
, info
->fix
.smem_len
);
300 printk(KERN_ERR
"%s: Cannot map FB\n", dev_name(dev
));
304 vid_base
= sfb_read(par
, SFB_REG_VID_BASE
);
305 info
->screen_base
= (void __iomem
*)par
->smem
+ vid_base
* 0x1000;
306 info
->screen_size
= info
->fix
.smem_len
- 2 * vid_base
* 0x1000;
308 pmagbbfb_erase_cursor(info
);
309 pmagbbfb_screen_setup(info
);
310 pmagbbfb_osc_setup(info
);
312 err
= register_framebuffer(info
);
314 printk(KERN_ERR
"%s: Cannot register framebuffer\n",
321 snprintf(freq0
, sizeof(freq0
), "%u.%03uMHz",
322 par
->osc0
/ 1000, par
->osc0
% 1000);
323 snprintf(freq1
, sizeof(freq1
), "%u.%03uMHz",
324 par
->osc1
/ 1000, par
->osc1
% 1000);
326 fb_info(info
, "%s frame buffer device at %s\n",
327 info
->fix
.id
, dev_name(dev
));
328 fb_info(info
, "Osc0: %s, Osc1: %s, Osc%u selected\n",
329 freq0
, par
->osc1
? freq1
: "disabled", par
->osc1
!= 0);
341 release_mem_region(start
, len
);
344 fb_dealloc_cmap(&info
->cmap
);
347 framebuffer_release(info
);
351 static int pmagbbfb_remove(struct device
*dev
)
353 struct tc_dev
*tdev
= to_tc_dev(dev
);
354 struct fb_info
*info
= dev_get_drvdata(dev
);
355 struct pmagbbfb_par
*par
= info
->par
;
356 resource_size_t start
, len
;
359 unregister_framebuffer(info
);
362 start
= tdev
->resource
.start
;
363 len
= tdev
->resource
.end
- start
+ 1;
364 release_mem_region(start
, len
);
365 fb_dealloc_cmap(&info
->cmap
);
366 framebuffer_release(info
);
372 * Initialize the framebuffer.
374 static const struct tc_device_id pmagbbfb_tc_table
[] = {
375 { "DEC ", "PMAGB-BA" },
378 MODULE_DEVICE_TABLE(tc
, pmagbbfb_tc_table
);
380 static struct tc_driver pmagbbfb_driver
= {
381 .id_table
= pmagbbfb_tc_table
,
385 .probe
= pmagbbfb_probe
,
386 .remove
= pmagbbfb_remove
,
390 static int __init
pmagbbfb_init(void)
393 if (fb_get_options("pmagbbfb", NULL
))
396 return tc_register_driver(&pmagbbfb_driver
);
399 static void __exit
pmagbbfb_exit(void)
401 tc_unregister_driver(&pmagbbfb_driver
);
405 module_init(pmagbbfb_init
);
406 module_exit(pmagbbfb_exit
);
408 MODULE_LICENSE("GPL");