add 16-bits/thousands of colors

This commit is contained in:
Romain Dolbeau 2022-04-22 22:00:25 +01:00
parent 44fa491540
commit f867f02c83
8 changed files with 114 additions and 34 deletions

View File

@ -6,4 +6,4 @@ The goal of this repository is to be able to interface a modern (2021 era) [FPGA
## Current status
First prototype is working, implements a basic single-resolution, 8-bits only framebuffer over HDMI or VGA.
First prototype is working in a Quadra 650. It implements a basic single-resolution, depth-switchable (1/2/4/8/16/32 bits) unaccellerated framebuffer over HDMI. The framebuffer can be used as secondary/primary/only framebuffer in the machine running OS8.1. QEmu tests indicate this should work with 7.1 & 7.5/7.6 as well.

View File

@ -9,6 +9,7 @@ Pages8s = 1
Pages4s = 1
Pages2s = 1
Pages1s = 1
Pages15s = 1
Pages24s = 1
defmBounds_Ls = 0
@ -23,6 +24,7 @@ RB8s = HRES
RB4s = HRES/2
RB2s = HRES/4
RB1s = HRES/8
RB15s = HRES*2
RB24s = HRES*4
DrHwNuBusFPGA = 0xBEEF /* placeholder */

View File

@ -42,6 +42,7 @@
#define GOBOFB_MODE_4BIT 0x2
#define GOBOFB_MODE_8BIT 0x3
#define GOBOFB_MODE_24BIT 0x10
#define GOBOFB_MODE_15BIT 0x11
struct MyGammaTbl {
short gVersion; /*gamma version number*/

View File

@ -164,6 +164,12 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_24BIT);
SwapMMUMode ( &busMode );
break;
case sixthVidMode:
dStore->curMode = sixthVidMode;
SwapMMUMode ( &busMode );
write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_15BIT);
SwapMMUMode ( &busMode );
break;
default:
return paramErr;
}
@ -274,7 +280,7 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
return paramErr;
SwapMMUMode ( &busMode );
if (dStore->curMode != kDepthMode5) {
if ((dStore->curMode != kDepthMode5) && (dStore->curMode != kDepthMode6)) {
/* grey the screen */
a32_l0 = a32;
a32_l1 = a32 + wb;
@ -346,6 +352,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
break;
case fifthVidMode:
break;
case sixthVidMode:
break;
default:
return paramErr;
}
@ -384,6 +392,11 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_24BIT);
SwapMMUMode ( &busMode );
break;
case kDepthMode6:
SwapMMUMode ( &busMode );
write_reg(dce, GOBOFB_MODE, GOBOFB_MODE_15BIT);
SwapMMUMode ( &busMode );
break;
default:
return paramErr;
}
@ -408,6 +421,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
break;
case fifthVidMode:
break;
case sixthVidMode:
break;
default:
return paramErr;
}
@ -422,6 +437,8 @@ OSErr cNuBusFPGACtl(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
break;
case fifthVidMode:
break;
case sixthVidMode:
break;
default:
return paramErr;
}

View File

@ -81,6 +81,8 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
break;
case fifthVidMode:
break;
case sixthVidMode:
break;
default:
return paramErr;
}
@ -206,11 +208,11 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
break;
case kDisplayModeIDFindFirstResolution:
vdres->csDisplayModeID = firstVidMode;
vdres->csMaxDepthMode = kDepthMode5;
vdres->csMaxDepthMode = kDepthMode6;
break;
case kDisplayModeIDCurrent:
vdres->csDisplayModeID = firstVidMode;
vdres->csMaxDepthMode = kDepthMode5;
vdres->csMaxDepthMode = kDepthMode6;
break;
default:
return paramErr;
@ -230,7 +232,8 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
(vdparam->csDepthMode != kDepthMode2) &&
(vdparam->csDepthMode != kDepthMode3) &&
(vdparam->csDepthMode != kDepthMode4) &&
(vdparam->csDepthMode != kDepthMode5))
(vdparam->csDepthMode != kDepthMode5) &&
(vdparam->csDepthMode != kDepthMode6))
return paramErr;
VPBlock* vpblock = vdparam->csVPBlockPtr;
/* basically the same as the EBVParms ? */
@ -276,6 +279,12 @@ OSErr cNuBusFPGAStatus(CntrlParamPtr pb, /* DCtlPtr */ AuxDCEPtr dce)
vpblock->vpPixelSize = 32;
vpblock->vpCmpCount = 3;
vpblock->vpCmpSize = 8;
} else if (vdparam->csDepthMode == kDepthMode6) {
vpblock->vpRowBytes = HRES*2;
vdparam->csDeviceType = directType;
vpblock->vpPixelSize = 16;
vpblock->vpCmpCount = 3;
vpblock->vpCmpSize = 5;
}
vpblock->vpPlaneBytes = 0;
ret = noErr;

