File Index
Files and directories
Sign in
FILE

atomisp_csi2.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/atomisp/pci/atomisp_csi2.c 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.