]>
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 | ||
22 | #include "hibmc_drm_drv.h" | |
23 | #include "hibmc_drm_regs.h" | |
24 | ||
25 | static const struct file_operations hibmc_fops = { | |
26 | .owner = THIS_MODULE, | |
27 | .open = drm_open, | |
28 | .release = drm_release, | |
29 | .unlocked_ioctl = drm_ioctl, | |
30 | .compat_ioctl = drm_compat_ioctl, | |
e4daebc7 | 31 | .mmap = hibmc_mmap, |
5e0df3a0 RZ |
32 | .poll = drm_poll, |
33 | .read = drm_read, | |
34 | .llseek = no_llseek, | |
35 | }; | |
36 | ||
37 | static int hibmc_enable_vblank(struct drm_device *dev, unsigned int pipe) | |
38 | { | |
39 | return 0; | |
40 | } | |
41 | ||
42 | static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe) | |
43 | { | |
44 | } | |
45 | ||
46 | static struct drm_driver hibmc_driver = { | |
e4daebc7 | 47 | .driver_features = DRIVER_GEM, |
5e0df3a0 RZ |
48 | .fops = &hibmc_fops, |
49 | .name = "hibmc", | |
50 | .date = "20160828", | |
51 | .desc = "hibmc drm driver", | |
52 | .major = 1, | |
53 | .minor = 0, | |
54 | .get_vblank_counter = drm_vblank_no_hw_counter, | |
55 | .enable_vblank = hibmc_enable_vblank, | |
56 | .disable_vblank = hibmc_disable_vblank, | |
e4daebc7 RZ |
57 | .gem_free_object_unlocked = hibmc_gem_free_object, |
58 | .dumb_create = hibmc_dumb_create, | |
59 | .dumb_map_offset = hibmc_dumb_mmap_offset, | |
60 | .dumb_destroy = drm_gem_dumb_destroy, | |
5e0df3a0 RZ |
61 | }; |
62 | ||
63 | static int hibmc_pm_suspend(struct device *dev) | |
64 | { | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static int hibmc_pm_resume(struct device *dev) | |
69 | { | |
70 | return 0; | |
71 | } | |
72 | ||
73 | static const struct dev_pm_ops hibmc_pm_ops = { | |
74 | SET_SYSTEM_SLEEP_PM_OPS(hibmc_pm_suspend, | |
75 | hibmc_pm_resume) | |
76 | }; | |
77 | ||
78 | /* | |
79 | * It can operate in one of three modes: 0, 1 or Sleep. | |
80 | */ | |
81 | void hibmc_set_power_mode(struct hibmc_drm_private *priv, | |
82 | unsigned int power_mode) | |
83 | { | |
84 | unsigned int control_value = 0; | |
85 | void __iomem *mmio = priv->mmio; | |
86 | unsigned int input = 1; | |
87 | ||
88 | if (power_mode > HIBMC_PW_MODE_CTL_MODE_SLEEP) | |
89 | return; | |
90 | ||
91 | if (power_mode == HIBMC_PW_MODE_CTL_MODE_SLEEP) | |
92 | input = 0; | |
93 | ||
94 | control_value = readl(mmio + HIBMC_POWER_MODE_CTRL); | |
95 | control_value &= ~(HIBMC_PW_MODE_CTL_MODE_MASK | | |
96 | HIBMC_PW_MODE_CTL_OSC_INPUT_MASK); | |
97 | control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_MODE, power_mode); | |
98 | control_value |= HIBMC_FIELD(HIBMC_PW_MODE_CTL_OSC_INPUT, input); | |
99 | writel(control_value, mmio + HIBMC_POWER_MODE_CTRL); | |
100 | } | |
101 | ||
102 | void hibmc_set_current_gate(struct hibmc_drm_private *priv, unsigned int gate) | |
103 | { | |
104 | unsigned int gate_reg; | |
105 | unsigned int mode; | |
106 | void __iomem *mmio = priv->mmio; | |
107 | ||
108 | /* Get current power mode. */ | |
109 | mode = (readl(mmio + HIBMC_POWER_MODE_CTRL) & | |
110 | HIBMC_PW_MODE_CTL_MODE_MASK) >> HIBMC_PW_MODE_CTL_MODE_SHIFT; | |
111 | ||
112 | switch (mode) { | |
113 | case HIBMC_PW_MODE_CTL_MODE_MODE0: | |
114 | gate_reg = HIBMC_MODE0_GATE; | |
115 | break; | |
116 | ||
117 | case HIBMC_PW_MODE_CTL_MODE_MODE1: | |
118 | gate_reg = HIBMC_MODE1_GATE; | |
119 | break; | |
120 | ||
121 | default: | |
122 | gate_reg = HIBMC_MODE0_GATE; | |
123 | break; | |
124 | } | |
125 | writel(gate, mmio + gate_reg); | |
126 | } | |
127 | ||
128 | static void hibmc_hw_config(struct hibmc_drm_private *priv) | |
129 | { | |
130 | unsigned int reg; | |
131 | ||
132 | /* On hardware reset, power mode 0 is default. */ | |
133 | hibmc_set_power_mode(priv, HIBMC_PW_MODE_CTL_MODE_MODE0); | |
134 | ||
135 | /* Enable display power gate & LOCALMEM power gate*/ | |
136 | reg = readl(priv->mmio + HIBMC_CURRENT_GATE); | |
137 | reg &= ~HIBMC_CURR_GATE_DISPLAY_MASK; | |
138 | reg &= ~HIBMC_CURR_GATE_LOCALMEM_MASK; | |
139 | reg |= HIBMC_CURR_GATE_DISPLAY(1); | |
140 | reg |= HIBMC_CURR_GATE_LOCALMEM(1); | |
141 | ||
142 | hibmc_set_current_gate(priv, reg); | |
143 | ||
144 | /* | |
145 | * Reset the memory controller. If the memory controller | |
146 | * is not reset in chip,the system might hang when sw accesses | |
147 | * the memory.The memory should be resetted after | |
148 | * changing the MXCLK. | |
149 | */ | |
150 | reg = readl(priv->mmio + HIBMC_MISC_CTRL); | |
151 | reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; | |
152 | reg |= HIBMC_MSCCTL_LOCALMEM_RESET(0); | |
153 | writel(reg, priv->mmio + HIBMC_MISC_CTRL); | |
154 | ||
155 | reg &= ~HIBMC_MSCCTL_LOCALMEM_RESET_MASK; | |
156 | reg |= HIBMC_MSCCTL_LOCALMEM_RESET(1); | |
157 | ||
158 | writel(reg, priv->mmio + HIBMC_MISC_CTRL); | |
159 | } | |
160 | ||
161 | static int hibmc_hw_map(struct hibmc_drm_private *priv) | |
162 | { | |
163 | struct drm_device *dev = priv->dev; | |
164 | struct pci_dev *pdev = dev->pdev; | |
165 | resource_size_t addr, size, ioaddr, iosize; | |
166 | ||
167 | ioaddr = pci_resource_start(pdev, 1); | |
168 | iosize = pci_resource_len(pdev, 1); | |
169 | priv->mmio = devm_ioremap_nocache(dev->dev, ioaddr, iosize); | |
170 | if (!priv->mmio) { | |
171 | DRM_ERROR("Cannot map mmio region\n"); | |
172 | return -ENOMEM; | |
173 | } | |
174 | ||
175 | addr = pci_resource_start(pdev, 0); | |
176 | size = pci_resource_len(pdev, 0); | |
177 | priv->fb_map = devm_ioremap(dev->dev, addr, size); | |
178 | if (!priv->fb_map) { | |
179 | DRM_ERROR("Cannot map framebuffer\n"); | |
180 | return -ENOMEM; | |
181 | } | |
182 | priv->fb_base = addr; | |
183 | priv->fb_size = size; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | static int hibmc_hw_init(struct hibmc_drm_private *priv) | |
189 | { | |
190 | int ret; | |
191 | ||
192 | ret = hibmc_hw_map(priv); | |
193 | if (ret) | |
194 | return ret; | |
195 | ||
196 | hibmc_hw_config(priv); | |
197 | ||
198 | return 0; | |
199 | } | |
200 | ||
201 | static int hibmc_unload(struct drm_device *dev) | |
202 | { | |
e4daebc7 RZ |
203 | struct hibmc_drm_private *priv = dev->dev_private; |
204 | ||
205 | hibmc_mm_fini(priv); | |
206 | dev->dev_private = NULL; | |
5e0df3a0 RZ |
207 | return 0; |
208 | } | |
209 | ||
210 | static int hibmc_load(struct drm_device *dev) | |
211 | { | |
212 | struct hibmc_drm_private *priv; | |
213 | int ret; | |
214 | ||
215 | priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); | |
216 | if (!priv) { | |
217 | DRM_ERROR("no memory to allocate for hibmc_drm_private\n"); | |
218 | return -ENOMEM; | |
219 | } | |
220 | dev->dev_private = priv; | |
221 | priv->dev = dev; | |
222 | ||
223 | ret = hibmc_hw_init(priv); | |
224 | if (ret) | |
225 | goto err; | |
226 | ||
e4daebc7 RZ |
227 | ret = hibmc_mm_init(priv); |
228 | if (ret) | |
229 | goto err; | |
230 | ||
5e0df3a0 RZ |
231 | return 0; |
232 | ||
233 | err: | |
234 | hibmc_unload(dev); | |
235 | DRM_ERROR("failed to initialize drm driver: %d\n", ret); | |
236 | return ret; | |
237 | } | |
238 | ||
239 | static int hibmc_pci_probe(struct pci_dev *pdev, | |
240 | const struct pci_device_id *ent) | |
241 | { | |
242 | struct drm_device *dev; | |
243 | int ret; | |
244 | ||
245 | dev = drm_dev_alloc(&hibmc_driver, &pdev->dev); | |
246 | if (!dev) { | |
247 | DRM_ERROR("failed to allocate drm_device\n"); | |
248 | return -ENOMEM; | |
249 | } | |
250 | ||
251 | dev->pdev = pdev; | |
252 | pci_set_drvdata(pdev, dev); | |
253 | ||
254 | ret = pci_enable_device(pdev); | |
255 | if (ret) { | |
256 | DRM_ERROR("failed to enable pci device: %d\n", ret); | |
257 | goto err_free; | |
258 | } | |
259 | ||
260 | ret = hibmc_load(dev); | |
261 | if (ret) { | |
262 | DRM_ERROR("failed to load hibmc: %d\n", ret); | |
263 | goto err_disable; | |
264 | } | |
265 | ||
266 | ret = drm_dev_register(dev, 0); | |
267 | if (ret) { | |
268 | DRM_ERROR("failed to register drv for userspace access: %d\n", | |
269 | ret); | |
270 | goto err_unload; | |
271 | } | |
272 | return 0; | |
273 | ||
274 | err_unload: | |
275 | hibmc_unload(dev); | |
276 | err_disable: | |
277 | pci_disable_device(pdev); | |
278 | err_free: | |
279 | drm_dev_unref(dev); | |
280 | ||
281 | return ret; | |
282 | } | |
283 | ||
284 | static void hibmc_pci_remove(struct pci_dev *pdev) | |
285 | { | |
286 | struct drm_device *dev = pci_get_drvdata(pdev); | |
287 | ||
288 | drm_dev_unregister(dev); | |
289 | hibmc_unload(dev); | |
290 | drm_dev_unref(dev); | |
291 | } | |
292 | ||
293 | static struct pci_device_id hibmc_pci_table[] = { | |
294 | {0x19e5, 0x1711, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | |
295 | {0,} | |
296 | }; | |
297 | ||
298 | static struct pci_driver hibmc_pci_driver = { | |
299 | .name = "hibmc-drm", | |
300 | .id_table = hibmc_pci_table, | |
301 | .probe = hibmc_pci_probe, | |
302 | .remove = hibmc_pci_remove, | |
303 | .driver.pm = &hibmc_pm_ops, | |
304 | }; | |
305 | ||
306 | static int __init hibmc_init(void) | |
307 | { | |
308 | return pci_register_driver(&hibmc_pci_driver); | |
309 | } | |
310 | ||
311 | static void __exit hibmc_exit(void) | |
312 | { | |
313 | return pci_unregister_driver(&hibmc_pci_driver); | |
314 | } | |
315 | ||
316 | module_init(hibmc_init); | |
317 | module_exit(hibmc_exit); | |
318 | ||
319 | MODULE_DEVICE_TABLE(pci, hibmc_pci_table); | |
320 | MODULE_AUTHOR("RongrongZou <zourongrong@huawei.com>"); | |
321 | MODULE_DESCRIPTION("DRM Driver for Hisilicon Hibmc"); | |
322 | MODULE_LICENSE("GPL v2"); |