]>
Commit | Line | Data |
---|---|---|
c8afe684 RC |
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 | ||
19 | #include "msm_drv.h" | |
871d812a | 20 | #include "msm_mmu.h" |
c8afe684 RC |
21 | #include "mdp4_kms.h" |
22 | ||
c8afe684 RC |
23 | static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev); |
24 | ||
25 | static int mdp4_hw_init(struct msm_kms *kms) | |
26 | { | |
9e0efa63 | 27 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); |
c8afe684 RC |
28 | struct drm_device *dev = mdp4_kms->dev; |
29 | uint32_t version, major, minor, dmap_cfg, vg_cfg; | |
30 | unsigned long clk; | |
31 | int ret = 0; | |
32 | ||
33 | pm_runtime_get_sync(dev->dev); | |
34 | ||
e529c7e6 | 35 | mdp4_enable(mdp4_kms); |
c8afe684 | 36 | version = mdp4_read(mdp4_kms, REG_MDP4_VERSION); |
e529c7e6 | 37 | mdp4_disable(mdp4_kms); |
c8afe684 RC |
38 | |
39 | major = FIELD(version, MDP4_VERSION_MAJOR); | |
40 | minor = FIELD(version, MDP4_VERSION_MINOR); | |
41 | ||
dada25bd | 42 | DBG("found MDP4 version v%d.%d", major, minor); |
c8afe684 RC |
43 | |
44 | if (major != 4) { | |
45 | dev_err(dev->dev, "unexpected MDP version: v%d.%d\n", | |
46 | major, minor); | |
47 | ret = -ENXIO; | |
48 | goto out; | |
49 | } | |
50 | ||
51 | mdp4_kms->rev = minor; | |
52 | ||
c8afe684 RC |
53 | if (mdp4_kms->rev > 1) { |
54 | mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff); | |
55 | mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f); | |
56 | } | |
57 | ||
58 | mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3); | |
59 | ||
60 | /* max read pending cmd config, 3 pending requests: */ | |
61 | mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222); | |
62 | ||
63 | clk = clk_get_rate(mdp4_kms->clk); | |
64 | ||
65 | if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) { | |
66 | dmap_cfg = 0x47; /* 16 bytes-burst x 8 req */ | |
67 | vg_cfg = 0x47; /* 16 bytes-burs x 8 req */ | |
68 | } else { | |
69 | dmap_cfg = 0x27; /* 8 bytes-burst x 8 req */ | |
70 | vg_cfg = 0x43; /* 16 bytes-burst x 4 req */ | |
71 | } | |
72 | ||
73 | DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg); | |
74 | ||
75 | mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg); | |
76 | mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg); | |
77 | ||
78 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg); | |
79 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg); | |
80 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg); | |
81 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg); | |
82 | ||
83 | if (mdp4_kms->rev >= 2) | |
84 | mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1); | |
d65bd0e4 | 85 | mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG, 0); |
c8afe684 RC |
86 | |
87 | /* disable CSC matrix / YUV by default: */ | |
88 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0); | |
89 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0); | |
90 | mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0); | |
91 | mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0); | |
92 | mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0); | |
93 | mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0); | |
94 | ||
95 | if (mdp4_kms->rev > 1) | |
96 | mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1); | |
97 | ||
570655b0 RC |
98 | dev->mode_config.allow_fb_modifiers = true; |
99 | ||
c8afe684 RC |
100 | out: |
101 | pm_runtime_put_sync(dev->dev); | |
102 | ||
103 | return ret; | |
104 | } | |
105 | ||
0b776d45 RC |
106 | static void mdp4_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state) |
107 | { | |
108 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); | |
109 | int i, ncrtcs = state->dev->mode_config.num_crtc; | |
110 | ||
111 | mdp4_enable(mdp4_kms); | |
112 | ||
113 | /* see 119ecb7fd */ | |
114 | for (i = 0; i < ncrtcs; i++) { | |
115 | struct drm_crtc *crtc = state->crtcs[i]; | |
116 | if (!crtc) | |
117 | continue; | |
118 | drm_crtc_vblank_get(crtc); | |
119 | } | |
120 | } | |
121 | ||
122 | static void mdp4_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state) | |
123 | { | |
124 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); | |
125 | int i, ncrtcs = state->dev->mode_config.num_crtc; | |
126 | ||
127 | /* see 119ecb7fd */ | |
128 | for (i = 0; i < ncrtcs; i++) { | |
129 | struct drm_crtc *crtc = state->crtcs[i]; | |
130 | if (!crtc) | |
131 | continue; | |
132 | drm_crtc_vblank_put(crtc); | |
133 | } | |
134 | ||
135 | mdp4_disable(mdp4_kms); | |
136 | } | |
137 | ||
0a5c9aad HL |
138 | static void mdp4_wait_for_crtc_commit_done(struct msm_kms *kms, |
139 | struct drm_crtc *crtc) | |
140 | { | |
141 | mdp4_crtc_wait_for_commit_done(crtc); | |
142 | } | |
143 | ||
c8afe684 RC |
144 | static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, |
145 | struct drm_encoder *encoder) | |
146 | { | |
147 | /* if we had >1 encoder, we'd need something more clever: */ | |
9696acbc AT |
148 | switch (encoder->encoder_type) { |
149 | case DRM_MODE_ENCODER_TMDS: | |
150 | return mdp4_dtv_round_pixclk(encoder, rate); | |
151 | case DRM_MODE_ENCODER_LVDS: | |
152 | case DRM_MODE_ENCODER_DSI: | |
153 | default: | |
154 | return rate; | |
155 | } | |
c8afe684 RC |
156 | } |
157 | ||
f7590205 S |
158 | static const char * const iommu_ports[] = { |
159 | "mdp_port0_cb0", "mdp_port1_cb0", | |
160 | }; | |
161 | ||
c8afe684 RC |
162 | static void mdp4_destroy(struct msm_kms *kms) |
163 | { | |
9e0efa63 | 164 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); |
f7590205 S |
165 | struct msm_mmu *mmu = mdp4_kms->mmu; |
166 | ||
167 | if (mmu) { | |
168 | mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); | |
169 | mmu->funcs->destroy(mmu); | |
170 | } | |
171 | ||
7d8d9f67 RC |
172 | if (mdp4_kms->blank_cursor_iova) |
173 | msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); | |
174 | if (mdp4_kms->blank_cursor_bo) | |
036c1708 | 175 | drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo); |
c8afe684 RC |
176 | kfree(mdp4_kms); |
177 | } | |
178 | ||
9e0efa63 RC |
179 | static const struct mdp_kms_funcs kms_funcs = { |
180 | .base = { | |
c8afe684 RC |
181 | .hw_init = mdp4_hw_init, |
182 | .irq_preinstall = mdp4_irq_preinstall, | |
183 | .irq_postinstall = mdp4_irq_postinstall, | |
184 | .irq_uninstall = mdp4_irq_uninstall, | |
185 | .irq = mdp4_irq, | |
186 | .enable_vblank = mdp4_enable_vblank, | |
187 | .disable_vblank = mdp4_disable_vblank, | |
0b776d45 RC |
188 | .prepare_commit = mdp4_prepare_commit, |
189 | .complete_commit = mdp4_complete_commit, | |
0a5c9aad | 190 | .wait_for_crtc_commit_done = mdp4_wait_for_crtc_commit_done, |
10a02eb6 | 191 | .get_format = mdp_get_format, |
c8afe684 | 192 | .round_pixclk = mdp4_round_pixclk, |
c8afe684 | 193 | .destroy = mdp4_destroy, |
9e0efa63 RC |
194 | }, |
195 | .set_irqmask = mdp4_set_irqmask, | |
c8afe684 RC |
196 | }; |
197 | ||
198 | int mdp4_disable(struct mdp4_kms *mdp4_kms) | |
199 | { | |
200 | DBG(""); | |
201 | ||
202 | clk_disable_unprepare(mdp4_kms->clk); | |
203 | if (mdp4_kms->pclk) | |
204 | clk_disable_unprepare(mdp4_kms->pclk); | |
205 | clk_disable_unprepare(mdp4_kms->lut_clk); | |
e8abb5b5 RC |
206 | if (mdp4_kms->axi_clk) |
207 | clk_disable_unprepare(mdp4_kms->axi_clk); | |
c8afe684 RC |
208 | |
209 | return 0; | |
210 | } | |
211 | ||
212 | int mdp4_enable(struct mdp4_kms *mdp4_kms) | |
213 | { | |
214 | DBG(""); | |
215 | ||
216 | clk_prepare_enable(mdp4_kms->clk); | |
217 | if (mdp4_kms->pclk) | |
218 | clk_prepare_enable(mdp4_kms->pclk); | |
219 | clk_prepare_enable(mdp4_kms->lut_clk); | |
e8abb5b5 RC |
220 | if (mdp4_kms->axi_clk) |
221 | clk_prepare_enable(mdp4_kms->axi_clk); | |
c8afe684 RC |
222 | |
223 | return 0; | |
224 | } | |
225 | ||
a6bf7f63 | 226 | static struct device_node *mdp4_detect_lcdc_panel(struct drm_device *dev) |
3e87599b | 227 | { |
3d6df062 AT |
228 | struct device_node *endpoint, *panel_node; |
229 | struct device_node *np = dev->dev->of_node; | |
3e87599b | 230 | |
3d6df062 AT |
231 | endpoint = of_graph_get_next_endpoint(np, NULL); |
232 | if (!endpoint) { | |
a6bf7f63 AT |
233 | DBG("no endpoint in MDP4 to fetch LVDS panel\n"); |
234 | return NULL; | |
3d6df062 AT |
235 | } |
236 | ||
a6bf7f63 | 237 | /* don't proceed if we have an endpoint but no panel_node tied to it */ |
3d6df062 AT |
238 | panel_node = of_graph_get_remote_port_parent(endpoint); |
239 | if (!panel_node) { | |
240 | dev_err(dev->dev, "no valid panel node\n"); | |
241 | of_node_put(endpoint); | |
242 | return ERR_PTR(-ENODEV); | |
243 | } | |
244 | ||
245 | of_node_put(endpoint); | |
246 | ||
a6bf7f63 | 247 | return panel_node; |
3e87599b | 248 | } |
3e87599b | 249 | |
ec141af6 AT |
250 | static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, |
251 | int intf_type) | |
c8afe684 RC |
252 | { |
253 | struct drm_device *dev = mdp4_kms->dev; | |
254 | struct msm_drm_private *priv = dev->dev_private; | |
c8afe684 | 255 | struct drm_encoder *encoder; |
3e87599b | 256 | struct drm_connector *connector; |
a6bf7f63 | 257 | struct device_node *panel_node; |
af6d0423 AT |
258 | struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM]; |
259 | int i, dsi_id; | |
c8afe684 RC |
260 | int ret; |
261 | ||
ec141af6 AT |
262 | switch (intf_type) { |
263 | case DRM_MODE_ENCODER_LVDS: | |
a6bf7f63 AT |
264 | /* |
265 | * bail out early if: | |
266 | * - there is no panel node (no need to initialize lcdc | |
267 | * encoder and lvds connector), or | |
268 | * - panel node is a bad pointer | |
269 | */ | |
270 | panel_node = mdp4_detect_lcdc_panel(dev); | |
271 | if (IS_ERR_OR_NULL(panel_node)) | |
272 | return PTR_ERR(panel_node); | |
273 | ||
274 | encoder = mdp4_lcdc_encoder_init(dev, panel_node); | |
ec141af6 AT |
275 | if (IS_ERR(encoder)) { |
276 | dev_err(dev->dev, "failed to construct LCDC encoder\n"); | |
277 | return PTR_ERR(encoder); | |
278 | } | |
a8623918 | 279 | |
ec141af6 AT |
280 | /* LCDC can be hooked to DMA_P (TODO: Add DMA_S later?) */ |
281 | encoder->possible_crtcs = 1 << DMA_P; | |
3e87599b | 282 | |
a6bf7f63 | 283 | connector = mdp4_lvds_connector_init(dev, panel_node, encoder); |
ec141af6 AT |
284 | if (IS_ERR(connector)) { |
285 | dev_err(dev->dev, "failed to initialize LVDS connector\n"); | |
286 | return PTR_ERR(connector); | |
287 | } | |
3e87599b | 288 | |
ec141af6 AT |
289 | priv->encoders[priv->num_encoders++] = encoder; |
290 | priv->connectors[priv->num_connectors++] = connector; | |
3e87599b | 291 | |
ec141af6 AT |
292 | break; |
293 | case DRM_MODE_ENCODER_TMDS: | |
294 | encoder = mdp4_dtv_encoder_init(dev); | |
295 | if (IS_ERR(encoder)) { | |
296 | dev_err(dev->dev, "failed to construct DTV encoder\n"); | |
297 | return PTR_ERR(encoder); | |
298 | } | |
3e87599b | 299 | |
ec141af6 AT |
300 | /* DTV can be hooked to DMA_E: */ |
301 | encoder->possible_crtcs = 1 << 1; | |
3e87599b | 302 | |
ec141af6 AT |
303 | if (priv->hdmi) { |
304 | /* Construct bridge/connector for HDMI: */ | |
fcda50c8 | 305 | ret = msm_hdmi_modeset_init(priv->hdmi, dev, encoder); |
ec141af6 AT |
306 | if (ret) { |
307 | dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); | |
308 | return ret; | |
309 | } | |
310 | } | |
3e87599b | 311 | |
ec141af6 | 312 | priv->encoders[priv->num_encoders++] = encoder; |
3e87599b | 313 | |
af6d0423 AT |
314 | break; |
315 | case DRM_MODE_ENCODER_DSI: | |
316 | /* only DSI1 supported for now */ | |
317 | dsi_id = 0; | |
318 | ||
319 | if (!priv->dsi[dsi_id]) | |
320 | break; | |
321 | ||
322 | for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) { | |
323 | dsi_encs[i] = mdp4_dsi_encoder_init(dev); | |
324 | if (IS_ERR(dsi_encs[i])) { | |
325 | ret = PTR_ERR(dsi_encs[i]); | |
326 | dev_err(dev->dev, | |
327 | "failed to construct DSI encoder: %d\n", | |
328 | ret); | |
329 | return ret; | |
330 | } | |
331 | ||
332 | /* TODO: Add DMA_S later? */ | |
333 | dsi_encs[i]->possible_crtcs = 1 << DMA_P; | |
334 | priv->encoders[priv->num_encoders++] = dsi_encs[i]; | |
335 | } | |
336 | ||
337 | ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs); | |
338 | if (ret) { | |
339 | dev_err(dev->dev, "failed to initialize DSI: %d\n", | |
340 | ret); | |
341 | return ret; | |
342 | } | |
343 | ||
ec141af6 AT |
344 | break; |
345 | default: | |
346 | dev_err(dev->dev, "Invalid or unsupported interface\n"); | |
347 | return -EINVAL; | |
3e87599b RC |
348 | } |
349 | ||
ec141af6 AT |
350 | return 0; |
351 | } | |
3e87599b | 352 | |
ec141af6 AT |
353 | static int modeset_init(struct mdp4_kms *mdp4_kms) |
354 | { | |
355 | struct drm_device *dev = mdp4_kms->dev; | |
356 | struct msm_drm_private *priv = dev->dev_private; | |
357 | struct drm_plane *plane; | |
358 | struct drm_crtc *crtc; | |
359 | int i, ret; | |
360 | static const enum mdp4_pipe rgb_planes[] = { | |
361 | RGB1, RGB2, | |
362 | }; | |
363 | static const enum mdp4_pipe vg_planes[] = { | |
364 | VG1, VG2, | |
365 | }; | |
366 | static const enum mdp4_dma mdp4_crtcs[] = { | |
367 | DMA_P, DMA_E, | |
368 | }; | |
369 | static const char * const mdp4_crtc_names[] = { | |
370 | "DMA_P", "DMA_E", | |
371 | }; | |
372 | static const int mdp4_intfs[] = { | |
373 | DRM_MODE_ENCODER_LVDS, | |
af6d0423 | 374 | DRM_MODE_ENCODER_DSI, |
ec141af6 AT |
375 | DRM_MODE_ENCODER_TMDS, |
376 | }; | |
c8afe684 | 377 | |
ec141af6 AT |
378 | /* construct non-private planes: */ |
379 | for (i = 0; i < ARRAY_SIZE(vg_planes); i++) { | |
380 | plane = mdp4_plane_init(dev, vg_planes[i], false); | |
381 | if (IS_ERR(plane)) { | |
382 | dev_err(dev->dev, | |
383 | "failed to construct plane for VG%d\n", i + 1); | |
384 | ret = PTR_ERR(plane); | |
385 | goto fail; | |
386 | } | |
387 | priv->planes[priv->num_planes++] = plane; | |
c8afe684 | 388 | } |
c8afe684 | 389 | |
ec141af6 AT |
390 | for (i = 0; i < ARRAY_SIZE(mdp4_crtcs); i++) { |
391 | plane = mdp4_plane_init(dev, rgb_planes[i], true); | |
392 | if (IS_ERR(plane)) { | |
393 | dev_err(dev->dev, | |
394 | "failed to construct plane for RGB%d\n", i + 1); | |
395 | ret = PTR_ERR(plane); | |
396 | goto fail; | |
397 | } | |
398 | ||
399 | crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, i, | |
400 | mdp4_crtcs[i]); | |
401 | if (IS_ERR(crtc)) { | |
402 | dev_err(dev->dev, "failed to construct crtc for %s\n", | |
403 | mdp4_crtc_names[i]); | |
404 | ret = PTR_ERR(crtc); | |
405 | goto fail; | |
406 | } | |
3e87599b | 407 | |
ec141af6 AT |
408 | priv->crtcs[priv->num_crtcs++] = crtc; |
409 | } | |
3e87599b | 410 | |
ec141af6 AT |
411 | /* |
412 | * we currently set up two relatively fixed paths: | |
413 | * | |
414 | * LCDC/LVDS path: RGB1 -> DMA_P -> LCDC -> LVDS | |
af6d0423 AT |
415 | * or |
416 | * DSI path: RGB1 -> DMA_P -> DSI1 -> DSI Panel | |
417 | * | |
ec141af6 AT |
418 | * DTV/HDMI path: RGB2 -> DMA_E -> DTV -> HDMI |
419 | */ | |
c8afe684 | 420 | |
ec141af6 AT |
421 | for (i = 0; i < ARRAY_SIZE(mdp4_intfs); i++) { |
422 | ret = mdp4_modeset_init_intf(mdp4_kms, mdp4_intfs[i]); | |
067fef37 | 423 | if (ret) { |
ec141af6 AT |
424 | dev_err(dev->dev, "failed to initialize intf: %d, %d\n", |
425 | i, ret); | |
067fef37 RC |
426 | goto fail; |
427 | } | |
c8afe684 | 428 | } |
c8afe684 RC |
429 | |
430 | return 0; | |
431 | ||
432 | fail: | |
433 | return ret; | |
434 | } | |
435 | ||
c8afe684 RC |
436 | struct msm_kms *mdp4_kms_init(struct drm_device *dev) |
437 | { | |
438 | struct platform_device *pdev = dev->platformdev; | |
439 | struct mdp4_platform_config *config = mdp4_get_config(pdev); | |
440 | struct mdp4_kms *mdp4_kms; | |
441 | struct msm_kms *kms = NULL; | |
871d812a | 442 | struct msm_mmu *mmu; |
c8afe684 RC |
443 | int ret; |
444 | ||
445 | mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL); | |
446 | if (!mdp4_kms) { | |
447 | dev_err(dev->dev, "failed to allocate kms\n"); | |
448 | ret = -ENOMEM; | |
449 | goto fail; | |
450 | } | |
451 | ||
9e0efa63 RC |
452 | mdp_kms_init(&mdp4_kms->base, &kms_funcs); |
453 | ||
454 | kms = &mdp4_kms->base.base; | |
c8afe684 RC |
455 | |
456 | mdp4_kms->dev = dev; | |
457 | ||
458 | mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4"); | |
459 | if (IS_ERR(mdp4_kms->mmio)) { | |
460 | ret = PTR_ERR(mdp4_kms->mmio); | |
461 | goto fail; | |
462 | } | |
463 | ||
b7bbd640 RC |
464 | /* NOTE: driver for this regulator still missing upstream.. use |
465 | * _get_exclusive() and ignore the error if it does not exist | |
466 | * (and hope that the bootloader left it on for us) | |
467 | */ | |
41e69778 | 468 | mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd"); |
c8afe684 RC |
469 | if (IS_ERR(mdp4_kms->vdd)) |
470 | mdp4_kms->vdd = NULL; | |
471 | ||
472 | if (mdp4_kms->vdd) { | |
473 | ret = regulator_enable(mdp4_kms->vdd); | |
474 | if (ret) { | |
475 | dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); | |
476 | goto fail; | |
477 | } | |
478 | } | |
479 | ||
480 | mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk"); | |
481 | if (IS_ERR(mdp4_kms->clk)) { | |
482 | dev_err(dev->dev, "failed to get core_clk\n"); | |
483 | ret = PTR_ERR(mdp4_kms->clk); | |
484 | goto fail; | |
485 | } | |
486 | ||
487 | mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk"); | |
488 | if (IS_ERR(mdp4_kms->pclk)) | |
489 | mdp4_kms->pclk = NULL; | |
490 | ||
491 | // XXX if (rev >= MDP_REV_42) { ??? | |
492 | mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk"); | |
493 | if (IS_ERR(mdp4_kms->lut_clk)) { | |
494 | dev_err(dev->dev, "failed to get lut_clk\n"); | |
495 | ret = PTR_ERR(mdp4_kms->lut_clk); | |
496 | goto fail; | |
497 | } | |
498 | ||
e8abb5b5 RC |
499 | mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "mdp_axi_clk"); |
500 | if (IS_ERR(mdp4_kms->axi_clk)) { | |
501 | dev_err(dev->dev, "failed to get axi_clk\n"); | |
502 | ret = PTR_ERR(mdp4_kms->axi_clk); | |
503 | goto fail; | |
504 | } | |
505 | ||
c8afe684 RC |
506 | clk_set_rate(mdp4_kms->clk, config->max_clk); |
507 | clk_set_rate(mdp4_kms->lut_clk, config->max_clk); | |
508 | ||
c8afe684 RC |
509 | /* make sure things are off before attaching iommu (bootloader could |
510 | * have left things on, in which case we'll start getting faults if | |
511 | * we don't disable): | |
512 | */ | |
e529c7e6 | 513 | mdp4_enable(mdp4_kms); |
c8afe684 RC |
514 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); |
515 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); | |
516 | mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0); | |
e529c7e6 | 517 | mdp4_disable(mdp4_kms); |
c8afe684 RC |
518 | mdelay(16); |
519 | ||
871d812a | 520 | if (config->iommu) { |
944fc36c | 521 | mmu = msm_iommu_new(&pdev->dev, config->iommu); |
871d812a RC |
522 | if (IS_ERR(mmu)) { |
523 | ret = PTR_ERR(mmu); | |
524 | goto fail; | |
525 | } | |
526 | ret = mmu->funcs->attach(mmu, iommu_ports, | |
527 | ARRAY_SIZE(iommu_ports)); | |
528 | if (ret) | |
529 | goto fail; | |
f7590205 S |
530 | |
531 | mdp4_kms->mmu = mmu; | |
871d812a RC |
532 | } else { |
533 | dev_info(dev->dev, "no iommu, fallback to phys " | |
534 | "contig buffers for scanout\n"); | |
535 | mmu = NULL; | |
536 | } | |
c8afe684 | 537 | |
871d812a | 538 | mdp4_kms->id = msm_register_mmu(dev, mmu); |
c8afe684 RC |
539 | if (mdp4_kms->id < 0) { |
540 | ret = mdp4_kms->id; | |
541 | dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); | |
542 | goto fail; | |
543 | } | |
544 | ||
545 | ret = modeset_init(mdp4_kms); | |
546 | if (ret) { | |
547 | dev_err(dev->dev, "modeset_init failed: %d\n", ret); | |
548 | goto fail; | |
549 | } | |
550 | ||
7d8d9f67 RC |
551 | mutex_lock(&dev->struct_mutex); |
552 | mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC); | |
553 | mutex_unlock(&dev->struct_mutex); | |
554 | if (IS_ERR(mdp4_kms->blank_cursor_bo)) { | |
555 | ret = PTR_ERR(mdp4_kms->blank_cursor_bo); | |
556 | dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); | |
557 | mdp4_kms->blank_cursor_bo = NULL; | |
558 | goto fail; | |
559 | } | |
560 | ||
561 | ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id, | |
562 | &mdp4_kms->blank_cursor_iova); | |
563 | if (ret) { | |
564 | dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret); | |
565 | goto fail; | |
566 | } | |
567 | ||
9b7a9fc2 HL |
568 | dev->mode_config.min_width = 0; |
569 | dev->mode_config.min_height = 0; | |
570 | dev->mode_config.max_width = 2048; | |
571 | dev->mode_config.max_height = 2048; | |
572 | ||
c8afe684 RC |
573 | return kms; |
574 | ||
575 | fail: | |
576 | if (kms) | |
577 | mdp4_destroy(kms); | |
578 | return ERR_PTR(ret); | |
579 | } | |
580 | ||
581 | static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev) | |
582 | { | |
583 | static struct mdp4_platform_config config = {}; | |
d50c192a AT |
584 | |
585 | /* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */ | |
41e69778 RC |
586 | config.max_clk = 266667000; |
587 | config.iommu = iommu_domain_alloc(&platform_bus_type); | |
d50c192a | 588 | |
c8afe684 RC |
589 | return &config; |
590 | } |