101535ea0SDaniel Scally // SPDX-License-Identifier: GPL-2.0 201535ea0SDaniel Scally /* 301535ea0SDaniel Scally * ARM Mali-C55 ISP Driver - Configuration parameters output device 401535ea0SDaniel Scally * 501535ea0SDaniel Scally * Copyright (C) 2025 Ideas on Board Oy 601535ea0SDaniel Scally */ 701535ea0SDaniel Scally #include <linux/media/arm/mali-c55-config.h> 801535ea0SDaniel Scally #include <linux/pm_runtime.h> 901535ea0SDaniel Scally 1001535ea0SDaniel Scally #include <media/media-entity.h> 1101535ea0SDaniel Scally #include <media/v4l2-dev.h> 1201535ea0SDaniel Scally #include <media/v4l2-event.h> 1301535ea0SDaniel Scally #include <media/v4l2-fh.h> 1401535ea0SDaniel Scally #include <media/v4l2-ioctl.h> 1501535ea0SDaniel Scally #include <media/v4l2-isp.h> 1601535ea0SDaniel Scally #include <media/videobuf2-core.h> 1701535ea0SDaniel Scally #include <media/videobuf2-dma-contig.h> 1801535ea0SDaniel Scally 1901535ea0SDaniel Scally #include "mali-c55-common.h" 2001535ea0SDaniel Scally #include "mali-c55-registers.h" 2101535ea0SDaniel Scally 2201535ea0SDaniel Scally /** 2301535ea0SDaniel Scally * union mali_c55_params_block - Generalisation of a parameter block 2401535ea0SDaniel Scally * 2501535ea0SDaniel Scally * This union allows the driver to treat a block as a generic pointer to this 2601535ea0SDaniel Scally * union and safely access the header and block-specific struct without having 2701535ea0SDaniel Scally * to resort to casting. The header member is accessed first, and the type field 2801535ea0SDaniel Scally * checked which allows the driver to determine which of the other members 2901535ea0SDaniel Scally * should be used. The data member at the end allows a pointer to an address 3001535ea0SDaniel Scally * within the data member of :c:type:`mali_c55_params_buffer` to initialise a 3101535ea0SDaniel Scally * union variable. 3201535ea0SDaniel Scally * 3301535ea0SDaniel Scally * @header: Pointer to the shared header struct embedded as the 3401535ea0SDaniel Scally * first member of all the possible other members (except 3501535ea0SDaniel Scally * @data). This member would be accessed first and the type 3601535ea0SDaniel Scally * field checked to determine which of the other members 3701535ea0SDaniel Scally * should be accessed. 3801535ea0SDaniel Scally * @sensor_offs: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS 3901535ea0SDaniel Scally * @aexp_hist: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST and 4001535ea0SDaniel Scally * header->type == MALI_C55_PARAM_BLOCK_AEXP_IHIST 4101535ea0SDaniel Scally * @aexp_weights: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS 4201535ea0SDaniel Scally * and header->type = MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS 4301535ea0SDaniel Scally * @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN 4401535ea0SDaniel Scally * @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and 4501535ea0SDaniel Scally * header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP 4601535ea0SDaniel Scally * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG 4701535ea0SDaniel Scally * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION 4801535ea0SDaniel Scally * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS 4901535ea0SDaniel Scally * @data: Allows easy initialisation of a union variable with a 5001535ea0SDaniel Scally * pointer into a __u8 array. 5101535ea0SDaniel Scally */ 5201535ea0SDaniel Scally union mali_c55_params_block { 5301535ea0SDaniel Scally const struct v4l2_isp_params_block_header *header; 5401535ea0SDaniel Scally const struct mali_c55_params_sensor_off_preshading *sensor_offs; 5501535ea0SDaniel Scally const struct mali_c55_params_aexp_hist *aexp_hist; 5601535ea0SDaniel Scally const struct mali_c55_params_aexp_weights *aexp_weights; 5701535ea0SDaniel Scally const struct mali_c55_params_digital_gain *digital_gain; 5801535ea0SDaniel Scally const struct mali_c55_params_awb_gains *awb_gains; 5901535ea0SDaniel Scally const struct mali_c55_params_awb_config *awb_config; 6001535ea0SDaniel Scally const struct mali_c55_params_mesh_shading_config *shading_config; 6101535ea0SDaniel Scally const struct mali_c55_params_mesh_shading_selection *shading_selection; 6201535ea0SDaniel Scally const __u8 *data; 6301535ea0SDaniel Scally }; 6401535ea0SDaniel Scally 6501535ea0SDaniel Scally typedef void (*mali_c55_params_handler)(struct mali_c55 *mali_c55, 6601535ea0SDaniel Scally union mali_c55_params_block block); 6701535ea0SDaniel Scally 6801535ea0SDaniel Scally #define to_mali_c55_params_buf(vbuf) \ 6901535ea0SDaniel Scally container_of(vbuf, struct mali_c55_params_buf, vb) 7001535ea0SDaniel Scally 7101535ea0SDaniel Scally static void mali_c55_params_sensor_offs(struct mali_c55 *mali_c55, 7201535ea0SDaniel Scally union mali_c55_params_block block) 7301535ea0SDaniel Scally { 7401535ea0SDaniel Scally const struct mali_c55_params_sensor_off_preshading *p; 7501535ea0SDaniel Scally __u32 global_offset; 7601535ea0SDaniel Scally 7701535ea0SDaniel Scally p = block.sensor_offs; 7801535ea0SDaniel Scally 7901535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 8001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, 8101535ea0SDaniel Scally MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, 8201535ea0SDaniel Scally MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH); 8301535ea0SDaniel Scally return; 8401535ea0SDaniel Scally } 8501535ea0SDaniel Scally 8601535ea0SDaniel Scally if (!(p->chan00 || p->chan01 || p->chan10 || p->chan11)) 8701535ea0SDaniel Scally return; 8801535ea0SDaniel Scally 8901535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_00, 9001535ea0SDaniel Scally p->chan00 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 9101535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_01, 9201535ea0SDaniel Scally p->chan01 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 9301535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_10, 9401535ea0SDaniel Scally p->chan10 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 9501535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_11, 9601535ea0SDaniel Scally p->chan11 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK); 9701535ea0SDaniel Scally 9801535ea0SDaniel Scally /* 9901535ea0SDaniel Scally * The average offset is applied as a global offset for the digital 10001535ea0SDaniel Scally * gain block 10101535ea0SDaniel Scally */ 10201535ea0SDaniel Scally global_offset = (p->chan00 + p->chan01 + p->chan10 + p->chan11) >> 2; 10301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN_OFFSET, 10401535ea0SDaniel Scally MALI_C55_DIGITAL_GAIN_OFFSET_MASK, 10501535ea0SDaniel Scally global_offset); 10601535ea0SDaniel Scally 10701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3, 10801535ea0SDaniel Scally MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH, 10901535ea0SDaniel Scally 0x00); 11001535ea0SDaniel Scally } 11101535ea0SDaniel Scally 11201535ea0SDaniel Scally static void mali_c55_params_aexp_hist(struct mali_c55 *mali_c55, 11301535ea0SDaniel Scally union mali_c55_params_block block) 11401535ea0SDaniel Scally { 11501535ea0SDaniel Scally const struct mali_c55_params_aexp_hist *params; 11601535ea0SDaniel Scally u32 disable_mask; 11701535ea0SDaniel Scally u32 disable_val; 11801535ea0SDaniel Scally u32 base; 11901535ea0SDaniel Scally 12001535ea0SDaniel Scally if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) { 12101535ea0SDaniel Scally disable_mask = MALI_C55_AEXP_HIST_DISABLE_MASK; 12201535ea0SDaniel Scally disable_val = MALI_C55_AEXP_HIST_DISABLE; 12301535ea0SDaniel Scally base = MALI_C55_REG_AEXP_HIST_BASE; 12401535ea0SDaniel Scally } else { 12501535ea0SDaniel Scally disable_mask = MALI_C55_AEXP_IHIST_DISABLE_MASK; 12601535ea0SDaniel Scally disable_val = MALI_C55_AEXP_IHIST_DISABLE; 12701535ea0SDaniel Scally base = MALI_C55_REG_AEXP_IHIST_BASE; 12801535ea0SDaniel Scally } 12901535ea0SDaniel Scally 13001535ea0SDaniel Scally params = block.aexp_hist; 13101535ea0SDaniel Scally 13201535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 13301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 13401535ea0SDaniel Scally disable_mask, disable_val); 13501535ea0SDaniel Scally return; 13601535ea0SDaniel Scally } 13701535ea0SDaniel Scally 13801535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 13901535ea0SDaniel Scally disable_mask, false); 14001535ea0SDaniel Scally 14101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 14201535ea0SDaniel Scally MALI_C55_AEXP_HIST_SKIP_X_MASK, params->skip_x); 14301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 14401535ea0SDaniel Scally MALI_C55_AEXP_HIST_OFFSET_X_MASK, 14501535ea0SDaniel Scally MALI_C55_AEXP_HIST_OFFSET_X(params->offset_x)); 14601535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 14701535ea0SDaniel Scally MALI_C55_AEXP_HIST_SKIP_Y_MASK, 14801535ea0SDaniel Scally MALI_C55_AEXP_HIST_SKIP_Y(params->skip_y)); 14901535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET, 15001535ea0SDaniel Scally MALI_C55_AEXP_HIST_OFFSET_Y_MASK, 15101535ea0SDaniel Scally MALI_C55_AEXP_HIST_OFFSET_Y(params->offset_y)); 15201535ea0SDaniel Scally 15301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, 15401535ea0SDaniel Scally MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK, 15501535ea0SDaniel Scally params->scale_bottom); 15601535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET, 15701535ea0SDaniel Scally MALI_C55_AEXP_HIST_SCALE_TOP_MASK, 15801535ea0SDaniel Scally MALI_C55_AEXP_HIST_SCALE_TOP(params->scale_top)); 15901535ea0SDaniel Scally 16001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET, 16101535ea0SDaniel Scally MALI_C55_AEXP_HIST_PLANE_MODE_MASK, 16201535ea0SDaniel Scally params->plane_mode); 16301535ea0SDaniel Scally 16401535ea0SDaniel Scally if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) 16501535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 16601535ea0SDaniel Scally MALI_C55_AEXP_HIST_SWITCH_MASK, 16701535ea0SDaniel Scally MALI_C55_AEXP_HIST_SWITCH(params->tap_point)); 16801535ea0SDaniel Scally } 16901535ea0SDaniel Scally 17001535ea0SDaniel Scally static void 17101535ea0SDaniel Scally mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55, 17201535ea0SDaniel Scally union mali_c55_params_block block) 17301535ea0SDaniel Scally { 17401535ea0SDaniel Scally const struct mali_c55_params_aexp_weights *params; 17501535ea0SDaniel Scally u32 base, val, addr; 17601535ea0SDaniel Scally 17701535ea0SDaniel Scally params = block.aexp_weights; 17801535ea0SDaniel Scally 17901535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) 18001535ea0SDaniel Scally return; 18101535ea0SDaniel Scally 18201535ea0SDaniel Scally base = block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS ? 18301535ea0SDaniel Scally MALI_C55_REG_AEXP_HIST_BASE : 18401535ea0SDaniel Scally MALI_C55_REG_AEXP_IHIST_BASE; 18501535ea0SDaniel Scally 18601535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, 18701535ea0SDaniel Scally base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, 18801535ea0SDaniel Scally MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK, 18901535ea0SDaniel Scally params->nodes_used_horiz); 19001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, 19101535ea0SDaniel Scally base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET, 19201535ea0SDaniel Scally MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK, 19301535ea0SDaniel Scally MALI_C55_AEXP_HIST_NODES_USED_VERT(params->nodes_used_vert)); 19401535ea0SDaniel Scally 19501535ea0SDaniel Scally /* 19601535ea0SDaniel Scally * The zone weights array is a 225-element array of u8 values, but that 19701535ea0SDaniel Scally * is a bit annoying to handle given the ISP expects 32-bit writes. We 19801535ea0SDaniel Scally * just reinterpret it as 56-element array of 32-bit values for the 19901535ea0SDaniel Scally * purposes of this transaction. The last register is handled separately 20001535ea0SDaniel Scally * to stop static analysers worrying about buffer overflow. The 3 bytes 20101535ea0SDaniel Scally * of additional space at the end of the write is just padding for the 20201535ea0SDaniel Scally * array of weights in the ISP memory space anyway, so there's no risk 20301535ea0SDaniel Scally * of overwriting other registers. 20401535ea0SDaniel Scally */ 20501535ea0SDaniel Scally for (unsigned int i = 0; i < 56; i++) { 20601535ea0SDaniel Scally val = ((u32 *)params->zone_weights)[i] 20701535ea0SDaniel Scally & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK; 20801535ea0SDaniel Scally addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * i); 20901535ea0SDaniel Scally 21001535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, addr, val); 21101535ea0SDaniel Scally } 21201535ea0SDaniel Scally 21301535ea0SDaniel Scally val = params->zone_weights[MALI_C55_MAX_ZONES - 1]; 21401535ea0SDaniel Scally addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56); 21501535ea0SDaniel Scally } 21601535ea0SDaniel Scally 21701535ea0SDaniel Scally static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55, 21801535ea0SDaniel Scally union mali_c55_params_block block) 21901535ea0SDaniel Scally { 22001535ea0SDaniel Scally const struct mali_c55_params_digital_gain *dgain; 22101535ea0SDaniel Scally u32 gain; 22201535ea0SDaniel Scally 22301535ea0SDaniel Scally dgain = block.digital_gain; 22401535ea0SDaniel Scally 22501535ea0SDaniel Scally /* 22601535ea0SDaniel Scally * If the block is flagged as disabled we write a gain of 1.0, which in 22701535ea0SDaniel Scally * Q5.8 format is 256. 22801535ea0SDaniel Scally */ 22901535ea0SDaniel Scally gain = block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE ? 23001535ea0SDaniel Scally 256 : dgain->gain; 23101535ea0SDaniel Scally 23201535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN, 23301535ea0SDaniel Scally MALI_C55_DIGITAL_GAIN_MASK, 23401535ea0SDaniel Scally gain); 23501535ea0SDaniel Scally } 23601535ea0SDaniel Scally 23701535ea0SDaniel Scally static void mali_c55_params_awb_gains(struct mali_c55 *mali_c55, 23801535ea0SDaniel Scally union mali_c55_params_block block) 23901535ea0SDaniel Scally { 24001535ea0SDaniel Scally const struct mali_c55_params_awb_gains *gains; 24101535ea0SDaniel Scally u32 gain00, gain01, gain10, gain11; 24201535ea0SDaniel Scally 24301535ea0SDaniel Scally gains = block.awb_gains; 24401535ea0SDaniel Scally 24501535ea0SDaniel Scally /* 24601535ea0SDaniel Scally * There are two places AWB gains can be set in the ISP; one affects the 24701535ea0SDaniel Scally * image output data and the other affects the statistics for the 24801535ea0SDaniel Scally * AEXP-0 tap point. 24901535ea0SDaniel Scally */ 25001535ea0SDaniel Scally u32 addr1 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? 25101535ea0SDaniel Scally MALI_C55_REG_AWB_GAINS1 : 25201535ea0SDaniel Scally MALI_C55_REG_AWB_GAINS1_AEXP; 25301535ea0SDaniel Scally u32 addr2 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ? 25401535ea0SDaniel Scally MALI_C55_REG_AWB_GAINS2 : 25501535ea0SDaniel Scally MALI_C55_REG_AWB_GAINS2_AEXP; 25601535ea0SDaniel Scally 25701535ea0SDaniel Scally /* If the block is flagged disabled, set all of the gains to 1.0 */ 25801535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 25901535ea0SDaniel Scally gain00 = 256; 26001535ea0SDaniel Scally gain01 = 256; 26101535ea0SDaniel Scally gain10 = 256; 26201535ea0SDaniel Scally gain11 = 256; 26301535ea0SDaniel Scally } else { 26401535ea0SDaniel Scally gain00 = gains->gain00; 26501535ea0SDaniel Scally gain01 = gains->gain01; 26601535ea0SDaniel Scally gain10 = gains->gain10; 26701535ea0SDaniel Scally gain11 = gains->gain11; 26801535ea0SDaniel Scally } 26901535ea0SDaniel Scally 27001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN00_MASK, 27101535ea0SDaniel Scally gain00); 27201535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN01_MASK, 27301535ea0SDaniel Scally MALI_C55_AWB_GAIN01(gain01)); 27401535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN10_MASK, 27501535ea0SDaniel Scally gain10); 27601535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN11_MASK, 27701535ea0SDaniel Scally MALI_C55_AWB_GAIN11(gain11)); 27801535ea0SDaniel Scally } 27901535ea0SDaniel Scally 28001535ea0SDaniel Scally static void mali_c55_params_awb_config(struct mali_c55 *mali_c55, 28101535ea0SDaniel Scally union mali_c55_params_block block) 28201535ea0SDaniel Scally { 28301535ea0SDaniel Scally const struct mali_c55_params_awb_config *params; 28401535ea0SDaniel Scally 28501535ea0SDaniel Scally params = block.awb_config; 28601535ea0SDaniel Scally 28701535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 28801535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 28901535ea0SDaniel Scally MALI_C55_AWB_DISABLE_MASK, 29001535ea0SDaniel Scally MALI_C55_AWB_DISABLE_MASK); 29101535ea0SDaniel Scally return; 29201535ea0SDaniel Scally } 29301535ea0SDaniel Scally 29401535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 29501535ea0SDaniel Scally MALI_C55_AWB_DISABLE_MASK, false); 29601535ea0SDaniel Scally 29701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_STATS_MODE, 29801535ea0SDaniel Scally MALI_C55_AWB_STATS_MODE_MASK, params->stats_mode); 29901535ea0SDaniel Scally 30001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_WHITE_LEVEL, 30101535ea0SDaniel Scally MALI_C55_AWB_WHITE_LEVEL_MASK, params->white_level); 30201535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_BLACK_LEVEL, 30301535ea0SDaniel Scally MALI_C55_AWB_BLACK_LEVEL_MASK, params->black_level); 30401535ea0SDaniel Scally 30501535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MAX, 30601535ea0SDaniel Scally MALI_C55_AWB_CR_MAX_MASK, params->cr_max); 30701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MIN, 30801535ea0SDaniel Scally MALI_C55_AWB_CR_MIN_MASK, params->cr_min); 30901535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MAX, 31001535ea0SDaniel Scally MALI_C55_AWB_CB_MAX_MASK, params->cb_max); 31101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MIN, 31201535ea0SDaniel Scally MALI_C55_AWB_CB_MIN_MASK, params->cb_min); 31301535ea0SDaniel Scally 31401535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, 31501535ea0SDaniel Scally MALI_C55_AWB_NODES_USED_HORIZ_MASK, 31601535ea0SDaniel Scally params->nodes_used_horiz); 31701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED, 31801535ea0SDaniel Scally MALI_C55_AWB_NODES_USED_VERT_MASK, 31901535ea0SDaniel Scally MALI_C55_AWB_NODES_USED_VERT(params->nodes_used_vert)); 32001535ea0SDaniel Scally 32101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_HIGH, 32201535ea0SDaniel Scally MALI_C55_AWB_CR_HIGH_MASK, params->cr_high); 32301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_LOW, 32401535ea0SDaniel Scally MALI_C55_AWB_CR_LOW_MASK, params->cr_low); 32501535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_HIGH, 32601535ea0SDaniel Scally MALI_C55_AWB_CB_HIGH_MASK, params->cb_high); 32701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_LOW, 32801535ea0SDaniel Scally MALI_C55_AWB_CB_LOW_MASK, params->cb_low); 32901535ea0SDaniel Scally 33001535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG, 33101535ea0SDaniel Scally MALI_C55_AWB_SWITCH_MASK, 33201535ea0SDaniel Scally MALI_C55_AWB_SWITCH(params->tap_point)); 33301535ea0SDaniel Scally } 33401535ea0SDaniel Scally 33501535ea0SDaniel Scally static void mali_c55_params_lsc_config(struct mali_c55 *mali_c55, 33601535ea0SDaniel Scally union mali_c55_params_block block) 33701535ea0SDaniel Scally { 33801535ea0SDaniel Scally const struct mali_c55_params_mesh_shading_config *params; 33901535ea0SDaniel Scally unsigned int i; 34001535ea0SDaniel Scally u32 addr; 34101535ea0SDaniel Scally 34201535ea0SDaniel Scally params = block.shading_config; 34301535ea0SDaniel Scally 34401535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { 34501535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 34601535ea0SDaniel Scally MALI_C55_MESH_SHADING_ENABLE_MASK, 34701535ea0SDaniel Scally false); 34801535ea0SDaniel Scally return; 34901535ea0SDaniel Scally } 35001535ea0SDaniel Scally 35101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 35201535ea0SDaniel Scally MALI_C55_MESH_SHADING_ENABLE_MASK, true); 35301535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 35401535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_SHOW_MASK, 35501535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_SHOW(params->mesh_show)); 35601535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 35701535ea0SDaniel Scally MALI_C55_MESH_SHADING_SCALE_MASK, 35801535ea0SDaniel Scally MALI_C55_MESH_SHADING_SCALE(params->mesh_scale)); 35901535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 36001535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_R_MASK, 36101535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_R(params->mesh_page_r)); 36201535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 36301535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_G_MASK, 36401535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_G(params->mesh_page_g)); 36501535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 36601535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_B_MASK, 36701535ea0SDaniel Scally MALI_C55_MESH_SHADING_PAGE_B(params->mesh_page_b)); 36801535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 36901535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_WIDTH_MASK, 37001535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_WIDTH(params->mesh_width)); 37101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG, 37201535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK, 37301535ea0SDaniel Scally MALI_C55_MESH_SHADING_MESH_HEIGHT(params->mesh_height)); 37401535ea0SDaniel Scally 37501535ea0SDaniel Scally for (i = 0; i < MALI_C55_NUM_MESH_SHADING_ELEMENTS; i++) { 37601535ea0SDaniel Scally addr = MALI_C55_REG_MESH_SHADING_TABLES + (i * 4); 37701535ea0SDaniel Scally mali_c55_ctx_write(mali_c55, addr, params->mesh[i]); 37801535ea0SDaniel Scally } 37901535ea0SDaniel Scally } 38001535ea0SDaniel Scally 38101535ea0SDaniel Scally static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55, 38201535ea0SDaniel Scally union mali_c55_params_block block) 38301535ea0SDaniel Scally { 38401535ea0SDaniel Scally const struct mali_c55_params_mesh_shading_selection *params; 38501535ea0SDaniel Scally 38601535ea0SDaniel Scally params = block.shading_selection; 38701535ea0SDaniel Scally 38801535ea0SDaniel Scally if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) 38901535ea0SDaniel Scally return; 39001535ea0SDaniel Scally 39101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 39201535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK, 39301535ea0SDaniel Scally params->mesh_alpha_bank_r); 39401535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 39501535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK, 39601535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_BANK_G(params->mesh_alpha_bank_g)); 39701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK, 39801535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK, 39901535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_BANK_B(params->mesh_alpha_bank_b)); 40001535ea0SDaniel Scally 40101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 40201535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_R_MASK, 40301535ea0SDaniel Scally params->mesh_alpha_r); 40401535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 40501535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_G_MASK, 40601535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_G(params->mesh_alpha_g)); 40701535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA, 40801535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_B_MASK, 40901535ea0SDaniel Scally MALI_C55_MESH_SHADING_ALPHA_B(params->mesh_alpha_b)); 41001535ea0SDaniel Scally 41101535ea0SDaniel Scally mali_c55_ctx_update_bits(mali_c55, 41201535ea0SDaniel Scally MALI_C55_REG_MESH_SHADING_MESH_STRENGTH, 41301535ea0SDaniel Scally MALI_c55_MESH_STRENGTH_MASK, 41401535ea0SDaniel Scally params->mesh_strength); 41501535ea0SDaniel Scally } 41601535ea0SDaniel Scally 41701535ea0SDaniel Scally static const mali_c55_params_handler mali_c55_params_handlers[] = { 41801535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs, 41901535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist, 42001535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = &mali_c55_params_aexp_hist, 42101535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, 42201535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights, 42301535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = &mali_c55_params_digital_gain, 42401535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_GAINS] = &mali_c55_params_awb_gains, 42501535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = &mali_c55_params_awb_config, 42601535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains, 42701535ea0SDaniel Scally [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config, 42801535ea0SDaniel Scally [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection, 42901535ea0SDaniel Scally }; 43001535ea0SDaniel Scally 431d619dd9aSJacopo Mondi static const struct v4l2_isp_params_block_type_info 432d619dd9aSJacopo Mondi mali_c55_params_block_types_info[] = { 43301535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = { 43401535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_sensor_off_preshading), 43501535ea0SDaniel Scally }, 43601535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_HIST] = { 43701535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_aexp_hist), 43801535ea0SDaniel Scally }, 43901535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = { 44001535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_aexp_hist), 44101535ea0SDaniel Scally }, 44201535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = { 44301535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_aexp_weights), 44401535ea0SDaniel Scally }, 44501535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = { 44601535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_aexp_weights), 44701535ea0SDaniel Scally }, 44801535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = { 44901535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_digital_gain), 45001535ea0SDaniel Scally }, 45101535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_GAINS] = { 45201535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_awb_gains), 45301535ea0SDaniel Scally }, 45401535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = { 45501535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_awb_config), 45601535ea0SDaniel Scally }, 45701535ea0SDaniel Scally [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = { 45801535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_awb_gains), 45901535ea0SDaniel Scally }, 46001535ea0SDaniel Scally [MALI_C55_PARAM_MESH_SHADING_CONFIG] = { 46101535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_mesh_shading_config), 46201535ea0SDaniel Scally }, 46301535ea0SDaniel Scally [MALI_C55_PARAM_MESH_SHADING_SELECTION] = { 46401535ea0SDaniel Scally .size = sizeof(struct mali_c55_params_mesh_shading_selection), 46501535ea0SDaniel Scally }, 46601535ea0SDaniel Scally }; 46701535ea0SDaniel Scally 468*1435db44SJacopo Mondi static_assert(ARRAY_SIZE(mali_c55_params_handlers) == 469*1435db44SJacopo Mondi ARRAY_SIZE(mali_c55_params_block_types_info)); 470*1435db44SJacopo Mondi 47101535ea0SDaniel Scally static int mali_c55_params_enum_fmt_meta_out(struct file *file, void *fh, 47201535ea0SDaniel Scally struct v4l2_fmtdesc *f) 47301535ea0SDaniel Scally { 47401535ea0SDaniel Scally if (f->index) 47501535ea0SDaniel Scally return -EINVAL; 47601535ea0SDaniel Scally 47701535ea0SDaniel Scally if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED) 47801535ea0SDaniel Scally return -EINVAL; 47901535ea0SDaniel Scally 48001535ea0SDaniel Scally f->pixelformat = V4L2_META_FMT_MALI_C55_PARAMS; 48101535ea0SDaniel Scally 48201535ea0SDaniel Scally return 0; 48301535ea0SDaniel Scally } 48401535ea0SDaniel Scally 48501535ea0SDaniel Scally static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh, 48601535ea0SDaniel Scally struct v4l2_format *f) 48701535ea0SDaniel Scally { 48801535ea0SDaniel Scally static const struct v4l2_meta_format mfmt = { 48901535ea0SDaniel Scally .dataformat = V4L2_META_FMT_MALI_C55_PARAMS, 49001535ea0SDaniel Scally .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), 49101535ea0SDaniel Scally }; 49201535ea0SDaniel Scally 49301535ea0SDaniel Scally f->fmt.meta = mfmt; 49401535ea0SDaniel Scally 49501535ea0SDaniel Scally return 0; 49601535ea0SDaniel Scally } 49701535ea0SDaniel Scally 49801535ea0SDaniel Scally static int mali_c55_params_querycap(struct file *file, 49901535ea0SDaniel Scally void *priv, struct v4l2_capability *cap) 50001535ea0SDaniel Scally { 50101535ea0SDaniel Scally strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver)); 50201535ea0SDaniel Scally strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card)); 50301535ea0SDaniel Scally 50401535ea0SDaniel Scally return 0; 50501535ea0SDaniel Scally } 50601535ea0SDaniel Scally 50701535ea0SDaniel Scally static const struct v4l2_ioctl_ops mali_c55_params_v4l2_ioctl_ops = { 50801535ea0SDaniel Scally .vidioc_reqbufs = vb2_ioctl_reqbufs, 50901535ea0SDaniel Scally .vidioc_querybuf = vb2_ioctl_querybuf, 51001535ea0SDaniel Scally .vidioc_create_bufs = vb2_ioctl_create_bufs, 51101535ea0SDaniel Scally .vidioc_qbuf = vb2_ioctl_qbuf, 51201535ea0SDaniel Scally .vidioc_expbuf = vb2_ioctl_expbuf, 51301535ea0SDaniel Scally .vidioc_dqbuf = vb2_ioctl_dqbuf, 51401535ea0SDaniel Scally .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 51501535ea0SDaniel Scally .vidioc_streamon = vb2_ioctl_streamon, 51601535ea0SDaniel Scally .vidioc_streamoff = vb2_ioctl_streamoff, 51701535ea0SDaniel Scally .vidioc_enum_fmt_meta_out = mali_c55_params_enum_fmt_meta_out, 51801535ea0SDaniel Scally .vidioc_g_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 51901535ea0SDaniel Scally .vidioc_s_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 52001535ea0SDaniel Scally .vidioc_try_fmt_meta_out = mali_c55_params_g_fmt_meta_out, 52101535ea0SDaniel Scally .vidioc_querycap = mali_c55_params_querycap, 52201535ea0SDaniel Scally .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 52301535ea0SDaniel Scally .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 52401535ea0SDaniel Scally }; 52501535ea0SDaniel Scally 52601535ea0SDaniel Scally static const struct v4l2_file_operations mali_c55_params_v4l2_fops = { 52701535ea0SDaniel Scally .owner = THIS_MODULE, 52801535ea0SDaniel Scally .unlocked_ioctl = video_ioctl2, 52901535ea0SDaniel Scally .open = v4l2_fh_open, 53001535ea0SDaniel Scally .release = vb2_fop_release, 53101535ea0SDaniel Scally .poll = vb2_fop_poll, 53201535ea0SDaniel Scally .mmap = vb2_fop_mmap, 53301535ea0SDaniel Scally }; 53401535ea0SDaniel Scally 53501535ea0SDaniel Scally static int 53601535ea0SDaniel Scally mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, 53701535ea0SDaniel Scally unsigned int *num_planes, unsigned int sizes[], 53801535ea0SDaniel Scally struct device *alloc_devs[]) 53901535ea0SDaniel Scally { 54001535ea0SDaniel Scally if (*num_planes && *num_planes > 1) 54101535ea0SDaniel Scally return -EINVAL; 54201535ea0SDaniel Scally 54301535ea0SDaniel Scally if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)) 54401535ea0SDaniel Scally return -EINVAL; 54501535ea0SDaniel Scally 54601535ea0SDaniel Scally *num_planes = 1; 54701535ea0SDaniel Scally 54801535ea0SDaniel Scally if (!sizes[0]) 54901535ea0SDaniel Scally sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE); 55001535ea0SDaniel Scally 55101535ea0SDaniel Scally return 0; 55201535ea0SDaniel Scally } 55301535ea0SDaniel Scally 55401535ea0SDaniel Scally static int mali_c55_params_buf_init(struct vb2_buffer *vb) 55501535ea0SDaniel Scally { 55601535ea0SDaniel Scally struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 55701535ea0SDaniel Scally struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 55801535ea0SDaniel Scally 55901535ea0SDaniel Scally buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), 56001535ea0SDaniel Scally GFP_KERNEL); 56101535ea0SDaniel Scally if (!buf->config) 56201535ea0SDaniel Scally return -ENOMEM; 56301535ea0SDaniel Scally 56401535ea0SDaniel Scally return 0; 56501535ea0SDaniel Scally } 56601535ea0SDaniel Scally 56701535ea0SDaniel Scally static void mali_c55_params_buf_cleanup(struct vb2_buffer *vb) 56801535ea0SDaniel Scally { 56901535ea0SDaniel Scally struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 57001535ea0SDaniel Scally struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 57101535ea0SDaniel Scally 57201535ea0SDaniel Scally kvfree(buf->config); 57301535ea0SDaniel Scally buf->config = NULL; 57401535ea0SDaniel Scally } 57501535ea0SDaniel Scally 57601535ea0SDaniel Scally static int mali_c55_params_buf_prepare(struct vb2_buffer *vb) 57701535ea0SDaniel Scally { 57801535ea0SDaniel Scally struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); 57901535ea0SDaniel Scally struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 58001535ea0SDaniel Scally struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 58101535ea0SDaniel Scally struct v4l2_isp_params_buffer *config = vb2_plane_vaddr(vb, 0); 58201535ea0SDaniel Scally struct mali_c55 *mali_c55 = params->mali_c55; 58301535ea0SDaniel Scally int ret; 58401535ea0SDaniel Scally 58501535ea0SDaniel Scally if (config->version != MALI_C55_PARAM_BUFFER_V1) { 58601535ea0SDaniel Scally dev_dbg(mali_c55->dev, 58701535ea0SDaniel Scally "Unsupported extensible format version: %u\n", 58801535ea0SDaniel Scally config->version); 58901535ea0SDaniel Scally return -EINVAL; 59001535ea0SDaniel Scally } 59101535ea0SDaniel Scally 59201535ea0SDaniel Scally ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb, 59301535ea0SDaniel Scally v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); 59401535ea0SDaniel Scally if (ret) 59501535ea0SDaniel Scally return ret; 59601535ea0SDaniel Scally 59701535ea0SDaniel Scally /* 59801535ea0SDaniel Scally * Copy the parameters buffer provided by userspace to the internal 59901535ea0SDaniel Scally * scratch buffer. This protects against the chance of userspace making 60001535ea0SDaniel Scally * changed to the buffer content whilst the driver processes it. 60101535ea0SDaniel Scally */ 60201535ea0SDaniel Scally 60301535ea0SDaniel Scally memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); 60401535ea0SDaniel Scally 60501535ea0SDaniel Scally return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config, 606d619dd9aSJacopo Mondi mali_c55_params_block_types_info, 607d619dd9aSJacopo Mondi ARRAY_SIZE(mali_c55_params_block_types_info)); 60801535ea0SDaniel Scally } 60901535ea0SDaniel Scally 61001535ea0SDaniel Scally static void mali_c55_params_buf_queue(struct vb2_buffer *vb) 61101535ea0SDaniel Scally { 61201535ea0SDaniel Scally struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue); 61301535ea0SDaniel Scally struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 61401535ea0SDaniel Scally struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); 61501535ea0SDaniel Scally 61601535ea0SDaniel Scally spin_lock(¶ms->buffers.lock); 61701535ea0SDaniel Scally list_add_tail(&buf->queue, ¶ms->buffers.queue); 61801535ea0SDaniel Scally spin_unlock(¶ms->buffers.lock); 61901535ea0SDaniel Scally } 62001535ea0SDaniel Scally 62101535ea0SDaniel Scally static void mali_c55_params_return_buffers(struct mali_c55_params *params, 62201535ea0SDaniel Scally enum vb2_buffer_state state) 62301535ea0SDaniel Scally { 62401535ea0SDaniel Scally struct mali_c55_params_buf *buf, *tmp; 62501535ea0SDaniel Scally 62601535ea0SDaniel Scally guard(spinlock)(¶ms->buffers.lock); 62701535ea0SDaniel Scally 62801535ea0SDaniel Scally list_for_each_entry_safe(buf, tmp, ¶ms->buffers.queue, queue) { 62901535ea0SDaniel Scally list_del(&buf->queue); 63001535ea0SDaniel Scally vb2_buffer_done(&buf->vb.vb2_buf, state); 63101535ea0SDaniel Scally } 63201535ea0SDaniel Scally } 63301535ea0SDaniel Scally 63401535ea0SDaniel Scally static int mali_c55_params_start_streaming(struct vb2_queue *q, 63501535ea0SDaniel Scally unsigned int count) 63601535ea0SDaniel Scally { 63701535ea0SDaniel Scally struct mali_c55_params *params = vb2_get_drv_priv(q); 63801535ea0SDaniel Scally struct mali_c55 *mali_c55 = params->mali_c55; 63901535ea0SDaniel Scally int ret; 64001535ea0SDaniel Scally 64101535ea0SDaniel Scally ret = pm_runtime_resume_and_get(mali_c55->dev); 64201535ea0SDaniel Scally if (ret) 64301535ea0SDaniel Scally goto err_return_buffers; 64401535ea0SDaniel Scally 64501535ea0SDaniel Scally ret = video_device_pipeline_alloc_start(¶ms->vdev); 64601535ea0SDaniel Scally if (ret) 64701535ea0SDaniel Scally goto err_pm_put; 64801535ea0SDaniel Scally 64901535ea0SDaniel Scally if (mali_c55_pipeline_ready(mali_c55)) { 65001535ea0SDaniel Scally ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd, 65101535ea0SDaniel Scally MALI_C55_ISP_PAD_SOURCE_VIDEO, 65201535ea0SDaniel Scally BIT(0)); 65301535ea0SDaniel Scally if (ret < 0) 65401535ea0SDaniel Scally goto err_stop_pipeline; 65501535ea0SDaniel Scally } 65601535ea0SDaniel Scally 65701535ea0SDaniel Scally return 0; 65801535ea0SDaniel Scally 65901535ea0SDaniel Scally err_stop_pipeline: 66001535ea0SDaniel Scally video_device_pipeline_stop(¶ms->vdev); 66101535ea0SDaniel Scally err_pm_put: 66201535ea0SDaniel Scally pm_runtime_put_autosuspend(mali_c55->dev); 66301535ea0SDaniel Scally err_return_buffers: 66401535ea0SDaniel Scally mali_c55_params_return_buffers(params, VB2_BUF_STATE_QUEUED); 66501535ea0SDaniel Scally 66601535ea0SDaniel Scally return ret; 66701535ea0SDaniel Scally } 66801535ea0SDaniel Scally 66901535ea0SDaniel Scally static void mali_c55_params_stop_streaming(struct vb2_queue *q) 67001535ea0SDaniel Scally { 67101535ea0SDaniel Scally struct mali_c55_params *params = vb2_get_drv_priv(q); 67201535ea0SDaniel Scally struct mali_c55 *mali_c55 = params->mali_c55; 67301535ea0SDaniel Scally struct mali_c55_isp *isp = &mali_c55->isp; 67401535ea0SDaniel Scally 67501535ea0SDaniel Scally if (mali_c55_pipeline_ready(mali_c55)) { 67601535ea0SDaniel Scally if (v4l2_subdev_is_streaming(&isp->sd)) 67701535ea0SDaniel Scally v4l2_subdev_disable_streams(&isp->sd, 67801535ea0SDaniel Scally MALI_C55_ISP_PAD_SOURCE_VIDEO, 67901535ea0SDaniel Scally BIT(0)); 68001535ea0SDaniel Scally } 68101535ea0SDaniel Scally 68201535ea0SDaniel Scally video_device_pipeline_stop(¶ms->vdev); 68301535ea0SDaniel Scally mali_c55_params_return_buffers(params, VB2_BUF_STATE_ERROR); 68401535ea0SDaniel Scally pm_runtime_put_autosuspend(params->mali_c55->dev); 68501535ea0SDaniel Scally } 68601535ea0SDaniel Scally 68701535ea0SDaniel Scally static const struct vb2_ops mali_c55_params_vb2_ops = { 68801535ea0SDaniel Scally .queue_setup = mali_c55_params_queue_setup, 68901535ea0SDaniel Scally .buf_init = mali_c55_params_buf_init, 69001535ea0SDaniel Scally .buf_cleanup = mali_c55_params_buf_cleanup, 69101535ea0SDaniel Scally .buf_queue = mali_c55_params_buf_queue, 69201535ea0SDaniel Scally .buf_prepare = mali_c55_params_buf_prepare, 69301535ea0SDaniel Scally .start_streaming = mali_c55_params_start_streaming, 69401535ea0SDaniel Scally .stop_streaming = mali_c55_params_stop_streaming, 69501535ea0SDaniel Scally }; 69601535ea0SDaniel Scally 69701535ea0SDaniel Scally void mali_c55_params_write_config(struct mali_c55 *mali_c55) 69801535ea0SDaniel Scally { 69901535ea0SDaniel Scally struct mali_c55_params *params = &mali_c55->params; 70001535ea0SDaniel Scally struct v4l2_isp_params_buffer *config; 70101535ea0SDaniel Scally struct mali_c55_params_buf *buf; 70201535ea0SDaniel Scally size_t block_offset = 0; 70301535ea0SDaniel Scally size_t max_offset; 70401535ea0SDaniel Scally 70501535ea0SDaniel Scally spin_lock(¶ms->buffers.lock); 70601535ea0SDaniel Scally 70701535ea0SDaniel Scally buf = list_first_entry_or_null(¶ms->buffers.queue, 70801535ea0SDaniel Scally struct mali_c55_params_buf, queue); 70901535ea0SDaniel Scally if (buf) 71001535ea0SDaniel Scally list_del(&buf->queue); 71101535ea0SDaniel Scally spin_unlock(¶ms->buffers.lock); 71201535ea0SDaniel Scally 71301535ea0SDaniel Scally if (!buf) 71401535ea0SDaniel Scally return; 71501535ea0SDaniel Scally 71601535ea0SDaniel Scally buf->vb.sequence = mali_c55->isp.frame_sequence; 71701535ea0SDaniel Scally config = buf->config; 71801535ea0SDaniel Scally 71901535ea0SDaniel Scally max_offset = config->data_size; 72001535ea0SDaniel Scally 72101535ea0SDaniel Scally /* 72201535ea0SDaniel Scally * Walk the list of parameter blocks and process them. No validation is 72301535ea0SDaniel Scally * done here, as the contents of the config buffer are already checked 72401535ea0SDaniel Scally * when the buffer is queued. 72501535ea0SDaniel Scally */ 72601535ea0SDaniel Scally while (max_offset && block_offset < max_offset) { 72701535ea0SDaniel Scally union mali_c55_params_block block; 72801535ea0SDaniel Scally mali_c55_params_handler handler; 72901535ea0SDaniel Scally 73001535ea0SDaniel Scally block.data = &config->data[block_offset]; 73101535ea0SDaniel Scally 73201535ea0SDaniel Scally /* We checked the array index already in .buf_queue() */ 73301535ea0SDaniel Scally handler = mali_c55_params_handlers[block.header->type]; 73401535ea0SDaniel Scally handler(mali_c55, block); 73501535ea0SDaniel Scally 73601535ea0SDaniel Scally block_offset += block.header->size; 73701535ea0SDaniel Scally } 73801535ea0SDaniel Scally 73901535ea0SDaniel Scally vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 74001535ea0SDaniel Scally } 74101535ea0SDaniel Scally 74201535ea0SDaniel Scally void mali_c55_unregister_params(struct mali_c55 *mali_c55) 74301535ea0SDaniel Scally { 74401535ea0SDaniel Scally struct mali_c55_params *params = &mali_c55->params; 74501535ea0SDaniel Scally 74601535ea0SDaniel Scally if (!video_is_registered(¶ms->vdev)) 74701535ea0SDaniel Scally return; 74801535ea0SDaniel Scally 74901535ea0SDaniel Scally vb2_video_unregister_device(¶ms->vdev); 75001535ea0SDaniel Scally media_entity_cleanup(¶ms->vdev.entity); 75101535ea0SDaniel Scally mutex_destroy(¶ms->lock); 75201535ea0SDaniel Scally } 75301535ea0SDaniel Scally 75401535ea0SDaniel Scally int mali_c55_register_params(struct mali_c55 *mali_c55) 75501535ea0SDaniel Scally { 75601535ea0SDaniel Scally struct mali_c55_params *params = &mali_c55->params; 75701535ea0SDaniel Scally struct video_device *vdev = ¶ms->vdev; 75801535ea0SDaniel Scally struct vb2_queue *vb2q = ¶ms->queue; 75901535ea0SDaniel Scally int ret; 76001535ea0SDaniel Scally 76101535ea0SDaniel Scally mutex_init(¶ms->lock); 76201535ea0SDaniel Scally INIT_LIST_HEAD(¶ms->buffers.queue); 76301535ea0SDaniel Scally spin_lock_init(¶ms->buffers.lock); 76401535ea0SDaniel Scally 76501535ea0SDaniel Scally params->pad.flags = MEDIA_PAD_FL_SOURCE; 76601535ea0SDaniel Scally ret = media_entity_pads_init(¶ms->vdev.entity, 1, ¶ms->pad); 76701535ea0SDaniel Scally if (ret) 76801535ea0SDaniel Scally goto err_destroy_mutex; 76901535ea0SDaniel Scally 77001535ea0SDaniel Scally vb2q->type = V4L2_BUF_TYPE_META_OUTPUT; 77101535ea0SDaniel Scally vb2q->io_modes = VB2_MMAP | VB2_DMABUF; 77201535ea0SDaniel Scally vb2q->drv_priv = params; 77301535ea0SDaniel Scally vb2q->mem_ops = &vb2_dma_contig_memops; 77401535ea0SDaniel Scally vb2q->ops = &mali_c55_params_vb2_ops; 77501535ea0SDaniel Scally vb2q->buf_struct_size = sizeof(struct mali_c55_params_buf); 77601535ea0SDaniel Scally vb2q->min_queued_buffers = 1; 77701535ea0SDaniel Scally vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 77801535ea0SDaniel Scally vb2q->lock = ¶ms->lock; 77901535ea0SDaniel Scally vb2q->dev = mali_c55->dev; 78001535ea0SDaniel Scally 78101535ea0SDaniel Scally ret = vb2_queue_init(vb2q); 78201535ea0SDaniel Scally if (ret) { 78301535ea0SDaniel Scally dev_err(mali_c55->dev, "params vb2 queue init failed\n"); 78401535ea0SDaniel Scally goto err_cleanup_entity; 78501535ea0SDaniel Scally } 78601535ea0SDaniel Scally 78701535ea0SDaniel Scally strscpy(params->vdev.name, "mali-c55 3a params", 78801535ea0SDaniel Scally sizeof(params->vdev.name)); 78901535ea0SDaniel Scally vdev->release = video_device_release_empty; 79001535ea0SDaniel Scally vdev->fops = &mali_c55_params_v4l2_fops; 79101535ea0SDaniel Scally vdev->ioctl_ops = &mali_c55_params_v4l2_ioctl_ops; 79201535ea0SDaniel Scally vdev->lock = ¶ms->lock; 79301535ea0SDaniel Scally vdev->v4l2_dev = &mali_c55->v4l2_dev; 79401535ea0SDaniel Scally vdev->queue = ¶ms->queue; 79501535ea0SDaniel Scally vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING | 79601535ea0SDaniel Scally V4L2_CAP_IO_MC; 79701535ea0SDaniel Scally vdev->vfl_dir = VFL_DIR_TX; 79801535ea0SDaniel Scally video_set_drvdata(vdev, params); 79901535ea0SDaniel Scally 80001535ea0SDaniel Scally ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 80101535ea0SDaniel Scally if (ret) { 80201535ea0SDaniel Scally dev_err(mali_c55->dev, 80301535ea0SDaniel Scally "failed to register params video device\n"); 80401535ea0SDaniel Scally goto err_release_vb2q; 80501535ea0SDaniel Scally } 80601535ea0SDaniel Scally 80701535ea0SDaniel Scally params->mali_c55 = mali_c55; 80801535ea0SDaniel Scally 80901535ea0SDaniel Scally return 0; 81001535ea0SDaniel Scally 81101535ea0SDaniel Scally err_release_vb2q: 81201535ea0SDaniel Scally vb2_queue_release(vb2q); 81301535ea0SDaniel Scally err_cleanup_entity: 81401535ea0SDaniel Scally media_entity_cleanup(¶ms->vdev.entity); 81501535ea0SDaniel Scally err_destroy_mutex: 81601535ea0SDaniel Scally mutex_destroy(¶ms->lock); 81701535ea0SDaniel Scally 81801535ea0SDaniel Scally return ret; 81901535ea0SDaniel Scally } 820