1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2012 Avionic Design GmbH
4 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_of.h>
9 #include <drm/drm_panel.h>
10 #include <drm/drm_simple_kms_helper.h>
15 #include <media/cec-notifier.h>
17 int tegra_output_connector_get_modes(struct drm_connector
*connector
)
19 struct tegra_output
*output
= connector_to_output(connector
);
20 struct edid
*edid
= NULL
;
24 * If the panel provides one or more modes, use them exclusively and
25 * ignore any other means of obtaining a mode.
28 err
= drm_panel_get_modes(output
->panel
, connector
);
34 edid
= kmemdup(output
->edid
, sizeof(*edid
), GFP_KERNEL
);
36 edid
= drm_get_edid(connector
, output
->ddc
);
38 cec_notifier_set_phys_addr_from_edid(output
->cec
, edid
);
39 drm_connector_update_edid_property(connector
, edid
);
42 err
= drm_add_edid_modes(connector
, edid
);
49 enum drm_connector_status
50 tegra_output_connector_detect(struct drm_connector
*connector
, bool force
)
52 struct tegra_output
*output
= connector_to_output(connector
);
53 enum drm_connector_status status
= connector_status_unknown
;
55 if (output
->hpd_gpio
) {
56 if (gpiod_get_value(output
->hpd_gpio
) == 0)
57 status
= connector_status_disconnected
;
59 status
= connector_status_connected
;
62 status
= connector_status_disconnected
;
64 status
= connector_status_connected
;
67 if (status
!= connector_status_connected
)
68 cec_notifier_phys_addr_invalidate(output
->cec
);
73 void tegra_output_connector_destroy(struct drm_connector
*connector
)
75 struct tegra_output
*output
= connector_to_output(connector
);
78 cec_notifier_conn_unregister(output
->cec
);
80 drm_connector_unregister(connector
);
81 drm_connector_cleanup(connector
);
84 static irqreturn_t
hpd_irq(int irq
, void *data
)
86 struct tegra_output
*output
= data
;
88 if (output
->connector
.dev
)
89 drm_helper_hpd_irq_event(output
->connector
.dev
);
94 int tegra_output_probe(struct tegra_output
*output
)
96 struct device_node
*ddc
, *panel
;
100 if (!output
->of_node
)
101 output
->of_node
= output
->dev
->of_node
;
103 err
= drm_of_find_panel_or_bridge(output
->of_node
, -1, -1,
104 &output
->panel
, &output
->bridge
);
105 if (err
&& err
!= -ENODEV
)
108 panel
= of_parse_phandle(output
->of_node
, "nvidia,panel", 0);
111 * Don't mix nvidia,panel phandle with the graph in a
114 WARN_ON(output
->panel
|| output
->bridge
);
116 output
->panel
= of_drm_find_panel(panel
);
119 if (IS_ERR(output
->panel
))
120 return PTR_ERR(output
->panel
);
123 output
->edid
= of_get_property(output
->of_node
, "nvidia,edid", &size
);
125 ddc
= of_parse_phandle(output
->of_node
, "nvidia,ddc-i2c-bus", 0);
127 output
->ddc
= of_get_i2c_adapter_by_node(ddc
);
136 output
->hpd_gpio
= devm_gpiod_get_from_of_node(output
->dev
,
138 "nvidia,hpd-gpio", 0,
140 "HDMI hotplug detect");
141 if (IS_ERR(output
->hpd_gpio
)) {
142 if (PTR_ERR(output
->hpd_gpio
) != -ENOENT
)
143 return PTR_ERR(output
->hpd_gpio
);
145 output
->hpd_gpio
= NULL
;
148 if (output
->hpd_gpio
) {
149 err
= gpiod_to_irq(output
->hpd_gpio
);
151 dev_err(output
->dev
, "gpiod_to_irq(): %d\n", err
);
155 output
->hpd_irq
= err
;
157 flags
= IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
|
160 err
= request_threaded_irq(output
->hpd_irq
, NULL
, hpd_irq
,
161 flags
, "hpd", output
);
163 dev_err(output
->dev
, "failed to request IRQ#%u: %d\n",
164 output
->hpd_irq
, err
);
168 output
->connector
.polled
= DRM_CONNECTOR_POLL_HPD
;
171 * Disable the interrupt until the connector has been
172 * initialized to avoid a race in the hotplug interrupt
175 disable_irq(output
->hpd_irq
);
181 void tegra_output_remove(struct tegra_output
*output
)
183 if (output
->hpd_gpio
)
184 free_irq(output
->hpd_irq
, output
);
187 i2c_put_adapter(output
->ddc
);
190 int tegra_output_init(struct drm_device
*drm
, struct tegra_output
*output
)
195 * The connector is now registered and ready to receive hotplug events
196 * so the hotplug interrupt can be enabled.
198 if (output
->hpd_gpio
)
199 enable_irq(output
->hpd_irq
);
201 connector_type
= output
->connector
.connector_type
;
203 * Create a CEC notifier for HDMI connector.
205 if (connector_type
== DRM_MODE_CONNECTOR_HDMIA
||
206 connector_type
== DRM_MODE_CONNECTOR_HDMIB
) {
207 struct cec_connector_info conn_info
;
209 cec_fill_conn_info_from_drm(&conn_info
, &output
->connector
);
210 output
->cec
= cec_notifier_conn_register(output
->dev
, NULL
,
219 void tegra_output_exit(struct tegra_output
*output
)
222 * The connector is going away, so the interrupt must be disabled to
223 * prevent the hotplug interrupt handler from potentially crashing.
225 if (output
->hpd_gpio
)
226 disable_irq(output
->hpd_irq
);
229 void tegra_output_find_possible_crtcs(struct tegra_output
*output
,
230 struct drm_device
*drm
)
232 struct device
*dev
= output
->dev
;
233 struct drm_crtc
*crtc
;
234 unsigned int mask
= 0;
236 drm_for_each_crtc(crtc
, drm
) {
237 struct tegra_dc
*dc
= to_tegra_dc(crtc
);
239 if (tegra_dc_has_output(dc
, dev
))
240 mask
|= drm_crtc_mask(crtc
);
244 dev_warn(dev
, "missing output definition for heads in DT\n");
248 output
->encoder
.possible_crtcs
= mask
;
251 int tegra_output_suspend(struct tegra_output
*output
)
254 disable_irq(output
->hpd_irq
);
259 int tegra_output_resume(struct tegra_output
*output
)
262 enable_irq(output
->hpd_irq
);