* [PATCH v4 3/5] libnvdimm: add dax_dev sync flag
From: Pankaj Gupta @ 2019-04-03 10:40 UTC (permalink / raw)
To: linux-nvdimm, linux-kernel, virtualization, kvm, linux-fsdevel,
linux-acpi, qemu-devel, linux-ext4, linux-xfs
Cc: pagupta, jack, mst, david, lcapitulino, adilger.kernel, zwisler,
aarcange, dave.jiang, darrick.wong, vishal.l.verma, willy, hch,
jmoyer, nilal, lenb, riel, stefanha, dan.j.williams, tytso,
xiaoguangrong.eric, cohuck, rjw, imammedo
In-Reply-To: <20190403104018.23947-1-pagupta@redhat.com>
This patch adds 'DAXDEV_SYNC' flag which is set
for nd_region doing synchronous flush. This later
is used to disable MAP_SYNC functionality for
ext4 & xfs filesystem for devices don't support
synchronous flush.
Signed-off-by: Pankaj Gupta <pagupta@redhat.com>
---
drivers/dax/bus.c | 2 +-
drivers/dax/super.c | 13 ++++++++++++-
drivers/md/dm.c | 2 +-
drivers/nvdimm/pmem.c | 3 ++-
drivers/nvdimm/region_devs.c | 7 +++++++
include/linux/dax.h | 9 +++++++--
include/linux/libnvdimm.h | 1 +
7 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/drivers/dax/bus.c b/drivers/dax/bus.c
index 2109cfe80219..431bf7d2a7f9 100644
--- a/drivers/dax/bus.c
+++ b/drivers/dax/bus.c
@@ -388,7 +388,7 @@ struct dev_dax *__devm_create_dev_dax(struct dax_region *dax_region, int id,
* No 'host' or dax_operations since there is no access to this
* device outside of mmap of the resulting character device.
*/
- dax_dev = alloc_dax(dev_dax, NULL, NULL);
+ dax_dev = alloc_dax(dev_dax, NULL, NULL, true);
if (!dax_dev)
goto err;
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index 0a339b85133e..bd6509308d05 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -186,6 +186,8 @@ enum dax_device_flags {
DAXDEV_ALIVE,
/* gate whether dax_flush() calls the low level flush routine */
DAXDEV_WRITE_CACHE,
+ /* flag to check if device supports synchronous flush */
+ DAXDEV_SYNC,
};
/**
@@ -354,6 +356,12 @@ bool dax_write_cache_enabled(struct dax_device *dax_dev)
}
EXPORT_SYMBOL_GPL(dax_write_cache_enabled);
+bool dax_synchronous(struct dax_device *dax_dev)
+{
+ return test_bit(DAXDEV_SYNC, &dax_dev->flags);
+}
+EXPORT_SYMBOL_GPL(dax_synchronous);
+
bool dax_alive(struct dax_device *dax_dev)
{
lockdep_assert_held(&dax_srcu);
@@ -511,7 +519,7 @@ static void dax_add_host(struct dax_device *dax_dev, const char *host)
}
struct dax_device *alloc_dax(void *private, const char *__host,
- const struct dax_operations *ops)
+ const struct dax_operations *ops, bool sync)
{
struct dax_device *dax_dev;
const char *host;
@@ -534,6 +542,9 @@ struct dax_device *alloc_dax(void *private, const char *__host,
dax_add_host(dax_dev, host);
dax_dev->ops = ops;
dax_dev->private = private;
+ if (sync)
+ set_bit(DAXDEV_SYNC, &dax_dev->flags);
+
return dax_dev;
err_dev:
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 68d24056d0b1..534e12ca6329 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1965,7 +1965,7 @@ static struct mapped_device *alloc_dev(int minor)
sprintf(md->disk->disk_name, "dm-%d", minor);
if (IS_ENABLED(CONFIG_DAX_DRIVER)) {
- dax_dev = alloc_dax(md, md->disk->disk_name, &dm_dax_ops);
+ dax_dev = alloc_dax(md, md->disk->disk_name, &dm_dax_ops, true);
if (!dax_dev)
goto bad;
}
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index 5a5b3ea4d073..78f71ba0e7cf 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -466,7 +466,8 @@ static int pmem_attach_disk(struct device *dev,
nvdimm_badblocks_populate(nd_region, &pmem->bb, &bb_res);
disk->bb = &pmem->bb;
- dax_dev = alloc_dax(pmem, disk->disk_name, &pmem_dax_ops);
+ dax_dev = alloc_dax(pmem, disk->disk_name, &pmem_dax_ops,
+ is_nvdimm_sync(nd_region));
if (!dax_dev) {
put_disk(disk);
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index fb1041ab32a6..8c7aa047fe2b 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -1231,6 +1231,13 @@ int nvdimm_has_cache(struct nd_region *nd_region)
}
EXPORT_SYMBOL_GPL(nvdimm_has_cache);
+bool is_nvdimm_sync(struct nd_region *nd_region)
+{
+ return is_nd_pmem(&nd_region->dev) &&
+ !test_bit(ND_REGION_ASYNC, &nd_region->flags);
+}
+EXPORT_SYMBOL_GPL(is_nvdimm_sync);
+
struct conflict_context {
struct nd_region *nd_region;
resource_size_t start, size;
diff --git a/include/linux/dax.h b/include/linux/dax.h
index 0dd316a74a29..9bdd50d06ef6 100644
--- a/include/linux/dax.h
+++ b/include/linux/dax.h
@@ -32,18 +32,19 @@ extern struct attribute_group dax_attribute_group;
#if IS_ENABLED(CONFIG_DAX)
struct dax_device *dax_get_by_host(const char *host);
struct dax_device *alloc_dax(void *private, const char *host,
- const struct dax_operations *ops);
+ const struct dax_operations *ops, bool sync);
void put_dax(struct dax_device *dax_dev);
void kill_dax(struct dax_device *dax_dev);
void dax_write_cache(struct dax_device *dax_dev, bool wc);
bool dax_write_cache_enabled(struct dax_device *dax_dev);
+bool dax_synchronous(struct dax_device *dax_dev);
#else
static inline struct dax_device *dax_get_by_host(const char *host)
{
return NULL;
}
static inline struct dax_device *alloc_dax(void *private, const char *host,
- const struct dax_operations *ops)
+ const struct dax_operations *ops, bool sync)
{
/*
* Callers should check IS_ENABLED(CONFIG_DAX) to know if this
@@ -64,6 +65,10 @@ static inline bool dax_write_cache_enabled(struct dax_device *dax_dev)
{
return false;
}
+static inline bool dax_synchronous(struct dax_device *dax_dev)
+{
+ return false;
+}
#endif
struct writeback_control;
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index d9d2ab8a6e64..9a8aea370cbc 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -270,6 +270,7 @@ int generic_nvdimm_flush(struct nd_region *nd_region);
int nvdimm_has_flush(struct nd_region *nd_region);
int nvdimm_has_cache(struct nd_region *nd_region);
int nvdimm_in_overwrite(struct nvdimm *nvdimm);
+bool is_nvdimm_sync(struct nd_region *nd_region);
static inline int nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd, void *buf,
unsigned int buf_len, int *cmd_rc)
--
2.20.1
^ permalink raw reply related
* [PATCH v4 2/5] virtio-pmem: Add virtio pmem driver
From: Pankaj Gupta @ 2019-04-03 10:40 UTC (permalink / raw)
To: linux-nvdimm, linux-kernel, virtualization, kvm, linux-fsdevel,
linux-acpi, qemu-devel, linux-ext4, linux-xfs
Cc: pagupta, jack, mst, david, lcapitulino, adilger.kernel, zwisler,
aarcange, dave.jiang, darrick.wong, vishal.l.verma, willy, hch,
jmoyer, nilal, lenb, riel, stefanha, dan.j.williams, tytso,
xiaoguangrong.eric, cohuck, rjw, imammedo
In-Reply-To: <20190403104018.23947-1-pagupta@redhat.com>
This patch adds virtio-pmem driver for KVM guest.
Guest reads the persistent memory range information from
Qemu over VIRTIO and registers it on nvdimm_bus. It also
creates a nd_region object with the persistent memory
range information so that existing 'nvdimm/pmem' driver
can reserve this into system memory map. This way
'virtio-pmem' driver uses existing functionality of pmem
driver to register persistent memory compatible for DAX
capable filesystems.
This also provides function to perform guest flush over
VIRTIO from 'pmem' driver when userspace performs flush
on DAX memory range.
Signed-off-by: Pankaj Gupta <pagupta@redhat.com>
---
drivers/nvdimm/virtio_pmem.c | 84 +++++++++++++++++++++
drivers/virtio/Kconfig | 10 +++
drivers/virtio/Makefile | 1 +
drivers/virtio/pmem.c | 125 +++++++++++++++++++++++++++++++
include/linux/virtio_pmem.h | 60 +++++++++++++++
include/uapi/linux/virtio_ids.h | 1 +
include/uapi/linux/virtio_pmem.h | 10 +++
7 files changed, 291 insertions(+)
create mode 100644 drivers/nvdimm/virtio_pmem.c
create mode 100644 drivers/virtio/pmem.c
create mode 100644 include/linux/virtio_pmem.h
create mode 100644 include/uapi/linux/virtio_pmem.h
diff --git a/drivers/nvdimm/virtio_pmem.c b/drivers/nvdimm/virtio_pmem.c
new file mode 100644
index 000000000000..2a1b1ba2c1ff
--- /dev/null
+++ b/drivers/nvdimm/virtio_pmem.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * virtio_pmem.c: Virtio pmem Driver
+ *
+ * Discovers persistent memory range information
+ * from host and provides a virtio based flushing
+ * interface.
+ */
+#include <linux/virtio_pmem.h>
+#include "nd.h"
+
+ /* The interrupt handler */
+void host_ack(struct virtqueue *vq)
+{
+ unsigned int len;
+ unsigned long flags;
+ struct virtio_pmem_request *req, *req_buf;
+ struct virtio_pmem *vpmem = vq->vdev->priv;
+
+ spin_lock_irqsave(&vpmem->pmem_lock, flags);
+ while ((req = virtqueue_get_buf(vq, &len)) != NULL) {
+ req->done = true;
+ wake_up(&req->host_acked);
+
+ if (!list_empty(&vpmem->req_list)) {
+ req_buf = list_first_entry(&vpmem->req_list,
+ struct virtio_pmem_request, list);
+ list_del(&vpmem->req_list);
+ req_buf->wq_buf_avail = true;
+ wake_up(&req_buf->wq_buf);
+ }
+ }
+ spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
+}
+EXPORT_SYMBOL_GPL(host_ack);
+
+ /* The request submission function */
+int virtio_pmem_flush(struct nd_region *nd_region)
+{
+ int err;
+ unsigned long flags;
+ struct scatterlist *sgs[2], sg, ret;
+ struct virtio_device *vdev = nd_region->provider_data;
+ struct virtio_pmem *vpmem = vdev->priv;
+ struct virtio_pmem_request *req;
+
+ might_sleep();
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->done = req->wq_buf_avail = false;
+ strcpy(req->name, "FLUSH");
+ init_waitqueue_head(&req->host_acked);
+ init_waitqueue_head(&req->wq_buf);
+ sg_init_one(&sg, req->name, strlen(req->name));
+ sgs[0] = &sg;
+ sg_init_one(&ret, &req->ret, sizeof(req->ret));
+ sgs[1] = &ret;
+
+ spin_lock_irqsave(&vpmem->pmem_lock, flags);
+ err = virtqueue_add_sgs(vpmem->req_vq, sgs, 1, 1, req, GFP_ATOMIC);
+ if (err) {
+ dev_err(&vdev->dev, "failed to send command to virtio pmem device\n");
+
+ list_add_tail(&vpmem->req_list, &req->list);
+ spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
+
+ /* When host has read buffer, this completes via host_ack */
+ wait_event(req->wq_buf, req->wq_buf_avail);
+ spin_lock_irqsave(&vpmem->pmem_lock, flags);
+ }
+ virtqueue_kick(vpmem->req_vq);
+ spin_unlock_irqrestore(&vpmem->pmem_lock, flags);
+
+ /* When host has read buffer, this completes via host_ack */
+ wait_event(req->host_acked, req->done);
+ err = req->ret;
+ kfree(req);
+
+ return err;
+};
+EXPORT_SYMBOL_GPL(virtio_pmem_flush);
+MODULE_LICENSE("GPL");
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 35897649c24f..9f634a2ed638 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -42,6 +42,16 @@ config VIRTIO_PCI_LEGACY
If unsure, say Y.
+config VIRTIO_PMEM
+ tristate "Support for virtio pmem driver"
+ depends on VIRTIO
+ depends on LIBNVDIMM
+ help
+ This driver provides support for virtio based flushing interface
+ for persistent memory range.
+
+ If unsure, say M.
+
config VIRTIO_BALLOON
tristate "Virtio balloon driver"
depends on VIRTIO
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 3a2b5c5dcf46..143ce91eabe9 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -6,3 +6,4 @@ virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
+obj-$(CONFIG_VIRTIO_PMEM) += pmem.o ../nvdimm/virtio_pmem.o
diff --git a/drivers/virtio/pmem.c b/drivers/virtio/pmem.c
new file mode 100644
index 000000000000..52f74064f67e
--- /dev/null
+++ b/drivers/virtio/pmem.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * virtio_pmem.c: Virtio pmem Driver
+ *
+ * Discovers persistent memory range information
+ * from host and registers the virtual pmem device
+ * with libnvdimm core.
+ */
+#include <linux/virtio_pmem.h>
+#include <../../drivers/nvdimm/nd.h>
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+ /* Initialize virt queue */
+static int init_vq(struct virtio_pmem *vpmem)
+{
+ struct virtqueue *vq;
+
+ /* single vq */
+ vpmem->req_vq = vq = virtio_find_single_vq(vpmem->vdev,
+ host_ack, "flush_queue");
+ if (IS_ERR(vq))
+ return PTR_ERR(vq);
+
+ spin_lock_init(&vpmem->pmem_lock);
+ INIT_LIST_HEAD(&vpmem->req_list);
+
+ return 0;
+};
+
+static int virtio_pmem_probe(struct virtio_device *vdev)
+{
+ int err = 0;
+ struct resource res;
+ struct virtio_pmem *vpmem;
+ struct nvdimm_bus *nvdimm_bus;
+ struct nd_region_desc ndr_desc;
+ int nid = dev_to_node(&vdev->dev);
+ struct nd_region *nd_region;
+
+ if (!vdev->config->get) {
+ dev_err(&vdev->dev, "%s failure: config disabled\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ vdev->priv = vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem),
+ GFP_KERNEL);
+ if (!vpmem) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ vpmem->vdev = vdev;
+ err = init_vq(vpmem);
+ if (err)
+ goto out_err;
+
+ virtio_cread(vpmem->vdev, struct virtio_pmem_config,
+ start, &vpmem->start);
+ virtio_cread(vpmem->vdev, struct virtio_pmem_config,
+ size, &vpmem->size);
+
+ res.start = vpmem->start;
+ res.end = vpmem->start + vpmem->size-1;
+ vpmem->nd_desc.provider_name = "virtio-pmem";
+ vpmem->nd_desc.module = THIS_MODULE;
+
+ vpmem->nvdimm_bus = nvdimm_bus = nvdimm_bus_register(&vdev->dev,
+ &vpmem->nd_desc);
+ if (!nvdimm_bus)
+ goto out_vq;
+
+ dev_set_drvdata(&vdev->dev, nvdimm_bus);
+ memset(&ndr_desc, 0, sizeof(ndr_desc));
+
+ ndr_desc.res = &res;
+ ndr_desc.numa_node = nid;
+ ndr_desc.flush = virtio_pmem_flush;
+ set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags);
+ set_bit(ND_REGION_ASYNC, &ndr_desc.flags);
+ nd_region = nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc);
+ nd_region->provider_data = dev_to_virtio
+ (nd_region->dev.parent->parent);
+
+ if (!nd_region)
+ goto out_nd;
+
+ //virtio_device_ready(vdev);
+ return 0;
+out_nd:
+ err = -ENXIO;
+ nvdimm_bus_unregister(nvdimm_bus);
+out_vq:
+ vdev->config->del_vqs(vdev);
+out_err:
+ dev_err(&vdev->dev, "failed to register virtio pmem memory\n");
+ return err;
+}
+
+static void virtio_pmem_remove(struct virtio_device *vdev)
+{
+ struct virtio_pmem *vpmem = vdev->priv;
+ struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev);
+
+ nvdimm_bus_unregister(nvdimm_bus);
+ vdev->config->del_vqs(vdev);
+ kfree(vpmem);
+}
+
+static struct virtio_driver virtio_pmem_driver = {
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtio_pmem_probe,
+ .remove = virtio_pmem_remove,
+};
+
+module_virtio_driver(virtio_pmem_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio pmem driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/virtio_pmem.h b/include/linux/virtio_pmem.h
new file mode 100644
index 000000000000..224f9d934be6
--- /dev/null
+++ b/include/linux/virtio_pmem.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * virtio_pmem.h: virtio pmem Driver
+ *
+ * Discovers persistent memory range information
+ * from host and provides a virtio based flushing
+ * interface.
+ **/
+
+#ifndef _LINUX_VIRTIO_PMEM_H
+#define _LINUX_VIRTIO_PMEM_H
+
+#include <linux/virtio_ids.h>
+#include <linux/module.h>
+#include <linux/virtio_config.h>
+#include <uapi/linux/virtio_pmem.h>
+#include <linux/libnvdimm.h>
+#include <linux/spinlock.h>
+
+struct virtio_pmem_request {
+ /* Host return status corresponding to flush request */
+ int ret;
+
+ /* command name*/
+ char name[16];
+
+ /* Wait queue to process deferred work after ack from host */
+ wait_queue_head_t host_acked;
+ bool done;
+
+ /* Wait queue to process deferred work after virt queue buffer avail */
+ wait_queue_head_t wq_buf;
+ bool wq_buf_avail;
+ struct list_head list;
+};
+
+struct virtio_pmem {
+ struct virtio_device *vdev;
+
+ /* Virtio pmem request queue */
+ struct virtqueue *req_vq;
+
+ /* nvdimm bus registers virtio pmem device */
+ struct nvdimm_bus *nvdimm_bus;
+ struct nvdimm_bus_descriptor nd_desc;
+
+ /* List to store deferred work if virtqueue is full */
+ struct list_head req_list;
+
+ /* Synchronize virtqueue data */
+ spinlock_t pmem_lock;
+
+ /* Memory region information */
+ uint64_t start;
+ uint64_t size;
+};
+
+void host_ack(struct virtqueue *vq);
+int virtio_pmem_flush(struct nd_region *nd_region);
+#endif
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index 6d5c3b2d4f4d..346389565ac1 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -43,5 +43,6 @@
#define VIRTIO_ID_INPUT 18 /* virtio input */
#define VIRTIO_ID_VSOCK 19 /* virtio vsock transport */
#define VIRTIO_ID_CRYPTO 20 /* virtio crypto */
+#define VIRTIO_ID_PMEM 25 /* virtio pmem */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/uapi/linux/virtio_pmem.h b/include/uapi/linux/virtio_pmem.h
new file mode 100644
index 000000000000..fa3f7d52717a
--- /dev/null
+++ b/include/uapi/linux/virtio_pmem.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _UAPI_LINUX_VIRTIO_PMEM_H
+#define _UAPI_LINUX_VIRTIO_PMEM_H
+
+struct virtio_pmem_config {
+ __le64 start;
+ __le64 size;
+};
+#endif
--
2.20.1
^ permalink raw reply related
* [PATCH v4 1/5] ibnvdimm: nd_region flush callback support
From: Pankaj Gupta @ 2019-04-03 10:40 UTC (permalink / raw)
To: linux-nvdimm, linux-kernel, virtualization, kvm, linux-fsdevel,
linux-acpi, qemu-devel, linux-ext4, linux-xfs
Cc: pagupta, jack, mst, david, lcapitulino, adilger.kernel, zwisler,
aarcange, dave.jiang, darrick.wong, vishal.l.verma, willy, hch,
jmoyer, nilal, lenb, riel, stefanha, dan.j.williams, tytso,
xiaoguangrong.eric, cohuck, rjw, imammedo
In-Reply-To: <20190403104018.23947-1-pagupta@redhat.com>
This patch adds functionality to perform flush from guest
to host over VIRTIO. We are registering a callback based
on 'nd_region' type. virtio_pmem driver requires this special
flush function. For rest of the region types we are registering
existing flush function. Report error returned by host fsync
failure to userspace.
This also handles asynchronous flush requests from the block layer
by creating a child bio and chaining it with parent bio.
Signed-off-by: Pankaj Gupta <pagupta@redhat.com>
---
drivers/acpi/nfit/core.c | 4 ++--
drivers/nvdimm/claim.c | 6 ++++--
drivers/nvdimm/nd.h | 1 +
drivers/nvdimm/pmem.c | 14 ++++++++-----
drivers/nvdimm/region_devs.c | 38 ++++++++++++++++++++++++++++++++++--
include/linux/libnvdimm.h | 8 +++++++-
6 files changed, 59 insertions(+), 12 deletions(-)
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 5a389a4f4f65..567017a2190e 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -2434,7 +2434,7 @@ static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw,
offset = to_interleave_offset(offset, mmio);
writeq(cmd, mmio->addr.base + offset);
- nvdimm_flush(nfit_blk->nd_region);
+ nvdimm_flush(nfit_blk->nd_region, NULL, false);
if (nfit_blk->dimm_flags & NFIT_BLK_DCR_LATCH)
readq(mmio->addr.base + offset);
@@ -2483,7 +2483,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk,
}
if (rw)
- nvdimm_flush(nfit_blk->nd_region);
+ nvdimm_flush(nfit_blk->nd_region, NULL, false);
rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0;
return rc;
diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c
index fb667bf469c7..a1dfa066786b 100644
--- a/drivers/nvdimm/claim.c
+++ b/drivers/nvdimm/claim.c
@@ -263,7 +263,7 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
struct nd_namespace_io *nsio = to_nd_namespace_io(&ndns->dev);
unsigned int sz_align = ALIGN(size + (offset & (512 - 1)), 512);
sector_t sector = offset >> 9;
- int rc = 0;
+ int rc = 0, ret = 0;
if (unlikely(!size))
return 0;
@@ -301,7 +301,9 @@ static int nsio_rw_bytes(struct nd_namespace_common *ndns,
}
memcpy_flushcache(nsio->addr + offset, buf, size);
- nvdimm_flush(to_nd_region(ndns->dev.parent));
+ ret = nvdimm_flush(to_nd_region(ndns->dev.parent), NULL, false);
+ if (ret)
+ rc = ret;
return rc;
}
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index a5ac3b240293..916cd6c5451a 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -159,6 +159,7 @@ struct nd_region {
struct badblocks bb;
struct nd_interleave_set *nd_set;
struct nd_percpu_lane __percpu *lane;
+ int (*flush)(struct nd_region *nd_region);
struct nd_mapping mapping[0];
};
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c
index bc2f700feef8..5a5b3ea4d073 100644
--- a/drivers/nvdimm/pmem.c
+++ b/drivers/nvdimm/pmem.c
@@ -192,6 +192,7 @@ static blk_status_t pmem_do_bvec(struct pmem_device *pmem, struct page *page,
static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
{
+ int ret = 0;
blk_status_t rc = 0;
bool do_acct;
unsigned long start;
@@ -201,7 +202,7 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
struct nd_region *nd_region = to_region(pmem);
if (bio->bi_opf & REQ_PREFLUSH)
- nvdimm_flush(nd_region);
+ ret = nvdimm_flush(nd_region, bio, true);
do_acct = nd_iostat_start(bio, &start);
bio_for_each_segment(bvec, bio, iter) {
@@ -216,7 +217,10 @@ static blk_qc_t pmem_make_request(struct request_queue *q, struct bio *bio)
nd_iostat_end(bio, start);
if (bio->bi_opf & REQ_FUA)
- nvdimm_flush(nd_region);
+ ret = nvdimm_flush(nd_region, bio, true);
+
+ if (ret)
+ bio->bi_status = errno_to_blk_status(ret);
bio_endio(bio);
return BLK_QC_T_NONE;
@@ -463,13 +467,13 @@ static int pmem_attach_disk(struct device *dev,
disk->bb = &pmem->bb;
dax_dev = alloc_dax(pmem, disk->disk_name, &pmem_dax_ops);
+
if (!dax_dev) {
put_disk(disk);
return -ENOMEM;
}
dax_write_cache(dax_dev, nvdimm_has_cache(nd_region));
pmem->dax_dev = dax_dev;
-
gendev = disk_to_dev(disk);
gendev->groups = pmem_attribute_groups;
@@ -527,14 +531,14 @@ static int nd_pmem_remove(struct device *dev)
sysfs_put(pmem->bb_state);
pmem->bb_state = NULL;
}
- nvdimm_flush(to_nd_region(dev->parent));
+ nvdimm_flush(to_nd_region(dev->parent), NULL, false);
return 0;
}
static void nd_pmem_shutdown(struct device *dev)
{
- nvdimm_flush(to_nd_region(dev->parent));
+ nvdimm_flush(to_nd_region(dev->parent), NULL, false);
}
static void nd_pmem_notify(struct device *dev, enum nvdimm_event event)
diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c
index b4ef7d9ff22e..fb1041ab32a6 100644
--- a/drivers/nvdimm/region_devs.c
+++ b/drivers/nvdimm/region_devs.c
@@ -295,7 +295,9 @@ static ssize_t deep_flush_store(struct device *dev, struct device_attribute *att
return rc;
if (!flush)
return -EINVAL;
- nvdimm_flush(nd_region);
+ rc = nvdimm_flush(nd_region, NULL, false);
+ if (rc)
+ return rc;
return len;
}
@@ -1085,6 +1087,11 @@ static struct nd_region *nd_region_create(struct nvdimm_bus *nvdimm_bus,
dev->of_node = ndr_desc->of_node;
nd_region->ndr_size = resource_size(ndr_desc->res);
nd_region->ndr_start = ndr_desc->res->start;
+ if (ndr_desc->flush)
+ nd_region->flush = ndr_desc->flush;
+ else
+ nd_region->flush = generic_nvdimm_flush;
+
nd_device_register(dev);
return nd_region;
@@ -1125,11 +1132,36 @@ struct nd_region *nvdimm_volatile_region_create(struct nvdimm_bus *nvdimm_bus,
}
EXPORT_SYMBOL_GPL(nvdimm_volatile_region_create);
+int nvdimm_flush(struct nd_region *nd_region, struct bio *bio, bool async)
+{
+ int rc = 0;
+
+ /* Create child bio for asynchronous flush and chain with
+ * parent bio. Otherwise directly call nd_region flush.
+ */
+ if (async && bio->bi_iter.bi_sector != -1) {
+
+ struct bio *child = bio_alloc(GFP_ATOMIC, 0);
+
+ if (!child)
+ return -ENOMEM;
+ bio_copy_dev(child, bio);
+ child->bi_opf = REQ_PREFLUSH;
+ child->bi_iter.bi_sector = -1;
+ bio_chain(child, bio);
+ submit_bio(child);
+ } else {
+ if (nd_region->flush(nd_region))
+ rc = -EIO;
+ }
+
+ return rc;
+}
/**
* nvdimm_flush - flush any posted write queues between the cpu and pmem media
* @nd_region: blk or interleaved pmem region
*/
-void nvdimm_flush(struct nd_region *nd_region)
+int generic_nvdimm_flush(struct nd_region *nd_region)
{
struct nd_region_data *ndrd = dev_get_drvdata(&nd_region->dev);
int i, idx;
@@ -1153,6 +1185,8 @@ void nvdimm_flush(struct nd_region *nd_region)
if (ndrd_get_flush_wpq(ndrd, i, 0))
writeq(1, ndrd_get_flush_wpq(ndrd, i, idx));
wmb();
+
+ return 0;
}
EXPORT_SYMBOL_GPL(nvdimm_flush);
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h
index feb342d026f2..d9d2ab8a6e64 100644
--- a/include/linux/libnvdimm.h
+++ b/include/linux/libnvdimm.h
@@ -65,6 +65,9 @@ enum {
*/
ND_REGION_PERSIST_MEMCTRL = 2,
+ /* Platform provides asynchronous flush mechanism */
+ ND_REGION_ASYNC = 3,
+
/* mark newly adjusted resources as requiring a label update */
DPA_RESOURCE_ADJUSTED = 1 << 0,
};
@@ -121,6 +124,7 @@ struct nd_mapping_desc {
int position;
};
+struct nd_region;
struct nd_region_desc {
struct resource *res;
struct nd_mapping_desc *mapping;
@@ -133,6 +137,7 @@ struct nd_region_desc {
int target_node;
unsigned long flags;
struct device_node *of_node;
+ int (*flush)(struct nd_region *nd_region);
};
struct device;
@@ -260,7 +265,8 @@ unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr);
unsigned int nd_region_acquire_lane(struct nd_region *nd_region);
void nd_region_release_lane(struct nd_region *nd_region, unsigned int lane);
u64 nd_fletcher64(void *addr, size_t len, bool le);
-void nvdimm_flush(struct nd_region *nd_region);
+int nvdimm_flush(struct nd_region *nd_region, struct bio *bio, bool async);
+int generic_nvdimm_flush(struct nd_region *nd_region);
int nvdimm_has_flush(struct nd_region *nd_region);
int nvdimm_has_cache(struct nd_region *nd_region);
int nvdimm_in_overwrite(struct nvdimm *nvdimm);
--
2.20.1
^ permalink raw reply related
* [PATCH v4 0/5] virtio pmem driver
From: Pankaj Gupta @ 2019-04-03 10:40 UTC (permalink / raw)
To: linux-nvdimm, linux-kernel, virtualization, kvm, linux-fsdevel,
linux-acpi, qemu-devel, linux-ext4, linux-xfs
Cc: pagupta, jack, mst, david, lcapitulino, adilger.kernel, zwisler,
aarcange, dave.jiang, darrick.wong, vishal.l.verma, willy, hch,
jmoyer, nilal, lenb, riel, stefanha, dan.j.williams, tytso,
xiaoguangrong.eric, cohuck, rjw, imammedo
This patch series has implementation for "virtio pmem".
"virtio pmem" is fake persistent memory(nvdimm) in guest
which allows to bypass the guest page cache. This also
implements a VIRTIO based asynchronous flush mechanism.
Sharing guest kernel driver in this patchset with the
changes suggested in v3. Tested with Qemu side device
emulation [6] for virtio-pmem.
We have incorporated all the suggestions in V3. Documented
the impact of possible page cache side channel attacks with
suggested countermeasures.
Details of project idea for 'virtio pmem' flushing interface
is shared [3] & [4].
Implementation is divided into two parts:
New virtio pmem guest driver and qemu code changes for new
virtio pmem paravirtualized device.
1. Guest virtio-pmem kernel driver
---------------------------------
- Reads persistent memory range from paravirt device and
registers with 'nvdimm_bus'.
- 'nvdimm/pmem' driver uses this information to allocate
persistent memory region and setup filesystem operations
to the allocated memory.
- virtio pmem driver implements asynchronous flushing
interface to flush from guest to host.
2. Qemu virtio-pmem device
---------------------------------
- Creates virtio pmem device and exposes a memory range to
KVM guest.
- At host side this is file backed memory which acts as
persistent memory.
- Qemu side flush uses aio thread pool API's and virtio
for asynchronous guest multi request handling.
David Hildenbrand CCed also posted a modified version[7] of
qemu virtio-pmem code based on updated Qemu memory device API.
Virtio-pmem security implications and countermeasures:
-----------------------------------------------------
In previous posting of kernel driver, there was discussion [9]
on possible implications of page cache side channel attacks with
virtio pmem. After thorough analysis of details of known side
channel attacks, below are the suggestions:
- Depends entirely on how host backing image file is mapped
into guest address space.
- virtio-pmem device emulation, by default shared mapping is used
to map host backing file. It is recommended to use separate
backing file at host side for every guest. This will prevent
any possibility of executing common code from multiple guests
and any chance of inferring guest local data based based on
execution time.
- If backing file is required to be shared among multiple guests
it is recommended to don't support host page cache eviction
commands from the guest driver. This will avoid any possibility
of inferring guest local data or host data from another guest.
- Proposed device specification [8] for virtio-pmem device with
details of possible security implications and suggested
countermeasures for device emulation.
Virtio-pmem errors handling:
----------------------------------------
Checked behaviour of virtio-pmem for below types of errors
Need suggestions on expected behaviour for handling these errors?
- Hardware Errors: Uncorrectable recoverable Errors:
a] virtio-pmem:
- As per current logic if error page belongs to Qemu process,
host MCE handler isolates(hwpoison) that page and send SIGBUS.
Qemu SIGBUS handler injects exception to KVM guest.
- KVM guest then isolates the page and send SIGBUS to guest
userspace process which has mapped the page.
b] Existing implementation for ACPI pmem driver:
- Handles such errors with MCE notifier and creates a list
of bad blocks. Read/direct access DAX operation return EIO
if accessed memory page fall in bad block list.
- It also starts backgound scrubbing.
- Similar functionality can be reused in virtio-pmem with MCE
notifier but without scrubbing(no ACPI/ARS)? Need inputs to
confirm if this behaviour is ok or needs any change?
Changes from PATCH v3: [1]
- Use generic dax_synchronous() helper to check for DAXDEV_SYNC
flag - [Dan, Darrick, Jan]
- Add 'is_nvdimm_async' function
- Document page cache side channel attacks implications &
countermeasures - [Dave Chinner, Michael]
Changes from PATCH v2: [2]
- Disable MAP_SYNC for ext4 & XFS filesystems - [Dan]
- Use name 'virtio pmem' in place of 'fake dax'
Changes from PATCH v1:
- 0-day build test for build dependency on libnvdimm
Changes suggested by - [Dan Williams]
- Split the driver into two parts virtio & pmem
- Move queuing of async block request to block layer
- Add "sync" parameter in nvdimm_flush function
- Use indirect call for nvdimm_flush
- Don’t move declarations to common global header e.g nd.h
- nvdimm_flush() return 0 or -EIO if it fails
- Teach nsio_rw_bytes() that the flush can fail
- Rename nvdimm_flush() to generic_nvdimm_flush()
- Use 'nd_region->provider_data' for long dereferencing
- Remove virtio_pmem_freeze/restore functions
- Remove BSD license text with SPDX license text
- Add might_sleep() in virtio_pmem_flush - [Luiz]
- Make spin_lock_irqsave() narrow
Changes from RFC v3
- Rebase to latest upstream - Luiz
- Call ndregion->flush in place of nvdimm_flush- Luiz
- kmalloc return check - Luiz
- virtqueue full handling - Stefan
- Don't map entire virtio_pmem_req to device - Stefan
- request leak, correct sizeof req- Stefan
- Move declaration to virtio_pmem.c
Changes from RFC v2:
- Add flush function in the nd_region in place of switching
on a flag - Dan & Stefan
- Add flush completion function with proper locking and wait
for host side flush completion - Stefan & Dan
- Keep userspace API in uapi header file - Stefan, MST
- Use LE fields & New device id - MST
- Indentation & spacing suggestions - MST & Eric
- Remove extra header files & add licensing - Stefan
Changes from RFC v1:
- Reuse existing 'pmem' code for registering persistent
memory and other operations instead of creating an entirely
new block driver.
- Use VIRTIO driver to register memory information with
nvdimm_bus and create region_type accordingly.
- Call VIRTIO flush from existing pmem driver.
Pankaj Gupta (5):
libnvdimm: nd_region flush callback support
virtio-pmem: Add virtio-pmem guest driver
libnvdimm: add nd_region buffered dax_dev flag
ext4: disable map_sync for virtio pmem
xfs: disable map_sync for virtio pmem
[1] https://lkml.org/lkml/2019/1/9/471
[2] https://lkml.org/lkml/2018/10/13/117
[3] https://www.spinics.net/lists/kvm/msg149761.html
[4] https://www.spinics.net/lists/kvm/msg153095.html
[5] https://lkml.org/lkml/2018/8/31/413
[6] https://marc.info/?l=linux-kernel&m=153572228719237&w=2
[7] https://marc.info/?l=qemu-devel&m=153555721901824&w=2
[8] https://lists.oasis-open.org/archives/virtio-dev/201903/msg00083.html
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH] drm/cirrus: rewrite and modernize driver.
From: Gerd Hoffmann @ 2019-04-03 10:27 UTC (permalink / raw)
To: Noralf Trønnes
Cc: David Airlie, open list, dri-devel,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE, Daniel Vetter,
Dave Airlie
In-Reply-To: <0c959f3a-15e9-8295-dc11-2f6b9a4229ac@tronnes.org>
Hi,
> > So I can just remove cirrus_fb_dirty() and hook up
> > drm_atomic_helper_dirtyfb() instead. Easy ;)
>
> You can even use drm_gem_fb_create_with_dirty() instead of
> drm_gem_fb_create_with_funcs().
Ah, cool. /me happily continues removing code lines.
thanks,
Gerd
PS: incremental fixups are at
https://git.kraxel.org/cgit/linux/log/?h=drm-rewrite-cirrus
^ permalink raw reply
* Re: [PATCH] drm/cirrus: rewrite and modernize driver.
From: Noralf Trønnes @ 2019-04-03 9:58 UTC (permalink / raw)
To: Gerd Hoffmann, Daniel Vetter
Cc: David Airlie, Dave Airlie, open list, dri-devel,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE
In-Reply-To: <20190403085341.2cxdgxqrjlwecx3n@sirius.home.kraxel.org>
Den 03.04.2019 10.53, skrev Gerd Hoffmann:
>>> +struct cirrus_device {
>>> + struct drm_device *dev;
>>
>> Why not embed drm_device? It's the latest rage :-)
>
> Sure, can do that.
>
>>> +void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
>>> + struct drm_plane_state *old_state)
>>> +{
>>> + struct drm_plane_state *state = pipe->plane.state;
>>> + struct drm_crtc *crtc = &pipe->crtc;
>>> + struct drm_rect rect;
>>> +
>>> + if (drm_atomic_helper_damage_merged(old_state, state, &rect))
>>> + cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
>>> +
>>> + if (crtc->state->event) {
>>> + spin_lock_irq(&crtc->dev->event_lock);
>>> + drm_crtc_send_vblank_event(crtc, crtc->state->event);
>>> + spin_unlock_irq(&crtc->dev->event_lock);
>>> + crtc->state->event = NULL;
>>> + }
>>> +}
>
>>> +static int cirrus_fb_dirty(struct drm_framebuffer *fb,
>>> + struct drm_file *file_priv,
>>> + unsigned int flags, unsigned int color,
>>> + struct drm_clip_rect *clips,
>>> + unsigned int num_clips)
>>> +{
>>> + struct cirrus_device *cirrus = fb->dev->dev_private;
>>> +
>>> + if (cirrus->pipe.plane.state->fb != fb)
>>> + return 0;
>>> +
>>> + if (num_clips)
>>> + cirrus_fb_blit_clips(fb, clips, num_clips);
>>> + else
>>> + cirrus_fb_blit_fullscreen(fb);
>>> + return 0;
>>> +}
>>
>> Why not use the dirty helpers and implement dirty rect support in your
>> main plane update function?
>
> Dirty rect support is already there, see above.
>
> So I can just remove cirrus_fb_dirty() and hook up
> drm_atomic_helper_dirtyfb() instead. Easy ;)
>
You can even use drm_gem_fb_create_with_dirty() instead of
drm_gem_fb_create_with_funcs().
Noralf.
> cheers,
> Gerd
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
^ permalink raw reply
* [PATCH] virtio-net: Fix some minor formatting errors
From: Yuval Shaia @ 2019-04-03 9:10 UTC (permalink / raw)
To: mst, jasowang, davem, virtualization, netdev; +Cc: Yuval Shaia
Signed-off-by: Yuval Shaia <yuval.shaia@oracle.com>
---
drivers/net/virtio_net.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 07c1e81087b2..be1188815c72 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1587,7 +1587,8 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_fifo_errors++;
if (net_ratelimit())
dev_warn(&dev->dev,
- "Unexpected TXQ (%d) queue failure: %d\n", qnum, err);
+ "Unexpected TXQ (%d) queue failure: %d\n",
+ qnum, err);
dev->stats.tx_dropped++;
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
@@ -2383,7 +2384,7 @@ static int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads)
if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_GUEST_OFFLOADS,
VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, &sg)) {
- dev_warn(&vi->dev->dev, "Fail to set guest offload. \n");
+ dev_warn(&vi->dev->dev, "Fail to set guest offload.\n");
return -EINVAL;
}
@@ -3114,8 +3115,9 @@ static int virtnet_probe(struct virtio_device *vdev)
/* Should never trigger: MTU was previously validated
* in virtnet_validate.
*/
- dev_err(&vdev->dev, "device MTU appears to have changed "
- "it is now %d < %d", mtu, dev->min_mtu);
+ dev_err(&vdev->dev,
+ "device MTU appears to have changed it is now %d < %d",
+ mtu, dev->min_mtu);
goto free;
}
--
2.19.1
^ permalink raw reply related
* Re: [PATCH] drm/cirrus: rewrite and modernize driver.
From: Gerd Hoffmann @ 2019-04-03 8:53 UTC (permalink / raw)
To: Daniel Vetter
Cc: David Airlie, Dave Airlie, open list, dri-devel,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE
In-Reply-To: <CAKMK7uGWh02L9n4etQC3V-RX3aWovJLpadNFe08LdWXRNz2yuw@mail.gmail.com>
> > +struct cirrus_device {
> > + struct drm_device *dev;
>
> Why not embed drm_device? It's the latest rage :-)
Sure, can do that.
> > +void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
> > + struct drm_plane_state *old_state)
> > +{
> > + struct drm_plane_state *state = pipe->plane.state;
> > + struct drm_crtc *crtc = &pipe->crtc;
> > + struct drm_rect rect;
> > +
> > + if (drm_atomic_helper_damage_merged(old_state, state, &rect))
> > + cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
> > +
> > + if (crtc->state->event) {
> > + spin_lock_irq(&crtc->dev->event_lock);
> > + drm_crtc_send_vblank_event(crtc, crtc->state->event);
> > + spin_unlock_irq(&crtc->dev->event_lock);
> > + crtc->state->event = NULL;
> > + }
> > +}
> > +static int cirrus_fb_dirty(struct drm_framebuffer *fb,
> > + struct drm_file *file_priv,
> > + unsigned int flags, unsigned int color,
> > + struct drm_clip_rect *clips,
> > + unsigned int num_clips)
> > +{
> > + struct cirrus_device *cirrus = fb->dev->dev_private;
> > +
> > + if (cirrus->pipe.plane.state->fb != fb)
> > + return 0;
> > +
> > + if (num_clips)
> > + cirrus_fb_blit_clips(fb, clips, num_clips);
> > + else
> > + cirrus_fb_blit_fullscreen(fb);
> > + return 0;
> > +}
>
> Why not use the dirty helpers and implement dirty rect support in your
> main plane update function?
Dirty rect support is already there, see above.
So I can just remove cirrus_fb_dirty() and hook up
drm_atomic_helper_dirtyfb() instead. Easy ;)
cheers,
Gerd
^ permalink raw reply
* [PATCH] virtio-net: Remove inclusion of pci.h
From: Yuval Shaia @ 2019-04-03 8:20 UTC (permalink / raw)
To: mst, jasowang, davem, virtualization, netdev; +Cc: Yuval Shaia
This header is not in use - remove it.
Signed-off-by: Yuval Shaia <yuval.shaia@oracle.com>
---
drivers/net/virtio_net.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 7eb38ea9ba56..07c1e81087b2 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -31,7 +31,6 @@
#include <linux/average.h>
#include <linux/filter.h>
#include <linux/kernel.h>
-#include <linux/pci.h>
#include <net/route.h>
#include <net/xdp.h>
#include <net/net_failover.h>
--
2.19.1
^ permalink raw reply related
* Re: [PATCH] drm/cirrus: rewrite and modernize driver.
From: Daniel Vetter @ 2019-04-03 7:47 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: David Airlie, Dave Airlie, open list, dri-devel,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE
In-Reply-To: <20190403072318.31507-1-kraxel@redhat.com>
On Wed, Apr 3, 2019 at 9:23 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
> Time to kill some bad sample code people are copying from ;)
>
> This is a complete rewrite of the cirrus driver. The cirrus_mode_set()
> function is pretty much the only function which is carried over largely
> unmodified. Everything else is upside down.
>
> It is a single monster patch. But given that it does some pretty
> fundamental changes to the drivers workflow and also reduces the code
> size by roughly 70% I think it'll still be alot easier to review than a
> longish baby-step patch series.
>
> Changes summary:
> - Given the small amout of video memory (4 MB) the cirrus device has
> the rewritten driver doesn't try to manage buffers there. Instead
> it will blit (memcpy) the active framebuffer to video memory.
> - All gem objects are stored in main memory and are manged using the
> new shmem helpers. ttm is out.
> - Only DRM_FORMAT_RGB565 (depth 16) is supported. The old driver does
> that too by default. There was a module parameter which enables 24/32
> bpp support and disables higher resolutions (due to cirrus hardware
> constrains). That parameter wasn't reimplemented.
> - The simple display pipeline is used.
> - The generic fbdev emulation is used.
> - It's a atomic driver now.
Sounds all awesome. Some tiny comments below, with those addressed
looks all good and gets my
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-Daniel
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> drivers/gpu/drm/cirrus/cirrus_drv.h | 251 -----------
> drivers/gpu/drm/cirrus/cirrus.c | 602 +++++++++++++++++++++++++
> drivers/gpu/drm/cirrus/cirrus_drv.c | 161 -------
> drivers/gpu/drm/cirrus/cirrus_fbdev.c | 309 -------------
> drivers/gpu/drm/cirrus/cirrus_main.c | 328 --------------
> drivers/gpu/drm/cirrus/cirrus_mode.c | 617 --------------------------
> drivers/gpu/drm/cirrus/cirrus_ttm.c | 343 --------------
> drivers/gpu/drm/cirrus/Kconfig | 2 +-
> drivers/gpu/drm/cirrus/Makefile | 3 -
> 9 files changed, 603 insertions(+), 2013 deletions(-)
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_drv.h
> create mode 100644 drivers/gpu/drm/cirrus/cirrus.c
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_drv.c
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_fbdev.c
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_main.c
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_mode.c
> delete mode 100644 drivers/gpu/drm/cirrus/cirrus_ttm.c
>
> diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
> deleted file mode 100644
> index 828b150cdb20..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_drv.h
> +++ /dev/null
> @@ -1,251 +0,0 @@
> -/*
> - * Copyright 2012 Red Hat
> - *
> - * This file is subject to the terms and conditions of the GNU General
> - * Public License version 2. See the file COPYING in the main
> - * directory of this archive for more details.
> - *
> - * Authors: Matthew Garrett
> - * Dave Airlie
> - */
> -#ifndef __CIRRUS_DRV_H__
> -#define __CIRRUS_DRV_H__
> -
> -#include <video/vga.h>
> -
> -#include <drm/drm_encoder.h>
> -#include <drm/drm_fb_helper.h>
> -
> -#include <drm/ttm/ttm_bo_api.h>
> -#include <drm/ttm/ttm_bo_driver.h>
> -#include <drm/ttm/ttm_placement.h>
> -#include <drm/ttm/ttm_memory.h>
> -#include <drm/ttm/ttm_module.h>
> -
> -#include <drm/drm_gem.h>
> -
> -#define DRIVER_AUTHOR "Matthew Garrett"
> -
> -#define DRIVER_NAME "cirrus"
> -#define DRIVER_DESC "qemu Cirrus emulation"
> -#define DRIVER_DATE "20110418"
> -
> -#define DRIVER_MAJOR 1
> -#define DRIVER_MINOR 0
> -#define DRIVER_PATCHLEVEL 0
> -
> -#define CIRRUSFB_CONN_LIMIT 1
> -
> -#define RREG8(reg) ioread8(((void __iomem *)cdev->rmmio) + (reg))
> -#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cdev->rmmio) + (reg))
> -#define RREG32(reg) ioread32(((void __iomem *)cdev->rmmio) + (reg))
> -#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cdev->rmmio) + (reg))
> -
> -#define SEQ_INDEX 4
> -#define SEQ_DATA 5
> -
> -#define WREG_SEQ(reg, v) \
> - do { \
> - WREG8(SEQ_INDEX, reg); \
> - WREG8(SEQ_DATA, v); \
> - } while (0) \
> -
> -#define CRT_INDEX 0x14
> -#define CRT_DATA 0x15
> -
> -#define WREG_CRT(reg, v) \
> - do { \
> - WREG8(CRT_INDEX, reg); \
> - WREG8(CRT_DATA, v); \
> - } while (0) \
> -
> -#define GFX_INDEX 0xe
> -#define GFX_DATA 0xf
> -
> -#define WREG_GFX(reg, v) \
> - do { \
> - WREG8(GFX_INDEX, reg); \
> - WREG8(GFX_DATA, v); \
> - } while (0) \
> -
> -/*
> - * Cirrus has a "hidden" DAC register that can be accessed by writing to
> - * the pixel mask register to reset the state, then reading from the register
> - * four times. The next write will then pass to the DAC
> - */
> -#define VGA_DAC_MASK 0x6
> -
> -#define WREG_HDR(v) \
> - do { \
> - RREG8(VGA_DAC_MASK); \
> - RREG8(VGA_DAC_MASK); \
> - RREG8(VGA_DAC_MASK); \
> - RREG8(VGA_DAC_MASK); \
> - WREG8(VGA_DAC_MASK, v); \
> - } while (0) \
> -
> -
> -#define CIRRUS_MAX_FB_HEIGHT 4096
> -#define CIRRUS_MAX_FB_WIDTH 4096
> -
> -#define CIRRUS_DPMS_CLEARED (-1)
> -
> -#define to_cirrus_crtc(x) container_of(x, struct cirrus_crtc, base)
> -#define to_cirrus_encoder(x) container_of(x, struct cirrus_encoder, base)
> -
> -struct cirrus_crtc {
> - struct drm_crtc base;
> - int last_dpms;
> - bool enabled;
> -};
> -
> -struct cirrus_fbdev;
> -struct cirrus_mode_info {
> - struct cirrus_crtc *crtc;
> - /* pointer to fbdev info structure */
> - struct cirrus_fbdev *gfbdev;
> -};
> -
> -struct cirrus_encoder {
> - struct drm_encoder base;
> - int last_dpms;
> -};
> -
> -struct cirrus_connector {
> - struct drm_connector base;
> -};
> -
> -struct cirrus_mc {
> - resource_size_t vram_size;
> - resource_size_t vram_base;
> -};
> -
> -struct cirrus_device {
> - struct drm_device *dev;
> - unsigned long flags;
> -
> - resource_size_t rmmio_base;
> - resource_size_t rmmio_size;
> - void __iomem *rmmio;
> -
> - struct cirrus_mc mc;
> - struct cirrus_mode_info mode_info;
> -
> - int num_crtc;
> - int fb_mtrr;
> -
> - struct {
> - struct ttm_bo_device bdev;
> - } ttm;
> - bool mm_inited;
> -};
> -
> -
> -struct cirrus_fbdev {
> - struct drm_fb_helper helper; /* must be first */
> - struct drm_framebuffer *gfb;
> - void *sysram;
> - int size;
> - int x1, y1, x2, y2; /* dirty rect */
> - spinlock_t dirty_lock;
> -};
> -
> -struct cirrus_bo {
> - struct ttm_buffer_object bo;
> - struct ttm_placement placement;
> - struct ttm_bo_kmap_obj kmap;
> - struct drm_gem_object gem;
> - struct ttm_place placements[3];
> - int pin_count;
> -};
> -#define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem)
> -
> -static inline struct cirrus_bo *
> -cirrus_bo(struct ttm_buffer_object *bo)
> -{
> - return container_of(bo, struct cirrus_bo, bo);
> -}
> -
> -
> -#define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base)
> -#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
> -
> - /* cirrus_main.c */
> -int cirrus_device_init(struct cirrus_device *cdev,
> - struct drm_device *ddev,
> - struct pci_dev *pdev,
> - uint32_t flags);
> -void cirrus_device_fini(struct cirrus_device *cdev);
> -void cirrus_gem_free_object(struct drm_gem_object *obj);
> -int cirrus_dumb_mmap_offset(struct drm_file *file,
> - struct drm_device *dev,
> - uint32_t handle,
> - uint64_t *offset);
> -int cirrus_gem_create(struct drm_device *dev,
> - u32 size, bool iskernel,
> - struct drm_gem_object **obj);
> -int cirrus_dumb_create(struct drm_file *file,
> - struct drm_device *dev,
> - struct drm_mode_create_dumb *args);
> -
> -int cirrus_framebuffer_init(struct drm_device *dev,
> - struct drm_framebuffer *gfb,
> - const struct drm_mode_fb_cmd2 *mode_cmd,
> - struct drm_gem_object *obj);
> -
> -bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
> - int bpp, int pitch);
> -
> - /* cirrus_display.c */
> -int cirrus_modeset_init(struct cirrus_device *cdev);
> -void cirrus_modeset_fini(struct cirrus_device *cdev);
> -
> - /* cirrus_fbdev.c */
> -int cirrus_fbdev_init(struct cirrus_device *cdev);
> -void cirrus_fbdev_fini(struct cirrus_device *cdev);
> -
> -
> -
> - /* cirrus_irq.c */
> -void cirrus_driver_irq_preinstall(struct drm_device *dev);
> -int cirrus_driver_irq_postinstall(struct drm_device *dev);
> -void cirrus_driver_irq_uninstall(struct drm_device *dev);
> -irqreturn_t cirrus_driver_irq_handler(int irq, void *arg);
> -
> - /* cirrus_kms.c */
> -int cirrus_driver_load(struct drm_device *dev, unsigned long flags);
> -void cirrus_driver_unload(struct drm_device *dev);
> -extern struct drm_ioctl_desc cirrus_ioctls[];
> -extern int cirrus_max_ioctl;
> -
> -int cirrus_mm_init(struct cirrus_device *cirrus);
> -void cirrus_mm_fini(struct cirrus_device *cirrus);
> -void cirrus_ttm_placement(struct cirrus_bo *bo, int domain);
> -int cirrus_bo_create(struct drm_device *dev, int size, int align,
> - uint32_t flags, struct cirrus_bo **pcirrusbo);
> -int cirrus_mmap(struct file *filp, struct vm_area_struct *vma);
> -
> -static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
> -{
> - int ret;
> -
> - ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL);
> - if (ret) {
> - if (ret != -ERESTARTSYS && ret != -EBUSY)
> - DRM_ERROR("reserve failed %p\n", bo);
> - return ret;
> - }
> - return 0;
> -}
> -
> -static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
> -{
> - ttm_bo_unreserve(&bo->bo);
> -}
> -
> -int cirrus_bo_push_sysram(struct cirrus_bo *bo);
> -int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr);
> -
> -extern int cirrus_bpp;
> -
> -#endif /* __CIRRUS_DRV_H__ */
> diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c
> new file mode 100644
> index 000000000000..e27bb13fc777
> --- /dev/null
> +++ b/drivers/gpu/drm/cirrus/cirrus.c
> @@ -0,0 +1,602 @@
> +/*
> + * Copyright 2012-2019 Red Hat
> + *
> + * This file is subject to the terms and conditions of the GNU General
> + * Public License version 2. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * Authors: Matthew Garrett
> + * Dave Airlie
> + * Gerd Hoffmann
> + *
> + * Portions of this code derived from cirrusfb.c:
> + * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
> + *
> + * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/console.h>
> +
> +#include <video/vga.h>
> +#include <video/cirrus.h>
> +
> +#include <drm/drm_drv.h>
> +#include <drm/drm_file.h>
> +#include <drm/drm_ioctl.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/drm_connector.h>
> +
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +#include <drm/drm_gem_shmem_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_modeset_helper_vtables.h>
> +#include <drm/drm_damage_helper.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_atomic_state_helper.h>
> +
> +#define DRIVER_NAME "cirrus"
> +#define DRIVER_DESC "qemu cirrus vga"
> +#define DRIVER_DATE "2019"
> +#define DRIVER_MAJOR 2
> +#define DRIVER_MINOR 0
> +
> +struct cirrus_device {
> + struct drm_device *dev;
Why not embed drm_device? It's the latest rage :-)
> + struct drm_simple_display_pipe pipe;
> + struct drm_connector conn;
> + unsigned int bpp;
> + unsigned int pitch;
> + void __iomem *vram;
> + void __iomem *mmio;
> +};
> +
> +/* ------------------------------------------------------------------ */
> +/*
> + * The meat of this driver. The core passes us a mode and we have to program
> + * it. The modesetting here is the bare minimum required to satisfy the qemu
> + * emulation of this hardware, and running this against a real device is
> + * likely to result in an inadequately programmed mode. We've already had
> + * the opportunity to modify the mode, so whatever we receive here should
> + * be something that can be correctly programmed and displayed
> + */
> +
> +#define RREG8(reg) ioread8(((void __iomem *)cirrus->mmio) + (reg))
> +#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cirrus->mmio) + (reg))
> +#define RREG32(reg) ioread32(((void __iomem *)cirrus->mmio) + (reg))
> +#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cirrus->mmio) + (reg))
> +
> +#define SEQ_INDEX 4
> +#define SEQ_DATA 5
> +
> +#define WREG_SEQ(reg, v) \
> + do { \
> + WREG8(SEQ_INDEX, reg); \
> + WREG8(SEQ_DATA, v); \
> + } while (0) \
> +
> +#define CRT_INDEX 0x14
> +#define CRT_DATA 0x15
> +
> +#define WREG_CRT(reg, v) \
> + do { \
> + WREG8(CRT_INDEX, reg); \
> + WREG8(CRT_DATA, v); \
> + } while (0) \
> +
> +#define GFX_INDEX 0xe
> +#define GFX_DATA 0xf
> +
> +#define WREG_GFX(reg, v) \
> + do { \
> + WREG8(GFX_INDEX, reg); \
> + WREG8(GFX_DATA, v); \
> + } while (0) \
> +
> +#define VGA_DAC_MASK 0x6
> +
> +#define WREG_HDR(v) \
> + do { \
> + RREG8(VGA_DAC_MASK); \
> + RREG8(VGA_DAC_MASK); \
> + RREG8(VGA_DAC_MASK); \
> + RREG8(VGA_DAC_MASK); \
> + WREG8(VGA_DAC_MASK, v); \
> + } while (0) \
> +
> +
> +static int cirrus_mode_set(struct cirrus_device *cirrus,
> + struct drm_crtc_state *crtc_state)
> +{
> + struct drm_display_mode *mode = &crtc_state->mode;
> + int hsyncstart, hsyncend, htotal, hdispend;
> + int vtotal, vdispend;
> + int tmp;
> + int sr07 = 0, hdr = 0;
> +
> + htotal = mode->htotal / 8;
> + hsyncend = mode->hsync_end / 8;
> + hsyncstart = mode->hsync_start / 8;
> + hdispend = mode->hdisplay / 8;
> +
> + vtotal = mode->vtotal;
> + vdispend = mode->vdisplay;
> +
> + vdispend -= 1;
> + vtotal -= 2;
> +
> + htotal -= 5;
> + hdispend -= 1;
> + hsyncstart += 1;
> + hsyncend += 1;
> +
> + WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20);
> + WREG_CRT(VGA_CRTC_H_TOTAL, htotal);
> + WREG_CRT(VGA_CRTC_H_DISP, hdispend);
> + WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart);
> + WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend);
> + WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff);
> + WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff);
> +
> + tmp = 0x40;
> + if ((vdispend + 1) & 512)
> + tmp |= 0x20;
> + WREG_CRT(VGA_CRTC_MAX_SCAN, tmp);
> +
> + /*
> + * Overflow bits for values that don't fit in the standard registers
> + */
> + tmp = 16;
> + if (vtotal & 256)
> + tmp |= 1;
> + if (vdispend & 256)
> + tmp |= 2;
> + if ((vdispend + 1) & 256)
> + tmp |= 8;
> + if (vtotal & 512)
> + tmp |= 32;
> + if (vdispend & 512)
> + tmp |= 64;
> + WREG_CRT(VGA_CRTC_OVERFLOW, tmp);
> +
> + tmp = 0;
> +
> + /* More overflow bits */
> +
> + if ((htotal + 5) & 64)
> + tmp |= 16;
> + if ((htotal + 5) & 128)
> + tmp |= 32;
> + if (vtotal & 256)
> + tmp |= 64;
> + if (vtotal & 512)
> + tmp |= 128;
> +
> + WREG_CRT(CL_CRT1A, tmp);
> +
> + /* Disable Hercules/CGA compatibility */
> + WREG_CRT(VGA_CRTC_MODE, 0x03);
> +
> + WREG8(SEQ_INDEX, 0x7);
> + sr07 = RREG8(SEQ_DATA);
> + sr07 &= 0xe0;
> + hdr = 0;
> +
> + cirrus->bpp = cirrus->dev->mode_config.preferred_depth;
> + switch (cirrus->bpp) {
> + case 8:
> + sr07 |= 0x11;
> + break;
> + case 16:
> + sr07 |= 0x17;
> + hdr = 0xc1;
> + break;
> + case 24:
> + sr07 |= 0x15;
> + hdr = 0xc5;
> + break;
> + case 32:
> + sr07 |= 0x19;
> + hdr = 0xc5;
> + break;
> + default:
> + return -1;
> + }
> +
> + WREG_SEQ(0x7, sr07);
> +
> + /* Program the pitch */
> + cirrus->pitch = mode->hdisplay * cirrus->bpp / 8;
> + tmp = cirrus->pitch / 8;
> + WREG_CRT(VGA_CRTC_OFFSET, tmp);
> +
> + /* Enable extended blanking and pitch bits, and enable full memory */
> + tmp = 0x22;
> + tmp |= (cirrus->pitch >> 7) & 0x10;
> + tmp |= (cirrus->pitch >> 6) & 0x40;
> + WREG_CRT(0x1b, tmp);
> +
> + /* Enable high-colour modes */
> + WREG_GFX(VGA_GFX_MODE, 0x40);
> +
> + /* And set graphics mode */
> + WREG_GFX(VGA_GFX_MISC, 0x01);
> +
> + WREG_HDR(hdr);
> + /* cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); */
> +
> + /* Unblank (needed on S3 resume, vgabios doesn't do it then) */
> + outb(0x20, 0x3c0);
> + return 0;
> +}
> +
> +static int cirrus_fb_blit_clips(struct drm_framebuffer *fb,
> + struct drm_clip_rect *clips,
> + unsigned int num_clips)
> +{
> + struct cirrus_device *cirrus = fb->dev->dev_private;
> + unsigned i, y, xoff, xlen, src, dst;
> + void *vmap;
> +
> + vmap = drm_gem_shmem_vmap(fb->obj[0]);
> + if (!vmap)
> + return -ENOMEM;
> +
> + for (i = 0; i < num_clips; i++) {
> + xoff = clips[i].x1 * cirrus->bpp / 8;
> + xlen = (clips[i].x2 - clips[i].x1) * cirrus->bpp / 8;
> + for (y = clips[i].y1; y < clips[i].y2; y++) {
> + src = xoff + y * fb->pitches[0];
> + dst = xoff + y * cirrus->pitch;
> + memcpy_toio(cirrus->vram + dst, vmap + src, xlen);
> + }
> + }
> +
> + drm_gem_shmem_vunmap(fb->obj[0], vmap);
> + return 0;
> +}
> +
> +static int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
> + struct drm_rect *rect)
> +{
> + struct drm_clip_rect clip_rect = {
> + .x1 = rect->x1,
> + .x2 = rect->x2,
> + .y1 = rect->y1,
> + .y2 = rect->y2,
> + };
> + return cirrus_fb_blit_clips(fb, &clip_rect, 1);
> +}
> +
> +static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb)
> +{
> + struct drm_clip_rect fullscreen = {
> + .x1 = 0,
> + .x2 = fb->width,
> + .y1 = 0,
> + .y2 = fb->height,
> + };
> + return cirrus_fb_blit_clips(fb, &fullscreen, 1);
> +}
> +
> +static int cirrus_check_size(int width, int height)
> +{
> + static const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
> + static const int max_size = 4 * 1024 * 1024; /* 4 MB */
> + int bytes_pp = 2; /* depth 16 */
> +
> + if (width * bytes_pp > max_pitch)
> + return -EINVAL;
> + if (width * height * bytes_pp > max_size)
> + return -EINVAL;
> + return 0;
> +}
> +
> +/* ------------------------------------------------------------------ */
> +/* cirrus connector */
> +
> +static int cirrus_conn_get_modes(struct drm_connector *conn)
> +{
> + int count;
> +
> + count = drm_add_modes_noedid(conn,
> + conn->dev->mode_config.max_width,
> + conn->dev->mode_config.max_height);
> + drm_set_preferred_mode(conn, 1024, 768);
> + return count;
> +}
> +
> +static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
> + .get_modes = cirrus_conn_get_modes,
> +};
> +
> +static const struct drm_connector_funcs cirrus_conn_funcs = {
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .destroy = drm_connector_cleanup,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int cirrus_conn_init(struct cirrus_device *cirrus)
> +{
> + drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
> + return drm_connector_init(cirrus->dev, &cirrus->conn,
> + &cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
> +
> +}
> +
> +/* ------------------------------------------------------------------ */
> +/* cirrus (simple) display pipe */
> +
> +enum drm_mode_status cirrus_pipe_mode_valid(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode)
> +{
> + if (cirrus_check_size(mode->hdisplay, mode->vdisplay) < 0)
> + return MODE_BAD;
> + return MODE_OK;
> +}
> +
> +int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
> + struct drm_plane_state *plane_state,
> + struct drm_crtc_state *crtc_state)
> +{
> + struct drm_framebuffer *fb = plane_state->fb;
> +
> + if (!fb)
> + return 0;
> + return cirrus_check_size(fb->width, fb->height);
> +}
> +
> +void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
> + struct drm_crtc_state *crtc_state,
> + struct drm_plane_state *plane_state)
> +{
> + struct cirrus_device *cirrus = pipe->crtc.dev->dev_private;
> +
> + cirrus_mode_set(cirrus, crtc_state);
> + cirrus_fb_blit_fullscreen(plane_state->fb);
> +}
> +
> +void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
> + struct drm_plane_state *old_state)
> +{
> + struct drm_plane_state *state = pipe->plane.state;
> + struct drm_crtc *crtc = &pipe->crtc;
> + struct drm_rect rect;
> +
> + if (drm_atomic_helper_damage_merged(old_state, state, &rect))
> + cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
> +
> + if (crtc->state->event) {
> + spin_lock_irq(&crtc->dev->event_lock);
> + drm_crtc_send_vblank_event(crtc, crtc->state->event);
> + spin_unlock_irq(&crtc->dev->event_lock);
> + crtc->state->event = NULL;
> + }
> +}
> +
> +static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
> + .mode_valid = cirrus_pipe_mode_valid,
> + .check = cirrus_pipe_check,
> + .enable = cirrus_pipe_enable,
> + .update = cirrus_pipe_update,
> +};
> +
> +static const uint32_t cirrus_formats[] = {
> + DRM_FORMAT_RGB565,
> +};
> +
> +static int cirrus_pipe_init(struct cirrus_device *cirrus)
> +{
> + return drm_simple_display_pipe_init(cirrus->dev,
> + &cirrus->pipe,
> + &cirrus_pipe_funcs,
> + cirrus_formats,
> + ARRAY_SIZE(cirrus_formats),
> + NULL,
> + &cirrus->conn);
> +}
> +
> +/* ------------------------------------------------------------------ */
> +/* cirrus framebuffers & mode config */
> +
> +static int cirrus_fb_dirty(struct drm_framebuffer *fb,
> + struct drm_file *file_priv,
> + unsigned int flags, unsigned int color,
> + struct drm_clip_rect *clips,
> + unsigned int num_clips)
> +{
> + struct cirrus_device *cirrus = fb->dev->dev_private;
> +
> + if (cirrus->pipe.plane.state->fb != fb)
> + return 0;
> +
> + if (num_clips)
> + cirrus_fb_blit_clips(fb, clips, num_clips);
> + else
> + cirrus_fb_blit_fullscreen(fb);
> + return 0;
> +}
Why not use the dirty helpers and implement dirty rect support in your
main plane update function? Would be nice since then cirrus would be a
really nice template for old fbdev drivers. And you already have all
the dirty rect upload code anyway.
> +static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
> + .destroy = drm_gem_fb_destroy,
> + .create_handle = drm_gem_fb_create_handle,
> + .dirty = cirrus_fb_dirty,
> +};
> +
> +static struct drm_framebuffer*
> +cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
> + const struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + if (mode_cmd->pixel_format != DRM_FORMAT_RGB565)
> + return ERR_PTR(-EINVAL);
> + if (cirrus_check_size(mode_cmd->width, mode_cmd->height) < 0)
> + return ERR_PTR(-EINVAL);
> + return drm_gem_fb_create_with_funcs(dev, file_priv, mode_cmd,
> + &cirrus_fb_funcs);
> +}
> +
> +static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
> + .fb_create = cirrus_fb_create,
> + .atomic_check = drm_atomic_helper_check,
> + .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void cirrus_mode_config_init(struct cirrus_device *cirrus)
> +{
> + struct drm_device *dev = cirrus->dev;
> +
> + drm_mode_config_init(dev);
> + dev->mode_config.min_width = 0;
> + dev->mode_config.min_height = 0;
> + dev->mode_config.max_width = 1600;
> + dev->mode_config.max_height = 1024;
> + dev->mode_config.preferred_depth = 16;
> + dev->mode_config.prefer_shadow = 0;
> + dev->mode_config.funcs = &cirrus_mode_config_funcs;
> +}
> +
> +/* ------------------------------------------------------------------ */
> +
> +DEFINE_DRM_GEM_SHMEM_FOPS(cirrus_fops);
> +
> +static struct drm_driver cirrus_driver = {
> + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_PRIME,
> +
> + .name = DRIVER_NAME,
> + .desc = DRIVER_DESC,
> + .date = DRIVER_DATE,
> + .major = DRIVER_MAJOR,
> + .minor = DRIVER_MINOR,
> +
> + .fops = &cirrus_fops,
> + DRM_GEM_SHMEM_DRIVER_OPS,
> +};
> +
> +static int cirrus_pci_probe(struct pci_dev *pdev,
> + const struct pci_device_id *ent)
> +{
> + struct drm_device *dev;
> + struct cirrus_device *cirrus;
> + int ret;
> +
> + ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
> + if (ret)
> + return ret;
> +
> + dev = drm_dev_alloc(&cirrus_driver, &pdev->dev);
> + if (IS_ERR(dev))
> + return PTR_ERR(dev);
> +
> + ret = pci_enable_device(pdev);
> + if (ret)
> + goto err_free_dev;
> +
> + ret = pci_request_regions(pdev, DRIVER_NAME);
> + if (ret)
> + goto err_free_dev;
> +
> + ret = -ENOMEM;
> + cirrus = kzalloc(sizeof(*cirrus), GFP_KERNEL);
> + if (cirrus == NULL)
> + goto err_pci_release;
> + dev->dev_private = cirrus;
> + cirrus->dev = dev;
> +
> + cirrus->vram = ioremap(pci_resource_start(pdev, 0),
> + pci_resource_len(pdev, 0));
> + if (cirrus->vram == NULL)
> + goto err_free_cirrus;
> +
> + cirrus->mmio = ioremap(pci_resource_start(pdev, 1),
> + pci_resource_len(pdev, 1));
> + if (cirrus->mmio == NULL)
> + goto err_unmap_vram;
> +
> + cirrus_mode_config_init(cirrus);
> +
> + ret = cirrus_conn_init(cirrus);
> + if (ret < 0)
> + goto err_cleanup;
> +
> + ret = cirrus_pipe_init(cirrus);
> + if (ret < 0)
> + goto err_cleanup;
> +
> + drm_mode_config_reset(dev);
> +
> + dev->pdev = pdev;
> + pci_set_drvdata(pdev, dev);
> + ret = drm_dev_register(dev, 0);
> + if (ret)
> + goto err_cleanup;
> +
> + drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
> + return 0;
> +
> +err_cleanup:
> + drm_mode_config_cleanup(dev);
> + iounmap(cirrus->mmio);
> +err_unmap_vram:
> + iounmap(cirrus->vram);
> +err_free_cirrus:
> + kfree(cirrus);
> +err_pci_release:
> + pci_release_regions(pdev);
> +err_free_dev:
> + drm_dev_put(dev);
> + return ret;
> +}
> +
> +static void cirrus_pci_remove(struct pci_dev *pdev)
> +{
> + struct drm_device *dev = pci_get_drvdata(pdev);
> + struct cirrus_device *cirrus = dev->dev_private;
> +
> + drm_dev_unregister(dev);
> + drm_mode_config_cleanup(dev);
> + iounmap(cirrus->mmio);
> + iounmap(cirrus->vram);
> + kfree(cirrus);
> + pci_release_regions(pdev);
> + drm_dev_put(dev);
> +}
> +
> +/* only bind to the cirrus chip in qemu */
> +static const struct pci_device_id pciidlist[] = {
> + { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
> + PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
> + 0, 0, 0 },
> + { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
> + PCI_VENDOR_ID_XEN, 0x0001,
> + 0, 0, 0 },
> + { /* end if list */}
> +};
> +static struct pci_driver cirrus_pci_driver = {
> + .name = DRIVER_NAME,
> + .id_table = pciidlist,
> + .probe = cirrus_pci_probe,
> + .remove = cirrus_pci_remove,
> +};
> +
> +static int __init cirrus_init(void)
> +{
> + if (vgacon_text_force())
> + return -EINVAL;
> + return pci_register_driver(&cirrus_pci_driver);
> +}
> +
> +static void __exit cirrus_exit(void)
> +{
> + pci_unregister_driver(&cirrus_pci_driver);
> +}
> +
> +module_init(cirrus_init);
> +module_exit(cirrus_exit);
> +
> +MODULE_DEVICE_TABLE(pci, pciidlist);
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
> deleted file mode 100644
> index 8ec880f3a322..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_drv.c
> +++ /dev/null
> @@ -1,161 +0,0 @@
> -/*
> - * Copyright 2012 Red Hat <mjg@redhat.com>
> - *
> - * This file is subject to the terms and conditions of the GNU General
> - * Public License version 2. See the file COPYING in the main
> - * directory of this archive for more details.
> - *
> - * Authors: Matthew Garrett
> - * Dave Airlie
> - */
> -#include <linux/module.h>
> -#include <linux/console.h>
> -#include <drm/drmP.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_probe_helper.h>
> -
> -#include "cirrus_drv.h"
> -
> -int cirrus_modeset = -1;
> -int cirrus_bpp = 16;
> -
> -MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
> -module_param_named(modeset, cirrus_modeset, int, 0400);
> -MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:16)");
> -module_param_named(bpp, cirrus_bpp, int, 0400);
> -
> -/*
> - * This is the generic driver code. This binds the driver to the drm core,
> - * which then performs further device association and calls our graphics init
> - * functions
> - */
> -
> -static struct drm_driver driver;
> -
> -/* only bind to the cirrus chip in qemu */
> -static const struct pci_device_id pciidlist[] = {
> - { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
> - PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
> - 0, 0, 0 },
> - { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN,
> - 0x0001, 0, 0, 0 },
> - {0,}
> -};
> -
> -
> -static int cirrus_pci_probe(struct pci_dev *pdev,
> - const struct pci_device_id *ent)
> -{
> - int ret;
> -
> - ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
> - if (ret)
> - return ret;
> -
> - return drm_get_pci_dev(pdev, ent, &driver);
> -}
> -
> -static void cirrus_pci_remove(struct pci_dev *pdev)
> -{
> - struct drm_device *dev = pci_get_drvdata(pdev);
> -
> - drm_put_dev(dev);
> -}
> -
> -#ifdef CONFIG_PM_SLEEP
> -static int cirrus_pm_suspend(struct device *dev)
> -{
> - struct pci_dev *pdev = to_pci_dev(dev);
> - struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - struct cirrus_device *cdev = drm_dev->dev_private;
> -
> - drm_kms_helper_poll_disable(drm_dev);
> -
> - if (cdev->mode_info.gfbdev) {
> - console_lock();
> - drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 1);
> - console_unlock();
> - }
> -
> - return 0;
> -}
> -
> -static int cirrus_pm_resume(struct device *dev)
> -{
> - struct pci_dev *pdev = to_pci_dev(dev);
> - struct drm_device *drm_dev = pci_get_drvdata(pdev);
> - struct cirrus_device *cdev = drm_dev->dev_private;
> -
> - drm_helper_resume_force_mode(drm_dev);
> -
> - if (cdev->mode_info.gfbdev) {
> - console_lock();
> - drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 0);
> - console_unlock();
> - }
> -
> - drm_kms_helper_poll_enable(drm_dev);
> - return 0;
> -}
> -#endif
> -
> -static const struct file_operations cirrus_driver_fops = {
> - .owner = THIS_MODULE,
> - .open = drm_open,
> - .release = drm_release,
> - .unlocked_ioctl = drm_ioctl,
> - .mmap = cirrus_mmap,
> - .poll = drm_poll,
> - .compat_ioctl = drm_compat_ioctl,
> -};
> -static struct drm_driver driver = {
> - .driver_features = DRIVER_MODESET | DRIVER_GEM,
> - .load = cirrus_driver_load,
> - .unload = cirrus_driver_unload,
> - .fops = &cirrus_driver_fops,
> - .name = DRIVER_NAME,
> - .desc = DRIVER_DESC,
> - .date = DRIVER_DATE,
> - .major = DRIVER_MAJOR,
> - .minor = DRIVER_MINOR,
> - .patchlevel = DRIVER_PATCHLEVEL,
> - .gem_free_object_unlocked = cirrus_gem_free_object,
> - .dumb_create = cirrus_dumb_create,
> - .dumb_map_offset = cirrus_dumb_mmap_offset,
> -};
> -
> -static const struct dev_pm_ops cirrus_pm_ops = {
> - SET_SYSTEM_SLEEP_PM_OPS(cirrus_pm_suspend,
> - cirrus_pm_resume)
> -};
> -
> -static struct pci_driver cirrus_pci_driver = {
> - .name = DRIVER_NAME,
> - .id_table = pciidlist,
> - .probe = cirrus_pci_probe,
> - .remove = cirrus_pci_remove,
> - .driver.pm = &cirrus_pm_ops,
> -};
> -
> -static int __init cirrus_init(void)
> -{
> - if (vgacon_text_force() && cirrus_modeset == -1)
> - return -EINVAL;
> -
> - if (cirrus_modeset == 0)
> - return -EINVAL;
> - return pci_register_driver(&cirrus_pci_driver);
> -}
> -
> -static void __exit cirrus_exit(void)
> -{
> - pci_unregister_driver(&cirrus_pci_driver);
> -}
> -
> -module_init(cirrus_init);
> -module_exit(cirrus_exit);
> -
> -MODULE_DEVICE_TABLE(pci, pciidlist);
> -MODULE_AUTHOR(DRIVER_AUTHOR);
> -MODULE_DESCRIPTION(DRIVER_DESC);
> -MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> deleted file mode 100644
> index 2e6128069fc3..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
> +++ /dev/null
> @@ -1,309 +0,0 @@
> -/*
> - * Copyright 2012 Red Hat
> - *
> - * This file is subject to the terms and conditions of the GNU General
> - * Public License version 2. See the file COPYING in the main
> - * directory of this archive for more details.
> - *
> - * Authors: Matthew Garrett
> - * Dave Airlie
> - */
> -#include <linux/module.h>
> -#include <drm/drmP.h>
> -#include <drm/drm_util.h>
> -#include <drm/drm_fb_helper.h>
> -#include <drm/drm_crtc_helper.h>
> -
> -#include "cirrus_drv.h"
> -
> -static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
> - int x, int y, int width, int height)
> -{
> - int i;
> - struct drm_gem_object *obj;
> - struct cirrus_bo *bo;
> - int src_offset, dst_offset;
> - int bpp = afbdev->gfb->format->cpp[0];
> - int ret = -EBUSY;
> - bool unmap = false;
> - bool store_for_later = false;
> - int x2, y2;
> - unsigned long flags;
> -
> - obj = afbdev->gfb->obj[0];
> - bo = gem_to_cirrus_bo(obj);
> -
> - /*
> - * try and reserve the BO, if we fail with busy
> - * then the BO is being moved and we should
> - * store up the damage until later.
> - */
> - if (drm_can_sleep())
> - ret = cirrus_bo_reserve(bo, true);
> - if (ret) {
> - if (ret != -EBUSY)
> - return;
> - store_for_later = true;
> - }
> -
> - x2 = x + width - 1;
> - y2 = y + height - 1;
> - spin_lock_irqsave(&afbdev->dirty_lock, flags);
> -
> - if (afbdev->y1 < y)
> - y = afbdev->y1;
> - if (afbdev->y2 > y2)
> - y2 = afbdev->y2;
> - if (afbdev->x1 < x)
> - x = afbdev->x1;
> - if (afbdev->x2 > x2)
> - x2 = afbdev->x2;
> -
> - if (store_for_later) {
> - afbdev->x1 = x;
> - afbdev->x2 = x2;
> - afbdev->y1 = y;
> - afbdev->y2 = y2;
> - spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
> - return;
> - }
> -
> - afbdev->x1 = afbdev->y1 = INT_MAX;
> - afbdev->x2 = afbdev->y2 = 0;
> - spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
> -
> - if (!bo->kmap.virtual) {
> - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
> - if (ret) {
> - DRM_ERROR("failed to kmap fb updates\n");
> - cirrus_bo_unreserve(bo);
> - return;
> - }
> - unmap = true;
> - }
> - for (i = y; i < y + height; i++) {
> - /* assume equal stride for now */
> - src_offset = dst_offset = i * afbdev->gfb->pitches[0] + (x * bpp);
> - memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
> -
> - }
> - if (unmap)
> - ttm_bo_kunmap(&bo->kmap);
> -
> - cirrus_bo_unreserve(bo);
> -}
> -
> -static void cirrus_fillrect(struct fb_info *info,
> - const struct fb_fillrect *rect)
> -{
> - struct cirrus_fbdev *afbdev = info->par;
> - drm_fb_helper_sys_fillrect(info, rect);
> - cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
> - rect->height);
> -}
> -
> -static void cirrus_copyarea(struct fb_info *info,
> - const struct fb_copyarea *area)
> -{
> - struct cirrus_fbdev *afbdev = info->par;
> - drm_fb_helper_sys_copyarea(info, area);
> - cirrus_dirty_update(afbdev, area->dx, area->dy, area->width,
> - area->height);
> -}
> -
> -static void cirrus_imageblit(struct fb_info *info,
> - const struct fb_image *image)
> -{
> - struct cirrus_fbdev *afbdev = info->par;
> - drm_fb_helper_sys_imageblit(info, image);
> - cirrus_dirty_update(afbdev, image->dx, image->dy, image->width,
> - image->height);
> -}
> -
> -
> -static struct fb_ops cirrusfb_ops = {
> - .owner = THIS_MODULE,
> - .fb_check_var = drm_fb_helper_check_var,
> - .fb_set_par = drm_fb_helper_set_par,
> - .fb_fillrect = cirrus_fillrect,
> - .fb_copyarea = cirrus_copyarea,
> - .fb_imageblit = cirrus_imageblit,
> - .fb_pan_display = drm_fb_helper_pan_display,
> - .fb_blank = drm_fb_helper_blank,
> - .fb_setcmap = drm_fb_helper_setcmap,
> -};
> -
> -static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
> - const struct drm_mode_fb_cmd2 *mode_cmd,
> - struct drm_gem_object **gobj_p)
> -{
> - struct drm_device *dev = afbdev->helper.dev;
> - struct cirrus_device *cdev = dev->dev_private;
> - u32 bpp;
> - u32 size;
> - struct drm_gem_object *gobj;
> - int ret = 0;
> -
> - bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
> -
> - if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
> - bpp, mode_cmd->pitches[0]))
> - return -EINVAL;
> -
> - size = mode_cmd->pitches[0] * mode_cmd->height;
> - ret = cirrus_gem_create(dev, size, true, &gobj);
> - if (ret)
> - return ret;
> -
> - *gobj_p = gobj;
> - return ret;
> -}
> -
> -static int cirrusfb_create(struct drm_fb_helper *helper,
> - struct drm_fb_helper_surface_size *sizes)
> -{
> - struct cirrus_fbdev *gfbdev =
> - container_of(helper, struct cirrus_fbdev, helper);
> - struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
> - struct fb_info *info;
> - struct drm_framebuffer *fb;
> - struct drm_mode_fb_cmd2 mode_cmd;
> - void *sysram;
> - struct drm_gem_object *gobj = NULL;
> - int size, ret;
> -
> - mode_cmd.width = sizes->surface_width;
> - mode_cmd.height = sizes->surface_height;
> - mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
> - mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
> - sizes->surface_depth);
> - size = mode_cmd.pitches[0] * mode_cmd.height;
> -
> - ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj);
> - if (ret) {
> - DRM_ERROR("failed to create fbcon backing object %d\n", ret);
> - return ret;
> - }
> -
> - sysram = vmalloc(size);
> - if (!sysram)
> - return -ENOMEM;
> -
> - info = drm_fb_helper_alloc_fbi(helper);
> - if (IS_ERR(info)) {
> - ret = PTR_ERR(info);
> - goto err_vfree;
> - }
> -
> - fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> - if (!fb) {
> - ret = -ENOMEM;
> - goto err_drm_gem_object_put_unlocked;
> - }
> -
> - ret = cirrus_framebuffer_init(cdev->dev, fb, &mode_cmd, gobj);
> - if (ret)
> - goto err_kfree;
> -
> - gfbdev->sysram = sysram;
> - gfbdev->size = size;
> - gfbdev->gfb = fb;
> -
> - /* setup helper */
> - gfbdev->helper.fb = fb;
> -
> - info->fbops = &cirrusfb_ops;
> -
> - drm_fb_helper_fill_info(info, &gfbdev->helper, sizes);
> -
> - /* setup aperture base/size for vesafb takeover */
> - info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base;
> - info->apertures->ranges[0].size = cdev->mc.vram_size;
> -
> - info->fix.smem_start = cdev->dev->mode_config.fb_base;
> - info->fix.smem_len = cdev->mc.vram_size;
> -
> - info->screen_base = sysram;
> - info->screen_size = size;
> -
> - info->fix.mmio_start = 0;
> - info->fix.mmio_len = 0;
> -
> - DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
> - DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start);
> - DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len);
> - DRM_INFO("fb depth is %d\n", fb->format->depth);
> - DRM_INFO(" pitch is %d\n", fb->pitches[0]);
> -
> - return 0;
> -
> -err_kfree:
> - kfree(fb);
> -err_drm_gem_object_put_unlocked:
> - drm_gem_object_put_unlocked(gobj);
> -err_vfree:
> - vfree(sysram);
> - return ret;
> -}
> -
> -static int cirrus_fbdev_destroy(struct drm_device *dev,
> - struct cirrus_fbdev *gfbdev)
> -{
> - struct drm_framebuffer *gfb = gfbdev->gfb;
> -
> - drm_helper_force_disable_all(dev);
> -
> - drm_fb_helper_unregister_fbi(&gfbdev->helper);
> -
> - vfree(gfbdev->sysram);
> - drm_fb_helper_fini(&gfbdev->helper);
> - if (gfb)
> - drm_framebuffer_put(gfb);
> -
> - return 0;
> -}
> -
> -static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
> - .fb_probe = cirrusfb_create,
> -};
> -
> -int cirrus_fbdev_init(struct cirrus_device *cdev)
> -{
> - struct cirrus_fbdev *gfbdev;
> - int ret;
> -
> - /*bpp_sel = 8;*/
> - gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL);
> - if (!gfbdev)
> - return -ENOMEM;
> -
> - cdev->mode_info.gfbdev = gfbdev;
> - spin_lock_init(&gfbdev->dirty_lock);
> -
> - drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
> - &cirrus_fb_helper_funcs);
> -
> - ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
> - CIRRUSFB_CONN_LIMIT);
> - if (ret)
> - return ret;
> -
> - ret = drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
> - if (ret)
> - return ret;
> -
> - /* disable all the possible outputs/crtcs before entering KMS mode */
> - drm_helper_disable_unused_functions(cdev->dev);
> -
> - return drm_fb_helper_initial_config(&gfbdev->helper, cirrus_bpp);
> -}
> -
> -void cirrus_fbdev_fini(struct cirrus_device *cdev)
> -{
> - if (!cdev->mode_info.gfbdev)
> - return;
> -
> - cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev);
> - kfree(cdev->mode_info.gfbdev);
> - cdev->mode_info.gfbdev = NULL;
> -}
> diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
> deleted file mode 100644
> index 57f8fe6d020b..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_main.c
> +++ /dev/null
> @@ -1,328 +0,0 @@
> -/*
> - * Copyright 2012 Red Hat
> - *
> - * This file is subject to the terms and conditions of the GNU General
> - * Public License version 2. See the file COPYING in the main
> - * directory of this archive for more details.
> - *
> - * Authors: Matthew Garrett
> - * Dave Airlie
> - */
> -#include <drm/drmP.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_gem_framebuffer_helper.h>
> -
> -#include "cirrus_drv.h"
> -
> -static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
> - .create_handle = drm_gem_fb_create_handle,
> - .destroy = drm_gem_fb_destroy,
> -};
> -
> -int cirrus_framebuffer_init(struct drm_device *dev,
> - struct drm_framebuffer *gfb,
> - const struct drm_mode_fb_cmd2 *mode_cmd,
> - struct drm_gem_object *obj)
> -{
> - int ret;
> -
> - drm_helper_mode_fill_fb_struct(dev, gfb, mode_cmd);
> - gfb->obj[0] = obj;
> - ret = drm_framebuffer_init(dev, gfb, &cirrus_fb_funcs);
> - if (ret) {
> - DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
> - return ret;
> - }
> - return 0;
> -}
> -
> -static struct drm_framebuffer *
> -cirrus_user_framebuffer_create(struct drm_device *dev,
> - struct drm_file *filp,
> - const struct drm_mode_fb_cmd2 *mode_cmd)
> -{
> - struct cirrus_device *cdev = dev->dev_private;
> - struct drm_gem_object *obj;
> - struct drm_framebuffer *fb;
> - u32 bpp;
> - int ret;
> -
> - bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
> -
> - if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
> - bpp, mode_cmd->pitches[0]))
> - return ERR_PTR(-EINVAL);
> -
> - obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
> - if (obj == NULL)
> - return ERR_PTR(-ENOENT);
> -
> - fb = kzalloc(sizeof(*fb), GFP_KERNEL);
> - if (!fb) {
> - drm_gem_object_put_unlocked(obj);
> - return ERR_PTR(-ENOMEM);
> - }
> -
> - ret = cirrus_framebuffer_init(dev, fb, mode_cmd, obj);
> - if (ret) {
> - drm_gem_object_put_unlocked(obj);
> - kfree(fb);
> - return ERR_PTR(ret);
> - }
> - return fb;
> -}
> -
> -static const struct drm_mode_config_funcs cirrus_mode_funcs = {
> - .fb_create = cirrus_user_framebuffer_create,
> -};
> -
> -/* Unmap the framebuffer from the core and release the memory */
> -static void cirrus_vram_fini(struct cirrus_device *cdev)
> -{
> - iounmap(cdev->rmmio);
> - cdev->rmmio = NULL;
> - if (cdev->mc.vram_base)
> - release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size);
> -}
> -
> -/* Map the framebuffer from the card and configure the core */
> -static int cirrus_vram_init(struct cirrus_device *cdev)
> -{
> - /* BAR 0 is VRAM */
> - cdev->mc.vram_base = pci_resource_start(cdev->dev->pdev, 0);
> - cdev->mc.vram_size = pci_resource_len(cdev->dev->pdev, 0);
> -
> - if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size,
> - "cirrusdrmfb_vram")) {
> - DRM_ERROR("can't reserve VRAM\n");
> - return -ENXIO;
> - }
> -
> - return 0;
> -}
> -
> -/*
> - * Our emulated hardware has two sets of memory. One is video RAM and can
> - * simply be used as a linear framebuffer - the other provides mmio access
> - * to the display registers. The latter can also be accessed via IO port
> - * access, but we map the range and use mmio to program them instead
> - */
> -
> -int cirrus_device_init(struct cirrus_device *cdev,
> - struct drm_device *ddev,
> - struct pci_dev *pdev, uint32_t flags)
> -{
> - int ret;
> -
> - cdev->dev = ddev;
> - cdev->flags = flags;
> -
> - /* Hardcode the number of CRTCs to 1 */
> - cdev->num_crtc = 1;
> -
> - /* BAR 0 is the framebuffer, BAR 1 contains registers */
> - cdev->rmmio_base = pci_resource_start(cdev->dev->pdev, 1);
> - cdev->rmmio_size = pci_resource_len(cdev->dev->pdev, 1);
> -
> - if (!request_mem_region(cdev->rmmio_base, cdev->rmmio_size,
> - "cirrusdrmfb_mmio")) {
> - DRM_ERROR("can't reserve mmio registers\n");
> - return -ENOMEM;
> - }
> -
> - cdev->rmmio = ioremap(cdev->rmmio_base, cdev->rmmio_size);
> -
> - if (cdev->rmmio == NULL)
> - return -ENOMEM;
> -
> - ret = cirrus_vram_init(cdev);
> - if (ret) {
> - release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
> - return ret;
> - }
> -
> - return 0;
> -}
> -
> -void cirrus_device_fini(struct cirrus_device *cdev)
> -{
> - release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
> - cirrus_vram_fini(cdev);
> -}
> -
> -/*
> - * Functions here will be called by the core once it's bound the driver to
> - * a PCI device
> - */
> -
> -int cirrus_driver_load(struct drm_device *dev, unsigned long flags)
> -{
> - struct cirrus_device *cdev;
> - int r;
> -
> - cdev = kzalloc(sizeof(struct cirrus_device), GFP_KERNEL);
> - if (cdev == NULL)
> - return -ENOMEM;
> - dev->dev_private = (void *)cdev;
> -
> - r = cirrus_device_init(cdev, dev, dev->pdev, flags);
> - if (r) {
> - dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
> - goto out;
> - }
> -
> - r = cirrus_mm_init(cdev);
> - if (r) {
> - dev_err(&dev->pdev->dev, "fatal err on mm init\n");
> - goto out;
> - }
> -
> - /*
> - * cirrus_modeset_init() is initializing/registering the emulated fbdev
> - * and DRM internals can access/test some of the fields in
> - * mode_config->funcs as part of the fbdev registration process.
> - * Make sure dev->mode_config.funcs is properly set to avoid
> - * dereferencing a NULL pointer.
> - * FIXME: mode_config.funcs assignment should probably be done in
> - * cirrus_modeset_init() (that's a common pattern seen in other DRM
> - * drivers).
> - */
> - dev->mode_config.funcs = &cirrus_mode_funcs;
> - r = cirrus_modeset_init(cdev);
> - if (r) {
> - dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
> - goto out;
> - }
> -
> - return 0;
> -out:
> - cirrus_driver_unload(dev);
> - return r;
> -}
> -
> -void cirrus_driver_unload(struct drm_device *dev)
> -{
> - struct cirrus_device *cdev = dev->dev_private;
> -
> - if (cdev == NULL)
> - return;
> - cirrus_modeset_fini(cdev);
> - cirrus_mm_fini(cdev);
> - cirrus_device_fini(cdev);
> - kfree(cdev);
> - dev->dev_private = NULL;
> -}
> -
> -int cirrus_gem_create(struct drm_device *dev,
> - u32 size, bool iskernel,
> - struct drm_gem_object **obj)
> -{
> - struct cirrus_bo *cirrusbo;
> - int ret;
> -
> - *obj = NULL;
> -
> - size = roundup(size, PAGE_SIZE);
> - if (size == 0)
> - return -EINVAL;
> -
> - ret = cirrus_bo_create(dev, size, 0, 0, &cirrusbo);
> - if (ret) {
> - if (ret != -ERESTARTSYS)
> - DRM_ERROR("failed to allocate GEM object\n");
> - return ret;
> - }
> - *obj = &cirrusbo->gem;
> - return 0;
> -}
> -
> -int cirrus_dumb_create(struct drm_file *file,
> - struct drm_device *dev,
> - struct drm_mode_create_dumb *args)
> -{
> - int ret;
> - struct drm_gem_object *gobj;
> - u32 handle;
> -
> - args->pitch = args->width * ((args->bpp + 7) / 8);
> - args->size = args->pitch * args->height;
> -
> - ret = cirrus_gem_create(dev, args->size, false,
> - &gobj);
> - if (ret)
> - return ret;
> -
> - ret = drm_gem_handle_create(file, gobj, &handle);
> - drm_gem_object_put_unlocked(gobj);
> - if (ret)
> - return ret;
> -
> - args->handle = handle;
> - return 0;
> -}
> -
> -static void cirrus_bo_unref(struct cirrus_bo **bo)
> -{
> - struct ttm_buffer_object *tbo;
> -
> - if ((*bo) == NULL)
> - return;
> -
> - tbo = &((*bo)->bo);
> - ttm_bo_put(tbo);
> - *bo = NULL;
> -}
> -
> -void cirrus_gem_free_object(struct drm_gem_object *obj)
> -{
> - struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj);
> -
> - cirrus_bo_unref(&cirrus_bo);
> -}
> -
> -
> -static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
> -{
> - return drm_vma_node_offset_addr(&bo->bo.vma_node);
> -}
> -
> -int
> -cirrus_dumb_mmap_offset(struct drm_file *file,
> - struct drm_device *dev,
> - uint32_t handle,
> - uint64_t *offset)
> -{
> - struct drm_gem_object *obj;
> - struct cirrus_bo *bo;
> -
> - obj = drm_gem_object_lookup(file, handle);
> - if (obj == NULL)
> - return -ENOENT;
> -
> - bo = gem_to_cirrus_bo(obj);
> - *offset = cirrus_bo_mmap_offset(bo);
> -
> - drm_gem_object_put_unlocked(obj);
> -
> - return 0;
> -}
> -
> -bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
> - int bpp, int pitch)
> -{
> - const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
> - const int max_size = cdev->mc.vram_size;
> -
> - if (bpp > cirrus_bpp)
> - return false;
> - if (bpp > 32)
> - return false;
> -
> - if (pitch > max_pitch)
> - return false;
> -
> - if (pitch * height > max_size)
> - return false;
> -
> - return true;
> -}
> diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
> deleted file mode 100644
> index b109cd71426f..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_mode.c
> +++ /dev/null
> @@ -1,617 +0,0 @@
> -
> -/*
> - * Copyright 2012 Red Hat
> - *
> - * This file is subject to the terms and conditions of the GNU General
> - * Public License version 2. See the file COPYING in the main
> - * directory of this archive for more details.
> - *
> - * Authors: Matthew Garrett
> - * Dave Airlie
> - *
> - * Portions of this code derived from cirrusfb.c:
> - * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
> - *
> - * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
> - */
> -#include <drm/drmP.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_plane_helper.h>
> -#include <drm/drm_probe_helper.h>
> -
> -#include <video/cirrus.h>
> -
> -#include "cirrus_drv.h"
> -
> -#define CIRRUS_LUT_SIZE 256
> -
> -#define PALETTE_INDEX 0x8
> -#define PALETTE_DATA 0x9
> -
> -/*
> - * This file contains setup code for the CRTC.
> - */
> -
> -/*
> - * The DRM core requires DPMS functions, but they make little sense in our
> - * case and so are just stubs
> - */
> -
> -static void cirrus_crtc_dpms(struct drm_crtc *crtc, int mode)
> -{
> - struct drm_device *dev = crtc->dev;
> - struct cirrus_device *cdev = dev->dev_private;
> - u8 sr01, gr0e;
> -
> - switch (mode) {
> - case DRM_MODE_DPMS_ON:
> - sr01 = 0x00;
> - gr0e = 0x00;
> - break;
> - case DRM_MODE_DPMS_STANDBY:
> - sr01 = 0x20;
> - gr0e = 0x02;
> - break;
> - case DRM_MODE_DPMS_SUSPEND:
> - sr01 = 0x20;
> - gr0e = 0x04;
> - break;
> - case DRM_MODE_DPMS_OFF:
> - sr01 = 0x20;
> - gr0e = 0x06;
> - break;
> - default:
> - return;
> - }
> -
> - WREG8(SEQ_INDEX, 0x1);
> - sr01 |= RREG8(SEQ_DATA) & ~0x20;
> - WREG_SEQ(0x1, sr01);
> -
> - WREG8(GFX_INDEX, 0xe);
> - gr0e |= RREG8(GFX_DATA) & ~0x06;
> - WREG_GFX(0xe, gr0e);
> -}
> -
> -static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset)
> -{
> - struct cirrus_device *cdev = crtc->dev->dev_private;
> - u32 addr;
> - u8 tmp;
> -
> - addr = offset >> 2;
> - WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff));
> - WREG_CRT(0x0d, (u8)(addr & 0xff));
> -
> - WREG8(CRT_INDEX, 0x1b);
> - tmp = RREG8(CRT_DATA);
> - tmp &= 0xf2;
> - tmp |= (addr >> 16) & 0x01;
> - tmp |= (addr >> 15) & 0x0c;
> - WREG_CRT(0x1b, tmp);
> - WREG8(CRT_INDEX, 0x1d);
> - tmp = RREG8(CRT_DATA);
> - tmp &= 0x7f;
> - tmp |= (addr >> 12) & 0x80;
> - WREG_CRT(0x1d, tmp);
> -}
> -
> -/* cirrus is different - we will force move buffers out of VRAM */
> -static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
> - struct drm_framebuffer *fb,
> - int x, int y, int atomic)
> -{
> - struct cirrus_device *cdev = crtc->dev->dev_private;
> - struct cirrus_bo *bo;
> - int ret;
> - u64 gpu_addr;
> -
> - /* push the previous fb to system ram */
> - if (!atomic && fb) {
> - bo = gem_to_cirrus_bo(fb->obj[0]);
> - ret = cirrus_bo_reserve(bo, false);
> - if (ret)
> - return ret;
> - cirrus_bo_push_sysram(bo);
> - cirrus_bo_unreserve(bo);
> - }
> -
> - bo = gem_to_cirrus_bo(crtc->primary->fb->obj[0]);
> -
> - ret = cirrus_bo_reserve(bo, false);
> - if (ret)
> - return ret;
> -
> - ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
> - if (ret) {
> - cirrus_bo_unreserve(bo);
> - return ret;
> - }
> -
> - if (cdev->mode_info.gfbdev->gfb == crtc->primary->fb) {
> - /* if pushing console in kmap it */
> - ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
> - if (ret)
> - DRM_ERROR("failed to kmap fbcon\n");
> - }
> - cirrus_bo_unreserve(bo);
> -
> - cirrus_set_start_address(crtc, (u32)gpu_addr);
> - return 0;
> -}
> -
> -static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
> - struct drm_framebuffer *old_fb)
> -{
> - return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
> -}
> -
> -/*
> - * The meat of this driver. The core passes us a mode and we have to program
> - * it. The modesetting here is the bare minimum required to satisfy the qemu
> - * emulation of this hardware, and running this against a real device is
> - * likely to result in an inadequately programmed mode. We've already had
> - * the opportunity to modify the mode, so whatever we receive here should
> - * be something that can be correctly programmed and displayed
> - */
> -static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
> - struct drm_display_mode *mode,
> - struct drm_display_mode *adjusted_mode,
> - int x, int y, struct drm_framebuffer *old_fb)
> -{
> - struct drm_device *dev = crtc->dev;
> - struct cirrus_device *cdev = dev->dev_private;
> - const struct drm_framebuffer *fb = crtc->primary->fb;
> - int hsyncstart, hsyncend, htotal, hdispend;
> - int vtotal, vdispend;
> - int tmp;
> - int sr07 = 0, hdr = 0;
> -
> - htotal = mode->htotal / 8;
> - hsyncend = mode->hsync_end / 8;
> - hsyncstart = mode->hsync_start / 8;
> - hdispend = mode->hdisplay / 8;
> -
> - vtotal = mode->vtotal;
> - vdispend = mode->vdisplay;
> -
> - vdispend -= 1;
> - vtotal -= 2;
> -
> - htotal -= 5;
> - hdispend -= 1;
> - hsyncstart += 1;
> - hsyncend += 1;
> -
> - WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20);
> - WREG_CRT(VGA_CRTC_H_TOTAL, htotal);
> - WREG_CRT(VGA_CRTC_H_DISP, hdispend);
> - WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart);
> - WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend);
> - WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff);
> - WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff);
> -
> - tmp = 0x40;
> - if ((vdispend + 1) & 512)
> - tmp |= 0x20;
> - WREG_CRT(VGA_CRTC_MAX_SCAN, tmp);
> -
> - /*
> - * Overflow bits for values that don't fit in the standard registers
> - */
> - tmp = 16;
> - if (vtotal & 256)
> - tmp |= 1;
> - if (vdispend & 256)
> - tmp |= 2;
> - if ((vdispend + 1) & 256)
> - tmp |= 8;
> - if (vtotal & 512)
> - tmp |= 32;
> - if (vdispend & 512)
> - tmp |= 64;
> - WREG_CRT(VGA_CRTC_OVERFLOW, tmp);
> -
> - tmp = 0;
> -
> - /* More overflow bits */
> -
> - if ((htotal + 5) & 64)
> - tmp |= 16;
> - if ((htotal + 5) & 128)
> - tmp |= 32;
> - if (vtotal & 256)
> - tmp |= 64;
> - if (vtotal & 512)
> - tmp |= 128;
> -
> - WREG_CRT(CL_CRT1A, tmp);
> -
> - /* Disable Hercules/CGA compatibility */
> - WREG_CRT(VGA_CRTC_MODE, 0x03);
> -
> - WREG8(SEQ_INDEX, 0x7);
> - sr07 = RREG8(SEQ_DATA);
> - sr07 &= 0xe0;
> - hdr = 0;
> - switch (fb->format->cpp[0] * 8) {
> - case 8:
> - sr07 |= 0x11;
> - break;
> - case 16:
> - sr07 |= 0x17;
> - hdr = 0xc1;
> - break;
> - case 24:
> - sr07 |= 0x15;
> - hdr = 0xc5;
> - break;
> - case 32:
> - sr07 |= 0x19;
> - hdr = 0xc5;
> - break;
> - default:
> - return -1;
> - }
> -
> - WREG_SEQ(0x7, sr07);
> -
> - /* Program the pitch */
> - tmp = fb->pitches[0] / 8;
> - WREG_CRT(VGA_CRTC_OFFSET, tmp);
> -
> - /* Enable extended blanking and pitch bits, and enable full memory */
> - tmp = 0x22;
> - tmp |= (fb->pitches[0] >> 7) & 0x10;
> - tmp |= (fb->pitches[0] >> 6) & 0x40;
> - WREG_CRT(0x1b, tmp);
> -
> - /* Enable high-colour modes */
> - WREG_GFX(VGA_GFX_MODE, 0x40);
> -
> - /* And set graphics mode */
> - WREG_GFX(VGA_GFX_MISC, 0x01);
> -
> - WREG_HDR(hdr);
> - cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
> -
> - /* Unblank (needed on S3 resume, vgabios doesn't do it then) */
> - outb(0x20, 0x3c0);
> - return 0;
> -}
> -
> -/*
> - * This is called before a mode is programmed. A typical use might be to
> - * enable DPMS during the programming to avoid seeing intermediate stages,
> - * but that's not relevant to us
> - */
> -static void cirrus_crtc_prepare(struct drm_crtc *crtc)
> -{
> -}
> -
> -static void cirrus_crtc_load_lut(struct drm_crtc *crtc)
> -{
> - struct drm_device *dev = crtc->dev;
> - struct cirrus_device *cdev = dev->dev_private;
> - u16 *r, *g, *b;
> - int i;
> -
> - if (!crtc->enabled)
> - return;
> -
> - r = crtc->gamma_store;
> - g = r + crtc->gamma_size;
> - b = g + crtc->gamma_size;
> -
> - for (i = 0; i < CIRRUS_LUT_SIZE; i++) {
> - /* VGA registers */
> - WREG8(PALETTE_INDEX, i);
> - WREG8(PALETTE_DATA, *r++ >> 8);
> - WREG8(PALETTE_DATA, *g++ >> 8);
> - WREG8(PALETTE_DATA, *b++ >> 8);
> - }
> -}
> -
> -/*
> - * This is called after a mode is programmed. It should reverse anything done
> - * by the prepare function
> - */
> -static void cirrus_crtc_commit(struct drm_crtc *crtc)
> -{
> - cirrus_crtc_load_lut(crtc);
> -}
> -
> -/*
> - * The core can pass us a set of gamma values to program. We actually only
> - * use this for 8-bit mode so can't perform smooth fades on deeper modes,
> - * but it's a requirement that we provide the function
> - */
> -static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
> - u16 *blue, uint32_t size,
> - struct drm_modeset_acquire_ctx *ctx)
> -{
> - cirrus_crtc_load_lut(crtc);
> -
> - return 0;
> -}
> -
> -/* Simple cleanup function */
> -static void cirrus_crtc_destroy(struct drm_crtc *crtc)
> -{
> - struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc);
> -
> - drm_crtc_cleanup(crtc);
> - kfree(cirrus_crtc);
> -}
> -
> -/* These provide the minimum set of functions required to handle a CRTC */
> -static const struct drm_crtc_funcs cirrus_crtc_funcs = {
> - .gamma_set = cirrus_crtc_gamma_set,
> - .set_config = drm_crtc_helper_set_config,
> - .destroy = cirrus_crtc_destroy,
> -};
> -
> -static const struct drm_crtc_helper_funcs cirrus_helper_funcs = {
> - .dpms = cirrus_crtc_dpms,
> - .mode_set = cirrus_crtc_mode_set,
> - .mode_set_base = cirrus_crtc_mode_set_base,
> - .prepare = cirrus_crtc_prepare,
> - .commit = cirrus_crtc_commit,
> -};
> -
> -/* CRTC setup */
> -static const uint32_t cirrus_formats_16[] = {
> - DRM_FORMAT_RGB565,
> -};
> -
> -static const uint32_t cirrus_formats_24[] = {
> - DRM_FORMAT_RGB888,
> - DRM_FORMAT_RGB565,
> -};
> -
> -static const uint32_t cirrus_formats_32[] = {
> - DRM_FORMAT_XRGB8888,
> - DRM_FORMAT_ARGB8888,
> - DRM_FORMAT_RGB888,
> - DRM_FORMAT_RGB565,
> -};
> -
> -static struct drm_plane *cirrus_primary_plane(struct drm_device *dev)
> -{
> - const uint32_t *formats;
> - uint32_t nformats;
> - struct drm_plane *primary;
> - int ret;
> -
> - switch (cirrus_bpp) {
> - case 16:
> - formats = cirrus_formats_16;
> - nformats = ARRAY_SIZE(cirrus_formats_16);
> - break;
> - case 24:
> - formats = cirrus_formats_24;
> - nformats = ARRAY_SIZE(cirrus_formats_24);
> - break;
> - case 32:
> - formats = cirrus_formats_32;
> - nformats = ARRAY_SIZE(cirrus_formats_32);
> - break;
> - default:
> - return NULL;
> - }
> -
> - primary = kzalloc(sizeof(*primary), GFP_KERNEL);
> - if (primary == NULL) {
> - DRM_DEBUG_KMS("Failed to allocate primary plane\n");
> - return NULL;
> - }
> -
> - ret = drm_universal_plane_init(dev, primary, 0,
> - &drm_primary_helper_funcs,
> - formats, nformats,
> - NULL,
> - DRM_PLANE_TYPE_PRIMARY, NULL);
> - if (ret) {
> - kfree(primary);
> - primary = NULL;
> - }
> -
> - return primary;
> -}
> -
> -static void cirrus_crtc_init(struct drm_device *dev)
> -{
> - struct cirrus_device *cdev = dev->dev_private;
> - struct cirrus_crtc *cirrus_crtc;
> - struct drm_plane *primary;
> -
> - cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) +
> - (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)),
> - GFP_KERNEL);
> -
> - if (cirrus_crtc == NULL)
> - return;
> -
> - primary = cirrus_primary_plane(dev);
> - if (primary == NULL) {
> - kfree(cirrus_crtc);
> - return;
> - }
> -
> - drm_crtc_init_with_planes(dev, &cirrus_crtc->base,
> - primary, NULL,
> - &cirrus_crtc_funcs, NULL);
> -
> - drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE);
> - cdev->mode_info.crtc = cirrus_crtc;
> -
> - drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs);
> -}
> -
> -static void cirrus_encoder_mode_set(struct drm_encoder *encoder,
> - struct drm_display_mode *mode,
> - struct drm_display_mode *adjusted_mode)
> -{
> -}
> -
> -static void cirrus_encoder_dpms(struct drm_encoder *encoder, int state)
> -{
> - return;
> -}
> -
> -static void cirrus_encoder_prepare(struct drm_encoder *encoder)
> -{
> -}
> -
> -static void cirrus_encoder_commit(struct drm_encoder *encoder)
> -{
> -}
> -
> -static void cirrus_encoder_destroy(struct drm_encoder *encoder)
> -{
> - struct cirrus_encoder *cirrus_encoder = to_cirrus_encoder(encoder);
> - drm_encoder_cleanup(encoder);
> - kfree(cirrus_encoder);
> -}
> -
> -static const struct drm_encoder_helper_funcs cirrus_encoder_helper_funcs = {
> - .dpms = cirrus_encoder_dpms,
> - .mode_set = cirrus_encoder_mode_set,
> - .prepare = cirrus_encoder_prepare,
> - .commit = cirrus_encoder_commit,
> -};
> -
> -static const struct drm_encoder_funcs cirrus_encoder_encoder_funcs = {
> - .destroy = cirrus_encoder_destroy,
> -};
> -
> -static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
> -{
> - struct drm_encoder *encoder;
> - struct cirrus_encoder *cirrus_encoder;
> -
> - cirrus_encoder = kzalloc(sizeof(struct cirrus_encoder), GFP_KERNEL);
> - if (!cirrus_encoder)
> - return NULL;
> -
> - encoder = &cirrus_encoder->base;
> - encoder->possible_crtcs = 0x1;
> -
> - drm_encoder_init(dev, encoder, &cirrus_encoder_encoder_funcs,
> - DRM_MODE_ENCODER_DAC, NULL);
> - drm_encoder_helper_add(encoder, &cirrus_encoder_helper_funcs);
> -
> - return encoder;
> -}
> -
> -
> -static int cirrus_vga_get_modes(struct drm_connector *connector)
> -{
> - int count;
> -
> - /* Just add a static list of modes */
> - if (cirrus_bpp <= 24) {
> - count = drm_add_modes_noedid(connector, 1280, 1024);
> - drm_set_preferred_mode(connector, 1024, 768);
> - } else {
> - count = drm_add_modes_noedid(connector, 800, 600);
> - drm_set_preferred_mode(connector, 800, 600);
> - }
> - return count;
> -}
> -
> -static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
> - *connector)
> -{
> - int enc_id = connector->encoder_ids[0];
> - /* pick the encoder ids */
> - if (enc_id)
> - return drm_encoder_find(connector->dev, NULL, enc_id);
> - return NULL;
> -}
> -
> -static void cirrus_connector_destroy(struct drm_connector *connector)
> -{
> - drm_connector_cleanup(connector);
> - kfree(connector);
> -}
> -
> -static const struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
> - .get_modes = cirrus_vga_get_modes,
> - .best_encoder = cirrus_connector_best_encoder,
> -};
> -
> -static const struct drm_connector_funcs cirrus_vga_connector_funcs = {
> - .dpms = drm_helper_connector_dpms,
> - .fill_modes = drm_helper_probe_single_connector_modes,
> - .destroy = cirrus_connector_destroy,
> -};
> -
> -static struct drm_connector *cirrus_vga_init(struct drm_device *dev)
> -{
> - struct drm_connector *connector;
> - struct cirrus_connector *cirrus_connector;
> -
> - cirrus_connector = kzalloc(sizeof(struct cirrus_connector), GFP_KERNEL);
> - if (!cirrus_connector)
> - return NULL;
> -
> - connector = &cirrus_connector->base;
> -
> - drm_connector_init(dev, connector,
> - &cirrus_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA);
> -
> - drm_connector_helper_add(connector, &cirrus_vga_connector_helper_funcs);
> -
> - drm_connector_register(connector);
> - return connector;
> -}
> -
> -
> -int cirrus_modeset_init(struct cirrus_device *cdev)
> -{
> - struct drm_encoder *encoder;
> - struct drm_connector *connector;
> - int ret;
> -
> - drm_mode_config_init(cdev->dev);
> -
> - cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH;
> - cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT;
> -
> - cdev->dev->mode_config.fb_base = cdev->mc.vram_base;
> - cdev->dev->mode_config.preferred_depth = cirrus_bpp;
> - /* don't prefer a shadow on virt GPU */
> - cdev->dev->mode_config.prefer_shadow = 0;
> -
> - cirrus_crtc_init(cdev->dev);
> -
> - encoder = cirrus_encoder_init(cdev->dev);
> - if (!encoder) {
> - DRM_ERROR("cirrus_encoder_init failed\n");
> - return -1;
> - }
> -
> - connector = cirrus_vga_init(cdev->dev);
> - if (!connector) {
> - DRM_ERROR("cirrus_vga_init failed\n");
> - return -1;
> - }
> -
> - drm_connector_attach_encoder(connector, encoder);
> -
> - ret = cirrus_fbdev_init(cdev);
> - if (ret) {
> - DRM_ERROR("cirrus_fbdev_init failed\n");
> - return ret;
> - }
> -
> - return 0;
> -}
> -
> -void cirrus_modeset_fini(struct cirrus_device *cdev)
> -{
> - cirrus_fbdev_fini(cdev);
> - drm_helper_force_disable_all(cdev->dev);
> - drm_mode_config_cleanup(cdev->dev);
> -}
> diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c
> deleted file mode 100644
> index e075810b4bd4..000000000000
> --- a/drivers/gpu/drm/cirrus/cirrus_ttm.c
> +++ /dev/null
> @@ -1,343 +0,0 @@
> -/*
> - * Copyright 2012 Red Hat Inc.
> - *
> - * Permission is hereby granted, free of charge, to any person obtaining a
> - * copy of this software and associated documentation files (the
> - * "Software"), to deal in the Software without restriction, including
> - * without limitation the rights to use, copy, modify, merge, publish,
> - * distribute, sub license, and/or sell copies of the Software, and to
> - * permit persons to whom the Software is furnished to do so, subject to
> - * the following conditions:
> - *
> - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
> - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
> - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
> - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
> - * USE OR OTHER DEALINGS IN THE SOFTWARE.
> - *
> - * The above copyright notice and this permission notice (including the
> - * next paragraph) shall be included in all copies or substantial portions
> - * of the Software.
> - *
> - */
> -/*
> - * Authors: Dave Airlie <airlied@redhat.com>
> - */
> -#include <drm/drmP.h>
> -#include <drm/ttm/ttm_page_alloc.h>
> -
> -#include "cirrus_drv.h"
> -
> -static inline struct cirrus_device *
> -cirrus_bdev(struct ttm_bo_device *bd)
> -{
> - return container_of(bd, struct cirrus_device, ttm.bdev);
> -}
> -
> -static void cirrus_bo_ttm_destroy(struct ttm_buffer_object *tbo)
> -{
> - struct cirrus_bo *bo;
> -
> - bo = container_of(tbo, struct cirrus_bo, bo);
> -
> - drm_gem_object_release(&bo->gem);
> - kfree(bo);
> -}
> -
> -static bool cirrus_ttm_bo_is_cirrus_bo(struct ttm_buffer_object *bo)
> -{
> - if (bo->destroy == &cirrus_bo_ttm_destroy)
> - return true;
> - return false;
> -}
> -
> -static int
> -cirrus_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
> - struct ttm_mem_type_manager *man)
> -{
> - switch (type) {
> - case TTM_PL_SYSTEM:
> - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
> - man->available_caching = TTM_PL_MASK_CACHING;
> - man->default_caching = TTM_PL_FLAG_CACHED;
> - break;
> - case TTM_PL_VRAM:
> - man->func = &ttm_bo_manager_func;
> - man->flags = TTM_MEMTYPE_FLAG_FIXED |
> - TTM_MEMTYPE_FLAG_MAPPABLE;
> - man->available_caching = TTM_PL_FLAG_UNCACHED |
> - TTM_PL_FLAG_WC;
> - man->default_caching = TTM_PL_FLAG_WC;
> - break;
> - default:
> - DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
> - return -EINVAL;
> - }
> - return 0;
> -}
> -
> -static void
> -cirrus_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
> -{
> - struct cirrus_bo *cirrusbo = cirrus_bo(bo);
> -
> - if (!cirrus_ttm_bo_is_cirrus_bo(bo))
> - return;
> -
> - cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_SYSTEM);
> - *pl = cirrusbo->placement;
> -}
> -
> -static int cirrus_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
> -{
> - struct cirrus_bo *cirrusbo = cirrus_bo(bo);
> -
> - return drm_vma_node_verify_access(&cirrusbo->gem.vma_node,
> - filp->private_data);
> -}
> -
> -static int cirrus_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
> - struct ttm_mem_reg *mem)
> -{
> - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
> - struct cirrus_device *cirrus = cirrus_bdev(bdev);
> -
> - mem->bus.addr = NULL;
> - mem->bus.offset = 0;
> - mem->bus.size = mem->num_pages << PAGE_SHIFT;
> - mem->bus.base = 0;
> - mem->bus.is_iomem = false;
> - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
> - return -EINVAL;
> - switch (mem->mem_type) {
> - case TTM_PL_SYSTEM:
> - /* system memory */
> - return 0;
> - case TTM_PL_VRAM:
> - mem->bus.offset = mem->start << PAGE_SHIFT;
> - mem->bus.base = pci_resource_start(cirrus->dev->pdev, 0);
> - mem->bus.is_iomem = true;
> - break;
> - default:
> - return -EINVAL;
> - break;
> - }
> - return 0;
> -}
> -
> -static void cirrus_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
> -{
> -}
> -
> -static void cirrus_ttm_backend_destroy(struct ttm_tt *tt)
> -{
> - ttm_tt_fini(tt);
> - kfree(tt);
> -}
> -
> -static struct ttm_backend_func cirrus_tt_backend_func = {
> - .destroy = &cirrus_ttm_backend_destroy,
> -};
> -
> -
> -static struct ttm_tt *cirrus_ttm_tt_create(struct ttm_buffer_object *bo,
> - uint32_t page_flags)
> -{
> - struct ttm_tt *tt;
> -
> - tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL);
> - if (tt == NULL)
> - return NULL;
> - tt->func = &cirrus_tt_backend_func;
> - if (ttm_tt_init(tt, bo, page_flags)) {
> - kfree(tt);
> - return NULL;
> - }
> - return tt;
> -}
> -
> -struct ttm_bo_driver cirrus_bo_driver = {
> - .ttm_tt_create = cirrus_ttm_tt_create,
> - .init_mem_type = cirrus_bo_init_mem_type,
> - .eviction_valuable = ttm_bo_eviction_valuable,
> - .evict_flags = cirrus_bo_evict_flags,
> - .move = NULL,
> - .verify_access = cirrus_bo_verify_access,
> - .io_mem_reserve = &cirrus_ttm_io_mem_reserve,
> - .io_mem_free = &cirrus_ttm_io_mem_free,
> -};
> -
> -int cirrus_mm_init(struct cirrus_device *cirrus)
> -{
> - int ret;
> - struct drm_device *dev = cirrus->dev;
> - struct ttm_bo_device *bdev = &cirrus->ttm.bdev;
> -
> - ret = ttm_bo_device_init(&cirrus->ttm.bdev,
> - &cirrus_bo_driver,
> - dev->anon_inode->i_mapping,
> - DRM_FILE_PAGE_OFFSET,
> - true);
> - if (ret) {
> - DRM_ERROR("Error initialising bo driver; %d\n", ret);
> - return ret;
> - }
> -
> - ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
> - cirrus->mc.vram_size >> PAGE_SHIFT);
> - if (ret) {
> - DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
> - return ret;
> - }
> -
> - arch_io_reserve_memtype_wc(pci_resource_start(dev->pdev, 0),
> - pci_resource_len(dev->pdev, 0));
> -
> - cirrus->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
> - pci_resource_len(dev->pdev, 0));
> -
> - cirrus->mm_inited = true;
> - return 0;
> -}
> -
> -void cirrus_mm_fini(struct cirrus_device *cirrus)
> -{
> - struct drm_device *dev = cirrus->dev;
> -
> - if (!cirrus->mm_inited)
> - return;
> -
> - ttm_bo_device_release(&cirrus->ttm.bdev);
> -
> - arch_phys_wc_del(cirrus->fb_mtrr);
> - cirrus->fb_mtrr = 0;
> - arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0),
> - pci_resource_len(dev->pdev, 0));
> -}
> -
> -void cirrus_ttm_placement(struct cirrus_bo *bo, int domain)
> -{
> - u32 c = 0;
> - unsigned i;
> - bo->placement.placement = bo->placements;
> - bo->placement.busy_placement = bo->placements;
> - if (domain & TTM_PL_FLAG_VRAM)
> - bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
> - if (domain & TTM_PL_FLAG_SYSTEM)
> - bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
> - if (!c)
> - bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
> - bo->placement.num_placement = c;
> - bo->placement.num_busy_placement = c;
> - for (i = 0; i < c; ++i) {
> - bo->placements[i].fpfn = 0;
> - bo->placements[i].lpfn = 0;
> - }
> -}
> -
> -int cirrus_bo_create(struct drm_device *dev, int size, int align,
> - uint32_t flags, struct cirrus_bo **pcirrusbo)
> -{
> - struct cirrus_device *cirrus = dev->dev_private;
> - struct cirrus_bo *cirrusbo;
> - size_t acc_size;
> - int ret;
> -
> - cirrusbo = kzalloc(sizeof(struct cirrus_bo), GFP_KERNEL);
> - if (!cirrusbo)
> - return -ENOMEM;
> -
> - ret = drm_gem_object_init(dev, &cirrusbo->gem, size);
> - if (ret) {
> - kfree(cirrusbo);
> - return ret;
> - }
> -
> - cirrusbo->bo.bdev = &cirrus->ttm.bdev;
> -
> - cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
> -
> - acc_size = ttm_bo_dma_acc_size(&cirrus->ttm.bdev, size,
> - sizeof(struct cirrus_bo));
> -
> - ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size,
> - ttm_bo_type_device, &cirrusbo->placement,
> - align >> PAGE_SHIFT, false, acc_size,
> - NULL, NULL, cirrus_bo_ttm_destroy);
> - if (ret)
> - return ret;
> -
> - *pcirrusbo = cirrusbo;
> - return 0;
> -}
> -
> -static inline u64 cirrus_bo_gpu_offset(struct cirrus_bo *bo)
> -{
> - return bo->bo.offset;
> -}
> -
> -int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr)
> -{
> - struct ttm_operation_ctx ctx = { false, false };
> - int i, ret;
> -
> - if (bo->pin_count) {
> - bo->pin_count++;
> - if (gpu_addr)
> - *gpu_addr = cirrus_bo_gpu_offset(bo);
> - }
> -
> - cirrus_ttm_placement(bo, pl_flag);
> - for (i = 0; i < bo->placement.num_placement; i++)
> - bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
> - ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
> - if (ret)
> - return ret;
> -
> - bo->pin_count = 1;
> - if (gpu_addr)
> - *gpu_addr = cirrus_bo_gpu_offset(bo);
> - return 0;
> -}
> -
> -int cirrus_bo_push_sysram(struct cirrus_bo *bo)
> -{
> - struct ttm_operation_ctx ctx = { false, false };
> - int i, ret;
> - if (!bo->pin_count) {
> - DRM_ERROR("unpin bad %p\n", bo);
> - return 0;
> - }
> - bo->pin_count--;
> - if (bo->pin_count)
> - return 0;
> -
> - if (bo->kmap.virtual)
> - ttm_bo_kunmap(&bo->kmap);
> -
> - cirrus_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
> - for (i = 0; i < bo->placement.num_placement ; i++)
> - bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
> -
> - ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
> - if (ret) {
> - DRM_ERROR("pushing to VRAM failed\n");
> - return ret;
> - }
> - return 0;
> -}
> -
> -int cirrus_mmap(struct file *filp, struct vm_area_struct *vma)
> -{
> - struct drm_file *file_priv;
> - struct cirrus_device *cirrus;
> -
> - if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
> - return -EINVAL;
> -
> - file_priv = filp->private_data;
> - cirrus = file_priv->minor->dev->dev_private;
> - return ttm_bo_mmap(filp, vma, &cirrus->ttm.bdev);
> -}
> diff --git a/drivers/gpu/drm/cirrus/Kconfig b/drivers/gpu/drm/cirrus/Kconfig
> index fc78c90ee931..dd4f52a0bc1c 100644
> --- a/drivers/gpu/drm/cirrus/Kconfig
> +++ b/drivers/gpu/drm/cirrus/Kconfig
> @@ -2,7 +2,7 @@ config DRM_CIRRUS_QEMU
> tristate "Cirrus driver for QEMU emulated device"
> depends on DRM && PCI && MMU
> select DRM_KMS_HELPER
> - select DRM_TTM
> + select DRM_GEM_SHMEM_HELPER
> help
> This is a KMS driver for emulated cirrus device in qemu.
> It is *NOT* intended for real cirrus devices. This requires
> diff --git a/drivers/gpu/drm/cirrus/Makefile b/drivers/gpu/drm/cirrus/Makefile
> index 919c0a336c97..acf8971d37a1 100644
> --- a/drivers/gpu/drm/cirrus/Makefile
> +++ b/drivers/gpu/drm/cirrus/Makefile
> @@ -1,4 +1 @@
> -cirrus-y := cirrus_main.o cirrus_mode.o \
> - cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o
> -
> obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o
> --
> 2.18.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
^ permalink raw reply
* [PATCH] drm/cirrus: rewrite and modernize driver.
From: Gerd Hoffmann @ 2019-04-03 7:23 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE, Daniel Vetter,
Dave Airlie
Time to kill some bad sample code people are copying from ;)
This is a complete rewrite of the cirrus driver. The cirrus_mode_set()
function is pretty much the only function which is carried over largely
unmodified. Everything else is upside down.
It is a single monster patch. But given that it does some pretty
fundamental changes to the drivers workflow and also reduces the code
size by roughly 70% I think it'll still be alot easier to review than a
longish baby-step patch series.
Changes summary:
- Given the small amout of video memory (4 MB) the cirrus device has
the rewritten driver doesn't try to manage buffers there. Instead
it will blit (memcpy) the active framebuffer to video memory.
- All gem objects are stored in main memory and are manged using the
new shmem helpers. ttm is out.
- Only DRM_FORMAT_RGB565 (depth 16) is supported. The old driver does
that too by default. There was a module parameter which enables 24/32
bpp support and disables higher resolutions (due to cirrus hardware
constrains). That parameter wasn't reimplemented.
- The simple display pipeline is used.
- The generic fbdev emulation is used.
- It's a atomic driver now.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/cirrus/cirrus_drv.h | 251 -----------
drivers/gpu/drm/cirrus/cirrus.c | 602 +++++++++++++++++++++++++
drivers/gpu/drm/cirrus/cirrus_drv.c | 161 -------
drivers/gpu/drm/cirrus/cirrus_fbdev.c | 309 -------------
drivers/gpu/drm/cirrus/cirrus_main.c | 328 --------------
drivers/gpu/drm/cirrus/cirrus_mode.c | 617 --------------------------
drivers/gpu/drm/cirrus/cirrus_ttm.c | 343 --------------
drivers/gpu/drm/cirrus/Kconfig | 2 +-
drivers/gpu/drm/cirrus/Makefile | 3 -
9 files changed, 603 insertions(+), 2013 deletions(-)
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_drv.h
create mode 100644 drivers/gpu/drm/cirrus/cirrus.c
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_drv.c
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_fbdev.c
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_main.c
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_mode.c
delete mode 100644 drivers/gpu/drm/cirrus/cirrus_ttm.c
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
deleted file mode 100644
index 828b150cdb20..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Copyright 2012 Red Hat
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License version 2. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Authors: Matthew Garrett
- * Dave Airlie
- */
-#ifndef __CIRRUS_DRV_H__
-#define __CIRRUS_DRV_H__
-
-#include <video/vga.h>
-
-#include <drm/drm_encoder.h>
-#include <drm/drm_fb_helper.h>
-
-#include <drm/ttm/ttm_bo_api.h>
-#include <drm/ttm/ttm_bo_driver.h>
-#include <drm/ttm/ttm_placement.h>
-#include <drm/ttm/ttm_memory.h>
-#include <drm/ttm/ttm_module.h>
-
-#include <drm/drm_gem.h>
-
-#define DRIVER_AUTHOR "Matthew Garrett"
-
-#define DRIVER_NAME "cirrus"
-#define DRIVER_DESC "qemu Cirrus emulation"
-#define DRIVER_DATE "20110418"
-
-#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 0
-#define DRIVER_PATCHLEVEL 0
-
-#define CIRRUSFB_CONN_LIMIT 1
-
-#define RREG8(reg) ioread8(((void __iomem *)cdev->rmmio) + (reg))
-#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cdev->rmmio) + (reg))
-#define RREG32(reg) ioread32(((void __iomem *)cdev->rmmio) + (reg))
-#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cdev->rmmio) + (reg))
-
-#define SEQ_INDEX 4
-#define SEQ_DATA 5
-
-#define WREG_SEQ(reg, v) \
- do { \
- WREG8(SEQ_INDEX, reg); \
- WREG8(SEQ_DATA, v); \
- } while (0) \
-
-#define CRT_INDEX 0x14
-#define CRT_DATA 0x15
-
-#define WREG_CRT(reg, v) \
- do { \
- WREG8(CRT_INDEX, reg); \
- WREG8(CRT_DATA, v); \
- } while (0) \
-
-#define GFX_INDEX 0xe
-#define GFX_DATA 0xf
-
-#define WREG_GFX(reg, v) \
- do { \
- WREG8(GFX_INDEX, reg); \
- WREG8(GFX_DATA, v); \
- } while (0) \
-
-/*
- * Cirrus has a "hidden" DAC register that can be accessed by writing to
- * the pixel mask register to reset the state, then reading from the register
- * four times. The next write will then pass to the DAC
- */
-#define VGA_DAC_MASK 0x6
-
-#define WREG_HDR(v) \
- do { \
- RREG8(VGA_DAC_MASK); \
- RREG8(VGA_DAC_MASK); \
- RREG8(VGA_DAC_MASK); \
- RREG8(VGA_DAC_MASK); \
- WREG8(VGA_DAC_MASK, v); \
- } while (0) \
-
-
-#define CIRRUS_MAX_FB_HEIGHT 4096
-#define CIRRUS_MAX_FB_WIDTH 4096
-
-#define CIRRUS_DPMS_CLEARED (-1)
-
-#define to_cirrus_crtc(x) container_of(x, struct cirrus_crtc, base)
-#define to_cirrus_encoder(x) container_of(x, struct cirrus_encoder, base)
-
-struct cirrus_crtc {
- struct drm_crtc base;
- int last_dpms;
- bool enabled;
-};
-
-struct cirrus_fbdev;
-struct cirrus_mode_info {
- struct cirrus_crtc *crtc;
- /* pointer to fbdev info structure */
- struct cirrus_fbdev *gfbdev;
-};
-
-struct cirrus_encoder {
- struct drm_encoder base;
- int last_dpms;
-};
-
-struct cirrus_connector {
- struct drm_connector base;
-};
-
-struct cirrus_mc {
- resource_size_t vram_size;
- resource_size_t vram_base;
-};
-
-struct cirrus_device {
- struct drm_device *dev;
- unsigned long flags;
-
- resource_size_t rmmio_base;
- resource_size_t rmmio_size;
- void __iomem *rmmio;
-
- struct cirrus_mc mc;
- struct cirrus_mode_info mode_info;
-
- int num_crtc;
- int fb_mtrr;
-
- struct {
- struct ttm_bo_device bdev;
- } ttm;
- bool mm_inited;
-};
-
-
-struct cirrus_fbdev {
- struct drm_fb_helper helper; /* must be first */
- struct drm_framebuffer *gfb;
- void *sysram;
- int size;
- int x1, y1, x2, y2; /* dirty rect */
- spinlock_t dirty_lock;
-};
-
-struct cirrus_bo {
- struct ttm_buffer_object bo;
- struct ttm_placement placement;
- struct ttm_bo_kmap_obj kmap;
- struct drm_gem_object gem;
- struct ttm_place placements[3];
- int pin_count;
-};
-#define gem_to_cirrus_bo(gobj) container_of((gobj), struct cirrus_bo, gem)
-
-static inline struct cirrus_bo *
-cirrus_bo(struct ttm_buffer_object *bo)
-{
- return container_of(bo, struct cirrus_bo, bo);
-}
-
-
-#define to_cirrus_obj(x) container_of(x, struct cirrus_gem_object, base)
-#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
-
- /* cirrus_main.c */
-int cirrus_device_init(struct cirrus_device *cdev,
- struct drm_device *ddev,
- struct pci_dev *pdev,
- uint32_t flags);
-void cirrus_device_fini(struct cirrus_device *cdev);
-void cirrus_gem_free_object(struct drm_gem_object *obj);
-int cirrus_dumb_mmap_offset(struct drm_file *file,
- struct drm_device *dev,
- uint32_t handle,
- uint64_t *offset);
-int cirrus_gem_create(struct drm_device *dev,
- u32 size, bool iskernel,
- struct drm_gem_object **obj);
-int cirrus_dumb_create(struct drm_file *file,
- struct drm_device *dev,
- struct drm_mode_create_dumb *args);
-
-int cirrus_framebuffer_init(struct drm_device *dev,
- struct drm_framebuffer *gfb,
- const struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object *obj);
-
-bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
- int bpp, int pitch);
-
- /* cirrus_display.c */
-int cirrus_modeset_init(struct cirrus_device *cdev);
-void cirrus_modeset_fini(struct cirrus_device *cdev);
-
- /* cirrus_fbdev.c */
-int cirrus_fbdev_init(struct cirrus_device *cdev);
-void cirrus_fbdev_fini(struct cirrus_device *cdev);
-
-
-
- /* cirrus_irq.c */
-void cirrus_driver_irq_preinstall(struct drm_device *dev);
-int cirrus_driver_irq_postinstall(struct drm_device *dev);
-void cirrus_driver_irq_uninstall(struct drm_device *dev);
-irqreturn_t cirrus_driver_irq_handler(int irq, void *arg);
-
- /* cirrus_kms.c */
-int cirrus_driver_load(struct drm_device *dev, unsigned long flags);
-void cirrus_driver_unload(struct drm_device *dev);
-extern struct drm_ioctl_desc cirrus_ioctls[];
-extern int cirrus_max_ioctl;
-
-int cirrus_mm_init(struct cirrus_device *cirrus);
-void cirrus_mm_fini(struct cirrus_device *cirrus);
-void cirrus_ttm_placement(struct cirrus_bo *bo, int domain);
-int cirrus_bo_create(struct drm_device *dev, int size, int align,
- uint32_t flags, struct cirrus_bo **pcirrusbo);
-int cirrus_mmap(struct file *filp, struct vm_area_struct *vma);
-
-static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
-{
- int ret;
-
- ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL);
- if (ret) {
- if (ret != -ERESTARTSYS && ret != -EBUSY)
- DRM_ERROR("reserve failed %p\n", bo);
- return ret;
- }
- return 0;
-}
-
-static inline void cirrus_bo_unreserve(struct cirrus_bo *bo)
-{
- ttm_bo_unreserve(&bo->bo);
-}
-
-int cirrus_bo_push_sysram(struct cirrus_bo *bo);
-int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr);
-
-extern int cirrus_bpp;
-
-#endif /* __CIRRUS_DRV_H__ */
diff --git a/drivers/gpu/drm/cirrus/cirrus.c b/drivers/gpu/drm/cirrus/cirrus.c
new file mode 100644
index 000000000000..e27bb13fc777
--- /dev/null
+++ b/drivers/gpu/drm/cirrus/cirrus.c
@@ -0,0 +1,602 @@
+/*
+ * Copyright 2012-2019 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License version 2. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Authors: Matthew Garrett
+ * Dave Airlie
+ * Gerd Hoffmann
+ *
+ * Portions of this code derived from cirrusfb.c:
+ * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
+ *
+ * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/console.h>
+
+#include <video/vga.h>
+#include <video/cirrus.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_connector.h>
+
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+
+#define DRIVER_NAME "cirrus"
+#define DRIVER_DESC "qemu cirrus vga"
+#define DRIVER_DATE "2019"
+#define DRIVER_MAJOR 2
+#define DRIVER_MINOR 0
+
+struct cirrus_device {
+ struct drm_device *dev;
+ struct drm_simple_display_pipe pipe;
+ struct drm_connector conn;
+ unsigned int bpp;
+ unsigned int pitch;
+ void __iomem *vram;
+ void __iomem *mmio;
+};
+
+/* ------------------------------------------------------------------ */
+/*
+ * The meat of this driver. The core passes us a mode and we have to program
+ * it. The modesetting here is the bare minimum required to satisfy the qemu
+ * emulation of this hardware, and running this against a real device is
+ * likely to result in an inadequately programmed mode. We've already had
+ * the opportunity to modify the mode, so whatever we receive here should
+ * be something that can be correctly programmed and displayed
+ */
+
+#define RREG8(reg) ioread8(((void __iomem *)cirrus->mmio) + (reg))
+#define WREG8(reg, v) iowrite8(v, ((void __iomem *)cirrus->mmio) + (reg))
+#define RREG32(reg) ioread32(((void __iomem *)cirrus->mmio) + (reg))
+#define WREG32(reg, v) iowrite32(v, ((void __iomem *)cirrus->mmio) + (reg))
+
+#define SEQ_INDEX 4
+#define SEQ_DATA 5
+
+#define WREG_SEQ(reg, v) \
+ do { \
+ WREG8(SEQ_INDEX, reg); \
+ WREG8(SEQ_DATA, v); \
+ } while (0) \
+
+#define CRT_INDEX 0x14
+#define CRT_DATA 0x15
+
+#define WREG_CRT(reg, v) \
+ do { \
+ WREG8(CRT_INDEX, reg); \
+ WREG8(CRT_DATA, v); \
+ } while (0) \
+
+#define GFX_INDEX 0xe
+#define GFX_DATA 0xf
+
+#define WREG_GFX(reg, v) \
+ do { \
+ WREG8(GFX_INDEX, reg); \
+ WREG8(GFX_DATA, v); \
+ } while (0) \
+
+#define VGA_DAC_MASK 0x6
+
+#define WREG_HDR(v) \
+ do { \
+ RREG8(VGA_DAC_MASK); \
+ RREG8(VGA_DAC_MASK); \
+ RREG8(VGA_DAC_MASK); \
+ RREG8(VGA_DAC_MASK); \
+ WREG8(VGA_DAC_MASK, v); \
+ } while (0) \
+
+
+static int cirrus_mode_set(struct cirrus_device *cirrus,
+ struct drm_crtc_state *crtc_state)
+{
+ struct drm_display_mode *mode = &crtc_state->mode;
+ int hsyncstart, hsyncend, htotal, hdispend;
+ int vtotal, vdispend;
+ int tmp;
+ int sr07 = 0, hdr = 0;
+
+ htotal = mode->htotal / 8;
+ hsyncend = mode->hsync_end / 8;
+ hsyncstart = mode->hsync_start / 8;
+ hdispend = mode->hdisplay / 8;
+
+ vtotal = mode->vtotal;
+ vdispend = mode->vdisplay;
+
+ vdispend -= 1;
+ vtotal -= 2;
+
+ htotal -= 5;
+ hdispend -= 1;
+ hsyncstart += 1;
+ hsyncend += 1;
+
+ WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20);
+ WREG_CRT(VGA_CRTC_H_TOTAL, htotal);
+ WREG_CRT(VGA_CRTC_H_DISP, hdispend);
+ WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart);
+ WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend);
+ WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff);
+ WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff);
+
+ tmp = 0x40;
+ if ((vdispend + 1) & 512)
+ tmp |= 0x20;
+ WREG_CRT(VGA_CRTC_MAX_SCAN, tmp);
+
+ /*
+ * Overflow bits for values that don't fit in the standard registers
+ */
+ tmp = 16;
+ if (vtotal & 256)
+ tmp |= 1;
+ if (vdispend & 256)
+ tmp |= 2;
+ if ((vdispend + 1) & 256)
+ tmp |= 8;
+ if (vtotal & 512)
+ tmp |= 32;
+ if (vdispend & 512)
+ tmp |= 64;
+ WREG_CRT(VGA_CRTC_OVERFLOW, tmp);
+
+ tmp = 0;
+
+ /* More overflow bits */
+
+ if ((htotal + 5) & 64)
+ tmp |= 16;
+ if ((htotal + 5) & 128)
+ tmp |= 32;
+ if (vtotal & 256)
+ tmp |= 64;
+ if (vtotal & 512)
+ tmp |= 128;
+
+ WREG_CRT(CL_CRT1A, tmp);
+
+ /* Disable Hercules/CGA compatibility */
+ WREG_CRT(VGA_CRTC_MODE, 0x03);
+
+ WREG8(SEQ_INDEX, 0x7);
+ sr07 = RREG8(SEQ_DATA);
+ sr07 &= 0xe0;
+ hdr = 0;
+
+ cirrus->bpp = cirrus->dev->mode_config.preferred_depth;
+ switch (cirrus->bpp) {
+ case 8:
+ sr07 |= 0x11;
+ break;
+ case 16:
+ sr07 |= 0x17;
+ hdr = 0xc1;
+ break;
+ case 24:
+ sr07 |= 0x15;
+ hdr = 0xc5;
+ break;
+ case 32:
+ sr07 |= 0x19;
+ hdr = 0xc5;
+ break;
+ default:
+ return -1;
+ }
+
+ WREG_SEQ(0x7, sr07);
+
+ /* Program the pitch */
+ cirrus->pitch = mode->hdisplay * cirrus->bpp / 8;
+ tmp = cirrus->pitch / 8;
+ WREG_CRT(VGA_CRTC_OFFSET, tmp);
+
+ /* Enable extended blanking and pitch bits, and enable full memory */
+ tmp = 0x22;
+ tmp |= (cirrus->pitch >> 7) & 0x10;
+ tmp |= (cirrus->pitch >> 6) & 0x40;
+ WREG_CRT(0x1b, tmp);
+
+ /* Enable high-colour modes */
+ WREG_GFX(VGA_GFX_MODE, 0x40);
+
+ /* And set graphics mode */
+ WREG_GFX(VGA_GFX_MISC, 0x01);
+
+ WREG_HDR(hdr);
+ /* cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0); */
+
+ /* Unblank (needed on S3 resume, vgabios doesn't do it then) */
+ outb(0x20, 0x3c0);
+ return 0;
+}
+
+static int cirrus_fb_blit_clips(struct drm_framebuffer *fb,
+ struct drm_clip_rect *clips,
+ unsigned int num_clips)
+{
+ struct cirrus_device *cirrus = fb->dev->dev_private;
+ unsigned i, y, xoff, xlen, src, dst;
+ void *vmap;
+
+ vmap = drm_gem_shmem_vmap(fb->obj[0]);
+ if (!vmap)
+ return -ENOMEM;
+
+ for (i = 0; i < num_clips; i++) {
+ xoff = clips[i].x1 * cirrus->bpp / 8;
+ xlen = (clips[i].x2 - clips[i].x1) * cirrus->bpp / 8;
+ for (y = clips[i].y1; y < clips[i].y2; y++) {
+ src = xoff + y * fb->pitches[0];
+ dst = xoff + y * cirrus->pitch;
+ memcpy_toio(cirrus->vram + dst, vmap + src, xlen);
+ }
+ }
+
+ drm_gem_shmem_vunmap(fb->obj[0], vmap);
+ return 0;
+}
+
+static int cirrus_fb_blit_rect(struct drm_framebuffer *fb,
+ struct drm_rect *rect)
+{
+ struct drm_clip_rect clip_rect = {
+ .x1 = rect->x1,
+ .x2 = rect->x2,
+ .y1 = rect->y1,
+ .y2 = rect->y2,
+ };
+ return cirrus_fb_blit_clips(fb, &clip_rect, 1);
+}
+
+static int cirrus_fb_blit_fullscreen(struct drm_framebuffer *fb)
+{
+ struct drm_clip_rect fullscreen = {
+ .x1 = 0,
+ .x2 = fb->width,
+ .y1 = 0,
+ .y2 = fb->height,
+ };
+ return cirrus_fb_blit_clips(fb, &fullscreen, 1);
+}
+
+static int cirrus_check_size(int width, int height)
+{
+ static const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
+ static const int max_size = 4 * 1024 * 1024; /* 4 MB */
+ int bytes_pp = 2; /* depth 16 */
+
+ if (width * bytes_pp > max_pitch)
+ return -EINVAL;
+ if (width * height * bytes_pp > max_size)
+ return -EINVAL;
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* cirrus connector */
+
+static int cirrus_conn_get_modes(struct drm_connector *conn)
+{
+ int count;
+
+ count = drm_add_modes_noedid(conn,
+ conn->dev->mode_config.max_width,
+ conn->dev->mode_config.max_height);
+ drm_set_preferred_mode(conn, 1024, 768);
+ return count;
+}
+
+static const struct drm_connector_helper_funcs cirrus_conn_helper_funcs = {
+ .get_modes = cirrus_conn_get_modes,
+};
+
+static const struct drm_connector_funcs cirrus_conn_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = drm_connector_cleanup,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cirrus_conn_init(struct cirrus_device *cirrus)
+{
+ drm_connector_helper_add(&cirrus->conn, &cirrus_conn_helper_funcs);
+ return drm_connector_init(cirrus->dev, &cirrus->conn,
+ &cirrus_conn_funcs, DRM_MODE_CONNECTOR_VGA);
+
+}
+
+/* ------------------------------------------------------------------ */
+/* cirrus (simple) display pipe */
+
+enum drm_mode_status cirrus_pipe_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ if (cirrus_check_size(mode->hdisplay, mode->vdisplay) < 0)
+ return MODE_BAD;
+ return MODE_OK;
+}
+
+int cirrus_pipe_check(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *plane_state,
+ struct drm_crtc_state *crtc_state)
+{
+ struct drm_framebuffer *fb = plane_state->fb;
+
+ if (!fb)
+ return 0;
+ return cirrus_check_size(fb->width, fb->height);
+}
+
+void cirrus_pipe_enable(struct drm_simple_display_pipe *pipe,
+ struct drm_crtc_state *crtc_state,
+ struct drm_plane_state *plane_state)
+{
+ struct cirrus_device *cirrus = pipe->crtc.dev->dev_private;
+
+ cirrus_mode_set(cirrus, crtc_state);
+ cirrus_fb_blit_fullscreen(plane_state->fb);
+}
+
+void cirrus_pipe_update(struct drm_simple_display_pipe *pipe,
+ struct drm_plane_state *old_state)
+{
+ struct drm_plane_state *state = pipe->plane.state;
+ struct drm_crtc *crtc = &pipe->crtc;
+ struct drm_rect rect;
+
+ if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+ cirrus_fb_blit_rect(pipe->plane.state->fb, &rect);
+
+ if (crtc->state->event) {
+ spin_lock_irq(&crtc->dev->event_lock);
+ drm_crtc_send_vblank_event(crtc, crtc->state->event);
+ spin_unlock_irq(&crtc->dev->event_lock);
+ crtc->state->event = NULL;
+ }
+}
+
+static const struct drm_simple_display_pipe_funcs cirrus_pipe_funcs = {
+ .mode_valid = cirrus_pipe_mode_valid,
+ .check = cirrus_pipe_check,
+ .enable = cirrus_pipe_enable,
+ .update = cirrus_pipe_update,
+};
+
+static const uint32_t cirrus_formats[] = {
+ DRM_FORMAT_RGB565,
+};
+
+static int cirrus_pipe_init(struct cirrus_device *cirrus)
+{
+ return drm_simple_display_pipe_init(cirrus->dev,
+ &cirrus->pipe,
+ &cirrus_pipe_funcs,
+ cirrus_formats,
+ ARRAY_SIZE(cirrus_formats),
+ NULL,
+ &cirrus->conn);
+}
+
+/* ------------------------------------------------------------------ */
+/* cirrus framebuffers & mode config */
+
+static int cirrus_fb_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int flags, unsigned int color,
+ struct drm_clip_rect *clips,
+ unsigned int num_clips)
+{
+ struct cirrus_device *cirrus = fb->dev->dev_private;
+
+ if (cirrus->pipe.plane.state->fb != fb)
+ return 0;
+
+ if (num_clips)
+ cirrus_fb_blit_clips(fb, clips, num_clips);
+ else
+ cirrus_fb_blit_fullscreen(fb);
+ return 0;
+}
+
+static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
+ .destroy = drm_gem_fb_destroy,
+ .create_handle = drm_gem_fb_create_handle,
+ .dirty = cirrus_fb_dirty,
+};
+
+static struct drm_framebuffer*
+cirrus_fb_create(struct drm_device *dev, struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ if (mode_cmd->pixel_format != DRM_FORMAT_RGB565)
+ return ERR_PTR(-EINVAL);
+ if (cirrus_check_size(mode_cmd->width, mode_cmd->height) < 0)
+ return ERR_PTR(-EINVAL);
+ return drm_gem_fb_create_with_funcs(dev, file_priv, mode_cmd,
+ &cirrus_fb_funcs);
+}
+
+static const struct drm_mode_config_funcs cirrus_mode_config_funcs = {
+ .fb_create = cirrus_fb_create,
+ .atomic_check = drm_atomic_helper_check,
+ .atomic_commit = drm_atomic_helper_commit,
+};
+
+static void cirrus_mode_config_init(struct cirrus_device *cirrus)
+{
+ struct drm_device *dev = cirrus->dev;
+
+ drm_mode_config_init(dev);
+ dev->mode_config.min_width = 0;
+ dev->mode_config.min_height = 0;
+ dev->mode_config.max_width = 1600;
+ dev->mode_config.max_height = 1024;
+ dev->mode_config.preferred_depth = 16;
+ dev->mode_config.prefer_shadow = 0;
+ dev->mode_config.funcs = &cirrus_mode_config_funcs;
+}
+
+/* ------------------------------------------------------------------ */
+
+DEFINE_DRM_GEM_SHMEM_FOPS(cirrus_fops);
+
+static struct drm_driver cirrus_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_PRIME,
+
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+
+ .fops = &cirrus_fops,
+ DRM_GEM_SHMEM_DRIVER_OPS,
+};
+
+static int cirrus_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct drm_device *dev;
+ struct cirrus_device *cirrus;
+ int ret;
+
+ ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
+ if (ret)
+ return ret;
+
+ dev = drm_dev_alloc(&cirrus_driver, &pdev->dev);
+ if (IS_ERR(dev))
+ return PTR_ERR(dev);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ goto err_free_dev;
+
+ ret = pci_request_regions(pdev, DRIVER_NAME);
+ if (ret)
+ goto err_free_dev;
+
+ ret = -ENOMEM;
+ cirrus = kzalloc(sizeof(*cirrus), GFP_KERNEL);
+ if (cirrus == NULL)
+ goto err_pci_release;
+ dev->dev_private = cirrus;
+ cirrus->dev = dev;
+
+ cirrus->vram = ioremap(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (cirrus->vram == NULL)
+ goto err_free_cirrus;
+
+ cirrus->mmio = ioremap(pci_resource_start(pdev, 1),
+ pci_resource_len(pdev, 1));
+ if (cirrus->mmio == NULL)
+ goto err_unmap_vram;
+
+ cirrus_mode_config_init(cirrus);
+
+ ret = cirrus_conn_init(cirrus);
+ if (ret < 0)
+ goto err_cleanup;
+
+ ret = cirrus_pipe_init(cirrus);
+ if (ret < 0)
+ goto err_cleanup;
+
+ drm_mode_config_reset(dev);
+
+ dev->pdev = pdev;
+ pci_set_drvdata(pdev, dev);
+ ret = drm_dev_register(dev, 0);
+ if (ret)
+ goto err_cleanup;
+
+ drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth);
+ return 0;
+
+err_cleanup:
+ drm_mode_config_cleanup(dev);
+ iounmap(cirrus->mmio);
+err_unmap_vram:
+ iounmap(cirrus->vram);
+err_free_cirrus:
+ kfree(cirrus);
+err_pci_release:
+ pci_release_regions(pdev);
+err_free_dev:
+ drm_dev_put(dev);
+ return ret;
+}
+
+static void cirrus_pci_remove(struct pci_dev *pdev)
+{
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ struct cirrus_device *cirrus = dev->dev_private;
+
+ drm_dev_unregister(dev);
+ drm_mode_config_cleanup(dev);
+ iounmap(cirrus->mmio);
+ iounmap(cirrus->vram);
+ kfree(cirrus);
+ pci_release_regions(pdev);
+ drm_dev_put(dev);
+}
+
+/* only bind to the cirrus chip in qemu */
+static const struct pci_device_id pciidlist[] = {
+ { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
+ PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
+ 0, 0, 0 },
+ { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
+ PCI_VENDOR_ID_XEN, 0x0001,
+ 0, 0, 0 },
+ { /* end if list */}
+};
+static struct pci_driver cirrus_pci_driver = {
+ .name = DRIVER_NAME,
+ .id_table = pciidlist,
+ .probe = cirrus_pci_probe,
+ .remove = cirrus_pci_remove,
+};
+
+static int __init cirrus_init(void)
+{
+ if (vgacon_text_force())
+ return -EINVAL;
+ return pci_register_driver(&cirrus_pci_driver);
+}
+
+static void __exit cirrus_exit(void)
+{
+ pci_unregister_driver(&cirrus_pci_driver);
+}
+
+module_init(cirrus_init);
+module_exit(cirrus_exit);
+
+MODULE_DEVICE_TABLE(pci, pciidlist);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c
deleted file mode 100644
index 8ec880f3a322..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_drv.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright 2012 Red Hat <mjg@redhat.com>
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License version 2. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Authors: Matthew Garrett
- * Dave Airlie
- */
-#include <linux/module.h>
-#include <linux/console.h>
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_probe_helper.h>
-
-#include "cirrus_drv.h"
-
-int cirrus_modeset = -1;
-int cirrus_bpp = 16;
-
-MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
-module_param_named(modeset, cirrus_modeset, int, 0400);
-MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:16)");
-module_param_named(bpp, cirrus_bpp, int, 0400);
-
-/*
- * This is the generic driver code. This binds the driver to the drm core,
- * which then performs further device association and calls our graphics init
- * functions
- */
-
-static struct drm_driver driver;
-
-/* only bind to the cirrus chip in qemu */
-static const struct pci_device_id pciidlist[] = {
- { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446,
- PCI_SUBVENDOR_ID_REDHAT_QUMRANET, PCI_SUBDEVICE_ID_QEMU,
- 0, 0, 0 },
- { PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CIRRUS_5446, PCI_VENDOR_ID_XEN,
- 0x0001, 0, 0, 0 },
- {0,}
-};
-
-
-static int cirrus_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- int ret;
-
- ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, 0, "cirrusdrmfb");
- if (ret)
- return ret;
-
- return drm_get_pci_dev(pdev, ent, &driver);
-}
-
-static void cirrus_pci_remove(struct pci_dev *pdev)
-{
- struct drm_device *dev = pci_get_drvdata(pdev);
-
- drm_put_dev(dev);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int cirrus_pm_suspend(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct drm_device *drm_dev = pci_get_drvdata(pdev);
- struct cirrus_device *cdev = drm_dev->dev_private;
-
- drm_kms_helper_poll_disable(drm_dev);
-
- if (cdev->mode_info.gfbdev) {
- console_lock();
- drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 1);
- console_unlock();
- }
-
- return 0;
-}
-
-static int cirrus_pm_resume(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct drm_device *drm_dev = pci_get_drvdata(pdev);
- struct cirrus_device *cdev = drm_dev->dev_private;
-
- drm_helper_resume_force_mode(drm_dev);
-
- if (cdev->mode_info.gfbdev) {
- console_lock();
- drm_fb_helper_set_suspend(&cdev->mode_info.gfbdev->helper, 0);
- console_unlock();
- }
-
- drm_kms_helper_poll_enable(drm_dev);
- return 0;
-}
-#endif
-
-static const struct file_operations cirrus_driver_fops = {
- .owner = THIS_MODULE,
- .open = drm_open,
- .release = drm_release,
- .unlocked_ioctl = drm_ioctl,
- .mmap = cirrus_mmap,
- .poll = drm_poll,
- .compat_ioctl = drm_compat_ioctl,
-};
-static struct drm_driver driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM,
- .load = cirrus_driver_load,
- .unload = cirrus_driver_unload,
- .fops = &cirrus_driver_fops,
- .name = DRIVER_NAME,
- .desc = DRIVER_DESC,
- .date = DRIVER_DATE,
- .major = DRIVER_MAJOR,
- .minor = DRIVER_MINOR,
- .patchlevel = DRIVER_PATCHLEVEL,
- .gem_free_object_unlocked = cirrus_gem_free_object,
- .dumb_create = cirrus_dumb_create,
- .dumb_map_offset = cirrus_dumb_mmap_offset,
-};
-
-static const struct dev_pm_ops cirrus_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(cirrus_pm_suspend,
- cirrus_pm_resume)
-};
-
-static struct pci_driver cirrus_pci_driver = {
- .name = DRIVER_NAME,
- .id_table = pciidlist,
- .probe = cirrus_pci_probe,
- .remove = cirrus_pci_remove,
- .driver.pm = &cirrus_pm_ops,
-};
-
-static int __init cirrus_init(void)
-{
- if (vgacon_text_force() && cirrus_modeset == -1)
- return -EINVAL;
-
- if (cirrus_modeset == 0)
- return -EINVAL;
- return pci_register_driver(&cirrus_pci_driver);
-}
-
-static void __exit cirrus_exit(void)
-{
- pci_unregister_driver(&cirrus_pci_driver);
-}
-
-module_init(cirrus_init);
-module_exit(cirrus_exit);
-
-MODULE_DEVICE_TABLE(pci, pciidlist);
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
deleted file mode 100644
index 2e6128069fc3..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * Copyright 2012 Red Hat
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License version 2. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Authors: Matthew Garrett
- * Dave Airlie
- */
-#include <linux/module.h>
-#include <drm/drmP.h>
-#include <drm/drm_util.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "cirrus_drv.h"
-
-static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
- int x, int y, int width, int height)
-{
- int i;
- struct drm_gem_object *obj;
- struct cirrus_bo *bo;
- int src_offset, dst_offset;
- int bpp = afbdev->gfb->format->cpp[0];
- int ret = -EBUSY;
- bool unmap = false;
- bool store_for_later = false;
- int x2, y2;
- unsigned long flags;
-
- obj = afbdev->gfb->obj[0];
- bo = gem_to_cirrus_bo(obj);
-
- /*
- * try and reserve the BO, if we fail with busy
- * then the BO is being moved and we should
- * store up the damage until later.
- */
- if (drm_can_sleep())
- ret = cirrus_bo_reserve(bo, true);
- if (ret) {
- if (ret != -EBUSY)
- return;
- store_for_later = true;
- }
-
- x2 = x + width - 1;
- y2 = y + height - 1;
- spin_lock_irqsave(&afbdev->dirty_lock, flags);
-
- if (afbdev->y1 < y)
- y = afbdev->y1;
- if (afbdev->y2 > y2)
- y2 = afbdev->y2;
- if (afbdev->x1 < x)
- x = afbdev->x1;
- if (afbdev->x2 > x2)
- x2 = afbdev->x2;
-
- if (store_for_later) {
- afbdev->x1 = x;
- afbdev->x2 = x2;
- afbdev->y1 = y;
- afbdev->y2 = y2;
- spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
- return;
- }
-
- afbdev->x1 = afbdev->y1 = INT_MAX;
- afbdev->x2 = afbdev->y2 = 0;
- spin_unlock_irqrestore(&afbdev->dirty_lock, flags);
-
- if (!bo->kmap.virtual) {
- ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
- if (ret) {
- DRM_ERROR("failed to kmap fb updates\n");
- cirrus_bo_unreserve(bo);
- return;
- }
- unmap = true;
- }
- for (i = y; i < y + height; i++) {
- /* assume equal stride for now */
- src_offset = dst_offset = i * afbdev->gfb->pitches[0] + (x * bpp);
- memcpy_toio(bo->kmap.virtual + src_offset, afbdev->sysram + src_offset, width * bpp);
-
- }
- if (unmap)
- ttm_bo_kunmap(&bo->kmap);
-
- cirrus_bo_unreserve(bo);
-}
-
-static void cirrus_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect)
-{
- struct cirrus_fbdev *afbdev = info->par;
- drm_fb_helper_sys_fillrect(info, rect);
- cirrus_dirty_update(afbdev, rect->dx, rect->dy, rect->width,
- rect->height);
-}
-
-static void cirrus_copyarea(struct fb_info *info,
- const struct fb_copyarea *area)
-{
- struct cirrus_fbdev *afbdev = info->par;
- drm_fb_helper_sys_copyarea(info, area);
- cirrus_dirty_update(afbdev, area->dx, area->dy, area->width,
- area->height);
-}
-
-static void cirrus_imageblit(struct fb_info *info,
- const struct fb_image *image)
-{
- struct cirrus_fbdev *afbdev = info->par;
- drm_fb_helper_sys_imageblit(info, image);
- cirrus_dirty_update(afbdev, image->dx, image->dy, image->width,
- image->height);
-}
-
-
-static struct fb_ops cirrusfb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = drm_fb_helper_check_var,
- .fb_set_par = drm_fb_helper_set_par,
- .fb_fillrect = cirrus_fillrect,
- .fb_copyarea = cirrus_copyarea,
- .fb_imageblit = cirrus_imageblit,
- .fb_pan_display = drm_fb_helper_pan_display,
- .fb_blank = drm_fb_helper_blank,
- .fb_setcmap = drm_fb_helper_setcmap,
-};
-
-static int cirrusfb_create_object(struct cirrus_fbdev *afbdev,
- const struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object **gobj_p)
-{
- struct drm_device *dev = afbdev->helper.dev;
- struct cirrus_device *cdev = dev->dev_private;
- u32 bpp;
- u32 size;
- struct drm_gem_object *gobj;
- int ret = 0;
-
- bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
-
- if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
- bpp, mode_cmd->pitches[0]))
- return -EINVAL;
-
- size = mode_cmd->pitches[0] * mode_cmd->height;
- ret = cirrus_gem_create(dev, size, true, &gobj);
- if (ret)
- return ret;
-
- *gobj_p = gobj;
- return ret;
-}
-
-static int cirrusfb_create(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
-{
- struct cirrus_fbdev *gfbdev =
- container_of(helper, struct cirrus_fbdev, helper);
- struct cirrus_device *cdev = gfbdev->helper.dev->dev_private;
- struct fb_info *info;
- struct drm_framebuffer *fb;
- struct drm_mode_fb_cmd2 mode_cmd;
- void *sysram;
- struct drm_gem_object *gobj = NULL;
- int size, ret;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
- mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
- size = mode_cmd.pitches[0] * mode_cmd.height;
-
- ret = cirrusfb_create_object(gfbdev, &mode_cmd, &gobj);
- if (ret) {
- DRM_ERROR("failed to create fbcon backing object %d\n", ret);
- return ret;
- }
-
- sysram = vmalloc(size);
- if (!sysram)
- return -ENOMEM;
-
- info = drm_fb_helper_alloc_fbi(helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_vfree;
- }
-
- fb = kzalloc(sizeof(*fb), GFP_KERNEL);
- if (!fb) {
- ret = -ENOMEM;
- goto err_drm_gem_object_put_unlocked;
- }
-
- ret = cirrus_framebuffer_init(cdev->dev, fb, &mode_cmd, gobj);
- if (ret)
- goto err_kfree;
-
- gfbdev->sysram = sysram;
- gfbdev->size = size;
- gfbdev->gfb = fb;
-
- /* setup helper */
- gfbdev->helper.fb = fb;
-
- info->fbops = &cirrusfb_ops;
-
- drm_fb_helper_fill_info(info, &gfbdev->helper, sizes);
-
- /* setup aperture base/size for vesafb takeover */
- info->apertures->ranges[0].base = cdev->dev->mode_config.fb_base;
- info->apertures->ranges[0].size = cdev->mc.vram_size;
-
- info->fix.smem_start = cdev->dev->mode_config.fb_base;
- info->fix.smem_len = cdev->mc.vram_size;
-
- info->screen_base = sysram;
- info->screen_size = size;
-
- info->fix.mmio_start = 0;
- info->fix.mmio_len = 0;
-
- DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start);
- DRM_INFO("vram aper at 0x%lX\n", (unsigned long)info->fix.smem_start);
- DRM_INFO("size %lu\n", (unsigned long)info->fix.smem_len);
- DRM_INFO("fb depth is %d\n", fb->format->depth);
- DRM_INFO(" pitch is %d\n", fb->pitches[0]);
-
- return 0;
-
-err_kfree:
- kfree(fb);
-err_drm_gem_object_put_unlocked:
- drm_gem_object_put_unlocked(gobj);
-err_vfree:
- vfree(sysram);
- return ret;
-}
-
-static int cirrus_fbdev_destroy(struct drm_device *dev,
- struct cirrus_fbdev *gfbdev)
-{
- struct drm_framebuffer *gfb = gfbdev->gfb;
-
- drm_helper_force_disable_all(dev);
-
- drm_fb_helper_unregister_fbi(&gfbdev->helper);
-
- vfree(gfbdev->sysram);
- drm_fb_helper_fini(&gfbdev->helper);
- if (gfb)
- drm_framebuffer_put(gfb);
-
- return 0;
-}
-
-static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
- .fb_probe = cirrusfb_create,
-};
-
-int cirrus_fbdev_init(struct cirrus_device *cdev)
-{
- struct cirrus_fbdev *gfbdev;
- int ret;
-
- /*bpp_sel = 8;*/
- gfbdev = kzalloc(sizeof(struct cirrus_fbdev), GFP_KERNEL);
- if (!gfbdev)
- return -ENOMEM;
-
- cdev->mode_info.gfbdev = gfbdev;
- spin_lock_init(&gfbdev->dirty_lock);
-
- drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
- &cirrus_fb_helper_funcs);
-
- ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
- CIRRUSFB_CONN_LIMIT);
- if (ret)
- return ret;
-
- ret = drm_fb_helper_single_add_all_connectors(&gfbdev->helper);
- if (ret)
- return ret;
-
- /* disable all the possible outputs/crtcs before entering KMS mode */
- drm_helper_disable_unused_functions(cdev->dev);
-
- return drm_fb_helper_initial_config(&gfbdev->helper, cirrus_bpp);
-}
-
-void cirrus_fbdev_fini(struct cirrus_device *cdev)
-{
- if (!cdev->mode_info.gfbdev)
- return;
-
- cirrus_fbdev_destroy(cdev->dev, cdev->mode_info.gfbdev);
- kfree(cdev->mode_info.gfbdev);
- cdev->mode_info.gfbdev = NULL;
-}
diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c
deleted file mode 100644
index 57f8fe6d020b..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_main.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2012 Red Hat
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License version 2. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Authors: Matthew Garrett
- * Dave Airlie
- */
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-
-#include "cirrus_drv.h"
-
-static const struct drm_framebuffer_funcs cirrus_fb_funcs = {
- .create_handle = drm_gem_fb_create_handle,
- .destroy = drm_gem_fb_destroy,
-};
-
-int cirrus_framebuffer_init(struct drm_device *dev,
- struct drm_framebuffer *gfb,
- const struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object *obj)
-{
- int ret;
-
- drm_helper_mode_fill_fb_struct(dev, gfb, mode_cmd);
- gfb->obj[0] = obj;
- ret = drm_framebuffer_init(dev, gfb, &cirrus_fb_funcs);
- if (ret) {
- DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static struct drm_framebuffer *
-cirrus_user_framebuffer_create(struct drm_device *dev,
- struct drm_file *filp,
- const struct drm_mode_fb_cmd2 *mode_cmd)
-{
- struct cirrus_device *cdev = dev->dev_private;
- struct drm_gem_object *obj;
- struct drm_framebuffer *fb;
- u32 bpp;
- int ret;
-
- bpp = drm_format_plane_cpp(mode_cmd->pixel_format, 0) * 8;
-
- if (!cirrus_check_framebuffer(cdev, mode_cmd->width, mode_cmd->height,
- bpp, mode_cmd->pitches[0]))
- return ERR_PTR(-EINVAL);
-
- obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]);
- if (obj == NULL)
- return ERR_PTR(-ENOENT);
-
- fb = kzalloc(sizeof(*fb), GFP_KERNEL);
- if (!fb) {
- drm_gem_object_put_unlocked(obj);
- return ERR_PTR(-ENOMEM);
- }
-
- ret = cirrus_framebuffer_init(dev, fb, mode_cmd, obj);
- if (ret) {
- drm_gem_object_put_unlocked(obj);
- kfree(fb);
- return ERR_PTR(ret);
- }
- return fb;
-}
-
-static const struct drm_mode_config_funcs cirrus_mode_funcs = {
- .fb_create = cirrus_user_framebuffer_create,
-};
-
-/* Unmap the framebuffer from the core and release the memory */
-static void cirrus_vram_fini(struct cirrus_device *cdev)
-{
- iounmap(cdev->rmmio);
- cdev->rmmio = NULL;
- if (cdev->mc.vram_base)
- release_mem_region(cdev->mc.vram_base, cdev->mc.vram_size);
-}
-
-/* Map the framebuffer from the card and configure the core */
-static int cirrus_vram_init(struct cirrus_device *cdev)
-{
- /* BAR 0 is VRAM */
- cdev->mc.vram_base = pci_resource_start(cdev->dev->pdev, 0);
- cdev->mc.vram_size = pci_resource_len(cdev->dev->pdev, 0);
-
- if (!request_mem_region(cdev->mc.vram_base, cdev->mc.vram_size,
- "cirrusdrmfb_vram")) {
- DRM_ERROR("can't reserve VRAM\n");
- return -ENXIO;
- }
-
- return 0;
-}
-
-/*
- * Our emulated hardware has two sets of memory. One is video RAM and can
- * simply be used as a linear framebuffer - the other provides mmio access
- * to the display registers. The latter can also be accessed via IO port
- * access, but we map the range and use mmio to program them instead
- */
-
-int cirrus_device_init(struct cirrus_device *cdev,
- struct drm_device *ddev,
- struct pci_dev *pdev, uint32_t flags)
-{
- int ret;
-
- cdev->dev = ddev;
- cdev->flags = flags;
-
- /* Hardcode the number of CRTCs to 1 */
- cdev->num_crtc = 1;
-
- /* BAR 0 is the framebuffer, BAR 1 contains registers */
- cdev->rmmio_base = pci_resource_start(cdev->dev->pdev, 1);
- cdev->rmmio_size = pci_resource_len(cdev->dev->pdev, 1);
-
- if (!request_mem_region(cdev->rmmio_base, cdev->rmmio_size,
- "cirrusdrmfb_mmio")) {
- DRM_ERROR("can't reserve mmio registers\n");
- return -ENOMEM;
- }
-
- cdev->rmmio = ioremap(cdev->rmmio_base, cdev->rmmio_size);
-
- if (cdev->rmmio == NULL)
- return -ENOMEM;
-
- ret = cirrus_vram_init(cdev);
- if (ret) {
- release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
- return ret;
- }
-
- return 0;
-}
-
-void cirrus_device_fini(struct cirrus_device *cdev)
-{
- release_mem_region(cdev->rmmio_base, cdev->rmmio_size);
- cirrus_vram_fini(cdev);
-}
-
-/*
- * Functions here will be called by the core once it's bound the driver to
- * a PCI device
- */
-
-int cirrus_driver_load(struct drm_device *dev, unsigned long flags)
-{
- struct cirrus_device *cdev;
- int r;
-
- cdev = kzalloc(sizeof(struct cirrus_device), GFP_KERNEL);
- if (cdev == NULL)
- return -ENOMEM;
- dev->dev_private = (void *)cdev;
-
- r = cirrus_device_init(cdev, dev, dev->pdev, flags);
- if (r) {
- dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
- goto out;
- }
-
- r = cirrus_mm_init(cdev);
- if (r) {
- dev_err(&dev->pdev->dev, "fatal err on mm init\n");
- goto out;
- }
-
- /*
- * cirrus_modeset_init() is initializing/registering the emulated fbdev
- * and DRM internals can access/test some of the fields in
- * mode_config->funcs as part of the fbdev registration process.
- * Make sure dev->mode_config.funcs is properly set to avoid
- * dereferencing a NULL pointer.
- * FIXME: mode_config.funcs assignment should probably be done in
- * cirrus_modeset_init() (that's a common pattern seen in other DRM
- * drivers).
- */
- dev->mode_config.funcs = &cirrus_mode_funcs;
- r = cirrus_modeset_init(cdev);
- if (r) {
- dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
- goto out;
- }
-
- return 0;
-out:
- cirrus_driver_unload(dev);
- return r;
-}
-
-void cirrus_driver_unload(struct drm_device *dev)
-{
- struct cirrus_device *cdev = dev->dev_private;
-
- if (cdev == NULL)
- return;
- cirrus_modeset_fini(cdev);
- cirrus_mm_fini(cdev);
- cirrus_device_fini(cdev);
- kfree(cdev);
- dev->dev_private = NULL;
-}
-
-int cirrus_gem_create(struct drm_device *dev,
- u32 size, bool iskernel,
- struct drm_gem_object **obj)
-{
- struct cirrus_bo *cirrusbo;
- int ret;
-
- *obj = NULL;
-
- size = roundup(size, PAGE_SIZE);
- if (size == 0)
- return -EINVAL;
-
- ret = cirrus_bo_create(dev, size, 0, 0, &cirrusbo);
- if (ret) {
- if (ret != -ERESTARTSYS)
- DRM_ERROR("failed to allocate GEM object\n");
- return ret;
- }
- *obj = &cirrusbo->gem;
- return 0;
-}
-
-int cirrus_dumb_create(struct drm_file *file,
- struct drm_device *dev,
- struct drm_mode_create_dumb *args)
-{
- int ret;
- struct drm_gem_object *gobj;
- u32 handle;
-
- args->pitch = args->width * ((args->bpp + 7) / 8);
- args->size = args->pitch * args->height;
-
- ret = cirrus_gem_create(dev, args->size, false,
- &gobj);
- if (ret)
- return ret;
-
- ret = drm_gem_handle_create(file, gobj, &handle);
- drm_gem_object_put_unlocked(gobj);
- if (ret)
- return ret;
-
- args->handle = handle;
- return 0;
-}
-
-static void cirrus_bo_unref(struct cirrus_bo **bo)
-{
- struct ttm_buffer_object *tbo;
-
- if ((*bo) == NULL)
- return;
-
- tbo = &((*bo)->bo);
- ttm_bo_put(tbo);
- *bo = NULL;
-}
-
-void cirrus_gem_free_object(struct drm_gem_object *obj)
-{
- struct cirrus_bo *cirrus_bo = gem_to_cirrus_bo(obj);
-
- cirrus_bo_unref(&cirrus_bo);
-}
-
-
-static inline u64 cirrus_bo_mmap_offset(struct cirrus_bo *bo)
-{
- return drm_vma_node_offset_addr(&bo->bo.vma_node);
-}
-
-int
-cirrus_dumb_mmap_offset(struct drm_file *file,
- struct drm_device *dev,
- uint32_t handle,
- uint64_t *offset)
-{
- struct drm_gem_object *obj;
- struct cirrus_bo *bo;
-
- obj = drm_gem_object_lookup(file, handle);
- if (obj == NULL)
- return -ENOENT;
-
- bo = gem_to_cirrus_bo(obj);
- *offset = cirrus_bo_mmap_offset(bo);
-
- drm_gem_object_put_unlocked(obj);
-
- return 0;
-}
-
-bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height,
- int bpp, int pitch)
-{
- const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */
- const int max_size = cdev->mc.vram_size;
-
- if (bpp > cirrus_bpp)
- return false;
- if (bpp > 32)
- return false;
-
- if (pitch > max_pitch)
- return false;
-
- if (pitch * height > max_size)
- return false;
-
- return true;
-}
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
deleted file mode 100644
index b109cd71426f..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ /dev/null
@@ -1,617 +0,0 @@
-
-/*
- * Copyright 2012 Red Hat
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License version 2. See the file COPYING in the main
- * directory of this archive for more details.
- *
- * Authors: Matthew Garrett
- * Dave Airlie
- *
- * Portions of this code derived from cirrusfb.c:
- * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
- *
- * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
- */
-#include <drm/drmP.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_plane_helper.h>
-#include <drm/drm_probe_helper.h>
-
-#include <video/cirrus.h>
-
-#include "cirrus_drv.h"
-
-#define CIRRUS_LUT_SIZE 256
-
-#define PALETTE_INDEX 0x8
-#define PALETTE_DATA 0x9
-
-/*
- * This file contains setup code for the CRTC.
- */
-
-/*
- * The DRM core requires DPMS functions, but they make little sense in our
- * case and so are just stubs
- */
-
-static void cirrus_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
- struct drm_device *dev = crtc->dev;
- struct cirrus_device *cdev = dev->dev_private;
- u8 sr01, gr0e;
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- sr01 = 0x00;
- gr0e = 0x00;
- break;
- case DRM_MODE_DPMS_STANDBY:
- sr01 = 0x20;
- gr0e = 0x02;
- break;
- case DRM_MODE_DPMS_SUSPEND:
- sr01 = 0x20;
- gr0e = 0x04;
- break;
- case DRM_MODE_DPMS_OFF:
- sr01 = 0x20;
- gr0e = 0x06;
- break;
- default:
- return;
- }
-
- WREG8(SEQ_INDEX, 0x1);
- sr01 |= RREG8(SEQ_DATA) & ~0x20;
- WREG_SEQ(0x1, sr01);
-
- WREG8(GFX_INDEX, 0xe);
- gr0e |= RREG8(GFX_DATA) & ~0x06;
- WREG_GFX(0xe, gr0e);
-}
-
-static void cirrus_set_start_address(struct drm_crtc *crtc, unsigned offset)
-{
- struct cirrus_device *cdev = crtc->dev->dev_private;
- u32 addr;
- u8 tmp;
-
- addr = offset >> 2;
- WREG_CRT(0x0c, (u8)((addr >> 8) & 0xff));
- WREG_CRT(0x0d, (u8)(addr & 0xff));
-
- WREG8(CRT_INDEX, 0x1b);
- tmp = RREG8(CRT_DATA);
- tmp &= 0xf2;
- tmp |= (addr >> 16) & 0x01;
- tmp |= (addr >> 15) & 0x0c;
- WREG_CRT(0x1b, tmp);
- WREG8(CRT_INDEX, 0x1d);
- tmp = RREG8(CRT_DATA);
- tmp &= 0x7f;
- tmp |= (addr >> 12) & 0x80;
- WREG_CRT(0x1d, tmp);
-}
-
-/* cirrus is different - we will force move buffers out of VRAM */
-static int cirrus_crtc_do_set_base(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- int x, int y, int atomic)
-{
- struct cirrus_device *cdev = crtc->dev->dev_private;
- struct cirrus_bo *bo;
- int ret;
- u64 gpu_addr;
-
- /* push the previous fb to system ram */
- if (!atomic && fb) {
- bo = gem_to_cirrus_bo(fb->obj[0]);
- ret = cirrus_bo_reserve(bo, false);
- if (ret)
- return ret;
- cirrus_bo_push_sysram(bo);
- cirrus_bo_unreserve(bo);
- }
-
- bo = gem_to_cirrus_bo(crtc->primary->fb->obj[0]);
-
- ret = cirrus_bo_reserve(bo, false);
- if (ret)
- return ret;
-
- ret = cirrus_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
- if (ret) {
- cirrus_bo_unreserve(bo);
- return ret;
- }
-
- if (cdev->mode_info.gfbdev->gfb == crtc->primary->fb) {
- /* if pushing console in kmap it */
- ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
- if (ret)
- DRM_ERROR("failed to kmap fbcon\n");
- }
- cirrus_bo_unreserve(bo);
-
- cirrus_set_start_address(crtc, (u32)gpu_addr);
- return 0;
-}
-
-static int cirrus_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
- struct drm_framebuffer *old_fb)
-{
- return cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
-}
-
-/*
- * The meat of this driver. The core passes us a mode and we have to program
- * it. The modesetting here is the bare minimum required to satisfy the qemu
- * emulation of this hardware, and running this against a real device is
- * likely to result in an inadequately programmed mode. We've already had
- * the opportunity to modify the mode, so whatever we receive here should
- * be something that can be correctly programmed and displayed
- */
-static int cirrus_crtc_mode_set(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode,
- int x, int y, struct drm_framebuffer *old_fb)
-{
- struct drm_device *dev = crtc->dev;
- struct cirrus_device *cdev = dev->dev_private;
- const struct drm_framebuffer *fb = crtc->primary->fb;
- int hsyncstart, hsyncend, htotal, hdispend;
- int vtotal, vdispend;
- int tmp;
- int sr07 = 0, hdr = 0;
-
- htotal = mode->htotal / 8;
- hsyncend = mode->hsync_end / 8;
- hsyncstart = mode->hsync_start / 8;
- hdispend = mode->hdisplay / 8;
-
- vtotal = mode->vtotal;
- vdispend = mode->vdisplay;
-
- vdispend -= 1;
- vtotal -= 2;
-
- htotal -= 5;
- hdispend -= 1;
- hsyncstart += 1;
- hsyncend += 1;
-
- WREG_CRT(VGA_CRTC_V_SYNC_END, 0x20);
- WREG_CRT(VGA_CRTC_H_TOTAL, htotal);
- WREG_CRT(VGA_CRTC_H_DISP, hdispend);
- WREG_CRT(VGA_CRTC_H_SYNC_START, hsyncstart);
- WREG_CRT(VGA_CRTC_H_SYNC_END, hsyncend);
- WREG_CRT(VGA_CRTC_V_TOTAL, vtotal & 0xff);
- WREG_CRT(VGA_CRTC_V_DISP_END, vdispend & 0xff);
-
- tmp = 0x40;
- if ((vdispend + 1) & 512)
- tmp |= 0x20;
- WREG_CRT(VGA_CRTC_MAX_SCAN, tmp);
-
- /*
- * Overflow bits for values that don't fit in the standard registers
- */
- tmp = 16;
- if (vtotal & 256)
- tmp |= 1;
- if (vdispend & 256)
- tmp |= 2;
- if ((vdispend + 1) & 256)
- tmp |= 8;
- if (vtotal & 512)
- tmp |= 32;
- if (vdispend & 512)
- tmp |= 64;
- WREG_CRT(VGA_CRTC_OVERFLOW, tmp);
-
- tmp = 0;
-
- /* More overflow bits */
-
- if ((htotal + 5) & 64)
- tmp |= 16;
- if ((htotal + 5) & 128)
- tmp |= 32;
- if (vtotal & 256)
- tmp |= 64;
- if (vtotal & 512)
- tmp |= 128;
-
- WREG_CRT(CL_CRT1A, tmp);
-
- /* Disable Hercules/CGA compatibility */
- WREG_CRT(VGA_CRTC_MODE, 0x03);
-
- WREG8(SEQ_INDEX, 0x7);
- sr07 = RREG8(SEQ_DATA);
- sr07 &= 0xe0;
- hdr = 0;
- switch (fb->format->cpp[0] * 8) {
- case 8:
- sr07 |= 0x11;
- break;
- case 16:
- sr07 |= 0x17;
- hdr = 0xc1;
- break;
- case 24:
- sr07 |= 0x15;
- hdr = 0xc5;
- break;
- case 32:
- sr07 |= 0x19;
- hdr = 0xc5;
- break;
- default:
- return -1;
- }
-
- WREG_SEQ(0x7, sr07);
-
- /* Program the pitch */
- tmp = fb->pitches[0] / 8;
- WREG_CRT(VGA_CRTC_OFFSET, tmp);
-
- /* Enable extended blanking and pitch bits, and enable full memory */
- tmp = 0x22;
- tmp |= (fb->pitches[0] >> 7) & 0x10;
- tmp |= (fb->pitches[0] >> 6) & 0x40;
- WREG_CRT(0x1b, tmp);
-
- /* Enable high-colour modes */
- WREG_GFX(VGA_GFX_MODE, 0x40);
-
- /* And set graphics mode */
- WREG_GFX(VGA_GFX_MISC, 0x01);
-
- WREG_HDR(hdr);
- cirrus_crtc_do_set_base(crtc, old_fb, x, y, 0);
-
- /* Unblank (needed on S3 resume, vgabios doesn't do it then) */
- outb(0x20, 0x3c0);
- return 0;
-}
-
-/*
- * This is called before a mode is programmed. A typical use might be to
- * enable DPMS during the programming to avoid seeing intermediate stages,
- * but that's not relevant to us
- */
-static void cirrus_crtc_prepare(struct drm_crtc *crtc)
-{
-}
-
-static void cirrus_crtc_load_lut(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct cirrus_device *cdev = dev->dev_private;
- u16 *r, *g, *b;
- int i;
-
- if (!crtc->enabled)
- return;
-
- r = crtc->gamma_store;
- g = r + crtc->gamma_size;
- b = g + crtc->gamma_size;
-
- for (i = 0; i < CIRRUS_LUT_SIZE; i++) {
- /* VGA registers */
- WREG8(PALETTE_INDEX, i);
- WREG8(PALETTE_DATA, *r++ >> 8);
- WREG8(PALETTE_DATA, *g++ >> 8);
- WREG8(PALETTE_DATA, *b++ >> 8);
- }
-}
-
-/*
- * This is called after a mode is programmed. It should reverse anything done
- * by the prepare function
- */
-static void cirrus_crtc_commit(struct drm_crtc *crtc)
-{
- cirrus_crtc_load_lut(crtc);
-}
-
-/*
- * The core can pass us a set of gamma values to program. We actually only
- * use this for 8-bit mode so can't perform smooth fades on deeper modes,
- * but it's a requirement that we provide the function
- */
-static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
- u16 *blue, uint32_t size,
- struct drm_modeset_acquire_ctx *ctx)
-{
- cirrus_crtc_load_lut(crtc);
-
- return 0;
-}
-
-/* Simple cleanup function */
-static void cirrus_crtc_destroy(struct drm_crtc *crtc)
-{
- struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc);
-
- drm_crtc_cleanup(crtc);
- kfree(cirrus_crtc);
-}
-
-/* These provide the minimum set of functions required to handle a CRTC */
-static const struct drm_crtc_funcs cirrus_crtc_funcs = {
- .gamma_set = cirrus_crtc_gamma_set,
- .set_config = drm_crtc_helper_set_config,
- .destroy = cirrus_crtc_destroy,
-};
-
-static const struct drm_crtc_helper_funcs cirrus_helper_funcs = {
- .dpms = cirrus_crtc_dpms,
- .mode_set = cirrus_crtc_mode_set,
- .mode_set_base = cirrus_crtc_mode_set_base,
- .prepare = cirrus_crtc_prepare,
- .commit = cirrus_crtc_commit,
-};
-
-/* CRTC setup */
-static const uint32_t cirrus_formats_16[] = {
- DRM_FORMAT_RGB565,
-};
-
-static const uint32_t cirrus_formats_24[] = {
- DRM_FORMAT_RGB888,
- DRM_FORMAT_RGB565,
-};
-
-static const uint32_t cirrus_formats_32[] = {
- DRM_FORMAT_XRGB8888,
- DRM_FORMAT_ARGB8888,
- DRM_FORMAT_RGB888,
- DRM_FORMAT_RGB565,
-};
-
-static struct drm_plane *cirrus_primary_plane(struct drm_device *dev)
-{
- const uint32_t *formats;
- uint32_t nformats;
- struct drm_plane *primary;
- int ret;
-
- switch (cirrus_bpp) {
- case 16:
- formats = cirrus_formats_16;
- nformats = ARRAY_SIZE(cirrus_formats_16);
- break;
- case 24:
- formats = cirrus_formats_24;
- nformats = ARRAY_SIZE(cirrus_formats_24);
- break;
- case 32:
- formats = cirrus_formats_32;
- nformats = ARRAY_SIZE(cirrus_formats_32);
- break;
- default:
- return NULL;
- }
-
- primary = kzalloc(sizeof(*primary), GFP_KERNEL);
- if (primary == NULL) {
- DRM_DEBUG_KMS("Failed to allocate primary plane\n");
- return NULL;
- }
-
- ret = drm_universal_plane_init(dev, primary, 0,
- &drm_primary_helper_funcs,
- formats, nformats,
- NULL,
- DRM_PLANE_TYPE_PRIMARY, NULL);
- if (ret) {
- kfree(primary);
- primary = NULL;
- }
-
- return primary;
-}
-
-static void cirrus_crtc_init(struct drm_device *dev)
-{
- struct cirrus_device *cdev = dev->dev_private;
- struct cirrus_crtc *cirrus_crtc;
- struct drm_plane *primary;
-
- cirrus_crtc = kzalloc(sizeof(struct cirrus_crtc) +
- (CIRRUSFB_CONN_LIMIT * sizeof(struct drm_connector *)),
- GFP_KERNEL);
-
- if (cirrus_crtc == NULL)
- return;
-
- primary = cirrus_primary_plane(dev);
- if (primary == NULL) {
- kfree(cirrus_crtc);
- return;
- }
-
- drm_crtc_init_with_planes(dev, &cirrus_crtc->base,
- primary, NULL,
- &cirrus_crtc_funcs, NULL);
-
- drm_mode_crtc_set_gamma_size(&cirrus_crtc->base, CIRRUS_LUT_SIZE);
- cdev->mode_info.crtc = cirrus_crtc;
-
- drm_crtc_helper_add(&cirrus_crtc->base, &cirrus_helper_funcs);
-}
-
-static void cirrus_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
-}
-
-static void cirrus_encoder_dpms(struct drm_encoder *encoder, int state)
-{
- return;
-}
-
-static void cirrus_encoder_prepare(struct drm_encoder *encoder)
-{
-}
-
-static void cirrus_encoder_commit(struct drm_encoder *encoder)
-{
-}
-
-static void cirrus_encoder_destroy(struct drm_encoder *encoder)
-{
- struct cirrus_encoder *cirrus_encoder = to_cirrus_encoder(encoder);
- drm_encoder_cleanup(encoder);
- kfree(cirrus_encoder);
-}
-
-static const struct drm_encoder_helper_funcs cirrus_encoder_helper_funcs = {
- .dpms = cirrus_encoder_dpms,
- .mode_set = cirrus_encoder_mode_set,
- .prepare = cirrus_encoder_prepare,
- .commit = cirrus_encoder_commit,
-};
-
-static const struct drm_encoder_funcs cirrus_encoder_encoder_funcs = {
- .destroy = cirrus_encoder_destroy,
-};
-
-static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
-{
- struct drm_encoder *encoder;
- struct cirrus_encoder *cirrus_encoder;
-
- cirrus_encoder = kzalloc(sizeof(struct cirrus_encoder), GFP_KERNEL);
- if (!cirrus_encoder)
- return NULL;
-
- encoder = &cirrus_encoder->base;
- encoder->possible_crtcs = 0x1;
-
- drm_encoder_init(dev, encoder, &cirrus_encoder_encoder_funcs,
- DRM_MODE_ENCODER_DAC, NULL);
- drm_encoder_helper_add(encoder, &cirrus_encoder_helper_funcs);
-
- return encoder;
-}
-
-
-static int cirrus_vga_get_modes(struct drm_connector *connector)
-{
- int count;
-
- /* Just add a static list of modes */
- if (cirrus_bpp <= 24) {
- count = drm_add_modes_noedid(connector, 1280, 1024);
- drm_set_preferred_mode(connector, 1024, 768);
- } else {
- count = drm_add_modes_noedid(connector, 800, 600);
- drm_set_preferred_mode(connector, 800, 600);
- }
- return count;
-}
-
-static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
- *connector)
-{
- int enc_id = connector->encoder_ids[0];
- /* pick the encoder ids */
- if (enc_id)
- return drm_encoder_find(connector->dev, NULL, enc_id);
- return NULL;
-}
-
-static void cirrus_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_cleanup(connector);
- kfree(connector);
-}
-
-static const struct drm_connector_helper_funcs cirrus_vga_connector_helper_funcs = {
- .get_modes = cirrus_vga_get_modes,
- .best_encoder = cirrus_connector_best_encoder,
-};
-
-static const struct drm_connector_funcs cirrus_vga_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = cirrus_connector_destroy,
-};
-
-static struct drm_connector *cirrus_vga_init(struct drm_device *dev)
-{
- struct drm_connector *connector;
- struct cirrus_connector *cirrus_connector;
-
- cirrus_connector = kzalloc(sizeof(struct cirrus_connector), GFP_KERNEL);
- if (!cirrus_connector)
- return NULL;
-
- connector = &cirrus_connector->base;
-
- drm_connector_init(dev, connector,
- &cirrus_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA);
-
- drm_connector_helper_add(connector, &cirrus_vga_connector_helper_funcs);
-
- drm_connector_register(connector);
- return connector;
-}
-
-
-int cirrus_modeset_init(struct cirrus_device *cdev)
-{
- struct drm_encoder *encoder;
- struct drm_connector *connector;
- int ret;
-
- drm_mode_config_init(cdev->dev);
-
- cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH;
- cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT;
-
- cdev->dev->mode_config.fb_base = cdev->mc.vram_base;
- cdev->dev->mode_config.preferred_depth = cirrus_bpp;
- /* don't prefer a shadow on virt GPU */
- cdev->dev->mode_config.prefer_shadow = 0;
-
- cirrus_crtc_init(cdev->dev);
-
- encoder = cirrus_encoder_init(cdev->dev);
- if (!encoder) {
- DRM_ERROR("cirrus_encoder_init failed\n");
- return -1;
- }
-
- connector = cirrus_vga_init(cdev->dev);
- if (!connector) {
- DRM_ERROR("cirrus_vga_init failed\n");
- return -1;
- }
-
- drm_connector_attach_encoder(connector, encoder);
-
- ret = cirrus_fbdev_init(cdev);
- if (ret) {
- DRM_ERROR("cirrus_fbdev_init failed\n");
- return ret;
- }
-
- return 0;
-}
-
-void cirrus_modeset_fini(struct cirrus_device *cdev)
-{
- cirrus_fbdev_fini(cdev);
- drm_helper_force_disable_all(cdev->dev);
- drm_mode_config_cleanup(cdev->dev);
-}
diff --git a/drivers/gpu/drm/cirrus/cirrus_ttm.c b/drivers/gpu/drm/cirrus/cirrus_ttm.c
deleted file mode 100644
index e075810b4bd4..000000000000
--- a/drivers/gpu/drm/cirrus/cirrus_ttm.c
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright 2012 Red Hat Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
- * USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- */
-/*
- * Authors: Dave Airlie <airlied@redhat.com>
- */
-#include <drm/drmP.h>
-#include <drm/ttm/ttm_page_alloc.h>
-
-#include "cirrus_drv.h"
-
-static inline struct cirrus_device *
-cirrus_bdev(struct ttm_bo_device *bd)
-{
- return container_of(bd, struct cirrus_device, ttm.bdev);
-}
-
-static void cirrus_bo_ttm_destroy(struct ttm_buffer_object *tbo)
-{
- struct cirrus_bo *bo;
-
- bo = container_of(tbo, struct cirrus_bo, bo);
-
- drm_gem_object_release(&bo->gem);
- kfree(bo);
-}
-
-static bool cirrus_ttm_bo_is_cirrus_bo(struct ttm_buffer_object *bo)
-{
- if (bo->destroy == &cirrus_bo_ttm_destroy)
- return true;
- return false;
-}
-
-static int
-cirrus_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
- struct ttm_mem_type_manager *man)
-{
- switch (type) {
- case TTM_PL_SYSTEM:
- man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
- man->available_caching = TTM_PL_MASK_CACHING;
- man->default_caching = TTM_PL_FLAG_CACHED;
- break;
- case TTM_PL_VRAM:
- man->func = &ttm_bo_manager_func;
- man->flags = TTM_MEMTYPE_FLAG_FIXED |
- TTM_MEMTYPE_FLAG_MAPPABLE;
- man->available_caching = TTM_PL_FLAG_UNCACHED |
- TTM_PL_FLAG_WC;
- man->default_caching = TTM_PL_FLAG_WC;
- break;
- default:
- DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
- return -EINVAL;
- }
- return 0;
-}
-
-static void
-cirrus_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
-{
- struct cirrus_bo *cirrusbo = cirrus_bo(bo);
-
- if (!cirrus_ttm_bo_is_cirrus_bo(bo))
- return;
-
- cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_SYSTEM);
- *pl = cirrusbo->placement;
-}
-
-static int cirrus_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
-{
- struct cirrus_bo *cirrusbo = cirrus_bo(bo);
-
- return drm_vma_node_verify_access(&cirrusbo->gem.vma_node,
- filp->private_data);
-}
-
-static int cirrus_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
- struct ttm_mem_reg *mem)
-{
- struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
- struct cirrus_device *cirrus = cirrus_bdev(bdev);
-
- mem->bus.addr = NULL;
- mem->bus.offset = 0;
- mem->bus.size = mem->num_pages << PAGE_SHIFT;
- mem->bus.base = 0;
- mem->bus.is_iomem = false;
- if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
- return -EINVAL;
- switch (mem->mem_type) {
- case TTM_PL_SYSTEM:
- /* system memory */
- return 0;
- case TTM_PL_VRAM:
- mem->bus.offset = mem->start << PAGE_SHIFT;
- mem->bus.base = pci_resource_start(cirrus->dev->pdev, 0);
- mem->bus.is_iomem = true;
- break;
- default:
- return -EINVAL;
- break;
- }
- return 0;
-}
-
-static void cirrus_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
-{
-}
-
-static void cirrus_ttm_backend_destroy(struct ttm_tt *tt)
-{
- ttm_tt_fini(tt);
- kfree(tt);
-}
-
-static struct ttm_backend_func cirrus_tt_backend_func = {
- .destroy = &cirrus_ttm_backend_destroy,
-};
-
-
-static struct ttm_tt *cirrus_ttm_tt_create(struct ttm_buffer_object *bo,
- uint32_t page_flags)
-{
- struct ttm_tt *tt;
-
- tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL);
- if (tt == NULL)
- return NULL;
- tt->func = &cirrus_tt_backend_func;
- if (ttm_tt_init(tt, bo, page_flags)) {
- kfree(tt);
- return NULL;
- }
- return tt;
-}
-
-struct ttm_bo_driver cirrus_bo_driver = {
- .ttm_tt_create = cirrus_ttm_tt_create,
- .init_mem_type = cirrus_bo_init_mem_type,
- .eviction_valuable = ttm_bo_eviction_valuable,
- .evict_flags = cirrus_bo_evict_flags,
- .move = NULL,
- .verify_access = cirrus_bo_verify_access,
- .io_mem_reserve = &cirrus_ttm_io_mem_reserve,
- .io_mem_free = &cirrus_ttm_io_mem_free,
-};
-
-int cirrus_mm_init(struct cirrus_device *cirrus)
-{
- int ret;
- struct drm_device *dev = cirrus->dev;
- struct ttm_bo_device *bdev = &cirrus->ttm.bdev;
-
- ret = ttm_bo_device_init(&cirrus->ttm.bdev,
- &cirrus_bo_driver,
- dev->anon_inode->i_mapping,
- DRM_FILE_PAGE_OFFSET,
- true);
- if (ret) {
- DRM_ERROR("Error initialising bo driver; %d\n", ret);
- return ret;
- }
-
- ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
- cirrus->mc.vram_size >> PAGE_SHIFT);
- if (ret) {
- DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
- return ret;
- }
-
- arch_io_reserve_memtype_wc(pci_resource_start(dev->pdev, 0),
- pci_resource_len(dev->pdev, 0));
-
- cirrus->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0),
- pci_resource_len(dev->pdev, 0));
-
- cirrus->mm_inited = true;
- return 0;
-}
-
-void cirrus_mm_fini(struct cirrus_device *cirrus)
-{
- struct drm_device *dev = cirrus->dev;
-
- if (!cirrus->mm_inited)
- return;
-
- ttm_bo_device_release(&cirrus->ttm.bdev);
-
- arch_phys_wc_del(cirrus->fb_mtrr);
- cirrus->fb_mtrr = 0;
- arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0),
- pci_resource_len(dev->pdev, 0));
-}
-
-void cirrus_ttm_placement(struct cirrus_bo *bo, int domain)
-{
- u32 c = 0;
- unsigned i;
- bo->placement.placement = bo->placements;
- bo->placement.busy_placement = bo->placements;
- if (domain & TTM_PL_FLAG_VRAM)
- bo->placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
- if (domain & TTM_PL_FLAG_SYSTEM)
- bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
- if (!c)
- bo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
- bo->placement.num_placement = c;
- bo->placement.num_busy_placement = c;
- for (i = 0; i < c; ++i) {
- bo->placements[i].fpfn = 0;
- bo->placements[i].lpfn = 0;
- }
-}
-
-int cirrus_bo_create(struct drm_device *dev, int size, int align,
- uint32_t flags, struct cirrus_bo **pcirrusbo)
-{
- struct cirrus_device *cirrus = dev->dev_private;
- struct cirrus_bo *cirrusbo;
- size_t acc_size;
- int ret;
-
- cirrusbo = kzalloc(sizeof(struct cirrus_bo), GFP_KERNEL);
- if (!cirrusbo)
- return -ENOMEM;
-
- ret = drm_gem_object_init(dev, &cirrusbo->gem, size);
- if (ret) {
- kfree(cirrusbo);
- return ret;
- }
-
- cirrusbo->bo.bdev = &cirrus->ttm.bdev;
-
- cirrus_ttm_placement(cirrusbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
-
- acc_size = ttm_bo_dma_acc_size(&cirrus->ttm.bdev, size,
- sizeof(struct cirrus_bo));
-
- ret = ttm_bo_init(&cirrus->ttm.bdev, &cirrusbo->bo, size,
- ttm_bo_type_device, &cirrusbo->placement,
- align >> PAGE_SHIFT, false, acc_size,
- NULL, NULL, cirrus_bo_ttm_destroy);
- if (ret)
- return ret;
-
- *pcirrusbo = cirrusbo;
- return 0;
-}
-
-static inline u64 cirrus_bo_gpu_offset(struct cirrus_bo *bo)
-{
- return bo->bo.offset;
-}
-
-int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr)
-{
- struct ttm_operation_ctx ctx = { false, false };
- int i, ret;
-
- if (bo->pin_count) {
- bo->pin_count++;
- if (gpu_addr)
- *gpu_addr = cirrus_bo_gpu_offset(bo);
- }
-
- cirrus_ttm_placement(bo, pl_flag);
- for (i = 0; i < bo->placement.num_placement; i++)
- bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
- ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
- if (ret)
- return ret;
-
- bo->pin_count = 1;
- if (gpu_addr)
- *gpu_addr = cirrus_bo_gpu_offset(bo);
- return 0;
-}
-
-int cirrus_bo_push_sysram(struct cirrus_bo *bo)
-{
- struct ttm_operation_ctx ctx = { false, false };
- int i, ret;
- if (!bo->pin_count) {
- DRM_ERROR("unpin bad %p\n", bo);
- return 0;
- }
- bo->pin_count--;
- if (bo->pin_count)
- return 0;
-
- if (bo->kmap.virtual)
- ttm_bo_kunmap(&bo->kmap);
-
- cirrus_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
- for (i = 0; i < bo->placement.num_placement ; i++)
- bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
-
- ret = ttm_bo_validate(&bo->bo, &bo->placement, &ctx);
- if (ret) {
- DRM_ERROR("pushing to VRAM failed\n");
- return ret;
- }
- return 0;
-}
-
-int cirrus_mmap(struct file *filp, struct vm_area_struct *vma)
-{
- struct drm_file *file_priv;
- struct cirrus_device *cirrus;
-
- if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
- return -EINVAL;
-
- file_priv = filp->private_data;
- cirrus = file_priv->minor->dev->dev_private;
- return ttm_bo_mmap(filp, vma, &cirrus->ttm.bdev);
-}
diff --git a/drivers/gpu/drm/cirrus/Kconfig b/drivers/gpu/drm/cirrus/Kconfig
index fc78c90ee931..dd4f52a0bc1c 100644
--- a/drivers/gpu/drm/cirrus/Kconfig
+++ b/drivers/gpu/drm/cirrus/Kconfig
@@ -2,7 +2,7 @@ config DRM_CIRRUS_QEMU
tristate "Cirrus driver for QEMU emulated device"
depends on DRM && PCI && MMU
select DRM_KMS_HELPER
- select DRM_TTM
+ select DRM_GEM_SHMEM_HELPER
help
This is a KMS driver for emulated cirrus device in qemu.
It is *NOT* intended for real cirrus devices. This requires
diff --git a/drivers/gpu/drm/cirrus/Makefile b/drivers/gpu/drm/cirrus/Makefile
index 919c0a336c97..acf8971d37a1 100644
--- a/drivers/gpu/drm/cirrus/Makefile
+++ b/drivers/gpu/drm/cirrus/Makefile
@@ -1,4 +1 @@
-cirrus-y := cirrus_main.o cirrus_mode.o \
- cirrus_drv.o cirrus_fbdev.o cirrus_ttm.o
-
obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o
--
2.18.1
^ permalink raw reply related
* Re: [PATCH net v5] failover: allow name change on IFF_UP slave interfaces
From: Samudrala, Sridhar @ 2019-04-03 5:22 UTC (permalink / raw)
To: Stephen Hemminger, si-wei liu
Cc: jiri, mst, kubakici, netdev, alexander.duyck, virtualization,
liran.alon, boris.ostrovsky, davem
In-Reply-To: <20190402201451.1f6201b8@shemminger-XPS-13-9360>
On 4/2/2019 8:14 PM, Stephen Hemminger wrote:
> On Tue, 2 Apr 2019 15:23:29 -0700
> si-wei liu <si-wei.liu@oracle.com> wrote:
>
>> On 4/2/2019 2:53 PM, Stephen Hemminger wrote:
>>> On Mon, 1 Apr 2019 19:04:53 -0400
>>> Si-Wei Liu <si-wei.liu@oracle.com> wrote:
>>>
>>>> + if (dev->flags & IFF_UP &&
>>>> + likely(!(dev->priv_flags & IFF_FAILOVER_SLAVE)))
>>> Why is property limited to failover slave, it would make sense for netvsc
>>> as well. Why not make it a flag like live address change?
>> Well, netvsc today is still taking the delayed approach meaning that it
>> is incompatible yet with this live name change flag if need be. ;-)
>>
>> I thought Sridhar did not like to introduce an additional
>> IFF_SLAVE_RENAME_OK flag given that failover slave is the only consumer
>> for the time being. Even though I can get it back, patch is needed for
>> netvsc to remove the VF takeover delay IMHO.
>>
>> Sridhar, what do you think we revive the IFF_SLAVE_RENAME_OK flag which
>> allows netvsc to be used later on? Or maybe, IFF_LIVE_RENAME_OK for a
>> better name?
>>
>> -Siwei
>
> I would name it IFF_LIVE_NAME_CHANGE to match IFF_LIVE_ADDR_CHANGE
> there is no reason its use should be restricted to SLAVE devices.
>
Stephen,
May be you should consider moving netvsc to use the net_failover driver now?
^ permalink raw reply
* Re: [PATCH net v5] failover: allow name change on IFF_UP slave interfaces
From: Stephen Hemminger @ 2019-04-03 3:14 UTC (permalink / raw)
To: si-wei liu
Cc: jiri, mst, kubakici, sridhar.samudrala, alexander.duyck,
virtualization, liran.alon, netdev, boris.ostrovsky, davem
In-Reply-To: <d62006dc-d602-7389-cc27-2c1873163ad2@oracle.com>
On Tue, 2 Apr 2019 15:23:29 -0700
si-wei liu <si-wei.liu@oracle.com> wrote:
> On 4/2/2019 2:53 PM, Stephen Hemminger wrote:
> > On Mon, 1 Apr 2019 19:04:53 -0400
> > Si-Wei Liu <si-wei.liu@oracle.com> wrote:
> >
> >> + if (dev->flags & IFF_UP &&
> >> + likely(!(dev->priv_flags & IFF_FAILOVER_SLAVE)))
> > Why is property limited to failover slave, it would make sense for netvsc
> > as well. Why not make it a flag like live address change?
> Well, netvsc today is still taking the delayed approach meaning that it
> is incompatible yet with this live name change flag if need be. ;-)
>
> I thought Sridhar did not like to introduce an additional
> IFF_SLAVE_RENAME_OK flag given that failover slave is the only consumer
> for the time being. Even though I can get it back, patch is needed for
> netvsc to remove the VF takeover delay IMHO.
>
> Sridhar, what do you think we revive the IFF_SLAVE_RENAME_OK flag which
> allows netvsc to be used later on? Or maybe, IFF_LIVE_RENAME_OK for a
> better name?
>
> -Siwei
I would name it IFF_LIVE_NAME_CHANGE to match IFF_LIVE_ADDR_CHANGE
there is no reason its use should be restricted to SLAVE devices.
^ permalink raw reply
* Re: [PATCH net v5] failover: allow name change on IFF_UP slave interfaces
From: Stephen Hemminger @ 2019-04-02 21:53 UTC (permalink / raw)
To: Si-Wei Liu
Cc: jiri, mst, kubakici, sridhar.samudrala, alexander.duyck,
virtualization, liran.alon, netdev, boris.ostrovsky, davem
In-Reply-To: <1554159893-29704-1-git-send-email-si-wei.liu@oracle.com>
On Mon, 1 Apr 2019 19:04:53 -0400
Si-Wei Liu <si-wei.liu@oracle.com> wrote:
> + if (dev->flags & IFF_UP &&
> + likely(!(dev->priv_flags & IFF_FAILOVER_SLAVE)))
Why is property limited to failover slave, it would make sense for netvsc
as well. Why not make it a flag like live address change?
^ permalink raw reply
* Re: [PATCH net v5] failover: allow name change on IFF_UP slave interfaces
From: Michael S. Tsirkin @ 2019-04-02 21:50 UTC (permalink / raw)
To: Si-Wei Liu
Cc: jiri, kubakici, sridhar.samudrala, alexander.duyck,
virtualization, liran.alon, netdev, boris.ostrovsky, davem
In-Reply-To: <1554159893-29704-1-git-send-email-si-wei.liu@oracle.com>
On Mon, Apr 01, 2019 at 07:04:53PM -0400, Si-Wei Liu wrote:
> When a netdev appears through hot plug then gets enslaved by a failover
> master that is already up and running, the slave will be opened
> right away after getting enslaved. Today there's a race that userspace
> (udev) may fail to rename the slave if the kernel (net_failover)
> opens the slave earlier than when the userspace rename happens.
> Unlike bond or team, the primary slave of failover can't be renamed by
> userspace ahead of time, since the kernel initiated auto-enslavement is
> unable to, or rather, is never meant to be synchronized with the rename
> request from userspace.
>
> As the failover slave interfaces are not designed to be operated
> directly by userspace apps: IP configuration, filter rules with
> regard to network traffic passing and etc., should all be done on master
> interface. In general, userspace apps only care about the
> name of master interface, while slave names are less important as long
> as admin users can see reliable names that may carry
> other information describing the netdev. For e.g., they can infer that
> "ens3nsby" is a standby slave of "ens3", while for a
> name like "eth0" they can't tell which master it belongs to.
>
> Historically the name of IFF_UP interface can't be changed because
> there might be admin script or management software that is already
> relying on such behavior and assumes that the slave name can't be
> changed once UP. But failover is special: with the in-kernel
> auto-enslavement mechanism, the userspace expectation for device
> enumeration and bring-up order is already broken. Previously initramfs
> and various userspace config tools were modified to bypass failover
> slaves because of auto-enslavement and duplicate MAC address. Similarly,
> in case that users care about seeing reliable slave name, the new type
> of failover slaves needs to be taken care of specifically in userspace
> anyway.
>
> It's less risky to lift up the rename restriction on failover slave
> which is already UP. Although it's possible this change may potentially
> break userspace component (most likely configuration scripts or
> management software) that assumes slave name can't be changed while
> UP, it's relatively a limited and controllable set among all userspace
> components, which can be fixed specifically to listen for the rename
> and/or link down/up events on failover slaves. Userspace component
> interacting with slaves is expected to be changed to operate on failover
> master interface instead, as the failover slave is dynamic in nature
> which may come and go at any point. The goal is to make the role of
> failover slaves less relevant, and userspace components should only
> deal with failover master in the long run.
>
> Fixes: 30c8bd5aa8b2 ("net: Introduce generic failover module")
> Signed-off-by: Si-Wei Liu <si-wei.liu@oracle.com>
> Reviewed-by: Liran Alon <liran.alon@oracle.com>
>
> --
> v1 -> v2:
> - Drop configurable module parameter (Sridhar)
>
> v2 -> v3:
> - Drop additional IFF_SLAVE_RENAME_OK flag (Sridhar)
> - Send down and up events around rename (Michael S. Tsirkin)
>
> v3 -> v4:
> - Simplify notification to be sent (Stephen Hemminger)
>
> v4 -> v5:
> - Sync up code with latest net-next (Sridhar)
> - Use proper structure initialization (Stephen, Jiri)
> ---
Acked-by: Michael S. Tsirkin <mst@redhat.com>
> net/core/dev.c | 25 ++++++++++++++++++++++++-
> 1 file changed, 24 insertions(+), 1 deletion(-)
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 9823b77..b694184 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -1185,7 +1185,21 @@ int dev_change_name(struct net_device *dev, const char *newname)
> BUG_ON(!dev_net(dev));
>
> net = dev_net(dev);
> - if (dev->flags & IFF_UP)
> +
> + /* Allow failover slave to rename even when
> + * it is up and running.
> + *
> + * Failover slaves are special, since userspace
> + * might rename the slave after the interface
> + * has been brought up and running due to
> + * auto-enslavement.
> + *
> + * Failover users don't actually care about slave
> + * name change, as they are only expected to operate
> + * on master interface directly.
> + */
> + if (dev->flags & IFF_UP &&
> + likely(!(dev->priv_flags & IFF_FAILOVER_SLAVE)))
> return -EBUSY;
>
> write_seqcount_begin(&devnet_rename_seq);
> @@ -1232,6 +1246,15 @@ int dev_change_name(struct net_device *dev, const char *newname)
> hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
> write_unlock_bh(&dev_base_lock);
>
> + if (unlikely(dev->flags & IFF_UP)) {
> + struct netdev_notifier_change_info change_info = {
> + .info.dev = dev,
> + };
> +
> + call_netdevice_notifiers_info(NETDEV_CHANGE,
> + &change_info.info);
> + }
> +
> ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
> ret = notifier_to_errno(ret);
>
> --
> 1.8.3.1
^ permalink raw reply
* Re: [PATCH net v5] failover: allow name change on IFF_UP slave interfaces
From: Samudrala, Sridhar @ 2019-04-02 20:03 UTC (permalink / raw)
To: Si-Wei Liu, mst, stephen, davem, kubakici, alexander.duyck, jiri,
netdev, virtualization
Cc: boris.ostrovsky, liran.alon
In-Reply-To: <1554159893-29704-1-git-send-email-si-wei.liu@oracle.com>
On 4/1/2019 4:04 PM, Si-Wei Liu wrote:
> When a netdev appears through hot plug then gets enslaved by a failover
> master that is already up and running, the slave will be opened
> right away after getting enslaved. Today there's a race that userspace
> (udev) may fail to rename the slave if the kernel (net_failover)
> opens the slave earlier than when the userspace rename happens.
> Unlike bond or team, the primary slave of failover can't be renamed by
> userspace ahead of time, since the kernel initiated auto-enslavement is
> unable to, or rather, is never meant to be synchronized with the rename
> request from userspace.
>
> As the failover slave interfaces are not designed to be operated
> directly by userspace apps: IP configuration, filter rules with
> regard to network traffic passing and etc., should all be done on master
> interface. In general, userspace apps only care about the
> name of master interface, while slave names are less important as long
> as admin users can see reliable names that may carry
> other information describing the netdev. For e.g., they can infer that
> "ens3nsby" is a standby slave of "ens3", while for a
> name like "eth0" they can't tell which master it belongs to.
>
> Historically the name of IFF_UP interface can't be changed because
> there might be admin script or management software that is already
> relying on such behavior and assumes that the slave name can't be
> changed once UP. But failover is special: with the in-kernel
> auto-enslavement mechanism, the userspace expectation for device
> enumeration and bring-up order is already broken. Previously initramfs
> and various userspace config tools were modified to bypass failover
> slaves because of auto-enslavement and duplicate MAC address. Similarly,
> in case that users care about seeing reliable slave name, the new type
> of failover slaves needs to be taken care of specifically in userspace
> anyway.
>
> It's less risky to lift up the rename restriction on failover slave
> which is already UP. Although it's possible this change may potentially
> break userspace component (most likely configuration scripts or
> management software) that assumes slave name can't be changed while
> UP, it's relatively a limited and controllable set among all userspace
> components, which can be fixed specifically to listen for the rename
> and/or link down/up events on failover slaves. Userspace component
> interacting with slaves is expected to be changed to operate on failover
> master interface instead, as the failover slave is dynamic in nature
> which may come and go at any point. The goal is to make the role of
> failover slaves less relevant, and userspace components should only
> deal with failover master in the long run.
>
> Fixes: 30c8bd5aa8b2 ("net: Introduce generic failover module")
> Signed-off-by: Si-Wei Liu <si-wei.liu@oracle.com>
> Reviewed-by: Liran Alon <liran.alon@oracle.com>
Acked-by: Sridhar Samudrala <sridhar.samudrala@intel.com>
>
> --
> v1 -> v2:
> - Drop configurable module parameter (Sridhar)
>
> v2 -> v3:
> - Drop additional IFF_SLAVE_RENAME_OK flag (Sridhar)
> - Send down and up events around rename (Michael S. Tsirkin)
>
> v3 -> v4:
> - Simplify notification to be sent (Stephen Hemminger)
>
> v4 -> v5:
> - Sync up code with latest net-next (Sridhar)
> - Use proper structure initialization (Stephen, Jiri)
> ---
> net/core/dev.c | 25 ++++++++++++++++++++++++-
> 1 file changed, 24 insertions(+), 1 deletion(-)
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 9823b77..b694184 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -1185,7 +1185,21 @@ int dev_change_name(struct net_device *dev, const char *newname)
> BUG_ON(!dev_net(dev));
>
> net = dev_net(dev);
> - if (dev->flags & IFF_UP)
> +
> + /* Allow failover slave to rename even when
> + * it is up and running.
> + *
> + * Failover slaves are special, since userspace
> + * might rename the slave after the interface
> + * has been brought up and running due to
> + * auto-enslavement.
> + *
> + * Failover users don't actually care about slave
> + * name change, as they are only expected to operate
> + * on master interface directly.
> + */
> + if (dev->flags & IFF_UP &&
> + likely(!(dev->priv_flags & IFF_FAILOVER_SLAVE)))
> return -EBUSY;
>
> write_seqcount_begin(&devnet_rename_seq);
> @@ -1232,6 +1246,15 @@ int dev_change_name(struct net_device *dev, const char *newname)
> hlist_add_head_rcu(&dev->name_hlist, dev_name_hash(net, dev->name));
> write_unlock_bh(&dev_base_lock);
>
> + if (unlikely(dev->flags & IFF_UP)) {
> + struct netdev_notifier_change_info change_info = {
> + .info.dev = dev,
> + };
> +
> + call_netdevice_notifiers_info(NETDEV_CHANGE,
> + &change_info.info);
> + }
> +
> ret = call_netdevice_notifiers(NETDEV_CHANGENAME, dev);
> ret = notifier_to_errno(ret);
>
>
^ permalink raw reply
* [PATCH 2/2] drm/cirrus: drop mode_info.mode_config_initialized
From: Gerd Hoffmann @ 2019-04-02 9:04 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE, Daniel Vetter,
Dave Airlie
In-Reply-To: <20190402090459.12126-1-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/cirrus/cirrus_drv.h | 1 -
drivers/gpu/drm/cirrus/cirrus_mode.c | 9 ++-------
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h
index 915709739257..828b150cdb20 100644
--- a/drivers/gpu/drm/cirrus/cirrus_drv.h
+++ b/drivers/gpu/drm/cirrus/cirrus_drv.h
@@ -101,7 +101,6 @@ struct cirrus_crtc {
struct cirrus_fbdev;
struct cirrus_mode_info {
- bool mode_config_initialized;
struct cirrus_crtc *crtc;
/* pointer to fbdev info structure */
struct cirrus_fbdev *gfbdev;
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 32ce2c1040b4..b109cd71426f 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -575,7 +575,6 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
int ret;
drm_mode_config_init(cdev->dev);
- cdev->mode_info.mode_config_initialized = true;
cdev->dev->mode_config.max_width = CIRRUS_MAX_FB_WIDTH;
cdev->dev->mode_config.max_height = CIRRUS_MAX_FB_HEIGHT;
@@ -613,10 +612,6 @@ int cirrus_modeset_init(struct cirrus_device *cdev)
void cirrus_modeset_fini(struct cirrus_device *cdev)
{
cirrus_fbdev_fini(cdev);
-
- if (cdev->mode_info.mode_config_initialized) {
- drm_helper_force_disable_all(cdev->dev);
- drm_mode_config_cleanup(cdev->dev);
- cdev->mode_info.mode_config_initialized = false;
- }
+ drm_helper_force_disable_all(cdev->dev);
+ drm_mode_config_cleanup(cdev->dev);
}
--
2.18.1
^ permalink raw reply related
* [PATCH 1/2] drm/bochs: drop mode_config_initialized
From: Gerd Hoffmann @ 2019-04-02 9:04 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list, Daniel Vetter,
open list:DRM DRIVER FOR BOCHS VIRTUAL GPU
In-Reply-To: <20190402090459.12126-1-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/bochs/bochs.h | 1 -
drivers/gpu/drm/bochs/bochs_kms.c | 8 ++------
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bochs/bochs.h b/drivers/gpu/drm/bochs/bochs.h
index 03711394f1ed..a7f6723bebdd 100644
--- a/drivers/gpu/drm/bochs/bochs.h
+++ b/drivers/gpu/drm/bochs/bochs.h
@@ -73,7 +73,6 @@ struct bochs_device {
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
- bool mode_config_initialized;
/* ttm */
struct {
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
index 93cb27f93d39..485f9cf05e8b 100644
--- a/drivers/gpu/drm/bochs/bochs_kms.c
+++ b/drivers/gpu/drm/bochs/bochs_kms.c
@@ -267,7 +267,6 @@ const struct drm_mode_config_funcs bochs_mode_funcs = {
int bochs_kms_init(struct bochs_device *bochs)
{
drm_mode_config_init(bochs->dev);
- bochs->mode_config_initialized = true;
bochs->dev->mode_config.max_width = 8192;
bochs->dev->mode_config.max_height = 8192;
@@ -292,9 +291,6 @@ int bochs_kms_init(struct bochs_device *bochs)
void bochs_kms_fini(struct bochs_device *bochs)
{
- if (bochs->mode_config_initialized) {
- drm_atomic_helper_shutdown(bochs->dev);
- drm_mode_config_cleanup(bochs->dev);
- bochs->mode_config_initialized = false;
- }
+ drm_atomic_helper_shutdown(bochs->dev);
+ drm_mode_config_cleanup(bochs->dev);
}
--
2.18.1
^ permalink raw reply related
* Re: [PATCH 2/3] drm/bochs: add missing drm_atomic_helper_shutdown() call.
From: Daniel Vetter @ 2019-04-02 7:54 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: David Airlie, open list, Daniel Vetter, dri-devel,
open list:DRM DRIVER FOR BOCHS VIRTUAL GPU
In-Reply-To: <20190401140306.28063-3-kraxel@redhat.com>
On Mon, Apr 01, 2019 at 04:03:05PM +0200, Gerd Hoffmann wrote:
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> drivers/gpu/drm/bochs/bochs_kms.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
> index 9e7cd6b34106..93cb27f93d39 100644
> --- a/drivers/gpu/drm/bochs/bochs_kms.c
> +++ b/drivers/gpu/drm/bochs/bochs_kms.c
> @@ -293,6 +293,7 @@ int bochs_kms_init(struct bochs_device *bochs)
> void bochs_kms_fini(struct bochs_device *bochs)
> {
> if (bochs->mode_config_initialized) {
This mode_config_initialized is hilarious. I think (from looking at git
history and all) this started in radeon, back when kms was an add-on, and
radeon.ko still supported ums. Then it was dutifully copypasted into
cirrus, bochs, mgag200, hisilicon and also amdgpu.
Afaict none of these drivers need this (but I didn't bother to review
amdgpu and radeon fully). Would be nice little cleanup to follow up with.
Anyway, on your 3 patches here:
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> + drm_atomic_helper_shutdown(bochs->dev);
> drm_mode_config_cleanup(bochs->dev);
> bochs->mode_config_initialized = false;
> }
> --
> 2.18.1
>
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply
* Re: [PATCH 1/3] drm/virtio: add missing drm_atomic_helper_shutdown() call.
From: Mukesh Ojha @ 2019-04-01 15:08 UTC (permalink / raw)
To: Gerd Hoffmann, dri-devel
Cc: David Airlie, open list, Daniel Vetter,
open list:VIRTIO GPU DRIVER
In-Reply-To: <20190401140306.28063-2-kraxel@redhat.com>
Please atleast mention here why it is required?
-Mukesh
On 4/1/2019 7:33 PM, Gerd Hoffmann wrote:
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
> drivers/gpu/drm/virtio/virtgpu_display.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
> index 653ec7d0bf4d..86843a4d6102 100644
> --- a/drivers/gpu/drm/virtio/virtgpu_display.c
> +++ b/drivers/gpu/drm/virtio/virtgpu_display.c
> @@ -385,5 +385,6 @@ void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
>
> for (i = 0 ; i < vgdev->num_scanouts; ++i)
> kfree(vgdev->outputs[i].edid);
> + drm_atomic_helper_shutdown(vgdev->ddev);
> drm_mode_config_cleanup(vgdev->ddev);
> }
^ permalink raw reply
* [PATCH 3/3] drm/cirrus: add missing drm_helper_force_disable_all() call.
From: Gerd Hoffmann @ 2019-04-01 14:03 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list,
open list:DRM DRIVER FOR QEMU'S CIRRUS DEVICE, Daniel Vetter,
Dave Airlie
In-Reply-To: <20190401140306.28063-1-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/cirrus/cirrus_mode.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index 7f9bc32af685..32ce2c1040b4 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -615,6 +615,7 @@ void cirrus_modeset_fini(struct cirrus_device *cdev)
cirrus_fbdev_fini(cdev);
if (cdev->mode_info.mode_config_initialized) {
+ drm_helper_force_disable_all(cdev->dev);
drm_mode_config_cleanup(cdev->dev);
cdev->mode_info.mode_config_initialized = false;
}
--
2.18.1
^ permalink raw reply related
* [PATCH 2/3] drm/bochs: add missing drm_atomic_helper_shutdown() call.
From: Gerd Hoffmann @ 2019-04-01 14:03 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list, Daniel Vetter,
open list:DRM DRIVER FOR BOCHS VIRTUAL GPU
In-Reply-To: <20190401140306.28063-1-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/bochs/bochs_kms.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c
index 9e7cd6b34106..93cb27f93d39 100644
--- a/drivers/gpu/drm/bochs/bochs_kms.c
+++ b/drivers/gpu/drm/bochs/bochs_kms.c
@@ -293,6 +293,7 @@ int bochs_kms_init(struct bochs_device *bochs)
void bochs_kms_fini(struct bochs_device *bochs)
{
if (bochs->mode_config_initialized) {
+ drm_atomic_helper_shutdown(bochs->dev);
drm_mode_config_cleanup(bochs->dev);
bochs->mode_config_initialized = false;
}
--
2.18.1
^ permalink raw reply related
* [PATCH 1/3] drm/virtio: add missing drm_atomic_helper_shutdown() call.
From: Gerd Hoffmann @ 2019-04-01 14:03 UTC (permalink / raw)
To: dri-devel
Cc: David Airlie, open list, Daniel Vetter,
open list:VIRTIO GPU DRIVER
In-Reply-To: <20190401140306.28063-1-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
drivers/gpu/drm/virtio/virtgpu_display.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index 653ec7d0bf4d..86843a4d6102 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -385,5 +385,6 @@ void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev)
for (i = 0 ; i < vgdev->num_scanouts; ++i)
kfree(vgdev->outputs[i].edid);
+ drm_atomic_helper_shutdown(vgdev->ddev);
drm_mode_config_cleanup(vgdev->ddev);
}
--
2.18.1
^ permalink raw reply related
* Re: [PATCH] x86/paravirt: Guard against invalid cpu # in pv_vcpu_is_preempted()
From: Waiman Long @ 2019-04-01 14:01 UTC (permalink / raw)
To: Juergen Gross, Alok Kataria, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, H. Peter Anvin, Peter Zijlstra, Will Deacon
Cc: Paolo Bonzini, x86, linux-kernel, virtualization
In-Reply-To: <ad016e8f-acb5-6685-b7b6-de801c002dc6@suse.com>
On 04/01/2019 02:38 AM, Juergen Gross wrote:
> On 25/03/2019 19:03, Waiman Long wrote:
>> On 03/25/2019 12:40 PM, Juergen Gross wrote:
>>> On 25/03/2019 16:57, Waiman Long wrote:
>>>> It was found that passing an invalid cpu number to pv_vcpu_is_preempted()
>>>> might panic the kernel in a VM guest. For example,
>>>>
>>>> [ 2.531077] Oops: 0000 [#1] SMP PTI
>>>> :
>>>> [ 2.532545] Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
>>>> [ 2.533321] RIP: 0010:__raw_callee_save___kvm_vcpu_is_preempted+0x0/0x20
>>>>
>>>> To guard against this kind of kernel panic, check is added to
>>>> pv_vcpu_is_preempted() to make sure that no invalid cpu number will
>>>> be used.
>>>>
>>>> Signed-off-by: Waiman Long <longman@redhat.com>
>>>> ---
>>>> arch/x86/include/asm/paravirt.h | 6 ++++++
>>>> 1 file changed, 6 insertions(+)
>>>>
>>>> diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
>>>> index c25c38a05c1c..4cfb465dcde4 100644
>>>> --- a/arch/x86/include/asm/paravirt.h
>>>> +++ b/arch/x86/include/asm/paravirt.h
>>>> @@ -671,6 +671,12 @@ static __always_inline void pv_kick(int cpu)
>>>>
>>>> static __always_inline bool pv_vcpu_is_preempted(long cpu)
>>>> {
>>>> + /*
>>>> + * Guard against invalid cpu number or the kernel might panic.
>>>> + */
>>>> + if (WARN_ON_ONCE((unsigned long)cpu >= nr_cpu_ids))
>>>> + return false;
>>>> +
>>>> return PVOP_CALLEE1(bool, lock.vcpu_is_preempted, cpu);
>>>> }
>>> Can this really happen without being a programming error?
>> This shouldn't happen without a programming error, I think. In my case,
>> it was caused by a race condition leading to use-after-free of the cpu
>> number. However, my point is that error like that shouldn't cause the
>> kernel to panic.
>>
>>> Basically you'd need to guard all percpu area accesses to foreign cpus
>>> this way. Why is this one special?
>> It depends. If out-of-bound access can only happen with obvious
>> programming error, I don't think we need to guard against them. In this
>> case, I am not totally sure if the race condition that I found may
>> happen with existing code or not. To be prudent, I decide to send this
>> patch out.
>>
>> The race condition that I am looking at is as follows:
>>
>> CPU 0 CPU 1
>> ----- -----
>> up_write:
>> owner = NULL;
>> <release-barrier>
>> count = 0;
>>
>> <rcu-free task structure>
>>
>> rwsem_can_spin_on_owner:
>> rcu_read_lock();
>> read owner;
>> :
>> vcpu_is_preempted(owner->cpu);
>> :
>> rcu_read_unlock()
>>
>> When I tried to merge the owner into the count (clear the owner after
>> the barrier), I can reproduce the crash 100% when booting up the kernel
>> in a VM guest. However, I am not sure if the configuration above is safe
>> and is just very hard to reproduce.
>>
>> Alternatively, I can also do the cpu check before calling
>> vcpu_is_preempted().
> I think I'd prefer that.
>
>
> Juergen
>
It turns out that it may be caused by a software bug after all. You can
ignore this patch for now.
Thanks,
Longman
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH] x86/paravirt: Guard against invalid cpu # in pv_vcpu_is_preempted()
From: Juergen Gross @ 2019-04-01 6:38 UTC (permalink / raw)
To: Waiman Long, Alok Kataria, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, H. Peter Anvin, Peter Zijlstra, Will Deacon
Cc: Paolo Bonzini, x86, linux-kernel, virtualization
In-Reply-To: <5a48f5fb-799e-84f8-1fa7-eda851878d50@redhat.com>
On 25/03/2019 19:03, Waiman Long wrote:
> On 03/25/2019 12:40 PM, Juergen Gross wrote:
>> On 25/03/2019 16:57, Waiman Long wrote:
>>> It was found that passing an invalid cpu number to pv_vcpu_is_preempted()
>>> might panic the kernel in a VM guest. For example,
>>>
>>> [ 2.531077] Oops: 0000 [#1] SMP PTI
>>> :
>>> [ 2.532545] Hardware name: Red Hat KVM, BIOS 0.5.1 01/01/2011
>>> [ 2.533321] RIP: 0010:__raw_callee_save___kvm_vcpu_is_preempted+0x0/0x20
>>>
>>> To guard against this kind of kernel panic, check is added to
>>> pv_vcpu_is_preempted() to make sure that no invalid cpu number will
>>> be used.
>>>
>>> Signed-off-by: Waiman Long <longman@redhat.com>
>>> ---
>>> arch/x86/include/asm/paravirt.h | 6 ++++++
>>> 1 file changed, 6 insertions(+)
>>>
>>> diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
>>> index c25c38a05c1c..4cfb465dcde4 100644
>>> --- a/arch/x86/include/asm/paravirt.h
>>> +++ b/arch/x86/include/asm/paravirt.h
>>> @@ -671,6 +671,12 @@ static __always_inline void pv_kick(int cpu)
>>>
>>> static __always_inline bool pv_vcpu_is_preempted(long cpu)
>>> {
>>> + /*
>>> + * Guard against invalid cpu number or the kernel might panic.
>>> + */
>>> + if (WARN_ON_ONCE((unsigned long)cpu >= nr_cpu_ids))
>>> + return false;
>>> +
>>> return PVOP_CALLEE1(bool, lock.vcpu_is_preempted, cpu);
>>> }
>> Can this really happen without being a programming error?
>
> This shouldn't happen without a programming error, I think. In my case,
> it was caused by a race condition leading to use-after-free of the cpu
> number. However, my point is that error like that shouldn't cause the
> kernel to panic.
>
>> Basically you'd need to guard all percpu area accesses to foreign cpus
>> this way. Why is this one special?
>
> It depends. If out-of-bound access can only happen with obvious
> programming error, I don't think we need to guard against them. In this
> case, I am not totally sure if the race condition that I found may
> happen with existing code or not. To be prudent, I decide to send this
> patch out.
>
> The race condition that I am looking at is as follows:
>
> CPU 0 CPU 1
> ----- -----
> up_write:
> owner = NULL;
> <release-barrier>
> count = 0;
>
> <rcu-free task structure>
>
> rwsem_can_spin_on_owner:
> rcu_read_lock();
> read owner;
> :
> vcpu_is_preempted(owner->cpu);
> :
> rcu_read_unlock()
>
> When I tried to merge the owner into the count (clear the owner after
> the barrier), I can reproduce the crash 100% when booting up the kernel
> in a VM guest. However, I am not sure if the configuration above is safe
> and is just very hard to reproduce.
>
> Alternatively, I can also do the cpu check before calling
> vcpu_is_preempted().
I think I'd prefer that.
Juergen
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox