1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020 DisplayLink (UK) Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "evdi_platform_dev.h"
#include <linux/version.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
#include <linux/iommu.h>
#endif
#include "evdi_platform_drv.h"
#include "evdi_debug.h"
#include "evdi_drm_drv.h"
struct evdi_platform_device_data {
struct drm_device *drm_dev;
struct device *parent;
bool symlinked;
};
struct platform_device *evdi_platform_dev_create(struct platform_device_info *info)
{
struct platform_device *platform_dev = NULL;
platform_dev = platform_device_register_full(info);
if (dma_set_mask(&platform_dev->dev, DMA_BIT_MASK(64))) {
EVDI_DEBUG("Unable to change dma mask to 64 bit. ");
EVDI_DEBUG("Sticking with 32 bit\n");
}
EVDI_INFO("Evdi platform_device create\n");
return platform_dev;
}
void evdi_platform_dev_destroy(struct platform_device *dev)
{
platform_device_unregister(dev);
EVDI_INFO("Evdi platform_device destroy\n");
}
int evdi_platform_device_probe(struct platform_device *pdev)
{
struct drm_device *dev = NULL;
struct evdi_platform_device_data *data =
kzalloc(sizeof(struct evdi_platform_device_data), GFP_KERNEL);
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
struct dev_iommu iommu;
#endif
#endif
EVDI_CHECKPT();
/* Intel-IOMMU workaround: platform-bus unsupported, force ID-mapping */
#if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU)
#if KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
memset(&iommu, 0, sizeof(iommu));
iommu.priv = (void *)-1;
pdev->dev.iommu = &iommu;
#else
#define INTEL_IOMMU_DUMMY_DOMAIN ((void *)-1)
pdev->dev.archdata.iommu = INTEL_IOMMU_DUMMY_DOMAIN;
#endif
#endif
dev = evdi_drm_device_create(&pdev->dev);
if (IS_ERR_OR_NULL(dev))
kfree(data);
else {
data->drm_dev = dev;
data->symlinked = false;
platform_set_drvdata(pdev, data);
}
return PTR_ERR_OR_ZERO(dev);
}
int evdi_platform_device_remove(struct platform_device *pdev)
{
struct evdi_platform_device_data *data =
(struct evdi_platform_device_data *)platform_get_drvdata(pdev);
EVDI_CHECKPT();
evdi_drm_device_remove(data->drm_dev);
kfree(data);
return 0;
}
bool evdi_platform_device_is_free(struct platform_device *pdev)
{
struct evdi_platform_device_data *data =
(struct evdi_platform_device_data *)platform_get_drvdata(pdev);
struct evdi_device *evdi = data->drm_dev->dev_private;
if (evdi && !evdi_painter_is_connected(evdi->painter) &&
!data->symlinked)
return true;
return false;
}
void evdi_platform_device_link(struct platform_device *pdev,
struct device *parent)
{
struct evdi_platform_device_data *data = NULL;
char evdi_name[10] = { 0 };
int ret = 0;
if (!parent || !pdev)
return;
data = (struct evdi_platform_device_data *)platform_get_drvdata(pdev);
if (!evdi_platform_device_is_free(pdev)) {
EVDI_FATAL("Device is already attached can't symlink again\n");
return;
}
snprintf(evdi_name, sizeof(evdi_name), "evdi.%d", pdev->id);
ret = sysfs_create_link(&parent->kobj, &pdev->dev.kobj, evdi_name);
if (ret)
EVDI_FATAL("Failed to create sysfs link to parent device\n");
else {
data->symlinked = true;
data->parent = parent;
}
}
void evdi_platform_device_unlink_if_linked_with(struct platform_device *pdev,
struct device *parent)
{
struct evdi_platform_device_data *data =
(struct evdi_platform_device_data *)platform_get_drvdata(pdev);
if (data->parent == parent) {
data->symlinked = false;
data->parent = NULL;
EVDI_INFO("Detached from parent device\n");
}
}
|