LCOV - code coverage report
Current view: top level - ipu4 - ipu6-isys-csi2.c (source / functions) Coverage Total Hit
Test: ipu4.info Lines: 57.6 % 210 121
Test Date: 2026-05-12 04:57:36 Functions: 60.0 % 15 9

            Line data    Source code
       1              : // SPDX-License-Identifier: GPL-2.0-only
       2              : /*
       3              :  * Copyright (C) 2013--2024 Intel Corporation
       4              :  */
       5              : 
       6              : #include <linux/atomic.h>
       7              : #include <linux/bitfield.h>
       8              : #include <linux/bits.h>
       9              : #include <linux/delay.h>
      10              : #include <linux/device.h>
      11              : #include <linux/err.h>
      12              : #include <linux/io.h>
      13              : #include <linux/minmax.h>
      14              : #include <linux/sprintf.h>
      15              : 
      16              : #include <media/media-entity.h>
      17              : #include <media/v4l2-ctrls.h>
      18              : #include <media/v4l2-device.h>
      19              : #include <media/v4l2-event.h>
      20              : #include <media/v4l2-subdev.h>
      21              : 
      22              : #include "ipu6-bus.h"
      23              : #include "ipu6-isys.h"
      24              : #include "ipu6-isys-csi2.h"
      25              : #include "ipu6-isys-subdev.h"
      26              : #include "ipu6-platform-isys-csi2-reg.h"
      27              : 
      28              : #include "ipu4-compat.h"
      29              : 
      30              : static int csi2_log_first_sof = 2;
      31              : module_param(csi2_log_first_sof, int, 0644);
      32              : MODULE_PARM_DESC(csi2_log_first_sof, "How many frames SOF should be logged during stream start");
      33              : 
      34              : 
      35              : static const u32 csi2_supported_codes[] = {
      36              :         MEDIA_BUS_FMT_RGB565_1X16,
      37              :         MEDIA_BUS_FMT_RGB888_1X24,
      38              :         MEDIA_BUS_FMT_UYVY8_1X16,
      39              :         MEDIA_BUS_FMT_YUYV8_1X16,
      40              :         MEDIA_BUS_FMT_SBGGR10_1X10,
      41              :         MEDIA_BUS_FMT_SGBRG10_1X10,
      42              :         MEDIA_BUS_FMT_SGRBG10_1X10,
      43              :         MEDIA_BUS_FMT_SRGGB10_1X10,
      44              :         MEDIA_BUS_FMT_SBGGR12_1X12,
      45              :         MEDIA_BUS_FMT_SGBRG12_1X12,
      46              :         MEDIA_BUS_FMT_SGRBG12_1X12,
      47              :         MEDIA_BUS_FMT_SRGGB12_1X12,
      48              :         MEDIA_BUS_FMT_SBGGR8_1X8,
      49              :         MEDIA_BUS_FMT_SGBRG8_1X8,
      50              :         MEDIA_BUS_FMT_SGRBG8_1X8,
      51              :         MEDIA_BUS_FMT_SRGGB8_1X8,
      52              :         MEDIA_BUS_FMT_Y8_1X8,
      53              :         MEDIA_BUS_FMT_Y10_1X10,
      54              :         MEDIA_BUS_FMT_Y12_1X12,
      55              :         MEDIA_BUS_FMT_Y16_1X16,
      56              :         MEDIA_BUS_FMT_META_8,
      57              :         MEDIA_BUS_FMT_META_10,
      58              :         MEDIA_BUS_FMT_META_12,
      59              :         MEDIA_BUS_FMT_META_16,
      60              :         MEDIA_BUS_FMT_META_24,
      61              :         0
      62              : };
      63              : 
      64              : /*
      65              :  * Strings corresponding to CSI-2 receiver errors are here.
      66              :  * Corresponding macros are defined in the header file.
      67              :  */
      68              : static const struct ipu6_csi2_error dphy_rx_errors[] = {
      69              :         { "Single packet header error corrected", true },
      70              :         { "Multiple packet header errors detected", true },
      71              :         { "Payload checksum (CRC) error", true },
      72              :         { "Transfer FIFO overflow", false },
      73              :         { "Reserved short packet data type detected", true },
      74              :         { "Reserved long packet data type detected", true },
      75              :         { "Incomplete long packet detected", false },
      76              :         { "Frame sync error", false },
      77              :         { "Line sync error", false },
      78              :         { "DPHY recoverable synchronization error", true },
      79              :         { "DPHY fatal error", false },
      80              :         { "DPHY elastic FIFO overflow", false },
      81              :         { "Inter-frame short packet discarded", true },
      82              :         { "Inter-frame long packet discarded", true },
      83              :         { "MIPI pktgen overflow", false },
      84              :         { "MIPI pktgen data loss", false },
      85              :         { "FIFO overflow", false },
      86              :         { "Lane deskew", false },
      87              :         { "SOT sync error", false },
      88              :         { "HSIDLE detected", false }
      89              : };
      90              : 
      91            1 : s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
      92              : {
      93              :         struct media_pad *src_pad;
      94              : 
      95            1 :         if (!csi2)
      96              :                 return -EINVAL;
      97              : 
      98            1 :         src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
      99            1 :         if (IS_ERR(src_pad)) {
     100            0 :                 dev_err(&csi2->isys->adev->auxdev.dev,
     101              :                         "can't get source pad of %s (%pe)\n",
     102              :                         csi2->asd.sd.name, src_pad);
     103            0 :                 return PTR_ERR(src_pad);
     104              :         }
     105              : 
     106            1 :         return v4l2_get_link_freq(src_pad, 0, 0);
     107              : }
     108              : 
     109            0 : static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
     110              :                                 struct v4l2_event_subscription *sub)
     111              : {
     112              :         struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
     113              :         struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
     114            0 :         struct device *dev = &csi2->isys->adev->auxdev.dev;
     115              : 
     116            0 :         dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
     117              :                 sub->type, sub->id);
     118              : 
     119            0 :         switch (sub->type) {
     120            0 :         case V4L2_EVENT_FRAME_SYNC:
     121            0 :                 return v4l2_event_subscribe(fh, sub, 10, NULL);
     122            0 :         case V4L2_EVENT_CTRL:
     123            0 :                 return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
     124              :         default:
     125              :                 return -EINVAL;
     126              :         }
     127              : }
     128              : 
     129              : static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
     130              :         .subscribe_event = csi2_subscribe_event,
     131              :         .unsubscribe_event = v4l2_event_subdev_unsubscribe,
     132              : };
     133              : 
     134              : /*
     135              :  * The input system CSI2+ receiver has several
     136              :  * parameters affecting the receiver timings. These depend
     137              :  * on the MIPI bus frequency F in Hz (sensor transmitter rate)
     138              :  * as follows:
     139              :  *      register value = (A/1e9 + B * UI) / COUNT_ACC
     140              :  * where
     141              :  *      UI = 1 / (2 * F) in seconds
     142              :  *      COUNT_ACC = counter accuracy in seconds
     143              :  *      COUNT_ACC = 0.125 ns = 1 / 8 ns, ACCINV = 8.
     144              :  *
     145              :  * A and B are coefficients from the table below,
     146              :  * depending whether the register minimum or maximum value is
     147              :  * calculated.
     148              :  *                                     Minimum     Maximum
     149              :  * Clock lane                          A     B     A     B
     150              :  * reg_rx_csi_dly_cnt_termen_clane     0     0    38     0
     151              :  * reg_rx_csi_dly_cnt_settle_clane    95    -8   300   -16
     152              :  * Data lanes
     153              :  * reg_rx_csi_dly_cnt_termen_dlane0    0     0    35     4
     154              :  * reg_rx_csi_dly_cnt_settle_dlane0   85    -2   145    -6
     155              :  * reg_rx_csi_dly_cnt_termen_dlane1    0     0    35     4
     156              :  * reg_rx_csi_dly_cnt_settle_dlane1   85    -2   145    -6
     157              :  * reg_rx_csi_dly_cnt_termen_dlane2    0     0    35     4
     158              :  * reg_rx_csi_dly_cnt_settle_dlane2   85    -2   145    -6
     159              :  * reg_rx_csi_dly_cnt_termen_dlane3    0     0    35     4
     160              :  * reg_rx_csi_dly_cnt_settle_dlane3   85    -2   145    -6
     161              :  *
     162              :  * We use the minimum values of both A and B.
     163              :  */
     164              : 
     165              : #define DIV_SHIFT       8
     166              : #define CSI2_ACCINV     8
     167              : 
     168              : static u32 calc_timing(s32 a, s32 b, s64 link_freq, s32 accinv)
     169              : {
     170            1 :         return accinv * a + (accinv * b * (500000000 >> DIV_SHIFT)
     171            1 :                              / (s32)(link_freq >> DIV_SHIFT));
     172              : }
     173              : 
     174              : static int
     175            1 : ipu6_isys_csi2_calc_timing(struct ipu6_isys_csi2 *csi2,
     176              :                            struct ipu6_isys_csi2_timing *timing, s32 accinv)
     177              : {
     178            1 :         struct device *dev = &csi2->isys->adev->auxdev.dev;
     179              :         s64 link_freq;
     180              : 
     181            1 :         link_freq = ipu6_isys_csi2_get_link_freq(csi2);
     182            1 :         if (link_freq < 0)
     183            0 :                 return link_freq;
     184              : 
     185            1 :         timing->ctermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_A,
     186              :                                       CSI2_CSI_RX_DLY_CNT_TERMEN_CLANE_B,
     187              :                                       link_freq, accinv);
     188            1 :         timing->csettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_A,
     189              :                                       CSI2_CSI_RX_DLY_CNT_SETTLE_CLANE_B,
     190              :                                       link_freq, accinv);
     191            1 :         timing->dtermen = calc_timing(CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_A,
     192              :                                       CSI2_CSI_RX_DLY_CNT_TERMEN_DLANE_B,
     193              :                                       link_freq, accinv);
     194            1 :         timing->dsettle = calc_timing(CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_A,
     195              :                                       CSI2_CSI_RX_DLY_CNT_SETTLE_DLANE_B,
     196              :                                       link_freq, accinv);
     197              : 
     198            1 :         dev_dbg(dev, "ctermen %u csettle %u dtermen %u dsettle %u\n",
     199              :                 timing->ctermen, timing->csettle,
     200              :                 timing->dtermen, timing->dsettle);
     201              : 
     202              :         return 0;
     203              : }
     204              : 
     205              : #ifdef IPU6
     206              : void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2)
     207              : {
     208              :         u32 irq = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     209              :                         CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
     210              :         struct ipu6_isys *isys = csi2->isys;
     211              :         u32 mask;
     212              : 
     213              :         mask = isys->pdata->ipdata->csi2.irq_mask;
     214              :         writel(irq & mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     215              :                CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
     216              :         csi2->receiver_errors |= irq & mask;
     217              : }
     218              : 
     219              : void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
     220              : {
     221              :         struct device *dev = &csi2->isys->adev->auxdev.dev;
     222              :         const struct ipu6_csi2_error *errors;
     223              :         u32 status;
     224              :         u32 i;
     225              : 
     226              :         /* register errors once more in case of interrupts are disabled */
     227              :         ipu6_isys_register_errors(csi2);
     228              :         status = csi2->receiver_errors;
     229              :         csi2->receiver_errors = 0;
     230              :         errors = dphy_rx_errors;
     231              : 
     232              :         for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
     233              :                 if (status & BIT(i))
     234              :                         dev_err_ratelimited(dev, "csi2-%i error: %s\n",
     235              :                                             csi2->port, errors[i].error_string);
     236              :         }
     237              : }
     238              : #else
     239            0 : void ipu4_isys_register_errors(struct ipu6_isys_csi2 *csi2)
     240              : {
     241            0 :         u32 status = readl(csi2->base + CSI2_REG_CSIRX_IRQ_STATUS);
     242              : 
     243            3 :         writel(status, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR);
     244            3 :         csi2->receiver_errors |= status;
     245            0 : }
     246              : 
     247            3 : void ipu4_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
     248              : {
     249            3 :         struct device *dev = &csi2->isys->adev->auxdev.dev;
     250              :         /*
     251              :          * Strings corresponding to CSI-2 receiver errors are here.
     252              :          * Corresponding macros are defined in the header file.
     253              :          */
     254              :         static const struct ipu_isys_csi2_error {
     255              :                 const char *error_string;
     256              :                 bool is_info_only;
     257              :         } errors[] = {
     258              :                 {"Single packet header error corrected", true},
     259              :                 {"Multiple packet header errors detected", true},
     260              :                 {"Payload checksum (CRC) error", true},
     261              :                 {"FIFO overflow", false},
     262              :                 {"Reserved short packet data type detected", true},
     263              :                 {"Reserved long packet data type detected", true},
     264              :                 {"Incomplete long packet detected", false},
     265              :                 {"Frame sync error", false},
     266              :                 {"Line sync error", false},
     267              :                 {"DPHY recoverable synchronization error", true},
     268              :                 {"DPHY non-recoverable synchronization error", false},
     269              :                 {"Escape mode error", true},
     270              :                 {"Escape mode trigger event", true},
     271              :                 {"Escape mode ultra-low power state for data lane(s)", true},
     272              :                 {"Escape mode ultra-low power state exit for clock lane", true},
     273              :                 {"Inter-frame short packet discarded", true},
     274              :                 {"Inter-frame long packet discarded", true},
     275              :         };
     276              :         u32 status;
     277              :         unsigned int i;
     278              : 
     279              :         /* Register errors once more in case of error interrupts are disabled */
     280              :         ipu4_isys_register_errors(csi2);
     281              :         status = csi2->receiver_errors;
     282            3 :         csi2->receiver_errors = 0;
     283              : 
     284           54 :         for (i = 0; i < ARRAY_SIZE(errors); i++) {
     285           51 :                 if (!(status & BIT(i)))
     286           51 :                         continue;
     287              : 
     288            0 :                 if (errors[i].is_info_only)
     289            0 :                         dev_dbg(dev,
     290              :                                 "csi2-%i info: %s\n",
     291              :                                 csi2->port, errors[i].error_string);
     292              :                 else
     293            0 :                         dev_err_ratelimited(dev,
     294              :                                             "csi2-%i error: %s\n",
     295              :                                             csi2->port,
     296              :                                             errors[i].error_string);
     297              :         }
     298            3 : }
     299              : #endif
     300              : 
     301            2 : static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
     302              :                                      const struct ipu6_isys_csi2_timing *timing,
     303              :                                      unsigned int nlanes, int enable)
     304              : {
     305              :         struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
     306              :         struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
     307            2 :         struct ipu6_isys *isys = csi2->isys;
     308            2 :         struct device *dev = &isys->adev->auxdev.dev;
     309              : #ifdef IPU6
     310              :         struct ipu6_isys_csi2_config cfg;
     311              :         unsigned int nports;
     312              :         int ret = 0;
     313              :         u32 mask = 0;
     314              :         u32 i;
     315              : 
     316              :         dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
     317              :                 csi2->port, nlanes);
     318              : 
     319              :         cfg.port = csi2->port;
     320              :         cfg.nlanes = nlanes;
     321              : 
     322              :         mask = isys->pdata->ipdata->csi2.irq_mask;
     323              :         nports = isys->pdata->ipdata->csi2.nports;
     324              : 
     325              :         if (!enable) {
     326              :                 writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
     327              :                 writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
     328              : 
     329              :                 writel(0,
     330              :                        csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     331              :                        CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
     332              :                 writel(mask,
     333              :                        csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     334              :                        CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
     335              :                 writel(0,
     336              :                        csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     337              :                        CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
     338              :                 writel(0xffffffff,
     339              :                        csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     340              :                        CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
     341              : 
     342              :                 isys->phy_set_power(isys, &cfg, timing, false);
     343              : 
     344              :                 writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
     345              :                        (isys->pdata->ipdata->csi2.fw_access_port_ofs,
     346              :                         csi2->port));
     347              :                 writel(0, isys->pdata->base +
     348              :                        CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
     349              : 
     350              :                 return ret;
     351              :         }
     352              : 
     353              :         /* reset port reset */
     354              :         writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
     355              :         usleep_range(100, 200);
     356              :         writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
     357              : 
     358              :         /* enable port clock */
     359              :         for (i = 0; i < nports; i++) {
     360              :                 writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));
     361              :                 writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
     362              :                        (isys->pdata->ipdata->csi2.fw_access_port_ofs, i));
     363              :         }
     364              : 
     365              :         /* enable all error related irq */
     366              :         writel(mask,
     367              :                csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     368              :                CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
     369              :         writel(mask,
     370              :                csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     371              :                CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
     372              :         writel(mask,
     373              :                csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     374              :                CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
     375              :         writel(mask,
     376              :                csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     377              :                CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
     378              :         writel(mask,
     379              :                csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
     380              :                CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
     381              : 
     382              :         /*
     383              :          * Using event from firmware instead of irq to handle CSI2 sync event
     384              :          * which can reduce system wakeups. If CSI2 sync irq enabled, we need
     385              :          * disable the firmware CSI2 sync event to avoid duplicate handling.
     386              :          */
     387              :         writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     388              :                CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
     389              :         writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     390              :                CSI_PORT_REG_BASE_IRQ_MASK_OFFSET);
     391              :         writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     392              :                CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
     393              :         writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     394              :                CSI_PORT_REG_BASE_IRQ_LEVEL_NOT_PULSE_OFFSET);
     395              :         writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
     396              :                CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
     397              : 
     398              :         /* configure to enable FE and PPI2CSI */
     399              :         writel(0, csi2->base + CSI_REG_CSI_FE_MODE);
     400              :         writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);
     401              :         writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,
     402              :                csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);
     403              :         writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),
     404              :                csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);
     405              : 
     406              :         writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);
     407              :         writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);
     408              : 
     409              :         ret = isys->phy_set_power(isys, &cfg, timing, true);
     410              :         if (ret)
     411              :                 dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
     412              :                         ret);
     413              : 
     414              :         return ret;
     415              : #else
     416              :         u32 i = 0;
     417              :         u32 val = 0;
     418              :         u32 csi2part = 0;
     419              :         u32 csi2csirx = 0;
     420              : 
     421            3 :         dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
     422              :                 csi2->port, nlanes);
     423              : 
     424            2 :         if (!enable) {
     425            1 :                 ipu4_isys_csi2_error(csi2);
     426              : 
     427            1 :                 val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG);
     428            1 :                 val &= ~(CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING |
     429              :                          CSI2_CSI_RX_CONFIG_RELEASE_LP11);
     430            1 :                 writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG);
     431              : 
     432            1 :                 writel(0, csi2->base + CSI2_REG_CSI_RX_ENABLE);
     433              : 
     434              :                 /* Disable interrupts */
     435            1 :                 writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_MASK);
     436            1 :                 writel(0, csi2->base + CSI2_REG_CSI2S2M_IRQ_ENABLE);
     437            1 :                 writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK);
     438            1 :                 writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE);
     439            1 :                 return 0;
     440              :         }
     441              : 
     442            1 :         writel(timing->ctermen,
     443            1 :                csi2->base + CSI2_REG_CSI_RX_DLY_CNT_TERMEN_CLANE);
     444            1 :         writel(timing->csettle,
     445            1 :                csi2->base + CSI2_REG_CSI_RX_DLY_CNT_SETTLE_CLANE);
     446              : 
     447            5 :         for (i = 0; i < nlanes; i++) {
     448            4 :                 writel(timing->dtermen,
     449            4 :                        csi2->base +
     450            4 :                        CSI2_REG_CSI_RX_DLY_CNT_TERMEN_DLANE(i));
     451            4 :                 writel(timing->dsettle,
     452            4 :                        csi2->base +
     453            4 :                        CSI2_REG_CSI_RX_DLY_CNT_SETTLE_DLANE(i));
     454              :         }
     455              : 
     456            1 :         val = readl(csi2->base + CSI2_REG_CSI_RX_CONFIG);
     457            1 :         val |= CSI2_CSI_RX_CONFIG_DISABLE_BYTE_CLK_GATING |
     458              :             CSI2_CSI_RX_CONFIG_RELEASE_LP11;
     459            1 :         writel(val, csi2->base + CSI2_REG_CSI_RX_CONFIG);
     460              : 
     461            1 :         writel(nlanes, csi2->base + CSI2_REG_CSI_RX_NOF_ENABLED_LANES);
     462              :         writel(CSI2_CSI_RX_ENABLE_ENABLE,
     463            1 :                csi2->base + CSI2_REG_CSI_RX_ENABLE);
     464              : 
     465              :         /* SOF/EOF of VC0-VC3 enabled from CSI2PART register in B0 */
     466            5 :         for (i = 0; i < NR_OF_CSI2_VC; i++)
     467            4 :                 csi2part |= IPU_CSI_RX_IRQ_FS_VC(i) | IPU_CSI_RX_IRQ_FE_VC(i);
     468              : 
     469              :         /* Enable csi2 receiver error interrupts */
     470              :         csi2csirx = BIT(CSI2_CSIRX_NUM_ERRORS) - 1;
     471            1 :         writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_EDGE);
     472            1 :         writel(0, csi2->base + CSI2_REG_CSIRX_IRQ_LEVEL_NOT_PULSE);
     473            1 :         writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_CLEAR);
     474            1 :         writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_MASK);
     475            1 :         writel(csi2csirx, csi2->base + CSI2_REG_CSIRX_IRQ_ENABLE);
     476              : 
     477              :         /* Enable csi2 error and SOF-related irqs */
     478            1 :         writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_EDGE);
     479            1 :         writel(0, csi2->base + CSI2_REG_CSI2PART_IRQ_LEVEL_NOT_PULSE);
     480            1 :         writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_CLEAR);
     481            1 :         writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_MASK);
     482            1 :         writel(csi2part, csi2->base + CSI2_REG_CSI2PART_IRQ_ENABLE);
     483            1 :         return 0;
     484              : #endif
     485              : }
     486              : 
     487            1 : static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
     488              :                                          struct v4l2_subdev_state *state,
     489              :                                          u32 pad, u64 streams_mask)
     490              : {
     491              :         struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
     492              :         struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
     493            1 :         struct ipu6_isys_csi2_timing timing = { };
     494              :         struct v4l2_subdev *remote_sd;
     495              :         struct media_pad *remote_pad;
     496              :         u64 sink_streams;
     497              :         int ret;
     498              : 
     499            1 :         remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
     500            1 :         remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
     501              : 
     502              :         sink_streams =
     503            1 :                 v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
     504              :                                                 &streams_mask);
     505              : 
     506            1 :         ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
     507            1 :         if (ret)
     508              :                 return ret;
     509              : 
     510            1 :         ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true);
     511            1 :         if (ret)
     512              :                 return ret;
     513              : 
     514            1 :         ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index,
     515              :                                          sink_streams);
     516            1 :         if (ret) {
     517            0 :                 ipu6_isys_csi2_set_stream(sd, NULL, 0, false);
     518            0 :                 return ret;
     519              :         }
     520              : 
     521              :         return 0;
     522              : }
     523              : 
     524            1 : static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd,
     525              :                                           struct v4l2_subdev_state *state,
     526              :                                           u32 pad, u64 streams_mask)
     527              : {
     528              :         struct v4l2_subdev *remote_sd;
     529              :         struct media_pad *remote_pad;
     530              :         u64 sink_streams;
     531              : 
     532              :         sink_streams =
     533            1 :                 v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
     534              :                                                 &streams_mask);
     535              : 
     536            1 :         remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
     537            1 :         remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
     538              : 
     539            1 :         ipu6_isys_csi2_set_stream(sd, NULL, 0, false);
     540              : 
     541            1 :         v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams);
     542              : 
     543            1 :         return 0;
     544              : }
     545              : 
     546            0 : static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
     547              :                                   struct v4l2_subdev_state *state,
     548              :                                   struct v4l2_subdev_selection *sel)
     549              : {
     550              :         struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
     551            0 :         struct device *dev = &asd->isys->adev->auxdev.dev;
     552              :         struct v4l2_mbus_framefmt *sink_ffmt;
     553              :         struct v4l2_mbus_framefmt *src_ffmt;
     554              :         struct v4l2_rect *crop;
     555              : 
     556            0 :         if (sel->pad == CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
     557              :                 return -EINVAL;
     558              : 
     559            0 :         sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
     560              :                                                                  sel->pad,
     561              :                                                                  sel->stream);
     562            0 :         if (!sink_ffmt)
     563              :                 return -EINVAL;
     564              : 
     565            0 :         src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
     566            0 :         if (!src_ffmt)
     567              :                 return -EINVAL;
     568              : 
     569            0 :         crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
     570            0 :         if (!crop)
     571              :                 return -EINVAL;
     572              : 
     573              :         /* Only vertical cropping is supported */
     574            0 :         sel->r.left = 0;
     575            0 :         sel->r.width = sink_ffmt->width;
     576              :         /* Non-bayer formats can't be single line cropped */
     577            0 :         if (!ipu6_isys_is_bayer_format(sink_ffmt->code))
     578            0 :                 sel->r.top &= ~1;
     579            0 :         sel->r.height = clamp(sel->r.height & ~1, IPU6_ISYS_MIN_HEIGHT,
     580              :                               sink_ffmt->height - sel->r.top);
     581            0 :         *crop = sel->r;
     582              : 
     583              :         /* update source pad format */
     584            0 :         src_ffmt->width = sel->r.width;
     585            0 :         src_ffmt->height = sel->r.height;
     586            0 :         if (ipu6_isys_is_bayer_format(sink_ffmt->code))
     587            0 :                 src_ffmt->code = ipu6_isys_convert_bayer_order(sink_ffmt->code,
     588              :                                                                sel->r.left,
     589              :                                                                sel->r.top);
     590            0 :         dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
     591              :                 sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
     592              :                 src_ffmt->code);
     593              : 
     594              :         return 0;
     595              : }
     596              : 
     597            0 : static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
     598              :                                   struct v4l2_subdev_state *state,
     599              :                                   struct v4l2_subdev_selection *sel)
     600              : {
     601              :         struct v4l2_mbus_framefmt *sink_ffmt;
     602              :         struct v4l2_rect *crop;
     603              :         int ret = 0;
     604              : 
     605            0 :         if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
     606              :                 return -EINVAL;
     607              : 
     608            0 :         sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
     609              :                                                                  sel->pad,
     610              :                                                                  sel->stream);
     611            0 :         if (!sink_ffmt)
     612              :                 return -EINVAL;
     613              : 
     614            0 :         crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
     615            0 :         if (!crop)
     616              :                 return -EINVAL;
     617              : 
     618            0 :         switch (sel->target) {
     619            0 :         case V4L2_SEL_TGT_CROP_DEFAULT:
     620              :         case V4L2_SEL_TGT_CROP_BOUNDS:
     621            0 :                 sel->r.left = 0;
     622            0 :                 sel->r.top = 0;
     623            0 :                 sel->r.width = sink_ffmt->width;
     624            0 :                 sel->r.height = sink_ffmt->height;
     625            0 :                 break;
     626            0 :         case V4L2_SEL_TGT_CROP:
     627            0 :                 sel->r = *crop;
     628            0 :                 break;
     629              :         default:
     630              :                 ret = -EINVAL;
     631              :         }
     632              : 
     633              :         return ret;
     634              : }
     635              : 
     636              : static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
     637              : #if KERNEL_VERSION(6, 10, 0) > LINUX_VERSION_CODE
     638              :         .init_cfg = ipu6_isys_subdev_init_cfg,
     639              : #endif
     640              :         .get_fmt = v4l2_subdev_get_fmt,
     641              :         .set_fmt = ipu6_isys_subdev_set_fmt,
     642              :         .get_selection = ipu6_isys_csi2_get_sel,
     643              :         .set_selection = ipu6_isys_csi2_set_sel,
     644              :         .enum_mbus_code = ipu6_isys_subdev_enum_mbus_code,
     645              :         .set_routing = ipu6_isys_subdev_set_routing,
     646              :         .enable_streams = ipu6_isys_csi2_enable_streams,
     647              :         .disable_streams = ipu6_isys_csi2_disable_streams,
     648              : };
     649              : 
     650              : static const struct v4l2_subdev_ops csi2_sd_ops = {
     651              :         .core = &csi2_sd_core_ops,
     652              :         .pad = &csi2_sd_pad_ops,
     653              : };
     654              : 
     655              : static const struct media_entity_operations csi2_entity_ops = {
     656              :         .link_validate = v4l2_subdev_link_validate,
     657              :         .has_pad_interdep = v4l2_subdev_has_pad_interdep,
     658              : };
     659              : 
     660            0 : void ipu6_isys_csi2_cleanup(struct ipu6_isys_csi2 *csi2)
     661              : {
     662            0 :         if (!csi2->isys)
     663              :                 return;
     664              : 
     665            0 :         v4l2_device_unregister_subdev(&csi2->asd.sd);
     666            0 :         v4l2_subdev_cleanup(&csi2->asd.sd);
     667            0 :         ipu6_isys_subdev_cleanup(&csi2->asd);
     668            0 :         csi2->isys = NULL;
     669              : }
     670              : 
     671            6 : int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
     672              :                         struct ipu6_isys *isys,
     673              :                         void __iomem *base, unsigned int index)
     674              : {
     675            6 :         struct device *dev = &isys->adev->auxdev.dev;
     676              :         int ret;
     677              : 
     678            6 :         csi2->isys = isys;
     679            6 :         csi2->base = base;
     680            6 :         csi2->port = index;
     681              : 
     682            6 :         csi2->asd.sd.entity.ops = &csi2_entity_ops;
     683            6 :         csi2->asd.isys = isys;
     684            6 :         ret = ipu6_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
     685              :                                     NR_OF_CSI2_SINK_PADS, NR_OF_CSI2_SRC_PADS);
     686            6 :         if (ret)
     687            0 :                 goto fail;
     688              : 
     689            6 :         csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;
     690            6 :         csi2->asd.supported_codes = csi2_supported_codes;
     691            6 :         snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
     692              :                  IPU6_ISYS_ENTITY_PREFIX " CSI2 %u", index);
     693              :         v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
     694            6 :         ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
     695            6 :         if (ret) {
     696            0 :                 dev_err(dev, "failed to init v4l2 subdev\n");
     697            0 :                 goto fail;
     698              :         }
     699              : 
     700            6 :         ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
     701            6 :         if (ret) {
     702            0 :                 dev_err(dev, "failed to register v4l2 subdev\n");
     703            0 :                 goto fail;
     704              :         }
     705              : 
     706              :         return 0;
     707              : 
     708            0 : fail:
     709            0 :         ipu6_isys_csi2_cleanup(csi2);
     710              : 
     711            0 :         return ret;
     712              : }
     713              : 
     714            2 : void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream)
     715              : {
     716            2 :         struct video_device *vdev = stream->asd->sd.devnode;
     717            2 :         struct device *dev = &stream->isys->adev->auxdev.dev;
     718              :         struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
     719            2 :         struct v4l2_event ev = {
     720              :                 .type = V4L2_EVENT_FRAME_SYNC,
     721              :         };
     722              : 
     723            2 :         ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
     724            2 :         v4l2_event_queue(vdev, &ev);
     725              : 
     726            2 :         dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
     727              :                 csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
     728              : 
     729              :         // Ambu-specific logging to see if we receive the first few frames from the FPGA
     730            2 :         if (ev.u.frame_sync.frame_sequence <= csi2_log_first_sof)
     731            2 :                 dev_info(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
     732              :                          csi2->port,
     733              :                          ev.u.frame_sync.frame_sequence,
     734              :                          stream->vc);
     735            2 : }
     736              : 
     737            0 : void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
     738              : {
     739            0 :         struct device *dev = &stream->isys->adev->auxdev.dev;
     740            0 :         struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
     741            0 :         u32 frame_sequence = atomic_read(&stream->sequence);
     742              : 
     743            0 :         dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
     744              :                 csi2->port, frame_sequence);
     745            0 : }
     746              : 
     747            1 : int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
     748              :                                    struct ipu6_isys_csi2 *csi2,
     749              :                                    struct media_entity *source_entity,
     750              :                                    struct v4l2_mbus_frame_desc_entry *entry)
     751              : {
     752              :         struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
     753            1 :         struct device *dev = &csi2->isys->adev->auxdev.dev;
     754            1 :         struct v4l2_mbus_frame_desc desc;
     755              :         struct v4l2_subdev *source;
     756              :         struct media_pad *pad;
     757              :         unsigned int i;
     758              :         int ret;
     759              : 
     760            1 :         source = media_entity_to_v4l2_subdev(source_entity);
     761              :         if (!source)
     762              :                 return -EPIPE;
     763              : 
     764            1 :         pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);
     765            1 :         if (!pad)
     766              :                 return -EPIPE;
     767              : 
     768            1 :         ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
     769            0 :         if (ret)
     770            1 :                 return ret;
     771              : 
     772            0 :         if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
     773            0 :                 dev_err(dev, "Unsupported frame descriptor type\n");
     774            0 :                 return -EINVAL;
     775              :         }
     776              : 
     777            0 :         for (i = 0; i < desc.num_entries; i++) {
     778            0 :                 if (source_stream == desc.entry[i].stream) {
     779            0 :                         desc_entry = &desc.entry[i];
     780              :                         break;
     781              :                 }
     782              :         }
     783              : 
     784              :         if (!desc_entry) {
     785            0 :                 dev_err(dev, "Failed to find stream %u from remote subdev\n",
     786              :                         source_stream);
     787            0 :                 return -EINVAL;
     788              :         }
     789              : 
     790            0 :         if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {
     791            0 :                 dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
     792            0 :                 return -EINVAL;
     793              :         }
     794              : 
     795            0 :         *entry = *desc_entry;
     796              : 
     797            0 :         return 0;
     798              : }
        

Generated by: LCOV version 2.0-1