FILE
atomisp_csi2.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/media/atomisp/pci/atomisp_csi2.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Support for Medifield PNW Camera Imaging ISP subsystem. 4 * 5 * Copyright (c) 2010 Intel Corporation. All Rights Reserved. 6 */ 7 8#include <media/v4l2-event.h> 9#include <media/v4l2-mediabus.h> 10#include "atomisp_cmd.h" 11#include "atomisp_internal.h" 12#include "atomisp-regs.h" 13 14static struct 15v4l2_mbus_framefmt *__csi2_get_format(struct atomisp_mipi_csi2_device *csi2, 16 struct v4l2_subdev_state *sd_state, 17 enum v4l2_subdev_format_whence which, 18 unsigned int pad) 19{ 20 if (which == V4L2_SUBDEV_FORMAT_TRY) 21 return v4l2_subdev_state_get_format(sd_state, pad); 22 else 23 return &csi2->formats[pad]; 24} 25 26/* 27 * csi2_enum_mbus_code - Handle pixel format enumeration 28 * @sd : pointer to v4l2 subdev structure 29 * @fh : V4L2 subdev file handle 30 * @code : pointer to v4l2_subdev_pad_mbus_code_enum structure 31 * return -EINVAL or zero on success 32 */ 33static int csi2_enum_mbus_code(struct v4l2_subdev *sd, 34 struct v4l2_subdev_state *sd_state, 35 struct v4l2_subdev_mbus_code_enum *code) 36{ 37 const struct atomisp_in_fmt_conv *ic = atomisp_in_fmt_conv; 38 unsigned int i = 0; 39 40 while (ic->code) { 41 if (i == code->index) { 42 code->code = ic->code; 43 return 0; 44 } 45 i++, ic++; 46 } 47 48 return -EINVAL; 49} 50 51/* 52 * csi2_get_format - Handle get format by pads subdev method 53 * @sd : pointer to v4l2 subdev structure 54 * @fh : V4L2 subdev file handle 55 * @pad: pad num 56 * @fmt: pointer to v4l2 format structure 57 * return -EINVAL or zero on success 58 */ 59static int csi2_get_format(struct v4l2_subdev *sd, 60 struct v4l2_subdev_state *sd_state, 61 struct v4l2_subdev_format *fmt) 62{ 63 struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd); 64 struct v4l2_mbus_framefmt *format; 65 66 format = __csi2_get_format(csi2, sd_state, fmt->which, fmt->pad); 67 68 fmt->format = *format; 69 70 return 0; 71} 72 73int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd, 74 struct v4l2_subdev_state *sd_state, 75 unsigned int which, uint16_t pad, 76 struct v4l2_mbus_framefmt *ffmt) 77{ 78 struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd); 79 struct v4l2_mbus_framefmt *actual_ffmt = __csi2_get_format(csi2, 80 sd_state, 81 which, pad); 82 83 if (pad == CSI2_PAD_SINK) { 84 const struct atomisp_in_fmt_conv *ic; 85 struct v4l2_mbus_framefmt tmp_ffmt; 86 87 ic = atomisp_find_in_fmt_conv(ffmt->code); 88 if (ic) 89 actual_ffmt->code = ic->code; 90 else 91 actual_ffmt->code = atomisp_in_fmt_conv[0].code; 92 93 actual_ffmt->width = clamp_t(u32, ffmt->width, 94 ATOM_ISP_MIN_WIDTH, 95 ATOM_ISP_MAX_WIDTH); 96 actual_ffmt->height = clamp_t(u32, ffmt->height, 97 ATOM_ISP_MIN_HEIGHT, 98 ATOM_ISP_MAX_HEIGHT); 99 actual_ffmt->field = ffmt->field; 100 101 tmp_ffmt = *ffmt = *actual_ffmt; 102 103 /* Always use V4L2_FIELD_ANY to match the ISP sink pad */ 104 tmp_ffmt.field = V4L2_FIELD_ANY; 105 return atomisp_csi2_set_ffmt(sd, sd_state, which, 106 CSI2_PAD_SOURCE, 107 &tmp_ffmt); 108 } 109 110 /* FIXME: DPCM decompression */ 111 *actual_ffmt = *ffmt = *__csi2_get_format(csi2, sd_state, which, 112 CSI2_PAD_SINK); 113 114 return 0; 115} 116 117/* 118 * csi2_set_format - Handle set format by pads subdev method 119 * @sd : pointer to v4l2 subdev structure 120 * @fh : V4L2 subdev file handle 121 * @pad: pad num 122 * @fmt: pointer to v4l2 format structure 123 * return -EINVAL or zero on success 124 */ 125static int csi2_set_format(struct v4l2_subdev *sd, 126 struct v4l2_subdev_state *sd_state, 127 struct v4l2_subdev_format *fmt) 128{ 129 return atomisp_csi2_set_ffmt(sd, sd_state, fmt->which, fmt->pad, 130 &fmt->format); 131} 132 133/* subdev pad operations */ 134static const struct v4l2_subdev_pad_ops csi2_pad_ops = { 135 .enum_mbus_code = csi2_enum_mbus_code, 136 .get_fmt = csi2_get_format, 137 .set_fmt = csi2_set_format, 138 .link_validate = v4l2_subdev_link_validate_default, 139}; 140 141/* subdev operations */ 142static const struct v4l2_subdev_ops csi2_ops = { 143 .pad = &csi2_pad_ops, 144}; 145 146/* media operations */ 147static const struct media_entity_operations csi2_media_ops = { 148 .link_validate = v4l2_subdev_link_validate, 149}; 150 151/* 152 * ispcsi2_init_entities - Initialize subdev and media entity. 153 * @csi2: Pointer to ispcsi2 structure. 154 * return -ENOMEM or zero on success 155 */ 156static int mipi_csi2_init_entities(struct atomisp_mipi_csi2_device *csi2, 157 int port) 158{ 159 struct v4l2_subdev *sd = &csi2->subdev; 160 struct media_pad *pads = csi2->pads; 161 struct media_entity *me = &sd->entity; 162 int ret; 163 164 v4l2_subdev_init(sd, &csi2_ops); 165 snprintf(sd->name, sizeof(sd->name), "ATOM ISP CSI2-port%d", port); 166 167 v4l2_set_subdevdata(sd, csi2); 168 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 169 170 pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 171 pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 172 173 me->ops = &csi2_media_ops; 174 me->function = MEDIA_ENT_F_VID_IF_BRIDGE; 175 ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); 176 if (ret < 0) 177 return ret; 178 179 csi2->formats[CSI2_PAD_SINK].code = atomisp_in_fmt_conv[0].code; 180 csi2->formats[CSI2_PAD_SOURCE].code = atomisp_in_fmt_conv[0].code; 181 182 return 0; 183} 184 185void 186atomisp_mipi_csi2_unregister_entities(struct atomisp_mipi_csi2_device *csi2) 187{ 188 media_entity_cleanup(&csi2->subdev.entity); 189 v4l2_device_unregister_subdev(&csi2->subdev); 190} 191 192int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2, 193 struct v4l2_device *vdev) 194{ 195 int ret; 196 197 /* Register the subdev and video nodes. */ 198 ret = v4l2_device_register_subdev(vdev, &csi2->subdev); 199 if (ret < 0) 200 goto error; 201 202 return 0; 203 204error: 205 atomisp_mipi_csi2_unregister_entities(csi2); 206 return ret; 207} 208 209static const int LIMIT_SHIFT = 6; /* Limit numeric range into 31 bits */ 210 211static int 212atomisp_csi2_configure_calc(const short int coeffs[2], int mipi_freq, int def) 213{ 214 /* Delay counter accuracy, 1/0.0625 for ANN/CHT, 1/0.125 for BXT */ 215 static const int accinv = 16; /* 1 / COUNT_ACC */ 216 int r; 217 218 if (mipi_freq >> LIMIT_SHIFT <= 0) 219 return def; 220 221 r = accinv * coeffs[1] * (500000000 >> LIMIT_SHIFT); 222 r /= mipi_freq >> LIMIT_SHIFT; 223 r += accinv * coeffs[0]; 224 225 return r; 226} 227 228static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd) 229{ 230 /* 231 * The ISP2401 new input system CSI2+ receiver has several 232 * parameters affecting the receiver timings. These depend 233 * on the MIPI bus frequency F in Hz (sensor transmitter rate) 234 * as follows: 235 * register value = (A/1e9 + B * UI) / COUNT_ACC 236 * where 237 * UI = 1 / (2 * F) in seconds 238 * COUNT_ACC = counter accuracy in seconds 239 * For ANN and CHV, COUNT_ACC = 0.0625 ns 240 * For BXT, COUNT_ACC = 0.125 ns 241 * A and B are coefficients from the table below, 242 * depending whether the register minimum or maximum value is 243 * calculated. 244 * Minimum Maximum 245 * Clock lane A B A B 246 * reg_rx_csi_dly_cnt_termen_clane 0 0 38 0 247 * reg_rx_csi_dly_cnt_settle_clane 95 -8 300 -16 248 * Data lanes 249 * reg_rx_csi_dly_cnt_termen_dlane0 0 0 35 4 250 * reg_rx_csi_dly_cnt_settle_dlane0 85 -2 145 -6 251 * reg_rx_csi_dly_cnt_termen_dlane1 0 0 35 4 252 * reg_rx_csi_dly_cnt_settle_dlane1 85 -2 145 -6 253 * reg_rx_csi_dly_cnt_termen_dlane2 0 0 35 4 254 * reg_rx_csi_dly_cnt_settle_dlane2 85 -2 145 -6 255 * reg_rx_csi_dly_cnt_termen_dlane3 0 0 35 4 256 * reg_rx_csi_dly_cnt_settle_dlane3 85 -2 145 -6 257 * 258 * We use the minimum values in the calculations below. 259 */ 260 static const short int coeff_clk_termen[] = { 0, 0 }; 261 static const short int coeff_clk_settle[] = { 95, -8 }; 262 static const short int coeff_dat_termen[] = { 0, 0 }; 263 static const short int coeff_dat_settle[] = { 85, -2 }; 264 static const int TERMEN_DEFAULT = 0 * 0; 265 static const int SETTLE_DEFAULT = 0x480; 266 267 static const hrt_address csi2_port_base[] = { 268 [ATOMISP_CAMERA_PORT_PRIMARY] = CSI2_PORT_A_BASE, 269 [ATOMISP_CAMERA_PORT_SECONDARY] = CSI2_PORT_B_BASE, 270 [ATOMISP_CAMERA_PORT_TERTIARY] = CSI2_PORT_C_BASE, 271 }; 272 /* Number of lanes on each port, excluding clock lane */ 273 static const unsigned char csi2_port_lanes[] = { 274 [ATOMISP_CAMERA_PORT_PRIMARY] = 4, 275 [ATOMISP_CAMERA_PORT_SECONDARY] = 2, 276 [ATOMISP_CAMERA_PORT_TERTIARY] = 2, 277 }; 278 static const hrt_address csi2_lane_base[] = { 279 CSI2_LANE_CL_BASE, 280 CSI2_LANE_D0_BASE, 281 CSI2_LANE_D1_BASE, 282 CSI2_LANE_D2_BASE, 283 CSI2_LANE_D3_BASE, 284 }; 285 286 int clk_termen; 287 int clk_settle; 288 int dat_termen; 289 int dat_settle; 290 291 struct v4l2_control ctrl; 292 struct atomisp_device *isp = asd->isp; 293 int mipi_freq = 0; 294 enum atomisp_camera_port port; 295 int n; 296 297 port = isp->inputs[asd->input_curr].port; 298 299 ctrl.id = V4L2_CID_LINK_FREQ; 300 if (v4l2_g_ctrl 301 (isp->inputs[asd->input_curr].sensor->ctrl_handler, &ctrl) == 0) 302 mipi_freq = ctrl.value; 303 304 clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen, mipi_freq, 305 TERMEN_DEFAULT); 306 clk_settle = atomisp_csi2_configure_calc(coeff_clk_settle, mipi_freq, 307 SETTLE_DEFAULT); 308 dat_termen = atomisp_csi2_configure_calc(coeff_dat_termen, mipi_freq, 309 TERMEN_DEFAULT); 310 dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle, mipi_freq, 311 SETTLE_DEFAULT); 312 313 for (n = 0; n < csi2_port_lanes[port] + 1; n++) { 314 hrt_address base = csi2_port_base[port] + csi2_lane_base[n]; 315 316 atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_TERMEN, 317 n == 0 ? clk_termen : dat_termen); 318 atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_SETTLE, 319 n == 0 ? clk_settle : dat_settle); 320 } 321} 322 323void atomisp_csi2_configure(struct atomisp_sub_device *asd) 324{ 325 if (IS_HWREVISION(asd->isp, ATOMISP_HW_REVISION_ISP2401)) 326 atomisp_csi2_configure_isp2401(asd); 327} 328 329/* 330 * atomisp_mipi_csi2_cleanup - Routine for module driver cleanup 331 */ 332void atomisp_mipi_csi2_cleanup(struct atomisp_device *isp) 333{ 334} 335 336int atomisp_mipi_csi2_init(struct atomisp_device *isp) 337{ 338 struct atomisp_mipi_csi2_device *csi2_port; 339 unsigned int i; 340 int ret; 341 342 ret = atomisp_csi2_bridge_init(isp); 343 if (ret < 0) 344 return ret; 345 346 for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) { 347 csi2_port = &isp->csi2_port[i]; 348 csi2_port->isp = isp; 349 ret = mipi_csi2_init_entities(csi2_port, i); 350 if (ret < 0) 351 goto fail; 352 } 353 354 return 0; 355 356fail: 357 atomisp_mipi_csi2_cleanup(isp); 358 return ret; 359}
~/
Read-only file preview.