Esempio n. 1
0
int evdi_painter_request_update_ioctl(struct drm_device *drm_dev,
				      __always_unused void *data,
				      __always_unused struct drm_file *file)
{
	struct evdi_device *evdi = drm_dev->dev_private;
	struct evdi_painter *painter = evdi->painter;
	int result = 0;

	if (painter) {
		painter_lock(painter);

		if (painter->was_update_requested) {
			EVDI_WARN
			  ("(dev=%d) Update was already requested - ignoring\n",
			   evdi->dev_index);
		} else {
			if (painter->num_dirts > 0)
				result = 1;
			else
				painter->was_update_requested = true;
		}

		painter_unlock(painter);

		return result;
	} else {
		return -ENODEV;
	}
}
Esempio n. 2
0
static void evdi_painter_send_mode_changed(struct evdi_painter *painter,
					   int32_t bits_per_pixel,
					   uint32_t pixel_format)
{
	struct evdi_event_mode_changed_pending *event;

	if (painter->drm_filp) {
		event = kzalloc(sizeof(*event), GFP_KERNEL);
		event->mode_changed.base.type = DRM_EVDI_EVENT_MODE_CHANGED;
		event->mode_changed.base.length = sizeof(event->mode_changed);

		event->mode_changed.hdisplay = painter->current_mode.hdisplay;
		event->mode_changed.vdisplay = painter->current_mode.vdisplay;
		event->mode_changed.vrefresh =
			drm_mode_vrefresh(&painter->current_mode);
		event->mode_changed.bits_per_pixel = bits_per_pixel;
		event->mode_changed.pixel_format = pixel_format;

		event->base.event = &event->mode_changed.base;
		event->base.file_priv = painter->drm_filp;
#if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE
		event->base.destroy =
		    (void (*)(struct drm_pending_event *))kfree;
#endif
		evdi_painter_send_event(painter->drm_filp, &event->base.link);
	} else {
		 EVDI_WARN("Painter is not connected!");
	}
}
Esempio n. 3
0
void evdi_painter_close(struct evdi_device *evdi, struct drm_file *file)
{
	EVDI_CHECKPT();

	if (evdi->painter)
		evdi_painter_disconnect(evdi, file);
	else
		EVDI_WARN("Painter does not exist!");
}
Esempio n. 4
0
void evdi_painter_disconnect(struct evdi_device *evdi, struct drm_file *file)
{
	struct evdi_painter *painter = evdi->painter;

	EVDI_CHECKPT();

	painter_lock(painter);

	if (file != painter->drm_filp) {
		EVDI_WARN
		    ("(dev=%d) An unknown connection to %p tries to close us",
		     evdi->dev_index, file);
		EVDI_WARN(" - ignoring\n");


		painter_unlock(painter);
		return;
	}

	if (painter->new_scanout_fb) {
		drm_framebuffer_unreference(&painter->new_scanout_fb->base);
		painter->new_scanout_fb = NULL;
	}

	if (painter->scanout_fb) {
		drm_framebuffer_unreference(&painter->scanout_fb->base);
		painter->scanout_fb = NULL;
	}

	painter->is_connected = false;

	EVDI_DEBUG("(dev=%d) Disconnected from %p\n", evdi->dev_index,
		   painter->drm_filp);
	painter->drm_filp = NULL;
	evdi->dev_index = -1;

	memset(&painter->current_mode, '\0', sizeof(struct drm_display_mode));
	painter->was_update_requested = false;

	painter_unlock(painter);

	drm_helper_hpd_irq_event(evdi->ddev);
}
Esempio n. 5
0
void evdi_painter_crtc_state_notify(struct evdi_device *evdi, int state)
{
	struct evdi_painter *painter = evdi->painter;

	if (painter) {
		EVDI_DEBUG("(dev=%d) Notifying crtc state: %d\n",
			   evdi->dev_index, state);
		evdi_painter_send_crtc_state(painter, state);
	} else {
		EVDI_WARN("Painter does not exist!");
	}
}
Esempio n. 6
0
void evdi_painter_dpms_notify(struct evdi_device *evdi, int mode)
{
	struct evdi_painter *painter = evdi->painter;

	if (painter) {
		EVDI_DEBUG("(dev=%d) Notifying dpms mode: %d\n",
			   evdi->dev_index, mode);
		evdi_painter_send_dpms(painter, mode);
	} else {
		EVDI_WARN("Painter does not exist!");
	}
}
Esempio n. 7
0
static void collapse_dirty_rects(struct drm_clip_rect *rects, int *count)
{
	int i;

	EVDI_CHECKPT();
	EVDI_WARN("Not enough space for clip rects! Rects will be collapsed");

	for (i = 1; i < *count; ++i)
		expand_rect(&rects[0], &rects[i]);

	*count = 1;
}
Esempio n. 8
0
void evdi_painter_cleanup(struct evdi_device *evdi)
{
	struct evdi_painter *painter = evdi->painter;

	EVDI_CHECKPT();
	if (painter) {
		painter_lock(painter);
		kfree(painter->edid);
		painter->edid_length = 0;
		painter->edid = 0;
		painter_unlock(painter);
	} else {
		EVDI_WARN("Painter does not exist\n");
	}
}
Esempio n. 9
0
static int copy_pixels(struct evdi_framebuffer *ufb,
			char __user *buffer,
			int buf_byte_stride,
			int num_rects, struct drm_clip_rect *rects,
			int const max_x,
			int const max_y,
			struct evdi_cursor *cursor_copy)
{
	struct drm_framebuffer *fb = &ufb->base;
	struct drm_clip_rect *r;

