int qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf, const struct qxl_rect *area) { int surface_id; uint32_t surface_width, surface_height; int ret; if (!surf->hw_surf_alloc) DRM_ERROR("got io update area with no hw surface\n"); if (surf->is_primary) surface_id = 0; else surface_id = surf->surface_id; surface_width = surf->surf.width; surface_height = surf->surf.height; if (area->left < 0 || area->top < 0 || area->right > surface_width || area->bottom > surface_height) { qxl_io_log(qdev, "%s: not doing area update for " "%d, (%d,%d,%d,%d) (%d,%d)\n", __func__, surface_id, area->left, area->top, area->right, area->bottom, surface_width, surface_height); return -EINVAL; } mutex_lock(&qdev->update_area_mutex); qdev->ram_header->update_area = *area; qdev->ram_header->update_surface = surface_id; ret = wait_for_io_cmd_user(qdev, 0, QXL_IO_UPDATE_AREA_ASYNC, true); mutex_unlock(&qdev->update_area_mutex); return ret; }
void qxl_display_read_client_monitors_config(struct qxl_device *qdev) { while (qxl_display_copy_rom_client_monitors_config(qdev)) { qxl_io_log(qdev, "failed crc check for client_monitors_config," " retrying\n"); } drm_helper_hpd_irq_event(qdev->ddev); }
irqreturn_t qxl_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; struct qxl_device *qdev = (struct qxl_device *)dev->dev_private; uint32_t pending; pending = xchg(&qdev->ram_header->int_pending, 0); if (!pending) return IRQ_NONE; atomic_inc(&qdev->irq_received); if (pending & QXL_INTERRUPT_DISPLAY) { atomic_inc(&qdev->irq_received_display); wake_up_all(&qdev->display_event); qxl_queue_garbage_collect(qdev, false); } if (pending & QXL_INTERRUPT_CURSOR) { atomic_inc(&qdev->irq_received_cursor); wake_up_all(&qdev->cursor_event); } if (pending & QXL_INTERRUPT_IO_CMD) { atomic_inc(&qdev->irq_received_io_cmd); wake_up_all(&qdev->io_cmd_event); } if (pending & QXL_INTERRUPT_ERROR) { /* TODO: log it, reset device (only way to exit this condition) * (do it a certain number of times, afterwards admit defeat, * to avoid endless loops). */ qdev->irq_received_error++; qxl_io_log(qdev, "%s: driver is in bug mode.\n", __func__); } if (pending & QXL_INTERRUPT_CLIENT_MONITORS_CONFIG) { qxl_io_log(qdev, "QXL_INTERRUPT_CLIENT_MONITORS_CONFIG\n"); schedule_work(&qdev->client_monitors_config_work); } qdev->ram_header->int_mask = QXL_INTERRUPT_MASK; outb(0, qdev->io_base + QXL_IO_UPDATE_IRQ); return IRQ_HANDLED; }
static bool qxl_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; struct qxl_device *qdev = dev->dev_private; qxl_io_log(qdev, "%s: (%d,%d) => (%d,%d)\n", __func__, mode->hdisplay, mode->vdisplay, adjusted_mode->hdisplay, adjusted_mode->vdisplay); return true; }
void qxl_display_read_client_monitors_config(struct qxl_device *qdev) { while (qxl_display_copy_rom_client_monitors_config(qdev)) { qxl_io_log(qdev, "failed crc check for client_monitors_config," " retrying\n"); } if (!drm_helper_hpd_irq_event(qdev->ddev)) { /* notify that the monitor configuration changed, to adjust at the arbitrary resolution */ drm_kms_helper_hotplug_event(qdev->ddev); } }
int qxl_garbage_collect(struct qxl_device *qdev) { struct qxl_release *release; uint64_t id, next_id; int i = 0; int ret; union qxl_release_info *info; while (qxl_ring_pop(qdev->release_ring, &id)) { QXL_INFO(qdev, "popped %lld\n", id); while (id) { release = qxl_release_from_id_locked(qdev, id); if (release == NULL) break; ret = qxl_release_reserve(qdev, release, false); if (ret) { qxl_io_log(qdev, "failed to reserve release on garbage collect %lld\n", id); DRM_ERROR("failed to reserve release %lld\n", id); } info = qxl_release_map(qdev, release); next_id = info->next; qxl_release_unmap(qdev, release, info); qxl_release_unreserve(qdev, release); QXL_INFO(qdev, "popped %lld, next %lld\n", id, next_id); switch (release->type) { case QXL_RELEASE_DRAWABLE: case QXL_RELEASE_SURFACE_CMD: case QXL_RELEASE_CURSOR_CMD: break; default: DRM_ERROR("unexpected release type\n"); break; } id = next_id; qxl_release_free(qdev, release); ++i; } } QXL_INFO(qdev, "%s: %lld\n", __func__, i); return i; }
void qxl_io_monitors_config(struct qxl_device *qdev) { qxl_io_log(qdev, "%s: %d [%dx%d+%d+%d]\n", __func__, qdev->monitors_config ? qdev->monitors_config->count : -1, qdev->monitors_config && qdev->monitors_config->count ? qdev->monitors_config->heads[0].width : -1, qdev->monitors_config && qdev->monitors_config->count ? qdev->monitors_config->heads[0].height : -1, qdev->monitors_config && qdev->monitors_config->count ? qdev->monitors_config->heads[0].x : -1, qdev->monitors_config && qdev->monitors_config->count ? qdev->monitors_config->heads[0].y : -1 ); wait_for_io_cmd(qdev, 0, QXL_IO_MONITORS_CONFIG_ASYNC); }
static int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) { int i; int num_monitors; uint32_t crc; num_monitors = qdev->rom->client_monitors_config.count; crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config, sizeof(qdev->rom->client_monitors_config)); if (crc != qdev->rom->client_monitors_config_crc) { qxl_io_log(qdev, "crc mismatch: have %X (%d) != %X\n", crc, sizeof(qdev->rom->client_monitors_config), qdev->rom->client_monitors_config_crc); return 1; } if (num_monitors > qdev->monitors_config->max_allowed) { DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n", qdev->monitors_config->max_allowed, num_monitors); num_monitors = qdev->monitors_config->max_allowed; } else { num_monitors = qdev->rom->client_monitors_config.count; } qxl_alloc_client_monitors_config(qdev, num_monitors); /* we copy max from the client but it isn't used */ qdev->client_monitors_config->max_allowed = qdev->monitors_config->max_allowed; for (i = 0 ; i < qdev->client_monitors_config->count ; ++i) { struct qxl_urect *c_rect = &qdev->rom->client_monitors_config.heads[i]; struct qxl_head *client_head = &qdev->client_monitors_config->heads[i]; client_head->x = c_rect->left; client_head->y = c_rect->top; client_head->width = c_rect->right - c_rect->left; client_head->height = c_rect->bottom - c_rect->top; client_head->surface_id = 0; client_head->id = i; client_head->flags = 0; DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head->width, client_head->height, client_head->x, client_head->y); } return 0; }
static void qxl_fb_dirty_flush(struct fb_info *info) { struct qxl_fbdev *qfbdev = info->par; struct qxl_device *qdev = qfbdev->qdev; struct qxl_fb_image qxl_fb_image; struct fb_image *image = &qxl_fb_image.fb_image; u32 x1, x2, y1, y2; /* TODO: hard coding 32 bpp */ int stride = qfbdev->qfb.base.pitches[0] * 4; x1 = qfbdev->dirty.x1; x2 = qfbdev->dirty.x2; y1 = qfbdev->dirty.y1; y2 = qfbdev->dirty.y2; /* * we are using a shadow draw buffer, at qdev->surface0_shadow */ qxl_io_log(qdev, "dirty x[%d, %d], y[%d, %d]", x1, x2, y1, y2); image->dx = x1; image->dy = y1; image->width = x2 - x1; image->height = y2 - y1; image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized warnings */ image->bg_color = 0; image->depth = 32; /* TODO: take from somewhere? */ image->cmap.start = 0; image->cmap.len = 0; image->cmap.red = NULL; image->cmap.green = NULL; image->cmap.blue = NULL; image->cmap.transp = NULL; image->data = qfbdev->shadow + (x1 * 4) + (stride * y1); qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL); qxl_draw_opaque_fb(&qxl_fb_image, stride); qfbdev->dirty.x1 = 0; qfbdev->dirty.x2 = 0; qfbdev->dirty.y1 = 0; qfbdev->dirty.y2 = 0; }
void qxl_alloc_client_monitors_config(struct qxl_device *qdev, unsigned count) { if (qdev->client_monitors_config && count > qdev->client_monitors_config->count) { kfree(qdev->client_monitors_config); qdev->client_monitors_config = NULL; } if (!qdev->client_monitors_config) { qdev->client_monitors_config = kzalloc( sizeof(struct qxl_monitors_config) + sizeof(struct qxl_head) * count, GFP_KERNEL); if (!qdev->client_monitors_config) { qxl_io_log(qdev, "%s: allocation failure for %u heads\n", __func__, count); return; } } qdev->client_monitors_config->count = count; }
/* * FIXME * It should not be necessary to have a special dirty() callback for fbdev. */ static int qxlfb_framebuffer_dirty(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) { struct qxl_device *qdev = fb->dev->dev_private; struct fb_info *info = qdev->fbdev_info; struct qxl_fbdev *qfbdev = info->par; struct qxl_fb_image qxl_fb_image; struct fb_image *image = &qxl_fb_image.fb_image; /* TODO: hard coding 32 bpp */ int stride = qfbdev->qfb.base.pitches[0]; /* * we are using a shadow draw buffer, at qdev->surface0_shadow */ qxl_io_log(qdev, "dirty x[%d, %d], y[%d, %d]", clips->x1, clips->x2, clips->y1, clips->y2); image->dx = clips->x1; image->dy = clips->y1; image->width = clips->x2 - clips->x1; image->height = clips->y2 - clips->y1; image->fg_color = 0xffffffff; /* unused, just to avoid uninitialized warnings */ image->bg_color = 0; image->depth = 32; /* TODO: take from somewhere? */ image->cmap.start = 0; image->cmap.len = 0; image->cmap.red = NULL; image->cmap.green = NULL; image->cmap.blue = NULL; image->cmap.transp = NULL; image->data = qfbdev->shadow + (clips->x1 * 4) + (stride * clips->y1); qxl_fb_image_init(&qxl_fb_image, qdev, info, NULL); qxl_draw_opaque_fb(&qxl_fb_image, stride); return 0; }
void qxl_send_monitors_config(struct qxl_device *qdev) { int i; BUG_ON(!qdev->ram_header->monitors_config); if (qdev->monitors_config->count == 0) { qxl_io_log(qdev, "%s: 0 monitors??\n", __func__); return; } for (i = 0 ; i < qdev->monitors_config->count ; ++i) { struct qxl_head *head = &qdev->monitors_config->heads[i]; if (head->y > 8192 || head->x > 8192 || head->width > 8192 || head->height > 8192) { DRM_ERROR("head %d wrong: %dx%d+%d+%d\n", i, head->width, head->height, head->x, head->y); return; } } qxl_io_monitors_config(qdev); }
static int qxl_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; struct qxl_device *qdev = dev->dev_private; struct qxl_mode *m = (void *)mode->private; struct qxl_framebuffer *qfb; struct qxl_bo *bo, *old_bo = NULL; struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); uint32_t width, height, base_offset; bool recreate_primary = false; int ret; int surf_id; if (!crtc->fb) { DRM_DEBUG_KMS("No FB bound\n"); return 0; } if (old_fb) { qfb = to_qxl_framebuffer(old_fb); old_bo = gem_to_qxl_bo(qfb->obj); } qfb = to_qxl_framebuffer(crtc->fb); bo = gem_to_qxl_bo(qfb->obj); if (!m) /* and do we care? */ DRM_DEBUG("%dx%d: not a native mode\n", x, y); else DRM_DEBUG("%dx%d: qxl id %d\n", mode->hdisplay, mode->vdisplay, m->id); DRM_DEBUG("+%d+%d (%d,%d) => (%d,%d)\n", x, y, mode->hdisplay, mode->vdisplay, adjusted_mode->hdisplay, adjusted_mode->vdisplay); if (qcrtc->index == 0) recreate_primary = true; width = mode->hdisplay; height = mode->vdisplay; base_offset = 0; ret = qxl_bo_reserve(bo, false); if (ret != 0) return ret; ret = qxl_bo_pin(bo, bo->type, NULL); if (ret != 0) { qxl_bo_unreserve(bo); return -EINVAL; } qxl_bo_unreserve(bo); if (recreate_primary) { qxl_io_destroy_primary(qdev); qxl_io_log(qdev, "recreate primary: %dx%d (was %dx%d,%d,%d)\n", width, height, bo->surf.width, bo->surf.height, bo->surf.stride, bo->surf.format); qxl_io_create_primary(qdev, base_offset, bo); bo->is_primary = true; surf_id = 0; } else { surf_id = bo->surface_id; } if (old_bo && old_bo != bo) { old_bo->is_primary = false; ret = qxl_bo_reserve(old_bo, false); qxl_bo_unpin(old_bo); qxl_bo_unreserve(old_bo); } qxl_monitors_config_set(qdev, qcrtc->index, x, y, mode->hdisplay, mode->vdisplay, surf_id); return 0; }