root/drivers/gpu/drm/tegra/output.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tegra_output_connector_get_modes
  2. tegra_output_connector_detect
  3. tegra_output_connector_destroy
  4. tegra_output_encoder_destroy
  5. hpd_irq
  6. tegra_output_probe
  7. tegra_output_remove
  8. tegra_output_init
  9. tegra_output_exit
  10. tegra_output_find_possible_crtcs

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

/* [<][>][^][v][top][bottom][index][help] */