18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Mediated virtual PCI display host device driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * See mdpy-defs.h for device specs 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * (c) Gerd Hoffmann <kraxel@redhat.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * based on mtty driver which is: 108c2ecf20Sopenharmony_ci * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 118c2ecf20Sopenharmony_ci * Author: Neo Jia <cjia@nvidia.com> 128c2ecf20Sopenharmony_ci * Kirti Wankhede <kwankhede@nvidia.com> 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 158c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 168c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#include <linux/init.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 248c2ecf20Sopenharmony_ci#include <linux/cdev.h> 258c2ecf20Sopenharmony_ci#include <linux/vfio.h> 268c2ecf20Sopenharmony_ci#include <linux/iommu.h> 278c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 288c2ecf20Sopenharmony_ci#include <linux/mdev.h> 298c2ecf20Sopenharmony_ci#include <linux/pci.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 318c2ecf20Sopenharmony_ci#include "mdpy-defs.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MDPY_NAME "mdpy" 348c2ecf20Sopenharmony_ci#define MDPY_CLASS_NAME "mdpy" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MDPY_CONFIG_SPACE_SIZE 0xff 378c2ecf20Sopenharmony_ci#define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE 388c2ecf20Sopenharmony_ci#define MDPY_DISPLAY_REGION 16 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define STORE_LE16(addr, val) (*(u16 *)addr = val) 418c2ecf20Sopenharmony_ci#define STORE_LE32(addr, val) (*(u32 *)addr = val) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int max_devices = 4; 478c2ecf20Sopenharmony_cimodule_param_named(count, max_devices, int, 0444); 488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MDPY_TYPE_1 "vga" 528c2ecf20Sopenharmony_ci#define MDPY_TYPE_2 "xga" 538c2ecf20Sopenharmony_ci#define MDPY_TYPE_3 "hd" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct mdpy_type { 568c2ecf20Sopenharmony_ci const char *name; 578c2ecf20Sopenharmony_ci u32 format; 588c2ecf20Sopenharmony_ci u32 bytepp; 598c2ecf20Sopenharmony_ci u32 width; 608c2ecf20Sopenharmony_ci u32 height; 618c2ecf20Sopenharmony_ci} mdpy_types[] = { 628c2ecf20Sopenharmony_ci { 638c2ecf20Sopenharmony_ci .name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, 648c2ecf20Sopenharmony_ci .format = DRM_FORMAT_XRGB8888, 658c2ecf20Sopenharmony_ci .bytepp = 4, 668c2ecf20Sopenharmony_ci .width = 640, 678c2ecf20Sopenharmony_ci .height = 480, 688c2ecf20Sopenharmony_ci }, { 698c2ecf20Sopenharmony_ci .name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, 708c2ecf20Sopenharmony_ci .format = DRM_FORMAT_XRGB8888, 718c2ecf20Sopenharmony_ci .bytepp = 4, 728c2ecf20Sopenharmony_ci .width = 1024, 738c2ecf20Sopenharmony_ci .height = 768, 748c2ecf20Sopenharmony_ci }, { 758c2ecf20Sopenharmony_ci .name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, 768c2ecf20Sopenharmony_ci .format = DRM_FORMAT_XRGB8888, 778c2ecf20Sopenharmony_ci .bytepp = 4, 788c2ecf20Sopenharmony_ci .width = 1920, 798c2ecf20Sopenharmony_ci .height = 1080, 808c2ecf20Sopenharmony_ci }, 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic dev_t mdpy_devt; 848c2ecf20Sopenharmony_cistatic struct class *mdpy_class; 858c2ecf20Sopenharmony_cistatic struct cdev mdpy_cdev; 868c2ecf20Sopenharmony_cistatic struct device mdpy_dev; 878c2ecf20Sopenharmony_cistatic u32 mdpy_count; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* State of each mdev device */ 908c2ecf20Sopenharmony_cistruct mdev_state { 918c2ecf20Sopenharmony_ci u8 *vconfig; 928c2ecf20Sopenharmony_ci u32 bar_mask; 938c2ecf20Sopenharmony_ci struct mutex ops_lock; 948c2ecf20Sopenharmony_ci struct mdev_device *mdev; 958c2ecf20Sopenharmony_ci struct vfio_device_info dev_info; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci const struct mdpy_type *type; 988c2ecf20Sopenharmony_ci u32 memsize; 998c2ecf20Sopenharmony_ci void *memblk; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct mdpy_type *mdpy_find_type(struct kobject *kobj) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci int i; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mdpy_types); i++) 1078c2ecf20Sopenharmony_ci if (strcmp(mdpy_types[i].name, kobj->name) == 0) 1088c2ecf20Sopenharmony_ci return mdpy_types + i; 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void mdpy_create_config_space(struct mdev_state *mdev_state) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID], 1158c2ecf20Sopenharmony_ci MDPY_PCI_VENDOR_ID); 1168c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID], 1178c2ecf20Sopenharmony_ci MDPY_PCI_DEVICE_ID); 1188c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID], 1198c2ecf20Sopenharmony_ci MDPY_PCI_SUBVENDOR_ID); 1208c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID], 1218c2ecf20Sopenharmony_ci MDPY_PCI_SUBDEVICE_ID); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND], 1248c2ecf20Sopenharmony_ci PCI_COMMAND_IO | PCI_COMMAND_MEMORY); 1258c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS], 1268c2ecf20Sopenharmony_ci PCI_STATUS_CAP_LIST); 1278c2ecf20Sopenharmony_ci STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE], 1288c2ecf20Sopenharmony_ci PCI_CLASS_DISPLAY_OTHER); 1298c2ecf20Sopenharmony_ci mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0], 1328c2ecf20Sopenharmony_ci PCI_BASE_ADDRESS_SPACE_MEMORY | 1338c2ecf20Sopenharmony_ci PCI_BASE_ADDRESS_MEM_TYPE_32 | 1348c2ecf20Sopenharmony_ci PCI_BASE_ADDRESS_MEM_PREFETCH); 1358c2ecf20Sopenharmony_ci mdev_state->bar_mask = ~(mdev_state->memsize) + 1; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* vendor specific capability for the config registers */ 1388c2ecf20Sopenharmony_ci mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET; 1398c2ecf20Sopenharmony_ci mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */ 1408c2ecf20Sopenharmony_ci mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */ 1418c2ecf20Sopenharmony_ci mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE; 1428c2ecf20Sopenharmony_ci STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET], 1438c2ecf20Sopenharmony_ci mdev_state->type->format); 1448c2ecf20Sopenharmony_ci STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET], 1458c2ecf20Sopenharmony_ci mdev_state->type->width); 1468c2ecf20Sopenharmony_ci STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET], 1478c2ecf20Sopenharmony_ci mdev_state->type->height); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, 1518c2ecf20Sopenharmony_ci char *buf, u32 count) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct device *dev = mdev_dev(mdev_state->mdev); 1548c2ecf20Sopenharmony_ci u32 cfg_addr; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (offset) { 1578c2ecf20Sopenharmony_ci case PCI_BASE_ADDRESS_0: 1588c2ecf20Sopenharmony_ci cfg_addr = *(u32 *)buf; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (cfg_addr == 0xffffffff) { 1618c2ecf20Sopenharmony_ci cfg_addr = (cfg_addr & mdev_state->bar_mask); 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK; 1648c2ecf20Sopenharmony_ci if (cfg_addr) 1658c2ecf20Sopenharmony_ci dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci cfg_addr |= (mdev_state->vconfig[offset] & 1698c2ecf20Sopenharmony_ci ~PCI_BASE_ADDRESS_MEM_MASK); 1708c2ecf20Sopenharmony_ci STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, 1768c2ecf20Sopenharmony_ci loff_t pos, bool is_write) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 1798c2ecf20Sopenharmony_ci struct device *dev = mdev_dev(mdev); 1808c2ecf20Sopenharmony_ci int ret = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mutex_lock(&mdev_state->ops_lock); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (pos < MDPY_CONFIG_SPACE_SIZE) { 1858c2ecf20Sopenharmony_ci if (is_write) 1868c2ecf20Sopenharmony_ci handle_pci_cfg_write(mdev_state, pos, buf, count); 1878c2ecf20Sopenharmony_ci else 1888c2ecf20Sopenharmony_ci memcpy(buf, (mdev_state->vconfig + pos), count); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) && 1918c2ecf20Sopenharmony_ci (pos + count <= 1928c2ecf20Sopenharmony_ci MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) { 1938c2ecf20Sopenharmony_ci pos -= MDPY_MEMORY_BAR_OFFSET; 1948c2ecf20Sopenharmony_ci if (is_write) 1958c2ecf20Sopenharmony_ci memcpy(mdev_state->memblk, buf, count); 1968c2ecf20Sopenharmony_ci else 1978c2ecf20Sopenharmony_ci memcpy(buf, mdev_state->memblk, count); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci } else { 2008c2ecf20Sopenharmony_ci dev_info(dev, "%s: %s @0x%llx (unhandled)\n", 2018c2ecf20Sopenharmony_ci __func__, is_write ? "WR" : "RD", pos); 2028c2ecf20Sopenharmony_ci ret = -1; 2038c2ecf20Sopenharmony_ci goto accessfailed; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = count; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciaccessfailed: 2108c2ecf20Sopenharmony_ci mutex_unlock(&mdev_state->ops_lock); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return ret; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int mdpy_reset(struct mdev_device *mdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 2188c2ecf20Sopenharmony_ci u32 stride, i; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* initialize with gray gradient */ 2218c2ecf20Sopenharmony_ci stride = mdev_state->type->width * mdev_state->type->bytepp; 2228c2ecf20Sopenharmony_ci for (i = 0; i < mdev_state->type->height; i++) 2238c2ecf20Sopenharmony_ci memset(mdev_state->memblk + i * stride, 2248c2ecf20Sopenharmony_ci i * 255 / mdev_state->type->height, 2258c2ecf20Sopenharmony_ci stride); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int mdpy_create(struct kobject *kobj, struct mdev_device *mdev) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci const struct mdpy_type *type = mdpy_find_type(kobj); 2328c2ecf20Sopenharmony_ci struct device *dev = mdev_dev(mdev); 2338c2ecf20Sopenharmony_ci struct mdev_state *mdev_state; 2348c2ecf20Sopenharmony_ci u32 fbsize; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (mdpy_count >= max_devices) 2378c2ecf20Sopenharmony_ci return -ENOMEM; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); 2408c2ecf20Sopenharmony_ci if (mdev_state == NULL) 2418c2ecf20Sopenharmony_ci return -ENOMEM; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); 2448c2ecf20Sopenharmony_ci if (mdev_state->vconfig == NULL) { 2458c2ecf20Sopenharmony_ci kfree(mdev_state); 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (!type) 2508c2ecf20Sopenharmony_ci type = &mdpy_types[0]; 2518c2ecf20Sopenharmony_ci fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci mdev_state->memblk = vmalloc_user(fbsize); 2548c2ecf20Sopenharmony_ci if (!mdev_state->memblk) { 2558c2ecf20Sopenharmony_ci kfree(mdev_state->vconfig); 2568c2ecf20Sopenharmony_ci kfree(mdev_state); 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci dev_info(dev, "%s: %s (%dx%d)\n", 2608c2ecf20Sopenharmony_ci __func__, kobj->name, type->width, type->height); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci mutex_init(&mdev_state->ops_lock); 2638c2ecf20Sopenharmony_ci mdev_state->mdev = mdev; 2648c2ecf20Sopenharmony_ci mdev_set_drvdata(mdev, mdev_state); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mdev_state->type = type; 2678c2ecf20Sopenharmony_ci mdev_state->memsize = fbsize; 2688c2ecf20Sopenharmony_ci mdpy_create_config_space(mdev_state); 2698c2ecf20Sopenharmony_ci mdpy_reset(mdev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci mdpy_count++; 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int mdpy_remove(struct mdev_device *mdev) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 2788c2ecf20Sopenharmony_ci struct device *dev = mdev_dev(mdev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dev_info(dev, "%s\n", __func__); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci mdev_set_drvdata(mdev, NULL); 2838c2ecf20Sopenharmony_ci vfree(mdev_state->memblk); 2848c2ecf20Sopenharmony_ci kfree(mdev_state->vconfig); 2858c2ecf20Sopenharmony_ci kfree(mdev_state); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci mdpy_count--; 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic ssize_t mdpy_read(struct mdev_device *mdev, char __user *buf, 2928c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci unsigned int done = 0; 2958c2ecf20Sopenharmony_ci int ret; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci while (count) { 2988c2ecf20Sopenharmony_ci size_t filled; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (count >= 4 && !(*ppos % 4)) { 3018c2ecf20Sopenharmony_ci u32 val; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3048c2ecf20Sopenharmony_ci *ppos, false); 3058c2ecf20Sopenharmony_ci if (ret <= 0) 3068c2ecf20Sopenharmony_ci goto read_err; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, sizeof(val))) 3098c2ecf20Sopenharmony_ci goto read_err; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci filled = 4; 3128c2ecf20Sopenharmony_ci } else if (count >= 2 && !(*ppos % 2)) { 3138c2ecf20Sopenharmony_ci u16 val; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3168c2ecf20Sopenharmony_ci *ppos, false); 3178c2ecf20Sopenharmony_ci if (ret <= 0) 3188c2ecf20Sopenharmony_ci goto read_err; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, sizeof(val))) 3218c2ecf20Sopenharmony_ci goto read_err; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci filled = 2; 3248c2ecf20Sopenharmony_ci } else { 3258c2ecf20Sopenharmony_ci u8 val; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3288c2ecf20Sopenharmony_ci *ppos, false); 3298c2ecf20Sopenharmony_ci if (ret <= 0) 3308c2ecf20Sopenharmony_ci goto read_err; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (copy_to_user(buf, &val, sizeof(val))) 3338c2ecf20Sopenharmony_ci goto read_err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci filled = 1; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci count -= filled; 3398c2ecf20Sopenharmony_ci done += filled; 3408c2ecf20Sopenharmony_ci *ppos += filled; 3418c2ecf20Sopenharmony_ci buf += filled; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return done; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciread_err: 3478c2ecf20Sopenharmony_ci return -EFAULT; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic ssize_t mdpy_write(struct mdev_device *mdev, const char __user *buf, 3518c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci unsigned int done = 0; 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci while (count) { 3578c2ecf20Sopenharmony_ci size_t filled; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (count >= 4 && !(*ppos % 4)) { 3608c2ecf20Sopenharmony_ci u32 val; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, sizeof(val))) 3638c2ecf20Sopenharmony_ci goto write_err; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3668c2ecf20Sopenharmony_ci *ppos, true); 3678c2ecf20Sopenharmony_ci if (ret <= 0) 3688c2ecf20Sopenharmony_ci goto write_err; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci filled = 4; 3718c2ecf20Sopenharmony_ci } else if (count >= 2 && !(*ppos % 2)) { 3728c2ecf20Sopenharmony_ci u16 val; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, sizeof(val))) 3758c2ecf20Sopenharmony_ci goto write_err; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3788c2ecf20Sopenharmony_ci *ppos, true); 3798c2ecf20Sopenharmony_ci if (ret <= 0) 3808c2ecf20Sopenharmony_ci goto write_err; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci filled = 2; 3838c2ecf20Sopenharmony_ci } else { 3848c2ecf20Sopenharmony_ci u8 val; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (copy_from_user(&val, buf, sizeof(val))) 3878c2ecf20Sopenharmony_ci goto write_err; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci ret = mdev_access(mdev, (char *)&val, sizeof(val), 3908c2ecf20Sopenharmony_ci *ppos, true); 3918c2ecf20Sopenharmony_ci if (ret <= 0) 3928c2ecf20Sopenharmony_ci goto write_err; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci filled = 1; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci count -= filled; 3978c2ecf20Sopenharmony_ci done += filled; 3988c2ecf20Sopenharmony_ci *ppos += filled; 3998c2ecf20Sopenharmony_ci buf += filled; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return done; 4038c2ecf20Sopenharmony_ciwrite_err: 4048c2ecf20Sopenharmony_ci return -EFAULT; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT) 4128c2ecf20Sopenharmony_ci return -EINVAL; 4138c2ecf20Sopenharmony_ci if (vma->vm_end < vma->vm_start) 4148c2ecf20Sopenharmony_ci return -EINVAL; 4158c2ecf20Sopenharmony_ci if (vma->vm_end - vma->vm_start > mdev_state->memsize) 4168c2ecf20Sopenharmony_ci return -EINVAL; 4178c2ecf20Sopenharmony_ci if ((vma->vm_flags & VM_SHARED) == 0) 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return remap_vmalloc_range_partial(vma, vma->vm_start, 4218c2ecf20Sopenharmony_ci mdev_state->memblk, 0, 4228c2ecf20Sopenharmony_ci vma->vm_end - vma->vm_start); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic int mdpy_get_region_info(struct mdev_device *mdev, 4268c2ecf20Sopenharmony_ci struct vfio_region_info *region_info, 4278c2ecf20Sopenharmony_ci u16 *cap_type_id, void **cap_type) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct mdev_state *mdev_state; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci mdev_state = mdev_get_drvdata(mdev); 4328c2ecf20Sopenharmony_ci if (!mdev_state) 4338c2ecf20Sopenharmony_ci return -EINVAL; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (region_info->index >= VFIO_PCI_NUM_REGIONS && 4368c2ecf20Sopenharmony_ci region_info->index != MDPY_DISPLAY_REGION) 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci switch (region_info->index) { 4408c2ecf20Sopenharmony_ci case VFIO_PCI_CONFIG_REGION_INDEX: 4418c2ecf20Sopenharmony_ci region_info->offset = 0; 4428c2ecf20Sopenharmony_ci region_info->size = MDPY_CONFIG_SPACE_SIZE; 4438c2ecf20Sopenharmony_ci region_info->flags = (VFIO_REGION_INFO_FLAG_READ | 4448c2ecf20Sopenharmony_ci VFIO_REGION_INFO_FLAG_WRITE); 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case VFIO_PCI_BAR0_REGION_INDEX: 4478c2ecf20Sopenharmony_ci case MDPY_DISPLAY_REGION: 4488c2ecf20Sopenharmony_ci region_info->offset = MDPY_MEMORY_BAR_OFFSET; 4498c2ecf20Sopenharmony_ci region_info->size = mdev_state->memsize; 4508c2ecf20Sopenharmony_ci region_info->flags = (VFIO_REGION_INFO_FLAG_READ | 4518c2ecf20Sopenharmony_ci VFIO_REGION_INFO_FLAG_WRITE | 4528c2ecf20Sopenharmony_ci VFIO_REGION_INFO_FLAG_MMAP); 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci default: 4558c2ecf20Sopenharmony_ci region_info->size = 0; 4568c2ecf20Sopenharmony_ci region_info->offset = 0; 4578c2ecf20Sopenharmony_ci region_info->flags = 0; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic int mdpy_get_irq_info(struct mdev_device *mdev, 4648c2ecf20Sopenharmony_ci struct vfio_irq_info *irq_info) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci irq_info->count = 0; 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int mdpy_get_device_info(struct mdev_device *mdev, 4718c2ecf20Sopenharmony_ci struct vfio_device_info *dev_info) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci dev_info->flags = VFIO_DEVICE_FLAGS_PCI; 4748c2ecf20Sopenharmony_ci dev_info->num_regions = VFIO_PCI_NUM_REGIONS; 4758c2ecf20Sopenharmony_ci dev_info->num_irqs = VFIO_PCI_NUM_IRQS; 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic int mdpy_query_gfx_plane(struct mdev_device *mdev, 4808c2ecf20Sopenharmony_ci struct vfio_device_gfx_plane_info *plane) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) { 4858c2ecf20Sopenharmony_ci if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE | 4868c2ecf20Sopenharmony_ci VFIO_GFX_PLANE_TYPE_REGION)) 4878c2ecf20Sopenharmony_ci return 0; 4888c2ecf20Sopenharmony_ci return -EINVAL; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION) 4928c2ecf20Sopenharmony_ci return -EINVAL; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci plane->drm_format = mdev_state->type->format; 4958c2ecf20Sopenharmony_ci plane->width = mdev_state->type->width; 4968c2ecf20Sopenharmony_ci plane->height = mdev_state->type->height; 4978c2ecf20Sopenharmony_ci plane->stride = (mdev_state->type->width * 4988c2ecf20Sopenharmony_ci mdev_state->type->bytepp); 4998c2ecf20Sopenharmony_ci plane->size = mdev_state->memsize; 5008c2ecf20Sopenharmony_ci plane->region_index = MDPY_DISPLAY_REGION; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* unused */ 5038c2ecf20Sopenharmony_ci plane->drm_format_mod = 0; 5048c2ecf20Sopenharmony_ci plane->x_pos = 0; 5058c2ecf20Sopenharmony_ci plane->y_pos = 0; 5068c2ecf20Sopenharmony_ci plane->x_hot = 0; 5078c2ecf20Sopenharmony_ci plane->y_hot = 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic long mdpy_ioctl(struct mdev_device *mdev, unsigned int cmd, 5138c2ecf20Sopenharmony_ci unsigned long arg) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci int ret = 0; 5168c2ecf20Sopenharmony_ci unsigned long minsz; 5178c2ecf20Sopenharmony_ci struct mdev_state *mdev_state; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci mdev_state = mdev_get_drvdata(mdev); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci switch (cmd) { 5228c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_INFO: 5238c2ecf20Sopenharmony_ci { 5248c2ecf20Sopenharmony_ci struct vfio_device_info info; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_device_info, num_irqs); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 5298c2ecf20Sopenharmony_ci return -EFAULT; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (info.argsz < minsz) 5328c2ecf20Sopenharmony_ci return -EINVAL; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci ret = mdpy_get_device_info(mdev, &info); 5358c2ecf20Sopenharmony_ci if (ret) 5368c2ecf20Sopenharmony_ci return ret; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci memcpy(&mdev_state->dev_info, &info, sizeof(info)); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, minsz)) 5418c2ecf20Sopenharmony_ci return -EFAULT; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return 0; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_REGION_INFO: 5468c2ecf20Sopenharmony_ci { 5478c2ecf20Sopenharmony_ci struct vfio_region_info info; 5488c2ecf20Sopenharmony_ci u16 cap_type_id = 0; 5498c2ecf20Sopenharmony_ci void *cap_type = NULL; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_region_info, offset); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 5548c2ecf20Sopenharmony_ci return -EFAULT; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci if (info.argsz < minsz) 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci ret = mdpy_get_region_info(mdev, &info, &cap_type_id, 5608c2ecf20Sopenharmony_ci &cap_type); 5618c2ecf20Sopenharmony_ci if (ret) 5628c2ecf20Sopenharmony_ci return ret; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, minsz)) 5658c2ecf20Sopenharmony_ci return -EFAULT; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return 0; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci case VFIO_DEVICE_GET_IRQ_INFO: 5718c2ecf20Sopenharmony_ci { 5728c2ecf20Sopenharmony_ci struct vfio_irq_info info; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_irq_info, count); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (copy_from_user(&info, (void __user *)arg, minsz)) 5778c2ecf20Sopenharmony_ci return -EFAULT; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if ((info.argsz < minsz) || 5808c2ecf20Sopenharmony_ci (info.index >= mdev_state->dev_info.num_irqs)) 5818c2ecf20Sopenharmony_ci return -EINVAL; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci ret = mdpy_get_irq_info(mdev, &info); 5848c2ecf20Sopenharmony_ci if (ret) 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &info, minsz)) 5888c2ecf20Sopenharmony_ci return -EFAULT; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci case VFIO_DEVICE_QUERY_GFX_PLANE: 5948c2ecf20Sopenharmony_ci { 5958c2ecf20Sopenharmony_ci struct vfio_device_gfx_plane_info plane; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci minsz = offsetofend(struct vfio_device_gfx_plane_info, 5988c2ecf20Sopenharmony_ci region_index); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (copy_from_user(&plane, (void __user *)arg, minsz)) 6018c2ecf20Sopenharmony_ci return -EFAULT; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (plane.argsz < minsz) 6048c2ecf20Sopenharmony_ci return -EINVAL; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci ret = mdpy_query_gfx_plane(mdev, &plane); 6078c2ecf20Sopenharmony_ci if (ret) 6088c2ecf20Sopenharmony_ci return ret; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, &plane, minsz)) 6118c2ecf20Sopenharmony_ci return -EFAULT; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return 0; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci case VFIO_DEVICE_SET_IRQS: 6178c2ecf20Sopenharmony_ci return -EINVAL; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci case VFIO_DEVICE_RESET: 6208c2ecf20Sopenharmony_ci return mdpy_reset(mdev); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci return -ENOTTY; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int mdpy_open(struct mdev_device *mdev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 6288c2ecf20Sopenharmony_ci return -ENODEV; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void mdpy_close(struct mdev_device *mdev) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic ssize_t 6398c2ecf20Sopenharmony_ciresolution_show(struct device *dev, struct device_attribute *attr, 6408c2ecf20Sopenharmony_ci char *buf) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct mdev_device *mdev = mdev_from_dev(dev); 6438c2ecf20Sopenharmony_ci struct mdev_state *mdev_state = mdev_get_drvdata(mdev); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return sprintf(buf, "%dx%d\n", 6468c2ecf20Sopenharmony_ci mdev_state->type->width, 6478c2ecf20Sopenharmony_ci mdev_state->type->height); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(resolution); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic struct attribute *mdev_dev_attrs[] = { 6528c2ecf20Sopenharmony_ci &dev_attr_resolution.attr, 6538c2ecf20Sopenharmony_ci NULL, 6548c2ecf20Sopenharmony_ci}; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic const struct attribute_group mdev_dev_group = { 6578c2ecf20Sopenharmony_ci .name = "vendor", 6588c2ecf20Sopenharmony_ci .attrs = mdev_dev_attrs, 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ciconst struct attribute_group *mdev_dev_groups[] = { 6628c2ecf20Sopenharmony_ci &mdev_dev_group, 6638c2ecf20Sopenharmony_ci NULL, 6648c2ecf20Sopenharmony_ci}; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic ssize_t 6678c2ecf20Sopenharmony_ciname_show(struct kobject *kobj, struct device *dev, char *buf) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", kobj->name); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ciMDEV_TYPE_ATTR_RO(name); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic ssize_t 6748c2ecf20Sopenharmony_cidescription_show(struct kobject *kobj, struct device *dev, char *buf) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci const struct mdpy_type *type = mdpy_find_type(kobj); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci return sprintf(buf, "virtual display, %dx%d framebuffer\n", 6798c2ecf20Sopenharmony_ci type ? type->width : 0, 6808c2ecf20Sopenharmony_ci type ? type->height : 0); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ciMDEV_TYPE_ATTR_RO(description); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_cistatic ssize_t 6858c2ecf20Sopenharmony_ciavailable_instances_show(struct kobject *kobj, struct device *dev, char *buf) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", max_devices - mdpy_count); 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ciMDEV_TYPE_ATTR_RO(available_instances); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic ssize_t device_api_show(struct kobject *kobj, struct device *dev, 6928c2ecf20Sopenharmony_ci char *buf) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING); 6958c2ecf20Sopenharmony_ci} 6968c2ecf20Sopenharmony_ciMDEV_TYPE_ATTR_RO(device_api); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic struct attribute *mdev_types_attrs[] = { 6998c2ecf20Sopenharmony_ci &mdev_type_attr_name.attr, 7008c2ecf20Sopenharmony_ci &mdev_type_attr_description.attr, 7018c2ecf20Sopenharmony_ci &mdev_type_attr_device_api.attr, 7028c2ecf20Sopenharmony_ci &mdev_type_attr_available_instances.attr, 7038c2ecf20Sopenharmony_ci NULL, 7048c2ecf20Sopenharmony_ci}; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_cistatic struct attribute_group mdev_type_group1 = { 7078c2ecf20Sopenharmony_ci .name = MDPY_TYPE_1, 7088c2ecf20Sopenharmony_ci .attrs = mdev_types_attrs, 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic struct attribute_group mdev_type_group2 = { 7128c2ecf20Sopenharmony_ci .name = MDPY_TYPE_2, 7138c2ecf20Sopenharmony_ci .attrs = mdev_types_attrs, 7148c2ecf20Sopenharmony_ci}; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic struct attribute_group mdev_type_group3 = { 7178c2ecf20Sopenharmony_ci .name = MDPY_TYPE_3, 7188c2ecf20Sopenharmony_ci .attrs = mdev_types_attrs, 7198c2ecf20Sopenharmony_ci}; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic struct attribute_group *mdev_type_groups[] = { 7228c2ecf20Sopenharmony_ci &mdev_type_group1, 7238c2ecf20Sopenharmony_ci &mdev_type_group2, 7248c2ecf20Sopenharmony_ci &mdev_type_group3, 7258c2ecf20Sopenharmony_ci NULL, 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const struct mdev_parent_ops mdev_fops = { 7298c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7308c2ecf20Sopenharmony_ci .mdev_attr_groups = mdev_dev_groups, 7318c2ecf20Sopenharmony_ci .supported_type_groups = mdev_type_groups, 7328c2ecf20Sopenharmony_ci .create = mdpy_create, 7338c2ecf20Sopenharmony_ci .remove = mdpy_remove, 7348c2ecf20Sopenharmony_ci .open = mdpy_open, 7358c2ecf20Sopenharmony_ci .release = mdpy_close, 7368c2ecf20Sopenharmony_ci .read = mdpy_read, 7378c2ecf20Sopenharmony_ci .write = mdpy_write, 7388c2ecf20Sopenharmony_ci .ioctl = mdpy_ioctl, 7398c2ecf20Sopenharmony_ci .mmap = mdpy_mmap, 7408c2ecf20Sopenharmony_ci}; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic const struct file_operations vd_fops = { 7438c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7448c2ecf20Sopenharmony_ci}; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_cistatic void mdpy_device_release(struct device *dev) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci /* nothing */ 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic int __init mdpy_dev_init(void) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci int ret = 0; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK + 1, MDPY_NAME); 7568c2ecf20Sopenharmony_ci if (ret < 0) { 7578c2ecf20Sopenharmony_ci pr_err("Error: failed to register mdpy_dev, err: %d\n", ret); 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci cdev_init(&mdpy_cdev, &vd_fops); 7618c2ecf20Sopenharmony_ci cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK + 1); 7628c2ecf20Sopenharmony_ci pr_info("%s: major %d\n", __func__, MAJOR(mdpy_devt)); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci mdpy_class = class_create(THIS_MODULE, MDPY_CLASS_NAME); 7658c2ecf20Sopenharmony_ci if (IS_ERR(mdpy_class)) { 7668c2ecf20Sopenharmony_ci pr_err("Error: failed to register mdpy_dev class\n"); 7678c2ecf20Sopenharmony_ci ret = PTR_ERR(mdpy_class); 7688c2ecf20Sopenharmony_ci goto failed1; 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci mdpy_dev.class = mdpy_class; 7718c2ecf20Sopenharmony_ci mdpy_dev.release = mdpy_device_release; 7728c2ecf20Sopenharmony_ci dev_set_name(&mdpy_dev, "%s", MDPY_NAME); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci ret = device_register(&mdpy_dev); 7758c2ecf20Sopenharmony_ci if (ret) 7768c2ecf20Sopenharmony_ci goto failed2; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci ret = mdev_register_device(&mdpy_dev, &mdev_fops); 7798c2ecf20Sopenharmony_ci if (ret) 7808c2ecf20Sopenharmony_ci goto failed3; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return 0; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cifailed3: 7858c2ecf20Sopenharmony_ci device_unregister(&mdpy_dev); 7868c2ecf20Sopenharmony_cifailed2: 7878c2ecf20Sopenharmony_ci class_destroy(mdpy_class); 7888c2ecf20Sopenharmony_cifailed1: 7898c2ecf20Sopenharmony_ci cdev_del(&mdpy_cdev); 7908c2ecf20Sopenharmony_ci unregister_chrdev_region(mdpy_devt, MINORMASK + 1); 7918c2ecf20Sopenharmony_ci return ret; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void __exit mdpy_dev_exit(void) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci mdpy_dev.bus = NULL; 7978c2ecf20Sopenharmony_ci mdev_unregister_device(&mdpy_dev); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci device_unregister(&mdpy_dev); 8008c2ecf20Sopenharmony_ci cdev_del(&mdpy_cdev); 8018c2ecf20Sopenharmony_ci unregister_chrdev_region(mdpy_devt, MINORMASK + 1); 8028c2ecf20Sopenharmony_ci class_destroy(mdpy_class); 8038c2ecf20Sopenharmony_ci mdpy_class = NULL; 8048c2ecf20Sopenharmony_ci} 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cimodule_init(mdpy_dev_init) 8078c2ecf20Sopenharmony_cimodule_exit(mdpy_dev_exit) 808