382 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 <drm/drm_atomic.h>12#include <drm/drm_atomic_helper.h>13#include <drm/drm_drv.h>14#include <drm/drm_fourcc.h>15#include <drm/drm_framebuffer.h>16#include <drm/drm_gem.h>17#include <drm/drm_gem_atomic_helper.h>18#include <drm/drm_gem_framebuffer_helper.h>19#include <drm/drm_probe_helper.h>20#include <drm/drm_vblank.h>21 22#include "xen_drm_front.h"23#include "xen_drm_front_conn.h"24#include "xen_drm_front_kms.h"25 26/*27 * Timeout in ms to wait for frame done event from the backend:28 * must be a bit more than IO time-out29 */30#define FRAME_DONE_TO_MS (XEN_DRM_FRONT_WAIT_BACK_MS + 100)31 32static struct xen_drm_front_drm_pipeline *33to_xen_drm_pipeline(struct drm_simple_display_pipe *pipe)34{35 return container_of(pipe, struct xen_drm_front_drm_pipeline, pipe);36}37 38static void fb_destroy(struct drm_framebuffer *fb)39{40 struct xen_drm_front_drm_info *drm_info = fb->dev->dev_private;41 int idx;42 43 if (drm_dev_enter(fb->dev, &idx)) {44 xen_drm_front_fb_detach(drm_info->front_info,45 xen_drm_front_fb_to_cookie(fb));46 drm_dev_exit(idx);47 }48 drm_gem_fb_destroy(fb);49}50 51static const struct drm_framebuffer_funcs fb_funcs = {52 .destroy = fb_destroy,53};54 55static struct drm_framebuffer *56fb_create(struct drm_device *dev, struct drm_file *filp,57 const struct drm_mode_fb_cmd2 *mode_cmd)58{59 struct xen_drm_front_drm_info *drm_info = dev->dev_private;60 struct drm_framebuffer *fb;61 struct drm_gem_object *gem_obj;62 int ret;63 64 fb = drm_gem_fb_create_with_funcs(dev, filp, mode_cmd, &fb_funcs);65 if (IS_ERR(fb))66 return fb;67 68 gem_obj = fb->obj[0];69 70 ret = xen_drm_front_fb_attach(drm_info->front_info,71 xen_drm_front_dbuf_to_cookie(gem_obj),72 xen_drm_front_fb_to_cookie(fb),73 fb->width, fb->height,74 fb->format->format);75 if (ret < 0) {76 DRM_ERROR("Back failed to attach FB %p: %d\n", fb, ret);77 goto fail;78 }79 80 return fb;81 82fail:83 drm_gem_fb_destroy(fb);84 return ERR_PTR(ret);85}86 87static const struct drm_mode_config_funcs mode_config_funcs = {88 .fb_create = fb_create,89 .atomic_check = drm_atomic_helper_check,90 .atomic_commit = drm_atomic_helper_commit,91};92 93static void send_pending_event(struct xen_drm_front_drm_pipeline *pipeline)94{95 struct drm_crtc *crtc = &pipeline->pipe.crtc;96 struct drm_device *dev = crtc->dev;97 unsigned long flags;98 99 spin_lock_irqsave(&dev->event_lock, flags);100 if (pipeline->pending_event)101 drm_crtc_send_vblank_event(crtc, pipeline->pending_event);102 pipeline->pending_event = NULL;103 spin_unlock_irqrestore(&dev->event_lock, flags);104}105 106static void display_enable(struct drm_simple_display_pipe *pipe,107 struct drm_crtc_state *crtc_state,108 struct drm_plane_state *plane_state)109{110 struct xen_drm_front_drm_pipeline *pipeline =111 to_xen_drm_pipeline(pipe);112 struct drm_crtc *crtc = &pipe->crtc;113 struct drm_framebuffer *fb = plane_state->fb;114 int ret, idx;115 116 if (!drm_dev_enter(pipe->crtc.dev, &idx))117 return;118 119 ret = xen_drm_front_mode_set(pipeline, crtc->x, crtc->y,120 fb->width, fb->height,121 fb->format->cpp[0] * 8,122 xen_drm_front_fb_to_cookie(fb));123 124 if (ret) {125 DRM_ERROR("Failed to enable display: %d\n", ret);126 pipeline->conn_connected = false;127 }128 129 drm_dev_exit(idx);130}131 132static void display_disable(struct drm_simple_display_pipe *pipe)133{134 struct xen_drm_front_drm_pipeline *pipeline =135 to_xen_drm_pipeline(pipe);136 int ret = 0, idx;137 138 if (drm_dev_enter(pipe->crtc.dev, &idx)) {139 ret = xen_drm_front_mode_set(pipeline, 0, 0, 0, 0, 0,140 xen_drm_front_fb_to_cookie(NULL));141 drm_dev_exit(idx);142 }143 if (ret)144 DRM_ERROR("Failed to disable display: %d\n", ret);145 146 /* Make sure we can restart with enabled connector next time */147 pipeline->conn_connected = true;148 149 /* release stalled event if any */150 send_pending_event(pipeline);151}152 153void xen_drm_front_kms_on_frame_done(struct xen_drm_front_drm_pipeline *pipeline,154 u64 fb_cookie)155{156 /*157 * This runs in interrupt context, e.g. under158 * drm_info->front_info->io_lock, so we cannot call _sync version159 * to cancel the work160 */161 cancel_delayed_work(&pipeline->pflip_to_worker);162 163 send_pending_event(pipeline);164}165 166static void pflip_to_worker(struct work_struct *work)167{168 struct delayed_work *delayed_work = to_delayed_work(work);169 struct xen_drm_front_drm_pipeline *pipeline =170 container_of(delayed_work,171 struct xen_drm_front_drm_pipeline,172 pflip_to_worker);173 174 DRM_ERROR("Frame done timed-out, releasing");175 send_pending_event(pipeline);176}177 178static bool display_send_page_flip(struct drm_simple_display_pipe *pipe,179 struct drm_plane_state *old_plane_state)180{181 struct drm_plane_state *plane_state =182 drm_atomic_get_new_plane_state(old_plane_state->state,183 &pipe->plane);184 185 /*186 * If old_plane_state->fb is NULL and plane_state->fb is not,187 * then this is an atomic commit which will enable display.188 * If old_plane_state->fb is not NULL and plane_state->fb is,189 * then this is an atomic commit which will disable display.190 * Ignore these and do not send page flip as this framebuffer will be191 * sent to the backend as a part of display_set_config call.192 */193 if (old_plane_state->fb && plane_state->fb) {194 struct xen_drm_front_drm_pipeline *pipeline =195 to_xen_drm_pipeline(pipe);196 struct xen_drm_front_drm_info *drm_info = pipeline->drm_info;197 int ret;198 199 schedule_delayed_work(&pipeline->pflip_to_worker,200 msecs_to_jiffies(FRAME_DONE_TO_MS));201 202 ret = xen_drm_front_page_flip(drm_info->front_info,203 pipeline->index,204 xen_drm_front_fb_to_cookie(plane_state->fb));205 if (ret) {206 DRM_ERROR("Failed to send page flip request to backend: %d\n", ret);207 208 pipeline->conn_connected = false;209 /*210 * Report the flip not handled, so pending event is211 * sent, unblocking user-space.212 */213 return false;214 }215 /*216 * Signal that page flip was handled, pending event will be sent217 * on frame done event from the backend.218 */219 return true;220 }221 222 return false;223}224 225static int display_check(struct drm_simple_display_pipe *pipe,226 struct drm_plane_state *plane_state,227 struct drm_crtc_state *crtc_state)228{229 /*230 * Xen doesn't initialize vblanking via drm_vblank_init(), so231 * DRM helpers assume that it doesn't handle vblanking and start232 * sending out fake VBLANK events automatically.233 *234 * As xen contains it's own logic for sending out VBLANK events235 * in send_pending_event(), disable no_vblank (i.e., the xen236 * driver has vblanking support).237 */238 crtc_state->no_vblank = false;239 240 return 0;241}242 243static void display_update(struct drm_simple_display_pipe *pipe,244 struct drm_plane_state *old_plane_state)245{246 struct xen_drm_front_drm_pipeline *pipeline =247 to_xen_drm_pipeline(pipe);248 struct drm_crtc *crtc = &pipe->crtc;249 struct drm_pending_vblank_event *event;250 int idx;251 252 event = crtc->state->event;253 if (event) {254 struct drm_device *dev = crtc->dev;255 unsigned long flags;256 257 WARN_ON(pipeline->pending_event);258 259 spin_lock_irqsave(&dev->event_lock, flags);260 crtc->state->event = NULL;261 262 pipeline->pending_event = event;263 spin_unlock_irqrestore(&dev->event_lock, flags);264 }265 266 if (!drm_dev_enter(pipe->crtc.dev, &idx)) {267 send_pending_event(pipeline);268 return;269 }270 271 /*272 * Send page flip request to the backend *after* we have event cached273 * above, so on page flip done event from the backend we can274 * deliver it and there is no race condition between this code and275 * event from the backend.276 * If this is not a page flip, e.g. no flip done event from the backend277 * is expected, then send now.278 */279 if (!display_send_page_flip(pipe, old_plane_state))280 send_pending_event(pipeline);281 282 drm_dev_exit(idx);283}284 285static enum drm_mode_status286display_mode_valid(struct drm_simple_display_pipe *pipe,287 const struct drm_display_mode *mode)288{289 struct xen_drm_front_drm_pipeline *pipeline =290 container_of(pipe, struct xen_drm_front_drm_pipeline,291 pipe);292 293 if (mode->hdisplay != pipeline->width)294 return MODE_ERROR;295 296 if (mode->vdisplay != pipeline->height)297 return MODE_ERROR;298 299 return MODE_OK;300}301 302static const struct drm_simple_display_pipe_funcs display_funcs = {303 .mode_valid = display_mode_valid,304 .enable = display_enable,305 .disable = display_disable,306 .check = display_check,307 .update = display_update,308};309 310static int display_pipe_init(struct xen_drm_front_drm_info *drm_info,311 int index, struct xen_drm_front_cfg_connector *cfg,312 struct xen_drm_front_drm_pipeline *pipeline)313{314 struct drm_device *dev = drm_info->drm_dev;315 const u32 *formats;316 int format_count;317 int ret;318 319 pipeline->drm_info = drm_info;320 pipeline->index = index;321 pipeline->height = cfg->height;322 pipeline->width = cfg->width;323 324 INIT_DELAYED_WORK(&pipeline->pflip_to_worker, pflip_to_worker);325 326 ret = xen_drm_front_conn_init(drm_info, &pipeline->conn);327 if (ret)328 return ret;329 330 formats = xen_drm_front_conn_get_formats(&format_count);331 332 return drm_simple_display_pipe_init(dev, &pipeline->pipe,333 &display_funcs, formats,334 format_count, NULL,335 &pipeline->conn);336}337 338int xen_drm_front_kms_init(struct xen_drm_front_drm_info *drm_info)339{340 struct drm_device *dev = drm_info->drm_dev;341 int i, ret;342 343 drm_mode_config_init(dev);344 345 dev->mode_config.min_width = 0;346 dev->mode_config.min_height = 0;347 dev->mode_config.max_width = 4095;348 dev->mode_config.max_height = 2047;349 dev->mode_config.funcs = &mode_config_funcs;350 351 for (i = 0; i < drm_info->front_info->cfg.num_connectors; i++) {352 struct xen_drm_front_cfg_connector *cfg =353 &drm_info->front_info->cfg.connectors[i];354 struct xen_drm_front_drm_pipeline *pipeline =355 &drm_info->pipeline[i];356 357 ret = display_pipe_init(drm_info, i, cfg, pipeline);358 if (ret) {359 drm_mode_config_cleanup(dev);360 return ret;361 }362 }363 364 drm_mode_config_reset(dev);365 drm_kms_helper_poll_init(dev);366 return 0;367}368 369void xen_drm_front_kms_fini(struct xen_drm_front_drm_info *drm_info)370{371 int i;372 373 for (i = 0; i < drm_info->front_info->cfg.num_connectors; i++) {374 struct xen_drm_front_drm_pipeline *pipeline =375 &drm_info->pipeline[i];376 377 cancel_delayed_work_sync(&pipeline->pflip_to_worker);378 379 send_pending_event(pipeline);380 }381}382