Skip to content

Commit 66c3b38

Browse files
jgneffkevinrushforth
authored andcommitted
8227425: Add support for e-paper displays on i.MX6 devices
Reviewed-by: jvos, kcr
1 parent e30049f commit 66c3b38

File tree

7 files changed

+236
-45
lines changed

7 files changed

+236
-45
lines changed

modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/EPDFrameBuffer.java

+79-16
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class EPDFrameBuffer {
6969
*/
7070
private static final int POWERDOWN_DELAY = 1_000;
7171

72+
/**
73+
* Linux system error: ENOTTY 25 Inappropriate ioctl for device.
74+
*/
75+
private static final int ENOTTY = 25;
76+
7277
private final PlatformLogger logger = Logging.getJavaFXLogger();
7378
private final EPDSettings settings;
7479
private final LinuxSystem system;
@@ -296,6 +301,16 @@ private MxcfbUpdateData createDefaultUpdate(int width, int height) {
296301
* <li>{@link EPDSystem#WAVEFORM_MODE_A2}</li>
297302
* </ul>
298303
*
304+
* @implNote This method fails on the Kobo Glo HD Model N437 with the error
305+
* ENOTTY (25), "Inappropriate ioctl for device." The driver on that device
306+
* uses an extended structure with four additional integers, changing its
307+
* size and its corresponding request code. This method could use the
308+
* extended structure, but the driver on the Kobo Glo HD ignores it and
309+
* returns immediately, anyway. Furthermore, newer devices support both the
310+
* current structure and the extended one, but define the extra fields in a
311+
* different order. Therefore, simply use the current structure and ignore
312+
* an error of ENOTTY, picking up the default values for any extra fields.
313+
*
299314
* @param init the initialization mode for clearing the screen to all white
300315
* @param du the direct update mode for changing any gray values to either
301316
* all black or all white
@@ -308,7 +323,7 @@ private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int
308323
var modes = new MxcfbWaveformModes();
309324
modes.setModes(modes.p, init, du, gc4, gc8, gc16, gc32);
310325
int rc = system.ioctl(fd, driver.MXCFB_SET_WAVEFORM_MODES, modes.p);
311-
if (rc != 0) {
326+
if (rc != 0 && system.errno() != ENOTTY) {
312327
logger.severe("Failed setting waveform modes: {0} ({1})",
313328
system.getErrorMessage(), system.errno());
314329
}
@@ -325,7 +340,7 @@ private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int
325340
private void setTemperature(int temp) {
326341
int rc = driver.ioctl(fd, driver.MXCFB_SET_TEMPERATURE, temp);
327342
if (rc != 0) {
328-
logger.severe("Failed setting temperature to {2} °C: {0} ({1})",
343+
logger.severe("Failed setting temperature to {2} degrees Celsius: {0} ({1})",
329344
system.getErrorMessage(), system.errno(), temp);
330345
}
331346
}
@@ -421,7 +436,7 @@ private int sendUpdate(MxcfbUpdateData update, int waveformMode) {
421436
logger.severe("Failed sending update {2}: {0} ({1})",
422437
system.getErrorMessage(), system.errno(), Integer.toUnsignedLong(updateMarker));
423438
} else if (logger.isLoggable(Level.FINER)) {
424-
logger.finer("Sent update: {0} × {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}",
439+
logger.finer("Sent update: {0} x {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}",
425440
update.getUpdateRegionWidth(update.p), update.getUpdateRegionHeight(update.p),
426441
waveformMode, update.getWaveformMode(update.p),
427442
Integer.toHexString(update.getFlags(update.p)).toUpperCase(),
@@ -482,7 +497,7 @@ private int getPowerdownDelay() {
482497
logger.severe("Failed getting power-down delay: {0} ({1})",
483498
system.getErrorMessage(), system.errno());
484499
}
485-
return integer.getInteger(integer.p);
500+
return integer.get(integer.p);
486501
}
487502

488503
/**
@@ -571,20 +586,57 @@ ByteBuffer getOffscreenBuffer() {
571586
* "QuantumRenderer modifies buffer in use by JavaFX Application Thread"
572587
* <https://bugs.openjdk.java.net/browse/JDK-8201567>.
573588
*/
574-
int size = xresVirtual * yresVirtual * Integer.SIZE;
589+
int size = xresVirtual * yres * Integer.BYTES;
575590
return ByteBuffer.allocateDirect(size);
576591
}
577592

578593
/**
579594
* Creates a new mapping of the Linux frame buffer device into memory.
580595
*
596+
* @implNote The virtual y-resolution reported by the device driver can be
597+
* wrong, as shown by the following example on the Kobo Glo HD Model N437
598+
* which reports 2,304 pixels when the correct value is 1,152 pixels
599+
* (6,782,976 / 5,888). Therefore, this method cannot use the frame buffer
600+
* virtual resolution to calculate its size.
601+
*
602+
* <pre>{@code
603+
* $ sudo fbset -i
604+
*
605+
* mode "1448x1072-46"
606+
* # D: 80.000 MHz, H: 50.188 kHz, V: 46.385 Hz
607+
* geometry 1448 1072 1472 2304 32
608+
* timings 12500 16 102 4 4 28 2
609+
* rgba 8/16,8/8,8/0,8/24
610+
* endmode
611+
*
612+
* Frame buffer device information:
613+
* Name : mxc_epdc_fb
614+
* Address : 0x88000000
615+
* Size : 6782976
616+
* Type : PACKED PIXELS
617+
* Visual : TRUECOLOR
618+
* XPanStep : 1
619+
* YPanStep : 1
620+
* YWrapStep : 0
621+
* LineLength : 5888
622+
* Accelerator : No
623+
* }</pre>
624+
*
581625
* @return a byte buffer containing the mapping of the Linux frame buffer
582-
* device
626+
* device if successful; otherwise {@code null}
583627
*/
584628
ByteBuffer getMappedBuffer() {
585-
int size = xresVirtual * yresVirtual * bytesPerPixel;
629+
ByteBuffer buffer = null;
630+
int size = xresVirtual * yres * bytesPerPixel;
631+
logger.fine("Mapping frame buffer: {0} bytes", size);
586632
long addr = system.mmap(0l, size, LinuxSystem.PROT_WRITE, LinuxSystem.MAP_SHARED, fd, 0);
587-
return addr == LinuxSystem.MAP_FAILED ? null : C.getC().NewDirectByteBuffer(addr, size);
633+
if (addr == LinuxSystem.MAP_FAILED) {
634+
logger.severe("Failed mapping {2} bytes of frame buffer: {0} ({1})",
635+
system.getErrorMessage(), system.errno(), size);
636+
} else {
637+
buffer = C.getC().NewDirectByteBuffer(addr, size);
638+
}
639+
return buffer;
588640
}
589641

590642
/**
@@ -594,7 +646,13 @@ ByteBuffer getMappedBuffer() {
594646
* buffer device
595647
*/
596648
void releaseMappedBuffer(ByteBuffer buffer) {
597-
system.munmap(C.getC().GetDirectBufferAddress(buffer), buffer.capacity());
649+
int size = buffer.capacity();
650+
logger.fine("Unmapping frame buffer: {0} bytes", size);
651+
int rc = system.munmap(C.getC().GetDirectBufferAddress(buffer), size);
652+
if (rc != 0) {
653+
logger.severe("Failed unmapping {2} bytes of frame buffer: {0} ({1})",
654+
system.getErrorMessage(), system.errno(), size);
655+
}
598656
}
599657

600658
/**
@@ -614,26 +672,31 @@ long getNativeHandle() {
614672
}
615673

616674
/**
617-
* Gets the virtual horizontal resolution of the frame buffer. See the notes
618-
* for the {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above.
675+
* Gets the frame buffer width in pixels. See the notes for the
676+
* {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above.
677+
*
678+
* @implNote When using an 8-bit, unrotated, and uninverted frame buffer in
679+
* the Y8 pixel format, the Kobo Clara HD Model N249 works only when this
680+
* method returns the visible x-resolution ({@code xres}) instead of the
681+
* normal virtual x-resolution ({@code xresVirtual}).
619682
*
620-
* @return the virtual width in pixels
683+
* @return the width in pixels
621684
*/
622685
int getWidth() {
623-
return xresVirtual;
686+
return settings.getWidthVisible ? xres : xresVirtual;
624687
}
625688

626689
/**
627-
* Gets the visible vertical resolution of the frame buffer.
690+
* Gets the frame buffer height in pixels.
628691
*
629-
* @return the visible height in pixels
692+
* @return the height in pixels
630693
*/
631694
int getHeight() {
632695
return yres;
633696
}
634697

635698
/**
636-
* Gets the color depth of the frame buffer.
699+
* Gets the frame buffer color depth in bits per pixel.
637700
*
638701
* @return the color depth in bits per pixel
639702
*/

modules/javafx.graphics/src/main/java/com/sun/glass/ui/monocle/EPDScreen.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class EPDScreen implements NativeScreen {
6060

6161
/**
6262
* The density of this screen in pixels per inch. For now, the value is
63-
* hard-coded to the density of a 6-inch display panel with 800 × 600 px at
63+
* hard-coded to the density of a 6-inch display panel with 800 x 600 px at
6464
* 167 ppi.
6565
*/
6666
private static final int DPI = 167;
@@ -99,6 +99,8 @@ class EPDScreen implements NativeScreen {
9999
width = fbDevice.getWidth();
100100
height = fbDevice.getHeight();
101101
bitDepth = fbDevice.getBitDepth();
102+
logger.fine("Native screen geometry: {0} px x {1} px x {2} bpp",
103+
width, height, bitDepth);
102104

103105
/*
104106
* If the Linux frame buffer is configured for 32-bit color, compose
@@ -112,8 +114,12 @@ class EPDScreen implements NativeScreen {
112114
* display, though, allows us to reuse the same frame buffer region
113115
* immediately after sending an update.
114116
*/
117+
ByteBuffer mapping = null;
115118
if (bitDepth == Integer.SIZE) {
116-
fbMapping = fbDevice.getMappedBuffer();
119+
mapping = fbDevice.getMappedBuffer();
120+
}
121+
if (mapping != null) {
122+
fbMapping = mapping;
117123
fbChannel = null;
118124
} else {
119125
Path path = FileSystems.getDefault().getPath(fbPath);

0 commit comments

Comments
 (0)