/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
 * Copyright (C) 2021-2022, Ideas On Board
 *
 * RkISP1 Defect Pixel Cluster Correction control
 */

#include "dpcc.h"

#include <libcamera/base/log.h>

#include "libcamera/internal/yaml_parser.h"

#include "linux/rkisp1-config.h"

/**
 * \file dpcc.h
 */

namespace libcamera {

namespace ipa::rkisp1::algorithms {

/**
 * \class DefectPixelClusterCorrection
 * \brief RkISP1 Defect Pixel Cluster Correction control
 *
 * Depending of the sensor quality, some pixels can be defective and then
 * appear significantly brighter or darker than the other pixels.
 *
 * The Defect Pixel Cluster Correction algorithms is responsible to minimize
 * the impact of the pixels. This can be done with algorithms applied at run
 * time (on-the-fly method) or with a table of defective pixels. Only the first
 * method is supported for the moment.
 */

LOG_DEFINE_CATEGORY(RkISP1Dpcc)

DefectPixelClusterCorrection::DefectPixelClusterCorrection()
	: config_({})
{
}

/**
 * \copydoc libcamera::ipa::Algorithm::init
 */
int DefectPixelClusterCorrection::init([[maybe_unused]] IPAContext &context,
				       const YamlObject &tuningData)
{
	config_.mode = RKISP1_CIF_ISP_DPCC_MODE_STAGE1_ENABLE;
	config_.output_mode = RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_STAGE1_INCL_G_CENTER
			    | RKISP1_CIF_ISP_DPCC_OUTPUT_MODE_STAGE1_INCL_RB_CENTER;

	config_.set_use = tuningData["fixed-set"].get<bool>(false)
			? RKISP1_CIF_ISP_DPCC_SET_USE_STAGE1_USE_FIX_SET : 0;

	/* Get all defined sets to apply (up to 3). */
	const YamlObject &setsObject = tuningData["sets"];
	if (!setsObject.isList()) {
		LOG(RkISP1Dpcc, Error)
			<< "'sets' parameter not found in tuning file";
		return -EINVAL;
	}

	if (setsObject.size() > RKISP1_CIF_ISP_DPCC_METHODS_MAX) {
		LOG(RkISP1Dpcc, Error)
			<< "'sets' size in tuning file (" << setsObject.size()
			<< ") exceeds the maximum hardware capacity (3)";
		return -EINVAL;
	}

	for (std::size_t i = 0; i < setsObject.size(); ++i) {
		struct rkisp1_cif_isp_dpcc_methods_config &method = config_.methods[i];
		const YamlObject &set = setsObject[i];
		uint16_t value;

		/* Enable set if described in YAML tuning file. */
		config_.set_use |= 1 << i;

		/* PG Method */
		const YamlObject &pgObject = set["pg-factor"];

		if (pgObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_PG_GREEN_ENABLE;

			value = pgObject["green"].get<uint16_t>(0);
			method.pg_fac |= RKISP1_CIF_ISP_DPCC_PG_FAC_G(value);
		}

		if (pgObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_PG_RED_BLUE_ENABLE;

			value = pgObject["red-blue"].get<uint16_t>(0);
			method.pg_fac |= RKISP1_CIF_ISP_DPCC_PG_FAC_RB(value);
		}

		/* RO Method */
		const YamlObject &roObject = set["ro-limits"];

		if (roObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RO_GREEN_ENABLE;

			value = roObject["green"].get<uint16_t>(0);
			config_.ro_limits |=
				RKISP1_CIF_ISP_DPCC_RO_LIMITS_n_G(i, value);
		}

		if (roObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RO_RED_BLUE_ENABLE;

			value = roObject["red-blue"].get<uint16_t>(0);
			config_.ro_limits |=
				RKISP1_CIF_ISP_DPCC_RO_LIMITS_n_RB(i, value);
		}

		/* RG Method */
		const YamlObject &rgObject = set["rg-factor"];
		method.rg_fac = 0;

		if (rgObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RG_GREEN_ENABLE;

			value = rgObject["green"].get<uint16_t>(0);
			method.rg_fac |= RKISP1_CIF_ISP_DPCC_RG_FAC_G(value);
		}

		if (rgObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RG_RED_BLUE_ENABLE;

			value = rgObject["red-blue"].get<uint16_t>(0);
			method.rg_fac |= RKISP1_CIF_ISP_DPCC_RG_FAC_RB(value);
		}

		/* RND Method */
		const YamlObject &rndOffsetsObject = set["rnd-offsets"];

		if (rndOffsetsObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RND_GREEN_ENABLE;

			value = rndOffsetsObject["green"].get<uint16_t>(0);
			config_.rnd_offs |=
				RKISP1_CIF_ISP_DPCC_RND_OFFS_n_G(i, value);
		}

		if (rndOffsetsObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RND_RED_BLUE_ENABLE;

			value = rndOffsetsObject["red-blue"].get<uint16_t>(0);
			config_.rnd_offs |=
				RKISP1_CIF_ISP_DPCC_RND_OFFS_n_RB(i, value);
		}

		const YamlObject &rndThresholdObject = set["rnd-threshold"];
		method.rnd_thresh = 0;

		if (rndThresholdObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RND_GREEN_ENABLE;

			value = rndThresholdObject["green"].get<uint16_t>(0);
			method.rnd_thresh |=
				RKISP1_CIF_ISP_DPCC_RND_THRESH_G(value);
		}

		if (rndThresholdObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_RND_RED_BLUE_ENABLE;

			value = rndThresholdObject["red-blue"].get<uint16_t>(0);
			method.rnd_thresh |=
				RKISP1_CIF_ISP_DPCC_RND_THRESH_RB(value);
		}

		/* LC Method */
		const YamlObject &lcThresholdObject = set["line-threshold"];
		method.line_thresh = 0;

		if (lcThresholdObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_LC_GREEN_ENABLE;

			value = lcThresholdObject["green"].get<uint16_t>(0);
			method.line_thresh |=
				RKISP1_CIF_ISP_DPCC_LINE_THRESH_G(value);
		}

		if (lcThresholdObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_LC_RED_BLUE_ENABLE;

			value = lcThresholdObject["red-blue"].get<uint16_t>(0);
			method.line_thresh |=
				RKISP1_CIF_ISP_DPCC_LINE_THRESH_RB(value);
		}

		const YamlObject &lcTMadFactorObject = set["line-mad-factor"];
		method.line_mad_fac = 0;

		if (lcTMadFactorObject.contains("green")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_LC_GREEN_ENABLE;

			value = lcTMadFactorObject["green"].get<uint16_t>(0);
			method.line_mad_fac |=
				RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_G(value);
		}

		if (lcTMadFactorObject.contains("red-blue")) {
			method.method |=
				RKISP1_CIF_ISP_DPCC_METHODS_SET_LC_RED_BLUE_ENABLE;

			value = lcTMadFactorObject["red-blue"].get<uint16_t>(0);
			method.line_mad_fac |=
				RKISP1_CIF_ISP_DPCC_LINE_MAD_FAC_RB(value);
		}
	}

	return 0;
}

/**
 * \copydoc libcamera::ipa::Algorithm::prepare
 */
void DefectPixelClusterCorrection::prepare([[maybe_unused]] IPAContext &context,
					   const uint32_t frame,
					   [[maybe_unused]] IPAFrameContext &frameContext,
					   rkisp1_params_cfg *params)
{
	if (frame > 0)
		return;

	params->others.dpcc_config = config_;

	params->module_en_update |= RKISP1_CIF_ISP_MODULE_DPCC;
	params->module_ens |= RKISP1_CIF_ISP_MODULE_DPCC;
	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_DPCC;
}

REGISTER_IPA_ALGORITHM(DefectPixelClusterCorrection, "DefectPixelClusterCorrection")

} /* namespace ipa::rkisp1::algorithms */

} /* namespace libcamera */