/* logging a event related with VPP */
static inline void disp_ss_event_log_vpp
	(disp_ss_event_t type, struct v4l2_subdev *sd, ktime_t time)
{
	struct decon_device *decon = get_decon_drvdata(__get_decon_id_for_vpp(sd));
	int idx = atomic_inc_return(&decon->disp_ss_log_idx) % DISP_EVENT_LOG_MAX;
	struct disp_ss_log *log = &decon->disp_ss_log[idx];
	struct vpp_dev *vpp = v4l2_get_subdevdata(sd);

	if (time.tv64)
		log->time = time;
	else
		log->time = ktime_get();
	log->type = type;

	switch (type) {
	case DISP_EVT_VPP_SUSPEND:
	case DISP_EVT_VPP_RESUME:
		log->data.pm.pm_status = pm_runtime_active(&vpp->pdev->dev);
		log->data.pm.elapsed = ktime_sub(ktime_get(), log->time);
		break;
	case DISP_EVT_VPP_FRAMEDONE:
	case DISP_EVT_VPP_STOP:
	case DISP_EVT_VPP_WINCON:
		log->data.vpp.id = vpp->id;
		log->data.vpp.start_cnt = vpp->start_count;
		log->data.vpp.done_cnt = vpp->done_count;
		break;
	default:
		log->data.vpp.id = vpp->id;
		break;
	}

	return;
}
/* get decon's id used by vpp */
static int __get_decon_id_for_vpp(struct v4l2_subdev *sd)
{
	struct decon_device *decon = get_decon_drvdata(0);
	struct vpp_dev *vpp = v4l2_get_subdevdata(sd);

	return decon->vpp_used[vpp->id]? 0 : 1;
}
/* logging a event related with DSIM */
static inline void disp_ss_event_log_dsim
	(disp_ss_event_t type, struct v4l2_subdev *sd, ktime_t time)
{
	struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
	struct decon_device *decon = get_decon_drvdata(dsim->id);
	int idx = atomic_inc_return(&decon->disp_ss_log_idx) % DISP_EVENT_LOG_MAX;
	struct disp_ss_log *log = &decon->disp_ss_log[idx];

	if (time.tv64)
		log->time = time;
	else
		log->time = ktime_get();
	log->type = type;

	switch (type) {
	case DISP_EVT_DSIM_SUSPEND:
	case DISP_EVT_DSIM_RESUME:
	case DISP_EVT_ENTER_ULPS:
	case DISP_EVT_EXIT_ULPS:
	case DISP_EVT_FRAMEDONE:
		log->data.pm.pm_status = pm_runtime_active(dsim->dev);
		log->data.pm.elapsed = ktime_sub(ktime_get(), log->time);
		break;
	default:
		/* Any remaining types will be log just time and type */
		break;
	}
}
dma_addr_t decon_map_sec_dma_buf(struct dma_buf *dbuf, int plane)
{
        struct decon_device *decon = get_decon_drvdata(0); /* 0: decon Int ID */

        if (!dbuf || (plane >= MAX_BUF_PLANE_CNT) || (plane < 0))
                return -EINVAL;

        dma.ion_handle = NULL;
        dma.fence = NULL;

        dma.dma_buf = dbuf;
	dma.attachment = dma_buf_attach(dbuf, decon->dev);

        if (IS_ERR(dma.attachment)) {
		decon_err("dma_buf_attach() failed: %ld\n",
				PTR_ERR(dma.attachment));
		goto err_buf_map_attach;
	}

	dma.sg_table = dma_buf_map_attachment(dma.attachment,
			DMA_TO_DEVICE);

	if (IS_ERR(dma.sg_table)) {
		decon_err("dma_buf_map_attachment() failed: %ld\n",
				PTR_ERR(dma.sg_table));
		goto err_buf_map_attachment;
	}

	dma.dma_addr = ion_iovmm_map(dma.attachment, 0,
			dma.dma_buf->size, DMA_TO_DEVICE, plane);

	if (IS_ERR_VALUE(dma.dma_addr)) {
		decon_err("iovmm_map() failed: %pa\n", &dma.dma_addr);
		goto err_iovmm_map;
	}

	exynos_ion_sync_dmabuf_for_device(decon->dev, dma.dma_buf,
			dma.dma_buf->size, DMA_TO_DEVICE);

	return dma.dma_addr;

err_iovmm_map:
	dma_buf_unmap_attachment(dma.attachment, dma.sg_table,
			DMA_TO_DEVICE);
err_buf_map_attachment:
	dma_buf_detach(dma.dma_buf, dma.attachment);
err_buf_map_attach:
        return 0;
}
void decon_free_sec_dma_buf(int plane)
{
	struct decon_device *decon = get_decon_drvdata(0); /* 0: decon Int ID */ 

	if (IS_ERR_VALUE(dma.dma_addr) || !dma.dma_buf)
		return;

	ion_iovmm_unmap(dma.attachment, dma.dma_addr);

	dma_buf_unmap_attachment(dma.attachment, dma.sg_table,
		DMA_TO_DEVICE);

	exynos_ion_sync_dmabuf_for_cpu(decon->dev, dma.dma_buf,
				dma.dma_buf->size, DMA_FROM_DEVICE);

	dma_buf_detach(dma.dma_buf, dma.attachment);
	memset(&dma, 0, sizeof(dma));
}
void DISP_SS_EVENT_LOG_DSIM_FRAMEDONE(struct v4l2_subdev *sd,
			struct disp_log_decon_frm_done* decon_sfr)
{
	struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
	struct decon_device *decon = get_decon_drvdata(dsim->id);
	int idx;
	struct disp_ss_log *log;

