f205aa8bad50939eb260141ba1c9324c4f3b29f7
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_connector.c
1 /*
2  * Copyright (C) ROCKCHIP, Inc.
3  * Author:yzq<yzq@rock-chips.com>
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #include <drm/drmP.h>
15 #include <drm/drm_crtc_helper.h>
16
17 #include <drm/rockchip_drm.h>
18 #include "rockchip_drm_drv.h"
19 #include "rockchip_drm_encoder.h"
20
21 #define to_rockchip_connector(x)        container_of(x, struct rockchip_drm_connector,\
22                                 drm_connector)
23
24 struct rockchip_drm_connector {
25         struct drm_connector    drm_connector;
26         uint32_t                encoder_id;
27         struct rockchip_drm_manager *manager;
28         uint32_t                dpms;
29 };
30
31 /* convert rockchip_video_timings to drm_display_mode */
32 static inline void
33 convert_to_display_mode(struct drm_display_mode *mode,
34                         struct rockchip_drm_panel_info *panel)
35 {
36         struct fb_videomode *timing = &panel->timing;
37         DRM_DEBUG_KMS("%s\n", __FILE__);
38
39         mode->clock = timing->pixclock / 1000;
40         mode->vrefresh = timing->refresh;
41
42         mode->hdisplay = timing->xres;
43         mode->hsync_start = mode->hdisplay + timing->right_margin;
44         mode->hsync_end = mode->hsync_start + timing->hsync_len;
45         mode->htotal = mode->hsync_end + timing->left_margin;
46
47         mode->vdisplay = timing->yres;
48         mode->vsync_start = mode->vdisplay + timing->lower_margin;
49         mode->vsync_end = mode->vsync_start + timing->vsync_len;
50         mode->vtotal = mode->vsync_end + timing->upper_margin;
51         mode->width_mm = panel->width_mm;
52         mode->height_mm = panel->height_mm;
53
54         if (timing->vmode & FB_VMODE_INTERLACED)
55                 mode->flags |= DRM_MODE_FLAG_INTERLACE;
56
57         if (timing->vmode & FB_VMODE_DOUBLE)
58                 mode->flags |= DRM_MODE_FLAG_DBLSCAN;
59 }
60 static inline void
61 convert_fbmode_to_display_mode(struct drm_display_mode *mode,
62                         struct fb_videomode *timing)
63 {
64         DRM_DEBUG_KMS("%s\n", __FILE__);
65
66         mode->clock = timing->pixclock / 1000;
67         mode->vrefresh = timing->refresh;
68
69         mode->hdisplay = timing->xres;
70         mode->hsync_start = mode->hdisplay + timing->right_margin;
71         mode->hsync_end = mode->hsync_start + timing->hsync_len;
72         mode->htotal = mode->hsync_end + timing->left_margin;
73
74         mode->vdisplay = timing->yres;
75         mode->vsync_start = mode->vdisplay + timing->lower_margin;
76         mode->vsync_end = mode->vsync_start + timing->vsync_len;
77         mode->vtotal = mode->vsync_end + timing->upper_margin;
78
79         if (timing->vmode & FB_VMODE_INTERLACED)
80                 mode->flags |= DRM_MODE_FLAG_INTERLACE;
81
82         if (timing->vmode & FB_VMODE_DOUBLE)
83                 mode->flags |= DRM_MODE_FLAG_DBLSCAN;
84 }
85 /* convert drm_display_mode to rockchip_video_timings */
86 static inline void
87 convert_to_video_timing(struct fb_videomode *timing,
88                         struct drm_display_mode *mode)
89 {
90         DRM_DEBUG_KMS("%s\n", __FILE__);
91
92         memset(timing, 0, sizeof(*timing));
93
94         timing->pixclock = mode->clock * 1000;
95         timing->refresh = drm_mode_vrefresh(mode);
96
97         timing->xres = mode->hdisplay;
98         timing->right_margin = mode->hsync_start - mode->hdisplay;
99         timing->hsync_len = mode->hsync_end - mode->hsync_start;
100         timing->left_margin = mode->htotal - mode->hsync_end;
101
102         timing->yres = mode->vdisplay;
103         timing->lower_margin = mode->vsync_start - mode->vdisplay;
104         timing->vsync_len = mode->vsync_end - mode->vsync_start;
105         timing->upper_margin = mode->vtotal - mode->vsync_end;
106
107         if (mode->flags & DRM_MODE_FLAG_INTERLACE)
108                 timing->vmode = FB_VMODE_INTERLACED;
109         else
110                 timing->vmode = FB_VMODE_NONINTERLACED;
111
112         if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
113                 timing->vmode |= FB_VMODE_DOUBLE;
114 }
115
116 static int rockchip_drm_connector_get_modes(struct drm_connector *connector)
117 {
118         struct rockchip_drm_connector *rockchip_connector =
119                                         to_rockchip_connector(connector);
120         struct rockchip_drm_manager *manager = rockchip_connector->manager;
121         struct rockchip_drm_display_ops *display_ops = manager->display_ops;
122         struct edid *edid = NULL;
123         unsigned int count = 0;
124         int ret;
125
126         DRM_DEBUG_KMS("%s\n", __FILE__);
127
128         if (!display_ops) {
129                 DRM_DEBUG_KMS("display_ops is null.\n");
130                 return 0;
131         }
132
133         /*
134          * if get_edid() exists then get_edid() callback of hdmi side
135          * is called to get edid data through i2c interface else
136          * get timing from the FIMD driver(display controller).
137          *
138          * P.S. in case of lcd panel, count is always 1 if success
139          * because lcd panel has only one mode.
140          */
141         if (display_ops->get_edid) {
142                 edid = display_ops->get_edid(manager->dev, connector);
143                 if (IS_ERR_OR_NULL(edid)) {
144                         ret = PTR_ERR(edid);
145                         edid = NULL;
146                         DRM_ERROR("Panel operation get_edid failed %d\n", ret);
147                         goto out;
148                 }
149
150                 count = drm_add_edid_modes(connector, edid);
151                 if (!count) {
152                         DRM_ERROR("Add edid modes failed %d\n", count);
153                         goto out;
154                 }
155
156                 drm_mode_connector_update_edid_property(connector, edid);
157         } else if(display_ops->get_modelist){
158                 struct list_head *pos,*head;
159                 struct fb_modelist *modelist;
160                 struct fb_videomode *mode;
161                 struct drm_display_mode *disp_mode = NULL;
162                 count=0;
163                 head = display_ops->get_modelist(manager->dev);
164                 
165                 list_for_each(pos,head){
166                         modelist = list_entry(pos, struct fb_modelist, list);
167                         mode = &modelist->mode;
168                         disp_mode = drm_mode_create(connector->dev);
169                         if (!mode) {
170                                 DRM_ERROR("failed to create a new display mode.\n");
171                                 return count;
172                         }
173                         convert_fbmode_to_display_mode(disp_mode, mode);
174
175                         if(mode->xres == 1280 && mode->yres == 720 && mode->refresh == 60)
176                                 disp_mode->type |=  DRM_MODE_TYPE_PREFERRED;
177
178                         drm_mode_set_name(disp_mode);
179 //                      snprintf(disp_mode->name, DRM_DISPLAY_MODE_LEN, "%dx%d%s-%d",
180 //                                      disp_mode->hdisplay, disp_mode->vdisplay,
181 //                                      !!(disp_mode->flags & DRM_MODE_FLAG_INTERLACE)? "i" : "p",disp_mode->vrefresh);
182                         drm_mode_probed_add(connector, disp_mode);
183                         count++;
184                 }
185         } else {
186                 struct rockchip_drm_panel_info *panel;
187                 struct drm_display_mode *mode = drm_mode_create(connector->dev);
188                 if (!mode) {
189                         DRM_ERROR("failed to create a new display mode.\n");
190                         return 0;
191                 }
192
193                 if (display_ops->get_panel)
194                         panel = display_ops->get_panel(manager->dev);
195                 else {
196                         drm_mode_destroy(connector->dev, mode);
197                         return 0;
198                 }
199
200                 convert_to_display_mode(mode, panel);
201                 connector->display_info.width_mm = mode->width_mm;
202                 connector->display_info.height_mm = mode->height_mm;
203
204                 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
205                 drm_mode_set_name(mode);
206                 drm_mode_probed_add(connector, mode);
207
208                 count = 1;
209         }
210
211 out:
212         kfree(edid);
213         return count;
214 }
215
216 static int rockchip_drm_connector_mode_valid(struct drm_connector *connector,
217                                             struct drm_display_mode *mode)
218 {
219         struct rockchip_drm_connector *rockchip_connector =
220                                         to_rockchip_connector(connector);
221         struct rockchip_drm_manager *manager = rockchip_connector->manager;
222         struct rockchip_drm_display_ops *display_ops = manager->display_ops;
223         struct fb_videomode timing;
224         int ret = MODE_BAD;
225
226         DRM_DEBUG_KMS("%s\n", __FILE__);
227
228         convert_to_video_timing(&timing, mode);
229
230         if (display_ops && display_ops->check_timing)
231                 if (!display_ops->check_timing(manager->dev, (void *)&timing))
232                         ret = MODE_OK;
233
234         return ret;
235 }
236
237 struct drm_encoder *rockchip_drm_best_encoder(struct drm_connector *connector)
238 {
239         struct drm_device *dev = connector->dev;
240         struct rockchip_drm_connector *rockchip_connector =
241                                         to_rockchip_connector(connector);
242         struct drm_mode_object *obj;
243         struct drm_encoder *encoder;
244
245         DRM_DEBUG_KMS("%s\n", __FILE__);
246
247         obj = drm_mode_object_find(dev, rockchip_connector->encoder_id,
248                                    DRM_MODE_OBJECT_ENCODER);
249         if (!obj) {
250                 DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
251                                 rockchip_connector->encoder_id);
252                 return NULL;
253         }
254
255         encoder = obj_to_encoder(obj);
256
257         return encoder;
258 }
259
260 static struct drm_connector_helper_funcs rockchip_connector_helper_funcs = {
261         .get_modes      = rockchip_drm_connector_get_modes,
262         .mode_valid     = rockchip_drm_connector_mode_valid,
263         .best_encoder   = rockchip_drm_best_encoder,
264 };
265
266 void rockchip_drm_display_power(struct drm_connector *connector, int mode)
267 {
268         struct drm_encoder *encoder = rockchip_drm_best_encoder(connector);
269         struct rockchip_drm_connector *rockchip_connector;
270         struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
271         struct rockchip_drm_display_ops *display_ops = manager->display_ops;
272
273         rockchip_connector = to_rockchip_connector(connector);
274
275         if (rockchip_connector->dpms == mode) {
276                 DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
277                 return;
278         }
279
280         if (display_ops && display_ops->power_on)
281                 display_ops->power_on(manager->dev, mode);
282
283         rockchip_connector->dpms = mode;
284 }
285
286 static void rockchip_drm_connector_dpms(struct drm_connector *connector,
287                                         int mode)
288 {
289         DRM_DEBUG_KMS("%s\n", __FILE__);
290
291         /*
292          * in case that drm_crtc_helper_set_mode() is called,
293          * encoder/crtc->funcs->dpms() will be just returned
294          * because they already were DRM_MODE_DPMS_ON so only
295          * rockchip_drm_display_power() will be called.
296          */
297         drm_helper_connector_dpms(connector, mode);
298
299         rockchip_drm_display_power(connector, mode);
300
301 }
302
303 static int rockchip_drm_connector_fill_modes(struct drm_connector *connector,
304                                 unsigned int max_width, unsigned int max_height)
305 {
306         struct rockchip_drm_connector *rockchip_connector =
307                                         to_rockchip_connector(connector);
308         struct rockchip_drm_manager *manager = rockchip_connector->manager;
309         struct rockchip_drm_manager_ops *ops = manager->ops;
310         unsigned int width, height;
311
312         width = max_width;
313         height = max_height;
314
315         /*
316          * if specific driver want to find desired_mode using maxmum
317          * resolution then get max width and height from that driver.
318          */
319         if (ops && ops->get_max_resol)
320                 ops->get_max_resol(manager->dev, &width, &height);
321
322         return drm_helper_probe_single_connector_modes(connector, width,
323                                                         height);
324 }
325
326 /* get detection status of display device. */
327 static enum drm_connector_status
328 rockchip_drm_connector_detect(struct drm_connector *connector, bool force)
329 {
330         struct rockchip_drm_connector *rockchip_connector =
331                                         to_rockchip_connector(connector);
332         struct rockchip_drm_manager *manager = rockchip_connector->manager;
333         struct rockchip_drm_display_ops *display_ops =
334                                         manager->display_ops;
335         enum drm_connector_status status = connector_status_disconnected;
336
337         DRM_DEBUG_KMS("%s\n", __FILE__);
338
339         if (display_ops && display_ops->is_connected) {
340                 if (display_ops->is_connected(manager->dev))
341                         status = connector_status_connected;
342                 else
343                         status = connector_status_disconnected;
344         }
345
346         return status;
347 }
348
349 static void rockchip_drm_connector_destroy(struct drm_connector *connector)
350 {
351         struct rockchip_drm_connector *rockchip_connector =
352                 to_rockchip_connector(connector);
353
354         DRM_DEBUG_KMS("%s\n", __FILE__);
355
356         drm_sysfs_connector_remove(connector);
357         drm_connector_cleanup(connector);
358         kfree(rockchip_connector);
359 }
360
361 static struct drm_connector_funcs rockchip_connector_funcs = {
362         .dpms           = rockchip_drm_connector_dpms,
363         .fill_modes     = rockchip_drm_connector_fill_modes,
364         .detect         = rockchip_drm_connector_detect,
365         .destroy        = rockchip_drm_connector_destroy,
366 };
367
368 struct drm_connector *rockchip_drm_connector_create(struct drm_device *dev,
369                                                    struct drm_encoder *encoder)
370 {
371         struct rockchip_drm_connector *rockchip_connector;
372         struct rockchip_drm_manager *manager = rockchip_drm_get_manager(encoder);
373         struct drm_connector *connector;
374         int type;
375         int err;
376
377         DRM_DEBUG_KMS("%s\n", __FILE__);
378
379         rockchip_connector = kzalloc(sizeof(*rockchip_connector), GFP_KERNEL);
380         if (!rockchip_connector) {
381                 DRM_ERROR("failed to allocate connector\n");
382                 return NULL;
383         }
384
385         connector = &rockchip_connector->drm_connector;
386
387         switch (manager->display_ops->type) {
388         case ROCKCHIP_DISPLAY_TYPE_HDMI:
389                 type = DRM_MODE_CONNECTOR_HDMIA;
390                 connector->interlace_allowed = true;
391                 connector->polled = DRM_CONNECTOR_POLL_HPD;
392                 break;
393         case ROCKCHIP_DISPLAY_TYPE_VIDI:
394                 type = DRM_MODE_CONNECTOR_VIRTUAL;
395                 connector->polled = DRM_CONNECTOR_POLL_HPD;
396                 break;
397         case ROCKCHIP_DISPLAY_TYPE_LCD:
398                 type = DRM_MODE_CONNECTOR_LVDS;
399                 break;
400         default:
401                 type = DRM_MODE_CONNECTOR_Unknown;
402                 break;
403         }
404
405         drm_connector_init(dev, connector, &rockchip_connector_funcs, type);
406         drm_connector_helper_add(connector, &rockchip_connector_helper_funcs);
407
408         err = drm_sysfs_connector_add(connector);
409         if (err)
410                 goto err_connector;
411
412         rockchip_connector->encoder_id = encoder->base.id;
413         rockchip_connector->manager = manager;
414         rockchip_connector->dpms = DRM_MODE_DPMS_OFF;
415         connector->dpms = DRM_MODE_DPMS_OFF;
416         connector->encoder = encoder;
417
418         err = drm_mode_connector_attach_encoder(connector, encoder);
419         if (err) {
420                 DRM_ERROR("failed to attach a connector to a encoder\n");
421                 goto err_sysfs;
422         }
423
424         DRM_DEBUG_KMS("connector has been created\n");
425
426         return connector;
427
428 err_sysfs:
429         drm_sysfs_connector_remove(connector);
430 err_connector:
431         drm_connector_cleanup(connector);
432         kfree(rockchip_connector);
433         return NULL;
434 }