[PATCH v7 11/18] media: rppx1: Add support for Black Level Subtraction

From: Jai Luthra

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


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

Extend the RPPX1 driver to allow setting the Black Level Subtraction
(BLS) 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.

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 | 4 +
drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 5 +-
drivers/media/platform/dreamchip/rppx1/rppx1_bls.c | 110 +++++++++++++++++++++
3 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
index 788d26f619d1a96d16e9dc499d2763366f70be0a..c218a509098991943a5333f1df061d1aa5ec53d6 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c
@@ -16,6 +16,7 @@

static const struct v4l2_isp_params_block_type_info
rppx1_ext_params_blocks_info[] = {
+ RPPX1_PARAMS_BLOCK_INFO(BLS, bls),
RPPX1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
RPPX1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
RPPX1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
@@ -54,6 +55,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size,
block_offset += block->header.size;

switch (block->header.type) {
+ case RPPX1_PARAMS_BLOCK_TYPE_BLS:
+ module = &rpp->pre1.bls;
+ break;
case RPPX1_PARAMS_BLOCK_TYPE_AWB_GAIN:
module = &rpp->pre1.awbg;
break;
diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
index 6f3be95026f60252d05370421bb2ec6ea090ce6f..aa07de4c6892b4da0511f4854a06621cbfef3afa 100644
--- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
+++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c
@@ -20,8 +20,11 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf)
if (!rpp_module_call(&rpp->post.hist, fill_stats, &stats->params))
stats->meas_type |= RPPX1_STAT_HIST;

- if (isc & RPPX1_IRQ_ID_PRE1_EXM)
+ if (isc & RPPX1_IRQ_ID_PRE1_EXM) {
if (!rpp_module_call(&rpp->pre1.exm, fill_stats, &stats->params))
stats->meas_type |= RPPX1_STAT_AUTOEXP;
+
+ rpp_module_call(&rpp->pre1.bls, fill_stats, &stats->params);
+ }
}
EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr);
diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
index de7008befd8ea79f7bca974de7714399e8bf443c..1e5153f0ba3600c0c9ead0be4b8c0feb4c5fd9ad 100644
--- a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
+++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c
@@ -5,6 +5,7 @@
*/

#include "rpp_module.h"
+#include "rppx1.h"

#define BLS_VERSION_REG 0x0000

@@ -54,6 +55,115 @@ static int rppx1_bls_probe(struct rpp_module *mod)
return 0;
}

+static void
+rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4])
+{
+ static const unsigned int swap[4][4] = {
+ [RPP_RGGB] = { 0, 1, 2, 3 },
+ [RPP_GRBG] = { 1, 0, 3, 2 },
+ [RPP_GBRG] = { 2, 3, 0, 1 },
+ [RPP_BGGR] = { 3, 2, 1, 0 },
+ };
+
+ /* Swap to pattern used in our path, PRE1 or PRE2. */
+ struct rpp_module *acq = mod == &mod->rpp->pre1.bls ?
+ &mod->rpp->pre1.acq : &mod->rpp->pre2.bls;
+ enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern;
+
+ for (unsigned int i = 0; i < 4; ++i)
+ output[i] = input[swap[pattern][i]];
+}
+
+static int
+rppx1_bls_fill_params(struct rpp_module *mod,
+ const union rppx1_params_block *block,
+ rppx1_reg_write write, void *priv)
+{
+ const struct rppx1_params_bls_config *cfg = &block->bls;
+
+ /* If the modules is disabled, simply bypass it. */
+ if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ write(priv, mod->base + BLS_CTRL_REG, 0);
+ return 0;
+ }
+
+ u32 ctrl = BLS_CTRL_BLS_EN;
+
+ if (!cfg->enable_auto) {
+ static const u32 regs[] = {
+ BLS_A_FIXED_REG,
+ BLS_B_FIXED_REG,
+ BLS_C_FIXED_REG,
+ BLS_D_FIXED_REG,
+ };
+ u32 swapped[4];
+
+ rppx1_bls_swap_regs(mod, regs, swapped);
+
+ /*
+ * The native params are 24-bit + 1 signed bit, while the RPP
+ * can be 12, 20 or 24 bit + 1 signed bit. Figure out how much
+ * we need to adjust the input parameters.
+ */
+ const unsigned int shift = 24 - mod->info.bls.colorbits;
+
+ write(priv, mod->base + swapped[0], cfg->fixed_val.r >> shift);
+ write(priv, mod->base + swapped[1], cfg->fixed_val.gr >> shift);
+ write(priv, mod->base + swapped[2], cfg->fixed_val.gb >> shift);
+ write(priv, mod->base + swapped[3], cfg->fixed_val.b >> shift);
+ } else {
+ write(priv, mod->base + BLS_SAMPLES_REG, cfg->bls_samples);
+
+ if (cfg->en_windows & BIT(0)) {
+ write(priv, mod->base + BLS_H1_START_REG, cfg->bls_window1.h_offs);
+ write(priv, mod->base + BLS_H1_STOP_REG, cfg->bls_window1.h_size);
+ write(priv, mod->base + BLS_V1_START_REG, cfg->bls_window1.v_offs);
+ write(priv, mod->base + BLS_V1_STOP_REG, cfg->bls_window1.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN1;
+ }
+
+ if (cfg->en_windows & BIT(1)) {
+ write(priv, mod->base + BLS_H2_START_REG, cfg->bls_window2.h_offs);
+ write(priv, mod->base + BLS_H2_STOP_REG, cfg->bls_window2.h_size);
+ write(priv, mod->base + BLS_V2_START_REG, cfg->bls_window2.v_offs);
+ write(priv, mod->base + BLS_V2_STOP_REG, cfg->bls_window2.v_size);
+ ctrl |= BLS_CTRL_BLS_WIN2;
+ }
+
+ ctrl |= BLS_CTRL_BLS_MODE_MEASURED;
+ }
+
+ write(priv, mod->base + BLS_CTRL_REG, ctrl);
+
+ return 0;
+}
+
+static int rppx1_bls_fill_stats(struct rpp_module *mod,
+ struct rppx1_stat *stats)
+{
+ struct rppx1_bls_meas_val *bls = &stats->ae.bls_val;
+
+ static const u32 regs[] = {
+ BLS_A_MEASURED_REG,
+ BLS_B_MEASURED_REG,
+ BLS_C_MEASURED_REG,
+ BLS_D_MEASURED_REG,
+ };
+ u32 swapped[4];
+
+ rppx1_bls_swap_regs(mod, regs, swapped);
+
+ /* Return measurements at native hardware precision. */
+ bls->meas_r = rpp_module_read(mod, swapped[0]);
+ bls->meas_gr = rpp_module_read(mod, swapped[1]);
+ bls->meas_gb = rpp_module_read(mod, swapped[2]);
+ bls->meas_b = rpp_module_read(mod, swapped[3]);
+
+ return 0;
+}
+
const struct rpp_module_ops rppx1_bls_ops = {
.probe = rppx1_bls_probe,
+ .fill_params = rppx1_bls_fill_params,
+ .fill_stats = rppx1_bls_fill_stats
};

--
2.53.0