On Tue, Jul 31, 2018 at 03:05:11PM +0200, Mark Kettenis wrote:
> You might want to look at what the udl(4) driver does. The kernel
> driver lives in sys/dev/usb/udl.c and implements a "damage" ioctl that
> updates a region of the actual framebuffer from the virtual
> framebuffer that lives in physical memory. X supposedly keeps track
> of the "damage" and the xf86-video-wsudl driver uses that to transfer
> the appropriate pixels. Maybe this mechanism could be generalized by
> implementing a wscons ioctl?
>
> Your approach is a bit wasteful since you're doing work even when the
> display is completely static. Another problem is that your timeout
> might run in the middle of an update of the virtual framebuffer.
>
> Not sure how this would work with graphics stuff that doesn't go
> through X though. Something that just draws into a mmap'ed virtual
> framebuffer and issues ioctls should work though.
That is very helpful indeed. I have come up with a quick diff to test
it, and it makes X work rather smooth on that SPI-connected OLED. I'm
rather surprised.
I guess I will have to come up with a diff to try and make this DAMAGE
ioctl more generic.
Patrick
diff --git a/sys/dev/fdt/ssdfb.c b/sys/dev/fdt/ssdfb.c
index 4c774855147..ea5e213076f 100644
--- a/sys/dev/fdt/ssdfb.c
+++ b/sys/dev/fdt/ssdfb.c
@@ -24,7 +24,10 @@
#include <sys/timeout.h>
#include <sys/task.h>
+#include <uvm/uvm_extern.h>
+
#include <dev/spi/spivar.h>
+#include <dev/usb/udlio.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_gpio.h>
@@ -72,6 +75,7 @@ struct ssdfb_softc {
uint8_t *sc_fb;
size_t sc_fbsize;
struct rasops_info sc_rinfo;
+ int sc_mode;
uint8_t sc_column_range[2];
uint8_t sc_page_range[2];
@@ -200,7 +204,8 @@ ssdfb_attach(struct device *parent, struct device *self,
void *aux)
sc->sc_fb = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK | M_ZERO);
ri = &sc->sc_rinfo;
- ri->ri_bits = malloc(sc->sc_fbsize, M_DEVBUF, M_WAITOK | M_ZERO);
+ ri->ri_bits = malloc(roundup(sc->sc_fbsize, PAGE_SIZE),
+ M_DEVBUF, M_WAITOK | M_ZERO);
ri->ri_bs = ssdfb_bs;
ri->ri_flg = RI_CLEAR | RI_VCONS;
ri->ri_depth = 1;
@@ -240,7 +245,7 @@ ssdfb_detach(struct device *self, int flags)
struct rasops_info *ri = &sc->sc_rinfo;
timeout_del(&sc->sc_to);
task_del(systq, &sc->sc_task);
- free(ri->ri_bits, M_DEVBUF, sc->sc_fbsize);
+ free(ri->ri_bits, M_DEVBUF, roundup(sc->sc_fbsize, PAGE_SIZE));
free(sc->sc_fb, M_DEVBUF, sc->sc_fbsize);
free(sc->sc_gpio, M_DEVBUF, sc->sc_gpiolen);
return 0;
@@ -417,13 +422,15 @@ ssdfb_ioctl(void *v, u_long cmd, caddr_t data, int flag,
struct proc *p)
struct ssdfb_softc *sc = v;
struct rasops_info *ri = &sc->sc_rinfo;
struct wsdisplay_fbinfo *wdf;
+ struct udl_ioctl_damage *d;
+ int mode;
switch (cmd) {
case WSDISPLAYIO_GETPARAM:
case WSDISPLAYIO_SETPARAM:
return (-1);
case WSDISPLAYIO_GTYPE:
- *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
+ *(u_int *)data = WSDISPLAY_TYPE_DL;
break;
case WSDISPLAYIO_GINFO:
wdf = (struct wsdisplay_fbinfo *)data;
@@ -436,10 +443,38 @@ ssdfb_ioctl(void *v, u_long cmd, caddr_t data, int flag,
struct proc *p)
*(u_int *)data = ri->ri_stride;
break;
case WSDISPLAYIO_SMODE:
+ mode = *(u_int *)data;
+ switch (mode) {
+ case WSDISPLAYIO_MODE_EMUL:
+ if (sc->sc_mode != WSDISPLAYIO_MODE_EMUL) {
+ memset(ri->ri_bits, 0, roundup(sc->sc_fbsize,
+ PAGE_SIZE));
+ ssdfb_update(sc);
+ sc->sc_mode = mode;
+ }
+ break;
+ case WSDISPLAYIO_MODE_DUMBFB:
+ if (sc->sc_mode != WSDISPLAYIO_MODE_DUMBFB) {
+ memset(ri->ri_bits, 0, roundup(sc->sc_fbsize,
+ PAGE_SIZE));
+ timeout_del(&sc->sc_to);
+ task_del(systq, &sc->sc_task);
+ sc->sc_mode = mode;
+ }
+ break;
+ case WSDISPLAYIO_MODE_MAPPED:
+ default:
+ return (-1);
+ }
break;
case WSDISPLAYIO_GETSUPPORTEDDEPTH:
*(u_int *)data = WSDISPLAYIO_DEPTH_1;
break;
+ case UDLIO_DAMAGE:
+ d = (struct udl_ioctl_damage *)data;
+ d->status = UDLIO_STATUS_OK;
+ ssdfb_partial(sc, d->x1, d->x2, d->y1, d->y2);
+ break;
default:
return (-1);
}
@@ -450,7 +485,17 @@ ssdfb_ioctl(void *v, u_long cmd, caddr_t data, int flag,
struct proc *p)
paddr_t
ssdfb_mmap(void *v, off_t off, int prot)
{
- return -1;
+ struct ssdfb_softc *sc = v;
+ struct rasops_info *ri = &sc->sc_rinfo;
+ paddr_t pa;
+
+ if (off >= sc->sc_fbsize || off < 0)
+ return (-1);
+
+ if (!pmap_extract(pmap_kernel(), (vaddr_t)ri->ri_bits, &pa))
+ return (-1);
+
+ return (pa + off);
}
int