View File

@ -99,7 +99,8 @@ _sRsrc_VidHiRes:
OSLstEntry secondVidMode,_HiRes4Modes /* offset to 4 Bit Mode parms */
OSLstEntry thirdVidMode,_HiRes2Modes /* offset to 2 Bit Mode parms */
OSLstEntry fourthVidMode,_HiRes1Modes /* offset to 1 Bit Mode parms */
OSLstEntry fifthVidMode,_HiRes24Modes /* offset to 24 Bit Mode parms */
OSLstEntry fifthVidMode,_HiRes24Modes /* offset to 24/32 Bit Mode parms */
OSLstEntry sixthVidMode,_HiRes15Modes /* offset to 1516 Bit Mode parms */
.long EndOfList /* end of list */
ALIGN 2
@ -271,4 +272,26 @@ _HRV24Parms:
.word 8 /* bmCmpSize */
.long defmPlaneBytes /* bmPlaneBytes */
_EndHRV24Parms:
ALIGN 2
_HiRes15Modes:
OSLstEntry mVidParams,_HRV15Parms /* offset to vid parameters */
DatLstEntry mPageCnt,Pages15s /* number of video pages */
DatLstEntry mDevType,directType /* device type */
.long EndOfList /* end of list */
_HRV15Parms:
.long _EndHRV15Parms-_HRV15Parms /* physical block size */
.long defmBaseOffset /* QuickDraw base offset ; vpBaseOffset */
.word RB15s /* physRowBytes ; vpRowBytes */
.word defmBounds_Ts,defmBounds_Ls,defmBounds_Bs,defmBounds_Rs /* vpBounds */
.word defVersion /* bmVersion ; vpVersion */
.word 0 /* packType not used ; vpPackType */
.long 0 /* packSize not used ; vpPackSize */
.long defmHRes /* bmHRes */
.long defmVRes /* bmVRes */
.word ChunkyDirect /* bmPixelType */
.word 16 /* bmPixelSize */
.word 3 /* bmCmpCount */
.word 5 /* bmCmpSize */
.long defmPlaneBytes /* bmPlaneBytes */
_EndHRV15Parms:

View File

