1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 v4l2_mbus_framefmt *__csi2_get_format(struct
26 atomisp_mipi_csi2_device
27 * csi2,
28 struct v4l2_subdev_state *sd_state,
29 enum
30 v4l2_subdev_format_whence
31 which, unsigned int pad)
32{
33 if (which == V4L2_SUBDEV_FORMAT_TRY)
34 return v4l2_subdev_get_try_format(&csi2->subdev, sd_state,
35 pad);
36 else
37 return &csi2->formats[pad];
38}
39
40
41
42
43
44
45
46
47static int csi2_enum_mbus_code(struct v4l2_subdev *sd,
48 struct v4l2_subdev_state *sd_state,
49 struct v4l2_subdev_mbus_code_enum *code)
50{
51 const struct atomisp_in_fmt_conv *ic = atomisp_in_fmt_conv;
52 unsigned int i = 0;
53
54 while (ic->code) {
55 if (i == code->index) {
56 code->code = ic->code;
57 return 0;
58 }
59 i++, ic++;
60 }
61
62 return -EINVAL;
63}
64
65
66
67
68
69
70
71
72
73static int csi2_get_format(struct v4l2_subdev *sd,
74 struct v4l2_subdev_state *sd_state,
75 struct v4l2_subdev_format *fmt)
76{
77 struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
78 struct v4l2_mbus_framefmt *format;
79
80 format = __csi2_get_format(csi2, sd_state, fmt->which, fmt->pad);
81
82 fmt->format = *format;
83
84 return 0;
85}
86
87int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd,
88 struct v4l2_subdev_state *sd_state,
89 unsigned int which, uint16_t pad,
90 struct v4l2_mbus_framefmt *ffmt)
91{
92 struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
93 struct v4l2_mbus_framefmt *actual_ffmt = __csi2_get_format(csi2,
94 sd_state,
95 which, pad);
96
97 if (pad == CSI2_PAD_SINK) {
98 const struct atomisp_in_fmt_conv *ic;
99 struct v4l2_mbus_framefmt tmp_ffmt;
100
101 ic = atomisp_find_in_fmt_conv(ffmt->code);
102 if (ic)
103 actual_ffmt->code = ic->code;
104 else
105 actual_ffmt->code = atomisp_in_fmt_conv[0].code;
106
107 actual_ffmt->width = clamp_t(
108 u32, ffmt->width, ATOM_ISP_MIN_WIDTH,
109 ATOM_ISP_MAX_WIDTH);
110 actual_ffmt->height = clamp_t(
111 u32, ffmt->height, ATOM_ISP_MIN_HEIGHT,
112 ATOM_ISP_MAX_HEIGHT);
113
114 tmp_ffmt = *ffmt = *actual_ffmt;
115
116 return atomisp_csi2_set_ffmt(sd, sd_state, which,
117 CSI2_PAD_SOURCE,
118 &tmp_ffmt);
119 }
120
121
122 *actual_ffmt = *ffmt = *__csi2_get_format(csi2, sd_state, which,
123 CSI2_PAD_SINK);
124
125 return 0;
126}
127
128
129
130
131
132
133
134
135
136static int csi2_set_format(struct v4l2_subdev *sd,
137 struct v4l2_subdev_state *sd_state,
138 struct v4l2_subdev_format *fmt)
139{
140 return atomisp_csi2_set_ffmt(sd, sd_state, fmt->which, fmt->pad,
141 &fmt->format);
142}
143
144
145
146
147
148
149
150
151static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
152{
153 return 0;
154}
155
156
157static const struct v4l2_subdev_core_ops csi2_core_ops = {
158};
159
160
161static const struct v4l2_subdev_video_ops csi2_video_ops = {
162 .s_stream = csi2_set_stream,
163};
164
165
166static const struct v4l2_subdev_pad_ops csi2_pad_ops = {
167 .enum_mbus_code = csi2_enum_mbus_code,
168 .get_fmt = csi2_get_format,
169 .set_fmt = csi2_set_format,
170 .link_validate = v4l2_subdev_link_validate_default,
171};
172
173
174static const struct v4l2_subdev_ops csi2_ops = {
175 .core = &csi2_core_ops,
176 .video = &csi2_video_ops,
177 .pad = &csi2_pad_ops,
178};
179
180
181
182
183
184
185
186
187
188static int csi2_link_setup(struct media_entity *entity,
189 const struct media_pad *local,
190 const struct media_pad *remote, u32 flags)
191{
192 struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
193 struct atomisp_mipi_csi2_device *csi2 = v4l2_get_subdevdata(sd);
194 u32 result = local->index | is_media_entity_v4l2_subdev(remote->entity);
195
196 switch (result) {
197 case CSI2_PAD_SOURCE | MEDIA_ENT_F_OLD_BASE:
198
199 return -EINVAL;
200
201 case CSI2_PAD_SOURCE | MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN:
202 if (flags & MEDIA_LNK_FL_ENABLED) {
203 if (csi2->output & ~CSI2_OUTPUT_ISP_SUBDEV)
204 return -EBUSY;
205 csi2->output |= CSI2_OUTPUT_ISP_SUBDEV;
206 } else {
207 csi2->output &= ~CSI2_OUTPUT_ISP_SUBDEV;
208 }
209 break;
210
211 default:
212
213 return -EINVAL;
214 }
215 return 0;
216}
217
218
219static const struct media_entity_operations csi2_media_ops = {
220 .link_setup = csi2_link_setup,
221 .link_validate = v4l2_subdev_link_validate,
222};
223
224
225
226
227
228
229static int mipi_csi2_init_entities(struct atomisp_mipi_csi2_device *csi2,
230 int port)
231{
232 struct v4l2_subdev *sd = &csi2->subdev;
233 struct media_pad *pads = csi2->pads;
234 struct media_entity *me = &sd->entity;
235 int ret;
236
237 v4l2_subdev_init(sd, &csi2_ops);
238 snprintf(sd->name, sizeof(sd->name), "ATOM ISP CSI2-port%d", port);
239
240 v4l2_set_subdevdata(sd, csi2);
241 sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
242
243 pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
244 pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
245
246 me->ops = &csi2_media_ops;
247 me->function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
248 ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads);
249 if (ret < 0)
250 return ret;
251
252 csi2->formats[CSI2_PAD_SINK].code =
253 csi2->formats[CSI2_PAD_SOURCE].code =
254 atomisp_in_fmt_conv[0].code;
255
256 return 0;
257}
258
259void
260atomisp_mipi_csi2_unregister_entities(struct atomisp_mipi_csi2_device *csi2)
261{
262 media_entity_cleanup(&csi2->subdev.entity);
263 v4l2_device_unregister_subdev(&csi2->subdev);
264}
265
266int atomisp_mipi_csi2_register_entities(struct atomisp_mipi_csi2_device *csi2,
267 struct v4l2_device *vdev)
268{
269 int ret;
270
271
272 ret = v4l2_device_register_subdev(vdev, &csi2->subdev);
273 if (ret < 0)
274 goto error;
275
276 return 0;
277
278error:
279 atomisp_mipi_csi2_unregister_entities(csi2);
280 return ret;
281}
282
283static const int LIMIT_SHIFT = 6;
284
285static int
286atomisp_csi2_configure_calc(const short int coeffs[2], int mipi_freq, int def)
287{
288
289 static const int accinv = 16;
290 int r;
291
292 if (mipi_freq >> LIMIT_SHIFT <= 0)
293 return def;
294
295 r = accinv * coeffs[1] * (500000000 >> LIMIT_SHIFT);
296 r /= mipi_freq >> LIMIT_SHIFT;
297 r += accinv * coeffs[0];
298
299 return r;
300}
301
302static void atomisp_csi2_configure_isp2401(struct atomisp_sub_device *asd)
303{
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334 static const short int coeff_clk_termen[] = { 0, 0 };
335 static const short int coeff_clk_settle[] = { 95, -8 };
336 static const short int coeff_dat_termen[] = { 0, 0 };
337 static const short int coeff_dat_settle[] = { 85, -2 };
338 static const int TERMEN_DEFAULT = 0 * 0;
339 static const int SETTLE_DEFAULT = 0x480;
340
341 static const hrt_address csi2_port_base[] = {
342 [ATOMISP_CAMERA_PORT_PRIMARY] = CSI2_PORT_A_BASE,
343 [ATOMISP_CAMERA_PORT_SECONDARY] = CSI2_PORT_B_BASE,
344 [ATOMISP_CAMERA_PORT_TERTIARY] = CSI2_PORT_C_BASE,
345 };
346
347 static const unsigned char csi2_port_lanes[] = {
348 [ATOMISP_CAMERA_PORT_PRIMARY] = 4,
349 [ATOMISP_CAMERA_PORT_SECONDARY] = 2,
350 [ATOMISP_CAMERA_PORT_TERTIARY] = 2,
351 };
352 static const hrt_address csi2_lane_base[] = {
353 CSI2_LANE_CL_BASE,
354 CSI2_LANE_D0_BASE,
355 CSI2_LANE_D1_BASE,
356 CSI2_LANE_D2_BASE,
357 CSI2_LANE_D3_BASE,
358 };
359
360 int clk_termen;
361 int clk_settle;
362 int dat_termen;
363 int dat_settle;
364
365 struct v4l2_control ctrl;
366 struct atomisp_device *isp = asd->isp;
367 struct camera_mipi_info *mipi_info;
368 int mipi_freq = 0;
369 enum atomisp_camera_port port;
370
371 int n;
372
373 mipi_info = atomisp_to_sensor_mipi_info(
374 isp->inputs[asd->input_curr].camera);
375 port = mipi_info->port;
376
377 ctrl.id = V4L2_CID_LINK_FREQ;
378 if (v4l2_g_ctrl
379 (isp->inputs[asd->input_curr].camera->ctrl_handler, &ctrl) == 0)
380 mipi_freq = ctrl.value;
381
382 clk_termen = atomisp_csi2_configure_calc(coeff_clk_termen,
383 mipi_freq, TERMEN_DEFAULT);
384 clk_settle = atomisp_csi2_configure_calc(coeff_clk_settle,
385 mipi_freq, SETTLE_DEFAULT);
386 dat_termen = atomisp_csi2_configure_calc(coeff_dat_termen,
387 mipi_freq, TERMEN_DEFAULT);
388 dat_settle = atomisp_csi2_configure_calc(coeff_dat_settle,
389 mipi_freq, SETTLE_DEFAULT);
390 for (n = 0; n < csi2_port_lanes[port] + 1; n++) {
391 hrt_address base = csi2_port_base[port] + csi2_lane_base[n];
392
393 atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_TERMEN,
394 n == 0 ? clk_termen : dat_termen);
395 atomisp_css2_hw_store_32(base + CSI2_REG_RX_CSI_DLY_CNT_SETTLE,
396 n == 0 ? clk_settle : dat_settle);
397 }
398}
399
400void atomisp_csi2_configure(struct atomisp_sub_device *asd)
401{
402 if (IS_HWREVISION(asd->isp, ATOMISP_HW_REVISION_ISP2401))
403 atomisp_csi2_configure_isp2401(asd);
404}
405
406
407
408
409void atomisp_mipi_csi2_cleanup(struct atomisp_device *isp)
410{
411}
412
413int atomisp_mipi_csi2_init(struct atomisp_device *isp)
414{
415 struct atomisp_mipi_csi2_device *csi2_port;
416 unsigned int i;
417 int ret;
418
419 for (i = 0; i < ATOMISP_CAMERA_NR_PORTS; i++) {
420 csi2_port = &isp->csi2_port[i];
421 csi2_port->isp = isp;
422 ret = mipi_csi2_init_entities(csi2_port, i);
423 if (ret < 0)
424 goto fail;
425 }
426
427 return 0;
428
429fail:
430 atomisp_mipi_csi2_cleanup(isp);
431 return ret;
432}
433