blob: 2d3b656bfd22604c27bcedfa679921c169b8149e [file] [log] [blame]
Thierry Redingd8f4a9e2012-11-15 21:28:22 +00001/*
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
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000010#include <linux/of_gpio.h>
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000011
Thierry Reding9be7d862013-08-30 15:22:36 +020012#include <drm/drm_panel.h>
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000013#include "drm.h"
14
Thierry Reding132085d2014-11-28 15:38:40 +010015int tegra_output_connector_get_modes(struct drm_connector *connector)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000016{
17 struct tegra_output *output = connector_to_output(connector);
18 struct edid *edid = NULL;
19 int err = 0;
20
Thierry Redingacde5412014-01-13 12:56:19 +010021 /*
22 * If the panel provides one or more modes, use them exclusively and
23 * ignore any other means of obtaining a mode.
24 */
Thierry Reding9be7d862013-08-30 15:22:36 +020025 if (output->panel) {
26 err = output->panel->funcs->get_modes(output->panel);
27 if (err > 0)
28 return err;
29 }
30
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000031 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 drm_mode_connector_update_edid_property(connector, edid);
37
38 if (edid) {
39 err = drm_add_edid_modes(connector, edid);
40 kfree(edid);
41 }
42
43 return err;
44}
45
Thierry Reding132085d2014-11-28 15:38:40 +010046struct drm_encoder *
47tegra_output_connector_best_encoder(struct drm_connector *connector)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000048{
49 struct tegra_output *output = connector_to_output(connector);
50
51 return &output->encoder;
52}
53
Thierry Reding132085d2014-11-28 15:38:40 +010054enum drm_connector_status
55tegra_output_connector_detect(struct drm_connector *connector, bool force)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000056{
57 struct tegra_output *output = connector_to_output(connector);
58 enum drm_connector_status status = connector_status_unknown;
59
60 if (gpio_is_valid(output->hpd_gpio)) {
61 if (gpio_get_value(output->hpd_gpio) == 0)
62 status = connector_status_disconnected;
63 else
64 status = connector_status_connected;
65 } else {
Thierry Reding9be7d862013-08-30 15:22:36 +020066 if (!output->panel)
67 status = connector_status_disconnected;
68 else
69 status = connector_status_connected;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000070 }
71
72 return status;
73}
74
Thierry Reding132085d2014-11-28 15:38:40 +010075void tegra_output_connector_destroy(struct drm_connector *connector)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000076{
Thomas Wood34ea3d32014-05-29 16:57:41 +010077 drm_connector_unregister(connector);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000078 drm_connector_cleanup(connector);
79}
80
Thierry Reding132085d2014-11-28 15:38:40 +010081void tegra_output_encoder_destroy(struct drm_encoder *encoder)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000082{
83 drm_encoder_cleanup(encoder);
84}
85
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000086static irqreturn_t hpd_irq(int irq, void *data)
87{
88 struct tegra_output *output = data;
89
Thierry Reding8fc8f7d2014-10-21 14:00:09 +020090 if (output->connector.dev)
91 drm_helper_hpd_irq_event(output->connector.dev);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000092
93 return IRQ_HANDLED;
94}
95
Thierry Reding59d29c02013-10-14 14:26:42 +020096int tegra_output_probe(struct tegra_output *output)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000097{
Thierry Reding9be7d862013-08-30 15:22:36 +020098 struct device_node *ddc, *panel;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000099 enum of_gpio_flags flags;
Thierry Reding7236aa02014-01-13 13:58:17 +0100100 int err, size;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000101
102 if (!output->of_node)
103 output->of_node = output->dev->of_node;
104
Thierry Reding9be7d862013-08-30 15:22:36 +0200105 panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
106 if (panel) {
107 output->panel = of_drm_find_panel(panel);
108 if (!output->panel)
109 return -EPROBE_DEFER;
110
111 of_node_put(panel);
112 }
113
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000114 output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
115
116 ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
117 if (ddc) {
118 output->ddc = of_find_i2c_adapter_by_node(ddc);
119 if (!output->ddc) {
120 err = -EPROBE_DEFER;
121 of_node_put(ddc);
122 return err;
123 }
124
125 of_node_put(ddc);
126 }
127
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000128 output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
129 "nvidia,hpd-gpio", 0,
130 &flags);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000131 if (gpio_is_valid(output->hpd_gpio)) {
132 unsigned long flags;
133
134 err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN,
135 "HDMI hotplug detect");
136 if (err < 0) {
137 dev_err(output->dev, "gpio_request_one(): %d\n", err);
138 return err;
139 }
140
141 err = gpio_to_irq(output->hpd_gpio);
142 if (err < 0) {
143 dev_err(output->dev, "gpio_to_irq(): %d\n", err);
Thierry Reding59d29c02013-10-14 14:26:42 +0200144 gpio_free(output->hpd_gpio);
145 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000146 }
147
148 output->hpd_irq = err;
149
150 flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
151 IRQF_ONESHOT;
152
153 err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
154 flags, "hpd", output);
155 if (err < 0) {
156 dev_err(output->dev, "failed to request IRQ#%u: %d\n",
157 output->hpd_irq, err);
Thierry Reding59d29c02013-10-14 14:26:42 +0200158 gpio_free(output->hpd_gpio);
159 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000160 }
161
162 output->connector.polled = DRM_CONNECTOR_POLL_HPD;
Thierry Reding8fc8f7d2014-10-21 14:00:09 +0200163
164 /*
165 * Disable the interrupt until the connector has been
166 * initialized to avoid a race in the hotplug interrupt
167 * handler.
168 */
169 disable_irq(output->hpd_irq);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000170 }
171
Thierry Reding59d29c02013-10-14 14:26:42 +0200172 return 0;
173}
174
175int tegra_output_remove(struct tegra_output *output)
176{
177 if (gpio_is_valid(output->hpd_gpio)) {
178 free_irq(output->hpd_irq, output);
179 gpio_free(output->hpd_gpio);
180 }
181
182 if (output->ddc)
183 put_device(&output->ddc->dev);
184
185 return 0;
186}
187
188int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
189{
Thierry Redingea130b22014-12-19 15:51:35 +0100190 int err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000191
Thierry Redingea130b22014-12-19 15:51:35 +0100192 if (output->panel) {
193 err = drm_panel_attach(output->panel, &output->connector);
194 if (err < 0)
195 return err;
196 }
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000197
Thierry Reding8fc8f7d2014-10-21 14:00:09 +0200198 /*
199 * The connector is now registered and ready to receive hotplug events
200 * so the hotplug interrupt can be enabled.
201 */
202 if (gpio_is_valid(output->hpd_gpio))
203 enable_irq(output->hpd_irq);
204
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000205 return 0;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000206}
207
208int tegra_output_exit(struct tegra_output *output)
209{
Thierry Reding8fc8f7d2014-10-21 14:00:09 +0200210 /*
211 * The connector is going away, so the interrupt must be disabled to
212 * prevent the hotplug interrupt handler from potentially crashing.
213 */
214 if (gpio_is_valid(output->hpd_gpio))
215 disable_irq(output->hpd_irq);
216
Thierry Reding9aaa0ce2014-11-04 16:20:20 +0100217 if (output->panel)
218 drm_panel_detach(output->panel);
219
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000220 return 0;
221}