Selaa lähdekoodia

rpmsg-kdrv: Add support for device virtualization

In J721E, multiple compute entities can run in parallel,
and can require to use any given H/W simultaneously.

Or, a device may be scheduled between multiple cores, and
a dedicated core can manage the switching based on
requests / policies

rpmsg-kdrv is a device virtualization framework that uses
rpmsg core functionalities. In a system using this feature,
a remoteproc is expected to advertise virtual device
endpoints that the host can connect to and request for
services.

Currently, a display virtualization endpoint is enabled
and is used by tidss to enable display sharing

Signed-off-by: Subhajit Paul <subhajit_paul@ti.com>
Signed-off-by: Jyri Sarha <jsarha@ti.com>
Subhajit Paul 6 vuotta sitten
vanhempi
commit
2d55a7a151

+ 2 - 0
drivers/Kconfig

@@ -155,6 +155,8 @@ source "drivers/remoteproc/Kconfig"
 
 source "drivers/rpmsg/Kconfig"
 
+source "drivers/rpmsg-kdrv/Kconfig"
+
 source "drivers/soundwire/Kconfig"
 
 source "drivers/soc/Kconfig"

+ 1 - 0
drivers/Makefile

@@ -155,6 +155,7 @@ obj-$(CONFIG_MAILBOX)		+= mailbox/
 obj-$(CONFIG_HWSPINLOCK)	+= hwspinlock/
 obj-$(CONFIG_REMOTEPROC)	+= remoteproc/
 obj-$(CONFIG_RPMSG)		+= rpmsg/
+obj-$(CONFIG_RPMSG_KDRV)	+= rpmsg-kdrv/
 obj-$(CONFIG_SOUNDWIRE)		+= soundwire/
 
 # Virtualization drivers

+ 25 - 0
drivers/rpmsg-kdrv/Kconfig

@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Rpmsg virtual device drivers"
+
+# RPMSG always gets selected by whoever wants it
+config RPMSG_KDRV
+	tristate "RPMSG virtual device interface"
+	select RPMSG
+	help
+	  Say Y here enable support for RPMSG based remote devices, usually
+	  exported by a firmware running rpmsg stack and remote_device stack.
+	  This feature enables the framework for para-virtualizing entire H/W
+	  or specific resources of a hardware
+
+
+config RPMSG_KDRV_DISPLAY
+	tristate "RPMSG virtual display device support"
+	select RPMSG_KDRV
+	help
+	  Say Y here to enable support for remote device based display
+	  virtualization. This setup expects that the display will be driven
+	  by a remoteproc and DRM driver will be able to use display features
+	  using remote_device framework
+
+endmenu

+ 3 - 0
drivers/rpmsg-kdrv/Makefile

@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_RPMSG_KDRV)		+= rpmsg_kdrv.o
+obj-$(CONFIG_RPMSG_KDRV_DISPLAY)	+= rpmsg_kdrv_display.o

+ 742 - 0
drivers/rpmsg-kdrv/rpmsg_kdrv.c

