2 * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_connector.h>
13 #include <drm/drm_crtc_helper.h>
14 #include <drm/drm_encoder.h>
15 #include <drm/drm_modeset_helper_vtables.h>
16 #include <drm/drm_panel.h>
18 #include <linux/of_graph.h>
23 struct drm_bridge bridge
;
24 struct drm_connector connector
;
25 struct drm_panel
*panel
;
28 static inline struct lvds_encoder
*
29 drm_bridge_to_lvds_encoder(struct drm_bridge
*bridge
)
31 return container_of(bridge
, struct lvds_encoder
, bridge
);
34 static inline struct lvds_encoder
*
35 drm_connector_to_lvds_encoder(struct drm_connector
*connector
)
37 return container_of(connector
, struct lvds_encoder
, connector
);
40 static int lvds_connector_get_modes(struct drm_connector
*connector
)
42 struct lvds_encoder
*lvds
= drm_connector_to_lvds_encoder(connector
);
44 return drm_panel_get_modes(lvds
->panel
);
47 static const struct drm_connector_helper_funcs lvds_connector_helper_funcs
= {
48 .get_modes
= lvds_connector_get_modes
,
51 static const struct drm_connector_funcs lvds_connector_funcs
= {
52 .dpms
= drm_atomic_helper_connector_dpms
,
53 .reset
= drm_atomic_helper_connector_reset
,
54 .fill_modes
= drm_helper_probe_single_connector_modes
,
55 .destroy
= drm_connector_cleanup
,
56 .atomic_duplicate_state
= drm_atomic_helper_connector_duplicate_state
,
57 .atomic_destroy_state
= drm_atomic_helper_connector_destroy_state
,
60 static int lvds_encoder_attach(struct drm_bridge
*bridge
)
62 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
63 struct drm_connector
*connector
= &lvds
->connector
;
66 if (!bridge
->encoder
) {
67 DRM_ERROR("Missing encoder\n");
71 drm_connector_helper_add(connector
, &lvds_connector_helper_funcs
);
73 ret
= drm_connector_init(bridge
->dev
, connector
, &lvds_connector_funcs
,
74 DRM_MODE_CONNECTOR_LVDS
);
76 DRM_ERROR("Failed to initialize connector\n");
80 drm_mode_connector_attach_encoder(&lvds
->connector
, bridge
->encoder
);
82 ret
= drm_panel_attach(lvds
->panel
, &lvds
->connector
);
89 static void lvds_encoder_detach(struct drm_bridge
*bridge
)
91 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
93 drm_panel_detach(lvds
->panel
);
96 static void lvds_encoder_pre_enable(struct drm_bridge
*bridge
)
98 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
100 drm_panel_prepare(lvds
->panel
);
103 static void lvds_encoder_enable(struct drm_bridge
*bridge
)
105 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
107 drm_panel_enable(lvds
->panel
);
110 static void lvds_encoder_disable(struct drm_bridge
*bridge
)
112 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
114 drm_panel_disable(lvds
->panel
);
117 static void lvds_encoder_post_disable(struct drm_bridge
*bridge
)
119 struct lvds_encoder
*lvds
= drm_bridge_to_lvds_encoder(bridge
);
121 drm_panel_unprepare(lvds
->panel
);
124 static const struct drm_bridge_funcs lvds_encoder_bridge_funcs
= {
125 .attach
= lvds_encoder_attach
,
126 .detach
= lvds_encoder_detach
,
127 .pre_enable
= lvds_encoder_pre_enable
,
128 .enable
= lvds_encoder_enable
,
129 .disable
= lvds_encoder_disable
,
130 .post_disable
= lvds_encoder_post_disable
,
133 static int lvds_encoder_probe(struct platform_device
*pdev
)
135 struct lvds_encoder
*lvds
;
136 struct device_node
*port
;
137 struct device_node
*endpoint
;
138 struct device_node
*panel
;
140 lvds
= devm_kzalloc(&pdev
->dev
, sizeof(*lvds
), GFP_KERNEL
);
144 lvds
->dev
= &pdev
->dev
;
145 platform_set_drvdata(pdev
, lvds
);
147 lvds
->bridge
.funcs
= &lvds_encoder_bridge_funcs
;
148 lvds
->bridge
.of_node
= pdev
->dev
.of_node
;
150 /* Locate the panel DT node. */
151 port
= of_graph_get_port_by_id(pdev
->dev
.of_node
, 1);
153 dev_dbg(&pdev
->dev
, "port 1 not found\n");
157 endpoint
= of_get_child_by_name(port
, "endpoint");
160 dev_dbg(&pdev
->dev
, "no endpoint for port 1\n");
164 panel
= of_graph_get_remote_port_parent(endpoint
);
165 of_node_put(endpoint
);
167 dev_dbg(&pdev
->dev
, "no remote endpoint for port 1\n");
171 lvds
->panel
= of_drm_find_panel(panel
);
174 dev_dbg(&pdev
->dev
, "panel not found, deferring probe\n");
175 return -EPROBE_DEFER
;
178 /* Register the bridge. */
179 return drm_bridge_add(&lvds
->bridge
);
182 static int lvds_encoder_remove(struct platform_device
*pdev
)
184 struct lvds_encoder
*encoder
= platform_get_drvdata(pdev
);
186 drm_bridge_remove(&encoder
->bridge
);
191 static const struct of_device_id lvds_encoder_match
[] = {
192 { .compatible
= "lvds-encoder" },
193 { .compatible
= "thine,thc63lvdm83d" },
196 MODULE_DEVICE_TABLE(of
, lvds_encoder_match
);
198 static struct platform_driver lvds_encoder_driver
= {
199 .probe
= lvds_encoder_probe
,
200 .remove
= lvds_encoder_remove
,
202 .name
= "lvds-encoder",
203 .of_match_table
= lvds_encoder_match
,
206 module_platform_driver(lvds_encoder_driver
);
208 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
209 MODULE_DESCRIPTION("Transparent parallel to LVDS encoder");
210 MODULE_LICENSE("GPL");