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 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 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}