@@ -0,0 +1,742 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subahjit_paul@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include <linux/rpmsg.h>
+#include <linux/rpmsg-remotedev/rpmsg-remotedev.h>
+#include "shared/rpmsg-kdrv-transport.h"
+#include "rpmsg_kdrv_internal.h"
+
+struct rpmsg_kdrv_priv {
+	struct rpmsg_device *rpdev;
+
+	struct idr message_idr;
+	struct mutex message_lock;
+
+	int num_raw_devices;
+	struct rpmsg_kdrv_init_device_info raw_devices[RPMSG_KDRV_TP_MAX_DEVICES];
+	void *raw_device_data[RPMSG_KDRV_TP_MAX_DEVICES];
+	int raw_device_data_size[RPMSG_KDRV_TP_MAX_DEVICES];
+};
+
+struct rpmsg_kdrv_ctx {
+	struct rpmsg_device *rpdev;
+	bool wait_for_response;
+	request_cb_t callback;
+	void *cb_data;
+	bool response_recv;
+	struct wait_queue_head response_wq;
+
+	struct rpmsg_kdrv_device_header *dev_hdr;
+	void *req;
+	void *resp;
+	int req_size;
+	int resp_size;
+};
+
+static struct bus_type rpmsg_kdrv_bus;
+
+#define to_rpmsg_kdrv_device(d) container_of(d, struct rpmsg_kdrv_device, dev)
+#define to_rpmsg_kdrv_driver(d) container_of(d, struct rpmsg_kdrv_driver, drv)
+
+static int rpmsg_kdrv_match_id(struct device *dev, void *data)
+{
+	uint32_t *idptr = data;
+	struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+
+	if (kddev->device_id == *idptr)
+		return 1;
+	return 0;
+}
+
+static int rpmsg_kdrv_match_remotedev(struct device *dev, void *data)
+{
+	struct rpmsg_remotedev *rdev = data;
+	struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+
+	if (kddev->remotedev == rdev)
+		return 1;
+	return 0;
+}
+
+static int rpmsg_kdrv_match_name(struct device *dev, void *data)
+{
+	const char *name = data;
+	struct rpmsg_kdrv_device *kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+
+	if (strcmp(kddev->device_name, name) == 0)
+		return 1;
+	return 0;
+}
+
+int rpmsg_kdrv_register_driver(struct rpmsg_kdrv_driver *drv)
+{
+	int ret;
+
+	drv->drv.bus = &rpmsg_kdrv_bus;
+	drv->drv.owner = THIS_MODULE;
+
+	ret = driver_register(&drv->drv);
+	if (ret)
+		pr_err("%s: driver_register failed\n", __func__);
+
+	return ret;
+}
+EXPORT_SYMBOL(rpmsg_kdrv_register_driver);
+
+static void rpmsg_kdrv_driver_handle_data(struct rpmsg_device *rpdev, void *data, int len, void *private, u32 src)
+{
+	struct device *dev;
+	struct rpmsg_kdrv_device_header *hdr = data;
+	struct rpmsg_kdrv_device *kddev = NULL;
+	struct rpmsg_kdrv_driver *kddrv = NULL;
+	void *message;
+	int message_size;
+	uint32_t msg_device_id;
+	int ret;
+
+	msg_device_id = hdr->device_id;
+	dev = bus_find_device(&rpmsg_kdrv_bus, NULL, &(msg_device_id), rpmsg_kdrv_match_id);
+	if (!dev) {
+		dev_err(&rpdev->dev, "%s: message received for unknown device\n", __func__);
+		return;
+	}
+	kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+	kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver);
+	if (!kddrv) {
+		dev_err(&rpdev->dev, "%s: message received for device with no driver\n", __func__);
+		return;
+	}
+
+	message = (void *)(&hdr[1]);
+	message_size = len - sizeof(*hdr);
+	ret = kddrv->callback(kddev, message, message_size);
+	if (ret)
+		dev_err(&rpdev->dev, "%s: message callback returns %d\n", __func__, ret);
+
+}
+
+static int rpmsg_kdrv_connect(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device *kddev)
+{
+	int ret;
+	struct rpmsg_kdrv_init_connect_message *connect_req;
+
+	connect_req = devm_kzalloc(&rpdev->dev, sizeof(*connect_req), GFP_KERNEL);
+	if (!connect_req)
+		return -ENOMEM;
+
+	connect_req->header.message_type = RPMSG_KDRV_TP_INIT_CONNECT_MESSAGE;
+	connect_req->device_id = kddev->device_id;
+
+	ret = rpmsg_kdrv_send_message(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT,
+			connect_req, sizeof(*connect_req));
+
+	devm_kfree(&rpdev->dev, connect_req);
+	return ret;
+}
+
+static int rpmsg_kdrv_disconnect(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device *kddev)
+{
+	int ret;
+	struct rpmsg_kdrv_init_disconnect_message *disconnect_req;
+
+	disconnect_req = devm_kzalloc(&rpdev->dev, sizeof(*disconnect_req), GFP_KERNEL);
+	if (!disconnect_req)
+		return -ENOMEM;
+
+	disconnect_req->header.message_type = RPMSG_KDRV_TP_INIT_DISCONNECT_MESSAGE;
+	disconnect_req->device_id = kddev->device_id;
+
+	ret = rpmsg_kdrv_send_message(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT,
+			disconnect_req, sizeof(*disconnect_req));
+
+	devm_kfree(&rpdev->dev, disconnect_req);
+	return ret;
+}
+
+struct rpmsg_remotedev *rpmsg_remotedev_get_named_device(const char *device_name)
+{
+	struct device *dev;
+	struct rpmsg_kdrv_device *kddev = NULL;
+
+	dev = bus_find_device(&rpmsg_kdrv_bus, NULL, (void *)device_name, rpmsg_kdrv_match_name);
+	if (!dev)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+	if (!kddev->remotedev)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	rpmsg_kdrv_connect(kddev->rpdev, kddev);
+
+	return kddev->remotedev;
+}
+EXPORT_SYMBOL(rpmsg_remotedev_get_named_device);
+
+void rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev)
+{
+	struct device *dev;
+	struct rpmsg_kdrv_device *kddev = NULL;
+
+	dev = bus_find_device(&rpmsg_kdrv_bus, NULL, (void *)rdev, rpmsg_kdrv_match_remotedev);
+	if (!dev) {
+		pr_err("%s: could not find device for remotedev\n", __func__);
+		return;
+	}
+
+	kddev = container_of(dev, struct rpmsg_kdrv_device, dev);
+
+	rpmsg_kdrv_disconnect(kddev->rpdev, kddev);
+}
+EXPORT_SYMBOL(rpmsg_remotedev_put_device);
+
+static void rpmsg_kdrv_release_device(struct device *dev)
+{
+	struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	devm_kfree(&kddev->rpdev->dev, kddev);
+}
+
+static struct rpmsg_kdrv_device *rpmsg_kdrv_device_create(struct rpmsg_device *rpdev, int index)
+{
+	struct rpmsg_kdrv_device *kddev = devm_kzalloc(&rpdev->dev, sizeof(*kddev), GFP_KERNEL);
+	struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev);
+	struct rpmsg_kdrv_init_device_info *dev = &priv->raw_devices[index];
+	int ret;
+
+	if (!kddev) {
+		dev_err(&rpdev->dev, "%s: could not allocate kddev\n", __func__);
+		return NULL;
+	}
+
+	kddev->rpdev = rpdev;
+	kddev->device_id = dev->device_id;
+	kddev->device_type = dev->device_type;
+	kddev->device_data_len = priv->raw_device_data_size[index];
+	kddev->device_data = priv->raw_device_data[index];
+	kddev->device_name = devm_kstrdup(&rpdev->dev, dev->device_name, GFP_KERNEL);
+	if (!kddev->device_name) {
+		dev_err(&rpdev->dev, "%s: could not allocate device name\n", __func__);
+		devm_kfree(&rpdev->dev, kddev);
+		return NULL;
+	}
+
+	kddev->dev.parent = &rpdev->dev;
+	kddev->dev.release = rpmsg_kdrv_release_device;
+	kddev->dev.bus = &rpmsg_kdrv_bus;
+
+	dev_set_name(&kddev->dev, "rpmsg-kdrv-%u-%s", dev->device_id, dev->device_name);
+
+	ret = device_register(&kddev->dev);
+	if (ret) {
+		dev_err(&rpdev->dev, "%s: device_register failed: %d\n", __func__, ret);
+		put_device(&kddev->dev);
+		return NULL;
+	}
+	dev_dbg(&rpdev->dev, "%s: registered new device : %s\n", __func__, dev_name(&kddev->dev));
+
+	return kddev;
+}
+
+static int rpmsg_kdrv_get_devices_cb(void *cb_data, void *req, int req_sz, void *resp, int resp_sz)
+{
+	int i, cnt;
+	struct rpmsg_device *rpdev = cb_data;
+	struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev);
+	struct rpmsg_kdrv_init_dev_info_response *info_resp = resp;
+	struct rpmsg_kdrv_init_device_info *dev;
+	int ret;
+
+	if (info_resp->header.message_type != RPMSG_KDRV_TP_INIT_DEV_INFO_RESPONSE) {
+		dev_err(&rpdev->dev, "%s: wrong response type\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; i < info_resp->num_devices; i++) {
+		dev = &info_resp->devices[i];
+		cnt = priv->num_raw_devices;
+
+		priv->raw_device_data_size[cnt] = dev->device_data_len;
+		priv->raw_device_data[cnt] = devm_kzalloc(&rpdev->dev, dev->device_data_len, GFP_KERNEL);
+		if (!priv->raw_device_data[cnt])
+			goto out;
+		memcpy(priv->raw_device_data[cnt],
+				&info_resp->device_data[dev->device_data_offset],
+				dev->device_data_len);
+		memcpy(&priv->raw_devices[cnt], dev, sizeof(*dev));
+		priv->num_raw_devices++;
+
+		dev_dbg(&rpdev->dev, "new device: %s\n", dev->device_name);
+	}
+
+	for (i = 0; i < priv->num_raw_devices; i++)
+		rpmsg_kdrv_device_create(rpdev, i);
+
+out:
+	devm_kfree(&rpdev->dev, req);
+	return ret;
+}
+
+static int rpmsg_kdrv_get_devices(struct rpmsg_device *rpdev)
+{
+	int ret;
+	struct rpmsg_kdrv_init_dev_info_request *info_req;
+
+	info_req = devm_kzalloc(&rpdev->dev, sizeof(*info_req), GFP_KERNEL);
+	if (!info_req)
+		return -ENOMEM;
+
+	info_req->header.message_type = RPMSG_KDRV_TP_INIT_DEV_INFO_REQUEST;
+
+	ret = rpmsg_kdrv_send_request_with_callback(rpdev, RPMSG_KDRV_TP_DEVICE_ID_INIT,
+			info_req, sizeof(*info_req), rpdev, rpmsg_kdrv_get_devices_cb);
+	if (ret)
+		goto nosend;
+
+	return 0;
+
+nosend:
+	devm_kfree(&rpdev->dev, info_req);
+	return ret;
+}
+
+static void rpmsg_kdrv_del_packet_id(struct rpmsg_device *rpdev, int id)
+{
+	struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev);
+
+	mutex_lock(&priv->message_lock);
+	idr_remove(&priv->message_idr, id);
+	mutex_unlock(&priv->message_lock);
+}
+
+static uint32_t rpmsg_kdrv_new_packet_id(struct rpmsg_device *rpdev, void *data)
+{
+	struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev);
+	int id;
+
+	mutex_lock(&priv->message_lock);
+	id = idr_alloc(&priv->message_idr, data, RPMSG_KDRV_TP_PACKET_ID_FIRST, 0, GFP_KERNEL);
+	mutex_unlock(&priv->message_lock);
+
+	if (id < 0)
+		return 0;
+
+	return id;
+}
+
+static void rpmsg_kdrv_dev_hdr_delete(struct rpmsg_device *rpdev, struct rpmsg_kdrv_device_header *hdr)
+{
+	rpmsg_kdrv_del_packet_id(rpdev, hdr->packet_id);
+	devm_kfree(&rpdev->dev, hdr);
+}
+
+static struct rpmsg_kdrv_device_header *rpmsg_kdrv_dev_hdr_alloc(struct rpmsg_device *rpdev,
+		int device_id, int size, int pkt_type, int pkt_src, void *msg, int len, struct rpmsg_kdrv_ctx *ctx)
+{
+	struct rpmsg_kdrv_device_header *dev_hdr;
+	void *dst;
+
+	dev_hdr = devm_kzalloc(&rpdev->dev, size, GFP_KERNEL);
+	if (!dev_hdr)
+		return NULL;
+
+	dev_hdr->device_id = device_id;
+	dev_hdr->packet_type = pkt_type;
+	dev_hdr->packet_source = pkt_src;
+	dev_hdr->packet_size = size;
+	dev_hdr->packet_id = RPMSG_KDRV_TP_PACKET_ID_NONE;
+
+
+	dst = (void *)(&dev_hdr[1]);
+	memcpy(dst, msg, len);
+
+	if (pkt_type == RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE)
+		return dev_hdr;
+
+	dev_hdr->packet_id = rpmsg_kdrv_new_packet_id(rpdev, ctx);
+	if (!dev_hdr->packet_id) {
+		devm_kfree(&rpdev->dev, dev_hdr);
+		return NULL;
+	}
+
+	ctx->dev_hdr = dev_hdr;
+
+	return dev_hdr;
+}
+
+static struct rpmsg_kdrv_ctx *rpmsg_kdrv_ctx_alloc(struct rpmsg_device *rpdev, bool blocking,
+		request_cb_t callback, void *cb_data, void *req, int req_size, void *resp, int resp_size)
+{
+	struct rpmsg_kdrv_ctx *ctx;
+
+	ctx = devm_kzalloc(&rpdev->dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return NULL;
+
+	ctx->rpdev = rpdev;
+	if (blocking) {
+		ctx->wait_for_response = true;
+		ctx->response_recv = false;
+		init_waitqueue_head(&ctx->response_wq);
+	} else {
+		ctx->wait_for_response = false;
+		ctx->callback = callback;
+	}
+
+	ctx->cb_data = cb_data;
+	ctx->req = req;
+	ctx->req_size = req_size;
+	ctx->resp = resp;
+	ctx->resp_size = resp_size;
+
+	return ctx;
+}
+
+static int rpmsg_kdrv_send_packet(struct rpmsg_device *rpdev, void *data, int len)
+{
+	return rpmsg_send(rpdev->ept, data, len);
+}
+
+/*
+ * rpmsg_kdrv_send_request_with_callback
+ *
+ * Send a message where
+ * a) the caller does not block
+ * b) the caller expects multile responses
+ *
+ * The callback function must return
+ * a) RRMSG_KDRV_CALLBACK_DONE when no more responses are expected
+ * b) RPMSG_KDRV_CALLBACK_MORE when more responses are awaited
+ *
+ * The caller is expected to destroy message when it does not
+ * expect any more responses
+ */
+int rpmsg_kdrv_send_request_with_callback(struct rpmsg_device *rpdev, uint32_t device_id,
+		void *message, uint32_t message_size,
+		void *cb_data, request_cb_t callback)
+{
+	struct rpmsg_kdrv_device_header *dev_hdr;
+	int total_size = message_size + sizeof(*dev_hdr);
+	struct rpmsg_kdrv_ctx *ctx = NULL;
+	int ret;
+
+	ctx = rpmsg_kdrv_ctx_alloc(rpdev, false, callback, cb_data, message, message_size, NULL, 0);
+	if (!ctx) {
+		dev_err(&rpdev->dev, "%s: ctx allocation failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size,
+			RPMSG_KDRV_TP_PACKET_TYPE_REQUEST,
+			RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT,
+			message, message_size,
+			ctx);
+	if (!dev_hdr) {
+		dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto dev_hdr_fail;
+	}
+
+	ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size);
+	if (ret) {
+		dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+		goto nosend;
+	}
+
+	return 0;
+
+nosend:
+	rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr);
+dev_hdr_fail:
+	devm_kfree(&rpdev->dev, ctx);
+	return ret;
+}
+EXPORT_SYMBOL(rpmsg_kdrv_send_request_with_callback);
+
+/*
+ * rpmsg_kdrv_send_request_with_response
+ *
+ * Send a message where the caller will block for a response
+ *
+ * The caller is expected to destroy message and response
+ * when this function returns
+ */
+int rpmsg_kdrv_send_request_with_response(struct rpmsg_device *rpdev, uint32_t device_id,
+		void *message, uint32_t message_size,
+		void *response, uint32_t response_size)
+{
+	struct rpmsg_kdrv_device_header *dev_hdr;
+	int total_size = message_size + sizeof(*dev_hdr);
+	struct rpmsg_kdrv_ctx *ctx = NULL;
+	int ret;
+
+	ctx = rpmsg_kdrv_ctx_alloc(rpdev, true, NULL, NULL, message, message_size, response, response_size);
+	if (!ctx) {
+		dev_err(&rpdev->dev, "%s: ctx allocation failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size,
+			RPMSG_KDRV_TP_PACKET_TYPE_REQUEST,
+			RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT,
+			message, message_size,
+			ctx);
+	if (!dev_hdr) {
+		dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__);
+		ret = -ENOMEM;
+		goto dev_hdr_fail;
+	}
+
+	ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size);
+	if (ret) {
+		dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+		goto nosend;
+	}
+
+	wait_event(ctx->response_wq, ctx->response_recv == true);
+
+nosend:
+	rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr);
+dev_hdr_fail:
+	devm_kfree(&rpdev->dev, ctx);
+	return ret;
+}
+EXPORT_SYMBOL(rpmsg_kdrv_send_request_with_response);
+
+/*
+ * rpmsg_kdrv_send_message
+ *
+ * Send a message and dont expect a response
+ *
+ * The caller is expected to destroy message when
+ * this function returns
+ */
+int rpmsg_kdrv_send_message(struct rpmsg_device *rpdev, uint32_t device_id,
+		void *message, uint32_t message_size)
+{
+	struct rpmsg_kdrv_device_header *dev_hdr;
+	int total_size = message_size + sizeof(*dev_hdr);
+	int ret;
+
+	/* We dont need a ctx for direct messages */
+
+	dev_hdr = rpmsg_kdrv_dev_hdr_alloc(rpdev, device_id, total_size,
+			RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE,
+			RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT,
+			message, message_size,
+			NULL);
+	if (!dev_hdr) {
+		dev_err(&rpdev->dev, "%s: device header allocation failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = rpmsg_kdrv_send_packet(rpdev, dev_hdr, total_size);
+	if (ret) {
+		dev_err(&rpdev->dev, "%s: rpmsg_send failed: %d\n", __func__, ret);
+		goto out;
+	}
+
+out:
+	rpmsg_kdrv_dev_hdr_delete(rpdev, dev_hdr);
+	return ret;
+}
+EXPORT_SYMBOL(rpmsg_kdrv_send_message);
+
+static int rpmsg_kdrv_cb(struct rpmsg_device *rpdev, void *data, int len,
+						void *private, u32 src)
+{
+	struct rpmsg_kdrv_priv *priv = dev_get_drvdata(&rpdev->dev);
+	struct rpmsg_kdrv_device_header *hdr = data;
+	struct rpmsg_kdrv_message_header *msg;
+	int msg_len;
+	struct rpmsg_kdrv_ctx *ctx;
+	int ret;
+
+	if (hdr->packet_type != RPMSG_KDRV_TP_PACKET_TYPE_RESPONSE) {
+		rpmsg_kdrv_driver_handle_data(rpdev, data, len, private, src);
+		return 0;
+	}
+
+	mutex_lock(&priv->message_lock);
+	ctx = idr_find(&priv->message_idr, hdr->packet_id);
+	mutex_unlock(&priv->message_lock);
+
+	if (!ctx) {
+		dev_err(&rpdev->dev, "%s: response received with no pending request\n", __func__);
+		return 0;
+	}
+
+	msg = (struct rpmsg_kdrv_message_header *)((void *)(&hdr[1]));
+	msg_len = len - sizeof(*hdr);
+
+	/* process callback if expected */
+	if (ctx->callback) {
+		ret = ctx->callback(ctx->cb_data, ctx->req, ctx->req_size, msg, msg_len);
+		if (ret == RRMSG_KDRV_CALLBACK_DONE) {
+			/* No need to keep the ctx alive */
+			rpmsg_kdrv_dev_hdr_delete(rpdev, ctx->dev_hdr);
+			devm_kfree(&rpdev->dev, ctx);
+		}
+		return 0;
+	}
+
+	/* copy the response and wake up caller, caller will destroy ctx & dev_hdr */
+	memcpy(ctx->resp, msg, min(msg_len, ctx->resp_size));
+
+	ctx->response_recv = true;
+	wake_up(&ctx->response_wq);
+
+	return 0;
+}
+
+static int rpmsg_kdrv_dev_match(struct device *dev, struct device_driver *drv)
+{
+	struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev);
+	struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(drv);
+
+	if (kddrv->device_type == kddev->device_type) {
+		dev_dbg(dev, "%s: matching with driver %s\n", __func__, drv->name);
+		return 1;
+	}
+
+	dev_dbg(dev, "%s: does not match driver %s\n", __func__, drv->name);
+	return 0;
+}
+
+static int rpmsg_kdrv_dev_probe(struct device *dev)
+{
+	struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev);
+	struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver);
+	int ret;
+
+	dev_dbg(dev, "%s: probe\n", __func__);
+
+	ret = kddrv->probe(kddev);
+	if (ret) {
+		dev_err(dev, "%s: child probe failed\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rpmsg_kdrv_dev_remove(struct device *dev)
+{
+	struct rpmsg_kdrv_device *kddev = to_rpmsg_kdrv_device(dev);
+	struct rpmsg_kdrv_driver *kddrv = to_rpmsg_kdrv_driver(kddev->dev.driver);
+
+	dev_dbg(dev, "%s: remove\n", __func__);
+
+	kddrv->remove(kddev);
+	return 0;
+}
+
+static int rpmsg_kdrv_probe(struct rpmsg_device *rpdev)
+{
+	int ret;
+	struct rpmsg_kdrv_priv *priv;
+
+	dev_dbg(&rpdev->dev, "%s: probing rpmsg kdrv driver\n", __func__);
+
+	priv = devm_kzalloc(&rpdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	dev_set_drvdata(&rpdev->dev, priv);
+	priv->rpdev = rpdev;
+
+	idr_init(&priv->message_idr);
+	mutex_init(&priv->message_lock);
+
+	dev_dbg(&rpdev->dev, "%s: sending device info request\n", __func__);
+	ret = rpmsg_kdrv_get_devices(rpdev);
+	if (ret) {
+		dev_err(&rpdev->dev, "%s: error collecting device info\n", __func__);
+		goto out;
+	}
+
+	return 0;
+
+out:
+	dev_set_drvdata(&rpdev->dev, NULL);
+	devm_kfree(&rpdev->dev, priv);
+	return ret;
+}
+
+static void rpmsg_kdrv_remove(struct rpmsg_device *rpdev)
+{
+	dev_dbg(&rpdev->dev, "removing rpmsg kdrv driver\n");
+
+	/* TODO check for pending responses for any of the child devices */
+	/* TODO disconnect them all */
+}
+
+static struct bus_type rpmsg_kdrv_bus = {
+	.name		= "rpmsg_kdrv",
+	.match		= rpmsg_kdrv_dev_match,
+	.probe		= rpmsg_kdrv_dev_probe,
+	.remove		= rpmsg_kdrv_dev_remove,
+};
+
+static struct rpmsg_device_id rpmsg_kdrv_id_table[] = {
+	{ .name	= "rpmsg-kdrv" },
+	{ },
+};
+
+static struct rpmsg_driver rpmsg_kdrv = {
+	.drv.name	= "rpmsg-kdrv",
+	.id_table	= rpmsg_kdrv_id_table,
+	.probe		= rpmsg_kdrv_probe,
+	.callback	= rpmsg_kdrv_cb,
+	.remove		= rpmsg_kdrv_remove,
+};
+
+static int __init rpmsg_kdrv_init(void)
+{
+	int ret;
+
+	ret = bus_register(&rpmsg_kdrv_bus);
+	if (ret) {
+		pr_err("failed to register rpmsg kdrv bus: %d\n", ret);
+		goto out;
+	}
+
+	ret = register_rpmsg_driver(&rpmsg_kdrv);
+	if (ret) {
+		pr_err("failed to register rpmsg kdrv driver: %d\n", ret);
+		goto rpdrv_fail;
+	}
+
+	pr_debug("registered rpmsg kdrv driver\n");
+
+	return 0;
+
+rpdrv_fail:
+	bus_unregister(&rpmsg_kdrv_bus);
+out:
+	return ret;
+}
+module_init(rpmsg_kdrv_init);
+
+static void __exit rpmsg_kdrv_fini(void)
+{
+	pr_debug("unregistering rpmsg kdrv driver\n");
+
+	unregister_rpmsg_driver(&rpmsg_kdrv);
+	bus_unregister(&rpmsg_kdrv_bus);
+}
+module_exit(rpmsg_kdrv_fini);
+
+MODULE_AUTHOR("Subhajit Paul <subhajit_paul@ti.com>");
+MODULE_DESCRIPTION("TI Remote-device framework Driver");
+MODULE_LICENSE("GPL v2");

+ 473 - 0
drivers/rpmsg-kdrv/rpmsg_kdrv_display.c

@@ -0,0 +1,473 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subahjit_paul@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drm_fourcc.h>
+#include <linux/rpmsg.h>
+#include <linux/rpmsg-remotedev/rpmsg-remotedev.h>
+
+#include "shared/rpmsg-kdrv-transport-display.h"
+#include "rpmsg_kdrv_internal.h"
+
+#define RPMSG_KDRV_DISPLAY_RES_ID_FIRST		(0x10)
+
+struct rpmsg_kdrv_display_private {
+	struct rpmsg_kdrv_device *kddev;
+
+	struct rpmsg_remotedev rdev;
+
+	struct idr res_idr;
+	struct mutex res_lock;
+
+};
+
+static uint32_t check_min(uint32_t a, uint32_t b, int line)
+{
+	uint32_t res = min(a, b);
+
+	if (res != b) {
+		pr_err("Copy mismatch at Line %d\n", line);
+		WARN_ON(1);
+	}
+
+	return res;
+}
+
+static inline enum rpmsg_kdrv_display_format rpmsg_kdrv_display_fmt_to_rpmsg_fmt(uint32_t in_fmt)
+{
+	switch (in_fmt) {
+	case DRM_FORMAT_ARGB8888:
+		return RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888;
+	case DRM_FORMAT_XRGB8888:
+		return RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888;
+	default:
+		return RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX;
+	}
+}
+
+static inline uint32_t rpmsg_kdrv_display_fmt_to_drm_fmt(uint32_t in_fmt)
+{
+	switch (in_fmt) {
+	case RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888:
+		return DRM_FORMAT_ARGB8888;
+	case RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888:
+		return DRM_FORMAT_XRGB8888;
+	default:
+		return 0;
+	}
+}
+
+static bool rpmsg_kdrv_display_ready(struct rpmsg_remotedev *rdev)
+{
+	struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_display_ready_query_request *req;
+	struct rpmsg_kdrv_display_ready_query_response *resp;
+	int ret;
+	bool retval;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return false;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return false;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_DISPLAY_READY_QUERY_REQUEST;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), resp, sizeof(*resp));
+	if (ret) {
+		dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__);
+		retval = false;
+		goto out;
+	}
+
+	if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_READY_QUERY_RESPONSE) {
+		dev_err(&kddev->dev, "%s: wrong response type\n", __func__);
+		retval = false;
+		goto out;
+	}
+
+	retval = resp->ready ? true : false;
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return retval;
+
+}
+
+static void rpmsg_kdrv_display_copy_vid_info(struct rpmsg_remotedev_display_pipe *dst, struct rpmsg_kdrv_display_vid_info *src)
+{
+	int cnt;
+	uint32_t out_fmt;
+
+	dst->pipe_id = src->id;
+	dst->can_scale = src->can_scale ? true : false;
+	dst->can_mod_win = src->mutable_window ? true : false;
+	if (dst->can_mod_win)
+		dst->fixed_win_x = dst->fixed_win_y = dst->fixed_win_w = dst->fixed_win_h = 0;
+	else {
+		dst->fixed_win_x = src->fixed_window_x;
+		dst->fixed_win_y = src->fixed_window_y;
+		dst->fixed_win_w = src->fixed_window_w;
+		dst->fixed_win_h = src->fixed_window_h;
+	}
+	dst->initial_zorder = src->init_zorder;
+	dst->num_formats = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS, src->num_formats, __LINE__);
+
+	dst->num_allowed_zorders = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS, src->num_zorders, __LINE__);
+
+	for (cnt = 0; cnt < dst->num_formats; cnt++) {
+		out_fmt = rpmsg_kdrv_display_fmt_to_drm_fmt(src->format[cnt]);
+		WARN_ON(out_fmt == 0);
+		dst->formats[cnt] = out_fmt;
+	}
+
+	for (cnt = 0; cnt < dst->num_allowed_zorders; cnt++)
+		dst->allowed_zorders[cnt] = src->zorder[cnt];
+}
+
+static void rpmsg_kdrv_display_copy_vp_info(struct rpmsg_remotedev_display_disp *dst, struct rpmsg_kdrv_display_vp_info *src)
+{
+	int vidcnt;
+
+	dst->disp_id = src->id;
+	dst->width = src->width;
+	dst->height = src->height;
+	dst->refresh = src->refresh;
+	dst->num_pipes = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES, src->num_vids, __LINE__);
+
+	for (vidcnt = 0; vidcnt < dst->num_pipes; vidcnt++)
+		rpmsg_kdrv_display_copy_vid_info(&dst->pipes[vidcnt], &src->vid[vidcnt]);
+}
+
+static int rpmsg_kdrv_display_get_res(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_resinfo *res)
+{
+	struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_display_res_info_request *req;
+	struct rpmsg_kdrv_display_res_info_response *resp;
+	int ret, vpcnt;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_DISPLAY_RES_INFO_REQUEST;
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req), resp, sizeof(*resp));
+	if (ret) {
+		dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__);
+		goto out;
+	}
+
+	if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_RES_INFO_RESPONSE) {
+		dev_err(&kddev->dev, "%s: wrong response type\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	res->num_disps = check_min(RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS, resp->num_vps, __LINE__);
+
+	for (vpcnt = 0; vpcnt < res->num_disps; vpcnt++)
+		rpmsg_kdrv_display_copy_vp_info(&res->disps[vpcnt], &resp->vp[vpcnt]);
+
+out:
+	devm_kfree(&kddev->dev, resp);
+	devm_kfree(&kddev->dev, req);
+	return ret;
+}
+
+static uint32_t rpmsg_kdrv_display_res_id_new(struct rpmsg_kdrv_device *kddev, void *data)
+{
+	struct rpmsg_kdrv_display_private *priv = kddev->driver_private;
+	int id;
+
+	mutex_lock(&priv->res_lock);
+	id = idr_alloc(&priv->res_idr, data, RPMSG_KDRV_DISPLAY_RES_ID_FIRST, 0, GFP_KERNEL);
+	mutex_unlock(&priv->res_lock);
+
+	if (id < 0)
+		return 0;
+
+	return id;
+}
+
+static void rpmsg_kdrv_display_free_res_id(struct rpmsg_kdrv_device *kddev, uint32_t id)
+{
+	struct rpmsg_kdrv_display_private *priv = kddev->driver_private;
+
+	mutex_lock(&priv->res_lock);
+	idr_remove(&priv->res_idr, id);
+	mutex_unlock(&priv->res_lock);
+}
+
+static void rpmsg_kdrv_free_request_res(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_commit_request *req)
+{
+	int i;
+
+	rpmsg_kdrv_display_free_res_id(kddev, req->commit_id);
+
+	for (i = 0; i < req->num_vid_updates; i++)
+		if (req->vid[i].enabled)
+			rpmsg_kdrv_display_free_res_id(kddev, req->vid[i].buffer.buffer_id);
+
+}
+
+static bool rpmsg_kdrv_display_copy_buffer(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_buffer_info *dst,
+		struct rpmsg_remotedev_display_buffer *src)
+{
+	int i;
+
+	dst->width = src->width;
+	dst->height = src->height;
+
+	dst->format = rpmsg_kdrv_display_fmt_to_rpmsg_fmt(src->format);
+	if (WARN_ON(dst->format == RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX))
+		return false;
+
+	dst->num_planes = check_min(RPMSG_KDRV_TP_DISPLAY_MAX_PLANES, src->num_planes, __LINE__);
+	if (dst->num_planes != src->num_planes)
+		return false;
+
+	for (i = 0; i < dst->num_planes; i++) {
+		dst->plane[i] = (uint64_t)src->planes[i];
+		dst->pitch[i] = src->pitches[i];
+	}
+
+	dst->buffer_id = rpmsg_kdrv_display_res_id_new(kddev, src);
+	if (!dst->buffer_id)
+		return false;
+
+	return true;
+}
+
+static bool rpmsg_kdrv_display_copy_vid_commit(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_vid_update_info *dst,
+		struct rpmsg_remotedev_display_pipe_update *src)
+{
+	dst->id = src->pipe_id;
+	dst->enabled = src->enabled ? 1 : 0;
+	if (dst->enabled) {
+		dst->dst_w = src->dst_w;
+		dst->dst_h = src->dst_h;
+		dst->dst_x = src->dst_x;
+		dst->dst_y = src->dst_y;
+
+		if (!rpmsg_kdrv_display_copy_buffer(kddev, &dst->buffer, src->buffer))
+			return false;
+	}
+
+	return true;
+}
+
+static bool rpmsg_kdrv_display_copy_commit(struct rpmsg_kdrv_device *kddev, struct rpmsg_kdrv_display_commit_request *dst,
+		struct rpmsg_remotedev_display_commit *src)
+{
+	int i, copied_vids;
+
+	dst->id = src->disp_id;
+	dst->num_vid_updates = check_min(RPMSG_KDRV_TP_DISPLAY_MAX_VIDS, src->num_pipe_updates, __LINE__);
+
+	for (i = 0, copied_vids = 0; i < dst->num_vid_updates; i++, copied_vids++)
+		if (!rpmsg_kdrv_display_copy_vid_commit(kddev, &dst->vid[i], &src->pipes[i]))
+			goto free_vid_res;
+
+	dst->commit_id = rpmsg_kdrv_display_res_id_new(kddev, src);
+	if (!dst->commit_id)
+		goto free_vid_res;
+
+	return true;
+
+free_vid_res:
+	for (i = 0; i < copied_vids; i++)
+		if (dst->vid[i].enabled)
+			rpmsg_kdrv_display_free_res_id(kddev, dst->vid[i].buffer.buffer_id);
+	return false;
+
+}
+
+static int rpmsg_kdrv_display_commit(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_commit *commit)
+{
+	struct rpmsg_kdrv_display_private *priv = container_of(rdev, struct rpmsg_kdrv_display_private, rdev);
+	struct rpmsg_kdrv_device *kddev = priv->kddev;
+	struct rpmsg_device *rpdev = kddev->rpdev;
+	struct rpmsg_kdrv_display_commit_request *req;
+	struct rpmsg_kdrv_display_commit_response *resp;
+	int ret;
+
+	req = devm_kzalloc(&kddev->dev, sizeof(*req), GFP_KERNEL);
+	if (!req)
+		return -ENOMEM;
+
+	resp = devm_kzalloc(&kddev->dev, sizeof(*resp), GFP_KERNEL);
+	if (!resp) {
+		devm_kfree(&kddev->dev, req);
+		return -ENOMEM;
+	}
+
+	req->header.message_type = RPMSG_KDRV_TP_DISPLAY_COMMIT_REQUEST;
+
+	if (!rpmsg_kdrv_display_copy_commit(kddev, req, commit)) {
+		dev_err(&kddev->dev, "%s: failed to copy commit request\n", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = rpmsg_kdrv_send_request_with_response(rpdev, kddev->device_id, req, sizeof(*req),
+			resp, sizeof(*resp));
+	if (ret) {
+		dev_err(&kddev->dev, "%s: rpmsg_kdrv_send_request_with_response\n", __func__);
+		goto nosend;
+	}
+
+
+	if (resp->header.message_type != RPMSG_KDRV_TP_DISPLAY_COMMIT_RESPONSE) {
+		dev_err(&kddev->dev, "%s: wrong response type\n", __func__);
+		goto out;
+	}
+
+	ret = ((resp->status == 0) ? 0 : -EINVAL);
+	goto out;
+
+nosend:
+	rpmsg_kdrv_free_request_res(kddev, req);
+out:
+	devm_kfree(&kddev->dev, req);
+	devm_kfree(&kddev->dev, resp);
+	return ret;
+}
+
+
+struct rpmsg_remotedev_display_ops disp_ops = {
+	.ready = rpmsg_kdrv_display_ready,
+	.get_res_info = rpmsg_kdrv_display_get_res,
+	.commit = rpmsg_kdrv_display_commit,
+};
+
+static void rpmsg_kdrv_display_device_init(struct rpmsg_kdrv_device *kddev, void *data, int len)
+{
+}
+
+static int rpmsg_kdrv_display_probe(struct rpmsg_kdrv_device *dev)
+{
+	struct rpmsg_kdrv_display_private *priv;
+
+	dev_dbg(&dev->dev, "%s\n", __func__);
+
+	priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->rdev.type = RPMSG_REMOTEDEV_DISPLAY_DEVICE;
+	priv->rdev.device.display.ops = &disp_ops;
+
+	mutex_init(&priv->res_lock);
+	idr_init(&priv->res_idr);
+
+	priv->kddev = dev;
+	dev->driver_private = priv;
+	dev->remotedev = &priv->rdev;
+
+	rpmsg_kdrv_display_device_init(dev, dev->device_data, dev->device_data_len);
+
+	return 0;
+}
+
+static void rpmsg_kdrv_display_remove(struct rpmsg_kdrv_device *dev)
+{
+	dev_dbg(&dev->dev, "%s\n", __func__);
+}
+
+static void rpmsg_kdrv_display_handle_commit(struct rpmsg_kdrv_device *dev, struct rpmsg_kdrv_display_commit_done_message *msg)
+{
+	struct rpmsg_kdrv_display_private *priv = dev->driver_private;
+	struct rpmsg_remotedev *rdev = &priv->rdev;
+	struct rpmsg_remotedev_display_commit *commit;
+
+	mutex_lock(&priv->res_lock);
+	commit = idr_find(&priv->res_idr, msg->commit_id);
+	idr_remove(&priv->res_idr, msg->commit_id);
+	mutex_unlock(&priv->res_lock);
+
+	if (!commit) {
+		dev_err(&dev->dev, "%s: no pending commit found\n", __func__);
+		return;
+	}
+
+	if (rdev->device.display.cb_ops && rdev->device.display.cb_ops->commit_done)
+		rdev->device.display.cb_ops->commit_done(commit, rdev->cb_data);
+}
+
+static void rpmsg_kdrv_display_handle_buffer(struct rpmsg_kdrv_device *dev, struct rpmsg_kdrv_display_buffer_done_message *msg)
+{
+	struct rpmsg_kdrv_display_private *priv = dev->driver_private;
+	struct rpmsg_remotedev *rdev = &priv->rdev;
+	struct rpmsg_remotedev_display_buffer *buffer;
+
+	mutex_lock(&priv->res_lock);
+	buffer = idr_find(&priv->res_idr, msg->buffer_id);
+	idr_remove(&priv->res_idr, msg->buffer_id);
+	mutex_unlock(&priv->res_lock);
+
+	if (!buffer) {
+		dev_err(&dev->dev, "%s: no pending buffer found\n", __func__);
+		return;
+	}
+
+	if (rdev->device.display.cb_ops && rdev->device.display.cb_ops->buffer_done)
+		rdev->device.display.cb_ops->buffer_done(buffer, rdev->cb_data);
+}
+
+static int rpmsg_kdrv_display_callback(struct rpmsg_kdrv_device *dev, void *msg, int len)
+{
+	struct rpmsg_kdrv_display_message_header *hdr = msg;
+
+	if (hdr->message_type == RPMSG_KDRV_TP_DISPLAY_COMMIT_DONE_MESSAGE)
+		rpmsg_kdrv_display_handle_commit(dev, msg);
+	else if (hdr->message_type == RPMSG_KDRV_TP_DISPLAY_BUFFER_DONE_MESSAGE)
+		rpmsg_kdrv_display_handle_buffer(dev, msg);
+
+	return 0;
+}
+
+
+struct rpmsg_kdrv_driver rpmsg_kdrv_display = {
+	.drv.name = "rpmsg-kdrv-display",
+	.device_type = RPMSG_KDRV_TP_DEVICE_TYPE_DISPLAY,
+	.probe = rpmsg_kdrv_display_probe,
+	.remove = rpmsg_kdrv_display_remove,
+	.callback = rpmsg_kdrv_display_callback,
+};
+
+static int __init rpmsg_kdrv_display_driver_init(void)
+{
+	return rpmsg_kdrv_register_driver(&rpmsg_kdrv_display);
+}
+module_init(rpmsg_kdrv_display_driver_init);
+
+static void rpmsg_kdrv_display_driver_fini(void)
+{
+}
+module_exit(rpmsg_kdrv_display_driver_fini);
+
+MODULE_AUTHOR("Subhajit Paul <subhajit_paul@ti.com>");
+MODULE_DESCRIPTION("TI Remote-device Virtual Display Driver");
+MODULE_LICENSE("GPL v2");

+ 48 - 0
drivers/rpmsg-kdrv/rpmsg_kdrv_internal.h

@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_KDRV_INTERNAL_H__
+#define __RPMSG_KDRV_INTERNAL_H__
+
+#define RRMSG_KDRV_CALLBACK_DONE		(0)
+#define RRMSG_KDRV_CALLBACK_MORE		(1)
+
+struct rpmsg_kdrv_device {
+	struct device dev;
+	struct rpmsg_device *rpdev;
+	int device_type;
+	int device_id;
+	void *device_data;
+	int device_data_len;
+	char *device_name;
+	void *device_private;
+	void *driver_private;
+	struct rpmsg_remotedev *remotedev;
+};
+
+struct rpmsg_kdrv_driver {
+	struct device_driver drv;
+	int device_type;
+	int (*probe)(struct rpmsg_kdrv_device *dev);
+	void (*remove)(struct rpmsg_kdrv_device *dev);
+	int (*callback)(struct rpmsg_kdrv_device *dev, void *msg, int len);
+};
+
+typedef int (*request_cb_t)(void *data, void *req, int req_sz, void *resp, int resp_sz);
+
+extern int rpmsg_kdrv_register_driver(struct rpmsg_kdrv_driver *drv);
+
+extern int rpmsg_kdrv_send_request_with_callback(struct rpmsg_device *rpdev,
+		uint32_t device_id, void *message, uint32_t message_size, void *cb_data,
+		request_cb_t callback);
+extern int rpmsg_kdrv_send_request_with_response(struct rpmsg_device *rpdev,
+		uint32_t device_id, void *message, uint32_t message_size,
+		void *response, uint32_t response_size);
+extern int rpmsg_kdrv_send_message(struct rpmsg_device *rpdev,
+		uint32_t device_id, void *message, uint32_t message_size);
+
+
+#endif

+ 78 - 0
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-common.h

@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_KDRV_TRANSPORT_COMMON_H__
+#define __RPMSG_KDRV_TRANSPORT_COMMON_H__
+
+/*
+ * Device types supported by RPMSG-KDRV framework
+ * Currently supported device types: display
+ * Planned future support for capture and i2c devices
+ */
+#define RPMSG_KDRV_TP_DEVICE_TYPE_INIT		(0x0)
+#define RPMSG_KDRV_TP_DEVICE_TYPE_DISPLAY	(0x1)
+#define RPMSG_KDRV_TP_DEVICE_TYPE_DEMO		(0x2)
+/* More device types here*/
+#define RPMSG_KDRV_TP_DEVICE_TYPE_MAX		(0x3)
+
+/*
+ * Maximum number of proxy devices per remotecore
+ */
+#define RPMSG_KDRV_TP_MAX_DEVICES		(2)
+
+/*
+ * Maximum length of proxy device name
+ */
+#define RPMSG_KDRV_TP_DEVICE_NAME_LEN		(32)
+
+/*
+ * Statically assigned device ID for init device
+ * Remote device framework dynamically assigns device
+ * IDs for other devices. All dynamically assigned IDs
+ * are greater than RPMSG_KDRV_TP_DEVICE_ID_INIT
+ */
+#define RPMSG_KDRV_TP_DEVICE_ID_INIT		(0)
+
+/*
+ * Packet IDs are assigned dynamically (for REQUEST packets)
+ * starting from RPMSG_KDRV_TP_PACKET_ID_FIRST
+ * For MESSAGE packets, framework can use RPMSG_KDRV_TP_PACKET_ID_NONE
+ */
+#define RPMSG_KDRV_TP_PACKET_ID_NONE		(0x10)
+#define RPMSG_KDRV_TP_PACKET_ID_FIRST		(RPMSG_KDRV_TP_PACKET_ID_NONE + 1)
+
+enum rpmsg_kdrv_packet_source {
+	RPMSG_KDRV_TP_PACKET_SOURCE_SERVER,
+	RPMSG_KDRV_TP_PACKET_SOURCE_CLIENT,
+	RPMSG_KDRV_TP_PACKET_SOURCE_MAX,
+};
+
+enum rpmsg_kdrv_packet_type {
+	RPMSG_KDRV_TP_PACKET_TYPE_REQUEST,
+	RPMSG_KDRV_TP_PACKET_TYPE_RESPONSE,
+	RPMSG_KDRV_TP_PACKET_TYPE_MESSAGE,
+	RPMSG_KDRV_TP_PACKET_TYPE_MAX,
+};
+
+/*RPMSG_KDRV message :
+ * => device_header
+ * => message_header : defined by each device type
+ * => request / response / message payload
+ */
+struct rpmsg_kdrv_device_header {
+	/* ID of device sending the packet */
+	u8 device_id;
+	/* enum: rpmsg_kdrv_packet_type */
+	u8 packet_type;
+	/* enum: rpmsg_kdrv_packet_source */
+	u8 packet_source;
+	/* dynamically assigned packet ID for response matching */
+	u32 packet_id;
+	/* size of packet */
+	u32 packet_size;
+} __packed;
+
+#endif

+ 78 - 0
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-demo.h

@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_KDRV_TRANSPORT_DEMODEV_H__
+#define __RPMSG_KDRV_TRANSPORT_DEMODEV_H__
+
+#include "rpmsg-kdrv-transport-common.h"
+
+enum rpmsg_kdrv_display_message_type {
+	RPMSG_KDRV_TP_DEMODEV_PING_REQUEST,
+	RPMSG_KDRV_TP_DEMODEV_PING_RESPONSE,
+	RPMSG_KDRV_TP_DEMODEV_S2C_MESSAGE,
+	RPMSG_KDRV_TP_DEMODEV_C2S_MESSAGE,
+	RPMSG_KDRV_TP_DEMODEV_MAX,
+};
+
+/*
+ * Maximum length of demo device data
+ */
+#define RPMSG_KDRV_TP_DEMODEV_DEVICE_DATA_LEN	(32)
+
+/*
+ * Maximum length of demo device message data
+ */
+#define RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN	(128)
+
+/*
+ * per-device data for demo device
+ */
+struct rpmsg_kdrv_demodev_device_data {
+	/* Does the device send all vsyncs? */
+	u8 charString[RPMSG_KDRV_TP_DEMODEV_DEVICE_DATA_LEN];
+} __packed;
+
+/*
+ * message header for demo device
+ */
+struct rpmsg_kdrv_demodev_message_header {
+	/* enum: rpmsg_kdrv_demodev_message_type */
+	u8 message_type;
+} __packed;
+
+/* demo device ping request - always client to server */
+struct rpmsg_kdrv_demodev_ping_request {
+	/* message header */
+	struct rpmsg_kdrv_demodev_message_header header;
+	/* ping data */
+	u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN];
+} __packed;
+
+/* demo device ping response - always server to client */
+struct rpmsg_kdrv_demodev_ping_response {
+	/* message header */
+	struct rpmsg_kdrv_demodev_message_header header;
+	/* ping data */
+	u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN];
+} __packed;
+
+/* demo device server to client one-way message */
+struct rpmsg_kdrv_demodev_s2c_message {
+	/* message header */
+	struct rpmsg_kdrv_demodev_message_header header;
+	/* message data */
+	u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN];
+} __packed;
+
+/* demo device client to server one-way message */
+struct rpmsg_kdrv_demodev_c2s_message {
+	/* message header */
+	struct rpmsg_kdrv_demodev_message_header header;
+	/* message data */
+	u8 data[RPMSG_KDRV_TP_DEMODEV_MESSAGE_DATA_LEN];
+} __packed;
+
+#endif

