]> Git Repo - linux.git/blob - drivers/gpu/drm/tegra/output.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / drivers / gpu / drm / tegra / output.c
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         struct edid *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->edid)
38                 edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
39         else if (output->ddc)
40                 edid = drm_get_edid(connector, output->ddc);
41
42         cec_notifier_set_phys_addr_from_edid(output->cec, edid);
43         drm_connector_update_edid_property(connector, edid);
44
45         if (edid) {
46                 err = drm_add_edid_modes(connector, edid);
47                 kfree(edid);
48         }
49
50         return err;
51 }
52
53 enum drm_connector_status
54 tegra_output_connector_detect(struct drm_connector *connector, bool force)
55 {
56         struct tegra_output *output = connector_to_output(connector);
57         enum drm_connector_status status = connector_status_unknown;
58
59         if (output->hpd_gpio) {
60                 if (gpiod_get_value(output->hpd_gpio) == 0)
61                         status = connector_status_disconnected;
62                 else
63                         status = connector_status_connected;
64         } else {
65                 if (!output->panel)
66                         status = connector_status_disconnected;
67                 else
68                         status = connector_status_connected;
69         }
70
71         if (status != connector_status_connected)
72                 cec_notifier_phys_addr_invalidate(output->cec);
73
74         return status;
75 }
76
77 void tegra_output_connector_destroy(struct drm_connector *connector)
78 {
79         struct tegra_output *output = connector_to_output(connector);
80
81         if (output->cec)
82                 cec_notifier_conn_unregister(output->cec);
83
84         drm_connector_unregister(connector);
85         drm_connector_cleanup(connector);
86 }
87
88 static irqreturn_t hpd_irq(int irq, void *data)
89 {
90         struct tegra_output *output = data;
91
92         if (output->connector.dev)
93                 drm_helper_hpd_irq_event(output->connector.dev);
94
95         return IRQ_HANDLED;
96 }
97
98 int tegra_output_probe(struct tegra_output *output)
99 {
100         struct device_node *ddc, *panel;
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                 output->panel = of_drm_find_panel(panel);
121                 of_node_put(panel);
122
123                 if (IS_ERR(output->panel))
124                         return PTR_ERR(output->panel);
125         }
126
127         output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
128
129         ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
130         if (ddc) {
131                 output->ddc = of_get_i2c_adapter_by_node(ddc);
132                 of_node_put(ddc);
133
134                 if (!output->ddc) {
135                         err = -EPROBE_DEFER;
136                         return err;
137                 }
138         }
139
140         output->hpd_gpio = devm_fwnode_gpiod_get(output->dev,
141                                         of_fwnode_handle(output->of_node),
142                                         "nvidia,hpd",
143                                         GPIOD_IN,
144                                         "HDMI hotplug detect");
145         if (IS_ERR(output->hpd_gpio)) {
146                 if (PTR_ERR(output->hpd_gpio) != -ENOENT) {
147                         err = PTR_ERR(output->hpd_gpio);
148                         goto put_i2c;
149                 }
150
151                 output->hpd_gpio = NULL;
152         }
153
154         if (output->hpd_gpio) {
155                 err = gpiod_to_irq(output->hpd_gpio);
156                 if (err < 0) {
157                         dev_err(output->dev, "gpiod_to_irq(): %d\n", err);
158                         goto put_i2c;
159                 }
160
161                 output->hpd_irq = err;
162
163                 flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
164                         IRQF_ONESHOT;
165
166                 err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
167                                            flags, "hpd", output);
168                 if (err < 0) {
169                         dev_err(output->dev, "failed to request IRQ#%u: %d\n",
170                                 output->hpd_irq, err);
171                         goto put_i2c;
172                 }
173
174                 output->connector.polled = DRM_CONNECTOR_POLL_HPD;
175
176                 /*
177                  * Disable the interrupt until the connector has been
178                  * initialized to avoid a race in the hotplug interrupt
179                  * handler.
180                  */
181                 disable_irq(output->hpd_irq);
182         }
183
184         return 0;
185
186 put_i2c:
187         if (output->ddc)
188                 i2c_put_adapter(output->ddc);
189
190         return err;
191 }
192
193 void tegra_output_remove(struct tegra_output *output)
194 {
195         if (output->hpd_gpio)
196                 free_irq(output->hpd_irq, output);
197
198         if (output->ddc)
199                 i2c_put_adapter(output->ddc);
200 }
201
202 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
203 {
204         int connector_type;
205
206         /*
207          * The connector is now registered and ready to receive hotplug events
208          * so the hotplug interrupt can be enabled.
209          */
210         if (output->hpd_gpio)
211                 enable_irq(output->hpd_irq);
212
213         connector_type = output->connector.connector_type;
214         /*
215          * Create a CEC notifier for HDMI connector.
216          */
217         if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
218             connector_type == DRM_MODE_CONNECTOR_HDMIB) {
219                 struct cec_connector_info conn_info;
220
221                 cec_fill_conn_info_from_drm(&conn_info, &output->connector);
222                 output->cec = cec_notifier_conn_register(output->dev, NULL,
223                                                          &conn_info);
224                 if (!output->cec)
225                         return -ENOMEM;
226         }
227
228         return 0;
229 }
230
231 void tegra_output_exit(struct tegra_output *output)
232 {
233         /*
234          * The connector is going away, so the interrupt must be disabled to
235          * prevent the hotplug interrupt handler from potentially crashing.
236          */
237         if (output->hpd_gpio)
238                 disable_irq(output->hpd_irq);
239 }
240
241 void tegra_output_find_possible_crtcs(struct tegra_output *output,
242                                       struct drm_device *drm)
243 {
244         struct device *dev = output->dev;
245         struct drm_crtc *crtc;
246         unsigned int mask = 0;
247
248         drm_for_each_crtc(crtc, drm) {
249                 struct tegra_dc *dc = to_tegra_dc(crtc);
250
251                 if (tegra_dc_has_output(dc, dev))
252                         mask |= drm_crtc_mask(crtc);
253         }
254
255         if (mask == 0) {
256                 dev_warn(dev, "missing output definition for heads in DT\n");
257                 mask = 0x3;
258         }
259
260         output->encoder.possible_crtcs = mask;
261 }
262
263 int tegra_output_suspend(struct tegra_output *output)
264 {
265         if (output->hpd_irq)
266                 disable_irq(output->hpd_irq);
267
268         return 0;
269 }
270
271 int tegra_output_resume(struct tegra_output *output)
272 {
273         if (output->hpd_irq)
274                 enable_irq(output->hpd_irq);
275
276         return 0;
277 }
This page took 0.071363 seconds and 4 git commands to generate.