Commit | Line | Data |
---|---|---|
06c0dd96 | 1 | /* |
00453981 | 2 | * Copyright (c) 2014, The Linux Foundation. All rights reserved. |
06c0dd96 RC |
3 | * Copyright (C) 2013 Red Hat |
4 | * Author: Rob Clark <robdclark@gmail.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 "mdp5_kms.h" | |
20 | ||
21 | #include "drm_crtc.h" | |
22 | #include "drm_crtc_helper.h" | |
23 | ||
24 | struct mdp5_encoder { | |
25 | struct drm_encoder base; | |
26 | int intf; | |
27 | enum mdp5_intf intf_id; | |
0deed25b | 28 | spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */ |
06c0dd96 RC |
29 | bool enabled; |
30 | uint32_t bsc; | |
31 | }; | |
32 | #define to_mdp5_encoder(x) container_of(x, struct mdp5_encoder, base) | |
33 | ||
34 | static struct mdp5_kms *get_kms(struct drm_encoder *encoder) | |
35 | { | |
36 | struct msm_drm_private *priv = encoder->dev->dev_private; | |
37 | return to_mdp5_kms(to_mdp_kms(priv->kms)); | |
38 | } | |
39 | ||
40 | #ifdef CONFIG_MSM_BUS_SCALING | |
41 | #include <mach/board.h> | |
42 | #include <mach/msm_bus.h> | |
43 | #include <mach/msm_bus_board.h> | |
44 | #define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ | |
45 | { \ | |
46 | .src = MSM_BUS_MASTER_MDP_PORT0, \ | |
47 | .dst = MSM_BUS_SLAVE_EBI_CH0, \ | |
48 | .ab = (ab_val), \ | |
49 | .ib = (ib_val), \ | |
50 | } | |
51 | ||
52 | static struct msm_bus_vectors mdp_bus_vectors[] = { | |
53 | MDP_BUS_VECTOR_ENTRY(0, 0), | |
54 | MDP_BUS_VECTOR_ENTRY(2000000000, 2000000000), | |
55 | }; | |
56 | static struct msm_bus_paths mdp_bus_usecases[] = { { | |
57 | .num_paths = 1, | |
58 | .vectors = &mdp_bus_vectors[0], | |
59 | }, { | |
60 | .num_paths = 1, | |
61 | .vectors = &mdp_bus_vectors[1], | |
62 | } }; | |
63 | static struct msm_bus_scale_pdata mdp_bus_scale_table = { | |
64 | .usecase = mdp_bus_usecases, | |
65 | .num_usecases = ARRAY_SIZE(mdp_bus_usecases), | |
66 | .name = "mdss_mdp", | |
67 | }; | |
68 | ||
69 | static void bs_init(struct mdp5_encoder *mdp5_encoder) | |
70 | { | |
71 | mdp5_encoder->bsc = msm_bus_scale_register_client( | |
72 | &mdp_bus_scale_table); | |
73 | DBG("bus scale client: %08x", mdp5_encoder->bsc); | |
74 | } | |
75 | ||
76 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) | |
77 | { | |
78 | if (mdp5_encoder->bsc) { | |
79 | msm_bus_scale_unregister_client(mdp5_encoder->bsc); | |
80 | mdp5_encoder->bsc = 0; | |
81 | } | |
82 | } | |
83 | ||
84 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) | |
85 | { | |
86 | if (mdp5_encoder->bsc) { | |
87 | DBG("set bus scaling: %d", idx); | |
88 | /* HACK: scaling down, and then immediately back up | |
89 | * seems to leave things broken (underflow).. so | |
90 | * never disable: | |
91 | */ | |
92 | idx = 1; | |
93 | msm_bus_scale_client_update_request(mdp5_encoder->bsc, idx); | |
94 | } | |
95 | } | |
96 | #else | |
97 | static void bs_init(struct mdp5_encoder *mdp5_encoder) {} | |
98 | static void bs_fini(struct mdp5_encoder *mdp5_encoder) {} | |
99 | static void bs_set(struct mdp5_encoder *mdp5_encoder, int idx) {} | |
100 | #endif | |
101 | ||
102 | static void mdp5_encoder_destroy(struct drm_encoder *encoder) | |
103 | { | |
104 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
105 | bs_fini(mdp5_encoder); | |
106 | drm_encoder_cleanup(encoder); | |
107 | kfree(mdp5_encoder); | |
108 | } | |
109 | ||
110 | static const struct drm_encoder_funcs mdp5_encoder_funcs = { | |
111 | .destroy = mdp5_encoder_destroy, | |
112 | }; | |
113 | ||
06c0dd96 RC |
114 | static bool mdp5_encoder_mode_fixup(struct drm_encoder *encoder, |
115 | const struct drm_display_mode *mode, | |
116 | struct drm_display_mode *adjusted_mode) | |
117 | { | |
118 | return true; | |
119 | } | |
120 | ||
121 | static void mdp5_encoder_mode_set(struct drm_encoder *encoder, | |
122 | struct drm_display_mode *mode, | |
123 | struct drm_display_mode *adjusted_mode) | |
124 | { | |
125 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
126 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
00453981 HL |
127 | struct drm_device *dev = encoder->dev; |
128 | struct drm_connector *connector; | |
06c0dd96 RC |
129 | int intf = mdp5_encoder->intf; |
130 | uint32_t dtv_hsync_skew, vsync_period, vsync_len, ctrl_pol; | |
131 | uint32_t display_v_start, display_v_end; | |
132 | uint32_t hsync_start_x, hsync_end_x; | |
00453981 | 133 | uint32_t format = 0x2100; |
0deed25b | 134 | unsigned long flags; |
06c0dd96 RC |
135 | |
136 | mode = adjusted_mode; | |
137 | ||
138 | DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", | |
139 | mode->base.id, mode->name, | |
140 | mode->vrefresh, mode->clock, | |
141 | mode->hdisplay, mode->hsync_start, | |
142 | mode->hsync_end, mode->htotal, | |
143 | mode->vdisplay, mode->vsync_start, | |
144 | mode->vsync_end, mode->vtotal, | |
145 | mode->type, mode->flags); | |
146 | ||
147 | ctrl_pol = 0; | |
148 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | |
149 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_HSYNC_LOW; | |
150 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | |
151 | ctrl_pol |= MDP5_INTF_POLARITY_CTL_VSYNC_LOW; | |
152 | /* probably need to get DATA_EN polarity from panel.. */ | |
153 | ||
154 | dtv_hsync_skew = 0; /* get this from panel? */ | |
00453981 HL |
155 | |
156 | /* Get color format from panel, default is 8bpc */ | |
157 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
158 | if (connector->encoder == encoder) { | |
159 | switch (connector->display_info.bpc) { | |
160 | case 4: | |
161 | format |= 0; | |
162 | break; | |
163 | case 5: | |
164 | format |= 0x15; | |
165 | break; | |
166 | case 6: | |
167 | format |= 0x2A; | |
168 | break; | |
169 | case 8: | |
170 | default: | |
171 | format |= 0x3F; | |
172 | break; | |
173 | } | |
174 | break; | |
175 | } | |
176 | } | |
06c0dd96 RC |
177 | |
178 | hsync_start_x = (mode->htotal - mode->hsync_start); | |
179 | hsync_end_x = mode->htotal - (mode->hsync_start - mode->hdisplay) - 1; | |
180 | ||
181 | vsync_period = mode->vtotal * mode->htotal; | |
182 | vsync_len = (mode->vsync_end - mode->vsync_start) * mode->htotal; | |
183 | display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew; | |
184 | display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1; | |
185 | ||
00453981 HL |
186 | /* |
187 | * For edp only: | |
188 | * DISPLAY_V_START = (VBP * HCYCLE) + HBP | |
189 | * DISPLAY_V_END = (VBP + VACTIVE) * HCYCLE - 1 - HFP | |
190 | */ | |
191 | if (mdp5_encoder->intf_id == INTF_eDP) { | |
192 | display_v_start += mode->htotal - mode->hsync_start; | |
193 | display_v_end -= mode->hsync_start - mode->hdisplay; | |
194 | } | |
195 | ||
0deed25b SV |
196 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); |
197 | ||
06c0dd96 RC |
198 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf), |
199 | MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) | | |
200 | MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal)); | |
201 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_PERIOD_F0(intf), vsync_period); | |
202 | mdp5_write(mdp5_kms, REG_MDP5_INTF_VSYNC_LEN_F0(intf), vsync_len); | |
203 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_HCTL(intf), | |
204 | MDP5_INTF_DISPLAY_HCTL_START(hsync_start_x) | | |
205 | MDP5_INTF_DISPLAY_HCTL_END(hsync_end_x)); | |
206 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VSTART_F0(intf), display_v_start); | |
207 | mdp5_write(mdp5_kms, REG_MDP5_INTF_DISPLAY_VEND_F0(intf), display_v_end); | |
208 | mdp5_write(mdp5_kms, REG_MDP5_INTF_BORDER_COLOR(intf), 0); | |
209 | mdp5_write(mdp5_kms, REG_MDP5_INTF_UNDERFLOW_COLOR(intf), 0xff); | |
210 | mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_SKEW(intf), dtv_hsync_skew); | |
211 | mdp5_write(mdp5_kms, REG_MDP5_INTF_POLARITY_CTL(intf), ctrl_pol); | |
212 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_HCTL(intf), | |
213 | MDP5_INTF_ACTIVE_HCTL_START(0) | | |
214 | MDP5_INTF_ACTIVE_HCTL_END(0)); | |
215 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VSTART_F0(intf), 0); | |
216 | mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0); | |
217 | mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format); | |
218 | mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3); /* frame+line? */ | |
0deed25b SV |
219 | |
220 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); | |
06c0dd96 RC |
221 | } |
222 | ||
0b776d45 | 223 | static void mdp5_encoder_disable(struct drm_encoder *encoder) |
06c0dd96 | 224 | { |
0b776d45 RC |
225 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); |
226 | struct mdp5_kms *mdp5_kms = get_kms(encoder); | |
227 | int intf = mdp5_encoder->intf; | |
228 | unsigned long flags; | |
229 | ||
230 | if (WARN_ON(!mdp5_encoder->enabled)) | |
231 | return; | |
232 | ||
233 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); | |
234 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 0); | |
235 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); | |
236 | ||
237 | /* | |
238 | * Wait for a vsync so we know the ENABLE=0 latched before | |
239 | * the (connector) source of the vsync's gets disabled, | |
240 | * otherwise we end up in a funny state if we re-enable | |
241 | * before the disable latches, which results that some of | |
242 | * the settings changes for the new modeset (like new | |
243 | * scanout buffer) don't latch properly.. | |
244 | */ | |
245 | mdp_irq_wait(&mdp5_kms->base, intf2vblank(intf)); | |
246 | ||
247 | bs_set(mdp5_encoder, 0); | |
248 | ||
249 | mdp5_encoder->enabled = false; | |
06c0dd96 RC |
250 | } |
251 | ||
0b776d45 | 252 | static void mdp5_encoder_enable(struct drm_encoder *encoder) |
06c0dd96 RC |
253 | { |
254 | struct mdp5_encoder *mdp5_encoder = to_mdp5_encoder(encoder); | |
0b776d45 RC |
255 | struct mdp5_kms *mdp5_kms = get_kms(encoder); |
256 | int intf = mdp5_encoder->intf; | |
257 | unsigned long flags; | |
258 | ||
259 | if (WARN_ON(mdp5_encoder->enabled)) | |
260 | return; | |
261 | ||
06c0dd96 RC |
262 | mdp5_crtc_set_intf(encoder->crtc, mdp5_encoder->intf, |
263 | mdp5_encoder->intf_id); | |
0b776d45 RC |
264 | |
265 | bs_set(mdp5_encoder, 1); | |
266 | spin_lock_irqsave(&mdp5_encoder->intf_lock, flags); | |
267 | mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1); | |
268 | spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags); | |
269 | ||
5db0f6e8 | 270 | mdp5_encoder->enabled = true; |
06c0dd96 RC |
271 | } |
272 | ||
273 | static const struct drm_encoder_helper_funcs mdp5_encoder_helper_funcs = { | |
06c0dd96 RC |
274 | .mode_fixup = mdp5_encoder_mode_fixup, |
275 | .mode_set = mdp5_encoder_mode_set, | |
5db0f6e8 SV |
276 | .disable = mdp5_encoder_disable, |
277 | .enable = mdp5_encoder_enable, | |
06c0dd96 RC |
278 | }; |
279 | ||
280 | /* initialize encoder */ | |
281 | struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, int intf, | |
282 | enum mdp5_intf intf_id) | |
283 | { | |
284 | struct drm_encoder *encoder = NULL; | |
285 | struct mdp5_encoder *mdp5_encoder; | |
286 | int ret; | |
287 | ||
288 | mdp5_encoder = kzalloc(sizeof(*mdp5_encoder), GFP_KERNEL); | |
289 | if (!mdp5_encoder) { | |
290 | ret = -ENOMEM; | |
291 | goto fail; | |
292 | } | |
293 | ||
294 | mdp5_encoder->intf = intf; | |
295 | mdp5_encoder->intf_id = intf_id; | |
296 | encoder = &mdp5_encoder->base; | |
297 | ||
0deed25b SV |
298 | spin_lock_init(&mdp5_encoder->intf_lock); |
299 | ||
06c0dd96 RC |
300 | drm_encoder_init(dev, encoder, &mdp5_encoder_funcs, |
301 | DRM_MODE_ENCODER_TMDS); | |
302 | drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs); | |
303 | ||
304 | bs_init(mdp5_encoder); | |
305 | ||
306 | return encoder; | |
307 | ||
308 | fail: | |
309 | if (encoder) | |
310 | mdp5_encoder_destroy(encoder); | |
311 | ||
312 | return ERR_PTR(ret); | |
313 | } |