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 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>
28 #include <linux/types.h>
32 #include <asm/system.h>
34 #include <asm/dec/tc.h>
36 #include <video/pmagb-b-fb.h>
41 volatile void __iomem
*mmio
;
42 volatile void __iomem
*smem
;
43 volatile u32 __iomem
*sfb
;
44 volatile u32 __iomem
*dac
;
51 static struct fb_info
*root_pmagbbfb_dev
;
53 static struct fb_var_screeninfo pmagbbfb_defined __initdata
= {
58 .activate
= FB_ACTIVATE_NOW
,
61 .accel_flags
= FB_ACCEL_NONE
,
62 .sync
= FB_SYNC_ON_GREEN
,
63 .vmode
= FB_VMODE_NONINTERLACED
,
66 static struct fb_fix_screeninfo pmagbbfb_fix __initdata
= {
68 .smem_len
= (2048 * 1024),
69 .type
= FB_TYPE_PACKED_PIXELS
,
70 .visual
= FB_VISUAL_PSEUDOCOLOR
,
71 .mmio_len
= PMAGB_B_FBMEM
,
75 static inline void sfb_write(struct pmagbbfb_par
*par
, unsigned int reg
, u32 v
)
77 writel(v
, par
->sfb
+ reg
/ 4);
80 static inline u32
sfb_read(struct pmagbbfb_par
*par
, unsigned int reg
)
82 return readl(par
->sfb
+ reg
/ 4);
85 static inline void dac_write(struct pmagbbfb_par
*par
, unsigned int reg
, u8 v
)
87 writeb(v
, par
->dac
+ reg
/ 4);
90 static inline u8
dac_read(struct pmagbbfb_par
*par
, unsigned int reg
)
92 return readb(par
->dac
+ reg
/ 4);
95 static inline void gp0_write(struct pmagbbfb_par
*par
, u32 v
)
97 writel(v
, par
->mmio
+ PMAGB_B_GP0
);
104 static int pmagbbfb_setcolreg(unsigned int regno
, unsigned int red
,
105 unsigned int green
, unsigned int blue
,
106 unsigned int transp
, struct fb_info
*info
)
108 struct pmagbbfb_par
*par
= info
->par
;
110 BUG_ON(regno
>= info
->cmap
.len
);
112 red
>>= 8; /* The cmap fields are 16 bits */
113 green
>>= 8; /* wide, but the hardware colormap */
114 blue
>>= 8; /* registers are only 8 bits wide */
117 dac_write(par
, BT459_ADDR_LO
, regno
);
118 dac_write(par
, BT459_ADDR_HI
, 0x00);
120 dac_write(par
, BT459_CMAP
, red
);
122 dac_write(par
, BT459_CMAP
, green
);
124 dac_write(par
, BT459_CMAP
, blue
);
129 static struct fb_ops pmagbbfb_ops
= {
130 .owner
= THIS_MODULE
,
131 .fb_setcolreg
= pmagbbfb_setcolreg
,
132 .fb_fillrect
= cfb_fillrect
,
133 .fb_copyarea
= cfb_copyarea
,
134 .fb_imageblit
= cfb_imageblit
,
135 .fb_cursor
= soft_cursor
,
140 * Turn the hardware cursor off.
142 static void __init
pmagbbfb_erase_cursor(struct fb_info
*info
)
144 struct pmagbbfb_par
*par
= info
->par
;
147 dac_write(par
, BT459_ADDR_LO
, 0x00);
148 dac_write(par
, BT459_ADDR_HI
, 0x03);
150 dac_write(par
, BT459_DATA
, 0x00);
154 * Set up screen parameters.
156 static void __init
pmagbbfb_screen_setup(struct fb_info
*info
)
158 struct pmagbbfb_par
*par
= info
->par
;
160 info
->var
.xres
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
161 SFB_VID_HOR_PIX_SHIFT
) & SFB_VID_HOR_PIX_MASK
) * 4;
162 info
->var
.xres_virtual
= info
->var
.xres
;
163 info
->var
.yres
= (sfb_read(par
, SFB_REG_VID_VER
) >>
164 SFB_VID_VER_SL_SHIFT
) & SFB_VID_VER_SL_MASK
;
165 info
->var
.yres_virtual
= info
->var
.yres
;
166 info
->var
.left_margin
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
167 SFB_VID_HOR_BP_SHIFT
) &
168 SFB_VID_HOR_BP_MASK
) * 4;
169 info
->var
.right_margin
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
170 SFB_VID_HOR_FP_SHIFT
) &
171 SFB_VID_HOR_FP_MASK
) * 4;
172 info
->var
.upper_margin
= (sfb_read(par
, SFB_REG_VID_VER
) >>
173 SFB_VID_VER_BP_SHIFT
) & SFB_VID_VER_BP_MASK
;
174 info
->var
.lower_margin
= (sfb_read(par
, SFB_REG_VID_VER
) >>
175 SFB_VID_VER_FP_SHIFT
) & SFB_VID_VER_FP_MASK
;
176 info
->var
.hsync_len
= ((sfb_read(par
, SFB_REG_VID_HOR
) >>
177 SFB_VID_HOR_SYN_SHIFT
) &
178 SFB_VID_HOR_SYN_MASK
) * 4;
179 info
->var
.vsync_len
= (sfb_read(par
, SFB_REG_VID_VER
) >>
180 SFB_VID_VER_SYN_SHIFT
) & SFB_VID_VER_SYN_MASK
;
182 info
->fix
.line_length
= info
->var
.xres
;
186 * Determine oscillator configuration.
188 static void __init
pmagbbfb_osc_setup(struct fb_info
*info
)
190 static unsigned int pmagbbfb_freqs
[] __initdata
= {
191 130808, 119843, 104000, 92980, 74367, 72800,
192 69197, 66000, 65000, 50350, 36000, 32000, 25175
194 struct pmagbbfb_par
*par
= info
->par
;
195 u32 count0
= 8, count1
= 8, counttc
= 16 * 256 + 8;
196 u32 freq0
, freq1
, freqtc
= get_tc_speed() / 250;
199 gp0_write(par
, 0); /* select Osc0 */
200 for (j
= 0; j
< 16; j
++) {
202 sfb_write(par
, SFB_REG_TCCLK_COUNT
, 0);
204 for (i
= 0; i
< 100; i
++) { /* nominally max. 20.5us */
205 if (sfb_read(par
, SFB_REG_TCCLK_COUNT
) == 0)
209 count0
+= sfb_read(par
, SFB_REG_VIDCLK_COUNT
);
212 gp0_write(par
, 1); /* select Osc1 */
213 for (j
= 0; j
< 16; j
++) {
215 sfb_write(par
, SFB_REG_TCCLK_COUNT
, 0);
217 for (i
= 0; i
< 100; i
++) { /* nominally max. 20.5us */
218 if (sfb_read(par
, SFB_REG_TCCLK_COUNT
) == 0)
222 count1
+= sfb_read(par
, SFB_REG_VIDCLK_COUNT
);
225 freq0
= (freqtc
* count0
+ counttc
/ 2) / counttc
;
227 if (freq0
>= pmagbbfb_freqs
[0] - (pmagbbfb_freqs
[0] + 32) / 64 &&
228 freq0
<= pmagbbfb_freqs
[0] + (pmagbbfb_freqs
[0] + 32) / 64)
229 par
->osc0
= pmagbbfb_freqs
[0];
231 freq1
= (par
->osc0
* count1
+ count0
/ 2) / count0
;
233 for (i
= 0; i
< sizeof(pmagbbfb_freqs
) / sizeof(*pmagbbfb_freqs
); i
++)
234 if (freq1
>= pmagbbfb_freqs
[i
] -
235 (pmagbbfb_freqs
[i
] + 128) / 256 &&
236 freq1
<= pmagbbfb_freqs
[i
] +
237 (pmagbbfb_freqs
[i
] + 128) / 256) {
238 par
->osc1
= pmagbbfb_freqs
[i
];
242 if (par
->osc0
- par
->osc1
<= (par
->osc0
+ par
->osc1
+ 256) / 512 ||
243 par
->osc1
- par
->osc0
<= (par
->osc0
+ par
->osc1
+ 256) / 512)
246 gp0_write(par
, par
->osc1
!= 0); /* reselect OscX */
248 info
->var
.pixclock
= par
->osc1
?
249 (1000000000 + par
->osc1
/ 2) / par
->osc1
:
250 (1000000000 + par
->osc0
/ 2) / par
->osc0
;
254 static int __init
pmagbbfb_init_one(int slot
)
256 char freq0
[12], freq1
[12];
257 struct fb_info
*info
;
258 struct pmagbbfb_par
*par
;
259 unsigned long base_addr
;
262 info
= framebuffer_alloc(sizeof(struct pmagbbfb_par
), NULL
);
268 claim_tc_card(par
->slot
);
270 base_addr
= get_tc_base_addr(par
->slot
);
272 par
->next
= root_pmagbbfb_dev
;
273 root_pmagbbfb_dev
= info
;
275 if (fb_alloc_cmap(&info
->cmap
, 256, 0) < 0)
278 info
->fbops
= &pmagbbfb_ops
;
279 info
->fix
= pmagbbfb_fix
;
280 info
->var
= pmagbbfb_defined
;
281 info
->flags
= FBINFO_DEFAULT
;
283 /* MMIO mapping setup. */
284 info
->fix
.mmio_start
= base_addr
;
285 par
->mmio
= ioremap_nocache(info
->fix
.mmio_start
, info
->fix
.mmio_len
);
288 par
->sfb
= par
->mmio
+ PMAGB_B_SFB
;
289 par
->dac
= par
->mmio
+ PMAGB_B_BT459
;
291 /* Frame buffer mapping setup. */
292 info
->fix
.smem_start
= base_addr
+ PMAGB_B_FBMEM
;
293 par
->smem
= ioremap_nocache(info
->fix
.smem_start
, info
->fix
.smem_len
);
296 vid_base
= sfb_read(par
, SFB_REG_VID_BASE
);
297 info
->screen_base
= (void __iomem
*)par
->smem
+ vid_base
* 0x1000;
298 info
->screen_size
= info
->fix
.smem_len
- 2 * vid_base
* 0x1000;
300 pmagbbfb_erase_cursor(info
);
301 pmagbbfb_screen_setup(info
);
302 pmagbbfb_osc_setup(info
);
304 if (register_framebuffer(info
) < 0)
307 snprintf(freq0
, sizeof(freq0
), "%u.%03uMHz",
308 par
->osc0
/ 1000, par
->osc0
% 1000);
309 snprintf(freq1
, sizeof(freq1
), "%u.%03uMHz",
310 par
->osc1
/ 1000, par
->osc1
% 1000);
312 pr_info("fb%d: %s frame buffer device in slot %d\n",
313 info
->node
, info
->fix
.id
, par
->slot
);
314 pr_info("fb%d: Osc0: %s, Osc1: %s, Osc%u selected\n",
315 info
->node
, freq0
, par
->osc1
? freq1
: "disabled",
328 fb_dealloc_cmap(&info
->cmap
);
331 root_pmagbbfb_dev
= par
->next
;
332 release_tc_card(par
->slot
);
333 framebuffer_release(info
);
337 static void __exit
pmagbbfb_exit_one(void)
339 struct fb_info
*info
= root_pmagbbfb_dev
;
340 struct pmagbbfb_par
*par
= info
->par
;
342 unregister_framebuffer(info
);
345 fb_dealloc_cmap(&info
->cmap
);
346 root_pmagbbfb_dev
= par
->next
;
347 release_tc_card(par
->slot
);
348 framebuffer_release(info
);
353 * Initialise the framebuffer.
355 static int __init
pmagbbfb_init(void)
360 if (fb_get_options("pmagbbfb", NULL
))
363 while ((slot
= search_tc_card("PMAGB-BA")) >= 0) {
364 if (pmagbbfb_init_one(slot
) < 0)
368 return (count
> 0) ? 0 : -ENXIO
;
371 static void __exit
pmagbbfb_exit(void)
373 while (root_pmagbbfb_dev
)
378 module_init(pmagbbfb_init
);
379 module_exit(pmagbbfb_exit
);
381 MODULE_LICENSE("GPL");