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
;
108 list_for_each_entry(connector
, &ddev
->mode_config
.connector_list
, head
)
109 for (i
= 0; i
< DRM_CONNECTOR_MAX_ENCODER
; i
++)
110 if (connector
->encoder_ids
[i
] == encoder
->base
.id
)
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 device_node
*remote_node
;
192 struct drm_bridge
*bridge
;
195 remote_node
= of_graph_get_remote_node(ddev
->dev
->of_node
, 0, 0);
199 bridge
= of_drm_find_bridge(remote_node
);
200 of_node_put(remote_node
);
202 return -EPROBE_DEFER
;
204 priv
->external_encoder
= devm_kzalloc(ddev
->dev
,
205 sizeof(*priv
->external_encoder
),
207 if (!priv
->external_encoder
)
210 ret
= drm_encoder_init(ddev
, priv
->external_encoder
,
211 &tilcdc_external_encoder_funcs
,
212 DRM_MODE_ENCODER_NONE
, NULL
);
214 dev_err(ddev
->dev
, "drm_encoder_init() failed %d\n", ret
);
218 ret
= tilcdc_attach_bridge(ddev
, bridge
);
220 drm_encoder_cleanup(priv
->external_encoder
);
225 static int dev_match_of(struct device
*dev
, void *data
)
227 return dev
->of_node
== data
;
230 int tilcdc_get_external_components(struct device
*dev
,
231 struct component_match
**match
)
233 struct device_node
*node
;
235 node
= of_graph_get_remote_node(dev
->of_node
, 0, 0);
237 if (!of_device_is_compatible(node
, "nxp,tda998x")) {
243 drm_of_component_match_add(dev
, match
, dev_match_of
, node
);