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 */
30static 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 */
51static 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
65static 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
71static 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
81static 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
89static 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 */
110static 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
131static 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
168err_exit:
169	mei_cldev_set_drvdata(cldev, NULL);
170	kfree(comp_master);
171	mei_cldev_disable(cldev);
172enable_err_exit:
173	return ret;
174}
175
176static 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
193static struct mei_cl_device_id mei_gsc_proxy_tbl[] = {
194	{ .uuid = MEI_UUID_GSC_PROXY, .version = MEI_CL_VERSION_ANY },
195	{ }
196};
197MODULE_DEVICE_TABLE(mei, mei_gsc_proxy_tbl);
198
199static 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
206module_mei_cl_driver(mei_gsc_proxy_driver);
207
208MODULE_AUTHOR("Intel Corporation");
209MODULE_LICENSE("GPL");
210MODULE_DESCRIPTION("MEI GSC PROXY");
211