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