	EVDI_CHECKPT();

	for (r = rects; r != rects + num_rects; ++r) {
		const int byte_offset = r->x1 * 4;
		const int byte_span = (r->x2 - r->x1) * 4;
		const int src_offset = fb->pitches[0] * r->y1 + byte_offset;
		const char *src = (char *)ufb->obj->vmapping + src_offset;
		const int dst_offset = buf_byte_stride * r->y1 + byte_offset;
		char __user *dst = buffer + dst_offset;
		int y = r->y2 - r->y1;

		/* rect size may correspond to previous resolution */
		if (max_x < r->x2 || max_y < r->y2) {
			EVDI_WARN("Rect size beyond expected dimensions\n");
			return -EFAULT;
		}

		EVDI_VERBOSE("copy rect %d,%d-%d,%d\n", r->x1, r->y1, r->x2,
			     r->y2);

		for (; y > 0; --y) {
			if (copy_to_user(dst, src, byte_span))
				return -EFAULT;

			src += fb->pitches[0];
			dst += buf_byte_stride;
		}
	}

	return evdi_cursor_composing_and_copy(cursor_copy,
				       ufb,
				       buffer,
				       buf_byte_stride,
				       max_x, max_y);
}
Esempio n. 10
0
static void evdi_painter_send_dpms(struct evdi_painter *painter, int mode)
{
	struct evdi_event_dpms_pending *event;

	if (painter->drm_filp) {
		event = kzalloc(sizeof(*event), GFP_KERNEL);
		event->dpms.base.type = DRM_EVDI_EVENT_DPMS;
		event->dpms.base.length = sizeof(event->dpms);
		event->dpms.mode = mode;
		event->base.event = &event->dpms.base;
		event->base.file_priv = painter->drm_filp;
#if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE
		event->base.destroy =
		    (void (*)(struct drm_pending_event *))kfree;
#endif
		evdi_painter_send_event(painter->drm_filp, &event->base.link);
	} else {
		EVDI_WARN("Painter is not connected!");
	}
}
Esempio n. 11
0
static void evdi_painter_send_update_ready(struct evdi_painter *painter)
{
	struct evdi_event_update_ready_pending *event;

	if (painter->drm_filp) {
		event = kzalloc(sizeof(*event), GFP_KERNEL);
		event->update_ready.base.type = DRM_EVDI_EVENT_UPDATE_READY;
		event->update_ready.base.length = sizeof(event->update_ready);
		event->base.event = &event->update_ready.base;
		event->base.file_priv = painter->drm_filp;
#if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE
		event->base.destroy =
		    (void (*)(struct drm_pending_event *))kfree;
#endif

		evdi_painter_send_event(painter->drm_filp, &event->base.link);
	} else {
		EVDI_WARN("Painter is not connected!");
	}
}
Esempio n. 12
0
void evdi_painter_mode_changed_notify(struct evdi_device *evdi,
				      struct drm_framebuffer *fb,
				      struct drm_display_mode *new_mode)
{
	struct evdi_painter *painter = evdi->painter;

