1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2026 Renesas Electronics Corp. 4 * Copyright (C) 2026 Ideas on Board Oy 5 * Copyright (C) 2026 Ragnatech AB 6 */ 7 8 #include "rpp_module.h" 9 10 #define EXM_VERSION_REG 0x0000 11 #define EXM_START_REG 0x0004 12 13 #define EXM_CTRL_REG 0x0008 14 #define EXM_CTRL_EXM_AUTOSTOP BIT(1) /* HW doc says not supported. */ 15 #define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0) 16 17 #define EXM_MODE_REG 0x000c 18 19 #define EXM_CHANNEL_SEL_REG 0x0010 20 #define EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0) 21 22 #define EXM_LAST_MEAS_LINE_REG 0x0014 23 #define EXM_COEFF_R_REG 0x0018 24 #define EXM_COEFF_G_GR_REG 0x001c 25 #define EXM_COEFF_B_REG 0x0020 26 #define EXM_COEFF_GB_REG 0x0024 27 #define EXM_H_OFFS_REG 0x0028 28 #define EXM_V_OFFS_REG 0x002c 29 #define EXM_H_SIZE_REG 0x0030 30 #define EXM_V_SIZE_REG 0x0034 31 #define EXM_FORCED_UPD_START_LINE_REG 0x0038 32 #define EXM_VSTART_STATUS_REG 0x003c 33 34 #define EXM_MEAN_REG(n) (0x0040 + (4 * (n))) 35 36 static int rppx1_exm_probe(struct rpp_module *mod) 37 { 38 /* Version check. */ 39 switch (rpp_module_read(mod, EXM_VERSION_REG)) { 40 case 1: 41 /* 8-bit. */ 42 break; 43 case 3: 44 /* 20-bit. */ 45 break; 46 default: 47 return -EINVAL; 48 } 49 50 return 0; 51 } 52 53 static int 54 rppx1_exm_fill_params(struct rpp_module *mod, 55 const union rppx1_params_block *block, 56 rppx1_reg_write write, void *priv) 57 { 58 const struct rppx1_exm_params *cfg = &block->exm; 59 u32 h_offs, v_offs, h_size, v_size; 60 61 /* If the modules is disabled, simply bypass it. */ 62 if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 63 write(priv, mod->base + EXM_MODE_REG, 0); 64 return 0; 65 } 66 67 switch (cfg->mode) { 68 case RPPX1_EXP_MEASURING_MODE_RGB: 69 case RPPX1_EXP_MEASURING_MODE_BAYER: 70 write(priv, mod->base + EXM_MODE_REG, cfg->mode); 71 break; 72 default: 73 write(priv, mod->base + EXM_MODE_REG, 0); 74 return 0; 75 } 76 77 write(priv, mod->base + EXM_COEFF_R_REG, cfg->coeff_r); 78 write(priv, mod->base + EXM_COEFF_G_GR_REG, cfg->coeff_g_gr); 79 write(priv, mod->base + EXM_COEFF_GB_REG, cfg->coeff_gb); 80 write(priv, mod->base + EXM_COEFF_B_REG, cfg->coeff_b); 81 82 /* Select sample point */ 83 write(priv, mod->base + EXM_CHANNEL_SEL_REG, 84 cfg->channel_sel & EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK); 85 86 /* 87 * Adjust and set measurement window, 88 * - Offsets must be even. 89 * - Width and height must be even and divisible in 5 windows. 90 */ 91 h_offs = cfg->wnd.h_offs & 0x1ffe; 92 v_offs = cfg->wnd.v_offs & 0x1ffe; 93 h_size = (cfg->wnd.h_size - 1) - ((cfg->wnd.h_size - 1) % 10); 94 v_size = (cfg->wnd.v_size - 1) - ((cfg->wnd.v_size - 1) % 10); 95 96 write(priv, mod->base + EXM_H_OFFS_REG, h_offs); 97 write(priv, mod->base + EXM_V_OFFS_REG, v_offs); 98 write(priv, mod->base + EXM_H_SIZE_REG, h_size / 5); 99 write(priv, mod->base + EXM_V_SIZE_REG, v_size / 5); 100 101 /* 102 * Set last measurement line for ready interrupt. Ignore the value 103 * from the parameters as it is only useful for fast-channel switching. 104 */ 105 write(priv, mod->base + EXM_LAST_MEAS_LINE_REG, v_offs + v_size + 1); 106 107 write(priv, mod->base + EXM_START_REG, 1); 108 109 return 0; 110 } 111 112 static int rppx1_exm_fill_stats(struct rpp_module *mod, 113 union rppx1_stats_block *block) 114 { 115 struct rppx1_exm_stats *stats = &block->exm; 116 117 for (unsigned int i = 0; i < RPPX1_EXM_NUM_WIN; i++) 118 stats->exp_mean[i] = rpp_module_read(mod, EXM_MEAN_REG(i)); 119 120 return 0; 121 } 122 123 const struct rpp_module_ops rppx1_exm_ops = { 124 .probe = rppx1_exm_probe, 125 .fill_params = rppx1_exm_fill_params, 126 .fill_stats = rppx1_exm_fill_stats, 127 }; 128