diff options
Diffstat (limited to 'drivers/media/platform/renesas/vsp1')
29 files changed, 1075 insertions, 585 deletions
diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile index 4bb4dcbef7b5..de8c802e1d1a 100644 --- a/drivers/media/platform/renesas/vsp1/Makefile +++ b/drivers/media/platform/renesas/vsp1/Makefile @@ -5,6 +5,6 @@ vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o -vsp1-y += vsp1_lif.o vsp1_uif.o +vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h index 2f6f0c6ae555..f97a1a31bfab 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1.h +++ b/drivers/media/platform/renesas/vsp1/vsp1.h @@ -32,6 +32,7 @@ struct vsp1_clu; struct vsp1_hgo; struct vsp1_hgt; struct vsp1_hsit; +struct vsp1_iif; struct vsp1_lif; struct vsp1_lut; struct vsp1_rwpf; @@ -56,6 +57,8 @@ struct vsp1_uif; #define VSP1_HAS_BRS BIT(9) #define VSP1_HAS_EXT_DL BIT(10) #define VSP1_HAS_NON_ZERO_LBA BIT(11) +#define VSP1_HAS_IIF BIT(12) +#define VSP1_HAS_HSIT BIT(13) struct vsp1_device_info { u32 version; @@ -91,6 +94,7 @@ struct vsp1_device { struct vsp1_hgt *hgt; struct vsp1_hsit *hsi; struct vsp1_hsit *hst; + struct vsp1_iif *iif; struct vsp1_lif *lif[VSP1_MAX_LIF]; struct vsp1_lut *lut; struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index a8535c6e2c46..5fc2e5a3bb30 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -15,6 +15,7 @@ #include "vsp1.h" #include "vsp1_brx.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -96,13 +97,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev, return 0; } -static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_compose(sd_state, pad); -} - static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_state *sd_state, unsigned int pad, struct v4l2_mbus_framefmt *fmt) @@ -115,20 +109,26 @@ static void brx_try_format(struct vsp1_brx *brx, if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + + vsp1_entity_adjust_color_space(fmt); break; default: /* The BRx can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&brx->entity, sd_state, - BRX_PAD_SINK(0)); + format = v4l2_subdev_state_get_format(sd_state, + BRX_PAD_SINK(0)); fmt->code = format->code; + + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; break; } fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE); fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE); fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int brx_set_format(struct v4l2_subdev *subdev, @@ -150,14 +150,14 @@ static int brx_set_format(struct v4l2_subdev *subdev, brx_try_format(brx, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; /* Reset the compose rectangle. */ if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = brx_get_compose(brx, state, fmt->pad); + compose = v4l2_subdev_state_get_compose(state, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,8 +169,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, unsigned int i; for (i = 0; i <= brx->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&brx->entity, - state, i); + format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } } @@ -205,7 +204,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&brx->entity.lock); - sel->r = *brx_get_compose(brx, state, sel->pad); + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); mutex_unlock(&brx->entity.lock); return 0; @@ -242,8 +241,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -251,11 +249,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev, * Scaling isn't supported, the compose rectangle size must be identical * to the sink format size. */ - format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad); + format = v4l2_subdev_state_get_format(state, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = brx_get_compose(brx, state, sel->pad); + compose = v4l2_subdev_state_get_compose(state, sel->pad); *compose = sel->r; done: @@ -281,6 +279,7 @@ static const struct v4l2_subdev_ops brx_ops = { */ static void brx_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -290,8 +289,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, unsigned int flags; unsigned int i; - format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state, - brx->entity.source_pad); + format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index 625776a9bda4..98645bd2a983 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = { */ static void clu_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -181,8 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity, * The yuv_mode can't be changed during streaming. Cache it internally * for future runtime configuration calls. */ - format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state, - CLU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK); clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index ad3fa1c9cc73..bb8228b19824 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -1099,7 +1099,12 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, return NULL; dlm->index = index; - dlm->singleshot = vsp1->info->uapi; + /* + * uapi = single shot mode; + * DRM = continuous mode; + * VSPX = single shot mode; + */ + dlm->singleshot = vsp1->info->uapi || vsp1->iif; dlm->vsp1 = vsp1; spin_lock_init(&dlm->lock); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index 9b087bd8df7d..fe55e8747b05 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -118,26 +118,26 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, struct vsp1_entity *uif, unsigned int brx_input) { + const struct vsp1_drm_input *input = &vsp1->drm->inputs[rpf->entity.index]; struct v4l2_subdev_selection sel = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; struct v4l2_subdev_format format = { .which = V4L2_SUBDEV_FORMAT_ACTIVE, }; - const struct v4l2_rect *crop; int ret; /* * Configure the format on the RPF sink pad and propagate it up to the * BRx sink pad. */ - crop = &vsp1->drm->inputs[rpf->entity.index].crop; - format.pad = RWPF_PAD_SINK; - format.format.width = crop->width + crop->left; - format.format.height = crop->height + crop->top; + format.format.width = input->crop.width + input->crop.left; + format.format.height = input->crop.height + input->crop.top; format.format.code = rpf->fmtinfo->mbus; format.format.field = V4L2_FIELD_NONE; + format.format.ycbcr_enc = input->ycbcr_enc; + format.format.quantization = input->quantization; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, &format); @@ -151,7 +151,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, sel.pad = RWPF_PAD_SINK; sel.target = V4L2_SEL_TGT_CROP; - sel.r = *crop; + sel.r = input->crop; ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, &sel); @@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, list_add_tail(&released_brx->list_pipe, &pipe->entities); - /* Add the BRx to the pipeline. */ + /* + * Add the BRx to the pipeline, inserting it just before the + * WPF. + */ dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", __func__, pipe->lif->index, BRX_NAME(brx)); @@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, pipe->brx->sink = &pipe->output->entity; pipe->brx->sink_pad = 0; - list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + list_add_tail(&pipe->brx->list_pipe, + &pipe->output->entity.list_pipe); } /* @@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, if (!rpf->entity.pipe) { rpf->entity.pipe = pipe; - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + list_add(&rpf->entity.list_pipe, &pipe->entities); } brx->inputs[i].rpf = rpf; @@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) struct vsp1_dl_body *dlb; unsigned int dl_flags = 0; + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], + drm_pipe->width, 0); + if (drm_pipe->force_brx_release) dl_flags |= VSP1_DL_FRAME_END_INTERNAL; if (pipe->output->writeback) @@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) } vsp1_entity_route_setup(entity, pipe, dlb); - vsp1_entity_configure_stream(entity, pipe, dl, dlb); + vsp1_entity_configure_stream(entity, entity->state, pipe, + dl, dlb); vsp1_entity_configure_frame(entity, pipe, dl, dlb); - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, + &pipe->part_table[0], dl, dlb); } vsp1_dl_list_commit(dl, dl_flags); @@ -584,8 +593,8 @@ static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1, fmtinfo = vsp1_get_format_info(vsp1, pixelformat); if (!fmtinfo) { - dev_dbg(vsp1->dev, "Unsupported pixel format %08x\n", - pixelformat); + dev_dbg(vsp1->dev, "Unsupported pixel format %p4cc\n", + &pixelformat); return -EINVAL; } @@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret < 0) goto unlock; + vsp1_pipeline_dump(pipe, "LIF setup"); + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) @@ -815,12 +826,14 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; + struct vsp1_drm_input *input; struct vsp1_rwpf *rpf; int ret; if (rpf_index >= vsp1->info->rpf_count) return -EINVAL; + input = &vsp1->drm->inputs[rpf_index]; rpf = vsp1->rpf[rpf_index]; if (!cfg) { @@ -838,11 +851,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, } dev_dbg(vsp1->dev, - "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n", + "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%p4cc), pitch %u dma { %pad, %pad, %pad } zpos %u\n", __func__, rpf_index, cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height, cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height, - cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], + &cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1], &cfg->mem[2], cfg->zpos); /* @@ -862,9 +875,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, rpf->format.flags = cfg->premult ? V4L2_PIX_FMT_FLAG_PREMUL_ALPHA : 0; - vsp1->drm->inputs[rpf_index].crop = cfg->src; - vsp1->drm->inputs[rpf_index].compose = cfg->dst; - vsp1->drm->inputs[rpf_index].zpos = cfg->zpos; + input->crop = cfg->src; + input->compose = cfg->dst; + input->zpos = cfg->zpos; + input->ycbcr_enc = cfg->color_encoding; + input->quantization = cfg->color_range; drm_pipe->pipe.inputs[rpf_index] = rpf; @@ -906,6 +921,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, } vsp1_du_pipeline_setup_inputs(vsp1, pipe); + + vsp1_pipeline_dump(pipe, "atomic update"); + vsp1_du_pipeline_configure(pipe); done: @@ -959,6 +977,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1) vsp1_pipeline_init(pipe); + pipe->partitions = 1; + pipe->part_table = &drm_pipe->partition; + pipe->frame_end = vsp1_du_pipeline_frame_end; /* diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index ab8b7e3161a2..07a5d0adbd08 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -20,6 +20,7 @@ /** * struct vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display + * @partition: the pre-calculated partition used by the pipeline * @width: output display width * @height: output display height * @force_brx_release: when set, release the BRx during the next reconfiguration @@ -31,6 +32,7 @@ */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; + struct vsp1_partition partition; unsigned int width; unsigned int height; @@ -50,17 +52,19 @@ struct vsp1_drm_pipeline { * struct vsp1_drm - State for the API exposed to the DRM driver * @pipe: the VSP1 DRM pipeline used for display * @lock: protects the BRU and BRS allocation - * @inputs: source crop rectangle, destination compose rectangle and z-order - * position for every input (indexed by RPF index) + * @inputs: source crop rectangle, destination compose rectangle, z-order + * position and colorspace for every input (indexed by RPF index) */ struct vsp1_drm { struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF]; struct mutex lock; - struct { + struct vsp1_drm_input { struct v4l2_rect crop; struct v4l2_rect compose; unsigned int zpos; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; } inputs[VSP1_MAX_RPF]; }; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 1aac44d68731..8270a9d207cb 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -29,6 +29,7 @@ #include "vsp1_hgo.h" #include "vsp1_hgt.h" #include "vsp1_hsit.h" +#include "vsp1_iif.h" #include "vsp1_lif.h" #include "vsp1_lut.h" #include "vsp1_pipe.h" @@ -302,22 +303,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities); } - vsp1->hsi = vsp1_hsit_create(vsp1, true); - if (IS_ERR(vsp1->hsi)) { - ret = PTR_ERR(vsp1->hsi); - goto done; - } - - list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); - - vsp1->hst = vsp1_hsit_create(vsp1, false); - if (IS_ERR(vsp1->hst)) { - ret = PTR_ERR(vsp1->hst); - goto done; - } - - list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) { vsp1->hgo = vsp1_hgo_create(vsp1); if (IS_ERR(vsp1->hgo)) { @@ -340,6 +325,34 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) &vsp1->entities); } + if (vsp1_feature(vsp1, VSP1_HAS_IIF)) { + vsp1->iif = vsp1_iif_create(vsp1); + if (IS_ERR(vsp1->iif)) { + ret = PTR_ERR(vsp1->iif); + goto done; + } + + list_add_tail(&vsp1->iif->entity.list_dev, &vsp1->entities); + } + + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + vsp1->hsi = vsp1_hsit_create(vsp1, true); + if (IS_ERR(vsp1->hsi)) { + ret = PTR_ERR(vsp1->hsi); + goto done; + } + + list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities); + + vsp1->hst = vsp1_hsit_create(vsp1, false); + if (IS_ERR(vsp1->hst)) { + ret = PTR_ERR(vsp1->hst); + goto done; + } + + list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); + } + /* * The LIFs are only supported when used in conjunction with the DU, in * which case the userspace API is disabled. If the userspace API is @@ -683,8 +696,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO - | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -694,7 +707,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPR_H2, .model = "VSP1-R", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_HSIT | VSP1_HAS_SRU + | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 3, .wpf_count = 4, @@ -704,7 +718,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_GEN2, .model = "VSP1-D", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_HSIT + | VSP1_HAS_LUT, .lif_count = 1, .rpf_count = 4, .uds_count = 1, @@ -716,8 +731,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP1-S", .gen = 2, .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO - | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT + | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 5, .uds_count = 1, .wpf_count = 4, @@ -727,8 +742,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPS_V2H, .model = "VSP1V-S", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT - | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT + | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP, .rpf_count = 4, .uds_count = 1, .wpf_count = 4, @@ -738,7 +753,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .version = VI6_IP_VERSION_MODEL_VSPD_V2H, .model = "VSP1V-D", .gen = 2, - .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT, + .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT + | VSP1_HAS_LUT, .lif_count = 1, .rpf_count = 4, .uds_count = 1, @@ -750,8 +766,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .model = "VSP2-I", .gen = 3, .features = VSP1_HAS_CLU | VSP1_HAS_HGO | VSP1_HAS_HGT - | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP - | VSP1_HAS_WPF_VFLIP, + | VSP1_HAS_HSIT | VSP1_HAS_LUT | VSP1_HAS_SRU + | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP, .rpf_count = 1, .uds_count = 1, .wpf_count = 1, @@ -1005,7 +1021,7 @@ MODULE_DEVICE_TABLE(of, vsp1_of_match); static struct platform_driver vsp1_platform_driver = { .probe = vsp1_probe, - .remove_new = vsp1_remove, + .remove = vsp1_remove, .driver = { .name = "vsp1", .pm = &vsp1_pm_ops, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 0a5a7f9cc870..a6680d531872 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -63,19 +63,25 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, /* * The ILV and BRS share the same data path route. The extra BRSSEL bit * selects between the ILV and BRS. + * + * The BRU and IIF share the same data path route. The extra IIFSEL bit + * selects between the IIF and BRU. */ if (source->type == VSP1_ENTITY_BRS) route |= VI6_DPR_ROUTE_BRSSEL; + else if (source->type == VSP1_ENTITY_IIF) + route |= VI6_DPR_ROUTE_IIFSEL; vsp1_dl_body_write(dlb, source->route->reg, route); } void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_stream) - entity->ops->configure_stream(entity, pipe, dl, dlb); + entity->ops->configure_stream(entity, state, pipe, dl, dlb); } void vsp1_entity_configure_frame(struct vsp1_entity *entity, @@ -89,11 +95,27 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { if (entity->ops->configure_partition) - entity->ops->configure_partition(entity, pipe, dl, dlb); + entity->ops->configure_partition(entity, pipe, partition, + dl, dlb); +} + +void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format) +{ + u8 xfer_func = format->xfer_func; + u8 ycbcr_enc = format->ycbcr_enc; + u8 quantization = format->quantization; + + vsp1_adjust_color_space(format->code, &format->colorspace, &xfer_func, + &ycbcr_enc, &quantization); + + format->xfer_func = xfer_func; + format->ycbcr_enc = ycbcr_enc; + format->quantization = quantization; } /* ----------------------------------------------------------------------------- @@ -127,49 +149,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity, } } -/** - * vsp1_entity_get_pad_format - Get a pad format from storage for an entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * - * Return the format stored in the given configuration for an entity's pad. The - * configuration can be an ACTIVE or TRY configuration. - */ -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad) -{ - return v4l2_subdev_state_get_format(sd_state, pad); -} - -/** - * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity - * @entity: the entity - * @sd_state: the state storage - * @pad: the pad number - * @target: the selection target - * - * Return the selection rectangle stored in the given configuration for an - * entity's pad. The configuration can be an ACTIVE or TRY configuration. The - * selection target can be COMPOSE or CROP. - */ -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target) -{ - switch (target) { - case V4L2_SEL_TGT_COMPOSE: - return v4l2_subdev_state_get_compose(sd_state, pad); - case V4L2_SEL_TGT_CROP: - return v4l2_subdev_state_get_crop(sd_state, pad); - default: - return NULL; - } -} - /* * vsp1_subdev_get_pad_format - Subdev pad get_fmt handler * @subdev: V4L2 subdevice @@ -191,7 +170,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad); + fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad); mutex_unlock(&entity->lock); return 0; @@ -238,7 +217,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, return -EINVAL; mutex_lock(&entity->lock); - format = vsp1_entity_get_pad_format(entity, state, 0); + format = v4l2_subdev_state_get_format(state, 0); code->code = format->code; mutex_unlock(&entity->lock); } @@ -276,7 +255,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(entity, state, fse->pad); + format = v4l2_subdev_state_get_format(state, fse->pad); mutex_lock(&entity->lock); @@ -346,7 +325,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == entity->source_pad) { /* The output format can't be modified. */ @@ -369,24 +348,28 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, min_height, max_height); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(entity, state, entity->source_pad); + format = v4l2_subdev_state_get_format(state, entity->source_pad); *format = fmt->format; /* Reset the crop and compose rectangles. */ - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; selection->height = format->height; - selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad, - V4L2_SEL_TGT_COMPOSE); + selection = v4l2_subdev_state_get_compose(state, fmt->pad); selection->left = 0; selection->top = 0; selection->width = format->width; @@ -570,6 +553,9 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad) { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } static const struct vsp1_route vsp1_routes[] = { + { VSP1_ENTITY_IIF, 0, VI6_DPR_BRU_ROUTE, + { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1), + VI6_DPR_NODE_BRU_IN(3) }, VI6_DPR_NODE_WPF(0) }, { VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE, { VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 }, { VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h index 735f32dde4b5..b7c72d0b7f8e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -19,7 +19,6 @@ struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; -struct vsp1_partition_window; enum vsp1_entity_type { VSP1_ENTITY_BRS, @@ -29,6 +28,7 @@ enum vsp1_entity_type { VSP1_ENTITY_HGT, VSP1_ENTITY_HSI, VSP1_ENTITY_HST, + VSP1_ENTITY_IIF, VSP1_ENTITY_LIF, VSP1_ENTITY_LUT, VSP1_ENTITY_RPF, @@ -78,19 +78,30 @@ struct vsp1_route { * configuration. */ struct vsp1_entity_operations { - void (*destroy)(struct vsp1_entity *); - void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, struct vsp1_dl_body *); - void (*configure_partition)(struct vsp1_entity *, - struct vsp1_pipeline *, - struct vsp1_dl_list *, - struct vsp1_dl_body *); - unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *); - void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_partition *, unsigned int, - struct vsp1_partition_window *); + void (*destroy)(struct vsp1_entity *entity); + void (*configure_stream)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_frame)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + void (*configure_partition)(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + unsigned int (*max_width)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe); + void (*partition)(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window); }; struct vsp1_entity { @@ -138,20 +149,13 @@ struct v4l2_subdev_state * vsp1_entity_get_state(struct vsp1_entity *entity, struct v4l2_subdev_state *sd_state, enum v4l2_subdev_format_whence which); -struct v4l2_mbus_framefmt * -vsp1_entity_get_pad_format(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad); -struct v4l2_rect * -vsp1_entity_get_pad_selection(struct vsp1_entity *entity, - struct v4l2_subdev_state *sd_state, - unsigned int pad, unsigned int target); void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb); void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); @@ -163,9 +167,12 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity, void vsp1_entity_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); +void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format); + struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad); int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 40c571a987ef..2c8ce7175a4e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { */ static void hgo_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); @@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) if (hgo == NULL) return ERR_PTR(-ENOMEM); + /* Initialize the video device and queue for statistics data. */ + ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", + &hgo_entity_ops, hgo_mbus_formats, + ARRAY_SIZE(hgo_mbus_formats), + HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); + if (ret < 0) { + vsp1_entity_destroy(&hgo->histo.entity); + return ERR_PTR(ret); + } + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&hgo->ctrls.handler, vsp1->info->gen >= 3 ? 2 : 1); @@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1) hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler; - /* Initialize the video device and queue for statistics data. */ - ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo", - &hgo_entity_ops, hgo_mbus_formats, - ARRAY_SIZE(hgo_mbus_formats), - HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO); - if (ret < 0) { - vsp1_entity_destroy(&hgo->histo.entity); - return ERR_PTR(ret); - } - return hgo; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 8281b86874ab..858f330d44fa 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { */ static void hgt_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity, u8 upper; unsigned int i; - crop = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); - compose = vsp1_entity_get_pad_selection(entity, entity->state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_COMPOSE); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); + compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); @@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) if (hgt == NULL) return ERR_PTR(-ENOMEM); - /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&hgt->ctrls, 1); - v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); - - hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; - /* Initialize the video device and queue for statistics data. */ ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt", &hgt_entity_ops, hgt_mbus_formats, @@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1) return ERR_PTR(ret); } + /* Initialize the control handler. */ + v4l2_ctrl_handler_init(&hgt->ctrls, 1); + v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL); + + hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls; + v4l2_ctrl_handler_setup(&hgt->ctrls); return hgt; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c index 71155282ca11..c762202877ba 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c @@ -36,9 +36,8 @@ struct vsp1_histogram_buffer * vsp1_histogram_buffer_get(struct vsp1_histogram *histo) { struct vsp1_histogram_buffer *buf = NULL; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); if (list_empty(&histo->irqqueue)) goto done; @@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo) histo->readout = true; done: - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); return buf; } @@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, size_t size) { struct vsp1_pipeline *pipe = histo->entity.pipe; - unsigned long flags; /* * The pipeline pointer is guaranteed to be valid as this function is @@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size); vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE); - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock(&histo->irqlock); histo->readout = false; wake_up(&histo->wait_queue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock(&histo->irqlock); } /* ----------------------------------------------------------------------------- @@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue); struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf); - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); list_add_tail(&buf->queue, &histo->irqqueue); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static int histo_start_streaming(struct vb2_queue *vq, unsigned int count) @@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq) { struct vsp1_histogram *histo = vb2_get_drv_priv(vq); struct vsp1_histogram_buffer *buffer; - unsigned long flags; - spin_lock_irqsave(&histo->irqlock, flags); + spin_lock_irq(&histo->irqlock); /* Remove all buffers from the IRQ queue. */ list_for_each_entry(buffer, &histo->irqqueue, queue) @@ -152,15 +148,13 @@ static void histo_stop_streaming(struct vb2_queue *vq) /* Wait for the buffer being read out (if any) to complete. */ wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock); - spin_unlock_irqrestore(&histo->irqlock, flags); + spin_unlock_irq(&histo->irqlock); } static const struct vb2_ops histo_video_queue_qops = { .queue_setup = histo_queue_setup, .buf_prepare = histo_buffer_prepare, .buf_queue = histo_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = histo_start_streaming, .stop_streaming = histo_stop_streaming, }; @@ -222,9 +216,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: case V4L2_SEL_TGT_COMPOSE_DEFAULT: - crop = vsp1_entity_get_pad_selection(&histo->entity, state, - HISTO_PAD_SINK, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = crop->width; @@ -233,8 +225,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev, case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&histo->entity, state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -242,9 +233,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_COMPOSE: + sel->r = *v4l2_subdev_state_get_compose(state, sel->pad); + break; + case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -261,13 +254,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&histo->entity, sd_state, - HISTO_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE, @@ -276,14 +266,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Set the crop rectangle and reset the compose rectangle. */ - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, V4L2_SEL_TGT_CROP); - *selection = sel->r; - - selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); - *selection = sel->r; + *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r; + *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r; return 0; } @@ -292,7 +276,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { - struct vsp1_histogram *histo = subdev_to_histo(subdev); struct v4l2_rect *compose; struct v4l2_rect *crop; unsigned int ratio; @@ -305,9 +288,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, sel->r.left = 0; sel->r.top = 0; - crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(sd_state, sel->pad); /* * Clamp the width and height to acceptable values first and then @@ -332,9 +313,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev, ratio = 1 << (crop->height * 2 / sel->r.height / 3); sel->r.height = crop->height / ratio; - compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state, - sel->pad, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(sd_state, sel->pad); *compose = sel->r; return 0; @@ -371,31 +350,22 @@ done: return ret; } -static int histo_get_format(struct v4l2_subdev *subdev, +static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct vsp1_histogram *histo = subdev_to_histo(subdev); + if (fmt->pad == HISTO_PAD_SOURCE) { fmt->format.code = MEDIA_BUS_FMT_FIXED; fmt->format.width = 0; fmt->format.height = 0; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; + return 0; } - return vsp1_subdev_get_pad_format(subdev, sd_state, fmt); -} - -static int histo_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct vsp1_histogram *histo = subdev_to_histo(subdev); - - if (fmt->pad != HISTO_PAD_SINK) - return histo_get_format(subdev, sd_state, fmt); - return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, histo->formats, histo->num_formats, HISTO_MIN_SIZE, HISTO_MIN_SIZE, @@ -405,7 +375,7 @@ static int histo_set_format(struct v4l2_subdev *subdev, static const struct v4l2_subdev_pad_ops histo_pad_ops = { .enum_mbus_code = histo_enum_mbus_code, .enum_frame_size = histo_enum_frame_size, - .get_fmt = histo_get_format, + .get_fmt = vsp1_subdev_get_pad_format, .set_fmt = histo_set_format, .get_selection = histo_get_selection, .set_selection = histo_set_selection, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index bc1299c29ac9..1fcd1967d3b2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_hsit.h" #define HSIT_MIN_SIZE 4U @@ -78,7 +79,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev, goto done; } - format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == HSIT_PAD_SOURCE) { /* @@ -96,17 +97,24 @@ static int hsit_set_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, HSIT_MIN_SIZE, HSIT_MAX_SIZE); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&hsit->entity, state, - HSIT_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE); *format = fmt->format; format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32 : MEDIA_BUS_FMT_AHSV8888_1X32; + vsp1_entity_adjust_color_space(format); + done: mutex_unlock(&hsit->entity.lock); return ret; @@ -128,6 +136,7 @@ static const struct v4l2_subdev_ops hsit_ops = { */ static void hsit_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c new file mode 100644 index 000000000000..5dd62bebbe8c --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vsp1_iif.c -- R-Car VSP1 IIF (ISP Interface) + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Corporation + */ + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_iif.h" + +#define IIF_MIN_WIDTH 128U +#define IIF_MIN_HEIGHT 32U +#define IIF_MAX_WIDTH 5120U +#define IIF_MAX_HEIGHT 4096U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline void vsp1_iif_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) +{ + vsp1_dl_body_write(dlb, reg, data); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const unsigned int iif_codes[] = { + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_Y16_1X16, + MEDIA_BUS_FMT_METADATA_FIXED +}; + +static int iif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes, + ARRAY_SIZE(iif_codes)); +} + +static int iif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, + IIF_MIN_WIDTH, IIF_MIN_HEIGHT, + IIF_MAX_WIDTH, IIF_MAX_HEIGHT); +} + +static int iif_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes, + ARRAY_SIZE(iif_codes), + IIF_MIN_WIDTH, IIF_MIN_HEIGHT, + IIF_MAX_WIDTH, IIF_MAX_HEIGHT); +} + +static const struct v4l2_subdev_pad_ops iif_pad_ops = { + .enum_mbus_code = iif_enum_mbus_code, + .enum_frame_size = iif_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = iif_set_format, +}; + +static const struct v4l2_subdev_ops iif_ops = { + .pad = &iif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void iif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + vsp1_iif_write(dlb, VI6_IIF_CTRL, VI6_IIF_CTRL_CTRL); +} + +static const struct vsp1_entity_operations iif_entity_ops = { + .configure_stream = iif_configure_stream, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1) +{ + struct vsp1_iif *iif; + int ret; + + iif = devm_kzalloc(vsp1->dev, sizeof(*iif), GFP_KERNEL); + if (!iif) + return ERR_PTR(-ENOMEM); + + iif->entity.ops = &iif_entity_ops; + iif->entity.type = VSP1_ENTITY_IIF; + + /* + * The IIF is never exposed to userspace, but media entity registration + * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to + * avoid triggering a WARN_ON(), the value won't be seen anywhere. + */ + ret = vsp1_entity_init(vsp1, &iif->entity, "iif", 3, &iif_ops, + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); + if (ret < 0) + return ERR_PTR(ret); + + return iif; +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.h b/drivers/media/platform/renesas/vsp1/vsp1_iif.h new file mode 100644 index 000000000000..46f327851c35 --- /dev/null +++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_iif.h -- R-Car VSP1 IIF (ISP Interface) + * + * Copyright (C) 2025 Ideas On Board Oy + * Copyright (C) 2025 Renesas Corporation + */ +#ifndef __VSP1_IIF_H__ +#define __VSP1_IIF_H__ + +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +#define VSPX_IIF_SINK_PAD_IMG 0 +#define VSPX_IIF_SINK_PAD_CONFIG 2 + +struct vsp1_iif { + struct vsp1_entity entity; +}; + +static inline struct vsp1_iif *to_iif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_iif, entity.subdev); +} + +struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1); + +#endif /* __VSP1_IIF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c index b1d21a54837b..b3d83d1c5306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c @@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = { */ static void lif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity, unsigned int obth; unsigned int lbth; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state, - LIF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE); switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) { case VI6_IP_VERSION_MODEL_VSPD_GEN2: diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index 451d24ab0b56..dd264e6532e0 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = { */ static void lut_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 68d05243c3ee..3cbb768cf6ad 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -138,14 +138,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 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, false }, - { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_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, false }, - { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_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, false }, { V4L2_PIX_FMT_RGBX1010102, MEDIA_BUS_FMT_ARGB8888_1X32, VI6_FMT_RGB10_RGB10A2_A2RGB10, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS, @@ -162,10 +154,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 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, false }, - { V4L2_PIX_FMT_VYUY, MEDIA_BUS_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, false }, { V4L2_PIX_FMT_YUYV, MEDIA_BUS_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, @@ -222,6 +210,24 @@ static const struct vsp1_format_info vsp1_video_formats[] = { 1, { 32, 0, 0 }, false, false, 2, 1, false }, }; +static const struct vsp1_format_info vsp1_video_gen2_formats[] = { + { V4L2_PIX_FMT_VYUY, MEDIA_BUS_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, false }, +}; + +static const struct vsp1_format_info vsp1_video_hsit_formats[] = { + { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_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, false }, + { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_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, false }, +}; + /** * vsp1_get_format_info - Retrieve format information for a 4CC * @vsp1: the VSP1 device @@ -235,26 +241,164 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, { unsigned int i; - /* Special case, the VYUY and HSV formats are supported on Gen2 only. */ - if (vsp1->info->gen != 2) { - switch (fourcc) { - case V4L2_PIX_FMT_VYUY: - case V4L2_PIX_FMT_HSV24: - case V4L2_PIX_FMT_HSV32: - return NULL; + for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { + const struct vsp1_format_info *info = &vsp1_video_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + + if (vsp1->info->gen == 2) { + for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { + const struct vsp1_format_info *info = + &vsp1_video_gen2_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + } + + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { + const struct vsp1_format_info *info = + &vsp1_video_hsit_formats[i]; + + if (info->fourcc == fourcc) + return info; + } + } + + return NULL; +} + +/** + * vsp1_get_format_info_by_index - Enumerate format information + * @vsp1: the VSP1 device + * @index: the format index + * @code: media bus code to limit enumeration + * + * Return a pointer to the format information structure corresponding to the + * given index, or NULL if the index exceeds the supported formats list. If the + * @code parameter is not zero, only formats compatible with the media bus code + * will be enumerated. + */ +const struct vsp1_format_info * +vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, + u32 code) +{ + unsigned int i; + + if (!code) { + if (index < ARRAY_SIZE(vsp1_video_formats)) + return &vsp1_video_formats[index]; + + if (vsp1->info->gen == 2) { + index -= ARRAY_SIZE(vsp1_video_formats); + if (index < ARRAY_SIZE(vsp1_video_gen2_formats)) + return &vsp1_video_gen2_formats[index]; + } + + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + index -= ARRAY_SIZE(vsp1_video_gen2_formats); + if (index < ARRAY_SIZE(vsp1_video_hsit_formats)) + return &vsp1_video_hsit_formats[index]; } + + return NULL; } for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) { const struct vsp1_format_info *info = &vsp1_video_formats[i]; - if (info->fourcc == fourcc) - return info; + if (info->mbus == code) { + if (!index) + return info; + index--; + } + } + + if (vsp1->info->gen == 2) { + for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) { + const struct vsp1_format_info *info = + &vsp1_video_gen2_formats[i]; + + if (info->mbus == code) { + if (!index) + return info; + index--; + } + } + } + + if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) { + for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) { + const struct vsp1_format_info *info = + &vsp1_video_hsit_formats[i]; + + if (info->mbus == code) { + if (!index) + return info; + index--; + } + } } return NULL; } +/** + * vsp1_adjust_color_space - Adjust color space fields in a format + * @code: the media bus code + * @colorspace: the colorspace + * @xfer_func: the transfer function + * @encoding: the encoding + * @quantization: the quantization + * + * This function adjusts all color space fields of a video device of subdev + * format structure, taking into account the requested format, requested color + * space and limitations of the VSP1. It should be used in the video device and + * subdev set format handlers. + * + * The colorspace and xfer_func fields are freely configurable, as they are out + * of scope for VSP processing. The encoding and quantization is hardcoded for + * non-YUV formats, and can be configured for YUV formats. + */ +void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, + u8 *encoding, u8 *quantization) +{ + if (*colorspace == V4L2_COLORSPACE_DEFAULT || + *colorspace >= V4L2_COLORSPACE_LAST) + *colorspace = code == MEDIA_BUS_FMT_AYUV8_1X32 + ? V4L2_COLORSPACE_SMPTE170M + : V4L2_COLORSPACE_SRGB; + + if (*xfer_func == V4L2_XFER_FUNC_DEFAULT || + *xfer_func >= V4L2_XFER_FUNC_LAST) + *xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(*colorspace); + + switch (code) { + case MEDIA_BUS_FMT_ARGB8888_1X32: + default: + *encoding = V4L2_YCBCR_ENC_601; + *quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + + case MEDIA_BUS_FMT_AHSV8888_1X32: + *encoding = V4L2_HSV_ENC_256; + *quantization = V4L2_QUANTIZATION_FULL_RANGE; + break; + + case MEDIA_BUS_FMT_AYUV8_1X32: + if (*encoding != V4L2_YCBCR_ENC_601 && + *encoding != V4L2_YCBCR_ENC_709) + *encoding = V4L2_YCBCR_ENC_601; + if (*quantization != V4L2_QUANTIZATION_FULL_RANGE && + *quantization != V4L2_QUANTIZATION_LIM_RANGE) + *quantization = V4L2_QUANTIZATION_LIM_RANGE; + break; + } +} + /* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -286,6 +430,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) pipe->brx = NULL; pipe->hgo = NULL; pipe->hgt = NULL; + pipe->iif = NULL; pipe->lif = NULL; pipe->uds = NULL; } @@ -301,6 +446,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) pipe->state = VSP1_PIPELINE_STOPPED; } +void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe, + const char *msg) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_entity *entity; + bool first = true; + + printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg); + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + const char *name; + + name = strchrnul(entity->subdev.name, ' '); + name = name ? name + 1 : entity->subdev.name; + + pr_cont("%s%s", first ? "" : ", ", name); + first = false; + } + + pr_cont("\n"); +} + /* Must be called with the pipe irqlock held. */ void vsp1_pipeline_run(struct vsp1_pipeline *pipe) { @@ -444,6 +611,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } +/* ----------------------------------------------------------------------------- + * VSP1 Partition Algorithm support + */ + /* * Propagate the partition calculations through the pipeline * @@ -452,17 +623,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, * source. Each entity must produce the partition required for the previous * entity in the pipeline. */ -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window) +static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int index, + struct v4l2_rect *window) { struct vsp1_entity *entity; list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) { if (entity->ops->partition) - entity->ops->partition(entity, pipe, partition, index, - window); + entity->ops->partition(entity, entity->state, pipe, + partition, index, window); } } +/* + * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a + * partition + * + * @pipe: the pipeline + * @partition: partition that will hold the calculated values + * @div_size: pre-determined maximum partition division size + * @index: partition index + */ +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, + struct vsp1_partition *partition, + unsigned int div_size, + unsigned int index) +{ + const struct v4l2_mbus_framefmt *format; + struct v4l2_rect window; + unsigned int modulus; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + + /* Initialise the partition with sane starting conditions. */ + window.left = index * div_size; + window.width = div_size; + window.top = 0; + window.height = format->height; + + modulus = format->width % div_size; + + /* + * We need to prevent the last partition from being smaller than the + * *minimum* width of the hardware capabilities. + * + * If the modulus is less than half of the partition size, + * the penultimate partition is reduced to half, which is added + * to the final partition: |1234|1234|1234|12|341| + * to prevent this: |1234|1234|1234|1234|1|. + */ + if (modulus) { + /* + * pipe->partitions is 1 based, whilst index is a 0 based index. + * Normalise this locally. + */ + unsigned int partitions = pipe->partitions - 1; + + if (modulus < div_size / 2) { + if (index == partitions - 1) { + /* Halve the penultimate partition. */ + window.width = div_size / 2; + } else if (index == partitions) { + /* Increase the final partition. */ + window.width = (div_size / 2) + modulus; + window.left -= div_size / 2; + } + } else if (index == partitions) { + window.width = modulus; + } + } + + vsp1_pipeline_propagate_partition(pipe, partition, index, &window); +} diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 674b5748d929..7f623b8cbe5c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -9,6 +9,7 @@ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ +#include <linux/dynamic_debug.h> #include <linux/kref.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -54,17 +55,6 @@ enum vsp1_pipeline_state { }; /* - * struct vsp1_partition_window - Partition window coordinates - * @left: horizontal coordinate of the partition start in pixels relative to the - * left edge of the image - * @width: partition width in pixels - */ -struct vsp1_partition_window { - unsigned int left; - unsigned int width; -}; - -/* * struct vsp1_partition - A description of a slice for the partition algorithm * @rpf: The RPF partition window configuration * @uds_sink: The UDS input partition window configuration @@ -73,11 +63,11 @@ struct vsp1_partition_window { * @wpf: The WPF partition window configuration */ struct vsp1_partition { - struct vsp1_partition_window rpf; - struct vsp1_partition_window uds_sink; - struct vsp1_partition_window uds_source; - struct vsp1_partition_window sru; - struct vsp1_partition_window wpf; + struct v4l2_rect rpf[VSP1_MAX_RPF]; + struct v4l2_rect uds_sink; + struct v4l2_rect uds_source; + struct v4l2_rect sru; + struct v4l2_rect wpf; }; /* @@ -106,7 +96,6 @@ struct vsp1_partition { * @configured: when false the @stream_config shall be written to the hardware * @interlaced: True when the pipeline is configured in interlaced mode * @partitions: The number of partitions used to process one frame - * @partition: The current partition for configuration to process * @part_table: The pre-calculated partitions used by the pipeline */ struct vsp1_pipeline { @@ -130,6 +119,7 @@ struct vsp1_pipeline { struct vsp1_entity *brx; struct vsp1_entity *hgo; struct vsp1_entity *hgt; + struct vsp1_entity *iif; struct vsp1_entity *lif; struct vsp1_entity *uds; struct vsp1_entity *uds_input; @@ -146,7 +136,6 @@ struct vsp1_pipeline { bool interlaced; unsigned int partitions; - struct vsp1_partition *partition; struct vsp1_partition *part_table; u32 underrun_count; @@ -155,6 +144,24 @@ struct vsp1_pipeline { void vsp1_pipeline_reset(struct vsp1_pipeline *pipe); void vsp1_pipeline_init(struct vsp1_pipeline *pipe); +void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe, + const char *msg); + +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define vsp1_pipeline_dump(pipe, msg) \ + _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg) +#elif defined(DEBUG) +#define vsp1_pipeline_dump(pipe, msg) \ + __vsp1_pipeline_dump(NULL, pipe, msg) +#else +#define vsp1_pipeline_dump(pipe, msg) \ +({ \ + if (0) \ + __vsp1_pipeline_dump(NULL, pipe, msg); \ +}) +#endif + void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); @@ -166,12 +173,17 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, struct vsp1_dl_body *dlb, unsigned int alpha); -void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, +void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, - unsigned int index, - struct vsp1_partition_window *window); + unsigned int div_size, + unsigned int index); const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); +const struct vsp1_format_info * +vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index, + u32 code); +void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, + u8 *encoding, u8 *quantization); #endif /* __VSP1_PIPE_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h index 7eca82e0ba7e..86e47c2d991f 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h @@ -253,6 +253,13 @@ #define VI6_RPF_BRDITH_CTRL_CBRM BIT(0) /* ----------------------------------------------------------------------------- + * IIF Control Registers + */ + +#define VI6_IIF_CTRL 0x0608 +#define VI6_IIF_CTRL_CTRL 0x13 + +/* ----------------------------------------------------------------------------- * WPF Control Registers */ @@ -388,6 +395,7 @@ #define VI6_DPR_HST_ROUTE 0x2044 #define VI6_DPR_HSI_ROUTE 0x2048 #define VI6_DPR_BRU_ROUTE 0x204c +#define VI6_DPR_ROUTE_IIFSEL BIT(28) #define VI6_DPR_ILV_BRS_ROUTE 0x2050 #define VI6_DPR_ROUTE_BRSSEL BIT(28) #define VI6_DPR_ROUTE_FXA_MASK (0xff << 16) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c index c47579efc65f..811f2b7c5cc5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, */ static void rpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -80,14 +81,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity, vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ - sink_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&rpf->entity, - rpf->entity.state, - RWPF_PAD_SOURCE); - - infmt = VI6_RPF_INFMT_CIPM + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); + + infmt = (pipe->iif ? 0 : VI6_RPF_INFMT_CIPM) | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); if (fmtinfo->swap_yc) @@ -95,12 +92,44 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (fmtinfo->swap_uv) infmt |= VI6_RPF_INFMT_SPUVS; - if (sink_format->code != source_format->code) - infmt |= VI6_RPF_INFMT_CSC; + if (sink_format->code != source_format->code) { + u16 ycbcr_enc; + u16 quantization; + u32 rdtm; + + if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + ycbcr_enc = sink_format->ycbcr_enc; + quantization = sink_format->quantization; + } else { + ycbcr_enc = source_format->ycbcr_enc; + quantization = source_format->quantization; + } + + if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT601; + else if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_FULL_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT601_EXT; + else if (ycbcr_enc == V4L2_YCBCR_ENC_709 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + rdtm = VI6_RPF_INFMT_RDTM_BT709; + else + rdtm = VI6_RPF_INFMT_RDTM_BT709_EXT; + + infmt |= VI6_RPF_INFMT_CSC | rdtm; + } vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); + /* No further configuration for VSPX. */ + if (pipe->iif) { + /* VSPX wants alpha_sel to be set to 0. */ + vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, 0); + return; + } + if (entity->vsp1->info->gen == 4) { u32 ext_infmt0; u32 ext_infmt1; @@ -157,10 +186,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity, if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->brx, - pipe->brx->state, - rpf->brx_input, - V4L2_SEL_TGT_COMPOSE); + compose = v4l2_subdev_state_get_compose(pipe->brx->state, + rpf->brx_input); left = compose->left; top = compose->top; } @@ -284,6 +311,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity, static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { @@ -292,7 +320,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity, struct vsp1_device *vsp1 = rpf->entity.vsp1; const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; const struct v4l2_pix_format_mplane *format = &rpf->format; - struct v4l2_rect crop; + struct v4l2_rect crop = partition->rpf[rpf->entity.index]; /* * Source size and crop offsets. @@ -302,22 +330,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity, * offsets are needed, as planes 2 and 3 always have identical * strides. */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state); - - /* - * Partition Algorithm Control - * - * The partition algorithm can split this frame into multiple - * slices. We must scale our partition window based on the pipe - * configuration to match the destination partition window. - * To achieve this, we adjust our crop to provide a 'sub-crop' - * matching the expected partition window. Only 'left' and - * 'width' need to be adjusted. - */ - if (pipe->partitions > 1) { - crop.width = pipe->partition->rpf.width; - crop.left += pipe->partition->rpf.left; - } if (pipe->interlaced) { crop.height = round_down(crop.height / 2, fmtinfo->vsub); @@ -366,12 +378,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity, } static void rpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - partition->rpf = *window; + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index]; + + /* + * Partition Algorithm Control + * + * The partition algorithm can split this frame into multiple slices. We + * must adjust our partition window based on the pipe configuration to + * match the destination partition window. To achieve this, we adjust + * our crop to provide a 'sub-crop' matching the expected partition + * window. + */ + *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); + + if (pipe->partitions > 1) { + rpf_rect->width = window->width; + rpf_rect->left += window->left; + } } static const struct vsp1_entity_operations rpf_entity_ops = { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 09fb6ffa14e2..9c8085d5d306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -10,18 +10,13 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" +#include "vsp1_entity.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" #define RWPF_MIN_WIDTH 1 #define RWPF_MIN_HEIGHT 1 -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state) -{ - return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK); -} - /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -41,6 +36,11 @@ static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, code->code = codes[code->index]; + if (code->pad == RWPF_PAD_SOURCE && + code->code == MEDIA_BUS_FMT_AYUV8_1X32) + code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC + | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION; + return 0; } @@ -79,15 +79,48 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); if (fmt->pad == RWPF_PAD_SOURCE) { + const struct v4l2_mbus_framefmt *sink_format = + v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC; + bool csc; + /* * The RWPF performs format conversion but can't scale, only the - * format code can be changed on the source pad. + * format code, encoding and quantization can be changed on the + * source pad when converting between RGB and YUV. + */ + if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 && + fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32) + format->code = fmt->format.code; + else + format->code = sink_format->code; + + /* + * Encoding and quantization can only be configured when YCbCr + * <-> RGB is enabled. The V4L2 API requires userspace to set + * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these + * conditions is not met, use the encoding and quantization + * values from the sink pad. */ - format->code = fmt->format.code; + csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) != + (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32); + + if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) { + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + } else { + format->ycbcr_enc = sink_format->ycbcr_enc; + format->quantization = sink_format->quantization; + } + + vsp1_entity_adjust_color_space(format); + fmt->format = *format; + fmt->format.flags = flags; + goto done; } @@ -97,7 +130,13 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, format->height = clamp_t(unsigned int, fmt->format.height, RWPF_MIN_HEIGHT, rwpf->max_height); format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + + format->colorspace = fmt->format.colorspace; + format->xfer_func = fmt->format.xfer_func; + format->ycbcr_enc = fmt->format.ycbcr_enc; + format->quantization = fmt->format.quantization; + + vsp1_entity_adjust_color_space(format); fmt->format = *format; @@ -105,7 +144,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_rect *crop; /* Update the sink crop rectangle. */ - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -113,8 +152,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, } /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); *format = fmt->format; if (rwpf->flip.rotate) { @@ -153,12 +191,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_rwpf_get_crop(rwpf, state); + sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); break; case V4L2_SEL_TGT_CROP_BOUNDS: - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -204,8 +241,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, } /* Make sure the crop rectangle is entirely contained in the image. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); /* * Restrict the crop rectangle coordinates to multiples of 2 to avoid @@ -225,12 +261,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, sel->r.height = min_t(unsigned int, sel->r.height, format->height - sel->r.top); - crop = vsp1_rwpf_get_crop(rwpf, state); + crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); *crop = sel->r; /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&rwpf->entity, state, - RWPF_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); format->width = crop->width; format->height = crop->height; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h index e0d212c70b2f..5ac9f0a6fafc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h @@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols); extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops; -struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, - struct v4l2_subdev_state *sd_state); - #endif /* __VSP1_RWPF_H__ */ diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 11e008aa9f20..bba2872afaf2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_sru.h" @@ -131,7 +132,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); mutex_lock(&sru->entity.lock); @@ -178,16 +179,22 @@ static void sru_try_format(struct vsp1_sru *sru, fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + vsp1_entity_adjust_color_space(fmt); + fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE); fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE); break; case SRU_PAD_SOURCE: /* The SRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&sru->entity, sd_state, - SRU_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); fmt->code = format->code; + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; + /* * We can upscale by 2 in both direction, but not independently. * Compare the input and output rectangles areas (avoiding @@ -212,7 +219,6 @@ static void sru_try_format(struct vsp1_sru *sru, } fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int sru_set_format(struct v4l2_subdev *subdev, @@ -234,13 +240,12 @@ static int sru_set_format(struct v4l2_subdev *subdev, sru_try_format(sru, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == SRU_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&sru->entity, state, - SRU_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); *format = fmt->format; sru_try_format(sru, state, SRU_PAD_SOURCE, format); @@ -267,6 +272,7 @@ static const struct v4l2_subdev_ops sru_ops = { */ static void sru_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -277,10 +283,8 @@ static void sru_configure_stream(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 @@ -301,16 +305,14 @@ static void sru_configure_stream(struct vsp1_entity *entity, } static unsigned int sru_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* * The maximum input width of the SRU is 288 input pixels, but 32 @@ -324,24 +326,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity, } static void sru_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_sru *sru = to_sru(&entity->subdev); struct v4l2_mbus_framefmt *input; struct v4l2_mbus_framefmt *output; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SINK); - output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state, - SRU_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); + output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); /* Adapt if SRUx2 is enabled. */ if (input->width != output->width) { window->width /= 2; window->left /= 2; + window->height /= 2; + window->top /= 2; } partition->sru = *window; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index d89f1197b86c..2db473b6f83c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c @@ -14,6 +14,7 @@ #include "vsp1.h" #include "vsp1_dl.h" +#include "vsp1_entity.h" #include "vsp1_pipe.h" #include "vsp1_uds.h" @@ -136,7 +137,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev, if (!state) return -EINVAL; - format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); mutex_lock(&uds->entity.lock); @@ -177,16 +178,22 @@ static void uds_try_format(struct vsp1_uds *uds, fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; + vsp1_entity_adjust_color_space(fmt); + fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE); fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE); break; case UDS_PAD_SOURCE: /* The UDS scales but can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&uds->entity, sd_state, - UDS_PAD_SINK); + format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK); fmt->code = format->code; + fmt->colorspace = format->colorspace; + fmt->xfer_func = format->xfer_func; + fmt->ycbcr_enc = format->ycbcr_enc; + fmt->quantization = format->quantization; + uds_output_limits(format->width, &minimum, &maximum); fmt->width = clamp(fmt->width, minimum, maximum); uds_output_limits(format->height, &minimum, &maximum); @@ -195,7 +202,6 @@ static void uds_try_format(struct vsp1_uds *uds, } fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = V4L2_COLORSPACE_SRGB; } static int uds_set_format(struct v4l2_subdev *subdev, @@ -217,13 +223,12 @@ static int uds_set_format(struct v4l2_subdev *subdev, uds_try_format(uds, state, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad); + format = v4l2_subdev_state_get_format(state, fmt->pad); *format = fmt->format; if (fmt->pad == UDS_PAD_SINK) { /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&uds->entity, state, - UDS_PAD_SOURCE); + format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); *format = fmt->format; uds_try_format(uds, state, UDS_PAD_SOURCE, format); @@ -254,6 +259,7 @@ static const struct v4l2_subdev_ops uds_ops = { */ static void uds_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -265,10 +271,8 @@ static void uds_configure_stream(struct vsp1_entity *entity, unsigned int vscale; bool multitap; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -303,15 +307,11 @@ static void uds_configure_stream(struct vsp1_entity *entity, static void uds_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); - struct vsp1_partition *partition = pipe->partition; - const struct v4l2_mbus_framefmt *output; - - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); /* Input size clipping. */ vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | @@ -323,22 +323,20 @@ static void uds_configure_partition(struct vsp1_entity *entity, vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, (partition->uds_source.width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height + (partition->uds_source.height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); } static unsigned int uds_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; unsigned int hscale; - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); hscale = output->width / input->width; /* @@ -364,28 +362,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity, */ static void uds_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { - struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; const struct v4l2_mbus_framefmt *input; - /* Initialise the partition state. */ - partition->uds_sink = *window; - partition->uds_source = *window; - - input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SINK); - output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state, - UDS_PAD_SOURCE); + input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK); + output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE); partition->uds_sink.width = window->width * input->width / output->width; partition->uds_sink.left = window->left * input->width / output->width; + partition->uds_sink.height = input->height; + partition->uds_sink.top = 0; + + partition->uds_source = *window; *window = partition->uds_sink; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c index f66936a28a2a..edaf28b544d2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c @@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_CROP_DEFAULT: - format = vsp1_entity_get_pad_format(&uif->entity, state, - UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = 0; sel->r.top = 0; sel->r.width = format->width; @@ -113,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev, break; case V4L2_SEL_TGT_CROP: - sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, sel->target); + sel->r = *v4l2_subdev_state_get_crop(state, sel->pad); break; default: @@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, } /* The crop rectangle must be inside the input frame. */ - format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK); + format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK); sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); @@ -160,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev, format->height - sel->r.top); /* Store the crop rectangle. */ - selection = vsp1_entity_get_pad_selection(&uif->entity, state, - sel->pad, V4L2_SEL_TGT_CROP); + selection = v4l2_subdev_state_get_crop(state, sel->pad); *selection = sel->r; done: @@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = { */ static void uif_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -203,8 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity, vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, VI6_UIF_DISCOM_DOCMPMR_SEL(9)); - crop = vsp1_entity_get_pad_selection(entity, entity->state, - UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK); left = crop->left; width = crop->width; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a9cb0e5640e..bc66fbdde3cc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video) if (video->rwpf->fmtinfo->mbus != fmt.format.code || video->rwpf->format.height != fmt.format.height || - video->rwpf->format.width != fmt.format.width) - return -EINVAL; + video->rwpf->format.width != fmt.format.width) { + dev_dbg(video->vsp1->dev, + "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n", + video->rwpf->fmtinfo->mbus, video->rwpf->format.width, + video->rwpf->format.height, fmt.format.code, + fmt.format.width, fmt.format.height); + return -EPIPE; + } return 0; } @@ -121,12 +127,24 @@ static int __vsp1_video_try_format(struct vsp1_video *video, info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT); pix->pixelformat = info->fourcc; - pix->colorspace = V4L2_COLORSPACE_SRGB; pix->field = V4L2_FIELD_NONE; - if (info->fourcc == V4L2_PIX_FMT_HSV24 || - info->fourcc == V4L2_PIX_FMT_HSV32) - pix->hsv_enc = V4L2_HSV_ENC_256; + /* + * Adjust the colour space fields. On capture devices, userspace needs + * to set the V4L2_PIX_FMT_FLAG_SET_CSC to override the defaults. Reset + * all fields to *_DEFAULT if the flag isn't set, to then handle + * capture and output devices in the same way. + */ + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + !(pix->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) { + pix->colorspace = V4L2_COLORSPACE_DEFAULT; + pix->xfer_func = V4L2_XFER_FUNC_DEFAULT; + pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + pix->quantization = V4L2_QUANTIZATION_DEFAULT; + } + + vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func, + &pix->ycbcr_enc, &pix->quantization); memset(pix->reserved, 0, sizeof(pix->reserved)); @@ -173,131 +191,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video, } /* ----------------------------------------------------------------------------- - * VSP1 Partition Algorithm support - */ - -/** - * vsp1_video_calculate_partition - Calculate the active partition output window - * - * @pipe: the pipeline - * @partition: partition that will hold the calculated values - * @div_size: pre-determined maximum partition division size - * @index: partition index - */ -static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe, - struct vsp1_partition *partition, - unsigned int div_size, - unsigned int index) -{ - const struct v4l2_mbus_framefmt *format; - struct vsp1_partition_window window; - unsigned int modulus; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - - /* A single partition simply processes the output size in full. */ - if (pipe->partitions <= 1) { - window.left = 0; - window.width = format->width; - - vsp1_pipeline_propagate_partition(pipe, partition, index, - &window); - return; - } - - /* Initialise the partition with sane starting conditions. */ - window.left = index * div_size; - window.width = div_size; - - modulus = format->width % div_size; - - /* - * We need to prevent the last partition from being smaller than the - * *minimum* width of the hardware capabilities. - * - * If the modulus is less than half of the partition size, - * the penultimate partition is reduced to half, which is added - * to the final partition: |1234|1234|1234|12|341| - * to prevent this: |1234|1234|1234|1234|1|. - */ - if (modulus) { - /* - * pipe->partitions is 1 based, whilst index is a 0 based index. - * Normalise this locally. - */ - unsigned int partitions = pipe->partitions - 1; - - if (modulus < div_size / 2) { - if (index == partitions - 1) { - /* Halve the penultimate partition. */ - window.width = div_size / 2; - } else if (index == partitions) { - /* Increase the final partition. */ - window.width = (div_size / 2) + modulus; - window.left -= div_size / 2; - } - } else if (index == partitions) { - window.width = modulus; - } - } - - vsp1_pipeline_propagate_partition(pipe, partition, index, &window); -} - -static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) -{ - struct vsp1_device *vsp1 = pipe->output->entity.vsp1; - const struct v4l2_mbus_framefmt *format; - struct vsp1_entity *entity; - unsigned int div_size; - unsigned int i; - - /* - * Partitions are computed on the size before rotation, use the format - * at the WPF sink. - */ - format = vsp1_entity_get_pad_format(&pipe->output->entity, - pipe->output->entity.state, - RWPF_PAD_SINK); - div_size = format->width; - - /* - * Only Gen3+ hardware requires image partitioning, Gen2 will operate - * with a single partition that covers the whole output. - */ - if (vsp1->info->gen >= 3) { - list_for_each_entry(entity, &pipe->entities, list_pipe) { - unsigned int entity_max; - - if (!entity->ops->max_width) - continue; - - entity_max = entity->ops->max_width(entity, pipe); - if (entity_max) - div_size = min(div_size, entity_max); - } - } - - pipe->partitions = DIV_ROUND_UP(format->width, div_size); - pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), - GFP_KERNEL); - if (!pipe->part_table) - return -ENOMEM; - - for (i = 0; i < pipe->partitions; ++i) - vsp1_video_calculate_partition(pipe, &pipe->part_table[i], - div_size, i); - - return 0; -} - -/* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -365,13 +258,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_partition *part = &pipe->part_table[partition]; struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; - pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) - vsp1_entity_configure_partition(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, part, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) @@ -646,11 +538,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe, struct vsp1_video *video) { + int ret; + vsp1_pipeline_init(pipe); pipe->frame_end = vsp1_video_pipeline_frame_end; - return vsp1_video_pipeline_build(pipe, video); + ret = vsp1_video_pipeline_build(pipe, video); + if (ret) + return ret; + + vsp1_pipeline_dump(pipe, "video"); + + return 0; } static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) @@ -784,6 +684,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } +static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) +{ + struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + const struct v4l2_mbus_framefmt *format; + struct vsp1_entity *entity; + unsigned int div_size; + unsigned int i; + + /* + * Partitions are computed on the size before rotation, use the format + * at the WPF sink. + */ + format = v4l2_subdev_state_get_format(pipe->output->entity.state, + RWPF_PAD_SINK); + div_size = format->width; + + /* + * Only Gen3+ hardware requires image partitioning, Gen2 will operate + * with a single partition that covers the whole output. + */ + if (vsp1->info->gen >= 3) { + list_for_each_entry(entity, &pipe->entities, list_pipe) { + unsigned int entity_max; + + if (!entity->ops->max_width) + continue; + + entity_max = entity->ops->max_width(entity, + entity->state, + pipe); + if (entity_max) + div_size = min(div_size, entity_max); + } + } + + pipe->partitions = DIV_ROUND_UP(format->width, div_size); + pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table), + GFP_KERNEL); + if (!pipe->part_table) + return -ENOMEM; + + for (i = 0; i < pipe->partitions; ++i) + vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], + div_size, i); + + return 0; +} + static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; @@ -826,7 +774,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) list_for_each_entry(entity, &pipe->entities, list_pipe) { vsp1_entity_route_setup(entity, pipe, pipe->stream_config); - vsp1_entity_configure_stream(entity, pipe, NULL, + vsp1_entity_configure_stream(entity, entity->state, pipe, NULL, pipe->stream_config); } @@ -937,8 +885,6 @@ static const struct vb2_ops vsp1_video_queue_qops = { .queue_setup = vsp1_video_queue_setup, .buf_prepare = vsp1_video_buffer_prepare, .buf_queue = vsp1_video_buffer_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, .start_streaming = vsp1_video_start_streaming, .stop_streaming = vsp1_video_stop_streaming, }; @@ -954,16 +900,36 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) struct vsp1_video *video = to_vsp1_video(vfh->vdev); cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING - | V4L2_CAP_VIDEO_CAPTURE_MPLANE + | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - strscpy(cap->driver, "vsp1", sizeof(cap->driver)); strscpy(cap->card, video->video.name, sizeof(cap->card)); return 0; } +static int vsp1_video_enum_format(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct v4l2_fh *vfh = file->private_data; + struct vsp1_video *video = to_vsp1_video(vfh->vdev); + const struct vsp1_format_info *info; + + info = vsp1_get_format_info_by_index(video->vsp1, f->index, f->mbus_code); + if (!info) + return -EINVAL; + + f->pixelformat = info->fourcc; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + info->mbus == MEDIA_BUS_FMT_AYUV8_1X32) + f->flags = V4L2_FMT_FLAG_CSC_YCBCR_ENC + | V4L2_FMT_FLAG_CSC_QUANTIZATION; + + return 0; +} + static int vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format) { @@ -1079,6 +1045,8 @@ err_pipe: static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = { .vidioc_querycap = vsp1_video_querycap, + .vidioc_enum_fmt_vid_cap = vsp1_video_enum_format, + .vidioc_enum_fmt_vid_out = vsp1_video_enum_format, .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format, .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format, .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format, @@ -1146,6 +1114,27 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Media entity operations + */ + +static int vsp1_video_link_validate(struct media_link *link) +{ + /* + * Ideally, link validation should be implemented here instead of + * calling vsp1_video_verify_format() in vsp1_video_streamon() + * manually. That would however break userspace that start one video + * device before configures formats on other video devices in the + * pipeline. This operation is just a no-op to silence the warnings + * from v4l2_subdev_link_validate(). + */ + return 0; +} + +static const struct media_entity_operations vsp1_video_media_ops = { + .link_validate = vsp1_video_link_validate, +}; + +/* ----------------------------------------------------------------------------- * Suspend and Resume */ @@ -1252,14 +1241,14 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->pad.flags = MEDIA_PAD_FL_SOURCE; video->video.vfl_dir = VFL_DIR_TX; video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE | - V4L2_CAP_STREAMING; + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; } else { direction = "output"; video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; video->pad.flags = MEDIA_PAD_FL_SINK; video->video.vfl_dir = VFL_DIR_RX; video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | - V4L2_CAP_STREAMING; + V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; } mutex_init(&video->lock); @@ -1279,6 +1268,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, /* ... and the video node... */ video->video.v4l2_dev = &video->vsp1->v4l2_dev; + video->video.entity.ops = &vsp1_video_media_ops; video->video.fops = &vsp1_video_fops; snprintf(video->video.name, sizeof(video->video.name), "%s %s", rwpf->entity.subdev.name, direction); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 9693aeab1cac..30662cfdf837 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) goto done; } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(wpf->entity.state, + RWPF_PAD_SOURCE); mutex_lock(&wpf->entity.lock); @@ -135,6 +133,7 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) { struct vsp1_device *vsp1 = wpf->entity.vsp1; unsigned int num_flip_ctrls; + int ret; spin_lock_init(&wpf->flip.lock); @@ -158,7 +157,9 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) num_flip_ctrls = 0; } - vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + ret = vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); + if (ret < 0) + return ret; if (num_flip_ctrls >= 1) { wpf->flip.ctrls.vflip = @@ -176,11 +177,8 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf) v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip); } - if (wpf->ctrls.error) { - dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", - wpf->entity.index); + if (wpf->ctrls.error) return wpf->ctrls.error; - } return 0; } @@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, } static void wpf_configure_stream(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) @@ -245,15 +244,14 @@ static void wpf_configure_stream(struct vsp1_entity *entity, u32 srcrpf = 0; int ret; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - source_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SOURCE); + sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); + source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); - /* Format */ - if (!pipe->lif || wpf->writeback) { + /* + * Format configuration. Skip for IIF (VSPX) or if the pipe doesn't + * write to memory. + */ + if (!pipe->iif && (!pipe->lif || wpf->writeback)) { const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; @@ -284,8 +282,33 @@ static void wpf_configure_stream(struct vsp1_entity *entity, (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } - if (sink_format->code != source_format->code) - outfmt |= VI6_WPF_OUTFMT_CSC; + if (sink_format->code != source_format->code) { + u16 ycbcr_enc; + u16 quantization; + u32 wrtm; + + if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) { + ycbcr_enc = sink_format->ycbcr_enc; + quantization = sink_format->quantization; + } else { + ycbcr_enc = source_format->ycbcr_enc; + quantization = source_format->quantization; + } + + if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT601; + else if (ycbcr_enc == V4L2_YCBCR_ENC_601 && + quantization == V4L2_QUANTIZATION_FULL_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT601_EXT; + else if (ycbcr_enc == V4L2_YCBCR_ENC_709 && + quantization == V4L2_QUANTIZATION_LIM_RANGE) + wrtm = VI6_WPF_OUTFMT_WRTM_BT709; + else + wrtm = VI6_WPF_OUTFMT_WRTM_BT709_EXT; + + outfmt |= VI6_WPF_OUTFMT_CSC | wrtm; + } wpf->outfmt = outfmt; @@ -296,7 +319,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, * Sources. If the pipeline has a single input and BRx is not used, * configure it as the master layer. Otherwise configure all * inputs as sub-layers and select the virtual RPF as the master - * layer. + * layer. For VSPX configure the enabled sources as masters. */ for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; @@ -304,7 +327,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, if (!input) continue; - srcrpf |= (!pipe->brx && pipe->num_inputs == 1) + srcrpf |= (pipe->iif || (!pipe->brx && pipe->num_inputs == 1)) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } @@ -321,6 +344,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity, vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index), VI6_WPF_IRQ_ENB_DFEE); + if (pipe->iif) + return; + /* * Configure writeback for display pipelines (the wpf writeback flag is * never set for memory-to-memory pipelines). Start by adding a chained @@ -367,13 +393,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity, static void wpf_configure_partition(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, + const struct vsp1_partition *partition, struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; struct vsp1_rwpf_memory mem = wpf->mem; - const struct v4l2_mbus_framefmt *sink_format; const struct v4l2_pix_format_mplane *format = &wpf->format; const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; unsigned int width; @@ -383,21 +409,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int flip; unsigned int i; - sink_format = vsp1_entity_get_pad_format(&wpf->entity, - wpf->entity.state, - RWPF_PAD_SINK); - width = sink_format->width; - height = sink_format->height; - left = 0; - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. + * Cropping. The partition algorithm can split the image into multiple + * slices. */ - if (pipe->partitions > 1) { - width = pipe->partition->wpf.width; - left = pipe->partition->wpf.left; - } + width = partition->wpf.width; + left = partition->wpf.left; + height = partition->wpf.height; vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | @@ -508,6 +526,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, } static unsigned int wpf_max_width(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); @@ -516,10 +535,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity, } static void wpf_partition(struct vsp1_entity *entity, + struct v4l2_subdev_state *state, struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int partition_idx, - struct vsp1_partition_window *window) + struct v4l2_rect *window) { partition->wpf = *window; } |