brintos

brintos / linux-shallow public Read only

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