[PATCH v4 3/5] media: platform: mediatek: isp_30: add mediatek ISP3.0 sensor interface

Julien Stephan jstephan at baylibre.com
Mon Jun 10 07:39:19 PDT 2024


Le jeu. 11 janv. 2024 à 13:04, AngeloGioacchino Del Regno
<angelogioacchino.delregno at collabora.com> a écrit :
>
> Il 10/01/24 15:14, Julien Stephan ha scritto:
> > From: Louis Kuo <louis.kuo at mediatek.com>
> >
> > This will add the mediatek ISP3.0 seninf (sensor interface) driver found
> > on several Mediatek SoCs such as the mt8365.
> >
> > Then seninf module has 4 physical CSI-2 inputs. Depending on the soc they
> > may not be all connected.
> >
> > Signed-off-by: Louis Kuo <louis.kuo at mediatek.com>
> > Signed-off-by: Phi-bang Nguyen <pnguyen at baylibre.com>
> > Signed-off-by: Florian Sylvestre <fsylvestre at baylibre.com>
> > Co-developed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > Co-developed-by: Julien Stephan <jstephan at baylibre.com>
> > Signed-off-by: Julien Stephan <jstephan at baylibre.com>
> > ---
> >   MAINTAINERS                                   |    1 +
> >   drivers/media/platform/mediatek/Kconfig       |    1 +
> >   drivers/media/platform/mediatek/Makefile      |    1 +
> >   drivers/media/platform/mediatek/isp/Kconfig   |    2 +
> >   drivers/media/platform/mediatek/isp/Makefile  |    3 +
> >   .../platform/mediatek/isp/isp_30/Kconfig      |   16 +
> >   .../platform/mediatek/isp/isp_30/Makefile     |    3 +
> >   .../mediatek/isp/isp_30/seninf/Makefile       |    5 +
> >   .../mediatek/isp/isp_30/seninf/mtk_seninf.c   | 1488 +++++++++++++++++
> >   .../isp/isp_30/seninf/mtk_seninf_reg.h        |  112 ++
> >   10 files changed, 1632 insertions(+)
> >   create mode 100644 drivers/media/platform/mediatek/isp/Kconfig
> >   create mode 100644 drivers/media/platform/mediatek/isp/Makefile
> >   create mode 100644 drivers/media/platform/mediatek/isp/isp_30/Kconfig
> >   create mode 100644 drivers/media/platform/mediatek/isp/isp_30/Makefile
> >   create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
> >   create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
> >   create mode 100644 drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf_reg.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 3ea2158864e1..52d200d5e36c 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -13618,6 +13618,7 @@ M:    Andy Hsieh <andy.hsieh at mediatek.com>
> >   S:  Supported
> >   F:  Documentation/devicetree/bindings/media/mediatek,mt8365-camsv.yaml
> >   F:  Documentation/devicetree/bindings/media/mediatek,mt8365-seninf.yaml
> > +F:   drivers/media/platform/mediatek/isp/isp_30/seninf/*
> >
> >   MEDIATEK SMI DRIVER
> >   M:  Yong Wu <yong.wu at mediatek.com>
> > diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig
> > index 84104e2cd024..4e0a5a43f35e 100644
> > --- a/drivers/media/platform/mediatek/Kconfig
> > +++ b/drivers/media/platform/mediatek/Kconfig
> > @@ -7,3 +7,4 @@ source "drivers/media/platform/mediatek/mdp/Kconfig"
> >   source "drivers/media/platform/mediatek/vcodec/Kconfig"
> >   source "drivers/media/platform/mediatek/vpu/Kconfig"
> >   source "drivers/media/platform/mediatek/mdp3/Kconfig"
> > +source "drivers/media/platform/mediatek/isp/Kconfig"
> > diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile
> > index 38e6ba917fe5..695f05f525a6 100644
> > --- a/drivers/media/platform/mediatek/Makefile
> > +++ b/drivers/media/platform/mediatek/Makefile
> > @@ -4,3 +4,4 @@ obj-y += mdp/
> >   obj-y += vcodec/
> >   obj-y += vpu/
> >   obj-y += mdp3/
> > +obj-y += isp/
> > diff --git a/drivers/media/platform/mediatek/isp/Kconfig b/drivers/media/platform/mediatek/isp/Kconfig
> > new file mode 100644
> > index 000000000000..708b9a6660d2
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/Kconfig
> > @@ -0,0 +1,2 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +source "drivers/media/platform/mediatek/isp/isp_30/Kconfig"
> > diff --git a/drivers/media/platform/mediatek/isp/Makefile b/drivers/media/platform/mediatek/isp/Makefile
> > new file mode 100644
> > index 000000000000..a81ab33d0dd3
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +
> > +obj-y += isp_30/
> > diff --git a/drivers/media/platform/mediatek/isp/isp_30/Kconfig b/drivers/media/platform/mediatek/isp/isp_30/Kconfig
> > new file mode 100644
> > index 000000000000..9791312589fb
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/isp_30/Kconfig
> > @@ -0,0 +1,16 @@
> > +# SPDX-License-Identifier: GPL-2.0-only
> > +config MTK_SENINF30
> > +     tristate "MediaTek ISP3.0 SENINF driver"
> > +     depends on VIDEO_V4L2_SUBDEV_API
> > +     depends on MEDIA_CAMERA_SUPPORT
> > +     depends on ARCH_MEDIATEK || COMPILE_TEST
> > +     depends on OF
> > +     select V4L2_FWNODE
> > +     default n
> > +     help
> > +       This driver provides a MIPI CSI-2 receiver interface to connect
> > +       an external camera module with MediaTek ISP3.0. It is able to handle
> > +       multiple cameras at the same time.
> > +
> > +       To compile this driver as a module, choose M here: the
> > +       module will be called mtk-seninf.
> > diff --git a/drivers/media/platform/mediatek/isp/isp_30/Makefile b/drivers/media/platform/mediatek/isp/isp_30/Makefile
> > new file mode 100644
> > index 000000000000..ac3142de4739
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/isp_30/Makefile
> > @@ -0,0 +1,3 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +obj-$(CONFIG_MTK_SENINF30) += seninf/
> > diff --git a/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile b/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
> > new file mode 100644
> > index 000000000000..f28480d6d6c3
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/isp_30/seninf/Makefile
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +mtk-seninf-objs += mtk_seninf.o
> > +
> > +obj-$(CONFIG_MTK_SENINF30) += mtk-seninf.o
> > diff --git a/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c b/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
> > new file mode 100644
> > index 000000000000..67b2c697d9ca
> > --- /dev/null
> > +++ b/drivers/media/platform/mediatek/isp/isp_30/seninf/mtk_seninf.c
> > @@ -0,0 +1,1488 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2022 MediaTek Inc.
> > + */
> > +#include <linux/bitfield.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/videodev2.h>
> > +#include <media/media-device.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-async.h>
> > +#include <media/v4l2-common.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-dev.h>
> > +#include <media/v4l2-device.h>
> > +#include <media/v4l2-event.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-mc.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#include "mtk_seninf_reg.h"
> > +
> > +#define SENINF_TIMESTAMP_STEP                0x67
> > +#define SENINF_SETTLE_DELAY          0x15
> > +#define SENINF_HS_TRAIL_PARAMETER    0x8
> > +
> > +#define SENINF_MAX_NUM_INPUTS                4
> > +#define SENINF_MAX_NUM_OUTPUTS               6
> > +#define SENINF_MAX_NUM_MUXES         6
> > +#define SENINF_MAX_NUM_PADS          (SENINF_MAX_NUM_INPUTS + \
> > +                                      SENINF_MAX_NUM_OUTPUTS)
> > +
> > +#define SENINF_DEFAULT_BUS_FMT               MEDIA_BUS_FMT_SGRBG10_1X10
> > +#define SENINF_DEFAULT_WIDTH         1920
> > +#define SENINF_DEFAULT_HEIGHT                1080
> > +
> > +#define SENINF_PAD_10BIT             0
> > +
> > +#define SENINF_TEST_MODEL            0
> > +#define SENINF_NORMAL_MODEL          1
> > +#define SENINF_ALL_ERR_IRQ_EN                0x7f
> > +#define SENINF_IRQ_CLR_SEL           0x80000000
> > +
> > +#define SENINF_MIPI_SENSOR           0x8
> > +
> > +#define MTK_CSI_MAX_LANES 4
> > +
> > +/* Port number in the device tree. */
> > +enum mtk_seninf_port {
> > +     CSI_PORT_0 = 0, /* 4D1C or 2D1C */
> > +     CSI_PORT_1,     /* 4D1C */
> > +     CSI_PORT_2,     /* 4D1C */
> > +     CSI_PORT_0B,    /* 2D1C */
> > +};
> > +
> > +enum mtk_seninf_id {
> > +     SENINF_1 = 0,
> > +     SENINF_2 = 1,
> > +     SENINF_3 = 2,
> > +     SENINF_5 = 4,
> > +};
> > +
> > +static const u32 port_to_seninf_id[] = {
> > +     [CSI_PORT_0] = SENINF_1,
> > +     [CSI_PORT_1] = SENINF_3,
> > +     [CSI_PORT_2] = SENINF_5,
> > +     [CSI_PORT_0B] = SENINF_2,
> > +};
> > +
> > +enum mtk_seninf_phy_mode {
> > +     SENINF_PHY_MODE_NONE,
> > +     SENINF_PHY_MODE_4D1C,
> > +     SENINF_PHY_MODE_2D1C,
> > +};
> > +
> > +enum mtk_seninf_format_flag {
> > +     MTK_SENINF_FORMAT_BAYER = BIT(0),
> > +     MTK_SENINF_FORMAT_DPCM = BIT(1),
> > +     MTK_SENINF_FORMAT_JPEG = BIT(2),
> > +     MTK_SENINF_FORMAT_INPUT_ONLY = BIT(3),
> > +};
> > +
> > +/**
> > + * struct mtk_seninf_conf - Model-specific SENINF parameters
> > + * @model: Model description
> > + * @nb_inputs: Number of SENINF inputs
> > + * @nb_muxes: Number of SENINF MUX (FIFO) instances
> > + * @nb_outputs: Number of outputs (to CAM and CAMSV instances)
> > + */
> > +struct mtk_seninf_conf {
> > +     const char *model;
> > +     u8 nb_inputs;
> > +     u8 nb_muxes;
> > +     u8 nb_outputs;
> > +};
> > +
> > +/**
> > + * struct mtk_seninf_format_info - Information about media bus formats
> > + * @code: V4L2 media bus code
> > + * @flags: Flags describing the format, as a combination of MTK_SENINF_FORMAT_*
> > + */
> > +struct mtk_seninf_format_info {
> > +     u32 code;
> > +     u32 flags;
> > +};
> > +
> > +/**
> > + * struct mtk_seninf_input - SENINF input block
> > + * @pad: DT port and media entity pad number
> > + * @seninf_id: SENINF hardware instance ID
> > + * @base: Memory mapped I/O based address
> > + * @seninf: Back pointer to the mtk_seninf
> > + * @phy: PHY connected to the input
> > + * @phy_mode: PHY operation mode (NONE when the input is not connected)
> > + * @bus: CSI-2 bus configuration from DT
> > + * @source_sd: Source subdev connected to the input
> > + */
> > +struct mtk_seninf_input {
> > +     enum mtk_seninf_port pad;
> > +     enum mtk_seninf_id seninf_id;
> > +     void __iomem *base;
> > +     struct mtk_seninf *seninf;
> > +
> > +     struct phy *phy;
> > +     enum mtk_seninf_phy_mode phy_mode;
> > +
> > +     struct v4l2_mbus_config_mipi_csi2 bus;
> > +
> > +     struct v4l2_subdev *source_sd;
> > +};
> > +
> > +/**
> > + * struct mtk_seninf_mux - SENINF MUX channel
> > + * @pad: DT port and media entity pad number
> > + * @mux_id: MUX hardware instance ID
> > + * @base: Memory mapped I/O based address
> > + * @seninf: Back pointer to the mtk_seninf
> > + */
> > +struct mtk_seninf_mux {
> > +     unsigned int pad;
> > +     unsigned int mux_id;
> > +     void __iomem *base;
> > +     struct mtk_seninf *seninf;
> > +};
> > +
> > +/**
> > + * struct mtk_seninf - Top-level SENINF device
> > + * @dev: The (platform) device
> > + * @phy: PHYs at the SENINF inputs
> > + * @num_clks: Number of clocks in the clks array
> > + * @clks: Clocks
> > + * @base: Memory mapped I/O base address
> > + * @media_dev: Media controller device
> > + * @v4l2_dev: V4L2 device
> > + * @subdev: V4L2 subdevice
> > + * @pads: Media entity pads
> > + * @notifier: V4L2 async notifier for source subdevs
> > + * @ctrl_handler: V4L2 controls handler
> > + * @source_format: Active format on the source pad
> > + * @inputs: Array of SENINF inputs
> > + * @muxes: Array of MUXes
> > + * @conf: Model-specific SENINF parameters
> > + * @is_testmode: Whether or not the test pattern generator is enabled
> > + */
> > +struct mtk_seninf {
> > +     struct device *dev;
> > +     struct phy *phy[5];
> > +     unsigned int num_clks;
> > +     struct clk_bulk_data *clks;
> > +     void __iomem *base;
> > +
> > +     struct media_device media_dev;
> > +     struct v4l2_device v4l2_dev;
> > +     struct v4l2_subdev subdev;
> > +     struct media_pad pads[SENINF_MAX_NUM_PADS];
> > +     struct v4l2_async_notifier notifier;
> > +     struct v4l2_ctrl_handler ctrl_handler;
> > +
> > +     struct mtk_seninf_input inputs[SENINF_MAX_NUM_INPUTS];
> > +     struct mtk_seninf_mux muxes[SENINF_MAX_NUM_MUXES];
> > +
> > +     const struct mtk_seninf_conf *conf;
> > +
> > +     bool is_testmode;
> > +};
> > +
> > +inline struct mtk_seninf *sd_to_mtk_seninf(struct v4l2_subdev *sd)
> > +{
> > +     return container_of(sd, struct mtk_seninf, subdev);
> > +}
> > +
> > +static inline bool mtk_seninf_pad_is_sink(struct mtk_seninf *priv,
> > +                                       unsigned int pad)
> > +{
> > +     return pad < priv->conf->nb_inputs;
> > +}
> > +
> > +static inline bool mtk_seninf_pad_is_source(struct mtk_seninf *priv,
> > +                                         unsigned int pad)
> > +{
> > +     return !mtk_seninf_pad_is_sink(priv, pad);
> > +}
> > +
> > +/* -----------------------------------------------------------------------------
> > + * Formats
> > + */
> > +
> > +static const struct mtk_seninf_format_info mtk_seninf_formats[] = {
> > +     {
> > +             .code = MEDIA_BUS_FMT_SBGGR8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGRBG8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGRBG10_1X10,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB10_1X10,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SBGGR10_1X10,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG10_1X10,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SBGGR12_1X12,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG12_1X12,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGRBG12_1X12,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB12_1X12,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SBGGR14_1X14,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG14_1X14,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGRBG14_1X14,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB14_1X14,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SBGGR16_1X16,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG16_1X16,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGRBG16_1X16,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB16_1X16,
> > +             .flags = MTK_SENINF_FORMAT_BAYER,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_UYVY8_1X16,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_VYUY8_1X16,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_YUYV8_1X16,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_YVYU8_1X16,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_JPEG_1X8,
> > +             .flags = MTK_SENINF_FORMAT_JPEG,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8,
> > +             .flags = MTK_SENINF_FORMAT_JPEG,
> > +     },
> > +     /* Keep the input-only formats last. */
>
> Your comment doesn't make me understand why input-only formats shall be
> placed last - and makes me think that having two arrays (one for both
> and one for input only) would be easier and less error prone, other than
> making you able to drop the MTK_SENINF_FORMAT_INPUT_ONLY flag entirely.
>
> > +     {
> > +             .code = MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
> > +     }, {
> > +             .code = MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8,
> > +             .flags = MTK_SENINF_FORMAT_DPCM | MTK_SENINF_FORMAT_INPUT_ONLY,
> > +     }
> > +};
> > +
> > +static const struct mtk_seninf_format_info *mtk_seninf_format_info(u32 code)
> > +{
> > +     unsigned int i;
> > +
> > +     for (i = 0; i < ARRAY_SIZE(mtk_seninf_formats); ++i) {
> > +             if (mtk_seninf_formats[i].code == code)
> > +                     return &mtk_seninf_formats[i];
> > +     }
> > +
> > +     return NULL;
> > +}
> > +
>
> ..snip..
>
> > +
> > +static void mtk_seninf_input_setup_csi2(struct mtk_seninf_input *input,
> > +                                     struct v4l2_subdev_state *state)
> > +{
> > +     const struct mtk_seninf_format_info *fmtinfo;
> > +     const struct v4l2_mbus_framefmt *format;
> > +     unsigned int num_data_lanes = input->bus.num_data_lanes;
> > +     unsigned int val = 0;
> > +
> > +     format = v4l2_subdev_state_get_stream_format(state, input->pad, 0);
> > +     fmtinfo = mtk_seninf_format_info(format->code);
> > +
> > +     /* Configure timestamp */
> > +     writel(SENINF_TIMESTAMP_STEP, input->base + SENINF_TG1_TM_STP);
> > +
> > +     /* HQ */
> > +     writel(0x0, input->base + SENINF_TG1_PH_CNT);
>
> Zero means:
>   - Sensor master clock: ISP_CLK
>   - Sensor clock polarity: Rising edge
>   - Sensor reset deasserted
>   - Sensor powered up
>   - Pixel clock inversion disabled
>   - Sensor master clock polarity disabled
>   - Phase counter disabled
>
> > +     writel(0x10001, input->base + SENINF_TG1_SEN_CK);
>
> Unroll this one... this is the TG1 sensor clock divider.
>
> CLKFL GENMASK(5, 0)
> CLKRS GENMASK(13, 8)
> CLKCNT GENMASK(21,16)
>
> Like this, I don't get what you're trying to set, because you're using a fixed
> sensor clock rate, meaning that only a handful of camera sensors will be usable.
>
> Is this 8Mhz? 16? 24? what? :-)
>
> Two hints:
>   - sensor_clk = clk_get_rate(isp_clk) / (tg1_sen_ck_clkcnt + 1);
>   - int mtk_seninf_set_sensor_clk(u8 rate_mhz);
>
> Please :-)