	if (evdi_painter_replace_mode(painter, new_mode)) {
		EVDI_DEBUG(
		"(dev=%d) Notifying mode changed: %dx%d@%d; bpp %d; ",
		     evdi->dev_index, new_mode->hdisplay, new_mode->vdisplay,
		     drm_mode_vrefresh(new_mode), fb->bits_per_pixel);
		EVDI_DEBUG("pixel format %d\n", fb->pixel_format);

		evdi_painter_send_mode_changed(painter,
					       fb->bits_per_pixel,
					       fb->pixel_format);
	} else {
		EVDI_WARN("(dev=%d) Change mode duplicated - ignoring\n",
			  evdi->dev_index);
	}
}
Esempio n. 13
0
void evdi_painter_mark_dirty(struct evdi_device *evdi,
			     const struct drm_clip_rect *dirty_rect)
{
	struct drm_clip_rect rect;
	struct evdi_framebuffer *efb = NULL;
	struct evdi_painter *painter = evdi->painter;

	painter_lock(evdi->painter);
	efb = evdi->painter->scanout_fb;
	if (!efb) {
		EVDI_WARN("Skip clip rect. Scanout buffer not set.\n");
		goto unlock;
	}

	rect = evdi_framebuffer_sanitize_rect(efb, dirty_rect);

	EVDI_VERBOSE("(dev=%d) %d,%d-%d,%d\n", evdi->dev_index, rect.x1,
		     rect.y1, rect.x2, rect.y2);

	if (painter->num_dirts == MAX_DIRTS)
		merge_dirty_rects(&painter->dirty_rects[0],
				  &painter->num_dirts);

	if (painter->num_dirts == MAX_DIRTS)
		collapse_dirty_rects(&painter->dirty_rects[0],
				     &painter->num_dirts);

	memcpy(&painter->dirty_rects[painter->num_dirts], &rect, sizeof(rect));
	painter->num_dirts++;

	if (painter->was_update_requested) {
		evdi_painter_send_update_ready(painter);
		painter->was_update_requested = false;
	}

unlock:
	painter_unlock(evdi->painter);
}
Esempio n. 14
0
struct drm_clip_rect evdi_framebuffer_sanitize_rect(
				const struct evdi_framebuffer *fb,
				const struct drm_clip_rect *dirty_rect)
{
	struct drm_clip_rect rect = *dirty_rect;

	if (rect.x1 > rect.x2) {
		unsigned short tmp = rect.x2;

		EVDI_WARN("Wrong clip rect: x1 > x2\n");
		rect.x2 = rect.x1;
		rect.x1 = tmp;
	}

	if (rect.y1 > rect.y2) {
		unsigned short tmp = rect.y2;

		EVDI_WARN("Wrong clip rect: y1 > y2\n");
		rect.y2 = rect.y1;
		rect.y1 = tmp;
	}


	if (rect.x1 > fb->base.width) {
		EVDI_WARN("Wrong clip rect: x1 > fb.width\n");
		rect.x1 = fb->base.width;
	}

	if (rect.y1 > fb->base.height) {
		EVDI_WARN("Wrong clip rect: y1 > fb.height\n");
		rect.y1 = fb->base.height;
	}

	if (rect.x2 > fb->base.width) {
		EVDI_WARN("Wrong clip rect: x2 > fb.width\n");
		rect.x2 = fb->base.width;
	}

	if (rect.y2 > fb->base.height) {
		EVDI_WARN("Wrong clip rect: y2 > fb.height\n");
		rect.y2 = fb->base.height;
	}

	return rect;
}
Esempio n. 15
0
int evdi_painter_connect_ioctl(struct drm_device *drm_dev, void *data,
			       struct drm_file *file)
{
	struct evdi_device *evdi = drm_dev->dev_private;
	struct evdi_painter *painter = evdi->painter;
	struct drm_evdi_connect *cmd = data;

	EVDI_CHECKPT();
	if (painter) {
		if (cmd->connected)
			evdi_painter_connect(evdi,
					     cmd->edid,
					     cmd->edid_length,
					     file,
					     cmd->dev_index);
		else
			evdi_painter_disconnect(evdi, file);

		return 0;
	}
	EVDI_WARN("Painter does not exist!");
	return -ENODEV;
}
Esempio n. 16
0
int
evdi_painter_connect(struct evdi_device *evdi,
		     void const __user *edid_data, unsigned int edid_length,
		     struct drm_file *file, int dev_index)
{
	struct evdi_painter *painter = evdi->painter;
	struct edid *new_edid = NULL;
	int expected_edid_size = 0;

	EVDI_CHECKPT();

	if (edid_length < sizeof(struct edid)) {
		EVDI_ERROR("Edid length too small\n");
		return -EINVAL;
	}

	if (edid_length > MAX_EDID_SIZE) {
		EVDI_ERROR("Edid length too large\n");
		return -EINVAL;
	}

