xref: /linux/drivers/gpu/drm/tegra/output.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 Avionic Design GmbH
4  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
5  */
6 
7 #include <linux/i2c.h>
8 #include <linux/of.h>
9 
10 #include <drm/drm_atomic_helper.h>
11 #include <drm/drm_edid.h>
12 #include <drm/drm_of.h>
13 #include <drm/drm_panel.h>
14 #include <drm/drm_simple_kms_helper.h>
15 
16 #include "drm.h"
17 #include "dc.h"
18 
19 #include <media/cec-notifier.h>
20 
21 int tegra_output_connector_get_modes(struct drm_connector *connector)
22 {
23 	struct tegra_output *output = connector_to_output(connector);
24 	const struct drm_edid *drm_edid = NULL;
25 	int err = 0;
26 
27 	/*
28 	 * If the panel provides one or more modes, use them exclusively and
29 	 * ignore any other means of obtaining a mode.
30 	 */
31 	if (output->panel) {
32 		err = drm_panel_get_modes(output->panel, connector);
33 		if (err > 0)
34 			return err;
35 	}
36 
37 	if (output->drm_edid)
38 		drm_edid = drm_edid_dup(output->drm_edid);
39 	else if (output->ddc)
40 		drm_edid = drm_edid_read_ddc(connector, output->ddc);
41 
42 	drm_edid_connector_update(connector, drm_edid);
43 	cec_notifier_set_phys_addr(output->cec,
44 				   connector->display_info.source_physical_address);
45 
46 	err = drm_edid_connector_add_modes(connector);
47 	drm_edid_free(drm_edid);
48 
49 	return err;
50 }
51 
52 enum drm_connector_status
53 tegra_output_connector_detect(struct drm_connector *connector, bool force)
54 {
55 	struct tegra_output *output = connector_to_output(connector);
56 	enum drm_connector_status status = connector_status_unknown;
57 
58 	if (output->hpd_gpio) {
59 		if (gpiod_get_value(output->hpd_gpio) == 0)
60 			status = connector_status_disconnected;
61 		else
62 			status = connector_status_connected;
63 	} else {
64 		if (!output->panel)
65 			status = connector_status_disconnected;
66 		else
67 			status = connector_status_connected;
68 	}
69 
70 	if (status != connector_status_connected)
71 		cec_notifier_phys_addr_invalidate(output->cec);
72 
73 	return status;
74 }
75 
76 void tegra_output_connector_destroy(struct drm_connector *connector)
77 {
78 	struct tegra_output *output = connector_to_output(connector);
79 
80 	if (output->cec)
81 		cec_notifier_conn_unregister(output->cec);
82 
83 	drm_connector_unregister(connector);
84 	drm_connector_cleanup(connector);
85 }
86 
87 static irqreturn_t hpd_irq(int irq, void *data)
88 {
89 	struct tegra_output *output = data;
90 
91 	if (output->connector.dev)
92 		drm_helper_hpd_irq_event(output->connector.dev);
93 
94 	return IRQ_HANDLED;
95 }
96 
97 int tegra_output_probe(struct tegra_output *output)
98 {
99 	struct device_node *ddc, *panel;
100 	const void *edid;
101 	unsigned long flags;
102 	int err, size;
103 
104 	if (!output->of_node)
105 		output->of_node = output->dev->of_node;
106 
107 	err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
108 					  &output->panel, &output->bridge);
109 	if (err && err != -ENODEV)
110 		return err;
111 
112 	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
113 	if (panel) {
114 		/*
115 		 * Don't mix nvidia,panel phandle with the graph in a
116 		 * device-tree.
117 		 */
118 		WARN_ON(output->panel || output->bridge);
119 
120 		if (output->panel) {
121 			drm_panel_put(output->panel);
122 			output->panel = NULL;
123 		}
124 
125 		output->panel = of_drm_find_panel(panel);
126 		of_node_put(panel);
127 
128 		if (IS_ERR(output->panel)) {
129 			err = PTR_ERR(output->panel);
130 			output->panel = NULL;
131 			return err;
132 		}
133 	}
134 
135 	ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
136 	if (ddc) {
137 		output->ddc = of_get_i2c_adapter_by_node(ddc);
138 		of_node_put(ddc);
139 
140 		if (!output->ddc) {
141 			err = -EPROBE_DEFER;
142 			goto put_i2c;
143 		}
144 	}
145 
146 	edid = of_get_property(output->of_node, "nvidia,edid", &size);
147 	output->drm_edid = drm_edid_alloc(edid, size);
148 
149 	output->hpd_gpio = devm_fwnode_gpiod_get(output->dev,
150 					of_fwnode_handle(output->of_node),
151 					"nvidia,hpd",
152 					GPIOD_IN,
153 					"HDMI hotplug detect");
154 	if (IS_ERR(output->hpd_gpio)) {
155 		if (PTR_ERR(output->hpd_gpio) != -ENOENT) {
156 			err = PTR_ERR(output->hpd_gpio);
157 			goto put_i2c;
158 		}
159 
160 		output->hpd_gpio = NULL;
161 	}
162 
163 	if (output->hpd_gpio) {
164 		err = gpiod_to_irq(output->hpd_gpio);
165 		if (err < 0) {
166 			dev_err(output->dev, "gpiod_to_irq(): %d\n", err);
167 			goto put_i2c;
168 		}
169 
170 		output->hpd_irq = err;
171 
172 		flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
173 			IRQF_ONESHOT;
174 
175 		err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
176 					   flags, "hpd", output);
177 		if (err < 0) {
178 			dev_err(output->dev, "failed to request IRQ#%u: %d\n",
179 				output->hpd_irq, err);
180 			goto put_i2c;
181 		}
182 
183 		output->connector.polled = DRM_CONNECTOR_POLL_HPD;
184 
185 		/*
186 		 * Disable the interrupt until the connector has been
187 		 * initialized to avoid a race in the hotplug interrupt
188 		 * handler.
189 		 */
190 		disable_irq(output->hpd_irq);
191 	}
192 
193 	return 0;
194 
195 put_i2c:
196 	if (output->panel) {
197 		drm_panel_put(output->panel);
198 		output->panel = NULL;
199 	}
200 
201 	if (output->ddc)
202 		i2c_put_adapter(output->ddc);
203 
204 	drm_edid_free(output->drm_edid);
205 
206 	return err;
207 }
208 
209 void tegra_output_remove(struct tegra_output *output)
210 {
211 	if (output->panel) {
212 		drm_panel_put(output->panel);
213 		output->panel = NULL;
214 	}
215 
216 	if (output->hpd_gpio)
217 		free_irq(output->hpd_irq, output);
218 
219 	if (output->ddc)
220 		i2c_put_adapter(output->ddc);
221 
222 	drm_edid_free(output->drm_edid);
223 }
224 
225 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
226 {
227 	int connector_type;
228 
229 	/*
230 	 * The connector is now registered and ready to receive hotplug events
231 	 * so the hotplug interrupt can be enabled.
232 	 */
233 	if (output->hpd_gpio)
234 		enable_irq(output->hpd_irq);
235 
236 	connector_type = output->connector.connector_type;
237 	/*
238 	 * Create a CEC notifier for HDMI connector.
239 	 */
240 	if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
241 	    connector_type == DRM_MODE_CONNECTOR_HDMIB) {
242 		struct cec_connector_info conn_info;
243 
244 		cec_fill_conn_info_from_drm(&conn_info, &output->connector);
245 		output->cec = cec_notifier_conn_register(output->dev, NULL,
246 							 &conn_info);
247 		if (!output->cec)
248 			return -ENOMEM;
249 	}
250 
251 	return 0;
252 }
253 
254 void tegra_output_exit(struct tegra_output *output)
255 {
256 	/*
257 	 * The connector is going away, so the interrupt must be disabled to
258 	 * prevent the hotplug interrupt handler from potentially crashing.
259 	 */
260 	if (output->hpd_gpio)
261 		disable_irq(output->hpd_irq);
262 }
263 
264 void tegra_output_find_possible_crtcs(struct tegra_output *output,
265 				      struct drm_device *drm)
266 {
267 	struct device *dev = output->dev;
268 	struct drm_crtc *crtc;
269 	unsigned int mask = 0;
270 
271 	drm_for_each_crtc(crtc, drm) {
272 		struct tegra_dc *dc = to_tegra_dc(crtc);
273 
274 		if (tegra_dc_has_output(dc, dev))
275 			mask |= drm_crtc_mask(crtc);
276 	}
277 
278 	if (mask == 0) {
279 		dev_warn(dev, "missing output definition for heads in DT\n");
280 		mask = 0x3;
281 	}
282 
283 	output->encoder.possible_crtcs = mask;
284 }
285 
286 int tegra_output_suspend(struct tegra_output *output)
287 {
288 	if (output->hpd_irq)
289 		disable_irq(output->hpd_irq);
290 
291 	return 0;
292 }
293 
294 int tegra_output_resume(struct tegra_output *output)
295 {
296 	if (output->hpd_irq)
297 		enable_irq(output->hpd_irq);
298 
299 	return 0;
300 }
301