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