[PATCH v7 17/18] media: rppx1: Add support for Bilateral Denoising

From: Jai Luthra

Date: Fri Apr 10 2026 - 05:25:19 EST


From: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx>

Extend the RPPX1 driver to allow setting the Bilateral Denoising
configuration using the parameter buffer format. It uses the RPPX1
framework for parameters and its writer abstraction to allow the user to
control how (and when) configuration is applied to the RPPX1.

Compared to RkISP, RPP-X1 has dropped the hardware bit AWB_GAIN_COMP.
Luckily it's behavior is easy to emulate in software, so we have kept
the uAPI same.

Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@xxxxxxxxxxxx>
Signed-off-by: Jai Luthra <jai.luthra+renesas@xxxxxxxxxxxxxxxx>
---
.../media/platform/dreamchip/rppx1/rpp_params.c | 7 +
drivers/media/platform/dreamchip/rppx1/rppx1_bd.c | 150 +++++++++++++++++++++
2 files changed, 157 insertions(+)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 07ce9d22265a444038bb8bdc91c129a47cb94e0d..2847647dcf42d7975dee446c8e29c87828db746c 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -22,6 +22,8 @@ rppx1_ext_params_blocks_info[] = {
RPPX1_PARAMS_BLOCK_INFO(BDM, bdm),
RPPX1_PARAMS_BLOCK_INFO(CTK, ctk),
RPPX1_PARAMS_BLOCK_INFO(GOC, goc),
+ RPPX1_PARAMS_BLOCK_INFO(DPF, dpf),
+ RPPX1_PARAMS_BLOCK_INFO(DPF_STRENGTH, dpf_strength),
RPPX1_PARAMS_BLOCK_INFO(LSC, lsc),
RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
@@ -77,6 +79,11 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
case RPPX1_PARAMS_BLOCK_TYPE_GOC:
module = &rpp->hv.ga;
break;
+ case RPPX1_PARAMS_BLOCK_TYPE_DPF:
+ case RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH:
+ /* Both types handled by the same block. */
+ module = &rpp->pre1.bd;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_LSC:
module = &rpp->pre1.lsc;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
index acbfbcd595915fbb36221bc3e6a63cfdc954409e..0d629c3752905687dc67d1cd90ec6d6fd3249e93 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c
@@ -47,6 +47,156 @@ static int rppx1_bd_probe(struct rpp_module *mod)
return 0;
}