+ 223 - 0
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport-display.h

@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_KDRV_TRANSPORT_DISPLAY_H__
+#define __RPMSG_KDRV_TRANSPORT_DISPLAY_H__
+
+#include "rpmsg-kdrv-transport-common.h"
+
+/*
+ * Maximum number of planes per buffer
+ */
+#define RPMSG_KDRV_TP_DISPLAY_MAX_PLANES	(2)
+
+/*
+ * Maximum number of shared displays
+ */
+#define RPMSG_KDRV_TP_DISPLAY_MAX_VPS		(2)
+
+/*
+ * Maximum number of pipes per shared display
+ */
+#define RPMSG_KDRV_TP_DISPLAY_MAX_VIDS		(4)
+
+/*
+ * Maximum number of formats supported per pipe
+ */
+#define RPMSG_KDRV_TP_DISPLAY_MAX_FORMATS	(2)
+
+/*
+ * Maximum number of zorders supported per pipe
+ */
+#define RPMSG_KDRV_TP_DISPLAY_MAX_ZORDERS	(4)
+
+enum rpmsg_kdrv_display_format {
+	RPMSG_KDRV_TP_DISPLAY_FORMAT_ARGB8888,
+	RPMSG_KDRV_TP_DISPLAY_FORMAT_XRGB8888,
+	RPMSG_KDRV_TP_DISPLAY_FORMAT_MAX,
+};
+
+enum rpmsg_kdrv_display_message_type {
+	RPMSG_KDRV_TP_DISPLAY_READY_QUERY_REQUEST,
+	RPMSG_KDRV_TP_DISPLAY_READY_QUERY_RESPONSE,
+	RPMSG_KDRV_TP_DISPLAY_RES_INFO_REQUEST,
+	RPMSG_KDRV_TP_DISPLAY_RES_INFO_RESPONSE,
+	RPMSG_KDRV_TP_DISPLAY_COMMIT_REQUEST,
+	RPMSG_KDRV_TP_DISPLAY_COMMIT_RESPONSE,
+	RPMSG_KDRV_TP_DISPLAY_COMMIT_DONE_MESSAGE,
+	RPMSG_KDRV_TP_DISPLAY_BUFFER_DONE_MESSAGE,
+	RPMSG_KDRV_TP_DISPLAY_MAX,
+};
+
+/*
+ * per-device data for display device
+ */
+struct rpmsg_kdrv_display_device_data {
+	/* Does the device send all vsyncs? */
+	u8 periodic_vsync;
+	/*Does the device defer the use of buffers? */
+	u8 deferred_buffer_usage;
+} __packed;
+
+/*
+ * message header for display device
+ */
+struct rpmsg_kdrv_display_message_header {
+	/* enum: rpmsg_kdrv_display_message_type */
+	u8 message_type;
+} __packed;
+
+/* display device request to provide ready / not-ready info */
+struct rpmsg_kdrv_display_ready_query_request {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+} __packed;
+
+/* display device response indicating ready / not-ready status */
+struct rpmsg_kdrv_display_ready_query_response {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/* can be 0 : if not ready 1: if ready */
+	u8 ready;
+} __packed;
+
+/* display device buffer update info */
+struct rpmsg_kdrv_display_buffer_info {
+	/* buffer width */
+	u16 width;
+	/* buffer height */
+	u16 height;
+	/* enum: rpmsg_kdrv_display_format */
+	u8 format;
+	/* number of planes */
+	u8 num_planes;
+	/* per plane start addresses */
+	u64 plane[RPMSG_KDRV_TP_DISPLAY_MAX_PLANES];
+	/* per plane pitch */
+	u16 pitch[RPMSG_KDRV_TP_DISPLAY_MAX_PLANES];
+	/* buffer id : to be used in buffer-done message */
+	u32 buffer_id;
+} __packed;
+
+/* display device pipe update info */
+struct rpmsg_kdrv_display_vid_update_info {
+	/* pipe ID */
+	u8 id;
+	/*enable / disable request */
+	u8 enabled;
+	/* window width */
+	u16 dst_w;
+	/* window height */
+	u16 dst_h;
+	/* window position X */
+	u16 dst_x;
+	/* window position Y */
+	u16 dst_y;
+	/* buffer */
+	struct rpmsg_kdrv_display_buffer_info buffer;
+} __packed;
+
+/* display device commit request */
+struct rpmsg_kdrv_display_commit_request {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/*ID of shared display */
+	u8 id;
+	/* number of pipe updates in the commit */
+	u8 num_vid_updates;
+	/* list of pipe updates */
+	struct rpmsg_kdrv_display_vid_update_info vid[RPMSG_KDRV_TP_DISPLAY_MAX_VIDS];
+	/*commit id : to be used in commit-done message */
+	u32 commit_id;
+} __packed;
+
+/* display device commit response */
+struct rpmsg_kdrv_display_commit_response {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/*commit id : from commit request */
+	u32 commit_id;
+	/*status : 0 = accepted, 1 = rejected */
+	u8 status;
+} __packed;
+
+/* display device commit done message */
+struct rpmsg_kdrv_display_commit_done_message {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/* commit id : from commit request */
+	u32 commit_id;
+} __packed;
+
+/*display device buffer deferred release message */
+struct rpmsg_kdrv_display_buffer_done_message {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/* buffer id: from bufer_info */
+	u32 buffer_id;
+} __packed;
+
+/* display device request to provide list of shared resources */
+struct rpmsg_kdrv_display_res_info_request {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+} __packed;
+
+/* display device shared pipe */
+struct rpmsg_kdrv_display_vid_info {
+	/* pipe ID */
+	u8 id;
+	/* is pipe window fixed on display? */
+	u8 mutable_window;
+	/* fixed window position X, if applicable */
+	u16 fixed_window_x;
+	/* fixed window position Y, if applicable */
+	u16 fixed_window_y;
+	/* fixed window width, if applicable */
+	u16 fixed_window_w;
+	/* fixed window height, if applicable */
+	u16 fixed_window_h;
+	/* can pipe scale buffers? */
+	u8 can_scale;
+	/* number of formats supported */
+	u8 num_formats;
+	/*enum: rpmsg_kdrv_display_format */
+	u8 format[RPMSG_KDRV_TP_DISPLAY_MAX_FORMATS];
+	/* initial zorder of pipe */
+	u8 init_zorder;
+	/* number of allowed zorders */
+	u8 num_zorders;
+	/* list of allowed zorders */
+	u8 zorder[RPMSG_KDRV_TP_DISPLAY_MAX_ZORDERS];
+} __packed;
+
+/* display device shared display */
+struct rpmsg_kdrv_display_vp_info {
+	/* ID of shared display */
+	u8 id;
+	/* raster width */
+	u16 width;
+	/* raster height */
+	u16 height;
+	/* refresh rate */
+	u8 refresh;
+	/* number of pipes for this display */
+	u8 num_vids;
+	/* list of pipes */
+	struct rpmsg_kdrv_display_vid_info vid[RPMSG_KDRV_TP_DISPLAY_MAX_VIDS];
+} __packed;
+
+/* display device response providing list of shared resources */
+struct rpmsg_kdrv_display_res_info_response {
+	/* message header */
+	struct rpmsg_kdrv_display_message_header header;
+	/* number of shared displays */
+	u8 num_vps;
+	/* list of shared displays */
+	struct rpmsg_kdrv_display_vp_info vp[RPMSG_KDRV_TP_DISPLAY_MAX_VPS];
+} __packed;
+
+#endif