Hi Angelo,

I think I get your point about not hardcoding the sensor rate, but I
am not sure how to use
a mtk_seninf_set_sensor_clk(u8 rate_mhz); function.

Where would it be called? How is it exposed to the user?

Cheers
Julien

>
> > +
> > +     /* First Enable Sensor interface and select pad (0x1a04_0200) */
> > +     mtk_seninf_input_update(input, SENINF_CTRL, SENINF_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_CTRL, PAD2CAM_DATA_SEL, SENINF_PAD_10BIT);
> > +     mtk_seninf_input_update(input, SENINF_CTRL, SENINF_SRC_SEL, 0);
> > +     mtk_seninf_input_update(input, SENINF_CTRL_EXT, SENINF_CSI2_IP_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_CTRL_EXT, SENINF_NCSI2_IP_EN, 0);
> > +
> > +     /* DPCM Enable */
> > +     if (fmtinfo->flags & MTK_SENINF_FORMAT_DPCM)
> > +             val = SENINF_CSI2_DPCM_DI_2A_DPCM_EN;
> > +     else
> > +             val = SENINF_CSI2_DPCM_DI_30_DPCM_EN;
> > +     writel(val, input->base + SENINF_CSI2_DPCM);
> > +
> > +     /* Settle delay */
> > +     mtk_seninf_input_update(input, SENINF_CSI2_LNRD_TIMING,
> > +                             DATA_SETTLE_PARAMETER, SENINF_SETTLE_DELAY);
> > +
> > +     /* HQ */
> > +     writel(0x10, input->base + SENINF_CSI2_LNRC_FSM);
>
> As far as I know, SENINF_CSI2_LNRC_FSM is a read-only register: this write will do
> exactly nothing...
>
> > +
> > +     /* CSI2 control */
> > +     val = readl(input->base + SENINF_CSI2_CTL)
> > +         | (FIELD_PREP(SENINF_CSI2_CTL_ED_SEL, DATA_HEADER_ORDER_DI_WCL_WCH)
> > +         | SENINF_CSI2_CTL_CLOCK_LANE_EN | (BIT(num_data_lanes) - 1));
> > +     writel(val, input->base + SENINF_CSI2_CTL);
> > +
> > +     mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL,
> > +                             BYPASS_LANE_RESYNC, 0);
>
> 93 columns: fits in one line (not only this one!).
>
> > +     mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL, CDPHY_SEL, 0);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_RESYNC_MERGE_CTL,
> > +                             CPHY_LANE_RESYNC_CNT, 3);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_MODE, CSR_CSI2_MODE, 0);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_MODE, CSR_CSI2_HEADER_LEN, 0);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_DPHY_SYNC, SYNC_SEQ_MASK_0, 0xff00);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_DPHY_SYNC, SYNC_SEQ_PAT_0, 0x001d);
> > +
> > +     mtk_seninf_input_update(input, SENINF_CSI2_CTL, CLOCK_HS_OPTION, 0);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_CTL, HSRX_DET_EN, 0);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_CTL, HS_TRAIL_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_HS_TRAIL, HS_TRAIL_PARAMETER,
> > +                             SENINF_HS_TRAIL_PARAMETER);
> > +
> > +     /* Set debug port to output packet number */
> > +     mtk_seninf_input_update(input, SENINF_CSI2_DGB_SEL, DEBUG_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_CSI2_DGB_SEL, DEBUG_SEL, 0x1a);
> > +
> > +     /* HQ */
> > +     writel(0xfffffffe, input->base + SENINF_CSI2_SPARE0);
>
> I have no idea what this SPARE0 does, but I think that this is something that you
> want to get from platform_data, as I guess this would be different on various SoCs?
>
> > +
> > +     /* Enable CSI2 IRQ mask */
> > +     /* Turn on all interrupt */
> > +     writel(0xffffffff, input->base + SENINF_CSI2_INT_EN);
> > +     /* Write clear CSI2 IRQ */
> > +     writel(0xffffffff, input->base + SENINF_CSI2_INT_STATUS);
> > +     /* Enable CSI2 Extend IRQ mask */
>
> You missed:
>         writel(0xffffffff, input->base + SENINF_CSI2_INT_EN_EXT);
>
> P.S.: #define SENINF_CSI2_INT_EN_EXT 0x0b10
>
>
> > +     /* Turn on all interrupt */
>
> /* Reset the CSI2 to commit changes */ <-- makes more sense, doesn't it?
>
> > +     mtk_seninf_input_update(input, SENINF_CTRL, CSI2_SW_RST, 1);
> > +     udelay(1);
> > +     mtk_seninf_input_update(input, SENINF_CTRL, CSI2_SW_RST, 0);
> > +}
> > +
> > +static void mtk_seninf_mux_setup(struct mtk_seninf_mux *mux,
> > +                              struct mtk_seninf_input *input,
> > +                              struct v4l2_subdev_state *state)
> > +{
> > +     const struct mtk_seninf_format_info *fmtinfo;
> > +     const struct v4l2_mbus_framefmt *format;
> > +     unsigned int pix_sel_ext;
> > +     unsigned int pix_sel;
> > +     unsigned int hs_pol = 0;
> > +     unsigned int vs_pol = 0;
> > +     unsigned int val;
> > +     u32 rst_mask;
> > +
> > +     format = v4l2_subdev_state_get_stream_format(state, input->pad, 0);
> > +     fmtinfo = mtk_seninf_format_info(format->code);
> > +
> > +     /* Enable mux */
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_MUX_EN, 1);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_SRC_SEL, SENINF_MIPI_SENSOR);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT, SENINF_SRC_SEL_EXT, SENINF_NORMAL_MODEL);
> > +
> > +     pix_sel_ext = 0;
> > +     pix_sel = 1;
>
>
>         pixels_per_cycle = 1;
>         bus_width = pixels_per_cycle >> 1;
>
> because:  0 == 1pix/cyc, 1 == 2pix/cyc, 2 == 4pix/cyc, 3 == 8pix... etc
> ...but the width of this register depends on the SoC, so you also want to set
> constraints to the bus width on a per-soc basis (platform data again, or at
> least leave a comment here).
>
>         mtk_seninf_mux_update(....  PIX_SEL_EXT, bus_width);
>         mtk_seninf_mux_update(....  PIX_SEL, bus_width);
>
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT, SENINF_PIX_SEL_EXT, pix_sel_ext);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_PIX_SEL, pix_sel);
> > +
> > +     if (fmtinfo->flags & MTK_SENINF_FORMAT_JPEG) {
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 0);
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN,
> > +                                   FIFO_FLUSH_EN_JPEG_2_PIXEL_MODE);
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN,
> > +                             FIFO_PUSH_EN_JPEG_2_PIXEL_MODE);
> > +     } else {
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 2);
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN,
> > +                                  FIFO_FLUSH_EN_NORMAL_MODE);
> > +             mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN,
> > +                             FIFO_PUSH_EN_NORMAL_MODE);
> > +     }
> > +
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_POL, hs_pol);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_VSYNC_POL, vs_pol);
> > +
> > +     val = mtk_seninf_mux_read(mux, SENINF_MUX_CTRL);
> > +     rst_mask = SENINF_MUX_CTRL_SENINF_IRQ_SW_RST | SENINF_MUX_CTRL_SENINF_MUX_SW_RST;
> > +
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_CTRL, val | rst_mask);
>
> Are you sure that you don't need any wait between assertion and deassertion of RST?
> Looks strange, but I don't really know then.
>
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_CTRL, val & ~rst_mask);
> > +
> > +     /* HQ */
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_SPARE, 0xc2000);
>
> val = SENINF_FIFO_FULL_SEL;
>
> /* SPARE field meaning is unknown */
> val |= 0xc0000;
>
>         mtk_seninf_mux_write(mux, SENINF_MUX_SPARE, val);
>
> > +}
> > +
> > +static void mtk_seninf_top_mux_setup(struct mtk_seninf *priv,
> > +                                  enum mtk_seninf_id seninf_id,
> > +                                  struct mtk_seninf_mux *mux)
> > +{
> > +     unsigned int val;
> > +
> > +     /*
> > +      * Use the top mux (from SENINF input to MUX) to configure routing, and
> > +      * hardcode a 1:1 mapping from the MUX instances to the SENINF outputs.
> > +      */
> > +     val = readl(priv->base + SENINF_TOP_MUX_CTRL)
> > +             & ~(0xf << (mux->mux_id * 4));
> > +     val |= (seninf_id & 0xf) << (mux->mux_id * 4);
> > +     writel(val, priv->base + SENINF_TOP_MUX_CTRL);
> > +
> > +     writel(0x76541010, priv->base + SENINF_TOP_CAM_MUX_CTRL);
>
> Each four bits of TOP_CAM_MUX_CTRL selects between seninf1 to seninf8 muxes, and
> TOP_MUX_CTRL is laid out in the very same way.
>
> This means that if you're calculating a value for TOP_MUX_CTRL, you can do exactly
> the same for TOP_CAM_MUX_CTRL.
>
> > +}
> > +
> > +static void seninf_enable_test_pattern(struct mtk_seninf *priv,
> > +                                    struct v4l2_subdev_state *state)
> > +{
> > +     struct mtk_seninf_input *input = &priv->inputs[CSI_PORT_0];
> > +     struct mtk_seninf_mux *mux = &priv->muxes[0];
> > +     const struct mtk_seninf_format_info *fmtinfo;
> > +     const struct v4l2_mbus_framefmt *format;
> > +     unsigned int val;
> > +     unsigned int pix_sel_ext;
> > +     unsigned int pix_sel;
> > +     unsigned int hs_pol = 0;
> > +     unsigned int vs_pol = 0;
> > +     unsigned int seninf = 0;
> > +     unsigned int tm_size = 0;
> > +     unsigned int mux_id = mux->mux_id;
> > +
> > +     format = v4l2_subdev_state_get_stream_format(state, priv->conf->nb_inputs, 0);
> > +     fmtinfo = mtk_seninf_format_info(format->code);
> > +
> > +     mtk_seninf_update(priv, SENINF_TOP_CTRL, MUX_LP_MODE, 0);
> > +
> > +     mtk_seninf_update(priv, SENINF_TOP_CTRL, SENINF_PCLK_EN, 1);
> > +     mtk_seninf_update(priv, SENINF_TOP_CTRL, SENINF2_PCLK_EN, 1);
> > +
> > +     mtk_seninf_input_update(input, SENINF_CTRL, SENINF_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_CTRL, SENINF_SRC_SEL, 1);
> > +     mtk_seninf_input_update(input, SENINF_CTRL_EXT,
> > +                             SENINF_TESTMDL_IP_EN, 1);
> > +
> > +     mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_EN, 1);
> > +     mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_PAT, 0xc);
> > +     mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_VSYNC, 4);
> > +     mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_DUMMYPXL, 0x28);
> > +
> > +     if (fmtinfo->flags & MTK_SENINF_FORMAT_BAYER)
> > +             mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_FMT, 0x0);
> > +     else
> > +             mtk_seninf_input_update(input, SENINF_TG1_TM_CTL, TM_FMT, 0x1);
> > +
> > +     tm_size = FIELD_PREP(SENINF_TG1_TM_SIZE_TM_LINE, format->height + 8);
> > +     switch (format->code) {
> > +     case MEDIA_BUS_FMT_UYVY8_1X16:
> > +     case MEDIA_BUS_FMT_VYUY8_1X16:
> > +     case MEDIA_BUS_FMT_YUYV8_1X16:
> > +     case MEDIA_BUS_FMT_YVYU8_1X16:
> > +             tm_size |= FIELD_PREP(SENINF_TG1_TM_SIZE_TM_PXL, format->width * 2);
> > +             break;
> > +     default:
> > +             tm_size |= FIELD_PREP(SENINF_TG1_TM_SIZE_TM_PXL, format->width);
> > +             break;
> > +     }
> > +     writel(tm_size, input->base + SENINF_TG1_TM_SIZE);
> > +
> > +     writel(TEST_MODEL_CLK_DIVIDED_CNT, input->base + SENINF_TG1_TM_CLK);
> > +     writel(TIME_STAMP_DIVIDER, input->base + SENINF_TG1_TM_STP);
> > +
> > +     /* Set top mux */
> > +     val = (readl(priv->base + SENINF_TOP_MUX_CTRL) & (~(0xf << (mux_id * 4)))) |
> > +             ((seninf & 0xf) << (mux_id * 4));
> > +     writel(val, priv->base + SENINF_TOP_MUX_CTRL);
>
> This is duplicated, and it is the same that you have in mtk_seninf_top_mux_setup()
>
> > +
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_MUX_EN, 1);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT,
> > +                           SENINF_SRC_SEL_EXT, SENINF_TEST_MODEL);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_SRC_SEL, 1);
> > +
> > +     pix_sel_ext = 0;
> > +     pix_sel = 1;
> > +
>
> This is in mtk_seninf_mux_setup(), but if you apply my suggestion, it won't be in
> there anymore, so you'll call a function here to set the right value :-)
>
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL_EXT,
> > +                           SENINF_PIX_SEL_EXT, pix_sel_ext);
> > +
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_PIX_SEL, pix_sel);
> > +
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_PUSH_EN, 0x1f);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FLUSH_EN, 0x1b);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, FIFO_FULL_WR_EN, 2);
> > +
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_POL, hs_pol);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_VSYNC_POL, vs_pol);
> > +     mtk_seninf_mux_update(mux, SENINF_MUX_CTRL, SENINF_HSYNC_MASK, 1);
> > +
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_INTEN,
> > +                          SENINF_IRQ_CLR_SEL | SENINF_ALL_ERR_IRQ_EN);
> > +
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_CTRL,
> > +                          mtk_seninf_mux_read(mux, SENINF_MUX_CTRL) |
> > +                          SENINF_MUX_CTRL_SENINF_IRQ_SW_RST |
> > +                          SENINF_MUX_CTRL_SENINF_MUX_SW_RST);
> > +     udelay(1);
> > +     mtk_seninf_mux_write(mux, SENINF_MUX_CTRL,
> > +                          mtk_seninf_mux_read(mux, SENINF_MUX_CTRL) &
> > +                          ~(SENINF_MUX_CTRL_SENINF_IRQ_SW_RST |
> > +                            SENINF_MUX_CTRL_SENINF_MUX_SW_RST));
> > +
> > +     //check this
> > +     writel(0x76540010, priv->base + SENINF_TOP_CAM_MUX_CTRL);
> > +
> > +     dev_dbg(priv->dev, "%s: OK\n", __func__);
> > +}
> > +
>
> Cheers,
> Angelo
>



More information about the Linux-mediatek mailing list