cadmus_core/framebuffer/
kobo2.rs

1use super::ion_sys::*;
2use super::linuxfb_sys::*;
3use super::sunxi_sys::*;
4use super::transform::*;
5use super::{Framebuffer, UpdateMode};
6use crate::color::Color;
7use crate::device::CURRENT_DEVICE;
8use crate::geom::Rectangle;
9use anyhow::{Context, Error};
10use std::fs::File;
11use std::io;
12use std::mem;
13use std::mem::ManuallyDrop;
14use std::ops::Drop;
15use std::os::unix::io::AsRawFd;
16use std::path::Path;
17use std::ptr;
18use std::slice;
19
20impl From<Rectangle> for AreaInfo {
21    fn from(rect: Rectangle) -> Self {
22        AreaInfo {
23            x_top: rect.min.x as libc::c_uint,
24            y_top: rect.min.y as libc::c_uint,
25            x_bottom: (rect.max.x - 1) as libc::c_uint,
26            y_bottom: (rect.max.y - 1) as libc::c_uint,
27        }
28    }
29}
30
31pub struct KoboFramebuffer2 {
32    ion: File,
33    display: File,
34    fd_data: IonFdData,
35    layer: DispLayerConfig2,
36    frame: *mut libc::c_void,
37    frame_size: usize,
38    alloc_size: libc::size_t,
39    var_info: VarScreenInfo,
40    fix_info: FixScreenInfo,
41    transform: ColorTransform,
42    token: u32,
43    monochrome: bool,
44    inverted: bool,
45    dithered: bool,
46}
47
48const MEM_ALIGN: u32 = 4096;
49
50#[inline]
51fn align(value: u32, align: u32) -> u32 {
52    let mask = align - 1;
53    (value + mask) & !mask
54}
55
56impl KoboFramebuffer2 {
57    pub fn new<P: AsRef<Path>>(path: P) -> Result<KoboFramebuffer2, Error> {
58        let file = File::open(&path).with_context(|| {
59            format!("can't open framebuffer device {}", path.as_ref().display())
60        })?;
61
62        let mut var_info = var_screen_info(&file)?;
63        let mut fix_info = fix_screen_info(&file)?;
64
65        var_info.rotate = CURRENT_DEVICE.startup_rotation() as u32;
66
67        if var_info.xres > var_info.yres {
68            mem::swap(&mut var_info.xres, &mut var_info.yres);
69        }
70
71        var_info.xres_virtual = var_info.xres;
72        var_info.yres_virtual = var_info.yres;
73        var_info.bits_per_pixel = 8;
74        var_info.grayscale = 1;
75        fix_info.line_length = var_info.xres_virtual;
76        fix_info.smem_len = fix_info.line_length * var_info.yres_virtual;
77
78        let ion = File::open("/dev/ion").with_context(|| "can't open ion device")?;
79
80        let alloc_size = align(fix_info.smem_len, MEM_ALIGN) as libc::size_t;
81
82        let mut data = IonAllocationData {
83            len: alloc_size,
84            align: MEM_ALIGN as libc::size_t,
85            heap_id_mask: ION_HEAP_MASK_CARVEOUT,
86            flags: 0,
87            handle: 0,
88        };
89
90        let result = unsafe { ion_alloc(ion.as_raw_fd(), &mut data) };
91
92        if let Err(e) = result {
93            return Err(Error::from(e).context("can't allocate memory"));
94        }
95
96        let mut data = IonFdData {
97            handle: data.handle,
98            fd: -1,
99        };
100
101        let result = unsafe { ion_map(ion.as_raw_fd(), &mut data) };
102
103        if let Err(e) = result {
104            let mut data = IonHandleData {
105                handle: data.handle,
106            };
107            let _ = unsafe { ion_free(ion.as_raw_fd(), &mut data) };
108            return Err(Error::from(e).context("can't get mappable file descriptor"));
109        }
110
111        let frame = unsafe {
112            libc::mmap(
113                ptr::null_mut(),
114                alloc_size,
115                libc::PROT_READ | libc::PROT_WRITE,
116                libc::MAP_SHARED,
117                data.fd,
118                0,
119            )
120        };
121
122        if frame == libc::MAP_FAILED {
123            unsafe { libc::close(data.fd) };
124            let mut data = IonHandleData {
125                handle: data.handle,
126            };
127            let _ = unsafe { ion_free(ion.as_raw_fd(), &mut data) };
128            return Err(Error::from(io::Error::last_os_error()).context("can't map memory"));
129        }
130
131        let display = File::open("/dev/disp");
132
133        if let Err(e) = display {
134            let _ = unsafe { libc::munmap(frame, alloc_size) };
135            unsafe { libc::close(data.fd) };
136            let mut data = IonHandleData {
137                handle: data.handle,
138            };
139            let _ = unsafe { ion_free(ion.as_raw_fd(), &mut data) };
140            return Err(Error::from(e).context("can't open display device"));
141        }
142
143        let frame_size = (var_info.yres * fix_info.line_length) as usize;
144
145        let layer = DispLayerConfig2 {
146            info: DispLayerInfo2 {
147                mode: LAYER_MODE_BUFFER,
148                zorder: 0,
149                alpha_mode: 1,
150                alpha_value: 0xFF,
151                screen_win: DispRect {
152                    x: 0,
153                    y: 0,
154                    width: var_info.xres,
155                    height: var_info.yres,
156                },
157                b_trd_out: false,
158                out_trd_mode: 0,
159                color_fb: ColorFb {
160                    fb: ManuallyDrop::new(DispFbInfo2 {
161                        fd: 0,
162                        y8_fd: data.fd,
163                        size: [
164                            DispRectsz {
165                                width: var_info.xres_virtual,
166                                height: var_info.yres_virtual,
167                            },
168                            DispRectsz {
169                                width: 0,
170                                height: 0,
171                            },
172                            DispRectsz {
173                                width: 0,
174                                height: 0,
175                            },
176                        ],
177                        align: [0; 3],
178                        format: DISP_FORMAT_8BIT_GRAY,
179                        color_space: DISP_GBR_F,
180                        trd_right_fd: 0,
181                        pre_multiply: true,
182                        crop: DispRect64 {
183                            x: 0,
184                            y: 0,
185                            width: (var_info.xres as libc::c_longlong) << 32,
186                            height: (var_info.yres as libc::c_longlong) << 32,
187                        },
188                        flags: DISP_BF_NORMAL,
189                        scan: DISP_SCAN_PROGRESSIVE,
190                        eotf: DISP_EOTF_GAMMA22,
191                        depth: 0,
192                        fbd_en: 0,
193                        metadata_fd: 0,
194                        metadata_size: 0,
195                        metadata_flag: 0,
196                    }),
197                },
198                id: 0,
199                atw: DispAtwInfo {
200                    used: false,
201                    mode: 0,
202                    b_row: 0,
203                    b_col: 0,
204                    colf_fd: 0,
205                },
206            },
207            enable: true,
208            channel: 0,
209            layer_id: 1,
210        };
211
212        Ok(KoboFramebuffer2 {
213            ion,
214            display: display.unwrap(),
215            fd_data: data,
216            layer,
217            frame,
218            frame_size,
219            alloc_size,
220            token: 1,
221            monochrome: false,
222            inverted: false,
223            dithered: false,
224            transform: transform_identity,
225            var_info,
226            fix_info,
227        })
228    }
229
230    fn as_bytes(&self) -> &[u8] {
231        unsafe { slice::from_raw_parts(self.frame as *const u8, self.frame_size) }
232    }
233
234    fn get_pixel(&self, x: u32, y: u32) -> Color {
235        let addr = (x + y * self.fix_info.line_length) as isize;
236        let c = unsafe { *(self.frame.offset(addr) as *const u8) };
237        Color::Gray(if self.inverted { 255 - c } else { c })
238    }
239}
240
241impl Framebuffer for KoboFramebuffer2 {
242    fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
243        let mut c = (self.transform)(x, y, color);
244        if self.inverted {
245            c.invert();
246        }
247        let addr = (x + y * self.fix_info.line_length) as isize;
248        let spot = unsafe { self.frame.offset(addr) as *mut u8 };
249        unsafe { *spot = c.gray() };
250    }
251
252    fn set_blended_pixel(&mut self, x: u32, y: u32, color: Color, alpha: f32) {
253        if alpha >= 1.0 {
254            self.set_pixel(x, y, color);
255            return;
256        }
257        let background = self.get_pixel(x, y);
258        let interp = background.lerp(color, alpha);
259        let c = (self.transform)(x, y, interp);
260        self.set_pixel(x, y, c);
261    }
262
263    fn invert_region(&mut self, rect: &Rectangle) {
264        for y in rect.min.y..rect.max.y {
265            for x in rect.min.x..rect.max.x {
266                let mut color = self.get_pixel(x as u32, y as u32);
267                color.invert();
268                self.set_pixel(x as u32, y as u32, color);
269            }
270        }
271    }
272
273    fn shift_region(&mut self, rect: &Rectangle, drift: u8) {
274        for y in rect.min.y..rect.max.y {
275            for x in rect.min.x..rect.max.x {
276                let mut color = self.get_pixel(x as u32, y as u32);
277                color.shift(drift);
278                self.set_pixel(x as u32, y as u32, color);
279            }
280        }
281    }
282
283    // Tell the driver that the screen needs to be redrawn.
284    fn update(&mut self, rect: &Rectangle, mode: UpdateMode) -> Result<u32, Error> {
285        let mut flags = 0;
286        let mut monochrome = self.monochrome;
287
288        let mut waveform_mode = match mode {
289            UpdateMode::Gui => EINK_GL16_MODE,
290            UpdateMode::Partial => EINK_GLR16_MODE,
291            UpdateMode::Full => {
292                monochrome = false;
293                EINK_GC16_MODE
294            }
295            UpdateMode::Fast => EINK_A2_MODE,
296            UpdateMode::FastMono => {
297                flags |= EINK_MONOCHROME;
298                EINK_A2_MODE
299            }
300        };
301
302        if self.inverted {
303            if waveform_mode == EINK_GL16_MODE || waveform_mode == EINK_GLR16_MODE {
304                waveform_mode = EINK_GLK16_MODE;
305            } else if waveform_mode == EINK_GC16_MODE {
306                waveform_mode = EINK_GCK16_MODE;
307            }
308        }
309
310        if mode != UpdateMode::Full && waveform_mode != EINK_AUTO_MODE {
311            flags |= EINK_PARTIAL_MODE;
312        }
313
314        if waveform_mode == EINK_A2_MODE {
315            flags |= EINK_MONOCHROME;
316        }
317
318        if mode == UpdateMode::Full {
319            flags |= EINK_NO_MERGE;
320        }
321
322        if waveform_mode == EINK_GLR16_MODE || waveform_mode == EINK_GLD16_MODE {
323            flags |= EINK_REGAL_MODE;
324        }
325
326        if monochrome && waveform_mode != EINK_A2_MODE {
327            waveform_mode = EINK_DU_MODE;
328            if !self.dithered {
329                flags |= EINK_DITHERING_Y1;
330            }
331        }
332
333        let area: AreaInfo = (*rect).into();
334
335        let mut update_data = SunxiDispEinkUpdate2 {
336            area: &area,
337            layer_num: 1,
338            update_mode: (waveform_mode | flags) as libc::c_ulong,
339            lyr_cfg2: &self.layer,
340            frame_id: &mut self.token as *mut libc::c_uint,
341            rotate: &(90 * self.rotation() as u32),
342            cfa_use: 0,
343        };
344
345        let result = unsafe { send_update(self.display.as_raw_fd(), &mut update_data) };
346
347        match result {
348            Err(e) => Err(Error::from(e).context("can't send framebuffer update")),
349            _ => Ok(self.token),
350        }
351    }
352
353    // Wait for a specific update to complete.
354    fn wait(&self, token: u32) -> Result<i32, Error> {
355        let marker_data = SunxiDispEinkWaitFrameSyncComplete { frame_id: token };
356        let result = unsafe { wait_for_update(self.display.as_raw_fd(), &marker_data) };
357        result.context("can't wait for framebuffer update")
358    }
359
360    fn save(&self, path: &str) -> Result<(), Error> {
361        let (width, height) = self.dims();
362        let file =
363            File::create(path).with_context(|| format!("can't create output file {}", path))?;
364        let mut encoder = png::Encoder::new(file, width, height);
365        encoder.set_depth(png::BitDepth::Eight);
366        encoder.set_color(png::ColorType::Grayscale);
367        let mut writer = encoder
368            .write_header()
369            .with_context(|| format!("can't write PNG header for {}", path))?;
370        writer
371            .write_image_data(self.as_bytes())
372            .with_context(|| format!("can't write PNG data to {}", path))?;
373        Ok(())
374    }
375
376    #[inline]
377    fn rotation(&self) -> i8 {
378        self.var_info.rotate as i8
379    }
380
381    fn set_rotation(&mut self, n: i8) -> Result<(u32, u32), Error> {
382        let delta = (self.rotation() - n).abs();
383
384        if delta % 2 == 1 {
385            mem::swap(&mut self.var_info.xres, &mut self.var_info.yres);
386            mem::swap(
387                &mut self.var_info.xres_virtual,
388                &mut self.var_info.yres_virtual,
389            );
390            mem::swap(
391                &mut self.layer.info.screen_win.width,
392                &mut self.layer.info.screen_win.height,
393            );
394            unsafe {
395                let rect = &mut (*self.layer.info.color_fb.fb).size[0];
396                mem::swap(&mut rect.width, &mut rect.height);
397                let rect = &mut (*self.layer.info.color_fb.fb).crop;
398                mem::swap(&mut rect.width, &mut rect.height);
399            }
400            self.fix_info.line_length = self.var_info.xres_virtual;
401            self.fix_info.smem_len = self.fix_info.line_length * self.var_info.yres_virtual;
402            self.frame_size = (self.var_info.yres * self.fix_info.line_length) as usize;
403        }
404
405        self.var_info.rotate = n as u32;
406        Ok((self.var_info.xres, self.var_info.yres))
407    }
408
409    fn set_inverted(&mut self, enable: bool) {
410        if enable == self.inverted {
411            return;
412        }
413
414        self.inverted = enable;
415    }
416
417    fn inverted(&self) -> bool {
418        self.inverted
419    }
420
421    fn set_monochrome(&mut self, enable: bool) {
422        self.monochrome = enable;
423    }
424
425    fn monochrome(&self) -> bool {
426        self.monochrome
427    }
428
429    fn set_dithered(&mut self, enable: bool) {
430        if enable == self.dithered {
431            return;
432        }
433
434        self.dithered = enable;
435
436        if enable {
437            self.transform = transform_dither_g16;
438        } else {
439            self.transform = transform_identity;
440        }
441    }
442
443    fn dithered(&self) -> bool {
444        self.dithered
445    }
446
447    fn width(&self) -> u32 {
448        self.var_info.xres
449    }
450
451    fn height(&self) -> u32 {
452        self.var_info.yres
453    }
454}
455
456impl Drop for KoboFramebuffer2 {
457    fn drop(&mut self) {
458        unsafe {
459            libc::munmap(self.frame, self.alloc_size);
460            libc::close(self.fd_data.fd);
461            let mut data = IonHandleData {
462                handle: self.fd_data.handle,
463            };
464            let _ = ion_free(self.ion.as_raw_fd(), &mut data);
465            ManuallyDrop::drop(&mut self.layer.info.color_fb.fb);
466        }
467    }
468}