+ 83 - 0
drivers/rpmsg-kdrv/shared/rpmsg-kdrv-transport.h

@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_KDRV_TRANSPORT_H__
+#define __RPMSG_KDRV_TRANSPORT_H__
+
+#include "rpmsg-kdrv-transport-common.h"
+
+enum rpmsg_kdrv_init_message_type {
+	RPMSG_KDRV_TP_INIT_DEV_INFO_REQUEST,
+	RPMSG_KDRV_TP_INIT_DEV_INFO_RESPONSE,
+	RPMSG_KDRV_TP_INIT_CONNECT_MESSAGE,
+	RPMSG_KDRV_TP_INIT_DISCONNECT_MESSAGE,
+	RPMSG_KDRV_TP_INIT_MAX,
+};
+
+/*
+ * message header for init device
+ */
+struct rpmsg_kdrv_init_message_header {
+	/* enum: rpmsg_kdrv_init_message_type */
+	u8 message_type;
+} __packed;
+
+/*
+ * init device request to provide list of devices
+ */
+struct rpmsg_kdrv_init_dev_info_request {
+	/* message header */
+	struct rpmsg_kdrv_init_message_header header;
+} __packed;
+
+struct rpmsg_kdrv_init_device_info {
+	/* device id */
+	u8 device_id;
+	/* device type (display, capture etc) */
+	u8 device_type;
+	/* name of device */
+	u8 device_name[RPMSG_KDRV_TP_DEVICE_NAME_LEN];
+	/* device specific info length */
+	u16 device_data_len;
+	/* per device-type info offset */
+	u16 device_data_offset;
+} __packed;
+
+/*
+ * init device response with list of devices
+ */
+struct rpmsg_kdrv_init_dev_info_response {
+	/* message header */
+	struct rpmsg_kdrv_init_message_header header;
+	/*number of exported devices */
+	u8 num_devices;
+	/* list of exported devices */
+	struct rpmsg_kdrv_init_device_info devices[RPMSG_KDRV_TP_MAX_DEVICES];
+	/* device specific data */
+	u8 device_data[0];
+} __packed;
+
+/*
+ * init device per-device connect message
+ */
+struct rpmsg_kdrv_init_connect_message {
+	/* message header */
+	struct rpmsg_kdrv_init_message_header header;
+	/* device ID to connect */
+	u8 device_id;
+} __packed;
+
+/*
+ * init device per-device disconnect message
+ */
+struct rpmsg_kdrv_init_disconnect_message {
+	/* message header */
+	struct rpmsg_kdrv_init_message_header header;
+	/* device ID to disconnect */
+	u8 device_id;
+} __packed;
+
+#endif

