Move the shared drm ioctl abi into internal/drm
This commit is contained in:
@@ -8,116 +8,21 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"g.fc.run/theater/cabletest/internal/drm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Drawing lands in memory and is blitted to a buffer the display is not
|
// Drawing lands in memory and is blitted to a buffer the display is not
|
||||||
// reading, then swapped in whole at a vertical blank. Writing into the live
|
// reading, then swapped in whole at a vertical blank. Writing into the live
|
||||||
// scanout buffer instead, as fbdev invites, races the beam: the blit takes a
|
// scanout buffer instead, as fbdev invites, races the beam: the blit takes a
|
||||||
// few hundred microseconds and the display reads half of each frame.
|
// few hundred microseconds and the display reads half of each frame.
|
||||||
const (
|
//
|
||||||
drmIoctlBase = 0x64
|
// Two buffers would be enough to stop the panel tearing. More than two is for
|
||||||
|
// anything reading a frame back out: they are cycled in order, so a buffer is
|
||||||
drmModeConnected = 1
|
// left alone for the three frames between going on screen and coming round
|
||||||
drmModeTypePreferred = 1 << 3
|
// again, and reading one out of uncached scanout memory takes a good fraction
|
||||||
drmModePageFlipEvent = 0x01
|
// of a frame.
|
||||||
drmEventFlipComplete = 0x02
|
const scanoutBuffers = 4
|
||||||
|
|
||||||
// What ADDFB means by 32 bits per pixel and 24 bits of colour.
|
|
||||||
xrgbRedShift = 16
|
|
||||||
xrgbGreenShift = 8
|
|
||||||
xrgbBlueShift = 0
|
|
||||||
|
|
||||||
// Two would be enough to stop the panel tearing, since one buffer being
|
|
||||||
// displayed while the other is drawn into is all double buffering means.
|
|
||||||
// More than two is for anything reading a frame back out: they are cycled
|
|
||||||
// in order, so a buffer is left alone for the three frames between going on
|
|
||||||
// screen and coming round again, and reading one out of uncached scanout
|
|
||||||
// memory takes a good fraction of a frame.
|
|
||||||
scanoutBuffers = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func drmIO(nr uintptr) uintptr { return drmIoctlBase<<8 | nr }
|
|
||||||
func drmIOWR(nr, size uintptr) uintptr { return 3<<30 | size<<16 | drmIoctlBase<<8 | nr }
|
|
||||||
|
|
||||||
type drmModeInfo struct {
|
|
||||||
clock uint32
|
|
||||||
hdisplay, hsyncStart, hsyncEnd, htotal, hskew uint16
|
|
||||||
vdisplay, vsyncStart, vsyncEnd, vtotal, vscan uint16
|
|
||||||
vrefresh, flags, typ uint32
|
|
||||||
name [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCardRes struct {
|
|
||||||
fbIDPtr, crtcIDPtr, connIDPtr, encIDPtr uint64
|
|
||||||
countFBs, countCRTCs, countConns, countEncs uint32
|
|
||||||
minWidth, maxWidth, minHeight, maxHeight uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeGetConnector struct {
|
|
||||||
encodersPtr, modesPtr, propsPtr, propValuesPtr uint64
|
|
||||||
countModes, countProps, countEncoders uint32
|
|
||||||
encoderID, connectorID, connectorType uint32
|
|
||||||
connectorTypeID, connection uint32
|
|
||||||
mmWidth, mmHeight, subpixel, pad uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeGetEncoder struct {
|
|
||||||
encoderID, encoderType uint32
|
|
||||||
crtcID uint32
|
|
||||||
possibleCRTCs, possibleClones uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCrtc struct {
|
|
||||||
setConnectorsPtr uint64
|
|
||||||
countConnectors uint32
|
|
||||||
crtcID uint32
|
|
||||||
fbID uint32
|
|
||||||
x, y uint32
|
|
||||||
gammaSize uint32
|
|
||||||
modeValid uint32
|
|
||||||
mode drmModeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeFBCmd struct {
|
|
||||||
fbID, width, height, pitch, bpp, depth uint32
|
|
||||||
handle uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCreateDumb struct {
|
|
||||||
height, width, bpp, flags uint32
|
|
||||||
handle, pitch uint32
|
|
||||||
size uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeMapDumb struct {
|
|
||||||
handle, pad uint32
|
|
||||||
offset uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCrtcPageFlip struct {
|
|
||||||
crtcID, fbID, flags, reserved uint32
|
|
||||||
userData uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
drmSetMaster = drmIO(0x1e)
|
|
||||||
drmDropMaster = drmIO(0x1f)
|
|
||||||
drmGetResources = drmIOWR(0xa0, unsafe.Sizeof(drmModeCardRes{}))
|
|
||||||
drmSetCrtc = drmIOWR(0xa2, unsafe.Sizeof(drmModeCrtc{}))
|
|
||||||
drmGetEncoder = drmIOWR(0xa6, unsafe.Sizeof(drmModeGetEncoder{}))
|
|
||||||
drmGetConnector = drmIOWR(0xa7, unsafe.Sizeof(drmModeGetConnector{}))
|
|
||||||
drmAddFB = drmIOWR(0xae, unsafe.Sizeof(drmModeFBCmd{}))
|
|
||||||
drmPageFlip = drmIOWR(0xb0, unsafe.Sizeof(drmModeCrtcPageFlip{}))
|
|
||||||
drmCreateDumb = drmIOWR(0xb2, unsafe.Sizeof(drmModeCreateDumb{}))
|
|
||||||
drmMapDumb = drmIOWR(0xb3, unsafe.Sizeof(drmModeMapDumb{}))
|
|
||||||
)
|
|
||||||
|
|
||||||
func drmIoctl(fd int, req uintptr, arg unsafe.Pointer) error {
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg)); errno != 0 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type scanout struct {
|
type scanout struct {
|
||||||
fbID uint32
|
fbID uint32
|
||||||
@@ -125,8 +30,7 @@ type scanout struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// w and h are the logical canvas, which is portrait; pw and ph are the panel,
|
// w and h are the logical canvas, which is portrait; pw and ph are the panel,
|
||||||
// which is landscape. Every draw is turned a quarter turn on its way to memory,
|
// which is landscape.
|
||||||
// so logical top lands on the panel's right edge.
|
|
||||||
type framebuffer struct {
|
type framebuffer struct {
|
||||||
fd int
|
fd int
|
||||||
back []byte
|
back []byte
|
||||||
@@ -147,7 +51,7 @@ type framebuffer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fb *framebuffer) offset(x, y int) int {
|
func (fb *framebuffer) offset(x, y int) int {
|
||||||
return x*fb.stride + (fb.pw-1-y)*4
|
return drm.Offset(x, y, fb.stride, fb.pw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *framebuffer) fromPanel(x, y int) (int, int) {
|
func (fb *framebuffer) fromPanel(x, y int) (int, int) {
|
||||||
@@ -155,20 +59,20 @@ func (fb *framebuffer) fromPanel(x, y int) (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cardResources(fd int) (crtcs, conns []uint32, err error) {
|
func cardResources(fd int) (crtcs, conns []uint32, err error) {
|
||||||
var res drmModeCardRes
|
var res drm.ModeCardRes
|
||||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||||
return nil, nil, fmt.Errorf("get resources: %w", err)
|
return nil, nil, fmt.Errorf("get resources: %w", err)
|
||||||
}
|
}
|
||||||
if res.countCRTCs == 0 || res.countConns == 0 {
|
if res.CountCRTCs == 0 || res.CountConns == 0 {
|
||||||
return nil, nil, fmt.Errorf("card has no crtcs or connectors")
|
return nil, nil, fmt.Errorf("card has no crtcs or connectors")
|
||||||
}
|
}
|
||||||
crtcs = make([]uint32, res.countCRTCs)
|
crtcs = make([]uint32, res.CountCRTCs)
|
||||||
conns = make([]uint32, res.countConns)
|
conns = make([]uint32, res.CountConns)
|
||||||
res.countFBs, res.countEncs = 0, 0
|
res.CountFBs, res.CountEncs = 0, 0
|
||||||
res.fbIDPtr, res.encIDPtr = 0, 0
|
res.FBIDPtr, res.EncIDPtr = 0, 0
|
||||||
res.crtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
res.CrtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
||||||
res.connIDPtr = uint64(uintptr(unsafe.Pointer(&conns[0])))
|
res.ConnIDPtr = uint64(uintptr(unsafe.Pointer(&conns[0])))
|
||||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||||
return nil, nil, fmt.Errorf("get resources: %w", err)
|
return nil, nil, fmt.Errorf("get resources: %w", err)
|
||||||
}
|
}
|
||||||
return crtcs, conns, nil
|
return crtcs, conns, nil
|
||||||
@@ -176,66 +80,66 @@ func cardResources(fd int) (crtcs, conns []uint32, err error) {
|
|||||||
|
|
||||||
// The preferred mode is the panel's native one; anything else would be the
|
// The preferred mode is the panel's native one; anything else would be the
|
||||||
// driver scaling a wrong-sized image onto it.
|
// driver scaling a wrong-sized image onto it.
|
||||||
func preferredMode(fd int, connID uint32) (drmModeInfo, error) {
|
func preferredMode(fd int, connID uint32) (drm.ModeInfo, error) {
|
||||||
c := drmModeGetConnector{connectorID: connID}
|
c := drm.ModeGetConnector{ConnectorID: connID}
|
||||||
if err := drmIoctl(fd, drmGetConnector, unsafe.Pointer(&c)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetConnector, unsafe.Pointer(&c)); err != nil {
|
||||||
return drmModeInfo{}, err
|
return drm.ModeInfo{}, err
|
||||||
}
|
}
|
||||||
if c.countModes == 0 {
|
if c.CountModes == 0 {
|
||||||
return drmModeInfo{}, fmt.Errorf("connector %d reported no modes", connID)
|
return drm.ModeInfo{}, fmt.Errorf("connector %d reported no modes", connID)
|
||||||
}
|
}
|
||||||
modes := make([]drmModeInfo, c.countModes)
|
modes := make([]drm.ModeInfo, c.CountModes)
|
||||||
q := drmModeGetConnector{
|
q := drm.ModeGetConnector{
|
||||||
connectorID: connID,
|
ConnectorID: connID,
|
||||||
countModes: c.countModes,
|
CountModes: c.CountModes,
|
||||||
modesPtr: uint64(uintptr(unsafe.Pointer(&modes[0]))),
|
ModesPtr: uint64(uintptr(unsafe.Pointer(&modes[0]))),
|
||||||
}
|
}
|
||||||
if err := drmIoctl(fd, drmGetConnector, unsafe.Pointer(&q)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetConnector, unsafe.Pointer(&q)); err != nil {
|
||||||
return drmModeInfo{}, err
|
return drm.ModeInfo{}, err
|
||||||
}
|
}
|
||||||
if q.countModes == 0 {
|
if q.CountModes == 0 {
|
||||||
return drmModeInfo{}, fmt.Errorf("connector %d reported no modes", connID)
|
return drm.ModeInfo{}, fmt.Errorf("connector %d reported no modes", connID)
|
||||||
}
|
}
|
||||||
for _, m := range modes[:q.countModes] {
|
for _, m := range modes[:q.CountModes] {
|
||||||
if m.typ&drmModeTypePreferred != 0 {
|
if m.Type&drm.TypePreferred != 0 {
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return modes[0], nil
|
return modes[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func crtcFor(fd int, c drmModeGetConnector, crtcs []uint32) (uint32, error) {
|
func crtcFor(fd int, c drm.ModeGetConnector, crtcs []uint32) (uint32, error) {
|
||||||
encoders := []uint32{c.encoderID}
|
encoders := []uint32{c.EncoderID}
|
||||||
if c.countEncoders > 0 {
|
if c.CountEncoders > 0 {
|
||||||
list := make([]uint32, c.countEncoders)
|
list := make([]uint32, c.CountEncoders)
|
||||||
q := drmModeGetConnector{
|
q := drm.ModeGetConnector{
|
||||||
connectorID: c.connectorID,
|
ConnectorID: c.ConnectorID,
|
||||||
countEncoders: c.countEncoders,
|
CountEncoders: c.CountEncoders,
|
||||||
encodersPtr: uint64(uintptr(unsafe.Pointer(&list[0]))),
|
EncodersPtr: uint64(uintptr(unsafe.Pointer(&list[0]))),
|
||||||
}
|
}
|
||||||
if err := drmIoctl(fd, drmGetConnector, unsafe.Pointer(&q)); err == nil {
|
if err := drm.Ioctl(fd, drm.GetConnector, unsafe.Pointer(&q)); err == nil {
|
||||||
encoders = append(encoders, list[:q.countEncoders]...)
|
encoders = append(encoders, list[:q.CountEncoders]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, id := range encoders {
|
for _, id := range encoders {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
e := drmModeGetEncoder{encoderID: id}
|
e := drm.ModeGetEncoder{EncoderID: id}
|
||||||
if err := drmIoctl(fd, drmGetEncoder, unsafe.Pointer(&e)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetEncoder, unsafe.Pointer(&e)); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Already driving this connector, otherwise anything it can be wired to.
|
// Already driving this connector, otherwise anything it can be wired to.
|
||||||
if e.crtcID != 0 {
|
if e.CrtcID != 0 {
|
||||||
return e.crtcID, nil
|
return e.CrtcID, nil
|
||||||
}
|
}
|
||||||
for i, crtc := range crtcs {
|
for i, crtc := range crtcs {
|
||||||
if e.possibleCRTCs&(1<<uint(i)) != 0 {
|
if e.PossibleCRTCs&(1<<uint(i)) != 0 {
|
||||||
return crtc, nil
|
return crtc, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("connector %d has no usable crtc", c.connectorID)
|
return 0, fmt.Errorf("connector %d has no usable crtc", c.ConnectorID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findDisplay(fd int) (connID, crtcID uint32, err error) {
|
func findDisplay(fd int) (connID, crtcID uint32, err error) {
|
||||||
@@ -244,11 +148,11 @@ func findDisplay(fd int) (connID, crtcID uint32, err error) {
|
|||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
for _, id := range conns {
|
for _, id := range conns {
|
||||||
c := drmModeGetConnector{connectorID: id}
|
c := drm.ModeGetConnector{ConnectorID: id}
|
||||||
if err := drmIoctl(fd, drmGetConnector, unsafe.Pointer(&c)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetConnector, unsafe.Pointer(&c)); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.connection != drmModeConnected || c.countModes == 0 {
|
if c.Connection != drm.Connected || c.CountModes == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
crtc, err := crtcFor(fd, c, crtcs)
|
crtc, err := crtcFor(fd, c, crtcs)
|
||||||
@@ -281,30 +185,30 @@ func openCard() (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (fb *framebuffer) addScanout(i int) error {
|
func (fb *framebuffer) addScanout(i int) error {
|
||||||
create := drmModeCreateDumb{width: uint32(fb.pw), height: uint32(fb.ph), bpp: 32}
|
create := drm.ModeCreateDumb{Width: uint32(fb.pw), Height: uint32(fb.ph), Bpp: 32}
|
||||||
if err := drmIoctl(fb.fd, drmCreateDumb, unsafe.Pointer(&create)); err != nil {
|
if err := drm.Ioctl(fb.fd, drm.CreateDumb, unsafe.Pointer(&create)); err != nil {
|
||||||
return fmt.Errorf("create dumb buffer: %w", err)
|
return fmt.Errorf("create dumb buffer: %w", err)
|
||||||
}
|
}
|
||||||
fb.stride = int(create.pitch)
|
fb.stride = int(create.Pitch)
|
||||||
|
|
||||||
add := drmModeFBCmd{
|
add := drm.ModeFBCmd{
|
||||||
width: uint32(fb.pw),
|
Width: uint32(fb.pw),
|
||||||
height: uint32(fb.ph),
|
Height: uint32(fb.ph),
|
||||||
pitch: create.pitch,
|
Pitch: create.Pitch,
|
||||||
bpp: 32,
|
Bpp: 32,
|
||||||
depth: 24,
|
Depth: 24,
|
||||||
handle: create.handle,
|
Handle: create.Handle,
|
||||||
}
|
}
|
||||||
if err := drmIoctl(fb.fd, drmAddFB, unsafe.Pointer(&add)); err != nil {
|
if err := drm.Ioctl(fb.fd, drm.AddFB, unsafe.Pointer(&add)); err != nil {
|
||||||
return fmt.Errorf("add fb: %w", err)
|
return fmt.Errorf("add fb: %w", err)
|
||||||
}
|
}
|
||||||
fb.bufs[i].fbID = add.fbID
|
fb.bufs[i].fbID = add.FBID
|
||||||
|
|
||||||
m := drmModeMapDumb{handle: create.handle}
|
m := drm.ModeMapDumb{Handle: create.Handle}
|
||||||
if err := drmIoctl(fb.fd, drmMapDumb, unsafe.Pointer(&m)); err != nil {
|
if err := drm.Ioctl(fb.fd, drm.MapDumb, unsafe.Pointer(&m)); err != nil {
|
||||||
return fmt.Errorf("map dumb buffer: %w", err)
|
return fmt.Errorf("map dumb buffer: %w", err)
|
||||||
}
|
}
|
||||||
mem, err := unix.Mmap(fb.fd, int64(m.offset), int(create.size),
|
mem, err := unix.Mmap(fb.fd, int64(m.Offset), int(create.Size),
|
||||||
unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("mmap scanout: %w", err)
|
return fmt.Errorf("mmap scanout: %w", err)
|
||||||
@@ -322,7 +226,7 @@ func openFramebuffer() (*framebuffer, error) {
|
|||||||
|
|
||||||
// Without master the modeset below is refused, and taking it is also what
|
// Without master the modeset below is refused, and taking it is also what
|
||||||
// stops the kernel console drawing into the display behind us.
|
// stops the kernel console drawing into the display behind us.
|
||||||
if err := drmIoctl(fd, drmSetMaster, nil); err != nil {
|
if err := drm.Ioctl(fd, drm.SetMaster, nil); err != nil {
|
||||||
unix.Close(fd)
|
unix.Close(fd)
|
||||||
return nil, fmt.Errorf("take drm master: %w", err)
|
return nil, fmt.Errorf("take drm master: %w", err)
|
||||||
}
|
}
|
||||||
@@ -338,7 +242,7 @@ func openFramebuffer() (*framebuffer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fb.connID, fb.crtcID = connID, crtcID
|
fb.connID, fb.crtcID = connID, crtcID
|
||||||
fb.pw, fb.ph = int(mode.hdisplay), int(mode.vdisplay)
|
fb.pw, fb.ph = int(mode.Hdisplay), int(mode.Vdisplay)
|
||||||
fb.w, fb.h = fb.ph, fb.pw
|
fb.w, fb.h = fb.ph, fb.pw
|
||||||
|
|
||||||
for i := range fb.bufs {
|
for i := range fb.bufs {
|
||||||
@@ -349,15 +253,15 @@ func openFramebuffer() (*framebuffer, error) {
|
|||||||
}
|
}
|
||||||
fb.back = make([]byte, fb.stride*fb.ph)
|
fb.back = make([]byte, fb.stride*fb.ph)
|
||||||
|
|
||||||
set := drmModeCrtc{
|
set := drm.ModeCrtc{
|
||||||
setConnectorsPtr: uint64(uintptr(unsafe.Pointer(&fb.connID))),
|
SetConnectorsPtr: uint64(uintptr(unsafe.Pointer(&fb.connID))),
|
||||||
countConnectors: 1,
|
CountConnectors: 1,
|
||||||
crtcID: crtcID,
|
CrtcID: crtcID,
|
||||||
fbID: fb.bufs[0].fbID,
|
FBID: fb.bufs[0].fbID,
|
||||||
modeValid: 1,
|
ModeValid: 1,
|
||||||
mode: mode,
|
Mode: mode,
|
||||||
}
|
}
|
||||||
if err := drmIoctl(fd, drmSetCrtc, unsafe.Pointer(&set)); err != nil {
|
if err := drm.Ioctl(fd, drm.SetCrtc, unsafe.Pointer(&set)); err != nil {
|
||||||
fb.close()
|
fb.close()
|
||||||
return nil, fmt.Errorf("set crtc: %w", err)
|
return nil, fmt.Errorf("set crtc: %w", err)
|
||||||
}
|
}
|
||||||
@@ -382,7 +286,7 @@ func (fb *framebuffer) readEvents() {
|
|||||||
if length < 8 || off+length > n {
|
if length < 8 || off+length > n {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if typ == drmEventFlipComplete {
|
if typ == drm.EventFlipComplete {
|
||||||
select {
|
select {
|
||||||
case fb.flips <- struct{}{}:
|
case fb.flips <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
@@ -402,20 +306,18 @@ func (fb *framebuffer) close() {
|
|||||||
// Dropping master hands the display back to the kernel console, which
|
// Dropping master hands the display back to the kernel console, which
|
||||||
// restores its own mode. The framebuffers and dumb buffers are reclaimed
|
// restores its own mode. The framebuffers and dumb buffers are reclaimed
|
||||||
// when the last reference to the fd goes.
|
// when the last reference to the fd goes.
|
||||||
drmIoctl(fb.fd, drmDropMaster, nil)
|
drm.Ioctl(fb.fd, drm.DropMaster, nil)
|
||||||
unix.Close(fb.fd)
|
unix.Close(fb.fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *framebuffer) pixel(c rgb) uint32 {
|
|
||||||
return uint32(c.r)<<xrgbRedShift | uint32(c.g)<<xrgbGreenShift | uint32(c.b)<<xrgbBlueShift
|
|
||||||
}
|
|
||||||
|
|
||||||
type rgb struct {
|
type rgb struct {
|
||||||
r, g, b uint8
|
r, g, b uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pixel(c rgb) uint32 { return drm.Pack(c.r, c.g, c.b) }
|
||||||
|
|
||||||
func (fb *framebuffer) fill(c rgb) {
|
func (fb *framebuffer) fill(c rgb) {
|
||||||
v := fb.pixel(c)
|
v := pixel(c)
|
||||||
row := make([]byte, fb.stride)
|
row := make([]byte, fb.stride)
|
||||||
for x := 0; x+4 <= fb.stride; x += 4 {
|
for x := 0; x+4 <= fb.stride; x += 4 {
|
||||||
row[x+0] = byte(v)
|
row[x+0] = byte(v)
|
||||||
@@ -435,7 +337,7 @@ func (fb *framebuffer) rect(x0, y0, w, h int, c rgb) {
|
|||||||
if x0 >= x1 || y0 >= y1 {
|
if x0 >= x1 || y0 >= y1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v := fb.pixel(c)
|
v := pixel(c)
|
||||||
span := make([]byte, (y1-y0)*4)
|
span := make([]byte, (y1-y0)*4)
|
||||||
for i := 0; i+4 <= len(span); i += 4 {
|
for i := 0; i+4 <= len(span); i += 4 {
|
||||||
span[i+0] = byte(v)
|
span[i+0] = byte(v)
|
||||||
@@ -480,7 +382,7 @@ func (fb *framebuffer) blend(x, y int, c rgb, cov uint8) {
|
|||||||
}
|
}
|
||||||
o := fb.offset(x, y)
|
o := fb.offset(x, y)
|
||||||
if cov == 255 {
|
if cov == 255 {
|
||||||
v := fb.pixel(c)
|
v := pixel(c)
|
||||||
fb.back[o+0] = byte(v)
|
fb.back[o+0] = byte(v)
|
||||||
fb.back[o+1] = byte(v >> 8)
|
fb.back[o+1] = byte(v >> 8)
|
||||||
fb.back[o+2] = byte(v >> 16)
|
fb.back[o+2] = byte(v >> 16)
|
||||||
@@ -490,15 +392,13 @@ func (fb *framebuffer) blend(x, y int, c rgb, cov uint8) {
|
|||||||
a := uint32(cov)
|
a := uint32(cov)
|
||||||
old := uint32(fb.back[o+0]) | uint32(fb.back[o+1])<<8 |
|
old := uint32(fb.back[o+0]) | uint32(fb.back[o+1])<<8 |
|
||||||
uint32(fb.back[o+2])<<16 | uint32(fb.back[o+3])<<24
|
uint32(fb.back[o+2])<<16 | uint32(fb.back[o+3])<<24
|
||||||
orr := uint8(old >> xrgbRedShift)
|
orr, og, ob := drm.Unpack(old)
|
||||||
og := uint8(old >> xrgbGreenShift)
|
|
||||||
ob := uint8(old >> xrgbBlueShift)
|
|
||||||
mix := rgb{
|
mix := rgb{
|
||||||
r: uint8((uint32(c.r)*a + uint32(orr)*(255-a)) / 255),
|
r: uint8((uint32(c.r)*a + uint32(orr)*(255-a)) / 255),
|
||||||
g: uint8((uint32(c.g)*a + uint32(og)*(255-a)) / 255),
|
g: uint8((uint32(c.g)*a + uint32(og)*(255-a)) / 255),
|
||||||
b: uint8((uint32(c.b)*a + uint32(ob)*(255-a)) / 255),
|
b: uint8((uint32(c.b)*a + uint32(ob)*(255-a)) / 255),
|
||||||
}
|
}
|
||||||
v := fb.pixel(mix)
|
v := pixel(mix)
|
||||||
fb.back[o+0] = byte(v)
|
fb.back[o+0] = byte(v)
|
||||||
fb.back[o+1] = byte(v >> 8)
|
fb.back[o+1] = byte(v >> 8)
|
||||||
fb.back[o+2] = byte(v >> 16)
|
fb.back[o+2] = byte(v >> 16)
|
||||||
@@ -511,12 +411,12 @@ func (fb *framebuffer) flush() error {
|
|||||||
next := (fb.front + 1) % scanoutBuffers
|
next := (fb.front + 1) % scanoutBuffers
|
||||||
copy(fb.bufs[next].mem, fb.back)
|
copy(fb.bufs[next].mem, fb.back)
|
||||||
|
|
||||||
flip := drmModeCrtcPageFlip{
|
flip := drm.ModeCrtcPageFlip{
|
||||||
crtcID: fb.crtcID,
|
CrtcID: fb.crtcID,
|
||||||
fbID: fb.bufs[next].fbID,
|
FBID: fb.bufs[next].fbID,
|
||||||
flags: drmModePageFlipEvent,
|
Flags: drm.PageFlipEvent,
|
||||||
}
|
}
|
||||||
if err := drmIoctl(fb.fd, drmPageFlip, unsafe.Pointer(&flip)); err != nil {
|
if err := drm.Ioctl(fb.fd, drm.PageFlip, unsafe.Pointer(&flip)); err != nil {
|
||||||
return fmt.Errorf("page flip: %w", err)
|
return fmt.Errorf("page flip: %w", err)
|
||||||
}
|
}
|
||||||
fb.front = next
|
fb.front = next
|
||||||
|
|||||||
+39
-112
@@ -9,83 +9,15 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"g.fc.run/theater/cabletest/internal/drm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cabletest drives the panel through drm, so /dev/fb0 holds the kernel console
|
// cabletest drives the panel through drm, so /dev/fb0 holds the kernel console
|
||||||
// and is not worth reading. The displayed buffer is reachable from out here
|
// and is not worth reading. The displayed buffer is reachable from out here
|
||||||
// instead: GETCRTC names it, and root is allowed a handle to it without being
|
// instead: GETCRTC names it, and root is allowed a handle to it without being
|
||||||
// drm master, so it can be read without the owning process cooperating.
|
// drm master, so it can be read without the owning process cooperating.
|
||||||
const (
|
const drmCardGlob = "/dev/dri/card*"
|
||||||
drmCardGlob = "/dev/dri/card*"
|
|
||||||
|
|
||||||
drmVblankRelative = 0x1
|
|
||||||
drmVblankHighCrtcMask = 0x3e
|
|
||||||
drmVblankHighCrtcShft = 1
|
|
||||||
|
|
||||||
xrgbRedShift = 16
|
|
||||||
xrgbGreenShift = 8
|
|
||||||
xrgbBlueShift = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
func drmIOWR(nr, size uintptr) uintptr { return 3<<30 | size<<16 | 0x64<<8 | nr }
|
|
||||||
|
|
||||||
type drmModeInfo struct {
|
|
||||||
clock uint32
|
|
||||||
hdisplay, hsyncStart, hsyncEnd, htotal, hskew uint16
|
|
||||||
vdisplay, vsyncStart, vsyncEnd, vtotal, vscan uint16
|
|
||||||
vrefresh, flags, typ uint32
|
|
||||||
name [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCardRes struct {
|
|
||||||
fbIDPtr, crtcIDPtr, connIDPtr, encIDPtr uint64
|
|
||||||
countFBs, countCRTCs, countConns, countEncs uint32
|
|
||||||
minWidth, maxWidth, minHeight, maxHeight uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeCrtc struct {
|
|
||||||
setConnectorsPtr uint64
|
|
||||||
countConnectors uint32
|
|
||||||
crtcID uint32
|
|
||||||
fbID uint32
|
|
||||||
x, y uint32
|
|
||||||
gammaSize uint32
|
|
||||||
modeValid uint32
|
|
||||||
mode drmModeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeFBCmd struct {
|
|
||||||
fbID, width, height, pitch, bpp, depth uint32
|
|
||||||
handle uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmModeMapDumb struct {
|
|
||||||
handle, pad uint32
|
|
||||||
offset uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type drmWaitVblank struct {
|
|
||||||
typ uint32
|
|
||||||
sequence uint32
|
|
||||||
signal uint64
|
|
||||||
tvSec int64
|
|
||||||
tvUsec int64
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
drmGetResources = drmIOWR(0xa0, unsafe.Sizeof(drmModeCardRes{}))
|
|
||||||
drmGetCrtc = drmIOWR(0xa1, unsafe.Sizeof(drmModeCrtc{}))
|
|
||||||
drmGetFB = drmIOWR(0xad, unsafe.Sizeof(drmModeFBCmd{}))
|
|
||||||
drmMapDumb = drmIOWR(0xb3, unsafe.Sizeof(drmModeMapDumb{}))
|
|
||||||
drmWaitVblankIO = drmIOWR(0x3a, 24)
|
|
||||||
)
|
|
||||||
|
|
||||||
func drmIoctl(fd int, req uintptr, arg unsafe.Pointer) error {
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg)); errno != 0 {
|
|
||||||
return errno
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type grabber struct {
|
type grabber struct {
|
||||||
fd int
|
fd int
|
||||||
@@ -113,26 +45,26 @@ func openGrabber() (*grabber, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func activeCrtc(fd int) (uint32, int, error) {
|
func activeCrtc(fd int) (uint32, int, error) {
|
||||||
var res drmModeCardRes
|
var res drm.ModeCardRes
|
||||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
if res.countCRTCs == 0 {
|
if res.CountCRTCs == 0 {
|
||||||
return 0, 0, fmt.Errorf("card has no crtcs")
|
return 0, 0, fmt.Errorf("card has no crtcs")
|
||||||
}
|
}
|
||||||
crtcs := make([]uint32, res.countCRTCs)
|
crtcs := make([]uint32, res.CountCRTCs)
|
||||||
res.countFBs, res.countConns, res.countEncs = 0, 0, 0
|
res.CountFBs, res.CountConns, res.CountEncs = 0, 0, 0
|
||||||
res.fbIDPtr, res.connIDPtr, res.encIDPtr = 0, 0, 0
|
res.FBIDPtr, res.ConnIDPtr, res.EncIDPtr = 0, 0, 0
|
||||||
res.crtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
res.CrtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
||||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
for i, id := range crtcs {
|
for i, id := range crtcs {
|
||||||
c := drmModeCrtc{crtcID: id}
|
c := drm.ModeCrtc{CrtcID: id}
|
||||||
if err := drmIoctl(fd, drmGetCrtc, unsafe.Pointer(&c)); err != nil {
|
if err := drm.Ioctl(fd, drm.GetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if c.fbID != 0 && c.modeValid != 0 {
|
if c.FBID != 0 && c.ModeValid != 0 {
|
||||||
return id, i, nil
|
return id, i, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,12 +77,12 @@ func (g *grabber) close() { unix.Close(g.fd) }
|
|||||||
// the flip cycle decides whether it stays ahead of the writer. Woken at a
|
// the flip cycle decides whether it stays ahead of the writer. Woken at a
|
||||||
// blank, the buffer GETCRTC then names has just gone on screen.
|
// blank, the buffer GETCRTC then names has just gone on screen.
|
||||||
func (g *grabber) waitVblank() error {
|
func (g *grabber) waitVblank() error {
|
||||||
v := drmWaitVblank{
|
v := drm.WaitVblank{
|
||||||
typ: drmVblankRelative | uint32(g.index<<drmVblankHighCrtcShft)&drmVblankHighCrtcMask,
|
Type: drm.VblankRelative | uint32(g.index<<drm.VblankHighCrtcShift)&drm.VblankHighCrtcMask,
|
||||||
sequence: 1,
|
Sequence: 1,
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
err := drmIoctl(g.fd, drmWaitVblankIO, unsafe.Pointer(&v))
|
err := drm.Ioctl(g.fd, drm.WaitVblankIO, unsafe.Pointer(&v))
|
||||||
if err == unix.EINTR {
|
if err == unix.EINTR {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -159,35 +91,35 @@ func (g *grabber) waitVblank() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *grabber) mapFront() (mem []byte, pitch, pw, ph int, err error) {
|
func (g *grabber) mapFront() (mem []byte, pitch, pw, ph int, err error) {
|
||||||
c := drmModeCrtc{crtcID: g.crtc}
|
c := drm.ModeCrtc{CrtcID: g.crtc}
|
||||||
if err := drmIoctl(g.fd, drmGetCrtc, unsafe.Pointer(&c)); err != nil {
|
if err := drm.Ioctl(g.fd, drm.GetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("get crtc: %w", err)
|
return nil, 0, 0, 0, fmt.Errorf("get crtc: %w", err)
|
||||||
}
|
}
|
||||||
if c.fbID == 0 {
|
if c.FBID == 0 {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("crtc %d is not scanning out anything", g.crtc)
|
return nil, 0, 0, 0, fmt.Errorf("crtc %d is not scanning out anything", g.crtc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only a drm master or root is given a handle to someone else's framebuffer.
|
// Only a drm master or root is given a handle to someone else's framebuffer.
|
||||||
fb := drmModeFBCmd{fbID: c.fbID}
|
fb := drm.ModeFBCmd{FBID: c.FBID}
|
||||||
if err := drmIoctl(g.fd, drmGetFB, unsafe.Pointer(&fb)); err != nil {
|
if err := drm.Ioctl(g.fd, drm.GetFB, unsafe.Pointer(&fb)); err != nil {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("get fb %d: %w", c.fbID, err)
|
return nil, 0, 0, 0, fmt.Errorf("get fb %d: %w", c.FBID, err)
|
||||||
}
|
}
|
||||||
if fb.handle == 0 {
|
if fb.Handle == 0 {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("kernel gave no handle for fb %d", c.fbID)
|
return nil, 0, 0, 0, fmt.Errorf("kernel gave no handle for fb %d", c.FBID)
|
||||||
}
|
}
|
||||||
if fb.bpp != 32 {
|
if fb.Bpp != 32 {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("expected 32bpp, got %d", fb.bpp)
|
return nil, 0, 0, 0, fmt.Errorf("expected 32bpp, got %d", fb.Bpp)
|
||||||
}
|
}
|
||||||
|
|
||||||
m := drmModeMapDumb{handle: fb.handle}
|
m := drm.ModeMapDumb{Handle: fb.Handle}
|
||||||
if err := drmIoctl(g.fd, drmMapDumb, unsafe.Pointer(&m)); err != nil {
|
if err := drm.Ioctl(g.fd, drm.MapDumb, unsafe.Pointer(&m)); err != nil {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("map fb %d: %w", c.fbID, err)
|
return nil, 0, 0, 0, fmt.Errorf("map fb %d: %w", c.FBID, err)
|
||||||
}
|
}
|
||||||
pitch, pw, ph = int(fb.pitch), int(fb.width), int(fb.height)
|
pitch, pw, ph = int(fb.Pitch), int(fb.Width), int(fb.Height)
|
||||||
mem, err = unix.Mmap(g.fd, int64(m.offset), pitch*ph,
|
mem, err = unix.Mmap(g.fd, int64(m.Offset), pitch*ph,
|
||||||
unix.PROT_READ, unix.MAP_SHARED)
|
unix.PROT_READ, unix.MAP_SHARED)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, 0, 0, fmt.Errorf("mmap fb %d: %w", c.fbID, err)
|
return nil, 0, 0, 0, fmt.Errorf("mmap fb %d: %w", c.FBID, err)
|
||||||
}
|
}
|
||||||
return mem, pitch, pw, ph, nil
|
return mem, pitch, pw, ph, nil
|
||||||
}
|
}
|
||||||
@@ -217,22 +149,17 @@ func (g *grabber) frame() (*image.NRGBA, error) {
|
|||||||
return decode(a, pitch, pw, ph), nil
|
return decode(a, pitch, pw, ph), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The panel is landscape and every draw is turned a quarter turn on its way
|
// Undoes the quarter turn drm.Offset applies, to get back what a person
|
||||||
// into it, so the turn is undone here to get back what a person standing in
|
// standing in front of the panel sees.
|
||||||
// front of it sees.
|
|
||||||
func decode(buf []byte, pitch, pw, ph int) *image.NRGBA {
|
func decode(buf []byte, pitch, pw, ph int) *image.NRGBA {
|
||||||
img := image.NewNRGBA(image.Rect(0, 0, ph, pw))
|
img := image.NewNRGBA(image.Rect(0, 0, ph, pw))
|
||||||
for x := 0; x < ph; x++ {
|
for x := 0; x < ph; x++ {
|
||||||
for y := 0; y < pw; y++ {
|
for y := 0; y < pw; y++ {
|
||||||
o := x*pitch + (pw-1-y)*4
|
o := drm.Offset(x, y, pitch, pw)
|
||||||
v := uint32(buf[o]) | uint32(buf[o+1])<<8 |
|
v := uint32(buf[o]) | uint32(buf[o+1])<<8 |
|
||||||
uint32(buf[o+2])<<16 | uint32(buf[o+3])<<24
|
uint32(buf[o+2])<<16 | uint32(buf[o+3])<<24
|
||||||
img.SetNRGBA(x, y, color.NRGBA{
|
r, g, b := drm.Unpack(v)
|
||||||
R: uint8(v >> xrgbRedShift),
|
img.SetNRGBA(x, y, color.NRGBA{R: r, G: g, B: b, A: 255})
|
||||||
G: uint8(v >> xrgbGreenShift),
|
|
||||||
B: uint8(v >> xrgbBlueShift),
|
|
||||||
A: 255,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return img
|
return img
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// Package drm is the kernel mode-setting ABI, shared by the panel this draws on
|
||||||
|
// and the grabber that reads it back out. These structs are a layout contract,
|
||||||
|
// so a field added or reordered in one copy and not the other would silently
|
||||||
|
// misread the kernel: there is deliberately only one copy.
|
||||||
|
package drm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ioctlBase = 0x64
|
||||||
|
|
||||||
|
func IO(nr uintptr) uintptr { return ioctlBase<<8 | nr }
|
||||||
|
func IOWR(nr, size uintptr) uintptr { return 3<<30 | size<<16 | ioctlBase<<8 | nr }
|
||||||
|
|
||||||
|
func Ioctl(fd int, req uintptr, arg unsafe.Pointer) error {
|
||||||
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg)); errno != 0 {
|
||||||
|
return errno
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
Connected = 1
|
||||||
|
TypePreferred = 1 << 3
|
||||||
|
PageFlipEvent = 0x01
|
||||||
|
EventFlipComplete = 0x02
|
||||||
|
|
||||||
|
VblankRelative = 0x1
|
||||||
|
VblankHighCrtcMask = 0x3e
|
||||||
|
VblankHighCrtcShift = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type ModeInfo struct {
|
||||||
|
Clock uint32
|
||||||
|
Hdisplay, HsyncStart, HsyncEnd, Htotal, Hskew uint16
|
||||||
|
Vdisplay, VsyncStart, VsyncEnd, Vtotal, Vscan uint16
|
||||||
|
Vrefresh, Flags, Type uint32
|
||||||
|
Name [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeCardRes struct {
|
||||||
|
FBIDPtr, CrtcIDPtr, ConnIDPtr, EncIDPtr uint64
|
||||||
|
CountFBs, CountCRTCs, CountConns, CountEncs uint32
|
||||||
|
MinWidth, MaxWidth, MinHeight, MaxHeight uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeGetConnector struct {
|
||||||
|
EncodersPtr, ModesPtr, PropsPtr, PropValuesPtr uint64
|
||||||
|
CountModes, CountProps, CountEncoders uint32
|
||||||
|
EncoderID, ConnectorID, ConnectorType uint32
|
||||||
|
ConnectorTypeID, Connection uint32
|
||||||
|
MmWidth, MmHeight, Subpixel, Pad uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeGetEncoder struct {
|
||||||
|
EncoderID, EncoderType uint32
|
||||||
|
CrtcID uint32
|
||||||
|
PossibleCRTCs, PossibleClones uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeCrtc struct {
|
||||||
|
SetConnectorsPtr uint64
|
||||||
|
CountConnectors uint32
|
||||||
|
CrtcID uint32
|
||||||
|
FBID uint32
|
||||||
|
X, Y uint32
|
||||||
|
GammaSize uint32
|
||||||
|
ModeValid uint32
|
||||||
|
Mode ModeInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeFBCmd struct {
|
||||||
|
FBID, Width, Height, Pitch, Bpp, Depth uint32
|
||||||
|
Handle uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeCreateDumb struct {
|
||||||
|
Height, Width, Bpp, Flags uint32
|
||||||
|
Handle, Pitch uint32
|
||||||
|
Size uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeMapDumb struct {
|
||||||
|
Handle, Pad uint32
|
||||||
|
Offset uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeCrtcPageFlip struct {
|
||||||
|
CrtcID, FBID, Flags, Reserved uint32
|
||||||
|
UserData uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitVblank has only Type and Sequence ever set, and those sit at the same
|
||||||
|
// offsets in both arms of the kernel's union. The request below carries the
|
||||||
|
// union's own size rather than this struct's, which is larger.
|
||||||
|
type WaitVblank struct {
|
||||||
|
Type uint32
|
||||||
|
Sequence uint32
|
||||||
|
Signal uint64
|
||||||
|
TvSec int64
|
||||||
|
TvUsec int64
|
||||||
|
}
|
||||||
|
|
||||||
|
const waitVblankUnionSize = 24
|
||||||
|
|
||||||
|
var (
|
||||||
|
SetMaster = IO(0x1e)
|
||||||
|
DropMaster = IO(0x1f)
|
||||||
|
WaitVblankIO = IOWR(0x3a, waitVblankUnionSize)
|
||||||
|
GetResources = IOWR(0xa0, unsafe.Sizeof(ModeCardRes{}))
|
||||||
|
GetCrtc = IOWR(0xa1, unsafe.Sizeof(ModeCrtc{}))
|
||||||
|
SetCrtc = IOWR(0xa2, unsafe.Sizeof(ModeCrtc{}))
|
||||||
|
GetEncoder = IOWR(0xa6, unsafe.Sizeof(ModeGetEncoder{}))
|
||||||
|
GetConnector = IOWR(0xa7, unsafe.Sizeof(ModeGetConnector{}))
|
||||||
|
GetFB = IOWR(0xad, unsafe.Sizeof(ModeFBCmd{}))
|
||||||
|
AddFB = IOWR(0xae, unsafe.Sizeof(ModeFBCmd{}))
|
||||||
|
PageFlip = IOWR(0xb0, unsafe.Sizeof(ModeCrtcPageFlip{}))
|
||||||
|
CreateDumb = IOWR(0xb2, unsafe.Sizeof(ModeCreateDumb{}))
|
||||||
|
MapDumb = IOWR(0xb3, unsafe.Sizeof(ModeMapDumb{}))
|
||||||
|
)
|
||||||
|
|
||||||
|
// What ADDFB means by 32 bits per pixel and 24 bits of colour.
|
||||||
|
const (
|
||||||
|
redShift = 16
|
||||||
|
greenShift = 8
|
||||||
|
blueShift = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func Pack(r, g, b uint8) uint32 {
|
||||||
|
return uint32(r)<<redShift | uint32(g)<<greenShift | uint32(b)<<blueShift
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unpack(v uint32) (r, g, b uint8) {
|
||||||
|
return uint8(v >> redShift), uint8(v >> greenShift), uint8(v >> blueShift)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset turns the quarter turn between the portrait canvas and the landscape
|
||||||
|
// panel, putting logical top on the panel's right edge. Drawing and reading
|
||||||
|
// back have to agree on this exactly.
|
||||||
|
func Offset(x, y, stride, panelW int) int {
|
||||||
|
return x*stride + (panelW-1-y)*4
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user