hc
2024-05-16 8d2a02b24d66aa359e83eebc1ed3c0f85367a1cb
kernel/drivers/gpu/ipu-v3/ipu-csi.c
....@@ -1,16 +1,7 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Copyright (C) 2012-2014 Mentor Graphics Inc.
34 * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
4
- *
5
- * This program is free software; you can redistribute it and/or modify it
6
- * under the terms of the GNU General Public License as published by the
7
- * Free Software Foundation; either version 2 of the License, or (at your
8
- * option) any later version.
9
- *
10
- * This program is distributed in the hope that it will be useful, but
11
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13
- * for more details.
145 */
156 #include <linux/export.h>
167 #include <linux/module.h>
....@@ -232,7 +223,7 @@
232223 case MEDIA_BUS_FMT_BGR565_2X8_LE:
233224 case MEDIA_BUS_FMT_RGB565_2X8_BE:
234225 case MEDIA_BUS_FMT_RGB565_2X8_LE:
235
- if (mbus_type == V4L2_MBUS_CSI2)
226
+ if (mbus_type == V4L2_MBUS_CSI2_DPHY)
236227 cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_RGB565;
237228 else
238229 cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
....@@ -325,12 +316,21 @@
325316 return 0;
326317 }
327318
319
+/* translate alternate field mode based on given standard */
320
+static inline enum v4l2_field
321
+ipu_csi_translate_field(enum v4l2_field field, v4l2_std_id std)
322
+{
323
+ return (field != V4L2_FIELD_ALTERNATE) ? field :
324
+ ((std & V4L2_STD_525_60) ?
325
+ V4L2_FIELD_SEQ_BT : V4L2_FIELD_SEQ_TB);
326
+}
327
+
328328 /*
329329 * Fill a CSI bus config struct from mbus_config and mbus_framefmt.
330330 */
331331 static int fill_csi_bus_cfg(struct ipu_csi_bus_config *csicfg,
332
- struct v4l2_mbus_config *mbus_cfg,
333
- struct v4l2_mbus_framefmt *mbus_fmt)
332
+ const struct v4l2_mbus_config *mbus_cfg,
333
+ const struct v4l2_mbus_framefmt *mbus_fmt)
334334 {
335335 int ret;
336336
....@@ -359,7 +359,7 @@
359359 else
360360 csicfg->clk_mode = IPU_CSI_CLK_MODE_CCIR656_PROGRESSIVE;
361361 break;
362
- case V4L2_MBUS_CSI2:
362
+ case V4L2_MBUS_CSI2_DPHY:
363363 /*
364364 * MIPI CSI-2 requires non gated clock mode, all other
365365 * parameters are not applicable for MIPI CSI-2 bus.
....@@ -374,22 +374,76 @@
374374 return 0;
375375 }
376376
377
+static int
378
+ipu_csi_set_bt_interlaced_codes(struct ipu_csi *csi,
379
+ const struct v4l2_mbus_framefmt *infmt,
380
+ const struct v4l2_mbus_framefmt *outfmt,
381
+ v4l2_std_id std)
382
+{
383
+ enum v4l2_field infield, outfield;
384
+ bool swap_fields;
385
+
386
+ /* get translated field type of input and output */
387
+ infield = ipu_csi_translate_field(infmt->field, std);
388
+ outfield = ipu_csi_translate_field(outfmt->field, std);
389
+
390
+ /*
391
+ * Write the H-V-F codes the CSI will match against the
392
+ * incoming data for start/end of active and blanking
393
+ * field intervals. If input and output field types are
394
+ * sequential but not the same (one is SEQ_BT and the other
395
+ * is SEQ_TB), swap the F-bit so that the CSI will capture
396
+ * field 1 lines before field 0 lines.
397
+ */
398
+ swap_fields = (V4L2_FIELD_IS_SEQUENTIAL(infield) &&
399
+ V4L2_FIELD_IS_SEQUENTIAL(outfield) &&
400
+ infield != outfield);
401
+
402
+ if (!swap_fields) {
403
+ /*
404
+ * Field0BlankEnd = 110, Field0BlankStart = 010
405
+ * Field0ActiveEnd = 100, Field0ActiveStart = 000
406
+ * Field1BlankEnd = 111, Field1BlankStart = 011
407
+ * Field1ActiveEnd = 101, Field1ActiveStart = 001
408
+ */
409
+ ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
410
+ CSI_CCIR_CODE_1);
411
+ ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
412
+ } else {
413
+ dev_dbg(csi->ipu->dev, "capture field swap\n");
414
+
415
+ /* same as above but with F-bit inverted */
416
+ ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
417
+ CSI_CCIR_CODE_1);
418
+ ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
419
+ }
420
+
421
+ ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
422
+
423
+ return 0;
424
+}
425
+
426
+
377427 int ipu_csi_init_interface(struct ipu_csi *csi,
378
- struct v4l2_mbus_config *mbus_cfg,
379
- struct v4l2_mbus_framefmt *mbus_fmt)
428
+ const struct v4l2_mbus_config *mbus_cfg,
429
+ const struct v4l2_mbus_framefmt *infmt,
430
+ const struct v4l2_mbus_framefmt *outfmt)
380431 {
381432 struct ipu_csi_bus_config cfg;
382433 unsigned long flags;
383434 u32 width, height, data = 0;
435
+ v4l2_std_id std;
384436 int ret;
385437
386
- ret = fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
438
+ ret = fill_csi_bus_cfg(&cfg, mbus_cfg, infmt);
387439 if (ret < 0)
388440 return ret;
389441
390442 /* set default sensor frame width and height */
391
- width = mbus_fmt->width;
392
- height = mbus_fmt->height;
443
+ width = infmt->width;
444
+ height = infmt->height;
445
+ if (infmt->field == V4L2_FIELD_ALTERNATE)
446
+ height *= 2;
393447
394448 /* Set the CSI_SENS_CONF register remaining fields */
395449 data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
....@@ -416,42 +470,22 @@
416470 ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
417471 break;
418472 case IPU_CSI_CLK_MODE_CCIR656_INTERLACED:
419
- if (mbus_fmt->width == 720 && mbus_fmt->height == 576) {
420
- /*
421
- * PAL case
422
- *
423
- * Field0BlankEnd = 0x6, Field0BlankStart = 0x2,
424
- * Field0ActiveEnd = 0x4, Field0ActiveStart = 0
425
- * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
426
- * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
427
- */
428
- height = 625; /* framelines for PAL */
429
-
430
- ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
431
- CSI_CCIR_CODE_1);
432
- ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
433
- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
434
- } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
435
- /*
436
- * NTSC case
437
- *
438
- * Field0BlankEnd = 0x7, Field0BlankStart = 0x3,
439
- * Field0ActiveEnd = 0x5, Field0ActiveStart = 0x1
440
- * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
441
- * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
442
- */
443
- height = 525; /* framelines for NTSC */
444
-
445
- ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
446
- CSI_CCIR_CODE_1);
447
- ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
448
- ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
473
+ if (width == 720 && height == 480) {
474
+ std = V4L2_STD_NTSC;
475
+ height = 525;
476
+ } else if (width == 720 && height == 576) {
477
+ std = V4L2_STD_PAL;
478
+ height = 625;
449479 } else {
450480 dev_err(csi->ipu->dev,
451
- "Unsupported CCIR656 interlaced video mode\n");
452
- spin_unlock_irqrestore(&csi->lock, flags);
453
- return -EINVAL;
481
+ "Unsupported interlaced video mode\n");
482
+ ret = -EINVAL;
483
+ goto out_unlock;
454484 }
485
+
486
+ ret = ipu_csi_set_bt_interlaced_codes(csi, infmt, outfmt, std);
487
+ if (ret)
488
+ goto out_unlock;
455489 break;
456490 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_DDR:
457491 case IPU_CSI_CLK_MODE_CCIR1120_PROGRESSIVE_SDR:
....@@ -476,9 +510,10 @@
476510 dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
477511 ipu_csi_read(csi, CSI_ACT_FRM_SIZE));
478512
513
+out_unlock:
479514 spin_unlock_irqrestore(&csi->lock, flags);
480515
481
- return 0;
516
+ return ret;
482517 }
483518 EXPORT_SYMBOL_GPL(ipu_csi_init_interface);
484519
....@@ -611,7 +646,7 @@
611646 if (vc > 3)
612647 return -EINVAL;
613648
614
- ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2);
649
+ ret = mbus_code_to_bus_cfg(&cfg, mbus_fmt->code, V4L2_MBUS_CSI2_DPHY);
615650 if (ret < 0)
616651 return ret;
617652