/* Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. */

#include "dmub_abm.h"
#include "dmub_abm_cacp.h"
#include "dce_abm.h"
#include "dc.h"
#include "dc_dmub_srv.h"
#include "dmub/dmub_srv.h"
#include "core_types.h"

#define CACP_LEVEL_NUM 4

void dmub_cacp_init(struct abm *abm, const char *src, unsigned int bytes, unsigned int panel_inst)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;
	uint8_t panel_mask = 0x01 << panel_inst;
	struct dc_link *edp_links[MAX_NUM_EDP];
	unsigned int i, edp_num;

	// TODO: Optimize by only reading back final 4 bytes
	dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);

	// Copy iramtable into cw7
	memcpy(dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, (void *)src, bytes);

	memset(&cmd, 0, sizeof(cmd));
	// Fw will copy from cw7 to fw_state
	cmd.cacp_init_config.header.type = DMUB_CMD__CACP;
	cmd.cacp_init_config.header.sub_type = DMUB_CMD__CACP_INIT_CONFIG;
	cmd.cacp_init_config.cacp_init_config_data.src.quad_part = dc->dmub_srv->dmub->scratch_mem_fb.gpu_addr;
	cmd.cacp_init_config.cacp_init_config_data.bytes = (uint16_t)bytes;
	cmd.cacp_init_config.cacp_init_config_data.panel_mask = panel_mask;
	cmd.cacp_init_config.cacp_init_config_data.visual_confirm =
		(dc->dc->debug.visual_confirm == VISUAL_CONFIRM_ABM) ? true : false;

	cmd.cacp_init_config.header.payload_bytes = sizeof(struct dmub_cmd_cacp_init_config_data);

	dc_get_edp_links(dc->dc, edp_links, &edp_num);
	for (i = 0; i < edp_num; i++) {
		if (panel_inst == i)
			break;
	}

	if (i < edp_num) {
		cmd.cacp_init_config.cacp_init_config_data.strscl_valid =
			(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_valid;
		cmd.cacp_init_config.cacp_init_config_data.mode =
			edp_links[panel_inst]->panel_config.cacp.cacp_control_mode ?
			DMUB_CMD_CACP_CONTROL_MODE_1 : DMUB_CMD_CACP_CONTROL_MODE_0;
		for (int j = 0; j < CACP_LEVEL_NUM; j++) {
			cmd.cacp_init_config.cacp_init_config_data.strscl_sdr[j] =
				(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_sdr[j];
			cmd.cacp_init_config.cacp_init_config_data.strscl_hdr[j] =
				(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_hdr[j];
		}
	}

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}

bool dmub_cacp_set_level(struct abm *abm, unsigned int abm_level, unsigned char panel_mask)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_set_level.header.type = DMUB_CMD__CACP;
	cmd.cacp_set_level.header.sub_type = DMUB_CMD__CACP_SET_LEVEL;

	cmd.cacp_set_level.cacp_set_level_data.level = abm_level;
	cmd.cacp_set_level.cacp_set_level_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
	cmd.cacp_set_level.cacp_set_level_data.panel_mask = panel_mask;

	cmd.cacp_set_level.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_level_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	return true;
}

bool dmub_cacp_set_pipe(struct abm *abm, unsigned int otg_inst,
		unsigned int pipe_option, unsigned int panel_inst, unsigned int pwrseq_inst)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_set_pipe.header.type = DMUB_CMD__CACP;
	cmd.cacp_set_pipe.header.sub_type = DMUB_CMD__CACP_SET_PIPE;

	cmd.cacp_set_pipe.cacp_set_pipe_data.otg_inst = (uint8_t)otg_inst;
	cmd.cacp_set_pipe.cacp_set_pipe_data.panel_inst = (uint8_t)panel_inst;
	cmd.cacp_set_pipe.cacp_set_pipe_data.set_pipe_option = (uint8_t)pipe_option;
	cmd.cacp_set_pipe.cacp_set_pipe_data.pwrseq_inst = (uint8_t)pwrseq_inst;
	cmd.cacp_set_pipe.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_pipe_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	return true;
}

