diff options
Diffstat (limited to 'drivers/media/platform/renesas/vsp1')
21 files changed, 639 insertions, 96 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 5dee0490c593..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" @@ -108,6 +109,8 @@ 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: @@ -115,13 +118,17 @@ static void brx_try_format(struct vsp1_brx *brx, 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, 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 b5d1f238f7be..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); @@ -593,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; } @@ -826,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) { @@ -849,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); /* @@ -873,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; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h index 3fd95b53f27e..07a5d0adbd08 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h @@ -52,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 9fc6bf624a52..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, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 8b8945bd8f10..a6680d531872 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -63,9 +63,14 @@ 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); } @@ -99,6 +104,20 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, 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; +} + /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ @@ -329,7 +348,13 @@ 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; @@ -528,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 1bcc9e27dfdc..b7c72d0b7f8e 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h @@ -28,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, @@ -170,6 +171,8 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity, 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_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c index 8ba2a7c7305c..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 @@ -96,7 +97,13 @@ 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; @@ -106,6 +113,8 @@ static int hsit_set_format(struct v4l2_subdev *subdev, 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; 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_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index bb0739f684f3..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; } diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h index 1ba7bdbad5a8..7f623b8cbe5c 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h @@ -119,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; @@ -179,5 +180,10 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe, 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 5c8b3ba1bd3c..811f2b7c5cc5 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c @@ -84,7 +84,7 @@ static void rpf_configure_stream(struct vsp1_entity *entity, sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); - infmt = VI6_RPF_INFMT_CIPM + infmt = (pipe->iif ? 0 : VI6_RPF_INFMT_CIPM) | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT); if (fmtinfo->swap_yc) @@ -92,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; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c index 9d38203e73d0..9c8085d5d306 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c @@ -10,6 +10,7 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" +#include "vsp1_entity.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" @@ -35,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; } @@ -76,12 +82,45 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, 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; } @@ -91,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; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c index 1759ce642e6e..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" @@ -178,6 +179,8 @@ 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; @@ -187,6 +190,11 @@ static void sru_try_format(struct vsp1_sru *sru, 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 @@ -211,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, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c index c5a38478cf8c..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" @@ -177,6 +178,8 @@ 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; @@ -186,6 +189,11 @@ static void uds_try_format(struct vsp1_uds *uds, 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); @@ -194,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, diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 03f4efd6b82b..bc66fbdde3cc 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -127,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)); @@ -888,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) { @@ -1013,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, @@ -1207,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); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index f176750ccd98..30662cfdf837 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -133,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); @@ -156,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 = @@ -174,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; } @@ -247,8 +247,11 @@ static void wpf_configure_stream(struct vsp1_entity *entity, 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; @@ -279,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; @@ -291,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]; @@ -299,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); } @@ -316,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 |