xref: /linux/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c (revision f4cdf7ca9a1fdcca413157df19753f388a5a224e)
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