Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2012 Avionic Design GmbH
0004  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
0005  */
0006 
0007 #include <drm/drm_atomic_helper.h>
0008 #include <drm/drm_of.h>
0009 #include <drm/drm_panel.h>
0010 #include <drm/drm_simple_kms_helper.h>
0011 
0012 #include "drm.h"
0013 #include "dc.h"
0014 
0015 #include <media/cec-notifier.h>
0016 
0017 int tegra_output_connector_get_modes(struct drm_connector *connector)
0018 {
0019     struct tegra_output *output = connector_to_output(connector);
0020     struct edid *edid = NULL;
0021     int err = 0;
0022 
0023     /*
0024      * If the panel provides one or more modes, use them exclusively and
0025      * ignore any other means of obtaining a mode.
0026      */
0027     if (output->panel) {
0028         err = drm_panel_get_modes(output->panel, connector);
0029         if (err > 0)
0030             return err;
0031     }
0032 
0033     if (output->edid)
0034         edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
0035     else if (output->ddc)
0036         edid = drm_get_edid(connector, output->ddc);
0037 
0038     cec_notifier_set_phys_addr_from_edid(output->cec, edid);
0039     drm_connector_update_edid_property(connector, edid);
0040 
0041     if (edid) {
0042         err = drm_add_edid_modes(connector, edid);
0043         kfree(edid);
0044     }
0045 
0046     return err;
0047 }
0048 
0049 enum drm_connector_status
0050 tegra_output_connector_detect(struct drm_connector *connector, bool force)
0051 {
0052     struct tegra_output *output = connector_to_output(connector);
0053     enum drm_connector_status status = connector_status_unknown;
0054 
0055     if (output->hpd_gpio) {
0056         if (gpiod_get_value(output->hpd_gpio) == 0)
0057             status = connector_status_disconnected;
0058         else
0059             status = connector_status_connected;
0060     } else {
0061         if (!output->panel)
0062             status = connector_status_disconnected;
0063         else
0064             status = connector_status_connected;
0065     }
0066 
0067     if (status != connector_status_connected)
0068         cec_notifier_phys_addr_invalidate(output->cec);
0069 
0070     return status;
0071 }
0072 
0073 void tegra_output_connector_destroy(struct drm_connector *connector)
0074 {
0075     struct tegra_output *output = connector_to_output(connector);
0076 
0077     if (output->cec)
0078         cec_notifier_conn_unregister(output->cec);
0079 
0080     drm_connector_unregister(connector);
0081     drm_connector_cleanup(connector);
0082 }
0083 
0084 static irqreturn_t hpd_irq(int irq, void *data)
0085 {
0086     struct tegra_output *output = data;
0087 
0088     if (output->connector.dev)
0089         drm_helper_hpd_irq_event(output->connector.dev);
0090 
0091     return IRQ_HANDLED;
0092 }
0093 
0094 int tegra_output_probe(struct tegra_output *output)
0095 {
0096     struct device_node *ddc, *panel;
0097     unsigned long flags;
0098     int err, size;
0099 
0100     if (!output->of_node)
0101         output->of_node = output->dev->of_node;
0102 
0103     err = drm_of_find_panel_or_bridge(output->of_node, -1, -1,
0104                       &output->panel, &output->bridge);
0105     if (err && err != -ENODEV)
0106         return err;
0107 
0108     panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
0109     if (panel) {
0110         /*
0111          * Don't mix nvidia,panel phandle with the graph in a
0112          * device-tree.
0113          */
0114         WARN_ON(output->panel || output->bridge);
0115 
0116         output->panel = of_drm_find_panel(panel);
0117         of_node_put(panel);
0118 
0119         if (IS_ERR(output->panel))
0120             return PTR_ERR(output->panel);
0121     }
0122 
0123     output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
0124 
0125     ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
0126     if (ddc) {
0127         output->ddc = of_get_i2c_adapter_by_node(ddc);
0128         of_node_put(ddc);
0129 
0130         if (!output->ddc) {
0131             err = -EPROBE_DEFER;
0132             return err;
0133         }
0134     }
0135 
0136     output->hpd_gpio = devm_gpiod_get_from_of_node(output->dev,
0137                                output->of_node,
0138                                "nvidia,hpd-gpio", 0,
0139                                GPIOD_IN,
0140                                "HDMI hotplug detect");
0141     if (IS_ERR(output->hpd_gpio)) {
0142         if (PTR_ERR(output->hpd_gpio) != -ENOENT)
0143             return PTR_ERR(output->hpd_gpio);
0144 
0145         output->hpd_gpio = NULL;
0146     }
0147 
0148     if (output->hpd_gpio) {
0149         err = gpiod_to_irq(output->hpd_gpio);
0150         if (err < 0) {
0151             dev_err(output->dev, "gpiod_to_irq(): %d\n", err);
0152             return err;
0153         }
0154 
0155         output->hpd_irq = err;
0156 
0157         flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
0158             IRQF_ONESHOT;
0159 
0160         err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
0161                        flags, "hpd", output);
0162         if (err < 0) {
0163             dev_err(output->dev, "failed to request IRQ#%u: %d\n",
0164                 output->hpd_irq, err);
0165             return err;
0166         }
0167 
0168         output->connector.polled = DRM_CONNECTOR_POLL_HPD;
0169 
0170         /*
0171          * Disable the interrupt until the connector has been
0172          * initialized to avoid a race in the hotplug interrupt
0173          * handler.
0174          */
0175         disable_irq(output->hpd_irq);
0176     }
0177 
0178     return 0;
0179 }
0180 
0181 void tegra_output_remove(struct tegra_output *output)
0182 {
0183     if (output->hpd_gpio)
0184         free_irq(output->hpd_irq, output);
0185 
0186     if (output->ddc)
0187         i2c_put_adapter(output->ddc);
0188 }
0189 
0190 int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
0191 {
0192     int connector_type;
0193 
0194     /*
0195      * The connector is now registered and ready to receive hotplug events
0196      * so the hotplug interrupt can be enabled.
0197      */
0198     if (output->hpd_gpio)
0199         enable_irq(output->hpd_irq);
0200 
0201     connector_type = output->connector.connector_type;
0202     /*
0203      * Create a CEC notifier for HDMI connector.
0204      */
0205     if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
0206         connector_type == DRM_MODE_CONNECTOR_HDMIB) {
0207         struct cec_connector_info conn_info;
0208 
0209         cec_fill_conn_info_from_drm(&conn_info, &output->connector);
0210         output->cec = cec_notifier_conn_register(output->dev, NULL,
0211                              &conn_info);
0212         if (!output->cec)
0213             return -ENOMEM;
0214     }
0215 
0216     return 0;
0217 }
0218 
0219 void tegra_output_exit(struct tegra_output *output)
0220 {
0221     /*
0222      * The connector is going away, so the interrupt must be disabled to
0223      * prevent the hotplug interrupt handler from potentially crashing.
0224      */
0225     if (output->hpd_gpio)
0226         disable_irq(output->hpd_irq);
0227 }
0228 
0229 void tegra_output_find_possible_crtcs(struct tegra_output *output,
0230                       struct drm_device *drm)
0231 {
0232     struct device *dev = output->dev;
0233     struct drm_crtc *crtc;
0234     unsigned int mask = 0;
0235 
0236     drm_for_each_crtc(crtc, drm) {
0237         struct tegra_dc *dc = to_tegra_dc(crtc);
0238 
0239         if (tegra_dc_has_output(dc, dev))
0240             mask |= drm_crtc_mask(crtc);
0241     }
0242 
0243     if (mask == 0) {
0244         dev_warn(dev, "missing output definition for heads in DT\n");
0245         mask = 0x3;
0246     }
0247 
0248     output->encoder.possible_crtcs = mask;
0249 }
0250 
0251 int tegra_output_suspend(struct tegra_output *output)
0252 {
0253     if (output->hpd_irq)
0254         disable_irq(output->hpd_irq);
0255 
0256     return 0;
0257 }
0258 
0259 int tegra_output_resume(struct tegra_output *output)
0260 {
0261     if (output->hpd_irq)
0262         enable_irq(output->hpd_irq);
0263 
0264     return 0;
0265 }