mirror of
https://github.com/torvalds/linux.git
synced 2026-04-21 00:04:01 -04:00
With on-boards graphics card, both i915 and MEI are in the same device hierarchy with the same parent, while for discrete gfx card the MEI is its child device. Adjust the match function for that scenario by matching MEI parent device with i915. Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com> Cc: Vitaly Lubart <vitaly.lubart@intel.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Alan Previn <alan.previn.teres.alexis@intel.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://patchwork.freedesktop.org/patch/msgid/20220928004145.745803-7-daniele.ceraolospurio@intel.com
262 lines
6.4 KiB
C
262 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright © 2020 - 2021 Intel Corporation
|
|
*/
|
|
|
|
/**
|
|
* DOC: MEI_PXP Client Driver
|
|
*
|
|
* The mei_pxp driver acts as a translation layer between PXP
|
|
* protocol implementer (I915) and ME FW by translating PXP
|
|
* negotiation messages to ME FW command payloads and vice versa.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/uuid.h>
|
|
#include <linux/mei_cl_bus.h>
|
|
#include <linux/component.h>
|
|
#include <drm/drm_connector.h>
|
|
#include <drm/i915_component.h>
|
|
#include <drm/i915_pxp_tee_interface.h>
|
|
|
|
#include "mei_pxp.h"
|
|
|
|
/**
|
|
* mei_pxp_send_message() - Sends a PXP message to ME FW.
|
|
* @dev: device corresponding to the mei_cl_device
|
|
* @message: a message buffer to send
|
|
* @size: size of the message
|
|
* Return: 0 on Success, <0 on Failure
|
|
*/
|
|
static int
|
|
mei_pxp_send_message(struct device *dev, const void *message, size_t size)
|
|
{
|
|
struct mei_cl_device *cldev;
|
|
ssize_t byte;
|
|
|
|
if (!dev || !message)
|
|
return -EINVAL;
|
|
|
|
cldev = to_mei_cl_device(dev);
|
|
|
|
/* temporary drop const qualifier till the API is fixed */
|
|
byte = mei_cldev_send(cldev, (u8 *)message, size);
|
|
if (byte < 0) {
|
|
dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
|
|
return byte;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* mei_pxp_receive_message() - Receives a PXP message from ME FW.
|
|
* @dev: device corresponding to the mei_cl_device
|
|
* @buffer: a message buffer to contain the received message
|
|
* @size: size of the buffer
|
|
* Return: bytes sent on Success, <0 on Failure
|
|
*/
|
|
static int
|
|
mei_pxp_receive_message(struct device *dev, void *buffer, size_t size)
|
|
{
|
|
struct mei_cl_device *cldev;
|
|
ssize_t byte;
|
|
|
|
if (!dev || !buffer)
|
|
return -EINVAL;
|
|
|
|
cldev = to_mei_cl_device(dev);
|
|
|
|
byte = mei_cldev_recv(cldev, buffer, size);
|
|
if (byte < 0) {
|
|
dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
|
|
return byte;
|
|
}
|
|
|
|
return byte;
|
|
}
|
|
|
|
/**
|
|
* mei_pxp_gsc_command() - sends a gsc command, by sending
|
|
* a sgl mei message to gsc and receiving reply from gsc
|
|
*
|
|
* @dev: device corresponding to the mei_cl_device
|
|
* @client_id: client id to send the command to
|
|
* @fence_id: fence id to send the command to
|
|
* @sg_in: scatter gather list containing addresses for rx message buffer
|
|
* @total_in_len: total length of data in 'in' sg, can be less than the sum of buffers sizes
|
|
* @sg_out: scatter gather list containing addresses for tx message buffer
|
|
*
|
|
* Return: bytes sent on Success, <0 on Failure
|
|
*/
|
|
static ssize_t mei_pxp_gsc_command(struct device *dev, u8 client_id, u32 fence_id,
|
|
struct scatterlist *sg_in, size_t total_in_len,
|
|
struct scatterlist *sg_out)
|
|
{
|
|
struct mei_cl_device *cldev;
|
|
|
|
cldev = to_mei_cl_device(dev);
|
|
|
|
return mei_cldev_send_gsc_command(cldev, client_id, fence_id, sg_in, total_in_len, sg_out);
|
|
}
|
|
|
|
static const struct i915_pxp_component_ops mei_pxp_ops = {
|
|
.owner = THIS_MODULE,
|
|
.send = mei_pxp_send_message,
|
|
.recv = mei_pxp_receive_message,
|
|
.gsc_command = mei_pxp_gsc_command,
|
|
};
|
|
|
|
static int mei_component_master_bind(struct device *dev)
|
|
{
|
|
struct mei_cl_device *cldev = to_mei_cl_device(dev);
|
|
struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
|
|
int ret;
|
|
|
|
comp_master->ops = &mei_pxp_ops;
|
|
comp_master->tee_dev = dev;
|
|
ret = component_bind_all(dev, comp_master);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mei_component_master_unbind(struct device *dev)
|
|
{
|
|
struct mei_cl_device *cldev = to_mei_cl_device(dev);
|
|
struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
|
|
|
|
component_unbind_all(dev, comp_master);
|
|
}
|
|
|
|
static const struct component_master_ops mei_component_master_ops = {
|
|
.bind = mei_component_master_bind,
|
|
.unbind = mei_component_master_unbind,
|
|
};
|
|
|
|
/**
|
|
* mei_pxp_component_match - compare function for matching mei pxp.
|
|
*
|
|
* The function checks if the driver is i915, the subcomponent is PXP
|
|
* and the grand parent of pxp and the parent of i915 are the same
|
|
* PCH device.
|
|
*
|
|
* @dev: master device
|
|
* @subcomponent: subcomponent to match (I915_COMPONENT_PXP)
|
|
* @data: compare data (mei pxp device)
|
|
*
|
|
* Return:
|
|
* * 1 - if components match
|
|
* * 0 - otherwise
|
|
*/
|
|
static int mei_pxp_component_match(struct device *dev, int subcomponent,
|
|
void *data)
|
|
{
|
|
struct device *base = data;
|
|
|
|
if (!dev)
|
|
return 0;
|
|
|
|
if (!dev->driver || strcmp(dev->driver->name, "i915") ||
|
|
subcomponent != I915_COMPONENT_PXP)
|
|
return 0;
|
|
|
|
base = base->parent;
|
|
if (!base) /* mei device */
|
|
return 0;
|
|
|
|
base = base->parent; /* pci device */
|
|
/* for dgfx */
|
|
if (base && dev == base)
|
|
return 1;
|
|
|
|
/* for pch */
|
|
dev = dev->parent;
|
|
return (base && dev && dev == base);
|
|
}
|
|
|
|
static int mei_pxp_probe(struct mei_cl_device *cldev,
|
|
const struct mei_cl_device_id *id)
|
|
{
|
|
struct i915_pxp_component *comp_master;
|
|
struct component_match *master_match;
|
|
int ret;
|
|
|
|
ret = mei_cldev_enable(cldev);
|
|
if (ret < 0) {
|
|
dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
|
|
goto enable_err_exit;
|
|
}
|
|
|
|
comp_master = kzalloc(sizeof(*comp_master), GFP_KERNEL);
|
|
if (!comp_master) {
|
|
ret = -ENOMEM;
|
|
goto err_exit;
|
|
}
|
|
|
|
master_match = NULL;
|
|
component_match_add_typed(&cldev->dev, &master_match,
|
|
mei_pxp_component_match, &cldev->dev);
|
|
if (IS_ERR_OR_NULL(master_match)) {
|
|
ret = -ENOMEM;
|
|
goto err_exit;
|
|
}
|
|
|
|
mei_cldev_set_drvdata(cldev, comp_master);
|
|
ret = component_master_add_with_match(&cldev->dev,
|
|
&mei_component_master_ops,
|
|
master_match);
|
|
if (ret < 0) {
|
|
dev_err(&cldev->dev, "Master comp add failed %d\n", ret);
|
|
goto err_exit;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_exit:
|
|
mei_cldev_set_drvdata(cldev, NULL);
|
|
kfree(comp_master);
|
|
mei_cldev_disable(cldev);
|
|
enable_err_exit:
|
|
return ret;
|
|
}
|
|
|
|
static void mei_pxp_remove(struct mei_cl_device *cldev)
|
|
{
|
|
struct i915_pxp_component *comp_master = mei_cldev_get_drvdata(cldev);
|
|
int ret;
|
|
|
|
component_master_del(&cldev->dev, &mei_component_master_ops);
|
|
kfree(comp_master);
|
|
mei_cldev_set_drvdata(cldev, NULL);
|
|
|
|
ret = mei_cldev_disable(cldev);
|
|
if (ret)
|
|
dev_warn(&cldev->dev, "mei_cldev_disable() failed\n");
|
|
}
|
|
|
|
/* fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1 : PAVP GUID*/
|
|
#define MEI_GUID_PXP GUID_INIT(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \
|
|
0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1)
|
|
|
|
static struct mei_cl_device_id mei_pxp_tbl[] = {
|
|
{ .uuid = MEI_GUID_PXP, .version = MEI_CL_VERSION_ANY },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(mei, mei_pxp_tbl);
|
|
|
|
static struct mei_cl_driver mei_pxp_driver = {
|
|
.id_table = mei_pxp_tbl,
|
|
.name = KBUILD_MODNAME,
|
|
.probe = mei_pxp_probe,
|
|
.remove = mei_pxp_remove,
|
|
};
|
|
|
|
module_mei_cl_driver(mei_pxp_driver);
|
|
|
|
MODULE_AUTHOR("Intel Corporation");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("MEI PXP");
|