1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ARM Mali-C55 ISP Driver - Configuration parameters output device 4 * 5 * Copyright (C) 2025 Ideas on Board Oy 6 */ 7 #include <linux/media/arm/mali-c55-config.h> 8 #include <linux/pm_runtime.h> 9 10 #include <media/media-entity.h> 11 #include <media/v4l2-dev.h> 12 #include <media/v4l2-event.h> 13 #include <media/v4l2-fh.h> 14 #include <media/v4l2-ioctl.h> 15 #include <media/v4l2-isp.h> 16 #include <media/videobuf2-core.h> 17 #include <media/videobuf2-dma-contig.h> 18 19 #include "mali-c55-common.h" 20 #include "mali-c55-registers.h" 21 22 /** 23 * union mali_c55_params_block - Generalisation of a parameter block 24 * 25 * This union allows the driver to treat a block as a generic pointer to this 26 * union and safely access the header and block-specific struct without having 27 * to resort to casting. The header member is accessed first, and the type field 28 * checked which allows the driver to determine which of the other members 29 * should be used. The data member at the end allows a pointer to an address 30 * within the data member of :c:type:`mali_c55_params_buffer` to initialise a 31 * union variable. 32 * 33 * @header: Pointer to the shared header struct embedded as the 34 * first member of all the possible other members (except 35 * @data). This member would be accessed first and the type 36 * field checked to determine which of the other members 37 * should be accessed. 38 * @sensor_offs: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS 39 * @aexp_hist: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST and 40 * header->type == MALI_C55_PARAM_BLOCK_AEXP_IHIST 41 * @aexp_weights: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS 42 * and header->type = MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS 43 * @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN 44 * @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and 45 * header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP 46 * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG 47 * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION 48 * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS 49 * @data: Allows easy initialisation of a union variable with a 50 * pointer into a __u8 array. 51 */ 52 union mali_c55_params_block { 53 const struct v4l2_isp_params_block_header *header; 54 const struct mali_c55_params_sensor_off_preshading *sensor_offs; 55 const struct mali_c55_params_aexp_hist *aexp_hist; 56 const struct mali_c55_params_aexp_weights *aexp_weights; 57 const struct mali_c55_params_digital_gain *digital_gain; 58 const struct mali_c55_params_awb_gains *awb_gains; 59 const struct mali_c55_params_awb_config *awb_config; 60 const struct mali_c55_params_mesh_shading_config *shading_config; 61 const struct mali_c55_params_mesh_shading_selection *shading_selection; 62 const __u8 *data; 63 }; 64 65 typedef void (*mali_c55_params_handler)(struct mali_c55 *mali_c55, 66 union mali_c55_params_block block); 67 68 #define to_mali_c55_params_buf(vbuf) \ 69 container_of(vbuf, struct mali_c55_params_buf, vb) 70 71 static void mali_c55_params_sensor_offs(struct mali_c55 *mali_c55, 72 union mali_c55_params_block block) 73 { 74 const struct mali_c55_params_sensor_off_preshading *p; 75 __u32 global_offset; 76 77 p = block.sensor_offs; 78 79 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 80 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, 81 MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, 82 MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH); 83 return; 84 } 85 86 if (!(p->chan00 || p->chan01 || p->chan10 || p->chan11)) 87 return; 88 89 mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_00, 90 p->chan00 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 91 mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_01, 92 p->chan01 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 93 mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_10, 94 p->chan10 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 95 mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_11, 96 p->chan11 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 97 98 /* 99 * The average offset is applied as a global offset for the digital 100 * gain block 101 */ 102 global_offset = (p->chan00 + p->chan01 + p->chan10 + p->chan11) >> 2; 103 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN_OFFSET, 104 MALI_C55_DIGITAL_GAIN_OFFSET_MASK, 105 global_offset); 106 107 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, 108 MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, 109 0x00); 110 } 111 112 static void mali_c55_params_aexp_hist(struct mali_c55 *mali_c55, 113 union mali_c55_params_block block) 114 { 115 const struct mali_c55_params_aexp_hist *params; 116 u32 disable_mask; 117 u32 disable_val; 118 u32 base; 119 120 if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) { 121 disable_mask = MALI_C55_AEXP_HIST_DISABLE_MASK; 122 disable_val = MALI_C55_AEXP_HIST_DISABLE; 123 base = MALI_C55_REG_AEXP_HIST_BASE; 124 } else { 125 disable_mask = MALI_C55_AEXP_IHIST_DISABLE_MASK; 126 disable_val = MALI_C55_AEXP_IHIST_DISABLE; 127 base = MALI_C55_REG_AEXP_IHIST_BASE; 128 } 129 130 params = block.aexp_hist; 131 132 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 133 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 134 disable_mask, disable_val); 135 return; 136 } 137 138 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 139 disable_mask, false); 140 141 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 142 MALI_C55_AEXP_HIST_SKIP_X_MASK, params->skip_x); 143 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 144 MALI_C55_AEXP_HIST_OFFSET_X_MASK, 145 MALI_C55_AEXP_HIST_OFFSET_X(params->offset_x)); 146 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 147 MALI_C55_AEXP_HIST_SKIP_Y_MASK, 148 MALI_C55_AEXP_HIST_SKIP_Y(params->skip_y)); 149 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 150 MALI_C55_AEXP_HIST_OFFSET_Y_MASK, 151 MALI_C55_AEXP_HIST_OFFSET_Y(params->offset_y)); 152 153 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, 154 MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK, 155 params->scale_bottom); 156 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, 157 MALI_C55_AEXP_HIST_SCALE_TOP_MASK, 158 MALI_C55_AEXP_HIST_SCALE_TOP(params->scale_top)); 159 160 mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET, 161 MALI_C55_AEXP_HIST_PLANE_MODE_MASK, 162 params->plane_mode); 163 164 if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) 165 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 166 MALI_C55_AEXP_HIST_SWITCH_MASK, 167 MALI_C55_AEXP_HIST_SWITCH(params->tap_point)); 168 } 169 170 static void 171 mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55, 172 union mali_c55_params_block block) 173 { 174 const struct mali_c55_params_aexp_weights *params; 175 u32 base, val, addr; 176 177 params = block.aexp_weights; 178 179 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) 180 return; 181 182 base = block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS ? 183 MALI_C55_REG_AEXP_HIST_BASE : 184 MALI_C55_REG_AEXP_IHIST_BASE; 185 186 mali_c55_ctx_update_bits(mali_c55, 187 base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, 188 MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK, 189 params->nodes_used_horiz); 190 mali_c55_ctx_update_bits(mali_c55, 191 base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, 192 MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK, 193 MALI_C55_AEXP_HIST_NODES_USED_VERT(params->nodes_used_vert)); 194 195 /* 196 * The zone weights array is a 225-element array of u8 values, but that 197 * is a bit annoying to handle given the ISP expects 32-bit writes. We 198 * just reinterpret it as 56-element array of 32-bit values for the 199 * purposes of this transaction. The last register is handled separately 200 * to stop static analysers worrying about buffer overflow. The 3 bytes 201 * of additional space at the end of the write is just padding for the 202 * array of weights in the ISP memory space anyway, so there's no risk 203 * of overwriting other registers. 204 */ 205 for (unsigned int i = 0; i < 56; i++) { 206 val = ((u32 *)params->zone_weights)[i] 207 & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK; 208 addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * i); 209 210 mali_c55_ctx_write(mali_c55, addr, val); 211 } 212 213 val = params->zone_weights[MALI_C55_MAX_ZONES - 1]; 214 addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56); 215 } 216 217 static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55, 218 union mali_c55_params_block block) 219 { 220 const struct mali_c55_params_digital_gain *dgain; 221 u32 gain; 222 223 dgain = block.digital_gain; 224 225 /* 226 * If the block is flagged as disabled we write a gain of 1.0, which in 227 * Q5.8 format is 256. 228 */ 229 gain = block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE ? 230 256 : dgain->gain; 231 232 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN, 233 MALI_C55_DIGITAL_GAIN_MASK, 234 gain); 235 } 236 237 static void mali_c55_params_awb_gains(struct mali_c55 *mali_c55, 238 union mali_c55_params_block block) 239 { 240 const struct mali_c55_params_awb_gains *gains; 241 u32 gain00, gain01, gain10, gain11; 242 243 gains = block.awb_gains; 244 245 /* 246 * There are two places AWB gains can be set in the ISP; one affects the 247 * image output data and the other affects the statistics for the 248 * AEXP-0 tap point. 249 */ 250 u32 addr1 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? 251 MALI_C55_REG_AWB_GAINS1 : 252 MALI_C55_REG_AWB_GAINS1_AEXP; 253 u32 addr2 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? 254 MALI_C55_REG_AWB_GAINS2 : 255 MALI_C55_REG_AWB_GAINS2_AEXP; 256 257 /* If the block is flagged disabled, set all of the gains to 1.0 */ 258 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 259 gain00 = 256; 260 gain01 = 256; 261 gain10 = 256; 262 gain11 = 256; 263 } else { 264 gain00 = gains->gain00; 265 gain01 = gains->gain01; 266 gain10 = gains->gain10; 267 gain11 = gains->gain11; 268 } 269 270 mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN00_MASK, 271 gain00); 272 mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN01_MASK, 273 MALI_C55_AWB_GAIN01(gain01)); 274 mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN10_MASK, 275 gain10); 276 mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN11_MASK, 277 MALI_C55_AWB_GAIN11(gain11)); 278 } 279 280 static void mali_c55_params_awb_config(struct mali_c55 *mali_c55, 281 union mali_c55_params_block block) 282 { 283 const struct mali_c55_params_awb_config *params; 284 285 params = block.awb_config; 286 287 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 288 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 289 MALI_C55_AWB_DISABLE_MASK, 290 MALI_C55_AWB_DISABLE_MASK); 291 return; 292 } 293 294 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 295 MALI_C55_AWB_DISABLE_MASK, false); 296 297 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_STATS_MODE, 298 MALI_C55_AWB_STATS_MODE_MASK, params->stats_mode); 299 300 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_WHITE_LEVEL, 301 MALI_C55_AWB_WHITE_LEVEL_MASK, params->white_level); 302 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_BLACK_LEVEL, 303 MALI_C55_AWB_BLACK_LEVEL_MASK, params->black_level); 304 305 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MAX, 306 MALI_C55_AWB_CR_MAX_MASK, params->cr_max); 307 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MIN, 308 MALI_C55_AWB_CR_MIN_MASK, params->cr_min); 309 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MAX, 310 MALI_C55_AWB_CB_MAX_MASK, params->cb_max); 311 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MIN, 312 MALI_C55_AWB_CB_MIN_MASK, params->cb_min); 313 314 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, 315 MALI_C55_AWB_NODES_USED_HORIZ_MASK, 316 params->nodes_used_horiz); 317 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, 318 MALI_C55_AWB_NODES_USED_VERT_MASK, 319 MALI_C55_AWB_NODES_USED_VERT(params->nodes_used_vert)); 320 321 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_HIGH, 322 MALI_C55_AWB_CR_HIGH_MASK, params->cr_high); 323 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_LOW, 324 MALI_C55_AWB_CR_LOW_MASK, params->cr_low); 325 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_HIGH, 326 MALI_C55_AWB_CB_HIGH_MASK, params->cb_high); 327 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_LOW, 328 MALI_C55_AWB_CB_LOW_MASK, params->cb_low); 329 330 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 331 MALI_C55_AWB_SWITCH_MASK, 332 MALI_C55_AWB_SWITCH(params->tap_point)); 333 } 334 335 static void mali_c55_params_lsc_config(struct mali_c55 *mali_c55, 336 union mali_c55_params_block block) 337 { 338 const struct mali_c55_params_mesh_shading_config *params; 339 unsigned int i; 340 u32 addr; 341 342 params = block.shading_config; 343 344 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 345 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 346 MALI_C55_MESH_SHADING_ENABLE_MASK, 347 false); 348 return; 349 } 350 351 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 352 MALI_C55_MESH_SHADING_ENABLE_MASK, true); 353 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 354 MALI_C55_MESH_SHADING_MESH_SHOW_MASK, 355 MALI_C55_MESH_SHADING_MESH_SHOW(params->mesh_show)); 356 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 357 MALI_C55_MESH_SHADING_SCALE_MASK, 358 MALI_C55_MESH_SHADING_SCALE(params->mesh_scale)); 359 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 360 MALI_C55_MESH_SHADING_PAGE_R_MASK, 361 MALI_C55_MESH_SHADING_PAGE_R(params->mesh_page_r)); 362 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 363 MALI_C55_MESH_SHADING_PAGE_G_MASK, 364 MALI_C55_MESH_SHADING_PAGE_G(params->mesh_page_g)); 365 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 366 MALI_C55_MESH_SHADING_PAGE_B_MASK, 367 MALI_C55_MESH_SHADING_PAGE_B(params->mesh_page_b)); 368 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 369 MALI_C55_MESH_SHADING_MESH_WIDTH_MASK, 370 MALI_C55_MESH_SHADING_MESH_WIDTH(params->mesh_width)); 371 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 372 MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK, 373 MALI_C55_MESH_SHADING_MESH_HEIGHT(params->mesh_height)); 374 375 for (i = 0; i < MALI_C55_NUM_MESH_SHADING_ELEMENTS; i++) { 376 addr = MALI_C55_REG_MESH_SHADING_TABLES + (i * 4); 377 mali_c55_ctx_write(mali_c55, addr, params->mesh[i]); 378 } 379 } 380 381 static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55, 382 union mali_c55_params_block block) 383 { 384 const struct mali_c55_params_mesh_shading_selection *params; 385 386 params = block.shading_selection; 387 388 if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) 389 return; 390 391 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 392 MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK, 393 params->mesh_alpha_bank_r); 394 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 395 MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK, 396 MALI_C55_MESH_SHADING_ALPHA_BANK_G(params->mesh_alpha_bank_g)); 397 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 398 MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK, 399 MALI_C55_MESH_SHADING_ALPHA_BANK_B(params->mesh_alpha_bank_b)); 400 401 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 402 MALI_C55_MESH_SHADING_ALPHA_R_MASK, 403 params->mesh_alpha_r); 404 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 405 MALI_C55_MESH_SHADING_ALPHA_G_MASK, 406 MALI_C55_MESH_SHADING_ALPHA_G(params->mesh_alpha_g)); 407 mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 408 MALI_C55_MESH_SHADING_ALPHA_B_MASK, 409 MALI_C55_MESH_SHADING_ALPHA_B(params->mesh_alpha_b)); 410 411 mali_c55_ctx_update_bits(mali_c55, 412 MALI_C55_REG_MESH_SHADING_MESH_STRENGTH, 413 MALI_c55_MESH_STRENGTH_MASK, 414 params->mesh_strength); 415 } 416 417 static const mali_c55_params_handler mali_c55_params_handlers[] = { 418 [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs, 419 [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist, 420 [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = &mali_c55_params_aexp_hist, 421 [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, 422 [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, 423 [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = &mali_c55_params_digital_gain, 424 [MALI_C55_PARAM_BLOCK_AWB_GAINS] = &mali_c55_params_awb_gains, 425 [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = &mali_c55_params_awb_config, 426 [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains, 427 [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config, 428 [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection, 429 }; 430 431 static const struct v4l2_isp_params_block_type_info 432 mali_c55_params_block_types_info[] = { 433 [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = { 434 .size = sizeof(struct mali_c55_params_sensor_off_preshading), 435 }, 436 [MALI_C55_PARAM_BLOCK_AEXP_HIST] = { 437 .size = sizeof(struct mali_c55_params_aexp_hist), 438 }, 439 [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = { 440 .size = sizeof(struct mali_c55_params_aexp_hist), 441 }, 442 [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = { 443 .size = sizeof(struct mali_c55_params_aexp_weights), 444 }, 445 [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = { 446 .size = sizeof(struct mali_c55_params_aexp_weights), 447 }, 448 [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = { 449 .size = sizeof(struct mali_c55_params_digital_gain), 450 }, 451 [MALI_C55_PARAM_BLOCK_AWB_GAINS] = { 452 .size = sizeof(struct mali_c55_params_awb_gains), 453 }, 454 [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = { 455 .size = sizeof(struct mali_c55_params_awb_config), 456 }, 457 [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = { 458 .size = sizeof(struct mali_c55_params_awb_gains), 459 }, 460 [MALI_C55_PARAM_MESH_SHADING_CONFIG] = { 461 .size = sizeof(struct mali_c55_params_mesh_shading_config), 462 }, 463 [MALI_C55_PARAM_MESH_SHADING_SELECTION] = { 464 .size = sizeof(struct mali_c55_params_mesh_shading_selection), 465 }, 466 }; 467 468 static_assert(ARRAY_SIZE(mali_c55_params_handlers) == 469 ARRAY_SIZE(mali_c55_params_block_types_info)); 470 471 static int mali_c55_params_enum_fmt_meta_out(struct file *file, void *fh, 472 struct v4l2_fmtdesc *f) 473 { 474 if (f->index) 475 return -EINVAL; 476 477 if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED) 478 return -EINVAL; 479 480 f->pixelformat = V4L2_META_FMT_MALI_C55_PARAMS; 481 482 return 0; 483 } 484 485 static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh, 486 struct v4l2_format *f) 487 { 488 static const struct v4l2_meta_format mfmt = { 489 .dataformat = V4L2_META_FMT_MALI_C55_PARAMS, 490 .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), 491 }; 492 493 f->fmt.meta = mfmt; 494 495 return 0; 496 } 497 498 static int mali_c55_params_querycap(struct file *file, 499 void *priv, struct v4l2_capability *cap) 500 { 501 strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver)); 502 strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card)); 503 504 return 0; 505 } 506 507 static const struct v4l2_ioctl_ops mali_c55_params_v4l2_ioctl_ops = { 508 .vidioc_reqbufs = vb2_ioctl_reqbufs, 509 .vidioc_querybuf = vb2_ioctl_querybuf, 510 .vidioc_create_bufs = vb2_ioctl_create_bufs, 511 .vidioc_qbuf = vb2_ioctl_qbuf, 512 .vidioc_expbuf = vb2_ioctl_expbuf, 513 .vidioc_dqbuf = vb2_ioctl_dqbuf, 514 .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 515 .vidioc_streamon = vb2_ioctl_streamon, 516 .vidioc_streamoff = vb2_ioctl_streamoff, 517 .vidioc_enum_fmt_meta_out = mali_c55_params_enum_fmt_meta_out, 518 .vidioc_g_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 519 .vidioc_s_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 520 .vidioc_try_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 521 .vidioc_querycap = mali_c55_params_querycap, 522 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 523 .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 524 }; 525 526 static const struct v4l2_file_operations mali_c55_params_v4l2_fops = { 527 .owner = THIS_MODULE, 528 .unlocked_ioctl = video_ioctl2, 529 .open = v4l2_fh_open, 530 .release = vb2_fop_release, 531 .poll = vb2_fop_poll, 532 .mmap = vb2_fop_mmap, 533 }; 534 535 static int 536 mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, 537 unsigned int *num_planes, unsigned int sizes[], 538 struct device *alloc_devs[]) 539 { 540 if (*num_planes && *num_planes > 1) 541 return -EINVAL; 542 543 if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)) 544 return -EINVAL; 545 546 *num_planes = 1; 547 548 if (!sizes[0]) 549 sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE); 550 551 return 0; 552 } 553 554 static int mali_c55_params_buf_init(struct vb2_buffer *vb) 555 { 556 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 557 struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 558 559 buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), 560 GFP_KERNEL); 561 if (!buf->config) 562 return -ENOMEM; 563 564 return 0; 565 } 566 567 static void mali_c55_params_buf_cleanup(struct vb2_buffer *vb) 568 { 569 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 570 struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 571 572 kvfree(buf->config); 573 buf->config = NULL; 574 } 575 576 static int mali_c55_params_buf_prepare(struct vb2_buffer *vb) 577 { 578 struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); 579 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 580 struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 581 struct v4l2_isp_params_buffer *config = vb2_plane_vaddr(vb, 0); 582 struct mali_c55 *mali_c55 = params->mali_c55; 583 int ret; 584 585 if (config->version != MALI_C55_PARAM_BUFFER_V1) { 586 dev_dbg(mali_c55->dev, 587 "Unsupported extensible format version: %u\n", 588 config->version); 589 return -EINVAL; 590 } 591 592 ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb, 593 v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); 594 if (ret) 595 return ret; 596 597 /* 598 * Copy the parameters buffer provided by userspace to the internal 599 * scratch buffer. This protects against the chance of userspace making 600 * changed to the buffer content whilst the driver processes it. 601 */ 602 603 memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); 604 605 return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config, 606 mali_c55_params_block_types_info, 607 ARRAY_SIZE(mali_c55_params_block_types_info)); 608 } 609 610 static void mali_c55_params_buf_queue(struct vb2_buffer *vb) 611 { 612 struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); 613 struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 614 struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 615 616 spin_lock(¶ms->buffers.lock); 617 list_add_tail(&buf->queue, ¶ms->buffers.queue); 618 spin_unlock(¶ms->buffers.lock); 619 } 620 621 static void mali_c55_params_return_buffers(struct mali_c55_params *params, 622 enum vb2_buffer_state state) 623 { 624 struct mali_c55_params_buf *buf, *tmp; 625 626 guard(spinlock)(¶ms->buffers.lock); 627 628 list_for_each_entry_safe(buf, tmp, ¶ms->buffers.queue, queue) { 629 list_del(&buf->queue); 630 vb2_buffer_done(&buf->vb.vb2_buf, state); 631 } 632 } 633 634 static int mali_c55_params_start_streaming(struct vb2_queue *q, 635 unsigned int count) 636 { 637 struct mali_c55_params *params = vb2_get_drv_priv(q); 638 struct mali_c55 *mali_c55 = params->mali_c55; 639 int ret; 640 641 ret = pm_runtime_resume_and_get(mali_c55->dev); 642 if (ret) 643 goto err_return_buffers; 644 645 ret = video_device_pipeline_alloc_start(¶ms->vdev); 646 if (ret) 647 goto err_pm_put; 648 649 if (mali_c55_pipeline_ready(mali_c55)) { 650 ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd, 651 MALI_C55_ISP_PAD_SOURCE_VIDEO, 652 BIT(0)); 653 if (ret < 0) 654 goto err_stop_pipeline; 655 } 656 657 return 0; 658 659 err_stop_pipeline: 660 video_device_pipeline_stop(¶ms->vdev); 661 err_pm_put: 662 pm_runtime_put_autosuspend(mali_c55->dev); 663 err_return_buffers: 664 mali_c55_params_return_buffers(params, VB2_BUF_STATE_QUEUED); 665 666 return ret; 667 } 668 669 static void mali_c55_params_stop_streaming(struct vb2_queue *q) 670 { 671 struct mali_c55_params *params = vb2_get_drv_priv(q); 672 struct mali_c55 *mali_c55 = params->mali_c55; 673 struct mali_c55_isp *isp = &mali_c55->isp; 674 675 if (mali_c55_pipeline_ready(mali_c55)) { 676 if (v4l2_subdev_is_streaming(&isp->sd)) 677 v4l2_subdev_disable_streams(&isp->sd, 678 MALI_C55_ISP_PAD_SOURCE_VIDEO, 679 BIT(0)); 680 } 681 682 video_device_pipeline_stop(¶ms->vdev); 683 mali_c55_params_return_buffers(params, VB2_BUF_STATE_ERROR); 684 pm_runtime_put_autosuspend(params->mali_c55->dev); 685 } 686 687 static const struct vb2_ops mali_c55_params_vb2_ops = { 688 .queue_setup = mali_c55_params_queue_setup, 689 .buf_init = mali_c55_params_buf_init, 690 .buf_cleanup = mali_c55_params_buf_cleanup, 691 .buf_queue = mali_c55_params_buf_queue, 692 .buf_prepare = mali_c55_params_buf_prepare, 693 .start_streaming = mali_c55_params_start_streaming, 694 .stop_streaming = mali_c55_params_stop_streaming, 695 }; 696 697 void mali_c55_params_write_config(struct mali_c55 *mali_c55) 698 { 699 struct mali_c55_params *params = &mali_c55->params; 700 struct v4l2_isp_params_buffer *config; 701 struct mali_c55_params_buf *buf; 702 size_t block_offset = 0; 703 size_t max_offset; 704 705 spin_lock(¶ms->buffers.lock); 706 707 buf = list_first_entry_or_null(¶ms->buffers.queue, 708 struct mali_c55_params_buf, queue); 709 if (buf) 710 list_del(&buf->queue); 711 spin_unlock(¶ms->buffers.lock); 712 713 if (!buf) 714 return; 715 716 buf->vb.sequence = mali_c55->isp.frame_sequence; 717 config = buf->config; 718 719 max_offset = config->data_size; 720 721 /* 722 * Walk the list of parameter blocks and process them. No validation is 723 * done here, as the contents of the config buffer are already checked 724 * when the buffer is queued. 725 */ 726 while (max_offset && block_offset < max_offset) { 727 union mali_c55_params_block block; 728 mali_c55_params_handler handler; 729 730 block.data = &config->data[block_offset]; 731 732 /* We checked the array index already in .buf_queue() */ 733 handler = mali_c55_params_handlers[block.header->type]; 734 handler(mali_c55, block); 735 736 block_offset += block.header->size; 737 } 738 739 vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 740 } 741 742 void mali_c55_unregister_params(struct mali_c55 *mali_c55) 743 { 744 struct mali_c55_params *params = &mali_c55->params; 745 746 if (!video_is_registered(¶ms->vdev)) 747 return; 748 749 vb2_video_unregister_device(¶ms->vdev); 750 media_entity_cleanup(¶ms->vdev.entity); 751 mutex_destroy(¶ms->lock); 752 } 753 754 int mali_c55_register_params(struct mali_c55 *mali_c55) 755 { 756 struct mali_c55_params *params = &mali_c55->params; 757 struct video_device *vdev = ¶ms->vdev; 758 struct vb2_queue *vb2q = ¶ms->queue; 759 int ret; 760 761 mutex_init(¶ms->lock); 762 INIT_LIST_HEAD(¶ms->buffers.queue); 763 spin_lock_init(¶ms->buffers.lock); 764 765 params->pad.flags = MEDIA_PAD_FL_SOURCE; 766 ret = media_entity_pads_init(¶ms->vdev.entity, 1, ¶ms->pad); 767 if (ret) 768 goto err_destroy_mutex; 769 770 vb2q->type = V4L2_BUF_TYPE_META_OUTPUT; 771 vb2q->io_modes = VB2_MMAP | VB2_DMABUF; 772 vb2q->drv_priv = params; 773 vb2q->mem_ops = &vb2_dma_contig_memops; 774 vb2q->ops = &mali_c55_params_vb2_ops; 775 vb2q->buf_struct_size = sizeof(struct mali_c55_params_buf); 776 vb2q->min_queued_buffers = 1; 777 vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 778 vb2q->lock = ¶ms->lock; 779 vb2q->dev = mali_c55->dev; 780 781 ret = vb2_queue_init(vb2q); 782 if (ret) { 783 dev_err(mali_c55->dev, "params vb2 queue init failed\n"); 784 goto err_cleanup_entity; 785 } 786 787 strscpy(params->vdev.name, "mali-c55 3a params", 788 sizeof(params->vdev.name)); 789 vdev->release = video_device_release_empty; 790 vdev->fops = &mali_c55_params_v4l2_fops; 791 vdev->ioctl_ops = &mali_c55_params_v4l2_ioctl_ops; 792 vdev->lock = ¶ms->lock; 793 vdev->v4l2_dev = &mali_c55->v4l2_dev; 794 vdev->queue = ¶ms->queue; 795 vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING | 796 V4L2_CAP_IO_MC; 797 vdev->vfl_dir = VFL_DIR_TX; 798 video_set_drvdata(vdev, params); 799 800 ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 801 if (ret) { 802 dev_err(mali_c55->dev, 803 "failed to register params video device\n"); 804 goto err_release_vb2q; 805 } 806 807 params->mali_c55 = mali_c55; 808 809 return 0; 810 811 err_release_vb2q: 812 vb2_queue_release(vb2q); 813 err_cleanup_entity: 814 media_entity_cleanup(¶ms->vdev.entity); 815 err_destroy_mutex: 816 mutex_destroy(¶ms->lock); 817 818 return ret; 819 } 820