/*
	epdc-init-auto.c
	Initializes the EPDC framebuffer into a deferred-IO automatic-update
	mode

	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>

int main()
{
	int ret;
	int fb = open("/dev/fb0", O_RDWR);
	struct fb_var_screeninfo vinfo;
	ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
	if (0 != ret) {
		fprintf(stderr, "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;
		}
	}

	// Pull the screeninfo agian
	ret = ioctl(fb, FBIOGET_VSCREENINFO, &vinfo);
	if (0 != ret) {
		fprintf(stderr, "FBIOGET_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 auto update mode
	__u32 aumode = AUTO_UPDATE_MODE_AUTOMATIC_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;
	}

	// Queue-and-merge is best-performing
	__u32 uscheme = UPDATE_SCHEME_QUEUE_AND_MERGE;
	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;
	}

	close(fb);
	return 0;
}