xref: /openbmc/linux/drivers/gpu/drm/qxl/qxl_kms.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1f64122c1SDave Airlie /*
2f64122c1SDave Airlie  * Copyright 2013 Red Hat Inc.
3f64122c1SDave Airlie  *
4f64122c1SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
5f64122c1SDave Airlie  * copy of this software and associated documentation files (the "Software"),
6f64122c1SDave Airlie  * to deal in the Software without restriction, including without limitation
7f64122c1SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8f64122c1SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
9f64122c1SDave Airlie  * Software is furnished to do so, subject to the following conditions:
10f64122c1SDave Airlie  *
11f64122c1SDave Airlie  * The above copyright notice and this permission notice shall be included in
12f64122c1SDave Airlie  * all copies or substantial portions of the Software.
13f64122c1SDave Airlie  *
14f64122c1SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15f64122c1SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16f64122c1SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17f64122c1SDave Airlie  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18f64122c1SDave Airlie  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19f64122c1SDave Airlie  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20f64122c1SDave Airlie  * OTHER DEALINGS IN THE SOFTWARE.
21f64122c1SDave Airlie  *
22f64122c1SDave Airlie  * Authors: Dave Airlie
23f64122c1SDave Airlie  *          Alon Levy
24f64122c1SDave Airlie  */
25f64122c1SDave Airlie 
26c0f4b75cSSam Ravnborg #include <linux/io-mapping.h>
27c0f4b75cSSam Ravnborg #include <linux/pci.h>
28c0f4b75cSSam Ravnborg 
29c0f4b75cSSam Ravnborg #include <drm/drm_drv.h>
30873863b6SDaniel Vetter #include <drm/drm_managed.h>
31c0f4b75cSSam Ravnborg #include <drm/drm_probe_helper.h>
32c0f4b75cSSam Ravnborg 
33f64122c1SDave Airlie #include "qxl_drv.h"
34f64122c1SDave Airlie #include "qxl_object.h"
35f64122c1SDave Airlie 
qxl_check_device(struct qxl_device * qdev)36f64122c1SDave Airlie static bool qxl_check_device(struct qxl_device *qdev)
37f64122c1SDave Airlie {
38f64122c1SDave Airlie 	struct qxl_rom *rom = qdev->rom;
39f64122c1SDave Airlie 
40f64122c1SDave Airlie 	if (rom->magic != 0x4f525851) {
41f64122c1SDave Airlie 		DRM_ERROR("bad rom signature %x\n", rom->magic);
42f64122c1SDave Airlie 		return false;
43f64122c1SDave Airlie 	}
44f64122c1SDave Airlie 
45f64122c1SDave Airlie 	DRM_INFO("Device Version %d.%d\n", rom->id, rom->update_id);
46f64122c1SDave Airlie 	DRM_INFO("Compression level %d log level %d\n", rom->compression_level,
47f64122c1SDave Airlie 		 rom->log_level);
48f64122c1SDave Airlie 	DRM_INFO("%d io pages at offset 0x%x\n",
49f64122c1SDave Airlie 		 rom->num_io_pages, rom->pages_offset);
50f64122c1SDave Airlie 	DRM_INFO("%d byte draw area at offset 0x%x\n",
51f64122c1SDave Airlie 		 rom->surface0_area_size, rom->draw_area_offset);
52f64122c1SDave Airlie 
53f64122c1SDave Airlie 	qdev->vram_size = rom->surface0_area_size;
54f64122c1SDave Airlie 	DRM_INFO("RAM header offset: 0x%x\n", rom->ram_header_offset);
55f64122c1SDave Airlie 	return true;
56f64122c1SDave Airlie }
57f64122c1SDave Airlie 
setup_hw_slot(struct qxl_device * qdev,struct qxl_memslot * slot)582ec6bd67SGerd Hoffmann static void setup_hw_slot(struct qxl_device *qdev, struct qxl_memslot *slot)
59c9fdda2aSDave Airlie {
60c9fdda2aSDave Airlie 	qdev->ram_header->mem_slot.mem_start = slot->start_phys_addr;
612ec6bd67SGerd Hoffmann 	qdev->ram_header->mem_slot.mem_end = slot->start_phys_addr + slot->size;
622ec6bd67SGerd Hoffmann 	qxl_io_memslot_add(qdev, qdev->rom->slots_start + slot->index);
63c9fdda2aSDave Airlie }
64c9fdda2aSDave Airlie 
setup_slot(struct qxl_device * qdev,struct qxl_memslot * slot,unsigned int slot_index,const char * slot_name,unsigned long start_phys_addr,unsigned long size)652ec6bd67SGerd Hoffmann static void setup_slot(struct qxl_device *qdev,
662ec6bd67SGerd Hoffmann 		       struct qxl_memslot *slot,
672ec6bd67SGerd Hoffmann 		       unsigned int slot_index,
682ec6bd67SGerd Hoffmann 		       const char *slot_name,
692ec6bd67SGerd Hoffmann 		       unsigned long start_phys_addr,
702ec6bd67SGerd Hoffmann 		       unsigned long size)
71f64122c1SDave Airlie {
72f64122c1SDave Airlie 	uint64_t high_bits;
73f64122c1SDave Airlie 
742ec6bd67SGerd Hoffmann 	slot->index = slot_index;
752ec6bd67SGerd Hoffmann 	slot->name = slot_name;
76f64122c1SDave Airlie 	slot->start_phys_addr = start_phys_addr;
772ec6bd67SGerd Hoffmann 	slot->size = size;
78c9fdda2aSDave Airlie 
792ec6bd67SGerd Hoffmann 	setup_hw_slot(qdev, slot);
80c9fdda2aSDave Airlie 
81f64122c1SDave Airlie 	slot->generation = qdev->rom->slot_generation;
822ec6bd67SGerd Hoffmann 	high_bits = (qdev->rom->slots_start + slot->index)
833ebe3ddfSGerd Hoffmann 		<< qdev->rom->slot_gen_bits;
84f64122c1SDave Airlie 	high_bits |= slot->generation;
853ebe3ddfSGerd Hoffmann 	high_bits <<= (64 - (qdev->rom->slot_gen_bits + qdev->rom->slot_id_bits));
86f64122c1SDave Airlie 	slot->high_bits = high_bits;
872ec6bd67SGerd Hoffmann 
888ef963baSNirmoy Das 	DRM_INFO("slot %d (%s): base 0x%08lx, size 0x%08lx\n",
892ec6bd67SGerd Hoffmann 		 slot->index, slot->name,
902ec6bd67SGerd Hoffmann 		 (unsigned long)slot->start_phys_addr,
918ef963baSNirmoy Das 		 (unsigned long)slot->size);
92f64122c1SDave Airlie }
93f64122c1SDave Airlie 
qxl_reinit_memslots(struct qxl_device * qdev)94c9fdda2aSDave Airlie void qxl_reinit_memslots(struct qxl_device *qdev)
95c9fdda2aSDave Airlie {
962ec6bd67SGerd Hoffmann 	setup_hw_slot(qdev, &qdev->main_slot);
972ec6bd67SGerd Hoffmann 	setup_hw_slot(qdev, &qdev->surfaces_slot);
98c9fdda2aSDave Airlie }
99c9fdda2aSDave Airlie 
qxl_gc_work(struct work_struct * work)100f64122c1SDave Airlie static void qxl_gc_work(struct work_struct *work)
101f64122c1SDave Airlie {
102f64122c1SDave Airlie 	struct qxl_device *qdev = container_of(work, struct qxl_device, gc_work);
103408799ebSShayenne da Luz Moura 
104f64122c1SDave Airlie 	qxl_garbage_collect(qdev);
105f64122c1SDave Airlie }
106f64122c1SDave Airlie 
qxl_device_init(struct qxl_device * qdev,struct pci_dev * pdev)1072b65d567SGabriel Krisman Bertazi int qxl_device_init(struct qxl_device *qdev,
108aa5b62baSGabriel Krisman Bertazi 		    struct pci_dev *pdev)
109f64122c1SDave Airlie {
11035541782SGerd Hoffmann 	int r, sb;
111f64122c1SDave Airlie 
112cbdded7fSGabriel Krisman Bertazi 	pci_set_drvdata(pdev, &qdev->ddev);
113cbdded7fSGabriel Krisman Bertazi 
114f64122c1SDave Airlie 	mutex_init(&qdev->gem.mutex);
115f64122c1SDave Airlie 	mutex_init(&qdev->update_area_mutex);
116f64122c1SDave Airlie 	mutex_init(&qdev->release_mutex);
117f64122c1SDave Airlie 	mutex_init(&qdev->surf_evict_mutex);
118b3740e88SChristophe Fergeau 	qxl_gem_init(qdev);
119f64122c1SDave Airlie 
120f64122c1SDave Airlie 	qdev->rom_base = pci_resource_start(pdev, 2);
121f64122c1SDave Airlie 	qdev->rom_size = pci_resource_len(pdev, 2);
122f64122c1SDave Airlie 	qdev->vram_base = pci_resource_start(pdev, 0);
123f64122c1SDave Airlie 	qdev->io_base = pci_resource_start(pdev, 3);
124f64122c1SDave Airlie 
125f64122c1SDave Airlie 	qdev->vram_mapping = io_mapping_create_wc(qdev->vram_base, pci_resource_len(pdev, 0));
1265043348aSAnton Vasilyev 	if (!qdev->vram_mapping) {
1275043348aSAnton Vasilyev 		pr_err("Unable to create vram_mapping");
128a9b0b24aSDaniel Vetter 		return -ENOMEM;
1295043348aSAnton Vasilyev 	}
13035541782SGerd Hoffmann 
13135541782SGerd Hoffmann 	if (pci_resource_len(pdev, 4) > 0) {
13235541782SGerd Hoffmann 		/* 64bit surface bar present */
13335541782SGerd Hoffmann 		sb = 4;
13435541782SGerd Hoffmann 		qdev->surfaceram_base = pci_resource_start(pdev, sb);
13535541782SGerd Hoffmann 		qdev->surfaceram_size = pci_resource_len(pdev, sb);
13635541782SGerd Hoffmann 		qdev->surface_mapping =
13735541782SGerd Hoffmann 			io_mapping_create_wc(qdev->surfaceram_base,
13835541782SGerd Hoffmann 					     qdev->surfaceram_size);
13935541782SGerd Hoffmann 	}
14035541782SGerd Hoffmann 	if (qdev->surface_mapping == NULL) {
14135541782SGerd Hoffmann 		/* 64bit surface bar not present (or mapping failed) */
14235541782SGerd Hoffmann 		sb = 1;
14335541782SGerd Hoffmann 		qdev->surfaceram_base = pci_resource_start(pdev, sb);
14435541782SGerd Hoffmann 		qdev->surfaceram_size = pci_resource_len(pdev, sb);
14535541782SGerd Hoffmann 		qdev->surface_mapping =
14635541782SGerd Hoffmann 			io_mapping_create_wc(qdev->surfaceram_base,
14735541782SGerd Hoffmann 					     qdev->surfaceram_size);
1485043348aSAnton Vasilyev 		if (!qdev->surface_mapping) {
1495043348aSAnton Vasilyev 			pr_err("Unable to create surface_mapping");
1505043348aSAnton Vasilyev 			r = -ENOMEM;
1515043348aSAnton Vasilyev 			goto vram_mapping_free;
1525043348aSAnton Vasilyev 		}
15335541782SGerd Hoffmann 	}
15435541782SGerd Hoffmann 
15535541782SGerd Hoffmann 	DRM_DEBUG_KMS("qxl: vram %llx-%llx(%dM %dk), surface %llx-%llx(%dM %dk, %s)\n",
156970fa986SDave Airlie 		 (unsigned long long)qdev->vram_base,
157970fa986SDave Airlie 		 (unsigned long long)pci_resource_end(pdev, 0),
158f64122c1SDave Airlie 		 (int)pci_resource_len(pdev, 0) / 1024 / 1024,
159f64122c1SDave Airlie 		 (int)pci_resource_len(pdev, 0) / 1024,
160970fa986SDave Airlie 		 (unsigned long long)qdev->surfaceram_base,
16135541782SGerd Hoffmann 		 (unsigned long long)pci_resource_end(pdev, sb),
162f64122c1SDave Airlie 		 (int)qdev->surfaceram_size / 1024 / 1024,
16335541782SGerd Hoffmann 		 (int)qdev->surfaceram_size / 1024,
16435541782SGerd Hoffmann 		 (sb == 4) ? "64bit" : "32bit");
165f64122c1SDave Airlie 
16659ab4ee0SCong Liu 	qdev->rom = ioremap_wc(qdev->rom_base, qdev->rom_size);
167f64122c1SDave Airlie 	if (!qdev->rom) {
168f64122c1SDave Airlie 		pr_err("Unable to ioremap ROM\n");
1695043348aSAnton Vasilyev 		r = -ENOMEM;
1705043348aSAnton Vasilyev 		goto surface_mapping_free;
171f64122c1SDave Airlie 	}
172f64122c1SDave Airlie 
1735043348aSAnton Vasilyev 	if (!qxl_check_device(qdev)) {
1745043348aSAnton Vasilyev 		r = -ENODEV;
175dbe3ad61SMarkus Elfring 		goto rom_unmap;
1765043348aSAnton Vasilyev 	}
177f64122c1SDave Airlie 
178f64122c1SDave Airlie 	r = qxl_bo_init(qdev);
179f64122c1SDave Airlie 	if (r) {
180f64122c1SDave Airlie 		DRM_ERROR("bo init failed %d\n", r);
1815043348aSAnton Vasilyev 		goto rom_unmap;
182f64122c1SDave Airlie 	}
183f64122c1SDave Airlie 
18459ab4ee0SCong Liu 	qdev->ram_header = ioremap_wc(qdev->vram_base +
185f64122c1SDave Airlie 				   qdev->rom->ram_header_offset,
186f64122c1SDave Airlie 				   sizeof(*qdev->ram_header));
1875043348aSAnton Vasilyev 	if (!qdev->ram_header) {
1885043348aSAnton Vasilyev 		DRM_ERROR("Unable to ioremap RAM header\n");
1895043348aSAnton Vasilyev 		r = -ENOMEM;
1905043348aSAnton Vasilyev 		goto bo_fini;
1915043348aSAnton Vasilyev 	}
192f64122c1SDave Airlie 
193f64122c1SDave Airlie 	qdev->command_ring = qxl_ring_create(&(qdev->ram_header->cmd_ring_hdr),
194f64122c1SDave Airlie 					     sizeof(struct qxl_command),
195f64122c1SDave Airlie 					     QXL_COMMAND_RING_SIZE,
196f64122c1SDave Airlie 					     qdev->io_base + QXL_IO_NOTIFY_CMD,
197f64122c1SDave Airlie 					     &qdev->display_event);
1985043348aSAnton Vasilyev 	if (!qdev->command_ring) {
1995043348aSAnton Vasilyev 		DRM_ERROR("Unable to create command ring\n");
2005043348aSAnton Vasilyev 		r = -ENOMEM;
2015043348aSAnton Vasilyev 		goto ram_header_unmap;
2025043348aSAnton Vasilyev 	}
203f64122c1SDave Airlie 
204f64122c1SDave Airlie 	qdev->cursor_ring = qxl_ring_create(
205f64122c1SDave Airlie 				&(qdev->ram_header->cursor_ring_hdr),
206f64122c1SDave Airlie 				sizeof(struct qxl_command),
207f64122c1SDave Airlie 				QXL_CURSOR_RING_SIZE,
20880e5f89dSHuacai Chen 				qdev->io_base + QXL_IO_NOTIFY_CURSOR,
209f64122c1SDave Airlie 				&qdev->cursor_event);
210f64122c1SDave Airlie 
2115043348aSAnton Vasilyev 	if (!qdev->cursor_ring) {
2125043348aSAnton Vasilyev 		DRM_ERROR("Unable to create cursor ring\n");
2135043348aSAnton Vasilyev 		r = -ENOMEM;
2145043348aSAnton Vasilyev 		goto command_ring_free;
2155043348aSAnton Vasilyev 	}
2165043348aSAnton Vasilyev 
217f64122c1SDave Airlie 	qdev->release_ring = qxl_ring_create(
218f64122c1SDave Airlie 				&(qdev->ram_header->release_ring_hdr),
219f64122c1SDave Airlie 				sizeof(uint64_t),
220*461a4df2SZongmin Zhou 				QXL_RELEASE_RING_SIZE, 0,
221f64122c1SDave Airlie 				NULL);
222f64122c1SDave Airlie 
2235043348aSAnton Vasilyev 	if (!qdev->release_ring) {
2245043348aSAnton Vasilyev 		DRM_ERROR("Unable to create release ring\n");
2255043348aSAnton Vasilyev 		r = -ENOMEM;
2265043348aSAnton Vasilyev 		goto cursor_ring_free;
2275043348aSAnton Vasilyev 	}
228f64122c1SDave Airlie 
2295d4c1edfSDeepak R Varma 	idr_init_base(&qdev->release_idr, 1);
230f64122c1SDave Airlie 	spin_lock_init(&qdev->release_idr_lock);
2312f453ed4SMaarten Lankhorst 	spin_lock_init(&qdev->release_lock);
232f64122c1SDave Airlie 
2335d4c1edfSDeepak R Varma 	idr_init_base(&qdev->surf_id_idr, 1);
234f64122c1SDave Airlie 	spin_lock_init(&qdev->surf_id_idr_lock);
235f64122c1SDave Airlie 
236f64122c1SDave Airlie 	mutex_init(&qdev->async_io_mutex);
237f64122c1SDave Airlie 
238f64122c1SDave Airlie 	/* reset the device into a known state - no memslots, no primary
239f64122c1SDave Airlie 	 * created, no surfaces. */
240f64122c1SDave Airlie 	qxl_io_reset(qdev);
241f64122c1SDave Airlie 
242f64122c1SDave Airlie 	/* must initialize irq before first async io - slot creation */
243f64122c1SDave Airlie 	r = qxl_irq_init(qdev);
2445043348aSAnton Vasilyev 	if (r) {
2455043348aSAnton Vasilyev 		DRM_ERROR("Unable to init qxl irq\n");
2462ec6bd67SGerd Hoffmann 		goto release_ring_free;
2475043348aSAnton Vasilyev 	}
248f64122c1SDave Airlie 
249f64122c1SDave Airlie 	/*
250f64122c1SDave Airlie 	 * Note that virtual is surface0. We rely on the single ioremap done
251f64122c1SDave Airlie 	 * before.
252f64122c1SDave Airlie 	 */
2532ec6bd67SGerd Hoffmann 	setup_slot(qdev, &qdev->main_slot, 0, "main",
254f64122c1SDave Airlie 		   (unsigned long)qdev->vram_base,
2552ec6bd67SGerd Hoffmann 		   (unsigned long)qdev->rom->ram_header_offset);
2562ec6bd67SGerd Hoffmann 	setup_slot(qdev, &qdev->surfaces_slot, 1, "surfaces",
257d9bbf189SGerd Hoffmann 		   (unsigned long)qdev->surfaceram_base,
258d9bbf189SGerd Hoffmann 		   (unsigned long)qdev->surfaceram_size);
259f64122c1SDave Airlie 
260f64122c1SDave Airlie 	INIT_WORK(&qdev->gc_work, qxl_gc_work);
261f64122c1SDave Airlie 
262f64122c1SDave Airlie 	return 0;
2635043348aSAnton Vasilyev 
2645043348aSAnton Vasilyev release_ring_free:
2655043348aSAnton Vasilyev 	qxl_ring_free(qdev->release_ring);
2665043348aSAnton Vasilyev cursor_ring_free:
2675043348aSAnton Vasilyev 	qxl_ring_free(qdev->cursor_ring);
2685043348aSAnton Vasilyev command_ring_free:
2695043348aSAnton Vasilyev 	qxl_ring_free(qdev->command_ring);
2705043348aSAnton Vasilyev ram_header_unmap:
2715043348aSAnton Vasilyev 	iounmap(qdev->ram_header);
2725043348aSAnton Vasilyev bo_fini:
2735043348aSAnton Vasilyev 	qxl_bo_fini(qdev);
2745043348aSAnton Vasilyev rom_unmap:
2755043348aSAnton Vasilyev 	iounmap(qdev->rom);
2765043348aSAnton Vasilyev surface_mapping_free:
2775043348aSAnton Vasilyev 	io_mapping_free(qdev->surface_mapping);
2785043348aSAnton Vasilyev vram_mapping_free:
2795043348aSAnton Vasilyev 	io_mapping_free(qdev->vram_mapping);
2805043348aSAnton Vasilyev 	return r;
281f64122c1SDave Airlie }
282f64122c1SDave Airlie 
qxl_device_fini(struct qxl_device * qdev)2832b65d567SGabriel Krisman Bertazi void qxl_device_fini(struct qxl_device *qdev)
284f64122c1SDave Airlie {
2855f6c871fSGerd Hoffmann 	int cur_idx;
2865f6c871fSGerd Hoffmann 
28726fe1f4cSGerd Hoffmann 	/* check if qxl_device_init() was successful (gc_work is initialized last) */
28826fe1f4cSGerd Hoffmann 	if (!qdev->gc_work.func)
28926fe1f4cSGerd Hoffmann 		return;
29026fe1f4cSGerd Hoffmann 
2915f6c871fSGerd Hoffmann 	for (cur_idx = 0; cur_idx < 3; cur_idx++) {
2925f6c871fSGerd Hoffmann 		if (!qdev->current_release_bo[cur_idx])
2935f6c871fSGerd Hoffmann 			continue;
2945f6c871fSGerd Hoffmann 		qxl_bo_unpin(qdev->current_release_bo[cur_idx]);
2955f6c871fSGerd Hoffmann 		qxl_bo_unref(&qdev->current_release_bo[cur_idx]);
2965f6c871fSGerd Hoffmann 		qdev->current_release_bo_offset[cur_idx] = 0;
2975f6c871fSGerd Hoffmann 		qdev->current_release_bo[cur_idx] = NULL;
2985f6c871fSGerd Hoffmann 	}
2995f6c871fSGerd Hoffmann 
3005f6c871fSGerd Hoffmann 	/*
3015f6c871fSGerd Hoffmann 	 * Ask host to release resources (+fill release ring),
3025f6c871fSGerd Hoffmann 	 * then wait for the release actually happening.
3035f6c871fSGerd Hoffmann 	 */
3045f6c871fSGerd Hoffmann 	qxl_io_notify_oom(qdev);
3055f6c871fSGerd Hoffmann 	wait_event_timeout(qdev->release_event,
3065f6c871fSGerd Hoffmann 			   atomic_read(&qdev->release_count) == 0,
3075f6c871fSGerd Hoffmann 			   HZ);
3085f6c871fSGerd Hoffmann 	flush_work(&qdev->gc_work);
3095f6c871fSGerd Hoffmann 	qxl_surf_evict(qdev);
3105f6c871fSGerd Hoffmann 	qxl_vram_evict(qdev);
3115f6c871fSGerd Hoffmann 
312c2874a13SGerd Hoffmann 	qxl_gem_fini(qdev);
313c2874a13SGerd Hoffmann 	qxl_bo_fini(qdev);
314f64122c1SDave Airlie 	qxl_ring_free(qdev->command_ring);
315f64122c1SDave Airlie 	qxl_ring_free(qdev->cursor_ring);
316f64122c1SDave Airlie 	qxl_ring_free(qdev->release_ring);
317f64122c1SDave Airlie 	io_mapping_free(qdev->surface_mapping);
318f64122c1SDave Airlie 	io_mapping_free(qdev->vram_mapping);
319f64122c1SDave Airlie 	iounmap(qdev->ram_header);
320f64122c1SDave Airlie 	iounmap(qdev->rom);
321f64122c1SDave Airlie 	qdev->rom = NULL;
322f64122c1SDave Airlie }
323