v4l: vsp1: Add interlaced mode support
Set extended display list header, and set Auto-FLD mode for
interlaced mode.
Signed-off-by: Koji Matsuoka <koji.matsuoka.xm@renesas.com>
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 5685d5a..f47a705 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -1,7 +1,7 @@
/*
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -248,6 +248,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
/* Signal polarities */
value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
| ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
+ | ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? DSMR_ODEV : 0)
| DSMR_DIPM_DISP | DSMR_CSPM;
rcar_du_crtc_write(rcrtc, DSMR, value);
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
index 2c260c3..4ad483b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c
@@ -1,7 +1,7 @@
/*
* rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -178,6 +178,12 @@ static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane)
};
unsigned int i;
+ if (plane->plane.state->crtc->mode.flags
+ & DRM_MODE_FLAG_INTERLACE)
+ cfg.interlaced = true;
+ else
+ cfg.interlaced = false;
+
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;
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
index 78ef838..0a9ac8f 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -41,6 +41,11 @@ struct vsp1_rwpf;
struct vsp1_sru;
struct vsp1_uds;
+/* Workaround for hung up at the time of underrun in R-Car H3(ES1.x) */
+#define VSP1_UNDERRUN_WORKAROUND BIT(0)
+/* Auto-FLD for Display List not support */
+#define VSP1_AUTO_FLD_NOT_SUPPORT BIT(1)
+
#define VSP1_MAX_LIF 2
#define VSP1_MAX_RPF 5
#define VSP1_MAX_UDS 3
@@ -73,6 +78,7 @@ struct vsp1_device {
struct device *dev;
const struct vsp1_device_info *info;
u32 version;
+ u32 ths_quirks;
void __iomem *mmio;
struct rcar_fcp_device *fcp;
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c
index 37e2c98..74b0aa0 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/vsp1/vsp1_dl.c
@@ -20,8 +20,12 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_drm.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
#define VSP1_DL_NUM_ENTRIES 256
+#define VSP1_DL_EXT_NUM_ENTRIES 160
#define VSP1_DLH_INT_ENABLE (1 << 1)
#define VSP1_DLH_AUTO_START (1 << 0)
@@ -36,6 +40,24 @@ struct vsp1_dl_header {
struct vsp1_dl_header_list lists[8];
u32 next_header;
u32 flags;
+ /* if (VI6_DL_EXT_CTRL.EXT) */
+ u32 zero_bits;
+ /* zero_bits:6 + pre_ext_dl_exec:1 + */
+ /* post_ext_dl_exec:1 + zero_bits:8 + pre_ext_dl_num_cmd:16 */
+ u32 pre_post_num;
+ u32 pre_ext_dl_plist;
+ /* zero_bits:16 + post_ext_dl_num_cmd:16 */
+ u32 post_ext_dl_num_cmd;
+ u32 post_ext_dl_p_list;
+} __attribute__((__packed__));
+
+struct vsp1_ext_dl_body {
+ u32 ext_dl_cmd[2];
+ u32 ext_dl_data[2];
+} __attribute__((__packed__));
+
+struct vsp1_ext_addr {
+ u32 addr;
} __attribute__((__packed__));
struct vsp1_dl_entry {
@@ -49,6 +71,10 @@ struct vsp1_dl_entry {
* @free: entry in the pool free body list
* @pool: pool to which this body belongs
* @vsp1: the VSP1 device
+ * @ext_body: display list extended body
+ * @ext_dma: DMA address for extended body
+ * @src_dst_addr: display list (Auto-FLD) source/destination address
+ * @ext_addr_dma: DMA address for display list (Auto-FLD)
* @entries: array of entries
* @dma: DMA address of the entries
* @size: size of the DMA memory in bytes
@@ -64,6 +90,12 @@ struct vsp1_dl_body {
struct vsp1_dl_body_pool *pool;
struct vsp1_device *vsp1;
+ struct vsp1_ext_dl_body *ext_body;
+ dma_addr_t ext_dma;
+
+ struct vsp1_ext_addr *src_dst_addr;
+ dma_addr_t ext_addr_dma;
+
struct vsp1_dl_entry *entries;
dma_addr_t dma;
size_t size;
@@ -212,6 +244,9 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
for (i = 0; i < num_bodies; ++i) {
struct vsp1_dl_body *dlb = &pool->bodies[i];
+ size_t header_offset;
+ size_t ex_body_offset;
+ size_t ex_addr_offset;
dlb->pool = pool;
dlb->max_entries = num_entries;
@@ -219,6 +254,24 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
dlb->dma = pool->dma + i * dlb_size;
dlb->entries = pool->mem + i * dlb_size;
+ if (!(vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT)) {
+ header_offset = dlb->max_entries *
+ sizeof(*dlb->entries);
+ ex_body_offset = sizeof(struct vsp1_dl_header);
+ ex_addr_offset = sizeof(struct vsp1_ext_dl_body);
+
+ dlb->ext_dma = pool->dma + (i * dlb_size) +
+ header_offset + ex_body_offset;
+ dlb->ext_body = pool->mem + (i * dlb_size) +
+ header_offset + ex_body_offset;
+ dlb->ext_addr_dma = pool->dma + (i * dlb_size) +
+ header_offset + ex_body_offset +
+ ex_addr_offset;
+ dlb->src_dst_addr = pool->mem + (i * dlb_size) +
+ header_offset + ex_body_offset +
+ ex_addr_offset;
+ }
+
list_add_tail(&dlb->free, &pool->free);
}
@@ -318,6 +371,80 @@ void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
* Display List Transaction Management
*/
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_body *dlb,
+ struct vsp1_rwpf *rpf,
+ struct vsp1_rwpf_memory mem)
+{
+ const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
+ const struct v4l2_rect *crop;
+ u32 y_top_index, y_bot_index;
+ u32 u_top_index, u_bot_index;
+ u32 v_top_index, v_bot_index;
+ dma_addr_t y_top_addr, y_bot_addr;
+ dma_addr_t u_top_addr, u_bot_addr;
+ dma_addr_t v_top_addr, v_bot_addr;
+ u32 width, stride;
+
+ crop = vsp1_rwpf_get_crop(rpf, rpf->entity.config);
+ width = ALIGN(crop->width, 16);
+ stride = width * fmtinfo->bpp[0] / 8;
+
+ y_top_index = rpf->entity.index * 8;
+ y_bot_index = rpf->entity.index * 8 + 1;
+ u_top_index = rpf->entity.index * 8 + 2;
+ u_bot_index = rpf->entity.index * 8 + 3;
+ v_top_index = rpf->entity.index * 8 + 4;
+ v_bot_index = rpf->entity.index * 8 + 5;
+
+ switch (rpf->fmtinfo->fourcc) {
+ case V4L2_PIX_FMT_YUV420M:
+ case V4L2_PIX_FMT_YVU420M:
+ y_top_addr = mem.addr[0];
+ y_bot_addr = mem.addr[0] + stride;
+ u_top_addr = mem.addr[1];
+ u_bot_addr = mem.addr[1] + stride / 2;
+ v_top_addr = mem.addr[2];
+ v_bot_addr = mem.addr[2] + stride / 2;
+ break;
+
+ case V4L2_PIX_FMT_YUV422M:
+ case V4L2_PIX_FMT_YVU422M:
+ y_top_addr = mem.addr[0];
+ y_bot_addr = mem.addr[0] + stride * 2;
+ u_top_addr = mem.addr[1];
+ u_bot_addr = mem.addr[1] + stride;
+ v_top_addr = mem.addr[2];
+ v_bot_addr = mem.addr[2] + stride;
+ break;
+
+ case V4L2_PIX_FMT_YUV444M:
+ case V4L2_PIX_FMT_YVU444M:
+ y_top_addr = mem.addr[0];
+ y_bot_addr = mem.addr[0] + stride * 3;
+ u_top_addr = mem.addr[1];
+ u_bot_addr = mem.addr[1] + stride * 3;
+ v_top_addr = mem.addr[2];
+ v_bot_addr = mem.addr[2] + stride * 3;
+ break;
+
+ default:
+ y_top_addr = mem.addr[0];
+ y_bot_addr = mem.addr[0] + stride;
+ u_top_addr = mem.addr[1];
+ u_bot_addr = mem.addr[1] + stride;
+ v_top_addr = mem.addr[2];
+ v_bot_addr = mem.addr[2] + stride;
+ break;
+ }
+
+ dlb->src_dst_addr[y_top_index].addr = y_top_addr;
+ dlb->src_dst_addr[y_bot_index].addr = y_bot_addr;
+ dlb->src_dst_addr[u_top_index].addr = u_top_addr;
+ dlb->src_dst_addr[u_bot_index].addr = u_bot_addr;
+ dlb->src_dst_addr[v_top_index].addr = v_top_addr;
+ dlb->src_dst_addr[v_bot_index].addr = v_bot_addr;
+}
+
static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm,
struct vsp1_dl_body_pool *pool)
{
@@ -521,12 +648,17 @@ int vsp1_dl_list_add_chain(struct vsp1_dl_list *head,
return 0;
}
-static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
+static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last,
+ unsigned int pipe_index)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_dl_header_list *hdr = dl->header->lists;
struct vsp1_dl_body *dlb;
unsigned int num_lists = 0;
+ struct vsp1_device *vsp1 = dlm->vsp1;
+ struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
+ struct vsp1_pipeline *pipe = &drm_pipe->pipe;
+ unsigned int i, rpf_update = 0;
/*
* Fill the header with the display list bodies addresses and sizes. The
@@ -566,6 +698,36 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last)
*/
dl->header->next_header = dl->dma;
dl->header->flags = VSP1_DLH_INT_ENABLE | VSP1_DLH_AUTO_START;
+
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ if (!pipe->inputs[i])
+ continue;
+
+ rpf_update |= 0x01 << (16 + i);
+ }
+
+ if (!vsp1->info->uapi &&
+ !(dl->dlm->vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT)) {
+ /* Set extended display list header */
+ /* pre_ext_dl_exec = 1, pre_ext_dl_num_cmd = 1 */
+ dl->header->pre_post_num = (1 << 25) | (0x01);
+ dl->header->pre_ext_dl_plist = dl->body0->ext_dma;
+ dl->header->post_ext_dl_num_cmd = 0;
+ dl->header->post_ext_dl_p_list = 0;
+
+ /* Set extended display list (Auto-FLD) */
+ /* Set opecode */
+ dl->body0->ext_body->ext_dl_cmd[0] = 0x00000003;
+ /* RPF[0]-[4] address is updated */
+ dl->body0->ext_body->ext_dl_cmd[1] =
+ 0x00000001 | rpf_update;
+
+ /* Set pointer of source/destination address */
+ dl->body0->ext_body->ext_dl_data[0] =
+ dl->body0->ext_addr_dma;
+ /* Should be set to 0 */
+ dl->body0->ext_body->ext_dl_data[1] = 0;
+ }
} else {
/*
* Otherwise, in mem-to-mem mode, we work in single-shot mode
@@ -664,7 +826,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl)
dlm->active = dl;
}
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int pipe_index)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_dl_list *dl_child;
@@ -672,12 +834,13 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
if (dlm->mode == VSP1_DL_MODE_HEADER) {
/* Fill the header for the head and chained display lists. */
- vsp1_dl_list_fill_header(dl, list_empty(&dl->chain));
+ vsp1_dl_list_fill_header(dl, list_empty(&dl->chain),
+ pipe_index);
list_for_each_entry(dl_child, &dl->chain, chain) {
bool last = list_is_last(&dl_child->chain, &dl->chain);
- vsp1_dl_list_fill_header(dl_child, last);
+ vsp1_dl_list_fill_header(dl_child, last, pipe_index);
}
}
@@ -704,8 +867,9 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl)
* with the frame end interrupt. The function always returns true in header mode
* as display list processing is then not continuous and races never occur.
*/
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
+bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm, bool interlaced)
{
+ struct vsp1_device *vsp1 = dlm->vsp1;
bool completed = false;
spin_lock(&dlm->lock);
@@ -730,6 +894,10 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
if (vsp1_dl_list_hw_update_pending(dlm))
goto done;
+ if (interlaced && ((vsp1_read(vsp1, VI6_STATUS) &
+ VI6_STATUS_FLD_STD(dlm->index)) !=
+ VI6_STATUS_FLD_STD(dlm->index)))
+ goto done;
/*
* The device starts processing the queued display list right after the
* frame end interrupt. The display list thus becomes active.
@@ -759,21 +927,28 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
}
/* Hardware Setup */
-void vsp1_dlm_setup(struct vsp1_device *vsp1)
+void vsp1_dlm_setup(struct vsp1_device *vsp1, unsigned int pipe_index)
{
u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT)
| VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0
| VI6_DL_CTRL_DLE;
+ if (!vsp1->info->uapi &&
+ !(vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT)) {
+ vsp1_write(vsp1, VI6_DL_EXT_CTRL(pipe_index),
+ (0x02 << VI6_DL_EXT_CTRL_POLINT_SHIFT) |
+ VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT);
+ }
/*
* The DRM pipeline operates with display lists in Continuous Frame
* Mode, all other pipelines use manual start.
*/
- if (vsp1->drm)
+ if (vsp1->drm && vsp1->info->uapi)
ctrl |= VI6_DL_CTRL_CFM0 | VI6_DL_CTRL_NH0;
vsp1_write(vsp1, VI6_DL_CTRL, ctrl);
- vsp1_write(vsp1, VI6_DL_SWAP, VI6_DL_SWAP_LWS);
+ vsp1_write(vsp1, VI6_DL_SWAP(pipe_index), VI6_DL_SWAP_LWS |
+ ((pipe_index == 1) ? VI6_DL_SWAP_IND : 0));
}
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
@@ -808,6 +983,13 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
dlm->index = index;
dlm->mode = index == 0 && !vsp1->info->uapi
? VSP1_DL_MODE_HEADERLESS : VSP1_DL_MODE_HEADER;
+
+ if (((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
+ VI6_IP_VERSION_MODEL_VSPDL_GEN3) ||
+ (vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
+ VI6_IP_VERSION_MODEL_VSPD_GEN3)
+ dlm->mode = VSP1_DL_MODE_HEADER;
+
dlm->singleshot = vsp1->info->uapi;
dlm->vsp1 = vsp1;
@@ -824,6 +1006,17 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
? ALIGN(sizeof(struct vsp1_dl_header), 8)
: 0;
+ if (!vsp1->info->uapi &&
+ !(vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT)) {
+ size_t ex_addr_offset;
+ size_t ex_addr_size;
+
+ ex_addr_offset = sizeof(struct vsp1_ext_dl_body);
+ ex_addr_size = sizeof(struct vsp1_ext_dl_body)
+ * VSP1_DL_EXT_NUM_ENTRIES;
+ header_size += (ex_addr_offset + ex_addr_size);
+ }
+
dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc,
VSP1_DL_NUM_ENTRIES, header_size);
if (!dlm->pool)
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h
index 5ad2cec..b692760 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/vsp1/vsp1_dl.h
@@ -1,7 +1,7 @@
/*
* vsp1_dl.h -- R-Car VSP1 Display List
*
- * Copyright (C) 2015 Renesas Corporation
+ * Copyright (C) 2015-2017 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -15,25 +15,27 @@
#include <linux/types.h>
+#include "vsp1_rwpf.h"
+
struct vsp1_device;
struct vsp1_dl_body;
struct vsp1_dl_body_pool;
struct vsp1_dl_list;
struct vsp1_dl_manager;
-void vsp1_dlm_setup(struct vsp1_device *vsp1);
+void vsp1_dlm_setup(struct vsp1_device *vsp1, unsigned int pipe_index);
struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
unsigned int index,
unsigned int prealloc);
void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm);
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm);
-bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm);
+bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm, bool interlaced);
struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm);
void vsp1_dl_list_put(struct vsp1_dl_list *dl);
struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl);
-void vsp1_dl_list_commit(struct vsp1_dl_list *dl);
+void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int pipe_index);
struct vsp1_dl_body_pool *
vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies,
@@ -44,5 +46,8 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb);
void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data);
int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb);
int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl);
+void vsp1_dl_set_addr_auto_fld(struct vsp1_dl_body *dlb,
+ struct vsp1_rwpf *rpf,
+ struct vsp1_rwpf_memory mem);
#endif /* __VSP1_DL_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index 3c8b195..858a79f 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -265,7 +265,7 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
vsp1_entity_configure_frame(entity, pipe, dl, dlb, 0);
}
- vsp1_dl_list_commit(dl);
+ vsp1_dl_list_commit(dl, pipe_index);
/* Start the pipeline. */
spin_lock_irqsave(&pipe->irqlock, flags);
@@ -374,6 +374,15 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
rpf->format.plane_fmt[1].bytesperline = cfg->pitch;
rpf->alpha = cfg->alpha;
+ rpf->interlaced = cfg->interlaced;
+
+ if ((vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT) &&
+ rpf->interlaced) {
+ dev_err(vsp1->dev,
+ "Interlaced mode is not supported.\n");
+ return -EINVAL;
+ }
+
rpf->mem.addr[0] = cfg->mem[0];
rpf->mem.addr[1] = cfg->mem[1];
rpf->mem.addr[2] = cfg->mem[2];
@@ -592,7 +601,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
vsp1_entity_configure_frame(entity, pipe, dl, dlb, 0);
}
- vsp1_dl_list_commit(dl);
+ vsp1_dl_list_commit(dl, pipe_index);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index eed9516..ba75e33 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -20,6 +20,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/sys_soc.h>
#include <linux/videodev2.h>
#include <media/rcar-fcp.h>
@@ -41,6 +42,17 @@
#include "vsp1_uds.h"
#include "vsp1_video.h"
+static const struct soc_device_attribute ths_quirks_match[] = {
+ { .soc_id = "r8a7795", .revision = "ES1.*",
+ .data = (void *)(VSP1_UNDERRUN_WORKAROUND |
+ VSP1_AUTO_FLD_NOT_SUPPORT), },
+ { .soc_id = "r8a7795", .revision = "ES2.0",
+ .data = NULL, },
+ { .soc_id = "r8a7796",
+ .data = NULL, },
+ {/*sentinel*/}
+};
+
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
@@ -532,7 +544,12 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
- vsp1_dlm_setup(vsp1);
+ if (vsp1->info->lif_count == 2) {
+ vsp1_dlm_setup(vsp1, 0);
+ vsp1_dlm_setup(vsp1, 1);
+ } else {
+ vsp1_dlm_setup(vsp1, 0);
+ }
return 0;
}
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c
index fa445b1a..54b8fbc 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/vsp1/vsp1_pipe.c
@@ -1,7 +1,7 @@
/*
* vsp1_pipe.c -- R-Car VSP1 Pipeline
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -331,17 +331,26 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
{
- bool completed;
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ bool completed, interlaced = false;
+ int i;
if (pipe == NULL)
return;
+ for (i = 0; i < vsp1->info->rpf_count; ++i) {
+ if (!pipe->inputs[i])
+ continue;
+
+ interlaced = pipe->inputs[i]->interlaced;
+ }
+
/*
* If the DL commit raced with the frame end interrupt, the commit ends
* up being postponed by one frame. @completed represents whether the
* active frame was finished or postponed.
*/
- completed = vsp1_dlm_irq_frame_end(pipe->output->dlm);
+ completed = vsp1_dlm_irq_frame_end(pipe->output->dlm, interlaced);
if (pipe->hgo)
vsp1_hgo_frame_end(pipe->hgo);
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index dae0c19..e2dffbe 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -31,6 +31,7 @@
#define VI6_SRESET_SRTS(n) (1 << (n))
#define VI6_STATUS 0x0038
+#define VI6_STATUS_FLD_STD(n) (1 << ((n) + 28))
#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
@@ -70,12 +71,13 @@
#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4)
-#define VI6_DL_SWAP 0x0114
+#define VI6_DL_SWAP(n) (0x0114 + (n) * 56)
+#define VI6_DL_SWAP_IND (1 << 31)
#define VI6_DL_SWAP_LWS (1 << 2)
#define VI6_DL_SWAP_WDS (1 << 1)
#define VI6_DL_SWAP_BTS (1 << 0)
-#define VI6_DL_EXT_CTRL 0x011c
+#define VI6_DL_EXT_CTRL(n) (0x011c + (n) * 36)
#define VI6_DL_EXT_CTRL_NWE (1 << 16)
#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8)
#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
index 67f2fb3..6a04a4d 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -68,7 +68,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
+ if (rpf->interlaced)
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride * 2);
+ else
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
sink_format = vsp1_entity_get_pad_format(&rpf->entity,
@@ -104,9 +107,14 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
top = compose->top;
}
- vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
- (left << VI6_RPF_LOC_HCOORD_SHIFT) |
- (top << VI6_RPF_LOC_VCOORD_SHIFT));
+ if (rpf->interlaced)
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
+ (left << VI6_RPF_LOC_HCOORD_SHIFT) |
+ ((top / 2) << VI6_RPF_LOC_VCOORD_SHIFT));
+ else
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC,
+ (left << VI6_RPF_LOC_HCOORD_SHIFT) |
+ (top << VI6_RPF_LOC_VCOORD_SHIFT));
/*
* On Gen2 use the alpha channel (extended to 8 bits) when available or
@@ -185,6 +193,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity,
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->format;
struct v4l2_rect crop;
+ u32 crop_width, crop_height, crop_x, crop_y, fourcc;
if (partition == 0) {
vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET,
@@ -220,21 +229,63 @@ static void rpf_configure_frame(struct vsp1_entity *entity,
crop.left += pipe->partition->rpf.left;
}
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,
- (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,
- (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
- (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+ crop_width = crop.width;
+ crop_x = crop.left;
+ fourcc = rpf->fmtinfo->fourcc;
- mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline
- + crop.left * fmtinfo->bpp[0] / 8;
+ if (rpf->interlaced) {
+ crop_height = crop.height / 2;
+ crop_y = crop.top / 2;
+
+ if (fourcc == V4L2_PIX_FMT_UYVY ||
+ fourcc == V4L2_PIX_FMT_VYUY ||
+ fourcc == V4L2_PIX_FMT_YUYV ||
+ fourcc == V4L2_PIX_FMT_YVYU) {
+ crop_width = round_down(crop_width, 2);
+ crop_x = round_down(crop_x, 2);
+ } else if ((fourcc == V4L2_PIX_FMT_NV12M) ||
+ (fourcc == V4L2_PIX_FMT_NV21M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ crop_x = round_down(crop_x, 2);
+ crop_y = round_down(crop_y, 2);
+ } else if ((fourcc == V4L2_PIX_FMT_NV16M) ||
+ (fourcc == V4L2_PIX_FMT_NV61M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_x = round_down(crop_x, 2);
+ } else if ((fourcc == V4L2_PIX_FMT_YUV420M) ||
+ (fourcc == V4L2_PIX_FMT_YUV444M) ||
+ (fourcc == V4L2_PIX_FMT_YVU420M) ||
+ (fourcc == V4L2_PIX_FMT_YVU444M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ } else if ((fourcc == V4L2_PIX_FMT_YUV422M) ||
+ (fourcc == V4L2_PIX_FMT_YVU422M)) {
+ crop_width = round_down(crop_width, 2);
+ crop_height = round_down(crop_height, 2);
+ crop_x = round_down(crop_x, 2);
+ crop_y = round_down(crop_y, 2);
+ }
+ } else {
+ crop_height = crop.height;
+ crop_y = crop.top;
+ }
+
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE,
+ (crop_width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (crop_height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE,
+ (crop_width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (crop_height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ mem.addr[0] += crop_y * format->plane_fmt[0].bytesperline
+ + crop_x * fmtinfo->bpp[0] / 8;
if (format->num_planes > 1) {
unsigned int offset;
- offset = crop.top * format->plane_fmt[1].bytesperline
- + crop.left / fmtinfo->hsub
+ offset = crop_y * format->plane_fmt[1].bytesperline
+ + crop_x / fmtinfo->hsub
* fmtinfo->bpp[1] / 8;
mem.addr[1] += offset;
mem.addr[2] += offset;
@@ -248,9 +299,13 @@ static void rpf_configure_frame(struct vsp1_entity *entity,
fmtinfo->swap_uv)
swap(mem.addr[1], mem.addr[2]);
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
- vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
+ if (!(vsp1->ths_quirks & VSP1_AUTO_FLD_NOT_SUPPORT)) {
+ vsp1_dl_set_addr_auto_fld(dlb, rpf, mem);
+ } else {
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]);
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]);
+ }
}
static void rpf_partition(struct vsp1_entity *entity,
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
index 58215a7..a86f20b 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -1,7 +1,7 @@
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -69,6 +69,8 @@ struct vsp1_rwpf {
struct vsp1_rwpf_memory mem;
struct vsp1_dl_manager *dlm;
+
+ bool interlaced;
};
static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index 96d9872..0ce878f 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,7 +1,7 @@
/*
* vsp1_video.c -- R-Car VSP1 Video Node
*
- * Copyright (C) 2013-2015 Renesas Electronics Corporation
+ * Copyright (C) 2013-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -430,7 +430,7 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
}
/* Complete, and commit the head display list. */
- vsp1_dl_list_commit(dl);
+ vsp1_dl_list_commit(dl, 0);
vsp1_pipeline_run(pipe);
}
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index 68a8abe..7d66a85 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -1,7 +1,7 @@
/*
* vsp1.h -- R-Car VSP1 API
*
- * Copyright (C) 2015 Renesas Electronics Corporation
+ * Copyright (C) 2015-2017 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -49,6 +49,7 @@ struct vsp1_du_atomic_config {
struct v4l2_rect dst;
unsigned int alpha;
unsigned int zpos;
+ bool interlaced;
};
void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index);