1d61fc96fSGerd Hoffmann // SPDX-License-Identifier: GPL-2.0
2d61fc96fSGerd Hoffmann /*
3d61fc96fSGerd Hoffmann * Mediated virtual PCI display host device driver
4d61fc96fSGerd Hoffmann *
5d61fc96fSGerd Hoffmann * See mdpy-defs.h for device specs
6d61fc96fSGerd Hoffmann *
7d61fc96fSGerd Hoffmann * (c) Gerd Hoffmann <kraxel@redhat.com>
8d61fc96fSGerd Hoffmann *
9d61fc96fSGerd Hoffmann * based on mtty driver which is:
10d61fc96fSGerd Hoffmann * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
11d61fc96fSGerd Hoffmann * Author: Neo Jia <cjia@nvidia.com>
12d61fc96fSGerd Hoffmann * Kirti Wankhede <kwankhede@nvidia.com>
13d61fc96fSGerd Hoffmann *
14d61fc96fSGerd Hoffmann * This program is free software; you can redistribute it and/or modify
15d61fc96fSGerd Hoffmann * it under the terms of the GNU General Public License version 2 as
16d61fc96fSGerd Hoffmann * published by the Free Software Foundation.
17d61fc96fSGerd Hoffmann */
18d61fc96fSGerd Hoffmann #include <linux/init.h>
19d61fc96fSGerd Hoffmann #include <linux/module.h>
20d61fc96fSGerd Hoffmann #include <linux/kernel.h>
21d61fc96fSGerd Hoffmann #include <linux/slab.h>
22d61fc96fSGerd Hoffmann #include <linux/vmalloc.h>
23d61fc96fSGerd Hoffmann #include <linux/cdev.h>
24d61fc96fSGerd Hoffmann #include <linux/vfio.h>
25d61fc96fSGerd Hoffmann #include <linux/iommu.h>
26d61fc96fSGerd Hoffmann #include <linux/sysfs.h>
27d61fc96fSGerd Hoffmann #include <linux/mdev.h>
28d61fc96fSGerd Hoffmann #include <linux/pci.h>
29d61fc96fSGerd Hoffmann #include <drm/drm_fourcc.h>
30d61fc96fSGerd Hoffmann #include "mdpy-defs.h"
31d61fc96fSGerd Hoffmann
32d61fc96fSGerd Hoffmann #define MDPY_NAME "mdpy"
33d61fc96fSGerd Hoffmann #define MDPY_CLASS_NAME "mdpy"
34d61fc96fSGerd Hoffmann
35d61fc96fSGerd Hoffmann #define MDPY_CONFIG_SPACE_SIZE 0xff
36d61fc96fSGerd Hoffmann #define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE
37d61fc96fSGerd Hoffmann #define MDPY_DISPLAY_REGION 16
38d61fc96fSGerd Hoffmann
39d61fc96fSGerd Hoffmann #define STORE_LE16(addr, val) (*(u16 *)addr = val)
40d61fc96fSGerd Hoffmann #define STORE_LE32(addr, val) (*(u32 *)addr = val)
41d61fc96fSGerd Hoffmann
42d61fc96fSGerd Hoffmann
43d61fc96fSGerd Hoffmann MODULE_LICENSE("GPL v2");
44d61fc96fSGerd Hoffmann
45d61fc96fSGerd Hoffmann #define MDPY_TYPE_1 "vga"
46d61fc96fSGerd Hoffmann #define MDPY_TYPE_2 "xga"
47d61fc96fSGerd Hoffmann #define MDPY_TYPE_3 "hd"
48d61fc96fSGerd Hoffmann
49da44c340SChristoph Hellwig static struct mdpy_type {
50da44c340SChristoph Hellwig struct mdev_type type;
51d61fc96fSGerd Hoffmann u32 format;
52d61fc96fSGerd Hoffmann u32 bytepp;
53d61fc96fSGerd Hoffmann u32 width;
54d61fc96fSGerd Hoffmann u32 height;
55d61fc96fSGerd Hoffmann } mdpy_types[] = {
56d61fc96fSGerd Hoffmann {
57da44c340SChristoph Hellwig .type.sysfs_name = MDPY_TYPE_1,
580bc79069SChristoph Hellwig .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_1,
59d61fc96fSGerd Hoffmann .format = DRM_FORMAT_XRGB8888,
60d61fc96fSGerd Hoffmann .bytepp = 4,
61d61fc96fSGerd Hoffmann .width = 640,
62d61fc96fSGerd Hoffmann .height = 480,
63d61fc96fSGerd Hoffmann }, {
64da44c340SChristoph Hellwig .type.sysfs_name = MDPY_TYPE_2,
650bc79069SChristoph Hellwig .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_2,
66d61fc96fSGerd Hoffmann .format = DRM_FORMAT_XRGB8888,
67d61fc96fSGerd Hoffmann .bytepp = 4,
68d61fc96fSGerd Hoffmann .width = 1024,
69d61fc96fSGerd Hoffmann .height = 768,
70d61fc96fSGerd Hoffmann }, {
71da44c340SChristoph Hellwig .type.sysfs_name = MDPY_TYPE_3,
720bc79069SChristoph Hellwig .type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_3,
73d61fc96fSGerd Hoffmann .format = DRM_FORMAT_XRGB8888,
74d61fc96fSGerd Hoffmann .bytepp = 4,
75d61fc96fSGerd Hoffmann .width = 1920,
76d61fc96fSGerd Hoffmann .height = 1080,
77d61fc96fSGerd Hoffmann },
78d61fc96fSGerd Hoffmann };
79d61fc96fSGerd Hoffmann
80da44c340SChristoph Hellwig static struct mdev_type *mdpy_mdev_types[] = {
81da44c340SChristoph Hellwig &mdpy_types[0].type,
82da44c340SChristoph Hellwig &mdpy_types[1].type,
83da44c340SChristoph Hellwig &mdpy_types[2].type,
84da44c340SChristoph Hellwig };
85da44c340SChristoph Hellwig
86d61fc96fSGerd Hoffmann static dev_t mdpy_devt;
87d61fc96fSGerd Hoffmann static struct class *mdpy_class;
88d61fc96fSGerd Hoffmann static struct cdev mdpy_cdev;
89d61fc96fSGerd Hoffmann static struct device mdpy_dev;
9089345d51SChristoph Hellwig static struct mdev_parent mdpy_parent;
91437e4136SJason Gunthorpe static const struct vfio_device_ops mdpy_dev_ops;
92d61fc96fSGerd Hoffmann
93d61fc96fSGerd Hoffmann /* State of each mdev device */
94d61fc96fSGerd Hoffmann struct mdev_state {
95437e4136SJason Gunthorpe struct vfio_device vdev;
96d61fc96fSGerd Hoffmann u8 *vconfig;
97d61fc96fSGerd Hoffmann u32 bar_mask;
98d61fc96fSGerd Hoffmann struct mutex ops_lock;
99d61fc96fSGerd Hoffmann struct mdev_device *mdev;
100d61fc96fSGerd Hoffmann struct vfio_device_info dev_info;
101d61fc96fSGerd Hoffmann
102d61fc96fSGerd Hoffmann const struct mdpy_type *type;
103d61fc96fSGerd Hoffmann u32 memsize;
104d61fc96fSGerd Hoffmann void *memblk;
105d61fc96fSGerd Hoffmann };
106d61fc96fSGerd Hoffmann
mdpy_create_config_space(struct mdev_state * mdev_state)107d61fc96fSGerd Hoffmann static void mdpy_create_config_space(struct mdev_state *mdev_state)
108d61fc96fSGerd Hoffmann {
109d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID],
110d61fc96fSGerd Hoffmann MDPY_PCI_VENDOR_ID);
111d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID],
112d61fc96fSGerd Hoffmann MDPY_PCI_DEVICE_ID);
113d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID],
114d61fc96fSGerd Hoffmann MDPY_PCI_SUBVENDOR_ID);
115d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID],
116d61fc96fSGerd Hoffmann MDPY_PCI_SUBDEVICE_ID);
117d61fc96fSGerd Hoffmann
118d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND],
119d61fc96fSGerd Hoffmann PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
120d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS],
121d61fc96fSGerd Hoffmann PCI_STATUS_CAP_LIST);
122d61fc96fSGerd Hoffmann STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE],
123d61fc96fSGerd Hoffmann PCI_CLASS_DISPLAY_OTHER);
124d61fc96fSGerd Hoffmann mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01;
125d61fc96fSGerd Hoffmann
126d61fc96fSGerd Hoffmann STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0],
127d61fc96fSGerd Hoffmann PCI_BASE_ADDRESS_SPACE_MEMORY |
128d61fc96fSGerd Hoffmann PCI_BASE_ADDRESS_MEM_TYPE_32 |
129d61fc96fSGerd Hoffmann PCI_BASE_ADDRESS_MEM_PREFETCH);
130d61fc96fSGerd Hoffmann mdev_state->bar_mask = ~(mdev_state->memsize) + 1;
131d61fc96fSGerd Hoffmann
132d61fc96fSGerd Hoffmann /* vendor specific capability for the config registers */
133d61fc96fSGerd Hoffmann mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET;
134d61fc96fSGerd Hoffmann mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */
135d61fc96fSGerd Hoffmann mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */
136d61fc96fSGerd Hoffmann mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE;
137d61fc96fSGerd Hoffmann STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET],
138d61fc96fSGerd Hoffmann mdev_state->type->format);
139d61fc96fSGerd Hoffmann STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET],
140d61fc96fSGerd Hoffmann mdev_state->type->width);
141d61fc96fSGerd Hoffmann STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET],
142d61fc96fSGerd Hoffmann mdev_state->type->height);
143d61fc96fSGerd Hoffmann }
144d61fc96fSGerd Hoffmann
handle_pci_cfg_write(struct mdev_state * mdev_state,u16 offset,char * buf,u32 count)145d61fc96fSGerd Hoffmann static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
146d61fc96fSGerd Hoffmann char *buf, u32 count)
147d61fc96fSGerd Hoffmann {
148d61fc96fSGerd Hoffmann struct device *dev = mdev_dev(mdev_state->mdev);
149d61fc96fSGerd Hoffmann u32 cfg_addr;
150d61fc96fSGerd Hoffmann
151d61fc96fSGerd Hoffmann switch (offset) {
152d61fc96fSGerd Hoffmann case PCI_BASE_ADDRESS_0:
153d61fc96fSGerd Hoffmann cfg_addr = *(u32 *)buf;
154d61fc96fSGerd Hoffmann
155d61fc96fSGerd Hoffmann if (cfg_addr == 0xffffffff) {
156d61fc96fSGerd Hoffmann cfg_addr = (cfg_addr & mdev_state->bar_mask);
157d61fc96fSGerd Hoffmann } else {
158d61fc96fSGerd Hoffmann cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK;
159d61fc96fSGerd Hoffmann if (cfg_addr)
160d61fc96fSGerd Hoffmann dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr);
161d61fc96fSGerd Hoffmann }
162d61fc96fSGerd Hoffmann
163d61fc96fSGerd Hoffmann cfg_addr |= (mdev_state->vconfig[offset] &
164d61fc96fSGerd Hoffmann ~PCI_BASE_ADDRESS_MEM_MASK);
165d61fc96fSGerd Hoffmann STORE_LE32(&mdev_state->vconfig[offset], cfg_addr);
166d61fc96fSGerd Hoffmann break;
167d61fc96fSGerd Hoffmann }
168d61fc96fSGerd Hoffmann }
169d61fc96fSGerd Hoffmann
mdev_access(struct mdev_state * mdev_state,char * buf,size_t count,loff_t pos,bool is_write)170437e4136SJason Gunthorpe static ssize_t mdev_access(struct mdev_state *mdev_state, char *buf,
171437e4136SJason Gunthorpe size_t count, loff_t pos, bool is_write)
172d61fc96fSGerd Hoffmann {
173d61fc96fSGerd Hoffmann int ret = 0;
174d61fc96fSGerd Hoffmann
175d61fc96fSGerd Hoffmann mutex_lock(&mdev_state->ops_lock);
176d61fc96fSGerd Hoffmann
177d61fc96fSGerd Hoffmann if (pos < MDPY_CONFIG_SPACE_SIZE) {
178d61fc96fSGerd Hoffmann if (is_write)
179d61fc96fSGerd Hoffmann handle_pci_cfg_write(mdev_state, pos, buf, count);
180d61fc96fSGerd Hoffmann else
181d61fc96fSGerd Hoffmann memcpy(buf, (mdev_state->vconfig + pos), count);
182d61fc96fSGerd Hoffmann
183d61fc96fSGerd Hoffmann } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) &&
184d61fc96fSGerd Hoffmann (pos + count <=
185d61fc96fSGerd Hoffmann MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) {
186d61fc96fSGerd Hoffmann pos -= MDPY_MEMORY_BAR_OFFSET;
187d61fc96fSGerd Hoffmann if (is_write)
188d61fc96fSGerd Hoffmann memcpy(mdev_state->memblk, buf, count);
189d61fc96fSGerd Hoffmann else
190d61fc96fSGerd Hoffmann memcpy(buf, mdev_state->memblk, count);
191d61fc96fSGerd Hoffmann
192d61fc96fSGerd Hoffmann } else {
193437e4136SJason Gunthorpe dev_info(mdev_state->vdev.dev,
194437e4136SJason Gunthorpe "%s: %s @0x%llx (unhandled)\n", __func__,
195437e4136SJason Gunthorpe is_write ? "WR" : "RD", pos);
196d61fc96fSGerd Hoffmann ret = -1;
197d61fc96fSGerd Hoffmann goto accessfailed;
198d61fc96fSGerd Hoffmann }
199d61fc96fSGerd Hoffmann
200d61fc96fSGerd Hoffmann ret = count;
201d61fc96fSGerd Hoffmann
202d61fc96fSGerd Hoffmann
203d61fc96fSGerd Hoffmann accessfailed:
204d61fc96fSGerd Hoffmann mutex_unlock(&mdev_state->ops_lock);
205d61fc96fSGerd Hoffmann
206d61fc96fSGerd Hoffmann return ret;
207d61fc96fSGerd Hoffmann }
208d61fc96fSGerd Hoffmann
mdpy_reset(struct mdev_state * mdev_state)209437e4136SJason Gunthorpe static int mdpy_reset(struct mdev_state *mdev_state)
210d61fc96fSGerd Hoffmann {
211d61fc96fSGerd Hoffmann u32 stride, i;
212d61fc96fSGerd Hoffmann
213d61fc96fSGerd Hoffmann /* initialize with gray gradient */
214d61fc96fSGerd Hoffmann stride = mdev_state->type->width * mdev_state->type->bytepp;
215d61fc96fSGerd Hoffmann for (i = 0; i < mdev_state->type->height; i++)
216d61fc96fSGerd Hoffmann memset(mdev_state->memblk + i * stride,
217d61fc96fSGerd Hoffmann i * 255 / mdev_state->type->height,
218d61fc96fSGerd Hoffmann stride);
219d61fc96fSGerd Hoffmann return 0;
220d61fc96fSGerd Hoffmann }
221d61fc96fSGerd Hoffmann
mdpy_init_dev(struct vfio_device * vdev)222603c09f2SYi Liu static int mdpy_init_dev(struct vfio_device *vdev)
223d61fc96fSGerd Hoffmann {
224603c09f2SYi Liu struct mdev_state *mdev_state =
225603c09f2SYi Liu container_of(vdev, struct mdev_state, vdev);
226603c09f2SYi Liu struct mdev_device *mdev = to_mdev_device(vdev->dev);
227adc9d1f6SJason Gunthorpe const struct mdpy_type *type =
228da44c340SChristoph Hellwig container_of(mdev->type, struct mdpy_type, type);
229d61fc96fSGerd Hoffmann u32 fbsize;
230603c09f2SYi Liu int ret = -ENOMEM;
231d61fc96fSGerd Hoffmann
232d61fc96fSGerd Hoffmann mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL);
233603c09f2SYi Liu if (!mdev_state->vconfig)
234603c09f2SYi Liu return ret;
235d61fc96fSGerd Hoffmann
236d61fc96fSGerd Hoffmann fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp);
237d61fc96fSGerd Hoffmann
238d61fc96fSGerd Hoffmann mdev_state->memblk = vmalloc_user(fbsize);
239603c09f2SYi Liu if (!mdev_state->memblk)
240603c09f2SYi Liu goto out_vconfig;
241d61fc96fSGerd Hoffmann
242d61fc96fSGerd Hoffmann mutex_init(&mdev_state->ops_lock);
243d61fc96fSGerd Hoffmann mdev_state->mdev = mdev;
244d61fc96fSGerd Hoffmann mdev_state->type = type;
245d61fc96fSGerd Hoffmann mdev_state->memsize = fbsize;
246d61fc96fSGerd Hoffmann mdpy_create_config_space(mdev_state);
247437e4136SJason Gunthorpe mdpy_reset(mdev_state);
248d61fc96fSGerd Hoffmann
2490bc79069SChristoph Hellwig dev_info(vdev->dev, "%s: %s (%dx%d)\n", __func__, type->type.pretty_name,
2500bc79069SChristoph Hellwig type->width, type->height);
251603c09f2SYi Liu return 0;
252603c09f2SYi Liu
253603c09f2SYi Liu out_vconfig:
254603c09f2SYi Liu kfree(mdev_state->vconfig);
255603c09f2SYi Liu return ret;
256603c09f2SYi Liu }
257603c09f2SYi Liu
mdpy_probe(struct mdev_device * mdev)258603c09f2SYi Liu static int mdpy_probe(struct mdev_device *mdev)
259603c09f2SYi Liu {
260603c09f2SYi Liu struct mdev_state *mdev_state;
261603c09f2SYi Liu int ret;
262603c09f2SYi Liu
263603c09f2SYi Liu mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev,
264603c09f2SYi Liu &mdpy_dev_ops);
265603c09f2SYi Liu if (IS_ERR(mdev_state))
266603c09f2SYi Liu return PTR_ERR(mdev_state);
267437e4136SJason Gunthorpe
268c68ea0d0SChristoph Hellwig ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev);
269ae03c377SMax Gurtovoy if (ret)
270603c09f2SYi Liu goto err_put_vdev;
271437e4136SJason Gunthorpe dev_set_drvdata(&mdev->dev, mdev_state);
272d61fc96fSGerd Hoffmann return 0;
273603c09f2SYi Liu
274603c09f2SYi Liu err_put_vdev:
275603c09f2SYi Liu vfio_put_device(&mdev_state->vdev);
276ae03c377SMax Gurtovoy return ret;
277d61fc96fSGerd Hoffmann }
278d61fc96fSGerd Hoffmann
mdpy_release_dev(struct vfio_device * vdev)279603c09f2SYi Liu static void mdpy_release_dev(struct vfio_device *vdev)
280603c09f2SYi Liu {
281603c09f2SYi Liu struct mdev_state *mdev_state =
282603c09f2SYi Liu container_of(vdev, struct mdev_state, vdev);
283603c09f2SYi Liu
284603c09f2SYi Liu vfree(mdev_state->memblk);
285603c09f2SYi Liu kfree(mdev_state->vconfig);
286603c09f2SYi Liu }
287603c09f2SYi Liu
mdpy_remove(struct mdev_device * mdev)288437e4136SJason Gunthorpe static void mdpy_remove(struct mdev_device *mdev)
289d61fc96fSGerd Hoffmann {
290437e4136SJason Gunthorpe struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev);
291d61fc96fSGerd Hoffmann
292437e4136SJason Gunthorpe dev_info(&mdev->dev, "%s\n", __func__);
293d61fc96fSGerd Hoffmann
294437e4136SJason Gunthorpe vfio_unregister_group_dev(&mdev_state->vdev);
295603c09f2SYi Liu vfio_put_device(&mdev_state->vdev);
296d61fc96fSGerd Hoffmann }
297d61fc96fSGerd Hoffmann
mdpy_read(struct vfio_device * vdev,char __user * buf,size_t count,loff_t * ppos)298437e4136SJason Gunthorpe static ssize_t mdpy_read(struct vfio_device *vdev, char __user *buf,
299d61fc96fSGerd Hoffmann size_t count, loff_t *ppos)
300d61fc96fSGerd Hoffmann {
301437e4136SJason Gunthorpe struct mdev_state *mdev_state =
302437e4136SJason Gunthorpe container_of(vdev, struct mdev_state, vdev);
303d61fc96fSGerd Hoffmann unsigned int done = 0;
304d61fc96fSGerd Hoffmann int ret;
305d61fc96fSGerd Hoffmann
306d61fc96fSGerd Hoffmann while (count) {
307d61fc96fSGerd Hoffmann size_t filled;
308d61fc96fSGerd Hoffmann
309d61fc96fSGerd Hoffmann if (count >= 4 && !(*ppos % 4)) {
310d61fc96fSGerd Hoffmann u32 val;
311d61fc96fSGerd Hoffmann
312437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
313d61fc96fSGerd Hoffmann *ppos, false);
314d61fc96fSGerd Hoffmann if (ret <= 0)
315d61fc96fSGerd Hoffmann goto read_err;
316d61fc96fSGerd Hoffmann
317d61fc96fSGerd Hoffmann if (copy_to_user(buf, &val, sizeof(val)))
318d61fc96fSGerd Hoffmann goto read_err;
319d61fc96fSGerd Hoffmann
320d61fc96fSGerd Hoffmann filled = 4;
321d61fc96fSGerd Hoffmann } else if (count >= 2 && !(*ppos % 2)) {
322d61fc96fSGerd Hoffmann u16 val;
323d61fc96fSGerd Hoffmann
324437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
325d61fc96fSGerd Hoffmann *ppos, false);
326d61fc96fSGerd Hoffmann if (ret <= 0)
327d61fc96fSGerd Hoffmann goto read_err;
328d61fc96fSGerd Hoffmann
329d61fc96fSGerd Hoffmann if (copy_to_user(buf, &val, sizeof(val)))
330d61fc96fSGerd Hoffmann goto read_err;
331d61fc96fSGerd Hoffmann
332d61fc96fSGerd Hoffmann filled = 2;
333d61fc96fSGerd Hoffmann } else {
334d61fc96fSGerd Hoffmann u8 val;
335d61fc96fSGerd Hoffmann
336437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
337d61fc96fSGerd Hoffmann *ppos, false);
338d61fc96fSGerd Hoffmann if (ret <= 0)
339d61fc96fSGerd Hoffmann goto read_err;
340d61fc96fSGerd Hoffmann
341d61fc96fSGerd Hoffmann if (copy_to_user(buf, &val, sizeof(val)))
342d61fc96fSGerd Hoffmann goto read_err;
343d61fc96fSGerd Hoffmann
344d61fc96fSGerd Hoffmann filled = 1;
345d61fc96fSGerd Hoffmann }
346d61fc96fSGerd Hoffmann
347d61fc96fSGerd Hoffmann count -= filled;
348d61fc96fSGerd Hoffmann done += filled;
349d61fc96fSGerd Hoffmann *ppos += filled;
350d61fc96fSGerd Hoffmann buf += filled;
351d61fc96fSGerd Hoffmann }
352d61fc96fSGerd Hoffmann
353d61fc96fSGerd Hoffmann return done;
354d61fc96fSGerd Hoffmann
355d61fc96fSGerd Hoffmann read_err:
356d61fc96fSGerd Hoffmann return -EFAULT;
357d61fc96fSGerd Hoffmann }
358d61fc96fSGerd Hoffmann
mdpy_write(struct vfio_device * vdev,const char __user * buf,size_t count,loff_t * ppos)359437e4136SJason Gunthorpe static ssize_t mdpy_write(struct vfio_device *vdev, const char __user *buf,
360d61fc96fSGerd Hoffmann size_t count, loff_t *ppos)
361d61fc96fSGerd Hoffmann {
362437e4136SJason Gunthorpe struct mdev_state *mdev_state =
363437e4136SJason Gunthorpe container_of(vdev, struct mdev_state, vdev);
364d61fc96fSGerd Hoffmann unsigned int done = 0;
365d61fc96fSGerd Hoffmann int ret;
366d61fc96fSGerd Hoffmann
367d61fc96fSGerd Hoffmann while (count) {
368d61fc96fSGerd Hoffmann size_t filled;
369d61fc96fSGerd Hoffmann
370d61fc96fSGerd Hoffmann if (count >= 4 && !(*ppos % 4)) {
371d61fc96fSGerd Hoffmann u32 val;
372d61fc96fSGerd Hoffmann
373d61fc96fSGerd Hoffmann if (copy_from_user(&val, buf, sizeof(val)))
374d61fc96fSGerd Hoffmann goto write_err;
375d61fc96fSGerd Hoffmann
376437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
377d61fc96fSGerd Hoffmann *ppos, true);
378d61fc96fSGerd Hoffmann if (ret <= 0)
379d61fc96fSGerd Hoffmann goto write_err;
380d61fc96fSGerd Hoffmann
381d61fc96fSGerd Hoffmann filled = 4;
382d61fc96fSGerd Hoffmann } else if (count >= 2 && !(*ppos % 2)) {
383d61fc96fSGerd Hoffmann u16 val;
384d61fc96fSGerd Hoffmann
385d61fc96fSGerd Hoffmann if (copy_from_user(&val, buf, sizeof(val)))
386d61fc96fSGerd Hoffmann goto write_err;
387d61fc96fSGerd Hoffmann
388437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
389d61fc96fSGerd Hoffmann *ppos, true);
390d61fc96fSGerd Hoffmann if (ret <= 0)
391d61fc96fSGerd Hoffmann goto write_err;
392d61fc96fSGerd Hoffmann
393d61fc96fSGerd Hoffmann filled = 2;
394d61fc96fSGerd Hoffmann } else {
395d61fc96fSGerd Hoffmann u8 val;
396d61fc96fSGerd Hoffmann
397d61fc96fSGerd Hoffmann if (copy_from_user(&val, buf, sizeof(val)))
398d61fc96fSGerd Hoffmann goto write_err;
399d61fc96fSGerd Hoffmann
400437e4136SJason Gunthorpe ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
401d61fc96fSGerd Hoffmann *ppos, true);
402d61fc96fSGerd Hoffmann if (ret <= 0)
403d61fc96fSGerd Hoffmann goto write_err;
404d61fc96fSGerd Hoffmann
405d61fc96fSGerd Hoffmann filled = 1;
406d61fc96fSGerd Hoffmann }
407d61fc96fSGerd Hoffmann count -= filled;
408d61fc96fSGerd Hoffmann done += filled;
409d61fc96fSGerd Hoffmann *ppos += filled;
410d61fc96fSGerd Hoffmann buf += filled;
411d61fc96fSGerd Hoffmann }
412d61fc96fSGerd Hoffmann
413d61fc96fSGerd Hoffmann return done;
414d61fc96fSGerd Hoffmann write_err:
415d61fc96fSGerd Hoffmann return -EFAULT;
416d61fc96fSGerd Hoffmann }
417d61fc96fSGerd Hoffmann
mdpy_mmap(struct vfio_device * vdev,struct vm_area_struct * vma)418437e4136SJason Gunthorpe static int mdpy_mmap(struct vfio_device *vdev, struct vm_area_struct *vma)
419d61fc96fSGerd Hoffmann {
420437e4136SJason Gunthorpe struct mdev_state *mdev_state =
421437e4136SJason Gunthorpe container_of(vdev, struct mdev_state, vdev);
422d61fc96fSGerd Hoffmann
423d61fc96fSGerd Hoffmann if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT)
424d61fc96fSGerd Hoffmann return -EINVAL;
425d61fc96fSGerd Hoffmann if (vma->vm_end < vma->vm_start)
426d61fc96fSGerd Hoffmann return -EINVAL;
427d61fc96fSGerd Hoffmann if (vma->vm_end - vma->vm_start > mdev_state->memsize)
428d61fc96fSGerd Hoffmann return -EINVAL;
429d61fc96fSGerd Hoffmann if ((vma->vm_flags & VM_SHARED) == 0)
430d61fc96fSGerd Hoffmann return -EINVAL;
431d61fc96fSGerd Hoffmann
4328c2acfe8SChristoph Hellwig return remap_vmalloc_range(vma, mdev_state->memblk, 0);
433d61fc96fSGerd Hoffmann }
434d61fc96fSGerd Hoffmann
mdpy_get_region_info(struct mdev_state * mdev_state,struct vfio_region_info * region_info,u16 * cap_type_id,void ** cap_type)435437e4136SJason Gunthorpe static int mdpy_get_region_info(struct mdev_state *mdev_state,
436d61fc96fSGerd Hoffmann struct vfio_region_info *region_info,
437d61fc96fSGerd Hoffmann u16 *cap_type_id, void **cap_type)
438d61fc96fSGerd Hoffmann {
439d61fc96fSGerd Hoffmann if (region_info->index >= VFIO_PCI_NUM_REGIONS &&
440d61fc96fSGerd Hoffmann region_info->index != MDPY_DISPLAY_REGION)
441d61fc96fSGerd Hoffmann return -EINVAL;
442d61fc96fSGerd Hoffmann
443d61fc96fSGerd Hoffmann switch (region_info->index) {
444d61fc96fSGerd Hoffmann case VFIO_PCI_CONFIG_REGION_INDEX:
445d61fc96fSGerd Hoffmann region_info->offset = 0;
446d61fc96fSGerd Hoffmann region_info->size = MDPY_CONFIG_SPACE_SIZE;
447d61fc96fSGerd Hoffmann region_info->flags = (VFIO_REGION_INFO_FLAG_READ |
448d61fc96fSGerd Hoffmann VFIO_REGION_INFO_FLAG_WRITE);
449d61fc96fSGerd Hoffmann break;
450d61fc96fSGerd Hoffmann case VFIO_PCI_BAR0_REGION_INDEX:
451d61fc96fSGerd Hoffmann case MDPY_DISPLAY_REGION:
452d61fc96fSGerd Hoffmann region_info->offset = MDPY_MEMORY_BAR_OFFSET;
453d61fc96fSGerd Hoffmann region_info->size = mdev_state->memsize;
454d61fc96fSGerd Hoffmann region_info->flags = (VFIO_REGION_INFO_FLAG_READ |
455d61fc96fSGerd Hoffmann VFIO_REGION_INFO_FLAG_WRITE |
456d61fc96fSGerd Hoffmann VFIO_REGION_INFO_FLAG_MMAP);
457d61fc96fSGerd Hoffmann break;
458d61fc96fSGerd Hoffmann default:
459d61fc96fSGerd Hoffmann region_info->size = 0;
460d61fc96fSGerd Hoffmann region_info->offset = 0;
461d61fc96fSGerd Hoffmann region_info->flags = 0;
462d61fc96fSGerd Hoffmann }
463d61fc96fSGerd Hoffmann
464d61fc96fSGerd Hoffmann return 0;
465d61fc96fSGerd Hoffmann }
466d61fc96fSGerd Hoffmann
mdpy_get_irq_info(struct vfio_irq_info * irq_info)467437e4136SJason Gunthorpe static int mdpy_get_irq_info(struct vfio_irq_info *irq_info)
468d61fc96fSGerd Hoffmann {
469d61fc96fSGerd Hoffmann irq_info->count = 0;
470d61fc96fSGerd Hoffmann return 0;
471d61fc96fSGerd Hoffmann }
472d61fc96fSGerd Hoffmann
mdpy_get_device_info(struct vfio_device_info * dev_info)473437e4136SJason Gunthorpe static int mdpy_get_device_info(struct vfio_device_info *dev_info)
474d61fc96fSGerd Hoffmann {
475d61fc96fSGerd Hoffmann dev_info->flags = VFIO_DEVICE_FLAGS_PCI;
476d61fc96fSGerd Hoffmann dev_info->num_regions = VFIO_PCI_NUM_REGIONS;
477d61fc96fSGerd Hoffmann dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
478d61fc96fSGerd Hoffmann return 0;
479d61fc96fSGerd Hoffmann }
480d61fc96fSGerd Hoffmann
mdpy_query_gfx_plane(struct mdev_state * mdev_state,struct vfio_device_gfx_plane_info * plane)481437e4136SJason Gunthorpe static int mdpy_query_gfx_plane(struct mdev_state *mdev_state,
482d61fc96fSGerd Hoffmann struct vfio_device_gfx_plane_info *plane)
483d61fc96fSGerd Hoffmann {
484d61fc96fSGerd Hoffmann if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) {
485d61fc96fSGerd Hoffmann if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE |
486d61fc96fSGerd Hoffmann VFIO_GFX_PLANE_TYPE_REGION))
487d61fc96fSGerd Hoffmann return 0;
488d61fc96fSGerd Hoffmann return -EINVAL;
489d61fc96fSGerd Hoffmann }
490d61fc96fSGerd Hoffmann
491d61fc96fSGerd Hoffmann if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION)
492d61fc96fSGerd Hoffmann return -EINVAL;
493d61fc96fSGerd Hoffmann
494d61fc96fSGerd Hoffmann plane->drm_format = mdev_state->type->format;
495d61fc96fSGerd Hoffmann plane->width = mdev_state->type->width;
496d61fc96fSGerd Hoffmann plane->height = mdev_state->type->height;
497d61fc96fSGerd Hoffmann plane->stride = (mdev_state->type->width *
498d61fc96fSGerd Hoffmann mdev_state->type->bytepp);
499d61fc96fSGerd Hoffmann plane->size = mdev_state->memsize;
500d61fc96fSGerd Hoffmann plane->region_index = MDPY_DISPLAY_REGION;
501d61fc96fSGerd Hoffmann
502d61fc96fSGerd Hoffmann /* unused */
503d61fc96fSGerd Hoffmann plane->drm_format_mod = 0;
504d61fc96fSGerd Hoffmann plane->x_pos = 0;
505d61fc96fSGerd Hoffmann plane->y_pos = 0;
506d61fc96fSGerd Hoffmann plane->x_hot = 0;
507d61fc96fSGerd Hoffmann plane->y_hot = 0;
508d61fc96fSGerd Hoffmann
509d61fc96fSGerd Hoffmann return 0;
510d61fc96fSGerd Hoffmann }
511d61fc96fSGerd Hoffmann
mdpy_ioctl(struct vfio_device * vdev,unsigned int cmd,unsigned long arg)512437e4136SJason Gunthorpe static long mdpy_ioctl(struct vfio_device *vdev, unsigned int cmd,
513d61fc96fSGerd Hoffmann unsigned long arg)
514d61fc96fSGerd Hoffmann {
515d61fc96fSGerd Hoffmann int ret = 0;
516d61fc96fSGerd Hoffmann unsigned long minsz;
517437e4136SJason Gunthorpe struct mdev_state *mdev_state =
518437e4136SJason Gunthorpe container_of(vdev, struct mdev_state, vdev);
519d61fc96fSGerd Hoffmann
520d61fc96fSGerd Hoffmann switch (cmd) {
521d61fc96fSGerd Hoffmann case VFIO_DEVICE_GET_INFO:
522d61fc96fSGerd Hoffmann {
523d61fc96fSGerd Hoffmann struct vfio_device_info info;
524d61fc96fSGerd Hoffmann
525d61fc96fSGerd Hoffmann minsz = offsetofend(struct vfio_device_info, num_irqs);
526d61fc96fSGerd Hoffmann
527d61fc96fSGerd Hoffmann if (copy_from_user(&info, (void __user *)arg, minsz))
528d61fc96fSGerd Hoffmann return -EFAULT;
529d61fc96fSGerd Hoffmann
530d61fc96fSGerd Hoffmann if (info.argsz < minsz)
531d61fc96fSGerd Hoffmann return -EINVAL;
532d61fc96fSGerd Hoffmann
533437e4136SJason Gunthorpe ret = mdpy_get_device_info(&info);
534d61fc96fSGerd Hoffmann if (ret)
535d61fc96fSGerd Hoffmann return ret;
536d61fc96fSGerd Hoffmann
537d61fc96fSGerd Hoffmann memcpy(&mdev_state->dev_info, &info, sizeof(info));
538d61fc96fSGerd Hoffmann
539d61fc96fSGerd Hoffmann if (copy_to_user((void __user *)arg, &info, minsz))
540d61fc96fSGerd Hoffmann return -EFAULT;
541d61fc96fSGerd Hoffmann
542d61fc96fSGerd Hoffmann return 0;
543d61fc96fSGerd Hoffmann }
544d61fc96fSGerd Hoffmann case VFIO_DEVICE_GET_REGION_INFO:
545d61fc96fSGerd Hoffmann {
546d61fc96fSGerd Hoffmann struct vfio_region_info info;
547d61fc96fSGerd Hoffmann u16 cap_type_id = 0;
548d61fc96fSGerd Hoffmann void *cap_type = NULL;
549d61fc96fSGerd Hoffmann
550d61fc96fSGerd Hoffmann minsz = offsetofend(struct vfio_region_info, offset);
551d61fc96fSGerd Hoffmann
552d61fc96fSGerd Hoffmann if (copy_from_user(&info, (void __user *)arg, minsz))
553d61fc96fSGerd Hoffmann return -EFAULT;
554d61fc96fSGerd Hoffmann
555d61fc96fSGerd Hoffmann if (info.argsz < minsz)
556d61fc96fSGerd Hoffmann return -EINVAL;
557d61fc96fSGerd Hoffmann
558437e4136SJason Gunthorpe ret = mdpy_get_region_info(mdev_state, &info, &cap_type_id,
559d61fc96fSGerd Hoffmann &cap_type);
560d61fc96fSGerd Hoffmann if (ret)
561d61fc96fSGerd Hoffmann return ret;
562d61fc96fSGerd Hoffmann
563d61fc96fSGerd Hoffmann if (copy_to_user((void __user *)arg, &info, minsz))
564d61fc96fSGerd Hoffmann return -EFAULT;
565d61fc96fSGerd Hoffmann
566d61fc96fSGerd Hoffmann return 0;
567d61fc96fSGerd Hoffmann }
568d61fc96fSGerd Hoffmann
569d61fc96fSGerd Hoffmann case VFIO_DEVICE_GET_IRQ_INFO:
570d61fc96fSGerd Hoffmann {
571d61fc96fSGerd Hoffmann struct vfio_irq_info info;
572d61fc96fSGerd Hoffmann
573d61fc96fSGerd Hoffmann minsz = offsetofend(struct vfio_irq_info, count);
574d61fc96fSGerd Hoffmann
575d61fc96fSGerd Hoffmann if (copy_from_user(&info, (void __user *)arg, minsz))
576d61fc96fSGerd Hoffmann return -EFAULT;
577d61fc96fSGerd Hoffmann
578d61fc96fSGerd Hoffmann if ((info.argsz < minsz) ||
579d61fc96fSGerd Hoffmann (info.index >= mdev_state->dev_info.num_irqs))
580d61fc96fSGerd Hoffmann return -EINVAL;
581d61fc96fSGerd Hoffmann
582437e4136SJason Gunthorpe ret = mdpy_get_irq_info(&info);
583d61fc96fSGerd Hoffmann if (ret)
584d61fc96fSGerd Hoffmann return ret;
585d61fc96fSGerd Hoffmann
586d61fc96fSGerd Hoffmann if (copy_to_user((void __user *)arg, &info, minsz))
587d61fc96fSGerd Hoffmann return -EFAULT;
588d61fc96fSGerd Hoffmann
589d61fc96fSGerd Hoffmann return 0;
590d61fc96fSGerd Hoffmann }
591d61fc96fSGerd Hoffmann
592d61fc96fSGerd Hoffmann case VFIO_DEVICE_QUERY_GFX_PLANE:
593d61fc96fSGerd Hoffmann {
594d61fc96fSGerd Hoffmann struct vfio_device_gfx_plane_info plane;
595d61fc96fSGerd Hoffmann
596d61fc96fSGerd Hoffmann minsz = offsetofend(struct vfio_device_gfx_plane_info,
597d61fc96fSGerd Hoffmann region_index);
598d61fc96fSGerd Hoffmann
599d61fc96fSGerd Hoffmann if (copy_from_user(&plane, (void __user *)arg, minsz))
600d61fc96fSGerd Hoffmann return -EFAULT;
601d61fc96fSGerd Hoffmann
602d61fc96fSGerd Hoffmann if (plane.argsz < minsz)
603d61fc96fSGerd Hoffmann return -EINVAL;
604d61fc96fSGerd Hoffmann
605437e4136SJason Gunthorpe ret = mdpy_query_gfx_plane(mdev_state, &plane);
606d61fc96fSGerd Hoffmann if (ret)
607d61fc96fSGerd Hoffmann return ret;
608d61fc96fSGerd Hoffmann
609d61fc96fSGerd Hoffmann if (copy_to_user((void __user *)arg, &plane, minsz))
610d61fc96fSGerd Hoffmann return -EFAULT;
611d61fc96fSGerd Hoffmann
612d61fc96fSGerd Hoffmann return 0;
613d61fc96fSGerd Hoffmann }
614d61fc96fSGerd Hoffmann
615d61fc96fSGerd Hoffmann case VFIO_DEVICE_SET_IRQS:
616d61fc96fSGerd Hoffmann return -EINVAL;
617d61fc96fSGerd Hoffmann
618d61fc96fSGerd Hoffmann case VFIO_DEVICE_RESET:
619437e4136SJason Gunthorpe return mdpy_reset(mdev_state);
620d61fc96fSGerd Hoffmann }
621d61fc96fSGerd Hoffmann return -ENOTTY;
622d61fc96fSGerd Hoffmann }
623d61fc96fSGerd Hoffmann
624d61fc96fSGerd Hoffmann static ssize_t
resolution_show(struct device * dev,struct device_attribute * attr,char * buf)625d61fc96fSGerd Hoffmann resolution_show(struct device *dev, struct device_attribute *attr,
626d61fc96fSGerd Hoffmann char *buf)
627d61fc96fSGerd Hoffmann {
628437e4136SJason Gunthorpe struct mdev_state *mdev_state = dev_get_drvdata(dev);
629d61fc96fSGerd Hoffmann
630d61fc96fSGerd Hoffmann return sprintf(buf, "%dx%d\n",
631d61fc96fSGerd Hoffmann mdev_state->type->width,
632d61fc96fSGerd Hoffmann mdev_state->type->height);
633d61fc96fSGerd Hoffmann }
634d61fc96fSGerd Hoffmann static DEVICE_ATTR_RO(resolution);
635d61fc96fSGerd Hoffmann
636d61fc96fSGerd Hoffmann static struct attribute *mdev_dev_attrs[] = {
637d61fc96fSGerd Hoffmann &dev_attr_resolution.attr,
638d61fc96fSGerd Hoffmann NULL,
639d61fc96fSGerd Hoffmann };
640d61fc96fSGerd Hoffmann
641d61fc96fSGerd Hoffmann static const struct attribute_group mdev_dev_group = {
642d61fc96fSGerd Hoffmann .name = "vendor",
643d61fc96fSGerd Hoffmann .attrs = mdev_dev_attrs,
644d61fc96fSGerd Hoffmann };
645d61fc96fSGerd Hoffmann
6466cbf507fSJason Gunthorpe static const struct attribute_group *mdev_dev_groups[] = {
647d61fc96fSGerd Hoffmann &mdev_dev_group,
648d61fc96fSGerd Hoffmann NULL,
649d61fc96fSGerd Hoffmann };
650d61fc96fSGerd Hoffmann
mdpy_show_description(struct mdev_type * mtype,char * buf)651685a1537SChristoph Hellwig static ssize_t mdpy_show_description(struct mdev_type *mtype, char *buf)
652d61fc96fSGerd Hoffmann {
653da44c340SChristoph Hellwig struct mdpy_type *type = container_of(mtype, struct mdpy_type, type);
654d61fc96fSGerd Hoffmann
655d61fc96fSGerd Hoffmann return sprintf(buf, "virtual display, %dx%d framebuffer\n",
656698f99edSDan Carpenter type->width, type->height);
657d61fc96fSGerd Hoffmann }
658d61fc96fSGerd Hoffmann
659437e4136SJason Gunthorpe static const struct vfio_device_ops mdpy_dev_ops = {
660603c09f2SYi Liu .init = mdpy_init_dev,
661603c09f2SYi Liu .release = mdpy_release_dev,
662d61fc96fSGerd Hoffmann .read = mdpy_read,
663d61fc96fSGerd Hoffmann .write = mdpy_write,
664d61fc96fSGerd Hoffmann .ioctl = mdpy_ioctl,
665d61fc96fSGerd Hoffmann .mmap = mdpy_mmap,
6660a782d15SYi Liu .bind_iommufd = vfio_iommufd_emulated_bind,
6670a782d15SYi Liu .unbind_iommufd = vfio_iommufd_emulated_unbind,
6680a782d15SYi Liu .attach_ioas = vfio_iommufd_emulated_attach_ioas,
669*8cfa7186SYi Liu .detach_ioas = vfio_iommufd_emulated_detach_ioas,
670d61fc96fSGerd Hoffmann };
671d61fc96fSGerd Hoffmann
672437e4136SJason Gunthorpe static struct mdev_driver mdpy_driver = {
673290aac5dSJason Gunthorpe .device_api = VFIO_DEVICE_API_PCI_STRING,
6749c799c22SJason Gunthorpe .max_instances = 4,
675437e4136SJason Gunthorpe .driver = {
676437e4136SJason Gunthorpe .name = "mdpy",
677437e4136SJason Gunthorpe .owner = THIS_MODULE,
678437e4136SJason Gunthorpe .mod_name = KBUILD_MODNAME,
679437e4136SJason Gunthorpe .dev_groups = mdev_dev_groups,
680437e4136SJason Gunthorpe },
681437e4136SJason Gunthorpe .probe = mdpy_probe,
682437e4136SJason Gunthorpe .remove = mdpy_remove,
683685a1537SChristoph Hellwig .show_description = mdpy_show_description,
684437e4136SJason Gunthorpe };
685437e4136SJason Gunthorpe
686d61fc96fSGerd Hoffmann static const struct file_operations vd_fops = {
687d61fc96fSGerd Hoffmann .owner = THIS_MODULE,
688d61fc96fSGerd Hoffmann };
689d61fc96fSGerd Hoffmann
mdpy_device_release(struct device * dev)690d61fc96fSGerd Hoffmann static void mdpy_device_release(struct device *dev)
691d61fc96fSGerd Hoffmann {
692d61fc96fSGerd Hoffmann /* nothing */
693d61fc96fSGerd Hoffmann }
694d61fc96fSGerd Hoffmann
mdpy_dev_init(void)695d61fc96fSGerd Hoffmann static int __init mdpy_dev_init(void)
696d61fc96fSGerd Hoffmann {
697d61fc96fSGerd Hoffmann int ret = 0;
698d61fc96fSGerd Hoffmann
69918bc04bcSChengguang Xu ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK + 1, MDPY_NAME);
700d61fc96fSGerd Hoffmann if (ret < 0) {
701d61fc96fSGerd Hoffmann pr_err("Error: failed to register mdpy_dev, err: %d\n", ret);
702d61fc96fSGerd Hoffmann return ret;
703d61fc96fSGerd Hoffmann }
704d61fc96fSGerd Hoffmann cdev_init(&mdpy_cdev, &vd_fops);
70518bc04bcSChengguang Xu cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK + 1);
706d61fc96fSGerd Hoffmann pr_info("%s: major %d\n", __func__, MAJOR(mdpy_devt));
707d61fc96fSGerd Hoffmann
708437e4136SJason Gunthorpe ret = mdev_register_driver(&mdpy_driver);
709437e4136SJason Gunthorpe if (ret)
710437e4136SJason Gunthorpe goto err_cdev;
711437e4136SJason Gunthorpe
7121aaba11dSGreg Kroah-Hartman mdpy_class = class_create(MDPY_CLASS_NAME);
713d61fc96fSGerd Hoffmann if (IS_ERR(mdpy_class)) {
714d61fc96fSGerd Hoffmann pr_err("Error: failed to register mdpy_dev class\n");
715d61fc96fSGerd Hoffmann ret = PTR_ERR(mdpy_class);
716437e4136SJason Gunthorpe goto err_driver;
717d61fc96fSGerd Hoffmann }
718d61fc96fSGerd Hoffmann mdpy_dev.class = mdpy_class;
719d61fc96fSGerd Hoffmann mdpy_dev.release = mdpy_device_release;
720d61fc96fSGerd Hoffmann dev_set_name(&mdpy_dev, "%s", MDPY_NAME);
721d61fc96fSGerd Hoffmann
722d61fc96fSGerd Hoffmann ret = device_register(&mdpy_dev);
723d61fc96fSGerd Hoffmann if (ret)
724ce389573SAlex Williamson goto err_put;
725d61fc96fSGerd Hoffmann
726da44c340SChristoph Hellwig ret = mdev_register_parent(&mdpy_parent, &mdpy_dev, &mdpy_driver,
727da44c340SChristoph Hellwig mdpy_mdev_types,
728da44c340SChristoph Hellwig ARRAY_SIZE(mdpy_mdev_types));
729d61fc96fSGerd Hoffmann if (ret)
730437e4136SJason Gunthorpe goto err_device;
731d61fc96fSGerd Hoffmann
732d61fc96fSGerd Hoffmann return 0;
733d61fc96fSGerd Hoffmann
734437e4136SJason Gunthorpe err_device:
735ce389573SAlex Williamson device_del(&mdpy_dev);
736ce389573SAlex Williamson err_put:
737ce389573SAlex Williamson put_device(&mdpy_dev);
738d61fc96fSGerd Hoffmann class_destroy(mdpy_class);
739437e4136SJason Gunthorpe err_driver:
740437e4136SJason Gunthorpe mdev_unregister_driver(&mdpy_driver);
741437e4136SJason Gunthorpe err_cdev:
742d61fc96fSGerd Hoffmann cdev_del(&mdpy_cdev);
74318bc04bcSChengguang Xu unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
744d61fc96fSGerd Hoffmann return ret;
745d61fc96fSGerd Hoffmann }
746d61fc96fSGerd Hoffmann
mdpy_dev_exit(void)747d61fc96fSGerd Hoffmann static void __exit mdpy_dev_exit(void)
748d61fc96fSGerd Hoffmann {
749d61fc96fSGerd Hoffmann mdpy_dev.bus = NULL;
75089345d51SChristoph Hellwig mdev_unregister_parent(&mdpy_parent);
751d61fc96fSGerd Hoffmann
752d61fc96fSGerd Hoffmann device_unregister(&mdpy_dev);
753437e4136SJason Gunthorpe mdev_unregister_driver(&mdpy_driver);
754d61fc96fSGerd Hoffmann cdev_del(&mdpy_cdev);
75518bc04bcSChengguang Xu unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
756d61fc96fSGerd Hoffmann class_destroy(mdpy_class);
757d61fc96fSGerd Hoffmann mdpy_class = NULL;
758d61fc96fSGerd Hoffmann }
759d61fc96fSGerd Hoffmann
7609c799c22SJason Gunthorpe module_param_named(count, mdpy_driver.max_instances, int, 0444);
7619c799c22SJason Gunthorpe MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices");
7629c799c22SJason Gunthorpe
763d61fc96fSGerd Hoffmann module_init(mdpy_dev_init)
764d61fc96fSGerd Hoffmann module_exit(mdpy_dev_exit)
765