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)); | |
8d76b79f DV |
109 | int i; |
110 | struct drm_crtc *crtc; | |
111 | struct drm_crtc_state *crtc_state; | |
0b776d45 RC |
112 | |
113 | mdp4_enable(mdp4_kms); | |
114 | ||
115 | /* see 119ecb7fd */ | |
8d76b79f | 116 | for_each_crtc_in_state(state, crtc, crtc_state, i) |
0b776d45 | 117 | drm_crtc_vblank_get(crtc); |
0b776d45 RC |
118 | } |
119 | ||
120 | static void mdp4_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state) | |
121 | { | |
122 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); | |
8d76b79f DV |
123 | int i; |
124 | struct drm_crtc *crtc; | |
125 | struct drm_crtc_state *crtc_state; | |
0b776d45 RC |
126 | |
127 | /* see 119ecb7fd */ | |
8d76b79f | 128 | for_each_crtc_in_state(state, crtc, crtc_state, i) |
0b776d45 | 129 | drm_crtc_vblank_put(crtc); |
0b776d45 RC |
130 | |
131 | mdp4_disable(mdp4_kms); | |
132 | } | |
133 | ||
0a5c9aad HL |
134 | static void mdp4_wait_for_crtc_commit_done(struct msm_kms *kms, |
135 | struct drm_crtc *crtc) | |
136 | { | |
137 | mdp4_crtc_wait_for_commit_done(crtc); | |
138 | } | |
139 | ||
c8afe684 RC |
140 | static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, |
141 | struct drm_encoder *encoder) | |
142 | { | |
143 | /* if we had >1 encoder, we'd need something more clever: */ | |
9696acbc AT |
144 | switch (encoder->encoder_type) { |
145 | case DRM_MODE_ENCODER_TMDS: | |
146 | return mdp4_dtv_round_pixclk(encoder, rate); | |
147 | case DRM_MODE_ENCODER_LVDS: | |
148 | case DRM_MODE_ENCODER_DSI: | |
149 | default: | |
150 | return rate; | |
151 | } | |
c8afe684 RC |
152 | } |
153 | ||
f7590205 S |
154 | static const char * const iommu_ports[] = { |
155 | "mdp_port0_cb0", "mdp_port1_cb0", | |
156 | }; | |
157 | ||
c8afe684 RC |
158 | static void mdp4_destroy(struct msm_kms *kms) |
159 | { | |
9e0efa63 | 160 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); |
cd792726 | 161 | struct device *dev = mdp4_kms->dev->dev; |
f7590205 S |
162 | struct msm_mmu *mmu = mdp4_kms->mmu; |
163 | ||
164 | if (mmu) { | |
165 | mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); | |
166 | mmu->funcs->destroy(mmu); | |
167 | } | |
168 | ||
7d8d9f67 RC |
169 | if (mdp4_kms->blank_cursor_iova) |
170 | msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); | |
e73a8569 | 171 | drm_gem_object_unreference_unlocked(mdp4_kms->blank_cursor_bo); |
cd792726 AT |
172 | |
173 | if (mdp4_kms->rpm_enabled) | |
174 | pm_runtime_disable(dev); | |
175 | ||
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; |
a2b3a557 | 443 | int irq, ret; |
c8afe684 RC |
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 | ||
a2b3a557 AT |
464 | irq = platform_get_irq(pdev, 0); |
465 | if (irq < 0) { | |
466 | ret = irq; | |
467 | dev_err(dev->dev, "failed to get irq: %d\n", ret); | |
468 | goto fail; | |
469 | } | |
470 | ||
471 | kms->irq = irq; | |
472 | ||
b7bbd640 RC |
473 | /* NOTE: driver for this regulator still missing upstream.. use |
474 | * _get_exclusive() and ignore the error if it does not exist | |
475 | * (and hope that the bootloader left it on for us) | |
476 | */ | |
41e69778 | 477 | mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd"); |
c8afe684 RC |
478 | if (IS_ERR(mdp4_kms->vdd)) |
479 | mdp4_kms->vdd = NULL; | |
480 | ||
481 | if (mdp4_kms->vdd) { | |
482 | ret = regulator_enable(mdp4_kms->vdd); | |
483 | if (ret) { | |
484 | dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); | |
485 | goto fail; | |
486 | } | |
487 | } | |
488 | ||
489 | mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk"); | |
490 | if (IS_ERR(mdp4_kms->clk)) { | |
491 | dev_err(dev->dev, "failed to get core_clk\n"); | |
492 | ret = PTR_ERR(mdp4_kms->clk); | |
493 | goto fail; | |
494 | } | |
495 | ||
496 | mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk"); | |
497 | if (IS_ERR(mdp4_kms->pclk)) | |
498 | mdp4_kms->pclk = NULL; | |
499 | ||
500 | // XXX if (rev >= MDP_REV_42) { ??? | |
501 | mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk"); | |
502 | if (IS_ERR(mdp4_kms->lut_clk)) { | |
503 | dev_err(dev->dev, "failed to get lut_clk\n"); | |
504 | ret = PTR_ERR(mdp4_kms->lut_clk); | |
505 | goto fail; | |
506 | } | |
507 | ||
db9e44fb | 508 | mdp4_kms->axi_clk = devm_clk_get(&pdev->dev, "bus_clk"); |
e8abb5b5 RC |
509 | if (IS_ERR(mdp4_kms->axi_clk)) { |
510 | dev_err(dev->dev, "failed to get axi_clk\n"); | |
511 | ret = PTR_ERR(mdp4_kms->axi_clk); | |
512 | goto fail; | |
513 | } | |
514 | ||
c8afe684 RC |
515 | clk_set_rate(mdp4_kms->clk, config->max_clk); |
516 | clk_set_rate(mdp4_kms->lut_clk, config->max_clk); | |
517 | ||
cd792726 AT |
518 | pm_runtime_enable(dev->dev); |
519 | mdp4_kms->rpm_enabled = true; | |
520 | ||
c8afe684 RC |
521 | /* make sure things are off before attaching iommu (bootloader could |
522 | * have left things on, in which case we'll start getting faults if | |
523 | * we don't disable): | |
524 | */ | |
e529c7e6 | 525 | mdp4_enable(mdp4_kms); |
c8afe684 RC |
526 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); |
527 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); | |
528 | mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0); | |
e529c7e6 | 529 | mdp4_disable(mdp4_kms); |
c8afe684 RC |
530 | mdelay(16); |
531 | ||
871d812a | 532 | if (config->iommu) { |
944fc36c | 533 | mmu = msm_iommu_new(&pdev->dev, config->iommu); |
871d812a RC |
534 | if (IS_ERR(mmu)) { |
535 | ret = PTR_ERR(mmu); | |
536 | goto fail; | |
537 | } | |
538 | ret = mmu->funcs->attach(mmu, iommu_ports, | |
539 | ARRAY_SIZE(iommu_ports)); | |
540 | if (ret) | |
541 | goto fail; | |
f7590205 S |
542 | |
543 | mdp4_kms->mmu = mmu; | |
871d812a RC |
544 | } else { |
545 | dev_info(dev->dev, "no iommu, fallback to phys " | |
546 | "contig buffers for scanout\n"); | |
547 | mmu = NULL; | |
548 | } | |
c8afe684 | 549 | |
871d812a | 550 | mdp4_kms->id = msm_register_mmu(dev, mmu); |
c8afe684 RC |
551 | if (mdp4_kms->id < 0) { |
552 | ret = mdp4_kms->id; | |
553 | dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); | |
554 | goto fail; | |
555 | } | |
556 | ||
557 | ret = modeset_init(mdp4_kms); | |
558 | if (ret) { | |
559 | dev_err(dev->dev, "modeset_init failed: %d\n", ret); | |
560 | goto fail; | |
561 | } | |
562 | ||
7d8d9f67 RC |
563 | mutex_lock(&dev->struct_mutex); |
564 | mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC); | |
565 | mutex_unlock(&dev->struct_mutex); | |
566 | if (IS_ERR(mdp4_kms->blank_cursor_bo)) { | |
567 | ret = PTR_ERR(mdp4_kms->blank_cursor_bo); | |
568 | dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); | |
569 | mdp4_kms->blank_cursor_bo = NULL; | |
570 | goto fail; | |
571 | } | |
572 | ||
573 | ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id, | |
574 | &mdp4_kms->blank_cursor_iova); | |
575 | if (ret) { | |
576 | dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret); | |
577 | goto fail; | |
578 | } | |
579 | ||
9b7a9fc2 HL |
580 | dev->mode_config.min_width = 0; |
581 | dev->mode_config.min_height = 0; | |
582 | dev->mode_config.max_width = 2048; | |
583 | dev->mode_config.max_height = 2048; | |
584 | ||
c8afe684 RC |
585 | return kms; |
586 | ||
587 | fail: | |
588 | if (kms) | |
589 | mdp4_destroy(kms); | |
590 | return ERR_PTR(ret); | |
591 | } | |
592 | ||
593 | static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev) | |
594 | { | |
595 | static struct mdp4_platform_config config = {}; | |
d50c192a AT |
596 | |
597 | /* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */ | |
41e69778 RC |
598 | config.max_clk = 266667000; |
599 | config.iommu = iommu_domain_alloc(&platform_bus_type); | |
d50c192a | 600 | |
c8afe684 RC |
601 | return &config; |
602 | } |