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/i915_component.h>
21 #include <drm/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 */
mei_gsc_proxy_send(struct device * dev,const void * buf,size_t size)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 */
mei_gsc_proxy_recv(struct device * dev,void * buf,size_t size)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
mei_component_master_bind(struct device * dev)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
mei_component_master_unbind(struct device * dev)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 parent of MEI PCI and the parent of VGA are the same PCH device.
100 *
101 * @dev: master device
102 * @subcomponent: subcomponent to match (I915_COMPONENT_SWPROXY)
103 * @data: compare data (mei pci parent)
104 *
105 * Return:
106 * * 1 - if components match
107 * * 0 - otherwise
108 */
mei_gsc_proxy_component_match(struct device * dev,int subcomponent,void * data)109 static int mei_gsc_proxy_component_match(struct device *dev, int subcomponent,
110 void *data)
111 {
112 struct pci_dev *pdev;
113
114 if (!dev_is_pci(dev))
115 return 0;
116
117 pdev = to_pci_dev(dev);
118
119 if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) ||
120 pdev->vendor != PCI_VENDOR_ID_INTEL)
121 return 0;
122
123 if (subcomponent != I915_COMPONENT_GSC_PROXY)
124 return 0;
125
126 return component_compare_dev(dev->parent, ((struct device *)data)->parent);
127 }
128
mei_gsc_proxy_probe(struct mei_cl_device * cldev,const struct mei_cl_device_id * id)129 static int mei_gsc_proxy_probe(struct mei_cl_device *cldev,
130 const struct mei_cl_device_id *id)
131 {
132 struct i915_gsc_proxy_component *comp_master;
133 struct component_match *master_match = NULL;
134 int ret;
135
136 ret = mei_cldev_enable(cldev);
137 if (ret < 0) {
138 dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
139 goto enable_err_exit;
140 }
141
142 comp_master = kzalloc(sizeof(*comp_master), GFP_KERNEL);
143 if (!comp_master) {
144 ret = -ENOMEM;
145 goto err_exit;
146 }
147
148 component_match_add_typed(&cldev->dev, &master_match,
149 mei_gsc_proxy_component_match, cldev->dev.parent);
150 if (IS_ERR_OR_NULL(master_match)) {
151 ret = -ENOMEM;
152 goto err_exit;
153 }
154
155 mei_cldev_set_drvdata(cldev, comp_master);
156 ret = component_master_add_with_match(&cldev->dev,
157 &mei_component_master_ops,
158 master_match);
159 if (ret < 0) {
160 dev_err(&cldev->dev, "Master comp add failed %d\n", ret);
161 goto err_exit;
162 }
163
164 return 0;
165
166 err_exit:
167 mei_cldev_set_drvdata(cldev, NULL);
168 kfree(comp_master);
169 mei_cldev_disable(cldev);
170 enable_err_exit:
171 return ret;
172 }
173
mei_gsc_proxy_remove(struct mei_cl_device * cldev)174 static void mei_gsc_proxy_remove(struct mei_cl_device *cldev)
175 {
176 struct i915_gsc_proxy_component *comp_master = mei_cldev_get_drvdata(cldev);
177 int ret;
178
179 component_master_del(&cldev->dev, &mei_component_master_ops);
180 kfree(comp_master);
181 mei_cldev_set_drvdata(cldev, NULL);
182
183 ret = mei_cldev_disable(cldev);
184 if (ret)
185 dev_warn(&cldev->dev, "mei_cldev_disable() failed %d\n", ret);
186 }
187
188 #define MEI_UUID_GSC_PROXY UUID_LE(0xf73db04, 0x97ab, 0x4125, \
189 0xb8, 0x93, 0xe9, 0x4, 0xad, 0xd, 0x54, 0x64)
190
191 static struct mei_cl_device_id mei_gsc_proxy_tbl[] = {
192 { .uuid = MEI_UUID_GSC_PROXY, .version = MEI_CL_VERSION_ANY },
193 { }
194 };
195 MODULE_DEVICE_TABLE(mei, mei_gsc_proxy_tbl);
196
197 static struct mei_cl_driver mei_gsc_proxy_driver = {
198 .id_table = mei_gsc_proxy_tbl,
199 .name = KBUILD_MODNAME,
200 .probe = mei_gsc_proxy_probe,
201 .remove = mei_gsc_proxy_remove,
202 };
203
204 module_mei_cl_driver(mei_gsc_proxy_driver);
205
206 MODULE_AUTHOR("Intel Corporation");
207 MODULE_LICENSE("GPL");
208 MODULE_DESCRIPTION("MEI GSC PROXY");
209