184 lines
5 KiB
C
184 lines
5 KiB
C
/*
|
|
epdc-show-bitmap.c
|
|
Displays a raw image to the EPDC framebuffer
|
|
|
|
Parabola-rM is a free operating system for the reMarakble tablet.
|
|
Copyright (C) 2020 Davis Remmel
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <linux/fb.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <linux/mxcfb.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
int main(int argc, char *argv[]) {
|
|
if (argc < 2) {
|
|
printf("Must pass an image as an argument.\n");
|
|
return 1;
|
|
}
|
|
|
|
int ret;
|
|
int fb = open("/dev/fb0", O_RDWR);
|
|
struct fb_var_screeninfo vinfo;
|
|
ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
|
|
if (0 != ret) {
|
|
printf("FBIOGET_VSCREENINFO failed with error %d"
|
|
", aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
vinfo.xres = 1872;
|
|
vinfo.yres = 1404;
|
|
vinfo.pixclock = 160000000;
|
|
vinfo.left_margin = 32;
|
|
vinfo.right_margin = 326;
|
|
vinfo.upper_margin = 4;
|
|
vinfo.lower_margin = 12;
|
|
vinfo.hsync_len = 44;
|
|
vinfo.vsync_len = 1;
|
|
vinfo.sync = 0;
|
|
vinfo.vmode = FB_VMODE_NONINTERLACED;
|
|
vinfo.accel_flags = 0;
|
|
vinfo.activate = FB_ACTIVATE_FORCE;
|
|
|
|
// Put screen info. Sometimes this fails when trying to set the
|
|
// pixclock. This may be a bug in the driver's arithmetic.
|
|
ret = ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "FBIOPUT_VSCREENINFO failed with error "
|
|
"%d, attempting To reset pixclock\n", ret);
|
|
vinfo.pixclock = 6250;
|
|
ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo);
|
|
vinfo.pixclock = 160000000;
|
|
ret = ioctl(fb, FBIOPUT_VSCREENINFO, &vinfo);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "FBIOPUT_VSCREENINFO failed "
|
|
"with error %d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
printf("x:%d y:%d activate:%d bpp:%d rotate:%d hsync_len:%d"
|
|
"vsync_len: %d sync:%d\n",
|
|
vinfo.xres, vinfo.yres, vinfo.activate,
|
|
vinfo.bits_per_pixel, vinfo.rotate, vinfo.hsync_len,
|
|
vinfo.vsync_len, vinfo.sync);
|
|
|
|
struct fb_fix_screeninfo finfo;
|
|
ret = ioctl(fb, FBIOGET_FSCREENINFO, &finfo);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "FBIOGET_FSCREENINFO failed with error "
|
|
"%d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
// In case the EPDC wasn't accessible
|
|
ret = ioctl(fb, MXCFB_ENABLE_EPDC_ACCESS);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "MXCFB_ENABLE_EPDC_ACCESS failed with "
|
|
"error %d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
// Set to partial mode to control update parameters
|
|
__u32 aumode = AUTO_UPDATE_MODE_REGION_MODE;
|
|
ret = ioctl(fb, MXCFB_SET_AUTO_UPDATE_MODE, &aumode);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "MXCFB_SET_AUTO_UPDATE_MODE failed "
|
|
"with error %d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
|
|
// No artifacts in display output
|
|
__u32 uscheme = UPDATE_SCHEME_SNAPSHOT;
|
|
ret = ioctl(fb, MXCFB_SET_UPDATE_SCHEME, &uscheme);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "MXCFB_SET_UPDATE_SCHEME failed with "
|
|
"error %d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
// Set up update (same region for all writes, gets reused)
|
|
struct mxcfb_update_data bupdate;
|
|
bupdate.update_region.left = 0;
|
|
bupdate.update_region.top = 0;
|
|
bupdate.update_region.width = 1872;
|
|
bupdate.update_region.height = 1404;
|
|
bupdate.waveform_mode = WAVEFORM_MODE_AUTO;
|
|
bupdate.update_mode = UPDATE_MODE_FULL;
|
|
bupdate.update_marker = 0;
|
|
bupdate.temp = TEMP_USE_AMBIENT;
|
|
bupdate.flags = 0;
|
|
|
|
struct mxcfb_update_marker_data updm;
|
|
updm.update_marker = 0;
|
|
|
|
// mmap to framebuffer
|
|
int buflength = vinfo.yres_virtual * finfo.line_length;
|
|
printf("buflength %d\n", buflength);
|
|
char * region = mmap(0, buflength, PROT_READ | PROT_WRITE,
|
|
MAP_SHARED, fb, (off_t)0);
|
|
if (region == MAP_FAILED) {
|
|
fprintf(stderr, "map failed!\n");
|
|
return 1;
|
|
}
|
|
|
|
// Write black
|
|
memset(region, 0x00, buflength);
|
|
ioctl(fb, MXCFB_SEND_UPDATE, &bupdate);
|
|
ioctl(fb, MXCFB_WAIT_FOR_UPDATE_COMPLETE, &updm);
|
|
|
|
// Write white
|
|
memset(region, 0xff, buflength);
|
|
ioctl(fb, MXCFB_SEND_UPDATE, &bupdate);
|
|
ioctl(fb, MXCFB_WAIT_FOR_UPDATE_COMPLETE, &updm);
|
|
|
|
// Write image
|
|
FILE *pattern = fopen(argv[1], "rb");
|
|
fseek(pattern, 0, SEEK_END);
|
|
long psize = ftell(pattern);
|
|
printf("psize is %d\n", psize);
|
|
fseek(pattern, 0, SEEK_SET);
|
|
|
|
if (psize != buflength) {
|
|
fprintf(stderr, "Image must match framebuffer size\n");
|
|
return 1;
|
|
}
|
|
|
|
char *buffer = malloc(psize);
|
|
fread(buffer, psize, 1, pattern);
|
|
fclose(pattern);
|
|
|
|
memcpy(region, buffer, psize);
|
|
ret = ioctl(fb, MXCFB_SEND_UPDATE, &bupdate);
|
|
ioctl(fb, MXCFB_WAIT_FOR_UPDATE_COMPLETE, &updm);
|
|
if (0 != ret) {
|
|
fprintf(stderr, "MXCFB_SEND_UPDATE failed with error "
|
|
"%d, aborting\n", ret);
|
|
return 1;
|
|
}
|
|
|
|
close(fb);
|
|
return 0;
|
|
}
|