cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}