08bc54ba29ee039614f3255e286fe0d329789521
[lunaix-os.git] / lunaix-os / hal / gfxa / vga / vga.c
1 /**
2  * @file ga_vtm.c
3  * @author Lunaixsky (lunaxisky@qq.com)
4  * @brief video graphic adapter - text mode with simpiled ansi color code
5  * @version 0.1
6  * @date 2023-10-06
7  *
8  * @copyright Copyright (c) 2023
9  *
10  */
11
12 #include <lunaix/compiler.h>
13 #include <lunaix/device.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/spike.h>
16
17 #include <sys/cpu.h>
18
19 #include "vga.h"
20
21 #define ALL_FIELDS -1
22
23 struct vga*
24 vga_new_state(ptr_t reg_base, ptr_t fb, ptr_t alloced_fbsz)
25 {
26     struct vga* state = valloc(sizeof(struct vga));
27     *state =
28       (struct vga){ .fb = fb, .fb_sz = alloced_fbsz, .io_off = reg_base };
29
30     return state;
31 }
32
33 void
34 vga_config_rect(struct vga* state,
35                 size_t width,
36                 size_t height,
37                 size_t freq,
38                 int d9)
39 {
40     size_t pel_sz = 8 + !!d9;
41
42     state->crt.h_cclk = width / pel_sz;
43     state->crt.v_cclk = height;
44     state->crt.freq = freq;
45
46     state->crt.pel_dot = pel_sz;
47 }
48
49 #define RETRACE_GAP 4
50
51 static void
52 vga_crt_update(struct vga* state)
53 {
54 #define R_BIT9(x, l) ((((x) & (1 << 9)) >> 9) << (l))
55 #define R_BIT8(x, l) ((((x) & (1 << 8)) >> 8) << (l))
56     struct vga_regops* reg = &state->reg_ops;
57
58     u32_t hdisp = state->crt.h_cclk;
59     u32_t vdisp = state->crt.v_cclk;
60     u32_t hdisp_end = hdisp - 1;
61     u32_t vdisp_end = vdisp - 1;
62
63     // VGA spec said so
64     u32_t htotal = hdisp - 5;
65     u32_t vtotal = vdisp - 5;
66
67     u32_t hsync_end = (hdisp + RETRACE_GAP) & 0b11111;
68     u32_t vsync_end = (vdisp + RETRACE_GAP) & 0b01111;
69
70     reg->write(state, VGA_CRX, VGA_CR11, 0, 1 << 7);
71     reg->write(state, VGA_CRX, VGA_CR03, 1 << 7, 1 << 7);
72
73     // set values of first 8 registers. Note, we prefer no H_BLANK region
74     u32_t crt_regs[VGA_CRCOUNT] = {
75         [VGA_CR00] = htotal, [VGA_CR01] = hdisp - 1,
76         [VGA_CR02] = 0xff,   [VGA_CR03] = (1 << 7) | 31,
77         [VGA_CR04] = hdisp,  [VGA_CR05] = hsync_end,
78         [VGA_CR06] = vtotal
79     };
80
81     // overflow: bit 9/8 of vtotal
82     crt_regs[VGA_CR07] = R_BIT9(vtotal, 5) | R_BIT8(vtotal, 0);
83     // overflow: bit 9/8 of vdisp_end
84     crt_regs[VGA_CR07] |= R_BIT9(vdisp_end, 6) | R_BIT8(vdisp_end, 1);
85     // overflow: bit 9/8 of vsync_start
86     crt_regs[VGA_CR07] |= R_BIT9(vdisp, 7) | R_BIT8(vdisp, 2);
87
88     crt_regs[VGA_CR10] = vdisp; // vsync_start
89
90     // V_SYNC_END, Disable Vertical INTR (GD5446)
91     crt_regs[VGA_CR11] = vsync_end | (1 << 5);
92     crt_regs[VGA_CR12] = vdisp_end; // vdisp_end
93
94     // no V_BLANK region (v blank start/end = 0x3ff)
95     crt_regs[VGA_CR15] = 0xff;
96     crt_regs[VGA_CR16] = 0xff;
97     crt_regs[VGA_CR09] |= (1 << 5);
98     crt_regs[VGA_CR07] |= (1 << 3);
99
100     // no split line (0x3FF)
101     crt_regs[VGA_CR18] = 0xff;
102     crt_regs[VGA_CR07] |= (1 << 4);
103     crt_regs[VGA_CR09] |= (1 << 6);
104
105     // disable text cursor by default
106     crt_regs[VGA_CR0A] = (1 << 5);
107     // CRB: text cusor end, 0x00
108     // CRE-CRF: text cusor location, cleared, 0x00
109
110     // CRC-CRD: screen start address(lo/hi): 0x00, 0x00
111     //  i.e., screen content start at the beginning of framebuffer
112
113     if (!(state->options & VGA_MODE_GFX)) {
114         // AN Mode, each char heights 16 scan lines
115         crt_regs[VGA_CR09] |= 0xf;
116     } else {
117         // GFX Mode, individual scanline, no repeat
118     }
119
120     // framebuffer: 16bit granule
121     u32_t divsor = 2 * 2;
122     if (state->lut.len == 256) {
123         // if 8 bit color is used, 2 pixels per granule
124         divsor *= 2;
125     } else if ((state->options & VGA_MODE_GFX)) {
126         // otherwise (gfx mode), 4 pixels per granule
127         divsor *= 4;
128     } // AN mode, one granule per character
129     crt_regs[VGA_CR13] = (hdisp * state->crt.pel_dot) / divsor;
130
131     // Timing En, Word Mode, Address Warp,
132     crt_regs[VGA_CR17] = (0b10100011);
133
134     reg->set_seq(state, VGA_CRX, 0, crt_regs, VGA_CRCOUNT);
135 }
136
137 void
138 vga_update_palette(struct vga* state)
139 {
140     state->reg_ops.set_dac_palette(state);
141
142     u32_t index[16];
143     u32_t clr_len = MIN(state->lut.len, 16);
144     for (size_t i = 0; i < clr_len; i++) {
145         index[i] = i;
146     }
147
148     state->reg_ops.set_seq(state, VGA_ARX, 0, index, clr_len);
149 }
150
151 void
152 vga_reload_config(struct vga* state)
153 {
154     cpu_disable_interrupt();
155
156     struct vga_regops* reg = &state->reg_ops;
157
158     u32_t color = state->lut.len;
159     assert_msg((color == 2 || color == 4 || color == 16 || color == 256),
160                "invalid color depth");
161
162     if (!(state->options & VGA_MODE_GFX)) {
163         assert(color < 256);
164     }
165
166     // estimate actual fb size
167     u32_t dpc_sq = state->crt.pel_dot;
168     u32_t total_px = state->crt.h_cclk * state->crt.v_cclk;
169     if ((state->options & VGA_MODE_GFX)) {
170         total_px *= dpc_sq;
171         total_px = total_px / (1 + (color < 256));
172     } else {
173         total_px = total_px * 2;
174     }
175
176     assert(state->fb_sz >= total_px);
177     state->fb_sz = total_px;
178
179     reg->write(state, VGA_SRX, VGA_SR00, 0x0, ALL_FIELDS);
180
181     // RAM Enable, I/OAS
182     u32_t clk_f = state->crt.h_cclk * state->crt.v_cclk * state->crt.freq;
183     u32_t misc = 0b11;
184     clk_f = (clk_f * dpc_sq) / 1000000;
185
186     assert(clk_f && clk_f <= 28);
187
188     // require 28 MHz clock
189     if (clk_f > 25) {
190         misc |= 0b01 << 2;
191     }
192     // 25 MHz clock: 0b00 << 2
193
194     reg->write(state, VGA_MISCX, 0, misc, 0b1111);
195
196     // SEQN_CLK: shift every CCLK, DCLK passthrough, 8/9 DCLK
197     u32_t d89 = (state->crt.pel_dot != 9);
198     reg->write(state, VGA_SRX, VGA_SR01, d89, ALL_FIELDS);
199
200     // GR0-4
201     u32_t gr_regs[] = { 0, 0, 0, 0, 0 };
202     reg->set_seq(state, VGA_GRX, 0, gr_regs, 5);
203
204     // Setup CRT
205     vga_crt_update(state);
206
207     if ((state->options & VGA_MODE_GFX)) { // GFX MODE
208         // GFX mode, check if 8 bit color depth is required
209         u32_t c256 = (color == 256);
210         reg->write(state, VGA_ARX, VGA_AR10, 1 | (c256 << 6), ALL_FIELDS);
211
212         // All 4 maps enabled, chain 4 mode
213         reg->write(state, VGA_SRX, VGA_SR02, 0b1111, ALL_FIELDS);
214
215         // Chain-4, Odd/Even, Extended Memory
216         reg->write(state, VGA_SRX, VGA_SR04, 0b1010, ALL_FIELDS);
217
218         // check 256 color mode, RdM0, WrM0
219         reg->write(
220           state, VGA_GRX, VGA_GR05, (c256 << 6) | (1 << 4), ALL_FIELDS);
221
222         // Legacy GFX FB: 0xA000, GFX mode
223         reg->write(state, VGA_GRX, VGA_GR06, 0b0011, ALL_FIELDS);
224
225     } else { // AN MOODE
226         // Only map 0,1 enabled, (ascii and attribute)
227         reg->write(state, VGA_SRX, VGA_SR02, 0b0011, ALL_FIELDS);
228
229         // Character (AN) mode
230         reg->write(state, VGA_ARX, VGA_AR10, 0, ALL_FIELDS);
231
232         // Extended Memory
233         reg->write(state, VGA_SRX, VGA_SR04, 0b0010, ALL_FIELDS);
234
235         // Shift Reg, RdM0, WrM0
236         reg->write(state, VGA_GRX, VGA_GR05, (1 << 5), ALL_FIELDS);
237
238         // Legacy CGA FB: 0xB800, AN mode
239         reg->write(state, VGA_GRX, VGA_GR06, 0b1100, 0b1111);
240     }
241
242     // all planes not involved in color compare
243     reg->write(state, VGA_GRX, VGA_GR07, 0, ALL_FIELDS);
244     // No rotate, no bit ops with latched px
245     reg->write(state, VGA_GRX, VGA_GR03, 0, ALL_FIELDS);
246
247     // Load default bit mask
248     reg->write(state, VGA_GRX, VGA_GR08, 0xff, ALL_FIELDS);
249
250     reg->write(state, VGA_ARX, VGA_AR12, 0xf, ALL_FIELDS);
251     reg->write(state, VGA_ARX, VGA_AR11, 0b100, ALL_FIELDS);
252     reg->write(state, VGA_ARX, VGA_AR13, 0, ALL_FIELDS);
253     reg->write(state, VGA_ARX, VGA_AR14, 0, ALL_FIELDS);
254
255     vga_update_palette(state);
256
257     reg->write(state, VGA_SRX, VGA_SR00, 0x3, ALL_FIELDS);
258
259     cpu_enable_interrupt();
260 }