brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · dfa78a4 Raw
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