@ -158,23 +158,45 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR):
# we have 5 possible conversion and mux/connect the appropriate one
if (truecolor):
self.submodules.conv32 = ClockDomainsRenamer({"sys": clock_domain})(stream.Converter(dram_port.data_width, 32))
handle_truecolor_sink = [ self.cdc.source.connect(self.conv32.sink) ]
handle_truecolor_source = [ source_buf_valid.eq(self.conv32.source.valid),
self.conv32.source.connect(source, keep={"ready"}), ]
self.submodules.conv16 = ClockDomainsRenamer({"sys": clock_domain})(stream.Converter(dram_port.data_width, 16))
handle_truecolor_sink = [ Case(self.indexed_mode, {
0x0: [ self.cdc.source.connect(self.conv32.sink) ],
0x1: [ self.cdc.source.connect(self.conv16.sink) ],
})]
handle_truecolor_source = [ Case(self.indexed_mode, {
0x0: [ source_buf_valid.eq(self.conv32.source.valid), self.conv32.source.connect(source, keep={"ready"}), ],
0x1: [ source_buf_valid.eq(self.conv16.source.valid), self.conv16.source.connect(source, keep={"ready"}), ],
})]
if (endian == "big"): # this starts to _really_ mean "i'm in the SBusFPGA"...
handle_truecolor_databuf = [ data_buf_direct[0].eq(self.conv32.source.data[24:32]),
data_buf_direct[1].eq(self.conv32.source.data[16:24]),
data_buf_direct[2].eq(self.conv32.source.data[8:16]), ]
handle_truecolor_databuf = [ Case(self.indexed_mode, {
0x0: [ data_buf_direct[0].eq(self.conv32.source.data[24:32]),
data_buf_direct[1].eq(self.conv32.source.data[16:24]),
data_buf_direct[2].eq(self.conv32.source.data[8:16]), ],
0x1: [ data_buf_direct[0].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[11:16])), # fixme: 16-bits in X11 ???
data_buf_direct[1].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[6:11])),
data_buf_direct[2].eq(Cat(Signal(3, reset = 0), self.conv16.source.data[1:6])), ]
})]
else:
handle_truecolor_databuf = [ data_buf_direct[2].eq(self.conv32.source.data[24:32]),
data_buf_direct[1].eq(self.conv32.source.data[16:24]),
data_buf_direct[0].eq(self.conv32.source.data[8:16]), ]
handle_truecolor_databuf =[ Case(self.indexed_mode, {
0x0: [ data_buf_direct[2].eq(self.conv32.source.data[24:32]),
data_buf_direct[1].eq(self.conv32.source.data[16:24]),
data_buf_direct[0].eq(self.conv32.source.data[8:16]), ],
0x1: [ data_buf_direct[0].eq(Cat(self.conv16.source.data[ 4: 7], self.conv16.source.data[ 2: 7])), # 16-bits in QD32
data_buf_direct[1].eq(Cat(self.conv16.source.data[15:16], self.conv16.source.data[ 0: 2], # seems byte-swapped in 5551 BGRx
self.conv16.source.data[13:16], self.conv16.source.data[ 0: 2])),
data_buf_direct[2].eq(Cat(self.conv16.source.data[10:13], self.conv16.source.data[ 8:13])), ]
})]
handle_truecolor_databuf_b = [ data_buf_b_direct[0].eq(data_buf_direct[0]),
data_buf_b_direct[1].eq(data_buf_direct[1]),
data_buf_b_direct[2].eq(data_buf_direct[2]), ]
handle_truecolor_final_source = [ source_out_r.eq(data_buf_b_direct[2]),
source_out_g.eq(data_buf_b_direct[1]),
source_out_b.eq(data_buf_b_direct[0]), ]
else:
handle_truecolor_sink = [ ]
handle_truecolor_source = [ ]
@ -356,7 +378,7 @@ class VideoFrameBufferMultiDepth(Module, AutoCSR):
self.comb += self.vblping.eq(self.vbl_ps.o)
class goblin(Module, AutoCSR):
def __init__(self, soc=None, phy=None, timings=None, clock_domain="sys", irq_line=None, endian="big", truecolor=True):
def __init__(self, soc=None, phy=None, timings=None, clock_domain="sys", irq_line=None, endian="big", hwcursor=True, truecolor=True):
# 2 bits for color (0/r, 1/g, 2/b), 8 for @ and 8 for value
self.submodules.upd_cmap_fifo = upd_cmap_fifo = ClockDomainsRenamer({"read": clock_domain, "write": "sys"})(AsyncFIFOBuffered(width=layout_len(cmap_layout), depth=8))
@ -372,7 +394,7 @@ class goblin(Module, AutoCSR):
name = "video_framebuffer"
# near duplicate of plaform.add_video_framebuffer
# Video Timing Generator.
vtg = FBVideoTimingGenerator(default_video_timings=timings if isinstance(timings, str) else timings[1], hwcursor=True)
vtg = FBVideoTimingGenerator(default_video_timings=timings if isinstance(timings, str) else timings[1], hwcursor=hwcursor)
vtg = ClockDomainsRenamer(clock_domain)(vtg)
setattr(self.submodules, f"{name}_vtg", vtg)
vtg_enable = Signal(reset = 0)
@ -417,12 +439,17 @@ class goblin(Module, AutoCSR):
soc.add_constant("VIDEO_FRAMEBUFFER_VRES", vres)
# HW Cursor
hwcursor_x = Signal(12)
hwcursor_y = Signal(12)
self.comb += vtg.hwcursor_x.eq(hwcursor_x)
self.comb += vtg.hwcursor_y.eq(hwcursor_y)
if (hwcursor):
hwcursor_x = Signal(12)
hwcursor_y = Signal(12)
self.comb += vtg.hwcursor_x.eq(hwcursor_x)
self.comb += vtg.hwcursor_y.eq(hwcursor_y)
handle_hwcursor = [ NextValue(hwcursor_x, bus.dat_w[16:28]), # FIXME: endianess
NextValue(hwcursor_y, bus.dat_w[ 0:12]), # FIXME: endianess
]
else:
handle_hwcursor = [ ]
self.bus = bus = wishbone.Interface()
@ -493,12 +520,10 @@ class goblin(Module, AutoCSR):
}),
],
# hw cursor x/y
0x9: [ NextValue(hwcursor_x, bus.dat_w[16:28]), # FIXME: endianess
NextValue(hwcursor_y, bus.dat_w[ 0:12]), # FIXME: endianess
],
0x9: [ *handle_hwcursor ],
}),
Case(bus.adr[5:18], {
"default": [],
"default": [], # fixme: hwcursor for 0x1/0x2
0x1 : [ upd_overlay_fifo.we.eq(1), # 1*32 = 32..63 / 0x20..0x3F
upd_overlay_fifo.din.eq(Cat(Signal(1, reset = 0), 31-bus.adr[0:5], bus.dat_w)) # FIXME: endianess
],
@ -540,7 +565,7 @@ class goblin(Module, AutoCSR):
),
If(in_reset & ~vtg_enable, # we asked for a reset and by now, the VTG has been turned off (or was off) so we reset the DMA and change the parameters
##dma_reset.eq(1), # hpefully this will clear the FIFO as well
self.video_framebuffer.indexed_mode.eq(bt_mode[0:2] & ~(Replicate(bt_mode[4:5], 2))),
self.video_framebuffer.indexed_mode.eq(bt_mode[0:2]), # & ~(Replicate(bt_mode[4:5], 2))
*handle_truecolor_bit,
in_reset.eq(0),
post_reset_ctr.eq(7),
@ -550,7 +575,10 @@ class goblin(Module, AutoCSR):
##),
If(post_reset_ctr == 4, # now reconfigure the DMA
If(bt_mode[4:5],
self.video_framebuffer.fb_dma.length.eq(npixels * 4),
Case(bt_mode[0:2], { # fixme: truecolor
0x0: self.video_framebuffer.fb_dma.length.eq(npixels * 4),
0x1: self.video_framebuffer.fb_dma.length.eq(npixels * 2),
}),
).Else(
Case(bt_mode[0:2], {
3: self.video_framebuffer.fb_dma.length.eq(npixels ),

View File

@ -355,15 +355,15 @@ class NuBusFPGA(SoCCore):
if (goblin):
if (not hdmi):
self.submodules.videophy = VideoVGAPHY(platform.request("vga"), clock_domain="vga")
self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="vga", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", truecolor=True) # clock_domain for the VGA side, goblin is running in cd_sys
self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="vga", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", hwcursor=False, truecolor=True) # clock_domain for the VGA side, goblin is running in cd_sys
else:
self.submodules.videophy = VideoS7HDMIPHY(platform.request("hdmi"), clock_domain="hdmi")
self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="hdmi", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", truecolor=True) # clock_domain for the HDMI side, goblin is running in cd_sys
self.submodules.goblin = goblin_fb.goblin(soc=self, phy=self.videophy, timings=goblin_res, clock_domain="hdmi", irq_line=self.platform.request("nmrq_3v3_n"), endian="little", hwcursor=False, truecolor=True) # clock_domain for the HDMI side, goblin is running in cd_sys
self.bus.add_slave("goblin_bt", self.goblin.bus, SoCRegion(origin=self.mem_map.get("goblin_bt", None), size=0x1000, cached=False))
pad_user_led_0 = platform.request("user_led", 0)
pad_user_led_1 = platform.request("user_led", 1)
self.comb += pad_user_led_0.eq(self.goblin.video_framebuffer.underflow)
self.comb += pad_user_led_1.eq(self.goblin.video_framebuffer.fb_dma.enable)
#pad_user_led_0 = platform.request("user_led", 0)
#pad_user_led_1 = platform.request("user_led", 1)
#self.comb += pad_user_led_0.eq(self.goblin.video_framebuffer.underflow)
#self.comb += pad_user_led_1.eq(self.goblin.video_framebuffer.fb_dma.enable)
# for testing
#from nubus_master_tst import PingMaster