static void virtio_gpu_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(plane->crtc); struct virtio_gpu_framebuffer *vgfb; struct virtio_gpu_object *bo; uint32_t handle; if (plane->fb) { vgfb = to_virtio_gpu_framebuffer(plane->fb); bo = gem_to_virtio_gpu_obj(vgfb->obj); handle = bo->hw_res_handle; } else { handle = 0; } DRM_DEBUG("handle 0x%x, crtc %dx%d+%d+%d\n", handle, plane->state->crtc_w, plane->state->crtc_h, plane->state->crtc_x, plane->state->crtc_y); virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, plane->state->crtc_w, plane->state->crtc_h, plane->state->crtc_x, plane->state->crtc_y); }
static void virtio_gpu_primary_plane_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct drm_device *dev = plane->dev; struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_output *output = NULL; struct virtio_gpu_framebuffer *vgfb; struct virtio_gpu_object *bo; uint32_t handle; if (plane->state->crtc) output = drm_crtc_to_virtio_gpu_output(plane->state->crtc); if (old_state->crtc) output = drm_crtc_to_virtio_gpu_output(old_state->crtc); if (WARN_ON(!output)) return; if (plane->state->fb && output->enabled) { vgfb = to_virtio_gpu_framebuffer(plane->state->fb); bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); handle = bo->hw_res_handle; if (bo->dumb) { virtio_gpu_cmd_transfer_to_host_2d (vgdev, bo, 0, cpu_to_le32(plane->state->src_w >> 16), cpu_to_le32(plane->state->src_h >> 16), cpu_to_le32(plane->state->src_x >> 16), cpu_to_le32(plane->state->src_y >> 16), NULL); }
void virtio_gpu_gem_free_object(struct drm_gem_object *gem_obj) { struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(gem_obj); if (obj) virtio_gpu_object_unref(&obj); }
int virtio_gpu_framebuffer_init(struct drm_device *dev, struct virtio_gpu_framebuffer *vgfb, const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object *obj) { int ret; struct virtio_gpu_object *bo; vgfb->base.obj[0] = obj; bo = gem_to_virtio_gpu_obj(obj); drm_helper_mode_fill_fb_struct(dev, &vgfb->base, mode_cmd); ret = drm_framebuffer_init(dev, &vgfb->base, &virtio_gpu_fb_funcs); if (ret) { vgfb->base.obj[0] = NULL; return ret; } spin_lock_init(&vgfb->dirty_lock); vgfb->x1 = vgfb->y1 = INT_MAX; vgfb->x2 = vgfb->y2 = 0; return 0; }
static int virtio_gpu_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y) { struct virtio_gpu_device *vgdev = crtc->dev->dev_private; struct virtio_gpu_output *output = container_of(crtc, struct virtio_gpu_output, crtc); struct drm_gem_object *gobj = NULL; struct virtio_gpu_object *qobj = NULL; struct virtio_gpu_fence *fence = NULL; int ret = 0; if (handle == 0) { virtio_gpu_hide_cursor(vgdev, output); return 0; } /* lookup the cursor */ gobj = drm_gem_object_lookup(crtc->dev, file_priv, handle); if (gobj == NULL) return -ENOENT; qobj = gem_to_virtio_gpu_obj(gobj); if (!qobj->hw_res_handle) { ret = -EINVAL; goto out; } virtio_gpu_cmd_transfer_to_host_2d(vgdev, qobj->hw_res_handle, 0, cpu_to_le32(64), cpu_to_le32(64), 0, 0, &fence); ret = virtio_gpu_object_reserve(qobj, false); if (!ret) { reservation_object_add_excl_fence(qobj->tbo.resv, &fence->f); fence_put(&fence->f); virtio_gpu_object_unreserve(qobj); virtio_gpu_object_wait(qobj, false); } output->cursor.hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_UPDATE_CURSOR); output->cursor.resource_id = cpu_to_le32(qobj->hw_res_handle); output->cursor.hot_x = cpu_to_le32(hot_x); output->cursor.hot_y = cpu_to_le32(hot_y); virtio_gpu_cursor_ping(vgdev, output); ret = 0; out: drm_gem_object_unreference_unlocked(gobj); return ret; }
int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset_p) { struct drm_gem_object *gobj; struct virtio_gpu_object *obj; BUG_ON(!offset_p); gobj = drm_gem_object_lookup(file_priv, handle); if (gobj == NULL) return -ENOENT; obj = gem_to_virtio_gpu_obj(gobj); *offset_p = virtio_gpu_object_mmap_offset(obj); drm_gem_object_unreference_unlocked(gobj); return 0; }
static int virtio_gpu_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct virtio_gpu_device *vgdev = crtc->dev->dev_private; struct virtio_gpu_output *output = container_of(crtc, struct virtio_gpu_output, crtc); struct drm_plane *plane = crtc->primary; struct virtio_gpu_framebuffer *vgfb; struct virtio_gpu_object *bo; unsigned long irqflags; uint32_t handle; plane->fb = fb; vgfb = to_virtio_gpu_framebuffer(plane->fb); bo = gem_to_virtio_gpu_obj(vgfb->obj); handle = bo->hw_res_handle; DRM_DEBUG("handle 0x%x%s, crtc %dx%d\n", handle, bo->dumb ? ", dumb" : "", crtc->mode.hdisplay, crtc->mode.vdisplay); if (bo->dumb) { virtio_gpu_cmd_transfer_to_host_2d (vgdev, handle, 0, cpu_to_le32(crtc->mode.hdisplay), cpu_to_le32(crtc->mode.vdisplay), 0, 0, NULL); } virtio_gpu_cmd_set_scanout(vgdev, output->index, handle, crtc->mode.hdisplay, crtc->mode.vdisplay, 0, 0); virtio_gpu_cmd_resource_flush(vgdev, handle, 0, 0, crtc->mode.hdisplay, crtc->mode.vdisplay); if (event) { spin_lock_irqsave(&crtc->dev->event_lock, irqflags); drm_send_vblank_event(crtc->dev, -1, event); spin_unlock_irqrestore(&crtc->dev->event_lock, irqflags); } return 0; }
void virtio_gpu_gem_object_close(struct drm_gem_object *obj, struct drm_file *file) { struct virtio_gpu_device *vgdev = obj->dev->dev_private; struct virtio_gpu_fpriv *vfpriv = file->driver_priv; struct virtio_gpu_object *qobj = gem_to_virtio_gpu_obj(obj); int r; if (!vgdev->has_virgl_3d) return; r = virtio_gpu_object_reserve(qobj, false); if (r) return; virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id, qobj->hw_res_handle); virtio_gpu_object_unreserve(qobj); }
int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb, struct drm_clip_rect *clips, unsigned int num_clips) { struct virtio_gpu_device *vgdev = vgfb->base.dev->dev_private; struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); struct drm_clip_rect norect; struct drm_clip_rect *clips_ptr; int left, right, top, bottom; int i; int inc = 1; if (!num_clips) { num_clips = 1; clips = &norect; norect.x1 = norect.y1 = 0; norect.x2 = vgfb->base.width; norect.y2 = vgfb->base.height; } left = clips->x1; right = clips->x2; top = clips->y1; bottom = clips->y2; /* skip the first clip rect */ for (i = 1, clips_ptr = clips + inc; i < num_clips; i++, clips_ptr += inc) { left = min_t(int, left, (int)clips_ptr->x1); right = max_t(int, right, (int)clips_ptr->x2); top = min_t(int, top, (int)clips_ptr->y1); bottom = max_t(int, bottom, (int)clips_ptr->y2); } if (obj->dumb) return virtio_gpu_dirty_update(vgfb, false, left, top, right - left, bottom - top); virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, left, top, right - left, bottom - top); return 0; }
int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args) { struct virtio_gpu_device *vgdev = dev->dev_private; struct drm_gem_object *gobj; struct virtio_gpu_object *obj; int ret; uint32_t pitch; uint32_t resid; uint32_t format; pitch = args->width * ((args->bpp + 1) / 8); args->size = pitch * args->height; args->size = ALIGN(args->size, PAGE_SIZE); ret = virtio_gpu_gem_create(file_priv, dev, args->size, &gobj, &args->handle); if (ret) goto fail; format = virtio_gpu_translate_format(DRM_FORMAT_XRGB8888); virtio_gpu_resource_id_get(vgdev, &resid); virtio_gpu_cmd_create_resource(vgdev, resid, format, args->width, args->height); /* attach the object to the resource */ obj = gem_to_virtio_gpu_obj(gobj); ret = virtio_gpu_object_attach(vgdev, obj, resid, NULL); if (ret) goto fail; obj->dumb = true; args->pitch = pitch; return ret; fail: return ret; }
static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, bool store, int x, int y, int width, int height) { struct drm_device *dev = fb->base.dev; struct virtio_gpu_device *vgdev = dev->dev_private; bool store_for_later = false; int bpp = fb->base.format->cpp[0]; int x2, y2; unsigned long flags; struct virtio_gpu_object *obj = gem_to_virtio_gpu_obj(fb->base.obj[0]); if ((width <= 0) || (x + width > fb->base.width) || (y + height > fb->base.height)) { DRM_DEBUG("values out of range %dx%d+%d+%d, fb %dx%d\n", width, height, x, y, fb->base.width, fb->base.height); return -EINVAL; } /* * Can be called with pretty much any context (console output * path). If we are in atomic just store the dirty rect info * to send out the update later. * * Can't test inside spin lock. */ if (in_atomic() || store) store_for_later = true; x2 = x + width - 1; y2 = y + height - 1; spin_lock_irqsave(&fb->dirty_lock, flags); if (fb->y1 < y) y = fb->y1; if (fb->y2 > y2) y2 = fb->y2; if (fb->x1 < x) x = fb->x1; if (fb->x2 > x2) x2 = fb->x2; if (store_for_later) { fb->x1 = x; fb->x2 = x2; fb->y1 = y; fb->y2 = y2; spin_unlock_irqrestore(&fb->dirty_lock, flags); return 0; } fb->x1 = fb->y1 = INT_MAX; fb->x2 = fb->y2 = 0; spin_unlock_irqrestore(&fb->dirty_lock, flags); { uint32_t offset; uint32_t w = x2 - x + 1; uint32_t h = y2 - y + 1; offset = (y * fb->base.pitches[0]) + x * bpp; virtio_gpu_cmd_transfer_to_host_2d(vgdev, obj->hw_res_handle, offset, cpu_to_le32(w), cpu_to_le32(h), cpu_to_le32(x), cpu_to_le32(y), NULL); } virtio_gpu_cmd_resource_flush(vgdev, obj->hw_res_handle, x, y, x2 - x + 1, y2 - y + 1); return 0; }