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
47bool hw_cursor = false;
48module_param_named(cursor, hw_cursor, bool, 0600);
49
50bool poll_connector = false;
51module_param_named(poll, poll_connector, bool, 0600);
52
53DEFINE_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 */
61static 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 */
77static 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 */
117int 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 */
175void loongson_modeset_fini(struct loongson_drm_device *ldev)
176{
177}
178
179static 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 */
212static 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 */
272static 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 */
300static 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
309DEFINE_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 */
326static 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 */
367static 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
373MODULE_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 */
381static 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
408err_pdev:
409	pci_disable_device(pdev);
410err_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 */
420static 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 */
440int 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
477int 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 */
512static 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 */
527static 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 */
543static 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 */
561static 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 */
595static 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 */
613static 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 */
624static 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 */
645static void __exit loongson_drm_exit(void)
646{
647	pci_unregister_driver(&loongson_drm_pci_driver);
648}
649
650module_init(loongson_drm_init);
651module_exit(loongson_drm_exit);
652
653MODULE_AUTHOR("Chen Zhu <zhuchen@loongson.cn>");
654MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");
655MODULE_DESCRIPTION("Loongson LS7A DRM Driver");
656MODULE_LICENSE("GPL");
657