Hi Lucas,

On Fri, 16 Dec 2022 22:07:42 +0100
Lucas Stach <l.st...@pengutronix.de> wrote:

> This IP block is found in the HDMI subsystem of the i.MX8MP SoC. It has a
> full timing generator and can switch between different video sources. On
> the i.MX8MP however the only supported source is the LCDIF. The block
> just needs to be powered up and told about the polarity of the video
> sync signals to act in bypass mode.
> 
> Signed-off-by: Lucas Stach <l.st...@pengutronix.de>
> Tested-by: Marek Vasut <ma...@denx.de>
> ---
>  drivers/gpu/drm/bridge/imx/Kconfig           |   7 +
>  drivers/gpu/drm/bridge/imx/Makefile          |   1 +
>  drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c | 202 +++++++++++++++++++
>  3 files changed, 210 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
> 
> diff --git a/drivers/gpu/drm/bridge/imx/Kconfig 
> b/drivers/gpu/drm/bridge/imx/Kconfig
> index d828d8bfd893..e6cc4000bccd 100644
> --- a/drivers/gpu/drm/bridge/imx/Kconfig
> +++ b/drivers/gpu/drm/bridge/imx/Kconfig
> @@ -53,4 +53,11 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE
>         Choose this to enable support for the internal HDMI encoder found
>         on the i.MX8MP SoC.
>  
> +config DRM_IMX8MP_HDMI_PVI
> +     tristate "i.MX8MP HDMI PVI bridge support"
> +     depends on OF
> +     help
> +       Choose this to enable support for the internal HDMI TX Parallel
> +       Video Interface found on the i.MX8MP SoC.
> +
>  endif # ARCH_MXC || COMPILE_TEST
> diff --git a/drivers/gpu/drm/bridge/imx/Makefile 
> b/drivers/gpu/drm/bridge/imx/Makefile
> index 03b0074ae538..b0fd56550dad 100644
> --- a/drivers/gpu/drm/bridge/imx/Makefile
> +++ b/drivers/gpu/drm/bridge/imx/Makefile
> @@ -9,3 +9,4 @@ obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
>  obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
>  
>  obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi.o
> +obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o
> diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c 
> b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
> new file mode 100644
> index 000000000000..30d40c21dabb
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c
> @@ -0,0 +1,202 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/*
> + * Copyright (C) 2022 Pengutronix, Lucas Stach <ker...@pengutronix.de>
> + */
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/pm_runtime.h>
> +
> +#define HTX_PVI_CTL  0x0

Personally I would s/CTL/CTRL/, to be consistent with the manual and
thus more search-friendly.

> +#define  PVI_CTL_OP_VSYNC_POL        BIT(18)
> +#define  PVI_CTL_OP_HSYNC_POL        BIT(17)
> +#define  PVI_CTL_OP_DE_POL   BIT(16)
> +#define  PVI_CTL_INP_VSYNC_POL       BIT(14)
> +#define  PVI_CTL_INP_HSYNC_POL       BIT(13)
> +#define  PVI_CTL_INP_DE_POL  BIT(12)
> +#define  PVI_CTL_INPUT_LCDIF BIT(2)

According to the reference manual there is actually a 2-bit field here:
HTX_PVI_MODE, using bits 2:1, and whose "LCDIF" value is 0b10. Thus
while it obviously won't change the resulting code, it seems more
correct to define this as (2 << 1).

> +static void imx8mp_hdmi_pvi_bridge_enable(struct drm_bridge *bridge,
> +                                       struct drm_bridge_state *bridge_state)
> +{
> +     struct drm_atomic_state *state = bridge_state->base.state;
> +     struct imx8mp_hdmi_pvi *pvi = to_imx8mp_hdmi_pvi(bridge);
> +     struct drm_connector_state *conn_state;
> +     const struct drm_display_mode *mode;
> +     struct drm_crtc_state *crtc_state;
> +     struct drm_connector *connector;
> +     u32 bus_flags, val;
> +
> +     connector = drm_atomic_get_new_connector_for_encoder(state, 
> bridge->encoder);
> +     conn_state = drm_atomic_get_new_connector_state(state, connector);
> +     crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
> +
> +     if (WARN_ON(pm_runtime_resume_and_get(pvi->dev)))
> +             return;
> +
> +     mode = &crtc_state->adjusted_mode;
> +
> +     val = PVI_CTL_INPUT_LCDIF;
> +
> +     if (mode->flags & DRM_MODE_FLAG_PVSYNC)
> +             val |= PVI_CTL_OP_VSYNC_POL | PVI_CTL_INP_VSYNC_POL;
> +
> +     if (mode->flags & DRM_MODE_FLAG_PHSYNC)
> +             val |= PVI_CTL_OP_HSYNC_POL | PVI_CTL_INP_HSYNC_POL;
> +
> +     if (pvi->next_bridge->timings)
> +             bus_flags = pvi->next_bridge->timings->input_bus_flags;
> +     else if (bridge_state)
> +             bus_flags = bridge_state->input_bus_cfg.flags;
> +
> +     if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
> +             val |= PVI_CTL_OP_DE_POL | PVI_CTL_INP_DE_POL;
> +
> +     writel(val, pvi->regs + HTX_PVI_CTL);
> +     val |= PVI_CTL_EN;
> +     writel(val, pvi->regs + HTX_PVI_CTL);

I guess I'm missing something here: why can't one just write the
register once, with the enable bit set? I tried removing the first
writel() and everything seems to work just the same.

With these fixed:
Reviewed-by: Luca Ceresoli <luca.ceres...@bootlin.com>

And definitely:
[Tested on a custom board using modetest on v6.5-rc6]
Tested-by: Luca Ceresoli <luca.ceres...@bootlin.com>



-- 
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

Reply via email to