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