]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/gpu/drm/exynos/exynos_drm_connector.c
drm/exynos: Remove dpms link between encoder/connector
[karo-tx-linux.git] / drivers / gpu / drm / exynos / exynos_drm_connector.c
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
3  * Authors:
4  *      Inki Dae <inki.dae@samsung.com>
5  *      Joonyoung Shim <jy0922.shim@samsung.com>
6  *      Seung-Woo Kim <sw0312.kim@samsung.com>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13
14 #include <drm/drmP.h>
15 #include <drm/drm_crtc_helper.h>
16
17 #include <drm/exynos_drm.h>
18 #include "exynos_drm_drv.h"
19 #include "exynos_drm_encoder.h"
20 #include "exynos_drm_connector.h"
21
22 #define to_exynos_connector(x)  container_of(x, struct exynos_drm_connector,\
23                                 drm_connector)
24
25 struct exynos_drm_connector {
26         struct drm_connector    drm_connector;
27         uint32_t                encoder_id;
28         struct exynos_drm_manager *manager;
29 };
30
31 static int exynos_drm_connector_get_modes(struct drm_connector *connector)
32 {
33         struct exynos_drm_connector *exynos_connector =
34                                         to_exynos_connector(connector);
35         struct exynos_drm_manager *manager = exynos_connector->manager;
36         struct exynos_drm_display_ops *display_ops = manager->display_ops;
37         struct edid *edid = NULL;
38         unsigned int count = 0;
39         int ret;
40
41         if (!display_ops) {
42                 DRM_DEBUG_KMS("display_ops is null.\n");
43                 return 0;
44         }
45
46         /*
47          * if get_edid() exists then get_edid() callback of hdmi side
48          * is called to get edid data through i2c interface else
49          * get timing from the FIMD driver(display controller).
50          *
51          * P.S. in case of lcd panel, count is always 1 if success
52          * because lcd panel has only one mode.
53          */
54         if (display_ops->get_edid) {
55                 edid = display_ops->get_edid(manager->dev, connector);
56                 if (IS_ERR_OR_NULL(edid)) {
57                         ret = PTR_ERR(edid);
58                         edid = NULL;
59                         DRM_ERROR("Panel operation get_edid failed %d\n", ret);
60                         goto out;
61                 }
62
63                 count = drm_add_edid_modes(connector, edid);
64                 if (!count) {
65                         DRM_ERROR("Add edid modes failed %d\n", count);
66                         goto out;
67                 }
68
69                 drm_mode_connector_update_edid_property(connector, edid);
70         } else {
71                 struct exynos_drm_panel_info *panel;
72                 struct drm_display_mode *mode = drm_mode_create(connector->dev);
73                 if (!mode) {
74                         DRM_ERROR("failed to create a new display mode.\n");
75                         return 0;
76                 }
77
78                 if (display_ops->get_panel)
79                         panel = display_ops->get_panel(manager->dev);
80                 else {
81                         drm_mode_destroy(connector->dev, mode);
82                         return 0;
83                 }
84
85                 drm_display_mode_from_videomode(&panel->vm, mode);
86                 mode->width_mm = panel->width_mm;
87                 mode->height_mm = panel->height_mm;
88                 connector->display_info.width_mm = mode->width_mm;
89                 connector->display_info.height_mm = mode->height_mm;
90
91                 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
92                 drm_mode_set_name(mode);
93                 drm_mode_probed_add(connector, mode);
94
95                 count = 1;
96         }
97
98 out:
99         kfree(edid);
100         return count;
101 }
102
103 static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
104                                             struct drm_display_mode *mode)
105 {
106         struct exynos_drm_connector *exynos_connector =
107                                         to_exynos_connector(connector);
108         struct exynos_drm_manager *manager = exynos_connector->manager;
109         struct exynos_drm_display_ops *display_ops = manager->display_ops;
110         int ret = MODE_BAD;
111
112         DRM_DEBUG_KMS("%s\n", __FILE__);
113
114         if (display_ops && display_ops->check_mode)
115                 if (!display_ops->check_mode(manager->dev, mode))
116                         ret = MODE_OK;
117
118         return ret;
119 }
120
121 static struct drm_encoder *exynos_drm_best_encoder(
122                 struct drm_connector *connector)
123 {
124         struct drm_device *dev = connector->dev;
125         struct exynos_drm_connector *exynos_connector =
126                                         to_exynos_connector(connector);
127         struct drm_mode_object *obj;
128         struct drm_encoder *encoder;
129
130         obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
131                                    DRM_MODE_OBJECT_ENCODER);
132         if (!obj) {
133                 DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
134                                 exynos_connector->encoder_id);
135                 return NULL;
136         }
137
138         encoder = obj_to_encoder(obj);
139
140         return encoder;
141 }
142
143 static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
144         .get_modes      = exynos_drm_connector_get_modes,
145         .mode_valid     = exynos_drm_connector_mode_valid,
146         .best_encoder   = exynos_drm_best_encoder,
147 };
148
149 static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
150                                 unsigned int max_width, unsigned int max_height)
151 {
152         struct exynos_drm_connector *exynos_connector =
153                                         to_exynos_connector(connector);
154         struct exynos_drm_manager *manager = exynos_connector->manager;
155         struct exynos_drm_manager_ops *ops = manager->ops;
156         unsigned int width, height;
157
158         width = max_width;
159         height = max_height;
160
161         /*
162          * if specific driver want to find desired_mode using maxmum
163          * resolution then get max width and height from that driver.
164          */
165         if (ops && ops->get_max_resol)
166                 ops->get_max_resol(manager, &width, &height);
167
168         return drm_helper_probe_single_connector_modes(connector, width,
169                                                         height);
170 }
171
172 /* get detection status of display device. */
173 static enum drm_connector_status
174 exynos_drm_connector_detect(struct drm_connector *connector, bool force)
175 {
176         struct exynos_drm_connector *exynos_connector =
177                                         to_exynos_connector(connector);
178         struct exynos_drm_manager *manager = exynos_connector->manager;
179         struct exynos_drm_display_ops *display_ops =
180                                         manager->display_ops;
181         enum drm_connector_status status = connector_status_disconnected;
182
183         if (display_ops && display_ops->is_connected) {
184                 if (display_ops->is_connected(manager->dev))
185                         status = connector_status_connected;
186                 else
187                         status = connector_status_disconnected;
188         }
189
190         return status;
191 }
192
193 static void exynos_drm_connector_destroy(struct drm_connector *connector)
194 {
195         struct exynos_drm_connector *exynos_connector =
196                 to_exynos_connector(connector);
197
198         drm_sysfs_connector_remove(connector);
199         drm_connector_cleanup(connector);
200         kfree(exynos_connector);
201 }
202
203 static struct drm_connector_funcs exynos_connector_funcs = {
204         .dpms           = drm_helper_connector_dpms,
205         .fill_modes     = exynos_drm_connector_fill_modes,
206         .detect         = exynos_drm_connector_detect,
207         .destroy        = exynos_drm_connector_destroy,
208 };
209
210 struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
211                                                    struct drm_encoder *encoder)
212 {
213         struct exynos_drm_connector *exynos_connector;
214         struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
215         struct drm_connector *connector;
216         int type;
217         int err;
218
219         exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
220         if (!exynos_connector)
221                 return NULL;
222
223         connector = &exynos_connector->drm_connector;
224
225         switch (manager->display_ops->type) {
226         case EXYNOS_DISPLAY_TYPE_HDMI:
227                 type = DRM_MODE_CONNECTOR_HDMIA;
228                 connector->interlace_allowed = true;
229                 connector->polled = DRM_CONNECTOR_POLL_HPD;
230                 break;
231         case EXYNOS_DISPLAY_TYPE_VIDI:
232                 type = DRM_MODE_CONNECTOR_VIRTUAL;
233                 connector->polled = DRM_CONNECTOR_POLL_HPD;
234                 break;
235         default:
236                 type = DRM_MODE_CONNECTOR_Unknown;
237                 break;
238         }
239
240         drm_connector_init(dev, connector, &exynos_connector_funcs, type);
241         drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
242
243         err = drm_sysfs_connector_add(connector);
244         if (err)
245                 goto err_connector;
246
247         exynos_connector->encoder_id = encoder->base.id;
248         exynos_connector->manager = manager;
249         connector->dpms = DRM_MODE_DPMS_OFF;
250         connector->encoder = encoder;
251
252         err = drm_mode_connector_attach_encoder(connector, encoder);
253         if (err) {
254                 DRM_ERROR("failed to attach a connector to a encoder\n");
255                 goto err_sysfs;
256         }
257
258         DRM_DEBUG_KMS("connector has been created\n");
259
260         return connector;
261
262 err_sysfs:
263         drm_sysfs_connector_remove(connector);
264 err_connector:
265         drm_connector_cleanup(connector);
266         kfree(exynos_connector);
267         return NULL;
268 }