1 /*
2 * Copyright (c) 2018 Loongson Technology Co., Ltd.
3 * Authors:
4 * Chen Zhu <zhuchen@loongson.cn>
5 * Yaling Fang <fangyaling@loongson.cn>
6 * Dandan Zhang <zhangdandan@loongson.cn>
7 * Huacai Chen <chenhc@lemote.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 */
13
14 #include <asm/addrspace.h>
15 #include <linux/dma-mapping.h>
16 #include <linux/vmalloc.h>
17 #include <linux/console.h>
18 #include <linux/device.h>
19 #include <linux/slab.h>
20 #include <linux/fs.h>
21 #include <linux/pci.h>
22 #include <linux/pci_ids.h>
23 #include <linux/platform_device.h>
24 #include <linux/module.h>
25 #include <linux/kernel.h>
26 #include <linux/errno.h>
27 #include <linux/init.h>
28 #include <linux/string.h>
29 #include <linux/vga_switcheroo.h>
30
31 #include <drm/drm_atomic_helper.h>
32 #include <drm/drm_crtc_helper.h>
33 #include <drm/drm_probe_helper.h>
34 #include <drm/drm_gem_cma_helper.h>
35 #include <drm/drm_gem_framebuffer_helper.h>
36 #include <loongson.h>
37 #include "loongson_drv.h"
38
39 #define DEVICE_NAME "loongson-drm"
40 #define DRIVER_NAME "loongson-drm"
41 #define DRIVER_DESC "Loongson DRM Driver"
42 #define DRIVER_DATE "20201201"
43 #define DRIVER_MAJOR 1
44 #define DRIVER_MINOR 0
45 #define DRIVER_PATCHLEVEL 1
46
47 bool hw_cursor = false;
48 module_param_named(cursor, hw_cursor, bool, 0600);
49
50 bool poll_connector = false;
51 module_param_named(poll, poll_connector, bool, 0600);
52
53 DEFINE_SPINLOCK(loongson_reglock);
54
55 /**
56 * loongson_mode_funcs---basic driver provided mode setting functions
57 *
58 * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that
59 * involve drivers.
60 */
61 static const struct drm_mode_config_funcs loongson_mode_funcs = {
62 .fb_create = drm_gem_fb_create,
63 .atomic_check = drm_atomic_helper_check,
64 .atomic_commit = drm_atomic_helper_commit,
65 .output_poll_changed = drm_fb_helper_output_poll_changed
66 };
67
68 /**
69 * loongson_drm_device_init ----init drm device
70 *
71 * @dev pointer to drm_device structure
72 * @flags start up flag
73 *
74 * RETURN
75 * drm device init result
76 */
loongson_drm_device_init(struct drm_device * dev,uint32_t flags)77 static int loongson_drm_device_init(struct drm_device *dev, uint32_t flags)
78 {
79 int ret = 0;
80 struct loongson_drm_device *ldev = dev->dev_private;
81
82 ldev->num_crtc = 2;
83 loongson_vbios_init(ldev);
84
85 /*BAR 0 contains registers */
86 ldev->mmio_base = pci_resource_start(ldev->dev->pdev, 0);
87 ldev->mmio_size = pci_resource_len(ldev->dev->pdev, 0);
88
89 ldev->mmio = pcim_iomap(dev->pdev, 0, 0);
90 if (ldev->mmio == NULL)
91 return -ENOMEM;
92
93 DRM_INFO("ldev->mmio_base = 0x%llx, ldev->mmio_size = 0x%llx\n",
94 ldev->mmio_base, ldev->mmio_size);
95
96 if (!devm_request_mem_region(ldev->dev->dev, ldev->mmio_base, ldev->mmio_size,
97 "loongson_drmfb_mmio")) {
98 DRM_ERROR("Can't reserve mmio registers\n");
99 return -ENOMEM;
100 }
101
102 ret = loongson_gpio_init(ldev);
103 if (ret < 0)
104 DRM_ERROR("Failed to initialize dc gpios\n");
105
106 return ret;
107 }
108
109 /**
110 * loongson_modeset_init --- init kernel mode setting
111 *
112 * @ldev: pointer to loongson_drm_device structure
113 *
114 * RETURN
115 * return init result
116 */
loongson_modeset_init(struct loongson_drm_device * ldev)117 int loongson_modeset_init(struct loongson_drm_device *ldev)
118 {
119 int i, ret;
120 struct drm_encoder *encoder;
121 struct drm_connector *connector;
122
123 ldev->mode_info[0].mode_config_initialized = true;
124 ldev->mode_info[1].mode_config_initialized = true;
125
126 ldev->dev->mode_config.max_width = LOONGSON_MAX_FB_WIDTH;
127 ldev->dev->mode_config.max_height = LOONGSON_MAX_FB_HEIGHT;
128
129 ldev->dev->mode_config.cursor_width = 32;
130 ldev->dev->mode_config.cursor_height = 32;
131
132 ldev->dev->mode_config.allow_fb_modifiers = true;
133
134 ret = loongson_i2c_init(ldev);
135 if (ret < 0) {
136 DRM_ERROR("Failed to initialize i2c\n");
137 return ret;
138 }
139
140 loongson_crtc_init(ldev);
141
142 for (i=0; i<ldev->num_crtc; i++) {
143 DRM_DEBUG("loongson drm encoder init\n");
144 ldev->mode_info[i].crtc = &ldev->lcrtc[i];
145 encoder = loongson_encoder_init(ldev->dev, i);
146 if (!encoder) {
147 DRM_ERROR("loongson_encoder_init failed\n");
148 return -1;
149 }
150 ldev->mode_info[i].encoder = to_loongson_encoder(encoder);
151
152 DRM_DEBUG("loongson drm connector init\n");
153 connector = loongson_connector_init(ldev->dev, i);
154 if (!connector) {
155 DRM_ERROR("loongson_connector_init failed\n");
156 return -1;
157 }
158 ldev->mode_info[i].connector = to_loongson_connector(connector);
159
160 drm_connector_attach_encoder(connector, encoder);
161 if (poll_connector)
162 connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
163 }
164
165 return 0;
166 }
167
168 /**
169 * loongson_modeset_fini --- deinit kernel mode setting
170 *
171 * @ldev: pointer to loongson_drm_device structure
172 *
173 * RETURN
174 */
loongson_modeset_fini(struct loongson_drm_device * ldev)175 void loongson_modeset_fini(struct loongson_drm_device *ldev)
176 {
177 }
178
loongson_detect_chip(struct loongson_drm_device * ldev)179 static int loongson_detect_chip(struct loongson_drm_device *ldev)
180 {
181 struct pci_dev *pdev;
182
183 pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1, NULL);
184 if (pdev) {
185 ldev->chip = dc_7a1000;
186 ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a15, NULL);
187 DRM_INFO("Set LS7A1000 DC device\n");
188 return 0;
189 }
190
191 pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2, NULL);
192 if (pdev) {
193 ldev->chip = dc_7a2000;
194 ldev->gpu_pdev = pci_get_device(PCI_VENDOR_ID_LOONGSON, 0x7a25, NULL);
195 DRM_INFO("Set LS7A2000 DC device\n");
196 return 0;
197 }
198
199 return -1;
200 }
201
202 /**
203 * loongson_vga_load - setup chip and create an initial config
204 * @dev: DRM device
205 * @flags: startup flags
206 *
207 * The driver load routine has to do several things:
208 * - initialize the memory manager
209 * - allocate initial config memory
210 * - setup the DRM framebuffer with the allocated memory
211 */
loongson_drm_load(struct drm_device * dev,unsigned long flags)212 static int loongson_drm_load(struct drm_device *dev, unsigned long flags)
213 {
214 int r, ret, irq;
215 struct loongson_drm_device *ldev;
216
217 dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(32));
218
219 ldev = devm_kzalloc(dev->dev, sizeof(struct loongson_drm_device), GFP_KERNEL);
220 if (ldev == NULL)
221 return -ENOMEM;
222 dev->dev_private = (void *)ldev;
223 ldev->dev = dev;
224
225 ret = loongson_detect_chip(ldev);
226 if (ret)
227 dev_err(dev->dev, "Fatal error during detect chip: %d\n", ret);
228
229 ret = loongson_drm_device_init(dev, flags);
230 DRM_DEBUG("end loongson drm device init.\n");
231
232 drm_mode_config_init(dev);
233 dev->mode_config.funcs = (void *)&loongson_mode_funcs;
234 dev->mode_config.preferred_depth = 24;
235 dev->mode_config.prefer_shadow = 1;
236
237 irq = dev->pdev->irq;
238 dev_set_drvdata(dev->dev, dev);
239 pci_set_drvdata(dev->pdev, dev);
240
241 r = loongson_modeset_init(ldev);
242 if (r)
243 dev_err(dev->dev, "Fatal error during modeset init: %d\n", r);
244
245 r = drm_irq_install(dev, irq);
246 if (r)
247 dev_err(dev->dev, "Fatal error during irq install: %d\n", r);
248
249 ldev->inited = true;
250 drm_mode_config_reset(dev);
251
252 r = drm_vblank_init(dev, ldev->num_crtc);
253 if (r)
254 dev_err(dev->dev, "Fatal error during vblank init: %d\n", r);
255
256 /* Make small buffers to store a hardware cursor (double buffered icon updates) */
257 ldev->cursor = drm_gem_cma_create(dev, roundup(32*32*4, PAGE_SIZE));
258
259 drm_kms_helper_poll_init(dev);
260
261 drm_fb_helper_remove_conflicting_framebuffers(NULL, "loongson-drmfb", false);
262
263 return 0;
264 }
265
266 /**
267 * loongson_drm_unload--release drm resource
268 *
269 * @dev: pointer to drm_device
270 *
271 */
loongson_drm_unload(struct drm_device * dev)272 static void loongson_drm_unload(struct drm_device *dev)
273 {
274 struct loongson_drm_device *ldev = dev->dev_private;
275
276 if (ldev == NULL)
277 return;
278
279 loongson_modeset_fini(ldev);
280 drm_mode_config_cleanup(dev);
281 dev->dev_private = NULL;
282 dev_set_drvdata(dev->dev, NULL);
283 ldev->inited = false;
284
285 return;
286 }
287
288 /**
289 * loongson_drm_open -Driver callback when a new struct drm_file is opened.
290 * Useful for setting up driver-private data structures like buffer allocators,
291 * execution contexts or similar things.
292 *
293 * @dev DRM device
294 * @file DRM file private date
295 *
296 * RETURN
297 * 0 on success, a negative error code on failure, which will be promoted to
298 * userspace as the result of the open() system call.
299 */
loongson_drm_open(struct drm_device * dev,struct drm_file * file)300 static int loongson_drm_open(struct drm_device *dev, struct drm_file *file)
301 {
302 file->driver_priv = NULL;
303
304 DRM_DEBUG("open: dev=%p, file=%p", dev, file);
305
306 return 0;
307 }
308
309 DEFINE_DRM_GEM_CMA_FOPS(fops);
310
311 /**
312 * loongson_drm_driver - DRM device structure
313 *
314 * .load: driver callback to complete initialization steps after the driver is registered
315 * .unload:Reverse the effects of the driver load callback
316 * .open:Driver callback when a new struct drm_file is opened
317 * .fops:File operations for the DRM device node.
318 * .gem_free_object:deconstructor for drm_gem_objects
319 * .dumb_create:This creates a new dumb buffer in the driver’s backing storage manager
320 * (GEM, TTM or something else entirely) and returns the resulting buffer handle.
321 * This handle can then be wrapped up into a framebuffer modeset object
322 * .dumb_map_offset:Allocate an offset in the drm device node’s address space
323 * to be able to memory map a dumb buffer
324 * .dump_destory:This destroys the userspace handle for the given dumb backing storage buffer
325 */
326 static struct drm_driver loongson_drm_driver = {
327 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_ATOMIC,
328 .open = loongson_drm_open,
329 .fops = &fops,
330
331 .dumb_create = drm_gem_cma_dumb_create,
332 .gem_free_object_unlocked = drm_gem_cma_free_object,
333 .gem_vm_ops = &drm_gem_cma_vm_ops,
334
335 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
336 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
337
338 .gem_prime_import = drm_gem_prime_import,
339 .gem_prime_export = drm_gem_prime_export,
340
341 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
342 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
343 .gem_prime_vmap = drm_gem_cma_prime_vmap,
344 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
345 .gem_prime_mmap = drm_gem_cma_prime_mmap,
346
347 .irq_handler = loongson_irq_handler,
348 .irq_preinstall = loongson_irq_preinstall,
349 .irq_postinstall = loongson_irq_postinstall,
350 .irq_uninstall = loongson_irq_uninstall,
351 .name = DRIVER_NAME,
352 .desc = DRIVER_DESC,
353 .date = DRIVER_DATE,
354 .major = DRIVER_MAJOR,
355 .minor = DRIVER_MINOR,
356 .patchlevel = DRIVER_PATCHLEVEL,
357 };
358
359 /**
360 * loongson_drm_pci_devices -- pci device id info
361 *
362 * __u32 vendor, device Vendor and device ID or PCI_ANY_ID
363 * __u32 subvendor, subdevice Subsystem ID's or PCI_ANY_ID
364 * __u32 class, class_mask (class,subclass,prog-if) triplet
365 * kernel_ulong_t driver_data Data private to the driver
366 */
367 static struct pci_device_id loongson_drm_pci_devices[] = {
368 {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC1)},
369 {PCI_DEVICE(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC2)},
370 {0, 0, 0, 0, 0, 0, 0}
371 };
372
373 MODULE_DEVICE_TABLE(pci, loongson_drm_pci_devices);
374
375 /**
376 * loongson_drm_pci_register -- add pci device
377 *
378 * @pdev PCI device
379 * @ent pci device id
380 */
loongson_drm_pci_register(struct pci_dev * pdev,const struct pci_device_id * ent)381 static int loongson_drm_pci_register(struct pci_dev *pdev,
382 const struct pci_device_id *ent)
383
384 {
385 int ret;
386 struct drm_device *dev;
387
388 dev = drm_dev_alloc(&loongson_drm_driver, &pdev->dev);
389 if (IS_ERR(dev))
390 return PTR_ERR(dev);
391
392 ret = pci_enable_device(pdev);
393 if (ret)
394 goto err_free;
395
396 dev->pdev = pdev;
397
398 loongson_drm_load(dev, 0x0);
399
400 ret = drm_dev_register(dev, 0);
401 if (ret)
402 goto err_pdev;
403
404 drm_fbdev_generic_setup(dev, 32);
405
406 return 0;
407
408 err_pdev:
409 pci_disable_device(pdev);
410 err_free:
411 drm_dev_put(dev);
412 return ret;
413 }
414
415 /**
416 * loongson_drm_pci_unregister -- release drm device
417 *
418 * @pdev PCI device
419 */
loongson_drm_pci_unregister(struct pci_dev * pdev)420 static void loongson_drm_pci_unregister(struct pci_dev *pdev)
421 {
422 struct drm_device *dev = pci_get_drvdata(pdev);
423 loongson_drm_unload(dev);
424 drm_dev_put(dev);
425 }
426
427 /*
428 * Suspend & resume.
429 */
430 /*
431 * loongson_drm_suspend - initiate device suspend
432 *
433 * @pdev: drm dev pointer
434 * @state: suspend state
435 *
436 * Puts the hw in the suspend state (all asics).
437 * Returns 0 for success or an error on failure.
438 * Called at driver suspend.
439 */
loongson_drm_suspend(struct drm_device * dev,bool suspend)440 int loongson_drm_suspend(struct drm_device *dev, bool suspend)
441 {
442 struct loongson_drm_device *ldev;
443
444 if (dev == NULL || dev->dev_private == NULL)
445 return -ENODEV;
446
447 ldev = dev->dev_private;
448
449 drm_kms_helper_poll_disable(dev);
450 ldev->state = drm_atomic_helper_suspend(dev);
451
452 pci_save_state(dev->pdev);
453 if (suspend) {
454 /* Shut down the device */
455 pci_disable_device(dev->pdev);
456 pci_set_power_state(dev->pdev, PCI_D3hot);
457 }
458
459 console_lock();
460 drm_fb_helper_set_suspend(ldev->dev->fb_helper, 1);
461 console_unlock();
462
463 return 0;
464 }
465
466 /*
467 * * loongson_drm_resume - initiate device suspend
468 *
469 * @pdev: drm dev pointer
470 * @state: suspend state
471 *
472 * Puts the hw in the suspend state (all asics).
473 * Returns 0 for success or an error on failure.
474 * Called at driver suspend.
475 */
476
loongson_drm_resume(struct drm_device * dev,bool resume)477 int loongson_drm_resume(struct drm_device *dev, bool resume)
478 {
479 struct loongson_drm_device *ldev = dev->dev_private;
480
481 console_lock();
482
483 if (resume) {
484 pci_set_power_state(dev->pdev, PCI_D0);
485 pci_restore_state(dev->pdev);
486 if (pci_enable_device(dev->pdev)) {
487 console_unlock();
488 return -1;
489 }
490 }
491
492 /* blat the mode back in */
493 drm_atomic_helper_resume(dev, ldev->state);
494
495 drm_kms_helper_poll_enable(dev);
496
497 drm_fb_helper_set_suspend(ldev->dev->fb_helper, 0);
498
499 console_unlock();
500
501 return 0;
502 }
503
504 /**
505 * loongson_drm_pm_suspend
506 *
507 * @dev pointer to the device
508 *
509 * Executed before putting the system into a sleep state in which the
510 * contents of main memory are preserved.
511 */
loongson_drm_pm_suspend(struct device * dev)512 static int loongson_drm_pm_suspend(struct device *dev)
513 {
514 struct drm_device *drm_dev = dev_get_drvdata(dev);
515
516 return loongson_drm_suspend(drm_dev, true);
517 }
518
519 /**
520 * loongson_drm_pm_resume
521 *
522 * @dev pointer to the device
523 *
524 * Executed after waking the system up from a sleep state in which the
525 * contents of main memory were preserved.
526 */
loongson_drm_pm_resume(struct device * dev)527 static int loongson_drm_pm_resume(struct device *dev)
528 {
529 struct drm_device *drm_dev = dev_get_drvdata(dev);
530
531 return loongson_drm_resume(drm_dev, true);
532 }
533
534 /**
535 * loongson_drm_pm_freeze
536 *
537 * @dev pointer to device
538 *
539 * Hibernation-specific, executed before creating a hibernation image.
540 * Analogous to @suspend(), but it should not enable the device to signal
541 * wakeup events or change its power state.
542 */
loongson_drm_pm_freeze(struct device * dev)543 static int loongson_drm_pm_freeze(struct device *dev)
544 {
545 struct drm_device *drm_dev = dev_get_drvdata(dev);
546
547 return loongson_drm_suspend(drm_dev, false);
548 }
549
550 /**
551 * loongson_drm_pm_draw
552 *
553 * @dev pointer to device
554 *
555 * Hibernation-specific, executed after creating a hibernation image OR
556 * if the creation of an image has failed. Also executed after a failing
557 * attempt to restore the contents of main memory from such an image.
558 * Undo the changes made by the preceding @freeze(), so the device can be
559 * operated in the same way as immediately before the call to @freeze().
560 */
loongson_drm_pm_thaw(struct device * dev)561 static int loongson_drm_pm_thaw(struct device *dev)
562 {
563 struct drm_device *drm_dev = dev_get_drvdata(dev);
564
565 return loongson_drm_resume(drm_dev, false);
566 }
567
568 #define loongson_drm_pm_poweroff loongson_drm_pm_freeze
569 #define loongson_drm_pm_restore loongson_drm_pm_resume
570
571 /*
572 * * struct dev_pm_ops - device PM callbacks
573 *
574 *@suspend: Executed before putting the system into a sleep state in which the
575 * contents of main memory are preserved.
576 *@resume: Executed after waking the system up from a sleep state in which the
577 * contents of main memory were preserved.
578 *@freeze: Hibernation-specific, executed before creating a hibernation image.
579 * Analogous to @suspend(), but it should not enable the device to signal
580 * wakeup events or change its power state. The majority of subsystems
581 * (with the notable exception of the PCI bus type) expect the driver-level
582 * @freeze() to save the device settings in memory to be used by @restore()
583 * during the subsequent resume from hibernation.
584 *@thaw: Hibernation-specific, executed after creating a hibernation image OR
585 * if the creation of an image has failed. Also executed after a failing
586 * attempt to restore the contents of main memory from such an image.
587 * Undo the changes made by the preceding @freeze(), so the device can be
588 * operated in the same way as immediately before the call to @freeze().
589 *@poweroff: Hibernation-specific, executed after saving a hibernation image.
590 * Analogous to @suspend(), but it need not save the device's settings in
591 * memory.
592 *@restore: Hibernation-specific, executed after restoring the contents of main
593 * memory from a hibernation image, analogous to @resume().
594 */
595 static const struct dev_pm_ops loongson_drm_pm_ops = {
596 .suspend = loongson_drm_pm_suspend,
597 .resume = loongson_drm_pm_resume,
598 .freeze = loongson_drm_pm_freeze,
599 .thaw = loongson_drm_pm_thaw,
600 .poweroff = loongson_drm_pm_poweroff,
601 .restore = loongson_drm_pm_restore,
602 };
603
604 /**
605 * loongson_drm_pci_driver -- pci driver structure
606 *
607 * .id_table : must be non-NULL for probe to be called
608 * .probe: New device inserted
609 * .remove: Device removed
610 * .resume: Device suspended
611 * .suspend: Device woken up
612 */
613 static struct pci_driver loongson_drm_pci_driver = {
614 .name = DRIVER_NAME,
615 .id_table = loongson_drm_pci_devices,
616 .probe = loongson_drm_pci_register,
617 .remove = loongson_drm_pci_unregister,
618 .driver.pm = &loongson_drm_pm_ops,
619 };
620
621 /**
622 * loongson_drm_pci_init() -- kernel module init function
623 */
loongson_drm_init(void)624 static int __init loongson_drm_init(void)
625 {
626 int ret;
627 struct pci_dev *pdev = NULL;
628
629 /* If external graphics card exist, use it as default */
630 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) {
631 if (pdev->vendor == PCI_VENDOR_ID_ATI)
632 return 0;
633 if (pdev->vendor == 0x1a03) /* ASpeed */
634 return 0;
635 }
636
637 ret = pci_register_driver(&loongson_drm_pci_driver);
638
639 return ret;
640 }
641
642 /**
643 * loongson_drm_pci_exit() -- kernel module exit function
644 */
loongson_drm_exit(void)645 static void __exit loongson_drm_exit(void)
646 {
647 pci_unregister_driver(&loongson_drm_pci_driver);
648 }
649
650 module_init(loongson_drm_init);
651 module_exit(loongson_drm_exit);
652
653 MODULE_AUTHOR("Chen Zhu <zhuchen@loongson.cn>");
654 MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
655 MODULE_DESCRIPTION("Loongson LS7A DRM Driver");
656 MODULE_LICENSE("GPL");
657