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