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

AngeloGioacchino Del Regno angelogioacchino.delregno at collabora.com
Wed Jun 12 01:06:35 PDT 2024


Il 10/06/24 16:39, Julien Stephan ha scritto:
> 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?
> 

As for where: setup, streaming start, resolution change (which may be covered
by streaming start anyway, as a change should be calling stop->start anyway).

And for the how is it exposed to the user - well, depends what you mean for user,
but it's all standard V4L2 API :-)

Last but not least, I can give you another hint....

struct media_entity *entity = (something_here);
struct media_pad *mpad;
struct v4l2_subdev *cam_subdev;
struct v4l2_ctrl *ctl;
s64 link_frequency, pixel_clock;

if (entity->pads[0].flags & MEDIA_PAD_FL_SINK)
    return -E_NOT_A_CAMERA_SENSOR_WE_IGNORE_THIS_ONE;

pad = media_pad_remote_pad_first(&entity->pads[0]);
if (!pad)
   return -ENOENT;

if (!is_media_entity_v4l2_subdev(pad->entity))
   return -ENOENT;

if (pad->entity->function != MEDIA_ENT_F_CAM_SENSOR)
   return -ENOENT;

cam_subdev = media_entity_to_v4l2_subdev(pad->entity);
ctl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);

/* multiplier is usually bits per pixel, divider is usually num of lanes */
link_frequency = v4l2_get_link_freq(cam_subdev->ctrl_handler, multiplier, divider);
pixel_clock = v4l2_ctrl_g_ctrl_int64(ctl);


....now you know what the sensor wants, set the seninf sensor clock accordingly.

Cheers
Angelo

> 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-arm-kernel mailing list