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_of.h>
15 #include "tilcdc_drv.h"
16 #include "tilcdc_external.h"
18 static const struct tilcdc_panel_info panel_info_tda998x
= {
31 static const struct tilcdc_panel_info panel_info_default
= {
43 static int tilcdc_external_mode_valid(struct drm_connector
*connector
,
44 struct drm_display_mode
*mode
)
46 struct tilcdc_drm_private
*priv
= connector
->dev
->dev_private
;
49 ret
= tilcdc_crtc_mode_valid(priv
->crtc
, mode
);
53 BUG_ON(priv
->external_connector
!= connector
);
54 BUG_ON(!priv
->connector_funcs
);
56 /* If the connector has its own mode_valid call it. */
57 if (!IS_ERR(priv
->connector_funcs
) &&
58 priv
->connector_funcs
->mode_valid
)
59 return priv
->connector_funcs
->mode_valid(connector
, mode
);
64 static int tilcdc_add_external_connector(struct drm_device
*dev
,
65 struct drm_connector
*connector
)
67 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
68 struct drm_connector_helper_funcs
*connector_funcs
;
70 /* There should never be more than one connector */
71 if (WARN_ON(priv
->external_connector
))
74 priv
->external_connector
= connector
;
75 connector_funcs
= devm_kzalloc(dev
->dev
, sizeof(*connector_funcs
),
80 /* connector->helper_private contains always struct
81 * connector_helper_funcs pointer. For tilcdc crtc to have a
82 * say if a specific mode is Ok, we need to install our own
83 * helper functions. In our helper functions we copy
84 * everything else but use our own mode_valid() (above).
86 if (connector
->helper_private
) {
87 priv
->connector_funcs
= connector
->helper_private
;
88 *connector_funcs
= *priv
->connector_funcs
;
90 priv
->connector_funcs
= ERR_PTR(-ENOENT
);
92 connector_funcs
->mode_valid
= tilcdc_external_mode_valid
;
93 drm_connector_helper_add(connector
, connector_funcs
);
95 dev_dbg(dev
->dev
, "External connector '%s' connected\n",
102 struct drm_connector
*tilcdc_encoder_find_connector(struct drm_device
*ddev
,
103 struct drm_encoder
*encoder
)
105 struct drm_connector
*connector
;
107 list_for_each_entry(connector
, &ddev
->mode_config
.connector_list
, head
) {
108 if (drm_connector_has_possible_encoder(connector
, encoder
))
112 dev_err(ddev
->dev
, "No connector found for %s encoder (id %d)\n",
113 encoder
->name
, encoder
->base
.id
);
118 int tilcdc_add_component_encoder(struct drm_device
*ddev
)
120 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
121 struct drm_connector
*connector
;
122 struct drm_encoder
*encoder
;
124 list_for_each_entry(encoder
, &ddev
->mode_config
.encoder_list
, head
)
125 if (encoder
->possible_crtcs
& (1 << priv
->crtc
->index
))
129 dev_err(ddev
->dev
, "%s: No suitable encoder found\n", __func__
);
133 connector
= tilcdc_encoder_find_connector(ddev
, encoder
);
138 /* Only tda998x is supported at the moment. */
139 tilcdc_crtc_set_simulate_vesa_sync(priv
->crtc
, true);
140 tilcdc_crtc_set_panel_info(priv
->crtc
, &panel_info_tda998x
);
142 return tilcdc_add_external_connector(ddev
, connector
);
145 void tilcdc_remove_external_device(struct drm_device
*dev
)
147 struct tilcdc_drm_private
*priv
= dev
->dev_private
;
149 /* Restore the original helper functions, if any. */
150 if (IS_ERR(priv
->connector_funcs
))
151 drm_connector_helper_add(priv
->external_connector
, NULL
);
152 else if (priv
->connector_funcs
)
153 drm_connector_helper_add(priv
->external_connector
,
154 priv
->connector_funcs
);
157 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs
= {
158 .destroy
= drm_encoder_cleanup
,
162 int tilcdc_attach_bridge(struct drm_device
*ddev
, struct drm_bridge
*bridge
)
164 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
165 struct drm_connector
*connector
;
168 priv
->external_encoder
->possible_crtcs
= BIT(0);
170 ret
= drm_bridge_attach(priv
->external_encoder
, bridge
, NULL
);
172 dev_err(ddev
->dev
, "drm_bridge_attach() failed %d\n", ret
);
176 tilcdc_crtc_set_panel_info(priv
->crtc
, &panel_info_default
);
178 connector
= tilcdc_encoder_find_connector(ddev
, priv
->external_encoder
);
182 ret
= tilcdc_add_external_connector(ddev
, connector
);
187 int tilcdc_attach_external_device(struct drm_device
*ddev
)
189 struct tilcdc_drm_private
*priv
= ddev
->dev_private
;
190 struct drm_bridge
*bridge
;
191 struct drm_panel
*panel
;
194 ret
= drm_of_find_panel_or_bridge(ddev
->dev
->of_node
, 0, 0,
201 priv
->external_encoder
= devm_kzalloc(ddev
->dev
,
202 sizeof(*priv
->external_encoder
),
204 if (!priv
->external_encoder
)
207 ret
= drm_encoder_init(ddev
, priv
->external_encoder
,
208 &tilcdc_external_encoder_funcs
,
209 DRM_MODE_ENCODER_NONE
, NULL
);
211 dev_err(ddev
->dev
, "drm_encoder_init() failed %d\n", ret
);
216 bridge
= devm_drm_panel_bridge_add(ddev
->dev
, panel
,
217 DRM_MODE_CONNECTOR_DPI
);
218 if (IS_ERR(bridge
)) {
219 ret
= PTR_ERR(bridge
);
220 goto err_encoder_cleanup
;
224 ret
= tilcdc_attach_bridge(ddev
, bridge
);
226 goto err_encoder_cleanup
;
231 drm_encoder_cleanup(priv
->external_encoder
);
235 static int dev_match_of(struct device
*dev
, void *data
)
237 return dev
->of_node
== data
;
240 int tilcdc_get_external_components(struct device
*dev
,
241 struct component_match
**match
)
243 struct device_node
*node
;
245 node
= of_graph_get_remote_node(dev
->of_node
, 0, 0);
247 if (!of_device_is_compatible(node
, "nxp,tda998x")) {
253 drm_of_component_match_add(dev
, match
, dev_match_of
, node
);