+static int
+rppx1_bd_fill_params_main(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_params_dpf_config *cfg = &block->dpf;
+ unsigned int isp_dpf_mode, spatial_coeff;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + DPF_MODE_REG, 0);
+ return 0;
+ }
+
+ /*
+ * RPP DB module version 4 and later removed the AWB_GAIN_COMP bit.
+ * Always use programmed nf-gains for gain compensation. Emulate the
+ * old behavior by programming default gains when gain compensation
+ * is not requested.
+ */
+ bool awb_gain_comp = false;
+
+ switch (cfg->gain.mode) {
+ case RPPX1_DPF_GAIN_USAGE_NF_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_USE_NF_GAIN;
+ break;
+ case RPPX1_DPF_GAIN_USAGE_LSC_GAINS:
+ isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_DPF_GAIN_USAGE_NF_LSC_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_USE_NF_GAIN | DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_DPF_GAIN_USAGE_AWB_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = 0;
+ break;
+ case RPPX1_DPF_GAIN_USAGE_AWB_LSC_GAINS:
+ awb_gain_comp = true;
+ isp_dpf_mode = DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case RPPX1_DPF_GAIN_USAGE_DISABLED:
+ default:
+ isp_dpf_mode = 0;
+ break;
+ }
+
+ /* NOTE: Hardware bit for scale_mode is inverted compared to RkISP1. */
+ if (cfg->nll.scale_mode == RPPX1_NLL_SCALE_LINEAR)
+ isp_dpf_mode |= DPF_MODE_NLL_SEGMENTATION;
+ if (cfg->rb_flt.fltsize == RPPX1_DPF_RB_FILTERSIZE_9x9)
+ isp_dpf_mode |= DPF_MODE_RB_FILTER_SIZE;
+ if (!cfg->rb_flt.r_enable)
+ isp_dpf_mode |= DPF_MODE_R_FILTER_OFF;
+ if (!cfg->rb_flt.b_enable)
+ isp_dpf_mode |= DPF_MODE_B_FILTER_OFF;
+ if (!cfg->g_flt.gb_enable)
+ isp_dpf_mode |= DPF_MODE_GB_FILTER_OFF;
+ if (!cfg->g_flt.gr_enable)
+ isp_dpf_mode |= DPF_MODE_GR_FILTER_OFF;
+
+ isp_dpf_mode |= DPF_MODE_DPF_ENABLE;
+
+ if (awb_gain_comp) {
+ write(priv, mod->base + DPF_NF_GAIN_B_REG, cfg->gain.nf_b_gain);
+ write(priv, mod->base + DPF_NF_GAIN_R_REG, cfg->gain.nf_r_gain);
+ write(priv, mod->base + DPF_NF_GAIN_GB_REG, cfg->gain.nf_gb_gain);
+ write(priv, mod->base + DPF_NF_GAIN_GR_REG, cfg->gain.nf_gr_gain);
+ } else {
+ write(priv, mod->base + DPF_NF_GAIN_B_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_R_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_GB_REG, 0x100);
+ write(priv, mod->base + DPF_NF_GAIN_GR_REG, 0x100);
+ }
+
+ for (unsigned int i = 0; i < RPPX1_DPF_MAX_NLF_COEFFS; i++) {
+ write(priv, mod->base + DPF_NLL_G_COEFF_REG(i), cfg->nll.coeff[i]);
+ write(priv, mod->base + DPF_NLL_RB_COEFF_REG(i), cfg->nll.coeff[i]);
+ }
+
+ spatial_coeff = cfg->g_flt.spatial_coeff[0] |
+ (cfg->g_flt.spatial_coeff[1] << 8) |
+ (cfg->g_flt.spatial_coeff[2] << 16) |
+ (cfg->g_flt.spatial_coeff[3] << 24);
+ write(priv, mod->base + DPF_S_WEIGHT_G_1_4_REG, spatial_coeff);
+
+ spatial_coeff = cfg->g_flt.spatial_coeff[4] |
+ (cfg->g_flt.spatial_coeff[5] << 8);
+ write(priv, mod->base + DPF_S_WEIGHT_G_5_6_REG, spatial_coeff);
+
+ spatial_coeff = cfg->rb_flt.spatial_coeff[0] |
+ (cfg->rb_flt.spatial_coeff[1] << 8) |
+ (cfg->rb_flt.spatial_coeff[2] << 16) |
+ (cfg->rb_flt.spatial_coeff[3] << 24);
+ write(priv, mod->base + DPF_S_WEIGHT_RB_1_4_REG, spatial_coeff);
+
+ spatial_coeff = cfg->rb_flt.spatial_coeff[4] |
+ (cfg->rb_flt.spatial_coeff[5] << 8);
+ write(priv, mod->base + DPF_S_WEIGHT_RB_5_6_REG, spatial_coeff);
+
+ /*
+ * Bilateral Denoising does not react on RPP_HDR_UPD::regs_gen_cfg_upd
+ * (see Table 25). A change in configuration needs write of 1 to
+ * RPP_HDR_UPD::regs_cfg_upd.
+ */
+ write(priv, 4, 1);
+
+ write(priv, mod->base + DPF_MODE_REG, isp_dpf_mode);
+
+ return 0;
+}
+
+static int
+rppx1_bd_fill_params_strength(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_params_dpf_strength_config *cfg = &block->dpfs;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + DPF_MODE_REG, 0);
+ return 0;
+ }
+
+ /* Module version 5 adds shadowing for mode and spatial weights. */
+ write(priv, mod->base + DPF_STRENGTH_R_REG, cfg->r);
+ write(priv, mod->base + DPF_STRENGTH_G_REG, cfg->g);
+ write(priv, mod->base + DPF_STRENGTH_B_REG, cfg->b);
+
+ return 0;
+}
+
+static int
+rppx1_bd_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_DPF:
+ return rppx1_bd_fill_params_main(mod, block, write, priv);
+ case RPPX1_PARAMS_BLOCK_TYPE_DPF_STRENGTH:
+ return rppx1_bd_fill_params_strength(mod, block, write, priv);
+ }
+
+ return -EINVAL;
+}
+
const struct rpp_module_ops rppx1_bd_ops = {
.probe = rppx1_bd_probe,
+ .fill_params = rppx1_bd_fill_params,
};

--
2.53.0