367 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR MIT2 3/*4 * Xen para-virtual DRM device5 *6 * Copyright (C) 2016-2018 EPAM Systems Inc.7 *8 * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>9 */10 11#include <linux/errno.h>12#include <linux/irq.h>13 14#include <drm/drm_print.h>15 16#include <xen/xenbus.h>17#include <xen/events.h>18#include <xen/grant_table.h>19 20#include "xen_drm_front.h"21#include "xen_drm_front_evtchnl.h"22 23static irqreturn_t evtchnl_interrupt_ctrl(int irq, void *dev_id)24{25 struct xen_drm_front_evtchnl *evtchnl = dev_id;26 struct xen_drm_front_info *front_info = evtchnl->front_info;27 struct xendispl_resp *resp;28 RING_IDX i, rp;29 unsigned long flags;30 31 if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))32 return IRQ_HANDLED;33 34 spin_lock_irqsave(&front_info->io_lock, flags);35 36again:37 rp = evtchnl->u.req.ring.sring->rsp_prod;38 /* ensure we see queued responses up to rp */39 virt_rmb();40 41 for (i = evtchnl->u.req.ring.rsp_cons; i != rp; i++) {42 resp = RING_GET_RESPONSE(&evtchnl->u.req.ring, i);43 if (unlikely(resp->id != evtchnl->evt_id))44 continue;45 46 switch (resp->operation) {47 case XENDISPL_OP_PG_FLIP:48 case XENDISPL_OP_FB_ATTACH:49 case XENDISPL_OP_FB_DETACH:50 case XENDISPL_OP_DBUF_CREATE:51 case XENDISPL_OP_DBUF_DESTROY:52 case XENDISPL_OP_SET_CONFIG:53 evtchnl->u.req.resp_status = resp->status;54 complete(&evtchnl->u.req.completion);55 break;56 57 default:58 DRM_ERROR("Operation %d is not supported\n",59 resp->operation);60 break;61 }62 }63 64 evtchnl->u.req.ring.rsp_cons = i;65 66 if (i != evtchnl->u.req.ring.req_prod_pvt) {67 int more_to_do;68 69 RING_FINAL_CHECK_FOR_RESPONSES(&evtchnl->u.req.ring,70 more_to_do);71 if (more_to_do)72 goto again;73 } else {74 evtchnl->u.req.ring.sring->rsp_event = i + 1;75 }76 77 spin_unlock_irqrestore(&front_info->io_lock, flags);78 return IRQ_HANDLED;79}80 81static irqreturn_t evtchnl_interrupt_evt(int irq, void *dev_id)82{83 struct xen_drm_front_evtchnl *evtchnl = dev_id;84 struct xen_drm_front_info *front_info = evtchnl->front_info;85 struct xendispl_event_page *page = evtchnl->u.evt.page;86 u32 cons, prod;87 unsigned long flags;88 89 if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED))90 return IRQ_HANDLED;91 92 spin_lock_irqsave(&front_info->io_lock, flags);93 94 prod = page->in_prod;95 /* ensure we see ring contents up to prod */96 virt_rmb();97 if (prod == page->in_cons)98 goto out;99 100 for (cons = page->in_cons; cons != prod; cons++) {101 struct xendispl_evt *event;102 103 event = &XENDISPL_IN_RING_REF(page, cons);104 if (unlikely(event->id != evtchnl->evt_id++))105 continue;106 107 switch (event->type) {108 case XENDISPL_EVT_PG_FLIP:109 xen_drm_front_on_frame_done(front_info, evtchnl->index,110 event->op.pg_flip.fb_cookie);111 break;112 }113 }114 page->in_cons = cons;115 /* ensure ring contents */116 virt_wmb();117 118out:119 spin_unlock_irqrestore(&front_info->io_lock, flags);120 return IRQ_HANDLED;121}122 123static void evtchnl_free(struct xen_drm_front_info *front_info,124 struct xen_drm_front_evtchnl *evtchnl)125{126 void *page = NULL;127 128 if (evtchnl->type == EVTCHNL_TYPE_REQ)129 page = evtchnl->u.req.ring.sring;130 else if (evtchnl->type == EVTCHNL_TYPE_EVT)131 page = evtchnl->u.evt.page;132 if (!page)133 return;134 135 evtchnl->state = EVTCHNL_STATE_DISCONNECTED;136 137 if (evtchnl->type == EVTCHNL_TYPE_REQ) {138 /* release all who still waits for response if any */139 evtchnl->u.req.resp_status = -EIO;140 complete_all(&evtchnl->u.req.completion);141 }142 143 if (evtchnl->irq)144 unbind_from_irqhandler(evtchnl->irq, evtchnl);145 146 if (evtchnl->port)147 xenbus_free_evtchn(front_info->xb_dev, evtchnl->port);148 149 /* end access and free the page */150 xenbus_teardown_ring(&page, 1, &evtchnl->gref);151 152 memset(evtchnl, 0, sizeof(*evtchnl));153}154 155static int evtchnl_alloc(struct xen_drm_front_info *front_info, int index,156 struct xen_drm_front_evtchnl *evtchnl,157 enum xen_drm_front_evtchnl_type type)158{159 struct xenbus_device *xb_dev = front_info->xb_dev;160 void *page;161 irq_handler_t handler;162 int ret;163 164 memset(evtchnl, 0, sizeof(*evtchnl));165 evtchnl->type = type;166 evtchnl->index = index;167 evtchnl->front_info = front_info;168 evtchnl->state = EVTCHNL_STATE_DISCONNECTED;169 170 ret = xenbus_setup_ring(xb_dev, GFP_NOIO | __GFP_HIGH, &page,171 1, &evtchnl->gref);172 if (ret)173 goto fail;174 175 if (type == EVTCHNL_TYPE_REQ) {176 struct xen_displif_sring *sring;177 178 init_completion(&evtchnl->u.req.completion);179 mutex_init(&evtchnl->u.req.req_io_lock);180 sring = page;181 XEN_FRONT_RING_INIT(&evtchnl->u.req.ring, sring, XEN_PAGE_SIZE);182 183 handler = evtchnl_interrupt_ctrl;184 } else {185 evtchnl->u.evt.page = page;186 handler = evtchnl_interrupt_evt;187 }188 189 ret = xenbus_alloc_evtchn(xb_dev, &evtchnl->port);190 if (ret < 0)191 goto fail;192 193 ret = bind_evtchn_to_irqhandler(evtchnl->port,194 handler, 0, xb_dev->devicetype,195 evtchnl);196 if (ret < 0)197 goto fail;198 199 evtchnl->irq = ret;200 return 0;201 202fail:203 DRM_ERROR("Failed to allocate ring: %d\n", ret);204 return ret;205}206 207int xen_drm_front_evtchnl_create_all(struct xen_drm_front_info *front_info)208{209 struct xen_drm_front_cfg *cfg;210 int ret, conn;211 212 cfg = &front_info->cfg;213 214 front_info->evt_pairs =215 kcalloc(cfg->num_connectors,216 sizeof(struct xen_drm_front_evtchnl_pair),217 GFP_KERNEL);218 if (!front_info->evt_pairs) {219 ret = -ENOMEM;220 goto fail;221 }222 223 for (conn = 0; conn < cfg->num_connectors; conn++) {224 ret = evtchnl_alloc(front_info, conn,225 &front_info->evt_pairs[conn].req,226 EVTCHNL_TYPE_REQ);227 if (ret < 0) {228 DRM_ERROR("Error allocating control channel\n");229 goto fail;230 }231 232 ret = evtchnl_alloc(front_info, conn,233 &front_info->evt_pairs[conn].evt,234 EVTCHNL_TYPE_EVT);235 if (ret < 0) {236 DRM_ERROR("Error allocating in-event channel\n");237 goto fail;238 }239 }240 front_info->num_evt_pairs = cfg->num_connectors;241 return 0;242 243fail:244 xen_drm_front_evtchnl_free_all(front_info);245 return ret;246}247 248static int evtchnl_publish(struct xenbus_transaction xbt,249 struct xen_drm_front_evtchnl *evtchnl,250 const char *path, const char *node_ring,251 const char *node_chnl)252{253 struct xenbus_device *xb_dev = evtchnl->front_info->xb_dev;254 int ret;255 256 /* write control channel ring reference */257 ret = xenbus_printf(xbt, path, node_ring, "%u", evtchnl->gref);258 if (ret < 0) {259 xenbus_dev_error(xb_dev, ret, "writing ring-ref");260 return ret;261 }262 263 /* write event channel ring reference */264 ret = xenbus_printf(xbt, path, node_chnl, "%u", evtchnl->port);265 if (ret < 0) {266 xenbus_dev_error(xb_dev, ret, "writing event channel");267 return ret;268 }269 270 return 0;271}272 273int xen_drm_front_evtchnl_publish_all(struct xen_drm_front_info *front_info)274{275 struct xenbus_transaction xbt;276 struct xen_drm_front_cfg *plat_data;277 int ret, conn;278 279 plat_data = &front_info->cfg;280 281again:282 ret = xenbus_transaction_start(&xbt);283 if (ret < 0) {284 xenbus_dev_fatal(front_info->xb_dev, ret,285 "starting transaction");286 return ret;287 }288 289 for (conn = 0; conn < plat_data->num_connectors; conn++) {290 ret = evtchnl_publish(xbt, &front_info->evt_pairs[conn].req,291 plat_data->connectors[conn].xenstore_path,292 XENDISPL_FIELD_REQ_RING_REF,293 XENDISPL_FIELD_REQ_CHANNEL);294 if (ret < 0)295 goto fail;296 297 ret = evtchnl_publish(xbt, &front_info->evt_pairs[conn].evt,298 plat_data->connectors[conn].xenstore_path,299 XENDISPL_FIELD_EVT_RING_REF,300 XENDISPL_FIELD_EVT_CHANNEL);301 if (ret < 0)302 goto fail;303 }304 305 ret = xenbus_transaction_end(xbt, 0);306 if (ret < 0) {307 if (ret == -EAGAIN)308 goto again;309 310 xenbus_dev_fatal(front_info->xb_dev, ret,311 "completing transaction");312 goto fail_to_end;313 }314 315 return 0;316 317fail:318 xenbus_transaction_end(xbt, 1);319 320fail_to_end:321 xenbus_dev_fatal(front_info->xb_dev, ret, "writing Xen store");322 return ret;323}324 325void xen_drm_front_evtchnl_flush(struct xen_drm_front_evtchnl *evtchnl)326{327 int notify;328 329 evtchnl->u.req.ring.req_prod_pvt++;330 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&evtchnl->u.req.ring, notify);331 if (notify)332 notify_remote_via_irq(evtchnl->irq);333}334 335void xen_drm_front_evtchnl_set_state(struct xen_drm_front_info *front_info,336 enum xen_drm_front_evtchnl_state state)337{338 unsigned long flags;339 int i;340 341 if (!front_info->evt_pairs)342 return;343 344 spin_lock_irqsave(&front_info->io_lock, flags);345 for (i = 0; i < front_info->num_evt_pairs; i++) {346 front_info->evt_pairs[i].req.state = state;347 front_info->evt_pairs[i].evt.state = state;348 }349 spin_unlock_irqrestore(&front_info->io_lock, flags);350}351 352void xen_drm_front_evtchnl_free_all(struct xen_drm_front_info *front_info)353{354 int i;355 356 if (!front_info->evt_pairs)357 return;358 359 for (i = 0; i < front_info->num_evt_pairs; i++) {360 evtchnl_free(front_info, &front_info->evt_pairs[i].req);361 evtchnl_free(front_info, &front_info->evt_pairs[i].evt);362 }363 364 kfree(front_info->evt_pairs);365 front_info->evt_pairs = NULL;366}367