Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / gpu / drm / msm / mdp / mdp4 / mdp4_lvds_connector.c
1 /*
2  * Copyright (C) 2014 Red Hat
3  * Author: Rob Clark <robdclark@gmail.com>
4  * Author: Vinay Simha <vinaysimha@inforcecomputing.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published by
8  * the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <linux/gpio.h>
20
21 #include "mdp4_kms.h"
22
23 struct mdp4_lvds_connector {
24         struct drm_connector base;
25         struct drm_encoder *encoder;
26         struct drm_panel *panel;
27 };
28 #define to_mdp4_lvds_connector(x) container_of(x, struct mdp4_lvds_connector, base)
29
30 static enum drm_connector_status mdp4_lvds_connector_detect(
31                 struct drm_connector *connector, bool force)
32 {
33         struct mdp4_lvds_connector *mdp4_lvds_connector =
34                         to_mdp4_lvds_connector(connector);
35
36         return mdp4_lvds_connector->panel ?
37                         connector_status_connected :
38                         connector_status_disconnected;
39 }
40
41 static void mdp4_lvds_connector_destroy(struct drm_connector *connector)
42 {
43         struct mdp4_lvds_connector *mdp4_lvds_connector =
44                         to_mdp4_lvds_connector(connector);
45         struct drm_panel *panel = mdp4_lvds_connector->panel;
46
47         if (panel)
48                 drm_panel_detach(panel);
49
50         drm_connector_unregister(connector);
51         drm_connector_cleanup(connector);
52
53         kfree(mdp4_lvds_connector);
54 }
55
56 static int mdp4_lvds_connector_get_modes(struct drm_connector *connector)
57 {
58         struct mdp4_lvds_connector *mdp4_lvds_connector =
59                         to_mdp4_lvds_connector(connector);
60         struct drm_panel *panel = mdp4_lvds_connector->panel;
61         int ret = 0;
62
63         if (panel)
64                 ret = panel->funcs->get_modes(panel);
65
66         return ret;
67 }
68
69 static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
70                                  struct drm_display_mode *mode)
71 {
72         struct mdp4_lvds_connector *mdp4_lvds_connector =
73                         to_mdp4_lvds_connector(connector);
74         struct drm_encoder *encoder = mdp4_lvds_connector->encoder;
75         long actual, requested;
76
77         requested = 1000 * mode->clock;
78         actual = mdp4_lcdc_round_pixclk(encoder, requested);
79
80         DBG("requested=%ld, actual=%ld", requested, actual);
81
82         if (actual != requested)
83                 return MODE_CLOCK_RANGE;
84
85         return MODE_OK;
86 }
87
88 static struct drm_encoder *
89 mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
90 {
91         struct mdp4_lvds_connector *mdp4_lvds_connector =
92                         to_mdp4_lvds_connector(connector);
93         return mdp4_lvds_connector->encoder;
94 }
95
96 static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
97         .dpms = drm_atomic_helper_connector_dpms,
98         .detect = mdp4_lvds_connector_detect,
99         .fill_modes = drm_helper_probe_single_connector_modes,
100         .destroy = mdp4_lvds_connector_destroy,
101         .reset = drm_atomic_helper_connector_reset,
102         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
103         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
104 };
105
106 static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
107         .get_modes = mdp4_lvds_connector_get_modes,
108         .mode_valid = mdp4_lvds_connector_mode_valid,
109         .best_encoder = mdp4_lvds_connector_best_encoder,
110 };
111
112 /* initialize connector */
113 struct drm_connector *mdp4_lvds_connector_init(struct drm_device *dev,
114                 struct drm_panel *panel, struct drm_encoder *encoder)
115 {
116         struct drm_connector *connector = NULL;
117         struct mdp4_lvds_connector *mdp4_lvds_connector;
118         int ret;
119
120         mdp4_lvds_connector = kzalloc(sizeof(*mdp4_lvds_connector), GFP_KERNEL);
121         if (!mdp4_lvds_connector) {
122                 ret = -ENOMEM;
123                 goto fail;
124         }
125
126         mdp4_lvds_connector->encoder = encoder;
127         mdp4_lvds_connector->panel = panel;
128
129         connector = &mdp4_lvds_connector->base;
130
131         drm_connector_init(dev, connector, &mdp4_lvds_connector_funcs,
132                         DRM_MODE_CONNECTOR_LVDS);
133         drm_connector_helper_add(connector, &mdp4_lvds_connector_helper_funcs);
134
135         connector->polled = 0;
136
137         connector->interlace_allowed = 0;
138         connector->doublescan_allowed = 0;
139
140         drm_connector_register(connector);
141
142         drm_mode_connector_attach_encoder(connector, encoder);
143
144         if (panel)
145                 drm_panel_attach(panel, connector);
146
147         return connector;
148
149 fail:
150         if (connector)
151                 mdp4_lvds_connector_destroy(connector);
152
153         return ERR_PTR(ret);
154 }