mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 07:36:36 +07:00
4e86c208dd
Use the newly exposed VSP1 interface to enable interlaced frame support through the VSP1 LIF pipelines. The DSMR register is updated to set the ODEV flag on interlaced pipelines, thus defining an interlaced stream as having the ODD field located in the second half (BOTTOM) of the frame buffer. Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
420 lines
10 KiB
C
420 lines
10 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor
|
|
*
|
|
* Copyright (C) 2015 Renesas Electronics Corporation
|
|
*
|
|
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
|
|
*/
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_crtc.h>
|
|
#include <drm/drm_crtc_helper.h>
|
|
#include <drm/drm_fb_cma_helper.h>
|
|
#include <drm/drm_gem_cma_helper.h>
|
|
#include <drm/drm_gem_framebuffer_helper.h>
|
|
#include <drm/drm_plane_helper.h>
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/videodev2.h>
|
|
|
|
#include <media/vsp1.h>
|
|
|
|
#include "rcar_du_drv.h"
|
|
#include "rcar_du_kms.h"
|
|
#include "rcar_du_vsp.h"
|
|
|
|
static void rcar_du_vsp_complete(void *private, bool completed, u32 crc)
|
|
{
|
|
struct rcar_du_crtc *crtc = private;
|
|
|
|
if (crtc->vblank_enable)
|
|
drm_crtc_handle_vblank(&crtc->crtc);
|
|
|
|
if (completed)
|
|
rcar_du_crtc_finish_page_flip(crtc);
|
|
|
|
drm_crtc_add_crc_entry(&crtc->crtc, false, 0, &crc);
|
|
}
|
|
|
|
void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
|
|
{
|
|
const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode;
|
|
struct rcar_du_device *rcdu = crtc->group->dev;
|
|
struct vsp1_du_lif_config cfg = {
|
|
.width = mode->hdisplay,
|
|
.height = mode->vdisplay,
|
|
.interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE,
|
|
.callback = rcar_du_vsp_complete,
|
|
.callback_data = crtc,
|
|
};
|
|
struct rcar_du_plane_state state = {
|
|
.state = {
|
|
.alpha = DRM_BLEND_ALPHA_OPAQUE,
|
|
.crtc = &crtc->crtc,
|
|
.dst.x1 = 0,
|
|
.dst.y1 = 0,
|
|
.dst.x2 = mode->hdisplay,
|
|
.dst.y2 = mode->vdisplay,
|
|
.src.x1 = 0,
|
|
.src.y1 = 0,
|
|
.src.x2 = mode->hdisplay << 16,
|
|
.src.y2 = mode->vdisplay << 16,
|
|
.zpos = 0,
|
|
},
|
|
.format = rcar_du_format_info(DRM_FORMAT_ARGB8888),
|
|
.source = RCAR_DU_PLANE_VSPD1,
|
|
.colorkey = 0,
|
|
};
|
|
|
|
if (rcdu->info->gen >= 3)
|
|
state.hwindex = (crtc->index % 2) ? 2 : 0;
|
|
else
|
|
state.hwindex = crtc->index % 2;
|
|
|
|
__rcar_du_plane_setup(crtc->group, &state);
|
|
|
|
/*
|
|
* Ensure that the plane source configuration takes effect by requesting
|
|
* a restart of the group. See rcar_du_plane_atomic_update() for a more
|
|
* detailed explanation.
|
|
*
|
|
* TODO: Check whether this is still needed on Gen3.
|
|
*/
|
|
crtc->group->need_restart = true;
|
|
|
|
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
|
|
}
|
|
|
|
void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
|
|
{
|
|
vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
|
|
}
|
|
|
|
void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
|
|
{
|
|
vsp1_du_atomic_begin(crtc->vsp->vsp, crtc->vsp_pipe);
|
|
}
|
|
|
|
void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc)
|
|
{
|
|
struct vsp1_du_atomic_pipe_config cfg = { { 0, } };
|
|
struct rcar_du_crtc_state *state;
|
|
|
|
state = to_rcar_crtc_state(crtc->crtc.state);
|
|
cfg.crc = state->crc;
|
|
|
|
vsp1_du_atomic_flush(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
|
|
}
|
|
|
|
/* Keep the two tables in sync. */
|
|
static const u32 formats_kms[] = {
|
|
DRM_FORMAT_RGB332,
|
|
DRM_FORMAT_ARGB4444,
|
|
DRM_FORMAT_XRGB4444,
|
|
DRM_FORMAT_ARGB1555,
|
|
DRM_FORMAT_XRGB1555,
|
|
DRM_FORMAT_RGB565,
|
|
DRM_FORMAT_BGR888,
|
|
DRM_FORMAT_RGB888,
|
|
DRM_FORMAT_BGRA8888,
|
|
DRM_FORMAT_BGRX8888,
|
|
DRM_FORMAT_ARGB8888,
|
|
DRM_FORMAT_XRGB8888,
|
|
DRM_FORMAT_UYVY,
|
|
DRM_FORMAT_VYUY,
|
|
DRM_FORMAT_YUYV,
|
|
DRM_FORMAT_YVYU,
|
|
DRM_FORMAT_NV12,
|
|
DRM_FORMAT_NV21,
|
|
DRM_FORMAT_NV16,
|
|
DRM_FORMAT_NV61,
|
|
DRM_FORMAT_YUV420,
|
|
DRM_FORMAT_YVU420,
|
|
DRM_FORMAT_YUV422,
|
|
DRM_FORMAT_YVU422,
|
|
DRM_FORMAT_YUV444,
|
|
DRM_FORMAT_YVU444,
|
|
};
|
|
|
|
static const u32 formats_v4l2[] = {
|
|
V4L2_PIX_FMT_RGB332,
|
|
V4L2_PIX_FMT_ARGB444,
|
|
V4L2_PIX_FMT_XRGB444,
|
|
V4L2_PIX_FMT_ARGB555,
|
|
V4L2_PIX_FMT_XRGB555,
|
|
V4L2_PIX_FMT_RGB565,
|
|
V4L2_PIX_FMT_RGB24,
|
|
V4L2_PIX_FMT_BGR24,
|
|
V4L2_PIX_FMT_ARGB32,
|
|
V4L2_PIX_FMT_XRGB32,
|
|
V4L2_PIX_FMT_ABGR32,
|
|
V4L2_PIX_FMT_XBGR32,
|
|
V4L2_PIX_FMT_UYVY,
|
|
V4L2_PIX_FMT_VYUY,
|
|
V4L2_PIX_FMT_YUYV,
|
|
V4L2_PIX_FMT_YVYU,
|
|
V4L2_PIX_FMT_NV12M,
|
|
V4L2_PIX_FMT_NV21M,
|
|
V4L2_PIX_FMT_NV16M,
|
|
V4L2_PIX_FMT_NV61M,
|
|
V4L2_PIX_FMT_YUV420M,
|
|
V4L2_PIX_FMT_YVU420M,
|
|
V4L2_PIX_FMT_YUV422M,
|
|
V4L2_PIX_FMT_YVU422M,
|
|
V4L2_PIX_FMT_YUV444M,
|
|
V4L2_PIX_FMT_YVU444M,
|
|
};
|
|
|
|
static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
|
|
{
|
|
struct rcar_du_vsp_plane_state *state =
|
|
to_rcar_vsp_plane_state(plane->plane.state);
|
|
struct rcar_du_crtc *crtc = to_rcar_crtc(state->state.crtc);
|
|
struct drm_framebuffer *fb = plane->plane.state->fb;
|
|
struct vsp1_du_atomic_config cfg = {
|
|
.pixelformat = 0,
|
|
.pitch = fb->pitches[0],
|
|
.alpha = state->state.alpha >> 8,
|
|
.zpos = state->state.zpos,
|
|
};
|
|
unsigned int i;
|
|
|
|
cfg.src.left = state->state.src.x1 >> 16;
|
|
cfg.src.top = state->state.src.y1 >> 16;
|
|
cfg.src.width = drm_rect_width(&state->state.src) >> 16;
|
|
cfg.src.height = drm_rect_height(&state->state.src) >> 16;
|
|
|
|
cfg.dst.left = state->state.dst.x1;
|
|
cfg.dst.top = state->state.dst.y1;
|
|
cfg.dst.width = drm_rect_width(&state->state.dst);
|
|
cfg.dst.height = drm_rect_height(&state->state.dst);
|
|
|
|
for (i = 0; i < state->format->planes; ++i)
|
|
cfg.mem[i] = sg_dma_address(state->sg_tables[i].sgl)
|
|
+ fb->offsets[i];
|
|
|
|
for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) {
|
|
if (formats_kms[i] == state->format->fourcc) {
|
|
cfg.pixelformat = formats_v4l2[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
vsp1_du_atomic_update(plane->vsp->vsp, crtc->vsp_pipe,
|
|
plane->index, &cfg);
|
|
}
|
|
|
|
static int rcar_du_vsp_plane_prepare_fb(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
{
|
|
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
|
|
struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
|
|
struct rcar_du_device *rcdu = vsp->dev;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
/*
|
|
* There's no need to prepare (and unprepare) the framebuffer when the
|
|
* plane is not visible, as it will not be displayed.
|
|
*/
|
|
if (!state->visible)
|
|
return 0;
|
|
|
|
for (i = 0; i < rstate->format->planes; ++i) {
|
|
struct drm_gem_cma_object *gem =
|
|
drm_fb_cma_get_gem_obj(state->fb, i);
|
|
struct sg_table *sgt = &rstate->sg_tables[i];
|
|
|
|
ret = dma_get_sgtable(rcdu->dev, sgt, gem->vaddr, gem->paddr,
|
|
gem->base.size);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
ret = vsp1_du_map_sg(vsp->vsp, sgt);
|
|
if (!ret) {
|
|
sg_free_table(sgt);
|
|
ret = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
ret = drm_gem_fb_prepare_fb(plane, state);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
while (i--) {
|
|
struct sg_table *sgt = &rstate->sg_tables[i];
|
|
|
|
vsp1_du_unmap_sg(vsp->vsp, sgt);
|
|
sg_free_table(sgt);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rcar_du_vsp_plane_cleanup_fb(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
{
|
|
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
|
|
struct rcar_du_vsp *vsp = to_rcar_vsp_plane(plane)->vsp;
|
|
unsigned int i;
|
|
|
|
if (!state->visible)
|
|
return;
|
|
|
|
for (i = 0; i < rstate->format->planes; ++i) {
|
|
struct sg_table *sgt = &rstate->sg_tables[i];
|
|
|
|
vsp1_du_unmap_sg(vsp->vsp, sgt);
|
|
sg_free_table(sgt);
|
|
}
|
|
}
|
|
|
|
static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
{
|
|
struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state);
|
|
|
|
return __rcar_du_plane_atomic_check(plane, state, &rstate->format);
|
|
}
|
|
|
|
static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane,
|
|
struct drm_plane_state *old_state)
|
|
{
|
|
struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane);
|
|
struct rcar_du_crtc *crtc = to_rcar_crtc(old_state->crtc);
|
|
|
|
if (plane->state->visible)
|
|
rcar_du_vsp_plane_setup(rplane);
|
|
else
|
|
vsp1_du_atomic_update(rplane->vsp->vsp, crtc->vsp_pipe,
|
|
rplane->index, NULL);
|
|
}
|
|
|
|
static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = {
|
|
.prepare_fb = rcar_du_vsp_plane_prepare_fb,
|
|
.cleanup_fb = rcar_du_vsp_plane_cleanup_fb,
|
|
.atomic_check = rcar_du_vsp_plane_atomic_check,
|
|
.atomic_update = rcar_du_vsp_plane_atomic_update,
|
|
};
|
|
|
|
static struct drm_plane_state *
|
|
rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane)
|
|
{
|
|
struct rcar_du_vsp_plane_state *copy;
|
|
|
|
if (WARN_ON(!plane->state))
|
|
return NULL;
|
|
|
|
copy = kzalloc(sizeof(*copy), GFP_KERNEL);
|
|
if (copy == NULL)
|
|
return NULL;
|
|
|
|
__drm_atomic_helper_plane_duplicate_state(plane, ©->state);
|
|
|
|
return ©->state;
|
|
}
|
|
|
|
static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane,
|
|
struct drm_plane_state *state)
|
|
{
|
|
__drm_atomic_helper_plane_destroy_state(state);
|
|
kfree(to_rcar_vsp_plane_state(state));
|
|
}
|
|
|
|
static void rcar_du_vsp_plane_reset(struct drm_plane *plane)
|
|
{
|
|
struct rcar_du_vsp_plane_state *state;
|
|
|
|
if (plane->state) {
|
|
rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state);
|
|
plane->state = NULL;
|
|
}
|
|
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
if (state == NULL)
|
|
return;
|
|
|
|
__drm_atomic_helper_plane_reset(plane, &state->state);
|
|
state->state.zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
|
|
}
|
|
|
|
static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = {
|
|
.update_plane = drm_atomic_helper_update_plane,
|
|
.disable_plane = drm_atomic_helper_disable_plane,
|
|
.reset = rcar_du_vsp_plane_reset,
|
|
.destroy = drm_plane_cleanup,
|
|
.atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state,
|
|
.atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state,
|
|
};
|
|
|
|
int rcar_du_vsp_init(struct rcar_du_vsp *vsp, struct device_node *np,
|
|
unsigned int crtcs)
|
|
{
|
|
struct rcar_du_device *rcdu = vsp->dev;
|
|
struct platform_device *pdev;
|
|
unsigned int num_crtcs = hweight32(crtcs);
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
/* Find the VSP device and initialize it. */
|
|
pdev = of_find_device_by_node(np);
|
|
if (!pdev)
|
|
return -ENXIO;
|
|
|
|
vsp->vsp = &pdev->dev;
|
|
|
|
ret = vsp1_du_init(vsp->vsp);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/*
|
|
* The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to
|
|
* 4 RPFs.
|
|
*/
|
|
vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4;
|
|
|
|
vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes,
|
|
sizeof(*vsp->planes), GFP_KERNEL);
|
|
if (!vsp->planes)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < vsp->num_planes; ++i) {
|
|
enum drm_plane_type type = i < num_crtcs
|
|
? DRM_PLANE_TYPE_PRIMARY
|
|
: DRM_PLANE_TYPE_OVERLAY;
|
|
struct rcar_du_vsp_plane *plane = &vsp->planes[i];
|
|
|
|
plane->vsp = vsp;
|
|
plane->index = i;
|
|
|
|
ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
|
|
&rcar_du_vsp_plane_funcs,
|
|
formats_kms,
|
|
ARRAY_SIZE(formats_kms),
|
|
NULL, type, NULL);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
drm_plane_helper_add(&plane->plane,
|
|
&rcar_du_vsp_plane_helper_funcs);
|
|
|
|
if (type == DRM_PLANE_TYPE_PRIMARY)
|
|
continue;
|
|
|
|
drm_plane_create_alpha_property(&plane->plane);
|
|
drm_plane_create_zpos_property(&plane->plane, 1, 1,
|
|
vsp->num_planes - 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|