]>
Commit | Line | Data |
---|---|---|
5e0df3a0 RZ |
1 | /* Hisilicon Hibmc SoC drm driver |
2 | * | |
3 | * Based on the bochs drm driver. | |
4 | * | |
5 | * Copyright (c) 2016 Huawei Limited. | |
6 | * | |
7 | * Author: | |
8 | * Rongrong Zou <zourongrong@huawei.com> | |
9 | * Rongrong Zou <zourongrong@gmail.com> | |
10 | * Jianhua Li <lijianhua@huawei.com> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or | |
15 | * (at your option) any later version. | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/console.h> | |
20 | #include <linux/module.h> | |
21 | ||
da52605e RZ |
22 | #include <drm/drm_atomic_helper.h> |
23 | #include <drm/drm_crtc_helper.h> | |
24 | ||
5e0df3a0 RZ |
25 | #include "hibmc_drm_drv.h" |
26 | #include "hibmc_drm_regs.h" | |
27 | ||
28 | static const struct file_operations hibmc_fops = { | |
29 | .owner = THIS_MODULE, | |
30 | .open = drm_open, | |
31 | .release = drm_release, | |
32 | .unlocked_ioctl = drm_ioctl, | |
33 | .compat_ioctl = drm_compat_ioctl, | |
e4daebc7 | 34 | .mmap = hibmc_mmap, |
5e0df3a0 RZ |
35 | .poll = drm_poll, |
36 | .read = drm_read, | |
37 | .llseek = no_llseek, | |
38 | }; | |
39 | ||
1d98b916 RZ |
40 | irqreturn_t hibmc_drm_interrupt(int irq, void *arg) |
41 | { | |
42 | struct drm_device *dev = (struct drm_device *)arg; | |
43 | struct hibmc_drm_private *priv = | |
44 | (struct hibmc_drm_private *)dev->dev_private; | |
45 | u32 status; | |
46 | ||
47 | status = readl(priv->mmio + HIBMC_RAW_INTERRUPT); | |
48 | ||
49 | if (status & HIBMC_RAW_INTERRUPT_VBLANK(1)) { | |
50 | writel(HIBMC_RAW_INTERRUPT_VBLANK(1), | |
51 | priv->mmio + HIBMC_RAW_INTERRUPT); | |
52 | drm_handle_vblank(dev, 0); | |
53 | } | |
54 | ||
55 | return IRQ_HANDLED; | |
5e0df3a0 RZ |
56 | } |
57 | ||
58 | static struct drm_driver hibmc_driver = { | |
da52605e | 59 | .driver_features = DRIVER_GEM | DRIVER_MODESET | |
1d98b916 | 60 | DRIVER_ATOMIC | DRIVER_HAVE_IRQ, |
5e0df3a0 RZ |
61 | .fops = &hibmc_fops, |
62 | .name = "hibmc", | |
63 | .date = "20160828", | |
64 | .desc = "hibmc drm driver", | |
65 | .major = 1, | |
66 | .minor = 0, | |
e4daebc7 RZ |
67 | .gem_free_object_unlocked = hibmc_gem_free_object, |
68 | .dumb_create = hibmc_dumb_create, | |
69 | .dumb_map_offset = hibmc_dumb_mmap_offset, | |
70 | .dumb_destroy = drm_gem_dumb_destroy, | |
1d98b916 | 71 | .irq_handler = hibmc_drm_interrupt, |
5e0df3a0 RZ |
72 | }; |
73 | ||
b61abd49 | 74 | static int __maybe_unused hibmc_pm_suspend(struct device *dev) |
5e0df3a0 | 75 | { |
da52605e RZ |
76 | struct pci_dev *pdev = to_pci_dev(dev); |
77 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
78 | struct hibmc_drm_private *priv = drm_dev->dev_private; | |
79 | ||
80 | drm_kms_helper_poll_disable(drm_dev); | |
81 | priv->suspend_state = drm_atomic_helper_suspend(drm_dev); | |
82 | if (IS_ERR(priv->suspend_state)) { | |
83 | DRM_ERROR("drm_atomic_helper_suspend failed: %ld\n", | |
84 | PTR_ERR(priv->suspend_state)); | |
85 | drm_kms_helper_poll_enable(drm_dev); | |
86 | return PTR_ERR(priv->suspend_state); | |
87 | } | |
88 | ||
5e0df3a0 RZ |
89 | return 0; |
90 | } | |
91 | ||
b61abd49 | 92 | static int __maybe_unused hibmc_pm_resume(struct device *dev) |
5e0df3a0 | 93 | { |
da52605e RZ |
94 | struct pci_dev *pdev = to_pci_dev(dev); |
95 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | |
96 | struct hibmc_drm_private *priv = drm_dev->dev_private; | |
97 | ||
98 | drm_atomic_helper_resume(drm_dev, priv->suspend_state); | |
99 | drm_kms_helper_poll_enable(drm_dev); | |
100 | ||
5e0df3a0 RZ |
101 | return 0; |
102 | } | |
103 | ||
104 | static const struct dev_pm_ops hibmc_pm_ops = { | |
105 | SET_SYSTEM_SLEEP_PM_OPS(hibmc_pm_suspend, | |
106 | hibmc_pm_resume) | |
107 | }; | |
108 | ||
da52605e RZ |
109 | static int hibmc_kms_init(struct hibmc_drm_private *priv) |
110 | { | |
111 | int ret; | |
112 | ||
113 | drm_mode_config_init(priv->dev); | |
114 | priv->mode_config_initialized = true; | |
115 | ||
116 | priv->dev->mode_config.min_width = 0; | |
117 | priv->dev->mode_config.min_height = 0; | |
118 | priv->dev->mode_config.max_width = 1920; | |
119 | priv->dev->mode_config.max_height = 1440; | |
120 | ||
121 | priv->dev->mode_config.fb_base = priv->fb_base; | |
122 | priv->dev->mode_config.preferred_depth = 24; | |
123 | priv->dev->mode_config.prefer_shadow = 0; | |
124 | ||
125 | priv->dev->mode_config.funcs = (void *)&hibmc_mode_funcs; | |
126 | ||
127 | ret = hibmc_de_init(priv); | |
128 | if (ret) { | |
129 | DRM_ERROR("failed to init de: %d\n", ret); | |
130 | return ret; | |
131 | } | |
132 | ||
5294967f RZ |
133 | ret = hibmc_vdac_init(priv); |
134 | if (ret) { | |
135 | DRM_ERROR("failed to init vdac: %d\n", ret); | |
136 | return ret; | |
137 | } | |
138 | ||
da52605e RZ |
139 | return 0; |
140 | } | |
141 | ||
142 | static void hibmc_kms_fini(struct hibmc_drm_private *priv) | |
143 | { | |
144 | if (priv->mode_config_initialized) { | |
145 | drm_mode_config_cleanup(priv->dev); | |
146 | priv->mode_config_initialized = false; | |
147 | } | |
148 | } | |
149 | ||
5e0df3a0 RZ |
150 | /* |
151 | * It can operate in one of three modes: 0, 1 or Sleep. | |
152 | */ | |
153 | void hibmc_set_power_mode(struct hibmc_drm_private *priv, | |
154 | unsigned int power_mode) | |
155 | { | |
156 | unsigned int control_value = 0; | |
157 | void __iomem *mmio = priv->mmio; | |
158 | unsigned int input = 1; | |
159 | ||
160 | if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP) | |
161 | return; | |
162 | ||
163 | if (power_mode == HIBMC_PW_MODE_CTL_MODE_SLEEP) | |
164 | input = 0; | |
165 | ||
166 | control_value = readl(mmio + HIBMC_POWER_MODE_CTRL); | |
167 | control_value &= ~(HIBMC_PW_MODE_CTL_MODE_MASK | | |
168 | HIBMC_PW_MODE_CTL_OSC_INPUT_MASK); | |
169 | control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_MODE, power_mode); | |
170 | control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_OSC_INPUT, input); | |
171 | writel(control_value, mmio + HIBMC_POWER_MODE_CTRL); | |
172 | } | |
173 | ||
174 | void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) | |
175 | { | |
176 | unsigned int gate_reg; | |
177 | unsigned int mode; | |
178 | void __iomem *mmio = priv->mmio; | |
179 | ||
180 | /* Get current power mode. */ | |
181 | mode = (readl(mmio + HIBMC_POWER_MODE_CTRL) & | |
182 | HIBMC_PW_MODE_CTL_MODE_MASK) >> HIBMC_PW_MODE_CTL_MODE_SHIFT; | |
183 | ||
184 | switch (mode) { | |
185 | case HIBMC_PW_MODE_CTL_MODE_MODE0: | |
186 | gate_reg = HIBMC_MODE0_GATE; | |
187 | break; | |
188 | ||
189 | case HIBMC_PW_MODE_CTL_MODE_MODE1: | |
190 | gate_reg = HIBMC_MODE1_GATE; | |
191 | break; | |
192 | ||
193 | default: | |
194 | gate_reg = HIBMC_MODE0_GATE; | |
195 | break; | |
196 | } | |
197 | writel(gate, mmio + gate_reg); | |
198 | } | |
199 | ||
200 | static void hibmc_hw_config(struct hibmc_drm_private *priv) | |
201 | { | |
202 | unsigned int reg; | |
203 | ||
204 | /* On hardware reset, power mode 0 is default. */ | |
205 | hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); | |
206 | ||
207 | /* Enable display power gate & LOCALMEM power gate*/ | |
208 | reg = readl(priv->mmio + HIBMC_CURRENT_GATE); | |
209 | reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; | |
210 | reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; | |
211 | reg |= HIBMC_CURR_GATE_DISPLAY(1); | |
212 | reg |= HIBMC_CURR_GATE_LOCALMEM(1); | |
213 | ||
214 | hibmc_set_current_gate(priv, reg); | |
215 | ||
216 | /* | |
217 | * Reset the memory controller. If the memory controller | |
218 | * is not reset in chip,the system might hang when sw accesses | |
219 | * the memory.The memory should be resetted after | |
220 | * changing the MXCLK. | |
221 | */ | |
222 | reg = readl(priv->mmio + HIBMC_MISC_CTRL); | |
223 | reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; | |
224 | reg |= HIBMC_MSCCTL_LOCALMEM_RESET(0); | |
225 | writel(reg, priv->mmio + HIBMC_MISC_CTRL); | |
226 | ||
227 | reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; | |
228 | reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1); | |
229 | ||
230 | writel(reg, priv->mmio + HIBMC_MISC_CTRL); | |
231 | } | |
232 | ||
233 | static int hibmc_hw_map(struct hibmc_drm_private *priv) | |
234 | { | |
235 | struct drm_device *dev = priv->dev; | |
236 | struct pci_dev *pdev = dev->pdev; | |
237 | resource_size_t addr, size, ioaddr, iosize; | |
238 | ||
239 | ioaddr = pci_resource_start(pdev, 1); | |
240 | iosize = pci_resource_len(pdev, 1); | |
241 | priv->mmio = devm_ioremap_nocache(dev->dev, ioaddr, iosize); | |
242 | if (!priv->mmio) { | |
243 | DRM_ERROR("Cannot map mmio region\n"); | |
244 | return -ENOMEM; | |
245 | } | |
246 | ||
247 | addr = pci_resource_start(pdev, 0); | |
248 | size = pci_resource_len(pdev, 0); | |
249 | priv->fb_map = devm_ioremap(dev->dev, addr, size); | |
250 | if (!priv->fb_map) { | |
251 | DRM_ERROR("Cannot map framebuffer\n"); | |
252 | return -ENOMEM; | |
253 | } | |
254 | priv->fb_base = addr; | |
255 | priv->fb_size = size; | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | static int hibmc_hw_init(struct hibmc_drm_private *priv) | |
261 | { | |
262 | int ret; | |
263 | ||
264 | ret = hibmc_hw_map(priv); | |
265 | if (ret) | |
266 | return ret; | |
267 | ||
268 | hibmc_hw_config(priv); | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static int hibmc_unload(struct drm_device *dev) | |
274 | { | |
e4daebc7 RZ |
275 | struct hibmc_drm_private *priv = dev->dev_private; |
276 | ||
d1667b86 | 277 | hibmc_fbdev_fini(priv); |
1d98b916 | 278 | |
b3df5e65 DV |
279 | drm_atomic_helper_shutdown(dev); |
280 | ||
1d98b916 RZ |
281 | if (dev->irq_enabled) |
282 | drm_irq_uninstall(dev); | |
283 | if (priv->msi_enabled) | |
284 | pci_disable_msi(dev->pdev); | |
1d98b916 | 285 | |
da52605e | 286 | hibmc_kms_fini(priv); |
e4daebc7 RZ |
287 | hibmc_mm_fini(priv); |
288 | dev->dev_private = NULL; | |
5e0df3a0 RZ |
289 | return 0; |
290 | } | |
291 | ||
292 | static int hibmc_load(struct drm_device *dev) | |
293 | { | |
294 | struct hibmc_drm_private *priv; | |
295 | int ret; | |
296 | ||
297 | priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); | |
298 | if (!priv) { | |
299 | DRM_ERROR("no memory to allocate for hibmc_drm_private\n"); | |
300 | return -ENOMEM; | |
301 | } | |
302 | dev->dev_private = priv; | |
303 | priv->dev = dev; | |
304 | ||
305 | ret = hibmc_hw_init(priv); | |
306 | if (ret) | |
307 | goto err; | |
308 | ||
e4daebc7 RZ |
309 | ret = hibmc_mm_init(priv); |
310 | if (ret) | |
311 | goto err; | |
312 | ||
da52605e RZ |
313 | ret = hibmc_kms_init(priv); |
314 | if (ret) | |
315 | goto err; | |
316 | ||
1d98b916 RZ |
317 | ret = drm_vblank_init(dev, dev->mode_config.num_crtc); |
318 | if (ret) { | |
319 | DRM_ERROR("failed to initialize vblank: %d\n", ret); | |
320 | goto err; | |
321 | } | |
322 | ||
323 | priv->msi_enabled = 0; | |
324 | ret = pci_enable_msi(dev->pdev); | |
325 | if (ret) { | |
326 | DRM_WARN("enabling MSI failed: %d\n", ret); | |
327 | } else { | |
328 | priv->msi_enabled = 1; | |
329 | ret = drm_irq_install(dev, dev->pdev->irq); | |
330 | if (ret) | |
331 | DRM_WARN("install irq failed: %d\n", ret); | |
332 | } | |
333 | ||
da52605e RZ |
334 | /* reset all the states of crtc/plane/encoder/connector */ |
335 | drm_mode_config_reset(dev); | |
336 | ||
d1667b86 RZ |
337 | ret = hibmc_fbdev_init(priv); |
338 | if (ret) { | |
339 | DRM_ERROR("failed to initialize fbdev: %d\n", ret); | |
340 | goto err; | |
341 | } | |
342 | ||
5e0df3a0 RZ |
343 | return 0; |
344 | ||
345 | err: | |
346 | hibmc_unload(dev); | |
347 | DRM_ERROR("failed to initialize drm driver: %d\n", ret); | |
348 | return ret; | |
349 | } | |
350 | ||
351 | static int hibmc_pci_probe(struct pci_dev *pdev, | |
352 | const struct pci_device_id *ent) | |
353 | { | |
354 | struct drm_device *dev; | |
355 | int ret; | |
356 | ||
357 | dev = drm_dev_alloc(&hibmc_driver, &pdev->dev); | |
0a4c9ffb | 358 | if (IS_ERR(dev)) { |
5e0df3a0 | 359 | DRM_ERROR("failed to allocate drm_device\n"); |
0a4c9ffb | 360 | return PTR_ERR(dev); |
5e0df3a0 RZ |
361 | } |
362 | ||
363 | dev->pdev = pdev; | |
364 | pci_set_drvdata(pdev, dev); | |
365 | ||
366 | ret = pci_enable_device(pdev); | |
367 | if (ret) { | |
368 | DRM_ERROR("failed to enable pci device: %d\n", ret); | |
369 | goto err_free; | |
370 | } | |
371 | ||
372 | ret = hibmc_load(dev); | |
373 | if (ret) { | |
374 | DRM_ERROR("failed to load hibmc: %d\n", ret); | |
375 | goto err_disable; | |
376 | } | |
377 | ||
378 | ret = drm_dev_register(dev, 0); | |
379 | if (ret) { | |
380 | DRM_ERROR("failed to register drv for userspace access: %d\n", | |
381 | ret); | |
382 | goto err_unload; | |
383 | } | |
384 | return 0; | |
385 | ||
386 | err_unload: | |
387 | hibmc_unload(dev); | |
388 | err_disable: | |
389 | pci_disable_device(pdev); | |
390 | err_free: | |
391 | drm_dev_unref(dev); | |
392 | ||
393 | return ret; | |
394 | } | |
395 | ||
396 | static void hibmc_pci_remove(struct pci_dev *pdev) | |
397 | { | |
398 | struct drm_device *dev = pci_get_drvdata(pdev); | |
399 | ||
400 | drm_dev_unregister(dev); | |
401 | hibmc_unload(dev); | |
402 | drm_dev_unref(dev); | |
403 | } | |
404 | ||
405 | static struct pci_device_id hibmc_pci_table[] = { | |
406 | {0x19e5, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
407 | {0,} | |
408 | }; | |
409 | ||
410 | static struct pci_driver hibmc_pci_driver = { | |
411 | .name = "hibmc-drm", | |
412 | .id_table = hibmc_pci_table, | |
413 | .probe = hibmc_pci_probe, | |
414 | .remove = hibmc_pci_remove, | |
415 | .driver.pm = &hibmc_pm_ops, | |
416 | }; | |
417 | ||
418 | static int __init hibmc_init(void) | |
419 | { | |
420 | return pci_register_driver(&hibmc_pci_driver); | |
421 | } | |
422 | ||
423 | static void __exit hibmc_exit(void) | |
424 | { | |
425 | return pci_unregister_driver(&hibmc_pci_driver); | |
426 | } | |
427 | ||
428 | module_init(hibmc_init); | |
429 | module_exit(hibmc_exit); | |
430 | ||
431 | MODULE_DEVICE_TABLE(pci, hibmc_pci_table); | |
432 | MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>"); | |
433 | MODULE_DESCRIPTION("DRM Driver for Hisilicon Hibmc"); | |
434 | MODULE_LICENSE("GPL v2"); |