Beispiel #1
0
int evdi_driver_load(struct drm_device *dev,
                     __always_unused unsigned long flags)
{
    struct platform_device *platdev = NULL;
    struct evdi_device *evdi;
    int ret;

    EVDI_CHECKPT();
    evdi = kzalloc(sizeof(struct evdi_device), GFP_KERNEL);
    if (!evdi)
        return -ENOMEM;

    evdi->ddev = dev;
    dev->dev_private = evdi;

    ret =  evdi_cursor_alloc(&evdi->cursor);
    if (ret)
        goto err;

    EVDI_CHECKPT();
    ret = evdi_modeset_init(dev);

    if (ret)
        goto err;

    ret = evdi_fbdev_init(dev);
    if (ret)
        goto err;

    ret = drm_vblank_init(dev, 1);
    if (ret)
        goto err_fb;

    ret = evdi_painter_init(evdi);
    if (ret)
        goto err_fb;

    evdi_stats_init(evdi);

    drm_kms_helper_poll_init(dev);

    platdev = to_platform_device(dev->dev);
    platform_set_drvdata(platdev, dev);

    return 0;

err_fb:
    evdi_fbdev_cleanup(dev);
err:
    kfree(evdi);
    EVDI_ERROR("%d\n", ret);
    if (evdi->cursor)
        evdi_cursor_free(evdi->cursor);
    return ret;
}
Beispiel #2
0
int evdi_driver_unload(struct drm_device *dev)
{
    struct evdi_device *evdi = dev->dev_private;

    EVDI_CHECKPT();

    drm_vblank_cleanup(dev);
    drm_kms_helper_poll_fini(dev);
#if KERNEL_VERSION(4, 8, 0) <= LINUX_VERSION_CODE

#elif KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE
    drm_connector_unregister_all(dev);
#else
    drm_connector_unplug_all(dev);
#endif

    evdi_fbdev_unplug(dev);
    if (evdi->cursor)
        evdi_cursor_free(evdi->cursor);
    evdi_painter_cleanup(evdi);
    evdi_stats_cleanup(evdi);
    evdi_fbdev_cleanup(dev);
    evdi_modeset_cleanup(dev);

    kfree(evdi);
    return 0;
}
Beispiel #3
0
void evdi_driver_preclose(struct drm_device *drm_dev, struct drm_file *file)
{
    struct evdi_device *evdi = drm_dev->dev_private;

    EVDI_CHECKPT();
    if (evdi)
        evdi_painter_close(evdi, file);
}
Beispiel #4
0
static void evdi_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
{
  struct evdi_fbdev *ufbdev = info->par;

  EVDI_CHECKPT();

  evdi_handle_damage(&ufbdev->ufb, region->dx, region->dy, region->width, region->height);
}
Beispiel #5
0
static void evdi_fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
  struct evdi_fbdev *ufbdev = info->par;

  EVDI_CHECKPT();

  evdi_handle_damage(&ufbdev->ufb, image->dx, image->dy, image->width, image->height);
}
Beispiel #6
0
static void evdi_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
  struct evdi_fbdev *ufbdev = info->par;

  EVDI_CHECKPT();

  evdi_handle_damage(&ufbdev->ufb, rect->dx, rect->dy, rect->width, rect->height);
}
Beispiel #7
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!");
}
Beispiel #8
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;
}
Beispiel #9
0
int evdi_painter_init(struct evdi_device *dev)
{
	EVDI_CHECKPT();
	dev->painter = kzalloc(sizeof(*dev->painter), GFP_KERNEL);
	if (dev->painter) {
		mutex_init(&dev->painter->lock);
		dev->painter->edid = NULL;
		dev->painter->edid_length = 0;
		return 0;
	}
	return -ENOMEM;
}
Beispiel #10
0
static void evdi_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
	struct evdi_framebuffer *ufb = to_evdi_fb(fb);

	EVDI_CHECKPT();
	if (ufb->obj->vmapping)
		evdi_gem_vunmap(ufb->obj);

	if (ufb->obj)
		drm_gem_object_unreference_unlocked(&ufb->obj->base);

	drm_framebuffer_cleanup(fb);
	kfree(ufb);
}
Beispiel #11
0
static int evdi_user_framebuffer_dirty(struct drm_framebuffer *fb,
				       __always_unused struct drm_file *file,
				       __always_unused unsigned int flags,
				       __always_unused unsigned int color,
				       struct drm_clip_rect *clips,
				       unsigned int num_clips)
{
	struct evdi_framebuffer *ufb = to_evdi_fb(fb);
	struct drm_device *dev = ufb->base.dev;
	struct evdi_device *evdi = dev->dev_private;
	int i;
	int ret = 0;

	EVDI_CHECKPT();
	drm_modeset_lock_all(fb->dev);

	if (!ufb->active)
		goto unlock;

	if (ufb->obj->base.import_attach) {
		ret =
			dma_buf_begin_cpu_access(
					ufb->obj->base.import_attach->dmabuf,
#if KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE
					0, ufb->obj->base.size,
#endif
					DMA_FROM_DEVICE);
		if (ret)
			goto unlock;
	}

	for (i = 0; i < num_clips; i++) {
		ret = evdi_handle_damage(ufb, clips[i].x1, clips[i].y1,
					 clips[i].x2 - clips[i].x1,
					 clips[i].y2 - clips[i].y1);
		if (ret)
			goto unlock;
	}

	if (ufb->obj->base.import_attach)
		dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf,
#if KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE
				       0, ufb->obj->base.size,
#endif
				       DMA_FROM_DEVICE);
	atomic_add(1, &evdi->frame_count);
 unlock:
	drm_modeset_unlock_all(fb->dev);
	return ret;
}
Beispiel #12
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");
	}
}
Beispiel #13
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);
}
Beispiel #14
0
int evdi_handle_damage(struct evdi_framebuffer *fb,
                       int x,
                       int y,
                       int width,
                       int height)
{
  struct drm_device *dev = fb->base.dev;
  struct evdi_device *evdi = dev->dev_private;

  EVDI_CHECKPT();

  if (!fb->obj->vmapping) {
    if (evdi_gem_vmap(fb->obj) == -ENOMEM) {
      DRM_ERROR("failed to vmap fb\n");
      return 0;
    }
    if (!fb->obj->vmapping) {
      DRM_ERROR("failed to vmapping\n");
      return 0;
    }
  }

  {
    const int line_offset = fb->base.pitches[0] * y;
    const int byte_offset = line_offset + (x * 4); //RG24
    const char* pix = (char*)fb->obj->vmapping + byte_offset;

    EVDI_VERBOSE("%p %d,%d-%dx%d %02x%02x%02x%02x%02x%02x%02x%02x\n",
               fb, x, y, width, height,
               ((int)(pix[0])&0xff),
               ((int)(pix[1])&0xff),
               ((int)(pix[2])&0xff),
               ((int)(pix[3])&0xff),
               ((int)(pix[4])&0xff),
               ((int)(pix[5])&0xff),
               ((int)(pix[6])&0xff),
               ((int)(pix[7])&0xff));
  }

  {
    struct drm_clip_rect rect = { x, y, x + width, y + height };
    evdi_painter_mark_dirty(evdi, fb, &rect);
  }

  return 0;
}
Beispiel #15
0
static int evdi_user_framebuffer_dirty(struct drm_framebuffer *fb,
                                       struct drm_file *file,
                                       unsigned flags,
                                       unsigned color,
                                       struct drm_clip_rect *clips,
                                       unsigned num_clips)
{
  struct evdi_framebuffer *ufb = to_evdi_fb(fb);
  struct drm_device *dev = ufb->base.dev;
  struct evdi_device *evdi = dev->dev_private;
  int i;
  int ret = 0;

  EVDI_CHECKPT();
  drm_modeset_lock_all(fb->dev);

  if (ufb->obj->base.import_attach) {
    ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf,
                                   0, ufb->obj->base.size,
                                   DMA_FROM_DEVICE);
    if (ret) {
      goto unlock;
    }
  }

  for (i = 0; i < num_clips; i++) {
    ret = evdi_handle_damage(ufb, clips[i].x1, clips[i].y1,
                             clips[i].x2 - clips[i].x1,
                             clips[i].y2 - clips[i].y1);
    if (ret) {
      goto unlock;
    }
  }

  if (ufb->obj->base.import_attach) {
    dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf,
                           0,
                           ufb->obj->base.size,
                           DMA_FROM_DEVICE);
  }
  atomic_add(1, &evdi->frame_count);
unlock:
  drm_modeset_unlock_all(fb->dev);
  return ret;
}
Beispiel #16
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);
}
Beispiel #17
0
int evdi_handle_damage(struct evdi_framebuffer *fb,
		       int x, int y, int width, int height)
{
	const struct drm_clip_rect dirty_rect = { x, y, x + width, y + height };
	const struct drm_clip_rect rect =
		evdi_framebuffer_sanitize_rect(fb, &dirty_rect);
	struct drm_device *dev = fb->base.dev;
	struct evdi_device *evdi = dev->dev_private;

	EVDI_CHECKPT();

	if (!fb->active)
		return 0;
	evdi_set_new_scanout_buffer(evdi, fb);
	evdi_flip_scanout_buffer(evdi);
	evdi_painter_mark_dirty(evdi, &rect);

	return 0;
}
Beispiel #18
0
u8 *evdi_painter_get_edid_copy(struct evdi_device *evdi)
{
	u8 *block = NULL;

	EVDI_CHECKPT();

	painter_lock(evdi->painter);
	if (evdi_painter_is_connected(evdi) &&
		evdi->painter->edid &&
		evdi->painter->edid_length) {
		block = kmalloc(evdi->painter->edid_length, GFP_KERNEL);
		if (block) {
			memcpy(block,
			       evdi->painter->edid,
			       evdi->painter->edid_length);
			EVDI_DEBUG("(dev=%d) %02x %02x %02x\n", evdi->dev_index,
				   block[0], block[1], block[2]);
		}
	}
	painter_unlock(evdi->painter);
	return block;
}
Beispiel #19
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;
}
Beispiel #20
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;
}
Beispiel #21
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;
}
Beispiel #22
0
static void painter_unlock(struct evdi_painter *painter)
{
	EVDI_CHECKPT();
	mutex_unlock(&painter->lock);
}