blob: 581dc5d37bed05cf6f7de00c69b0c0eb8dd74c91 [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
15static int tegra_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
Thierry Reding9be7d862013-08-30 15:22:36 +020021 if (output->panel) {
22 err = output->panel->funcs->get_modes(output->panel);
23 if (err > 0)
24 return err;
25 }
26
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000027 if (output->edid)
28 edid = kmemdup(output->edid, sizeof(*edid), GFP_KERNEL);
29 else if (output->ddc)
30 edid = drm_get_edid(connector, output->ddc);
31
32 drm_mode_connector_update_edid_property(connector, edid);
33
34 if (edid) {
35 err = drm_add_edid_modes(connector, edid);
36 kfree(edid);
37 }
38
39 return err;
40}
41
42static int tegra_connector_mode_valid(struct drm_connector *connector,
43 struct drm_display_mode *mode)
44{
45 struct tegra_output *output = connector_to_output(connector);
46 enum drm_mode_status status = MODE_OK;
47 int err;
48
49 err = tegra_output_check_mode(output, mode, &status);
50 if (err < 0)
51 return MODE_ERROR;
52
53 return status;
54}
55
56static struct drm_encoder *
57tegra_connector_best_encoder(struct drm_connector *connector)
58{
59 struct tegra_output *output = connector_to_output(connector);
60
61 return &output->encoder;
62}
63
64static const struct drm_connector_helper_funcs connector_helper_funcs = {
65 .get_modes = tegra_connector_get_modes,
66 .mode_valid = tegra_connector_mode_valid,
67 .best_encoder = tegra_connector_best_encoder,
68};
69
70static enum drm_connector_status
71tegra_connector_detect(struct drm_connector *connector, bool force)
72{
73 struct tegra_output *output = connector_to_output(connector);
74 enum drm_connector_status status = connector_status_unknown;
75
76 if (gpio_is_valid(output->hpd_gpio)) {
77 if (gpio_get_value(output->hpd_gpio) == 0)
78 status = connector_status_disconnected;
79 else
80 status = connector_status_connected;
81 } else {
Thierry Reding9be7d862013-08-30 15:22:36 +020082 if (!output->panel)
83 status = connector_status_disconnected;
84 else
85 status = connector_status_connected;
86
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000087 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
88 status = connector_status_connected;
89 }
90
91 return status;
92}
93
Thierry Redingf002abc2013-10-14 14:06:02 +020094static void drm_connector_clear(struct drm_connector *connector)
95{
96 memset(connector, 0, sizeof(*connector));
97}
98
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000099static void tegra_connector_destroy(struct drm_connector *connector)
100{
101 drm_sysfs_connector_remove(connector);
102 drm_connector_cleanup(connector);
Thierry Redingf002abc2013-10-14 14:06:02 +0200103 drm_connector_clear(connector);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000104}
105
106static const struct drm_connector_funcs connector_funcs = {
107 .dpms = drm_helper_connector_dpms,
108 .detect = tegra_connector_detect,
109 .fill_modes = drm_helper_probe_single_connector_modes,
110 .destroy = tegra_connector_destroy,
111};
112
Thierry Redingf002abc2013-10-14 14:06:02 +0200113static void drm_encoder_clear(struct drm_encoder *encoder)
114{
115 memset(encoder, 0, sizeof(*encoder));
116}
117
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000118static void tegra_encoder_destroy(struct drm_encoder *encoder)
119{
120 drm_encoder_cleanup(encoder);
Thierry Redingf002abc2013-10-14 14:06:02 +0200121 drm_encoder_clear(encoder);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000122}
123
124static const struct drm_encoder_funcs encoder_funcs = {
125 .destroy = tegra_encoder_destroy,
126};
127
128static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
129{
Thierry Reding9be7d862013-08-30 15:22:36 +0200130 struct tegra_output *output = encoder_to_output(encoder);
131 struct drm_panel *panel = output->panel;
132
Thierry Redingb5190022013-10-29 16:03:03 +0100133 if (mode != DRM_MODE_DPMS_ON) {
134 drm_panel_disable(panel);
135 tegra_output_disable(output);
136 } else {
137 tegra_output_enable(output);
138 drm_panel_enable(panel);
Thierry Reding9be7d862013-08-30 15:22:36 +0200139 }
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000140}
141
142static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder,
143 const struct drm_display_mode *mode,
144 struct drm_display_mode *adjusted)
145{
146 return true;
147}
148
149static void tegra_encoder_prepare(struct drm_encoder *encoder)
150{
151}
152
153static void tegra_encoder_commit(struct drm_encoder *encoder)
154{
155}
156
157static void tegra_encoder_mode_set(struct drm_encoder *encoder,
158 struct drm_display_mode *mode,
159 struct drm_display_mode *adjusted)
160{
161 struct tegra_output *output = encoder_to_output(encoder);
162 int err;
163
164 err = tegra_output_enable(output);
165 if (err < 0)
166 dev_err(encoder->dev->dev, "tegra_output_enable(): %d\n", err);
167}
168
169static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
170 .dpms = tegra_encoder_dpms,
171 .mode_fixup = tegra_encoder_mode_fixup,
172 .prepare = tegra_encoder_prepare,
173 .commit = tegra_encoder_commit,
174 .mode_set = tegra_encoder_mode_set,
175};
176
177static irqreturn_t hpd_irq(int irq, void *data)
178{
179 struct tegra_output *output = data;
180
181 drm_helper_hpd_irq_event(output->connector.dev);
182
183 return IRQ_HANDLED;
184}
185
Thierry Reding59d29c02013-10-14 14:26:42 +0200186int tegra_output_probe(struct tegra_output *output)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000187{
Thierry Reding9be7d862013-08-30 15:22:36 +0200188 struct device_node *ddc, *panel;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000189 enum of_gpio_flags flags;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000190 size_t size;
191 int err;
192
193 if (!output->of_node)
194 output->of_node = output->dev->of_node;
195
Thierry Reding9be7d862013-08-30 15:22:36 +0200196 panel = of_parse_phandle(output->of_node, "nvidia,panel", 0);
197 if (panel) {
198 output->panel = of_drm_find_panel(panel);
199 if (!output->panel)
200 return -EPROBE_DEFER;
201
202 of_node_put(panel);
203 }
204
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000205 output->edid = of_get_property(output->of_node, "nvidia,edid", &size);
206
207 ddc = of_parse_phandle(output->of_node, "nvidia,ddc-i2c-bus", 0);
208 if (ddc) {
209 output->ddc = of_find_i2c_adapter_by_node(ddc);
210 if (!output->ddc) {
211 err = -EPROBE_DEFER;
212 of_node_put(ddc);
213 return err;
214 }
215
216 of_node_put(ddc);
217 }
218
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000219 output->hpd_gpio = of_get_named_gpio_flags(output->of_node,
220 "nvidia,hpd-gpio", 0,
221 &flags);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000222 if (gpio_is_valid(output->hpd_gpio)) {
223 unsigned long flags;
224
225 err = gpio_request_one(output->hpd_gpio, GPIOF_DIR_IN,
226 "HDMI hotplug detect");
227 if (err < 0) {
228 dev_err(output->dev, "gpio_request_one(): %d\n", err);
229 return err;
230 }
231
232 err = gpio_to_irq(output->hpd_gpio);
233 if (err < 0) {
234 dev_err(output->dev, "gpio_to_irq(): %d\n", err);
Thierry Reding59d29c02013-10-14 14:26:42 +0200235 gpio_free(output->hpd_gpio);
236 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000237 }
238
239 output->hpd_irq = err;
240
241 flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
242 IRQF_ONESHOT;
243
244 err = request_threaded_irq(output->hpd_irq, NULL, hpd_irq,
245 flags, "hpd", output);
246 if (err < 0) {
247 dev_err(output->dev, "failed to request IRQ#%u: %d\n",
248 output->hpd_irq, err);
Thierry Reding59d29c02013-10-14 14:26:42 +0200249 gpio_free(output->hpd_gpio);
250 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000251 }
252
253 output->connector.polled = DRM_CONNECTOR_POLL_HPD;
254 }
255
Thierry Reding59d29c02013-10-14 14:26:42 +0200256 return 0;
257}
258
259int tegra_output_remove(struct tegra_output *output)
260{
261 if (gpio_is_valid(output->hpd_gpio)) {
262 free_irq(output->hpd_irq, output);
263 gpio_free(output->hpd_gpio);
264 }
265
266 if (output->ddc)
267 put_device(&output->ddc->dev);
268
269 return 0;
270}
271
272int tegra_output_init(struct drm_device *drm, struct tegra_output *output)
273{
274 int connector, encoder;
275
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000276 switch (output->type) {
277 case TEGRA_OUTPUT_RGB:
278 connector = DRM_MODE_CONNECTOR_LVDS;
279 encoder = DRM_MODE_ENCODER_LVDS;
280 break;
281
Thierry Redingedec4af2012-11-15 21:28:23 +0000282 case TEGRA_OUTPUT_HDMI:
283 connector = DRM_MODE_CONNECTOR_HDMIA;
284 encoder = DRM_MODE_ENCODER_TMDS;
285 break;
286
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000287 default:
288 connector = DRM_MODE_CONNECTOR_Unknown;
289 encoder = DRM_MODE_ENCODER_NONE;
290 break;
291 }
292
293 drm_connector_init(drm, &output->connector, &connector_funcs,
294 connector);
295 drm_connector_helper_add(&output->connector, &connector_helper_funcs);
Thierry Redingf8c33252013-09-24 09:58:08 +0200296 output->connector.dpms = DRM_MODE_DPMS_OFF;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000297
Thierry Reding9be7d862013-08-30 15:22:36 +0200298 if (output->panel)
299 drm_panel_attach(output->panel, &output->connector);
300
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000301 drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder);
302 drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs);
303
304 drm_mode_connector_attach_encoder(&output->connector, &output->encoder);
305 drm_sysfs_connector_add(&output->connector);
306
307 output->encoder.possible_crtcs = 0x3;
308
309 return 0;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000310}
311
312int tegra_output_exit(struct tegra_output *output)
313{
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000314 return 0;
315}