xref: /linux/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * ARM Mali-C55 ISP Driver - Image signal processor
4  *
5  * Copyright (C) 2025 Ideas on Board Oy
6  */
7 
8 #include <linux/math.h>
9 #include <linux/minmax.h>
10 
11 #include <media/media-entity.h>
12 #include <media/v4l2-subdev.h>
13 
14 #include "mali-c55-common.h"
15 #include "mali-c55-registers.h"
16 
17 /* Scaling factor in Q4.20 format. */
18 #define MALI_C55_RSZ_SCALER_FACTOR	(1U << 20)
19 
20 #define MALI_C55_RSZ_COEFS_BANKS	8
21 #define MALI_C55_RSZ_COEFS_ENTRIES	64
22 
23 static inline struct mali_c55_resizer *
24 sd_to_mali_c55_rsz(struct v4l2_subdev *sd)
25 {
26 	return container_of(sd, struct mali_c55_resizer, sd);
27 }
28 
29 static const unsigned int
30 mali_c55_rsz_filter_coeffs_h[MALI_C55_RSZ_COEFS_BANKS]
31 			    [MALI_C55_RSZ_COEFS_ENTRIES] = {
32 	{	/* Bank 0 */
33 		0x24fc0000, 0x0000fc24, 0x27fc0000, 0x0000fc21,
34 		0x28fc0000, 0x0000fd1f, 0x2cfb0000, 0x0000fd1c,
35 		0x2efb0000, 0x0000fd1a, 0x30fb0000, 0x0000fe17,
36 		0x32fb0000, 0x0000fe15, 0x35fb0000, 0x0000fe12,
37 		0x35fc0000, 0x0000ff10, 0x37fc0000, 0x0000ff0e,
38 		0x39fc0000, 0x0000ff0c, 0x3afd0000, 0x0000ff0a,
39 		0x3afe0000, 0x00000008, 0x3cfe0000, 0x00000006,
40 		0x3dff0000, 0x00000004, 0x3d000000, 0x00000003,
41 		0x3c020000, 0x00000002, 0x3d030000, 0x00000000,
42 		0x3d040000, 0x000000ff, 0x3c060000, 0x000000fe,
43 		0x3a080000, 0x000000fe, 0x3a0aff00, 0x000000fd,
44 		0x390cff00, 0x000000fc, 0x370eff00, 0x000000fc,
45 		0x3510ff00, 0x000000fc, 0x3512fe00, 0x000000fb,
46 		0x3215fe00, 0x000000fb, 0x3017fe00, 0x000000fb,
47 		0x2e1afd00, 0x000000fb, 0x2c1cfd00, 0x000000fb,
48 		0x281ffd00, 0x000000fc, 0x2721fc00, 0x000000fc,
49 	},
50 	{	/* Bank 1 */
51 		0x25fb0000, 0x0000fb25, 0x27fb0000, 0x0000fb23,
52 		0x29fb0000, 0x0000fb21, 0x2afc0000, 0x0000fb1f,
53 		0x2cfc0000, 0x0000fb1d, 0x2efc0000, 0x0000fb1b,
54 		0x2ffd0000, 0x0000fb19, 0x2ffe0000, 0x0000fc17,
55 		0x31fe0000, 0x0000fc15, 0x32ff0000, 0x0000fc13,
56 		0x3400ff00, 0x0000fc11, 0x3301ff00, 0x0000fd10,
57 		0x3402ff00, 0x0000fd0e, 0x3503ff00, 0x0000fd0c,
58 		0x3505ff00, 0x0000fd0a, 0x3506fe00, 0x0000fe09,
59 		0x3607fe00, 0x0000fe07, 0x3509fe00, 0x0000fe06,
60 		0x350afd00, 0x0000ff05, 0x350cfd00, 0x0000ff03,
61 		0x340efd00, 0x0000ff02, 0x3310fd00, 0x0000ff01,
62 		0x3411fc00, 0x0000ff00, 0x3213fc00, 0x000000ff,
63 		0x3115fc00, 0x000000fe, 0x2f17fc00, 0x000000fe,
64 		0x2f19fb00, 0x000000fd, 0x2e1bfb00, 0x000000fc,
65 		0x2c1dfb00, 0x000000fc, 0x2a1ffb00, 0x000000fc,
66 		0x2921fb00, 0x000000fb, 0x2723fb00, 0x000000fb,
67 	},
68 	{	/* Bank 2 */
69 		0x1f010000, 0x0000011f, 0x21010000, 0x0000001e,
70 		0x21020000, 0x0000001d, 0x22020000, 0x0000001c,
71 		0x23030000, 0x0000ff1b, 0x2404ff00, 0x0000ff1a,
72 		0x2504ff00, 0x0000ff19, 0x2505ff00, 0x0000ff18,
73 		0x2606ff00, 0x0000fe17, 0x2607ff00, 0x0000fe16,
74 		0x2708ff00, 0x0000fe14, 0x2709ff00, 0x0000fe13,
75 		0x270aff00, 0x0000fe12, 0x280bfe00, 0x0000fe11,
76 		0x280cfe00, 0x0000fe10, 0x280dfe00, 0x0000fe0f,
77 		0x280efe00, 0x0000fe0e, 0x280ffe00, 0x0000fe0d,
78 		0x2810fe00, 0x0000fe0c, 0x2811fe00, 0x0000fe0b,
79 		0x2712fe00, 0x0000ff0a, 0x2713fe00, 0x0000ff09,
80 		0x2714fe00, 0x0000ff08, 0x2616fe00, 0x0000ff07,
81 		0x2617fe00, 0x0000ff06, 0x2518ff00, 0x0000ff05,
82 		0x2519ff00, 0x0000ff04, 0x241aff00, 0x0000ff04,
83 		0x231bff00, 0x00000003, 0x221c0000, 0x00000002,
84 		0x211d0000, 0x00000002, 0x211e0000, 0x00000001,
85 	},
86 	{	/* Bank 3 */
87 		0x1b06ff00, 0x00ff061b, 0x1b07ff00, 0x00ff061a,
88 		0x1c07ff00, 0x00ff051a, 0x1c08ff00, 0x00ff0519,
89 		0x1c09ff00, 0x00ff0419, 0x1d09ff00, 0x00ff0418,
90 		0x1e0aff00, 0x00ff0317, 0x1e0aff00, 0x00ff0317,
91 		0x1e0bff00, 0x00ff0316, 0x1f0cff00, 0x00ff0215,
92 		0x1e0cff00, 0x00000215, 0x1e0dff00, 0x00000214,
93 		0x1e0e0000, 0x00000113, 0x1e0e0000, 0x00000113,
94 		0x1e0f0000, 0x00000112, 0x1f100000, 0x00000011,
95 		0x20100000, 0x00000010, 0x1f110000, 0x00000010,
96 		0x1e120100, 0x0000000f, 0x1e130100, 0x0000000e,
97 		0x1e130100, 0x0000000e, 0x1e140200, 0x0000ff0d,
98 		0x1e150200, 0x0000ff0c, 0x1f1502ff, 0x0000ff0c,
99 		0x1e1603ff, 0x0000ff0b, 0x1e1703ff, 0x0000ff0a,
100 		0x1e1703ff, 0x0000ff0a, 0x1d1804ff, 0x0000ff09,
101 		0x1c1904ff, 0x0000ff09, 0x1c1905ff, 0x0000ff08,
102 		0x1c1a05ff, 0x0000ff07, 0x1b1a06ff, 0x0000ff07,
103 	},
104 	{	/* Bank 4 */
105 		0x17090000, 0x00000917, 0x18090000, 0x00000916,
106 		0x170a0100, 0x00000816, 0x170a0100, 0x00000816,
107 		0x180b0100, 0x00000715, 0x180b0100, 0x00000715,
108 		0x170c0100, 0x00000715, 0x190c0100, 0x00000614,
109 		0x180d0100, 0x00000614, 0x190d0200, 0x00000513,
110 		0x180e0200, 0x00000513, 0x180e0200, 0x00000513,
111 		0x1a0e0200, 0x00000412, 0x190f0200, 0x00000412,
112 		0x190f0300, 0x00000411, 0x18100300, 0x00000411,
113 		0x1a100300, 0x00000310, 0x18110400, 0x00000310,
114 		0x19110400, 0x0000030f, 0x19120400, 0x0000020f,
115 		0x1a120400, 0x0000020e, 0x18130500, 0x0000020e,
116 		0x18130500, 0x0000020e, 0x19130500, 0x0000020d,
117 		0x18140600, 0x0000010d, 0x19140600, 0x0000010c,
118 		0x17150700, 0x0000010c, 0x18150700, 0x0000010b,
119 		0x18150700, 0x0000010b, 0x17160800, 0x0000010a,
120 		0x17160800, 0x0000010a, 0x18160900, 0x00000009,
121 	},
122 	{	/* Bank 5 */
123 		0x120b0300, 0x00030b12, 0x120c0300, 0x00030b11,
124 		0x110c0400, 0x00030b11, 0x110c0400, 0x00030b11,
125 		0x130c0400, 0x00020a11, 0x120d0400, 0x00020a11,
126 		0x110d0500, 0x00020a11, 0x110d0500, 0x00020a11,
127 		0x130d0500, 0x00010911, 0x130e0500, 0x00010910,
128 		0x120e0600, 0x00010910, 0x120e0600, 0x00010910,
129 		0x130e0600, 0x00010810, 0x120f0600, 0x00010810,
130 		0x120f0700, 0x00000810, 0x130f0700, 0x0000080f,
131 		0x140f0700, 0x0000070f, 0x130f0800, 0x0000070f,
132 		0x12100800, 0x0000070f, 0x12100801, 0x0000060f,
133 		0x13100801, 0x0000060e, 0x12100901, 0x0000060e,
134 		0x12100901, 0x0000060e, 0x13100901, 0x0000050e,
135 		0x13110901, 0x0000050d, 0x11110a02, 0x0000050d,
136 		0x11110a02, 0x0000050d, 0x12110a02, 0x0000040d,
137 		0x13110a02, 0x0000040c, 0x11110b03, 0x0000040c,
138 		0x11110b03, 0x0000040c, 0x12110b03, 0x0000030c,
139 	},
140 	{	/* Bank 6 */
141 		0x0b0a0805, 0x00080a0c, 0x0b0a0805, 0x00080a0c,
142 		0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b,
143 		0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b,
144 		0x0d0a0805, 0x00070a0b, 0x0c0a0806, 0x00070a0b,
145 		0x0b0b0806, 0x00070a0b, 0x0c0b0806, 0x0007090b,
146 		0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
147 		0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
148 		0x0b0b0906, 0x0007090b, 0x0c0b0906, 0x0006090b,
149 		0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b,
150 		0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
151 		0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
152 		0x0b0b0907, 0x0006090b, 0x0c0b0907, 0x0006080b,
153 		0x0b0b0a07, 0x0006080b, 0x0c0b0a07, 0x0006080a,
154 		0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a,
155 		0x0d0b0a07, 0x0005080a, 0x0c0b0a08, 0x0005080a,
156 		0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a,
157 	},
158 	{	/* Bank 7 */
159 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
160 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
161 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
162 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
163 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
164 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
165 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
166 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
167 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
168 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
169 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
170 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
171 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
172 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
173 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
174 		0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
175 	}
176 };
177 
178 static const unsigned int
179 mali_c55_rsz_filter_coeffs_v[MALI_C55_RSZ_COEFS_BANKS]
180 			    [MALI_C55_RSZ_COEFS_ENTRIES] = {
181 	{	/* Bank 0 */
182 		0x2424fc00, 0x000000fc, 0x2721fc00, 0x000000fc,
183 		0x281ffd00, 0x000000fc, 0x2c1cfd00, 0x000000fb,
184 		0x2e1afd00, 0x000000fb, 0x3017fe00, 0x000000fb,
185 		0x3215fe00, 0x000000fb, 0x3512fe00, 0x000000fb,
186 		0x3510ff00, 0x000000fc, 0x370eff00, 0x000000fc,
187 		0x390cff00, 0x000000fc, 0x3a0aff00, 0x000000fd,
188 		0x3a080000, 0x000000fe, 0x3c060000, 0x000000fe,
189 		0x3d040000, 0x000000ff, 0x3d030000, 0x00000000,
190 		0x3c020000, 0x00000002, 0x3d000000, 0x00000003,
191 		0x3dff0000, 0x00000004, 0x3cfe0000, 0x00000006,
192 		0x3afe0000, 0x00000008, 0x3afd0000, 0x0000ff0a,
193 		0x39fc0000, 0x0000ff0c, 0x37fc0000, 0x0000ff0e,
194 		0x35fc0000, 0x0000ff10, 0x35fb0000, 0x0000fe12,
195 		0x32fb0000, 0x0000fe15, 0x30fb0000, 0x0000fe17,
196 		0x2efb0000, 0x0000fd1a, 0x2cfb0000, 0x0000fd1c,
197 		0x28fc0000, 0x0000fd1f, 0x27fc0000, 0x0000fc21,
198 	},
199 	{	/* Bank 1 */
200 		0x2525fb00, 0x000000fb, 0x2723fb00, 0x000000fb,
201 		0x2921fb00, 0x000000fb, 0x2a1ffb00, 0x000000fc,
202 		0x2c1dfb00, 0x000000fc, 0x2e1bfb00, 0x000000fc,
203 		0x2f19fb00, 0x000000fd, 0x2f17fc00, 0x000000fe,
204 		0x3115fc00, 0x000000fe, 0x3213fc00, 0x000000ff,
205 		0x3411fc00, 0x0000ff00, 0x3310fd00, 0x0000ff01,
206 		0x340efd00, 0x0000ff02, 0x350cfd00, 0x0000ff03,
207 		0x350afd00, 0x0000ff05, 0x3509fe00, 0x0000fe06,
208 		0x3607fe00, 0x0000fe07, 0x3506fe00, 0x0000fe09,
209 		0x3505ff00, 0x0000fd0a, 0x3503ff00, 0x0000fd0c,
210 		0x3402ff00, 0x0000fd0e, 0x3301ff00, 0x0000fd10,
211 		0x3400ff00, 0x0000fc11, 0x32ff0000, 0x0000fc13,
212 		0x31fe0000, 0x0000fc15, 0x2ffe0000, 0x0000fc17,
213 		0x2ffd0000, 0x0000fb19, 0x2efc0000, 0x0000fb1b,
214 		0x2cfc0000, 0x0000fb1d, 0x2afc0000, 0x0000fb1f,
215 		0x29fb0000, 0x0000fb21, 0x27fb0000, 0x0000fb23,
216 	},
217 	{	/* Bank 2 */
218 		0x1f1f0100, 0x00000001, 0x211e0000, 0x00000001,
219 		0x211d0000, 0x00000002, 0x221c0000, 0x00000002,
220 		0x231bff00, 0x00000003, 0x241aff00, 0x0000ff04,
221 		0x2519ff00, 0x0000ff04, 0x2518ff00, 0x0000ff05,
222 		0x2617fe00, 0x0000ff06, 0x2616fe00, 0x0000ff07,
223 		0x2714fe00, 0x0000ff08, 0x2713fe00, 0x0000ff09,
224 		0x2712fe00, 0x0000ff0a, 0x2811fe00, 0x0000fe0b,
225 		0x2810fe00, 0x0000fe0c, 0x280ffe00, 0x0000fe0d,
226 		0x280efe00, 0x0000fe0e, 0x280dfe00, 0x0000fe0f,
227 		0x280cfe00, 0x0000fe10, 0x280bfe00, 0x0000fe11,
228 		0x270aff00, 0x0000fe12, 0x2709ff00, 0x0000fe13,
229 		0x2708ff00, 0x0000fe14, 0x2607ff00, 0x0000fe16,
230 		0x2606ff00, 0x0000fe17, 0x2505ff00, 0x0000ff18,
231 		0x2504ff00, 0x0000ff19, 0x2404ff00, 0x0000ff1a,
232 		0x23030000, 0x0000ff1b, 0x22020000, 0x0000001c,
233 		0x21020000, 0x0000001d, 0x21010000, 0x0000001e,
234 	},
235 	{	/* Bank 3 */
236 		0x1b1b06ff, 0x0000ff06, 0x1b1a06ff, 0x0000ff07,
237 		0x1c1a05ff, 0x0000ff07, 0x1c1905ff, 0x0000ff08,
238 		0x1c1904ff, 0x0000ff09, 0x1d1804ff, 0x0000ff09,
239 		0x1e1703ff, 0x0000ff0a, 0x1e1703ff, 0x0000ff0a,
240 		0x1e1603ff, 0x0000ff0b, 0x1f1502ff, 0x0000ff0c,
241 		0x1e150200, 0x0000ff0c, 0x1e140200, 0x0000ff0d,
242 		0x1e130100, 0x0000000e, 0x1e130100, 0x0000000e,
243 		0x1e120100, 0x0000000f, 0x1f110000, 0x00000010,
244 		0x20100000, 0x00000010, 0x1f100000, 0x00000011,
245 		0x1e0f0000, 0x00000112, 0x1e0e0000, 0x00000113,
246 		0x1e0e0000, 0x00000113, 0x1e0dff00, 0x00000214,
247 		0x1e0cff00, 0x00000215, 0x1f0cff00, 0x00ff0215,
248 		0x1e0bff00, 0x00ff0316, 0x1e0aff00, 0x00ff0317,
249 		0x1e0aff00, 0x00ff0317, 0x1d09ff00, 0x00ff0418,
250 		0x1c09ff00, 0x00ff0419, 0x1c08ff00, 0x00ff0519,
251 		0x1c07ff00, 0x00ff051a, 0x1b07ff00, 0x00ff061a,
252 	},
253 	{	/* Bank 4 */
254 		0x17170900, 0x00000009, 0x18160900, 0x00000009,
255 		0x17160800, 0x0000010a, 0x17160800, 0x0000010a,
256 		0x18150700, 0x0000010b, 0x18150700, 0x0000010b,
257 		0x17150700, 0x0000010c, 0x19140600, 0x0000010c,
258 		0x18140600, 0x0000010d, 0x19130500, 0x0000020d,
259 		0x18130500, 0x0000020e, 0x18130500, 0x0000020e,
260 		0x1a120400, 0x0000020e, 0x19120400, 0x0000020f,
261 		0x19110400, 0x0000030f, 0x18110400, 0x00000310,
262 		0x1a100300, 0x00000310, 0x18100300, 0x00000411,
263 		0x190f0300, 0x00000411, 0x190f0200, 0x00000412,
264 		0x1a0e0200, 0x00000412, 0x180e0200, 0x00000513,
265 		0x180e0200, 0x00000513, 0x190d0200, 0x00000513,
266 		0x180d0100, 0x00000614, 0x190c0100, 0x00000614,
267 		0x170c0100, 0x00000715, 0x180b0100, 0x00000715,
268 		0x180b0100, 0x00000715, 0x170a0100, 0x00000816,
269 		0x170a0100, 0x00000816, 0x18090000, 0x00000916,
270 	},
271 	{	/* Bank 5 */
272 		0x12120b03, 0x0000030b, 0x12110b03, 0x0000030c,
273 		0x11110b03, 0x0000040c, 0x11110b03, 0x0000040c,
274 		0x13110a02, 0x0000040c, 0x12110a02, 0x0000040d,
275 		0x11110a02, 0x0000050d, 0x11110a02, 0x0000050d,
276 		0x13110901, 0x0000050d, 0x13100901, 0x0000050e,
277 		0x12100901, 0x0000060e, 0x12100901, 0x0000060e,
278 		0x13100801, 0x0000060e, 0x12100801, 0x0000060f,
279 		0x12100800, 0x0000070f, 0x130f0800, 0x0000070f,
280 		0x140f0700, 0x0000070f, 0x130f0700, 0x0000080f,
281 		0x120f0700, 0x00000810, 0x120f0600, 0x00010810,
282 		0x130e0600, 0x00010810, 0x120e0600, 0x00010910,
283 		0x120e0600, 0x00010910, 0x130e0500, 0x00010910,
284 		0x130d0500, 0x00010911, 0x110d0500, 0x00020a11,
285 		0x110d0500, 0x00020a11, 0x120d0400, 0x00020a11,
286 		0x130c0400, 0x00020a11, 0x110c0400, 0x00030b11,
287 		0x110c0400, 0x00030b11, 0x120c0300, 0x00030b11,
288 	},
289 	{	/* Bank 6 */
290 		0x0b0c0a08, 0x0005080a, 0x0b0c0a08, 0x0005080a,
291 		0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a,
292 		0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a,
293 		0x0d0b0a07, 0x0005080a, 0x0c0b0a07, 0x0006080a,
294 		0x0b0b0a07, 0x0006080b, 0x0c0b0907, 0x0006080b,
295 		0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
296 		0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
297 		0x0b0b0907, 0x0006090b, 0x0c0b0906, 0x0006090b,
298 		0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b,
299 		0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
300 		0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
301 		0x0b0b0906, 0x0007090b, 0x0c0b0806, 0x0007090b,
302 		0x0b0b0806, 0x00070a0b, 0x0c0a0806, 0x00070a0b,
303 		0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b,
304 		0x0d0a0805, 0x00070a0b, 0x0c0a0805, 0x00080a0b,
305 		0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b,
306 	},
307 	{	/* Bank 7 */
308 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
309 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
310 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
311 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
312 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
313 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
314 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
315 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
316 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
317 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
318 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
319 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
320 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
321 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
322 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
323 		0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
324 	}
325 };
326 
327 static const unsigned int mali_c55_rsz_coef_banks_range_start[] = {
328 	770, 600, 460, 354, 273, 210, 162, 125
329 };
330 
331 /*
332  * Select the right filter coefficients bank based on the scaler input and the
333  * scaler output sizes ratio, set by the v4l2 crop and scale selection
334  * rectangles respectively.
335  */
336 static unsigned int mali_c55_rsz_calculate_bank(struct mali_c55 *mali_c55,
337 						unsigned int rsz_in,
338 						unsigned int rsz_out)
339 {
340 	unsigned int rsz_ratio = (rsz_out * 1000U) / rsz_in;
341 	unsigned int i;
342 
343 	for (i = 0; i < ARRAY_SIZE(mali_c55_rsz_coef_banks_range_start); i++)
344 		if (rsz_ratio >= mali_c55_rsz_coef_banks_range_start[i])
345 			break;
346 
347 	return i;
348 }
349 
350 static const u32 rsz_non_bypass_src_fmts[] = {
351 	MEDIA_BUS_FMT_RGB121212_1X36,
352 	MEDIA_BUS_FMT_YUV10_1X30
353 };
354 
355 static void mali_c55_resizer_program_coefficients(struct mali_c55_resizer *rsz)
356 {
357 	struct mali_c55 *mali_c55 = rsz->mali_c55;
358 	unsigned int haddr = rsz->id == MALI_C55_RSZ_FR ?
359 			     MALI_C55_REG_FR_SCALER_HFILT :
360 			     MALI_C55_REG_DS_SCALER_HFILT;
361 	unsigned int vaddr = rsz->id == MALI_C55_RSZ_FR ?
362 			     MALI_C55_REG_FR_SCALER_VFILT :
363 			     MALI_C55_REG_DS_SCALER_VFILT;
364 
365 	for (unsigned int i = 0; i < MALI_C55_RSZ_COEFS_BANKS; i++) {
366 		for (unsigned int j = 0; j < MALI_C55_RSZ_COEFS_ENTRIES; j++) {
367 			mali_c55_write(mali_c55, haddr,
368 				       mali_c55_rsz_filter_coeffs_h[i][j]);
369 			mali_c55_write(mali_c55, vaddr,
370 				       mali_c55_rsz_filter_coeffs_v[i][j]);
371 
372 			haddr += sizeof(u32);
373 			vaddr += sizeof(u32);
374 		}
375 	}
376 }
377 
378 static int mali_c55_rsz_program_crop(struct mali_c55_resizer *rsz,
379 				     const struct v4l2_subdev_state *state)
380 {
381 	const struct v4l2_mbus_framefmt *fmt;
382 	const struct v4l2_rect *crop;
383 
384 	/* Verify if crop should be enabled. */
385 	fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD, 0);
386 	crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0);
387 
388 	if (fmt->width == crop->width && fmt->height == crop->height)
389 		return MALI_C55_BYPASS_CROP;
390 
391 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_START,
392 			       crop->left);
393 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_START,
394 			       crop->top);
395 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_SIZE,
396 			       crop->width);
397 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_SIZE,
398 			       crop->height);
399 
400 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_EN,
401 			       MALI_C55_CROP_ENABLE);
402 
403 	return 0;
404 }
405 
406 static int mali_c55_rsz_program_resizer(struct mali_c55_resizer *rsz,
407 					struct v4l2_subdev_state *state)
408 {
409 	struct mali_c55 *mali_c55 = rsz->mali_c55;
410 	const struct v4l2_rect *crop, *scale;
411 	unsigned int h_bank, v_bank;
412 	u64 h_scale, v_scale;
413 
414 	/* Verify if scaling should be enabled. */
415 	crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0);
416 	scale = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD, 0);
417 
418 	if (crop->width == scale->width && crop->height == scale->height)
419 		return MALI_C55_BYPASS_SCALER;
420 
421 	/* Program the scaler coefficients if the scaler is in use. */
422 	mali_c55_resizer_program_coefficients(rsz);
423 
424 	/* Program the V/H scaling factor in Q4.20 format. */
425 	h_scale = crop->width * MALI_C55_RSZ_SCALER_FACTOR;
426 	v_scale = crop->height * MALI_C55_RSZ_SCALER_FACTOR;
427 
428 	do_div(h_scale, scale->width);
429 	do_div(v_scale, scale->height);
430 
431 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_WIDTH,
432 			       crop->width);
433 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_HEIGHT,
434 			       crop->height);
435 
436 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_WIDTH,
437 			       scale->width);
438 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_HEIGHT,
439 			       scale->height);
440 
441 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_TINC,
442 			       h_scale);
443 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_TINC,
444 			       v_scale);
445 
446 	/* Select the scaler coefficients bank to use. */
447 	h_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->width,
448 					     scale->width);
449 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_COEF,
450 			       h_bank);
451 
452 	v_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->height,
453 					     scale->height);
454 	mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_COEF,
455 			       v_bank);
456 
457 	return 0;
458 }
459 
460 static void mali_c55_rsz_program(struct mali_c55_resizer *rsz,
461 				 struct v4l2_subdev_state *state)
462 {
463 	struct mali_c55 *mali_c55 = rsz->mali_c55;
464 	u32 bypass = 0;
465 
466 	/* Verify if cropping and scaling should be enabled. */
467 	bypass |= mali_c55_rsz_program_crop(rsz, state);
468 	bypass |= mali_c55_rsz_program_resizer(rsz, state);
469 
470 	mali_c55_ctx_update_bits(mali_c55, rsz->id == MALI_C55_RSZ_FR ?
471 				 MALI_C55_REG_FR_BYPASS : MALI_C55_REG_DS_BYPASS,
472 				 MALI_C55_BYPASS_CROP | MALI_C55_BYPASS_SCALER,
473 				 bypass);
474 }
475 
476 /*
477  * Inspect the routing table to know which of the two (mutually exclusive)
478  * routes is enabled and return the sink pad id of the active route.
479  */
480 static unsigned int mali_c55_rsz_get_active_sink(struct v4l2_subdev_state *state)
481 {
482 	struct v4l2_subdev_krouting *routing = &state->routing;
483 	struct v4l2_subdev_route *route;
484 
485 	/* A single route is enabled at a time. */
486 	for_each_active_route(routing, route)
487 		return route->sink_pad;
488 
489 	return MALI_C55_RSZ_SINK_PAD;
490 }
491 
492 /*
493  * When operating in bypass mode, the ISP takes input in a 20-bit format, but
494  * can only output 16-bit RAW bayer data (with the 4 least significant bits from
495  * the input being lost). Return the 16-bit version of the 20-bit input formats.
496  */
497 static u32 mali_c55_rsz_shift_mbus_code(u32 mbus_code)
498 {
499 	const struct mali_c55_isp_format_info *fmt =
500 		mali_c55_isp_get_mbus_config_by_code(mbus_code);
501 
502 	if (!fmt)
503 		return -EINVAL;
504 
505 	return fmt->shifted_code;
506 }
507 
508 static int __mali_c55_rsz_set_routing(struct v4l2_subdev *sd,
509 				      struct v4l2_subdev_state *state,
510 				      const struct v4l2_subdev_krouting *routing)
511 {
512 	struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
513 	unsigned int active_sink = UINT_MAX;
514 	struct v4l2_mbus_framefmt *src_fmt;
515 	struct v4l2_subdev_route *route;
516 	unsigned int active_routes = 0;
517 	struct v4l2_mbus_framefmt *fmt;
518 	int ret;
519 
520 	ret = v4l2_subdev_routing_validate(sd, routing, 0);
521 	if (ret)
522 		return ret;
523 
524 	/* Only a single route can be enabled at a time. */
525 	for_each_active_route(routing, route) {
526 		if (++active_routes > 1) {
527 			dev_dbg(rsz->mali_c55->dev,
528 				"Only one route can be active");
529 			return -EINVAL;
530 		}
531 
532 		active_sink = route->sink_pad;
533 	}
534 	if (active_sink == UINT_MAX) {
535 		dev_dbg(rsz->mali_c55->dev, "One route has to be active");
536 		return -EINVAL;
537 	}
538 
539 	ret = v4l2_subdev_set_routing(sd, state, routing);
540 	if (ret) {
541 		dev_dbg(rsz->mali_c55->dev, "Failed to set routing\n");
542 		return ret;
543 	}
544 
545 	fmt = v4l2_subdev_state_get_format(state, active_sink, 0);
546 	fmt->width = MALI_C55_DEFAULT_WIDTH;
547 	fmt->height = MALI_C55_DEFAULT_HEIGHT;
548 	fmt->colorspace = V4L2_COLORSPACE_SRGB;
549 	fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
550 	fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
551 	fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false,
552 							  fmt->colorspace,
553 							  fmt->ycbcr_enc);
554 	fmt->field = V4L2_FIELD_NONE;
555 
556 	if (active_sink == MALI_C55_RSZ_SINK_PAD) {
557 		struct v4l2_rect *crop, *compose;
558 
559 		fmt->code = MEDIA_BUS_FMT_RGB121212_1X36;
560 
561 		crop = v4l2_subdev_state_get_crop(state, active_sink, 0);
562 		compose = v4l2_subdev_state_get_compose(state, active_sink, 0);
563 
564 		crop->left = 0;
565 		crop->top = 0;
566 		crop->width = MALI_C55_DEFAULT_WIDTH;
567 		crop->height = MALI_C55_DEFAULT_HEIGHT;
568 
569 		*compose = *crop;
570 	} else {
571 		fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
572 	}
573 
574 	/* Propagate the format to the source pad */
575 	src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD,
576 					       0);
577 	*src_fmt = *fmt;
578 
579 	/* In the event this is the bypass pad the mbus code needs correcting */
580 	if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD)
581 		src_fmt->code = mali_c55_rsz_shift_mbus_code(src_fmt->code);
582 
583 	return 0;
584 }
585 
586 static int mali_c55_rsz_enum_mbus_code(struct v4l2_subdev *sd,
587 				       struct v4l2_subdev_state *state,
588 				       struct v4l2_subdev_mbus_code_enum *code)
589 {
590 	const struct mali_c55_isp_format_info *fmt;
591 	struct v4l2_mbus_framefmt *sink_fmt;
592 	u32 sink_pad;
593 
594 	switch (code->pad) {
595 	case MALI_C55_RSZ_SINK_PAD:
596 		if (code->index)
597 			return -EINVAL;
598 
599 		code->code = MEDIA_BUS_FMT_RGB121212_1X36;
600 
601 		return 0;
602 	case MALI_C55_RSZ_SOURCE_PAD:
603 		sink_pad = mali_c55_rsz_get_active_sink(state);
604 		sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0);
605 
606 		/*
607 		 * If the active route is from the Bypass sink pad, then the
608 		 * source pad is a simple passthrough of the sink format,
609 		 * downshifted to 16-bits.
610 		 */
611 
612 		if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
613 			if (code->index)
614 				return -EINVAL;
615 
616 			code->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code);
617 			if (!code->code)
618 				return -EINVAL;
619 
620 			return 0;
621 		}
622 
623 		/*
624 		 * If the active route is from the non-bypass sink then we can
625 		 * select either RGB or conversion to YUV.
626 		 */
627 
628 		if (code->index >= ARRAY_SIZE(rsz_non_bypass_src_fmts))
629 			return -EINVAL;
630 
631 		code->code = rsz_non_bypass_src_fmts[code->index];
632 
633 		return 0;
634 	case MALI_C55_RSZ_SINK_BYPASS_PAD:
635 		fmt = mali_c55_isp_get_mbus_config_by_index(code->index);
636 		if (fmt) {
637 			code->code = fmt->code;
638 			return 0;
639 		}
640 
641 		break;
642 	}
643 
644 	return -EINVAL;
645 }
646 
647 static int mali_c55_rsz_enum_frame_size(struct v4l2_subdev *sd,
648 					struct v4l2_subdev_state *state,
649 					struct v4l2_subdev_frame_size_enum *fse)
650 {
651 	const struct mali_c55_isp_format_info *fmt;
652 	struct v4l2_mbus_framefmt *sink_fmt;
653 	struct v4l2_rect *compose;
654 	u32 sink_pad;
655 
656 	switch (fse->pad) {
657 	case MALI_C55_RSZ_SINK_PAD:
658 		if (fse->index || fse->code != MEDIA_BUS_FMT_RGB121212_1X36)
659 			return -EINVAL;
660 
661 		fse->max_width = MALI_C55_MAX_WIDTH;
662 		fse->max_height = MALI_C55_MAX_HEIGHT;
663 		fse->min_width = MALI_C55_MIN_WIDTH;
664 		fse->min_height = MALI_C55_MIN_HEIGHT;
665 
666 		return 0;
667 	case MALI_C55_RSZ_SOURCE_PAD:
668 		sink_pad = mali_c55_rsz_get_active_sink(state);
669 		sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0);
670 
671 		if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
672 			if (fse->index)
673 				return -EINVAL;
674 
675 			fmt = mali_c55_isp_get_mbus_config_by_shifted_code(fse->code);
676 			if (!fmt)
677 				return -EINVAL;
678 
679 			fse->min_width = sink_fmt->width;
680 			fse->max_width = sink_fmt->width;
681 			fse->min_height = sink_fmt->height;
682 			fse->max_height = sink_fmt->height;
683 
684 			return 0;
685 		}
686 
687 		if ((fse->code != MEDIA_BUS_FMT_RGB121212_1X36 &&
688 		     fse->code != MEDIA_BUS_FMT_YUV10_1X30) || fse->index > 1)
689 			return -EINVAL;
690 
691 		compose = v4l2_subdev_state_get_compose(state,
692 							MALI_C55_RSZ_SINK_PAD,
693 							0);
694 
695 		fse->min_width = compose->width;
696 		fse->max_width = compose->width;
697 		fse->min_height = compose->height;
698 		fse->max_height = compose->height;
699 
700 		return 0;
701 	case MALI_C55_RSZ_SINK_BYPASS_PAD:
702 		fmt = mali_c55_isp_get_mbus_config_by_code(fse->code);
703 		if (fse->index || !fmt)
704 			return -EINVAL;
705 
706 		fse->max_width = MALI_C55_MAX_WIDTH;
707 		fse->max_height = MALI_C55_MAX_HEIGHT;
708 		fse->min_width = MALI_C55_MIN_WIDTH;
709 		fse->min_height = MALI_C55_MIN_HEIGHT;
710 
711 		return 0;
712 	}
713 
714 	return -EINVAL;
715 }
716 
717 static int mali_c55_rsz_set_sink_fmt(struct v4l2_subdev *sd,
718 				     struct v4l2_subdev_state *state,
719 				     struct v4l2_subdev_format *format)
720 {
721 	struct v4l2_mbus_framefmt *fmt = &format->format;
722 	struct v4l2_mbus_framefmt *sink_fmt;
723 	unsigned int active_sink;
724 	struct v4l2_rect *rect;
725 
726 	sink_fmt = v4l2_subdev_state_get_format(state, format->pad, 0);
727 
728 	/*
729 	 * Clamp to min/max and then reset crop and compose rectangles to the
730 	 * newly applied size.
731 	 */
732 	sink_fmt->width = clamp_t(unsigned int, fmt->width,
733 				  MALI_C55_MIN_WIDTH, MALI_C55_MAX_WIDTH);
734 	sink_fmt->height = clamp_t(unsigned int, fmt->height,
735 				   MALI_C55_MIN_HEIGHT, MALI_C55_MAX_HEIGHT);
736 
737 	/*
738 	 * Make sure the media bus code for the bypass pad is one of the
739 	 * supported ISP input media bus codes. Default it to SRGGB otherwise.
740 	 */
741 	if (format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD)
742 		sink_fmt->code = mali_c55_isp_get_mbus_config_by_code(fmt->code) ?
743 				 fmt->code : MEDIA_BUS_FMT_SRGGB20_1X20;
744 
745 	*fmt = *sink_fmt;
746 
747 	if (format->pad == MALI_C55_RSZ_SINK_PAD) {
748 		rect = v4l2_subdev_state_get_crop(state, format->pad);
749 		rect->left = 0;
750 		rect->top = 0;
751 		rect->width = fmt->width;
752 		rect->height = fmt->height;
753 
754 		rect = v4l2_subdev_state_get_compose(state, format->pad);
755 		rect->left = 0;
756 		rect->top = 0;
757 		rect->width = fmt->width;
758 		rect->height = fmt->height;
759 	}
760 
761 	/* If format->pad is routed to the source pad, propagate the format. */
762 	active_sink = mali_c55_rsz_get_active_sink(state);
763 	if (active_sink == format->pad) {
764 		/* If the bypass route is used, downshift the code to 16bpp. */
765 		if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD)
766 			fmt->code = mali_c55_rsz_shift_mbus_code(fmt->code);
767 
768 		*v4l2_subdev_state_get_format(state,
769 					      MALI_C55_RSZ_SOURCE_PAD, 0) = *fmt;
770 	}
771 
772 	return 0;
773 }
774 
775 static int mali_c55_rsz_set_source_fmt(struct v4l2_subdev *sd,
776 				       struct v4l2_subdev_state *state,
777 				       struct v4l2_subdev_format *format)
778 {
779 	struct v4l2_mbus_framefmt *fmt = &format->format;
780 	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
781 	unsigned int active_sink;
782 
783 	active_sink = mali_c55_rsz_get_active_sink(state);
784 	sink_fmt = v4l2_subdev_state_get_format(state, active_sink, 0);
785 	src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD);
786 
787 	if (active_sink == MALI_C55_RSZ_SINK_PAD) {
788 		/*
789 		 * Regular processing pipe: RGB121212 can be color-space
790 		 * converted to YUV101010.
791 		 */
792 		unsigned int i;
793 
794 		for (i = 0; i < ARRAY_SIZE(rsz_non_bypass_src_fmts); i++) {
795 			if (fmt->code == rsz_non_bypass_src_fmts[i])
796 				break;
797 		}
798 
799 		src_fmt->code = i == ARRAY_SIZE(rsz_non_bypass_src_fmts) ?
800 				MEDIA_BUS_FMT_RGB121212_1X36 : fmt->code;
801 	} else {
802 		/*
803 		 * Bypass pipe: the source format is the same as the bypass
804 		 * sink pad downshifted to 16bpp.
805 		 */
806 		fmt->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code);
807 	}
808 
809 	*fmt = *src_fmt;
810 
811 	return 0;
812 }
813 
814 static int mali_c55_rsz_set_fmt(struct v4l2_subdev *sd,
815 				struct v4l2_subdev_state *state,
816 				struct v4l2_subdev_format *format)
817 {
818 	/*
819 	 * On sink pads fmt is either fixed for the 'regular' processing
820 	 * pad or a RAW format or 20-bit wide RGB/YUV format for the FR bypass
821 	 * pad.
822 	 *
823 	 * On source pad sizes are the result of crop+compose on the sink
824 	 * pad sizes, while the format depends on the active route.
825 	 */
826 
827 	if (format->pad == MALI_C55_RSZ_SINK_PAD ||
828 	    format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD)
829 		return mali_c55_rsz_set_sink_fmt(sd, state, format);
830 
831 	return mali_c55_rsz_set_source_fmt(sd, state, format);
832 }
833 
834 static int mali_c55_rsz_get_selection(struct v4l2_subdev *sd,
835 				      struct v4l2_subdev_state *state,
836 				      struct v4l2_subdev_selection *sel)
837 {
838 	if (sel->pad != MALI_C55_RSZ_SINK_PAD)
839 		return -EINVAL;
840 
841 	if (sel->target != V4L2_SEL_TGT_CROP &&
842 	    sel->target != V4L2_SEL_TGT_COMPOSE)
843 		return -EINVAL;
844 
845 	sel->r = sel->target == V4L2_SEL_TGT_CROP
846 	       ? *v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD)
847 	       : *v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
848 
849 	return 0;
850 }
851 
852 static int mali_c55_rsz_set_crop(struct v4l2_subdev *sd,
853 				 struct v4l2_subdev_state *state,
854 				 struct v4l2_subdev_selection *sel)
855 {
856 	struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
857 	struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
858 	struct v4l2_rect *crop, *compose;
859 
860 	sink_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD);
861 	crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD);
862 	compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
863 
864 	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
865 	    v4l2_subdev_is_streaming(sd)) {
866 		/*
867 		 * At runtime the compose rectangle and output size cannot be
868 		 * changed so we need to clamp the crop rectangle such that the
869 		 * compose rectangle can fit within it.
870 		 */
871 		crop->left = clamp_t(unsigned int, sel->r.left, 0,
872 				     sink_fmt->width - compose->width);
873 		crop->top = clamp_t(unsigned int, sel->r.top, 0,
874 				    sink_fmt->height - compose->height);
875 		crop->width = clamp_t(unsigned int, sel->r.width, compose->width,
876 				      sink_fmt->width - crop->left);
877 		crop->height = clamp_t(unsigned int, sel->r.height, compose->height,
878 				       sink_fmt->height - crop->top);
879 
880 		mali_c55_rsz_program(rsz, state);
881 	} else {
882 		/*
883 		 * If we're not streaming we can utilise the ISP's full range
884 		 * and simply need to propagate the selected rectangle to the
885 		 * compose target and source pad format.
886 		 */
887 		crop->left = clamp_t(unsigned int, sel->r.left, 0,
888 				     sink_fmt->width);
889 		crop->top = clamp_t(unsigned int, sel->r.top, 0,
890 				    sink_fmt->height);
891 		crop->width = clamp_t(unsigned int, sel->r.width,
892 				      MALI_C55_MIN_WIDTH,
893 				      sink_fmt->width - crop->left);
894 		crop->height = clamp_t(unsigned int, sel->r.height,
895 				       MALI_C55_MIN_HEIGHT,
896 				       sink_fmt->height - crop->top);
897 
898 		*compose = *crop;
899 
900 		src_fmt = v4l2_subdev_state_get_format(state,
901 						       MALI_C55_RSZ_SOURCE_PAD);
902 		src_fmt->width = compose->width;
903 		src_fmt->height = compose->height;
904 	}
905 
906 	sel->r = *crop;
907 	return 0;
908 }
909 
910 static int mali_c55_rsz_set_compose(struct v4l2_subdev *sd,
911 				    struct v4l2_subdev_state *state,
912 				    struct v4l2_subdev_selection *sel)
913 {
914 	struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
915 	struct mali_c55 *mali_c55 = rsz->mali_c55;
916 	struct v4l2_mbus_framefmt *src_fmt;
917 	struct v4l2_rect *compose, *crop;
918 
919 	/*
920 	 * We cannot change the compose rectangle during streaming, as that
921 	 * would require a change in the output buffer size.
922 	 */
923 	if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
924 	    v4l2_subdev_is_streaming(sd))
925 		return -EBUSY;
926 
927 	/*
928 	 * In the FR pipe, the scaler is an optional component that may not be
929 	 * fitted.
930 	 */
931 	if (rsz->id == MALI_C55_RSZ_FR &&
932 	    !(mali_c55->capabilities & MALI_C55_GPS_FRSCALER_FITTED))
933 		return -EINVAL;
934 
935 	compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
936 	crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD);
937 
938 	compose->left = 0;
939 	compose->top = 0;
940 	compose->width = clamp_t(unsigned int, sel->r.width, crop->width / 8,
941 				 crop->width);
942 	compose->height = clamp_t(unsigned int, sel->r.height, crop->height / 8,
943 				  crop->height);
944 
945 	sel->r = *compose;
946 
947 	/*
948 	 * We need to be sure to propagate the compose rectangle size to the
949 	 * source pad format.
950 	 */
951 	src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD);
952 	src_fmt->width = compose->width;
953 	src_fmt->height = compose->height;
954 
955 	return 0;
956 }
957 
958 static int mali_c55_rsz_set_selection(struct v4l2_subdev *sd,
959 				      struct v4l2_subdev_state *state,
960 				      struct v4l2_subdev_selection *sel)
961 {
962 	if (sel->pad != MALI_C55_RSZ_SINK_PAD)
963 		return -EINVAL;
964 
965 	if (sel->target == V4L2_SEL_TGT_CROP)
966 		return mali_c55_rsz_set_crop(sd, state, sel);
967 
968 	if (sel->target == V4L2_SEL_TGT_COMPOSE)
969 		return mali_c55_rsz_set_compose(sd, state, sel);
970 
971 	return -EINVAL;
972 }
973 
974 static int mali_c55_rsz_set_routing(struct v4l2_subdev *sd,
975 				    struct v4l2_subdev_state *state,
976 				    enum v4l2_subdev_format_whence which,
977 				    struct v4l2_subdev_krouting *routing)
978 {
979 	if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
980 	    media_entity_is_streaming(&sd->entity))
981 		return -EBUSY;
982 
983 	return __mali_c55_rsz_set_routing(sd, state, routing);
984 }
985 
986 static int mali_c55_rsz_enable_streams(struct v4l2_subdev *sd,
987 				       struct v4l2_subdev_state *state, u32 pad,
988 				       u64 streams_mask)
989 {
990 	struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
991 	struct mali_c55 *mali_c55 = rsz->mali_c55;
992 	unsigned int sink_pad;
993 
994 	sink_pad = mali_c55_rsz_get_active_sink(state);
995 	if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
996 		/* Bypass FR pipe processing if the bypass route is active. */
997 		mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
998 					 MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK,
999 					 MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS);
1000 		return 0;
1001 	}
1002 
1003 	/* Disable bypass and use regular processing. */
1004 	mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
1005 				 MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK, 0);
1006 	mali_c55_rsz_program(rsz, state);
1007 
1008 	return 0;
1009 }
1010 
1011 static int mali_c55_rsz_disable_streams(struct v4l2_subdev *sd,
1012 					struct v4l2_subdev_state *state, u32 pad,
1013 					u64 streams_mask)
1014 {
1015 	return 0;
1016 }
1017 
1018 static const struct v4l2_subdev_pad_ops mali_c55_resizer_pad_ops = {
1019 	.enum_mbus_code		= mali_c55_rsz_enum_mbus_code,
1020 	.enum_frame_size	= mali_c55_rsz_enum_frame_size,
1021 	.get_fmt		= v4l2_subdev_get_fmt,
1022 	.set_fmt		= mali_c55_rsz_set_fmt,
1023 	.get_selection		= mali_c55_rsz_get_selection,
1024 	.set_selection		= mali_c55_rsz_set_selection,
1025 	.set_routing		= mali_c55_rsz_set_routing,
1026 	.enable_streams		= mali_c55_rsz_enable_streams,
1027 	.disable_streams	= mali_c55_rsz_disable_streams,
1028 };
1029 
1030 static const struct v4l2_subdev_ops mali_c55_resizer_ops = {
1031 	.pad	= &mali_c55_resizer_pad_ops,
1032 };
1033 
1034 static int mali_c55_rsz_init_state(struct v4l2_subdev *sd,
1035 				   struct v4l2_subdev_state *state)
1036 {
1037 	struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
1038 	struct v4l2_subdev_route routes[2] = {
1039 		{
1040 			.sink_pad = MALI_C55_RSZ_SINK_PAD,
1041 			.source_pad = MALI_C55_RSZ_SOURCE_PAD,
1042 			.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
1043 		}, {
1044 			.sink_pad = MALI_C55_RSZ_SINK_BYPASS_PAD,
1045 			.source_pad = MALI_C55_RSZ_SOURCE_PAD,
1046 		},
1047 	};
1048 	struct v4l2_subdev_krouting routing = {
1049 		.num_routes = rsz->num_routes,
1050 		.routes = routes,
1051 	};
1052 
1053 	return __mali_c55_rsz_set_routing(sd, state, &routing);
1054 }
1055 
1056 static const struct v4l2_subdev_internal_ops mali_c55_resizer_internal_ops = {
1057 	.init_state = mali_c55_rsz_init_state,
1058 };
1059 
1060 static int mali_c55_register_resizer(struct mali_c55 *mali_c55,
1061 				     unsigned int index)
1062 {
1063 	struct mali_c55_resizer *rsz = &mali_c55->resizers[index];
1064 	struct v4l2_subdev *sd = &rsz->sd;
1065 	unsigned int num_pads;
1066 	int ret;
1067 
1068 	rsz->id = index;
1069 	v4l2_subdev_init(sd, &mali_c55_resizer_ops);
1070 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
1071 	sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
1072 	sd->internal_ops = &mali_c55_resizer_internal_ops;
1073 
1074 	rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
1075 	rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
1076 
1077 	if (rsz->id == MALI_C55_RSZ_FR) {
1078 		num_pads = MALI_C55_RSZ_NUM_PADS;
1079 		rsz->num_routes = 2;
1080 
1081 		rsz->pads[MALI_C55_RSZ_SINK_BYPASS_PAD].flags =
1082 			MEDIA_PAD_FL_SINK;
1083 
1084 		snprintf(sd->name, sizeof(sd->name), "%s resizer fr",
1085 			 MALI_C55_DRIVER_NAME);
1086 
1087 	} else {
1088 		num_pads = MALI_C55_RSZ_NUM_PADS - 1;
1089 		rsz->num_routes = 1;
1090 
1091 		snprintf(sd->name, sizeof(sd->name), "%s resizer ds",
1092 			 MALI_C55_DRIVER_NAME);
1093 	}
1094 
1095 	ret = media_entity_pads_init(&sd->entity, num_pads, rsz->pads);
1096 	if (ret)
1097 		return ret;
1098 
1099 	ret = v4l2_subdev_init_finalize(sd);
1100 	if (ret)
1101 		goto err_media_cleanup;
1102 
1103 	ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd);
1104 	if (ret)
1105 		goto err_subdev_cleanup;
1106 
1107 	rsz->cap_dev = &mali_c55->cap_devs[index];
1108 	rsz->mali_c55 = mali_c55;
1109 
1110 	return 0;
1111 
1112 err_subdev_cleanup:
1113 	v4l2_subdev_cleanup(sd);
1114 err_media_cleanup:
1115 	media_entity_cleanup(&sd->entity);
1116 
1117 	return ret;
1118 }
1119 
1120 static void mali_c55_unregister_resizer(struct mali_c55_resizer *rsz)
1121 {
1122 	if (!rsz->mali_c55)
1123 		return;
1124 
1125 	v4l2_device_unregister_subdev(&rsz->sd);
1126 	v4l2_subdev_cleanup(&rsz->sd);
1127 	media_entity_cleanup(&rsz->sd.entity);
1128 }
1129 
1130 int mali_c55_register_resizers(struct mali_c55 *mali_c55)
1131 {
1132 	int ret;
1133 
1134 	ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_FR);
1135 	if (ret)
1136 		return ret;
1137 
1138 	if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) {
1139 		ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_DS);
1140 		if (ret)
1141 			goto err_unregister_fr;
1142 	}
1143 
1144 	return 0;
1145 
1146 err_unregister_fr:
1147 	mali_c55_unregister_resizer(&mali_c55->resizers[MALI_C55_RSZ_FR]);
1148 
1149 	return ret;
1150 }
1151 
1152 void mali_c55_unregister_resizers(struct mali_c55 *mali_c55)
1153 {
1154 	for (unsigned int i = 0; i < MALI_C55_NUM_RSZS; i++)
1155 		mali_c55_unregister_resizer(&mali_c55->resizers[i]);
1156 }
1157