xref: /linux/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.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 LSC_VERSION_REG		0x0000
11 
12 #define LSC_CTRL_REG		0x0004
13 #define LSC_CTRL_LSC_EN		BIT(0)
14 
15 #define LSC_R_TABLE_ADDR_REG	0x0008
16 #define LSC_GR_TABLE_ADDR_REG	0x000c
17 #define LSC_B_TABLE_ADDR_REG	0x0010
18 #define LSC_GB_TABLE_ADDR_REG	0x0014
19 #define LSC_R_TABLE_DATA_REG	0x0018
20 #define LSC_GR_TABLE_DATA_REG	0x001c
21 #define LSC_B_TABLE_DATA_REG	0x0020
22 #define LSC_GB_TABLE_DATA_REG	0x0024
23 #define LSC_XGRAD_01_REG	0x0028
24 #define LSC_XGRAD_23_REG	0x002c
25 #define LSC_XGRAD_45_REG	0x0030
26 #define LSC_XGRAD_67_REG	0x0034
27 #define LSC_XGRAD_89_REG	0x0038
28 #define LSC_XGRAD_1011_REG	0x003c
29 #define LSC_XGRAD_1213_REG	0x0040
30 #define LSC_XGRAD_1415_REG	0x0044
31 #define LSC_YGRAD_01_REG	0x0048
32 #define LSC_YGRAD_23_REG	0x004c
33 #define LSC_YGRAD_45_REG	0x0050
34 #define LSC_YGRAD_67_REG	0x0054
35 #define LSC_YGRAD_89_REG	0x0058
36 #define LSC_YGRAD_1011_REG	0x005c
37 #define LSC_YGRAD_1213_REG	0x0060
38 #define LSC_YGRAD_1415_REG	0x0064
39 #define LSC_XSIZE_01_REG	0x0068
40 #define LSC_XSIZE_23_REG	0x006c
41 #define LSC_XSIZE_45_REG	0x0070
42 #define LSC_XSIZE_67_REG	0x0074
43 #define LSC_XSIZE_89_REG	0x0078
44 #define LSC_XSIZE_1011_REG	0x007c
45 #define LSC_XSIZE_1213_REG	0x0080
46 #define LSC_XSIZE_1415_REG	0x0084
47 #define LSC_YSIZE_01_REG	0x0088
48 #define LSC_YSIZE_23_REG	0x008c
49 #define LSC_YSIZE_45_REG	0x0090
50 #define LSC_YSIZE_67_REG	0x0094
51 #define LSC_YSIZE_89_REG	0x0098
52 #define LSC_YSIZE_1011_REG	0x009c
53 #define LSC_YSIZE_1213_REG	0x00a0
54 #define LSC_YSIZE_1415_REG	0x00a4
55 #define LSC_TABLE_SEL_REG	0x00a8
56 #define LSC_STATUS_REG		0x00ac
57 
58 #define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12))
59 #define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16))
60 #define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16))
61 
62 static int rppx1_lsc_probe(struct rpp_module *mod)
63 {
64 	/* Version check. */
65 	if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04)
66 		return -EINVAL;
67 
68 	return 0;
69 }
70 
71 static int
72 rppx1_lsc_fill_params(struct rpp_module *mod,
73 		      const union rppx1_params_block *block,
74 		      rppx1_reg_write write, void *priv)
75 {
76 	const struct rppx1_lsc_params *cfg = &block->lsc;
77 	const __u16 *v;
78 
79 	/* Always disable module as it needs be disabled before configuring. */
80 	write(priv, mod->base + LSC_CTRL_REG, 0);
81 	if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
82 		return 0;
83 
84 	/*
85 	 * Program the color correction sectors.
86 	 *
87 	 * There are two tables to one can program and switch between. As the
88 	 * RPPX1 supports preparing a buffer of commands to be applied later
89 	 * only use table 0. This works as long as the ISP is not used in
90 	 * inline-mode.
91 	 *
92 	 * For inline-mode support using DMA for configuration is not possible
93 	 * so this is not an issue, but needs to be address if inline-mode
94 	 * support is added to the driver.
95 	 */
96 
97 	/* Start writing at beginning of table 0. */
98 	write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0);
99 	write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0);
100 	write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0);
101 	write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0);
102 
103 	/* Program data tables. */
104 	for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) {
105 		const __u16 *r = cfg->r_data[i];
106 		const __u16 *gr = cfg->gr_data[i];
107 		const __u16 *b = cfg->b_data[i];
108 		const __u16 *gb = cfg->gb_data[i];
109 		unsigned int j;
110 
111 		for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) {
112 			write(priv, mod->base + LSC_R_TABLE_DATA_REG,
113 			      LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1]));
114 			write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
115 			      LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1]));
116 			write(priv, mod->base + LSC_B_TABLE_DATA_REG,
117 			      LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1]));
118 			write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
119 			      LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1]));
120 		}
121 
122 		write(priv, mod->base + LSC_R_TABLE_DATA_REG,
123 		      LSC_R_TABLE_DATA_VALUE(r[j], 0));
124 		write(priv, mod->base + LSC_GR_TABLE_DATA_REG,
125 		      LSC_R_TABLE_DATA_VALUE(gr[j], 0));
126 		write(priv, mod->base + LSC_B_TABLE_DATA_REG,
127 		      LSC_R_TABLE_DATA_VALUE(b[j], 0));
128 		write(priv, mod->base + LSC_GB_TABLE_DATA_REG,
129 		      LSC_R_TABLE_DATA_VALUE(gb[j], 0));
130 	}
131 
132 	/* Activate table 0. */
133 	write(priv, mod->base + LSC_TABLE_SEL_REG, 0);
134 
135 	/*
136 	 * Program X- and Y- sizes, and gradients.
137 	 */
138 
139 	v = cfg->x_grad;
140 	write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
141 	write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
142 	write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
143 	write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
144 	write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
145 	write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
146 	write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
147 	write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
148 
149 	v = cfg->y_grad;
150 	write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
151 	write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
152 	write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
153 	write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
154 	write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
155 	write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
156 	write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
157 	write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
158 
159 	v = cfg->x_sect_size;
160 	write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
161 	write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
162 	write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
163 	write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
164 	write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
165 	write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
166 	write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
167 	write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
168 
169 	v = cfg->y_sect_size;
170 	write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1]));
171 	write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3]));
172 	write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5]));
173 	write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7]));
174 	write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9]));
175 	write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11]));
176 	write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13]));
177 	write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15]));
178 
179 	/* Enable module. */
180 	write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN);
181 
182 	return 0;
183 }
184 
185 const struct rpp_module_ops rppx1_lsc_ops = {
186 	.probe = rppx1_lsc_probe,
187 	.fill_params = rppx1_lsc_fill_params,
188 };
189