/*
 * Copyright (c) 2019 Jan Van Winkel <jan.van_winkel@dxplore.eu>
 * Copyright 2023 NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/kernel.h>
#include <errno.h>

#include "lvgl_display.h"

#ifdef CONFIG_LV_Z_FLUSH_THREAD

K_SEM_DEFINE(flush_complete, 0, 1);
/* Message queue will only ever need to queue one message */
K_MSGQ_DEFINE(flush_queue, sizeof(struct lvgl_display_flush), 1, 1);

void lvgl_flush_thread_entry(void *arg1, void *arg2, void *arg3)
{
	struct lvgl_display_flush flush;
	struct lvgl_disp_data *data;

	while (1) {
		k_msgq_get(&flush_queue, &flush, K_FOREVER);
		data = (struct lvgl_disp_data *)flush.disp_drv->user_data;

		display_write(data->display_dev, flush.x, flush.y, &flush.desc,
			      flush.buf);

		lv_disp_flush_ready(flush.disp_drv);
		k_sem_give(&flush_complete);
	}
}

K_THREAD_DEFINE(lvgl_flush_thread, CONFIG_LV_Z_FLUSH_THREAD_STACK_SIZE,
		lvgl_flush_thread_entry, NULL, NULL, NULL,
		K_PRIO_COOP(CONFIG_LV_Z_FLUSH_THREAD_PRIO), 0, 0);


void lvgl_wait_cb(lv_disp_drv_t *disp_drv)
{
	k_sem_take(&flush_complete, K_FOREVER);
}

#endif /* CONFIG_LV_Z_FLUSH_THREAD */

int set_lvgl_rendering_cb(lv_disp_drv_t *disp_drv)
{
	int err = 0;
	struct lvgl_disp_data *data = (struct lvgl_disp_data *)disp_drv->user_data;

#ifdef CONFIG_LV_Z_FLUSH_THREAD
	disp_drv->wait_cb = lvgl_wait_cb;
#endif

	switch (data->cap.current_pixel_format) {
	case PIXEL_FORMAT_ARGB_8888:
		disp_drv->flush_cb = lvgl_flush_cb_32bit;
		disp_drv->rounder_cb = NULL;
#ifdef CONFIG_LV_COLOR_DEPTH_32
		disp_drv->set_px_cb = NULL;
#else
		disp_drv->set_px_cb = lvgl_set_px_cb_32bit;
#endif
		break;
	case PIXEL_FORMAT_RGB_888:
		disp_drv->flush_cb = lvgl_flush_cb_24bit;
		disp_drv->rounder_cb = NULL;
		disp_drv->set_px_cb = lvgl_set_px_cb_24bit;
		break;
	case PIXEL_FORMAT_RGB_565:
	case PIXEL_FORMAT_BGR_565:
		disp_drv->flush_cb = lvgl_flush_cb_16bit;
		disp_drv->rounder_cb = NULL;
#ifdef CONFIG_LV_COLOR_DEPTH_16
		disp_drv->set_px_cb = NULL;
#else
		disp_drv->set_px_cb = lvgl_set_px_cb_16bit;
#endif
		break;
	case PIXEL_FORMAT_MONO01:
	case PIXEL_FORMAT_MONO10:
		disp_drv->flush_cb = lvgl_flush_cb_mono;
		disp_drv->rounder_cb = lvgl_rounder_cb_mono;
		disp_drv->set_px_cb = lvgl_set_px_cb_mono;
		break;
	default:
		disp_drv->flush_cb = NULL;
		disp_drv->rounder_cb = NULL;
		disp_drv->set_px_cb = NULL;
		err = -ENOTSUP;
		break;
	}

	return err;
}

void lvgl_flush_display(struct lvgl_display_flush *request)
{
#ifdef CONFIG_LV_Z_FLUSH_THREAD
	/*
	 * LVGL will only start a flush once the previous one is complete,
	 * so we can reset the flush state semaphore here.
	 */
	k_sem_reset(&flush_complete);
	k_msgq_put(&flush_queue, request, K_FOREVER);
	/* Explicitly yield, in case the calling thread is a cooperative one */
	k_yield();
#else
	/* Write directly to the display */
	struct lvgl_disp_data *data =
		(struct lvgl_disp_data *)request->disp_drv->user_data;

	display_write(data->display_dev, request->x, request->y,
		      &request->desc, request->buf);
	lv_disp_flush_ready(request->disp_drv);
#endif
}
