1*f39db26cSSui Jingfeng // SPDX-License-Identifier: GPL-2.0+
2*f39db26cSSui Jingfeng /*
3*f39db26cSSui Jingfeng  * Copyright (C) 2023 Loongson Technology Corporation Limited
4*f39db26cSSui Jingfeng  */
5*f39db26cSSui Jingfeng 
6*f39db26cSSui Jingfeng #include <linux/pci.h>
7*f39db26cSSui Jingfeng #include <linux/vgaarb.h>
8*f39db26cSSui Jingfeng 
9*f39db26cSSui Jingfeng #include <drm/drm_aperture.h>
10*f39db26cSSui Jingfeng #include <drm/drm_atomic.h>
11*f39db26cSSui Jingfeng #include <drm/drm_atomic_helper.h>
12*f39db26cSSui Jingfeng #include <drm/drm_drv.h>
13*f39db26cSSui Jingfeng #include <drm/drm_fbdev_generic.h>
14*f39db26cSSui Jingfeng #include <drm/drm_gem_framebuffer_helper.h>
15*f39db26cSSui Jingfeng #include <drm/drm_ioctl.h>
16*f39db26cSSui Jingfeng #include <drm/drm_modeset_helper.h>
17*f39db26cSSui Jingfeng #include <drm/drm_probe_helper.h>
18*f39db26cSSui Jingfeng #include <drm/drm_vblank.h>
19*f39db26cSSui Jingfeng 
20*f39db26cSSui Jingfeng #include "loongson_module.h"
21*f39db26cSSui Jingfeng #include "lsdc_drv.h"
22*f39db26cSSui Jingfeng #include "lsdc_gem.h"
23*f39db26cSSui Jingfeng #include "lsdc_ttm.h"
24*f39db26cSSui Jingfeng 
25*f39db26cSSui Jingfeng #define DRIVER_AUTHOR               "Sui Jingfeng <suijingfeng@loongson.cn>"
26*f39db26cSSui Jingfeng #define DRIVER_NAME                 "loongson"
27*f39db26cSSui Jingfeng #define DRIVER_DESC                 "drm driver for loongson graphics"
28*f39db26cSSui Jingfeng #define DRIVER_DATE                 "20220701"
29*f39db26cSSui Jingfeng #define DRIVER_MAJOR                1
30*f39db26cSSui Jingfeng #define DRIVER_MINOR                0
31*f39db26cSSui Jingfeng #define DRIVER_PATCHLEVEL           0
32*f39db26cSSui Jingfeng 
33*f39db26cSSui Jingfeng DEFINE_DRM_GEM_FOPS(lsdc_gem_fops);
34*f39db26cSSui Jingfeng 
35*f39db26cSSui Jingfeng static const struct drm_driver lsdc_drm_driver = {
36*f39db26cSSui Jingfeng 	.driver_features = DRIVER_MODESET | DRIVER_RENDER | DRIVER_GEM | DRIVER_ATOMIC,
37*f39db26cSSui Jingfeng 	.fops = &lsdc_gem_fops,
38*f39db26cSSui Jingfeng 
39*f39db26cSSui Jingfeng 	.name = DRIVER_NAME,
40*f39db26cSSui Jingfeng 	.desc = DRIVER_DESC,
41*f39db26cSSui Jingfeng 	.date = DRIVER_DATE,
42*f39db26cSSui Jingfeng 	.major = DRIVER_MAJOR,
43*f39db26cSSui Jingfeng 	.minor = DRIVER_MINOR,
44*f39db26cSSui Jingfeng 	.patchlevel = DRIVER_PATCHLEVEL,
45*f39db26cSSui Jingfeng 
46*f39db26cSSui Jingfeng 	.debugfs_init = lsdc_debugfs_init,
47*f39db26cSSui Jingfeng 	.dumb_create = lsdc_dumb_create,
48*f39db26cSSui Jingfeng 	.dumb_map_offset = lsdc_dumb_map_offset,
49*f39db26cSSui Jingfeng 	.gem_prime_import_sg_table = lsdc_prime_import_sg_table,
50*f39db26cSSui Jingfeng };
51*f39db26cSSui Jingfeng 
52*f39db26cSSui Jingfeng static const struct drm_mode_config_funcs lsdc_mode_config_funcs = {
53*f39db26cSSui Jingfeng 	.fb_create = drm_gem_fb_create,
54*f39db26cSSui Jingfeng 	.atomic_check = drm_atomic_helper_check,
55*f39db26cSSui Jingfeng 	.atomic_commit = drm_atomic_helper_commit,
56*f39db26cSSui Jingfeng };
57*f39db26cSSui Jingfeng 
58*f39db26cSSui Jingfeng /* Display related */
59*f39db26cSSui Jingfeng 
lsdc_modeset_init(struct lsdc_device * ldev,unsigned int num_crtc,const struct lsdc_kms_funcs * funcs,bool has_vblank)60*f39db26cSSui Jingfeng static int lsdc_modeset_init(struct lsdc_device *ldev,
61*f39db26cSSui Jingfeng 			     unsigned int num_crtc,
62*f39db26cSSui Jingfeng 			     const struct lsdc_kms_funcs *funcs,
63*f39db26cSSui Jingfeng 			     bool has_vblank)
64*f39db26cSSui Jingfeng {
65*f39db26cSSui Jingfeng 	struct drm_device *ddev = &ldev->base;
66*f39db26cSSui Jingfeng 	struct lsdc_display_pipe *dispipe;
67*f39db26cSSui Jingfeng 	unsigned int i;
68*f39db26cSSui Jingfeng 	int ret;
69*f39db26cSSui Jingfeng 
70*f39db26cSSui Jingfeng 	for (i = 0; i < num_crtc; i++) {
71*f39db26cSSui Jingfeng 		dispipe = &ldev->dispipe[i];
72*f39db26cSSui Jingfeng 
73*f39db26cSSui Jingfeng 		/* We need an index before crtc is initialized */
74*f39db26cSSui Jingfeng 		dispipe->index = i;
75*f39db26cSSui Jingfeng 
76*f39db26cSSui Jingfeng 		ret = funcs->create_i2c(ddev, dispipe, i);
77*f39db26cSSui Jingfeng 		if (ret)
78*f39db26cSSui Jingfeng 			return ret;
79*f39db26cSSui Jingfeng 	}
80*f39db26cSSui Jingfeng 
81*f39db26cSSui Jingfeng 	for (i = 0; i < num_crtc; i++) {
82*f39db26cSSui Jingfeng 		struct i2c_adapter *ddc = NULL;
83*f39db26cSSui Jingfeng 
84*f39db26cSSui Jingfeng 		dispipe = &ldev->dispipe[i];
85*f39db26cSSui Jingfeng 		if (dispipe->li2c)
86*f39db26cSSui Jingfeng 			ddc = &dispipe->li2c->adapter;
87*f39db26cSSui Jingfeng 
88*f39db26cSSui Jingfeng 		ret = funcs->output_init(ddev, dispipe, ddc, i);
89*f39db26cSSui Jingfeng 		if (ret)
90*f39db26cSSui Jingfeng 			return ret;
91*f39db26cSSui Jingfeng 
92*f39db26cSSui Jingfeng 		ldev->num_output++;
93*f39db26cSSui Jingfeng 	}
94*f39db26cSSui Jingfeng 
95*f39db26cSSui Jingfeng 	for (i = 0; i < num_crtc; i++) {
96*f39db26cSSui Jingfeng 		dispipe = &ldev->dispipe[i];
97*f39db26cSSui Jingfeng 
98*f39db26cSSui Jingfeng 		ret = funcs->primary_plane_init(ddev, &dispipe->primary.base, i);
99*f39db26cSSui Jingfeng 		if (ret)
100*f39db26cSSui Jingfeng 			return ret;
101*f39db26cSSui Jingfeng 
102*f39db26cSSui Jingfeng 		ret = funcs->cursor_plane_init(ddev, &dispipe->cursor.base, i);
103*f39db26cSSui Jingfeng 		if (ret)
104*f39db26cSSui Jingfeng 			return ret;
105*f39db26cSSui Jingfeng 
106*f39db26cSSui Jingfeng 		ret = funcs->crtc_init(ddev, &dispipe->crtc.base,
107*f39db26cSSui Jingfeng 				       &dispipe->primary.base,
108*f39db26cSSui Jingfeng 				       &dispipe->cursor.base,
109*f39db26cSSui Jingfeng 				       i, has_vblank);
110*f39db26cSSui Jingfeng 		if (ret)
111*f39db26cSSui Jingfeng 			return ret;
112*f39db26cSSui Jingfeng 	}
113*f39db26cSSui Jingfeng 
114*f39db26cSSui Jingfeng 	drm_info(ddev, "Total %u outputs\n", ldev->num_output);
115*f39db26cSSui Jingfeng 
116*f39db26cSSui Jingfeng 	return 0;
117*f39db26cSSui Jingfeng }
118*f39db26cSSui Jingfeng 
119*f39db26cSSui Jingfeng static const struct drm_mode_config_helper_funcs lsdc_mode_config_helper_funcs = {
120*f39db26cSSui Jingfeng 	.atomic_commit_tail = drm_atomic_helper_commit_tail,
121*f39db26cSSui Jingfeng };
122*f39db26cSSui Jingfeng 
lsdc_mode_config_init(struct drm_device * ddev,const struct lsdc_desc * descp)123*f39db26cSSui Jingfeng static int lsdc_mode_config_init(struct drm_device *ddev,
124*f39db26cSSui Jingfeng 				 const struct lsdc_desc *descp)
125*f39db26cSSui Jingfeng {
126*f39db26cSSui Jingfeng 	int ret;
127*f39db26cSSui Jingfeng 
128*f39db26cSSui Jingfeng 	ret = drmm_mode_config_init(ddev);
129*f39db26cSSui Jingfeng 	if (ret)
130*f39db26cSSui Jingfeng 		return ret;
131*f39db26cSSui Jingfeng 
132*f39db26cSSui Jingfeng 	ddev->mode_config.funcs = &lsdc_mode_config_funcs;
133*f39db26cSSui Jingfeng 	ddev->mode_config.min_width = 1;
134*f39db26cSSui Jingfeng 	ddev->mode_config.min_height = 1;
135*f39db26cSSui Jingfeng 	ddev->mode_config.max_width = descp->max_width * LSDC_NUM_CRTC;
136*f39db26cSSui Jingfeng 	ddev->mode_config.max_height = descp->max_height * LSDC_NUM_CRTC;
137*f39db26cSSui Jingfeng 	ddev->mode_config.preferred_depth = 24;
138*f39db26cSSui Jingfeng 	ddev->mode_config.prefer_shadow = 1;
139*f39db26cSSui Jingfeng 
140*f39db26cSSui Jingfeng 	ddev->mode_config.cursor_width = descp->hw_cursor_h;
141*f39db26cSSui Jingfeng 	ddev->mode_config.cursor_height = descp->hw_cursor_h;
142*f39db26cSSui Jingfeng 
143*f39db26cSSui Jingfeng 	ddev->mode_config.helper_private = &lsdc_mode_config_helper_funcs;
144*f39db26cSSui Jingfeng 
145*f39db26cSSui Jingfeng 	if (descp->has_vblank_counter)
146*f39db26cSSui Jingfeng 		ddev->max_vblank_count = 0xffffffff;
147*f39db26cSSui Jingfeng 
148*f39db26cSSui Jingfeng 	return ret;
149*f39db26cSSui Jingfeng }
150*f39db26cSSui Jingfeng 
151*f39db26cSSui Jingfeng /*
152*f39db26cSSui Jingfeng  * The GPU and display controller in the LS7A1000/LS7A2000/LS2K2000 are
153*f39db26cSSui Jingfeng  * separated PCIE devices. They are two devices, not one. Bar 2 of the GPU
154*f39db26cSSui Jingfeng  * device contains the base address and size of the VRAM, both the GPU and
155*f39db26cSSui Jingfeng  * the DC could access the on-board VRAM.
156*f39db26cSSui Jingfeng  */
lsdc_get_dedicated_vram(struct lsdc_device * ldev,struct pci_dev * pdev_dc,const struct lsdc_desc * descp)157*f39db26cSSui Jingfeng static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
158*f39db26cSSui Jingfeng 				   struct pci_dev *pdev_dc,
159*f39db26cSSui Jingfeng 				   const struct lsdc_desc *descp)
160*f39db26cSSui Jingfeng {
161*f39db26cSSui Jingfeng 	struct drm_device *ddev = &ldev->base;
162*f39db26cSSui Jingfeng 	struct pci_dev *pdev_gpu;
163*f39db26cSSui Jingfeng 	resource_size_t base, size;
164*f39db26cSSui Jingfeng 
165*f39db26cSSui Jingfeng 	/*
166*f39db26cSSui Jingfeng 	 * The GPU has 00:06.0 as its BDF, while the DC has 00:06.1
167*f39db26cSSui Jingfeng 	 * This is true for the LS7A1000, LS7A2000 and LS2K2000.
168*f39db26cSSui Jingfeng 	 */
169*f39db26cSSui Jingfeng 	pdev_gpu = pci_get_domain_bus_and_slot(pci_domain_nr(pdev_dc->bus),
170*f39db26cSSui Jingfeng 					       pdev_dc->bus->number,
171*f39db26cSSui Jingfeng 					       PCI_DEVFN(6, 0));
172*f39db26cSSui Jingfeng 	if (!pdev_gpu) {
173*f39db26cSSui Jingfeng 		drm_err(ddev, "No GPU device, then no VRAM\n");
174*f39db26cSSui Jingfeng 		return -ENODEV;
175*f39db26cSSui Jingfeng 	}
176*f39db26cSSui Jingfeng 
177*f39db26cSSui Jingfeng 	base = pci_resource_start(pdev_gpu, 2);
178*f39db26cSSui Jingfeng 	size = pci_resource_len(pdev_gpu, 2);
179*f39db26cSSui Jingfeng 
180*f39db26cSSui Jingfeng 	ldev->vram_base = base;
181*f39db26cSSui Jingfeng 	ldev->vram_size = size;
182*f39db26cSSui Jingfeng 	ldev->gpu = pdev_gpu;
183*f39db26cSSui Jingfeng 
184*f39db26cSSui Jingfeng 	drm_info(ddev, "Dedicated vram start: 0x%llx, size: %uMiB\n",
185*f39db26cSSui Jingfeng 		 (u64)base, (u32)(size >> 20));
186*f39db26cSSui Jingfeng 
187*f39db26cSSui Jingfeng 	return 0;
188*f39db26cSSui Jingfeng }
189*f39db26cSSui Jingfeng 
190*f39db26cSSui Jingfeng static struct lsdc_device *
lsdc_create_device(struct pci_dev * pdev,const struct lsdc_desc * descp,const struct drm_driver * driver)191*f39db26cSSui Jingfeng lsdc_create_device(struct pci_dev *pdev,
192*f39db26cSSui Jingfeng 		   const struct lsdc_desc *descp,
193*f39db26cSSui Jingfeng 		   const struct drm_driver *driver)
194*f39db26cSSui Jingfeng {
195*f39db26cSSui Jingfeng 	struct lsdc_device *ldev;
196*f39db26cSSui Jingfeng 	struct drm_device *ddev;
197*f39db26cSSui Jingfeng 	int ret;
198*f39db26cSSui Jingfeng 
199*f39db26cSSui Jingfeng 	ldev = devm_drm_dev_alloc(&pdev->dev, driver, struct lsdc_device, base);
200*f39db26cSSui Jingfeng 	if (IS_ERR(ldev))
201*f39db26cSSui Jingfeng 		return ldev;
202*f39db26cSSui Jingfeng 
203*f39db26cSSui Jingfeng 	ldev->dc = pdev;
204*f39db26cSSui Jingfeng 	ldev->descp = descp;
205*f39db26cSSui Jingfeng 
206*f39db26cSSui Jingfeng 	ddev = &ldev->base;
207*f39db26cSSui Jingfeng 
208*f39db26cSSui Jingfeng 	loongson_gfxpll_create(ddev, &ldev->gfxpll);
209*f39db26cSSui Jingfeng 
210*f39db26cSSui Jingfeng 	ret = lsdc_get_dedicated_vram(ldev, pdev, descp);
211*f39db26cSSui Jingfeng 	if (ret) {
212*f39db26cSSui Jingfeng 		drm_err(ddev, "Init VRAM failed: %d\n", ret);
213*f39db26cSSui Jingfeng 		return ERR_PTR(ret);
214*f39db26cSSui Jingfeng 	}
215*f39db26cSSui Jingfeng 
216*f39db26cSSui Jingfeng 	ret = drm_aperture_remove_conflicting_framebuffers(ldev->vram_base,
217*f39db26cSSui Jingfeng 							   ldev->vram_size,
218*f39db26cSSui Jingfeng 							   driver);
219*f39db26cSSui Jingfeng 	if (ret) {
220*f39db26cSSui Jingfeng 		drm_err(ddev, "Remove firmware framebuffers failed: %d\n", ret);
221*f39db26cSSui Jingfeng 		return ERR_PTR(ret);
222*f39db26cSSui Jingfeng 	}
223*f39db26cSSui Jingfeng 
224*f39db26cSSui Jingfeng 	ret = lsdc_ttm_init(ldev);
225*f39db26cSSui Jingfeng 	if (ret) {
226*f39db26cSSui Jingfeng 		drm_err(ddev, "Memory manager init failed: %d\n", ret);
227*f39db26cSSui Jingfeng 		return ERR_PTR(ret);
228*f39db26cSSui Jingfeng 	}
229*f39db26cSSui Jingfeng 
230*f39db26cSSui Jingfeng 	lsdc_gem_init(ddev);
231*f39db26cSSui Jingfeng 
232*f39db26cSSui Jingfeng 	/* Bar 0 of the DC device contains the MMIO register's base address */
233*f39db26cSSui Jingfeng 	ldev->reg_base = pcim_iomap(pdev, 0, 0);
234*f39db26cSSui Jingfeng 	if (!ldev->reg_base)
235*f39db26cSSui Jingfeng 		return ERR_PTR(-ENODEV);
236*f39db26cSSui Jingfeng 
237*f39db26cSSui Jingfeng 	spin_lock_init(&ldev->reglock);
238*f39db26cSSui Jingfeng 
239*f39db26cSSui Jingfeng 	ret = lsdc_mode_config_init(ddev, descp);
240*f39db26cSSui Jingfeng 	if (ret)
241*f39db26cSSui Jingfeng 		return ERR_PTR(ret);
242*f39db26cSSui Jingfeng 
243*f39db26cSSui Jingfeng 	ret = lsdc_modeset_init(ldev, descp->num_of_crtc, descp->funcs,
244*f39db26cSSui Jingfeng 				loongson_vblank);
245*f39db26cSSui Jingfeng 	if (ret)
246*f39db26cSSui Jingfeng 		return ERR_PTR(ret);
247*f39db26cSSui Jingfeng 
248*f39db26cSSui Jingfeng 	drm_mode_config_reset(ddev);
249*f39db26cSSui Jingfeng 
250*f39db26cSSui Jingfeng 	return ldev;
251*f39db26cSSui Jingfeng }
252*f39db26cSSui Jingfeng 
253*f39db26cSSui Jingfeng /* For multiple GPU driver instance co-exixt in the system */
254*f39db26cSSui Jingfeng 
lsdc_vga_set_decode(struct pci_dev * pdev,bool state)255*f39db26cSSui Jingfeng static unsigned int lsdc_vga_set_decode(struct pci_dev *pdev, bool state)
256*f39db26cSSui Jingfeng {
257*f39db26cSSui Jingfeng 	return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
258*f39db26cSSui Jingfeng }
259*f39db26cSSui Jingfeng 
lsdc_pci_probe(struct pci_dev * pdev,const struct pci_device_id * ent)260*f39db26cSSui Jingfeng static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
261*f39db26cSSui Jingfeng {
262*f39db26cSSui Jingfeng 	const struct lsdc_desc *descp;
263*f39db26cSSui Jingfeng 	struct drm_device *ddev;
264*f39db26cSSui Jingfeng 	struct lsdc_device *ldev;
265*f39db26cSSui Jingfeng 	int ret;
266*f39db26cSSui Jingfeng 
267*f39db26cSSui Jingfeng 	descp = lsdc_device_probe(pdev, ent->driver_data);
268*f39db26cSSui Jingfeng 	if (IS_ERR_OR_NULL(descp))
269*f39db26cSSui Jingfeng 		return -ENODEV;
270*f39db26cSSui Jingfeng 
271*f39db26cSSui Jingfeng 	pci_set_master(pdev);
272*f39db26cSSui Jingfeng 
273*f39db26cSSui Jingfeng 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
274*f39db26cSSui Jingfeng 	if (ret)
275*f39db26cSSui Jingfeng 		return ret;
276*f39db26cSSui Jingfeng 
277*f39db26cSSui Jingfeng 	ret = pcim_enable_device(pdev);
278*f39db26cSSui Jingfeng 	if (ret)
279*f39db26cSSui Jingfeng 		return ret;
280*f39db26cSSui Jingfeng 
281*f39db26cSSui Jingfeng 	dev_info(&pdev->dev, "Found %s, revision: %u",
282*f39db26cSSui Jingfeng 		 to_loongson_gfx(descp)->model, pdev->revision);
283*f39db26cSSui Jingfeng 
284*f39db26cSSui Jingfeng 	ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
285*f39db26cSSui Jingfeng 	if (IS_ERR(ldev))
286*f39db26cSSui Jingfeng 		return PTR_ERR(ldev);
287*f39db26cSSui Jingfeng 
288*f39db26cSSui Jingfeng 	ddev = &ldev->base;
289*f39db26cSSui Jingfeng 
290*f39db26cSSui Jingfeng 	pci_set_drvdata(pdev, ddev);
291*f39db26cSSui Jingfeng 
292*f39db26cSSui Jingfeng 	vga_client_register(pdev, lsdc_vga_set_decode);
293*f39db26cSSui Jingfeng 
294*f39db26cSSui Jingfeng 	drm_kms_helper_poll_init(ddev);
295*f39db26cSSui Jingfeng 
296*f39db26cSSui Jingfeng 	if (loongson_vblank) {
297*f39db26cSSui Jingfeng 		ret = drm_vblank_init(ddev, descp->num_of_crtc);
298*f39db26cSSui Jingfeng 		if (ret)
299*f39db26cSSui Jingfeng 			return ret;
300*f39db26cSSui Jingfeng 
301*f39db26cSSui Jingfeng 		ret = devm_request_irq(&pdev->dev, pdev->irq,
302*f39db26cSSui Jingfeng 				       descp->funcs->irq_handler,
303*f39db26cSSui Jingfeng 				       IRQF_SHARED,
304*f39db26cSSui Jingfeng 				       dev_name(&pdev->dev), ddev);
305*f39db26cSSui Jingfeng 		if (ret) {
306*f39db26cSSui Jingfeng 			drm_err(ddev, "Failed to register interrupt: %d\n", ret);
307*f39db26cSSui Jingfeng 			return ret;
308*f39db26cSSui Jingfeng 		}
309*f39db26cSSui Jingfeng 
310*f39db26cSSui Jingfeng 		drm_info(ddev, "registered irq: %u\n", pdev->irq);
311*f39db26cSSui Jingfeng 	}
312*f39db26cSSui Jingfeng 
313*f39db26cSSui Jingfeng 	ret = drm_dev_register(ddev, 0);
314*f39db26cSSui Jingfeng 	if (ret)
315*f39db26cSSui Jingfeng 		return ret;
316*f39db26cSSui Jingfeng 
317*f39db26cSSui Jingfeng 	drm_fbdev_generic_setup(ddev, 32);
318*f39db26cSSui Jingfeng 
319*f39db26cSSui Jingfeng 	return 0;
320*f39db26cSSui Jingfeng }
321*f39db26cSSui Jingfeng 
lsdc_pci_remove(struct pci_dev * pdev)322*f39db26cSSui Jingfeng static void lsdc_pci_remove(struct pci_dev *pdev)
323*f39db26cSSui Jingfeng {
324*f39db26cSSui Jingfeng 	struct drm_device *ddev = pci_get_drvdata(pdev);
325*f39db26cSSui Jingfeng 
326*f39db26cSSui Jingfeng 	drm_dev_unregister(ddev);
327*f39db26cSSui Jingfeng 	drm_atomic_helper_shutdown(ddev);
328*f39db26cSSui Jingfeng }
329*f39db26cSSui Jingfeng 
lsdc_drm_freeze(struct drm_device * ddev)330*f39db26cSSui Jingfeng static int lsdc_drm_freeze(struct drm_device *ddev)
331*f39db26cSSui Jingfeng {
332*f39db26cSSui Jingfeng 	struct lsdc_device *ldev = to_lsdc(ddev);
333*f39db26cSSui Jingfeng 	struct lsdc_bo *lbo;
334*f39db26cSSui Jingfeng 	int ret;
335*f39db26cSSui Jingfeng 
336*f39db26cSSui Jingfeng 	/* unpin all of buffers in the VRAM */
337*f39db26cSSui Jingfeng 	mutex_lock(&ldev->gem.mutex);
338*f39db26cSSui Jingfeng 	list_for_each_entry(lbo, &ldev->gem.objects, list) {
339*f39db26cSSui Jingfeng 		struct ttm_buffer_object *tbo = &lbo->tbo;
340*f39db26cSSui Jingfeng 		struct ttm_resource *resource = tbo->resource;
341*f39db26cSSui Jingfeng 		unsigned int pin_count = tbo->pin_count;
342*f39db26cSSui Jingfeng 
343*f39db26cSSui Jingfeng 		drm_dbg(ddev, "bo[%p], size: %zuKiB, type: %s, pin count: %u\n",
344*f39db26cSSui Jingfeng 			lbo, lsdc_bo_size(lbo) >> 10,
345*f39db26cSSui Jingfeng 			lsdc_mem_type_to_str(resource->mem_type), pin_count);
346*f39db26cSSui Jingfeng 
347*f39db26cSSui Jingfeng 		if (!pin_count)
348*f39db26cSSui Jingfeng 			continue;
349*f39db26cSSui Jingfeng 
350*f39db26cSSui Jingfeng 		if (resource->mem_type == TTM_PL_VRAM) {
351*f39db26cSSui Jingfeng 			ret = lsdc_bo_reserve(lbo);
352*f39db26cSSui Jingfeng 			if (unlikely(ret)) {
353*f39db26cSSui Jingfeng 				drm_err(ddev, "bo reserve failed: %d\n", ret);
354*f39db26cSSui Jingfeng 				continue;
355*f39db26cSSui Jingfeng 			}
356*f39db26cSSui Jingfeng 
357*f39db26cSSui Jingfeng 			do {
358*f39db26cSSui Jingfeng 				lsdc_bo_unpin(lbo);
359*f39db26cSSui Jingfeng 				--pin_count;
360*f39db26cSSui Jingfeng 			} while (pin_count);
361*f39db26cSSui Jingfeng 
362*f39db26cSSui Jingfeng 			lsdc_bo_unreserve(lbo);
363*f39db26cSSui Jingfeng 		}
364*f39db26cSSui Jingfeng 	}
365*f39db26cSSui Jingfeng 	mutex_unlock(&ldev->gem.mutex);
366*f39db26cSSui Jingfeng 
367*f39db26cSSui Jingfeng 	lsdc_bo_evict_vram(ddev);
368*f39db26cSSui Jingfeng 
369*f39db26cSSui Jingfeng 	ret = drm_mode_config_helper_suspend(ddev);
370*f39db26cSSui Jingfeng 	if (unlikely(ret)) {
371*f39db26cSSui Jingfeng 		drm_err(ddev, "Freeze error: %d", ret);
372*f39db26cSSui Jingfeng 		return ret;
373*f39db26cSSui Jingfeng 	}
374*f39db26cSSui Jingfeng 
375*f39db26cSSui Jingfeng 	return 0;
376*f39db26cSSui Jingfeng }
377*f39db26cSSui Jingfeng 
lsdc_drm_resume(struct device * dev)378*f39db26cSSui Jingfeng static int lsdc_drm_resume(struct device *dev)
379*f39db26cSSui Jingfeng {
380*f39db26cSSui Jingfeng 	struct pci_dev *pdev = to_pci_dev(dev);
381*f39db26cSSui Jingfeng 	struct drm_device *ddev = pci_get_drvdata(pdev);
382*f39db26cSSui Jingfeng 
383*f39db26cSSui Jingfeng 	return drm_mode_config_helper_resume(ddev);
384*f39db26cSSui Jingfeng }
385*f39db26cSSui Jingfeng 
lsdc_pm_freeze(struct device * dev)386*f39db26cSSui Jingfeng static int lsdc_pm_freeze(struct device *dev)
387*f39db26cSSui Jingfeng {
388*f39db26cSSui Jingfeng 	struct pci_dev *pdev = to_pci_dev(dev);
389*f39db26cSSui Jingfeng 	struct drm_device *ddev = pci_get_drvdata(pdev);
390*f39db26cSSui Jingfeng 
391*f39db26cSSui Jingfeng 	return lsdc_drm_freeze(ddev);
392*f39db26cSSui Jingfeng }
393*f39db26cSSui Jingfeng 
lsdc_pm_thaw(struct device * dev)394*f39db26cSSui Jingfeng static int lsdc_pm_thaw(struct device *dev)
395*f39db26cSSui Jingfeng {
396*f39db26cSSui Jingfeng 	return lsdc_drm_resume(dev);
397*f39db26cSSui Jingfeng }
398*f39db26cSSui Jingfeng 
lsdc_pm_suspend(struct device * dev)399*f39db26cSSui Jingfeng static int lsdc_pm_suspend(struct device *dev)
400*f39db26cSSui Jingfeng {
401*f39db26cSSui Jingfeng 	struct pci_dev *pdev = to_pci_dev(dev);
402*f39db26cSSui Jingfeng 	int error;
403*f39db26cSSui Jingfeng 
404*f39db26cSSui Jingfeng 	error = lsdc_pm_freeze(dev);
405*f39db26cSSui Jingfeng 	if (error)
406*f39db26cSSui Jingfeng 		return error;
407*f39db26cSSui Jingfeng 
408*f39db26cSSui Jingfeng 	pci_save_state(pdev);
409*f39db26cSSui Jingfeng 	/* Shut down the device */
410*f39db26cSSui Jingfeng 	pci_disable_device(pdev);
411*f39db26cSSui Jingfeng 	pci_set_power_state(pdev, PCI_D3hot);
412*f39db26cSSui Jingfeng 
413*f39db26cSSui Jingfeng 	return 0;
414*f39db26cSSui Jingfeng }
415*f39db26cSSui Jingfeng 
lsdc_pm_resume(struct device * dev)416*f39db26cSSui Jingfeng static int lsdc_pm_resume(struct device *dev)
417*f39db26cSSui Jingfeng {
418*f39db26cSSui Jingfeng 	struct pci_dev *pdev = to_pci_dev(dev);
419*f39db26cSSui Jingfeng 
420*f39db26cSSui Jingfeng 	pci_set_power_state(pdev, PCI_D0);
421*f39db26cSSui Jingfeng 
422*f39db26cSSui Jingfeng 	pci_restore_state(pdev);
423*f39db26cSSui Jingfeng 
424*f39db26cSSui Jingfeng 	if (pcim_enable_device(pdev))
425*f39db26cSSui Jingfeng 		return -EIO;
426*f39db26cSSui Jingfeng 
427*f39db26cSSui Jingfeng 	return lsdc_pm_thaw(dev);
428*f39db26cSSui Jingfeng }
429*f39db26cSSui Jingfeng 
430*f39db26cSSui Jingfeng static const struct dev_pm_ops lsdc_pm_ops = {
431*f39db26cSSui Jingfeng 	.suspend = lsdc_pm_suspend,
432*f39db26cSSui Jingfeng 	.resume = lsdc_pm_resume,
433*f39db26cSSui Jingfeng 	.freeze = lsdc_pm_freeze,
434*f39db26cSSui Jingfeng 	.thaw = lsdc_pm_thaw,
435*f39db26cSSui Jingfeng 	.poweroff = lsdc_pm_freeze,
436*f39db26cSSui Jingfeng 	.restore = lsdc_pm_resume,
437*f39db26cSSui Jingfeng };
438*f39db26cSSui Jingfeng 
439*f39db26cSSui Jingfeng static const struct pci_device_id lsdc_pciid_list[] = {
440*f39db26cSSui Jingfeng 	{PCI_VDEVICE(LOONGSON, 0x7a06), CHIP_LS7A1000},
441*f39db26cSSui Jingfeng 	{PCI_VDEVICE(LOONGSON, 0x7a36), CHIP_LS7A2000},
442*f39db26cSSui Jingfeng 	{ }
443*f39db26cSSui Jingfeng };
444*f39db26cSSui Jingfeng 
445*f39db26cSSui Jingfeng struct pci_driver lsdc_pci_driver = {
446*f39db26cSSui Jingfeng 	.name = DRIVER_NAME,
447*f39db26cSSui Jingfeng 	.id_table = lsdc_pciid_list,
448*f39db26cSSui Jingfeng 	.probe = lsdc_pci_probe,
449*f39db26cSSui Jingfeng 	.remove = lsdc_pci_remove,
450*f39db26cSSui Jingfeng 	.driver.pm = &lsdc_pm_ops,
451*f39db26cSSui Jingfeng };
452*f39db26cSSui Jingfeng 
453*f39db26cSSui Jingfeng MODULE_DEVICE_TABLE(pci, lsdc_pciid_list);
454*f39db26cSSui Jingfeng MODULE_AUTHOR(DRIVER_AUTHOR);
455*f39db26cSSui Jingfeng MODULE_DESCRIPTION(DRIVER_DESC);
456*f39db26cSSui Jingfeng MODULE_LICENSE("GPL");
457