	new_edid = kzalloc(edid_length, GFP_KERNEL);
	if (!new_edid)
		return -ENOMEM;

	if (copy_from_user(new_edid, edid_data, edid_length)) {
		EVDI_ERROR("(dev=%d) LSP Failed to read edid\n", dev_index);
		kfree(new_edid);
		return -EFAULT;
	}

	expected_edid_size = sizeof(struct edid) +
			     new_edid->extensions * EDID_EXT_BLOCK_SIZE;
	if (expected_edid_size != edid_length) {
		EVDI_ERROR("Wrong edid size. Expected %d but is %d\n",
			   expected_edid_size, edid_length);
		kfree(new_edid);
		return -EINVAL;
	}

	if (painter->drm_filp)
		EVDI_WARN("(dev=%d) Double connect - replacing %p with %p\n",
			  dev_index, painter->drm_filp, file);

	EVDI_DEBUG("(dev=%d) Connected with %p\n", evdi->dev_index,
		   painter->drm_filp);

	painter_lock(painter);

	evdi->dev_index = dev_index;
	painter->drm_filp = file;
	kfree(painter->edid);
	painter->edid_length = edid_length;
	painter->edid = new_edid;
	painter->is_connected = true;

	painter_unlock(painter);

	drm_helper_hpd_irq_event(evdi->ddev);
	drm_helper_resume_force_mode(evdi->ddev);

	return 0;
}
Esempio n. 17
0
int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data,
			       __always_unused struct drm_file *file)
{
	struct evdi_device *evdi = drm_dev->dev_private;
	struct evdi_painter *painter = evdi->painter;
	struct drm_evdi_grabpix *cmd = data;
	struct drm_framebuffer *fb = NULL;
	struct evdi_framebuffer *efb = NULL;
	struct evdi_cursor *cursor_copy = NULL;
	int err = 0;

	EVDI_CHECKPT();

	if (!painter)
		return -ENODEV;

	mutex_lock(&drm_dev->struct_mutex);
	if (evdi_cursor_alloc(&cursor_copy) == 0)
		evdi_cursor_copy(cursor_copy, evdi->cursor);
	mutex_unlock(&drm_dev->struct_mutex);

	painter_lock(painter);

	efb = painter->scanout_fb;

	if (!efb) {
		EVDI_ERROR("Scanout buffer not set\n");
		err = -EAGAIN;
		goto unlock;
	}

	if (painter->was_update_requested) {
		EVDI_WARN("(dev=%d) Update ready not sent,",
			  evdi->dev_index);
		EVDI_WARN(" but pixels are grabbed.\n");
	}

	fb = &efb->base;
	if (!efb->obj->vmapping) {
		if (evdi_gem_vmap(efb->obj) == -ENOMEM) {
			EVDI_ERROR("Failed to map scanout buffer\n");
			err = -EFAULT;
			goto unlock;
		}
		if (!efb->obj->vmapping) {
			EVDI_ERROR("Failed to map scanout buffer\n");
			err = -EFAULT;
			goto unlock;
		}
	}

	if (cmd->buf_width != fb->width ||
		cmd->buf_height != fb->height) {
		EVDI_ERROR("Invalid buffer dimension\n");
		err = -EINVAL;
		goto unlock;
	}

	if (cmd->num_rects < 1) {
		EVDI_ERROR("No space for clip rects\n");
		err = -EINVAL;
		goto unlock;
	}

	if (cmd->mode == EVDI_GRABPIX_MODE_DIRTY) {
		if (painter->num_dirts < 0) {
			err = -EAGAIN;
			goto unlock;
		}
		merge_dirty_rects(&painter->dirty_rects[0],
				  &painter->num_dirts);
		if (painter->num_dirts > cmd->num_rects)
			collapse_dirty_rects(&painter->dirty_rects[0],
						 &painter->num_dirts);

		cmd->num_rects = painter->num_dirts;

		if (copy_to_user(cmd->rects, painter->dirty_rects,
			cmd->num_rects * sizeof(cmd->rects[0])))
			err = -EFAULT;
		else
			err = copy_pixels(efb,
					  cmd->buffer,
					  cmd->buf_byte_stride,
					  painter->num_dirts,
					  painter->dirty_rects,
					  cmd->buf_width,
					  cmd->buf_height,
					  cursor_copy);

		painter->num_dirts = 0;
	}
unlock:
	painter_unlock(painter);
	if (cursor_copy)
		evdi_cursor_free(cursor_copy);

	return err;
}