1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2015 Texas Instruments
4  * Author: Jyri Sarha <jsarha@ti.com>
5  */
6 
7 #include <linux/component.h>
8 #include <linux/of_graph.h>
9 
10 #include <drm/drm_atomic_helper.h>
11 #include <drm/drm_of.h>
12 
13 #include "tilcdc_drv.h"
14 #include "tilcdc_external.h"
15 
16 static const struct tilcdc_panel_info panel_info_tda998x = {
17 		.ac_bias                = 255,
18 		.ac_bias_intrpt         = 0,
19 		.dma_burst_sz           = 16,
20 		.bpp                    = 16,
21 		.fdd                    = 0x80,
22 		.tft_alt_mode           = 0,
23 		.invert_pxl_clk		= 1,
24 		.sync_edge              = 1,
25 		.sync_ctrl              = 1,
26 		.raster_order           = 0,
27 };
28 
29 static const struct tilcdc_panel_info panel_info_default = {
30 		.ac_bias                = 255,
31 		.ac_bias_intrpt         = 0,
32 		.dma_burst_sz           = 16,
33 		.bpp                    = 16,
34 		.fdd                    = 0x80,
35 		.tft_alt_mode           = 0,
36 		.sync_edge              = 0,
37 		.sync_ctrl              = 1,
38 		.raster_order           = 0,
39 };
40 
41 static
42 struct drm_connector *tilcdc_encoder_find_connector(struct drm_device *ddev,
43 						    struct drm_encoder *encoder)
44 {
45 	struct drm_connector *connector;
46 
47 	list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
48 		if (drm_connector_has_possible_encoder(connector, encoder))
49 			return connector;
50 	}
51 
52 	dev_err(ddev->dev, "No connector found for %s encoder (id %d)\n",
53 		encoder->name, encoder->base.id);
54 
55 	return NULL;
56 }
57 
58 int tilcdc_add_component_encoder(struct drm_device *ddev)
59 {
60 	struct tilcdc_drm_private *priv = ddev->dev_private;
61 	struct drm_encoder *encoder;
62 
63 	list_for_each_entry(encoder, &ddev->mode_config.encoder_list, head)
64 		if (encoder->possible_crtcs & (1 << priv->crtc->index))
65 			break;
66 
67 	if (!encoder) {
68 		dev_err(ddev->dev, "%s: No suitable encoder found\n", __func__);
69 		return -ENODEV;
70 	}
71 
72 	priv->external_connector =
73 		tilcdc_encoder_find_connector(ddev, encoder);
74 
75 	if (!priv->external_connector)
76 		return -ENODEV;
77 
78 	/* Only tda998x is supported at the moment. */
79 	tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true);
80 	tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x);
81 
82 	return 0;
83 }
84 
85 static const struct drm_encoder_funcs tilcdc_external_encoder_funcs = {
86 	.destroy	= drm_encoder_cleanup,
87 };
88 
89 static
90 int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge)
91 {
92 	struct tilcdc_drm_private *priv = ddev->dev_private;
93 	int ret;
94 
95 	priv->external_encoder->possible_crtcs = BIT(0);
96 
97 	ret = drm_bridge_attach(priv->external_encoder, bridge, NULL);
98 	if (ret) {
99 		dev_err(ddev->dev, "drm_bridge_attach() failed %d\n", ret);
100 		return ret;
101 	}
102 
103 	tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_default);
104 
105 	priv->external_connector =
106 		tilcdc_encoder_find_connector(ddev, priv->external_encoder);
107 	if (!priv->external_connector)
108 		return -ENODEV;
109 
110 	return 0;
111 }
112 
113 int tilcdc_attach_external_device(struct drm_device *ddev)
114 {
115 	struct tilcdc_drm_private *priv = ddev->dev_private;
116 	struct drm_bridge *bridge;
117 	struct drm_panel *panel;
118 	int ret;
119 
120 	ret = drm_of_find_panel_or_bridge(ddev->dev->of_node, 0, 0,
121 					  &panel, &bridge);
122 	if (ret == -ENODEV)
123 		return 0;
124 	else if (ret)
125 		return ret;
126 
127 	priv->external_encoder = devm_kzalloc(ddev->dev,
128 					      sizeof(*priv->external_encoder),
129 					      GFP_KERNEL);
130 	if (!priv->external_encoder)
131 		return -ENOMEM;
132 
133 	ret = drm_encoder_init(ddev, priv->external_encoder,
134 			       &tilcdc_external_encoder_funcs,
135 			       DRM_MODE_ENCODER_NONE, NULL);
136 	if (ret) {
137 		dev_err(ddev->dev, "drm_encoder_init() failed %d\n", ret);
138 		return ret;
139 	}
140 
141 	if (panel) {
142 		bridge = devm_drm_panel_bridge_add(ddev->dev, panel,
143 						   DRM_MODE_CONNECTOR_DPI);
144 		if (IS_ERR(bridge)) {
145 			ret = PTR_ERR(bridge);
146 			goto err_encoder_cleanup;
147 		}
148 	}
149 
150 	ret = tilcdc_attach_bridge(ddev, bridge);
151 	if (ret)
152 		goto err_encoder_cleanup;
153 
154 	return 0;
155 
156 err_encoder_cleanup:
157 	drm_encoder_cleanup(priv->external_encoder);
158 	return ret;
159 }
160 
161 static int dev_match_of(struct device *dev, void *data)
162 {
163 	return dev->of_node == data;
164 }
165 
166 int tilcdc_get_external_components(struct device *dev,
167 				   struct component_match **match)
168 {
169 	struct device_node *node;
170 
171 	node = of_graph_get_remote_node(dev->of_node, 0, 0);
172 
173 	if (!of_device_is_compatible(node, "nxp,tda998x")) {
174 		of_node_put(node);
175 		return 0;
176 	}
177 
178 	if (match)
179 		drm_of_component_match_add(dev, match, dev_match_of, node);
180 	of_node_put(node);
181 	return 1;
182 }
183