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