1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2022-2023 Intel Corporation 4 */ 5 6 /** 7 * DOC: MEI_GSC_PROXY Client Driver 8 * 9 * The mei_gsc_proxy driver acts as a translation layer between 10 * proxy user (I915) and ME FW by proxying messages to ME FW 11 */ 12 13 #include <linux/component.h> 14 #include <linux/mei_cl_bus.h> 15 #include <linux/module.h> 16 #include <linux/pci.h> 17 #include <linux/slab.h> 18 #include <linux/uuid.h> 19 #include <drm/drm_connector.h> 20 #include <drm/intel/i915_component.h> 21 #include <drm/intel/i915_gsc_proxy_mei_interface.h> 22 23 /** 24 * mei_gsc_proxy_send - Sends a proxy message to ME FW. 25 * @dev: device corresponding to the mei_cl_device 26 * @buf: a message buffer to send 27 * @size: size of the message 28 * Return: bytes sent on Success, <0 on Failure 29 */ 30 static int mei_gsc_proxy_send(struct device *dev, const void *buf, size_t size) 31 { 32 ssize_t ret; 33 34 if (!dev || !buf) 35 return -EINVAL; 36 37 ret = mei_cldev_send(to_mei_cl_device(dev), buf, size); 38 if (ret < 0) 39 dev_dbg(dev, "mei_cldev_send failed. %zd\n", ret); 40 41 return ret; 42 } 43 44 /** 45 * mei_gsc_proxy_recv - Receives a proxy message from ME FW. 46 * @dev: device corresponding to the mei_cl_device 47 * @buf: a message buffer to contain the received message 48 * @size: size of the buffer 49 * Return: bytes received on Success, <0 on Failure 50 */ 51 static int mei_gsc_proxy_recv(struct device *dev, void *buf, size_t size) 52 { 53 ssize_t ret; 54 55 if (!dev || !buf) 56 return -EINVAL; 57 58 ret = mei_cldev_recv(to_mei_cl_device(dev), buf, size); 59 if (ret < 0) 60 dev_dbg(dev, "mei_cldev_recv failed. %zd\n", ret); 61 62 return ret; 63 } 64 65 static const struct i915_gsc_proxy_component_ops mei_gsc_proxy_ops = { 66 .owner = THIS_MODULE, 67 .send = mei_gsc_proxy_send, 68 .recv = mei_gsc_proxy_recv, 69 }; 70 71 static int mei_component_master_bind(struct device *dev) 72 { 73 struct mei_cl_device *cldev = to_mei_cl_device(dev); 74 struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev); 75 76 comp_master->ops = &mei_gsc_proxy_ops; 77 comp_master->mei_dev = dev; 78 return component_bind_all(dev, comp_master); 79 } 80 81 static void mei_component_master_unbind(struct device *dev) 82 { 83 struct mei_cl_device *cldev = to_mei_cl_device(dev); 84 struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev); 85 86 component_unbind_all(dev, comp_master); 87 } 88 89 static const struct component_master_ops mei_component_master_ops = { 90 .bind = mei_component_master_bind, 91 .unbind = mei_component_master_unbind, 92 }; 93 94 /** 95 * mei_gsc_proxy_component_match - compare function for matching mei. 96 * 97 * The function checks if the device is pci device and 98 * Intel VGA adapter, the subcomponent is SW Proxy 99 * and the VGA is on the bus 0 reserved for built-in devices 100 * to reject discrete GFX. 101 * 102 * @dev: master device 103 * @subcomponent: subcomponent to match (I915_COMPONENT_SWPROXY) 104 * @data: compare data (mei pci parent) 105 * 106 * Return: 107 * * 1 - if components match 108 * * 0 - otherwise 109 */ 110 static int mei_gsc_proxy_component_match(struct device *dev, int subcomponent, 111 void *data) 112 { 113 struct pci_dev *pdev; 114 115 if (!dev_is_pci(dev)) 116 return 0; 117 118 pdev = to_pci_dev(dev); 119 120 if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) || 121 pdev->vendor != PCI_VENDOR_ID_INTEL) 122 return 0; 123 124 if (subcomponent != I915_COMPONENT_GSC_PROXY) 125 return 0; 126 127 /* Only built-in GFX */ 128 return (pdev->bus->number == 0); 129 } 130 131 static int mei_gsc_proxy_probe(struct mei_cl_device *cldev, 132 const struct mei_cl_device_id *id) 133 { 134 struct i915_gsc_proxy_component *comp_master; 135 struct component_match *master_match = NULL; 136 int ret; 137 138 ret = mei_cldev_enable(cldev); 139 if (ret < 0) { 140 dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret); 141 goto enable_err_exit; 142 } 143 144 comp_master = kzalloc(sizeof(*comp_master), GFP_KERNEL); 145 if (!comp_master) { 146 ret = -ENOMEM; 147 goto err_exit; 148 } 149 150 component_match_add_typed(&cldev->dev, &master_match, 151 mei_gsc_proxy_component_match, NULL); 152 if (IS_ERR_OR_NULL(master_match)) { 153 ret = -ENOMEM; 154 goto err_exit; 155 } 156 157 mei_cldev_set_drvdata(cldev, comp_master); 158 ret = component_master_add_with_match(&cldev->dev, 159 &mei_component_master_ops, 160 master_match); 161 if (ret < 0) { 162 dev_err(&cldev->dev, "Master comp add failed %d\n", ret); 163 goto err_exit; 164 } 165 166 return 0; 167 168 err_exit: 169 mei_cldev_set_drvdata(cldev, NULL); 170 kfree(comp_master); 171 mei_cldev_disable(cldev); 172 enable_err_exit: 173 return ret; 174 } 175 176 static void mei_gsc_proxy_remove(struct mei_cl_device *cldev) 177 { 178 struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev); 179 int ret; 180 181 component_master_del(&cldev->dev, &mei_component_master_ops); 182 kfree(comp_master); 183 mei_cldev_set_drvdata(cldev, NULL); 184 185 ret = mei_cldev_disable(cldev); 186 if (ret) 187 dev_warn(&cldev->dev, "mei_cldev_disable() failed %d\n", ret); 188 } 189 190 #define MEI_UUID_GSC_PROXY UUID_LE(0xf73db04, 0x97ab, 0x4125, \ 191 0xb8, 0x93, 0xe9, 0x4, 0xad, 0xd, 0x54, 0x64) 192 193 static struct mei_cl_device_id mei_gsc_proxy_tbl[] = { 194 { .uuid = MEI_UUID_GSC_PROXY, .version = MEI_CL_VERSION_ANY }, 195 { } 196 }; 197 MODULE_DEVICE_TABLE(mei, mei_gsc_proxy_tbl); 198 199 static struct mei_cl_driver mei_gsc_proxy_driver = { 200 .id_table = mei_gsc_proxy_tbl, 201 .name = KBUILD_MODNAME, 202 .probe = mei_gsc_proxy_probe, 203 .remove = mei_gsc_proxy_remove, 204 }; 205 206 module_mei_cl_driver(mei_gsc_proxy_driver); 207 208 MODULE_AUTHOR("Intel Corporation"); 209 MODULE_LICENSE("GPL"); 210 MODULE_DESCRIPTION("MEI GSC PROXY"); 211