11dd924f6SAlexander Usyskin // SPDX-License-Identifier: GPL-2.0
21dd924f6SAlexander Usyskin /*
31dd924f6SAlexander Usyskin * Copyright (c) 2022-2023 Intel Corporation
41dd924f6SAlexander Usyskin */
51dd924f6SAlexander Usyskin
61dd924f6SAlexander Usyskin /**
71dd924f6SAlexander Usyskin * DOC: MEI_GSC_PROXY Client Driver
81dd924f6SAlexander Usyskin *
91dd924f6SAlexander Usyskin * The mei_gsc_proxy driver acts as a translation layer between
101dd924f6SAlexander Usyskin * proxy user (I915) and ME FW by proxying messages to ME FW
111dd924f6SAlexander Usyskin */
121dd924f6SAlexander Usyskin
131dd924f6SAlexander Usyskin #include <linux/component.h>
141dd924f6SAlexander Usyskin #include <linux/mei_cl_bus.h>
151dd924f6SAlexander Usyskin #include <linux/module.h>
161dd924f6SAlexander Usyskin #include <linux/pci.h>
171dd924f6SAlexander Usyskin #include <linux/slab.h>
181dd924f6SAlexander Usyskin #include <linux/uuid.h>
191dd924f6SAlexander Usyskin #include <drm/drm_connector.h>
201dd924f6SAlexander Usyskin #include <drm/i915_component.h>
211dd924f6SAlexander Usyskin #include <drm/i915_gsc_proxy_mei_interface.h>
221dd924f6SAlexander Usyskin
231dd924f6SAlexander Usyskin /**
241dd924f6SAlexander Usyskin * mei_gsc_proxy_send - Sends a proxy message to ME FW.
251dd924f6SAlexander Usyskin * @dev: device corresponding to the mei_cl_device
261dd924f6SAlexander Usyskin * @buf: a message buffer to send
271dd924f6SAlexander Usyskin * @size: size of the message
281dd924f6SAlexander Usyskin * Return: bytes sent on Success, <0 on Failure
291dd924f6SAlexander Usyskin */
mei_gsc_proxy_send(struct device * dev,const void * buf,size_t size)301dd924f6SAlexander Usyskin static int mei_gsc_proxy_send(struct device *dev, const void *buf, size_t size)
311dd924f6SAlexander Usyskin {
321dd924f6SAlexander Usyskin ssize_t ret;
331dd924f6SAlexander Usyskin
341dd924f6SAlexander Usyskin if (!dev || !buf)
351dd924f6SAlexander Usyskin return -EINVAL;
361dd924f6SAlexander Usyskin
371dd924f6SAlexander Usyskin ret = mei_cldev_send(to_mei_cl_device(dev), buf, size);
381dd924f6SAlexander Usyskin if (ret < 0)
391dd924f6SAlexander Usyskin dev_dbg(dev, "mei_cldev_send failed. %zd\n", ret);
401dd924f6SAlexander Usyskin
411dd924f6SAlexander Usyskin return ret;
421dd924f6SAlexander Usyskin }
431dd924f6SAlexander Usyskin
441dd924f6SAlexander Usyskin /**
451dd924f6SAlexander Usyskin * mei_gsc_proxy_recv - Receives a proxy message from ME FW.
461dd924f6SAlexander Usyskin * @dev: device corresponding to the mei_cl_device
471dd924f6SAlexander Usyskin * @buf: a message buffer to contain the received message
481dd924f6SAlexander Usyskin * @size: size of the buffer
491dd924f6SAlexander Usyskin * Return: bytes received on Success, <0 on Failure
501dd924f6SAlexander Usyskin */
mei_gsc_proxy_recv(struct device * dev,void * buf,size_t size)511dd924f6SAlexander Usyskin static int mei_gsc_proxy_recv(struct device *dev, void *buf, size_t size)
521dd924f6SAlexander Usyskin {
531dd924f6SAlexander Usyskin ssize_t ret;
541dd924f6SAlexander Usyskin
551dd924f6SAlexander Usyskin if (!dev || !buf)
561dd924f6SAlexander Usyskin return -EINVAL;
571dd924f6SAlexander Usyskin
581dd924f6SAlexander Usyskin ret = mei_cldev_recv(to_mei_cl_device(dev), buf, size);
591dd924f6SAlexander Usyskin if (ret < 0)
601dd924f6SAlexander Usyskin dev_dbg(dev, "mei_cldev_recv failed. %zd\n", ret);
611dd924f6SAlexander Usyskin
621dd924f6SAlexander Usyskin return ret;
631dd924f6SAlexander Usyskin }
641dd924f6SAlexander Usyskin
651dd924f6SAlexander Usyskin static const struct i915_gsc_proxy_component_ops mei_gsc_proxy_ops = {
661dd924f6SAlexander Usyskin .owner = THIS_MODULE,
671dd924f6SAlexander Usyskin .send = mei_gsc_proxy_send,
681dd924f6SAlexander Usyskin .recv = mei_gsc_proxy_recv,
691dd924f6SAlexander Usyskin };
701dd924f6SAlexander Usyskin
mei_component_master_bind(struct device * dev)711dd924f6SAlexander Usyskin static int mei_component_master_bind(struct device *dev)
721dd924f6SAlexander Usyskin {
731dd924f6SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
741dd924f6SAlexander Usyskin struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev);
751dd924f6SAlexander Usyskin
761dd924f6SAlexander Usyskin comp_master->ops = &mei_gsc_proxy_ops;
771dd924f6SAlexander Usyskin comp_master->mei_dev = dev;
781dd924f6SAlexander Usyskin return component_bind_all(dev, comp_master);
791dd924f6SAlexander Usyskin }
801dd924f6SAlexander Usyskin
mei_component_master_unbind(struct device * dev)811dd924f6SAlexander Usyskin static void mei_component_master_unbind(struct device *dev)
821dd924f6SAlexander Usyskin {
831dd924f6SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev);
841dd924f6SAlexander Usyskin struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev);
851dd924f6SAlexander Usyskin
861dd924f6SAlexander Usyskin component_unbind_all(dev, comp_master);
871dd924f6SAlexander Usyskin }
881dd924f6SAlexander Usyskin
891dd924f6SAlexander Usyskin static const struct component_master_ops mei_component_master_ops = {
901dd924f6SAlexander Usyskin .bind = mei_component_master_bind,
911dd924f6SAlexander Usyskin .unbind = mei_component_master_unbind,
921dd924f6SAlexander Usyskin };
931dd924f6SAlexander Usyskin
941dd924f6SAlexander Usyskin /**
951dd924f6SAlexander Usyskin * mei_gsc_proxy_component_match - compare function for matching mei.
961dd924f6SAlexander Usyskin *
971dd924f6SAlexander Usyskin * The function checks if the device is pci device and
981dd924f6SAlexander Usyskin * Intel VGA adapter, the subcomponent is SW Proxy
99*9c4c28baSAlexander Usyskin * and the VGA is on the bus 0 reserved for built-in devices
100*9c4c28baSAlexander Usyskin * to reject discrete GFX.
1011dd924f6SAlexander Usyskin *
1021dd924f6SAlexander Usyskin * @dev: master device
1031dd924f6SAlexander Usyskin * @subcomponent: subcomponent to match (I915_COMPONENT_SWPROXY)
1041dd924f6SAlexander Usyskin * @data: compare data (mei pci parent)
1051dd924f6SAlexander Usyskin *
1061dd924f6SAlexander Usyskin * Return:
1071dd924f6SAlexander Usyskin * * 1 - if components match
1081dd924f6SAlexander Usyskin * * 0 - otherwise
1091dd924f6SAlexander Usyskin */
mei_gsc_proxy_component_match(struct device * dev,int subcomponent,void * data)1101dd924f6SAlexander Usyskin static int mei_gsc_proxy_component_match(struct device *dev, int subcomponent,
1111dd924f6SAlexander Usyskin void *data)
1121dd924f6SAlexander Usyskin {
1131dd924f6SAlexander Usyskin struct pci_dev *pdev;
1141dd924f6SAlexander Usyskin
1151dd924f6SAlexander Usyskin if (!dev_is_pci(dev))
1161dd924f6SAlexander Usyskin return 0;
1171dd924f6SAlexander Usyskin
1181dd924f6SAlexander Usyskin pdev = to_pci_dev(dev);
1191dd924f6SAlexander Usyskin
1201dd924f6SAlexander Usyskin if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) ||
1211dd924f6SAlexander Usyskin pdev->vendor != PCI_VENDOR_ID_INTEL)
1221dd924f6SAlexander Usyskin return 0;
1231dd924f6SAlexander Usyskin
1241dd924f6SAlexander Usyskin if (subcomponent != I915_COMPONENT_GSC_PROXY)
1251dd924f6SAlexander Usyskin return 0;
1261dd924f6SAlexander Usyskin
127*9c4c28baSAlexander Usyskin /* Only built-in GFX */
128*9c4c28baSAlexander Usyskin return (pdev->bus->number == 0);
1291dd924f6SAlexander Usyskin }
1301dd924f6SAlexander Usyskin
mei_gsc_proxy_probe(struct mei_cl_device * cldev,const struct mei_cl_device_id * id)1311dd924f6SAlexander Usyskin static int mei_gsc_proxy_probe(struct mei_cl_device *cldev,
1321dd924f6SAlexander Usyskin const struct mei_cl_device_id *id)
1331dd924f6SAlexander Usyskin {
1341dd924f6SAlexander Usyskin struct i915_gsc_proxy_component *comp_master;
1351dd924f6SAlexander Usyskin struct component_match *master_match = NULL;
1361dd924f6SAlexander Usyskin int ret;
1371dd924f6SAlexander Usyskin
1381dd924f6SAlexander Usyskin ret = mei_cldev_enable(cldev);
1391dd924f6SAlexander Usyskin if (ret < 0) {
1401dd924f6SAlexander Usyskin dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
1411dd924f6SAlexander Usyskin goto enable_err_exit;
1421dd924f6SAlexander Usyskin }
1431dd924f6SAlexander Usyskin
1441dd924f6SAlexander Usyskin comp_master = kzalloc(sizeof(*comp_master), GFP_KERNEL);
1451dd924f6SAlexander Usyskin if (!comp_master) {
1461dd924f6SAlexander Usyskin ret = -ENOMEM;
1471dd924f6SAlexander Usyskin goto err_exit;
1481dd924f6SAlexander Usyskin }
1491dd924f6SAlexander Usyskin
1501dd924f6SAlexander Usyskin component_match_add_typed(&cldev->dev, &master_match,
151*9c4c28baSAlexander Usyskin mei_gsc_proxy_component_match, NULL);
1521dd924f6SAlexander Usyskin if (IS_ERR_OR_NULL(master_match)) {
1531dd924f6SAlexander Usyskin ret = -ENOMEM;
1541dd924f6SAlexander Usyskin goto err_exit;
1551dd924f6SAlexander Usyskin }
1561dd924f6SAlexander Usyskin
1571dd924f6SAlexander Usyskin mei_cldev_set_drvdata(cldev, comp_master);
1581dd924f6SAlexander Usyskin ret = component_master_add_with_match(&cldev->dev,
1591dd924f6SAlexander Usyskin &mei_component_master_ops,
1601dd924f6SAlexander Usyskin master_match);
1611dd924f6SAlexander Usyskin if (ret < 0) {
1621dd924f6SAlexander Usyskin dev_err(&cldev->dev, "Master comp add failed %d\n", ret);
1631dd924f6SAlexander Usyskin goto err_exit;
1641dd924f6SAlexander Usyskin }
1651dd924f6SAlexander Usyskin
1661dd924f6SAlexander Usyskin return 0;
1671dd924f6SAlexander Usyskin
1681dd924f6SAlexander Usyskin err_exit:
1691dd924f6SAlexander Usyskin mei_cldev_set_drvdata(cldev, NULL);
1701dd924f6SAlexander Usyskin kfree(comp_master);
1711dd924f6SAlexander Usyskin mei_cldev_disable(cldev);
1721dd924f6SAlexander Usyskin enable_err_exit:
1731dd924f6SAlexander Usyskin return ret;
1741dd924f6SAlexander Usyskin }
1751dd924f6SAlexander Usyskin
mei_gsc_proxy_remove(struct mei_cl_device * cldev)1761dd924f6SAlexander Usyskin static void mei_gsc_proxy_remove(struct mei_cl_device *cldev)
1771dd924f6SAlexander Usyskin {
1781dd924f6SAlexander Usyskin struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev);
1791dd924f6SAlexander Usyskin int ret;
1801dd924f6SAlexander Usyskin
1811dd924f6SAlexander Usyskin component_master_del(&cldev->dev, &mei_component_master_ops);
1821dd924f6SAlexander Usyskin kfree(comp_master);
1831dd924f6SAlexander Usyskin mei_cldev_set_drvdata(cldev, NULL);
1841dd924f6SAlexander Usyskin
1851dd924f6SAlexander Usyskin ret = mei_cldev_disable(cldev);
1861dd924f6SAlexander Usyskin if (ret)
1871dd924f6SAlexander Usyskin dev_warn(&cldev->dev, "mei_cldev_disable() failed %d\n", ret);
1881dd924f6SAlexander Usyskin }
1891dd924f6SAlexander Usyskin
1901dd924f6SAlexander Usyskin #define MEI_UUID_GSC_PROXY UUID_LE(0xf73db04, 0x97ab, 0x4125, \
1911dd924f6SAlexander Usyskin 0xb8, 0x93, 0xe9, 0x4, 0xad, 0xd, 0x54, 0x64)
1921dd924f6SAlexander Usyskin
1931dd924f6SAlexander Usyskin static struct mei_cl_device_id mei_gsc_proxy_tbl[] = {
1941dd924f6SAlexander Usyskin { .uuid = MEI_UUID_GSC_PROXY, .version = MEI_CL_VERSION_ANY },
1951dd924f6SAlexander Usyskin { }
1961dd924f6SAlexander Usyskin };
1971dd924f6SAlexander Usyskin MODULE_DEVICE_TABLE(mei, mei_gsc_proxy_tbl);
1981dd924f6SAlexander Usyskin
1991dd924f6SAlexander Usyskin static struct mei_cl_driver mei_gsc_proxy_driver = {
2001dd924f6SAlexander Usyskin .id_table = mei_gsc_proxy_tbl,
2011dd924f6SAlexander Usyskin .name = KBUILD_MODNAME,
2021dd924f6SAlexander Usyskin .probe = mei_gsc_proxy_probe,
2031dd924f6SAlexander Usyskin .remove = mei_gsc_proxy_remove,
2041dd924f6SAlexander Usyskin };
2051dd924f6SAlexander Usyskin
2061dd924f6SAlexander Usyskin module_mei_cl_driver(mei_gsc_proxy_driver);
2071dd924f6SAlexander Usyskin
2081dd924f6SAlexander Usyskin MODULE_AUTHOR("Intel Corporation");
2091dd924f6SAlexander Usyskin MODULE_LICENSE("GPL");
2101dd924f6SAlexander Usyskin MODULE_DESCRIPTION("MEI GSC PROXY");
211