1/*
2 * Copyright (C) 2012 Avionic Design GmbH
3 * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/of_gpio.h>
11
12#include <drm/drm_atomic_helper.h>
13#include <drm/drm_panel.h>
14#include "drm.h"
15
16int tegra_output_connector_get_modes(struct drm_connector *connector)
17{
18	struct tegra_output *output = connector_to_output(connector);
19	struct edid *edid = NULL;
20	int err = 0;
21
22	/*
23	 * If the panel provides one or more modes, use them exclusively and
24	 * ignore any other means of obtaining a mode.
25	 */
26	if (output->panel) {
27		err = output->panel->funcs->get_modes(output->panel);
28		if (err > 0)
29			return err;
30	}
31
32	if (output->edid)
33		edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
34	else if (output->ddc)
35		edid = drm_get_edid(connector, output->ddc);
36
37	drm_mode_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
47struct drm_encoder *
48tegra_output_connector_best_encoder(struct drm_connector *connector)
49{
50	struct tegra_output *output = connector_to_output(connector);
51
52	return &output->encoder;
53}
54
55enum drm_connector_status
56tegra_output_connector_detect(struct drm_connector *connector, bool force)
57{
58	struct tegra_output *output = connector_to_output(connector);
59	enum drm_connector_status status = connector_status_unknown;
60
61	if (gpio_is_valid(output->hpd_gpio)) {
62		if (gpio_get_value(output->hpd_gpio) == 0)
63			status = connector_status_disconnected;
64		else
65			status = connector_status_connected;
66	} else {
67		if (!output->panel)
68			status = connector_status_disconnected;
69		else
70			status = connector_status_connected;
71	}
72
73	return status;
74}
75
76void tegra_output_connector_destroy(struct drm_connector *connector)
77{
78	drm_connector_unregister(connector);
79	drm_connector_cleanup(connector);
80}
81
82void tegra_output_encoder_destroy(struct drm_encoder *encoder)
83{
84	drm_encoder_cleanup(encoder);
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	enum of_gpio_flags flags;
101	int err, size;
102
103	if (!output->of_node)
104		output->of_node = output->dev->of_node;
105
106	panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
107	if (panel) {
108		output->panel = of_drm_find_panel(panel);
109		if (!output->panel)
110			return -EPROBE_DEFER;
111
112		of_node_put(panel);
113	}
114
115	output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
116
117	ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
118	if (ddc) {
119		output->ddc = of_find_i2c_adapter_by_node(ddc);
120		if (!output->ddc) {
121			err = -EPROBE_DEFER;
122			of_node_put(ddc);
123			return err;
124		}
125
126		of_node_put(ddc);
127	}
128
129	output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
130						   "nvidia,hpd-gpio", 0,
131						   &flags);
132	if (gpio_is_valid(output->hpd_gpio)) {
133		unsigned long flags;
134
135		err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN,
136				       "HDMI hotplug detect");
137		if (err < 0) {
138			dev_err(output->dev, "gpio_request_one(): %d\n", err);
139			return err;
140		}
141
142		err = gpio_to_irq(output->hpd_gpio);
143		if (err < 0) {
144			dev_err(output->dev, "gpio_to_irq(): %d\n", err);
145			gpio_free(output->hpd_gpio);
146			return err;
147		}
148
149		output->hpd_irq = err;
150
151		flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
152			IRQF_ONESHOT;
153
154		err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
155					   flags, "hpd", output);
156		if (err < 0) {
157			dev_err(output->dev, "failed to request IRQ#%u: %d\n",
158				output->hpd_irq, err);
159			gpio_free(output->hpd_gpio);
160			return err;
161		}
162
163		output->connector.polled = DRM_CONNECTOR_POLL_HPD;
164
165		/*
166		 * Disable the interrupt until the connector has been
167		 * initialized to avoid a race in the hotplug interrupt
168		 * handler.
169		 */
170		disable_irq(output->hpd_irq);
171	}
172
173	return 0;
174}
175
176void tegra_output_remove(struct tegra_output *output)
177{
178	if (gpio_is_valid(output->hpd_gpio)) {
179		free_irq(output->hpd_irq, output);
180		gpio_free(output->hpd_gpio);
181	}
182
183	if (output->ddc)
184		put_device(&output->ddc->dev);
185}
186
187int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
188{
189	int err;
190
191	if (output->panel) {
192		err = drm_panel_attach(output->panel, &output->connector);
193		if (err < 0)
194			return err;
195	}
196
197	/*
198	 * The connector is now registered and ready to receive hotplug events
199	 * so the hotplug interrupt can be enabled.
200	 */
201	if (gpio_is_valid(output->hpd_gpio))
202		enable_irq(output->hpd_irq);
203
204	return 0;
205}
206
207void tegra_output_exit(struct tegra_output *output)
208{
209	/*
210	 * The connector is going away, so the interrupt must be disabled to
211	 * prevent the hotplug interrupt handler from potentially crashing.
212	 */
213	if (gpio_is_valid(output->hpd_gpio))
214		disable_irq(output->hpd_irq);
215
216	if (output->panel)
217		drm_panel_detach(output->panel);
218}
219