// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ * Author: Subhajit Paul */ #include #include #include #include #include #include #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; } static 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; } static 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 "); MODULE_DESCRIPTION("TI Remote-device Virtual Display Driver"); MODULE_LICENSE("GPL v2");