diff options
Diffstat (limited to 'drivers/gpu')
45 files changed, 2410 insertions, 1243 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 05756f95be7c..f44c1a004f95 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1643,7 +1643,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (INTEL_INFO(dev)->num_pipes) { /* Must be done after probing outputs */ intel_opregion_init(dev); - acpi_video_register_with_quirks(); + acpi_video_register(); } if (IS_GEN5(dev)) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index af18da76c04b..044d11d05944 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -785,10 +785,22 @@ static void intel_disable_hdmi(struct intel_encoder *encoder) } } +static int hdmi_portclock_limit(struct intel_hdmi *hdmi) +{ + struct drm_device *dev = intel_hdmi_to_dev(hdmi); + + if (IS_G4X(dev)) + return 165000; + else if (IS_HASWELL(dev)) + return 300000; + else + return 225000; +} + static int intel_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - if (mode->clock > 165000) + if (mode->clock > hdmi_portclock_limit(intel_attached_hdmi(connector))) return MODE_CLOCK_HIGH; if (mode->clock < 20000) return MODE_CLOCK_LOW; @@ -806,6 +818,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; int clock_12bpc = pipe_config->requested_mode.clock * 3 / 2; + int portclock_limit = hdmi_portclock_limit(intel_hdmi); int desired_bpp; if (intel_hdmi->color_range_auto) { @@ -829,7 +842,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, * outputs. We also need to check that the higher clock still fits * within limits. */ - if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= 225000 + if (pipe_config->pipe_bpp > 8*3 && clock_12bpc <= portclock_limit && HAS_PCH_SPLIT(dev)) { DRM_DEBUG_KMS("picking bpc to 12 for HDMI output\n"); desired_bpp = 12*3; @@ -846,7 +859,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, pipe_config->pipe_bpp = desired_bpp; } - if (adjusted_mode->clock > 225000) { + if (adjusted_mode->clock > portclock_limit) { DRM_DEBUG_KMS("too high HDMI clock, rejecting mode\n"); return false; } diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index 93c2f2cceb51..eb89653a7a17 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -179,9 +179,10 @@ qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *relea uint32_t type, bool interruptible) { struct qxl_command cmd; + struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head); cmd.type = type; - cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset); + cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset); return qxl_ring_push(qdev->command_ring, &cmd, interruptible); } @@ -191,9 +192,10 @@ qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *releas uint32_t type, bool interruptible) { struct qxl_command cmd; + struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head); cmd.type = type; - cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset); + cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset); return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible); } @@ -214,7 +216,6 @@ 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)) { @@ -224,17 +225,10 @@ int qxl_garbage_collect(struct qxl_device *qdev) 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); @@ -259,27 +253,29 @@ int qxl_garbage_collect(struct qxl_device *qdev) return i; } -int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size, +int qxl_alloc_bo_reserved(struct qxl_device *qdev, + struct qxl_release *release, + unsigned long size, struct qxl_bo **_bo) { struct qxl_bo *bo; int ret; ret = qxl_bo_create(qdev, size, false /* not kernel - device */, - QXL_GEM_DOMAIN_VRAM, NULL, &bo); + false, QXL_GEM_DOMAIN_VRAM, NULL, &bo); if (ret) { DRM_ERROR("failed to allocate VRAM BO\n"); return ret; } - ret = qxl_bo_reserve(bo, false); - if (unlikely(ret != 0)) + ret = qxl_release_list_add(release, bo); + if (ret) goto out_unref; *_bo = bo; return 0; out_unref: qxl_bo_unref(&bo); - return 0; + return ret; } static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr) @@ -503,6 +499,10 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev, if (ret) return ret; + ret = qxl_release_reserve_list(release, true); + if (ret) + return ret; + cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); cmd->type = QXL_SURFACE_CMD_CREATE; cmd->u.surface_create.format = surf->surf.format; @@ -524,14 +524,11 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev, surf->surf_create = release; - /* no need to add a release to the fence for this bo, + /* no need to add a release to the fence for this surface bo, since it is only released when we ask to destroy the surface and it would never signal otherwise */ - qxl_fence_releaseable(qdev, release); - qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); - - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); surf->hw_surf_alloc = true; spin_lock(&qdev->surf_id_idr_lock); @@ -573,12 +570,9 @@ int qxl_hw_surface_dealloc(struct qxl_device *qdev, cmd->surface_id = id; qxl_release_unmap(qdev, release, &cmd->release_info); - qxl_fence_releaseable(qdev, release); - qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); - qxl_release_unreserve(qdev, release); - + qxl_release_fence_buffer_objects(release); return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index f76f5dd7bfc4..835caba026d3 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -179,7 +179,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc) kfree(qxl_crtc); } -static void +static int qxl_hide_cursor(struct qxl_device *qdev) { struct qxl_release *release; @@ -188,14 +188,22 @@ qxl_hide_cursor(struct qxl_device *qdev) ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, &release, NULL); + if (ret) + return ret; + + ret = qxl_release_reserve_list(release, true); + if (ret) { + qxl_release_free(qdev, release); + return ret; + } cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); cmd->type = QXL_CURSOR_HIDE; qxl_release_unmap(qdev, release, &cmd->release_info); - qxl_fence_releaseable(qdev, release); qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + return 0; } static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, @@ -216,10 +224,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, int size = 64*64*4; int ret = 0; - if (!handle) { - qxl_hide_cursor(qdev); - return 0; - } + if (!handle) + return qxl_hide_cursor(qdev); obj = drm_gem_object_lookup(crtc->dev, file_priv, handle); if (!obj) { @@ -234,8 +240,9 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, goto out_unref; ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL); + qxl_bo_unreserve(user_bo); if (ret) - goto out_unreserve; + goto out_unref; ret = qxl_bo_kmap(user_bo, &user_ptr); if (ret) @@ -246,14 +253,20 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, &release, NULL); if (ret) goto out_kunmap; - ret = qxl_alloc_bo_reserved(qdev, sizeof(struct qxl_cursor) + size, - &cursor_bo); + + ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_cursor) + size, + &cursor_bo); if (ret) goto out_free_release; - ret = qxl_bo_kmap(cursor_bo, (void **)&cursor); + + ret = qxl_release_reserve_list(release, false); if (ret) goto out_free_bo; + ret = qxl_bo_kmap(cursor_bo, (void **)&cursor); + if (ret) + goto out_backoff; + cursor->header.unique = 0; cursor->header.type = SPICE_CURSOR_TYPE_ALPHA; cursor->header.width = 64; @@ -269,11 +282,7 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, qxl_bo_kunmap(cursor_bo); - /* finish with the userspace bo */ qxl_bo_kunmap(user_bo); - qxl_bo_unpin(user_bo); - qxl_bo_unreserve(user_bo); - drm_gem_object_unreference_unlocked(obj); cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); cmd->type = QXL_CURSOR_SET; @@ -281,30 +290,35 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc, cmd->u.set.position.y = qcrtc->cur_y; cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0); - qxl_release_add_res(qdev, release, cursor_bo); cmd->u.set.visible = 1; qxl_release_unmap(qdev, release, &cmd->release_info); - qxl_fence_releaseable(qdev, release); qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + + /* finish with the userspace bo */ + ret = qxl_bo_reserve(user_bo, false); + if (!ret) { + qxl_bo_unpin(user_bo); + qxl_bo_unreserve(user_bo); + } + drm_gem_object_unreference_unlocked(obj); - qxl_bo_unreserve(cursor_bo); qxl_bo_unref(&cursor_bo); return ret; + +out_backoff: + qxl_release_backoff_reserve_list(release); out_free_bo: qxl_bo_unref(&cursor_bo); out_free_release: - qxl_release_unreserve(qdev, release); qxl_release_free(qdev, release); out_kunmap: qxl_bo_kunmap(user_bo); out_unpin: qxl_bo_unpin(user_bo); -out_unreserve: - qxl_bo_unreserve(user_bo); out_unref: drm_gem_object_unreference_unlocked(obj); return ret; @@ -322,6 +336,14 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc, ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD, &release, NULL); + if (ret) + return ret; + + ret = qxl_release_reserve_list(release, true); + if (ret) { + qxl_release_free(qdev, release); + return ret; + } qcrtc->cur_x = x; qcrtc->cur_y = y; @@ -332,9 +354,9 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc, cmd->u.position.y = qcrtc->cur_y; qxl_release_unmap(qdev, release, &cmd->release_info); - qxl_fence_releaseable(qdev, release); qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + return 0; } diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index 3c8c3dbf9378..56e1d633875e 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -23,25 +23,29 @@ #include "qxl_drv.h" #include "qxl_object.h" +static int alloc_clips(struct qxl_device *qdev, + struct qxl_release *release, + unsigned num_clips, + struct qxl_bo **clips_bo) +{ + int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips; + + return qxl_alloc_bo_reserved(qdev, release, size, clips_bo); +} + /* returns a pointer to the already allocated qxl_rect array inside * the qxl_clip_rects. This is *not* the same as the memory allocated * on the device, it is offset to qxl_clip_rects.chunk.data */ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, struct qxl_drawable *drawable, unsigned num_clips, - struct qxl_bo **clips_bo, - struct qxl_release *release) + struct qxl_bo *clips_bo) { struct qxl_clip_rects *dev_clips; int ret; - int size = sizeof(*dev_clips) + sizeof(struct qxl_rect) * num_clips; - ret = qxl_alloc_bo_reserved(qdev, size, clips_bo); - if (ret) - return NULL; - ret = qxl_bo_kmap(*clips_bo, (void **)&dev_clips); + ret = qxl_bo_kmap(clips_bo, (void **)&dev_clips); if (ret) { - qxl_bo_unref(clips_bo); return NULL; } dev_clips->num_rects = num_clips; @@ -52,20 +56,34 @@ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev, } static int +alloc_drawable(struct qxl_device *qdev, struct qxl_release **release) +{ + int ret; + ret = qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable), + QXL_RELEASE_DRAWABLE, release, + NULL); + return ret; +} + +static void +free_drawable(struct qxl_device *qdev, struct qxl_release *release) +{ + qxl_release_free(qdev, release); +} + +/* release needs to be reserved at this point */ +static int make_drawable(struct qxl_device *qdev, int surface, uint8_t type, const struct qxl_rect *rect, - struct qxl_release **release) + struct qxl_release *release) { struct qxl_drawable *drawable; - int i, ret; + int i; - ret = qxl_alloc_release_reserved(qdev, sizeof(*drawable), - QXL_RELEASE_DRAWABLE, release, - NULL); - if (ret) - return ret; + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); + if (!drawable) + return -ENOMEM; - drawable = (struct qxl_drawable *)qxl_release_map(qdev, *release); drawable->type = type; drawable->surface_id = surface; /* Only primary for now */ @@ -91,14 +109,23 @@ make_drawable(struct qxl_device *qdev, int surface, uint8_t type, drawable->bbox = *rect; drawable->mm_time = qdev->rom->mm_clock; - qxl_release_unmap(qdev, *release, &drawable->release_info); + qxl_release_unmap(qdev, release, &drawable->release_info); return 0; } -static int qxl_palette_create_1bit(struct qxl_bo **palette_bo, +static int alloc_palette_object(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_bo **palette_bo) +{ + return qxl_alloc_bo_reserved(qdev, release, + sizeof(struct qxl_palette) + sizeof(uint32_t) * 2, + palette_bo); +} + +static int qxl_palette_create_1bit(struct qxl_bo *palette_bo, + struct qxl_release *release, const struct qxl_fb_image *qxl_fb_image) { - struct qxl_device *qdev = qxl_fb_image->qdev; const struct fb_image *fb_image = &qxl_fb_image->fb_image; uint32_t visual = qxl_fb_image->visual; const uint32_t *pseudo_palette = qxl_fb_image->pseudo_palette; @@ -108,12 +135,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo, static uint64_t unique; /* we make no attempt to actually set this * correctly globaly, since that would require * tracking all of our palettes. */ - - ret = qxl_alloc_bo_reserved(qdev, - sizeof(struct qxl_palette) + sizeof(uint32_t) * 2, - palette_bo); - - ret = qxl_bo_kmap(*palette_bo, (void **)&pal); + ret = qxl_bo_kmap(palette_bo, (void **)&pal); pal->num_ents = 2; pal->unique = unique++; if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) { @@ -126,7 +148,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo, } pal->ents[0] = bgcolor; pal->ents[1] = fgcolor; - qxl_bo_kunmap(*palette_bo); + qxl_bo_kunmap(palette_bo); return 0; } @@ -144,44 +166,63 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, const char *src = fb_image->data; int depth = fb_image->depth; struct qxl_release *release; - struct qxl_bo *image_bo; struct qxl_image *image; int ret; - + struct qxl_drm_image *dimage; + struct qxl_bo *palette_bo = NULL; if (stride == 0) stride = depth * width / 8; + ret = alloc_drawable(qdev, &release); + if (ret) + return; + + ret = qxl_image_alloc_objects(qdev, release, + &dimage, + height, stride); + if (ret) + goto out_free_drawable; + + if (depth == 1) { + ret = alloc_palette_object(qdev, release, &palette_bo); + if (ret) + goto out_free_image; + } + + /* do a reservation run over all the objects we just allocated */ + ret = qxl_release_reserve_list(release, true); + if (ret) + goto out_free_palette; + rect.left = x; rect.right = x + width; rect.top = y; rect.bottom = y + height; - ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, &release); - if (ret) - return; + ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, release); + if (ret) { + qxl_release_backoff_reserve_list(release); + goto out_free_palette; + } - ret = qxl_image_create(qdev, release, &image_bo, - (const uint8_t *)src, 0, 0, - width, height, depth, stride); + ret = qxl_image_init(qdev, release, dimage, + (const uint8_t *)src, 0, 0, + width, height, depth, stride); if (ret) { - qxl_release_unreserve(qdev, release); + qxl_release_backoff_reserve_list(release); qxl_release_free(qdev, release); return; } if (depth == 1) { - struct qxl_bo *palette_bo; void *ptr; - ret = qxl_palette_create_1bit(&palette_bo, qxl_fb_image); - qxl_release_add_res(qdev, release, palette_bo); + ret = qxl_palette_create_1bit(palette_bo, release, qxl_fb_image); - ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0); + ptr = qxl_bo_kmap_atomic_page(qdev, dimage->bo, 0); image = ptr; image->u.bitmap.palette = qxl_bo_physical_address(qdev, palette_bo, 0); - qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr); - qxl_bo_unreserve(palette_bo); - qxl_bo_unref(&palette_bo); + qxl_bo_kunmap_atomic_page(qdev, dimage->bo, ptr); } drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); @@ -199,16 +240,20 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, drawable->u.copy.mask.bitmap = 0; drawable->u.copy.src_bitmap = - qxl_bo_physical_address(qdev, image_bo, 0); + qxl_bo_physical_address(qdev, dimage->bo, 0); qxl_release_unmap(qdev, release, &drawable->release_info); - qxl_release_add_res(qdev, release, image_bo); - qxl_bo_unreserve(image_bo); - qxl_bo_unref(&image_bo); - - qxl_fence_releaseable(qdev, release); qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + +out_free_palette: + if (palette_bo) + qxl_bo_unref(&palette_bo); +out_free_image: + qxl_image_free_objects(qdev, dimage); +out_free_drawable: + if (ret) + free_drawable(qdev, release); } /* push a draw command using the given clipping rectangles as @@ -243,10 +288,14 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, int depth = qxl_fb->base.bits_per_pixel; uint8_t *surface_base; struct qxl_release *release; - struct qxl_bo *image_bo; struct qxl_bo *clips_bo; + struct qxl_drm_image *dimage; int ret; + ret = alloc_drawable(qdev, &release); + if (ret) + return; + left = clips->x1; right = clips->x2; top = clips->y1; @@ -263,36 +312,52 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, width = right - left; height = bottom - top; + + ret = alloc_clips(qdev, release, num_clips, &clips_bo); + if (ret) + goto out_free_drawable; + + ret = qxl_image_alloc_objects(qdev, release, + &dimage, + height, stride); + if (ret) + goto out_free_clips; + + /* do a reservation run over all the objects we just allocated */ + ret = qxl_release_reserve_list(release, true); + if (ret) + goto out_free_image; + drawable_rect.left = left; drawable_rect.right = right; drawable_rect.top = top; drawable_rect.bottom = bottom; + ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &drawable_rect, - &release); + release); if (ret) - return; + goto out_release_backoff; ret = qxl_bo_kmap(bo, (void **)&surface_base); if (ret) - goto out_unref; + goto out_release_backoff; - ret = qxl_image_create(qdev, release, &image_bo, surface_base, - left, top, width, height, depth, stride); + + ret = qxl_image_init(qdev, release, dimage, surface_base, + left, top, width, height, depth, stride); qxl_bo_kunmap(bo); if (ret) - goto out_unref; + goto out_release_backoff; + + rects = drawable_set_clipping(qdev, drawable, num_clips, clips_bo); + if (!rects) + goto out_release_backoff; - rects = drawable_set_clipping(qdev, drawable, num_clips, &clips_bo, release); - if (!rects) { - qxl_bo_unref(&image_bo); - goto out_unref; - } drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); drawable->clip.type = SPICE_CLIP_TYPE_RECTS; drawable->clip.data = qxl_bo_physical_address(qdev, clips_bo, 0); - qxl_release_add_res(qdev, release, clips_bo); drawable->u.copy.src_area.top = 0; drawable->u.copy.src_area.bottom = height; @@ -306,11 +371,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, drawable->u.copy.mask.pos.y = 0; drawable->u.copy.mask.bitmap = 0; - drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, image_bo, 0); + drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, dimage->bo, 0); qxl_release_unmap(qdev, release, &drawable->release_info); - qxl_release_add_res(qdev, release, image_bo); - qxl_bo_unreserve(image_bo); - qxl_bo_unref(&image_bo); + clips_ptr = clips; for (i = 0; i < num_clips; i++, clips_ptr += inc) { rects[i].left = clips_ptr->x1; @@ -319,17 +382,22 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev, rects[i].bottom = clips_ptr->y2; } qxl_bo_kunmap(clips_bo); - qxl_bo_unreserve(clips_bo); - qxl_bo_unref(&clips_bo); - qxl_fence_releaseable(qdev, release); qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); - qxl_release_unreserve(qdev, release); - return; + qxl_release_fence_buffer_objects(release); + +out_release_backoff: + if (ret) + qxl_release_backoff_reserve_list(release); +out_free_image: + qxl_image_free_objects(qdev, dimage); +out_free_clips: + qxl_bo_unref(&clips_bo); +out_free_drawable: + /* only free drawable on error */ + if (ret) + free_drawable(qdev, release); -out_unref: - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); } void qxl_draw_copyarea(struct qxl_device *qdev, @@ -342,22 +410,36 @@ void qxl_draw_copyarea(struct qxl_device *qdev, struct qxl_release *release; int ret; + ret = alloc_drawable(qdev, &release); + if (ret) + return; + + /* do a reservation run over all the objects we just allocated */ + ret = qxl_release_reserve_list(release, true); + if (ret) + goto out_free_release; + rect.left = dx; rect.top = dy; rect.right = dx + width; rect.bottom = dy + height; - ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, &release); - if (ret) - return; + ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, release); + if (ret) { + qxl_release_backoff_reserve_list(release); + goto out_free_release; + } drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); drawable->u.copy_bits.src_pos.x = sx; drawable->u.copy_bits.src_pos.y = sy; - qxl_release_unmap(qdev, release, &drawable->release_info); - qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + +out_free_release: + if (ret) + free_drawable(qdev, release); } void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec) @@ -370,10 +452,21 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec) struct qxl_release *release; int ret; - ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, &release); + ret = alloc_drawable(qdev, &release); if (ret) return; + /* do a reservation run over all the objects we just allocated */ + ret = qxl_release_reserve_list(release, true); + if (ret) + goto out_free_release; + + ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, release); + if (ret) { + qxl_release_backoff_reserve_list(release); + goto out_free_release; + } + drawable = (struct qxl_drawable *)qxl_release_map(qdev, release); drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID; drawable->u.fill.brush.u.color = color; @@ -384,7 +477,11 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec) drawable->u.fill.mask.bitmap = 0; qxl_release_unmap(qdev, release, &drawable->release_info); - qxl_fence_releaseable(qdev, release); + qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false); - qxl_release_unreserve(qdev, release); + qxl_release_fence_buffer_objects(release); + +out_free_release: + if (ret) + free_drawable(qdev, release); } diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index afd09d48d72c..4708621fe720 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -42,6 +42,9 @@ #include <ttm/ttm_placement.h> #include <ttm/ttm_module.h> +/* just for ttm_validate_buffer */ +#include <ttm/ttm_execbuf_util.h> + #include <drm/qxl_drm.h> #include "qxl_dev.h" @@ -118,9 +121,9 @@ struct qxl_bo { uint32_t surface_id; struct qxl_fence fence; /* per bo fence - list of releases */ struct qxl_release *surf_create; - atomic_t reserve_count; }; #define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base) +#define to_qxl_bo(tobj) container_of((tobj), struct qxl_bo, tbo) struct qxl_gem { struct mutex mutex; @@ -128,12 +131,7 @@ struct qxl_gem { }; struct qxl_bo_list { - struct list_head lhead; - struct qxl_bo *bo; -}; - -struct qxl_reloc_list { - struct list_head bos; + struct ttm_validate_buffer tv; }; struct qxl_crtc { @@ -195,10 +193,20 @@ enum { struct qxl_release { int id; int type; - int bo_count; uint32_t release_offset; uint32_t surface_release_id; - struct qxl_bo *bos[QXL_MAX_RES]; + struct ww_acquire_ctx ticket; + struct list_head bos; +}; + +struct qxl_drm_chunk { + struct list_head head; + struct qxl_bo *bo; +}; + +struct qxl_drm_image { + struct qxl_bo *bo; + struct list_head chunk_list; }; struct qxl_fb_image { @@ -314,6 +322,7 @@ struct qxl_device { struct workqueue_struct *gc_queue; struct work_struct gc_work; + struct work_struct fb_work; }; /* forward declaration for QXL_INFO_IO */ @@ -430,12 +439,19 @@ int qxl_mmap(struct file *filp, struct vm_area_struct *vma); /* qxl image */ -int qxl_image_create(struct qxl_device *qdev, - struct qxl_release *release, - struct qxl_bo **image_bo, - const uint8_t *data, - int x, int y, int width, int height, - int depth, int stride); +int qxl_image_init(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_drm_image *dimage, + const uint8_t *data, + int x, int y, int width, int height, + int depth, int stride); +int +qxl_image_alloc_objects(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_drm_image **image_ptr, + int height, int stride); +void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage); + void qxl_update_screen(struct qxl_device *qxl); /* qxl io operations (qxl_cmd.c) */ @@ -456,20 +472,15 @@ int qxl_ring_push(struct qxl_ring *ring, const void *new_elt, bool interruptible void qxl_io_flush_release(struct qxl_device *qdev); void qxl_io_flush_surfaces(struct qxl_device *qdev); -int qxl_release_reserve(struct qxl_device *qdev, - struct qxl_release *release, bool no_wait); -void qxl_release_unreserve(struct qxl_device *qdev, - struct qxl_release *release); union qxl_release_info *qxl_release_map(struct qxl_device *qdev, struct qxl_release *release); void qxl_release_unmap(struct qxl_device *qdev, struct qxl_release *release, union qxl_release_info *info); -/* - * qxl_bo_add_resource. - * - */ -void qxl_bo_add_resource(struct qxl_bo *main_bo, struct qxl_bo *resource); +int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo); +int qxl_release_reserve_list(struct qxl_release *release, bool no_intr); +void qxl_release_backoff_reserve_list(struct qxl_release *release); +void qxl_release_fence_buffer_objects(struct qxl_release *release); int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, enum qxl_surface_cmd_type surface_cmd_type, @@ -478,15 +489,16 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, int type, struct qxl_release **release, struct qxl_bo **rbo); -int qxl_fence_releaseable(struct qxl_device *qdev, - struct qxl_release *release); + int qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release, uint32_t type, bool interruptible); int qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release, uint32_t type, bool interruptible); -int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size, +int qxl_alloc_bo_reserved(struct qxl_device *qdev, + struct qxl_release *release, + unsigned long size, struct qxl_bo **_bo); /* qxl drawing commands */ @@ -507,15 +519,9 @@ void qxl_draw_copyarea(struct qxl_device *qdev, u32 sx, u32 sy, u32 dx, u32 dy); -uint64_t -qxl_release_alloc(struct qxl_device *qdev, int type, - struct qxl_release **ret); - void qxl_release_free(struct qxl_device *qdev, struct qxl_release *release); -void qxl_release_add_res(struct qxl_device *qdev, - struct qxl_release *release, - struct qxl_bo *bo); + /* used by qxl_debugfs_release */ struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev, uint64_t id); @@ -558,7 +564,7 @@ void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freein int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf); /* qxl_fence.c */ -int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id); +void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id); int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id); int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence); void qxl_fence_fini(struct qxl_fence *qfence); diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 76f39d88d684..88722f233430 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -37,12 +37,29 @@ #define QXL_DIRTY_DELAY (HZ / 30) +#define QXL_FB_OP_FILLRECT 1 +#define QXL_FB_OP_COPYAREA 2 +#define QXL_FB_OP_IMAGEBLIT 3 + +struct qxl_fb_op { + struct list_head head; + int op_type; + union { + struct fb_fillrect fr; + struct fb_copyarea ca; + struct fb_image ib; + } op; + void *img_data; +}; + struct qxl_fbdev { struct drm_fb_helper helper; struct qxl_framebuffer qfb; struct list_head fbdev_list; struct qxl_device *qdev; + spinlock_t delayed_ops_lock; + struct list_head delayed_ops; void *shadow; int size; @@ -164,8 +181,69 @@ static struct fb_deferred_io qxl_defio = { .deferred_io = qxl_deferred_io, }; -static void qxl_fb_fillrect(struct fb_info *info, - const struct fb_fillrect *fb_rect) +static void qxl_fb_delayed_fillrect(struct qxl_fbdev *qfbdev, + const struct fb_fillrect *fb_rect) +{ + struct qxl_fb_op *op; + unsigned long flags; + + op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN); + if (!op) + return; + + op->op.fr = *fb_rect; + op->img_data = NULL; + op->op_type = QXL_FB_OP_FILLRECT; + + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags); + list_add_tail(&op->head, &qfbdev->delayed_ops); + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags); +} + +static void qxl_fb_delayed_copyarea(struct qxl_fbdev *qfbdev, + const struct fb_copyarea *fb_copy) +{ + struct qxl_fb_op *op; + unsigned long flags; + + op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN); + if (!op) + return; + + op->op.ca = *fb_copy; + op->img_data = NULL; + op->op_type = QXL_FB_OP_COPYAREA; + + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags); + list_add_tail(&op->head, &qfbdev->delayed_ops); + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags); +} + +static void qxl_fb_delayed_imageblit(struct qxl_fbdev *qfbdev, + const struct fb_image *fb_image) +{ + struct qxl_fb_op *op; + unsigned long flags; + uint32_t size = fb_image->width * fb_image->height * (fb_image->depth >= 8 ? fb_image->depth / 8 : 1); + + op = kmalloc(sizeof(struct qxl_fb_op) + size, GFP_ATOMIC | __GFP_NOWARN); + if (!op) + return; + + op->op.ib = *fb_image; + op->img_data = (void *)(op + 1); + op->op_type = QXL_FB_OP_IMAGEBLIT; + + memcpy(op->img_data, fb_image->data, size); + + op->op.ib.data = op->img_data; + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags); + list_add_tail(&op->head, &qfbdev->delayed_ops); + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags); +} + +static void qxl_fb_fillrect_internal(struct fb_info *info, + const struct fb_fillrect *fb_rect) { struct qxl_fbdev *qfbdev = info->par; struct qxl_device *qdev = qfbdev->qdev; @@ -203,17 +281,28 @@ static void qxl_fb_fillrect(struct fb_info *info, qxl_draw_fill_rec.rect = rect; qxl_draw_fill_rec.color = color; qxl_draw_fill_rec.rop = rop; + + qxl_draw_fill(&qxl_draw_fill_rec); +} + +static void qxl_fb_fillrect(struct fb_info *info, + const struct fb_fillrect *fb_rect) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_device *qdev = qfbdev->qdev; + if (!drm_can_sleep()) { - qxl_io_log(qdev, - "%s: TODO use RCU, mysterious locks with spin_lock\n", - __func__); + qxl_fb_delayed_fillrect(qfbdev, fb_rect); + schedule_work(&qdev->fb_work); return; } - qxl_draw_fill(&qxl_draw_fill_rec); + /* make sure any previous work is done */ + flush_work(&qdev->fb_work); + qxl_fb_fillrect_internal(info, fb_rect); } -static void qxl_fb_copyarea(struct fb_info *info, - const struct fb_copyarea *region) +static void qxl_fb_copyarea_internal(struct fb_info *info, + const struct fb_copyarea *region) { struct qxl_fbdev *qfbdev = info->par; @@ -223,37 +312,89 @@ static void qxl_fb_copyarea(struct fb_info *info, region->dx, region->dy); } +static void qxl_fb_copyarea(struct fb_info *info, + const struct fb_copyarea *region) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_device *qdev = qfbdev->qdev; + + if (!drm_can_sleep()) { + qxl_fb_delayed_copyarea(qfbdev, region); + schedule_work(&qdev->fb_work); + return; + } + /* make sure any previous work is done */ + flush_work(&qdev->fb_work); + qxl_fb_copyarea_internal(info, region); +} + static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image) { qxl_draw_opaque_fb(qxl_fb_image, 0); } +static void qxl_fb_imageblit_internal(struct fb_info *info, + const struct fb_image *image) +{ + struct qxl_fbdev *qfbdev = info->par; + struct qxl_fb_image qxl_fb_image; + + /* ensure proper order rendering operations - TODO: must do this + * for everything. */ + qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image); + qxl_fb_imageblit_safe(&qxl_fb_image); +} + static void qxl_fb_imageblit(struct fb_info *info, const struct fb_image *image) { struct qxl_fbdev *qfbdev = info->par; struct qxl_device *qdev = qfbdev->qdev; - struct qxl_fb_image qxl_fb_image; if (!drm_can_sleep()) { - /* we cannot do any ttm_bo allocation since that will fail on - * ioremap_wc..__get_vm_area_node, so queue the work item - * instead This can happen from printk inside an interrupt - * context, i.e.: smp_apic_timer_interrupt..check_cpu_stall */ - qxl_io_log(qdev, - "%s: TODO use RCU, mysterious locks with spin_lock\n", - __func__); + qxl_fb_delayed_imageblit(qfbdev, image); + schedule_work(&qdev->fb_work); return; } + /* make sure any previous work is done */ + flush_work(&qdev->fb_work); + qxl_fb_imageblit_internal(info, image); +} - /* ensure proper order of rendering operations - TODO: must do this - * for everything. */ - qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image); - qxl_fb_imageblit_safe(&qxl_fb_image); +static void qxl_fb_work(struct work_struct *work) +{ + struct qxl_device *qdev = container_of(work, struct qxl_device, fb_work); + unsigned long flags; + struct qxl_fb_op *entry, *tmp; + struct qxl_fbdev *qfbdev = qdev->mode_info.qfbdev; + + /* since the irq context just adds entries to the end of the + list dropping the lock should be fine, as entry isn't modified + in the operation code */ + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags); + list_for_each_entry_safe(entry, tmp, &qfbdev->delayed_ops, head) { + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags); + switch (entry->op_type) { + case QXL_FB_OP_FILLRECT: + qxl_fb_fillrect_internal(qfbdev->helper.fbdev, &entry->op.fr); + break; + case QXL_FB_OP_COPYAREA: + qxl_fb_copyarea_internal(qfbdev->helper.fbdev, &entry->op.ca); + break; + case QXL_FB_OP_IMAGEBLIT: + qxl_fb_imageblit_internal(qfbdev->helper.fbdev, &entry->op.ib); + break; + } + spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags); + list_del(&entry->head); + kfree(entry); + } + spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags); } int qxl_fb_init(struct qxl_device *qdev) { + INIT_WORK(&qdev->fb_work, qxl_fb_work); return 0; } @@ -536,7 +677,8 @@ int qxl_fbdev_init(struct qxl_device *qdev) qfbdev->qdev = qdev; qdev->mode_info.qfbdev = qfbdev; qfbdev->helper.funcs = &qxl_fb_helper_funcs; - + spin_lock_init(&qfbdev->delayed_ops_lock); + INIT_LIST_HEAD(&qfbdev->delayed_ops); ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, qxl_num_crtc /* num_crtc - QXL supports just 1 */, QXLFB_CONN_LIMIT); diff --git a/drivers/gpu/drm/qxl/qxl_fence.c b/drivers/gpu/drm/qxl/qxl_fence.c index 63c6715ad385..ae59e91cfb9a 100644 --- a/drivers/gpu/drm/qxl/qxl_fence.c +++ b/drivers/gpu/drm/qxl/qxl_fence.c @@ -49,17 +49,11 @@ For some reason every so often qxl hw fails to release, things go wrong. */ - - -int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id) +/* must be called with the fence lock held */ +void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id) { - struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence); - - spin_lock(&bo->tbo.bdev->fence_lock); radix_tree_insert(&qfence->tree, rel_id, qfence); qfence->num_active_releases++; - spin_unlock(&bo->tbo.bdev->fence_lock); - return 0; } int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id) diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c index a235693aabba..25e1777fb0a2 100644 --- a/drivers/gpu/drm/qxl/qxl_gem.c +++ b/drivers/gpu/drm/qxl/qxl_gem.c @@ -55,7 +55,7 @@ int qxl_gem_object_create(struct qxl_device *qdev, int size, /* At least align on page size */ if (alignment < PAGE_SIZE) alignment = PAGE_SIZE; - r = qxl_bo_create(qdev, size, kernel, initial_domain, surf, &qbo); + r = qxl_bo_create(qdev, size, kernel, false, initial_domain, surf, &qbo); if (r) { if (r != -ERESTARTSYS) DRM_ERROR( diff --git a/drivers/gpu/drm/qxl/qxl_image.c b/drivers/gpu/drm/qxl/qxl_image.c index cf856206996b..7fbcc35e8ad3 100644 --- a/drivers/gpu/drm/qxl/qxl_image.c +++ b/drivers/gpu/drm/qxl/qxl_image.c @@ -30,31 +30,100 @@ #include "qxl_object.h" static int -qxl_image_create_helper(struct qxl_device *qdev, +qxl_allocate_chunk(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_drm_image *image, + unsigned int chunk_size) +{ + struct qxl_drm_chunk *chunk; + int ret; + + chunk = kmalloc(sizeof(struct qxl_drm_chunk), GFP_KERNEL); + if (!chunk) + return -ENOMEM; + + ret = qxl_alloc_bo_reserved(qdev, release, chunk_size, &chunk->bo); + if (ret) { + kfree(chunk); + return ret; + } + + list_add_tail(&chunk->head, &image->chunk_list); + return 0; +} + +int +qxl_image_alloc_objects(struct qxl_device *qdev, struct qxl_release *release, - struct qxl_bo **image_bo, - const uint8_t *data, - int width, int height, - int depth, unsigned int hash, - int stride) + struct qxl_drm_image **image_ptr, + int height, int stride) +{ + struct qxl_drm_image *image; + int ret; + + image = kmalloc(sizeof(struct qxl_drm_image), GFP_KERNEL); + if (!image) + return -ENOMEM; + + INIT_LIST_HEAD(&image->chunk_list); + + ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_image), &image->bo); + if (ret) { + kfree(image); + return ret; + } + + ret = qxl_allocate_chunk(qdev, release, image, sizeof(struct qxl_data_chunk) + stride * height); + if (ret) { + qxl_bo_unref(&image->bo); + kfree(image); + return ret; + } + *image_ptr = image; + return 0; +} + +void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage) { + struct qxl_drm_chunk *chunk, *tmp; + + list_for_each_entry_safe(chunk, tmp, &dimage->chunk_list, head) { + qxl_bo_unref(&chunk->bo); + kfree(chunk); + } + + qxl_bo_unref(&dimage->bo); + kfree(dimage); +} + +static int +qxl_image_init_helper(struct qxl_device *qdev, + struct qxl_release *release, + struct qxl_drm_image *dimage, + const uint8_t *data, + int width, int height, + int depth, unsigned int hash, + int stride) +{ + struct qxl_drm_chunk *drv_chunk; struct qxl_image *image; struct qxl_data_chunk *chunk; int i; int chunk_stride; int linesize = width * depth / 8; - struct qxl_bo *chunk_bo; - int ret; + struct qxl_bo *chunk_bo, *image_bo; void *ptr; /* Chunk */ /* FIXME: Check integer overflow */ /* TODO: variable number of chunks */ + + drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head); + + chunk_bo = drv_chunk->bo; chunk_stride = stride; /* TODO: should use linesize, but it renders wrong (check the bitmaps are sent correctly first) */ - ret = qxl_alloc_bo_reserved(qdev, sizeof(*chunk) + height * chunk_stride, - &chunk_bo); - + ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0); chunk = ptr; chunk->data_size = height * chunk_stride; @@ -102,7 +171,6 @@ qxl_image_create_helper(struct qxl_device *qdev, while (remain > 0) { page_base = out_offset & PAGE_MASK; page_offset = offset_in_page(out_offset); - size = min((int)(PAGE_SIZE - page_offset), remain); ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base); @@ -116,14 +184,10 @@ qxl_image_create_helper(struct qxl_device *qdev, } } } - - qxl_bo_kunmap(chunk_bo); - /* Image */ - ret = qxl_alloc_bo_reserved(qdev, sizeof(*image), image_bo); - - ptr = qxl_bo_kmap_atomic_page(qdev, *image_bo, 0); + image_bo = dimage->bo; + ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0); image = ptr; image->descriptor.id = 0; @@ -154,23 +218,20 @@ qxl_image_create_helper(struct qxl_device *qdev, image->u.bitmap.stride = chunk_stride; image->u.bitmap.palette = 0; image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0); - qxl_release_add_res(qdev, release, chunk_bo); - qxl_bo_unreserve(chunk_bo); - qxl_bo_unref(&chunk_bo); - qxl_bo_kunmap_atomic_page(qdev, *image_bo, ptr); + qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr); return 0; } -int qxl_image_create(struct qxl_device *qdev, +int qxl_image_init(struct qxl_device *qdev, struct qxl_release *release, - struct qxl_bo **image_bo, + struct qxl_drm_image *dimage, const uint8_t *data, int x, int y, int width, int height, int depth, int stride) { data += y * stride + x * (depth / 8); - return qxl_image_create_helper(qdev, release, image_bo, data, + return qxl_image_init_helper(qdev, release, dimage, data, width, height, depth, 0, stride); } diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c index 6cd7273c0804..7b95c75e9626 100644 --- a/drivers/gpu/drm/qxl/qxl_ioctl.c +++ b/drivers/gpu/drm/qxl/qxl_ioctl.c @@ -68,55 +68,60 @@ static int qxl_map_ioctl(struct drm_device *dev, void *data, &qxl_map->offset); } +struct qxl_reloc_info { + int type; + struct qxl_bo *dst_bo; + uint32_t dst_offset; + struct qxl_bo *src_bo; + int src_offset; +}; + /* * dst must be validated, i.e. whole bo on vram/surfacesram (right now all bo's * are on vram). * *(dst + dst_off) = qxl_bo_physical_address(src, src_off) */ static void -apply_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off, - struct qxl_bo *src, uint64_t src_off) +apply_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info) { void *reloc_page; - - reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK); - *(uint64_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = qxl_bo_physical_address(qdev, - src, src_off); - qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page); + reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK); + *(uint64_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = qxl_bo_physical_address(qdev, + info->src_bo, + info->src_offset); + qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page); } static void -apply_surf_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off, - struct qxl_bo *src) +apply_surf_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info) { uint32_t id = 0; void *reloc_page; - if (src && !src->is_primary) - id = src->surface_id; + if (info->src_bo && !info->src_bo->is_primary) + id = info->src_bo->surface_id; - reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK); - *(uint32_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = id; - qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page); + reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK); + *(uint32_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = id; + qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page); } /* return holding the reference to this object */ static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev, struct drm_file *file_priv, uint64_t handle, - struct qxl_reloc_list *reloc_list) + struct qxl_release *release) { struct drm_gem_object *gobj; struct qxl_bo *qobj; int ret; gobj = drm_gem_object_lookup(qdev->ddev, file_priv, handle); - if (!gobj) { - DRM_ERROR("bad bo handle %lld\n", handle); + if (!gobj) return NULL; - } + qobj = gem_to_qxl_bo(gobj); - ret = qxl_bo_list_add(reloc_list, qobj); + ret = qxl_release_list_add(release, qobj); if (ret) return NULL; @@ -129,151 +134,177 @@ static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev, * However, the command as passed from user space must *not* contain the initial * QXLReleaseInfo struct (first XXX bytes) */ -static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +static int qxl_process_single_command(struct qxl_device *qdev, + struct drm_qxl_command *cmd, + struct drm_file *file_priv) { - struct qxl_device *qdev = dev->dev_private; - struct drm_qxl_execbuffer *execbuffer = data; - struct drm_qxl_command user_cmd; - int cmd_num; - struct qxl_bo *reloc_src_bo; - struct qxl_bo *reloc_dst_bo; - struct drm_qxl_reloc reloc; + struct qxl_reloc_info *reloc_info; + int release_type; + struct qxl_release *release; + struct qxl_bo *cmd_bo; void *fb_cmd; - int i, ret; - struct qxl_reloc_list reloc_list; + int i, j, ret, num_relocs; int unwritten; - uint32_t reloc_dst_offset; - INIT_LIST_HEAD(&reloc_list.bos); - for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) { - struct qxl_release *release; - struct qxl_bo *cmd_bo; - int release_type; - struct drm_qxl_command *commands = - (struct drm_qxl_command *)(uintptr_t)execbuffer->commands; + switch (cmd->type) { + case QXL_CMD_DRAW: + release_type = QXL_RELEASE_DRAWABLE; + break; + case QXL_CMD_SURFACE: + case QXL_CMD_CURSOR: + default: + DRM_DEBUG("Only draw commands in execbuffers\n"); + return -EINVAL; + break; + } - if (DRM_COPY_FROM_USER(&user_cmd, &commands[cmd_num], - sizeof(user_cmd))) - return -EFAULT; - switch (user_cmd.type) { - case QXL_CMD_DRAW: - release_type = QXL_RELEASE_DRAWABLE; - break; - case QXL_CMD_SURFACE: - case QXL_CMD_CURSOR: - default: - DRM_DEBUG("Only draw commands in execbuffers\n"); - return -EINVAL; - break; - } + if (cmd->command_size > PAGE_SIZE - sizeof(union qxl_release_info)) + return -EINVAL; - if (user_cmd.command_size > PAGE_SIZE - sizeof(union qxl_release_info)) - return -EINVAL; + if (!access_ok(VERIFY_READ, + (void *)(unsigned long)cmd->command, + cmd->command_size)) + return -EFAULT; - if (!access_ok(VERIFY_READ, - (void *)(unsigned long)user_cmd.command, - user_cmd.command_size)) - return -EFAULT; + reloc_info = kmalloc(sizeof(struct qxl_reloc_info) * cmd->relocs_num, GFP_KERNEL); + if (!reloc_info) + return -ENOMEM; - ret = qxl_alloc_release_reserved(qdev, - sizeof(union qxl_release_info) + - user_cmd.command_size, - release_type, - &release, - &cmd_bo); - if (ret) - return ret; + ret = qxl_alloc_release_reserved(qdev, + sizeof(union qxl_release_info) + + cmd->command_size, + release_type, + &release, + &cmd_bo); + if (ret) + goto out_free_reloc; - /* TODO copy slow path code from i915 */ - fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE)); - unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)user_cmd.command, user_cmd.command_size); + /* TODO copy slow path code from i915 */ + fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE)); + unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)cmd->command, cmd->command_size); - { - struct qxl_drawable *draw = fb_cmd; + { + struct qxl_drawable *draw = fb_cmd; + draw->mm_time = qdev->rom->mm_clock; + } - draw->mm_time = qdev->rom->mm_clock; - } - qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd); - if (unwritten) { - DRM_ERROR("got unwritten %d\n", unwritten); - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); - return -EFAULT; + qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd); + if (unwritten) { + DRM_ERROR("got unwritten %d\n", unwritten); + ret = -EFAULT; + goto out_free_release; + } + + /* fill out reloc info structs */ + num_relocs = 0; + for (i = 0; i < cmd->relocs_num; ++i) { + struct drm_qxl_reloc reloc; + + if (DRM_COPY_FROM_USER(&reloc, + &((struct drm_qxl_reloc *)(uintptr_t)cmd->relocs)[i], + sizeof(reloc))) { + ret = -EFAULT; + goto out_free_bos; } - for (i = 0 ; i < user_cmd.relocs_num; ++i) { - if (DRM_COPY_FROM_USER(&reloc, - &((struct drm_qxl_reloc *)(uintptr_t)user_cmd.relocs)[i], - sizeof(reloc))) { - qxl_bo_list_unreserve(&reloc_list, true); - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); - return -EFAULT; - } + /* add the bos to the list of bos to validate - + need to validate first then process relocs? */ + if (reloc.reloc_type != QXL_RELOC_TYPE_BO && reloc.reloc_type != QXL_RELOC_TYPE_SURF) { + DRM_DEBUG("unknown reloc type %d\n", reloc_info[i].type); - /* add the bos to the list of bos to validate - - need to validate first then process relocs? */ - if (reloc.dst_handle) { - reloc_dst_bo = qxlhw_handle_to_bo(qdev, file_priv, - reloc.dst_handle, &reloc_list); - if (!reloc_dst_bo) { - qxl_bo_list_unreserve(&reloc_list, true); - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); - return -EINVAL; - } - reloc_dst_offset = 0; - } else { - reloc_dst_bo = cmd_bo; - reloc_dst_offset = release->release_offset; + ret = -EINVAL; + goto out_free_bos; + } + reloc_info[i].type = reloc.reloc_type; + + if (reloc.dst_handle) { + reloc_info[i].dst_bo = qxlhw_handle_to_bo(qdev, file_priv, + reloc.dst_handle, release); + if (!reloc_info[i].dst_bo) { + ret = -EINVAL; + reloc_info[i].src_bo = NULL; + goto out_free_bos; } - - /* reserve and validate the reloc dst bo */ - if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle > 0) { - reloc_src_bo = - qxlhw_handle_to_bo(qdev, file_priv, - reloc.src_handle, &reloc_list); - if (!reloc_src_bo) { - if (reloc_dst_bo != cmd_bo) - drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base); - qxl_bo_list_unreserve(&reloc_list, true); - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); - return -EINVAL; - } - } else - reloc_src_bo = NULL; - if (reloc.reloc_type == QXL_RELOC_TYPE_BO) { - apply_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset, - reloc_src_bo, reloc.src_offset); - } else if (reloc.reloc_type == QXL_RELOC_TYPE_SURF) { - apply_surf_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset, reloc_src_bo); - } else { - DRM_ERROR("unknown reloc type %d\n", reloc.reloc_type); - return -EINVAL; + reloc_info[i].dst_offset = reloc.dst_offset; + } else { + reloc_info[i].dst_bo = cmd_bo; + reloc_info[i].dst_offset = reloc.dst_offset + release->release_offset; + } + num_relocs++; + + /* reserve and validate the reloc dst bo */ + if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle > 0) { + reloc_info[i].src_bo = + qxlhw_handle_to_bo(qdev, file_priv, + reloc.src_handle, release); + if (!reloc_info[i].src_bo) { + if (reloc_info[i].dst_bo != cmd_bo) + drm_gem_object_unreference_unlocked(&reloc_info[i].dst_bo->gem_base); + ret = -EINVAL; + goto out_free_bos; } + reloc_info[i].src_offset = reloc.src_offset; + } else { + reloc_info[i].src_bo = NULL; + reloc_info[i].src_offset = 0; + } + } - if (reloc_src_bo && reloc_src_bo != cmd_bo) { - qxl_release_add_res(qdev, release, reloc_src_bo); - drm_gem_object_unreference_unlocked(&reloc_src_bo->gem_base); - } + /* validate all buffers */ + ret = qxl_release_reserve_list(release, false); + if (ret) + goto out_free_bos; - if (reloc_dst_bo != cmd_bo) - drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base); - } - qxl_fence_releaseable(qdev, release); + for (i = 0; i < cmd->relocs_num; ++i) { + if (reloc_info[i].type == QXL_RELOC_TYPE_BO) + apply_reloc(qdev, &reloc_info[i]); + else if (reloc_info[i].type == QXL_RELOC_TYPE_SURF) + apply_surf_reloc(qdev, &reloc_info[i]); + } - ret = qxl_push_command_ring_release(qdev, release, user_cmd.type, true); - if (ret == -ERESTARTSYS) { - qxl_release_unreserve(qdev, release); - qxl_release_free(qdev, release); - qxl_bo_list_unreserve(&reloc_list, true); + ret = qxl_push_command_ring_release(qdev, release, cmd->type, true); + if (ret) + qxl_release_backoff_reserve_list(release); + else + qxl_release_fence_buffer_objects(release); + +out_free_bos: + for (j = 0; j < num_relocs; j++) { + if (reloc_info[j].dst_bo != cmd_bo) + drm_gem_object_unreference_unlocked(&reloc_info[j].dst_bo->gem_base); + if (reloc_info[j].src_bo && reloc_info[j].src_bo != cmd_bo) + drm_gem_object_unreference_unlocked(&reloc_info[j].src_bo->gem_base); + } +out_free_release: + if (ret) + qxl_release_free(qdev, release); +out_free_reloc: + kfree(reloc_info); + return ret; +} + +static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct qxl_device *qdev = dev->dev_private; + struct drm_qxl_execbuffer *execbuffer = data; + struct drm_qxl_command user_cmd; + int cmd_num; + int ret; + + for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) { + + struct drm_qxl_command *commands = + (struct drm_qxl_command *)(uintptr_t)execbuffer->commands; + + if (DRM_COPY_FROM_USER(&user_cmd, &commands[cmd_num], + sizeof(user_cmd))) + return -EFAULT; + + ret = qxl_process_single_command(qdev, &user_cmd, file_priv); + if (ret) return ret; - } - qxl_release_unreserve(qdev, release); } - qxl_bo_list_unreserve(&reloc_list, 0); return 0; } @@ -305,7 +336,7 @@ static int qxl_update_area_ioctl(struct drm_device *dev, void *data, goto out; if (!qobj->pin_count) { - qxl_ttm_placement_from_domain(qobj, qobj->type); + qxl_ttm_placement_from_domain(qobj, qobj->type, false); ret = ttm_bo_validate(&qobj->tbo, &qobj->placement, true, false); if (unlikely(ret)) diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index 1191fe7788c9..aa161cddd87e 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -51,20 +51,21 @@ bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo) return false; } -void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain) +void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) { u32 c = 0; + u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; qbo->placement.fpfn = 0; qbo->placement.lpfn = 0; qbo->placement.placement = qbo->placements; qbo->placement.busy_placement = qbo->placements; if (domain == QXL_GEM_DOMAIN_VRAM) - qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM; + qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; if (domain == QXL_GEM_DOMAIN_SURFACE) - qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0; + qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag; if (domain == QXL_GEM_DOMAIN_CPU) - qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag; if (!c) qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; qbo->placement.num_placement = c; @@ -73,7 +74,7 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain) int qxl_bo_create(struct qxl_device *qdev, - unsigned long size, bool kernel, u32 domain, + unsigned long size, bool kernel, bool pinned, u32 domain, struct qxl_surface *surf, struct qxl_bo **bo_ptr) { @@ -99,15 +100,15 @@ int qxl_bo_create(struct qxl_device *qdev, } bo->gem_base.driver_private = NULL; bo->type = domain; - bo->pin_count = 0; + bo->pin_count = pinned ? 1 : 0; bo->surface_id = 0; qxl_fence_init(qdev, &bo->fence); INIT_LIST_HEAD(&bo->list); - atomic_set(&bo->reserve_count, 0); + if (surf) bo->surf = *surf; - qxl_ttm_placement_from_domain(bo, domain); + qxl_ttm_placement_from_domain(bo, domain, pinned); r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type, &bo->placement, 0, !kernel, NULL, size, @@ -228,7 +229,7 @@ struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) { struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; - int r, i; + int r; if (bo->pin_count) { bo->pin_count++; @@ -236,9 +237,7 @@ int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) *gpu_addr = qxl_bo_gpu_offset(bo); return 0; } - qxl_ttm_placement_from_domain(bo, domain); - for (i = 0; i < bo->placement.num_placement; i++) - bo->placements[i] |= TTM_PL_FLAG_NO_EVICT; + qxl_ttm_placement_from_domain(bo, domain, true); r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false); if (likely(r == 0)) { bo->pin_count = 1; @@ -317,53 +316,6 @@ int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo) return 0; } -void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed) -{ - struct qxl_bo_list *entry, *sf; - - list_for_each_entry_safe(entry, sf, &reloc_list->bos, lhead) { - qxl_bo_unreserve(entry->bo); - list_del(&entry->lhead); - kfree(entry); - } -} - -int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo) -{ - struct qxl_bo_list *entry; - int ret; - - list_for_each_entry(entry, &reloc_list->bos, lhead) { - if (entry->bo == bo) - return 0; - } - - entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - entry->bo = bo; - list_add(&entry->lhead, &reloc_list->bos); - - ret = qxl_bo_reserve(bo, false); - if (ret) - return ret; - - if (!bo->pin_count) { - qxl_ttm_placement_from_domain(bo, bo->type); - ret = ttm_bo_validate(&bo->tbo, &bo->placement, - true, false); - if (ret) - return ret; - } - - /* allocate a surface for reserved + validated buffers */ - ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo); - if (ret) - return ret; - return 0; -} - int qxl_surf_evict(struct qxl_device *qdev) { return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0); diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index af10165adb0d..d458a140c024 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -88,7 +88,7 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type, extern int qxl_bo_create(struct qxl_device *qdev, unsigned long size, - bool kernel, u32 domain, + bool kernel, bool pinned, u32 domain, struct qxl_surface *surf, struct qxl_bo **bo_ptr); extern int qxl_bo_kmap(struct qxl_bo *bo, void **ptr); @@ -99,9 +99,7 @@ extern struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo); extern void qxl_bo_unref(struct qxl_bo **bo); extern int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr); extern int qxl_bo_unpin(struct qxl_bo *bo); -extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain); +extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned); extern bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo); -extern int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo); -extern void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed); #endif diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index 1a648e1da6a6..0109a9644cb2 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -38,7 +38,8 @@ static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE }; static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO }; -uint64_t + +static uint64_t qxl_release_alloc(struct qxl_device *qdev, int type, struct qxl_release **ret) { @@ -53,9 +54,9 @@ qxl_release_alloc(struct qxl_device *qdev, int type, return 0; } release->type = type; - release->bo_count = 0; release->release_offset = 0; release->surface_release_id = 0; + INIT_LIST_HEAD(&release->bos); idr_preload(GFP_KERNEL); spin_lock(&qdev->release_idr_lock); @@ -77,20 +78,20 @@ void qxl_release_free(struct qxl_device *qdev, struct qxl_release *release) { - int i; - - QXL_INFO(qdev, "release %d, type %d, %d bos\n", release->id, - release->type, release->bo_count); + struct qxl_bo_list *entry, *tmp; + QXL_INFO(qdev, "release %d, type %d\n", release->id, + release->type); if (release->surface_release_id) qxl_surface_id_dealloc(qdev, release->surface_release_id); - for (i = 0 ; i < release->bo_count; ++i) { + list_for_each_entry_safe(entry, tmp, &release->bos, tv.head) { + struct qxl_bo *bo = to_qxl_bo(entry->tv.bo); QXL_INFO(qdev, "release %llx\n", - drm_vma_node_offset_addr(&release->bos[i]->tbo.vma_node) + drm_vma_node_offset_addr(&entry->tv.bo->vma_node) - DRM_FILE_OFFSET); - qxl_fence_remove_release(&release->bos[i]->fence, release->id); - qxl_bo_unref(&release->bos[i]); + qxl_fence_remove_release(&bo->fence, release->id); + qxl_bo_unref(&bo); } spin_lock(&qdev->release_idr_lock); idr_remove(&qdev->release_idr, release->id); @@ -98,83 +99,117 @@ qxl_release_free(struct qxl_device *qdev, kfree(release); } -void -qxl_release_add_res(struct qxl_device *qdev, struct qxl_release *release, - struct qxl_bo *bo) -{ - int i; - for (i = 0; i < release->bo_count; i++) - if (release->bos[i] == bo) - return; - - if (release->bo_count >= QXL_MAX_RES) { - DRM_ERROR("exceeded max resource on a qxl_release item\n"); - return; - } - release->bos[release->bo_count++] = qxl_bo_ref(bo); -} - static int qxl_release_bo_alloc(struct qxl_device *qdev, struct qxl_bo **bo) { int ret; - ret = qxl_bo_create(qdev, PAGE_SIZE, false, QXL_GEM_DOMAIN_VRAM, NULL, + /* pin releases bo's they are too messy to evict */ + ret = qxl_bo_create(qdev, PAGE_SIZE, false, true, + QXL_GEM_DOMAIN_VRAM, NULL, bo); return ret; } -int qxl_release_reserve(struct qxl_device *qdev, - struct qxl_release *release, bool no_wait) +int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo) +{ + struct qxl_bo_list *entry; + + list_for_each_entry(entry, &release->bos, tv.head) { + if (entry->tv.bo == &bo->tbo) + return 0; + } + + entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + qxl_bo_ref(bo); + entry->tv.bo = &bo->tbo; + list_add_tail(&entry->tv.head, &release->bos); + return 0; +} + +static int qxl_release_validate_bo(struct qxl_bo *bo) { int ret; - if (atomic_inc_return(&release->bos[0]->reserve_count) == 1) { - ret = qxl_bo_reserve(release->bos[0], no_wait); + + if (!bo->pin_count) { + qxl_ttm_placement_from_domain(bo, bo->type, false); + ret = ttm_bo_validate(&bo->tbo, &bo->placement, + true, false); if (ret) return ret; } + + /* allocate a surface for reserved + validated buffers */ + ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo); + if (ret) + return ret; + return 0; +} + +int qxl_release_reserve_list(struct qxl_release *release, bool no_intr) +{ + int ret; + struct qxl_bo_list *entry; + + /* if only one object on the release its the release itself + since these objects are pinned no need to reserve */ + if (list_is_singular(&release->bos)) + return 0; + + ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos); + if (ret) + return ret; + + list_for_each_entry(entry, &release->bos, tv.head) { + struct qxl_bo *bo = to_qxl_bo(entry->tv.bo); + + ret = qxl_release_validate_bo(bo); + if (ret) { + ttm_eu_backoff_reservation(&release->ticket, &release->bos); + return ret; + } + } return 0; } -void qxl_release_unreserve(struct qxl_device *qdev, - struct qxl_release *release) +void qxl_release_backoff_reserve_list(struct qxl_release *release) { - if (atomic_dec_and_test(&release->bos[0]->reserve_count)) - qxl_bo_unreserve(release->bos[0]); + /* if only one object on the release its the release itself + since these objects are pinned no need to reserve */ + if (list_is_singular(&release->bos)) + return; + + ttm_eu_backoff_reservation(&release->ticket, &release->bos); } + int qxl_alloc_surface_release_reserved(struct qxl_device *qdev, enum qxl_surface_cmd_type surface_cmd_type, struct qxl_release *create_rel, struct qxl_release **release) { - int ret; - if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) { int idr_ret; + struct qxl_bo_list *entry = list_first_entry(&create_rel->bos, struct qxl_bo_list, tv.head); struct qxl_bo *bo; union qxl_release_info *info; /* stash the release after the create command */ idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release); - bo = qxl_bo_ref(create_rel->bos[0]); + bo = qxl_bo_ref(to_qxl_bo(entry->tv.bo)); (*release)->release_offset = create_rel->release_offset + 64; - qxl_release_add_res(qdev, *release, bo); + qxl_release_list_add(*release, bo); - ret = qxl_release_reserve(qdev, *release, false); - if (ret) { - DRM_ERROR("release reserve failed\n"); - goto out_unref; - } info = qxl_release_map(qdev, *release); info->id = idr_ret; qxl_release_unmap(qdev, *release, info); - -out_unref: qxl_bo_unref(&bo); - return ret; + return 0; } return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd), @@ -187,7 +222,7 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, { struct qxl_bo *bo; int idr_ret; - int ret; + int ret = 0; union qxl_release_info *info; int cur_idx; @@ -216,11 +251,6 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, mutex_unlock(&qdev->release_mutex); return ret; } - - /* pin releases bo's they are too messy to evict */ - ret = qxl_bo_reserve(qdev->current_release_bo[cur_idx], false); - qxl_bo_pin(qdev->current_release_bo[cur_idx], QXL_GEM_DOMAIN_VRAM, NULL); - qxl_bo_unreserve(qdev->current_release_bo[cur_idx]); } bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]); @@ -231,36 +261,18 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size, if (rbo) *rbo = bo; - qxl_release_add_res(qdev, *release, bo); - - ret = qxl_release_reserve(qdev, *release, false); mutex_unlock(&qdev->release_mutex); - if (ret) - goto out_unref; + + qxl_release_list_add(*release, bo); info = qxl_release_map(qdev, *release); info->id = idr_ret; qxl_release_unmap(qdev, *release, info); -out_unref: qxl_bo_unref(&bo); return ret; } -int qxl_fence_releaseable(struct qxl_device *qdev, - struct qxl_release *release) -{ - int i, ret; - for (i = 0; i < release->bo_count; i++) { - if (!release->bos[i]->tbo.sync_obj) - release->bos[i]->tbo.sync_obj = &release->bos[i]->fence; - ret = qxl_fence_add_release(&release->bos[i]->fence, release->id); - if (ret) - return ret; - } - return 0; -} - struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev, uint64_t id) { @@ -273,10 +285,7 @@ struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev, DRM_ERROR("failed to find id in release_idr\n"); return NULL; } - if (release->bo_count < 1) { - DRM_ERROR("read a released resource with 0 bos\n"); - return NULL; - } + return release; } @@ -285,9 +294,12 @@ union qxl_release_info *qxl_release_map(struct qxl_device *qdev, { void *ptr; union qxl_release_info *info; - struct qxl_bo *bo = release->bos[0]; + struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head); + struct qxl_bo *bo = to_qxl_bo(entry->tv.bo); ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE); + if (!ptr) + return NULL; info = ptr + (release->release_offset & ~PAGE_SIZE); return info; } @@ -296,9 +308,51 @@ void qxl_release_unmap(struct qxl_device *qdev, struct qxl_release *release, union qxl_release_info *info) { - struct qxl_bo *bo = release->bos[0]; + struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head); + struct qxl_bo *bo = to_qxl_bo(entry->tv.bo); void *ptr; ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE); qxl_bo_kunmap_atomic_page(qdev, bo, ptr); } + +void qxl_release_fence_buffer_objects(struct qxl_release *release) +{ + struct ttm_validate_buffer *entry; + struct ttm_buffer_object *bo; + struct ttm_bo_global *glob; + struct ttm_bo_device *bdev; + struct ttm_bo_driver *driver; + struct qxl_bo *qbo; + + /* if only one object on the release its the release itself + since these objects are pinned no need to reserve */ + if (list_is_singular(&release->bos)) + return; + + bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo; + bdev = bo->bdev; + driver = bdev->driver; + glob = bo->glob; + + spin_lock(&glob->lru_lock); + spin_lock(&bdev->fence_lock); + + list_for_each_entry(entry, &release->bos, head) { + bo = entry->bo; + qbo = to_qxl_bo(bo); + + if (!entry->bo->sync_obj) + entry->bo->sync_obj = &qbo->fence; + + qxl_fence_add_release_locked(&qbo->fence, release->id); + + ttm_bo_add_to_lru(bo); + ww_mutex_unlock(&bo->resv->lock); + entry->reserved = false; + } + spin_unlock(&bdev->fence_lock); + spin_unlock(&glob->lru_lock); + ww_acquire_fini(&release->ticket); +} + diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 489cb8cece4d..1dfd84cda2a1 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -206,7 +206,7 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo, return; } qbo = container_of(bo, struct qxl_bo, tbo); - qxl_ttm_placement_from_domain(qbo, QXL_GEM_DOMAIN_CPU); + qxl_ttm_placement_from_domain(qbo, QXL_GEM_DOMAIN_CPU, false); *placement = qbo->placement; } diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index c239739736db..3569d89b9e41 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -44,6 +44,41 @@ static char *pre_emph_names[] = { }; /***** radeon AUX functions *****/ + +/* Atom needs data in little endian format + * so swap as appropriate when copying data to + * or from atom. Note that atom operates on + * dw units. + */ +static void radeon_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) +{ +#ifdef __BIG_ENDIAN + u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ + u32 *dst32, *src32; + int i; + + memcpy(src_tmp, src, num_bytes); + src32 = (u32 *)src_tmp; + dst32 = (u32 *)dst_tmp; + if (to_le) { + for (i = 0; i < ((num_bytes + 3) / 4); i++) + dst32[i] = cpu_to_le32(src32[i]); + memcpy(dst, dst_tmp, num_bytes); + } else { + u8 dws = num_bytes & ~3; + for (i = 0; i < ((num_bytes + 3) / 4); i++) + dst32[i] = le32_to_cpu(src32[i]); + memcpy(dst, dst_tmp, dws); + if (num_bytes % 4) { + for (i = 0; i < (num_bytes % 4); i++) + dst[dws+i] = dst_tmp[dws+i]; + } + } +#else + memcpy(dst, src, num_bytes); +#endif +} + union aux_channel_transaction { PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1; PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2; @@ -65,10 +100,10 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1); - memcpy(base, send, send_bytes); + radeon_copy_swap(base, send, send_bytes, true); - args.v1.lpAuxRequest = 0 + 4; - args.v1.lpDataOut = 16 + 4; + args.v1.lpAuxRequest = cpu_to_le16((u16)(0 + 4)); + args.v1.lpDataOut = cpu_to_le16((u16)(16 + 4)); args.v1.ucDataOutLen = 0; args.v1.ucChannelID = chan->rec.i2c_id; args.v1.ucDelay = delay / 10; @@ -102,7 +137,7 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, recv_bytes = recv_size; if (recv && recv_size) - memcpy(recv, base + 16, recv_bytes); + radeon_copy_swap(recv, base + 16, recv_bytes, false); return recv_bytes; } diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 319e1ee1844a..cfc1d28ade39 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3166,7 +3166,7 @@ int r600_copy_cpdma(struct radeon_device *rdev, size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); num_loops = DIV_ROUND_UP(size_in_bytes, 0x1fffff); - r = radeon_ring_lock(rdev, ring, num_loops * 6 + 21); + r = radeon_ring_lock(rdev, ring, num_loops * 6 + 24); if (r) { DRM_ERROR("radeon: moving bo (%d).\n", r); radeon_semaphore_free(rdev, &sem, NULL); @@ -3181,6 +3181,9 @@ int r600_copy_cpdma(struct radeon_device *rdev, radeon_semaphore_free(rdev, &sem, NULL); } + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1)); + radeon_ring_write(ring, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2); + radeon_ring_write(ring, WAIT_3D_IDLE_bit); for (i = 0; i < num_loops; i++) { cur_size_in_bytes = size_in_bytes; if (cur_size_in_bytes > 0x1fffff) diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index b88f54b134ab..e5c860f4ccbe 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -278,9 +278,9 @@ bool r600_dynamicpm_enabled(struct radeon_device *rdev) void r600_enable_sclk_control(struct radeon_device *rdev, bool enable) { if (enable) - WREG32_P(GENERAL_PWRMGT, 0, ~SCLK_PWRMGT_OFF); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF); else - WREG32_P(GENERAL_PWRMGT, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); + WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); } void r600_enable_mclk_control(struct radeon_device *rdev, bool enable) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 78bec1a58ed1..f8f8b3113ddd 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1161,6 +1161,7 @@ static struct radeon_asic rv6xx_asic = { .get_mclk = &rv6xx_dpm_get_mclk, .print_power_state = &rv6xx_dpm_print_power_state, .debugfs_print_current_performance_level = &rv6xx_dpm_debugfs_print_current_performance_level, + .force_performance_level = &rv6xx_dpm_force_performance_level, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index ca1895709908..902479fa737f 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -421,6 +421,8 @@ void rv6xx_dpm_print_power_state(struct radeon_device *rdev, struct radeon_ps *ps); void rv6xx_dpm_debugfs_print_current_performance_level(struct radeon_device *rdev, struct seq_file *m); +int rv6xx_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level); /* rs780 dpm */ int rs780_dpm_init(struct radeon_device *rdev); int rs780_dpm_enable(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 78edadc9e86b..68ce36056019 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -147,7 +147,7 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, enum radeon_combios_table_offset table) { struct radeon_device *rdev = dev->dev_private; - int rev; + int rev, size; uint16_t offset = 0, check_offset; if (!rdev->bios) @@ -156,174 +156,106 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, switch (table) { /* absolute offset tables */ case COMBIOS_ASIC_INIT_1_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0xc); - if (check_offset) - offset = check_offset; + check_offset = 0xc; break; case COMBIOS_BIOS_SUPPORT_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x14); - if (check_offset) - offset = check_offset; + check_offset = 0x14; break; case COMBIOS_DAC_PROGRAMMING_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x2a); - if (check_offset) - offset = check_offset; + check_offset = 0x2a; break; case COMBIOS_MAX_COLOR_DEPTH_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x2c); - if (check_offset) - offset = check_offset; + check_offset = 0x2c; break; case COMBIOS_CRTC_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x2e); - if (check_offset) - offset = check_offset; + check_offset = 0x2e; break; case COMBIOS_PLL_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x30); - if (check_offset) - offset = check_offset; + check_offset = 0x30; break; case COMBIOS_TV_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x32); - if (check_offset) - offset = check_offset; + check_offset = 0x32; break; case COMBIOS_DFP_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x34); - if (check_offset) - offset = check_offset; + check_offset = 0x34; break; case COMBIOS_HW_CONFIG_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x36); - if (check_offset) - offset = check_offset; + check_offset = 0x36; break; case COMBIOS_MULTIMEDIA_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x38); - if (check_offset) - offset = check_offset; + check_offset = 0x38; break; case COMBIOS_TV_STD_PATCH_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x3e); - if (check_offset) - offset = check_offset; + check_offset = 0x3e; break; case COMBIOS_LCD_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x40); - if (check_offset) - offset = check_offset; + check_offset = 0x40; break; case COMBIOS_MOBILE_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x42); - if (check_offset) - offset = check_offset; + check_offset = 0x42; break; case COMBIOS_PLL_INIT_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x46); - if (check_offset) - offset = check_offset; + check_offset = 0x46; break; case COMBIOS_MEM_CONFIG_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x48); - if (check_offset) - offset = check_offset; + check_offset = 0x48; break; case COMBIOS_SAVE_MASK_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x4a); - if (check_offset) - offset = check_offset; + check_offset = 0x4a; break; case COMBIOS_HARDCODED_EDID_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x4c); - if (check_offset) - offset = check_offset; + check_offset = 0x4c; break; case COMBIOS_ASIC_INIT_2_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x4e); - if (check_offset) - offset = check_offset; + check_offset = 0x4e; break; case COMBIOS_CONNECTOR_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x50); - if (check_offset) - offset = check_offset; + check_offset = 0x50; break; case COMBIOS_DYN_CLK_1_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x52); - if (check_offset) - offset = check_offset; + check_offset = 0x52; break; case COMBIOS_RESERVED_MEM_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x54); - if (check_offset) - offset = check_offset; + check_offset = 0x54; break; case COMBIOS_EXT_TMDS_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x58); - if (check_offset) - offset = check_offset; + check_offset = 0x58; break; case COMBIOS_MEM_CLK_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x5a); - if (check_offset) - offset = check_offset; + check_offset = 0x5a; break; case COMBIOS_EXT_DAC_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x5c); - if (check_offset) - offset = check_offset; + check_offset = 0x5c; break; case COMBIOS_MISC_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x5e); - if (check_offset) - offset = check_offset; + check_offset = 0x5e; break; case COMBIOS_CRT_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x60); - if (check_offset) - offset = check_offset; + check_offset = 0x60; break; case COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x62); - if (check_offset) - offset = check_offset; + check_offset = 0x62; break; case COMBIOS_COMPONENT_VIDEO_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x64); - if (check_offset) - offset = check_offset; + check_offset = 0x64; break; case COMBIOS_FAN_SPEED_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x66); - if (check_offset) - offset = check_offset; + check_offset = 0x66; break; case COMBIOS_OVERDRIVE_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x68); - if (check_offset) - offset = check_offset; + check_offset = 0x68; break; case COMBIOS_OEM_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x6a); - if (check_offset) - offset = check_offset; + check_offset = 0x6a; break; case COMBIOS_DYN_CLK_2_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x6c); - if (check_offset) - offset = check_offset; + check_offset = 0x6c; break; case COMBIOS_POWER_CONNECTOR_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x6e); - if (check_offset) - offset = check_offset; + check_offset = 0x6e; break; case COMBIOS_I2C_INFO_TABLE: - check_offset = RBIOS16(rdev->bios_header_start + 0x70); - if (check_offset) - offset = check_offset; + check_offset = 0x70; break; /* relative offset tables */ case COMBIOS_ASIC_INIT_3_TABLE: /* offset from misc info */ @@ -439,11 +371,16 @@ static uint16_t combios_get_table_offset(struct drm_device *dev, } break; default: + check_offset = 0; break; } - return offset; + size = RBIOS8(rdev->bios_header_start + 0x6); + /* check absolute offset tables */ + if (table < COMBIOS_ASIC_INIT_3_TABLE && check_offset && check_offset < size) + offset = RBIOS16(rdev->bios_header_start + check_offset); + return offset; } bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) @@ -965,16 +902,22 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct dac = RBIOS8(dac_info + 0x3) & 0xf; p_dac->ps2_pdac_adj = (bg << 8) | (dac); } - /* if the values are all zeros, use the table */ - if (p_dac->ps2_pdac_adj) + /* if the values are zeros, use the table */ + if ((dac == 0) || (bg == 0)) + found = 0; + else found = 1; } /* quirks */ + /* Radeon 7000 (RV100) */ + if (((dev->pdev->device == 0x5159) && + (dev->pdev->subsystem_vendor == 0x174B) && + (dev->pdev->subsystem_device == 0x7c28)) || /* Radeon 9100 (R200) */ - if ((dev->pdev->device == 0x514D) && + ((dev->pdev->device == 0x514D) && (dev->pdev->subsystem_vendor == 0x174B) && - (dev->pdev->subsystem_device == 0x7149)) { + (dev->pdev->subsystem_device == 0x7149))) { /* vbios value is bad, use the default */ found = 0; } diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index d9d31a383276..6a51d943ccf4 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -466,7 +466,7 @@ int radeon_vm_manager_init(struct radeon_device *rdev) size += rdev->vm_manager.max_pfn * 8; size *= 2; r = radeon_sa_bo_manager_init(rdev, &rdev->vm_manager.sa_manager, - RADEON_VM_PTB_ALIGN(size), + RADEON_GPU_PAGE_ALIGN(size), RADEON_VM_PTB_ALIGN_SIZE, RADEON_GEM_DOMAIN_VRAM); if (r) { @@ -621,7 +621,7 @@ int radeon_vm_alloc_pt(struct radeon_device *rdev, struct radeon_vm *vm) } retry: - pd_size = RADEON_VM_PTB_ALIGN(radeon_vm_directory_size(rdev)); + pd_size = radeon_vm_directory_size(rdev); r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager, &vm->page_directory, pd_size, RADEON_VM_PTB_ALIGN_SIZE, false); @@ -953,8 +953,8 @@ static int radeon_vm_update_pdes(struct radeon_device *rdev, retry: r = radeon_sa_bo_new(rdev, &rdev->vm_manager.sa_manager, &vm->page_tables[pt_idx], - RADEON_VM_PTB_ALIGN(RADEON_VM_PTE_COUNT * 8), - RADEON_VM_PTB_ALIGN_SIZE, false); + RADEON_VM_PTE_COUNT * 8, + RADEON_GPU_PAGE_SIZE, false); if (r == -ENOMEM) { r = radeon_vm_evict(rdev, vm); diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index 65e33f387341..363018c60412 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -819,7 +819,7 @@ static void rv6xx_program_memory_timing_parameters(struct radeon_device *rdev) POWERMODE1(calculate_memory_refresh_rate(rdev, pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) | POWERMODE2(calculate_memory_refresh_rate(rdev, - pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) | + pi->hw.sclks[R600_POWER_LEVEL_HIGH])) | POWERMODE3(calculate_memory_refresh_rate(rdev, pi->hw.sclks[R600_POWER_LEVEL_HIGH]))); WREG32(ARB_RFSH_RATE, arb_refresh_rate); @@ -1182,10 +1182,10 @@ static void rv6xx_program_display_gap(struct radeon_device *rdev) u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL); tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK); - if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) { + if (rdev->pm.dpm.new_active_crtcs & 1) { tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); - } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) { + } else if (rdev->pm.dpm.new_active_crtcs & 2) { tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); } else { @@ -1670,6 +1670,8 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev) struct radeon_ps *old_ps = rdev->pm.dpm.current_ps; int ret; + pi->restricted_levels = 0; + rv6xx_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv6xx_clear_vc(rdev); @@ -1756,6 +1758,8 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev) rv6xx_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); + rdev->pm.dpm.forced_level = RADEON_DPM_FORCED_LEVEL_AUTO; + return 0; } @@ -2085,3 +2089,34 @@ u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low) else return requested_state->high.mclk; } + +int rv6xx_dpm_force_performance_level(struct radeon_device *rdev, + enum radeon_dpm_forced_level level) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (level == RADEON_DPM_FORCED_LEVEL_HIGH) { + pi->restricted_levels = 3; + } else if (level == RADEON_DPM_FORCED_LEVEL_LOW) { + pi->restricted_levels = 2; + } else { + pi->restricted_levels = 0; + } + + rv6xx_clear_vc(rdev); + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); + r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW); + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false); + rv6xx_enable_medium(rdev); + rv6xx_enable_high(rdev); + if (pi->restricted_levels == 3) + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, false); + rv6xx_program_vc(rdev); + rv6xx_program_at(rdev); + + rdev->pm.dpm.forced_level = level; + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 72887df8dd76..c590cd9dca0b 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -7,3 +7,10 @@ config DRM_RCAR_DU help Choose this option if you have an R-Car chipset. If M is selected the module will be called rcar-du-drm. + +config DRM_RCAR_LVDS + bool "R-Car DU LVDS Encoder Support" + depends on DRM_RCAR_DU + help + Enable support the R-Car Display Unit embedded LVDS encoders + (currently only on R8A7790). diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 7333c0094015..12b8d4477835 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -1,8 +1,12 @@ rcar-du-drm-y := rcar_du_crtc.o \ rcar_du_drv.o \ + rcar_du_encoder.o \ + rcar_du_group.o \ rcar_du_kms.o \ - rcar_du_lvds.o \ + rcar_du_lvdscon.o \ rcar_du_plane.o \ - rcar_du_vga.o + rcar_du_vgacon.o -obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o +rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o + +obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 24183fb93592..33df7a583143 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -23,30 +23,26 @@ #include "rcar_du_crtc.h" #include "rcar_du_drv.h" #include "rcar_du_kms.h" -#include "rcar_du_lvds.h" #include "rcar_du_plane.h" #include "rcar_du_regs.h" -#include "rcar_du_vga.h" - -#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; return rcar_du_read(rcdu, rcrtc->mmio_offset + reg); } static void rcar_du_crtc_write(struct rcar_du_crtc *rcrtc, u32 reg, u32 data) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_write(rcdu, rcrtc->mmio_offset + reg, data); } static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_write(rcdu, rcrtc->mmio_offset + reg, rcar_du_read(rcdu, rcrtc->mmio_offset + reg) & ~clr); @@ -54,7 +50,7 @@ static void rcar_du_crtc_clr(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr) static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_write(rcdu, rcrtc->mmio_offset + reg, rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); @@ -63,29 +59,48 @@ static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 clr, u32 set) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; + struct rcar_du_device *rcdu = rcrtc->group->dev; u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg); rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); } +static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) +{ + int ret; + + ret = clk_prepare_enable(rcrtc->clock); + if (ret < 0) + return ret; + + ret = rcar_du_group_get(rcrtc->group); + if (ret < 0) + clk_disable_unprepare(rcrtc->clock); + + return ret; +} + +static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) +{ + rcar_du_group_put(rcrtc->group); + clk_disable_unprepare(rcrtc->clock); +} + static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) { - struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private; - const struct drm_display_mode *mode = &crtc->mode; + const struct drm_display_mode *mode = &rcrtc->crtc.mode; unsigned long clk; u32 value; u32 div; /* Dot clock */ - clk = clk_get_rate(rcdu->clock); + clk = clk_get_rate(rcrtc->clock); div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); div = clamp(div, 1U, 64U) - 1; - rcar_du_write(rcdu, rcrtc->index ? ESCR2 : ESCR, - ESCR_DCLKSEL_CLKS | div); - rcar_du_write(rcdu, rcrtc->index ? OTAR2 : OTAR, 0); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, + ESCR_DCLKSEL_CLKS | div); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); /* Signal polarities */ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) @@ -112,68 +127,25 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) rcar_du_crtc_write(rcrtc, DEWR, mode->hdisplay); } -static void rcar_du_crtc_set_routing(struct rcar_du_crtc *rcrtc) -{ - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - u32 dorcr = rcar_du_read(rcdu, DORCR); - - dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); - - /* Set the DU1 pins sources. Select CRTC 0 if explicitly requested and - * CRTC 1 in all other cases to avoid cloning CRTC 0 to DU0 and DU1 by - * default. - */ - if (rcrtc->outputs & (1 << 1) && rcrtc->index == 0) - dorcr |= DORCR_PG2D_DS1; - else - dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; - - rcar_du_write(rcdu, DORCR, dorcr); -} - -static void __rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) -{ - rcar_du_write(rcdu, DSYSR, - (rcar_du_read(rcdu, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | - (start ? DSYSR_DEN : DSYSR_DRES)); -} - -static void rcar_du_start_stop(struct rcar_du_device *rcdu, bool start) -{ - /* Many of the configuration bits are only updated when the display - * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some - * of those bits could be pre-configured, but others (especially the - * bits related to plane assignment to display timing controllers) need - * to be modified at runtime. - * - * Restart the display controller if a start is requested. Sorry for the - * flicker. It should be possible to move most of the "DRES-update" bits - * setup to driver initialization time and minimize the number of cases - * when the display controller will have to be restarted. - */ - if (start) { - if (rcdu->used_crtcs++ != 0) - __rcar_du_start_stop(rcdu, false); - __rcar_du_start_stop(rcdu, true); - } else { - if (--rcdu->used_crtcs == 0) - __rcar_du_start_stop(rcdu, false); - } -} - -void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output) +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev; /* Store the route from the CRTC output to the DU output. The DU will be * configured when starting the CRTC. */ - rcrtc->outputs |= 1 << output; + rcrtc->outputs |= BIT(output); + + /* Store RGB routing to DPAD0 for R8A7790. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_DEFR8) && + output == RCAR_DU_OUTPUT_DPAD0) + rcdu->dpad0_source = rcrtc->index; } void rcar_du_crtc_update_planes(struct drm_crtc *crtc) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; unsigned int num_planes = 0; @@ -182,8 +154,8 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) u32 dptsr = 0; u32 dspr = 0; - for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; unsigned int j; if (plane->crtc != &rcrtc->crtc || !plane->enabled) @@ -220,8 +192,8 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) /* Select display timing and dot clock generator 2 for planes associated * with superposition controller 2. */ - if (rcrtc->index) { - u32 value = rcar_du_read(rcdu, DPTSR); + if (rcrtc->index % 2) { + u32 value = rcar_du_group_read(rcrtc->group, DPTSR); /* The DPTSR register is updated when the display controller is * stopped. We thus need to restart the DU. Once again, sorry @@ -231,21 +203,19 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc) * occur only if we need to break the pre-association. */ if (value != dptsr) { - rcar_du_write(rcdu, DPTSR, dptsr); - if (rcdu->used_crtcs) { - __rcar_du_start_stop(rcdu, false); - __rcar_du_start_stop(rcdu, true); - } + rcar_du_group_write(rcrtc->group, DPTSR, dptsr); + if (rcrtc->group->used_crtcs) + rcar_du_group_restart(rcrtc->group); } } - rcar_du_write(rcdu, rcrtc->index ? DS2PR : DS1PR, dspr); + rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, + dspr); } static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private; unsigned int i; if (rcrtc->started) @@ -260,16 +230,16 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) /* Configure display timings and output routing */ rcar_du_crtc_set_display_timing(rcrtc); - rcar_du_crtc_set_routing(rcrtc); + rcar_du_group_set_routing(rcrtc->group); - mutex_lock(&rcdu->planes.lock); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = true; rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rcrtc->group->planes.lock); /* Setup planes. */ - for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; if (plane->crtc != crtc || !plane->enabled) continue; @@ -283,7 +253,7 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) */ rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_MASTER); - rcar_du_start_stop(rcdu, true); + rcar_du_group_start_stop(rcrtc->group, true); rcrtc->started = true; } @@ -291,42 +261,37 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) { struct drm_crtc *crtc = &rcrtc->crtc; - struct rcar_du_device *rcdu = crtc->dev->dev_private; if (!rcrtc->started) return; - mutex_lock(&rcdu->planes.lock); + mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = false; rcar_du_crtc_update_planes(crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rcrtc->group->planes.lock); /* Select switch sync mode. This stops display operation and configures * the HSYNC and VSYNC signals as inputs. */ rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); - rcar_du_start_stop(rcdu, false); + rcar_du_group_start_stop(rcrtc->group, false); rcrtc->started = false; } void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); } void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { - struct rcar_du_device *rcdu = rcrtc->crtc.dev->dev_private; - if (rcrtc->dpms != DRM_MODE_DPMS_ON) return; - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); } @@ -340,18 +305,17 @@ static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc) static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); if (rcrtc->dpms == mode) return; if (mode == DRM_MODE_DPMS_ON) { - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); } else { rcar_du_crtc_stop(rcrtc); - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); } rcrtc->dpms = mode; @@ -367,13 +331,12 @@ static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc, static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); /* We need to access the hardware during mode set, acquire a reference - * to the DU. + * to the CRTC. */ - rcar_du_get(rcdu); + rcar_du_crtc_get(rcrtc); /* Stop the CRTC and release the plane. Force the DPMS mode to off as a * result. @@ -390,8 +353,8 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - struct rcar_du_device *rcdu = crtc->dev->dev_private; struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_device *rcdu = rcrtc->group->dev; const struct rcar_du_format_info *format; int ret; @@ -423,10 +386,10 @@ static int rcar_du_crtc_mode_set(struct drm_crtc *crtc, error: /* There's no rollback/abort operation to clean up in case of error. We - * thus need to release the reference to the DU acquired in prepare() + * thus need to release the reference to the CRTC acquired in prepare() * here. */ - rcar_du_put(rcdu); + rcar_du_crtc_put(rcrtc); return ret; } @@ -514,6 +477,24 @@ static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc) drm_vblank_put(dev, rcrtc->index); } +static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) +{ + struct rcar_du_crtc *rcrtc = arg; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = rcar_du_crtc_read(rcrtc, DSSR); + rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); + + if (status & DSSR_VBK) { + drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); + rcar_du_crtc_finish_page_flip(rcrtc); + ret = IRQ_HANDLED; + } + + return ret; +} + static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) @@ -549,16 +530,41 @@ static const struct drm_crtc_funcs crtc_funcs = { .page_flip = rcar_du_crtc_page_flip, }; -int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) +int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { + static const unsigned int mmio_offsets[] = { + DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET + }; + + struct rcar_du_device *rcdu = rgrp->dev; + struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; + unsigned int irqflags; + char clk_name[5]; + char *name; + int irq; int ret; - rcrtc->mmio_offset = index ? DISP2_REG_OFFSET : 0; + /* Get the CRTC clock. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + sprintf(clk_name, "du.%u", index); + name = clk_name; + } else { + name = NULL; + } + + rcrtc->clock = devm_clk_get(rcdu->dev, name); + if (IS_ERR(rcrtc->clock)) { + dev_err(rcdu->dev, "no clock for CRTC %u\n", index); + return PTR_ERR(rcrtc->clock); + } + + rcrtc->group = rgrp; + rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; rcrtc->dpms = DRM_MODE_DPMS_OFF; - rcrtc->plane = &rcdu->planes.planes[index]; + rcrtc->plane = &rgrp->planes.planes[index % 2]; rcrtc->plane->crtc = crtc; @@ -568,6 +574,28 @@ int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index) drm_crtc_helper_add(crtc, &crtc_helper_funcs); + /* Register the interrupt handler. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { + irq = platform_get_irq(pdev, index); + irqflags = 0; + } else { + irq = platform_get_irq(pdev, 0); + irqflags = IRQF_SHARED; + } + + if (irq < 0) { + dev_err(rcdu->dev, "no IRQ for CRTC %u\n", index); + return ret; + } + + ret = devm_request_irq(rcdu->dev, irq, rcar_du_crtc_irq, irqflags, + dev_name(rcdu->dev), rcrtc); + if (ret < 0) { + dev_err(rcdu->dev, + "failed to register IRQ for CRTC %u\n", index); + return ret; + } + return 0; } @@ -580,16 +608,3 @@ void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable) rcar_du_crtc_clr(rcrtc, DIER, DIER_VBE); } } - -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc) -{ - u32 status; - - status = rcar_du_crtc_read(rcrtc, DSSR); - rcar_du_crtc_write(rcrtc, DSRCR, status & DSRCR_MASK); - - if (status & DSSR_VBK) { - drm_handle_vblank(rcrtc->crtc.dev, rcrtc->index); - rcar_du_crtc_finish_page_flip(rcrtc); - } -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 2a0365bcbd14..43e7575c700c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -15,16 +15,18 @@ #define __RCAR_DU_CRTC_H__ #include <linux/mutex.h> +#include <linux/platform_data/rcar-du.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> -struct rcar_du_device; +struct rcar_du_group; struct rcar_du_plane; struct rcar_du_crtc { struct drm_crtc crtc; + struct clk *clock; unsigned int mmio_offset; unsigned int index; bool started; @@ -33,18 +35,21 @@ struct rcar_du_crtc { unsigned int outputs; int dpms; + struct rcar_du_group *group; struct rcar_du_plane *plane; }; -int rcar_du_crtc_create(struct rcar_du_device *rcdu, unsigned int index); +#define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) + +int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index); void rcar_du_crtc_enable_vblank(struct rcar_du_crtc *rcrtc, bool enable); -void rcar_du_crtc_irq(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc, struct drm_file *file); void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc); void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc); -void rcar_du_crtc_route_output(struct drm_crtc *crtc, unsigned int output); +void rcar_du_crtc_route_output(struct drm_crtc *crtc, + enum rcar_du_output output); void rcar_du_crtc_update_planes(struct drm_crtc *crtc); #endif /* __RCAR_DU_CRTC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 5069d9c234bb..0a9f1bb88337 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -21,6 +21,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> #include "rcar_du_crtc.h" @@ -29,74 +30,21 @@ #include "rcar_du_regs.h" /* ----------------------------------------------------------------------------- - * Core device operations - */ - -/* - * rcar_du_get - Acquire a reference to the DU - * - * Acquiring a reference enables the device clock and setup core registers. A - * reference must be held before accessing any hardware registers. - * - * This function must be called with the DRM mode_config lock held. - * - * Return 0 in case of success or a negative error code otherwise. - */ -int rcar_du_get(struct rcar_du_device *rcdu) -{ - int ret; - - if (rcdu->use_count) - goto done; - - /* Enable clocks before accessing the hardware. */ - ret = clk_prepare_enable(rcdu->clock); - if (ret < 0) - return ret; - - /* Enable extended features */ - rcar_du_write(rcdu, DEFR, DEFR_CODE | DEFR_DEFE); - rcar_du_write(rcdu, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); - rcar_du_write(rcdu, DEFR3, DEFR3_CODE | DEFR3_DEFE3); - rcar_du_write(rcdu, DEFR4, DEFR4_CODE); - rcar_du_write(rcdu, DEFR5, DEFR5_CODE | DEFR5_DEFE5); - - /* Use DS1PR and DS2PR to configure planes priorities and connects the - * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. - */ - rcar_du_write(rcdu, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS); - -done: - rcdu->use_count++; - return 0; -} - -/* - * rcar_du_put - Release a reference to the DU - * - * Releasing the last reference disables the device clock. - * - * This function must be called with the DRM mode_config lock held. - */ -void rcar_du_put(struct rcar_du_device *rcdu) -{ - if (--rcdu->use_count) - return; - - clk_disable_unprepare(rcdu->clock); -} - -/* ----------------------------------------------------------------------------- * DRM operations */ static int rcar_du_unload(struct drm_device *dev) { + struct rcar_du_device *rcdu = dev->dev_private; + + if (rcdu->fbdev) + drm_fbdev_cma_fini(rcdu->fbdev); + drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); - drm_irq_uninstall(dev); + dev->irq_enabled = 0; dev->dev_private = NULL; return 0; @@ -107,7 +55,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) struct platform_device *pdev = dev->platformdev; struct rcar_du_platform_data *pdata = pdev->dev.platform_data; struct rcar_du_device *rcdu; - struct resource *ioarea; struct resource *mem; int ret; @@ -124,35 +71,15 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) rcdu->dev = &pdev->dev; rcdu->pdata = pdata; + rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data; rcdu->ddev = dev; dev->dev_private = rcdu; - /* I/O resources and clocks */ + /* I/O resources */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (mem == NULL) { - dev_err(&pdev->dev, "failed to get memory resource\n"); - return -EINVAL; - } - - ioarea = devm_request_mem_region(&pdev->dev, mem->start, - resource_size(mem), pdev->name); - if (ioarea == NULL) { - dev_err(&pdev->dev, "failed to request memory region\n"); - return -EBUSY; - } - - rcdu->mmio = devm_ioremap_nocache(&pdev->dev, ioarea->start, - resource_size(ioarea)); - if (rcdu->mmio == NULL) { - dev_err(&pdev->dev, "failed to remap memory resource\n"); - return -ENOMEM; - } - - rcdu->clock = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(rcdu->clock)) { - dev_err(&pdev->dev, "failed to get clock\n"); - return -ENOENT; - } + rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rcdu->mmio)) + return PTR_ERR(rcdu->mmio); /* DRM/KMS objects */ ret = rcar_du_modeset_init(rcdu); @@ -161,18 +88,14 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags) goto done; } - /* IRQ and vblank handling */ + /* vblank handling */ ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); if (ret < 0) { dev_err(&pdev->dev, "failed to initialize vblank\n"); goto done; } - ret = drm_irq_install(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to install IRQ handler\n"); - goto done; - } + dev->irq_enabled = 1; platform_set_drvdata(pdev, rcdu); @@ -188,20 +111,15 @@ static void rcar_du_preclose(struct drm_device *dev, struct drm_file *file) struct rcar_du_device *rcdu = dev->dev_private; unsigned int i; - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) + for (i = 0; i < rcdu->num_crtcs; ++i) rcar_du_crtc_cancel_page_flip(&rcdu->crtcs[i], file); } -static irqreturn_t rcar_du_irq(int irq, void *arg) +static void rcar_du_lastclose(struct drm_device *dev) { - struct drm_device *dev = arg; struct rcar_du_device *rcdu = dev->dev_private; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) - rcar_du_crtc_irq(&rcdu->crtcs[i]); - return IRQ_HANDLED; + drm_fbdev_cma_restore_mode(rcdu->fbdev); } static int rcar_du_enable_vblank(struct drm_device *dev, int crtc) @@ -236,12 +154,11 @@ static const struct file_operations rcar_du_fops = { }; static struct drm_driver rcar_du_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET - | DRIVER_PRIME, + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME, .load = rcar_du_load, .unload = rcar_du_unload, .preclose = rcar_du_preclose, - .irq_handler = rcar_du_irq, + .lastclose = rcar_du_lastclose, .get_vblank_counter = drm_vblank_count, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, @@ -313,6 +230,57 @@ static int rcar_du_remove(struct platform_device *pdev) return 0; } +static const struct rcar_du_device_info rcar_du_r8a7779_info = { + .features = 0, + .num_crtcs = 2, + .routes = { + /* R8A7779 has two RGB outputs and one (currently unsupported) + * TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + }, + .num_lvds = 0, +}; + +static const struct rcar_du_device_info rcar_du_r8a7790_info = { + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B + | RCAR_DU_FEATURE_DEFR8, + .num_crtcs = 3, + .routes = { + /* R8A7790 has one RGB output, two LVDS outputs and one + * (currently unsupported) TCON output. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(2) | BIT(1) | BIT(0), + .encoder_type = DRM_MODE_ENCODER_NONE, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + [RCAR_DU_OUTPUT_LVDS1] = { + .possible_crtcs = BIT(2) | BIT(1), + .encoder_type = DRM_MODE_ENCODER_LVDS, + }, + }, + .num_lvds = 2, +}; + +static const struct platform_device_id rcar_du_id_table[] = { + { "rcar-du-r8a7779", (kernel_ulong_t)&rcar_du_r8a7779_info }, + { "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info }, + { } +}; + +MODULE_DEVICE_TABLE(platform, rcar_du_id_table); + static struct platform_driver rcar_du_platform_driver = { .probe = rcar_du_probe, .remove = rcar_du_remove, @@ -321,6 +289,7 @@ static struct platform_driver rcar_du_platform_driver = { .name = "rcar-du", .pm = &rcar_du_pm_ops, }, + .id_table = rcar_du_id_table, }; module_platform_driver(rcar_du_platform_driver); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 193cc59d495c..65d2d636b002 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -15,43 +15,74 @@ #define __RCAR_DU_DRV_H__ #include <linux/kernel.h> -#include <linux/mutex.h> #include <linux/platform_data/rcar-du.h> #include "rcar_du_crtc.h" -#include "rcar_du_plane.h" +#include "rcar_du_group.h" struct clk; struct device; struct drm_device; +struct drm_fbdev_cma; +struct rcar_du_device; +struct rcar_du_lvdsenc; + +#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ +#define RCAR_DU_FEATURE_ALIGN_128B (1 << 1) /* Align pitches to 128 bytes */ +#define RCAR_DU_FEATURE_DEFR8 (1 << 2) /* Has DEFR8 register */ + +/* + * struct rcar_du_output_routing - Output routing specification + * @possible_crtcs: bitmask of possible CRTCs for the output + * @encoder_type: DRM type of the internal encoder associated with the output + * + * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data + * specify the valid SoC outputs, which CRTCs can drive the output, and the type + * of in-SoC encoder for the output. + */ +struct rcar_du_output_routing { + unsigned int possible_crtcs; + unsigned int encoder_type; +}; + +/* + * struct rcar_du_device_info - DU model-specific information + * @features: device features (RCAR_DU_FEATURE_*) + * @num_crtcs: total number of CRTCs + * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) + * @num_lvds: number of internal LVDS encoders + */ +struct rcar_du_device_info { + unsigned int features; + unsigned int num_crtcs; + struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; + unsigned int num_lvds; +}; struct rcar_du_device { struct device *dev; const struct rcar_du_platform_data *pdata; + const struct rcar_du_device_info *info; void __iomem *mmio; - struct clk *clock; - unsigned int use_count; struct drm_device *ddev; + struct drm_fbdev_cma *fbdev; - struct rcar_du_crtc crtcs[2]; - unsigned int used_crtcs; + struct rcar_du_crtc crtcs[3]; unsigned int num_crtcs; - struct { - struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; - unsigned int free; - struct mutex lock; + struct rcar_du_group groups[2]; - struct drm_property *alpha; - struct drm_property *colorkey; - struct drm_property *zpos; - } planes; + unsigned int dpad0_source; + struct rcar_du_lvdsenc *lvds[2]; }; -int rcar_du_get(struct rcar_du_device *rcdu); -void rcar_du_put(struct rcar_du_device *rcdu); +static inline bool rcar_du_has(struct rcar_du_device *rcdu, + unsigned int feature) +{ + return rcdu->info->features & feature; +} static inline u32 rcar_du_read(struct rcar_du_device *rcdu, u32 reg) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c new file mode 100644 index 000000000000..3daa7a168dc6 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -0,0 +1,202 @@ +/* + * rcar_du_encoder.c -- R-Car Display Unit Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/export.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> + +#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" +#include "rcar_du_kms.h" +#include "rcar_du_lvdscon.h" +#include "rcar_du_lvdsenc.h" +#include "rcar_du_vgacon.h" + +/* ----------------------------------------------------------------------------- + * Common connector functions + */ + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector) +{ + struct rcar_du_connector *rcon = to_rcar_connector(connector); + + return &rcon->encoder->encoder; +} + +/* ----------------------------------------------------------------------------- + * Encoder + */ + +static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode); +} + +static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + const struct drm_display_mode *panel_mode; + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + bool found = false; + + /* DAC encoders have currently no restriction on the mode. */ + if (encoder->encoder_type == DRM_MODE_ENCODER_DAC) + return true; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + found = true; + break; + } + } + + if (!found) { + dev_dbg(dev->dev, "mode_fixup: no connector found\n"); + return false; + } + + if (list_empty(&connector->modes)) { + dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); + return false; + } + + panel_mode = list_first_entry(&connector->modes, + struct drm_display_mode, head); + + /* We're not allowed to modify the resolution. */ + if (mode->hdisplay != panel_mode->hdisplay || + mode->vdisplay != panel_mode->vdisplay) + return false; + + /* The flat panel mode is fixed, just copy it to the adjusted mode. */ + drm_mode_copy(adjusted_mode, panel_mode); + + /* The internal LVDS encoder has a clock frequency operating range of + * 30MHz to 150MHz. Clamp the clock accordingly. + */ + if (renc->lvds) + adjusted_mode->clock = clamp(adjusted_mode->clock, + 30000, 150000); + + return true; +} + +static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, + DRM_MODE_DPMS_OFF); +} + +static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + if (renc->lvds) + rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, + DRM_MODE_DPMS_ON); +} + +static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + + rcar_du_crtc_route_output(encoder->crtc, renc->output); +} + +static const struct drm_encoder_helper_funcs encoder_helper_funcs = { + .dpms = rcar_du_encoder_dpms, + .mode_fixup = rcar_du_encoder_mode_fixup, + .prepare = rcar_du_encoder_mode_prepare, + .commit = rcar_du_encoder_mode_commit, + .mode_set = rcar_du_encoder_mode_set, +}; + +static const struct drm_encoder_funcs encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +int rcar_du_encoder_init(struct rcar_du_device *rcdu, + enum rcar_du_encoder_type type, + enum rcar_du_output output, + const struct rcar_du_encoder_data *data) +{ + struct rcar_du_encoder *renc; + unsigned int encoder_type; + int ret; + + renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); + if (renc == NULL) + return -ENOMEM; + + renc->output = output; + + switch (output) { + case RCAR_DU_OUTPUT_LVDS0: + renc->lvds = rcdu->lvds[0]; + break; + + case RCAR_DU_OUTPUT_LVDS1: + renc->lvds = rcdu->lvds[1]; + break; + + default: + break; + } + + switch (type) { + case RCAR_DU_ENCODER_VGA: + encoder_type = DRM_MODE_ENCODER_DAC; + break; + case RCAR_DU_ENCODER_LVDS: + encoder_type = DRM_MODE_ENCODER_LVDS; + break; + case RCAR_DU_ENCODER_NONE: + default: + /* No external encoder, use the internal encoder type. */ + encoder_type = rcdu->info->routes[output].encoder_type; + break; + } + + ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, + encoder_type); + if (ret < 0) + return ret; + + drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); + + switch (encoder_type) { + case DRM_MODE_ENCODER_LVDS: + return rcar_du_lvds_connector_init(rcdu, renc, + &data->connector.lvds.panel); + + case DRM_MODE_ENCODER_DAC: + return rcar_du_vga_connector_init(rcdu, renc); + + default: + return -EINVAL; + } +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h new file mode 100644 index 000000000000..0e5a65e45d0e --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -0,0 +1,49 @@ +/* + * rcar_du_encoder.h -- R-Car Display Unit Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_ENCODER_H__ +#define __RCAR_DU_ENCODER_H__ + +#include <linux/platform_data/rcar-du.h> + +#include <drm/drm_crtc.h> + +struct rcar_du_device; +struct rcar_du_lvdsenc; + +struct rcar_du_encoder { + struct drm_encoder encoder; + enum rcar_du_output output; + struct rcar_du_lvdsenc *lvds; +}; + +#define to_rcar_encoder(e) \ + container_of(e, struct rcar_du_encoder, encoder) + +struct rcar_du_connector { + struct drm_connector connector; + struct rcar_du_encoder *encoder; +}; + +#define to_rcar_connector(c) \ + container_of(c, struct rcar_du_connector, connector) + +struct drm_encoder * +rcar_du_connector_best_encoder(struct drm_connector *connector); + +int rcar_du_encoder_init(struct rcar_du_device *rcdu, + enum rcar_du_encoder_type type, + enum rcar_du_output output, + const struct rcar_du_encoder_data *data); + +#endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c new file mode 100644 index 000000000000..eb53cd97e8c6 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -0,0 +1,187 @@ +/* + * rcar_du_group.c -- R-Car Display Unit Channels Pair + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * The R8A7779 DU is split in per-CRTC resources (scan-out engine, blending + * unit, timings generator, ...) and device-global resources (start/stop + * control, planes, ...) shared between the two CRTCs. + * + * The R8A7790 introduced a third CRTC with its own set of global resources. + * This would be modeled as two separate DU device instances if it wasn't for + * a handful or resources that are shared between the three CRTCs (mostly + * related to input and output routing). For this reason the R8A7790 DU must be + * modeled as a single device with three CRTCs, two sets of "semi-global" + * resources, and a few device-global resources. + * + * The rcar_du_group object is a driver specific object, without any real + * counterpart in the DU documentation, that models those semi-global resources. + */ + +#include <linux/clk.h> +#include <linux/io.h> + +#include "rcar_du_drv.h" +#include "rcar_du_group.h" +#include "rcar_du_regs.h" + +u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg) +{ + return rcar_du_read(rgrp->dev, rgrp->mmio_offset + reg); +} + +void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) +{ + rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data); +} + +static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) +{ + u32 defr8 = DEFR8_CODE | DEFR8_DEFE8; + + if (!rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_DEFR8)) + return; + + /* The DEFR8 register for the first group also controls RGB output + * routing to DPAD0 + */ + if (rgrp->index == 0) + defr8 |= DEFR8_DRGBS_DU(rgrp->dev->dpad0_source); + + rcar_du_group_write(rgrp, DEFR8, defr8); +} + +static void rcar_du_group_setup(struct rcar_du_group *rgrp) +{ + /* Enable extended features */ + rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE); + rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); + rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); + rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); + rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); + + rcar_du_group_setup_defr8(rgrp); + + /* Use DS1PR and DS2PR to configure planes priorities and connects the + * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. + */ + rcar_du_group_write(rgrp, DORCR, DORCR_PG1D_DS1 | DORCR_DPRS); +} + +/* + * rcar_du_group_get - Acquire a reference to the DU channels group + * + * Acquiring the first reference setups core registers. A reference must be held + * before accessing any hardware registers. + * + * This function must be called with the DRM mode_config lock held. + * + * Return 0 in case of success or a negative error code otherwise. + */ +int rcar_du_group_get(struct rcar_du_group *rgrp) +{ + if (rgrp->use_count) + goto done; + + rcar_du_group_setup(rgrp); + +done: + rgrp->use_count++; + return 0; +} + +/* + * rcar_du_group_put - Release a reference to the DU + * + * This function must be called with the DRM mode_config lock held. + */ +void rcar_du_group_put(struct rcar_du_group *rgrp) +{ + --rgrp->use_count; +} + +static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) +{ + rcar_du_group_write(rgrp, DSYSR, + (rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | + (start ? DSYSR_DEN : DSYSR_DRES)); +} + +void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) +{ + /* Many of the configuration bits are only updated when the display + * reset (DRES) bit in DSYSR is set to 1, disabling *both* CRTCs. Some + * of those bits could be pre-configured, but others (especially the + * bits related to plane assignment to display timing controllers) need + * to be modified at runtime. + * + * Restart the display controller if a start is requested. Sorry for the + * flicker. It should be possible to move most of the "DRES-update" bits + * setup to driver initialization time and minimize the number of cases + * when the display controller will have to be restarted. + */ + if (start) { + if (rgrp->used_crtcs++ != 0) + __rcar_du_group_start_stop(rgrp, false); + __rcar_du_group_start_stop(rgrp, true); + } else { + if (--rgrp->used_crtcs == 0) + __rcar_du_group_start_stop(rgrp, false); + } +} + +void rcar_du_group_restart(struct rcar_du_group *rgrp) +{ + __rcar_du_group_start_stop(rgrp, false); + __rcar_du_group_start_stop(rgrp, true); +} + +static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu) +{ + int ret; + + /* RGB output routing to DPAD0 is configured in the DEFR8 register of + * the first group. As this function can be called with the DU0 and DU1 + * CRTCs disabled, we need to enable the first group clock before + * accessing the register. + */ + ret = clk_prepare_enable(rcdu->crtcs[0].clock); + if (ret < 0) + return ret; + + rcar_du_group_setup_defr8(&rcdu->groups[0]); + + clk_disable_unprepare(rcdu->crtcs[0].clock); + + return 0; +} + +int rcar_du_group_set_routing(struct rcar_du_group *rgrp) +{ + struct rcar_du_crtc *crtc0 = &rgrp->dev->crtcs[rgrp->index * 2]; + u32 dorcr = rcar_du_group_read(rgrp, DORCR); + + dorcr &= ~(DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_MASK); + + /* Set the DPAD1 pins sources. Select CRTC 0 if explicitly requested and + * CRTC 1 in all other cases to avoid cloning CRTC 0 to DPAD0 and DPAD1 + * by default. + */ + if (crtc0->outputs & BIT(RCAR_DU_OUTPUT_DPAD1)) + dorcr |= DORCR_PG2D_DS1; + else + dorcr |= DORCR_PG2T | DORCR_DK2S | DORCR_PG2D_DS2; + + rcar_du_group_write(rgrp, DORCR, dorcr); + + return rcar_du_set_dpad0_routing(rgrp->dev); +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h new file mode 100644 index 000000000000..5025930972ec --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -0,0 +1,50 @@ +/* + * rcar_du_group.c -- R-Car Display Unit Planes and CRTCs Group + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_GROUP_H__ +#define __RCAR_DU_GROUP_H__ + +#include "rcar_du_plane.h" + +struct rcar_du_device; + +/* + * struct rcar_du_group - CRTCs and planes group + * @dev: the DU device + * @mmio_offset: registers offset in the device memory map + * @index: group index + * @use_count: number of users of the group (rcar_du_group_(get|put)) + * @used_crtcs: number of CRTCs currently in use + * @planes: planes handled by the group + */ +struct rcar_du_group { + struct rcar_du_device *dev; + unsigned int mmio_offset; + unsigned int index; + + unsigned int use_count; + unsigned int used_crtcs; + + struct rcar_du_planes planes; +}; + +u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg); +void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data); + +int rcar_du_group_get(struct rcar_du_group *rgrp); +void rcar_du_group_put(struct rcar_du_group *rgrp); +void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); +void rcar_du_group_restart(struct rcar_du_group *rgrp); +int rcar_du_group_set_routing(struct rcar_du_group *rgrp); + +#endif /* __RCAR_DU_GROUP_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index d30c2e29bee2..b31ac080c4a7 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -19,10 +19,10 @@ #include "rcar_du_crtc.h" #include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" -#include "rcar_du_lvds.h" +#include "rcar_du_lvdsenc.h" #include "rcar_du_regs.h" -#include "rcar_du_vga.h" /* ----------------------------------------------------------------------------- * Format helpers @@ -106,46 +106,24 @@ const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc) } /* ----------------------------------------------------------------------------- - * Common connector and encoder functions - */ - -struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector) -{ - struct rcar_du_connector *rcon = to_rcar_connector(connector); - - return &rcon->encoder->encoder; -} - -void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder) -{ -} - -void rcar_du_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct rcar_du_encoder *renc = to_rcar_encoder(encoder); - - rcar_du_crtc_route_output(encoder->crtc, renc->output); -} - -void rcar_du_encoder_mode_commit(struct drm_encoder *encoder) -{ -} - -/* ----------------------------------------------------------------------------- * Frame buffer */ int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { + struct rcar_du_device *rcdu = dev->dev_private; unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); unsigned int align; - /* The pitch must be aligned to a 16 pixels boundary. */ - align = 16 * args->bpp / 8; + /* The R8A7779 DU requires a 16 pixels pitch alignment as documented, + * but the R8A7790 DU seems to require a 128 bytes pitch alignment. + */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_ALIGN_128B)) + align = 128; + else + align = 16 * args->bpp / 8; + args->pitch = roundup(max(args->pitch, min_pitch), align); return drm_gem_cma_dumb_create(file, dev, args); @@ -155,6 +133,7 @@ static struct drm_framebuffer * rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) { + struct rcar_du_device *rcdu = dev->dev_private; const struct rcar_du_format_info *format; unsigned int align; @@ -165,7 +144,10 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); } - align = 16 * format->bpp / 8; + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_ALIGN_128B)) + align = 128; + else + align = 16 * format->bpp / 8; if (mode_cmd->pitches[0] & (align - 1) || mode_cmd->pitches[0] >= 8192) { @@ -185,81 +167,124 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return drm_fb_cma_create(dev, file_priv, mode_cmd); } +static void rcar_du_output_poll_changed(struct drm_device *dev) +{ + struct rcar_du_device *rcdu = dev->dev_private; + + drm_fbdev_cma_hotplug_event(rcdu->fbdev); +} + static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, + .output_poll_changed = rcar_du_output_poll_changed, }; int rcar_du_modeset_init(struct rcar_du_device *rcdu) { + static const unsigned int mmio_offsets[] = { + DU0_REG_OFFSET, DU2_REG_OFFSET + }; + struct drm_device *dev = rcdu->ddev; struct drm_encoder *encoder; + struct drm_fbdev_cma *fbdev; + unsigned int num_groups; unsigned int i; int ret; - drm_mode_config_init(rcdu->ddev); + drm_mode_config_init(dev); - rcdu->ddev->mode_config.min_width = 0; - rcdu->ddev->mode_config.min_height = 0; - rcdu->ddev->mode_config.max_width = 4095; - rcdu->ddev->mode_config.max_height = 2047; - rcdu->ddev->mode_config.funcs = &rcar_du_mode_config_funcs; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.max_width = 4095; + dev->mode_config.max_height = 2047; + dev->mode_config.funcs = &rcar_du_mode_config_funcs; - ret = rcar_du_plane_init(rcdu); - if (ret < 0) - return ret; + rcdu->num_crtcs = rcdu->info->num_crtcs; + + /* Initialize the groups. */ + num_groups = DIV_ROUND_UP(rcdu->num_crtcs, 2); + + for (i = 0; i < num_groups; ++i) { + struct rcar_du_group *rgrp = &rcdu->groups[i]; + + rgrp->dev = rcdu; + rgrp->mmio_offset = mmio_offsets[i]; + rgrp->index = i; + + ret = rcar_du_planes_init(rgrp); + if (ret < 0) + return ret; + } + + /* Create the CRTCs. */ + for (i = 0; i < rcdu->num_crtcs; ++i) { + struct rcar_du_group *rgrp = &rcdu->groups[i / 2]; - for (i = 0; i < ARRAY_SIZE(rcdu->crtcs); ++i) { - ret = rcar_du_crtc_create(rcdu, i); + ret = rcar_du_crtc_create(rgrp, i); if (ret < 0) return ret; } - rcdu->used_crtcs = 0; - rcdu->num_crtcs = i; + /* Initialize the encoders. */ + ret = rcar_du_lvdsenc_init(rcdu); + if (ret < 0) + return ret; for (i = 0; i < rcdu->pdata->num_encoders; ++i) { const struct rcar_du_encoder_data *pdata = &rcdu->pdata->encoders[i]; + const struct rcar_du_output_routing *route = + &rcdu->info->routes[pdata->output]; + + if (pdata->type == RCAR_DU_ENCODER_UNUSED) + continue; - if (pdata->output >= ARRAY_SIZE(rcdu->crtcs)) { + if (pdata->output >= RCAR_DU_OUTPUT_MAX || + route->possible_crtcs == 0) { dev_warn(rcdu->dev, "encoder %u references unexisting output %u, skipping\n", i, pdata->output); continue; } - switch (pdata->encoder) { - case RCAR_DU_ENCODER_VGA: - rcar_du_vga_init(rcdu, &pdata->u.vga, pdata->output); - break; - - case RCAR_DU_ENCODER_LVDS: - rcar_du_lvds_init(rcdu, &pdata->u.lvds, pdata->output); - break; - - default: - break; - } + rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata); } - /* Set the possible CRTCs and possible clones. All encoders can be - * driven by the CRTC associated with the output they're connected to, - * as well as by CRTC 0. + /* Set the possible CRTCs and possible clones. There's always at least + * one way for all encoders to clone each other, set all bits in the + * possible clones field. */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct rcar_du_encoder *renc = to_rcar_encoder(encoder); + const struct rcar_du_output_routing *route = + &rcdu->info->routes[renc->output]; - encoder->possible_crtcs = (1 << 0) | (1 << renc->output); - encoder->possible_clones = 1 << 0; + encoder->possible_crtcs = route->possible_crtcs; + encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1; } - ret = rcar_du_plane_register(rcdu); - if (ret < 0) - return ret; + /* Now that the CRTCs have been initialized register the planes. */ + for (i = 0; i < num_groups; ++i) { + ret = rcar_du_planes_register(&rcdu->groups[i]); + if (ret < 0) + return ret; + } + + drm_kms_helper_poll_init(dev); + + drm_helper_disable_unused_functions(dev); + + fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc, + dev->mode_config.num_connector); + if (IS_ERR(fbdev)) + return PTR_ERR(fbdev); - drm_kms_helper_poll_init(rcdu->ddev); +#ifndef CONFIG_FRAMEBUFFER_CONSOLE + drm_fbdev_cma_restore_mode(fbdev); +#endif - drm_helper_disable_unused_functions(rcdu->ddev); + rcdu->fbdev = fbdev; return 0; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.h b/drivers/gpu/drm/rcar-du/rcar_du_kms.h index dba472263486..5750e6af5655 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.h @@ -16,8 +16,9 @@ #include <linux/types.h> -#include <drm/drm_crtc.h> - +struct drm_file; +struct drm_device; +struct drm_mode_create_dumb; struct rcar_du_device; struct rcar_du_format_info { @@ -28,32 +29,8 @@ struct rcar_du_format_info { unsigned int edf; }; -struct rcar_du_encoder { - struct drm_encoder encoder; - unsigned int output; -}; - -#define to_rcar_encoder(e) \ - container_of(e, struct rcar_du_encoder, encoder) - -struct rcar_du_connector { - struct drm_connector connector; - struct rcar_du_encoder *encoder; -}; - -#define to_rcar_connector(c) \ - container_of(c, struct rcar_du_connector, connector) - const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc); -struct drm_encoder * -rcar_du_connector_best_encoder(struct drm_connector *connector); -void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder); -void rcar_du_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode); -void rcar_du_encoder_mode_commit(struct drm_encoder *encoder); - int rcar_du_modeset_init(struct rcar_du_device *rcdu); int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 7aefe7267e1d..4f3ba93cd91d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -1,5 +1,5 @@ /* - * rcar_du_lvds.c -- R-Car Display Unit LVDS Encoder and Connector + * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector * * Copyright (C) 2013 Renesas Corporation * @@ -16,8 +16,9 @@ #include <drm/drm_crtc_helper.h> #include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" -#include "rcar_du_lvds.h" +#include "rcar_du_lvdscon.h" struct rcar_du_lvds_connector { struct rcar_du_connector connector; @@ -28,13 +29,10 @@ struct rcar_du_lvds_connector { #define to_rcar_lvds_connector(c) \ container_of(c, struct rcar_du_lvds_connector, connector.connector) -/* ----------------------------------------------------------------------------- - * Connector - */ - static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector) { - struct rcar_du_lvds_connector *lvdscon = to_rcar_lvds_connector(connector); + struct rcar_du_lvds_connector *lvdscon = + to_rcar_lvds_connector(connector); struct drm_display_mode *mode; mode = drm_mode_create(connector->dev); @@ -90,9 +88,9 @@ static const struct drm_connector_funcs connector_funcs = { .destroy = rcar_du_lvds_connector_destroy, }; -static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc, - const struct rcar_du_panel_data *panel) +int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc, + const struct rcar_du_panel_data *panel) { struct rcar_du_lvds_connector *lvdscon; struct drm_connector *connector; @@ -131,86 +129,3 @@ static int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, return 0; } - -/* ----------------------------------------------------------------------------- - * Encoder - */ - -static void rcar_du_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) -{ -} - -static bool rcar_du_lvds_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - const struct drm_display_mode *panel_mode; - struct drm_device *dev = encoder->dev; - struct drm_connector *connector; - bool found = false; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder == encoder) { - found = true; - break; - } - } - - if (!found) { - dev_dbg(dev->dev, "mode_fixup: no connector found\n"); - return false; - } - - if (list_empty(&connector->modes)) { - dev_dbg(dev->dev, "mode_fixup: empty modes list\n"); - return false; - } - - panel_mode = list_first_entry(&connector->modes, - struct drm_display_mode, head); - - /* We're not allowed to modify the resolution. */ - if (mode->hdisplay != panel_mode->hdisplay || - mode->vdisplay != panel_mode->vdisplay) - return false; - - /* The flat panel mode is fixed, just copy it to the adjusted mode. */ - drm_mode_copy(adjusted_mode, panel_mode); - - return true; -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_lvds_encoder_dpms, - .mode_fixup = rcar_du_lvds_encoder_mode_fixup, - .prepare = rcar_du_encoder_mode_prepare, - .commit = rcar_du_encoder_mode_commit, - .mode_set = rcar_du_encoder_mode_set, -}; - -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -int rcar_du_lvds_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_lvds_data *data, - unsigned int output) -{ - struct rcar_du_encoder *renc; - int ret; - - renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); - if (renc == NULL) - return -ENOMEM; - - renc->output = output; - - ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - DRM_MODE_ENCODER_LVDS); - if (ret < 0) - return ret; - - drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); - - return rcar_du_lvds_connector_init(rcdu, renc, &data->panel); -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h index b47f8328e103..bff8683699ca 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvds.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h @@ -1,5 +1,5 @@ /* - * rcar_du_lvds.h -- R-Car Display Unit LVDS Encoder and Connector + * rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector * * Copyright (C) 2013 Renesas Corporation * @@ -11,14 +11,15 @@ * (at your option) any later version. */ -#ifndef __RCAR_DU_LVDS_H__ -#define __RCAR_DU_LVDS_H__ +#ifndef __RCAR_DU_LVDSCON_H__ +#define __RCAR_DU_LVDSCON_H__ struct rcar_du_device; -struct rcar_du_encoder_lvds_data; +struct rcar_du_encoder; +struct rcar_du_panel_data; -int rcar_du_lvds_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_lvds_data *data, - unsigned int output); +int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc, + const struct rcar_du_panel_data *panel); -#endif /* __RCAR_DU_LVDS_H__ */ +#endif /* __RCAR_DU_LVDSCON_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c new file mode 100644 index 000000000000..a0f6a1781925 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c @@ -0,0 +1,196 @@ +/* + * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "rcar_du_drv.h" +#include "rcar_du_encoder.h" +#include "rcar_du_lvdsenc.h" +#include "rcar_lvds_regs.h" + +struct rcar_du_lvdsenc { + struct rcar_du_device *dev; + + unsigned int index; + void __iomem *mmio; + struct clk *clock; + int dpms; + + enum rcar_lvds_input input; +}; + +static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) +{ + iowrite32(data, lvds->mmio + reg); +} + +static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) +{ + const struct drm_display_mode *mode = &rcrtc->crtc.mode; + unsigned int freq = mode->clock; + u32 lvdcr0; + u32 pllcr; + int ret; + + if (lvds->dpms == DRM_MODE_DPMS_ON) + return 0; + + ret = clk_prepare_enable(lvds->clock); + if (ret < 0) + return ret; + + /* PLL clock configuration */ + if (freq <= 38000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; + else if (freq <= 60000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; + else if (freq <= 121000) + pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; + else + pllcr = LVDPLLCR_PLLDLYCNT_150M; + + rcar_lvds_write(lvds, LVDPLLCR, pllcr); + + /* Hardcode the channels and control signals routing for now. + * + * HSYNC -> CTRL0 + * VSYNC -> CTRL1 + * DISP -> CTRL2 + * 0 -> CTRL3 + * + * Channels 1 and 3 are switched on ES1. + */ + rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO | + LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC | + LVDCTRCR_CTR0SEL_HSYNC); + rcar_lvds_write(lvds, LVDCHCR, + LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3) | + LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1)); + + /* Select the input, hardcode mode 0, enable LVDS operation and turn + * bias circuitry on. + */ + lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; + if (rcrtc->index == 2) + lvdcr0 |= LVDCR0_DUSEL; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + /* Turn all the channels on. */ + rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | + LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); + + /* Turn the PLL on, wait for the startup delay, and turn the output + * on. + */ + lvdcr0 |= LVDCR0_PLLEN; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + lvds->dpms = DRM_MODE_DPMS_ON; + return 0; +} + +static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds) +{ + if (lvds->dpms == DRM_MODE_DPMS_OFF) + return; + + rcar_lvds_write(lvds, LVDCR0, 0); + rcar_lvds_write(lvds, LVDCR1, 0); + + clk_disable_unprepare(lvds->clock); + + lvds->dpms = DRM_MODE_DPMS_OFF; +} + +int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode) +{ + if (mode == DRM_MODE_DPMS_OFF) { + rcar_du_lvdsenc_stop(lvds); + return 0; + } else if (crtc) { + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + return rcar_du_lvdsenc_start(lvds, rcrtc); + } else + return -EINVAL; +} + +static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, + struct platform_device *pdev) +{ + struct resource *mem; + char name[7]; + + sprintf(name, "lvds.%u", lvds->index); + + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (mem == NULL) { + dev_err(&pdev->dev, "failed to get memory resource for %s\n", + name); + return -EINVAL; + } + + lvds->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (lvds->mmio == NULL) { + dev_err(&pdev->dev, "failed to remap memory resource for %s\n", + name); + return -ENOMEM; + } + + lvds->clock = devm_clk_get(&pdev->dev, name); + if (IS_ERR(lvds->clock)) { + dev_err(&pdev->dev, "failed to get clock for %s\n", name); + return PTR_ERR(lvds->clock); + } + + return 0; +} + +int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) +{ + struct platform_device *pdev = to_platform_device(rcdu->dev); + struct rcar_du_lvdsenc *lvds; + unsigned int i; + int ret; + + for (i = 0; i < rcdu->info->num_lvds; ++i) { + lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL); + if (lvds == NULL) { + dev_err(&pdev->dev, "failed to allocate private data\n"); + return -ENOMEM; + } + + lvds->dev = rcdu; + lvds->index = i; + lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0; + lvds->dpms = DRM_MODE_DPMS_OFF; + + ret = rcar_du_lvdsenc_get_resources(lvds, pdev); + if (ret < 0) + return ret; + + rcdu->lvds[i] = lvds; + } + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h new file mode 100644 index 000000000000..7051c6de19ae --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h @@ -0,0 +1,46 @@ +/* + * rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_LVDSENC_H__ +#define __RCAR_DU_LVDSENC_H__ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_data/rcar-du.h> + +struct rcar_drm_crtc; +struct rcar_du_lvdsenc; + +enum rcar_lvds_input { + RCAR_LVDS_INPUT_DU0, + RCAR_LVDS_INPUT_DU1, + RCAR_LVDS_INPUT_DU2, +}; + +#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) +int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); +int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode); +#else +static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) +{ + return 0; +} +static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds, + struct drm_crtc *crtc, int mode) +{ + return 0; +} +#endif + +#endif /* __RCAR_DU_LVDSENC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index a65f81ddf51d..53000644733f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -36,90 +36,95 @@ static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane) return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane; } -static u32 rcar_du_plane_read(struct rcar_du_device *rcdu, +static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, unsigned int index, u32 reg) { - return rcar_du_read(rcdu, index * PLANE_OFF + reg); + return rcar_du_read(rgrp->dev, + rgrp->mmio_offset + index * PLANE_OFF + reg); } -static void rcar_du_plane_write(struct rcar_du_device *rcdu, +static void rcar_du_plane_write(struct rcar_du_group *rgrp, unsigned int index, u32 reg, u32 data) { - rcar_du_write(rcdu, index * PLANE_OFF + reg, data); + rcar_du_write(rgrp->dev, rgrp->mmio_offset + index * PLANE_OFF + reg, + data); } int rcar_du_plane_reserve(struct rcar_du_plane *plane, const struct rcar_du_format_info *format) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; unsigned int i; int ret = -EBUSY; - mutex_lock(&rcdu->planes.lock); + mutex_lock(&rgrp->planes.lock); - for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - if (!(rcdu->planes.free & (1 << i))) + for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { + if (!(rgrp->planes.free & (1 << i))) continue; if (format->planes == 1 || - rcdu->planes.free & (1 << ((i + 1) % 8))) + rgrp->planes.free & (1 << ((i + 1) % 8))) break; } - if (i == ARRAY_SIZE(rcdu->planes.planes)) + if (i == ARRAY_SIZE(rgrp->planes.planes)) goto done; - rcdu->planes.free &= ~(1 << i); + rgrp->planes.free &= ~(1 << i); if (format->planes == 2) - rcdu->planes.free &= ~(1 << ((i + 1) % 8)); + rgrp->planes.free &= ~(1 << ((i + 1) % 8)); plane->hwindex = i; ret = 0; done: - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rgrp->planes.lock); return ret; } void rcar_du_plane_release(struct rcar_du_plane *plane) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; if (plane->hwindex == -1) return; - mutex_lock(&rcdu->planes.lock); - rcdu->planes.free |= 1 << plane->hwindex; + mutex_lock(&rgrp->planes.lock); + rgrp->planes.free |= 1 << plane->hwindex; if (plane->format->planes == 2) - rcdu->planes.free |= 1 << ((plane->hwindex + 1) % 8); - mutex_unlock(&rcdu->planes.lock); + rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8); + mutex_unlock(&rgrp->planes.lock); plane->hwindex = -1; } void rcar_du_plane_update_base(struct rcar_du_plane *plane) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; unsigned int index = plane->hwindex; - /* According to the datasheet the Y position is expressed in raster line - * units. However, 32bpp formats seem to require a doubled Y position - * value. Similarly, for the second plane, NV12 and NV21 formats seem to + /* The Y position is expressed in raster line units and must be doubled + * for 32bpp formats, according to the R8A7790 datasheet. No mention of + * doubling the Y position is found in the R8A7779 datasheet, but the + * rule seems to apply there as well. + * + * Similarly, for the second plane, NV12 and NV21 formats seem to * require a halved Y position value. */ - rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * (plane->format->bpp == 32 ? 2 : 1)); - rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[0]); + rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]); if (plane->format->planes == 2) { index = (index + 1) % 8; - rcar_du_plane_write(rcdu, index, PnSPXR, plane->src_x); - rcar_du_plane_write(rcdu, index, PnSPYR, plane->src_y * + rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x); + rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y * (plane->format->bpp == 16 ? 2 : 1) / 2); - rcar_du_plane_write(rcdu, index, PnDSA0R, plane->dma[1]); + rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]); } } @@ -140,7 +145,7 @@ void rcar_du_plane_compute_base(struct rcar_du_plane *plane, static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, unsigned int index) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; u32 colorkey; u32 pnmr; @@ -154,9 +159,9 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, * enable alpha-blending regardless of the X bit value. */ if (plane->format->fourcc != DRM_FORMAT_XRGB1555) - rcar_du_plane_write(rcdu, index, PnALPHAR, PnALPHAR_ABIT_0); + rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); else - rcar_du_plane_write(rcdu, index, PnALPHAR, + rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_X | plane->alpha); pnmr = PnMR_BM_MD | plane->format->pnmr; @@ -172,14 +177,14 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, if (plane->format->fourcc == DRM_FORMAT_YUYV) pnmr |= PnMR_YCDF_YUYV; - rcar_du_plane_write(rcdu, index, PnMR, pnmr); + rcar_du_plane_write(rgrp, index, PnMR, pnmr); switch (plane->format->fourcc) { case DRM_FORMAT_RGB565: colorkey = ((plane->colorkey & 0xf80000) >> 8) | ((plane->colorkey & 0x00fc00) >> 5) | ((plane->colorkey & 0x0000f8) >> 3); - rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break; case DRM_FORMAT_ARGB1555: @@ -187,12 +192,12 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, colorkey = ((plane->colorkey & 0xf80000) >> 9) | ((plane->colorkey & 0x00f800) >> 6) | ((plane->colorkey & 0x0000f8) >> 3); - rcar_du_plane_write(rcdu, index, PnTC2R, colorkey); + rcar_du_plane_write(rgrp, index, PnTC2R, colorkey); break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - rcar_du_plane_write(rcdu, index, PnTC3R, + rcar_du_plane_write(rgrp, index, PnTC3R, PnTC3R_CODE | (plane->colorkey & 0xffffff)); break; } @@ -201,7 +206,7 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, static void __rcar_du_plane_setup(struct rcar_du_plane *plane, unsigned int index) { - struct rcar_du_device *rcdu = plane->dev; + struct rcar_du_group *rgrp = plane->group; u32 ddcr2 = PnDDCR2_CODE; u32 ddcr4; u32 mwr; @@ -211,7 +216,7 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, * The data format is selected by the DDDF field in PnMR and the EDF * field in DDCR4. */ - ddcr4 = rcar_du_plane_read(rcdu, index, PnDDCR4); + ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); ddcr4 &= ~PnDDCR4_EDF_MASK; ddcr4 |= plane->format->edf | PnDDCR4_CODE; @@ -232,8 +237,8 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, } } - rcar_du_plane_write(rcdu, index, PnDDCR2, ddcr2); - rcar_du_plane_write(rcdu, index, PnDDCR4, ddcr4); + rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); + rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); /* Memory pitch (expressed in pixels) */ if (plane->format->planes == 2) @@ -241,19 +246,19 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, else mwr = plane->pitch * 8 / plane->format->bpp; - rcar_du_plane_write(rcdu, index, PnMWR, mwr); + rcar_du_plane_write(rgrp, index, PnMWR, mwr); /* Destination position and size */ - rcar_du_plane_write(rcdu, index, PnDSXR, plane->width); - rcar_du_plane_write(rcdu, index, PnDSYR, plane->height); - rcar_du_plane_write(rcdu, index, PnDPXR, plane->dst_x); - rcar_du_plane_write(rcdu, index, PnDPYR, plane->dst_y); + rcar_du_plane_write(rgrp, index, PnDSXR, plane->width); + rcar_du_plane_write(rgrp, index, PnDSYR, plane->height); + rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x); + rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y); /* Wrap-around and blinking, disabled */ - rcar_du_plane_write(rcdu, index, PnWASPR, 0); - rcar_du_plane_write(rcdu, index, PnWAMWR, 4095); - rcar_du_plane_write(rcdu, index, PnBTR, 0); - rcar_du_plane_write(rcdu, index, PnMLR, 0); + rcar_du_plane_write(rgrp, index, PnWASPR, 0); + rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); + rcar_du_plane_write(rgrp, index, PnBTR, 0); + rcar_du_plane_write(rgrp, index, PnMLR, 0); } void rcar_du_plane_setup(struct rcar_du_plane *plane) @@ -273,7 +278,7 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t src_w, uint32_t src_h) { struct rcar_du_plane *rplane = to_rcar_plane(plane); - struct rcar_du_device *rcdu = plane->dev->dev_private; + struct rcar_du_device *rcdu = rplane->group->dev; const struct rcar_du_format_info *format; unsigned int nplanes; int ret; @@ -316,26 +321,25 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, rcar_du_plane_compute_base(rplane, fb); rcar_du_plane_setup(rplane); - mutex_lock(&rcdu->planes.lock); + mutex_lock(&rplane->group->planes.lock); rplane->enabled = true; rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rplane->group->planes.lock); return 0; } static int rcar_du_plane_disable(struct drm_plane *plane) { - struct rcar_du_device *rcdu = plane->dev->dev_private; struct rcar_du_plane *rplane = to_rcar_plane(plane); if (!rplane->enabled) return 0; - mutex_lock(&rcdu->planes.lock); + mutex_lock(&rplane->group->planes.lock); rplane->enabled = false; rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&rplane->group->planes.lock); rcar_du_plane_release(rplane); @@ -377,9 +381,7 @@ static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane, static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, unsigned int zpos) { - struct rcar_du_device *rcdu = plane->dev; - - mutex_lock(&rcdu->planes.lock); + mutex_lock(&plane->group->planes.lock); if (plane->zpos == zpos) goto done; @@ -390,21 +392,21 @@ static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane, rcar_du_crtc_update_planes(plane->crtc); done: - mutex_unlock(&rcdu->planes.lock); + mutex_unlock(&plane->group->planes.lock); } static int rcar_du_plane_set_property(struct drm_plane *plane, struct drm_property *property, uint64_t value) { - struct rcar_du_device *rcdu = plane->dev->dev_private; struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct rcar_du_group *rgrp = rplane->group; - if (property == rcdu->planes.alpha) + if (property == rgrp->planes.alpha) rcar_du_plane_set_alpha(rplane, value); - else if (property == rcdu->planes.colorkey) + else if (property == rgrp->planes.colorkey) rcar_du_plane_set_colorkey(rplane, value); - else if (property == rcdu->planes.zpos) + else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else return -EINVAL; @@ -432,37 +434,39 @@ static const uint32_t formats[] = { DRM_FORMAT_NV16, }; -int rcar_du_plane_init(struct rcar_du_device *rcdu) +int rcar_du_planes_init(struct rcar_du_group *rgrp) { + struct rcar_du_planes *planes = &rgrp->planes; + struct rcar_du_device *rcdu = rgrp->dev; unsigned int i; - mutex_init(&rcdu->planes.lock); - rcdu->planes.free = 0xff; + mutex_init(&planes->lock); + planes->free = 0xff; - rcdu->planes.alpha = + planes->alpha = drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255); - if (rcdu->planes.alpha == NULL) + if (planes->alpha == NULL) return -ENOMEM; /* The color key is expressed as an RGB888 triplet stored in a 32-bit * integer in XRGB8888 format. Bit 24 is used as a flag to disable (0) * or enable source color keying (1). */ - rcdu->planes.colorkey = + planes->colorkey = drm_property_create_range(rcdu->ddev, 0, "colorkey", 0, 0x01ffffff); - if (rcdu->planes.colorkey == NULL) + if (planes->colorkey == NULL) return -ENOMEM; - rcdu->planes.zpos = + planes->zpos = drm_property_create_range(rcdu->ddev, 0, "zpos", 1, 7); - if (rcdu->planes.zpos == NULL) + if (planes->zpos == NULL) return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(rcdu->planes.planes); ++i) { - struct rcar_du_plane *plane = &rcdu->planes.planes[i]; + for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) { + struct rcar_du_plane *plane = &planes->planes[i]; - plane->dev = rcdu; + plane->group = rgrp; plane->hwindex = -1; plane->alpha = 255; plane->colorkey = RCAR_DU_COLORKEY_NONE; @@ -472,11 +476,16 @@ int rcar_du_plane_init(struct rcar_du_device *rcdu) return 0; } -int rcar_du_plane_register(struct rcar_du_device *rcdu) +int rcar_du_planes_register(struct rcar_du_group *rgrp) { + struct rcar_du_planes *planes = &rgrp->planes; + struct rcar_du_device *rcdu = rgrp->dev; + unsigned int crtcs; unsigned int i; int ret; + crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index)); + for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) { struct rcar_du_kms_plane *plane; @@ -484,23 +493,22 @@ int rcar_du_plane_register(struct rcar_du_device *rcdu) if (plane == NULL) return -ENOMEM; - plane->hwplane = &rcdu->planes.planes[i + 2]; + plane->hwplane = &planes->planes[i + 2]; plane->hwplane->zpos = 1; - ret = drm_plane_init(rcdu->ddev, &plane->plane, - (1 << rcdu->num_crtcs) - 1, + ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs, &rcar_du_plane_funcs, formats, ARRAY_SIZE(formats), false); if (ret < 0) return ret; drm_object_attach_property(&plane->plane.base, - rcdu->planes.alpha, 255); + planes->alpha, 255); drm_object_attach_property(&plane->plane.base, - rcdu->planes.colorkey, + planes->colorkey, RCAR_DU_COLORKEY_NONE); drm_object_attach_property(&plane->plane.base, - rcdu->planes.zpos, 1); + planes->zpos, 1); } return 0; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 5397dba2fe57..f94f9ce84998 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -14,10 +14,13 @@ #ifndef __RCAR_DU_PLANE_H__ #define __RCAR_DU_PLANE_H__ -struct drm_crtc; -struct drm_framebuffer; -struct rcar_du_device; +#include <linux/mutex.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> + struct rcar_du_format_info; +struct rcar_du_group; /* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As * using KMS planes requires at least one of the CRTCs being enabled, no more @@ -30,7 +33,7 @@ struct rcar_du_format_info; #define RCAR_DU_NUM_SW_PLANES 9 struct rcar_du_plane { - struct rcar_du_device *dev; + struct rcar_du_group *group; struct drm_crtc *crtc; bool enabled; @@ -54,8 +57,19 @@ struct rcar_du_plane { unsigned int dst_y; }; -int rcar_du_plane_init(struct rcar_du_device *rcdu); -int rcar_du_plane_register(struct rcar_du_device *rcdu); +struct rcar_du_planes { + struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES]; + unsigned int free; + struct mutex lock; + + struct drm_property *alpha; + struct drm_property *colorkey; + struct drm_property *zpos; +}; + +int rcar_du_planes_init(struct rcar_du_group *rgrp); +int rcar_du_planes_register(struct rcar_du_group *rgrp); + void rcar_du_plane_setup(struct rcar_du_plane *plane); void rcar_du_plane_update_base(struct rcar_du_plane *plane); void rcar_du_plane_compute_base(struct rcar_du_plane *plane, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index 69f21f19b51c..73f7347f740b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -13,14 +13,15 @@ #ifndef __RCAR_DU_REGS_H__ #define __RCAR_DU_REGS_H__ -#define DISP2_REG_OFFSET 0x30000 +#define DU0_REG_OFFSET 0x00000 +#define DU1_REG_OFFSET 0x30000 +#define DU2_REG_OFFSET 0x40000 /* ----------------------------------------------------------------------------- * Display Control Registers */ #define DSYSR 0x00000 /* display 1 */ -#define D2SYSR 0x30000 /* display 2 */ #define DSYSR_ILTS (1 << 29) #define DSYSR_DSEC (1 << 20) #define DSYSR_IUPD (1 << 16) @@ -35,7 +36,6 @@ #define DSYSR_SCM_INT_VIDEO (3 << 4) #define DSMR 0x00004 -#define D2SMR 0x30004 #define DSMR_VSPM (1 << 28) #define DSMR_ODPM (1 << 27) #define DSMR_DIPM_DISP (0 << 25) @@ -60,7 +60,6 @@ #define DSMR_CSY_MASK (3 << 6) #define DSSR 0x00008 -#define D2SSR 0x30008 #define DSSR_VC1FB_DSA0 (0 << 30) #define DSSR_VC1FB_DSA1 (1 << 30) #define DSSR_VC1FB_DSA2 (2 << 30) @@ -80,7 +79,6 @@ #define DSSR_ADC(n) (1 << ((n)-1)) #define DSRCR 0x0000c -#define D2SRCR 0x3000c #define DSRCR_TVCL (1 << 15) #define DSRCR_FRCL (1 << 14) #define DSRCR_VBCL (1 << 11) @@ -90,7 +88,6 @@ #define DSRCR_MASK 0x0000cbff #define DIER 0x00010 -#define D2IER 0x30010 #define DIER_TVE (1 << 15) #define DIER_FRE (1 << 14) #define DIER_VBE (1 << 11) @@ -114,7 +111,6 @@ #define DPPR_BPP32 (DPPR_BPP32_P1 | DPPR_BPP32_P2) /* plane1 & 2 */ #define DEFR 0x00020 -#define D2EFR 0x30020 #define DEFR_CODE (0x7773 << 16) #define DEFR_EXSL (1 << 12) #define DEFR_EXVL (1 << 11) @@ -137,12 +133,10 @@ #define DCPCR_DCE (1 << 0) #define DEFR2 0x00034 -#define D2EFR2 0x30034 #define DEFR2_CODE (0x7775 << 16) #define DEFR2_DEFE2G (1 << 0) #define DEFR3 0x00038 -#define D2EFR3 0x30038 #define DEFR3_CODE (0x7776 << 16) #define DEFR3_EVDA (1 << 14) #define DEFR3_EVDM_1 (1 << 12) @@ -153,7 +147,6 @@ #define DEFR3_DEFE3 (1 << 0) #define DEFR4 0x0003c -#define D2EFR4 0x3003c #define DEFR4_CODE (0x7777 << 16) #define DEFR4_LRUO (1 << 5) #define DEFR4_SPCE (1 << 4) @@ -205,6 +198,68 @@ #define DEFR6_DEFAULT (DEFR6_CODE | DEFR6_TCNE2) /* ----------------------------------------------------------------------------- + * R8A7790-only Control Registers + */ + +#define DD1SSR 0x20008 +#define DD1SSR_TVR (1 << 15) +#define DD1SSR_FRM (1 << 14) +#define DD1SSR_BUF (1 << 12) +#define DD1SSR_VBK (1 << 11) +#define DD1SSR_RINT (1 << 9) +#define DD1SSR_HBK (1 << 8) +#define DD1SSR_ADC(n) (1 << ((n)-1)) + +#define DD1SRCR 0x2000c +#define DD1SRCR_TVR (1 << 15) +#define DD1SRCR_FRM (1 << 14) +#define DD1SRCR_BUF (1 << 12) +#define DD1SRCR_VBK (1 << 11) +#define DD1SRCR_RINT (1 << 9) +#define DD1SRCR_HBK (1 << 8) +#define DD1SRCR_ADC(n) (1 << ((n)-1)) + +#define DD1IER 0x20010 +#define DD1IER_TVR (1 << 15) +#define DD1IER_FRM (1 << 14) +#define DD1IER_BUF (1 << 12) +#define DD1IER_VBK (1 << 11) +#define DD1IER_RINT (1 << 9) +#define DD1IER_HBK (1 << 8) +#define DD1IER_ADC(n) (1 << ((n)-1)) + +#define DEFR8 0x20020 +#define DEFR8_CODE (0x7790 << 16) +#define DEFR8_VSCS (1 << 6) +#define DEFR8_DRGBS_DU(n) ((n) << 4) +#define DEFR8_DRGBS_MASK (3 << 4) +#define DEFR8_DEFE8 (1 << 0) + +#define DOFLR 0x20024 +#define DOFLR_CODE (0x7790 << 16) +#define DOFLR_HSYCFL1 (1 << 13) +#define DOFLR_VSYCFL1 (1 << 12) +#define DOFLR_ODDFL1 (1 << 11) +#define DOFLR_DISPFL1 (1 << 10) +#define DOFLR_CDEFL1 (1 << 9) +#define DOFLR_RGBFL1 (1 << 8) +#define DOFLR_HSYCFL0 (1 << 5) +#define DOFLR_VSYCFL0 (1 << 4) +#define DOFLR_ODDFL0 (1 << 3) +#define DOFLR_DISPFL0 (1 << 2) +#define DOFLR_CDEFL0 (1 << 1) +#define DOFLR_RGBFL0 (1 << 0) + +#define DIDSR 0x20028 +#define DIDSR_CODE (0x7790 << 16) +#define DIDSR_LCDS_DCLKIN(n) (0 << (8 + (n) * 2)) +#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2)) +#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2)) +#define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2)) +#define DIDSR_PCDS_CLK(n, clk) (clk << ((n) * 2)) +#define DIDSR_PCDS_MASK(n) (3 << ((n) * 2)) + +/* ----------------------------------------------------------------------------- * Display Timing Generation Registers */ @@ -349,21 +404,34 @@ #define APnMR_BM_AD (2 << 4) /* Auto Display Change Mode */ #define APnMWR 0x0a104 + +#define APnDSXR 0x0a110 +#define APnDSYR 0x0a114 +#define APnDPXR 0x0a118 +#define APnDPYR 0x0a11c + #define APnDSA0R 0x0a120 #define APnDSA1R 0x0a124 #define APnDSA2R 0x0a128 + +#define APnSPXR 0x0a130 +#define APnSPYR 0x0a134 +#define APnWASPR 0x0a138 +#define APnWAMWR 0x0a13c + +#define APnBTR 0x0a140 + #define APnMLR 0x0a150 +#define APnSWAPR 0x0a180 /* ----------------------------------------------------------------------------- * Display Capture Registers */ +#define DCMR 0x0c100 #define DCMWR 0x0c104 -#define DC2MWR 0x0c204 #define DCSAR 0x0c120 -#define DC2SAR 0x0c220 #define DCMLR 0x0c150 -#define DC2MLR 0x0c250 /* ----------------------------------------------------------------------------- * Color Palette Registers diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 327289ec380d..41d563adfeaa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -1,5 +1,5 @@ /* - * rcar_du_vga.c -- R-Car Display Unit VGA DAC and Connector + * rcar_du_vgacon.c -- R-Car Display Unit VGA Connector * * Copyright (C) 2013 Renesas Corporation * @@ -16,12 +16,9 @@ #include <drm/drm_crtc_helper.h> #include "rcar_du_drv.h" +#include "rcar_du_encoder.h" #include "rcar_du_kms.h" -#include "rcar_du_vga.h" - -/* ----------------------------------------------------------------------------- - * Connector - */ +#include "rcar_du_vgacon.h" static int rcar_du_vga_connector_get_modes(struct drm_connector *connector) { @@ -49,7 +46,7 @@ static void rcar_du_vga_connector_destroy(struct drm_connector *connector) static enum drm_connector_status rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) { - return connector_status_unknown; + return connector_status_connected; } static const struct drm_connector_funcs connector_funcs = { @@ -59,8 +56,8 @@ static const struct drm_connector_funcs connector_funcs = { .destroy = rcar_du_vga_connector_destroy, }; -static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, - struct rcar_du_encoder *renc) +int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc) { struct rcar_du_connector *rcon; struct drm_connector *connector; @@ -97,53 +94,3 @@ static int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, return 0; } - -/* ----------------------------------------------------------------------------- - * Encoder - */ - -static void rcar_du_vga_encoder_dpms(struct drm_encoder *encoder, int mode) -{ -} - -static bool rcar_du_vga_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = rcar_du_vga_encoder_dpms, - .mode_fixup = rcar_du_vga_encoder_mode_fixup, - .prepare = rcar_du_encoder_mode_prepare, - .commit = rcar_du_encoder_mode_commit, - .mode_set = rcar_du_encoder_mode_set, -}; - -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = drm_encoder_cleanup, -}; - -int rcar_du_vga_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_vga_data *data, - unsigned int output) -{ - struct rcar_du_encoder *renc; - int ret; - - renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL); - if (renc == NULL) - return -ENOMEM; - - renc->output = output; - - ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs, - DRM_MODE_ENCODER_DAC); - if (ret < 0) - return ret; - - drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs); - - return rcar_du_vga_connector_init(rcdu, renc); -} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vga.h b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h index 66b4d2d7190d..b12b0cf7f117 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vga.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h @@ -1,5 +1,5 @@ /* - * rcar_du_vga.h -- R-Car Display Unit VGA DAC and Connector + * rcar_du_vgacon.h -- R-Car Display Unit VGA Connector * * Copyright (C) 2013 Renesas Corporation * @@ -11,14 +11,13 @@ * (at your option) any later version. */ -#ifndef __RCAR_DU_VGA_H__ -#define __RCAR_DU_VGA_H__ +#ifndef __RCAR_DU_VGACON_H__ +#define __RCAR_DU_VGACON_H__ struct rcar_du_device; -struct rcar_du_encoder_vga_data; +struct rcar_du_encoder; -int rcar_du_vga_init(struct rcar_du_device *rcdu, - const struct rcar_du_encoder_vga_data *data, - unsigned int output); +int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, + struct rcar_du_encoder *renc); -#endif /* __RCAR_DU_VGA_H__ */ +#endif /* __RCAR_DU_VGACON_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h new file mode 100644 index 000000000000..77cf9289ab65 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h @@ -0,0 +1,69 @@ +/* + * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions + * + * Copyright (C) 2013 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#ifndef __RCAR_LVDS_REGS_H__ +#define __RCAR_LVDS_REGS_H__ + +#define LVDCR0 0x0000 +#define LVDCR0_DUSEL (1 << 15) +#define LVDCR0_DMD (1 << 12) +#define LVDCR0_LVMD_MASK (0xf << 8) +#define LVDCR0_LVMD_SHIFT 8 +#define LVDCR0_PLLEN (1 << 4) +#define LVDCR0_BEN (1 << 2) +#define LVDCR0_LVEN (1 << 1) +#define LVDCR0_LVRES (1 << 0) + +#define LVDCR1 0x0004 +#define LVDCR1_CKSEL (1 << 15) +#define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2)) +#define LVDCR1_CLKSTBY (3 << 0) + +#define LVDPLLCR 0x0008 +#define LVDPLLCR_CEEN (1 << 14) +#define LVDPLLCR_FBEN (1 << 13) +#define LVDPLLCR_COSEL (1 << 12) +#define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) +#define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) +#define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) +#define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) +#define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) + +#define LVDCTRCR 0x000c +#define LVDCTRCR_CTR3SEL_ZERO (0 << 12) +#define LVDCTRCR_CTR3SEL_ODD (1 << 12) +#define LVDCTRCR_CTR3SEL_CDE (2 << 12) +#define LVDCTRCR_CTR3SEL_MASK (7 << 12) +#define LVDCTRCR_CTR2SEL_DISP (0 << 8) +#define LVDCTRCR_CTR2SEL_ODD (1 << 8) +#define LVDCTRCR_CTR2SEL_CDE (2 << 8) +#define LVDCTRCR_CTR2SEL_HSYNC (3 << 8) +#define LVDCTRCR_CTR2SEL_VSYNC (4 << 8) +#define LVDCTRCR_CTR2SEL_MASK (7 << 8) +#define LVDCTRCR_CTR1SEL_VSYNC (0 << 4) +#define LVDCTRCR_CTR1SEL_DISP (1 << 4) +#define LVDCTRCR_CTR1SEL_ODD (2 << 4) +#define LVDCTRCR_CTR1SEL_CDE (3 << 4) +#define LVDCTRCR_CTR1SEL_HSYNC (4 << 4) +#define LVDCTRCR_CTR1SEL_MASK (7 << 4) +#define LVDCTRCR_CTR0SEL_HSYNC (0 << 0) +#define LVDCTRCR_CTR0SEL_VSYNC (1 << 0) +#define LVDCTRCR_CTR0SEL_DISP (2 << 0) +#define LVDCTRCR_CTR0SEL_ODD (3 << 0) +#define LVDCTRCR_CTR0SEL_CDE (4 << 0) +#define LVDCTRCR_CTR0SEL_MASK (7 << 0) + +#define LVDCHCR 0x0010 +#define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) +#define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) + +#endif /* __RCAR_LVDS_REGS_H__ */ |