2 * Copyright (C) 2015 Texas Instruments
3 * Author: Jyri Sarha <jsarha@ti.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
11 #include <linux/component.h>
12 #include <linux/of_graph.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_of.h>
16 #include "tilcdc_drv.h"
17 #include "tilcdc_external.h"
19 static const struct tilcdc_panel_info panel_info_tda998x
= {
32 static const struct tilcdc_panel_info panel_info_default
= {
44 static int tilcdc_external_mode_valid(struct drm_connector
*connector
,
45 struct drm_display_mode
*mode
)
47 struct tilcdc_drm_private
*priv
= connector
->dev
->dev_private
;
50 ret
= tilcdc_crtc_mode_valid(priv
->crtc
, mode
);
54 BUG_ON(priv
->external_connector
!= connector
);
55 BUG_ON(!priv
->connector_funcs
);
57 /* If the connector has its own mode_valid call it. */
58 if (!IS_ERR(priv
->connector_funcs
) &&
59 priv
->connector_funcs
->mode_valid
)
60 return priv
->connector_funcs
->mode_valid(connector
, mode
);
65 static int tilcdc_add_external_connector(struct drm_device
*dev
,
66 struct drm_connector
*connector
)
68 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
69 struct drm_connector_helper_funcs
*connector_funcs
;
71 /* There should never be more than one connector */
72 if (WARN_ON(priv
->external_connector
))
75 priv
->external_connector
= connector
;
76 connector_funcs
= devm_kzalloc(dev
->dev
, sizeof(*connector_funcs
),
81 /* connector->helper_private contains always struct
82 * connector_helper_funcs pointer. For tilcdc crtc to have a
83 * say if a specific mode is Ok, we need to install our own
84 * helper functions. In our helper functions we copy
85 * everything else but use our own mode_valid() (above).
87 if (connector
->helper_private
) {
88 priv
->connector_funcs
= connector
->helper_private
;
89 *connector_funcs
= *priv
->connector_funcs
;
91 priv
->connector_funcs
= ERR_PTR(-ENOENT
);
93 connector_funcs
->mode_valid
= tilcdc_external_mode_valid
;
94 drm_connector_helper_add(connector
, connector_funcs
);
96 dev_dbg(dev
->dev
, "External connector '%s' connected\n",
103 struct drm_connector
*tilcdc_encoder_find_connector(struct drm_device
*ddev
,
104 struct drm_encoder
*encoder
)
106 struct drm_connector
*connector
;
108 list_for_each_entry(connector
, &ddev
->mode_config
.connector_list
, head
) {
109 if (drm_connector_has_possible_encoder(connector
, encoder
))
113 dev_err(ddev
->dev
, "No connector found for %s encoder (id %d)\n",
114 encoder
->name
, encoder
->base
.id
);
119 int tilcdc_add_component_encoder(struct drm_device
*ddev
)
121 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
122 struct drm_connector
*connector
;
123 struct drm_encoder
*encoder
;
125 list_for_each_entry(encoder
, &ddev
->mode_config
.encoder_list
, head
)
126 if (encoder
->possible_crtcs
& (1 << priv
->crtc
->index
))
130 dev_err(ddev
->dev
, "%s: No suitable encoder found\n", __func__
);
134 connector
= tilcdc_encoder_find_connector(ddev
, encoder
);
139 /* Only tda998x is supported at the moment. */
140 tilcdc_crtc_set_simulate_vesa_sync(priv
->crtc
, true);
141 tilcdc_crtc_set_panel_info(priv
->crtc
, &panel_info_tda998x
);
143 return tilcdc_add_external_connector(ddev
, connector
);
146 void tilcdc_remove_external_device(struct drm_device
*dev
)
148 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
150 /* Restore the original helper functions, if any. */
151 if (IS_ERR(priv
->connector_funcs
))
152 drm_connector_helper_add(priv
->external_connector
, NULL
);
153 else if (priv
->connector_funcs
)
154 drm_connector_helper_add(priv
->external_connector
,
155 priv
->connector_funcs
);
158 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs
= {
159 .destroy
= drm_encoder_cleanup
,
163 int tilcdc_attach_bridge(struct drm_device
*ddev
, struct drm_bridge
*bridge
)
165 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
166 struct drm_connector
*connector
;
169 priv
->external_encoder
->possible_crtcs
= BIT(0);
171 ret
= drm_bridge_attach(priv
->external_encoder
, bridge
, NULL
);
173 dev_err(ddev
->dev
, "drm_bridge_attach() failed %d\n", ret
);
177 tilcdc_crtc_set_panel_info(priv
->crtc
, &panel_info_default
);
179 connector
= tilcdc_encoder_find_connector(ddev
, priv
->external_encoder
);
183 ret
= tilcdc_add_external_connector(ddev
, connector
);
188 int tilcdc_attach_external_device(struct drm_device
*ddev
)
190 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
191 struct drm_bridge
*bridge
;
192 struct drm_panel
*panel
;
195 ret
= drm_of_find_panel_or_bridge(ddev
->dev
->of_node
, 0, 0,
202 priv
->external_encoder
= devm_kzalloc(ddev
->dev
,
203 sizeof(*priv
->external_encoder
),
205 if (!priv
->external_encoder
)
208 ret
= drm_encoder_init(ddev
, priv
->external_encoder
,
209 &tilcdc_external_encoder_funcs
,
210 DRM_MODE_ENCODER_NONE
, NULL
);
212 dev_err(ddev
->dev
, "drm_encoder_init() failed %d\n", ret
);
217 bridge
= devm_drm_panel_bridge_add(ddev
->dev
, panel
,
218 DRM_MODE_CONNECTOR_DPI
);
219 if (IS_ERR(bridge
)) {
220 ret
= PTR_ERR(bridge
);
221 goto err_encoder_cleanup
;
225 ret
= tilcdc_attach_bridge(ddev
, bridge
);
227 goto err_encoder_cleanup
;
232 drm_encoder_cleanup(priv
->external_encoder
);
236 static int dev_match_of(struct device
*dev
, void *data
)
238 return dev
->of_node
== data
;
241 int tilcdc_get_external_components(struct device
*dev
,
242 struct component_match
**match
)
244 struct device_node
*node
;
246 node
= of_graph_get_remote_node(dev
->of_node
, 0, 0);
248 if (!of_device_is_compatible(node
, "nxp,tda998x")) {
254 drm_of_component_match_add(dev
, match
, dev_match_of
, node
);