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