Merge tag 'zonefs-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal...
[sfrench/cifs-2.6.git] / drivers / misc / mei / gsc_proxy / mei_gsc_proxy.c
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  */
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");