1 // SPDX-License-Identifier: GPL-2.0+
3 * i.MX drm driver - parallel display implementation
5 * Copyright (C) 2012 Sascha Hauer, Pengutronix
8 #include <linux/component.h>
9 #include <linux/module.h>
10 #include <linux/platform_device.h>
11 #include <linux/videodev2.h>
13 #include <video/of_display_timing.h>
15 #include <drm/drm_atomic_helper.h>
16 #include <drm/drm_bridge.h>
17 #include <drm/drm_fb_helper.h>
18 #include <drm/drm_of.h>
19 #include <drm/drm_panel.h>
20 #include <drm/drm_probe_helper.h>
24 struct imx_parallel_display
{
25 struct drm_connector connector
;
26 struct drm_encoder encoder
;
32 struct drm_display_mode mode
;
33 struct drm_panel
*panel
;
34 struct drm_bridge
*bridge
;
37 static inline struct imx_parallel_display
*con_to_imxpd(struct drm_connector
*c
)
39 return container_of(c
, struct imx_parallel_display
, connector
);
42 static inline struct imx_parallel_display
*enc_to_imxpd(struct drm_encoder
*e
)
44 return container_of(e
, struct imx_parallel_display
, encoder
);
47 static int imx_pd_connector_get_modes(struct drm_connector
*connector
)
49 struct imx_parallel_display
*imxpd
= con_to_imxpd(connector
);
50 struct device_node
*np
= imxpd
->dev
->of_node
;
53 num_modes
= drm_panel_get_modes(imxpd
->panel
, connector
);
58 drm_connector_update_edid_property(connector
, imxpd
->edid
);
59 num_modes
= drm_add_edid_modes(connector
, imxpd
->edid
);
63 struct drm_display_mode
*mode
= drm_mode_create(connector
->dev
);
69 ret
= of_get_drm_display_mode(np
, &imxpd
->mode
,
75 drm_mode_copy(mode
, &imxpd
->mode
);
76 mode
->type
|= DRM_MODE_TYPE_DRIVER
| DRM_MODE_TYPE_PREFERRED
,
77 drm_mode_probed_add(connector
, mode
);
84 static struct drm_encoder
*imx_pd_connector_best_encoder(
85 struct drm_connector
*connector
)
87 struct imx_parallel_display
*imxpd
= con_to_imxpd(connector
);
89 return &imxpd
->encoder
;
92 static void imx_pd_encoder_enable(struct drm_encoder
*encoder
)
94 struct imx_parallel_display
*imxpd
= enc_to_imxpd(encoder
);
96 drm_panel_prepare(imxpd
->panel
);
97 drm_panel_enable(imxpd
->panel
);
100 static void imx_pd_encoder_disable(struct drm_encoder
*encoder
)
102 struct imx_parallel_display
*imxpd
= enc_to_imxpd(encoder
);
104 drm_panel_disable(imxpd
->panel
);
105 drm_panel_unprepare(imxpd
->panel
);
108 static int imx_pd_encoder_atomic_check(struct drm_encoder
*encoder
,
109 struct drm_crtc_state
*crtc_state
,
110 struct drm_connector_state
*conn_state
)
112 struct imx_crtc_state
*imx_crtc_state
= to_imx_crtc_state(crtc_state
);
113 struct drm_display_info
*di
= &conn_state
->connector
->display_info
;
114 struct imx_parallel_display
*imxpd
= enc_to_imxpd(encoder
);
116 if (!imxpd
->bus_format
&& di
->num_bus_formats
) {
117 imx_crtc_state
->bus_flags
= di
->bus_flags
;
118 imx_crtc_state
->bus_format
= di
->bus_formats
[0];
120 imx_crtc_state
->bus_flags
= imxpd
->bus_flags
;
121 imx_crtc_state
->bus_format
= imxpd
->bus_format
;
123 imx_crtc_state
->di_hsync_pin
= 2;
124 imx_crtc_state
->di_vsync_pin
= 3;
129 static const struct drm_connector_funcs imx_pd_connector_funcs
= {
130 .fill_modes
= drm_helper_probe_single_connector_modes
,
131 .destroy
= imx_drm_connector_destroy
,
132 .reset
= drm_atomic_helper_connector_reset
,
133 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
134 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
137 static const struct drm_connector_helper_funcs imx_pd_connector_helper_funcs
= {
138 .get_modes
= imx_pd_connector_get_modes
,
139 .best_encoder
= imx_pd_connector_best_encoder
,
142 static const struct drm_encoder_funcs imx_pd_encoder_funcs
= {
143 .destroy
= imx_drm_encoder_destroy
,
146 static const struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs
= {
147 .enable
= imx_pd_encoder_enable
,
148 .disable
= imx_pd_encoder_disable
,
149 .atomic_check
= imx_pd_encoder_atomic_check
,
152 static int imx_pd_register(struct drm_device
*drm
,
153 struct imx_parallel_display
*imxpd
)
155 struct drm_encoder
*encoder
= &imxpd
->encoder
;
158 ret
= imx_drm_encoder_parse_of(drm
, encoder
, imxpd
->dev
->of_node
);
162 /* set the connector's dpms to OFF so that
163 * drm_helper_connector_dpms() won't return
164 * immediately since the current state is ON
167 imxpd
->connector
.dpms
= DRM_MODE_DPMS_OFF
;
169 drm_encoder_helper_add(encoder
, &imx_pd_encoder_helper_funcs
);
170 drm_encoder_init(drm
, encoder
, &imx_pd_encoder_funcs
,
171 DRM_MODE_ENCODER_NONE
, NULL
);
173 if (!imxpd
->bridge
) {
174 drm_connector_helper_add(&imxpd
->connector
,
175 &imx_pd_connector_helper_funcs
);
176 drm_connector_init(drm
, &imxpd
->connector
,
177 &imx_pd_connector_funcs
,
178 DRM_MODE_CONNECTOR_DPI
);
182 drm_panel_attach(imxpd
->panel
, &imxpd
->connector
);
185 ret
= drm_bridge_attach(encoder
, imxpd
->bridge
, NULL
);
187 dev_err(imxpd
->dev
, "failed to attach bridge: %d\n",
192 drm_connector_attach_encoder(&imxpd
->connector
, encoder
);
198 static int imx_pd_bind(struct device
*dev
, struct device
*master
, void *data
)
200 struct drm_device
*drm
= data
;
201 struct device_node
*np
= dev
->of_node
;
203 struct imx_parallel_display
*imxpd
;
208 imxpd
= devm_kzalloc(dev
, sizeof(*imxpd
), GFP_KERNEL
);
212 edidp
= of_get_property(np
, "edid", &imxpd
->edid_len
);
214 imxpd
->edid
= kmemdup(edidp
, imxpd
->edid_len
, GFP_KERNEL
);
216 ret
= of_property_read_string(np
, "interface-pix-fmt", &fmt
);
218 if (!strcmp(fmt
, "rgb24"))
219 bus_format
= MEDIA_BUS_FMT_RGB888_1X24
;
220 else if (!strcmp(fmt
, "rgb565"))
221 bus_format
= MEDIA_BUS_FMT_RGB565_1X16
;
222 else if (!strcmp(fmt
, "bgr666"))
223 bus_format
= MEDIA_BUS_FMT_RGB666_1X18
;
224 else if (!strcmp(fmt
, "lvds666"))
225 bus_format
= MEDIA_BUS_FMT_RGB666_1X24_CPADHI
;
227 imxpd
->bus_format
= bus_format
;
229 /* port@1 is the output port */
230 ret
= drm_of_find_panel_or_bridge(np
, 1, 0, &imxpd
->panel
, &imxpd
->bridge
);
231 if (ret
&& ret
!= -ENODEV
)
236 ret
= imx_pd_register(drm
, imxpd
);
240 dev_set_drvdata(dev
, imxpd
);
245 static void imx_pd_unbind(struct device
*dev
, struct device
*master
,
248 struct imx_parallel_display
*imxpd
= dev_get_drvdata(dev
);
251 drm_panel_detach(imxpd
->panel
);
256 static const struct component_ops imx_pd_ops
= {
258 .unbind
= imx_pd_unbind
,
261 static int imx_pd_probe(struct platform_device
*pdev
)
263 return component_add(&pdev
->dev
, &imx_pd_ops
);
266 static int imx_pd_remove(struct platform_device
*pdev
)
268 component_del(&pdev
->dev
, &imx_pd_ops
);
273 static const struct of_device_id imx_pd_dt_ids
[] = {
274 { .compatible
= "fsl,imx-parallel-display", },
277 MODULE_DEVICE_TABLE(of
, imx_pd_dt_ids
);
279 static struct platform_driver imx_pd_driver
= {
280 .probe
= imx_pd_probe
,
281 .remove
= imx_pd_remove
,
283 .of_match_table
= imx_pd_dt_ids
,
284 .name
= "imx-parallel-display",
288 module_platform_driver(imx_pd_driver
);
290 MODULE_DESCRIPTION("i.MX parallel display driver");
291 MODULE_AUTHOR("Sascha Hauer, Pengutronix");
292 MODULE_LICENSE("GPL");
293 MODULE_ALIAS("platform:imx-parallel-display");