unplugged-kernel/drivers/misc/mediatek/ext_disp/mt6768/extd_epd.c

403 lines
9.8 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
/*****************************************************************************/
/*****************************************************************************/
#include "extd_info.h"
#if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \
(CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 1)
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/atomic.h>
#include <linux/io.h>
#include "mach/irqs.h"
#include "ddp_irq.h"
#include "ddp_info.h"
#include "mtkfb_fence.h"
#include "mtkfb_info.h"
#include "epd_drv.h"
#include "external_display.h"
#include "extd_log.h"
#include "extd_platform.h"
/* ~~~~~~~the static variabl~~~~~~~~~ */
static int epd_layer_num;
atomic_t epd_state = ATOMIC_INIT(0);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* ~~~~~~~the gloable variable~~~~~~~ */
LCM_EPD_PARAMS EPD_Params;
disp_ddp_path_config extd_epd_params;
struct task_struct *epd_fence_release_task;
wait_queue_head_t epd_fence_release_wq;
atomic_t epd_fence_release_event = ATOMIC_INIT(0);
wait_queue_head_t epd_vsync_wq;
atomic_t epd_vsync_event = ATOMIC_INIT(0);
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* ~~~~~~~~~~the definition~~~~~~~~~ */
enum EPD_POWER_STATE {
EPD_STATE_OFF = 0,
EPD_STATE_ON,
EPD_STATE_STANDBY,
};
#define IS_EPD_ON() (atomic_read(&epd_state) == EPD_STATE_ON)
#define IS_EPD_OFF() (atomic_read(&epd_state) == EPD_STATE_OFF)
#define IS_EPD_STANDBY() (atomic_read(&epd_state) == EPD_STATE_STANDBY)
#define SET_EPD_ON() atomic_set(&epd_state, EPD_STATE_ON)
#define SET_EPD_OFF() atomic_set(&epd_state, EPD_STATE_OFF)
#define SET_EPD_STANDBY() atomic_set(&epd_state, EPD_STATE_STANDBY)
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* ~~~~~~~~~extern declare~~~~~~~~~ */
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
static void _epd_rdma_irq_handler(DISP_MODULE_ENUM module, unsigned int param)
{
/*RET_VOID_IF_NOLOG(!IS_EPD_ON()); */
if (!IS_EPD_ON())
return;
if (param & 0x2) { /* start */
atomic_set(&epd_fence_release_event, 1);
wake_up_interruptible(&epd_fence_release_wq);
/* vsync */
atomic_set(&epd_vsync_event, 1);
wake_up_interruptible(&epd_vsync_wq);
}
}
/* extern int ddp_dpi_dump(DISP_MODULE_ENUM module, int level); */
static int epd_fence_release_kthread(void *data)
{
struct sched_param param = {.sched_priority = 94 };
sched_setscheduler(current, SCHED_RR, &param);
unsigned int session_id = 0;
int fence_idx = 0;
unsigned long input_curr_addr;
for (;;) {
wait_event_interruptible(epd_fence_release_wq,
atomic_read(&epd_fence_release_event));
atomic_set(&epd_fence_release_event, 0);
session_id = ext_disp_get_sess_id();
fence_idx = -1;
if (session_id == 0)
continue;
ext_disp_get_curr_addr(&input_curr_addr, 0);
fence_idx =
disp_sync_find_fence_idx_by_addr(session_id, 0,
input_curr_addr);
mtkfb_release_fence(session_id, 0, fence_idx);
if (kthread_should_stop())
break;
}
return 0;
}
int epd_waitVsync(void)
{
unsigned int session_id = ext_disp_get_sess_id();
disp_session_sync_info *session_info =
disp_get_session_sync_info_for_debug(session_id);
if (session_info)
dprec_start(&session_info->event_waitvsync, 0, 0);
if (!IS_EPD_ON()) {
EXTDERR("[epd]:epd has suspend, return directly\n");
msleep(20);
return 0;
}
if (wait_event_interruptible_timeout
(epd_vsync_wq, atomic_read(&epd_vsync_event), HZ / 10) == 0)
EXTD_ERR("[epd] Wait VSync timeout. early_suspend=%d\n",
IS_EPD_ON());
atomic_set(&epd_vsync_event, 0);
if (session_info)
dprec_done(&session_info->event_waitvsync, 1, 0);
return 0;
}
/*static*/ void epd_suspend(void)
{
EXTDFUNC();
/*RET_VOID_IF(!IS_EPD_ON());*/
if (IS_EPD_ON())
SET_EPD_STANDBY();
}
/*static*/ void epd_resume(void)
{
EXTDFUNC();
/*RET_VOID_IF(!IS_EPD_STANDBY());*/
if (IS_EPD_STANDBY())
SET_EPD_ON();
}
void epd_enable(int enable)
{
EXTDFUNC();
}
void epd_power_enable(int enable)
{
EXTDFUNC();
if (enable) {
/*RET_VOID_IF(!IS_EPD_OFF());*/
/* need add actions */
if (IS_EPD_OFF())
SET_EPD_ON();
} else {
/*RET_VOID_IF(IS_EPD_OFF());*/
/* need add actions */
if (!IS_EPD_OFF())
SET_EPD_OFF();
}
}
int epd_get_dev_info(int is_sf, void *info)
{
int ret = 0;
unsigned int Eink_width = 0;
unsigned int Eink_height = 0;
EPD_DRIVER *epd_drv = (EPD_DRIVER *) EPD_GetDriver();
if (epd_drv == NULL) {
EXTDERR("[epd]%s, can not get epd driver handle\n", __func__);
return -EFAULT;
}
if (epd_drv->get_screen_size)
epd_drv->get_screen_size(&Eink_width, &Eink_height);
if (is_sf == AP_GET_INFO) {
int displayid = 0;
mtk_dispif_info_t epd_info;
if (!info) {
EXTDERR("ioctl pointer is NULL\n");
return -EFAULT;
}
if (copy_from_user(&displayid, info, sizeof(displayid))) {
EXTDERR(": copy_from_user failed! line:%d\n", __LINE__);
return -EAGAIN;
}
memset(&epd_info, 0, sizeof(mtk_dispif_info_t));
epd_info.displayFormat = DISPIF_FORMAT_RGB888;
epd_info.displayHeight = Eink_height;
epd_info.displayWidth = Eink_width;
epd_info.display_id = displayid;
epd_info.isConnected = 1;
epd_info.displayMode = DISP_IF_MODE_VIDEO;
epd_info.displayType = DISP_IF_EPD;
epd_info.vsyncFPS = 6000;
epd_info.isHwVsyncAvailable = 1;
if (copy_to_user(info, &epd_info, sizeof(mtk_dispif_info_t))) {
EXTDERR("copy_to_user failed! line:%d\n", __LINE__);
ret = -EFAULT;
}
EXTDINFO("DEV_INFO configuration get displayType-%d\n",
epd_info.displayType);
} else if (is_sf == SF_GET_INFO) {
disp_session_info *dispif_info = (disp_session_info *) info;
memset((void *)dispif_info, 0, sizeof(disp_session_info));
dispif_info->maxLayerNum = epd_layer_num;
dispif_info->displayFormat = DISPIF_FORMAT_RGB888;
dispif_info->displayHeight = Eink_height;
dispif_info->displayWidth = Eink_width;
dispif_info->displayMode = DISP_IF_MODE_VIDEO;
dispif_info->isConnected = 1;
dispif_info->displayType = DISP_IF_EPD;
dispif_info->vsyncFPS = extd_epd_params.fps * 100;
dispif_info->isHwVsyncAvailable = 1;
EXTDINFO("%s lays:%d, type:%d, W:%d, H:%d\n",
__func__, dispif_info->maxLayerNum,
dispif_info->displayType, dispif_info->displayWidth,
dispif_info->displayHeight);
}
return ret;
}
int epd_get_device_type(void)
{
int device_type = -1;
device_type = DISP_IF_EPD;
return device_type;
}
void epd_set_layer_num(int layer_num)
{
if (layer_num >= 0)
epd_layer_num = (layer_num == 0 ? 0 : 1);
}
int epd_ioctl(unsigned int ioctl_cmd, int param1, int param2,
unsigned long *params)
{
EXTDINFO("%s ioctl_cmd:%d\n", __func__, ioctl_cmd);
int ret = 0;
switch (ioctl_cmd) {
case RECOMPUTE_BG_CMD:
/* */
break;
case GET_DEV_TYPE_CMD:
ret = epd_get_device_type();
break;
case SET_LAYER_NUM_CMD:
epd_set_layer_num(param1);
break;
default:
EXTDERR("%s unknown command\n", __func__);
break;
}
return ret;
}
void epd_init(void)
{
EXTDMSG("%s in+!\n", __func__);
memset((void *)&EPD_Params, 0, sizeof(LCM_EPD_PARAMS));
memset((void *)&extd_epd_params, 0, sizeof(disp_ddp_path_config));
EPD_DRIVER *epd_drv = (EPD_DRIVER *) EPD_GetDriver();
if (epd_drv == NULL) {
EXTDERR("[epd]%s, can not get epd driver handle\n", __func__);
return;
}
if (epd_drv->init) {
/* need to remove to power on function Donglei */
epd_drv->init();
}
if (epd_drv->get_params) {
epd_drv->get_params(&EPD_Params);
extd_epd_params.fps = EPD_Params.pannel_frq;
extd_epd_params.dispif_config.dpi.hsync_pulse_width =
EPD_Params.hsync_pulse_width;
extd_epd_params.dispif_config.dpi.hsync_back_porch =
EPD_Params.hsync_back_porch;
extd_epd_params.dispif_config.dpi.hsync_front_porch =
EPD_Params.hsync_front_porch;
extd_epd_params.dispif_config.dpi.vsync_pulse_width =
EPD_Params.vsync_pulse_width;
extd_epd_params.dispif_config.dpi.vsync_back_porch =
EPD_Params.vsync_back_porch;
extd_epd_params.dispif_config.dpi.vsync_front_porch =
EPD_Params.vsync_front_porch;
extd_epd_params.dispif_config.dpi.dpi_clock =
EPD_Params.PLL_CLOCK;
extd_epd_params.dispif_config.dpi.clk_pol = EPD_Params.clk_pol;
extd_epd_params.dispif_config.dpi.de_pol = EPD_Params.de_pol;
extd_epd_params.dispif_config.dpi.hsync_pol =
EPD_Params.hsync_pol;
extd_epd_params.dispif_config.dpi.vsync_pol =
EPD_Params.vsync_pol;
extd_epd_params.dispif_config.dpi.width = EPD_Params.width;
extd_epd_params.dispif_config.dpi.height = EPD_Params.height;
extd_epd_params.dispif_config.dpi.format =
LCM_DPI_FORMAT_RGB888;
extd_epd_params.dispif_config.dpi.rgb_order =
LCM_COLOR_ORDER_RGB;
extd_epd_params.dispif_config.dpi.i2x_en = EPD_Params.i2x_en;
extd_epd_params.dispif_config.dpi.i2x_edge =
EPD_Params.i2x_edge;
extd_epd_params.dispif_config.dpi.embsync = EPD_Params.embsync;
}
ext_disp_set_lcm_param(&(extd_epd_params.dispif_config));
init_waitqueue_head(&epd_fence_release_wq);
init_waitqueue_head(&epd_vsync_wq);
if (!epd_fence_release_task) {
disp_register_module_irq_callback(DISP_MODULE_RDMA,
_epd_rdma_irq_handler);
epd_fence_release_task =
kthread_create(epd_fence_release_kthread, NULL,
"epd_fence_release_kthread");
wake_up_process(epd_fence_release_task);
}
SET_EPD_OFF();
}
#endif
const struct EXTD_DRIVER *EXTD_EPD_Driver(void)
{
static const struct EXTD_DRIVER extd_driver_epd = {
#if defined(CONFIG_MTK_EPD_SUPPORT)
.init = epd_init,
.post_init = NULL,
.deinit = NULL,
.enable = epd_enable,
.power_enable = epd_power_enable,
.set_audio_enable = NULL,
.set_resolution = NULL,
.get_dev_info = epd_get_dev_info,
.get_capability = NULL,
.get_edid = NULL,
.wait_vsync = epd_waitVsync,
.fake_connect = NULL,
.factory_mode_test = NULL,
.ioctl = epd_ioctl
#else
.init = 0,
.post_init = 0
#endif
};
return &extd_driver_epd;
}