1 // SPDX-License-Identifier: GPL-2.0+
4 * Copyright (C) 2022 Pengutronix, Lucas Stach <kernel@pengutronix.de>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_bridge.h>
9 #include <drm/drm_crtc.h>
10 #include <linux/bitfield.h>
12 #include <linux/module.h>
14 #include <linux/of_graph.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
18 #define HTX_PVI_CTRL 0x0
19 #define PVI_CTRL_OP_VSYNC_POL BIT(18)
20 #define PVI_CTRL_OP_HSYNC_POL BIT(17)
21 #define PVI_CTRL_OP_DE_POL BIT(16)
22 #define PVI_CTRL_INP_VSYNC_POL BIT(14)
23 #define PVI_CTRL_INP_HSYNC_POL BIT(13)
24 #define PVI_CTRL_INP_DE_POL BIT(12)
25 #define PVI_CTRL_MODE_MASK GENMASK(2, 1)
26 #define PVI_CTRL_MODE_LCDIF 2
27 #define PVI_CTRL_EN BIT(0)
29 struct imx8mp_hdmi_pvi
{
30 struct drm_bridge bridge
;
32 struct drm_bridge
*next_bridge
;
36 static inline struct imx8mp_hdmi_pvi
*
37 to_imx8mp_hdmi_pvi(struct drm_bridge
*bridge
)
39 return container_of(bridge
, struct imx8mp_hdmi_pvi
, bridge
);
42 static int imx8mp_hdmi_pvi_bridge_attach(struct drm_bridge
*bridge
,
43 enum drm_bridge_attach_flags flags
)
45 struct imx8mp_hdmi_pvi
*pvi
= to_imx8mp_hdmi_pvi(bridge
);
47 return drm_bridge_attach(bridge
->encoder
, pvi
->next_bridge
,
51 static void imx8mp_hdmi_pvi_bridge_enable(struct drm_bridge
*bridge
,
52 struct drm_bridge_state
*bridge_state
)
54 struct drm_atomic_state
*state
= bridge_state
->base
.state
;
55 struct imx8mp_hdmi_pvi
*pvi
= to_imx8mp_hdmi_pvi(bridge
);
56 struct drm_connector_state
*conn_state
;
57 const struct drm_display_mode
*mode
;
58 struct drm_crtc_state
*crtc_state
;
59 struct drm_connector
*connector
;
60 u32 bus_flags
= 0, val
;
62 connector
= drm_atomic_get_new_connector_for_encoder(state
, bridge
->encoder
);
63 conn_state
= drm_atomic_get_new_connector_state(state
, connector
);
64 crtc_state
= drm_atomic_get_new_crtc_state(state
, conn_state
->crtc
);
66 if (WARN_ON(pm_runtime_resume_and_get(pvi
->dev
)))
69 mode
= &crtc_state
->adjusted_mode
;
71 val
= FIELD_PREP(PVI_CTRL_MODE_MASK
, PVI_CTRL_MODE_LCDIF
) | PVI_CTRL_EN
;
73 if (mode
->flags
& DRM_MODE_FLAG_PVSYNC
)
74 val
|= PVI_CTRL_OP_VSYNC_POL
| PVI_CTRL_INP_VSYNC_POL
;
76 if (mode
->flags
& DRM_MODE_FLAG_PHSYNC
)
77 val
|= PVI_CTRL_OP_HSYNC_POL
| PVI_CTRL_INP_HSYNC_POL
;
79 if (pvi
->next_bridge
->timings
)
80 bus_flags
= pvi
->next_bridge
->timings
->input_bus_flags
;
81 else if (bridge_state
)
82 bus_flags
= bridge_state
->input_bus_cfg
.flags
;
84 if (bus_flags
& DRM_BUS_FLAG_DE_HIGH
)
85 val
|= PVI_CTRL_OP_DE_POL
| PVI_CTRL_INP_DE_POL
;
87 writel(val
, pvi
->regs
+ HTX_PVI_CTRL
);
90 static void imx8mp_hdmi_pvi_bridge_disable(struct drm_bridge
*bridge
,
91 struct drm_bridge_state
*bridge_state
)
93 struct imx8mp_hdmi_pvi
*pvi
= to_imx8mp_hdmi_pvi(bridge
);
95 writel(0x0, pvi
->regs
+ HTX_PVI_CTRL
);
97 pm_runtime_put(pvi
->dev
);
101 imx8mp_hdmi_pvi_bridge_get_input_bus_fmts(struct drm_bridge
*bridge
,
102 struct drm_bridge_state
*bridge_state
,
103 struct drm_crtc_state
*crtc_state
,
104 struct drm_connector_state
*conn_state
,
106 unsigned int *num_input_fmts
)
108 struct imx8mp_hdmi_pvi
*pvi
= to_imx8mp_hdmi_pvi(bridge
);
109 struct drm_bridge
*next_bridge
= pvi
->next_bridge
;
110 struct drm_bridge_state
*next_state
;
112 if (!next_bridge
->funcs
->atomic_get_input_bus_fmts
)
115 next_state
= drm_atomic_get_new_bridge_state(crtc_state
->state
,
118 return next_bridge
->funcs
->atomic_get_input_bus_fmts(next_bridge
,
126 static const struct drm_bridge_funcs imx_hdmi_pvi_bridge_funcs
= {
127 .attach
= imx8mp_hdmi_pvi_bridge_attach
,
128 .atomic_enable
= imx8mp_hdmi_pvi_bridge_enable
,
129 .atomic_disable
= imx8mp_hdmi_pvi_bridge_disable
,
130 .atomic_get_input_bus_fmts
= imx8mp_hdmi_pvi_bridge_get_input_bus_fmts
,
131 .atomic_duplicate_state
= drm_atomic_helper_bridge_duplicate_state
,
132 .atomic_destroy_state
= drm_atomic_helper_bridge_destroy_state
,
133 .atomic_reset
= drm_atomic_helper_bridge_reset
,
136 static int imx8mp_hdmi_pvi_probe(struct platform_device
*pdev
)
138 struct device_node
*remote
;
139 struct imx8mp_hdmi_pvi
*pvi
;
141 pvi
= devm_kzalloc(&pdev
->dev
, sizeof(*pvi
), GFP_KERNEL
);
145 platform_set_drvdata(pdev
, pvi
);
146 pvi
->dev
= &pdev
->dev
;
148 pvi
->regs
= devm_platform_ioremap_resource(pdev
, 0);
149 if (IS_ERR(pvi
->regs
))
150 return PTR_ERR(pvi
->regs
);
152 /* Get the next bridge in the pipeline. */
153 remote
= of_graph_get_remote_node(pdev
->dev
.of_node
, 1, -1);
157 pvi
->next_bridge
= of_drm_find_bridge(remote
);
160 if (!pvi
->next_bridge
)
161 return dev_err_probe(&pdev
->dev
, -EPROBE_DEFER
,
162 "could not find next bridge\n");
164 pm_runtime_enable(&pdev
->dev
);
166 /* Register the bridge. */
167 pvi
->bridge
.funcs
= &imx_hdmi_pvi_bridge_funcs
;
168 pvi
->bridge
.of_node
= pdev
->dev
.of_node
;
169 pvi
->bridge
.timings
= pvi
->next_bridge
->timings
;
171 drm_bridge_add(&pvi
->bridge
);
176 static void imx8mp_hdmi_pvi_remove(struct platform_device
*pdev
)
178 struct imx8mp_hdmi_pvi
*pvi
= platform_get_drvdata(pdev
);
180 drm_bridge_remove(&pvi
->bridge
);
182 pm_runtime_disable(&pdev
->dev
);
185 static const struct of_device_id imx8mp_hdmi_pvi_match
[] = {
187 .compatible
= "fsl,imx8mp-hdmi-pvi",
192 MODULE_DEVICE_TABLE(of
, imx8mp_hdmi_pvi_match
);
194 static struct platform_driver imx8mp_hdmi_pvi_driver
= {
195 .probe
= imx8mp_hdmi_pvi_probe
,
196 .remove
= imx8mp_hdmi_pvi_remove
,
198 .name
= "imx-hdmi-pvi",
199 .of_match_table
= imx8mp_hdmi_pvi_match
,
202 module_platform_driver(imx8mp_hdmi_pvi_driver
);
204 MODULE_DESCRIPTION("i.MX8MP HDMI TX Parallel Video Interface bridge driver");
205 MODULE_LICENSE("GPL");