drm/panel: add support for LG LD070WX3-SL01 panel
[linux-2.6-microblaze.git] / drivers / gpu / drm / msm / hdmi / hdmi_bridge.c
1 /*
2  * Copyright (C) 2013 Red Hat
3  * Author: Rob Clark <robdclark@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "hdmi.h"
19
20 struct hdmi_bridge {
21         struct drm_bridge base;
22
23         struct hdmi *hdmi;
24         bool power_on;
25
26         unsigned long int pixclock;
27 };
28 #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base)
29
30 static void hdmi_bridge_destroy(struct drm_bridge *bridge)
31 {
32         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
33         hdmi_unreference(hdmi_bridge->hdmi);
34         drm_bridge_cleanup(bridge);
35         kfree(hdmi_bridge);
36 }
37
38 static void power_on(struct drm_bridge *bridge)
39 {
40         struct drm_device *dev = bridge->dev;
41         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
42         struct hdmi *hdmi = hdmi_bridge->hdmi;
43         const struct hdmi_platform_config *config = hdmi->config;
44         int i, ret;
45
46         for (i = 0; i < config->pwr_reg_cnt; i++) {
47                 ret = regulator_enable(hdmi->pwr_regs[i]);
48                 if (ret) {
49                         dev_err(dev->dev, "failed to enable pwr regulator: %s (%d)\n",
50                                         config->pwr_reg_names[i], ret);
51                 }
52         }
53
54         if (config->pwr_clk_cnt > 0) {
55                 DBG("pixclock: %lu", hdmi_bridge->pixclock);
56                 ret = clk_set_rate(hdmi->pwr_clks[0], hdmi_bridge->pixclock);
57                 if (ret) {
58                         dev_err(dev->dev, "failed to set pixel clk: %s (%d)\n",
59                                         config->pwr_clk_names[0], ret);
60                 }
61         }
62
63         for (i = 0; i < config->pwr_clk_cnt; i++) {
64                 ret = clk_prepare_enable(hdmi->pwr_clks[i]);
65                 if (ret) {
66                         dev_err(dev->dev, "failed to enable pwr clk: %s (%d)\n",
67                                         config->pwr_clk_names[i], ret);
68                 }
69         }
70 }
71
72 static void power_off(struct drm_bridge *bridge)
73 {
74         struct drm_device *dev = bridge->dev;
75         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
76         struct hdmi *hdmi = hdmi_bridge->hdmi;
77         const struct hdmi_platform_config *config = hdmi->config;
78         int i, ret;
79
80         /* TODO do we need to wait for final vblank somewhere before
81          * cutting the clocks?
82          */
83         mdelay(16 + 4);
84
85         for (i = 0; i < config->pwr_clk_cnt; i++)
86                 clk_disable_unprepare(hdmi->pwr_clks[i]);
87
88         for (i = 0; i < config->pwr_reg_cnt; i++) {
89                 ret = regulator_disable(hdmi->pwr_regs[i]);
90                 if (ret) {
91                         dev_err(dev->dev, "failed to disable pwr regulator: %s (%d)\n",
92                                         config->pwr_reg_names[i], ret);
93                 }
94         }
95 }
96
97 static void hdmi_bridge_pre_enable(struct drm_bridge *bridge)
98 {
99         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
100         struct hdmi *hdmi = hdmi_bridge->hdmi;
101         struct hdmi_phy *phy = hdmi->phy;
102
103         DBG("power up");
104
105         if (!hdmi_bridge->power_on) {
106                 power_on(bridge);
107                 hdmi_bridge->power_on = true;
108         }
109
110         phy->funcs->powerup(phy, hdmi_bridge->pixclock);
111         hdmi_set_mode(hdmi, true);
112 }
113
114 static void hdmi_bridge_enable(struct drm_bridge *bridge)
115 {
116 }
117
118 static void hdmi_bridge_disable(struct drm_bridge *bridge)
119 {
120 }
121
122 static void hdmi_bridge_post_disable(struct drm_bridge *bridge)
123 {
124         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
125         struct hdmi *hdmi = hdmi_bridge->hdmi;
126         struct hdmi_phy *phy = hdmi->phy;
127
128         DBG("power down");
129         hdmi_set_mode(hdmi, false);
130         phy->funcs->powerdown(phy);
131
132         if (hdmi_bridge->power_on) {
133                 power_off(bridge);
134                 hdmi_bridge->power_on = false;
135         }
136 }
137
138 static void hdmi_bridge_mode_set(struct drm_bridge *bridge,
139                  struct drm_display_mode *mode,
140                  struct drm_display_mode *adjusted_mode)
141 {
142         struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge);
143         struct hdmi *hdmi = hdmi_bridge->hdmi;
144         int hstart, hend, vstart, vend;
145         uint32_t frame_ctrl;
146
147         mode = adjusted_mode;
148
149         hdmi_bridge->pixclock = mode->clock * 1000;
150
151         hdmi->hdmi_mode = drm_match_cea_mode(mode) > 1;
152
153         hstart = mode->htotal - mode->hsync_start;
154         hend   = mode->htotal - mode->hsync_start + mode->hdisplay;
155
156         vstart = mode->vtotal - mode->vsync_start - 1;
157         vend   = mode->vtotal - mode->vsync_start + mode->vdisplay - 1;
158
159         DBG("htotal=%d, vtotal=%d, hstart=%d, hend=%d, vstart=%d, vend=%d",
160                         mode->htotal, mode->vtotal, hstart, hend, vstart, vend);
161
162         hdmi_write(hdmi, REG_HDMI_TOTAL,
163                         HDMI_TOTAL_H_TOTAL(mode->htotal - 1) |
164                         HDMI_TOTAL_V_TOTAL(mode->vtotal - 1));
165
166         hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC,
167                         HDMI_ACTIVE_HSYNC_START(hstart) |
168                         HDMI_ACTIVE_HSYNC_END(hend));
169         hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC,
170                         HDMI_ACTIVE_VSYNC_START(vstart) |
171                         HDMI_ACTIVE_VSYNC_END(vend));
172
173         if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
174                 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
175                                 HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal));
176                 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
177                                 HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) |
178                                 HDMI_VSYNC_ACTIVE_F2_END(vend + 1));
179         } else {
180                 hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2,
181                                 HDMI_VSYNC_TOTAL_F2_V_TOTAL(0));
182                 hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2,
183                                 HDMI_VSYNC_ACTIVE_F2_START(0) |
184                                 HDMI_VSYNC_ACTIVE_F2_END(0));
185         }
186
187         frame_ctrl = 0;
188         if (mode->flags & DRM_MODE_FLAG_NHSYNC)
189                 frame_ctrl |= HDMI_FRAME_CTRL_HSYNC_LOW;
190         if (mode->flags & DRM_MODE_FLAG_NVSYNC)
191                 frame_ctrl |= HDMI_FRAME_CTRL_VSYNC_LOW;
192         if (mode->flags & DRM_MODE_FLAG_INTERLACE)
193                 frame_ctrl |= HDMI_FRAME_CTRL_INTERLACED_EN;
194         DBG("frame_ctrl=%08x", frame_ctrl);
195         hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);
196
197         // TODO until we have audio, this might be safest:
198         if (hdmi->hdmi_mode)
199                 hdmi_write(hdmi, REG_HDMI_GC, HDMI_GC_MUTE);
200 }
201
202 static const struct drm_bridge_funcs hdmi_bridge_funcs = {
203                 .pre_enable = hdmi_bridge_pre_enable,
204                 .enable = hdmi_bridge_enable,
205                 .disable = hdmi_bridge_disable,
206                 .post_disable = hdmi_bridge_post_disable,
207                 .mode_set = hdmi_bridge_mode_set,
208                 .destroy = hdmi_bridge_destroy,
209 };
210
211
212 /* initialize bridge */
213 struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi)
214 {
215         struct drm_bridge *bridge = NULL;
216         struct hdmi_bridge *hdmi_bridge;
217         int ret;
218
219         hdmi_bridge = kzalloc(sizeof(*hdmi_bridge), GFP_KERNEL);
220         if (!hdmi_bridge) {
221                 ret = -ENOMEM;
222                 goto fail;
223         }
224
225         hdmi_bridge->hdmi = hdmi_reference(hdmi);
226
227         bridge = &hdmi_bridge->base;
228
229         drm_bridge_init(hdmi->dev, bridge, &hdmi_bridge_funcs);
230
231         return bridge;
232
233 fail:
234         if (bridge)
235                 hdmi_bridge_destroy(bridge);
236
237         return ERR_PTR(ret);
238 }