	if (!decon || IS_ERR_OR_NULL(decon->debug_event))
		return;

	idx = atomic_inc_return(&decon->disp_ss_log_idx) % DISP_EVENT_LOG_MAX;
	log = &decon->disp_ss_log[idx];

	log->time = ktime_get();
	log->type = DISP_EVT_DSIM_FRAMEDONE;

	memcpy(&log->data.decon_sfr, decon_sfr, sizeof(struct disp_log_decon_frm_done));
}
/* Common API to log a event related with DSIM COMMAND */
void DISP_SS_EVENT_LOG_CMD(struct v4l2_subdev *sd, u32 cmd_id, unsigned long data)
{
	struct dsim_device *dsim = container_of(sd, struct dsim_device, sd);
	struct decon_device *decon = get_decon_drvdata(dsim->id);
	int idx;
	struct disp_ss_log *log;

	if (!decon || IS_ERR_OR_NULL(decon->debug_event))
		return;

	idx = atomic_inc_return(&decon->disp_ss_log_idx) % DISP_EVENT_LOG_MAX;
	log = &decon->disp_ss_log[idx];

	log->time = ktime_get();
	log->type = DISP_EVT_DSIM_COMMAND;
	log->data.cmd_buf.id = cmd_id;
	if (cmd_id == MIPI_DSI_DCS_LONG_WRITE)
		log->data.cmd_buf.buf = *(u8 *)(data);
	else
		log->data.cmd_buf.buf = (u8)data;
}
/* Common API to log a event related with DECON/DSIM/VPP */
void DISP_SS_EVENT_LOG(disp_ss_event_t type, struct v4l2_subdev *sd, ktime_t time)
{
	struct decon_device *decon = get_decon_drvdata(0);

	if (!decon || IS_ERR_OR_NULL(decon->debug_event))
		return;

	/* log a eventy softly */
	switch (type) {
	case DISP_EVT_TE_INTERRUPT:
	case DISP_EVT_UNDERRUN:
		/* If occurs continuously, skipped. It is a burden */
		if (disp_ss_event_ignore(type, decon))
			break;
	case DISP_EVT_BLANK:
	case DISP_EVT_UNBLANK:
	case DISP_EVT_ENTER_LPD:
	case DISP_EVT_EXIT_LPD:
	case DISP_EVT_DECON_SUSPEND:
	case DISP_EVT_DECON_RESUME:
	case DISP_EVT_LINECNT_ZERO:
	case DISP_EVT_TRIG_MASK:
	case DISP_EVT_DECON_FRAMEDONE_WAIT:
	case DISP_EVT_WB_SET_BUFFER:
	case DISP_EVT_WB_SW_TRIGGER:
	case DISP_EVT_WB_TIMELINE_INC:
	case DISP_EVT_WB_FRAME_DONE:
	case DISP_EVT_TRIG_UNMASK:
	case DISP_EVT_TE_WAIT_DONE:
	case DISP_EVT_DECON_UPDATE_WAIT_DONE:
	case DISP_EVT_GIC_TE_ENABLE:
	case DISP_EVT_GIC_TE_DISABLE:
	case DISP_EVT_DECON_FRAMEDONE:
		disp_ss_event_log_decon(type, sd, time);
		break;
	case DISP_EVT_ENTER_ULPS:
	case DISP_EVT_EXIT_ULPS:
	case DISP_EVT_DSIM_FRAMEDONE:
	case DISP_EVT_FRAMEDONE:
		disp_ss_event_log_dsim(type, sd, time);
		break;
	case DISP_EVT_VPP_FRAMEDONE:
	case DISP_EVT_VPP_STOP:
	case DISP_EVT_VPP_WINCON:
	case DISP_EVT_VPP_UPDATE_DONE:
	case DISP_EVT_VPP_SHADOW_UPDATE:
		disp_ss_event_log_vpp(type, sd, time);
		break;
	default:
		break;
	}

	if (decon->disp_ss_log_level == DISP_EVENT_LEVEL_LOW)
		return;

	/* additionally logging hardly */
	switch (type) {
	case DISP_EVT_ACT_VSYNC:
	case DISP_EVT_DEACT_VSYNC:
	case DISP_EVT_WIN_CONFIG:
		disp_ss_event_log_decon(type, sd, time);
		break;
	
	case DISP_EVT_DSIM_SUSPEND:
	case DISP_EVT_DSIM_RESUME:
		disp_ss_event_log_dsim(type, sd, time);
		break;
	case DISP_EVT_VPP_SUSPEND:
	case DISP_EVT_VPP_RESUME:
		disp_ss_event_log_vpp(type, sd, time);
	default:
		break;
	}
}