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 | ||
53 | if (mdp4_kms->dsi_pll_vdda) { | |
54 | if ((mdp4_kms->rev == 2) || (mdp4_kms->rev == 4)) { | |
55 | ret = regulator_set_voltage(mdp4_kms->dsi_pll_vdda, | |
56 | 1200000, 1200000); | |
57 | if (ret) { | |
58 | dev_err(dev->dev, | |
59 | "failed to set dsi_pll_vdda voltage: %d\n", ret); | |
60 | goto out; | |
61 | } | |
62 | } | |
63 | } | |
64 | ||
65 | if (mdp4_kms->dsi_pll_vddio) { | |
66 | if (mdp4_kms->rev == 2) { | |
67 | ret = regulator_set_voltage(mdp4_kms->dsi_pll_vddio, | |
68 | 1800000, 1800000); | |
69 | if (ret) { | |
70 | dev_err(dev->dev, | |
71 | "failed to set dsi_pll_vddio voltage: %d\n", ret); | |
72 | goto out; | |
73 | } | |
74 | } | |
75 | } | |
76 | ||
77 | if (mdp4_kms->rev > 1) { | |
78 | mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER0, 0x0707ffff); | |
79 | mdp4_write(mdp4_kms, REG_MDP4_CS_CONTROLLER1, 0x03073f3f); | |
80 | } | |
81 | ||
82 | mdp4_write(mdp4_kms, REG_MDP4_PORTMAP_MODE, 0x3); | |
83 | ||
84 | /* max read pending cmd config, 3 pending requests: */ | |
85 | mdp4_write(mdp4_kms, REG_MDP4_READ_CNFG, 0x02222); | |
86 | ||
87 | clk = clk_get_rate(mdp4_kms->clk); | |
88 | ||
89 | if ((mdp4_kms->rev >= 1) || (clk >= 90000000)) { | |
90 | dmap_cfg = 0x47; /* 16 bytes-burst x 8 req */ | |
91 | vg_cfg = 0x47; /* 16 bytes-burs x 8 req */ | |
92 | } else { | |
93 | dmap_cfg = 0x27; /* 8 bytes-burst x 8 req */ | |
94 | vg_cfg = 0x43; /* 16 bytes-burst x 4 req */ | |
95 | } | |
96 | ||
97 | DBG("fetch config: dmap=%02x, vg=%02x", dmap_cfg, vg_cfg); | |
98 | ||
99 | mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_P), dmap_cfg); | |
100 | mdp4_write(mdp4_kms, REG_MDP4_DMA_FETCH_CONFIG(DMA_E), dmap_cfg); | |
101 | ||
102 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG1), vg_cfg); | |
103 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(VG2), vg_cfg); | |
104 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB1), vg_cfg); | |
105 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_FETCH_CONFIG(RGB2), vg_cfg); | |
106 | ||
107 | if (mdp4_kms->rev >= 2) | |
108 | mdp4_write(mdp4_kms, REG_MDP4_LAYERMIXER_IN_CFG_UPDATE_METHOD, 1); | |
109 | ||
110 | /* disable CSC matrix / YUV by default: */ | |
111 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG1), 0); | |
112 | mdp4_write(mdp4_kms, REG_MDP4_PIPE_OP_MODE(VG2), 0); | |
113 | mdp4_write(mdp4_kms, REG_MDP4_DMA_P_OP_MODE, 0); | |
114 | mdp4_write(mdp4_kms, REG_MDP4_DMA_S_OP_MODE, 0); | |
115 | mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(1), 0); | |
116 | mdp4_write(mdp4_kms, REG_MDP4_OVLP_CSC_CONFIG(2), 0); | |
117 | ||
118 | if (mdp4_kms->rev > 1) | |
119 | mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1); | |
120 | ||
121 | out: | |
122 | pm_runtime_put_sync(dev->dev); | |
123 | ||
124 | return ret; | |
125 | } | |
126 | ||
127 | static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, | |
128 | struct drm_encoder *encoder) | |
129 | { | |
130 | /* if we had >1 encoder, we'd need something more clever: */ | |
131 | return mdp4_dtv_round_pixclk(encoder, rate); | |
132 | } | |
133 | ||
134 | static void mdp4_preclose(struct msm_kms *kms, struct drm_file *file) | |
135 | { | |
9e0efa63 | 136 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); |
c8afe684 RC |
137 | struct msm_drm_private *priv = mdp4_kms->dev->dev_private; |
138 | unsigned i; | |
139 | ||
140 | for (i = 0; i < priv->num_crtcs; i++) | |
2a2b8fa6 | 141 | mdp4_crtc_cancel_pending_flip(priv->crtcs[i], file); |
c8afe684 RC |
142 | } |
143 | ||
144 | static void mdp4_destroy(struct msm_kms *kms) | |
145 | { | |
9e0efa63 | 146 | struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); |
7d8d9f67 RC |
147 | if (mdp4_kms->blank_cursor_iova) |
148 | msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); | |
149 | if (mdp4_kms->blank_cursor_bo) | |
150 | drm_gem_object_unreference(mdp4_kms->blank_cursor_bo); | |
c8afe684 RC |
151 | kfree(mdp4_kms); |
152 | } | |
153 | ||
9e0efa63 RC |
154 | static const struct mdp_kms_funcs kms_funcs = { |
155 | .base = { | |
c8afe684 RC |
156 | .hw_init = mdp4_hw_init, |
157 | .irq_preinstall = mdp4_irq_preinstall, | |
158 | .irq_postinstall = mdp4_irq_postinstall, | |
159 | .irq_uninstall = mdp4_irq_uninstall, | |
160 | .irq = mdp4_irq, | |
161 | .enable_vblank = mdp4_enable_vblank, | |
162 | .disable_vblank = mdp4_disable_vblank, | |
10a02eb6 | 163 | .get_format = mdp_get_format, |
c8afe684 RC |
164 | .round_pixclk = mdp4_round_pixclk, |
165 | .preclose = mdp4_preclose, | |
166 | .destroy = mdp4_destroy, | |
9e0efa63 RC |
167 | }, |
168 | .set_irqmask = mdp4_set_irqmask, | |
c8afe684 RC |
169 | }; |
170 | ||
171 | int mdp4_disable(struct mdp4_kms *mdp4_kms) | |
172 | { | |
173 | DBG(""); | |
174 | ||
175 | clk_disable_unprepare(mdp4_kms->clk); | |
176 | if (mdp4_kms->pclk) | |
177 | clk_disable_unprepare(mdp4_kms->pclk); | |
178 | clk_disable_unprepare(mdp4_kms->lut_clk); | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | int mdp4_enable(struct mdp4_kms *mdp4_kms) | |
184 | { | |
185 | DBG(""); | |
186 | ||
187 | clk_prepare_enable(mdp4_kms->clk); | |
188 | if (mdp4_kms->pclk) | |
189 | clk_prepare_enable(mdp4_kms->pclk); | |
190 | clk_prepare_enable(mdp4_kms->lut_clk); | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static int modeset_init(struct mdp4_kms *mdp4_kms) | |
196 | { | |
197 | struct drm_device *dev = mdp4_kms->dev; | |
198 | struct msm_drm_private *priv = dev->dev_private; | |
199 | struct drm_plane *plane; | |
200 | struct drm_crtc *crtc; | |
201 | struct drm_encoder *encoder; | |
dada25bd | 202 | struct hdmi *hdmi; |
c8afe684 RC |
203 | int ret; |
204 | ||
205 | /* | |
206 | * NOTE: this is a bit simplistic until we add support | |
207 | * for more than just RGB1->DMA_E->DTV->HDMI | |
208 | */ | |
209 | ||
a8623918 RC |
210 | /* construct non-private planes: */ |
211 | plane = mdp4_plane_init(dev, VG1, false); | |
212 | if (IS_ERR(plane)) { | |
213 | dev_err(dev->dev, "failed to construct plane for VG1\n"); | |
214 | ret = PTR_ERR(plane); | |
215 | goto fail; | |
216 | } | |
217 | priv->planes[priv->num_planes++] = plane; | |
218 | ||
219 | plane = mdp4_plane_init(dev, VG2, false); | |
220 | if (IS_ERR(plane)) { | |
221 | dev_err(dev->dev, "failed to construct plane for VG2\n"); | |
222 | ret = PTR_ERR(plane); | |
223 | goto fail; | |
224 | } | |
225 | priv->planes[priv->num_planes++] = plane; | |
226 | ||
c8afe684 RC |
227 | /* the CRTCs get constructed with a private plane: */ |
228 | plane = mdp4_plane_init(dev, RGB1, true); | |
229 | if (IS_ERR(plane)) { | |
230 | dev_err(dev->dev, "failed to construct plane for RGB1\n"); | |
231 | ret = PTR_ERR(plane); | |
232 | goto fail; | |
233 | } | |
234 | ||
235 | crtc = mdp4_crtc_init(dev, plane, priv->num_crtcs, 1, DMA_E); | |
236 | if (IS_ERR(crtc)) { | |
237 | dev_err(dev->dev, "failed to construct crtc for DMA_E\n"); | |
238 | ret = PTR_ERR(crtc); | |
239 | goto fail; | |
240 | } | |
241 | priv->crtcs[priv->num_crtcs++] = crtc; | |
242 | ||
243 | encoder = mdp4_dtv_encoder_init(dev); | |
244 | if (IS_ERR(encoder)) { | |
245 | dev_err(dev->dev, "failed to construct DTV encoder\n"); | |
246 | ret = PTR_ERR(encoder); | |
247 | goto fail; | |
248 | } | |
249 | encoder->possible_crtcs = 0x1; /* DTV can be hooked to DMA_E */ | |
250 | priv->encoders[priv->num_encoders++] = encoder; | |
251 | ||
dada25bd RC |
252 | hdmi = hdmi_init(dev, encoder); |
253 | if (IS_ERR(hdmi)) { | |
254 | ret = PTR_ERR(hdmi); | |
255 | dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); | |
c8afe684 RC |
256 | goto fail; |
257 | } | |
c8afe684 RC |
258 | |
259 | return 0; | |
260 | ||
261 | fail: | |
262 | return ret; | |
263 | } | |
264 | ||
265 | static const char *iommu_ports[] = { | |
266 | "mdp_port0_cb0", "mdp_port1_cb0", | |
267 | }; | |
268 | ||
269 | struct msm_kms *mdp4_kms_init(struct drm_device *dev) | |
270 | { | |
271 | struct platform_device *pdev = dev->platformdev; | |
272 | struct mdp4_platform_config *config = mdp4_get_config(pdev); | |
273 | struct mdp4_kms *mdp4_kms; | |
274 | struct msm_kms *kms = NULL; | |
871d812a | 275 | struct msm_mmu *mmu; |
c8afe684 RC |
276 | int ret; |
277 | ||
278 | mdp4_kms = kzalloc(sizeof(*mdp4_kms), GFP_KERNEL); | |
279 | if (!mdp4_kms) { | |
280 | dev_err(dev->dev, "failed to allocate kms\n"); | |
281 | ret = -ENOMEM; | |
282 | goto fail; | |
283 | } | |
284 | ||
9e0efa63 RC |
285 | mdp_kms_init(&mdp4_kms->base, &kms_funcs); |
286 | ||
287 | kms = &mdp4_kms->base.base; | |
c8afe684 RC |
288 | |
289 | mdp4_kms->dev = dev; | |
290 | ||
291 | mdp4_kms->mmio = msm_ioremap(pdev, NULL, "MDP4"); | |
292 | if (IS_ERR(mdp4_kms->mmio)) { | |
293 | ret = PTR_ERR(mdp4_kms->mmio); | |
294 | goto fail; | |
295 | } | |
296 | ||
41e69778 RC |
297 | mdp4_kms->dsi_pll_vdda = |
298 | devm_regulator_get_optional(&pdev->dev, "dsi_pll_vdda"); | |
c8afe684 RC |
299 | if (IS_ERR(mdp4_kms->dsi_pll_vdda)) |
300 | mdp4_kms->dsi_pll_vdda = NULL; | |
301 | ||
41e69778 RC |
302 | mdp4_kms->dsi_pll_vddio = |
303 | devm_regulator_get_optional(&pdev->dev, "dsi_pll_vddio"); | |
c8afe684 RC |
304 | if (IS_ERR(mdp4_kms->dsi_pll_vddio)) |
305 | mdp4_kms->dsi_pll_vddio = NULL; | |
306 | ||
41e69778 | 307 | mdp4_kms->vdd = devm_regulator_get_exclusive(&pdev->dev, "vdd"); |
c8afe684 RC |
308 | if (IS_ERR(mdp4_kms->vdd)) |
309 | mdp4_kms->vdd = NULL; | |
310 | ||
311 | if (mdp4_kms->vdd) { | |
312 | ret = regulator_enable(mdp4_kms->vdd); | |
313 | if (ret) { | |
314 | dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret); | |
315 | goto fail; | |
316 | } | |
317 | } | |
318 | ||
319 | mdp4_kms->clk = devm_clk_get(&pdev->dev, "core_clk"); | |
320 | if (IS_ERR(mdp4_kms->clk)) { | |
321 | dev_err(dev->dev, "failed to get core_clk\n"); | |
322 | ret = PTR_ERR(mdp4_kms->clk); | |
323 | goto fail; | |
324 | } | |
325 | ||
326 | mdp4_kms->pclk = devm_clk_get(&pdev->dev, "iface_clk"); | |
327 | if (IS_ERR(mdp4_kms->pclk)) | |
328 | mdp4_kms->pclk = NULL; | |
329 | ||
330 | // XXX if (rev >= MDP_REV_42) { ??? | |
331 | mdp4_kms->lut_clk = devm_clk_get(&pdev->dev, "lut_clk"); | |
332 | if (IS_ERR(mdp4_kms->lut_clk)) { | |
333 | dev_err(dev->dev, "failed to get lut_clk\n"); | |
334 | ret = PTR_ERR(mdp4_kms->lut_clk); | |
335 | goto fail; | |
336 | } | |
337 | ||
338 | clk_set_rate(mdp4_kms->clk, config->max_clk); | |
339 | clk_set_rate(mdp4_kms->lut_clk, config->max_clk); | |
340 | ||
c8afe684 RC |
341 | /* make sure things are off before attaching iommu (bootloader could |
342 | * have left things on, in which case we'll start getting faults if | |
343 | * we don't disable): | |
344 | */ | |
e529c7e6 | 345 | mdp4_enable(mdp4_kms); |
c8afe684 RC |
346 | mdp4_write(mdp4_kms, REG_MDP4_DTV_ENABLE, 0); |
347 | mdp4_write(mdp4_kms, REG_MDP4_LCDC_ENABLE, 0); | |
348 | mdp4_write(mdp4_kms, REG_MDP4_DSI_ENABLE, 0); | |
e529c7e6 | 349 | mdp4_disable(mdp4_kms); |
c8afe684 RC |
350 | mdelay(16); |
351 | ||
871d812a RC |
352 | if (config->iommu) { |
353 | mmu = msm_iommu_new(dev, config->iommu); | |
354 | if (IS_ERR(mmu)) { | |
355 | ret = PTR_ERR(mmu); | |
356 | goto fail; | |
357 | } | |
358 | ret = mmu->funcs->attach(mmu, iommu_ports, | |
359 | ARRAY_SIZE(iommu_ports)); | |
360 | if (ret) | |
361 | goto fail; | |
362 | } else { | |
363 | dev_info(dev->dev, "no iommu, fallback to phys " | |
364 | "contig buffers for scanout\n"); | |
365 | mmu = NULL; | |
366 | } | |
c8afe684 | 367 | |
871d812a | 368 | mdp4_kms->id = msm_register_mmu(dev, mmu); |
c8afe684 RC |
369 | if (mdp4_kms->id < 0) { |
370 | ret = mdp4_kms->id; | |
371 | dev_err(dev->dev, "failed to register mdp4 iommu: %d\n", ret); | |
372 | goto fail; | |
373 | } | |
374 | ||
375 | ret = modeset_init(mdp4_kms); | |
376 | if (ret) { | |
377 | dev_err(dev->dev, "modeset_init failed: %d\n", ret); | |
378 | goto fail; | |
379 | } | |
380 | ||
7d8d9f67 RC |
381 | mutex_lock(&dev->struct_mutex); |
382 | mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC); | |
383 | mutex_unlock(&dev->struct_mutex); | |
384 | if (IS_ERR(mdp4_kms->blank_cursor_bo)) { | |
385 | ret = PTR_ERR(mdp4_kms->blank_cursor_bo); | |
386 | dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); | |
387 | mdp4_kms->blank_cursor_bo = NULL; | |
388 | goto fail; | |
389 | } | |
390 | ||
391 | ret = msm_gem_get_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id, | |
392 | &mdp4_kms->blank_cursor_iova); | |
393 | if (ret) { | |
394 | dev_err(dev->dev, "could not pin blank-cursor bo: %d\n", ret); | |
395 | goto fail; | |
396 | } | |
397 | ||
c8afe684 RC |
398 | return kms; |
399 | ||
400 | fail: | |
401 | if (kms) | |
402 | mdp4_destroy(kms); | |
403 | return ERR_PTR(ret); | |
404 | } | |
405 | ||
406 | static struct mdp4_platform_config *mdp4_get_config(struct platform_device *dev) | |
407 | { | |
408 | static struct mdp4_platform_config config = {}; | |
409 | #ifdef CONFIG_OF | |
410 | /* TODO */ | |
41e69778 RC |
411 | config.max_clk = 266667000; |
412 | config.iommu = iommu_domain_alloc(&platform_bus_type); | |
c8afe684 RC |
413 | #else |
414 | if (cpu_is_apq8064()) | |
415 | config.max_clk = 266667000; | |
416 | else | |
417 | config.max_clk = 200000000; | |
418 | ||
419 | config.iommu = msm_get_iommu_domain(DISPLAY_READ_DOMAIN); | |
420 | #endif | |
421 | return &config; | |
422 | } |