diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_video.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 219 |
1 files changed, 167 insertions, 52 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index 8a1253e51f04..915a20eb003e 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -31,6 +31,7 @@ #include "vsp1_bru.h" #include "vsp1_entity.h" #include "vsp1_rwpf.h" +#include "vsp1_uds.h" #include "vsp1_video.h" #define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV @@ -50,70 +51,85 @@ static const struct vsp1_format_info vsp1_video_formats[] = { { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 8, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 8, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB444, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB444, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_ARGB555, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS, + 1, { 16, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB555, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS, - 1, { 16, 0, 0 }, false, false, 1, 1 }, + 1, { 16, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 24, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 24, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ABGR32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XBGR32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, - { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, + { V4L2_PIX_FMT_ARGB32, V4L2_MBUS_FMT_ARGB8888_1X32, + VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | + VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, + 1, { 32, 0, 0 }, false, false, 1, 1, true }, + { V4L2_PIX_FMT_XRGB32, V4L2_MBUS_FMT_ARGB8888_1X32, VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 32, 0, 0 }, false, false, 1, 1 }, + 1, { 32, 0, 0 }, false, false, 1, 1, false }, { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, false, 2, 1 }, + 1, { 16, 0, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, false, true, 2, 1 }, + 1, { 16, 0, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, false, 2, 1 }, + 1, { 16, 0, 0 }, true, false, 2, 1, false }, { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 1, { 16, 0, 0 }, true, true, 2, 1 }, + 1, { 16, 0, 0 }, true, true, 2, 1, false }, { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 2 }, + 2, { 8, 16, 0 }, false, false, 2, 2, false }, { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 2 }, + 2, { 8, 16, 0 }, false, true, 2, 2, false }, { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, false, 2, 1 }, + 2, { 8, 16, 0 }, false, false, 2, 1, false }, { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 2, { 8, 16, 0 }, false, true, 2, 1 }, + 2, { 8, 16, 0 }, false, true, 2, 1, false }, { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32, VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS | VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS, - 3, { 8, 8, 8 }, false, false, 2, 2 }, + 3, { 8, 8, 8 }, false, false, 2, 2, false }, }; /* @@ -181,11 +197,29 @@ static int __vsp1_video_try_format(struct vsp1_video *video, struct v4l2_pix_format_mplane *pix, const struct vsp1_format_info **fmtinfo) { + static const u32 xrgb_formats[][2] = { + { V4L2_PIX_FMT_RGB444, V4L2_PIX_FMT_XRGB444 }, + { V4L2_PIX_FMT_RGB555, V4L2_PIX_FMT_XRGB555 }, + { V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_XBGR32 }, + { V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_XRGB32 }, + }; + const struct vsp1_format_info *info; unsigned int width = pix->width; unsigned int height = pix->height; unsigned int i; + /* Backward compatibility: replace deprecated RGB formats by their XRGB + * equivalent. This selects the format older userspace applications want + * while still exposing the new format. + */ + for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) { + if (xrgb_formats[i][0] == pix->pixelformat) { + pix->pixelformat = xrgb_formats[i][1]; + break; + } + } + /* Retrieve format information and select the default format if the * requested format isn't supported. */ @@ -273,13 +307,14 @@ vsp1_video_format_adjust(struct vsp1_video *video, * Pipeline Management */ -static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, +static int vsp1_pipeline_validate_branch(struct vsp1_pipeline *pipe, + struct vsp1_rwpf *input, struct vsp1_rwpf *output) { struct vsp1_entity *entity; unsigned int entities = 0; struct media_pad *pad; - bool uds_found = false; + bool bru_found = false; input->location.left = 0; input->location.top = 0; @@ -301,10 +336,15 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, */ if (entity->type == VSP1_ENTITY_BRU) { struct vsp1_bru *bru = to_bru(&entity->subdev); - struct v4l2_rect *rect = &bru->compose[pad->index]; + struct v4l2_rect *rect = + &bru->inputs[pad->index].compose; + + bru->inputs[pad->index].rpf = input; input->location.left = rect->left; input->location.top = rect->top; + + bru_found = true; } /* We've reached the WPF, we're done. */ @@ -319,9 +359,12 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, /* UDS can't be chained. */ if (entity->type == VSP1_ENTITY_UDS) { - if (uds_found) + if (pipe->uds) return -EPIPE; - uds_found = true; + + pipe->uds = entity; + pipe->uds_input = bru_found ? pipe->bru + : &input->entity; } /* Follow the source link. The link setup operations ensure @@ -340,6 +383,27 @@ static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input, return 0; } +static void __vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) +{ + if (pipe->bru) { + struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) + bru->inputs[i].rpf = NULL; + } + + INIT_LIST_HEAD(&pipe->entities); + pipe->state = VSP1_PIPELINE_STOPPED; + pipe->buffers_ready = 0; + pipe->num_video = 0; + pipe->num_inputs = 0; + pipe->output = NULL; + pipe->bru = NULL; + pipe->lif = NULL; + pipe->uds = NULL; +} + static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, struct vsp1_video *video) { @@ -395,7 +459,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, * contains no loop and that all branches end at the output WPF. */ for (i = 0; i < pipe->num_inputs; ++i) { - ret = vsp1_pipeline_validate_branch(pipe->inputs[i], + ret = vsp1_pipeline_validate_branch(pipe, pipe->inputs[i], pipe->output); if (ret < 0) goto error; @@ -404,13 +468,7 @@ static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe, return 0; error: - INIT_LIST_HEAD(&pipe->entities); - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; + __vsp1_pipeline_cleanup(pipe); return ret; } @@ -441,16 +499,8 @@ static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe) mutex_lock(&pipe->lock); /* If we're the last user clean up the pipeline. */ - if (--pipe->use_count == 0) { - INIT_LIST_HEAD(&pipe->entities); - pipe->state = VSP1_PIPELINE_STOPPED; - pipe->buffers_ready = 0; - pipe->num_video = 0; - pipe->num_inputs = 0; - pipe->output = NULL; - pipe->bru = NULL; - pipe->lif = NULL; - } + if (--pipe->use_count == 0) + __vsp1_pipeline_cleanup(pipe); mutex_unlock(&pipe->lock); } @@ -471,7 +521,8 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) int ret; spin_lock_irqsave(&pipe->irqlock, flags); - pipe->state = VSP1_PIPELINE_STOPPING; + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; spin_unlock_irqrestore(&pipe->irqlock, flags); ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED, @@ -479,7 +530,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe) ret = ret == 0 ? -ETIMEDOUT : 0; list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->route) + if (entity->route && entity->route->reg) vsp1_write(entity->vsp1, entity->route->reg, VI6_DPR_NODE_UNUSED); @@ -576,6 +627,7 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { + enum vsp1_pipeline_state state; unsigned long flags; unsigned int i; @@ -591,11 +643,13 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) spin_lock_irqsave(&pipe->irqlock, flags); + state = pipe->state; + pipe->state = VSP1_PIPELINE_STOPPED; + /* If a stop has been requested, mark the pipeline as stopped and * return. */ - if (pipe->state == VSP1_PIPELINE_STOPPING) { - pipe->state = VSP1_PIPELINE_STOPPED; + if (state == VSP1_PIPELINE_STOPPING) { wake_up(&pipe->wq); goto done; } @@ -608,6 +662,47 @@ done: spin_unlock_irqrestore(&pipe->irqlock, flags); } +/* + * Propagate the alpha value through the pipeline. + * + * As the UDS has restricted scaling capabilities when the alpha component needs + * to be scaled, we disable alpha scaling when the UDS input has a fixed alpha + * value. The UDS then outputs a fixed alpha value which needs to be programmed + * from the input RPF alpha. + */ +void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, + struct vsp1_entity *input, + unsigned int alpha) +{ + struct vsp1_entity *entity; + struct media_pad *pad; + + pad = media_entity_remote_pad(&input->pads[RWPF_PAD_SOURCE]); + + while (pad) { + if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity)); + + /* The BRU background color has a fixed alpha value set to 255, + * the output alpha value is thus always equal to 255. + */ + if (entity->type == VSP1_ENTITY_BRU) + alpha = 255; + + if (entity->type == VSP1_ENTITY_UDS) { + struct vsp1_uds *uds = to_uds(&entity->subdev); + + vsp1_uds_set_alpha(uds, alpha); + break; + } + + pad = &entity->pads[entity->source_pad]; + pad = media_entity_remote_pad(pad); + } +} + /* ----------------------------------------------------------------------------- * videobuf2 Queue Operations */ @@ -654,8 +749,6 @@ static int vsp1_video_buffer_prepare(struct vb2_buffer *vb) if (vb->num_planes < format->num_planes) return -EINVAL; - buf->video = video; - for (i = 0; i < vb->num_planes; ++i) { buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); buf->length[i] = vb2_plane_size(vb, i); @@ -717,6 +810,25 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) mutex_lock(&pipe->lock); if (pipe->stream_count == pipe->num_video - 1) { + if (pipe->uds) { + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); + + /* If a BRU is present in the pipeline before the UDS, + * the alpha component doesn't need to be scaled as the + * BRU output alpha value is fixed to 255. Otherwise we + * need to scale the alpha component only when available + * at the input RPF. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { + uds->scale_alpha = false; + } else { + struct vsp1_rwpf *rpf = + to_rwpf(&pipe->uds_input->subdev); + + uds->scale_alpha = rpf->video.fmtinfo->alpha; + } + } + list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity); @@ -744,6 +856,7 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); + struct vsp1_video_buffer *buffer; unsigned long flags; int ret; @@ -761,6 +874,8 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) /* Remove all buffers from the IRQ queue. */ spin_lock_irqsave(&video->irqlock, flags); + list_for_each_entry(buffer, &video->irqqueue, queue) + vb2_buffer_done(&buffer->buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); } @@ -950,8 +1065,8 @@ static int vsp1_video_open(struct file *file) file->private_data = vfh; - if (!vsp1_device_get(video->vsp1)) { - ret = -EBUSY; + ret = vsp1_device_get(video->vsp1); + if (ret < 0) { v4l2_fh_del(vfh); kfree(vfh); } |