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