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