+ 117 - 0
include/linux/rpmsg-remotedev/rpmsg-remotedev.h

@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Subhajit Paul <subhajit_paul@ti.com>
+ */
+
+#ifndef __RPMSG_REMOTEDEV_H__
+#define __RPMSG_REMOTEDEV_H__
+
+#define RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES		(3)
+#define RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS		(8)
+#define RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES		(8)
+#define RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS		(32)
+#define RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS		(8)
+
+struct rpmsg_remotedev;
+
+struct rpmsg_remotedev_display_buffer {
+	uint32_t width;
+	uint32_t height;
+	uint32_t format;
+	uint32_t num_planes;
+	dma_addr_t planes[RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES];
+	uint32_t pitches[RPMSG_REMOTEDEV_DISPLAY_MAX_PLANES];
+	struct rpmsg_remotedev *rdev;
+	void *priv;
+};
+
+struct rpmsg_remotedev_display_pipe_update {
+	uint32_t pipe_id;
+	bool enabled;
+	uint32_t dst_w;
+	uint32_t dst_h;
+	uint32_t dst_x;
+	uint32_t dst_y;
+	struct rpmsg_remotedev_display_buffer *buffer;
+};
+
+struct rpmsg_remotedev_display_commit {
+	uint32_t disp_id;
+	uint32_t num_pipe_updates;
+	struct rpmsg_remotedev_display_pipe_update pipes[RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES];
+	struct rpmsg_remotedev *rdev;
+	void *priv;
+};
+
+struct rpmsg_remotedev_display_pipe {
+	uint32_t pipe_id;
+	bool can_scale;
+	bool can_mod_win;
+	uint32_t fixed_win_x;
+	uint32_t fixed_win_y;
+	uint32_t fixed_win_w;
+	uint32_t fixed_win_h;
+	uint32_t initial_zorder;
+	uint32_t num_formats;
+	uint32_t formats[RPMSG_REMOTEDEV_DISPLAY_MAX_FORMATS];
+	uint32_t num_allowed_zorders;
+	uint32_t allowed_zorders[RPMSG_REMOTEDEV_DISPLAY_MAX_ZORDERS];
+};
+
+struct rpmsg_remotedev_display_disp {
+	uint32_t disp_id;
+	uint32_t width;
+	uint32_t height;
+	uint32_t refresh;
+	uint32_t num_pipes;
+	struct rpmsg_remotedev_display_pipe pipes[RPMSG_REMOTEDEV_DISPLAY_MAX_PIPES];
+};
+
+struct rpmsg_remotedev_display_resinfo {
+	uint32_t num_disps;
+	struct rpmsg_remotedev_display_disp disps[RPMSG_REMOTEDEV_DISPLAY_MAX_DISPS];
+};
+
+struct rpmsg_remotedev_display_cb {
+	void (*commit_done)(struct rpmsg_remotedev_display_commit *commit, void *cb_data);
+	void (*buffer_done)(struct rpmsg_remotedev_display_buffer *buffer, void *cb_data);
+};
+
+struct rpmsg_remotedev_display_ops {
+	bool (*ready)(struct rpmsg_remotedev *rdev);
+	int (*get_res_info)(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_resinfo *res);
+	int (*commit)(struct rpmsg_remotedev *rdev, struct rpmsg_remotedev_display_commit *commit);
+};
+
+enum rpmsg_remotedev_type {
+	RPMSG_REMOTEDEV_DISPLAY_DEVICE,
+};
+
+struct rpmsg_remotedev {
+	enum rpmsg_remotedev_type type;
+	union {
+		struct {
+			struct rpmsg_remotedev_display_ops *ops;
+			struct rpmsg_remotedev_display_cb *cb_ops;
+		} display;
+	} device;
+	void *cb_data;
+
+};
+
+#if IS_REACHABLE(CONFIG_RPMSG_KDRV)
+extern struct rpmsg_remotedev *rpmsg_remotedev_get_named_device(const char *device_name);
+extern void rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev);
+#else
+static inline struct rpmsg_remotedev * __maybe_unused rpmsg_remotedev_get_named_device(const char *device_name)
+{
+	return NULL;
+}
+
+static inline void __maybe_unused rpmsg_remotedev_put_device(struct rpmsg_remotedev *rdev)
+{
+}
+#endif
+
+#endif