1b562e72bSNiklas Söderlund // SPDX-License-Identifier: GPL-2.0 2b562e72bSNiklas Söderlund /* 3b562e72bSNiklas Söderlund * Copyright (C) 2026 Renesas Electronics Corp. 4b562e72bSNiklas Söderlund * Copyright (C) 2026 Ideas on Board Oy 5b562e72bSNiklas Söderlund * Copyright (C) 2026 Ragnatech AB 6b562e72bSNiklas Söderlund */ 7b562e72bSNiklas Söderlund 8b562e72bSNiklas Söderlund #include "rpp_module.h" 9b562e72bSNiklas Söderlund 10*9a52f7deSNiklas Söderlund #include <linux/bitfield.h> 11*9a52f7deSNiklas Söderlund 12b562e72bSNiklas Söderlund #define HIST_VERSION_REG 0x0000 13b562e72bSNiklas Söderlund 14b562e72bSNiklas Söderlund #define HIST_CTRL_REG 0x0004 15b562e72bSNiklas Söderlund #define HIST_CTRL_HIST_UPDATE_ENABLE BIT(0) 16b562e72bSNiklas Söderlund 17b562e72bSNiklas Söderlund #define HIST_MODE_REG 0x0008 18b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_MASK GENMASK(2, 0) 19b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_DISABLE 0 20b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_YRGB 1 21b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_R 2 22b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_GR 3 23b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_B 4 24b562e72bSNiklas Söderlund #define HIST_MODE_HIST_MODE_GB 5 25b562e72bSNiklas Söderlund 26b562e72bSNiklas Söderlund #define HIST_CHANNEL_SEL_REG 0x000c 27b562e72bSNiklas Söderlund #define HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0) 28b562e72bSNiklas Söderlund 29b562e72bSNiklas Söderlund #define HIST_LAST_MEAS_LINE_REG 0x0010 30b562e72bSNiklas Söderlund #define HIST_SUBSAMPLING_REG 0x0014 31*9a52f7deSNiklas Söderlund #define HIST_SUBSAMPLING_V_STEPSIZE(x) (((x) & 0x7f) << 24) 32*9a52f7deSNiklas Söderlund #define HIST_SUBSAMPLING_H_STEP_INC(x) (((x) & 0x1ffff)) 33*9a52f7deSNiklas Söderlund 34b562e72bSNiklas Söderlund #define HIST_COEFF_R_REG 0x0018 35b562e72bSNiklas Söderlund #define HIST_COEFF_G_REG 0x001c 36b562e72bSNiklas Söderlund #define HIST_COEFF_B_REG 0x0020 37b562e72bSNiklas Söderlund #define HIST_H_OFFS_REG 0x0024 38b562e72bSNiklas Söderlund #define HIST_V_OFFS_REG 0x0028 39b562e72bSNiklas Söderlund #define HIST_H_SIZE_REG 0x002c 40b562e72bSNiklas Söderlund #define HIST_V_SIZE_REG 0x0030 41b562e72bSNiklas Söderlund 42b562e72bSNiklas Söderlund #define HIST_SAMPLE_RANGE_REG 0x0034 43b562e72bSNiklas Söderlund #define HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK GENMASK(28, 24) 44b562e72bSNiklas Söderlund #define HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK GENMASK(23, 0) 45b562e72bSNiklas Söderlund 46b562e72bSNiklas Söderlund #define HIST_WEIGHT_00TO30_REG 0x0038 47b562e72bSNiklas Söderlund #define HIST_WEIGHT_40TO21_REG 0x003c 48b562e72bSNiklas Söderlund #define HIST_WEIGHT_31TO12_REG 0x0040 49b562e72bSNiklas Söderlund #define HIST_WEIGHT_22TO03_REG 0x0044 50b562e72bSNiklas Söderlund #define HIST_WEIGHT_13TO43_REG 0x0048 51b562e72bSNiklas Söderlund #define HIST_WEIGHT_04TO34_REG 0x004c 52b562e72bSNiklas Söderlund #define HIST_WEIGHT_44_REG 0x0050 53b562e72bSNiklas Söderlund #define HIST_FORCED_UPD_START_LINE_REG 0x0054 54b562e72bSNiklas Söderlund #define HIST_FORCED_UPDATE_REG 0x0058 55b562e72bSNiklas Söderlund #define HIST_VSTART_STATUS_REG 0x005c 56b562e72bSNiklas Söderlund 57b562e72bSNiklas Söderlund #define HIST_BIN_REG(n) (0x0060 + (4 * (n))) 58b562e72bSNiklas Söderlund 59b562e72bSNiklas Söderlund static int rppx1_hist_probe(struct rpp_module *mod) 60b562e72bSNiklas Söderlund { 61b562e72bSNiklas Söderlund /* Version check. */ 62b562e72bSNiklas Söderlund switch (rpp_module_read(mod, HIST_VERSION_REG)) { 63b562e72bSNiklas Söderlund case 3: 64b562e72bSNiklas Söderlund /* 12-bit. */ 65b562e72bSNiklas Söderlund break; 66b562e72bSNiklas Söderlund case 4: 67b562e72bSNiklas Söderlund /* 20-bit. */ 68b562e72bSNiklas Söderlund break; 69b562e72bSNiklas Söderlund case 5: 70b562e72bSNiklas Söderlund /* 24-bit. */ 71b562e72bSNiklas Söderlund break; 72b562e72bSNiklas Söderlund default: 73b562e72bSNiklas Söderlund return -EINVAL; 74b562e72bSNiklas Söderlund } 75b562e72bSNiklas Söderlund 76b562e72bSNiklas Söderlund return 0; 77b562e72bSNiklas Söderlund } 78b562e72bSNiklas Söderlund 79*9a52f7deSNiklas Söderlund #define RPPX1_HIST_WEIGHT(v0, v1, v2, v3) \ 80*9a52f7deSNiklas Söderlund (((v0) & 0x1f) | (((v1) & 0x1f) << 8) | \ 81*9a52f7deSNiklas Söderlund (((v2) & 0x1f) << 16) | \ 82*9a52f7deSNiklas Söderlund (((v3) & 0x1f) << 24)) 83*9a52f7deSNiklas Söderlund 84*9a52f7deSNiklas Söderlund static int rppx1_hist_fill_params(struct rpp_module *mod, 85*9a52f7deSNiklas Söderlund const union rppx1_params_block *block, 86*9a52f7deSNiklas Söderlund rppx1_reg_write write, void *priv) 87*9a52f7deSNiklas Söderlund { 88*9a52f7deSNiklas Söderlund const struct rppx1_hist_params *cfg = &block->hist; 89*9a52f7deSNiklas Söderlund u32 h_offs, v_offs, h_size, v_size; 90*9a52f7deSNiklas Söderlund 91*9a52f7deSNiklas Söderlund /* If the modules is disabled, simply bypass it. */ 92*9a52f7deSNiklas Söderlund if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 93*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_MODE_REG, 94*9a52f7deSNiklas Söderlund HIST_MODE_HIST_MODE_DISABLE); 95*9a52f7deSNiklas Söderlund return 0; 96*9a52f7deSNiklas Söderlund } 97*9a52f7deSNiklas Söderlund 98*9a52f7deSNiklas Söderlund /* Select sample point */ 99*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_CHANNEL_SEL_REG, 100*9a52f7deSNiklas Söderlund cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK); 101*9a52f7deSNiklas Söderlund 102*9a52f7deSNiklas Söderlund /* 103*9a52f7deSNiklas Söderlund * Configure the input subsampling. 104*9a52f7deSNiklas Söderlund * 105*9a52f7deSNiklas Söderlund * In Bayer mode the vertical and horizontal subsampling counters are 106*9a52f7deSNiklas Söderlund * only incremented for color channels selected by hist_mode. 107*9a52f7deSNiklas Söderlund */ 108*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_SUBSAMPLING_REG, 109*9a52f7deSNiklas Söderlund HIST_SUBSAMPLING_V_STEPSIZE(cfg->v_stepsize) | 110*9a52f7deSNiklas Söderlund HIST_SUBSAMPLING_H_STEP_INC(cfg->h_step_inc)); 111*9a52f7deSNiklas Söderlund 112*9a52f7deSNiklas Söderlund /* 113*9a52f7deSNiklas Söderlund * Adjust and set measurement window to hardware limitations, 114*9a52f7deSNiklas Söderlund * - Offsets must be even. 115*9a52f7deSNiklas Söderlund * - Width and height must be even and divisible in 5 windows. 116*9a52f7deSNiklas Söderlund */ 117*9a52f7deSNiklas Söderlund h_offs = cfg->wnd.h_offs & 0x1ffe; 118*9a52f7deSNiklas Söderlund v_offs = cfg->wnd.v_offs & 0x1ffe; 119*9a52f7deSNiklas Söderlund h_size = cfg->wnd.h_size - cfg->wnd.h_size % 10; 120*9a52f7deSNiklas Söderlund v_size = cfg->wnd.v_size - cfg->wnd.v_size % 10; 121*9a52f7deSNiklas Söderlund 122*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_H_OFFS_REG, h_offs); 123*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_V_OFFS_REG, v_offs); 124*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_H_SIZE_REG, h_size / 5); 125*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_V_SIZE_REG, v_size / 5); 126*9a52f7deSNiklas Söderlund 127*9a52f7deSNiklas Söderlund /* 128*9a52f7deSNiklas Söderlund * Set last measurement line for ready interrupt. Ignore the value 129*9a52f7deSNiklas Söderlund * from the parameters as it is only useful for fast-channel switching. 130*9a52f7deSNiklas Söderlund */ 131*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_LAST_MEAS_LINE_REG, v_offs + v_size + 1); 132*9a52f7deSNiklas Söderlund 133*9a52f7deSNiklas Söderlund /* Set measurement window weights. */ 134*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_00TO30_REG, 135*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[0], cfg->weights[1], 136*9a52f7deSNiklas Söderlund cfg->weights[2], cfg->weights[3])); 137*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_40TO21_REG, 138*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[4], cfg->weights[5], 139*9a52f7deSNiklas Söderlund cfg->weights[6], cfg->weights[7])); 140*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_31TO12_REG, 141*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[8], cfg->weights[9], 142*9a52f7deSNiklas Söderlund cfg->weights[10], cfg->weights[11])); 143*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_22TO03_REG, 144*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[12], cfg->weights[13], 145*9a52f7deSNiklas Söderlund cfg->weights[14], cfg->weights[15])); 146*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_13TO43_REG, 147*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[16], cfg->weights[17], 148*9a52f7deSNiklas Söderlund cfg->weights[18], cfg->weights[19])); 149*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_04TO34_REG, 150*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[20], cfg->weights[21], 151*9a52f7deSNiklas Söderlund cfg->weights[22], cfg->weights[23])); 152*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_WEIGHT_44_REG, 153*9a52f7deSNiklas Söderlund RPPX1_HIST_WEIGHT(cfg->weights[24], 0, 0, 0)); 154*9a52f7deSNiklas Söderlund 155*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_MODE_REG, cfg->mode); 156*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_COEFF_R_REG, cfg->coeff[0]); 157*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_COEFF_G_REG, cfg->coeff[1]); 158*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_COEFF_B_REG, cfg->coeff[2]); 159*9a52f7deSNiklas Söderlund 160*9a52f7deSNiklas Söderlund u32 sample_reg = FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK, 161*9a52f7deSNiklas Söderlund cfg->sample_shift) | 162*9a52f7deSNiklas Söderlund FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK, 163*9a52f7deSNiklas Söderlund cfg->sample_offs); 164*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_SAMPLE_RANGE_REG, sample_reg); 165*9a52f7deSNiklas Söderlund 166*9a52f7deSNiklas Söderlund write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1); 167*9a52f7deSNiklas Söderlund 168*9a52f7deSNiklas Söderlund return 0; 169*9a52f7deSNiklas Söderlund } 170*9a52f7deSNiklas Söderlund 171*9a52f7deSNiklas Söderlund static int rppx1_hist_fill_stats(struct rpp_module *mod, 172*9a52f7deSNiklas Söderlund union rppx1_stats_block *block) 173*9a52f7deSNiklas Söderlund { 174*9a52f7deSNiklas Söderlund struct rppx1_hist_stats *stats = &block->hist; 175*9a52f7deSNiklas Söderlund 176*9a52f7deSNiklas Söderlund for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++) 177*9a52f7deSNiklas Söderlund stats->hist_bins[i] = rpp_module_read(mod, HIST_BIN_REG(i)); 178*9a52f7deSNiklas Söderlund 179*9a52f7deSNiklas Söderlund return 0; 180*9a52f7deSNiklas Söderlund } 181*9a52f7deSNiklas Söderlund 182b562e72bSNiklas Söderlund const struct rpp_module_ops rppx1_hist_ops = { 183b562e72bSNiklas Söderlund .probe = rppx1_hist_probe, 184*9a52f7deSNiklas Söderlund .fill_params = rppx1_hist_fill_params, 185*9a52f7deSNiklas Söderlund .fill_stats = rppx1_hist_fill_stats, 186b562e72bSNiklas Söderlund }; 187