diff --git a/ffi-cdecl/include/mtk-kobo.h b/ffi-cdecl/include/mtk-kobo.h index c14f4f5a8..6dd352180 100644 --- a/ffi-cdecl/include/mtk-kobo.h +++ b/ffi-cdecl/include/mtk-kobo.h @@ -57,6 +57,9 @@ // NOTE: That confusing `enable_night_mode_by_wfm` mapping is never actually used, unless you enable it via the debug procfs knob. // FWIW, lab126 does GC16 => GCK16 & GLR16 => GLKW16... +// NOTE: Like it's always been the case on lab126 devices, on newer devices (i.e., not the Elipsa 2E), +// anything involving REAGL (i.e., GLR16, GLKW16 & GLRC16) is *always* paired with FULL. +// Likewise, stuff tweaked for Kaleido (GCC16 & GLRC16) is also *always* paired with FULL. enum HWTCON_WAVEFORM_MODE_ENUM { // Matches MXCFB @@ -70,6 +73,9 @@ enum HWTCON_WAVEFORM_MODE_ENUM HWTCON_WAVEFORM_MODE_A2 = 6, HWTCON_WAVEFORM_MODE_GCK16 = 8, HWTCON_WAVEFORM_MODE_GLKW16 = 9, // AKA. GCKW16; REAGL DARK + // Introduced with Kaleido devices + HWTCON_WAVEFORM_MODE_GCC16 = 10, // Used for images on color panels. + HWTCON_WAVEFORM_MODE_GLRC16 = 11, // Used for color highlights on text. (GLKW16 in dark mode). // Matches MXCFB HWTCON_WAVEFORM_MODE_AUTO = 257, }; diff --git a/ffi/framebuffer.lua b/ffi/framebuffer.lua index 33d005ea5..9368463fa 100644 --- a/ffi/framebuffer.lua +++ b/ffi/framebuffer.lua @@ -196,6 +196,12 @@ end function fb:refreshA2Imp(x, y, w, h, d) return self:refreshFastImp(x, y, w, h, d) end +function fb:refreshColorImp(x, y, w, h, d) + return self:refreshPartialImp(x, y, w, h, d) +end +function fb:refreshColorTextImp(x, y, w, h, d) + return self:refreshPartialImp(x, y, w, h, d) +end function fb:refreshWaitForLastImp() -- default is NOP end @@ -238,6 +244,14 @@ function fb:refreshA2(x, y, w, h, d) x, y, w, h = self:calculateRealCoordinates(x, y, w, h) return self:refreshA2Imp(x, y, w, h, d) end +function fb:refreshColor(x, y, w, h, d) + x, y, w, h = self:calculateRealCoordinates(x, y, w, h) + return self:refreshColorImp(x, y, w, h, d) +end +function fb:refreshColorText(x, y, w, h, d) + x, y, w, h = self:calculateRealCoordinates(x, y, w, h) + return self:refreshColorTextImp(x, y, w, h, d) +end function fb:refreshWaitForLast() return self:refreshWaitForLastImp() end diff --git a/ffi/framebuffer_mxcfb.lua b/ffi/framebuffer_mxcfb.lua index 837dcee11..87348133e 100644 --- a/ffi/framebuffer_mxcfb.lua +++ b/ffi/framebuffer_mxcfb.lua @@ -17,11 +17,16 @@ local framebuffer = { mech_poweron = nil, mech_wait_update_complete = nil, mech_wait_update_submission = nil, - waveform_partial = nil, + wait_for_submission_before = false, + wait_for_submission_after = false, + waveform_a2 = nil, + waveform_fast = nil, waveform_ui = nil, + waveform_partial = nil, waveform_flashui = nil, waveform_full = nil, - waveform_fast = nil, + waveform_color = nil, + waveform_color_reagl = nil, waveform_reagl = nil, waveform_night = nil, waveform_flashnight = nil, @@ -33,6 +38,8 @@ local framebuffer = { dont_wait_for_marker = nil, -- Set by frontend to 3 on Pocketbook Color Lux that refreshes based on bytes (not based on pixel) refresh_pixel_size = 1, + -- Used to enforce an alignment constraint on devices with quirky drivers + alignment_constraint = nil, -- We recycle ffi cdata marker_data = nil, @@ -55,21 +62,16 @@ end -- Returns true if waveform_mode arg matches the UI waveform mode for the current device -- NOTE: This is to avoid explicit comparison against device-specific waveform constants in mxc_update() --- Here, it's because of the Kindle-specific WAVEFORM_MODE_GC16_FAST function framebuffer:_isUIWaveFormMode(waveform_mode) return waveform_mode == self.waveform_ui end -- Returns true if waveform_mode arg matches the FlashUI waveform mode for the current device --- NOTE: This is to avoid explicit comparison against device-specific waveform constants in mxc_update() --- Here, it's because of the Kindle-specific WAVEFORM_MODE_GC16_FAST function framebuffer:_isFlashUIWaveFormMode(waveform_mode) return waveform_mode == self.waveform_flashui end -- Returns true if waveform_mode arg matches the REAGL waveform mode for the current device --- NOTE: This is to avoid explicit comparison against device-specific waveform constants in mxc_update() --- Here, it's Kindle's various WAVEFORM_MODE_REAGL vs. Kobo's WAVEFORM_MODE_REAGLD function framebuffer:_isREAGLWaveFormMode(waveform_mode) return waveform_mode == self.waveform_reagl end @@ -80,12 +82,30 @@ function framebuffer:_isNightREAGL() end -- Returns true if waveform_mode arg matches the partial waveform mode for the current device --- NOTE: This is to avoid explicit comparison against device-specific waveform constants in mxc_update() --- Here, because of REAGL or device-specific quirks. function framebuffer:_isPartialWaveFormMode(waveform_mode) return waveform_mode == self.waveform_partial end +-- Returns true if waveform_mode arg matches the fast waveform mode for the current device +function framebuffer:_isFastWaveFormMode(waveform_mode) + return waveform_mode == self.waveform_fast +end + +-- Returns true if waveform_mode arg does *NOT* match the A2 or fast waveform mode for the current device +function framebuffer:_isNotFastWaveFormMode(waveform_mode) + return waveform_mode ~= self.waveform_a2 and waveform_mode ~= self.waveform_fast +end + +-- Returns true if waveform_mode arg does *NOT* match the A2 waveform mode for the current device +function framebuffer:_isNotA2WaveFormMode(waveform_mode) + return waveform_mode ~= self.waveform_a2 +end + +-- Returns true if waveform_mode arg matches a Kaleido-specific waveform mode for the current device +function framebuffer:_isKaleidoWaveFormMode(waveform_mode) + return waveform_mode == self.waveform_color or waveform_mode == self.waveform_color_reagl +end + -- Returns the device-specific nightmode waveform mode function framebuffer:_getNightWaveFormMode() return self.waveform_night @@ -261,7 +281,7 @@ local function mxc_update(fb, ioc_cmd, ioc_data, is_flashing, waveform_mode, x, -- (Sidebar: this is probably a kernel issue, the EPDC driver is responsible for the alignment fixup, -- c.f., epdc_process_update @ drivers/video/fbdev/mxc/mxc_epdc_v2_fb.c on a Kobo Mk. 7 kernel...). -- And regardless of alignment constraints, make sure the rectangle is strictly bounded inside the screen. - x, y, w, h = bb:getBoundedRect(x, y, w, h, dither and 8) + x, y, w, h = bb:getBoundedRect(x, y, w, h, dither and 8 or fb.alignment_constraint) -- The ioctl operates in the native rotation, so, make sure we rotate the rectangle as needed x, y, w, h = bb:getPhysicalRect(x, y, w, h) @@ -294,13 +314,14 @@ local function mxc_update(fb, ioc_cmd, ioc_data, is_flashing, waveform_mode, x, -- * true FULL update, -- * GC16_FAST update (i.e., popping-up a menu), -- then wait for submission of previous marker first. + -- NOTE: This is mainly used on Kindles local marker = fb.marker -- NOTE: Technically, we might not always want to wait for *exactly* the previous marker -- (we might actually want the one before that), but in the vast majority of cases, that's good enough, -- and saves us a lot of annoying and hard-to-get-right heuristics anyway ;). -- Make sure it's a valid marker, to avoid doing something stupid on our first update. -- Also make sure we haven't already waited on this marker ;). - if fb.mech_wait_update_submission + if fb.wait_for_submission_before and (is_flashing or fb:_isUIWaveFormMode(waveform_mode)) and (marker ~= 0 and marker ~= fb.dont_wait_for_marker) then fb.debug("refresh: wait for submission of (previous) marker", marker) @@ -363,10 +384,10 @@ local function mxc_update(fb, ioc_cmd, ioc_data, is_flashing, waveform_mode, x, end end - -- Handle REAGL promotion... + -- Handle promotion to FULL for the specific waveform modes that require it... -- NOTE: We need to do this here, because we rely on the pre-promotion actual is_flashing in previous heuristics. - if fb:_isREAGLWaveFormMode(waveform_mode) then - -- NOTE: REAGL updates always need to be full. + if fb:_isREAGLWaveFormMode(waveform_mode) or fb:_isKaleidoWaveFormMode(waveform_mode) then + -- NOTE: REAGL & Kaleido updates (almost) always need to be full. ioc_data.update_mode = C.UPDATE_MODE_FULL end @@ -397,6 +418,33 @@ local function mxc_update(fb, ioc_cmd, ioc_data, is_flashing, waveform_mode, x, -- And make sure we won't wait for it again, in case the next refresh trips one of our wait_for_* heuristics ;). fb.dont_wait_for_marker = marker end + + -- NOTE: For PARTIAL, as long as they're not DU/A2, we'll instead just wait for the update's submission, if that's available. + -- NOTE: This is mainly used on (MTK) Kobos. + if fb.wait_for_submission_after + and ioc_data.update_mode == C.UPDATE_MODE_PARTIAL + and fb:_isNotFastWaveFormMode(waveform_mode) + and (marker ~= 0 and marker ~= fb.dont_wait_for_marker) then + fb.debug("refresh: wait for submission of marker", marker) + if fb:mech_wait_update_submission(marker) == -1 then + local err = ffi.errno() + fb.debug("MXCFB_WAIT_FOR_UPDATE_SUBMISSION ioctl failed:", ffi.string(C.strerror(err))) + end + end + + -- NOTE: Jotting down some notes about the flickering of disappearing highlights on Kobo MTK: + -- * Waiting for complete *before* Fast helps when we do HL -> UnHL -> UI + -- * Waiting for complete *before* UI helps when we do HL -> UI (i.e., when we elide the UnHL) + -- Both are... pretty bad for latency, though. + -- * Waiting for submission *after* Fast doesn't help. + -- * Waiting for complete *after* Fast helps everywhere, but obviously murders latency, too. + -- FWIW, Nickel does (using AUTO everywhere): Complete -> HL -> Submission -> UI -> Complete -> UnHL -> Submission + -- * Doing something similar with our usual DU + AUTO combos doesn't help. + -- NOTE: Much like on lab126 MTK, bumping UIManager:yieldToEPDC to something along the lines of 175ms helps, + -- but is also obviously not desirable latency-wise. + -- Interestingly enough, lab126 devices are affected the *other* way around: + -- they tend to optimize *out* the highlight, instead of having trouble dealing with the unhighlight... + -- In a fun twist, the flickers are gone in night mode -_-". end local function refresh_k51(fb, is_flashing, waveform_mode, x, y, w, h) @@ -626,27 +674,19 @@ local function refresh_kobo_mk7(fb, is_flashing, waveform_mode, x, y, w, h, dith end local function refresh_kobo_mtk(fb, is_flashing, waveform_mode, x, y, w, h, dither) - -- FIXME: Enable this, assuming it actually fits our purpose. - -- Enable the appropriate flag when requesting an any->2bit update, provided we're not dithering. - -- NOTE: See FBInk note about DITHER + MONOCHROME - --[[ - if waveform_mode == C.HWTCON_WAVEFORM_MODE_DU and not dither then - fb.update_data.flags = C.HWTCON_FLAG_FORCE_A2_OUTPUT - else - fb.update_data.flags = 0 - end - --]] - fb.update_data.flags = 0 - -- Did we request HW dithering? if dither and fb.device:canHWDither() then fb.update_data.flags = bor(fb.update_data.flags, C.HWTCON_FLAG_USE_DITHERING) - if waveform_mode == C.HWTCON_WAVEFORM_MODE_DU or waveform_mode == C.HWTCON_WAVEFORM_MODE_A2 then + -- NOTE: We only use A2 for the virtual keyboard, and Nickel forgoes dithering in that context. + if waveform_mode == C.HWTCON_WAVEFORM_MODE_DU then fb.update_data.dither_mode = C.HWTCON_FLAG_USE_DITHERING_Y8_Y1_S else fb.update_data.dither_mode = C.HWTCON_FLAG_USE_DITHERING_Y8_Y4_S end + else + fb.update_data.flags = 0 + fb.update_data.dither_mode = 0 end return mxc_update(fb, C.HWTCON_SEND_UPDATE, fb.update_data, is_flashing, waveform_mode, x, y, w, h, dither) @@ -730,6 +770,16 @@ function framebuffer:refreshA2Imp(x, y, w, h, dither) self:mech_refresh(false, self.waveform_a2, x, y, w, h, dither) end +function framebuffer:refreshColorImp(x, y, w, h, dither) + self.debug("refresh: color", x, y, w, h, dither) + self:mech_refresh(false, self.waveform_color, x, y, w, h, dither) +end + +function framebuffer:refreshColorTextImp(x, y, w, h, dither) + self.debug("refresh: color text", x, y, w, h, dither) + self:mech_refresh(false, self.waveform_color_reagl, x, y, w, h, dither) +end + function framebuffer:refreshWaitForLastImp() if self.mech_wait_update_complete and self.dont_wait_for_marker ~= self.marker then self.debug("refresh: waiting for previous update", self.marker) @@ -754,6 +804,8 @@ function framebuffer:init() self.mech_refresh = refresh_k51 self.mech_wait_update_complete = kindle_pearl_mxc_wait_for_update_complete self.mech_wait_update_submission = kindle_mxc_wait_for_update_submission + -- Kindles wait for submission of the *previous* marker + self.wait_for_submission_before = true self.waveform_a2 = C.WAVEFORM_MODE_A2 self.waveform_fast = C.WAVEFORM_MODE_DU @@ -924,8 +976,15 @@ function framebuffer:init() if self.device:isMTK() then self.mech_refresh = refresh_kobo_mtk self.mech_wait_update_complete = kobo_mtk_wait_for_update_complete - -- FIXME: Enable this, assuming it behaves... - --self.mech_wait_update_submission = kobo_mtk_wait_for_update_submission + self.mech_wait_update_submission = kobo_mtk_wait_for_update_submission + -- Kobos wait for submission of the *just sent* marker + self.wait_for_submission_after = true + + -- Regardless of dithering, there appears to be an off-by-one issue somewhere in the driver, + -- so partial refreshes at exact coordinates will sometimes be cut-off one pixel short... + -- That can obviously lead to leftover stale content visible on screen, + -- so, just enforce larger refresh regions on our side... + self.alignment_constraint = 8 self.waveform_a2 = C.HWTCON_WAVEFORM_MODE_A2 self.waveform_fast = C.HWTCON_WAVEFORM_MODE_DU @@ -933,12 +992,39 @@ function framebuffer:init() self.waveform_flashui = self.waveform_ui self.waveform_full = C.HWTCON_WAVEFORM_MODE_GC16 -- REAGL is *always* available - self.waveform_partial = C.HWTCON_WAVEFORM_MODE_GLR16 + self.waveform_reagl = C.HWTCON_WAVEFORM_MODE_GLR16 + self.waveform_partial = self.waveform_reagl -- Eclipse waveform modes are *always* available self.waveform_night = C.HWTCON_WAVEFORM_MODE_GLKW16 self.waveform_flashnight = C.HWTCON_WAVEFORM_MODE_GCK16 + self.night_is_reagl = true + -- Kaleido waveform modes are only ever used *conditionally* + self.waveform_color = C.HWTCON_WAVEFORM_MODE_GCC16 + self.waveform_color_reagl = C.HWTCON_WAVEFORM_MODE_GLRC16 self.mech_poweron = kobo_mtk_wakeup_epdc + + -- The Elipsa 2E was the first MTK device, and it does... a few things differently :/. + if self.device.model == "Kobo_condor" then + -- It doesn't use WAIT_FOR_UPDATE_SUBMISSION + self.wait_for_submission_after = false + -- It does *NOT* enforce FULL on REAGL updates + self.waveform_reagl = nil + self.night_is_reagl = false + -- For some mysterious reason, Eclipse waveform modes are completely broken outside of 32bpp. + -- (This is no longer a concern on later devices, as they no longer allow switching to 8bpp at all). + if self.fb_bb ~= 32 then + -- Trust @katadelos and let AUTO figure it out (https://github.com/koreader/koreader-base/pull/1768) + self.waveform_night = C.HWTCON_WAVEFORM_MODE_AUTO + self.waveform_flashnight = C.HWTCON_WAVEFORM_MODE_AUTO + end + end + + -- Disable Kaleido refresh imps when it's unsupported + if not self.device:hasColorScreen() then + self.refreshColorImp = self.refreshPartialImp + self.refreshColorTextImp = self.refreshPartialImp + end end local bypass_wait_for = self:getMxcWaitForBypass() diff --git a/ffi/framebuffer_sunxi.lua b/ffi/framebuffer_sunxi.lua index 4896baefc..815f86328 100644 --- a/ffi/framebuffer_sunxi.lua +++ b/ffi/framebuffer_sunxi.lua @@ -16,11 +16,12 @@ local framebuffer = { mech_poweron = nil, mech_wait_update_complete = nil, mech_wait_update_submission = nil, - waveform_partial = nil, + waveform_a2 = nil, + waveform_fast = nil, waveform_ui = nil, + waveform_partial = nil, waveform_flashui = nil, waveform_full = nil, - waveform_fast = nil, waveform_reagl = nil, waveform_night = nil, waveform_flashnight = nil, diff --git a/ffi/mxcfb_kobo_h.lua b/ffi/mxcfb_kobo_h.lua index 02b23d144..1efb11e39 100644 --- a/ffi/mxcfb_kobo_h.lua +++ b/ffi/mxcfb_kobo_h.lua @@ -109,6 +109,8 @@ enum HWTCON_WAVEFORM_MODE_ENUM { HWTCON_WAVEFORM_MODE_A2 = 6, HWTCON_WAVEFORM_MODE_GCK16 = 8, HWTCON_WAVEFORM_MODE_GLKW16 = 9, + HWTCON_WAVEFORM_MODE_GCC16 = 10, + HWTCON_WAVEFORM_MODE_GLRC16 = 11, HWTCON_WAVEFORM_MODE_AUTO = 257, }; enum hwtcon_dithering_mode { diff --git a/thirdparty/fbink/CMakeLists.txt b/thirdparty/fbink/CMakeLists.txt index f774030ce..eb867eea8 100644 --- a/thirdparty/fbink/CMakeLists.txt +++ b/thirdparty/fbink/CMakeLists.txt @@ -65,7 +65,7 @@ endif() ko_write_gitclone_script( GIT_CLONE_SCRIPT_FILENAME https://github.com/NiLuJe/FBInk.git - 1a989b30a195ca240a3cf37f9de61b4b3c7e891c + a60c6c814b910d24be5b9be43d1b4514a8ef6aef ${SOURCE_DIR} ) diff --git a/thirdparty/kobo-usbms/CMakeLists.txt b/thirdparty/kobo-usbms/CMakeLists.txt index 635a5f52d..b0a2d364b 100644 --- a/thirdparty/kobo-usbms/CMakeLists.txt +++ b/thirdparty/kobo-usbms/CMakeLists.txt @@ -25,7 +25,7 @@ endif() ko_write_gitclone_script( GIT_CLONE_SCRIPT_FILENAME https://github.com/koreader/KoboUSBMS.git - v1.3.9 + ef5b3a4f13565b0b282ce377517b6da252884597 ${SOURCE_DIR} ) diff --git a/thirdparty/luajit/CMakeLists.txt b/thirdparty/luajit/CMakeLists.txt index bef1aab53..7cf346e5b 100644 --- a/thirdparty/luajit/CMakeLists.txt +++ b/thirdparty/luajit/CMakeLists.txt @@ -78,7 +78,7 @@ list(APPEND BUILD_CMD COMMAND ${KO_MAKE_RECURSIVE} ${BUILD_ARGS}) ko_write_gitclone_script( GIT_CLONE_SCRIPT_FILENAME https://github.com/LuaJIT/LuaJIT - d06beb0480c5d1eb53b3343e78063950275aa281 + 5790d253972c9d78a0c2aece527eda5b134bbbf7 ${SOURCE_DIR} )