bool dmub_cacp_set_event(struct abm *abm, unsigned int full_screen, unsigned int trans_info,
		unsigned int hdr_mode, unsigned int scaling_enable, unsigned int panel_inst)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_set_event.header.type = DMUB_CMD__CACP;
	cmd.cacp_set_event.header.sub_type = DMUB_CMD__CACP_SET_EVENT;

	//TODO:
	cmd.cacp_set_event.cacp_set_event_data.full_screen_mode = (uint8_t)full_screen;
	cmd.cacp_set_event.cacp_set_event_data.trans_info = trans_info;
	cmd.cacp_set_event.cacp_set_event_data.hdr_mode = (uint8_t)hdr_mode;
	cmd.cacp_set_event.cacp_set_event_data.vb_scaling_enable = (uint8_t)scaling_enable;
	cmd.cacp_set_event.cacp_set_event_data.panel_mask = (1<<panel_inst);

	cmd.cacp_set_event.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_event_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	return true;
}

bool dmub_cacp_set_pause(struct abm *abm, bool pause, unsigned int panel_inst, unsigned int otg_inst)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_pause.header.type = DMUB_CMD__CACP;
	cmd.cacp_pause.header.sub_type = DMUB_CMD__CACP_PAUSE;

	cmd.cacp_pause.cacp_pause_data.panel_mask = (1<<panel_inst);
	cmd.cacp_pause.cacp_pause_data.otg_inst = (uint8_t)otg_inst;
	cmd.cacp_pause.cacp_pause_data.enable = pause;

	cmd.cacp_pause.header.payload_bytes = sizeof(struct dmub_cmd_cacp_pause_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	return true;
}

bool dmub_cacp_set_backlight_level(struct abm *abm,
		unsigned int backlight_pwm_u16_16,
		unsigned int frame_ramp,
		unsigned int panel_inst)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_set_backlight.header.type = DMUB_CMD__CACP;
	cmd.cacp_set_backlight.header.sub_type = DMUB_CMD__CACP_SET_BACKLIGHT;
	cmd.cacp_set_backlight.cacp_set_backlight_data.frame_ramp = frame_ramp;
	cmd.cacp_set_backlight.cacp_set_backlight_data.backlight_user_level = backlight_pwm_u16_16;
	cmd.cacp_set_backlight.cacp_set_backlight_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
	cmd.cacp_set_backlight.cacp_set_backlight_data.panel_mask = (0x01 << panel_inst);
	cmd.cacp_set_backlight.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_backlight_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	return true;
}

void dmub_cacp_enable_fractional_pwm(struct abm *abm, uint8_t panel_mask)
{
	union dmub_rb_cmd cmd;
	struct dc_context *dc = abm->ctx;
	uint32_t fractional_pwm = (dc->dc->config.disable_fractional_pwm == false) ? 1 : 0;

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_set_pwm_frac.header.type = DMUB_CMD__CACP;
	cmd.cacp_set_pwm_frac.header.sub_type = DMUB_CMD__CACP_SET_PWM_FRAC;
	cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.fractional_pwm = fractional_pwm;
	cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
	cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.panel_mask = panel_mask;
	cmd.cacp_set_pwm_frac.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_pwm_frac_data);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}

bool dmub_cacp_get_histogram(
		struct dc_context *dc,
		unsigned int panel_inst,
		unsigned int *histogram,
		enum dmub_abm_histogram_type histogram_type,
		unsigned int size)
{
	union dmub_rb_cmd cmd;

	dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);

	memset(&cmd, 0, sizeof(cmd));
	cmd.cacp_get_histogram.header.type = DMUB_CMD__CACP;
	cmd.cacp_get_histogram.header.sub_type = DMUB_CMD__CACP_GET_HISTOGRAM;

	cmd.cacp_get_histogram.dest.quad_part = dc->dmub_srv->dmub->scratch_mem_fb.gpu_addr;
	cmd.cacp_get_histogram.bytes = (uint16_t)size;
	cmd.cacp_get_histogram.panel_inst = (uint8_t)panel_inst;
	cmd.cacp_get_histogram.histogram_type = histogram_type;
	cmd.cacp_get_histogram.header.payload_bytes = sizeof(struct dmub_rb_cmd_cacp_get_histogram);

	dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);

	memcpy((void *)histogram, dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, size);

	return true;
}