[v2,1/1] media: i2c: max96717: add test pattern ctrl
Commit Message
Add v4l2 test pattern control.
Signed-off-by: Tommaso Merciai <tomm.merciai@gmail.com>
---
Changes since v1:
- Rename and move pattern regs under VTX section as suggested by JMassot
- Fix VTX regs order
- Add comment saying that the deserializer should manage the link in
pixel mode as suggested by JMassot
drivers/media/i2c/max96717.c | 213 ++++++++++++++++++++++++++++++++---
1 file changed, 197 insertions(+), 16 deletions(-)
Comments
Hi Tommaso,
On 6/27/24 5:18 PM, Tommaso Merciai wrote:
> Add v4l2 test pattern control.
>
> Signed-off-by: Tommaso Merciai <tomm.merciai@gmail.com>
Thanks for your patch,
Just tested again on MAX96717F.
Reviewed-by: Julien Massot <julien.massot@collabora.com>
Tested-by: Julien Massot <julien.massot@collabora.com>
> ---
> Changes since v1:
> - Rename and move pattern regs under VTX section as suggested by JMassot
> - Fix VTX regs order
> - Add comment saying that the deserializer should manage the link in
> pixel mode as suggested by JMassot
>
> drivers/media/i2c/max96717.c | 213 ++++++++++++++++++++++++++++++++---
> 1 file changed, 197 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
> index 949306485873..859a439b64d9 100644
> --- a/drivers/media/i2c/max96717.c
> +++ b/drivers/media/i2c/max96717.c
> @@ -16,6 +16,7 @@
> #include <linux/regmap.h>
>
> #include <media/v4l2-cci.h>
> +#include <media/v4l2-ctrls.h>
> #include <media/v4l2-fwnode.h>
> #include <media/v4l2-subdev.h>
>
> @@ -38,9 +39,35 @@
> #define MAX96717_DEV_REV_MASK GENMASK(3, 0)
>
> /* VID_TX Z */
> +#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
> +#define MAX96717_VIDEO_AUTO_BPP BIT(3)
> #define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
> #define MAX96717_VIDEO_PCLKDET BIT(7)
>
> +/* VTX_Z */
> +#define MAX96717_VTX0 CCI_REG8(0x24e)
> +#define MAX96717_VTX1 CCI_REG8(0x24f)
> +#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
> +#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
> +#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
> +#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
> +#define MAX96717_VTX_V2H CCI_REG24(0x259)
> +#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
> +#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
> +#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
> +#define MAX96717_VTX_V2D CCI_REG24(0x262)
> +#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
> +#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
> +#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
> +#define MAX96717_VTX29 CCI_REG8(0x26b)
> +#define MAX96717_VTX_MODE GENMASK(1, 0)
> +#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
> +#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
> +#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
> +#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
> +#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
> +#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
> +
> /* GPIO */
> #define MAX96717_NUM_GPIO 11
> #define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
> @@ -82,6 +109,12 @@
> /* MISC */
> #define PIO_SLEW_1 CCI_REG8(0x570)
>
> +enum max96717_vpg_mode {
> + MAX96717_VPG_DISABLED = 0,
> + MAX96717_VPG_CHECKERBOARD = 1,
> + MAX96717_VPG_GRADIENT = 2,
> +};
> +
> struct max96717_priv {
> struct i2c_client *client;
> struct regmap *regmap;
> @@ -89,6 +122,7 @@ struct max96717_priv {
> struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
> struct v4l2_subdev sd;
> struct media_pad pads[MAX96717_PORTS];
> + struct v4l2_ctrl_handler ctrl_handler;
> struct v4l2_async_notifier notifier;
> struct v4l2_subdev *source_sd;
> u16 source_sd_pad;
> @@ -96,6 +130,7 @@ struct max96717_priv {
> u8 pll_predef_index;
> struct clk_hw clk_hw;
> struct gpio_chip gpio_chip;
> + enum max96717_vpg_mode pattern;
> };
>
> static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
> @@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
> start ? MAX96717_START_PORT_B : 0, NULL);
> }
>
> +static int max96717_apply_patgen_timing(struct max96717_priv *priv,
> + struct v4l2_subdev_state *state)
> +{
> + struct v4l2_mbus_framefmt *fmt =
> + v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);
> + const u32 h_active = fmt->width;
> + const u32 h_fp = 88;
> + const u32 h_sw = 44;
> + const u32 h_bp = 148;
> + u32 h_tot;
> + const u32 v_active = fmt->height;
> + const u32 v_fp = 4;
> + const u32 v_sw = 5;
> + const u32 v_bp = 36;
> + u32 v_tot;
> + int ret = 0;
> +
> + h_tot = h_active + h_fp + h_sw + h_bp;
> + v_tot = v_active + v_fp + v_sw + v_bp;
> +
> + /* 75 Mhz pixel clock */
> + cci_update_bits(priv->regmap, MAX96717_VTX1,
> + MAX96717_PATTERN_CLK_FREQ, 0xa, &ret);
> +
> + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
> + fmt->width);
> +
> + cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_VS_LOW,
> + (v_active + v_fp + v_bp) * h_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp,
> + &ret);
> + cci_write(priv->regmap, MAX96717_VTX_V2D,
> + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp,
> + &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret);
> + /* B G R */
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret);
> + /* B G R */
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret);
> +
> + return ret;
> +}
> +
> +static int max96717_apply_patgen(struct max96717_priv *priv,
> + struct v4l2_subdev_state *state)
> +{
> + unsigned int val;
> + int ret = 0;
> +
> + if (priv->pattern)
> + ret = max96717_apply_patgen_timing(priv, state);
> +
> + cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0,
> + &ret);
> +
> + val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern);
> + cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE,
> + val, &ret);
> + return ret;
> +}
> +
> +static int max96717_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct max96717_priv *priv =
> + container_of(ctrl->handler, struct max96717_priv, ctrl_handler);
> + int ret;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_TEST_PATTERN:
> + if (priv->enabled_source_streams)
> + return -EBUSY;
> + priv->pattern = ctrl->val;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* Use bpp from bpp register */
> + ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0,
> + MAX96717_VIDEO_AUTO_BPP,
> + priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP,
> + NULL);
> +
> + /*
> + * Pattern generator doesn't work with tunnel mode.
> + * Needs RGB color format and deserializer tunnel mode must be disabled.
> + */
> + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11,
> + MAX96717_TUN_MODE,
> + priv->pattern ? 0 : MAX96717_TUN_MODE, &ret);
> +}
> +
> +static const char * const max96717_test_pattern[] = {
> + "Disabled",
> + "Checkerboard",
> + "Gradient"
> +};
> +
> +static const struct v4l2_ctrl_ops max96717_ctrl_ops = {
> + .s_ctrl = max96717_s_ctrl,
> +};
> +
> static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
> unsigned int offset)
> {
> @@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd,
> u64 sink_streams;
> int ret;
>
> - sink_streams = v4l2_subdev_state_xlate_streams(state,
> - MAX96717_PAD_SOURCE,
> - MAX96717_PAD_SINK,
> - &streams_mask);
> -
> if (!priv->enabled_source_streams)
> max96717_start_csi(priv, true);
>
> - ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
> - sink_streams);
> - if (ret) {
> - dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> - sink_streams);
> + ret = max96717_apply_patgen(priv, state);
> + if (ret)
> goto stop_csi;
> +
> + if (!priv->pattern) {
> + sink_streams =
> + v4l2_subdev_state_xlate_streams(state,
> + MAX96717_PAD_SOURCE,
> + MAX96717_PAD_SINK,
> + &streams_mask);
> +
> + ret = v4l2_subdev_enable_streams(priv->source_sd,
> + priv->source_sd_pad,
> + sink_streams);
> + if (ret) {
> + dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> + sink_streams);
> + goto stop_csi;
> + }
> }
>
> priv->enabled_source_streams |= streams_mask;
> @@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd,
> if (!priv->enabled_source_streams)
> max96717_start_csi(priv, false);
>
> - sink_streams = v4l2_subdev_state_xlate_streams(state,
> - MAX96717_PAD_SOURCE,
> - MAX96717_PAD_SINK,
> - &streams_mask);
> + if (!priv->pattern) {
> + int ret;
> +
> + sink_streams =
> + v4l2_subdev_state_xlate_streams(state,
> + MAX96717_PAD_SOURCE,
> + MAX96717_PAD_SINK,
> + &streams_mask);
>
> - return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
> - sink_streams);
> + ret = v4l2_subdev_disable_streams(priv->source_sd,
> + priv->source_sd_pad,
> + sink_streams);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> }
>
> static const struct v4l2_subdev_pad_ops max96717_pad_ops = {
> @@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops);
> priv->sd.internal_ops = &max96717_internal_ops;
>
> + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> + priv->sd.ctrl_handler = &priv->ctrl_handler;
> +
> + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> + &max96717_ctrl_ops,
> + V4L2_CID_TEST_PATTERN,
> + ARRAY_SIZE(max96717_test_pattern) - 1,
> + 0, 0, max96717_test_pattern);
> + if (priv->ctrl_handler.error) {
> + ret = priv->ctrl_handler.error;
> + goto err_free_ctrl;
> + }
> +
> priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
> priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> priv->sd.entity.ops = &max96717_entity_ops;
> @@ -552,6 +730,8 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> v4l2_subdev_cleanup(&priv->sd);
> err_entity_cleanup:
> media_entity_cleanup(&priv->sd.entity);
> +err_free_ctrl:
> + v4l2_ctrl_handler_free(&priv->ctrl_handler);
>
> return ret;
> }
> @@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv)
> v4l2_async_nf_cleanup(&priv->notifier);
> v4l2_subdev_cleanup(&priv->sd);
> media_entity_cleanup(&priv->sd.entity);
> + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> }
>
> struct max96717_pll_predef_freq {
Hi Tommaso,
Thanks for the patch.
On Thu, Jun 27, 2024 at 05:18:06PM +0200, Tommaso Merciai wrote:
> Add v4l2 test pattern control.
>
> Signed-off-by: Tommaso Merciai <tomm.merciai@gmail.com>
> ---
> Changes since v1:
> - Rename and move pattern regs under VTX section as suggested by JMassot
> - Fix VTX regs order
> - Add comment saying that the deserializer should manage the link in
> pixel mode as suggested by JMassot
>
> drivers/media/i2c/max96717.c | 213 ++++++++++++++++++++++++++++++++---
> 1 file changed, 197 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
> index 949306485873..859a439b64d9 100644
> --- a/drivers/media/i2c/max96717.c
> +++ b/drivers/media/i2c/max96717.c
> @@ -16,6 +16,7 @@
> #include <linux/regmap.h>
>
> #include <media/v4l2-cci.h>
> +#include <media/v4l2-ctrls.h>
> #include <media/v4l2-fwnode.h>
> #include <media/v4l2-subdev.h>
>
> @@ -38,9 +39,35 @@
> #define MAX96717_DEV_REV_MASK GENMASK(3, 0)
>
> /* VID_TX Z */
> +#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
> +#define MAX96717_VIDEO_AUTO_BPP BIT(3)
> #define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
> #define MAX96717_VIDEO_PCLKDET BIT(7)
>
> +/* VTX_Z */
> +#define MAX96717_VTX0 CCI_REG8(0x24e)
> +#define MAX96717_VTX1 CCI_REG8(0x24f)
> +#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
> +#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
> +#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
> +#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
> +#define MAX96717_VTX_V2H CCI_REG24(0x259)
> +#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
> +#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
> +#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
> +#define MAX96717_VTX_V2D CCI_REG24(0x262)
> +#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
> +#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
> +#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
> +#define MAX96717_VTX29 CCI_REG8(0x26b)
> +#define MAX96717_VTX_MODE GENMASK(1, 0)
> +#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
> +#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
> +#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
> +#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
> +#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
> +#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
> +
> /* GPIO */
> #define MAX96717_NUM_GPIO 11
> #define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
> @@ -82,6 +109,12 @@
> /* MISC */
> #define PIO_SLEW_1 CCI_REG8(0x570)
>
> +enum max96717_vpg_mode {
> + MAX96717_VPG_DISABLED = 0,
> + MAX96717_VPG_CHECKERBOARD = 1,
> + MAX96717_VPG_GRADIENT = 2,
> +};
> +
> struct max96717_priv {
> struct i2c_client *client;
> struct regmap *regmap;
> @@ -89,6 +122,7 @@ struct max96717_priv {
> struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
> struct v4l2_subdev sd;
> struct media_pad pads[MAX96717_PORTS];
> + struct v4l2_ctrl_handler ctrl_handler;
> struct v4l2_async_notifier notifier;
> struct v4l2_subdev *source_sd;
> u16 source_sd_pad;
> @@ -96,6 +130,7 @@ struct max96717_priv {
> u8 pll_predef_index;
> struct clk_hw clk_hw;
> struct gpio_chip gpio_chip;
> + enum max96717_vpg_mode pattern;
> };
>
> static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
> @@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
> start ? MAX96717_START_PORT_B : 0, NULL);
> }
>
> +static int max96717_apply_patgen_timing(struct max96717_priv *priv,
> + struct v4l2_subdev_state *state)
> +{
> + struct v4l2_mbus_framefmt *fmt =
> + v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);
> + const u32 h_active = fmt->width;
> + const u32 h_fp = 88;
> + const u32 h_sw = 44;
> + const u32 h_bp = 148;
> + u32 h_tot;
> + const u32 v_active = fmt->height;
> + const u32 v_fp = 4;
> + const u32 v_sw = 5;
> + const u32 v_bp = 36;
Some comments here would be nice, what do these values signify for
instance?
> + u32 v_tot;
> + int ret = 0;
> +
> + h_tot = h_active + h_fp + h_sw + h_bp;
> + v_tot = v_active + v_fp + v_sw + v_bp;
> +
> + /* 75 Mhz pixel clock */
> + cci_update_bits(priv->regmap, MAX96717_VTX1,
> + MAX96717_PATTERN_CLK_FREQ, 0xa, &ret);
> +
> + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
> + fmt->width);
> +
> + cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_VS_LOW,
> + (v_active + v_fp + v_bp) * h_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp,
> + &ret);
> + cci_write(priv->regmap, MAX96717_VTX_V2D,
> + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret);
> + cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp,
> + &ret);
> + cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret);
> + /* B G R */
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret);
> + /* B G R */
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret);
> + cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret);
> +
> + return ret;
> +}
> +
> +static int max96717_apply_patgen(struct max96717_priv *priv,
> + struct v4l2_subdev_state *state)
> +{
> + unsigned int val;
> + int ret = 0;
> +
> + if (priv->pattern)
> + ret = max96717_apply_patgen_timing(priv, state);
> +
> + cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0,
> + &ret);
> +
> + val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern);
> + cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE,
> + val, &ret);
> + return ret;
> +}
> +
> +static int max96717_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> + struct max96717_priv *priv =
> + container_of(ctrl->handler, struct max96717_priv, ctrl_handler);
> + int ret;
> +
> + switch (ctrl->id) {
> + case V4L2_CID_TEST_PATTERN:
> + if (priv->enabled_source_streams)
> + return -EBUSY;
> + priv->pattern = ctrl->val;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* Use bpp from bpp register */
> + ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0,
> + MAX96717_VIDEO_AUTO_BPP,
> + priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP,
> + NULL);
> +
> + /*
> + * Pattern generator doesn't work with tunnel mode.
> + * Needs RGB color format and deserializer tunnel mode must be disabled.
> + */
> + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11,
> + MAX96717_TUN_MODE,
> + priv->pattern ? 0 : MAX96717_TUN_MODE, &ret);
> +}
> +
> +static const char * const max96717_test_pattern[] = {
> + "Disabled",
> + "Checkerboard",
> + "Gradient"
> +};
> +
> +static const struct v4l2_ctrl_ops max96717_ctrl_ops = {
> + .s_ctrl = max96717_s_ctrl,
> +};
> +
> static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
> unsigned int offset)
> {
> @@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd,
> u64 sink_streams;
> int ret;
>
> - sink_streams = v4l2_subdev_state_xlate_streams(state,
> - MAX96717_PAD_SOURCE,
> - MAX96717_PAD_SINK,
> - &streams_mask);
> -
> if (!priv->enabled_source_streams)
> max96717_start_csi(priv, true);
>
> - ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
> - sink_streams);
> - if (ret) {
> - dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> - sink_streams);
> + ret = max96717_apply_patgen(priv, state);
> + if (ret)
> goto stop_csi;
> +
> + if (!priv->pattern) {
> + sink_streams =
> + v4l2_subdev_state_xlate_streams(state,
> + MAX96717_PAD_SOURCE,
> + MAX96717_PAD_SINK,
> + &streams_mask);
> +
> + ret = v4l2_subdev_enable_streams(priv->source_sd,
> + priv->source_sd_pad,
> + sink_streams);
> + if (ret) {
> + dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> + sink_streams);
> + goto stop_csi;
> + }
> }
>
> priv->enabled_source_streams |= streams_mask;
> @@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd,
> if (!priv->enabled_source_streams)
> max96717_start_csi(priv, false);
>
> - sink_streams = v4l2_subdev_state_xlate_streams(state,
> - MAX96717_PAD_SOURCE,
> - MAX96717_PAD_SINK,
> - &streams_mask);
> + if (!priv->pattern) {
> + int ret;
> +
> + sink_streams =
> + v4l2_subdev_state_xlate_streams(state,
> + MAX96717_PAD_SOURCE,
> + MAX96717_PAD_SINK,
> + &streams_mask);
>
> - return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
> - sink_streams);
> + ret = v4l2_subdev_disable_streams(priv->source_sd,
> + priv->source_sd_pad,
> + sink_streams);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> }
>
> static const struct v4l2_subdev_pad_ops max96717_pad_ops = {
> @@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops);
> priv->sd.internal_ops = &max96717_internal_ops;
>
> + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> + priv->sd.ctrl_handler = &priv->ctrl_handler;
> +
> + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> + &max96717_ctrl_ops,
> + V4L2_CID_TEST_PATTERN,
> + ARRAY_SIZE(max96717_test_pattern) - 1,
> + 0, 0, max96717_test_pattern);
> + if (priv->ctrl_handler.error) {
> + ret = priv->ctrl_handler.error;
> + goto err_free_ctrl;
> + }
> +
> priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
With controls, you should add the HAS_EVENTS flag.
I'll take this one as we're in rc5 already, please address these in a
separate patch.
> priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> priv->sd.entity.ops = &max96717_entity_ops;
> @@ -552,6 +730,8 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> v4l2_subdev_cleanup(&priv->sd);
> err_entity_cleanup:
> media_entity_cleanup(&priv->sd.entity);
> +err_free_ctrl:
> + v4l2_ctrl_handler_free(&priv->ctrl_handler);
>
> return ret;
> }
> @@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv)
> v4l2_async_nf_cleanup(&priv->notifier);
> v4l2_subdev_cleanup(&priv->sd);
> media_entity_cleanup(&priv->sd.entity);
> + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> }
>
> struct max96717_pll_predef_freq {
Hi Sakari,
Sorry for delay and thanks for reviewing this.
On Wed, Aug 28, 2024 at 06:44:19AM +0000, Sakari Ailus wrote:
> Hi Tommaso,
>
> Thanks for the patch.
>
> On Thu, Jun 27, 2024 at 05:18:06PM +0200, Tommaso Merciai wrote:
> > Add v4l2 test pattern control.
> >
> > Signed-off-by: Tommaso Merciai <tomm.merciai@gmail.com>
> > ---
> > Changes since v1:
> > - Rename and move pattern regs under VTX section as suggested by JMassot
> > - Fix VTX regs order
> > - Add comment saying that the deserializer should manage the link in
> > pixel mode as suggested by JMassot
> >
> > drivers/media/i2c/max96717.c | 213 ++++++++++++++++++++++++++++++++---
> > 1 file changed, 197 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
> > index 949306485873..859a439b64d9 100644
> > --- a/drivers/media/i2c/max96717.c
> > +++ b/drivers/media/i2c/max96717.c
> > @@ -16,6 +16,7 @@
> > #include <linux/regmap.h>
> >
> > #include <media/v4l2-cci.h>
> > +#include <media/v4l2-ctrls.h>
> > #include <media/v4l2-fwnode.h>
> > #include <media/v4l2-subdev.h>
> >
> > @@ -38,9 +39,35 @@
> > #define MAX96717_DEV_REV_MASK GENMASK(3, 0)
> >
> > /* VID_TX Z */
> > +#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
> > +#define MAX96717_VIDEO_AUTO_BPP BIT(3)
> > #define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
> > #define MAX96717_VIDEO_PCLKDET BIT(7)
> >
> > +/* VTX_Z */
> > +#define MAX96717_VTX0 CCI_REG8(0x24e)
> > +#define MAX96717_VTX1 CCI_REG8(0x24f)
> > +#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
> > +#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
> > +#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
> > +#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
> > +#define MAX96717_VTX_V2H CCI_REG24(0x259)
> > +#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
> > +#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
> > +#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
> > +#define MAX96717_VTX_V2D CCI_REG24(0x262)
> > +#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
> > +#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
> > +#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
> > +#define MAX96717_VTX29 CCI_REG8(0x26b)
> > +#define MAX96717_VTX_MODE GENMASK(1, 0)
> > +#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
> > +#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
> > +#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
> > +#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
> > +#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
> > +#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
> > +
> > /* GPIO */
> > #define MAX96717_NUM_GPIO 11
> > #define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
> > @@ -82,6 +109,12 @@
> > /* MISC */
> > #define PIO_SLEW_1 CCI_REG8(0x570)
> >
> > +enum max96717_vpg_mode {
> > + MAX96717_VPG_DISABLED = 0,
> > + MAX96717_VPG_CHECKERBOARD = 1,
> > + MAX96717_VPG_GRADIENT = 2,
> > +};
> > +
> > struct max96717_priv {
> > struct i2c_client *client;
> > struct regmap *regmap;
> > @@ -89,6 +122,7 @@ struct max96717_priv {
> > struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
> > struct v4l2_subdev sd;
> > struct media_pad pads[MAX96717_PORTS];
> > + struct v4l2_ctrl_handler ctrl_handler;
> > struct v4l2_async_notifier notifier;
> > struct v4l2_subdev *source_sd;
> > u16 source_sd_pad;
> > @@ -96,6 +130,7 @@ struct max96717_priv {
> > u8 pll_predef_index;
> > struct clk_hw clk_hw;
> > struct gpio_chip gpio_chip;
> > + enum max96717_vpg_mode pattern;
> > };
> >
> > static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
> > @@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
> > start ? MAX96717_START_PORT_B : 0, NULL);
> > }
> >
> > +static int max96717_apply_patgen_timing(struct max96717_priv *priv,
> > + struct v4l2_subdev_state *state)
> > +{
> > + struct v4l2_mbus_framefmt *fmt =
> > + v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);
> > + const u32 h_active = fmt->width;
> > + const u32 h_fp = 88;
> > + const u32 h_sw = 44;
> > + const u32 h_bp = 148;
> > + u32 h_tot;
> > + const u32 v_active = fmt->height;
> > + const u32 v_fp = 4;
> > + const u32 v_sw = 5;
> > + const u32 v_bp = 36;
>
> Some comments here would be nice, what do these values signify for
> instance?
Oks, I will send a patch with some comments for this.
>
> > + u32 v_tot;
> > + int ret = 0;
> > +
> > + h_tot = h_active + h_fp + h_sw + h_bp;
> > + v_tot = v_active + v_fp + v_sw + v_bp;
> > +
> > + /* 75 Mhz pixel clock */
> > + cci_update_bits(priv->regmap, MAX96717_VTX1,
> > + MAX96717_PATTERN_CLK_FREQ, 0xa, &ret);
> > +
> > + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
> > + fmt->width);
> > +
> > + cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_VS_LOW,
> > + (v_active + v_fp + v_bp) * h_tot, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp,
> > + &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_V2D,
> > + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp,
> > + &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret);
> > + /* B G R */
> > + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret);
> > + /* B G R */
> > + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret);
> > + cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret);
> > +
> > + return ret;
> > +}
> > +
> > +static int max96717_apply_patgen(struct max96717_priv *priv,
> > + struct v4l2_subdev_state *state)
> > +{
> > + unsigned int val;
> > + int ret = 0;
> > +
> > + if (priv->pattern)
> > + ret = max96717_apply_patgen_timing(priv, state);
> > +
> > + cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0,
> > + &ret);
> > +
> > + val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern);
> > + cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE,
> > + val, &ret);
> > + return ret;
> > +}
> > +
> > +static int max96717_s_ctrl(struct v4l2_ctrl *ctrl)
> > +{
> > + struct max96717_priv *priv =
> > + container_of(ctrl->handler, struct max96717_priv, ctrl_handler);
> > + int ret;
> > +
> > + switch (ctrl->id) {
> > + case V4L2_CID_TEST_PATTERN:
> > + if (priv->enabled_source_streams)
> > + return -EBUSY;
> > + priv->pattern = ctrl->val;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + /* Use bpp from bpp register */
> > + ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0,
> > + MAX96717_VIDEO_AUTO_BPP,
> > + priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP,
> > + NULL);
> > +
> > + /*
> > + * Pattern generator doesn't work with tunnel mode.
> > + * Needs RGB color format and deserializer tunnel mode must be disabled.
> > + */
> > + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11,
> > + MAX96717_TUN_MODE,
> > + priv->pattern ? 0 : MAX96717_TUN_MODE, &ret);
> > +}
> > +
> > +static const char * const max96717_test_pattern[] = {
> > + "Disabled",
> > + "Checkerboard",
> > + "Gradient"
> > +};
> > +
> > +static const struct v4l2_ctrl_ops max96717_ctrl_ops = {
> > + .s_ctrl = max96717_s_ctrl,
> > +};
> > +
> > static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
> > unsigned int offset)
> > {
> > @@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd,
> > u64 sink_streams;
> > int ret;
> >
> > - sink_streams = v4l2_subdev_state_xlate_streams(state,
> > - MAX96717_PAD_SOURCE,
> > - MAX96717_PAD_SINK,
> > - &streams_mask);
> > -
> > if (!priv->enabled_source_streams)
> > max96717_start_csi(priv, true);
> >
> > - ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
> > - sink_streams);
> > - if (ret) {
> > - dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> > - sink_streams);
> > + ret = max96717_apply_patgen(priv, state);
> > + if (ret)
> > goto stop_csi;
> > +
> > + if (!priv->pattern) {
> > + sink_streams =
> > + v4l2_subdev_state_xlate_streams(state,
> > + MAX96717_PAD_SOURCE,
> > + MAX96717_PAD_SINK,
> > + &streams_mask);
> > +
> > + ret = v4l2_subdev_enable_streams(priv->source_sd,
> > + priv->source_sd_pad,
> > + sink_streams);
> > + if (ret) {
> > + dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> > + sink_streams);
> > + goto stop_csi;
> > + }
> > }
> >
> > priv->enabled_source_streams |= streams_mask;
> > @@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd,
> > if (!priv->enabled_source_streams)
> > max96717_start_csi(priv, false);
> >
> > - sink_streams = v4l2_subdev_state_xlate_streams(state,
> > - MAX96717_PAD_SOURCE,
> > - MAX96717_PAD_SINK,
> > - &streams_mask);
> > + if (!priv->pattern) {
> > + int ret;
> > +
> > + sink_streams =
> > + v4l2_subdev_state_xlate_streams(state,
> > + MAX96717_PAD_SOURCE,
> > + MAX96717_PAD_SINK,
> > + &streams_mask);
> >
> > - return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
> > - sink_streams);
> > + ret = v4l2_subdev_disable_streams(priv->source_sd,
> > + priv->source_sd_pad,
> > + sink_streams);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + return 0;
> > }
> >
> > static const struct v4l2_subdev_pad_ops max96717_pad_ops = {
> > @@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> > v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops);
> > priv->sd.internal_ops = &max96717_internal_ops;
> >
> > + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> > + priv->sd.ctrl_handler = &priv->ctrl_handler;
> > +
> > + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> > + &max96717_ctrl_ops,
> > + V4L2_CID_TEST_PATTERN,
> > + ARRAY_SIZE(max96717_test_pattern) - 1,
> > + 0, 0, max96717_test_pattern);
> > + if (priv->ctrl_handler.error) {
> > + ret = priv->ctrl_handler.error;
> > + goto err_free_ctrl;
> > + }
> > +
> > priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
>
> With controls, you should add the HAS_EVENTS flag.
>
> I'll take this one as we're in rc5 already, please address these in a
> separate patch.
Will send a patch for this.
I think we should fix that also into max96717 driver or I'm wrong?
>
> > priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> > priv->sd.entity.ops = &max96717_entity_ops;
> > @@ -552,6 +730,8 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> > v4l2_subdev_cleanup(&priv->sd);
> > err_entity_cleanup:
> > media_entity_cleanup(&priv->sd.entity);
> > +err_free_ctrl:
> > + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> >
> > return ret;
> > }
> > @@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv)
> > v4l2_async_nf_cleanup(&priv->notifier);
> > v4l2_subdev_cleanup(&priv->sd);
> > media_entity_cleanup(&priv->sd.entity);
> > + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> > }
> >
> > struct max96717_pll_predef_freq {
>
> --
> Kind regards,
>
> Sakari Ailus
Thanks & Regards,
Tommaso
On Wed, Sep 04, 2024 at 02:53:55PM +0200, Tommaso Merciai wrote:
> Hi Sakari,
> Sorry for delay and thanks for reviewing this.
>
> On Wed, Aug 28, 2024 at 06:44:19AM +0000, Sakari Ailus wrote:
> > Hi Tommaso,
> >
> > Thanks for the patch.
> >
> > On Thu, Jun 27, 2024 at 05:18:06PM +0200, Tommaso Merciai wrote:
> > > Add v4l2 test pattern control.
> > >
> > > Signed-off-by: Tommaso Merciai <tomm.merciai@gmail.com>
> > > ---
> > > Changes since v1:
> > > - Rename and move pattern regs under VTX section as suggested by JMassot
> > > - Fix VTX regs order
> > > - Add comment saying that the deserializer should manage the link in
> > > pixel mode as suggested by JMassot
> > >
> > > drivers/media/i2c/max96717.c | 213 ++++++++++++++++++++++++++++++++---
> > > 1 file changed, 197 insertions(+), 16 deletions(-)
> > >
> > > diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
> > > index 949306485873..859a439b64d9 100644
> > > --- a/drivers/media/i2c/max96717.c
> > > +++ b/drivers/media/i2c/max96717.c
> > > @@ -16,6 +16,7 @@
> > > #include <linux/regmap.h>
> > >
> > > #include <media/v4l2-cci.h>
> > > +#include <media/v4l2-ctrls.h>
> > > #include <media/v4l2-fwnode.h>
> > > #include <media/v4l2-subdev.h>
> > >
> > > @@ -38,9 +39,35 @@
> > > #define MAX96717_DEV_REV_MASK GENMASK(3, 0)
> > >
> > > /* VID_TX Z */
> > > +#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
> > > +#define MAX96717_VIDEO_AUTO_BPP BIT(3)
> > > #define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
> > > #define MAX96717_VIDEO_PCLKDET BIT(7)
> > >
> > > +/* VTX_Z */
> > > +#define MAX96717_VTX0 CCI_REG8(0x24e)
> > > +#define MAX96717_VTX1 CCI_REG8(0x24f)
> > > +#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
> > > +#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
> > > +#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
> > > +#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
> > > +#define MAX96717_VTX_V2H CCI_REG24(0x259)
> > > +#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
> > > +#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
> > > +#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
> > > +#define MAX96717_VTX_V2D CCI_REG24(0x262)
> > > +#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
> > > +#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
> > > +#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
> > > +#define MAX96717_VTX29 CCI_REG8(0x26b)
> > > +#define MAX96717_VTX_MODE GENMASK(1, 0)
> > > +#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
> > > +#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
> > > +#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
> > > +#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
> > > +#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
> > > +#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
> > > +
> > > /* GPIO */
> > > #define MAX96717_NUM_GPIO 11
> > > #define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
> > > @@ -82,6 +109,12 @@
> > > /* MISC */
> > > #define PIO_SLEW_1 CCI_REG8(0x570)
> > >
> > > +enum max96717_vpg_mode {
> > > + MAX96717_VPG_DISABLED = 0,
> > > + MAX96717_VPG_CHECKERBOARD = 1,
> > > + MAX96717_VPG_GRADIENT = 2,
> > > +};
> > > +
> > > struct max96717_priv {
> > > struct i2c_client *client;
> > > struct regmap *regmap;
> > > @@ -89,6 +122,7 @@ struct max96717_priv {
> > > struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
> > > struct v4l2_subdev sd;
> > > struct media_pad pads[MAX96717_PORTS];
> > > + struct v4l2_ctrl_handler ctrl_handler;
> > > struct v4l2_async_notifier notifier;
> > > struct v4l2_subdev *source_sd;
> > > u16 source_sd_pad;
> > > @@ -96,6 +130,7 @@ struct max96717_priv {
> > > u8 pll_predef_index;
> > > struct clk_hw clk_hw;
> > > struct gpio_chip gpio_chip;
> > > + enum max96717_vpg_mode pattern;
> > > };
> > >
> > > static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
> > > @@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
> > > start ? MAX96717_START_PORT_B : 0, NULL);
> > > }
> > >
> > > +static int max96717_apply_patgen_timing(struct max96717_priv *priv,
> > > + struct v4l2_subdev_state *state)
> > > +{
> > > + struct v4l2_mbus_framefmt *fmt =
> > > + v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);
> > > + const u32 h_active = fmt->width;
> > > + const u32 h_fp = 88;
> > > + const u32 h_sw = 44;
> > > + const u32 h_bp = 148;
> > > + u32 h_tot;
> > > + const u32 v_active = fmt->height;
> > > + const u32 v_fp = 4;
> > > + const u32 v_sw = 5;
> > > + const u32 v_bp = 36;
> >
> > Some comments here would be nice, what do these values signify for
> > instance?
>
> Oks, I will send a patch with some comments for this.
>
> >
> > > + u32 v_tot;
> > > + int ret = 0;
> > > +
> > > + h_tot = h_active + h_fp + h_sw + h_bp;
> > > + v_tot = v_active + v_fp + v_sw + v_bp;
> > > +
> > > + /* 75 Mhz pixel clock */
> > > + cci_update_bits(priv->regmap, MAX96717_VTX1,
> > > + MAX96717_PATTERN_CLK_FREQ, 0xa, &ret);
> > > +
> > > + dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
> > > + fmt->width);
> > > +
> > > + cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_VS_LOW,
> > > + (v_active + v_fp + v_bp) * h_tot, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp,
> > > + &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_V2D,
> > > + h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp,
> > > + &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret);
> > > + /* B G R */
> > > + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret);
> > > + /* B G R */
> > > + cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret);
> > > + cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret);
> > > +
> > > + return ret;
> > > +}
> > > +
> > > +static int max96717_apply_patgen(struct max96717_priv *priv,
> > > + struct v4l2_subdev_state *state)
> > > +{
> > > + unsigned int val;
> > > + int ret = 0;
> > > +
> > > + if (priv->pattern)
> > > + ret = max96717_apply_patgen_timing(priv, state);
> > > +
> > > + cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0,
> > > + &ret);
> > > +
> > > + val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern);
> > > + cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE,
> > > + val, &ret);
> > > + return ret;
> > > +}
> > > +
> > > +static int max96717_s_ctrl(struct v4l2_ctrl *ctrl)
> > > +{
> > > + struct max96717_priv *priv =
> > > + container_of(ctrl->handler, struct max96717_priv, ctrl_handler);
> > > + int ret;
> > > +
> > > + switch (ctrl->id) {
> > > + case V4L2_CID_TEST_PATTERN:
> > > + if (priv->enabled_source_streams)
> > > + return -EBUSY;
> > > + priv->pattern = ctrl->val;
> > > + break;
> > > + default:
> > > + return -EINVAL;
> > > + }
> > > +
> > > + /* Use bpp from bpp register */
> > > + ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0,
> > > + MAX96717_VIDEO_AUTO_BPP,
> > > + priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP,
> > > + NULL);
> > > +
> > > + /*
> > > + * Pattern generator doesn't work with tunnel mode.
> > > + * Needs RGB color format and deserializer tunnel mode must be disabled.
> > > + */
> > > + return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11,
> > > + MAX96717_TUN_MODE,
> > > + priv->pattern ? 0 : MAX96717_TUN_MODE, &ret);
> > > +}
> > > +
> > > +static const char * const max96717_test_pattern[] = {
> > > + "Disabled",
> > > + "Checkerboard",
> > > + "Gradient"
> > > +};
> > > +
> > > +static const struct v4l2_ctrl_ops max96717_ctrl_ops = {
> > > + .s_ctrl = max96717_s_ctrl,
> > > +};
> > > +
> > > static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
> > > unsigned int offset)
> > > {
> > > @@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd,
> > > u64 sink_streams;
> > > int ret;
> > >
> > > - sink_streams = v4l2_subdev_state_xlate_streams(state,
> > > - MAX96717_PAD_SOURCE,
> > > - MAX96717_PAD_SINK,
> > > - &streams_mask);
> > > -
> > > if (!priv->enabled_source_streams)
> > > max96717_start_csi(priv, true);
> > >
> > > - ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
> > > - sink_streams);
> > > - if (ret) {
> > > - dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> > > - sink_streams);
> > > + ret = max96717_apply_patgen(priv, state);
> > > + if (ret)
> > > goto stop_csi;
> > > +
> > > + if (!priv->pattern) {
> > > + sink_streams =
> > > + v4l2_subdev_state_xlate_streams(state,
> > > + MAX96717_PAD_SOURCE,
> > > + MAX96717_PAD_SINK,
> > > + &streams_mask);
> > > +
> > > + ret = v4l2_subdev_enable_streams(priv->source_sd,
> > > + priv->source_sd_pad,
> > > + sink_streams);
> > > + if (ret) {
> > > + dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
> > > + sink_streams);
> > > + goto stop_csi;
> > > + }
> > > }
> > >
> > > priv->enabled_source_streams |= streams_mask;
> > > @@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd,
> > > if (!priv->enabled_source_streams)
> > > max96717_start_csi(priv, false);
> > >
> > > - sink_streams = v4l2_subdev_state_xlate_streams(state,
> > > - MAX96717_PAD_SOURCE,
> > > - MAX96717_PAD_SINK,
> > > - &streams_mask);
> > > + if (!priv->pattern) {
> > > + int ret;
> > > +
> > > + sink_streams =
> > > + v4l2_subdev_state_xlate_streams(state,
> > > + MAX96717_PAD_SOURCE,
> > > + MAX96717_PAD_SINK,
> > > + &streams_mask);
> > >
> > > - return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
> > > - sink_streams);
> > > + ret = v4l2_subdev_disable_streams(priv->source_sd,
> > > + priv->source_sd_pad,
> > > + sink_streams);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + return 0;
> > > }
> > >
> > > static const struct v4l2_subdev_pad_ops max96717_pad_ops = {
> > > @@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> > > v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops);
> > > priv->sd.internal_ops = &max96717_internal_ops;
> > >
> > > + v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
> > > + priv->sd.ctrl_handler = &priv->ctrl_handler;
> > > +
> > > + v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
> > > + &max96717_ctrl_ops,
> > > + V4L2_CID_TEST_PATTERN,
> > > + ARRAY_SIZE(max96717_test_pattern) - 1,
> > > + 0, 0, max96717_test_pattern);
> > > + if (priv->ctrl_handler.error) {
> > > + ret = priv->ctrl_handler.error;
> > > + goto err_free_ctrl;
> > > + }
> > > +
> > > priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
> >
> > With controls, you should add the HAS_EVENTS flag.
> >
> > I'll take this one as we're in rc5 already, please address these in a
> > separate patch.
>
>
> Will send a patch for this.
> I think we should fix that also into max96717 driver or I'm wrong?
max96714 sorry.
>
> >
> > > priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
> > > priv->sd.entity.ops = &max96717_entity_ops;
> > > @@ -552,6 +730,8 @@ static int max96717_subdev_init(struct max96717_priv *priv)
> > > v4l2_subdev_cleanup(&priv->sd);
> > > err_entity_cleanup:
> > > media_entity_cleanup(&priv->sd.entity);
> > > +err_free_ctrl:
> > > + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> > >
> > > return ret;
> > > }
> > > @@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv)
> > > v4l2_async_nf_cleanup(&priv->notifier);
> > > v4l2_subdev_cleanup(&priv->sd);
> > > media_entity_cleanup(&priv->sd.entity);
> > > + v4l2_ctrl_handler_free(&priv->ctrl_handler);
> > > }
> > >
> > > struct max96717_pll_predef_freq {
> >
> > --
> > Kind regards,
> >
> > Sakari Ailus
>
>
> Thanks & Regards,
> Tommaso
Hi Tommaso,
On Wed, Sep 04, 2024 at 03:01:36PM +0200, Tommaso Merciai wrote:
> On Wed, Sep 04, 2024 at 02:53:55PM +0200, Tommaso Merciai wrote:
> > Hi Sakari,
> > Sorry for delay and thanks for reviewing this.
No problem.
> >
> > On Wed, Aug 28, 2024 at 06:44:19AM +0000, Sakari Ailus wrote:
> > > > priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
> > >
> > > With controls, you should add the HAS_EVENTS flag.
> > >
> > > I'll take this one as we're in rc5 already, please address these in a
> > > separate patch.
> >
> >
> > Will send a patch for this.
> > I think we should fix that also into max96717 driver or I'm wrong?
>
> max96714 sorry.
Yes, please.
@@ -16,6 +16,7 @@
#include <linux/regmap.h>
#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
@@ -38,9 +39,35 @@
#define MAX96717_DEV_REV_MASK GENMASK(3, 0)
/* VID_TX Z */
+#define MAX96717_VIDEO_TX0 CCI_REG8(0x110)
+#define MAX96717_VIDEO_AUTO_BPP BIT(3)
#define MAX96717_VIDEO_TX2 CCI_REG8(0x112)
#define MAX96717_VIDEO_PCLKDET BIT(7)
+/* VTX_Z */
+#define MAX96717_VTX0 CCI_REG8(0x24e)
+#define MAX96717_VTX1 CCI_REG8(0x24f)
+#define MAX96717_PATTERN_CLK_FREQ GENMASK(3, 1)
+#define MAX96717_VTX_VS_DLY CCI_REG24(0x250)
+#define MAX96717_VTX_VS_HIGH CCI_REG24(0x253)
+#define MAX96717_VTX_VS_LOW CCI_REG24(0x256)
+#define MAX96717_VTX_V2H CCI_REG24(0x259)
+#define MAX96717_VTX_HS_HIGH CCI_REG16(0x25c)
+#define MAX96717_VTX_HS_LOW CCI_REG16(0x25e)
+#define MAX96717_VTX_HS_CNT CCI_REG16(0x260)
+#define MAX96717_VTX_V2D CCI_REG24(0x262)
+#define MAX96717_VTX_DE_HIGH CCI_REG16(0x265)
+#define MAX96717_VTX_DE_LOW CCI_REG16(0x267)
+#define MAX96717_VTX_DE_CNT CCI_REG16(0x269)
+#define MAX96717_VTX29 CCI_REG8(0x26b)
+#define MAX96717_VTX_MODE GENMASK(1, 0)
+#define MAX96717_VTX_GRAD_INC CCI_REG8(0x26c)
+#define MAX96717_VTX_CHKB_COLOR_A CCI_REG24(0x26d)
+#define MAX96717_VTX_CHKB_COLOR_B CCI_REG24(0x270)
+#define MAX96717_VTX_CHKB_RPT_CNT_A CCI_REG8(0x273)
+#define MAX96717_VTX_CHKB_RPT_CNT_B CCI_REG8(0x274)
+#define MAX96717_VTX_CHKB_ALT CCI_REG8(0x275)
+
/* GPIO */
#define MAX96717_NUM_GPIO 11
#define MAX96717_GPIO_REG_A(gpio) CCI_REG8(0x2be + (gpio) * 3)
@@ -82,6 +109,12 @@
/* MISC */
#define PIO_SLEW_1 CCI_REG8(0x570)
+enum max96717_vpg_mode {
+ MAX96717_VPG_DISABLED = 0,
+ MAX96717_VPG_CHECKERBOARD = 1,
+ MAX96717_VPG_GRADIENT = 2,
+};
+
struct max96717_priv {
struct i2c_client *client;
struct regmap *regmap;
@@ -89,6 +122,7 @@ struct max96717_priv {
struct v4l2_mbus_config_mipi_csi2 mipi_csi2;
struct v4l2_subdev sd;
struct media_pad pads[MAX96717_PORTS];
+ struct v4l2_ctrl_handler ctrl_handler;
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source_sd;
u16 source_sd_pad;
@@ -96,6 +130,7 @@ struct max96717_priv {
u8 pll_predef_index;
struct clk_hw clk_hw;
struct gpio_chip gpio_chip;
+ enum max96717_vpg_mode pattern;
};
static inline struct max96717_priv *sd_to_max96717(struct v4l2_subdev *sd)
@@ -131,6 +166,118 @@ static inline int max96717_start_csi(struct max96717_priv *priv, bool start)
start ? MAX96717_START_PORT_B : 0, NULL);
}
+static int max96717_apply_patgen_timing(struct max96717_priv *priv,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(state, MAX96717_PAD_SOURCE);
+ const u32 h_active = fmt->width;
+ const u32 h_fp = 88;
+ const u32 h_sw = 44;
+ const u32 h_bp = 148;
+ u32 h_tot;
+ const u32 v_active = fmt->height;
+ const u32 v_fp = 4;
+ const u32 v_sw = 5;
+ const u32 v_bp = 36;
+ u32 v_tot;
+ int ret = 0;
+
+ h_tot = h_active + h_fp + h_sw + h_bp;
+ v_tot = v_active + v_fp + v_sw + v_bp;
+
+ /* 75 Mhz pixel clock */
+ cci_update_bits(priv->regmap, MAX96717_VTX1,
+ MAX96717_PATTERN_CLK_FREQ, 0xa, &ret);
+
+ dev_info(&priv->client->dev, "height: %d width: %d\n", fmt->height,
+ fmt->width);
+
+ cci_write(priv->regmap, MAX96717_VTX_VS_DLY, 0, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_VS_HIGH, v_sw * h_tot, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_VS_LOW,
+ (v_active + v_fp + v_bp) * h_tot, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_HS_HIGH, h_sw, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_HS_LOW, h_active + h_fp + h_bp,
+ &ret);
+ cci_write(priv->regmap, MAX96717_VTX_V2D,
+ h_tot * (v_sw + v_bp) + (h_sw + h_bp), &ret);
+ cci_write(priv->regmap, MAX96717_VTX_HS_CNT, v_tot, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_DE_HIGH, h_active, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_DE_LOW, h_fp + h_sw + h_bp,
+ &ret);
+ cci_write(priv->regmap, MAX96717_VTX_DE_CNT, v_active, &ret);
+ /* B G R */
+ cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_A, 0xfecc00, &ret);
+ /* B G R */
+ cci_write(priv->regmap, MAX96717_VTX_CHKB_COLOR_B, 0x006aa7, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_A, 0x3c, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_CHKB_RPT_CNT_B, 0x3c, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_CHKB_ALT, 0x3c, &ret);
+ cci_write(priv->regmap, MAX96717_VTX_GRAD_INC, 0x10, &ret);
+
+ return ret;
+}
+
+static int max96717_apply_patgen(struct max96717_priv *priv,
+ struct v4l2_subdev_state *state)
+{
+ unsigned int val;
+ int ret = 0;
+
+ if (priv->pattern)
+ ret = max96717_apply_patgen_timing(priv, state);
+
+ cci_write(priv->regmap, MAX96717_VTX0, priv->pattern ? 0xfb : 0,
+ &ret);
+
+ val = FIELD_PREP(MAX96717_VTX_MODE, priv->pattern);
+ cci_update_bits(priv->regmap, MAX96717_VTX29, MAX96717_VTX_MODE,
+ val, &ret);
+ return ret;
+}
+
+static int max96717_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct max96717_priv *priv =
+ container_of(ctrl->handler, struct max96717_priv, ctrl_handler);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TEST_PATTERN:
+ if (priv->enabled_source_streams)
+ return -EBUSY;
+ priv->pattern = ctrl->val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Use bpp from bpp register */
+ ret = cci_update_bits(priv->regmap, MAX96717_VIDEO_TX0,
+ MAX96717_VIDEO_AUTO_BPP,
+ priv->pattern ? 0 : MAX96717_VIDEO_AUTO_BPP,
+ NULL);
+
+ /*
+ * Pattern generator doesn't work with tunnel mode.
+ * Needs RGB color format and deserializer tunnel mode must be disabled.
+ */
+ return cci_update_bits(priv->regmap, MAX96717_MIPI_RX_EXT11,
+ MAX96717_TUN_MODE,
+ priv->pattern ? 0 : MAX96717_TUN_MODE, &ret);
+}
+
+static const char * const max96717_test_pattern[] = {
+ "Disabled",
+ "Checkerboard",
+ "Gradient"
+};
+
+static const struct v4l2_ctrl_ops max96717_ctrl_ops = {
+ .s_ctrl = max96717_s_ctrl,
+};
+
static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
unsigned int offset)
{
@@ -352,20 +499,28 @@ static int max96717_enable_streams(struct v4l2_subdev *sd,
u64 sink_streams;
int ret;
- sink_streams = v4l2_subdev_state_xlate_streams(state,
- MAX96717_PAD_SOURCE,
- MAX96717_PAD_SINK,
- &streams_mask);
-
if (!priv->enabled_source_streams)
max96717_start_csi(priv, true);
- ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
- sink_streams);
- if (ret) {
- dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
- sink_streams);
+ ret = max96717_apply_patgen(priv, state);
+ if (ret)
goto stop_csi;
+
+ if (!priv->pattern) {
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state,
+ MAX96717_PAD_SOURCE,
+ MAX96717_PAD_SINK,
+ &streams_mask);
+
+ ret = v4l2_subdev_enable_streams(priv->source_sd,
+ priv->source_sd_pad,
+ sink_streams);
+ if (ret) {
+ dev_err(dev, "Fail to start streams:%llu on remote subdev\n",
+ sink_streams);
+ goto stop_csi;
+ }
}
priv->enabled_source_streams |= streams_mask;
@@ -394,13 +549,23 @@ static int max96717_disable_streams(struct v4l2_subdev *sd,
if (!priv->enabled_source_streams)
max96717_start_csi(priv, false);
- sink_streams = v4l2_subdev_state_xlate_streams(state,
- MAX96717_PAD_SOURCE,
- MAX96717_PAD_SINK,
- &streams_mask);
+ if (!priv->pattern) {
+ int ret;
+
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state,
+ MAX96717_PAD_SOURCE,
+ MAX96717_PAD_SINK,
+ &streams_mask);
- return v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
- sink_streams);
+ ret = v4l2_subdev_disable_streams(priv->source_sd,
+ priv->source_sd_pad,
+ sink_streams);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static const struct v4l2_subdev_pad_ops max96717_pad_ops = {
@@ -513,6 +678,19 @@ static int max96717_subdev_init(struct max96717_priv *priv)
v4l2_i2c_subdev_init(&priv->sd, priv->client, &max96717_subdev_ops);
priv->sd.internal_ops = &max96717_internal_ops;
+ v4l2_ctrl_handler_init(&priv->ctrl_handler, 1);
+ priv->sd.ctrl_handler = &priv->ctrl_handler;
+
+ v4l2_ctrl_new_std_menu_items(&priv->ctrl_handler,
+ &max96717_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(max96717_test_pattern) - 1,
+ 0, 0, max96717_test_pattern);
+ if (priv->ctrl_handler.error) {
+ ret = priv->ctrl_handler.error;
+ goto err_free_ctrl;
+ }
+
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
priv->sd.entity.ops = &max96717_entity_ops;
@@ -552,6 +730,8 @@ static int max96717_subdev_init(struct max96717_priv *priv)
v4l2_subdev_cleanup(&priv->sd);
err_entity_cleanup:
media_entity_cleanup(&priv->sd.entity);
+err_free_ctrl:
+ v4l2_ctrl_handler_free(&priv->ctrl_handler);
return ret;
}
@@ -563,6 +743,7 @@ static void max96717_subdev_uninit(struct max96717_priv *priv)
v4l2_async_nf_cleanup(&priv->notifier);
v4l2_subdev_cleanup(&priv->sd);
media_entity_cleanup(&priv->sd.entity);
+ v4l2_ctrl_handler_free(&priv->ctrl_handler);
}
struct max96717_pll_predef_freq {