1c575b7eeSOleksandr Andrushchenko // SPDX-License-Identifier: GPL-2.0 OR MIT 2c575b7eeSOleksandr Andrushchenko 3c575b7eeSOleksandr Andrushchenko /* 4c575b7eeSOleksandr Andrushchenko * Xen para-virtual DRM device 5c575b7eeSOleksandr Andrushchenko * 6c575b7eeSOleksandr Andrushchenko * Copyright (C) 2016-2018 EPAM Systems Inc. 7c575b7eeSOleksandr Andrushchenko * 8c575b7eeSOleksandr Andrushchenko * Author: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com> 9c575b7eeSOleksandr Andrushchenko */ 10c575b7eeSOleksandr Andrushchenko 11c575b7eeSOleksandr Andrushchenko #include <drm/drmP.h> 12c575b7eeSOleksandr Andrushchenko #include <drm/drm_atomic_helper.h> 13c575b7eeSOleksandr Andrushchenko #include <drm/drm_crtc_helper.h> 14c575b7eeSOleksandr Andrushchenko #include <drm/drm_gem.h> 15c575b7eeSOleksandr Andrushchenko 16c575b7eeSOleksandr Andrushchenko #include <linux/of_device.h> 17c575b7eeSOleksandr Andrushchenko 18c575b7eeSOleksandr Andrushchenko #include <xen/platform_pci.h> 19c575b7eeSOleksandr Andrushchenko #include <xen/xen.h> 20c575b7eeSOleksandr Andrushchenko #include <xen/xenbus.h> 21c575b7eeSOleksandr Andrushchenko 22c575b7eeSOleksandr Andrushchenko #include <xen/interface/io/displif.h> 23c575b7eeSOleksandr Andrushchenko 24c575b7eeSOleksandr Andrushchenko #include "xen_drm_front.h" 25c575b7eeSOleksandr Andrushchenko #include "xen_drm_front_cfg.h" 26c575b7eeSOleksandr Andrushchenko #include "xen_drm_front_evtchnl.h" 27c575b7eeSOleksandr Andrushchenko #include "xen_drm_front_gem.h" 28c575b7eeSOleksandr Andrushchenko #include "xen_drm_front_kms.h" 29c575b7eeSOleksandr Andrushchenko #include "xen_drm_front_shbuf.h" 30c575b7eeSOleksandr Andrushchenko 31c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf { 32c575b7eeSOleksandr Andrushchenko struct list_head list; 33c575b7eeSOleksandr Andrushchenko u64 dbuf_cookie; 34c575b7eeSOleksandr Andrushchenko u64 fb_cookie; 35c575b7eeSOleksandr Andrushchenko struct xen_drm_front_shbuf *shbuf; 36c575b7eeSOleksandr Andrushchenko }; 37c575b7eeSOleksandr Andrushchenko 38c575b7eeSOleksandr Andrushchenko static int dbuf_add_to_list(struct xen_drm_front_info *front_info, 39c575b7eeSOleksandr Andrushchenko struct xen_drm_front_shbuf *shbuf, u64 dbuf_cookie) 40c575b7eeSOleksandr Andrushchenko { 41c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *dbuf; 42c575b7eeSOleksandr Andrushchenko 43c575b7eeSOleksandr Andrushchenko dbuf = kzalloc(sizeof(*dbuf), GFP_KERNEL); 44c575b7eeSOleksandr Andrushchenko if (!dbuf) 45c575b7eeSOleksandr Andrushchenko return -ENOMEM; 46c575b7eeSOleksandr Andrushchenko 47c575b7eeSOleksandr Andrushchenko dbuf->dbuf_cookie = dbuf_cookie; 48c575b7eeSOleksandr Andrushchenko dbuf->shbuf = shbuf; 49c575b7eeSOleksandr Andrushchenko list_add(&dbuf->list, &front_info->dbuf_list); 50c575b7eeSOleksandr Andrushchenko return 0; 51c575b7eeSOleksandr Andrushchenko } 52c575b7eeSOleksandr Andrushchenko 53c575b7eeSOleksandr Andrushchenko static struct xen_drm_front_dbuf *dbuf_get(struct list_head *dbuf_list, 54c575b7eeSOleksandr Andrushchenko u64 dbuf_cookie) 55c575b7eeSOleksandr Andrushchenko { 56c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *buf, *q; 57c575b7eeSOleksandr Andrushchenko 58c575b7eeSOleksandr Andrushchenko list_for_each_entry_safe(buf, q, dbuf_list, list) 59c575b7eeSOleksandr Andrushchenko if (buf->dbuf_cookie == dbuf_cookie) 60c575b7eeSOleksandr Andrushchenko return buf; 61c575b7eeSOleksandr Andrushchenko 62c575b7eeSOleksandr Andrushchenko return NULL; 63c575b7eeSOleksandr Andrushchenko } 64c575b7eeSOleksandr Andrushchenko 65c575b7eeSOleksandr Andrushchenko static void dbuf_flush_fb(struct list_head *dbuf_list, u64 fb_cookie) 66c575b7eeSOleksandr Andrushchenko { 67c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *buf, *q; 68c575b7eeSOleksandr Andrushchenko 69c575b7eeSOleksandr Andrushchenko list_for_each_entry_safe(buf, q, dbuf_list, list) 70c575b7eeSOleksandr Andrushchenko if (buf->fb_cookie == fb_cookie) 71c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_flush(buf->shbuf); 72c575b7eeSOleksandr Andrushchenko } 73c575b7eeSOleksandr Andrushchenko 74c575b7eeSOleksandr Andrushchenko static void dbuf_free(struct list_head *dbuf_list, u64 dbuf_cookie) 75c575b7eeSOleksandr Andrushchenko { 76c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *buf, *q; 77c575b7eeSOleksandr Andrushchenko 78c575b7eeSOleksandr Andrushchenko list_for_each_entry_safe(buf, q, dbuf_list, list) 79c575b7eeSOleksandr Andrushchenko if (buf->dbuf_cookie == dbuf_cookie) { 80c575b7eeSOleksandr Andrushchenko list_del(&buf->list); 81c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_unmap(buf->shbuf); 82c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_free(buf->shbuf); 83c575b7eeSOleksandr Andrushchenko kfree(buf); 84c575b7eeSOleksandr Andrushchenko break; 85c575b7eeSOleksandr Andrushchenko } 86c575b7eeSOleksandr Andrushchenko } 87c575b7eeSOleksandr Andrushchenko 88c575b7eeSOleksandr Andrushchenko static void dbuf_free_all(struct list_head *dbuf_list) 89c575b7eeSOleksandr Andrushchenko { 90c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *buf, *q; 91c575b7eeSOleksandr Andrushchenko 92c575b7eeSOleksandr Andrushchenko list_for_each_entry_safe(buf, q, dbuf_list, list) { 93c575b7eeSOleksandr Andrushchenko list_del(&buf->list); 94c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_unmap(buf->shbuf); 95c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_free(buf->shbuf); 96c575b7eeSOleksandr Andrushchenko kfree(buf); 97c575b7eeSOleksandr Andrushchenko } 98c575b7eeSOleksandr Andrushchenko } 99c575b7eeSOleksandr Andrushchenko 100c575b7eeSOleksandr Andrushchenko static struct xendispl_req * 101c575b7eeSOleksandr Andrushchenko be_prepare_req(struct xen_drm_front_evtchnl *evtchnl, u8 operation) 102c575b7eeSOleksandr Andrushchenko { 103c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 104c575b7eeSOleksandr Andrushchenko 105c575b7eeSOleksandr Andrushchenko req = RING_GET_REQUEST(&evtchnl->u.req.ring, 106c575b7eeSOleksandr Andrushchenko evtchnl->u.req.ring.req_prod_pvt); 107c575b7eeSOleksandr Andrushchenko req->operation = operation; 108c575b7eeSOleksandr Andrushchenko req->id = evtchnl->evt_next_id++; 109c575b7eeSOleksandr Andrushchenko evtchnl->evt_id = req->id; 110c575b7eeSOleksandr Andrushchenko return req; 111c575b7eeSOleksandr Andrushchenko } 112c575b7eeSOleksandr Andrushchenko 113c575b7eeSOleksandr Andrushchenko static int be_stream_do_io(struct xen_drm_front_evtchnl *evtchnl, 114c575b7eeSOleksandr Andrushchenko struct xendispl_req *req) 115c575b7eeSOleksandr Andrushchenko { 116c575b7eeSOleksandr Andrushchenko reinit_completion(&evtchnl->u.req.completion); 117c575b7eeSOleksandr Andrushchenko if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) 118c575b7eeSOleksandr Andrushchenko return -EIO; 119c575b7eeSOleksandr Andrushchenko 120c575b7eeSOleksandr Andrushchenko xen_drm_front_evtchnl_flush(evtchnl); 121c575b7eeSOleksandr Andrushchenko return 0; 122c575b7eeSOleksandr Andrushchenko } 123c575b7eeSOleksandr Andrushchenko 124c575b7eeSOleksandr Andrushchenko static int be_stream_wait_io(struct xen_drm_front_evtchnl *evtchnl) 125c575b7eeSOleksandr Andrushchenko { 126c575b7eeSOleksandr Andrushchenko if (wait_for_completion_timeout(&evtchnl->u.req.completion, 127c575b7eeSOleksandr Andrushchenko msecs_to_jiffies(XEN_DRM_FRONT_WAIT_BACK_MS)) <= 0) 128c575b7eeSOleksandr Andrushchenko return -ETIMEDOUT; 129c575b7eeSOleksandr Andrushchenko 130c575b7eeSOleksandr Andrushchenko return evtchnl->u.req.resp_status; 131c575b7eeSOleksandr Andrushchenko } 132c575b7eeSOleksandr Andrushchenko 133c575b7eeSOleksandr Andrushchenko int xen_drm_front_mode_set(struct xen_drm_front_drm_pipeline *pipeline, 134c575b7eeSOleksandr Andrushchenko u32 x, u32 y, u32 width, u32 height, 135c575b7eeSOleksandr Andrushchenko u32 bpp, u64 fb_cookie) 136c575b7eeSOleksandr Andrushchenko { 137c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 138c575b7eeSOleksandr Andrushchenko struct xen_drm_front_info *front_info; 139c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 140c575b7eeSOleksandr Andrushchenko unsigned long flags; 141c575b7eeSOleksandr Andrushchenko int ret; 142c575b7eeSOleksandr Andrushchenko 143c575b7eeSOleksandr Andrushchenko front_info = pipeline->drm_info->front_info; 144c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[pipeline->index].req; 145c575b7eeSOleksandr Andrushchenko if (unlikely(!evtchnl)) 146c575b7eeSOleksandr Andrushchenko return -EIO; 147c575b7eeSOleksandr Andrushchenko 148c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 149c575b7eeSOleksandr Andrushchenko 150c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 151c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_SET_CONFIG); 152c575b7eeSOleksandr Andrushchenko req->op.set_config.x = x; 153c575b7eeSOleksandr Andrushchenko req->op.set_config.y = y; 154c575b7eeSOleksandr Andrushchenko req->op.set_config.width = width; 155c575b7eeSOleksandr Andrushchenko req->op.set_config.height = height; 156c575b7eeSOleksandr Andrushchenko req->op.set_config.bpp = bpp; 157c575b7eeSOleksandr Andrushchenko req->op.set_config.fb_cookie = fb_cookie; 158c575b7eeSOleksandr Andrushchenko 159c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 160c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 161c575b7eeSOleksandr Andrushchenko 162c575b7eeSOleksandr Andrushchenko if (ret == 0) 163c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 164c575b7eeSOleksandr Andrushchenko 165c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 166c575b7eeSOleksandr Andrushchenko return ret; 167c575b7eeSOleksandr Andrushchenko } 168c575b7eeSOleksandr Andrushchenko 1694394e964SOleksandr Andrushchenko int xen_drm_front_dbuf_create(struct xen_drm_front_info *front_info, 170c575b7eeSOleksandr Andrushchenko u64 dbuf_cookie, u32 width, u32 height, 1714394e964SOleksandr Andrushchenko u32 bpp, u64 size, struct page **pages) 172c575b7eeSOleksandr Andrushchenko { 173c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 174c575b7eeSOleksandr Andrushchenko struct xen_drm_front_shbuf *shbuf; 175c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 176c575b7eeSOleksandr Andrushchenko struct xen_drm_front_shbuf_cfg buf_cfg; 177c575b7eeSOleksandr Andrushchenko unsigned long flags; 178c575b7eeSOleksandr Andrushchenko int ret; 179c575b7eeSOleksandr Andrushchenko 180c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; 181c575b7eeSOleksandr Andrushchenko if (unlikely(!evtchnl)) 182c575b7eeSOleksandr Andrushchenko return -EIO; 183c575b7eeSOleksandr Andrushchenko 184c575b7eeSOleksandr Andrushchenko memset(&buf_cfg, 0, sizeof(buf_cfg)); 185c575b7eeSOleksandr Andrushchenko buf_cfg.xb_dev = front_info->xb_dev; 186c575b7eeSOleksandr Andrushchenko buf_cfg.pages = pages; 187c575b7eeSOleksandr Andrushchenko buf_cfg.size = size; 188c575b7eeSOleksandr Andrushchenko buf_cfg.be_alloc = front_info->cfg.be_alloc; 189c575b7eeSOleksandr Andrushchenko 190c575b7eeSOleksandr Andrushchenko shbuf = xen_drm_front_shbuf_alloc(&buf_cfg); 19118f20bc5SDan Carpenter if (IS_ERR(shbuf)) 19218f20bc5SDan Carpenter return PTR_ERR(shbuf); 193c575b7eeSOleksandr Andrushchenko 194c575b7eeSOleksandr Andrushchenko ret = dbuf_add_to_list(front_info, shbuf, dbuf_cookie); 195c575b7eeSOleksandr Andrushchenko if (ret < 0) { 196c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_free(shbuf); 197c575b7eeSOleksandr Andrushchenko return ret; 198c575b7eeSOleksandr Andrushchenko } 199c575b7eeSOleksandr Andrushchenko 200c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 201c575b7eeSOleksandr Andrushchenko 202c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 203c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_CREATE); 204c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.gref_directory = 205c575b7eeSOleksandr Andrushchenko xen_drm_front_shbuf_get_dir_start(shbuf); 206c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.buffer_sz = size; 207c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.dbuf_cookie = dbuf_cookie; 208c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.width = width; 209c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.height = height; 210c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.bpp = bpp; 211c575b7eeSOleksandr Andrushchenko if (buf_cfg.be_alloc) 212c575b7eeSOleksandr Andrushchenko req->op.dbuf_create.flags |= XENDISPL_DBUF_FLG_REQ_ALLOC; 213c575b7eeSOleksandr Andrushchenko 214c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 215c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 216c575b7eeSOleksandr Andrushchenko 217c575b7eeSOleksandr Andrushchenko if (ret < 0) 218c575b7eeSOleksandr Andrushchenko goto fail; 219c575b7eeSOleksandr Andrushchenko 220c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 221c575b7eeSOleksandr Andrushchenko if (ret < 0) 222c575b7eeSOleksandr Andrushchenko goto fail; 223c575b7eeSOleksandr Andrushchenko 224c575b7eeSOleksandr Andrushchenko ret = xen_drm_front_shbuf_map(shbuf); 225c575b7eeSOleksandr Andrushchenko if (ret < 0) 226c575b7eeSOleksandr Andrushchenko goto fail; 227c575b7eeSOleksandr Andrushchenko 228c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 229c575b7eeSOleksandr Andrushchenko return 0; 230c575b7eeSOleksandr Andrushchenko 231c575b7eeSOleksandr Andrushchenko fail: 232c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 233c575b7eeSOleksandr Andrushchenko dbuf_free(&front_info->dbuf_list, dbuf_cookie); 234c575b7eeSOleksandr Andrushchenko return ret; 235c575b7eeSOleksandr Andrushchenko } 236c575b7eeSOleksandr Andrushchenko 237c575b7eeSOleksandr Andrushchenko static int xen_drm_front_dbuf_destroy(struct xen_drm_front_info *front_info, 238c575b7eeSOleksandr Andrushchenko u64 dbuf_cookie) 239c575b7eeSOleksandr Andrushchenko { 240c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 241c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 242c575b7eeSOleksandr Andrushchenko unsigned long flags; 243c575b7eeSOleksandr Andrushchenko bool be_alloc; 244c575b7eeSOleksandr Andrushchenko int ret; 245c575b7eeSOleksandr Andrushchenko 246c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; 247c575b7eeSOleksandr Andrushchenko if (unlikely(!evtchnl)) 248c575b7eeSOleksandr Andrushchenko return -EIO; 249c575b7eeSOleksandr Andrushchenko 250c575b7eeSOleksandr Andrushchenko be_alloc = front_info->cfg.be_alloc; 251c575b7eeSOleksandr Andrushchenko 252c575b7eeSOleksandr Andrushchenko /* 253c575b7eeSOleksandr Andrushchenko * For the backend allocated buffer release references now, so backend 254c575b7eeSOleksandr Andrushchenko * can free the buffer. 255c575b7eeSOleksandr Andrushchenko */ 256c575b7eeSOleksandr Andrushchenko if (be_alloc) 257c575b7eeSOleksandr Andrushchenko dbuf_free(&front_info->dbuf_list, dbuf_cookie); 258c575b7eeSOleksandr Andrushchenko 259c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 260c575b7eeSOleksandr Andrushchenko 261c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 262c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_DESTROY); 263c575b7eeSOleksandr Andrushchenko req->op.dbuf_destroy.dbuf_cookie = dbuf_cookie; 264c575b7eeSOleksandr Andrushchenko 265c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 266c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 267c575b7eeSOleksandr Andrushchenko 268c575b7eeSOleksandr Andrushchenko if (ret == 0) 269c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 270c575b7eeSOleksandr Andrushchenko 271c575b7eeSOleksandr Andrushchenko /* 272c575b7eeSOleksandr Andrushchenko * Do this regardless of communication status with the backend: 273c575b7eeSOleksandr Andrushchenko * if we cannot remove remote resources remove what we can locally. 274c575b7eeSOleksandr Andrushchenko */ 275c575b7eeSOleksandr Andrushchenko if (!be_alloc) 276c575b7eeSOleksandr Andrushchenko dbuf_free(&front_info->dbuf_list, dbuf_cookie); 277c575b7eeSOleksandr Andrushchenko 278c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 279c575b7eeSOleksandr Andrushchenko return ret; 280c575b7eeSOleksandr Andrushchenko } 281c575b7eeSOleksandr Andrushchenko 282c575b7eeSOleksandr Andrushchenko int xen_drm_front_fb_attach(struct xen_drm_front_info *front_info, 283c575b7eeSOleksandr Andrushchenko u64 dbuf_cookie, u64 fb_cookie, u32 width, 284c575b7eeSOleksandr Andrushchenko u32 height, u32 pixel_format) 285c575b7eeSOleksandr Andrushchenko { 286c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 287c575b7eeSOleksandr Andrushchenko struct xen_drm_front_dbuf *buf; 288c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 289c575b7eeSOleksandr Andrushchenko unsigned long flags; 290c575b7eeSOleksandr Andrushchenko int ret; 291c575b7eeSOleksandr Andrushchenko 292c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; 293c575b7eeSOleksandr Andrushchenko if (unlikely(!evtchnl)) 294c575b7eeSOleksandr Andrushchenko return -EIO; 295c575b7eeSOleksandr Andrushchenko 296c575b7eeSOleksandr Andrushchenko buf = dbuf_get(&front_info->dbuf_list, dbuf_cookie); 297c575b7eeSOleksandr Andrushchenko if (!buf) 298c575b7eeSOleksandr Andrushchenko return -EINVAL; 299c575b7eeSOleksandr Andrushchenko 300c575b7eeSOleksandr Andrushchenko buf->fb_cookie = fb_cookie; 301c575b7eeSOleksandr Andrushchenko 302c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 303c575b7eeSOleksandr Andrushchenko 304c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 305c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_FB_ATTACH); 306c575b7eeSOleksandr Andrushchenko req->op.fb_attach.dbuf_cookie = dbuf_cookie; 307c575b7eeSOleksandr Andrushchenko req->op.fb_attach.fb_cookie = fb_cookie; 308c575b7eeSOleksandr Andrushchenko req->op.fb_attach.width = width; 309c575b7eeSOleksandr Andrushchenko req->op.fb_attach.height = height; 310c575b7eeSOleksandr Andrushchenko req->op.fb_attach.pixel_format = pixel_format; 311c575b7eeSOleksandr Andrushchenko 312c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 313c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 314c575b7eeSOleksandr Andrushchenko 315c575b7eeSOleksandr Andrushchenko if (ret == 0) 316c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 317c575b7eeSOleksandr Andrushchenko 318c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 319c575b7eeSOleksandr Andrushchenko return ret; 320c575b7eeSOleksandr Andrushchenko } 321c575b7eeSOleksandr Andrushchenko 322c575b7eeSOleksandr Andrushchenko int xen_drm_front_fb_detach(struct xen_drm_front_info *front_info, 323c575b7eeSOleksandr Andrushchenko u64 fb_cookie) 324c575b7eeSOleksandr Andrushchenko { 325c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 326c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 327c575b7eeSOleksandr Andrushchenko unsigned long flags; 328c575b7eeSOleksandr Andrushchenko int ret; 329c575b7eeSOleksandr Andrushchenko 330c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; 331c575b7eeSOleksandr Andrushchenko if (unlikely(!evtchnl)) 332c575b7eeSOleksandr Andrushchenko return -EIO; 333c575b7eeSOleksandr Andrushchenko 334c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 335c575b7eeSOleksandr Andrushchenko 336c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 337c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_FB_DETACH); 338c575b7eeSOleksandr Andrushchenko req->op.fb_detach.fb_cookie = fb_cookie; 339c575b7eeSOleksandr Andrushchenko 340c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 341c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 342c575b7eeSOleksandr Andrushchenko 343c575b7eeSOleksandr Andrushchenko if (ret == 0) 344c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 345c575b7eeSOleksandr Andrushchenko 346c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 347c575b7eeSOleksandr Andrushchenko return ret; 348c575b7eeSOleksandr Andrushchenko } 349c575b7eeSOleksandr Andrushchenko 350c575b7eeSOleksandr Andrushchenko int xen_drm_front_page_flip(struct xen_drm_front_info *front_info, 351c575b7eeSOleksandr Andrushchenko int conn_idx, u64 fb_cookie) 352c575b7eeSOleksandr Andrushchenko { 353c575b7eeSOleksandr Andrushchenko struct xen_drm_front_evtchnl *evtchnl; 354c575b7eeSOleksandr Andrushchenko struct xendispl_req *req; 355c575b7eeSOleksandr Andrushchenko unsigned long flags; 356c575b7eeSOleksandr Andrushchenko int ret; 357c575b7eeSOleksandr Andrushchenko 358c575b7eeSOleksandr Andrushchenko if (unlikely(conn_idx >= front_info->num_evt_pairs)) 359c575b7eeSOleksandr Andrushchenko return -EINVAL; 360c575b7eeSOleksandr Andrushchenko 361c575b7eeSOleksandr Andrushchenko dbuf_flush_fb(&front_info->dbuf_list, fb_cookie); 362c575b7eeSOleksandr Andrushchenko evtchnl = &front_info->evt_pairs[conn_idx].req; 363c575b7eeSOleksandr Andrushchenko 364c575b7eeSOleksandr Andrushchenko mutex_lock(&evtchnl->u.req.req_io_lock); 365c575b7eeSOleksandr Andrushchenko 366c575b7eeSOleksandr Andrushchenko spin_lock_irqsave(&front_info->io_lock, flags); 367c575b7eeSOleksandr Andrushchenko req = be_prepare_req(evtchnl, XENDISPL_OP_PG_FLIP); 368c575b7eeSOleksandr Andrushchenko req->op.pg_flip.fb_cookie = fb_cookie; 369c575b7eeSOleksandr Andrushchenko 370c575b7eeSOleksandr Andrushchenko ret = be_stream_do_io(evtchnl, req); 371c575b7eeSOleksandr Andrushchenko spin_unlock_irqrestore(&front_info->io_lock, flags); 372c575b7eeSOleksandr Andrushchenko 373c575b7eeSOleksandr Andrushchenko if (ret == 0) 374c575b7eeSOleksandr Andrushchenko ret = be_stream_wait_io(evtchnl); 375c575b7eeSOleksandr Andrushchenko 376c575b7eeSOleksandr Andrushchenko mutex_unlock(&evtchnl->u.req.req_io_lock); 377c575b7eeSOleksandr Andrushchenko return ret; 378c575b7eeSOleksandr Andrushchenko } 379c575b7eeSOleksandr Andrushchenko 380c575b7eeSOleksandr Andrushchenko void xen_drm_front_on_frame_done(struct xen_drm_front_info *front_info, 381c575b7eeSOleksandr Andrushchenko int conn_idx, u64 fb_cookie) 382c575b7eeSOleksandr Andrushchenko { 383c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info = front_info->drm_info; 384c575b7eeSOleksandr Andrushchenko 385c575b7eeSOleksandr Andrushchenko if (unlikely(conn_idx >= front_info->cfg.num_connectors)) 386c575b7eeSOleksandr Andrushchenko return; 387c575b7eeSOleksandr Andrushchenko 388c575b7eeSOleksandr Andrushchenko xen_drm_front_kms_on_frame_done(&drm_info->pipeline[conn_idx], 389c575b7eeSOleksandr Andrushchenko fb_cookie); 390c575b7eeSOleksandr Andrushchenko } 391c575b7eeSOleksandr Andrushchenko 392c575b7eeSOleksandr Andrushchenko static int xen_drm_drv_dumb_create(struct drm_file *filp, 393c575b7eeSOleksandr Andrushchenko struct drm_device *dev, 394c575b7eeSOleksandr Andrushchenko struct drm_mode_create_dumb *args) 395c575b7eeSOleksandr Andrushchenko { 396c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info = dev->dev_private; 397c575b7eeSOleksandr Andrushchenko struct drm_gem_object *obj; 398c575b7eeSOleksandr Andrushchenko int ret; 399c575b7eeSOleksandr Andrushchenko 400c575b7eeSOleksandr Andrushchenko /* 401c575b7eeSOleksandr Andrushchenko * Dumb creation is a two stage process: first we create a fully 402c575b7eeSOleksandr Andrushchenko * constructed GEM object which is communicated to the backend, and 403c575b7eeSOleksandr Andrushchenko * only after that we can create GEM's handle. This is done so, 404c575b7eeSOleksandr Andrushchenko * because of the possible races: once you create a handle it becomes 405c575b7eeSOleksandr Andrushchenko * immediately visible to user-space, so the latter can try accessing 406c575b7eeSOleksandr Andrushchenko * object without pages etc. 407c575b7eeSOleksandr Andrushchenko * For details also see drm_gem_handle_create 408c575b7eeSOleksandr Andrushchenko */ 409c575b7eeSOleksandr Andrushchenko args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 410c575b7eeSOleksandr Andrushchenko args->size = args->pitch * args->height; 411c575b7eeSOleksandr Andrushchenko 412c575b7eeSOleksandr Andrushchenko obj = xen_drm_front_gem_create(dev, args->size); 413c575b7eeSOleksandr Andrushchenko if (IS_ERR_OR_NULL(obj)) { 414c575b7eeSOleksandr Andrushchenko ret = PTR_ERR(obj); 415c575b7eeSOleksandr Andrushchenko goto fail; 416c575b7eeSOleksandr Andrushchenko } 417c575b7eeSOleksandr Andrushchenko 4184394e964SOleksandr Andrushchenko ret = xen_drm_front_dbuf_create(drm_info->front_info, 419c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_to_cookie(obj), 420c575b7eeSOleksandr Andrushchenko args->width, args->height, args->bpp, 421c575b7eeSOleksandr Andrushchenko args->size, 422c575b7eeSOleksandr Andrushchenko xen_drm_front_gem_get_pages(obj)); 423c575b7eeSOleksandr Andrushchenko if (ret) 424c575b7eeSOleksandr Andrushchenko goto fail_backend; 425c575b7eeSOleksandr Andrushchenko 426c575b7eeSOleksandr Andrushchenko /* This is the tail of GEM object creation */ 427c575b7eeSOleksandr Andrushchenko ret = drm_gem_handle_create(filp, obj, &args->handle); 428c575b7eeSOleksandr Andrushchenko if (ret) 429c575b7eeSOleksandr Andrushchenko goto fail_handle; 430c575b7eeSOleksandr Andrushchenko 431c575b7eeSOleksandr Andrushchenko /* Drop reference from allocate - handle holds it now */ 432c575b7eeSOleksandr Andrushchenko drm_gem_object_put_unlocked(obj); 433c575b7eeSOleksandr Andrushchenko return 0; 434c575b7eeSOleksandr Andrushchenko 435c575b7eeSOleksandr Andrushchenko fail_handle: 436c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_destroy(drm_info->front_info, 437c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_to_cookie(obj)); 438c575b7eeSOleksandr Andrushchenko fail_backend: 439c575b7eeSOleksandr Andrushchenko /* drop reference from allocate */ 440c575b7eeSOleksandr Andrushchenko drm_gem_object_put_unlocked(obj); 441c575b7eeSOleksandr Andrushchenko fail: 442c575b7eeSOleksandr Andrushchenko DRM_ERROR("Failed to create dumb buffer: %d\n", ret); 443c575b7eeSOleksandr Andrushchenko return ret; 444c575b7eeSOleksandr Andrushchenko } 445c575b7eeSOleksandr Andrushchenko 446c575b7eeSOleksandr Andrushchenko static void xen_drm_drv_free_object_unlocked(struct drm_gem_object *obj) 447c575b7eeSOleksandr Andrushchenko { 448c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info = obj->dev->dev_private; 449c575b7eeSOleksandr Andrushchenko int idx; 450c575b7eeSOleksandr Andrushchenko 451c575b7eeSOleksandr Andrushchenko if (drm_dev_enter(obj->dev, &idx)) { 452c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_destroy(drm_info->front_info, 453c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_to_cookie(obj)); 454c575b7eeSOleksandr Andrushchenko drm_dev_exit(idx); 455c575b7eeSOleksandr Andrushchenko } else { 456c575b7eeSOleksandr Andrushchenko dbuf_free(&drm_info->front_info->dbuf_list, 457c575b7eeSOleksandr Andrushchenko xen_drm_front_dbuf_to_cookie(obj)); 458c575b7eeSOleksandr Andrushchenko } 459c575b7eeSOleksandr Andrushchenko 460c575b7eeSOleksandr Andrushchenko xen_drm_front_gem_free_object_unlocked(obj); 461c575b7eeSOleksandr Andrushchenko } 462c575b7eeSOleksandr Andrushchenko 463c575b7eeSOleksandr Andrushchenko static void xen_drm_drv_release(struct drm_device *dev) 464c575b7eeSOleksandr Andrushchenko { 465c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info = dev->dev_private; 466c575b7eeSOleksandr Andrushchenko struct xen_drm_front_info *front_info = drm_info->front_info; 467c575b7eeSOleksandr Andrushchenko 468c575b7eeSOleksandr Andrushchenko xen_drm_front_kms_fini(drm_info); 469c575b7eeSOleksandr Andrushchenko 470c575b7eeSOleksandr Andrushchenko drm_atomic_helper_shutdown(dev); 471c575b7eeSOleksandr Andrushchenko drm_mode_config_cleanup(dev); 472c575b7eeSOleksandr Andrushchenko 473c575b7eeSOleksandr Andrushchenko drm_dev_fini(dev); 474c575b7eeSOleksandr Andrushchenko kfree(dev); 475c575b7eeSOleksandr Andrushchenko 476c575b7eeSOleksandr Andrushchenko if (front_info->cfg.be_alloc) 477c575b7eeSOleksandr Andrushchenko xenbus_switch_state(front_info->xb_dev, 478c575b7eeSOleksandr Andrushchenko XenbusStateInitialising); 479c575b7eeSOleksandr Andrushchenko 480c575b7eeSOleksandr Andrushchenko kfree(drm_info); 481c575b7eeSOleksandr Andrushchenko } 482c575b7eeSOleksandr Andrushchenko 483c575b7eeSOleksandr Andrushchenko static const struct file_operations xen_drm_dev_fops = { 484c575b7eeSOleksandr Andrushchenko .owner = THIS_MODULE, 485c575b7eeSOleksandr Andrushchenko .open = drm_open, 486c575b7eeSOleksandr Andrushchenko .release = drm_release, 487c575b7eeSOleksandr Andrushchenko .unlocked_ioctl = drm_ioctl, 488c575b7eeSOleksandr Andrushchenko #ifdef CONFIG_COMPAT 489c575b7eeSOleksandr Andrushchenko .compat_ioctl = drm_compat_ioctl, 490c575b7eeSOleksandr Andrushchenko #endif 491c575b7eeSOleksandr Andrushchenko .poll = drm_poll, 492c575b7eeSOleksandr Andrushchenko .read = drm_read, 493c575b7eeSOleksandr Andrushchenko .llseek = no_llseek, 494c575b7eeSOleksandr Andrushchenko .mmap = xen_drm_front_gem_mmap, 495c575b7eeSOleksandr Andrushchenko }; 496c575b7eeSOleksandr Andrushchenko 497c575b7eeSOleksandr Andrushchenko static const struct vm_operations_struct xen_drm_drv_vm_ops = { 498c575b7eeSOleksandr Andrushchenko .open = drm_gem_vm_open, 499c575b7eeSOleksandr Andrushchenko .close = drm_gem_vm_close, 500c575b7eeSOleksandr Andrushchenko }; 501c575b7eeSOleksandr Andrushchenko 502c575b7eeSOleksandr Andrushchenko static struct drm_driver xen_drm_driver = { 503c575b7eeSOleksandr Andrushchenko .driver_features = DRIVER_GEM | DRIVER_MODESET | 504c575b7eeSOleksandr Andrushchenko DRIVER_PRIME | DRIVER_ATOMIC, 505c575b7eeSOleksandr Andrushchenko .release = xen_drm_drv_release, 506c575b7eeSOleksandr Andrushchenko .gem_vm_ops = &xen_drm_drv_vm_ops, 507c575b7eeSOleksandr Andrushchenko .gem_free_object_unlocked = xen_drm_drv_free_object_unlocked, 508c575b7eeSOleksandr Andrushchenko .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 509c575b7eeSOleksandr Andrushchenko .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 510c575b7eeSOleksandr Andrushchenko .gem_prime_import = drm_gem_prime_import, 511c575b7eeSOleksandr Andrushchenko .gem_prime_export = drm_gem_prime_export, 512c575b7eeSOleksandr Andrushchenko .gem_prime_import_sg_table = xen_drm_front_gem_import_sg_table, 513c575b7eeSOleksandr Andrushchenko .gem_prime_get_sg_table = xen_drm_front_gem_get_sg_table, 5144394e964SOleksandr Andrushchenko .gem_prime_vmap = xen_drm_front_gem_prime_vmap, 5154394e964SOleksandr Andrushchenko .gem_prime_vunmap = xen_drm_front_gem_prime_vunmap, 5164394e964SOleksandr Andrushchenko .gem_prime_mmap = xen_drm_front_gem_prime_mmap, 517c575b7eeSOleksandr Andrushchenko .dumb_create = xen_drm_drv_dumb_create, 518c575b7eeSOleksandr Andrushchenko .fops = &xen_drm_dev_fops, 519c575b7eeSOleksandr Andrushchenko .name = "xendrm-du", 520c575b7eeSOleksandr Andrushchenko .desc = "Xen PV DRM Display Unit", 521c575b7eeSOleksandr Andrushchenko .date = "20180221", 522c575b7eeSOleksandr Andrushchenko .major = 1, 523c575b7eeSOleksandr Andrushchenko .minor = 0, 524c575b7eeSOleksandr Andrushchenko 525c575b7eeSOleksandr Andrushchenko }; 526c575b7eeSOleksandr Andrushchenko 527c575b7eeSOleksandr Andrushchenko static int xen_drm_drv_init(struct xen_drm_front_info *front_info) 528c575b7eeSOleksandr Andrushchenko { 529c575b7eeSOleksandr Andrushchenko struct device *dev = &front_info->xb_dev->dev; 530c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info; 531c575b7eeSOleksandr Andrushchenko struct drm_device *drm_dev; 532c575b7eeSOleksandr Andrushchenko int ret; 533c575b7eeSOleksandr Andrushchenko 534c575b7eeSOleksandr Andrushchenko DRM_INFO("Creating %s\n", xen_drm_driver.desc); 535c575b7eeSOleksandr Andrushchenko 536c575b7eeSOleksandr Andrushchenko drm_info = kzalloc(sizeof(*drm_info), GFP_KERNEL); 537c575b7eeSOleksandr Andrushchenko if (!drm_info) { 538c575b7eeSOleksandr Andrushchenko ret = -ENOMEM; 539c575b7eeSOleksandr Andrushchenko goto fail; 540c575b7eeSOleksandr Andrushchenko } 541c575b7eeSOleksandr Andrushchenko 542c575b7eeSOleksandr Andrushchenko drm_info->front_info = front_info; 543c575b7eeSOleksandr Andrushchenko front_info->drm_info = drm_info; 544c575b7eeSOleksandr Andrushchenko 545c575b7eeSOleksandr Andrushchenko drm_dev = drm_dev_alloc(&xen_drm_driver, dev); 546e30ca4bcSDan Carpenter if (IS_ERR(drm_dev)) { 547e30ca4bcSDan Carpenter ret = PTR_ERR(drm_dev); 548c575b7eeSOleksandr Andrushchenko goto fail; 549c575b7eeSOleksandr Andrushchenko } 550c575b7eeSOleksandr Andrushchenko 551c575b7eeSOleksandr Andrushchenko drm_info->drm_dev = drm_dev; 552c575b7eeSOleksandr Andrushchenko 553c575b7eeSOleksandr Andrushchenko drm_dev->dev_private = drm_info; 554c575b7eeSOleksandr Andrushchenko 555c575b7eeSOleksandr Andrushchenko ret = xen_drm_front_kms_init(drm_info); 556c575b7eeSOleksandr Andrushchenko if (ret) { 557c575b7eeSOleksandr Andrushchenko DRM_ERROR("Failed to initialize DRM/KMS, ret %d\n", ret); 558c575b7eeSOleksandr Andrushchenko goto fail_modeset; 559c575b7eeSOleksandr Andrushchenko } 560c575b7eeSOleksandr Andrushchenko 561c575b7eeSOleksandr Andrushchenko ret = drm_dev_register(drm_dev, 0); 562c575b7eeSOleksandr Andrushchenko if (ret) 563c575b7eeSOleksandr Andrushchenko goto fail_register; 564c575b7eeSOleksandr Andrushchenko 565c575b7eeSOleksandr Andrushchenko DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", 566c575b7eeSOleksandr Andrushchenko xen_drm_driver.name, xen_drm_driver.major, 567c575b7eeSOleksandr Andrushchenko xen_drm_driver.minor, xen_drm_driver.patchlevel, 568c575b7eeSOleksandr Andrushchenko xen_drm_driver.date, drm_dev->primary->index); 569c575b7eeSOleksandr Andrushchenko 570c575b7eeSOleksandr Andrushchenko return 0; 571c575b7eeSOleksandr Andrushchenko 572c575b7eeSOleksandr Andrushchenko fail_register: 573c575b7eeSOleksandr Andrushchenko drm_dev_unregister(drm_dev); 574c575b7eeSOleksandr Andrushchenko fail_modeset: 575c575b7eeSOleksandr Andrushchenko drm_kms_helper_poll_fini(drm_dev); 576c575b7eeSOleksandr Andrushchenko drm_mode_config_cleanup(drm_dev); 577c575b7eeSOleksandr Andrushchenko fail: 578c575b7eeSOleksandr Andrushchenko kfree(drm_info); 579c575b7eeSOleksandr Andrushchenko return ret; 580c575b7eeSOleksandr Andrushchenko } 581c575b7eeSOleksandr Andrushchenko 582c575b7eeSOleksandr Andrushchenko static void xen_drm_drv_fini(struct xen_drm_front_info *front_info) 583c575b7eeSOleksandr Andrushchenko { 584c575b7eeSOleksandr Andrushchenko struct xen_drm_front_drm_info *drm_info = front_info->drm_info; 585c575b7eeSOleksandr Andrushchenko struct drm_device *dev; 586c575b7eeSOleksandr Andrushchenko 587c575b7eeSOleksandr Andrushchenko if (!drm_info) 588c575b7eeSOleksandr Andrushchenko return; 589c575b7eeSOleksandr Andrushchenko 590c575b7eeSOleksandr Andrushchenko dev = drm_info->drm_dev; 591c575b7eeSOleksandr Andrushchenko if (!dev) 592c575b7eeSOleksandr Andrushchenko return; 593c575b7eeSOleksandr Andrushchenko 594c575b7eeSOleksandr Andrushchenko /* Nothing to do if device is already unplugged */ 595c575b7eeSOleksandr Andrushchenko if (drm_dev_is_unplugged(dev)) 596c575b7eeSOleksandr Andrushchenko return; 597c575b7eeSOleksandr Andrushchenko 598c575b7eeSOleksandr Andrushchenko drm_kms_helper_poll_fini(dev); 599c575b7eeSOleksandr Andrushchenko drm_dev_unplug(dev); 600c575b7eeSOleksandr Andrushchenko 601c575b7eeSOleksandr Andrushchenko front_info->drm_info = NULL; 602c575b7eeSOleksandr Andrushchenko 603c575b7eeSOleksandr Andrushchenko xen_drm_front_evtchnl_free_all(front_info); 604c575b7eeSOleksandr Andrushchenko dbuf_free_all(&front_info->dbuf_list); 605c575b7eeSOleksandr Andrushchenko 606c575b7eeSOleksandr Andrushchenko /* 607c575b7eeSOleksandr Andrushchenko * If we are not using backend allocated buffers, then tell the 608c575b7eeSOleksandr Andrushchenko * backend we are ready to (re)initialize. Otherwise, wait for 609c575b7eeSOleksandr Andrushchenko * drm_driver.release. 610c575b7eeSOleksandr Andrushchenko */ 611c575b7eeSOleksandr Andrushchenko if (!front_info->cfg.be_alloc) 612c575b7eeSOleksandr Andrushchenko xenbus_switch_state(front_info->xb_dev, 613c575b7eeSOleksandr Andrushchenko XenbusStateInitialising); 614c575b7eeSOleksandr Andrushchenko } 615c575b7eeSOleksandr Andrushchenko 616c575b7eeSOleksandr Andrushchenko static int displback_initwait(struct xen_drm_front_info *front_info) 617c575b7eeSOleksandr Andrushchenko { 618c575b7eeSOleksandr Andrushchenko struct xen_drm_front_cfg *cfg = &front_info->cfg; 619c575b7eeSOleksandr Andrushchenko int ret; 620c575b7eeSOleksandr Andrushchenko 621c575b7eeSOleksandr Andrushchenko cfg->front_info = front_info; 622c575b7eeSOleksandr Andrushchenko ret = xen_drm_front_cfg_card(front_info, cfg); 623c575b7eeSOleksandr Andrushchenko if (ret < 0) 624c575b7eeSOleksandr Andrushchenko return ret; 625c575b7eeSOleksandr Andrushchenko 626aefff491SColin Ian King DRM_INFO("Have %d connector(s)\n", cfg->num_connectors); 627c575b7eeSOleksandr Andrushchenko /* Create event channels for all connectors and publish */ 628c575b7eeSOleksandr Andrushchenko ret = xen_drm_front_evtchnl_create_all(front_info); 629c575b7eeSOleksandr Andrushchenko if (ret < 0) 630c575b7eeSOleksandr Andrushchenko return ret; 631c575b7eeSOleksandr Andrushchenko 632c575b7eeSOleksandr Andrushchenko return xen_drm_front_evtchnl_publish_all(front_info); 633c575b7eeSOleksandr Andrushchenko } 634c575b7eeSOleksandr Andrushchenko 635c575b7eeSOleksandr Andrushchenko static int displback_connect(struct xen_drm_front_info *front_info) 636c575b7eeSOleksandr Andrushchenko { 637c575b7eeSOleksandr Andrushchenko xen_drm_front_evtchnl_set_state(front_info, EVTCHNL_STATE_CONNECTED); 638c575b7eeSOleksandr Andrushchenko return xen_drm_drv_init(front_info); 639c575b7eeSOleksandr Andrushchenko } 640c575b7eeSOleksandr Andrushchenko 641c575b7eeSOleksandr Andrushchenko static void displback_disconnect(struct xen_drm_front_info *front_info) 642c575b7eeSOleksandr Andrushchenko { 643c575b7eeSOleksandr Andrushchenko if (!front_info->drm_info) 644c575b7eeSOleksandr Andrushchenko return; 645c575b7eeSOleksandr Andrushchenko 646c575b7eeSOleksandr Andrushchenko /* Tell the backend to wait until we release the DRM driver. */ 647c575b7eeSOleksandr Andrushchenko xenbus_switch_state(front_info->xb_dev, XenbusStateReconfiguring); 648c575b7eeSOleksandr Andrushchenko 649c575b7eeSOleksandr Andrushchenko xen_drm_drv_fini(front_info); 650c575b7eeSOleksandr Andrushchenko } 651c575b7eeSOleksandr Andrushchenko 652c575b7eeSOleksandr Andrushchenko static void displback_changed(struct xenbus_device *xb_dev, 653c575b7eeSOleksandr Andrushchenko enum xenbus_state backend_state) 654c575b7eeSOleksandr Andrushchenko { 655c575b7eeSOleksandr Andrushchenko struct xen_drm_front_info *front_info = dev_get_drvdata(&xb_dev->dev); 656c575b7eeSOleksandr Andrushchenko int ret; 657c575b7eeSOleksandr Andrushchenko 658c575b7eeSOleksandr Andrushchenko DRM_DEBUG("Backend state is %s, front is %s\n", 659c575b7eeSOleksandr Andrushchenko xenbus_strstate(backend_state), 660c575b7eeSOleksandr Andrushchenko xenbus_strstate(xb_dev->state)); 661c575b7eeSOleksandr Andrushchenko 662c575b7eeSOleksandr Andrushchenko switch (backend_state) { 663c575b7eeSOleksandr Andrushchenko case XenbusStateReconfiguring: 664c575b7eeSOleksandr Andrushchenko /* fall through */ 665c575b7eeSOleksandr Andrushchenko case XenbusStateReconfigured: 666c575b7eeSOleksandr Andrushchenko /* fall through */ 667c575b7eeSOleksandr Andrushchenko case XenbusStateInitialised: 668c575b7eeSOleksandr Andrushchenko break; 669c575b7eeSOleksandr Andrushchenko 670c575b7eeSOleksandr Andrushchenko case XenbusStateInitialising: 671c575b7eeSOleksandr Andrushchenko if (xb_dev->state == XenbusStateReconfiguring) 672c575b7eeSOleksandr Andrushchenko break; 673c575b7eeSOleksandr Andrushchenko 674c575b7eeSOleksandr Andrushchenko /* recovering after backend unexpected closure */ 675c575b7eeSOleksandr Andrushchenko displback_disconnect(front_info); 676c575b7eeSOleksandr Andrushchenko break; 677c575b7eeSOleksandr Andrushchenko 678c575b7eeSOleksandr Andrushchenko case XenbusStateInitWait: 679c575b7eeSOleksandr Andrushchenko if (xb_dev->state == XenbusStateReconfiguring) 680c575b7eeSOleksandr Andrushchenko break; 681c575b7eeSOleksandr Andrushchenko 682c575b7eeSOleksandr Andrushchenko /* recovering after backend unexpected closure */ 683c575b7eeSOleksandr Andrushchenko displback_disconnect(front_info); 684c575b7eeSOleksandr Andrushchenko if (xb_dev->state != XenbusStateInitialising) 685c575b7eeSOleksandr Andrushchenko break; 686c575b7eeSOleksandr Andrushchenko 687c575b7eeSOleksandr Andrushchenko ret = displback_initwait(front_info); 688c575b7eeSOleksandr Andrushchenko if (ret < 0) 689c575b7eeSOleksandr Andrushchenko xenbus_dev_fatal(xb_dev, ret, "initializing frontend"); 690c575b7eeSOleksandr Andrushchenko else 691c575b7eeSOleksandr Andrushchenko xenbus_switch_state(xb_dev, XenbusStateInitialised); 692c575b7eeSOleksandr Andrushchenko break; 693c575b7eeSOleksandr Andrushchenko 694c575b7eeSOleksandr Andrushchenko case XenbusStateConnected: 695c575b7eeSOleksandr Andrushchenko if (xb_dev->state != XenbusStateInitialised) 696c575b7eeSOleksandr Andrushchenko break; 697c575b7eeSOleksandr Andrushchenko 698c575b7eeSOleksandr Andrushchenko ret = displback_connect(front_info); 699c575b7eeSOleksandr Andrushchenko if (ret < 0) { 700c575b7eeSOleksandr Andrushchenko displback_disconnect(front_info); 701c575b7eeSOleksandr Andrushchenko xenbus_dev_fatal(xb_dev, ret, "connecting backend"); 702c575b7eeSOleksandr Andrushchenko } else { 703c575b7eeSOleksandr Andrushchenko xenbus_switch_state(xb_dev, XenbusStateConnected); 704c575b7eeSOleksandr Andrushchenko } 705c575b7eeSOleksandr Andrushchenko break; 706c575b7eeSOleksandr Andrushchenko 707c575b7eeSOleksandr Andrushchenko case XenbusStateClosing: 708c575b7eeSOleksandr Andrushchenko /* 709c575b7eeSOleksandr Andrushchenko * in this state backend starts freeing resources, 710c575b7eeSOleksandr Andrushchenko * so let it go into closed state, so we can also 711c575b7eeSOleksandr Andrushchenko * remove ours 712c575b7eeSOleksandr Andrushchenko */ 713c575b7eeSOleksandr Andrushchenko break; 714c575b7eeSOleksandr Andrushchenko 715c575b7eeSOleksandr Andrushchenko case XenbusStateUnknown: 716c575b7eeSOleksandr Andrushchenko /* fall through */ 717c575b7eeSOleksandr Andrushchenko case XenbusStateClosed: 718c575b7eeSOleksandr Andrushchenko if (xb_dev->state == XenbusStateClosed) 719c575b7eeSOleksandr Andrushchenko break; 720c575b7eeSOleksandr Andrushchenko 721c575b7eeSOleksandr Andrushchenko displback_disconnect(front_info); 722c575b7eeSOleksandr Andrushchenko break; 723c575b7eeSOleksandr Andrushchenko } 724c575b7eeSOleksandr Andrushchenko } 725c575b7eeSOleksandr Andrushchenko 726c575b7eeSOleksandr Andrushchenko static int xen_drv_probe(struct xenbus_device *xb_dev, 727c575b7eeSOleksandr Andrushchenko const struct xenbus_device_id *id) 728c575b7eeSOleksandr Andrushchenko { 729c575b7eeSOleksandr Andrushchenko struct xen_drm_front_info *front_info; 730c575b7eeSOleksandr Andrushchenko struct device *dev = &xb_dev->dev; 731c575b7eeSOleksandr Andrushchenko int ret; 732c575b7eeSOleksandr Andrushchenko 733c575b7eeSOleksandr Andrushchenko /* 734c575b7eeSOleksandr Andrushchenko * The device is not spawn from a device tree, so arch_setup_dma_ops 735c575b7eeSOleksandr Andrushchenko * is not called, thus leaving the device with dummy DMA ops. 736c575b7eeSOleksandr Andrushchenko * This makes the device return error on PRIME buffer import, which 737c575b7eeSOleksandr Andrushchenko * is not correct: to fix this call of_dma_configure() with a NULL 738c575b7eeSOleksandr Andrushchenko * node to set default DMA ops. 739c575b7eeSOleksandr Andrushchenko */ 740c575b7eeSOleksandr Andrushchenko dev->bus->force_dma = true; 741c575b7eeSOleksandr Andrushchenko dev->coherent_dma_mask = DMA_BIT_MASK(32); 742c575b7eeSOleksandr Andrushchenko ret = of_dma_configure(dev, NULL); 743c575b7eeSOleksandr Andrushchenko if (ret < 0) { 744c575b7eeSOleksandr Andrushchenko DRM_ERROR("Cannot setup DMA ops, ret %d", ret); 745c575b7eeSOleksandr Andrushchenko return ret; 746c575b7eeSOleksandr Andrushchenko } 747c575b7eeSOleksandr Andrushchenko 748c575b7eeSOleksandr Andrushchenko front_info = devm_kzalloc(&xb_dev->dev, 749c575b7eeSOleksandr Andrushchenko sizeof(*front_info), GFP_KERNEL); 750c575b7eeSOleksandr Andrushchenko if (!front_info) 751c575b7eeSOleksandr Andrushchenko return -ENOMEM; 752c575b7eeSOleksandr Andrushchenko 753c575b7eeSOleksandr Andrushchenko front_info->xb_dev = xb_dev; 754c575b7eeSOleksandr Andrushchenko spin_lock_init(&front_info->io_lock); 755c575b7eeSOleksandr Andrushchenko INIT_LIST_HEAD(&front_info->dbuf_list); 756c575b7eeSOleksandr Andrushchenko dev_set_drvdata(&xb_dev->dev, front_info); 757c575b7eeSOleksandr Andrushchenko 758c575b7eeSOleksandr Andrushchenko return xenbus_switch_state(xb_dev, XenbusStateInitialising); 759c575b7eeSOleksandr Andrushchenko } 760c575b7eeSOleksandr Andrushchenko 761c575b7eeSOleksandr Andrushchenko static int xen_drv_remove(struct xenbus_device *dev) 762c575b7eeSOleksandr Andrushchenko { 763c575b7eeSOleksandr Andrushchenko struct xen_drm_front_info *front_info = dev_get_drvdata(&dev->dev); 764c575b7eeSOleksandr Andrushchenko int to = 100; 765c575b7eeSOleksandr Andrushchenko 766c575b7eeSOleksandr Andrushchenko xenbus_switch_state(dev, XenbusStateClosing); 767c575b7eeSOleksandr Andrushchenko 768c575b7eeSOleksandr Andrushchenko /* 769c575b7eeSOleksandr Andrushchenko * On driver removal it is disconnected from XenBus, 770c575b7eeSOleksandr Andrushchenko * so no backend state change events come via .otherend_changed 771c575b7eeSOleksandr Andrushchenko * callback. This prevents us from exiting gracefully, e.g. 772c575b7eeSOleksandr Andrushchenko * signaling the backend to free event channels, waiting for its 773c575b7eeSOleksandr Andrushchenko * state to change to XenbusStateClosed and cleaning at our end. 774c575b7eeSOleksandr Andrushchenko * Normally when front driver removed backend will finally go into 775c575b7eeSOleksandr Andrushchenko * XenbusStateInitWait state. 776c575b7eeSOleksandr Andrushchenko * 777c575b7eeSOleksandr Andrushchenko * Workaround: read backend's state manually and wait with time-out. 778c575b7eeSOleksandr Andrushchenko */ 779c575b7eeSOleksandr Andrushchenko while ((xenbus_read_unsigned(front_info->xb_dev->otherend, "state", 780c575b7eeSOleksandr Andrushchenko XenbusStateUnknown) != XenbusStateInitWait) && 781f45140dfSDan Carpenter --to) 782c575b7eeSOleksandr Andrushchenko msleep(10); 783c575b7eeSOleksandr Andrushchenko 784c575b7eeSOleksandr Andrushchenko if (!to) { 785c575b7eeSOleksandr Andrushchenko unsigned int state; 786c575b7eeSOleksandr Andrushchenko 787c575b7eeSOleksandr Andrushchenko state = xenbus_read_unsigned(front_info->xb_dev->otherend, 788c575b7eeSOleksandr Andrushchenko "state", XenbusStateUnknown); 789c575b7eeSOleksandr Andrushchenko DRM_ERROR("Backend state is %s while removing driver\n", 790c575b7eeSOleksandr Andrushchenko xenbus_strstate(state)); 791c575b7eeSOleksandr Andrushchenko } 792c575b7eeSOleksandr Andrushchenko 793c575b7eeSOleksandr Andrushchenko xen_drm_drv_fini(front_info); 794c575b7eeSOleksandr Andrushchenko xenbus_frontend_closed(dev); 795c575b7eeSOleksandr Andrushchenko return 0; 796c575b7eeSOleksandr Andrushchenko } 797c575b7eeSOleksandr Andrushchenko 798c575b7eeSOleksandr Andrushchenko static const struct xenbus_device_id xen_driver_ids[] = { 799c575b7eeSOleksandr Andrushchenko { XENDISPL_DRIVER_NAME }, 800c575b7eeSOleksandr Andrushchenko { "" } 801c575b7eeSOleksandr Andrushchenko }; 802c575b7eeSOleksandr Andrushchenko 803c575b7eeSOleksandr Andrushchenko static struct xenbus_driver xen_driver = { 804c575b7eeSOleksandr Andrushchenko .ids = xen_driver_ids, 805c575b7eeSOleksandr Andrushchenko .probe = xen_drv_probe, 806c575b7eeSOleksandr Andrushchenko .remove = xen_drv_remove, 807c575b7eeSOleksandr Andrushchenko .otherend_changed = displback_changed, 808c575b7eeSOleksandr Andrushchenko }; 809c575b7eeSOleksandr Andrushchenko 810c575b7eeSOleksandr Andrushchenko static int __init xen_drv_init(void) 811c575b7eeSOleksandr Andrushchenko { 812c575b7eeSOleksandr Andrushchenko /* At the moment we only support case with XEN_PAGE_SIZE == PAGE_SIZE */ 813c575b7eeSOleksandr Andrushchenko if (XEN_PAGE_SIZE != PAGE_SIZE) { 814c575b7eeSOleksandr Andrushchenko DRM_ERROR(XENDISPL_DRIVER_NAME ": different kernel and Xen page sizes are not supported: XEN_PAGE_SIZE (%lu) != PAGE_SIZE (%lu)\n", 815c575b7eeSOleksandr Andrushchenko XEN_PAGE_SIZE, PAGE_SIZE); 816c575b7eeSOleksandr Andrushchenko return -ENODEV; 817c575b7eeSOleksandr Andrushchenko } 818c575b7eeSOleksandr Andrushchenko 819c575b7eeSOleksandr Andrushchenko if (!xen_domain()) 820c575b7eeSOleksandr Andrushchenko return -ENODEV; 821c575b7eeSOleksandr Andrushchenko 822c575b7eeSOleksandr Andrushchenko if (!xen_has_pv_devices()) 823c575b7eeSOleksandr Andrushchenko return -ENODEV; 824c575b7eeSOleksandr Andrushchenko 825c575b7eeSOleksandr Andrushchenko DRM_INFO("Registering XEN PV " XENDISPL_DRIVER_NAME "\n"); 826c575b7eeSOleksandr Andrushchenko return xenbus_register_frontend(&xen_driver); 827c575b7eeSOleksandr Andrushchenko } 828c575b7eeSOleksandr Andrushchenko 829c575b7eeSOleksandr Andrushchenko static void __exit xen_drv_fini(void) 830c575b7eeSOleksandr Andrushchenko { 831c575b7eeSOleksandr Andrushchenko DRM_INFO("Unregistering XEN PV " XENDISPL_DRIVER_NAME "\n"); 832c575b7eeSOleksandr Andrushchenko xenbus_unregister_driver(&xen_driver); 833c575b7eeSOleksandr Andrushchenko } 834c575b7eeSOleksandr Andrushchenko 835c575b7eeSOleksandr Andrushchenko module_init(xen_drv_init); 836c575b7eeSOleksandr Andrushchenko module_exit(xen_drv_fini); 837c575b7eeSOleksandr Andrushchenko 838c575b7eeSOleksandr Andrushchenko MODULE_DESCRIPTION("Xen para-virtualized display device frontend"); 839c575b7eeSOleksandr Andrushchenko MODULE_LICENSE("GPL"); 840c575b7eeSOleksandr Andrushchenko MODULE_ALIAS("xen:" XENDISPL_DRIVER_NAME); 841