2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #define grub_video_render_target grub_video_fbrender_target
22 #include <grub/types.h>
24 #include <grub/misc.h>
26 #include <grub/video.h>
27 #include <grub/video_fb.h>
31 GRUB_MOD_LICENSE ("GPLv3+");
35 struct grub_video_mode_info mode_info
;
36 grub_size_t page_size
; /* The size of a page in bytes. */
41 grub_pci_device_t dev
;
44 #define CIRRUS_APERTURE_SIZE 0x1000000
46 #define CIRRUS_MAX_WIDTH 0x800
47 #define CIRRUS_MAX_HEIGHT 0x800
48 #define CIRRUS_MAX_PITCH (0x1ff * GRUB_VGA_CR_PITCH_DIVISOR)
52 CIRRUS_CR_EXTENDED_DISPLAY
= 0x1b,
53 CIRRUS_CR_EXTENDED_OVERLAY
= 0x1d,
57 #define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK 0x10
58 #define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT 4
59 #define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1 0x1
60 #define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT1 16
61 #define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2 0xc
62 #define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT2 15
64 #define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK 0x80
65 #define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_SHIFT 12
69 CIRRUS_SR_EXTENDED_MODE
= 7,
73 #define CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE 0xf0
74 #define CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT 0x01
75 #define CIRRUS_SR_EXTENDED_MODE_8BPP 0x00
76 #define CIRRUS_SR_EXTENDED_MODE_16BPP 0x06
77 #define CIRRUS_SR_EXTENDED_MODE_24BPP 0x04
78 #define CIRRUS_SR_EXTENDED_MODE_32BPP 0x08
80 #define CIRRUS_HIDDEN_DAC_ENABLE_EXT 0x80
81 #define CIRRUS_HIDDEN_DAC_ENABLE_ALL 0x40
82 #define CIRRUS_HIDDEN_DAC_8BPP 0
83 #define CIRRUS_HIDDEN_DAC_15BPP (CIRRUS_HIDDEN_DAC_ENABLE_EXT \
84 | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 0)
85 #define CIRRUS_HIDDEN_DAC_16BPP (CIRRUS_HIDDEN_DAC_ENABLE_EXT \
86 | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 1)
87 #define CIRRUS_HIDDEN_DAC_888COLOR (CIRRUS_HIDDEN_DAC_ENABLE_EXT \
88 | CIRRUS_HIDDEN_DAC_ENABLE_ALL | 5)
91 write_hidden_dac (grub_uint8_t data
)
93 grub_inb (GRUB_VGA_IO_PALLETTE_WRITE_INDEX
);
94 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
95 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
96 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
97 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
98 grub_outb (data
, GRUB_VGA_IO_PIXEL_MASK
);
102 read_hidden_dac (void)
104 grub_inb (GRUB_VGA_IO_PALLETTE_WRITE_INDEX
);
105 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
106 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
107 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
108 grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
109 return grub_inb (GRUB_VGA_IO_PIXEL_MASK
);
114 grub_uint8_t cr
[CIRRUS_CR_MAX
];
115 grub_uint8_t gr
[GRUB_VGA_GR_MAX
];
116 grub_uint8_t sr
[CIRRUS_SR_MAX
];
117 grub_uint8_t hidden_dac
;
118 /* We need to preserve VGA font and VGA text. */
119 grub_uint8_t vram
[32 * 4 * 256];
125 static struct saved_state initial_state
;
126 static int state_saved
= 0;
129 save_state (struct saved_state
*st
)
132 for (i
= 0; i
< ARRAY_SIZE (st
->cr
); i
++)
133 st
->cr
[i
] = grub_vga_cr_read (i
);
134 for (i
= 0; i
< ARRAY_SIZE (st
->sr
); i
++)
135 st
->sr
[i
] = grub_vga_sr_read (i
);
136 for (i
= 0; i
< ARRAY_SIZE (st
->gr
); i
++)
137 st
->gr
[i
] = grub_vga_gr_read (i
);
138 for (i
= 0; i
< 256; i
++)
139 grub_vga_palette_read (i
, st
->r
+ i
, st
->g
+ i
, st
->b
+ i
);
141 st
->hidden_dac
= read_hidden_dac ();
142 grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4
, GRUB_VGA_SR_MEMORY_MODE
);
143 grub_memcpy (st
->vram
, framebuffer
.ptr
, sizeof (st
->vram
));
147 restore_state (struct saved_state
*st
)
150 grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_CHAIN4
, GRUB_VGA_SR_MEMORY_MODE
);
151 grub_memcpy (framebuffer
.ptr
, st
->vram
, sizeof (st
->vram
));
152 for (i
= 0; i
< ARRAY_SIZE (st
->cr
); i
++)
153 grub_vga_cr_write (st
->cr
[i
], i
);
154 for (i
= 0; i
< ARRAY_SIZE (st
->sr
); i
++)
155 grub_vga_sr_write (st
->sr
[i
], i
);
156 for (i
= 0; i
< ARRAY_SIZE (st
->gr
); i
++)
157 grub_vga_gr_write (st
->gr
[i
], i
);
158 for (i
= 0; i
< 256; i
++)
159 grub_vga_palette_write (i
, st
->r
[i
], st
->g
[i
], st
->b
[i
]);
161 write_hidden_dac (st
->hidden_dac
);
165 grub_video_cirrus_video_init (void)
167 /* Reset frame buffer. */
168 grub_memset (&framebuffer
, 0, sizeof(framebuffer
));
170 return grub_video_fb_init ();
174 grub_video_cirrus_video_fini (void)
176 if (framebuffer
.mapped
)
177 grub_pci_device_unmap_range (framebuffer
.dev
, framebuffer
.ptr
,
178 CIRRUS_APERTURE_SIZE
);
182 restore_state (&initial_state
);
186 return grub_video_fb_fini ();
190 doublebuf_pageflipping_set_page (int page
)
192 int start
= framebuffer
.page_size
* page
/ 4;
193 grub_uint8_t cr_ext
, cr_overlay
;
195 grub_vga_cr_write (start
& 0xff, GRUB_VGA_CR_START_ADDR_LOW_REGISTER
);
196 grub_vga_cr_write ((start
& 0xff00) >> 8,
197 GRUB_VGA_CR_START_ADDR_HIGH_REGISTER
);
199 cr_ext
= grub_vga_cr_read (CIRRUS_CR_EXTENDED_DISPLAY
);
200 cr_ext
&= ~(CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1
201 | CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2
);
202 cr_ext
|= ((start
>> CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT1
)
203 & CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1
);
204 cr_ext
|= ((start
>> CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT2
)
205 & CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2
);
206 grub_vga_cr_write (cr_ext
, CIRRUS_CR_EXTENDED_DISPLAY
);
208 cr_overlay
= grub_vga_cr_read (CIRRUS_CR_EXTENDED_OVERLAY
);
209 cr_overlay
&= ~(CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK
);
210 cr_overlay
|= ((start
>> CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_SHIFT
)
211 & CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK
);
212 grub_vga_cr_write (cr_overlay
, CIRRUS_CR_EXTENDED_OVERLAY
);
214 return GRUB_ERR_NONE
;
218 grub_video_cirrus_set_palette (unsigned int start
, unsigned int count
,
219 struct grub_video_palette_data
*palette_data
)
221 if (framebuffer
.mode_info
.mode_type
== GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
)
225 return GRUB_ERR_NONE
;
226 if (start
+ count
>= 0x100)
227 count
= 0x100 - start
;
229 for (i
= 0; i
< count
; i
++)
230 grub_vga_palette_write (start
+ i
, palette_data
[i
].r
, palette_data
[i
].g
,
234 /* Then set color to emulated palette. */
235 return grub_video_fb_set_palette (start
, count
, palette_data
);
238 /* Helper for grub_video_cirrus_setup. */
240 find_card (grub_pci_device_t dev
, grub_pci_id_t pciid
, void *data
)
243 grub_pci_address_t addr
;
246 addr
= grub_pci_make_address (dev
, GRUB_PCI_REG_CLASS
);
247 class = grub_pci_read (addr
);
249 if (((class >> 16) & 0xffff) != 0x0300 || pciid
!= 0x00b81013)
252 addr
= grub_pci_make_address (dev
, GRUB_PCI_REG_ADDRESS_REG0
);
253 framebuffer
.base
= grub_pci_read (addr
) & GRUB_PCI_ADDR_MEM_MASK
;
254 if (!framebuffer
.base
)
259 /* Enable address spaces. */
260 addr
= grub_pci_make_address (framebuffer
.dev
, GRUB_PCI_REG_COMMAND
);
261 grub_pci_write (addr
, 0x7);
263 framebuffer
.dev
= dev
;
269 grub_video_cirrus_setup (unsigned int width
, unsigned int height
,
270 grub_video_mode_type_t mode_type
,
271 grub_video_mode_type_t mode_mask
)
276 int pitch
, bytes_per_pixel
;
278 /* Decode depth from mode_type. If it is zero, then autodetect. */
279 depth
= (mode_type
& GRUB_VIDEO_MODE_TYPE_DEPTH_MASK
)
280 >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS
;
282 if (width
== 0 || height
== 0)
288 if (width
& (GRUB_VGA_CR_WIDTH_DIVISOR
- 1))
289 return grub_error (GRUB_ERR_IO
,
290 "screen width must be a multiple of %d",
291 GRUB_VGA_CR_WIDTH_DIVISOR
);
293 if (width
> CIRRUS_MAX_WIDTH
)
294 return grub_error (GRUB_ERR_IO
,
295 "screen width must be at most %d", CIRRUS_MAX_WIDTH
);
297 if (height
> CIRRUS_MAX_HEIGHT
)
298 return grub_error (GRUB_ERR_IO
,
299 "screen height must be at most %d", CIRRUS_MAX_HEIGHT
);
302 && !grub_video_check_mode_flag (mode_type
, mode_mask
,
303 GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
, 0))
308 if (depth
!= 32 && depth
!= 24 && depth
!= 16 && depth
!= 15 && depth
!= 8)
309 return grub_error (GRUB_ERR_IO
, "only 32, 24, 16, 15 and 8-bit bpp are"
310 " supported by cirrus video");
312 bytes_per_pixel
= (depth
+ GRUB_CHAR_BIT
- 1) / GRUB_CHAR_BIT
;
313 pitch
= width
* bytes_per_pixel
;
315 if (pitch
> CIRRUS_MAX_PITCH
)
316 return grub_error (GRUB_ERR_IO
,
317 "screen width must be at most %d at bitdepth %d",
318 CIRRUS_MAX_PITCH
/ bytes_per_pixel
, depth
);
320 framebuffer
.page_size
= pitch
* height
;
322 if (framebuffer
.page_size
> CIRRUS_APERTURE_SIZE
)
323 return grub_error (GRUB_ERR_IO
, "Not enough video memory for this mode");
325 grub_pci_iterate (find_card
, &found
);
327 return grub_error (GRUB_ERR_IO
, "Couldn't find graphics card");
329 if (found
&& framebuffer
.base
== 0)
331 /* FIXME: change framebuffer base */
332 return grub_error (GRUB_ERR_IO
, "PCI BAR not set");
335 /* We can safely discard volatile attribute. */
336 framebuffer
.ptr
= (void *) grub_pci_device_map_range (framebuffer
.dev
,
338 CIRRUS_APERTURE_SIZE
);
339 framebuffer
.mapped
= 1;
343 save_state (&initial_state
);
348 struct grub_video_hw_config config
= {
349 .pitch
= pitch
/ GRUB_VGA_CR_PITCH_DIVISOR
,
350 .line_compare
= 0x3ff,
351 .vdisplay_end
= height
- 1,
352 .horizontal_end
= width
/ GRUB_VGA_CR_WIDTH_DIVISOR
354 grub_uint8_t sr_ext
= 0, hidden_dac
= 0;
356 grub_vga_set_geometry (&config
, grub_vga_cr_write
);
358 grub_vga_gr_write (GRUB_VGA_GR_MODE_256_COLOR
| GRUB_VGA_GR_MODE_READ_MODE1
,
360 grub_vga_gr_write (GRUB_VGA_GR_GR6_GRAPHICS_MODE
, GRUB_VGA_GR_GR6
);
362 grub_vga_sr_write (GRUB_VGA_SR_MEMORY_MODE_NORMAL
, GRUB_VGA_SR_MEMORY_MODE
);
364 grub_vga_cr_write ((config
.pitch
>> CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT
)
365 & CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK
,
366 CIRRUS_CR_EXTENDED_DISPLAY
);
368 grub_vga_cr_write (GRUB_VGA_CR_MODE_TIMING_ENABLE
369 | GRUB_VGA_CR_MODE_BYTE_MODE
370 | GRUB_VGA_CR_MODE_NO_HERCULES
| GRUB_VGA_CR_MODE_NO_CGA
,
373 doublebuf_pageflipping_set_page (0);
375 sr_ext
= CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE
376 | CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT
;
379 /* FIXME: support 8-bit grayscale and 8-bit RGB. */
381 hidden_dac
= CIRRUS_HIDDEN_DAC_888COLOR
;
382 sr_ext
|= CIRRUS_SR_EXTENDED_MODE_32BPP
;
385 hidden_dac
= CIRRUS_HIDDEN_DAC_888COLOR
;
386 sr_ext
|= CIRRUS_SR_EXTENDED_MODE_24BPP
;
389 hidden_dac
= CIRRUS_HIDDEN_DAC_16BPP
;
390 sr_ext
|= CIRRUS_SR_EXTENDED_MODE_16BPP
;
393 hidden_dac
= CIRRUS_HIDDEN_DAC_15BPP
;
394 sr_ext
|= CIRRUS_SR_EXTENDED_MODE_16BPP
;
397 hidden_dac
= CIRRUS_HIDDEN_DAC_8BPP
;
398 sr_ext
|= CIRRUS_SR_EXTENDED_MODE_8BPP
;
401 grub_vga_sr_write (sr_ext
, CIRRUS_SR_EXTENDED_MODE
);
402 write_hidden_dac (hidden_dac
);
405 /* Fill mode info details. */
406 framebuffer
.mode_info
.width
= width
;
407 framebuffer
.mode_info
.height
= height
;
408 framebuffer
.mode_info
.mode_type
= GRUB_VIDEO_MODE_TYPE_RGB
;
409 framebuffer
.mode_info
.bpp
= depth
;
410 framebuffer
.mode_info
.bytes_per_pixel
= bytes_per_pixel
;
411 framebuffer
.mode_info
.pitch
= pitch
;
412 framebuffer
.mode_info
.number_of_colors
= 256;
413 framebuffer
.mode_info
.reserved_mask_size
= 0;
414 framebuffer
.mode_info
.reserved_field_pos
= 0;
419 framebuffer
.mode_info
.mode_type
= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR
;
420 framebuffer
.mode_info
.number_of_colors
= 16;
423 framebuffer
.mode_info
.red_mask_size
= 5;
424 framebuffer
.mode_info
.red_field_pos
= 11;
425 framebuffer
.mode_info
.green_mask_size
= 6;
426 framebuffer
.mode_info
.green_field_pos
= 5;
427 framebuffer
.mode_info
.blue_mask_size
= 5;
428 framebuffer
.mode_info
.blue_field_pos
= 0;
432 framebuffer
.mode_info
.red_mask_size
= 5;
433 framebuffer
.mode_info
.red_field_pos
= 10;
434 framebuffer
.mode_info
.green_mask_size
= 5;
435 framebuffer
.mode_info
.green_field_pos
= 5;
436 framebuffer
.mode_info
.blue_mask_size
= 5;
437 framebuffer
.mode_info
.blue_field_pos
= 0;
441 framebuffer
.mode_info
.reserved_mask_size
= 8;
442 framebuffer
.mode_info
.reserved_field_pos
= 24;
446 framebuffer
.mode_info
.red_mask_size
= 8;
447 framebuffer
.mode_info
.red_field_pos
= 16;
448 framebuffer
.mode_info
.green_mask_size
= 8;
449 framebuffer
.mode_info
.green_field_pos
= 8;
450 framebuffer
.mode_info
.blue_mask_size
= 8;
451 framebuffer
.mode_info
.blue_field_pos
= 0;
455 framebuffer
.mode_info
.blit_format
= grub_video_get_blit_format (&framebuffer
.mode_info
);
457 if (CIRRUS_APERTURE_SIZE
>= 2 * framebuffer
.page_size
)
458 err
= grub_video_fb_setup (mode_type
, mode_mask
,
459 &framebuffer
.mode_info
,
461 doublebuf_pageflipping_set_page
,
462 framebuffer
.ptr
+ framebuffer
.page_size
);
464 err
= grub_video_fb_setup (mode_type
, mode_mask
,
465 &framebuffer
.mode_info
,
466 framebuffer
.ptr
, 0, 0);
469 /* Copy default palette to initialize emulated palette. */
470 err
= grub_video_cirrus_set_palette (0, GRUB_VIDEO_FBSTD_NUMCOLORS
,
471 grub_video_fbstd_colors
);
475 static struct grub_video_adapter grub_video_cirrus_adapter
=
477 .name
= "Cirrus CLGD 5446 PCI Video Driver",
478 .id
= GRUB_VIDEO_DRIVER_CIRRUS
,
480 .prio
= GRUB_VIDEO_ADAPTER_PRIO_NATIVE
,
482 .init
= grub_video_cirrus_video_init
,
483 .fini
= grub_video_cirrus_video_fini
,
484 .setup
= grub_video_cirrus_setup
,
485 .get_info
= grub_video_fb_get_info
,
486 .get_info_and_fini
= grub_video_fb_get_info_and_fini
,
487 .set_palette
= grub_video_cirrus_set_palette
,
488 .get_palette
= grub_video_fb_get_palette
,
489 .set_viewport
= grub_video_fb_set_viewport
,
490 .get_viewport
= grub_video_fb_get_viewport
,
491 .set_region
= grub_video_fb_set_region
,
492 .get_region
= grub_video_fb_get_region
,
493 .set_area_status
= grub_video_fb_set_area_status
,
494 .get_area_status
= grub_video_fb_get_area_status
,
495 .map_color
= grub_video_fb_map_color
,
496 .map_rgb
= grub_video_fb_map_rgb
,
497 .map_rgba
= grub_video_fb_map_rgba
,
498 .unmap_color
= grub_video_fb_unmap_color
,
499 .fill_rect
= grub_video_fb_fill_rect
,
500 .blit_bitmap
= grub_video_fb_blit_bitmap
,
501 .blit_render_target
= grub_video_fb_blit_render_target
,
502 .scroll
= grub_video_fb_scroll
,
503 .swap_buffers
= grub_video_fb_swap_buffers
,
504 .create_render_target
= grub_video_fb_create_render_target
,
505 .delete_render_target
= grub_video_fb_delete_render_target
,
506 .set_active_render_target
= grub_video_fb_set_active_render_target
,
507 .get_active_render_target
= grub_video_fb_get_active_render_target
,
512 GRUB_MOD_INIT(video_cirrus
)
514 grub_video_register (&grub_video_cirrus_adapter
);
517 GRUB_MOD_FINI(video_cirrus
)
519 grub_video_unregister (&grub_video_cirrus_adapter
);