diff options
Diffstat (limited to 'drivers/media/platform/vsp1')
41 files changed, 2345 insertions, 1721 deletions
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index f5cd6f0491cb..4bb4dcbef7b5 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -3,8 +3,8 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o 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_bru.o vsp1_sru.o vsp1_uds.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-y += vsp1_lif.o vsp1_uif.o obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 78ef838416b3..33f632331474 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1.h -- R-Car VSP1 Driver * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_H__ #define __VSP1_H__ @@ -30,7 +26,7 @@ struct rcar_fcp_device; struct vsp1_drm; struct vsp1_entity; struct vsp1_platform_data; -struct vsp1_bru; +struct vsp1_brx; struct vsp1_clu; struct vsp1_hgo; struct vsp1_hgt; @@ -40,10 +36,12 @@ struct vsp1_lut; struct vsp1_rwpf; struct vsp1_sru; struct vsp1_uds; +struct vsp1_uif; #define VSP1_MAX_LIF 2 #define VSP1_MAX_RPF 5 #define VSP1_MAX_UDS 3 +#define VSP1_MAX_UIF 2 #define VSP1_MAX_WPF 4 #define VSP1_HAS_LUT (1 << 1) @@ -64,6 +62,7 @@ struct vsp1_device_info { unsigned int lif_count; unsigned int rpf_count; unsigned int uds_count; + unsigned int uif_count; unsigned int wpf_count; unsigned int num_bru_inputs; bool uapi; @@ -78,8 +77,8 @@ struct vsp1_device { struct rcar_fcp_device *fcp; struct device *bus_master; - struct vsp1_bru *brs; - struct vsp1_bru *bru; + struct vsp1_brx *brs; + struct vsp1_brx *bru; struct vsp1_clu *clu; struct vsp1_hgo *hgo; struct vsp1_hgt *hgt; @@ -90,6 +89,7 @@ struct vsp1_device { struct vsp1_rwpf *rpf[VSP1_MAX_RPF]; struct vsp1_sru *sru; struct vsp1_uds *uds[VSP1_MAX_UDS]; + struct vsp1_uif *uif[VSP1_MAX_UIF]; struct vsp1_rwpf *wpf[VSP1_MAX_WPF]; struct list_head entities; diff --git a/drivers/media/platform/vsp1/vsp1_bru.h b/drivers/media/platform/vsp1/vsp1_bru.h deleted file mode 100644 index c98ed96d8de6..000000000000 --- a/drivers/media/platform/vsp1/vsp1_bru.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * vsp1_bru.h -- R-Car VSP1 Blend ROP Unit - * - * Copyright (C) 2013 Renesas Corporation - * - * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ -#ifndef __VSP1_BRU_H__ -#define __VSP1_BRU_H__ - -#include <media/media-entity.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-subdev.h> - -#include "vsp1_entity.h" - -struct vsp1_device; -struct vsp1_rwpf; - -#define BRU_PAD_SINK(n) (n) - -struct vsp1_bru { - struct vsp1_entity entity; - unsigned int base; - - struct v4l2_ctrl_handler ctrls; - - struct { - struct vsp1_rwpf *rpf; - } inputs[VSP1_MAX_RPF]; - - u32 bgcolor; -}; - -static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct vsp1_bru, entity.subdev); -} - -struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, - enum vsp1_entity_type type); - -#endif /* __VSP1_BRU_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_brx.c index e8fd2ae3b3eb..359917b5d842 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_brx.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_bru.c -- R-Car VSP1 Blend ROP Unit + * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS) * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -17,45 +13,45 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" #include "vsp1_video.h" -#define BRU_MIN_SIZE 1U -#define BRU_MAX_SIZE 8190U +#define BRX_MIN_SIZE 1U +#define BRX_MAX_SIZE 8190U /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_bru_write(struct vsp1_bru *bru, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_brx_write(struct vsp1_brx *brx, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, bru->base + reg, data); + vsp1_dl_body_write(dlb, brx->base + reg, data); } /* ----------------------------------------------------------------------------- * Controls */ -static int bru_s_ctrl(struct v4l2_ctrl *ctrl) +static int brx_s_ctrl(struct v4l2_ctrl *ctrl) { - struct vsp1_bru *bru = - container_of(ctrl->handler, struct vsp1_bru, ctrls); + struct vsp1_brx *brx = + container_of(ctrl->handler, struct vsp1_brx, ctrls); switch (ctrl->id) { case V4L2_CID_BG_COLOR: - bru->bgcolor = ctrl->val; + brx->bgcolor = ctrl->val; break; } return 0; } -static const struct v4l2_ctrl_ops bru_ctrl_ops = { - .s_ctrl = bru_s_ctrl, +static const struct v4l2_ctrl_ops brx_ctrl_ops = { + .s_ctrl = brx_s_ctrl, }; /* ----------------------------------------------------------------------------- @@ -63,12 +59,12 @@ static const struct v4l2_ctrl_ops bru_ctrl_ops = { */ /* - * The BRU can't perform format conversion, all sink and source formats must be + * The BRx can't perform format conversion, all sink and source formats must be * identical. We pick the format on the first sink pad (pad 0) and propagate it * to all other pads. */ -static int bru_enum_mbus_code(struct v4l2_subdev *subdev, +static int brx_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { @@ -81,7 +77,7 @@ static int bru_enum_mbus_code(struct v4l2_subdev *subdev, ARRAY_SIZE(codes)); } -static int bru_enum_frame_size(struct v4l2_subdev *subdev, +static int brx_enum_frame_size(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { @@ -92,29 +88,29 @@ static int bru_enum_frame_size(struct v4l2_subdev *subdev, fse->code != MEDIA_BUS_FMT_AYUV8_1X32) return -EINVAL; - fse->min_width = BRU_MIN_SIZE; - fse->max_width = BRU_MAX_SIZE; - fse->min_height = BRU_MIN_SIZE; - fse->max_height = BRU_MAX_SIZE; + fse->min_width = BRX_MIN_SIZE; + fse->max_width = BRX_MAX_SIZE; + fse->min_height = BRX_MIN_SIZE; + fse->max_height = BRX_MAX_SIZE; return 0; } -static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru, +static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, struct v4l2_subdev_pad_config *cfg, unsigned int pad) { - return v4l2_subdev_get_try_compose(&bru->entity.subdev, cfg, pad); + return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad); } -static void bru_try_format(struct vsp1_bru *bru, +static void brx_try_format(struct vsp1_brx *brx, struct v4l2_subdev_pad_config *config, unsigned int pad, struct v4l2_mbus_framefmt *fmt) { struct v4l2_mbus_framefmt *format; switch (pad) { - case BRU_PAD_SINK(0): + case BRX_PAD_SINK(0): /* Default to YUV if the requested format is not supported. */ if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) @@ -122,46 +118,46 @@ static void bru_try_format(struct vsp1_bru *bru, break; default: - /* The BRU can't perform format conversion. */ - format = vsp1_entity_get_pad_format(&bru->entity, config, - BRU_PAD_SINK(0)); + /* The BRx can't perform format conversion. */ + format = vsp1_entity_get_pad_format(&brx->entity, config, + BRX_PAD_SINK(0)); fmt->code = format->code; break; } - fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE); - fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE); + 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 bru_set_format(struct v4l2_subdev *subdev, +static int brx_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; int ret = 0; - mutex_lock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&bru->entity, cfg, fmt->which); + config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which); if (!config) { ret = -EINVAL; goto done; } - bru_try_format(bru, config, fmt->pad, &fmt->format); + brx_try_format(brx, config, fmt->pad, &fmt->format); - format = vsp1_entity_get_pad_format(&bru->entity, config, fmt->pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad); *format = fmt->format; /* Reset the compose rectangle */ - if (fmt->pad != bru->entity.source_pad) { + if (fmt->pad != brx->entity.source_pad) { struct v4l2_rect *compose; - compose = bru_get_compose(bru, config, fmt->pad); + compose = brx_get_compose(brx, config, fmt->pad); compose->left = 0; compose->top = 0; compose->width = format->width; @@ -169,48 +165,48 @@ static int bru_set_format(struct v4l2_subdev *subdev, } /* Propagate the format code to all pads */ - if (fmt->pad == BRU_PAD_SINK(0)) { + if (fmt->pad == BRX_PAD_SINK(0)) { unsigned int i; - for (i = 0; i <= bru->entity.source_pad; ++i) { - format = vsp1_entity_get_pad_format(&bru->entity, + for (i = 0; i <= brx->entity.source_pad; ++i) { + format = vsp1_entity_get_pad_format(&brx->entity, config, i); format->code = fmt->format.code; } } done: - mutex_unlock(&bru->entity.lock); + mutex_unlock(&brx->entity.lock); return ret; } -static int bru_get_selection(struct v4l2_subdev *subdev, +static int brx_get_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; - if (sel->pad == bru->entity.source_pad) + if (sel->pad == brx->entity.source_pad) return -EINVAL; switch (sel->target) { case V4L2_SEL_TGT_COMPOSE_BOUNDS: sel->r.left = 0; sel->r.top = 0; - sel->r.width = BRU_MAX_SIZE; - sel->r.height = BRU_MAX_SIZE; + sel->r.width = BRX_MAX_SIZE; + sel->r.height = BRX_MAX_SIZE; return 0; case V4L2_SEL_TGT_COMPOSE: - config = vsp1_entity_get_pad_config(&bru->entity, cfg, + config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); if (!config) return -EINVAL; - mutex_lock(&bru->entity.lock); - sel->r = *bru_get_compose(bru, config, sel->pad); - mutex_unlock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); + sel->r = *brx_get_compose(brx, config, sel->pad); + mutex_unlock(&brx->entity.lock); return 0; default: @@ -218,25 +214,25 @@ static int bru_get_selection(struct v4l2_subdev *subdev, } } -static int bru_set_selection(struct v4l2_subdev *subdev, +static int brx_set_selection(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { - struct vsp1_bru *bru = to_bru(subdev); + struct vsp1_brx *brx = to_brx(subdev); struct v4l2_subdev_pad_config *config; struct v4l2_mbus_framefmt *format; struct v4l2_rect *compose; int ret = 0; - if (sel->pad == bru->entity.source_pad) + if (sel->pad == brx->entity.source_pad) return -EINVAL; if (sel->target != V4L2_SEL_TGT_COMPOSE) return -EINVAL; - mutex_lock(&bru->entity.lock); + mutex_lock(&brx->entity.lock); - config = vsp1_entity_get_pad_config(&bru->entity, cfg, sel->which); + config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); if (!config) { ret = -EINVAL; goto done; @@ -246,8 +242,8 @@ static int bru_set_selection(struct v4l2_subdev *subdev, * The compose rectangle top left corner must be inside the output * frame. */ - format = vsp1_entity_get_pad_format(&bru->entity, config, - bru->entity.source_pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, + 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); @@ -255,51 +251,47 @@ static int bru_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(&bru->entity, config, sel->pad); + format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad); sel->r.width = format->width; sel->r.height = format->height; - compose = bru_get_compose(bru, config, sel->pad); + compose = brx_get_compose(brx, config, sel->pad); *compose = sel->r; done: - mutex_unlock(&bru->entity.lock); + mutex_unlock(&brx->entity.lock); return ret; } -static const struct v4l2_subdev_pad_ops bru_pad_ops = { +static const struct v4l2_subdev_pad_ops brx_pad_ops = { .init_cfg = vsp1_entity_init_cfg, - .enum_mbus_code = bru_enum_mbus_code, - .enum_frame_size = bru_enum_frame_size, + .enum_mbus_code = brx_enum_mbus_code, + .enum_frame_size = brx_enum_frame_size, .get_fmt = vsp1_subdev_get_pad_format, - .set_fmt = bru_set_format, - .get_selection = bru_get_selection, - .set_selection = bru_set_selection, + .set_fmt = brx_set_format, + .get_selection = brx_get_selection, + .set_selection = brx_set_selection, }; -static const struct v4l2_subdev_ops bru_ops = { - .pad = &bru_pad_ops, +static const struct v4l2_subdev_ops brx_ops = { + .pad = &brx_pad_ops, }; /* ----------------------------------------------------------------------------- * VSP1 Entity Operations */ -static void bru_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void brx_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { - struct vsp1_bru *bru = to_bru(&entity->subdev); + struct vsp1_brx *brx = to_brx(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; unsigned int i; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - - format = vsp1_entity_get_pad_format(&bru->entity, bru->entity.config, - bru->entity.source_pad); + format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config, + brx->entity.source_pad); /* * The hardware is extremely flexible but we have no userspace API to @@ -313,7 +305,7 @@ static void bru_configure(struct vsp1_entity *entity, * format at the pipeline output is premultiplied. */ flags = pipe->output ? pipe->output->format.flags : 0; - vsp1_bru_write(bru, dl, VI6_BRU_INCTRL, + vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL, flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 0 : VI6_BRU_INCTRL_NRM); @@ -321,12 +313,12 @@ static void bru_configure(struct vsp1_entity *entity, * Set the background position to cover the whole output image and * configure its color. */ - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_SIZE, + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE, (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_LOC, 0); + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0); - vsp1_bru_write(bru, dl, VI6_BRU_VIRRPF_COL, bru->bgcolor | + vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor | (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); /* @@ -336,25 +328,25 @@ static void bru_configure(struct vsp1_entity *entity, * unit. */ if (entity->type == VSP1_ENTITY_BRU) - vsp1_bru_write(bru, dl, VI6_BRU_ROP, + vsp1_brx_write(brx, dlb, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) | VI6_BRU_ROP_CROP(VI6_ROP_NOP) | VI6_BRU_ROP_AROP(VI6_ROP_NOP)); - for (i = 0; i < bru->entity.source_pad; ++i) { + for (i = 0; i < brx->entity.source_pad; ++i) { bool premultiplied = false; u32 ctrl = 0; /* - * Configure all Blend/ROP units corresponding to an enabled BRU + * Configure all Blend/ROP units corresponding to an enabled BRx * input for alpha blending. Blend/ROP units corresponding to - * disabled BRU inputs are used in ROP NOP mode to ignore the + * disabled BRx inputs are used in ROP NOP mode to ignore the * SRC input. */ - if (bru->inputs[i].rpf) { + if (brx->inputs[i].rpf) { ctrl |= VI6_BRU_CTRL_RBC; - premultiplied = bru->inputs[i].rpf->format.flags + premultiplied = brx->inputs[i].rpf->format.flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; } else { ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) @@ -378,7 +370,7 @@ static void bru_configure(struct vsp1_entity *entity, if (!(entity->type == VSP1_ENTITY_BRU && i == 1)) ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); - vsp1_bru_write(bru, dl, VI6_BRU_CTRL(i), ctrl); + vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl); /* * Harcode the blending formula to @@ -393,7 +385,7 @@ static void bru_configure(struct vsp1_entity *entity, * * otherwise. */ - vsp1_bru_write(bru, dl, VI6_BRU_BLD(i), + vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i), VI6_BRU_BLD_CCMDX_255_SRC_A | (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : VI6_BRU_BLD_CCMDY_SRC_A) | @@ -403,29 +395,29 @@ static void bru_configure(struct vsp1_entity *entity, } } -static const struct vsp1_entity_operations bru_entity_ops = { - .configure = bru_configure, +static const struct vsp1_entity_operations brx_entity_ops = { + .configure_stream = brx_configure_stream, }; /* ----------------------------------------------------------------------------- * Initialization and Cleanup */ -struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, +struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, enum vsp1_entity_type type) { - struct vsp1_bru *bru; + struct vsp1_brx *brx; unsigned int num_pads; const char *name; int ret; - bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL); - if (bru == NULL) + brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL); + if (brx == NULL) return ERR_PTR(-ENOMEM); - bru->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; - bru->entity.ops = &bru_entity_ops; - bru->entity.type = type; + brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; + brx->entity.ops = &brx_entity_ops; + brx->entity.type = type; if (type == VSP1_ENTITY_BRU) { num_pads = vsp1->info->num_bru_inputs + 1; @@ -435,26 +427,26 @@ struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1, name = "brs"; } - ret = vsp1_entity_init(vsp1, &bru->entity, name, num_pads, &bru_ops, + ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops, MEDIA_ENT_F_PROC_VIDEO_COMPOSER); if (ret < 0) return ERR_PTR(ret); /* Initialize the control handler. */ - v4l2_ctrl_handler_init(&bru->ctrls, 1); - v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR, + v4l2_ctrl_handler_init(&brx->ctrls, 1); + v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR, 0, 0xffffff, 1, 0); - bru->bgcolor = 0; + brx->bgcolor = 0; - bru->entity.subdev.ctrl_handler = &bru->ctrls; + brx->entity.subdev.ctrl_handler = &brx->ctrls; - if (bru->ctrls.error) { + if (brx->ctrls.error) { dev_err(vsp1->dev, "%s: failed to initialize controls\n", name); - ret = bru->ctrls.error; - vsp1_entity_destroy(&bru->entity); + ret = brx->ctrls.error; + vsp1_entity_destroy(&brx->entity); return ERR_PTR(ret); } - return bru; + return brx; } diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/vsp1/vsp1_brx.h new file mode 100644 index 000000000000..6abbb8c3343c --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_brx.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_brx.h -- R-Car VSP1 Blend ROP Unit (BRU and BRS) + * + * Copyright (C) 2013 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ +#ifndef __VSP1_BRX_H__ +#define __VSP1_BRX_H__ + +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-subdev.h> + +#include "vsp1_entity.h" + +struct vsp1_device; +struct vsp1_rwpf; + +#define BRX_PAD_SINK(n) (n) + +struct vsp1_brx { + struct vsp1_entity entity; + unsigned int base; + + struct v4l2_ctrl_handler ctrls; + + struct { + struct vsp1_rwpf *rpf; + } inputs[VSP1_MAX_RPF]; + + u32 bgcolor; +}; + +static inline struct vsp1_brx *to_brx(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_brx, entity.subdev); +} + +struct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, + enum vsp1_entity_type type); + +#endif /* __VSP1_BRX_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/vsp1/vsp1_clu.c index f2fb26e5ab4e..942fc14c19d1 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.c +++ b/drivers/media/platform/vsp1/vsp1_clu.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_clu.c -- R-Car VSP1 Cubic Look-Up Table * * Copyright (C) 2015-2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -23,14 +19,16 @@ #define CLU_MIN_SIZE 4U #define CLU_MAX_SIZE 8190U +#define CLU_SIZE (17 * 17 * 17) + /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_clu_write(struct vsp1_clu *clu, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_clu_write(struct vsp1_clu *clu, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -47,19 +45,19 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(clu->entity.vsp1, 1 + 17 * 17 * 17); + dlb = vsp1_dl_body_get(clu->pool); if (!dlb) return -ENOMEM; - vsp1_dl_fragment_write(dlb, VI6_CLU_ADDR, 0); - for (i = 0; i < 17 * 17 * 17; ++i) - vsp1_dl_fragment_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); + vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0); + for (i = 0; i < CLU_SIZE; ++i) + vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); spin_lock_irq(&clu->lock); swap(clu->clu, dlb); spin_unlock_irq(&clu->lock); - vsp1_dl_fragment_free(dlb); + vsp1_dl_body_put(dlb); return 0; } @@ -118,18 +116,18 @@ static const struct v4l2_ctrl_config clu_mode_control = { * V4L2 Subdevice Pad Operations */ +static const unsigned int clu_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int clu_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AHSV8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, clu_codes, + ARRAY_SIZE(clu_codes)); } static int clu_enum_frame_size(struct v4l2_subdev *subdev, @@ -145,51 +143,10 @@ static int clu_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_clu *clu = to_clu(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&clu->entity.lock); - - config = vsp1_entity_get_pad_config(&clu->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&clu->entity, config, fmt->pad); - - if (fmt->pad == CLU_PAD_SOURCE) { - /* The CLU output format can't be modified. */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - CLU_MIN_SIZE, CLU_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - CLU_MIN_SIZE, CLU_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&clu->entity, config, - CLU_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&clu->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, clu_codes, + ARRAY_SIZE(clu_codes), + CLU_MIN_SIZE, CLU_MIN_SIZE, + CLU_MAX_SIZE, CLU_MAX_SIZE); } /* ----------------------------------------------------------------------------- @@ -212,57 +169,65 @@ static const struct v4l2_subdev_ops clu_ops = { * VSP1 Entity Operations */ -static void clu_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void clu_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_clu *clu = to_clu(&entity->subdev); - struct vsp1_dl_body *dlb; + struct v4l2_mbus_framefmt *format; + + /* + * 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.config, + CLU_PAD_SINK); + clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; +} + +static void clu_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + struct vsp1_dl_body *clu_dlb; unsigned long flags; u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN; - switch (params) { - case VSP1_ENTITY_PARAMS_INIT: { - /* - * The format can't be changed during streaming, only verify it - * at setup time and store the information internally for future - * runtime configuration calls. - */ - struct v4l2_mbus_framefmt *format; - - format = vsp1_entity_get_pad_format(&clu->entity, - clu->entity.config, - CLU_PAD_SINK); - clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32; - break; - } - - case VSP1_ENTITY_PARAMS_PARTITION: - break; + /* 2D mode can only be used with the YCbCr pixel encoding. */ + if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) + ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D + | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D + | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; - case VSP1_ENTITY_PARAMS_RUNTIME: - /* 2D mode can only be used with the YCbCr pixel encoding. */ - if (clu->mode == V4L2_CID_VSP1_CLU_MODE_2D && clu->yuv_mode) - ctrl |= VI6_CLU_CTRL_AX1I_2D | VI6_CLU_CTRL_AX2I_2D - | VI6_CLU_CTRL_OS0_2D | VI6_CLU_CTRL_OS1_2D - | VI6_CLU_CTRL_OS2_2D | VI6_CLU_CTRL_M2D; + vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl); - vsp1_clu_write(clu, dl, VI6_CLU_CTRL, ctrl); + spin_lock_irqsave(&clu->lock, flags); + clu_dlb = clu->clu; + clu->clu = NULL; + spin_unlock_irqrestore(&clu->lock, flags); - spin_lock_irqsave(&clu->lock, flags); - dlb = clu->clu; - clu->clu = NULL; - spin_unlock_irqrestore(&clu->lock, flags); + if (clu_dlb) { + vsp1_dl_list_add_body(dl, clu_dlb); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); - break; + /* Release our local reference. */ + vsp1_dl_body_put(clu_dlb); } } +static void clu_destroy(struct vsp1_entity *entity) +{ + struct vsp1_clu *clu = to_clu(&entity->subdev); + + vsp1_dl_body_pool_destroy(clu->pool); +} + static const struct vsp1_entity_operations clu_entity_ops = { - .configure = clu_configure, + .configure_stream = clu_configure_stream, + .configure_frame = clu_configure_frame, + .destroy = clu_destroy, }; /* ----------------------------------------------------------------------------- @@ -288,6 +253,17 @@ struct vsp1_clu *vsp1_clu_create(struct vsp1_device *vsp1) if (ret < 0) return ERR_PTR(ret); + /* + * Pre-allocate a body pool, with 3 bodies allowing a userspace update + * before the hardware has committed a previous set of tables, handling + * both the queued and pending dl entries. One extra entry is added to + * the CLU_SIZE to allow for the VI6_CLU_ADDR header. + */ + clu->pool = vsp1_dl_body_pool_create(clu->entity.vsp1, 3, CLU_SIZE + 1, + 0); + if (!clu->pool) + return ERR_PTR(-ENOMEM); + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&clu->ctrls, 2); v4l2_ctrl_new_custom(&clu->ctrls, &clu_table_control, NULL); diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/vsp1/vsp1_clu.h index 036e0a2f1a42..cef2f44481ba 100644 --- a/drivers/media/platform/vsp1/vsp1_clu.h +++ b/drivers/media/platform/vsp1/vsp1_clu.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_clu.h -- R-Car VSP1 Cubic Look-Up Table * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_CLU_H__ #define __VSP1_CLU_H__ @@ -36,6 +32,7 @@ struct vsp1_clu { spinlock_t lock; unsigned int mode; struct vsp1_dl_body *clu; + struct vsp1_dl_body_pool *pool; }; static inline struct vsp1_clu *to_clu(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 0b86ed01e85d..d9b9cdd8fbe2 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -1,19 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_dl.h -- R-Car VSP1 Display List + * vsp1_dl.c -- R-Car VSP1 Display List * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/gfp.h> +#include <linux/refcount.h> #include <linux/slab.h> #include <linux/workqueue.h> @@ -45,14 +42,22 @@ struct vsp1_dl_entry { /** * struct vsp1_dl_body - Display list body * @list: entry in the display list list of bodies + * @free: entry in the pool free body list + * @pool: pool to which this body belongs * @vsp1: the VSP1 device * @entries: array of entries * @dma: DMA address of the entries * @size: size of the DMA memory in bytes * @num_entries: number of stored entries + * @max_entries: number of entries available */ struct vsp1_dl_body { struct list_head list; + struct list_head free; + + refcount_t refcnt; + + struct vsp1_dl_body_pool *pool; struct vsp1_device *vsp1; struct vsp1_dl_entry *entries; @@ -60,6 +65,31 @@ struct vsp1_dl_body { size_t size; unsigned int num_entries; + unsigned int max_entries; +}; + +/** + * struct vsp1_dl_body_pool - display list body pool + * @dma: DMA address of the entries + * @size: size of the full DMA memory pool in bytes + * @mem: CPU memory pointer for the pool + * @bodies: Array of DLB structures for the pool + * @free: List of free DLB entries + * @lock: Protects the free list + * @vsp1: the VSP1 device + */ +struct vsp1_dl_body_pool { + /* DMA allocation */ + dma_addr_t dma; + size_t size; + void *mem; + + /* Body management */ + struct vsp1_dl_body *bodies; + struct list_head free; + spinlock_t lock; + + struct vsp1_device *vsp1; }; /** @@ -69,9 +99,10 @@ struct vsp1_dl_body { * @header: display list header, NULL for headerless lists * @dma: DMA address for the header * @body0: first display list body - * @fragments: list of extra display list bodies + * @bodies: list of extra display list bodies * @has_chain: if true, indicates that there's a partition chain * @chain: entry in the display list partition chain + * @internal: whether the display list is used for internal purpose */ struct vsp1_dl_list { struct list_head list; @@ -80,11 +111,13 @@ struct vsp1_dl_list { struct vsp1_dl_header *header; dma_addr_t dma; - struct vsp1_dl_body body0; - struct list_head fragments; + struct vsp1_dl_body *body0; + struct list_head bodies; bool has_chain; struct list_head chain; + + bool internal; }; enum vsp1_dl_mode { @@ -98,13 +131,12 @@ enum vsp1_dl_mode { * @mode: display list operation mode (header or headerless) * @singleshot: execute the display list in single-shot mode * @vsp1: the VSP1 device - * @lock: protects the free, active, queued, pending and gc_fragments lists + * @lock: protects the free, active, queued, and pending lists * @free: array of all free display lists * @active: list currently being processed (loaded) by hardware * @queued: list queued to the hardware (written to the DL registers) * @pending: list waiting to be queued to the hardware - * @gc_work: fragments garbage collector work struct - * @gc_fragments: array of display list fragments waiting to be freed + * @pool: body pool for the display list bodies */ struct vsp1_dl_manager { unsigned int index; @@ -118,109 +150,164 @@ struct vsp1_dl_manager { struct vsp1_dl_list *queued; struct vsp1_dl_list *pending; - struct work_struct gc_work; - struct list_head gc_fragments; + struct vsp1_dl_body_pool *pool; }; /* ----------------------------------------------------------------------------- * Display List Body Management */ -/* - * Initialize a display list body object and allocate DMA memory for the body - * data. The display list body object is expected to have been initialized to - * 0 when allocated. +/** + * vsp1_dl_body_pool_create - Create a pool of bodies from a single allocation + * @vsp1: The VSP1 device + * @num_bodies: The number of bodies to allocate + * @num_entries: The maximum number of entries that a body can contain + * @extra_size: Extra allocation provided for the bodies + * + * Allocate a pool of display list bodies each with enough memory to contain the + * requested number of entries plus the @extra_size. + * + * Return a pointer to a pool on success or NULL if memory can't be allocated. */ -static int vsp1_dl_body_init(struct vsp1_device *vsp1, - struct vsp1_dl_body *dlb, unsigned int num_entries, - size_t extra_size) +struct vsp1_dl_body_pool * +vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, + unsigned int num_entries, size_t extra_size) { - size_t size = num_entries * sizeof(*dlb->entries) + extra_size; + struct vsp1_dl_body_pool *pool; + size_t dlb_size; + unsigned int i; - dlb->vsp1 = vsp1; - dlb->size = size; + pool = kzalloc(sizeof(*pool), GFP_KERNEL); + if (!pool) + return NULL; - dlb->entries = dma_alloc_wc(vsp1->bus_master, dlb->size, &dlb->dma, - GFP_KERNEL); - if (!dlb->entries) - return -ENOMEM; + pool->vsp1 = vsp1; - return 0; + /* + * TODO: 'extra_size' is only used by vsp1_dlm_create(), to allocate + * extra memory for the display list header. We need only one header per + * display list, not per display list body, thus this allocation is + * extraneous and should be reworked in the future. + */ + dlb_size = num_entries * sizeof(struct vsp1_dl_entry) + extra_size; + pool->size = dlb_size * num_bodies; + + pool->bodies = kcalloc(num_bodies, sizeof(*pool->bodies), GFP_KERNEL); + if (!pool->bodies) { + kfree(pool); + return NULL; + } + + pool->mem = dma_alloc_wc(vsp1->bus_master, pool->size, &pool->dma, + GFP_KERNEL); + if (!pool->mem) { + kfree(pool->bodies); + kfree(pool); + return NULL; + } + + spin_lock_init(&pool->lock); + INIT_LIST_HEAD(&pool->free); + + for (i = 0; i < num_bodies; ++i) { + struct vsp1_dl_body *dlb = &pool->bodies[i]; + + dlb->pool = pool; + dlb->max_entries = num_entries; + + dlb->dma = pool->dma + i * dlb_size; + dlb->entries = pool->mem + i * dlb_size; + + list_add_tail(&dlb->free, &pool->free); + } + + return pool; } -/* - * Cleanup a display list body and free allocated DMA memory allocated. +/** + * vsp1_dl_body_pool_destroy - Release a body pool + * @pool: The body pool + * + * Release all components of a pool allocation. */ -static void vsp1_dl_body_cleanup(struct vsp1_dl_body *dlb) +void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool) { - dma_free_wc(dlb->vsp1->bus_master, dlb->size, dlb->entries, dlb->dma); + if (!pool) + return; + + if (pool->mem) + dma_free_wc(pool->vsp1->bus_master, pool->size, pool->mem, + pool->dma); + + kfree(pool->bodies); + kfree(pool); } /** - * vsp1_dl_fragment_alloc - Allocate a display list fragment - * @vsp1: The VSP1 device - * @num_entries: The maximum number of entries that the fragment can contain + * vsp1_dl_body_get - Obtain a body from a pool + * @pool: The body pool * - * Allocate a display list fragment with enough memory to contain the requested - * number of entries. + * Obtain a body from the pool without blocking. * - * Return a pointer to a fragment on success or NULL if memory can't be - * allocated. + * Returns a display list body or NULL if there are none available. */ -struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, - unsigned int num_entries) +struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool) { - struct vsp1_dl_body *dlb; - int ret; + struct vsp1_dl_body *dlb = NULL; + unsigned long flags; - dlb = kzalloc(sizeof(*dlb), GFP_KERNEL); - if (!dlb) - return NULL; + spin_lock_irqsave(&pool->lock, flags); - ret = vsp1_dl_body_init(vsp1, dlb, num_entries, 0); - if (ret < 0) { - kfree(dlb); - return NULL; + if (!list_empty(&pool->free)) { + dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free); + list_del(&dlb->free); + refcount_set(&dlb->refcnt, 1); } + spin_unlock_irqrestore(&pool->lock, flags); + return dlb; } /** - * vsp1_dl_fragment_free - Free a display list fragment - * @dlb: The fragment - * - * Free the given display list fragment and the associated DMA memory. - * - * Fragments must only be freed explicitly if they are not added to a display - * list, as the display list will take ownership of them and free them - * otherwise. Manual free typically happens at cleanup time for fragments that - * have been allocated but not used. + * vsp1_dl_body_put - Return a body back to its pool + * @dlb: The display list body * - * Passing a NULL pointer to this function is safe, in that case no operation - * will be performed. + * Return a body back to the pool, and reset the num_entries to clear the list. */ -void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb) +void vsp1_dl_body_put(struct vsp1_dl_body *dlb) { + unsigned long flags; + if (!dlb) return; - vsp1_dl_body_cleanup(dlb); - kfree(dlb); + if (!refcount_dec_and_test(&dlb->refcnt)) + return; + + dlb->num_entries = 0; + + spin_lock_irqsave(&dlb->pool->lock, flags); + list_add_tail(&dlb->free, &dlb->pool->free); + spin_unlock_irqrestore(&dlb->pool->lock, flags); } /** - * vsp1_dl_fragment_write - Write a register to a display list fragment - * @dlb: The fragment + * vsp1_dl_body_write - Write a register to a display list body + * @dlb: The body * @reg: The register address * @data: The register value * - * Write the given register and value to the display list fragment. The maximum - * number of entries that can be written in a fragment is specified when the - * fragment is allocated by vsp1_dl_fragment_alloc(). + * Write the given register and value to the display list body. The maximum + * number of entries that can be written in a body is specified when the body is + * allocated by vsp1_dl_body_alloc(). */ -void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) +void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) { + if (WARN_ONCE(dlb->num_entries >= dlb->max_entries, + "DLB size exceeded (max %u)", dlb->max_entries)) + return; + dlb->entries[dlb->num_entries].addr = reg; dlb->entries[dlb->num_entries].data = data; dlb->num_entries++; @@ -233,51 +320,47 @@ void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data) static struct vsp1_dl_list *vsp1_dl_list_alloc(struct vsp1_dl_manager *dlm) { struct vsp1_dl_list *dl; - size_t header_size; - int ret; dl = kzalloc(sizeof(*dl), GFP_KERNEL); if (!dl) return NULL; - INIT_LIST_HEAD(&dl->fragments); + INIT_LIST_HEAD(&dl->bodies); dl->dlm = dlm; - /* - * Initialize the display list body and allocate DMA memory for the body - * and the optional header. Both are allocated together to avoid memory - * fragmentation, with the header located right after the body in - * memory. - */ - header_size = dlm->mode == VSP1_DL_MODE_HEADER - ? ALIGN(sizeof(struct vsp1_dl_header), 8) - : 0; - - ret = vsp1_dl_body_init(dlm->vsp1, &dl->body0, VSP1_DL_NUM_ENTRIES, - header_size); - if (ret < 0) { - kfree(dl); + /* Get a default body for our list. */ + dl->body0 = vsp1_dl_body_get(dlm->pool); + if (!dl->body0) return NULL; - } - if (dlm->mode == VSP1_DL_MODE_HEADER) { - size_t header_offset = VSP1_DL_NUM_ENTRIES - * sizeof(*dl->body0.entries); + size_t header_offset = dl->body0->max_entries + * sizeof(*dl->body0->entries); - dl->header = ((void *)dl->body0.entries) + header_offset; - dl->dma = dl->body0.dma + header_offset; + dl->header = ((void *)dl->body0->entries) + header_offset; + dl->dma = dl->body0->dma + header_offset; memset(dl->header, 0, sizeof(*dl->header)); - dl->header->lists[0].addr = dl->body0.dma; + dl->header->lists[0].addr = dl->body0->dma; } return dl; } +static void vsp1_dl_list_bodies_put(struct vsp1_dl_list *dl) +{ + struct vsp1_dl_body *dlb, *tmp; + + list_for_each_entry_safe(dlb, tmp, &dl->bodies, list) { + list_del(&dlb->list); + vsp1_dl_body_put(dlb); + } +} + static void vsp1_dl_list_free(struct vsp1_dl_list *dl) { - vsp1_dl_body_cleanup(&dl->body0); - list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); + vsp1_dl_body_put(dl->body0); + vsp1_dl_list_bodies_put(dl); + kfree(dl); } @@ -331,18 +414,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl) dl->has_chain = false; + vsp1_dl_list_bodies_put(dl); + /* - * We can't free fragments here as DMA memory can only be freed in - * interruptible context. Move all fragments to the display list - * manager's list of fragments to be freed, they will be - * garbage-collected by the work queue. + * body0 is reused as as an optimisation as presently every display list + * has at least one body, thus we reinitialise the entries list. */ - if (!list_empty(&dl->fragments)) { - list_splice_init(&dl->fragments, &dl->dlm->gc_fragments); - schedule_work(&dl->dlm->gc_work); - } - - dl->body0.num_entries = 0; + dl->body0->num_entries = 0; list_add_tail(&dl->list, &dl->dlm->free); } @@ -369,43 +447,46 @@ void vsp1_dl_list_put(struct vsp1_dl_list *dl) } /** - * vsp1_dl_list_write - Write a register to the display list + * vsp1_dl_list_get_body0 - Obtain the default body for the display list * @dl: The display list - * @reg: The register address - * @data: The register value * - * Write the given register and value to the display list. Up to 256 registers - * can be written per display list. + * Obtain a pointer to the internal display list body allowing this to be passed + * directly to configure operations. */ -void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data) +struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl) { - vsp1_dl_fragment_write(&dl->body0, reg, data); + return dl->body0; } /** - * vsp1_dl_list_add_fragment - Add a fragment to the display list + * vsp1_dl_list_add_body - Add a body to the display list * @dl: The display list - * @dlb: The fragment + * @dlb: The body + * + * Add a display list body to a display list. Registers contained in bodies are + * processed after registers contained in the main display list, in the order in + * which bodies are added. * - * Add a display list body as a fragment to a display list. Registers contained - * in fragments are processed after registers contained in the main display - * list, in the order in which fragments are added. + * Adding a body to a display list passes ownership of the body to the list. The + * caller retains its reference to the fragment when adding it to the display + * list, but is not allowed to add new entries to the body. * - * Adding a fragment to a display list passes ownership of the fragment to the - * list. The caller must not touch the fragment after this call, and must not - * free it explicitly with vsp1_dl_fragment_free(). + * The reference must be explicitly released by a call to vsp1_dl_body_put() + * when the body isn't needed anymore. * - * Fragments are only usable for display lists in header mode. Attempt to - * add a fragment to a header-less display list will return an error. + * Additional bodies are only usable for display lists in header mode. + * Attempting to add a body to a header-less display list will return an error. */ -int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, - struct vsp1_dl_body *dlb) +int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb) { /* Multi-body lists are only available in header mode. */ if (dl->dlm->mode != VSP1_DL_MODE_HEADER) return -EINVAL; - list_add_tail(&dlb->list, &dl->fragments); + refcount_inc(&dlb->refcnt); + + list_add_tail(&dlb->list, &dl->bodies); + return 0; } @@ -451,10 +532,10 @@ static void vsp1_dl_list_fill_header(struct vsp1_dl_list *dl, bool is_last) * list was allocated. */ - hdr->num_bytes = dl->body0.num_entries + hdr->num_bytes = dl->body0->num_entries * sizeof(*dl->header->lists); - list_for_each_entry(dlb, &dl->fragments, list) { + list_for_each_entry(dlb, &dl->bodies, list) { num_lists++; hdr++; @@ -525,9 +606,9 @@ static void vsp1_dl_list_hw_enqueue(struct vsp1_dl_list *dl) * bit will be cleared by the hardware when the display list * processing starts. */ - vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0.dma); + vsp1_write(vsp1, VI6_DL_HDR_ADDR(0), dl->body0->dma); vsp1_write(vsp1, VI6_DL_BODY_SIZE, VI6_DL_BODY_SIZE_UPD | - (dl->body0.num_entries * sizeof(*dl->header->lists))); + (dl->body0->num_entries * sizeof(*dl->header->lists))); } else { /* * In header mode, program the display list header address. If @@ -550,8 +631,16 @@ static void vsp1_dl_list_commit_continuous(struct vsp1_dl_list *dl) * case we can't replace the queued list by the new one, as we could * race with the hardware. We thus mark the update as pending, it will * be queued up to the hardware by the frame end interrupt handler. + * + * If a display list is already pending we simply drop it as the new + * display list is assumed to contain a more recent configuration. It is + * an error if the already pending list has the internal flag set, as + * there is then a process waiting for that list to complete. This + * shouldn't happen as the waiting process should perform proper + * locking, but warn just in case. */ if (vsp1_dl_list_hw_update_pending(dlm)) { + WARN_ON(dlm->pending && dlm->pending->internal); __vsp1_dl_list_put(dlm->pending); dlm->pending = dl; return; @@ -581,7 +670,7 @@ static void vsp1_dl_list_commit_singleshot(struct vsp1_dl_list *dl) dlm->active = dl; } -void vsp1_dl_list_commit(struct vsp1_dl_list *dl) +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal) { struct vsp1_dl_manager *dlm = dl->dlm; struct vsp1_dl_list *dl_child; @@ -598,6 +687,8 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) } } + dl->internal = internal; + spin_lock_irqsave(&dlm->lock, flags); if (dlm->singleshot) @@ -616,14 +707,22 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl) * vsp1_dlm_irq_frame_end - Display list handler for the frame end interrupt * @dlm: the display list manager * - * Return true if the previous display list has completed at frame end, or false - * if it has been delayed by one frame because the display list commit raced - * with the frame end interrupt. The function always returns true in header mode - * as display list processing is then not continuous and races never occur. + * Return a set of flags that indicates display list completion status. + * + * The VSP1_DL_FRAME_END_COMPLETED flag indicates that the previous display list + * has completed at frame end. If the flag is not returned display list + * completion has been delayed by one frame because the display list commit + * raced with the frame end interrupt. The function always returns with the flag + * set in header mode as display list processing is then not continuous and + * races never occur. + * + * The VSP1_DL_FRAME_END_INTERNAL flag indicates that the previous display list + * has completed and had been queued with the internal notification flag. + * Internal notification is only supported for continuous mode. */ -bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) +unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) { - bool completed = false; + unsigned int flags = 0; spin_lock(&dlm->lock); @@ -634,7 +733,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) if (dlm->singleshot) { __vsp1_dl_list_put(dlm->active); dlm->active = NULL; - completed = true; + flags |= VSP1_DL_FRAME_END_COMPLETED; goto done; } @@ -652,10 +751,14 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) * frame end interrupt. The display list thus becomes active. */ if (dlm->queued) { + if (dlm->queued->internal) + flags |= VSP1_DL_FRAME_END_INTERNAL; + dlm->queued->internal = false; + __vsp1_dl_list_put(dlm->active); dlm->active = dlm->queued; dlm->queued = NULL; - completed = true; + flags |= VSP1_DL_FRAME_END_COMPLETED; } /* @@ -672,7 +775,7 @@ bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) done: spin_unlock(&dlm->lock); - return completed; + return flags; } /* Hardware Setup */ @@ -710,38 +813,9 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm) dlm->pending = NULL; } -/* - * Free all fragments awaiting to be garbage-collected. - * - * This function must be called without the display list manager lock held. - */ -static void vsp1_dlm_fragments_free(struct vsp1_dl_manager *dlm) +struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm) { - unsigned long flags; - - spin_lock_irqsave(&dlm->lock, flags); - - while (!list_empty(&dlm->gc_fragments)) { - struct vsp1_dl_body *dlb; - - dlb = list_first_entry(&dlm->gc_fragments, struct vsp1_dl_body, - list); - list_del(&dlb->list); - - spin_unlock_irqrestore(&dlm->lock, flags); - vsp1_dl_fragment_free(dlb); - spin_lock_irqsave(&dlm->lock, flags); - } - - spin_unlock_irqrestore(&dlm->lock, flags); -} - -static void vsp1_dlm_garbage_collect(struct work_struct *work) -{ - struct vsp1_dl_manager *dlm = - container_of(work, struct vsp1_dl_manager, gc_work); - - vsp1_dlm_fragments_free(dlm); + return vsp1_dl_body_get(dlm->pool); } struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, @@ -749,6 +823,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int prealloc) { struct vsp1_dl_manager *dlm; + size_t header_size; unsigned int i; dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL); @@ -763,8 +838,22 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, spin_lock_init(&dlm->lock); INIT_LIST_HEAD(&dlm->free); - INIT_LIST_HEAD(&dlm->gc_fragments); - INIT_WORK(&dlm->gc_work, vsp1_dlm_garbage_collect); + + /* + * Initialize the display list body and allocate DMA memory for the body + * and the optional header. Both are allocated together to avoid memory + * fragmentation, with the header located right after the body in + * memory. An extra body is allocated on top of the prealloc to account + * for the cached body used by the vsp1_pipeline object. + */ + header_size = dlm->mode == VSP1_DL_MODE_HEADER + ? ALIGN(sizeof(struct vsp1_dl_header), 8) + : 0; + + dlm->pool = vsp1_dl_body_pool_create(vsp1, prealloc + 1, + VSP1_DL_NUM_ENTRIES, header_size); + if (!dlm->pool) + return NULL; for (i = 0; i < prealloc; ++i) { struct vsp1_dl_list *dl; @@ -786,12 +875,10 @@ void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm) if (!dlm) return; - cancel_work_sync(&dlm->gc_work); - list_for_each_entry_safe(dl, next, &dlm->free, list) { list_del(&dl->list); vsp1_dl_list_free(dl); } - vsp1_dlm_fragments_free(dlm); + vsp1_dl_body_pool_destroy(dlm->pool); } diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index ee3508172f0a..7dba0469c92e 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_dl.h -- R-Car VSP1 Display List * * Copyright (C) 2015 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_DL_H__ #define __VSP1_DL_H__ @@ -16,10 +12,14 @@ #include <linux/types.h> struct vsp1_device; -struct vsp1_dl_fragment; +struct vsp1_dl_body; +struct vsp1_dl_body_pool; struct vsp1_dl_list; struct vsp1_dl_manager; +#define VSP1_DL_FRAME_END_COMPLETED BIT(0) +#define VSP1_DL_FRAME_END_INTERNAL BIT(1) + void vsp1_dlm_setup(struct vsp1_device *vsp1); struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, @@ -27,19 +27,23 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, unsigned int prealloc); void vsp1_dlm_destroy(struct vsp1_dl_manager *dlm); void vsp1_dlm_reset(struct vsp1_dl_manager *dlm); -bool vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); +unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm); +struct vsp1_dl_body *vsp1_dlm_dl_body_get(struct vsp1_dl_manager *dlm); struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm); void vsp1_dl_list_put(struct vsp1_dl_list *dl); -void vsp1_dl_list_write(struct vsp1_dl_list *dl, u32 reg, u32 data); -void vsp1_dl_list_commit(struct vsp1_dl_list *dl); - -struct vsp1_dl_body *vsp1_dl_fragment_alloc(struct vsp1_device *vsp1, - unsigned int num_entries); -void vsp1_dl_fragment_free(struct vsp1_dl_body *dlb); -void vsp1_dl_fragment_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); -int vsp1_dl_list_add_fragment(struct vsp1_dl_list *dl, - struct vsp1_dl_body *dlb); +struct vsp1_dl_body *vsp1_dl_list_get_body0(struct vsp1_dl_list *dl); +void vsp1_dl_list_commit(struct vsp1_dl_list *dl, bool internal); + +struct vsp1_dl_body_pool * +vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, + unsigned int num_entries, size_t extra_size); +void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool); +struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool); +void vsp1_dl_body_put(struct vsp1_dl_body *dlb); + +void vsp1_dl_body_write(struct vsp1_dl_body *dlb, u32 reg, u32 data); +int vsp1_dl_list_add_body(struct vsp1_dl_list *dl, struct vsp1_dl_body *dlb); int vsp1_dl_list_add_chain(struct vsp1_dl_list *head, struct vsp1_dl_list *dl); #endif /* __VSP1_DL_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c index b8fee1834253..edb35a5c57ea 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.c +++ b/drivers/media/platform/vsp1/vsp1_drm.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* - * vsp1_drm.c -- R-Car VSP1 DRM API + * vsp1_drm.c -- R-Car VSP1 DRM/KMS Interface * * Copyright (C) 2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -20,26 +16,550 @@ #include <media/vsp1.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_drm.h" #include "vsp1_lif.h" #include "vsp1_pipe.h" #include "vsp1_rwpf.h" +#include "vsp1_uif.h" -#define BRU_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS" +#define BRX_NAME(e) (e)->type == VSP1_ENTITY_BRU ? "BRU" : "BRS" /* ----------------------------------------------------------------------------- * Interrupt Handling */ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe, - bool completed) + unsigned int completion) { struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + bool complete = completion == VSP1_DL_FRAME_END_COMPLETED; + + if (drm_pipe->du_complete) { + struct vsp1_entity *uif = drm_pipe->uif; + u32 crc; - if (drm_pipe->du_complete) - drm_pipe->du_complete(drm_pipe->du_private, completed); + crc = uif ? vsp1_uif_get_crc(to_uif(&uif->subdev)) : 0; + drm_pipe->du_complete(drm_pipe->du_private, complete, crc); + } + + if (completion & VSP1_DL_FRAME_END_INTERNAL) { + drm_pipe->force_brx_release = false; + wake_up(&drm_pipe->wait_queue); + } +} + +/* ----------------------------------------------------------------------------- + * Pipeline Configuration + */ + +/* + * Insert the UIF in the pipeline between the prev and next entities. If no UIF + * is available connect the two entities directly. + */ +static int vsp1_du_insert_uif(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe, + struct vsp1_entity *uif, + struct vsp1_entity *prev, unsigned int prev_pad, + struct vsp1_entity *next, unsigned int next_pad) +{ + struct v4l2_subdev_format format; + int ret; + + if (!uif) { + /* + * If there's no UIF to be inserted, connect the previous and + * next entities directly. + */ + prev->sink = next; + prev->sink_pad = next_pad; + return 0; + } + + prev->sink = uif; + prev->sink_pad = UIF_PAD_SINK; + + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = prev_pad; + + ret = v4l2_subdev_call(&prev->subdev, pad, get_fmt, NULL, &format); + if (ret < 0) + return ret; + + format.pad = UIF_PAD_SINK; + + ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on UIF sink\n", + __func__, format.format.width, format.format.height, + format.format.code); + + /* + * The UIF doesn't mangle the format between its sink and source pads, + * so there is no need to retrieve the format on its source pad. + */ + + uif->sink = next; + uif->sink_pad = next_pad; + + return 0; +} + +/* Setup one RPF and the connected BRx sink pad. */ +static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe, + struct vsp1_rwpf *rpf, + struct vsp1_entity *uif, + unsigned int brx_input) +{ + struct v4l2_subdev_selection sel; + struct v4l2_subdev_format format; + 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; + + memset(&format, 0, sizeof(format)); + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = crop->width + crop->left; + format.format.height = crop->height + crop->top; + format.format.code = rpf->fmtinfo->mbus; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set format %ux%u (%x) on RPF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + memset(&sel, 0, sizeof(sel)); + sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sel.pad = RWPF_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r = *crop; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, + &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + rpf->entity.index); + + /* + * RPF source, hardcode the format to ARGB8888 to turn on format + * conversion if needed. + */ + format.pad = RWPF_PAD_SOURCE; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, + "%s: got format %ux%u (%x) on RPF%u source\n", + __func__, format.format.width, format.format.height, + format.format.code, rpf->entity.index); + + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + + ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + /* Insert and configure the UIF if available. */ + ret = vsp1_du_insert_uif(vsp1, pipe, uif, &rpf->entity, RWPF_PAD_SOURCE, + pipe->brx, brx_input); + if (ret < 0) + return ret; + + /* BRx sink, propagate the format from the RPF source. */ + format.pad = brx_input; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, BRX_NAME(pipe->brx), format.pad); + + sel.pad = brx_input; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.r = vsp1->drm->inputs[rpf->entity.index].compose; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_selection, NULL, + &sel); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n", + __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, + BRX_NAME(pipe->brx), sel.pad); + + return 0; +} + +/* Setup the BRx source pad. */ +static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe); +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe); + +static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct vsp1_entity *brx; + int ret; + + /* + * Pick a BRx: + * - If we need more than two inputs, use the BRU. + * - Otherwise, if we are not forced to release our BRx, keep it. + * - Else, use any free BRx (randomly starting with the BRU). + */ + if (pipe->num_inputs > 2) + brx = &vsp1->bru->entity; + else if (pipe->brx && !drm_pipe->force_brx_release) + brx = pipe->brx; + else if (!vsp1->bru->entity.pipe) + brx = &vsp1->bru->entity; + else + brx = &vsp1->brs->entity; + + /* Switch BRx if needed. */ + if (brx != pipe->brx) { + struct vsp1_entity *released_brx = NULL; + + /* Release our BRx if we have one. */ + if (pipe->brx) { + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + /* + * The BRx might be acquired by the other pipeline in + * the next step. We must thus remove it from the list + * of entities for this pipeline. The other pipeline's + * hardware configuration will reconfigure the BRx + * routing. + * + * However, if the other pipeline doesn't acquire our + * BRx, we need to keep it in the list, otherwise the + * hardware configuration step won't disconnect it from + * the pipeline. To solve this, store the released BRx + * pointer to add it back to the list of entities later + * if it isn't acquired by the other pipeline. + */ + released_brx = pipe->brx; + + list_del(&pipe->brx->list_pipe); + pipe->brx->sink = NULL; + pipe->brx->pipe = NULL; + pipe->brx = NULL; + } + + /* + * If the BRx we need is in use, force the owner pipeline to + * switch to the other BRx and wait until the switch completes. + */ + if (brx->pipe) { + struct vsp1_drm_pipeline *owner_pipe; + + dev_dbg(vsp1->dev, "%s: pipe %u: waiting for %s\n", + __func__, pipe->lif->index, BRX_NAME(brx)); + + owner_pipe = to_vsp1_drm_pipeline(brx->pipe); + owner_pipe->force_brx_release = true; + + vsp1_du_pipeline_setup_inputs(vsp1, &owner_pipe->pipe); + vsp1_du_pipeline_configure(&owner_pipe->pipe); + + ret = wait_event_timeout(owner_pipe->wait_queue, + !owner_pipe->force_brx_release, + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, + "DRM pipeline %u reconfiguration timeout\n", + owner_pipe->pipe.lif->index); + } + + /* + * If the BRx we have released previously hasn't been acquired + * by the other pipeline, add it back to the entities list (with + * the pipe pointer NULL) to let vsp1_du_pipeline_configure() + * disconnect it from the hardware pipeline. + */ + if (released_brx && !released_brx->pipe) + list_add_tail(&released_brx->list_pipe, + &pipe->entities); + + /* Add the BRx to the pipeline. */ + dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n", + __func__, pipe->lif->index, BRX_NAME(brx)); + + pipe->brx = brx; + pipe->brx->pipe = pipe; + pipe->brx->sink = &pipe->output->entity; + pipe->brx->sink_pad = 0; + + list_add_tail(&pipe->brx->list_pipe, &pipe->entities); + } + + /* + * Configure the format on the BRx source and verify that it matches the + * requested format. We don't set the media bus code as it is configured + * on the BRx sink pad 0 and propagated inside the entity, not on the + * source pad. + */ + format.pad = pipe->brx->source_pad; + format.format.width = drm_pipe->width; + format.format.height = drm_pipe->height; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", + __func__, format.format.width, format.format.height, + format.format.code, BRX_NAME(pipe->brx), pipe->brx->source_pad); + + if (format.format.width != drm_pipe->width || + format.format.height != drm_pipe->height) { + dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__); + return -EPIPE; + } + + return 0; +} + +static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf) +{ + return vsp1->drm->inputs[rpf->entity.index].zpos; +} + +/* Setup the input side of the pipeline (RPFs and BRx). */ +static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, }; + struct vsp1_entity *uif; + bool use_uif = false; + struct vsp1_brx *brx; + unsigned int i; + int ret; + + /* Count the number of enabled inputs and sort them by Z-order. */ + pipe->num_inputs = 0; + + for (i = 0; i < vsp1->info->rpf_count; ++i) { + struct vsp1_rwpf *rpf = vsp1->rpf[i]; + unsigned int j; + + if (!pipe->inputs[i]) + continue; + + /* Insert the RPF in the sorted RPFs array. */ + for (j = pipe->num_inputs++; j > 0; --j) { + if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf)) + break; + inputs[j] = inputs[j-1]; + } + + inputs[j] = rpf; + } + + /* + * Setup the BRx. This must be done before setting up the RPF input + * pipelines as the BRx sink compose rectangles depend on the BRx source + * format. + */ + ret = vsp1_du_pipeline_setup_brx(vsp1, pipe); + if (ret < 0) { + dev_err(vsp1->dev, "%s: failed to setup %s source\n", __func__, + BRX_NAME(pipe->brx)); + return ret; + } + + brx = to_brx(&pipe->brx->subdev); + + /* Setup the RPF input pipeline for every enabled input. */ + for (i = 0; i < pipe->brx->source_pad; ++i) { + struct vsp1_rwpf *rpf = inputs[i]; + + if (!rpf) { + brx->inputs[i].rpf = NULL; + continue; + } + + if (!rpf->entity.pipe) { + rpf->entity.pipe = pipe; + list_add_tail(&rpf->entity.list_pipe, &pipe->entities); + } + + brx->inputs[i].rpf = rpf; + rpf->brx_input = i; + rpf->entity.sink = pipe->brx; + rpf->entity.sink_pad = i; + + dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n", + __func__, rpf->entity.index, BRX_NAME(pipe->brx), i); + + uif = drm_pipe->crc.source == VSP1_DU_CRC_PLANE && + drm_pipe->crc.index == i ? drm_pipe->uif : NULL; + if (uif) + use_uif = true; + ret = vsp1_du_pipeline_setup_rpf(vsp1, pipe, rpf, uif, i); + if (ret < 0) { + dev_err(vsp1->dev, + "%s: failed to setup RPF.%u\n", + __func__, rpf->entity.index); + return ret; + } + } + + /* Insert and configure the UIF at the BRx output if available. */ + uif = drm_pipe->crc.source == VSP1_DU_CRC_OUTPUT ? drm_pipe->uif : NULL; + if (uif) + use_uif = true; + ret = vsp1_du_insert_uif(vsp1, pipe, uif, + pipe->brx, pipe->brx->source_pad, + &pipe->output->entity, 0); + if (ret < 0) + dev_err(vsp1->dev, "%s: failed to setup UIF after %s\n", + __func__, BRX_NAME(pipe->brx)); + + /* + * If the UIF is not in use schedule it for removal by setting its pipe + * pointer to NULL, vsp1_du_pipeline_configure() will remove it from the + * hardware pipeline and from the pipeline's list of entities. Otherwise + * make sure it is present in the pipeline's list of entities if it + * wasn't already. + */ + if (!use_uif) { + drm_pipe->uif->pipe = NULL; + } else if (!drm_pipe->uif->pipe) { + drm_pipe->uif->pipe = pipe; + list_add_tail(&drm_pipe->uif->list_pipe, &pipe->entities); + } + + return 0; +} + +/* Setup the output side of the pipeline (WPF and LIF). */ +static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1, + struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct v4l2_subdev_format format = { 0, }; + int ret; + + format.which = V4L2_SUBDEV_FORMAT_ACTIVE; + format.pad = RWPF_PAD_SINK; + format.format.width = drm_pipe->width; + format.format.height = drm_pipe->height; + format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; + format.format.field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->output->entity.index); + + format.pad = RWPF_PAD_SOURCE; + ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->output->entity.index); + + format.pad = LIF_PAD_SINK; + ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL, + &format); + if (ret < 0) + return ret; + + dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n", + __func__, format.format.width, format.format.height, + format.format.code, pipe->lif->index); + + /* + * Verify that the format at the output of the pipeline matches the + * requested frame size and media bus code. + */ + if (format.format.width != drm_pipe->width || + format.format.height != drm_pipe->height || + format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) { + dev_dbg(vsp1->dev, "%s: format mismatch on LIF%u\n", __func__, + pipe->lif->index); + return -EPIPE; + } + + return 0; +} + +/* Configure all entities in the pipeline. */ +static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe) +{ + struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe); + struct vsp1_entity *entity; + struct vsp1_entity *next; + struct vsp1_dl_list *dl; + struct vsp1_dl_body *dlb; + + dl = vsp1_dl_list_get(pipe->output->dlm); + dlb = vsp1_dl_list_get_body0(dl); + + list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { + /* Disconnect unused entities from the pipeline. */ + if (!entity->pipe) { + vsp1_dl_body_write(dlb, entity->route->reg, + VI6_DPR_NODE_UNUSED); + + entity->sink = NULL; + list_del(&entity->list_pipe); + + continue; + } + + vsp1_entity_route_setup(entity, pipe, dlb); + vsp1_entity_configure_stream(entity, pipe, dlb); + vsp1_entity_configure_frame(entity, pipe, dl, dlb); + vsp1_entity_configure_partition(entity, pipe, dl, dlb); + } + + vsp1_dl_list_commit(dl, drm_pipe->force_brx_release); } /* ----------------------------------------------------------------------------- @@ -64,8 +584,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_init); * @cfg: the LIF configuration * * Configure the output part of VSP DRM pipeline for the given frame @cfg.width - * and @cfg.height. This sets up formats on the blend unit (BRU or BRS) source - * pad, the WPF sink and source pads, and the LIF sink pad. + * and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and + * source pads, and the LIF sink pad. * * The @pipe_index argument selects which DRM pipeline to setup. The number of * available pipelines depend on the VSP instance. @@ -84,11 +604,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - struct vsp1_bru *bru; - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - struct v4l2_subdev_format format; unsigned long flags; unsigned int i; int ret; @@ -98,9 +613,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe = &vsp1->drm->pipe[pipe_index]; pipe = &drm_pipe->pipe; - bru = to_bru(&pipe->bru->subdev); if (!cfg) { + struct vsp1_brx *brx; + + mutex_lock(&vsp1->drm->lock); + + brx = to_brx(&pipe->brx->subdev); + /* * NULL configuration means the CRTC is being disabled, stop * the pipeline and turn the light off. @@ -109,8 +629,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, if (ret == -ETIMEDOUT) dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - media_pipeline_stop(&pipe->output->entity.subdev.entity); - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { struct vsp1_rwpf *rpf = pipe->inputs[i]; @@ -118,19 +636,30 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, continue; /* - * Remove the RPF from the pipe and the list of BRU + * Remove the RPF from the pipe and the list of BRx * inputs. */ - WARN_ON(list_empty(&rpf->entity.list_pipe)); - list_del_init(&rpf->entity.list_pipe); + WARN_ON(!rpf->entity.pipe); + rpf->entity.pipe = NULL; + list_del(&rpf->entity.list_pipe); pipe->inputs[i] = NULL; - bru->inputs[rpf->bru_input].rpf = NULL; + brx->inputs[rpf->brx_input].rpf = NULL; } drm_pipe->du_complete = NULL; pipe->num_inputs = 0; + dev_dbg(vsp1->dev, "%s: pipe %u: releasing %s\n", + __func__, pipe->lif->index, + BRX_NAME(pipe->brx)); + + list_del(&pipe->brx->list_pipe); + pipe->brx->pipe = NULL; + pipe->brx = NULL; + + mutex_unlock(&vsp1->drm->lock); + vsp1_dlm_reset(pipe->output->dlm); vsp1_device_put(vsp1); @@ -139,100 +668,27 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, return 0; } + drm_pipe->width = cfg->width; + drm_pipe->height = cfg->height; + dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n", __func__, pipe_index, cfg->width, cfg->height); - /* - * Configure the format at the BRU sinks and propagate it through the - * pipeline. - */ - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - for (i = 0; i < pipe->bru->source_pad; ++i) { - format.pad = i; - - format.format.width = cfg->width; - format.format.height = cfg->height; - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, - set_fmt, NULL, &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), i); - } - - format.pad = pipe->bru->source_pad; - format.format.width = cfg->width; - format.format.height = cfg->height; - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), i); - - format.pad = RWPF_PAD_SINK; - ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on WPF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe->output->entity.index); + mutex_lock(&vsp1->drm->lock); - format.pad = RWPF_PAD_SOURCE; - ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, get_fmt, NULL, - &format); + /* Setup formats through the pipeline. */ + ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe); if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: got format %ux%u (%x) on WPF%u source\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe->output->entity.index); + goto unlock; - format.pad = LIF_PAD_SINK; - ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL, - &format); + ret = vsp1_du_pipeline_setup_output(vsp1, pipe); if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on LIF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, pipe_index); + goto unlock; - /* - * Verify that the format at the output of the pipeline matches the - * requested frame size and media bus code. - */ - if (format.format.width != cfg->width || - format.format.height != cfg->height || - format.format.code != MEDIA_BUS_FMT_ARGB8888_1X32) { - dev_dbg(vsp1->dev, "%s: format mismatch\n", __func__); - return -EPIPE; - } - - /* - * Mark the pipeline as streaming and enable the VSP1. This will store - * the pipeline pointer in all entities, which the s_stream handlers - * will need. We don't start the entities themselves right at this point - * as there's no plane configured yet, so we can't start processing - * buffers. - */ + /* Enable the VSP1. */ ret = vsp1_device_get(vsp1); if (ret < 0) - return ret; + goto unlock; /* * Register a callback to allow us to notify the DRM driver of frame @@ -241,35 +697,18 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index, drm_pipe->du_complete = cfg->callback; drm_pipe->du_private = cfg->callback_data; - ret = media_pipeline_start(&pipe->output->entity.subdev.entity, - &pipe->pipe); - if (ret < 0) { - dev_dbg(vsp1->dev, "%s: pipeline start failed\n", __func__); - vsp1_device_put(vsp1); - return ret; - } - /* Disable the display interrupts. */ vsp1_write(vsp1, VI6_DISP_IRQ_STA, 0); vsp1_write(vsp1, VI6_DISP_IRQ_ENB, 0); /* Configure all entities in the pipeline. */ - dl = vsp1_dl_list_get(pipe->output->dlm); + vsp1_du_pipeline_configure(pipe); - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity, pipe, dl); - - if (entity->ops->configure) { - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_INIT); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_RUNTIME); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } - } +unlock: + mutex_unlock(&vsp1->drm->lock); - vsp1_dl_list_commit(dl); + if (ret < 0) + return ret; /* Start the pipeline. */ spin_lock_irqsave(&pipe->irqlock, flags); @@ -290,9 +729,8 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif); void vsp1_du_atomic_begin(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]; - drm_pipe->enabled = drm_pipe->pipe.num_inputs != 0; + mutex_lock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin); @@ -345,10 +783,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, rpf_index); /* - * Remove the RPF from the pipe's inputs. The atomic flush - * handler will disable the input and remove the entity from the - * pipe's entities list. + * Remove the RPF from the pipeline's inputs. Keep it in the + * pipeline's entity list to let vsp1_du_pipeline_configure() + * remove it from the hardware pipeline. */ + rpf->entity.pipe = NULL; drm_pipe->pipe.inputs[rpf_index] = NULL; return 0; } @@ -392,214 +831,24 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index, } EXPORT_SYMBOL_GPL(vsp1_du_atomic_update); -static int vsp1_du_setup_rpf_pipe(struct vsp1_device *vsp1, - struct vsp1_pipeline *pipe, - struct vsp1_rwpf *rpf, unsigned int bru_input) -{ - struct v4l2_subdev_selection sel; - struct v4l2_subdev_format format; - const struct v4l2_rect *crop; - int ret; - - /* - * Configure the format on the RPF sink pad and propagate it up to the - * BRU sink pad. - */ - crop = &vsp1->drm->inputs[rpf->entity.index].crop; - - memset(&format, 0, sizeof(format)); - format.which = V4L2_SUBDEV_FORMAT_ACTIVE; - format.pad = RWPF_PAD_SINK; - format.format.width = crop->width + crop->left; - format.format.height = crop->height + crop->top; - format.format.code = rpf->fmtinfo->mbus; - format.format.field = V4L2_FIELD_NONE; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: set format %ux%u (%x) on RPF%u sink\n", - __func__, format.format.width, format.format.height, - format.format.code, rpf->entity.index); - - memset(&sel, 0, sizeof(sel)); - sel.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sel.pad = RWPF_PAD_SINK; - sel.target = V4L2_SEL_TGT_CROP; - sel.r = *crop; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL, - &sel); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: set selection (%u,%u)/%ux%u on RPF%u sink\n", - __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, - rpf->entity.index); - - /* - * RPF source, hardcode the format to ARGB8888 to turn on format - * conversion if needed. - */ - format.pad = RWPF_PAD_SOURCE; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, get_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, - "%s: got format %ux%u (%x) on RPF%u source\n", - __func__, format.format.width, format.format.height, - format.format.code, rpf->entity.index); - - format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32; - - ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - /* BRU sink, propagate the format from the RPF source. */ - format.pad = bru_input; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_fmt, NULL, - &format); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set format %ux%u (%x) on %s pad %u\n", - __func__, format.format.width, format.format.height, - format.format.code, BRU_NAME(pipe->bru), format.pad); - - sel.pad = bru_input; - sel.target = V4L2_SEL_TGT_COMPOSE; - sel.r = vsp1->drm->inputs[rpf->entity.index].compose; - - ret = v4l2_subdev_call(&pipe->bru->subdev, pad, set_selection, NULL, - &sel); - if (ret < 0) - return ret; - - dev_dbg(vsp1->dev, "%s: set selection (%u,%u)/%ux%u on %s pad %u\n", - __func__, sel.r.left, sel.r.top, sel.r.width, sel.r.height, - BRU_NAME(pipe->bru), sel.pad); - - return 0; -} - -static unsigned int rpf_zpos(struct vsp1_device *vsp1, struct vsp1_rwpf *rpf) -{ - return vsp1->drm->inputs[rpf->entity.index].zpos; -} - /** * vsp1_du_atomic_flush - Commit an atomic update * @dev: the VSP device * @pipe_index: the DRM pipeline index + * @cfg: atomic pipe configuration */ -void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index) +void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index, + const struct vsp1_du_atomic_pipe_config *cfg) { struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; - struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, }; - struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); - struct vsp1_entity *entity; - struct vsp1_entity *next; - struct vsp1_dl_list *dl; - unsigned int i; - int ret; - - /* Prepare the display list. */ - dl = vsp1_dl_list_get(pipe->output->dlm); - - /* Count the number of enabled inputs and sort them by Z-order. */ - pipe->num_inputs = 0; - - for (i = 0; i < vsp1->info->rpf_count; ++i) { - struct vsp1_rwpf *rpf = vsp1->rpf[i]; - unsigned int j; - /* - * Make sure we don't accept more inputs than the hardware can - * handle. This is a temporary fix to avoid display stall, we - * need to instead allocate the BRU or BRS to display pipelines - * dynamically based on the number of planes they each use. - */ - if (pipe->num_inputs >= pipe->bru->source_pad) - pipe->inputs[i] = NULL; + drm_pipe->crc = cfg->crc; - if (!pipe->inputs[i]) - continue; - - /* Insert the RPF in the sorted RPFs array. */ - for (j = pipe->num_inputs++; j > 0; --j) { - if (rpf_zpos(vsp1, inputs[j-1]) <= rpf_zpos(vsp1, rpf)) - break; - inputs[j] = inputs[j-1]; - } - - inputs[j] = rpf; - } - - /* Setup the RPF input pipeline for every enabled input. */ - for (i = 0; i < pipe->bru->source_pad; ++i) { - struct vsp1_rwpf *rpf = inputs[i]; - - if (!rpf) { - bru->inputs[i].rpf = NULL; - continue; - } - - if (list_empty(&rpf->entity.list_pipe)) - list_add_tail(&rpf->entity.list_pipe, &pipe->entities); - - bru->inputs[i].rpf = rpf; - rpf->bru_input = i; - rpf->entity.sink = pipe->bru; - rpf->entity.sink_pad = i; - - dev_dbg(vsp1->dev, "%s: connecting RPF.%u to %s:%u\n", - __func__, rpf->entity.index, BRU_NAME(pipe->bru), i); - - ret = vsp1_du_setup_rpf_pipe(vsp1, pipe, rpf, i); - if (ret < 0) - dev_err(vsp1->dev, - "%s: failed to setup RPF.%u\n", - __func__, rpf->entity.index); - } - - /* Configure all entities in the pipeline. */ - list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) { - /* Disconnect unused RPFs from the pipeline. */ - if (entity->type == VSP1_ENTITY_RPF && - !pipe->inputs[entity->index]) { - vsp1_dl_list_write(dl, entity->route->reg, - VI6_DPR_NODE_UNUSED); - - list_del_init(&entity->list_pipe); - - continue; - } - - vsp1_entity_route_setup(entity, pipe, dl); - - if (entity->ops->configure) { - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_INIT); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_RUNTIME); - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } - } - - vsp1_dl_list_commit(dl); + vsp1_du_pipeline_setup_inputs(vsp1, pipe); + vsp1_du_pipeline_configure(pipe); + mutex_unlock(&vsp1->drm->lock); } EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush); @@ -638,31 +887,40 @@ int vsp1_drm_init(struct vsp1_device *vsp1) if (!vsp1->drm) return -ENOMEM; + mutex_init(&vsp1->drm->lock); + /* Create one DRM pipeline per LIF. */ for (i = 0; i < vsp1->info->lif_count; ++i) { struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; + init_waitqueue_head(&drm_pipe->wait_queue); + vsp1_pipeline_init(pipe); + pipe->frame_end = vsp1_du_pipeline_frame_end; + /* - * The DRM pipeline is static, add entities manually. The first - * pipeline uses the BRU and the second pipeline the BRS. + * The output side of the DRM pipeline is static, add the + * corresponding entities manually. */ - pipe->bru = i == 0 ? &vsp1->bru->entity : &vsp1->brs->entity; - pipe->lif = &vsp1->lif[i]->entity; pipe->output = vsp1->wpf[i]; - pipe->output->pipe = pipe; - pipe->frame_end = vsp1_du_pipeline_frame_end; + pipe->lif = &vsp1->lif[i]->entity; - pipe->bru->sink = &pipe->output->entity; - pipe->bru->sink_pad = 0; + pipe->output->entity.pipe = pipe; pipe->output->entity.sink = pipe->lif; pipe->output->entity.sink_pad = 0; + list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities); - list_add_tail(&pipe->bru->list_pipe, &pipe->entities); + pipe->lif->pipe = pipe; list_add_tail(&pipe->lif->list_pipe, &pipe->entities); - list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities); + + /* + * CRC computation is initially disabled, don't add the UIF to + * the pipeline. + */ + if (i < vsp1->info->uif_count) + drm_pipe->uif = &vsp1->uif[i]->entity; } /* Disable all RPFs initially. */ @@ -677,4 +935,5 @@ int vsp1_drm_init(struct vsp1_device *vsp1) void vsp1_drm_cleanup(struct vsp1_device *vsp1) { + mutex_destroy(&vsp1->drm->lock); } diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h index 1cd9db785bf7..8dfd274a59e2 100644 --- a/drivers/media/platform/vsp1/vsp1_drm.h +++ b/drivers/media/platform/vsp1/vsp1_drm.h @@ -1,46 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_drm.h -- R-Car VSP1 DRM/KMS Interface * * Copyright (C) 2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_DRM_H__ #define __VSP1_DRM_H__ +#include <linux/mutex.h> #include <linux/videodev2.h> +#include <linux/wait.h> + +#include <media/vsp1.h> #include "vsp1_pipe.h" /** * vsp1_drm_pipeline - State for the API exposed to the DRM driver * @pipe: the VSP1 pipeline used for display - * @enabled: pipeline state at the beginning of an update + * @width: output display width + * @height: output display height + * @force_brx_release: when set, release the BRx during the next reconfiguration + * @wait_queue: wait queue to wait for BRx release completion + * @uif: UIF entity if available for the pipeline + * @crc: CRC computation configuration * @du_complete: frame completion callback for the DU driver (optional) * @du_private: data to be passed to the du_complete callback */ struct vsp1_drm_pipeline { struct vsp1_pipeline pipe; - bool enabled; + + unsigned int width; + unsigned int height; + + bool force_brx_release; + wait_queue_head_t wait_queue; + + struct vsp1_entity *uif; + struct vsp1_du_crc_config crc; /* Frame synchronisation */ - void (*du_complete)(void *, bool); + void (*du_complete)(void *data, bool completed, u32 crc); void *du_private; }; /** * 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) */ struct vsp1_drm { struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF]; + struct mutex lock; struct { struct v4l2_rect crop; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index eed9516e25e1..5d82f6ee56ea 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_drv.c -- R-Car VSP1 Driver * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/clk.h> @@ -26,7 +22,7 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_clu.h" #include "vsp1_dl.h" #include "vsp1_drm.h" @@ -39,6 +35,7 @@ #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" +#include "vsp1_uif.h" #include "vsp1_video.h" /* ----------------------------------------------------------------------------- @@ -63,7 +60,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask); if (status & VI6_WFP_IRQ_STA_DFE) { - vsp1_pipeline_frame_end(wpf->pipe); + vsp1_pipeline_frame_end(wpf->entity.pipe); ret = IRQ_HANDLED; } } @@ -269,7 +266,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) /* Instantiate all the entities. */ if (vsp1->info->features & VSP1_HAS_BRS) { - vsp1->brs = vsp1_bru_create(vsp1, VSP1_ENTITY_BRS); + vsp1->brs = vsp1_brx_create(vsp1, VSP1_ENTITY_BRS); if (IS_ERR(vsp1->brs)) { ret = PTR_ERR(vsp1->brs); goto done; @@ -279,7 +276,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) } if (vsp1->info->features & VSP1_HAS_BRU) { - vsp1->bru = vsp1_bru_create(vsp1, VSP1_ENTITY_BRU); + vsp1->bru = vsp1_brx_create(vsp1, VSP1_ENTITY_BRU); if (IS_ERR(vsp1->bru)) { ret = PTR_ERR(vsp1->bru); goto done; @@ -413,6 +410,19 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } + for (i = 0; i < vsp1->info->uif_count; ++i) { + struct vsp1_uif *uif; + + uif = vsp1_uif_create(vsp1, i); + if (IS_ERR(uif)) { + ret = PTR_ERR(uif); + goto done; + } + + vsp1->uif[i] = uif; + list_add_tail(&uif->entity.list_dev, &vsp1->entities); + } + for (i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf; @@ -517,6 +527,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1) for (i = 0; i < vsp1->info->uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); + for (i = 0; i < vsp1->info->uif_count; ++i) + vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED); + vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -576,7 +589,7 @@ static int __maybe_unused vsp1_pm_suspend(struct device *dev) * restarted explicitly by the DU. */ if (!vsp1->drm) - vsp1_pipelines_suspend(vsp1); + vsp1_video_suspend(vsp1); pm_runtime_force_suspend(vsp1->dev); @@ -594,7 +607,7 @@ static int __maybe_unused vsp1_pm_resume(struct device *dev) * restarted explicitly by the DU. */ if (!vsp1->drm) - vsp1_pipelines_resume(vsp1); + vsp1_video_resume(vsp1); return 0; } @@ -744,6 +757,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRU | VSP1_HAS_WPF_VFLIP, .lif_count = 1, .rpf_count = 5, + .uif_count = 1, .wpf_count = 2, .num_bru_inputs = 5, }, { @@ -753,6 +767,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRS | VSP1_HAS_BRU, .lif_count = 1, .rpf_count = 5, + .uif_count = 1, .wpf_count = 1, .num_bru_inputs = 5, }, { @@ -762,6 +777,7 @@ static const struct vsp1_device_info vsp1_device_infos[] = { .features = VSP1_HAS_BRS | VSP1_HAS_BRU, .lif_count = 2, .rpf_count = 5, + .uif_count = 2, .wpf_count = 2, .num_bru_inputs = 5, }, diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c index 54de15095709..36a29e13109e 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.c +++ b/drivers/media/platform/vsp1/vsp1_entity.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_entity.c -- R-Car VSP1 Base Entity * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -26,7 +22,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl) + struct vsp1_dl_body *dlb) { struct vsp1_entity *source; u32 route; @@ -42,7 +38,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); - vsp1_dl_list_write(dl, VI6_DPR_HGO_SMPPT, smppt); + vsp1_dl_body_write(dlb, VI6_DPR_HGO_SMPPT, smppt); return; } else if (entity->type == VSP1_ENTITY_HGT) { u32 smppt; @@ -55,7 +51,7 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, smppt = (pipe->output->entity.index << VI6_DPR_SMPPT_TGW_SHIFT) | (source->route->output << VI6_DPR_SMPPT_PT_SHIFT); - vsp1_dl_list_write(dl, VI6_DPR_HGT_SMPPT, smppt); + vsp1_dl_body_write(dlb, VI6_DPR_HGT_SMPPT, smppt); return; } @@ -70,7 +66,33 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity, */ if (source->type == VSP1_ENTITY_BRS) route |= VI6_DPR_ROUTE_BRSSEL; - vsp1_dl_list_write(dl, source->route->reg, route); + vsp1_dl_body_write(dlb, source->route->reg, route); +} + +void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_stream) + entity->ops->configure_stream(entity, pipe, dlb); +} + +void vsp1_entity_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_frame) + entity->ops->configure_frame(entity, pipe, dl, dlb); +} + +void vsp1_entity_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + if (entity->ops->configure_partition) + entity->ops->configure_partition(entity, pipe, dl, dlb); } /* ----------------------------------------------------------------------------- @@ -311,6 +333,97 @@ done: return ret; } +/* + * vsp1_subdev_set_pad_format - Subdev pad set_fmt handler + * @subdev: V4L2 subdevice + * @cfg: V4L2 subdev pad configuration + * @fmt: V4L2 subdev format + * @codes: Array of supported media bus codes + * @ncodes: Number of supported media bus codes + * @min_width: Minimum image width + * @min_height: Minimum image height + * @max_width: Maximum image width + * @max_height: Maximum image height + * + * This function implements the subdev set_fmt pad operation for entities that + * do not support scaling or cropping. It defaults to the first supplied media + * bus code if the requested code isn't supported, clamps the size to the + * supplied minimum and maximum, and propagates the sink pad format to the + * source pad. + */ +int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt, + const unsigned int *codes, unsigned int ncodes, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height) +{ + struct vsp1_entity *entity = to_vsp1_entity(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *selection; + unsigned int i; + int ret = 0; + + mutex_lock(&entity->lock); + + config = vsp1_entity_get_pad_config(entity, cfg, fmt->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + format = vsp1_entity_get_pad_format(entity, config, fmt->pad); + + if (fmt->pad == entity->source_pad) { + /* The output format can't be modified. */ + fmt->format = *format; + goto done; + } + + /* + * Default to the first media bus code if the requested format is not + * supported. + */ + for (i = 0; i < ncodes; ++i) { + if (fmt->format.code == codes[i]) + break; + } + + format->code = i < ncodes ? codes[i] : codes[0]; + format->width = clamp_t(unsigned int, fmt->format.width, + min_width, max_width); + format->height = clamp_t(unsigned int, fmt->format.height, + min_height, max_height); + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_SRGB; + + fmt->format = *format; + + /* Propagate the format to the source pad. */ + format = vsp1_entity_get_pad_format(entity, config, entity->source_pad); + *format = fmt->format; + + /* Reset the crop and compose rectangles */ + selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + V4L2_SEL_TGT_CROP); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + + selection = vsp1_entity_get_pad_selection(entity, config, fmt->pad, + V4L2_SEL_TGT_COMPOSE); + selection->left = 0; + selection->top = 0; + selection->width = format->width; + selection->height = format->height; + +done: + mutex_unlock(&entity->lock); + return ret; +} + /* ----------------------------------------------------------------------------- * Media Operations */ @@ -452,6 +565,10 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad) { VSP1_ENTITY_UDS, idx, VI6_DPR_UDS_ROUTE(idx), \ { VI6_DPR_NODE_UDS(idx) }, VI6_DPR_NODE_UDS(idx) } +#define VSP1_ENTITY_ROUTE_UIF(idx) \ + { VSP1_ENTITY_UIF, idx, VI6_DPR_UIF_ROUTE(idx), \ + { VI6_DPR_NODE_UIF(idx) }, VI6_DPR_NODE_UIF(idx) } + #define VSP1_ENTITY_ROUTE_WPF(idx) \ { VSP1_ENTITY_WPF, idx, 0, \ { VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) } @@ -480,6 +597,8 @@ static const struct vsp1_route vsp1_routes[] = { VSP1_ENTITY_ROUTE_UDS(0), VSP1_ENTITY_ROUTE_UDS(1), VSP1_ENTITY_ROUTE_UDS(2), + VSP1_ENTITY_ROUTE_UIF(0), /* Named UIF4 in the documentation */ + VSP1_ENTITY_ROUTE_UIF(1), /* Named UIF5 in the documentation */ VSP1_ENTITY_ROUTE_WPF(0), VSP1_ENTITY_ROUTE_WPF(1), VSP1_ENTITY_ROUTE_WPF(2), @@ -511,7 +630,8 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity, entity->source_pad = num_pads - 1; /* Allocate and initialize pads. */ - entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads), + entity->pads = devm_kcalloc(vsp1->dev, + num_pads, sizeof(*entity->pads), GFP_KERNEL); if (entity->pads == NULL) return -ENOMEM; diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h index 408602ebeb97..97acb7795cf1 100644 --- a/drivers/media/platform/vsp1/vsp1_entity.h +++ b/drivers/media/platform/vsp1/vsp1_entity.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_entity.h -- R-Car VSP1 Base Entity * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_ENTITY_H__ #define __VSP1_ENTITY_H__ @@ -19,6 +15,7 @@ #include <media/v4l2-subdev.h> struct vsp1_device; +struct vsp1_dl_body; struct vsp1_dl_list; struct vsp1_pipeline; struct vsp1_partition; @@ -37,21 +34,10 @@ enum vsp1_entity_type { VSP1_ENTITY_RPF, VSP1_ENTITY_SRU, VSP1_ENTITY_UDS, + VSP1_ENTITY_UIF, VSP1_ENTITY_WPF, }; -/** - * enum vsp1_entity_params - Entity configuration parameters class - * @VSP1_ENTITY_PARAMS_INIT - Initial parameters - * @VSP1_ENTITY_PARAMS_PARTITION - Per-image partition parameters - * @VSP1_ENTITY_PARAMS_RUNTIME - Runtime-configurable parameters - */ -enum vsp1_entity_params { - VSP1_ENTITY_PARAMS_INIT, - VSP1_ENTITY_PARAMS_PARTITION, - VSP1_ENTITY_PARAMS_RUNTIME, -}; - #define VSP1_ENTITY_MAX_INPUTS 5 /* For the BRU */ /* @@ -80,8 +66,10 @@ struct vsp1_route { /** * struct vsp1_entity_operations - Entity operations * @destroy: Destroy the entity. - * @configure: Setup the hardware based on the entity state (pipeline, formats, - * selection rectangles, ...) + * @configure_stream: Setup the hardware parameters for the stream which do + * not vary between frames (pipeline, formats). + * @configure_frame: Configure the runtime parameters for each frame. + * @configure_partition: Configure partition specific parameters. * @max_width: Return the max supported width of data that the entity can * process in a single operation. * @partition: Process the partition construction based on this entity's @@ -89,8 +77,14 @@ struct vsp1_route { */ struct vsp1_entity_operations { void (*destroy)(struct vsp1_entity *); - void (*configure)(struct vsp1_entity *, struct vsp1_pipeline *, - struct vsp1_dl_list *, enum vsp1_entity_params); + void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *, + 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, @@ -106,6 +100,8 @@ struct vsp1_entity { unsigned int index; const struct vsp1_route *route; + struct vsp1_pipeline *pipe; + struct list_head list_dev; struct list_head list_pipe; @@ -155,13 +151,33 @@ int vsp1_entity_init_cfg(struct v4l2_subdev *subdev, void vsp1_entity_route_setup(struct vsp1_entity *entity, struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl); + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); + +void vsp1_entity_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb); struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad); int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt); +int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt, + const unsigned int *codes, unsigned int ncodes, + unsigned int min_width, unsigned int min_height, + unsigned int max_width, unsigned int max_height); int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code, diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/vsp1/vsp1_hgo.c index 50309c053b78..827373c25351 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/vsp1/vsp1_hgo.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hgo.c -- R-Car VSP1 Histogram Generator 1D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -32,10 +28,10 @@ static inline u32 vsp1_hgo_read(struct vsp1_hgo *hgo, u32 reg) return vsp1_read(hgo->histo.entity.vsp1, reg); } -static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_hgo_write(struct vsp1_hgo *hgo, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -133,10 +129,9 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = { * VSP1 Entity Operations */ -static void hgo_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hgo_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hgo *hgo = to_hgo(&entity->subdev); struct v4l2_rect *compose; @@ -144,21 +139,18 @@ static void hgo_configure(struct vsp1_entity *entity, unsigned int hratio; unsigned int vratio; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - crop = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); compose = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); - vsp1_hgo_write(hgo, dl, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); + vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA); - vsp1_hgo_write(hgo, dl, VI6_HGO_OFFSET, + vsp1_hgo_write(hgo, dlb, VI6_HGO_OFFSET, (crop->left << VI6_HGO_OFFSET_HOFFSET_SHIFT) | (crop->top << VI6_HGO_OFFSET_VOFFSET_SHIFT)); - vsp1_hgo_write(hgo, dl, VI6_HGO_SIZE, + vsp1_hgo_write(hgo, dlb, VI6_HGO_SIZE, (crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGO_SIZE_VSIZE_SHIFT)); @@ -170,7 +162,7 @@ static void hgo_configure(struct vsp1_entity *entity, hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; - vsp1_hgo_write(hgo, dl, VI6_HGO_MODE, + vsp1_hgo_write(hgo, dlb, VI6_HGO_MODE, (hgo->num_bins == 256 ? VI6_HGO_MODE_STEP : 0) | (hgo->max_rgb ? VI6_HGO_MODE_MAXRGB : 0) | (hratio << VI6_HGO_MODE_HRATIO_SHIFT) | @@ -178,7 +170,7 @@ static void hgo_configure(struct vsp1_entity *entity, } static const struct vsp1_entity_operations hgo_entity_ops = { - .configure = hgo_configure, + .configure_stream = hgo_configure_stream, .destroy = vsp1_histogram_destroy, }; diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/vsp1/vsp1_hgo.h index c6c0b7a80e0c..6b0c8580e1bf 100644 --- a/drivers/media/platform/vsp1/vsp1_hgo.h +++ b/drivers/media/platform/vsp1/vsp1_hgo.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hgo.h -- R-Car VSP1 Histogram Generator 1D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HGO_H__ #define __VSP1_HGO_H__ diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/vsp1/vsp1_hgt.c index b5ce305e3e6f..bb6ce6fdd5f4 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/vsp1/vsp1_hgt.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hgt.c -- R-Car VSP1 Histogram Generator 2D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -32,10 +28,10 @@ static inline u32 vsp1_hgt_read(struct vsp1_hgt *hgt, u32 reg) return vsp1_read(hgt->histo.entity.vsp1, reg); } -static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_hgt_write(struct vsp1_hgt *hgt, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -129,10 +125,9 @@ static const struct v4l2_ctrl_config hgt_hue_areas = { * VSP1 Entity Operations */ -static void hgt_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hgt_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hgt *hgt = to_hgt(&entity->subdev); struct v4l2_rect *compose; @@ -143,21 +138,18 @@ static void hgt_configure(struct vsp1_entity *entity, u8 upper; unsigned int i; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - crop = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_CROP); compose = vsp1_entity_get_pad_selection(entity, entity->config, HISTO_PAD_SINK, V4L2_SEL_TGT_COMPOSE); - vsp1_hgt_write(hgt, dl, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); + vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA); - vsp1_hgt_write(hgt, dl, VI6_HGT_OFFSET, + vsp1_hgt_write(hgt, dlb, VI6_HGT_OFFSET, (crop->left << VI6_HGT_OFFSET_HOFFSET_SHIFT) | (crop->top << VI6_HGT_OFFSET_VOFFSET_SHIFT)); - vsp1_hgt_write(hgt, dl, VI6_HGT_SIZE, + vsp1_hgt_write(hgt, dlb, VI6_HGT_SIZE, (crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) | (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); @@ -165,7 +157,7 @@ static void hgt_configure(struct vsp1_entity *entity, for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { lower = hgt->hue_areas[i*2 + 0]; upper = hgt->hue_areas[i*2 + 1]; - vsp1_hgt_write(hgt, dl, VI6_HGT_HUE_AREA(i), + vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) | (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT)); } @@ -173,13 +165,13 @@ static void hgt_configure(struct vsp1_entity *entity, hratio = crop->width * 2 / compose->width / 3; vratio = crop->height * 2 / compose->height / 3; - vsp1_hgt_write(hgt, dl, VI6_HGT_MODE, + vsp1_hgt_write(hgt, dlb, VI6_HGT_MODE, (hratio << VI6_HGT_MODE_HRATIO_SHIFT) | (vratio << VI6_HGT_MODE_VRATIO_SHIFT)); } static const struct vsp1_entity_operations hgt_entity_ops = { - .configure = hgt_configure, + .configure_stream = hgt_configure_stream, .destroy = vsp1_histogram_destroy, }; diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/vsp1/vsp1_hgt.h index 83f2e130942a..38ec237bdd2d 100644 --- a/drivers/media/platform/vsp1/vsp1_hgt.h +++ b/drivers/media/platform/vsp1/vsp1_hgt.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hgt.h -- R-Car VSP1 Histogram Generator 2D * * Copyright (C) 2016 Renesas Electronics Corporation * * Contact: Niklas Söderlund (niklas.soderlund@ragnatech.se) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HGT_H__ #define __VSP1_HGT_H__ diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/vsp1/vsp1_histo.c index afab77cf4fa5..5e15c8ff88d9 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.c +++ b/drivers/media/platform/vsp1/vsp1_histo.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_histo.c -- R-Car VSP1 Histogram API * @@ -5,11 +6,6 @@ * Copyright (C) 2016 Laurent Pinchart * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -61,7 +57,7 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo, struct vsp1_histogram_buffer *buf, size_t size) { - struct vsp1_pipeline *pipe = histo->pipe; + struct vsp1_pipeline *pipe = histo->entity.pipe; unsigned long flags; /* @@ -393,65 +389,14 @@ static int histo_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_format *fmt) { struct vsp1_histogram *histo = subdev_to_histo(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *selection; - unsigned int i; - int ret = 0; if (fmt->pad != HISTO_PAD_SINK) return histo_get_format(subdev, cfg, fmt); - mutex_lock(&histo->entity.lock); - - config = vsp1_entity_get_pad_config(&histo->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* - * Default to the first format if the requested format is not - * supported. - */ - for (i = 0; i < histo->num_formats; ++i) { - if (fmt->format.code == histo->formats[i]) - break; - } - if (i == histo->num_formats) - fmt->format.code = histo->formats[0]; - - format = vsp1_entity_get_pad_format(&histo->entity, config, fmt->pad); - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - HISTO_MIN_SIZE, HISTO_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - HISTO_MIN_SIZE, HISTO_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Reset the crop and compose rectangles */ - selection = vsp1_entity_get_pad_selection(&histo->entity, config, - fmt->pad, V4L2_SEL_TGT_CROP); - selection->left = 0; - selection->top = 0; - selection->width = format->width; - selection->height = format->height; - - selection = vsp1_entity_get_pad_selection(&histo->entity, config, - fmt->pad, - V4L2_SEL_TGT_COMPOSE); - selection->left = 0; - selection->top = 0; - selection->width = format->width; - selection->height = format->height; - -done: - mutex_unlock(&histo->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, + histo->formats, histo->num_formats, + HISTO_MIN_SIZE, HISTO_MIN_SIZE, + HISTO_MAX_SIZE, HISTO_MAX_SIZE); } static const struct v4l2_subdev_pad_ops histo_pad_ops = { diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/vsp1/vsp1_histo.h index af2874f6031d..06f029846244 100644 --- a/drivers/media/platform/vsp1/vsp1_histo.h +++ b/drivers/media/platform/vsp1/vsp1_histo.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_histo.h -- R-Car VSP1 Histogram API * @@ -5,11 +6,6 @@ * Copyright (C) 2016 Laurent Pinchart * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HISTO_H__ #define __VSP1_HISTO_H__ @@ -25,7 +21,6 @@ #include "vsp1_entity.h" struct vsp1_device; -struct vsp1_pipeline; #define HISTO_PAD_SINK 0 #define HISTO_PAD_SOURCE 1 @@ -37,8 +32,6 @@ struct vsp1_histogram_buffer { }; struct vsp1_histogram { - struct vsp1_pipeline *pipe; - struct vsp1_entity entity; struct video_device video; struct media_pad pad; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 764d405345ee..39ab2e0c7c18 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -28,9 +24,9 @@ */ static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -131,24 +127,20 @@ static const struct v4l2_subdev_ops hsit_ops = { * VSP1 Entity Operations */ -static void hsit_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void hsit_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_hsit *hsit = to_hsit(&entity->subdev); - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - if (hsit->inverse) - vsp1_hsit_write(hsit, dl, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); + vsp1_hsit_write(hsit, dlb, VI6_HSI_CTRL, VI6_HSI_CTRL_EN); else - vsp1_hsit_write(hsit, dl, VI6_HST_CTRL, VI6_HST_CTRL_EN); + vsp1_hsit_write(hsit, dlb, VI6_HST_CTRL, VI6_HST_CTRL_EN); } static const struct vsp1_entity_operations hsit_entity_ops = { - .configure = hsit_configure, + .configure_stream = hsit_configure_stream, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/vsp1/vsp1_hsit.h index 82f1c8426900..a658b1aa49e7 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.h +++ b/drivers/media/platform/vsp1/vsp1_hsit.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_HSIT_H__ #define __VSP1_HSIT_H__ diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c index 704920753998..0cb63244b21a 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.c +++ b/drivers/media/platform/vsp1/vsp1_lif.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -27,27 +23,28 @@ * Device Access */ -static inline void vsp1_lif_write(struct vsp1_lif *lif, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_lif_write(struct vsp1_lif *lif, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + lif->entity.index * VI6_LIF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + lif->entity.index * VI6_LIF_OFFSET, + data); } /* ----------------------------------------------------------------------------- * V4L2 Subdevice Operations */ +static const unsigned int lif_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int lif_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lif_codes, + ARRAY_SIZE(lif_codes)); } static int lif_enum_frame_size(struct v4l2_subdev *subdev, @@ -63,53 +60,10 @@ static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_lif *lif = to_lif(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&lif->entity.lock); - - config = vsp1_entity_get_pad_config(&lif->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&lif->entity, config, fmt->pad); - - if (fmt->pad == LIF_PAD_SOURCE) { - /* - * The LIF source format is always identical to its sink - * format. - */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - LIF_MIN_SIZE, LIF_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - LIF_MIN_SIZE, LIF_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lif->entity, config, - LIF_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&lif->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lif_codes, + ARRAY_SIZE(lif_codes), + LIF_MIN_SIZE, LIF_MIN_SIZE, + LIF_MAX_SIZE, LIF_MAX_SIZE); } static const struct v4l2_subdev_pad_ops lif_pad_ops = { @@ -128,10 +82,9 @@ static const struct v4l2_subdev_ops lif_ops = { * VSP1 Entity Operations */ -static void lif_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void lif_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { const struct v4l2_mbus_framefmt *format; struct vsp1_lif *lif = to_lif(&entity->subdev); @@ -139,19 +92,16 @@ static void lif_configure(struct vsp1_entity *entity, unsigned int obth = 400; unsigned int lbth = 200; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.config, LIF_PAD_SOURCE); obth = min(obth, (format->width + 1) / 2 * format->height - 4); - vsp1_lif_write(lif, dl, VI6_LIF_CSBTH, + vsp1_lif_write(lif, dlb, VI6_LIF_CSBTH, (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) | (lbth << VI6_LIF_CSBTH_LBTH_SHIFT)); - vsp1_lif_write(lif, dl, VI6_LIF_CTRL, + vsp1_lif_write(lif, dlb, VI6_LIF_CTRL, (obth << VI6_LIF_CTRL_OBTH_SHIFT) | (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) | VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN); @@ -164,13 +114,13 @@ static void lif_configure(struct vsp1_entity *entity, */ if ((entity->vsp1->version & VI6_IP_VERSION_MASK) == (VI6_IP_VERSION_MODEL_VSPD_V3 | VI6_IP_VERSION_SOC_V3M)) - vsp1_lif_write(lif, dl, VI6_LIF_LBA, + vsp1_lif_write(lif, dlb, VI6_LIF_LBA, VI6_LIF_LBA_LBA0 | (1536 << VI6_LIF_LBA_LBA1_SHIFT)); } static const struct vsp1_entity_operations lif_entity_ops = { - .configure = lif_configure, + .configure_stream = lif_configure_stream, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h index 3417339379b1..71a4eda9c2b2 100644 --- a/drivers/media/platform/vsp1/vsp1_lif.h +++ b/drivers/media/platform/vsp1/vsp1_lif.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_LIF_H__ #define __VSP1_LIF_H__ diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/vsp1/vsp1_lut.c index c67cc60db0db..64c48d9459b0 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.c +++ b/drivers/media/platform/vsp1/vsp1_lut.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_lut.c -- R-Car VSP1 Look-Up Table * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -23,14 +19,16 @@ #define LUT_MIN_SIZE 4U #define LUT_MAX_SIZE 8190U +#define LUT_SIZE 256 + /* ----------------------------------------------------------------------------- * Device Access */ -static inline void vsp1_lut_write(struct vsp1_lut *lut, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_lut_write(struct vsp1_lut *lut, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -44,19 +42,19 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) struct vsp1_dl_body *dlb; unsigned int i; - dlb = vsp1_dl_fragment_alloc(lut->entity.vsp1, 256); + dlb = vsp1_dl_body_get(lut->pool); if (!dlb) return -ENOMEM; - for (i = 0; i < 256; ++i) - vsp1_dl_fragment_write(dlb, VI6_LUT_TABLE + 4 * i, + for (i = 0; i < LUT_SIZE; ++i) + vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); spin_lock_irq(&lut->lock); swap(lut->lut, dlb); spin_unlock_irq(&lut->lock); - vsp1_dl_fragment_free(dlb); + vsp1_dl_body_put(dlb); return 0; } @@ -87,25 +85,25 @@ static const struct v4l2_ctrl_config lut_table_control = { .max = 0x00ffffff, .step = 1, .def = 0, - .dims = { 256}, + .dims = { LUT_SIZE }, }; /* ----------------------------------------------------------------------------- * V4L2 Subdevice Pad Operations */ +static const unsigned int lut_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + static int lut_enum_mbus_code(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - static const unsigned int codes[] = { - MEDIA_BUS_FMT_ARGB8888_1X32, - MEDIA_BUS_FMT_AHSV8888_1X32, - MEDIA_BUS_FMT_AYUV8_1X32, - }; - - return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, - ARRAY_SIZE(codes)); + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, lut_codes, + ARRAY_SIZE(lut_codes)); } static int lut_enum_frame_size(struct v4l2_subdev *subdev, @@ -121,51 +119,10 @@ static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { - struct vsp1_lut *lut = to_lut(subdev); - struct v4l2_subdev_pad_config *config; - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&lut->entity.lock); - - config = vsp1_entity_get_pad_config(&lut->entity, cfg, fmt->which); - if (!config) { - ret = -EINVAL; - goto done; - } - - /* Default to YUV if the requested format is not supported. */ - if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && - fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) - fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; - - format = vsp1_entity_get_pad_format(&lut->entity, config, fmt->pad); - - if (fmt->pad == LUT_PAD_SOURCE) { - /* The LUT output format can't be modified. */ - fmt->format = *format; - goto done; - } - - format->code = fmt->format.code; - format->width = clamp_t(unsigned int, fmt->format.width, - LUT_MIN_SIZE, LUT_MAX_SIZE); - format->height = clamp_t(unsigned int, fmt->format.height, - LUT_MIN_SIZE, LUT_MAX_SIZE); - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - fmt->format = *format; - - /* Propagate the format to the source pad. */ - format = vsp1_entity_get_pad_format(&lut->entity, config, - LUT_PAD_SOURCE); - *format = fmt->format; - -done: - mutex_unlock(&lut->entity.lock); - return ret; + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, lut_codes, + ARRAY_SIZE(lut_codes), + LUT_MIN_SIZE, LUT_MIN_SIZE, + LUT_MAX_SIZE, LUT_MAX_SIZE); } /* ----------------------------------------------------------------------------- @@ -188,37 +145,48 @@ static const struct v4l2_subdev_ops lut_ops = { * VSP1 Entity Operations */ -static void lut_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void lut_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_lut *lut = to_lut(&entity->subdev); - struct vsp1_dl_body *dlb; - unsigned long flags; - switch (params) { - case VSP1_ENTITY_PARAMS_INIT: - vsp1_lut_write(lut, dl, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); - break; + vsp1_lut_write(lut, dlb, VI6_LUT_CTRL, VI6_LUT_CTRL_EN); +} - case VSP1_ENTITY_PARAMS_PARTITION: - break; +static void lut_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_lut *lut = to_lut(&entity->subdev); + struct vsp1_dl_body *lut_dlb; + unsigned long flags; - case VSP1_ENTITY_PARAMS_RUNTIME: - spin_lock_irqsave(&lut->lock, flags); - dlb = lut->lut; - lut->lut = NULL; - spin_unlock_irqrestore(&lut->lock, flags); + spin_lock_irqsave(&lut->lock, flags); + lut_dlb = lut->lut; + lut->lut = NULL; + spin_unlock_irqrestore(&lut->lock, flags); - if (dlb) - vsp1_dl_list_add_fragment(dl, dlb); - break; + if (lut_dlb) { + vsp1_dl_list_add_body(dl, lut_dlb); + + /* Release our local reference. */ + vsp1_dl_body_put(lut_dlb); } } +static void lut_destroy(struct vsp1_entity *entity) +{ + struct vsp1_lut *lut = to_lut(&entity->subdev); + + vsp1_dl_body_pool_destroy(lut->pool); +} + static const struct vsp1_entity_operations lut_entity_ops = { - .configure = lut_configure, + .configure_stream = lut_configure_stream, + .configure_frame = lut_configure_frame, + .destroy = lut_destroy, }; /* ----------------------------------------------------------------------------- @@ -244,6 +212,15 @@ struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1) if (ret < 0) return ERR_PTR(ret); + /* + * Pre-allocate a body pool, with 3 bodies allowing a userspace update + * before the hardware has committed a previous set of tables, handling + * both the queued and pending dl entries. + */ + lut->pool = vsp1_dl_body_pool_create(vsp1, 3, LUT_SIZE, 0); + if (!lut->pool) + return ERR_PTR(-ENOMEM); + /* Initialize the control handler. */ v4l2_ctrl_handler_init(&lut->ctrls, 1); v4l2_ctrl_new_custom(&lut->ctrls, &lut_table_control, NULL); diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/vsp1/vsp1_lut.h index f8c4e8f0a79d..8cb0df1b7e27 100644 --- a/drivers/media/platform/vsp1/vsp1_lut.h +++ b/drivers/media/platform/vsp1/vsp1_lut.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_lut.h -- R-Car VSP1 Look-Up Table * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_LUT_H__ #define __VSP1_LUT_H__ @@ -33,6 +29,7 @@ struct vsp1_lut { spinlock_t lock; struct vsp1_dl_body *lut; + struct vsp1_dl_body_pool *pool; }; static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 44944ac86d9b..54ff539ffea0 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_pipe.c -- R-Car VSP1 Pipeline * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/delay.h> @@ -20,7 +16,7 @@ #include <media/v4l2-subdev.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_hgo.h" @@ -185,44 +181,29 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) { + struct vsp1_entity *entity; unsigned int i; - if (pipe->bru) { - struct vsp1_bru *bru = to_bru(&pipe->bru->subdev); - - for (i = 0; i < ARRAY_SIZE(bru->inputs); ++i) - bru->inputs[i].rpf = NULL; - } - - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { - if (pipe->inputs[i]) { - pipe->inputs[i]->pipe = NULL; - pipe->inputs[i] = NULL; - } - } + if (pipe->brx) { + struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); - if (pipe->output) { - pipe->output->pipe = NULL; - pipe->output = NULL; + for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i) + brx->inputs[i].rpf = NULL; } - if (pipe->hgo) { - struct vsp1_hgo *hgo = to_hgo(&pipe->hgo->subdev); - - hgo->histo.pipe = NULL; - } + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) + pipe->inputs[i] = NULL; - if (pipe->hgt) { - struct vsp1_hgt *hgt = to_hgt(&pipe->hgt->subdev); + pipe->output = NULL; - hgt->histo.pipe = NULL; - } + list_for_each_entry(entity, &pipe->entities, list_pipe) + entity->pipe = NULL; INIT_LIST_HEAD(&pipe->entities); pipe->state = VSP1_PIPELINE_STOPPED; pipe->buffers_ready = 0; pipe->num_inputs = 0; - pipe->bru = NULL; + pipe->brx = NULL; pipe->hgo = NULL; pipe->hgt = NULL; pipe->lif = NULL; @@ -330,17 +311,17 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { - bool completed; + unsigned int flags; if (pipe == NULL) return; /* * If the DL commit raced with the frame end interrupt, the commit ends - * up being postponed by one frame. @completed represents whether the + * up being postponed by one frame. The returned flags tell whether the * active frame was finished or postponed. */ - completed = vsp1_dlm_irq_frame_end(pipe->output->dlm); + flags = vsp1_dlm_irq_frame_end(pipe->output->dlm); if (pipe->hgo) vsp1_hgo_frame_end(pipe->hgo); @@ -353,7 +334,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * frame_end to account for vblank events. */ if (pipe->frame_end) - pipe->frame_end(pipe, completed); + pipe->frame_end(pipe, flags); pipe->sequence++; } @@ -367,7 +348,7 @@ void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) * from the input RPF alpha. */ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, unsigned int alpha) + struct vsp1_dl_body *dlb, unsigned int alpha) { if (!pipe->uds) return; @@ -380,7 +361,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, pipe->uds_input->type == VSP1_ENTITY_BRS) alpha = 255; - vsp1_uds_set_alpha(pipe->uds, dl, alpha); + vsp1_uds_set_alpha(pipe->uds, dlb, alpha); } /* @@ -405,73 +386,3 @@ void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, } } -void vsp1_pipelines_suspend(struct vsp1_device *vsp1) -{ - unsigned long flags; - unsigned int i; - int ret; - - /* - * To avoid increasing the system suspend time needlessly, loop over the - * pipelines twice, first to set them all to the stopping state, and - * then to wait for the stop to complete. - */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (pipe->state == VSP1_PIPELINE_RUNNING) - pipe->state = VSP1_PIPELINE_STOPPING; - spin_unlock_irqrestore(&pipe->irqlock, flags); - } - - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), - msecs_to_jiffies(500)); - if (ret == 0) - dev_warn(vsp1->dev, "pipeline %u stop timeout\n", - wpf->entity.index); - } -} - -void vsp1_pipelines_resume(struct vsp1_device *vsp1) -{ - unsigned long flags; - unsigned int i; - - /* Resume all running pipelines. */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { - struct vsp1_rwpf *wpf = vsp1->wpf[i]; - struct vsp1_pipeline *pipe; - - if (wpf == NULL) - continue; - - pipe = wpf->pipe; - if (pipe == NULL) - continue; - - spin_lock_irqsave(&pipe->irqlock, flags); - if (vsp1_pipeline_ready(pipe)) - vsp1_pipeline_run(pipe); - spin_unlock_irqrestore(&pipe->irqlock, flags); - } -} diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index dfff9b5685fe..743d8f0db45c 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_pipe.h -- R-Car VSP1 Pipeline * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_PIPE_H__ #define __VSP1_PIPE_H__ @@ -99,14 +95,15 @@ struct vsp1_partition { * @num_inputs: number of RPFs * @inputs: array of RPFs in the pipeline (indexed by RPF index) * @output: WPF at the output of the pipeline - * @bru: BRU entity, if present + * @brx: BRx entity, if present * @hgo: HGO entity, if present * @hgt: HGT entity, if present * @lif: LIF entity, if present * @uds: UDS entity, if present * @uds_input: entity at the input of the UDS, if the UDS is present * @entities: list of entities in the pipeline - * @dl: display list associated with the pipeline + * @stream_config: cached stream configuration for video pipelines + * @configured: when false the @stream_config shall be written to the hardware * @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 @@ -118,7 +115,7 @@ struct vsp1_pipeline { enum vsp1_pipeline_state state; wait_queue_head_t wq; - void (*frame_end)(struct vsp1_pipeline *pipe, bool completed); + void (*frame_end)(struct vsp1_pipeline *pipe, unsigned int completion); struct mutex lock; struct kref kref; @@ -129,7 +126,7 @@ struct vsp1_pipeline { unsigned int num_inputs; struct vsp1_rwpf *inputs[VSP1_MAX_RPF]; struct vsp1_rwpf *output; - struct vsp1_entity *bru; + struct vsp1_entity *brx; struct vsp1_entity *hgo; struct vsp1_entity *hgt; struct vsp1_entity *lif; @@ -143,7 +140,8 @@ struct vsp1_pipeline { */ struct list_head entities; - struct vsp1_dl_list *dl; + struct vsp1_dl_body *stream_config; + bool configured; unsigned int partitions; struct vsp1_partition *partition; @@ -161,16 +159,14 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, unsigned int alpha); + struct vsp1_dl_body *dlb, + unsigned int alpha); void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe, struct vsp1_partition *partition, unsigned int index, struct vsp1_partition_window *window); -void vsp1_pipelines_suspend(struct vsp1_device *vsp1); -void vsp1_pipelines_resume(struct vsp1_device *vsp1); - const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1, u32 fourcc); diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index dae0c1901297..0d249ff9f564 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * vsp1_regs.h -- R-Car VSP1 Registers Definitions * * Copyright (C) 2013 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. */ #ifndef __VSP1_REGS_H__ @@ -311,6 +308,44 @@ #define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0) /* ----------------------------------------------------------------------------- + * UIF Control Registers + */ + +#define VI6_UIF_OFFSET 0x100 + +#define VI6_UIF_DISCOM_DOCMCR 0x1c00 +#define VI6_UIF_DISCOM_DOCMCR_CMPRU (1 << 16) +#define VI6_UIF_DISCOM_DOCMCR_CMPR (1 << 0) + +#define VI6_UIF_DISCOM_DOCMSTR 0x1c04 +#define VI6_UIF_DISCOM_DOCMSTR_CMPPRE (1 << 1) +#define VI6_UIF_DISCOM_DOCMSTR_CMPST (1 << 0) + +#define VI6_UIF_DISCOM_DOCMCLSTR 0x1c08 +#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLPRE (1 << 1) +#define VI6_UIF_DISCOM_DOCMCLSTR_CMPCLST (1 << 0) + +#define VI6_UIF_DISCOM_DOCMIENR 0x1c0c +#define VI6_UIF_DISCOM_DOCMIENR_CMPPREIEN (1 << 1) +#define VI6_UIF_DISCOM_DOCMIENR_CMPIEN (1 << 0) + +#define VI6_UIF_DISCOM_DOCMMDR 0x1c10 +#define VI6_UIF_DISCOM_DOCMMDR_INTHRH(n) ((n) << 16) + +#define VI6_UIF_DISCOM_DOCMPMR 0x1c14 +#define VI6_UIF_DISCOM_DOCMPMR_CMPDFF(n) ((n) << 17) +#define VI6_UIF_DISCOM_DOCMPMR_CMPDFA(n) ((n) << 8) +#define VI6_UIF_DISCOM_DOCMPMR_CMPDAUF (1 << 7) +#define VI6_UIF_DISCOM_DOCMPMR_SEL(n) ((n) << 0) + +#define VI6_UIF_DISCOM_DOCMECRCR 0x1c18 +#define VI6_UIF_DISCOM_DOCMCCRCR 0x1c1c +#define VI6_UIF_DISCOM_DOCMSPXR 0x1c20 +#define VI6_UIF_DISCOM_DOCMSPYR 0x1c24 +#define VI6_UIF_DISCOM_DOCMSZXR 0x1c28 +#define VI6_UIF_DISCOM_DOCMSZYR 0x1c2c + +/* ----------------------------------------------------------------------------- * DPR Control Registers */ @@ -342,7 +377,10 @@ #define VI6_DPR_SMPPT_PT_MASK (0x3f << 0) #define VI6_DPR_SMPPT_PT_SHIFT 0 +#define VI6_DPR_UIF_ROUTE(n) (0x2074 + (n) * 4) + #define VI6_DPR_NODE_RPF(n) (n) +#define VI6_DPR_NODE_UIF(n) (12 + (n)) #define VI6_DPR_NODE_SRU 16 #define VI6_DPR_NODE_UDS(n) (17 + (n)) #define VI6_DPR_NODE_LUT 22 diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index fe0633da5a5f..69e5fe6e6b50 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -29,9 +25,10 @@ */ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + rpf->entity.index * VI6_RPF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + rpf->entity.index * VI6_RPF_OFFSET, + data); } /* ----------------------------------------------------------------------------- @@ -46,10 +43,9 @@ static const struct v4l2_subdev_ops rpf_ops = { * VSP1 Entity Operations */ -static void rpf_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void rpf_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); const struct vsp1_format_info *fmtinfo = rpf->fmtinfo; @@ -61,80 +57,6 @@ static void rpf_configure(struct vsp1_entity *entity, u32 pstride; u32 infmt; - if (params == VSP1_ENTITY_PARAMS_RUNTIME) { - vsp1_rpf_write(rpf, dl, VI6_RPF_VRTCOL_SET, - rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); - vsp1_rpf_write(rpf, dl, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | - (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); - - vsp1_pipeline_propagate_alpha(pipe, dl, rpf->alpha); - return; - } - - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - struct vsp1_device *vsp1 = rpf->entity.vsp1; - struct vsp1_rwpf_memory mem = rpf->mem; - struct v4l2_rect crop; - - /* - * Source size and crop offsets. - * - * The crop offsets correspond to the location of the crop - * rectangle top left corner in the plane buffer. Only two - * offsets are needed, as planes 2 and 3 always have identical - * strides. - */ - crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); - - /* - * 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; - } - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_BSIZE, - (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | - (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRC_ESIZE, - (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | - (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); - - mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline - + crop.left * fmtinfo->bpp[0] / 8; - - if (format->num_planes > 1) { - unsigned int offset; - - offset = crop.top * format->plane_fmt[1].bytesperline - + crop.left / fmtinfo->hsub - * fmtinfo->bpp[1] / 8; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - - /* - * On Gen3 hardware the SPUVS bit has no effect on 3-planar - * formats. Swap the U and V planes manually in that case. - */ - if (vsp1->info->gen == 3 && format->num_planes == 3 && - fmtinfo->swap_uv) - swap(mem.addr[1], mem.addr[2]); - - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); - return; - } - /* Stride */ pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; @@ -142,7 +64,7 @@ static void rpf_configure(struct vsp1_entity *entity, pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; - vsp1_rpf_write(rpf, dl, VI6_RPF_SRCM_PSTRIDE, pstride); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride); /* Format */ sink_format = vsp1_entity_get_pad_format(&rpf->entity, @@ -163,22 +85,22 @@ static void rpf_configure(struct vsp1_entity *entity, if (sink_format->code != source_format->code) infmt |= VI6_RPF_INFMT_CSC; - vsp1_rpf_write(rpf, dl, VI6_RPF_INFMT, infmt); - vsp1_rpf_write(rpf, dl, VI6_RPF_DSWAP, fmtinfo->swap); + vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt); + vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap); /* Output location */ - if (pipe->bru) { + if (pipe->brx) { const struct v4l2_rect *compose; - compose = vsp1_entity_get_pad_selection(pipe->bru, - pipe->bru->config, - rpf->bru_input, + compose = vsp1_entity_get_pad_selection(pipe->brx, + pipe->brx->config, + rpf->brx_input, V4L2_SEL_TGT_COMPOSE); left = compose->left; top = compose->top; } - vsp1_rpf_write(rpf, dl, VI6_RPF_LOC, + vsp1_rpf_write(rpf, dlb, VI6_RPF_LOC, (left << VI6_RPF_LOC_HCOORD_SHIFT) | (top << VI6_RPF_LOC_VCOORD_SHIFT)); @@ -191,10 +113,10 @@ static void rpf_configure(struct vsp1_entity *entity, * alpha channel by a fixed global alpha value, and multiply the pixel * components to convert the input to premultiplied alpha. * - * As alpha premultiplication is available in the BRU for both Gen2 and + * As alpha premultiplication is available in the BRx for both Gen2 and * Gen3 we handle it there and use the Gen3 alpha multiplier for global * alpha multiplication only. This however prevents conversion to - * premultiplied alpha if no BRU is present in the pipeline. If that use + * premultiplied alpha if no BRx is present in the pipeline. If that use * case turns out to be useful we will revisit the implementation (for * Gen3 only). * @@ -205,7 +127,7 @@ static void rpf_configure(struct vsp1_entity *entity, * * In all cases, disable color keying. */ - vsp1_rpf_write(rpf, dl, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | + vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT | (fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED : VI6_RPF_ALPH_SEL_ASEL_FIXED)); @@ -242,9 +164,94 @@ static void rpf_configure(struct vsp1_entity *entity, rpf->mult_alpha = mult; } - vsp1_rpf_write(rpf, dl, VI6_RPF_MSK_CTRL, 0); - vsp1_rpf_write(rpf, dl, VI6_RPF_CKEY_CTRL, 0); + vsp1_rpf_write(rpf, dlb, VI6_RPF_MSK_CTRL, 0); + vsp1_rpf_write(rpf, dlb, VI6_RPF_CKEY_CTRL, 0); + +} + +static void rpf_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + + vsp1_rpf_write(rpf, dlb, VI6_RPF_VRTCOL_SET, + rpf->alpha << VI6_RPF_VRTCOL_SET_LAYA_SHIFT); + vsp1_rpf_write(rpf, dlb, VI6_RPF_MULT_ALPHA, rpf->mult_alpha | + (rpf->alpha << VI6_RPF_MULT_ALPHA_RATIO_SHIFT)); + + vsp1_pipeline_propagate_alpha(pipe, dlb, rpf->alpha); +} + +static void rpf_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev); + struct vsp1_rwpf_memory mem = rpf->mem; + 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; + + /* + * Source size and crop offsets. + * + * The crop offsets correspond to the location of the crop + * rectangle top left corner in the plane buffer. Only two + * offsets are needed, as planes 2 and 3 always have identical + * strides. + */ + crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.config); + + /* + * 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; + } + + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_BSIZE, + (crop.width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT)); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRC_ESIZE, + (crop.width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) | + (crop.height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT)); + + mem.addr[0] += crop.top * format->plane_fmt[0].bytesperline + + crop.left * fmtinfo->bpp[0] / 8; + + if (format->num_planes > 1) { + unsigned int offset; + + offset = crop.top * format->plane_fmt[1].bytesperline + + crop.left / fmtinfo->hsub + * fmtinfo->bpp[1] / 8; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + + /* + * On Gen3 hardware the SPUVS bit has no effect on 3-planar + * formats. Swap the U and V planes manually in that case. + */ + if (vsp1->info->gen == 3 && format->num_planes == 3 && + fmtinfo->swap_uv) + swap(mem.addr[1], mem.addr[2]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_Y, mem.addr[0]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C0, mem.addr[1]); + vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_ADDR_C1, mem.addr[2]); } static void rpf_partition(struct vsp1_entity *entity, @@ -257,7 +264,9 @@ static void rpf_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations rpf_entity_ops = { - .configure = rpf_configure, + .configure_stream = rpf_configure_stream, + .configure_frame = rpf_configure_frame, + .configure_partition = rpf_configure_partition, .partition = rpf_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c index cfd8f1904fa6..049bdd958e56 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.c +++ b/drivers/media/platform/vsp1/vsp1_rwpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <media/v4l2-subdev.h> diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 58215a7ab631..70742ecf766f 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_RWPF_H__ #define __VSP1_RWPF_H__ @@ -27,7 +23,6 @@ struct v4l2_ctrl; struct vsp1_dl_manager; -struct vsp1_pipeline; struct vsp1_rwpf; struct vsp1_video; @@ -39,7 +34,6 @@ struct vsp1_rwpf { struct vsp1_entity entity; struct v4l2_ctrl_handler ctrls; - struct vsp1_pipeline *pipe; struct vsp1_video *video; unsigned int max_width; @@ -47,7 +41,7 @@ struct vsp1_rwpf { struct v4l2_pix_format_mplane format; const struct vsp1_format_info *fmtinfo; - unsigned int bru_input; + unsigned int brx_input; unsigned int alpha; diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/vsp1/vsp1_sru.c index 51e5691187c3..04e4e05af6ae 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.c +++ b/drivers/media/platform/vsp1/vsp1_sru.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -28,10 +24,10 @@ * Device Access */ -static inline void vsp1_sru_write(struct vsp1_sru *sru, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_sru_write(struct vsp1_sru *sru, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg, data); + vsp1_dl_body_write(dlb, reg, data); } /* ----------------------------------------------------------------------------- @@ -271,10 +267,9 @@ static const struct v4l2_subdev_ops sru_ops = { * VSP1 Entity Operations */ -static void sru_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void sru_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { const struct vsp1_sru_param *param; struct vsp1_sru *sru = to_sru(&entity->subdev); @@ -282,9 +277,6 @@ static void sru_configure(struct vsp1_entity *entity, struct v4l2_mbus_framefmt *output; u32 ctrl0; - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, SRU_PAD_SINK); output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.config, @@ -303,9 +295,9 @@ static void sru_configure(struct vsp1_entity *entity, ctrl0 |= param->ctrl0; - vsp1_sru_write(sru, dl, VI6_SRU_CTRL0, ctrl0); - vsp1_sru_write(sru, dl, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); - vsp1_sru_write(sru, dl, VI6_SRU_CTRL2, param->ctrl2); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); + vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2); } static unsigned int sru_max_width(struct vsp1_entity *entity, @@ -351,7 +343,7 @@ static void sru_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations sru_entity_ops = { - .configure = sru_configure, + .configure_stream = sru_configure_stream, .max_width = sru_max_width, .partition = sru_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/vsp1/vsp1_sru.h index 85e241457af2..ddb00eadd1ea 100644 --- a/drivers/media/platform/vsp1/vsp1_sru.h +++ b/drivers/media/platform/vsp1/vsp1_sru.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_sru.h -- R-Car VSP1 Super Resolution Unit * * Copyright (C) 2013 Renesas Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_SRU_H__ #define __VSP1_SRU_H__ diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c index 72f72a9d2152..c20c84b54936 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.c +++ b/drivers/media/platform/vsp1/vsp1_uds.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -31,22 +27,22 @@ * Device Access */ -static inline void vsp1_uds_write(struct vsp1_uds *uds, struct vsp1_dl_list *dl, - u32 reg, u32 data) +static inline void vsp1_uds_write(struct vsp1_uds *uds, + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + uds->entity.index * VI6_UDS_OFFSET, data); + vsp1_dl_body_write(dlb, reg + uds->entity.index * VI6_UDS_OFFSET, data); } /* ----------------------------------------------------------------------------- * Scaling Computation */ -void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *entity, struct vsp1_dl_body *dlb, unsigned int alpha) { struct vsp1_uds *uds = to_uds(&entity->subdev); - vsp1_uds_write(uds, dl, VI6_UDS_ALPVAL, + vsp1_uds_write(uds, dlb, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT); } @@ -259,10 +255,9 @@ static const struct v4l2_subdev_ops uds_ops = { * VSP1 Entity Operations */ -static void uds_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void uds_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_uds *uds = to_uds(&entity->subdev); const struct v4l2_mbus_framefmt *output; @@ -276,27 +271,6 @@ static void uds_configure(struct vsp1_entity *entity, output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.config, UDS_PAD_SOURCE); - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - struct vsp1_partition *partition = pipe->partition; - - /* Input size clipping */ - vsp1_uds_write(uds, dl, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | - (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) | - (partition->uds_sink.width - << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT)); - - /* Output size clipping */ - vsp1_uds_write(uds, dl, VI6_UDS_CLIP_SIZE, - (partition->uds_source.width - << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | - (output->height - << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); - return; - } - - if (params != VSP1_ENTITY_PARAMS_INIT) - return; - hscale = uds_compute_ratio(input->width, output->width); vscale = uds_compute_ratio(input->height, output->height); @@ -312,22 +286,48 @@ static void uds_configure(struct vsp1_entity *entity, else multitap = true; - vsp1_uds_write(uds, dl, VI6_UDS_CTRL, + vsp1_uds_write(uds, dlb, VI6_UDS_CTRL, (uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) | (multitap ? VI6_UDS_CTRL_BC : 0)); - vsp1_uds_write(uds, dl, VI6_UDS_PASS_BWIDTH, + vsp1_uds_write(uds, dlb, VI6_UDS_PASS_BWIDTH, (uds_passband_width(hscale) << VI6_UDS_PASS_BWIDTH_H_SHIFT) | (uds_passband_width(vscale) << VI6_UDS_PASS_BWIDTH_V_SHIFT)); /* Set the scaling ratios. */ - vsp1_uds_write(uds, dl, VI6_UDS_SCALE, + vsp1_uds_write(uds, dlb, VI6_UDS_SCALE, (hscale << VI6_UDS_SCALE_HFRAC_SHIFT) | (vscale << VI6_UDS_SCALE_VFRAC_SHIFT)); } +static void uds_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + 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.config, + UDS_PAD_SOURCE); + + /* Input size clipping */ + vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN | + (0 << VI6_UDS_HSZCLIP_HCL_OFST_SHIFT) | + (partition->uds_sink.width + << VI6_UDS_HSZCLIP_HCL_SIZE_SHIFT)); + + /* Output size clipping */ + vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE, + (partition->uds_source.width + << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) | + (output->height + << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT)); +} + static unsigned int uds_max_width(struct vsp1_entity *entity, struct vsp1_pipeline *pipe) { @@ -384,7 +384,8 @@ static void uds_partition(struct vsp1_entity *entity, } static const struct vsp1_entity_operations uds_entity_ops = { - .configure = uds_configure, + .configure_stream = uds_configure_stream, + .configure_partition = uds_configure_partition, .max_width = uds_max_width, .partition = uds_partition, }; diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h index 7bf3cdcffc65..c34f95a666d2 100644 --- a/drivers/media/platform/vsp1/vsp1_uds.h +++ b/drivers/media/platform/vsp1/vsp1_uds.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_UDS_H__ #define __VSP1_UDS_H__ @@ -35,7 +31,7 @@ static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev) struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index); -void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_list *dl, +void vsp1_uds_set_alpha(struct vsp1_entity *uds, struct vsp1_dl_body *dlb, unsigned int alpha); #endif /* __VSP1_UDS_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/vsp1/vsp1_uif.c new file mode 100644 index 000000000000..4b58d51df231 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uif.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * vsp1_uif.c -- R-Car VSP1 User Logic Interface + * + * Copyright (C) 2017-2018 Laurent Pinchart + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#include <linux/device.h> +#include <linux/gfp.h> +#include <linux/sys_soc.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_entity.h" +#include "vsp1_uif.h" + +#define UIF_MIN_SIZE 4U +#define UIF_MAX_SIZE 8190U + +/* ----------------------------------------------------------------------------- + * Device Access + */ + +static inline u32 vsp1_uif_read(struct vsp1_uif *uif, u32 reg) +{ + return vsp1_read(uif->entity.vsp1, + uif->entity.index * VI6_UIF_OFFSET + reg); +} + +static inline void vsp1_uif_write(struct vsp1_uif *uif, + struct vsp1_dl_body *dlb, u32 reg, u32 data) +{ + vsp1_dl_body_write(dlb, reg + uif->entity.index * VI6_UIF_OFFSET, data); +} + +u32 vsp1_uif_get_crc(struct vsp1_uif *uif) +{ + return vsp1_uif_read(uif, VI6_UIF_DISCOM_DOCMCCRCR); +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Pad Operations + */ + +static const unsigned int uif_codes[] = { + MEDIA_BUS_FMT_ARGB8888_1X32, + MEDIA_BUS_FMT_AHSV8888_1X32, + MEDIA_BUS_FMT_AYUV8_1X32, +}; + +static int uif_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + return vsp1_subdev_enum_mbus_code(subdev, cfg, code, uif_codes, + ARRAY_SIZE(uif_codes)); +} + +static int uif_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + return vsp1_subdev_enum_frame_size(subdev, cfg, fse, UIF_MIN_SIZE, + UIF_MIN_SIZE, UIF_MAX_SIZE, + UIF_MAX_SIZE); +} + +static int uif_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + return vsp1_subdev_set_pad_format(subdev, cfg, fmt, uif_codes, + ARRAY_SIZE(uif_codes), + UIF_MIN_SIZE, UIF_MIN_SIZE, + UIF_MAX_SIZE, UIF_MAX_SIZE); +} + +static int uif_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_uif *uif = to_uif(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + int ret = 0; + + if (sel->pad != UIF_PAD_SINK) + return -EINVAL; + + mutex_lock(&uif->entity.lock); + + config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + format = vsp1_entity_get_pad_format(&uif->entity, config, + UIF_PAD_SINK); + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; + break; + + case V4L2_SEL_TGT_CROP: + sel->r = *vsp1_entity_get_pad_selection(&uif->entity, config, + sel->pad, sel->target); + break; + + default: + ret = -EINVAL; + break; + } + +done: + mutex_unlock(&uif->entity.lock); + return ret; +} + +static int uif_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct vsp1_uif *uif = to_uif(subdev); + struct v4l2_subdev_pad_config *config; + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *selection; + int ret = 0; + + if (sel->pad != UIF_PAD_SINK || + sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + mutex_lock(&uif->entity.lock); + + config = vsp1_entity_get_pad_config(&uif->entity, cfg, sel->which); + if (!config) { + ret = -EINVAL; + goto done; + } + + /* The crop rectangle must be inside the input frame. */ + format = vsp1_entity_get_pad_format(&uif->entity, config, 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); + sel->r.width = clamp_t(unsigned int, sel->r.width, UIF_MIN_SIZE, + format->width - sel->r.left); + sel->r.height = clamp_t(unsigned int, sel->r.height, UIF_MIN_SIZE, + format->height - sel->r.top); + + /* Store the crop rectangle. */ + selection = vsp1_entity_get_pad_selection(&uif->entity, config, + sel->pad, V4L2_SEL_TGT_CROP); + *selection = sel->r; + +done: + mutex_unlock(&uif->entity.lock); + return ret; +} + +/* ----------------------------------------------------------------------------- + * V4L2 Subdevice Operations + */ + +static const struct v4l2_subdev_pad_ops uif_pad_ops = { + .init_cfg = vsp1_entity_init_cfg, + .enum_mbus_code = uif_enum_mbus_code, + .enum_frame_size = uif_enum_frame_size, + .get_fmt = vsp1_subdev_get_pad_format, + .set_fmt = uif_set_format, + .get_selection = uif_get_selection, + .set_selection = uif_set_selection, +}; + +static const struct v4l2_subdev_ops uif_ops = { + .pad = &uif_pad_ops, +}; + +/* ----------------------------------------------------------------------------- + * VSP1 Entity Operations + */ + +static void uif_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) +{ + struct vsp1_uif *uif = to_uif(&entity->subdev); + const struct v4l2_rect *crop; + unsigned int left; + unsigned int width; + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR, + VI6_UIF_DISCOM_DOCMPMR_SEL(9)); + + crop = vsp1_entity_get_pad_selection(entity, entity->config, + UIF_PAD_SINK, V4L2_SEL_TGT_CROP); + + left = crop->left; + width = crop->width; + + /* On M3-W the horizontal coordinates are twice the register value. */ + if (uif->m3w_quirk) { + left /= 2; + width /= 2; + } + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPXR, left); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSPYR, crop->top); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZXR, width); + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMSZYR, crop->height); + + vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMCR, + VI6_UIF_DISCOM_DOCMCR_CMPR); +} + +static const struct vsp1_entity_operations uif_entity_ops = { + .configure_stream = uif_configure_stream, +}; + +/* ----------------------------------------------------------------------------- + * Initialization and Cleanup + */ + +static const struct soc_device_attribute vsp1_r8a7796[] = { + { .soc_id = "r8a7796" }, + { /* sentinel */ } +}; + +struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index) +{ + struct vsp1_uif *uif; + char name[6]; + int ret; + + uif = devm_kzalloc(vsp1->dev, sizeof(*uif), GFP_KERNEL); + if (!uif) + return ERR_PTR(-ENOMEM); + + if (soc_device_match(vsp1_r8a7796)) + uif->m3w_quirk = true; + + uif->entity.ops = &uif_entity_ops; + uif->entity.type = VSP1_ENTITY_UIF; + uif->entity.index = index; + + /* The datasheet names the two UIF instances UIF4 and UIF5. */ + sprintf(name, "uif.%u", index + 4); + ret = vsp1_entity_init(vsp1, &uif->entity, name, 2, &uif_ops, + MEDIA_ENT_F_PROC_VIDEO_STATISTICS); + if (ret < 0) + return ERR_PTR(ret); + + return uif; +} diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/vsp1/vsp1_uif.h new file mode 100644 index 000000000000..c71ab5f6a6f8 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_uif.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * vsp1_uif.h -- R-Car VSP1 User Logic Interface + * + * Copyright (C) 2017-2018 Laurent Pinchart + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ +#ifndef __VSP1_UIF_H__ +#define __VSP1_UIF_H__ + +#include "vsp1_entity.h" + +struct vsp1_device; + +#define UIF_PAD_SINK 0 +#define UIF_PAD_SOURCE 1 + +struct vsp1_uif { + struct vsp1_entity entity; + bool m3w_quirk; +}; + +static inline struct vsp1_uif *to_uif(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct vsp1_uif, entity.subdev); +} + +struct vsp1_uif *vsp1_uif_create(struct vsp1_device *vsp1, unsigned int index); +u32 vsp1_uif_get_crc(struct vsp1_uif *uif); + +#endif /* __VSP1_UIF_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c2d3b8f0f487..81d47a09d7bc 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_video.c -- R-Car VSP1 Video Node * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/list.h> @@ -28,7 +24,7 @@ #include <media/videobuf2-dma-contig.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_hgo.h" @@ -324,7 +320,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) static struct vsp1_vb2_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; unsigned long flags; @@ -382,69 +378,71 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int 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) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_partition(entity, pipe, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; + struct vsp1_dl_body *dlb; + struct vsp1_dl_list *dl; unsigned int partition; - if (!pipe->dl) - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + dl = vsp1_dl_list_get(pipe->output->dlm); /* - * Start with the runtime parameters as the configure operation can - * compute/cache information needed when configuring partitions. This - * is the case with flipping in the WPF. + * If the VSP hardware isn't configured yet (which occurs either when + * processing the first frame or after a system suspend/resume), add the + * cached stream configuration to the display list to perform a full + * initialisation. */ - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_RUNTIME); - } + if (!pipe->configured) + vsp1_dl_list_add_body(dl, pipe->stream_config); + + dlb = vsp1_dl_list_get_body0(dl); - /* Run the first partition */ - vsp1_video_pipeline_run_partition(pipe, pipe->dl, 0); + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_frame(entity, pipe, dl, dlb); - /* Process consecutive partitions as necessary */ + /* Run the first partition. */ + vsp1_video_pipeline_run_partition(pipe, dl, 0); + + /* Process consecutive partitions as necessary. */ for (partition = 1; partition < pipe->partitions; ++partition) { - struct vsp1_dl_list *dl; + struct vsp1_dl_list *dl_next; - dl = vsp1_dl_list_get(pipe->output->dlm); + dl_next = vsp1_dl_list_get(pipe->output->dlm); /* * An incomplete chain will still function, but output only * the partitions that had a dl available. The frame end * interrupt will be marked on the last dl in the chain. */ - if (!dl) { + if (!dl_next) { dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); break; } - vsp1_video_pipeline_run_partition(pipe, dl, partition); - vsp1_dl_list_add_chain(pipe->dl, dl); + vsp1_video_pipeline_run_partition(pipe, dl_next, partition); + vsp1_dl_list_add_chain(dl, dl_next); } /* Complete, and commit the head display list. */ - vsp1_dl_list_commit(pipe->dl); - pipe->dl = NULL; + vsp1_dl_list_commit(dl, false); + pipe->configured = true; vsp1_pipeline_run(pipe); } static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, - bool completed) + unsigned int completion) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; enum vsp1_pipeline_state state; @@ -452,7 +450,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, unsigned int i; /* M2M Pipelines should never call here with an incomplete frame. */ - WARN_ON_ONCE(!completed); + WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED)); spin_lock_irqsave(&pipe->irqlock, flags); @@ -488,7 +486,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, struct media_entity_enum ent_enum; struct vsp1_entity *entity; struct media_pad *pad; - struct vsp1_bru *bru = NULL; + struct vsp1_brx *brx = NULL; int ret; ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev); @@ -524,14 +522,14 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, if (entity->type == VSP1_ENTITY_BRU || entity->type == VSP1_ENTITY_BRS) { /* BRU and BRS can't be chained. */ - if (bru) { + if (brx) { ret = -EPIPE; goto out; } - bru = to_bru(&entity->subdev); - bru->inputs[pad->index].rpf = input; - input->bru_input = pad->index; + brx = to_brx(&entity->subdev); + brx->inputs[pad->index].rpf = input; + input->brx_input = pad->index; } /* We've reached the WPF, we're done. */ @@ -553,7 +551,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, } pipe->uds = entity; - pipe->uds_input = bru ? &bru->entity : &input->entity; + pipe->uds_input = brx ? &brx->entity : &input->entity; } /* Follow the source link, ignoring any HGO or HGT. */ @@ -598,20 +596,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, subdev = media_entity_to_v4l2_subdev(entity); e = to_vsp1_entity(subdev); list_add_tail(&e->list_pipe, &pipe->entities); + e->pipe = pipe; switch (e->type) { case VSP1_ENTITY_RPF: rwpf = to_rwpf(subdev); pipe->inputs[rwpf->entity.index] = rwpf; rwpf->video->pipe_index = ++pipe->num_inputs; - rwpf->pipe = pipe; break; case VSP1_ENTITY_WPF: rwpf = to_rwpf(subdev); pipe->output = rwpf; rwpf->video->pipe_index = 0; - rwpf->pipe = pipe; break; case VSP1_ENTITY_LIF: @@ -620,17 +617,15 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, case VSP1_ENTITY_BRU: case VSP1_ENTITY_BRS: - pipe->bru = e; + pipe->brx = e; break; case VSP1_ENTITY_HGO: pipe->hgo = e; - to_hgo(subdev)->histo.pipe = pipe; break; case VSP1_ENTITY_HGT: pipe->hgt = e; - to_hgt(subdev)->histo.pipe = pipe; break; default: @@ -682,7 +677,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) * Otherwise allocate a new pipeline and initialize it, it will be freed * when the last reference is released. */ - if (!video->rwpf->pipe) { + if (!video->rwpf->entity.pipe) { pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); if (!pipe) return ERR_PTR(-ENOMEM); @@ -694,7 +689,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) return ERR_PTR(ret); } } else { - pipe = video->rwpf->pipe; + pipe = video->rwpf->entity.pipe; kref_get(&pipe->kref); } @@ -777,7 +772,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); unsigned long flags; bool empty; @@ -812,11 +807,6 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) if (ret < 0) return ret; - /* Prepare the display list. */ - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); - if (!pipe->dl) - return -ENOMEM; - if (pipe->uds) { struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); @@ -838,20 +828,25 @@ 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->dl); + /* + * Compute and cache the stream configuration into a body. The cached + * body will be added to the display list by vsp1_video_pipeline_run() + * whenever the pipeline needs to be fully reconfigured. + */ + pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm); + if (!pipe->stream_config) + return -ENOMEM; - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_INIT); + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity, pipe, pipe->stream_config); + vsp1_entity_configure_stream(entity, pipe, pipe->stream_config); } return 0; } -static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +static void vsp1_video_release_buffers(struct vsp1_video *video) { - struct vsp1_video *video = pipe->output->video; struct vsp1_vb2_buffer *buffer; unsigned long flags; @@ -861,18 +856,26 @@ static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); +} + +static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +{ + lockdep_assert_held(&pipe->lock); + + /* Release any cached configuration from our output video. */ + vsp1_dl_body_put(pipe->stream_config); + pipe->stream_config = NULL; + pipe->configured = false; /* Release our partition table allocation */ - mutex_lock(&pipe->lock); kfree(pipe->part_table); pipe->part_table = NULL; - mutex_unlock(&pipe->lock); } static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; unsigned long flags; int ret; @@ -881,8 +884,9 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (pipe->stream_count == pipe->num_inputs) { ret = vsp1_video_setup_pipeline(pipe); if (ret < 0) { - mutex_unlock(&pipe->lock); + vsp1_video_release_buffers(video); vsp1_video_cleanup_pipeline(pipe); + mutex_unlock(&pipe->lock); return ret; } @@ -913,7 +917,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; unsigned long flags; int ret; @@ -932,13 +936,12 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) if (ret == -ETIMEDOUT) dev_err(video->vsp1->dev, "pipeline stop timeout\n"); - vsp1_dl_list_put(pipe->dl); - pipe->dl = NULL; + vsp1_video_cleanup_pipeline(pipe); } mutex_unlock(&pipe->lock); media_pipeline_stop(&video->video.entity); - vsp1_video_cleanup_pipeline(pipe); + vsp1_video_release_buffers(video); vsp1_video_pipeline_put(pipe); } @@ -1173,6 +1176,87 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Suspend and Resume + */ + +void vsp1_video_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* + * To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and + * then to wait for the stop to complete. + */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_video_resume(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + + /* Resume all running pipelines. */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + /* + * The hardware may have been reset during a suspend and will + * need a full reconfiguration. + */ + pipe->configured = false; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); + } +} + +/* ----------------------------------------------------------------------------- * Initialization and Cleanup */ diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h index 50ea7f02205f..f3cf5e2fdf5a 100644 --- a/drivers/media/platform/vsp1/vsp1_video.h +++ b/drivers/media/platform/vsp1/vsp1_video.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * vsp1_video.h -- R-Car VSP1 Video Node * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #ifndef __VSP1_VIDEO_H__ #define __VSP1_VIDEO_H__ @@ -55,6 +51,9 @@ static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev) return container_of(vdev, struct vsp1_video, video); } +void vsp1_video_suspend(struct vsp1_device *vsp1); +void vsp1_video_resume(struct vsp1_device *vsp1); + struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, struct vsp1_rwpf *rwpf); void vsp1_video_cleanup(struct vsp1_video *video); diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 8bd6b2f1af15..23c8f706b3f2 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter * * Copyright (C) 2013-2014 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/device.h> @@ -31,9 +27,9 @@ */ static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, - struct vsp1_dl_list *dl, u32 reg, u32 data) + struct vsp1_dl_body *dlb, u32 reg, u32 data) { - vsp1_dl_list_write(dl, reg + wpf->entity.index * VI6_WPF_OFFSET, data); + vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data); } /* ----------------------------------------------------------------------------- @@ -236,10 +232,9 @@ static void vsp1_wpf_destroy(struct vsp1_entity *entity) vsp1_dlm_destroy(wpf->dlm); } -static void wpf_configure(struct vsp1_entity *entity, - struct vsp1_pipeline *pipe, - struct vsp1_dl_list *dl, - enum vsp1_entity_params params) +static void wpf_configure_stream(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_body *dlb) { struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); struct vsp1_device *vsp1 = wpf->entity.vsp1; @@ -249,149 +244,12 @@ static void wpf_configure(struct vsp1_entity *entity, u32 outfmt = 0; u32 srcrpf = 0; - if (params == VSP1_ENTITY_PARAMS_RUNTIME) { - const unsigned int mask = BIT(WPF_CTRL_VFLIP) - | BIT(WPF_CTRL_HFLIP); - unsigned long flags; - - spin_lock_irqsave(&wpf->flip.lock, flags); - wpf->flip.active = (wpf->flip.active & ~mask) - | (wpf->flip.pending & mask); - spin_unlock_irqrestore(&wpf->flip.lock, flags); - - outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; - - if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) - outfmt |= VI6_WPF_OUTFMT_FLP; - if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) - outfmt |= VI6_WPF_OUTFMT_HFLP; - - vsp1_wpf_write(wpf, dl, VI6_WPF_OUTFMT, outfmt); - return; - } - sink_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SINK); source_format = vsp1_entity_get_pad_format(&wpf->entity, wpf->entity.config, RWPF_PAD_SOURCE); - - if (params == VSP1_ENTITY_PARAMS_PARTITION) { - const struct v4l2_pix_format_mplane *format = &wpf->format; - const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; - struct vsp1_rwpf_memory mem = wpf->mem; - unsigned int flip = wpf->flip.active; - unsigned int width = sink_format->width; - unsigned int height = sink_format->height; - unsigned int offset; - - /* - * Cropping. The partition algorithm can split the image into - * multiple slices. - */ - if (pipe->partitions > 1) - width = pipe->partition->wpf.width; - - vsp1_wpf_write(wpf, dl, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | - (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | - (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); - vsp1_wpf_write(wpf, dl, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | - (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | - (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); - - if (pipe->lif) - return; - - /* - * Update the memory offsets based on flipping configuration. - * The destination addresses point to the locations where the - * VSP starts writing to memory, which can be any corner of the - * image depending on the combination of flipping and rotation. - */ - - /* - * First take the partition left coordinate into account. - * Compute the offset to order the partitions correctly on the - * output based on whether flipping is enabled. Consider - * horizontal flipping when rotation is disabled but vertical - * flipping when rotation is enabled, as rotating the image - * switches the horizontal and vertical directions. The offset - * is applied horizontally or vertically accordingly. - */ - if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) - offset = format->width - pipe->partition->wpf.left - - pipe->partition->wpf.width; - else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) - offset = format->height - pipe->partition->wpf.left - - pipe->partition->wpf.width; - else - offset = pipe->partition->wpf.left; - - for (i = 0; i < format->num_planes; ++i) { - unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; - unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; - - if (wpf->flip.rotate) - mem.addr[i] += offset / vsub - * format->plane_fmt[i].bytesperline; - else - mem.addr[i] += offset / hsub - * fmtinfo->bpp[i] / 8; - } - - if (flip & BIT(WPF_CTRL_VFLIP)) { - /* - * When rotating the output (after rotation) image - * height is equal to the partition width (before - * rotation). Otherwise it is equal to the output - * image height. - */ - if (wpf->flip.rotate) - height = pipe->partition->wpf.width; - else - height = format->height; - - mem.addr[0] += (height - 1) - * format->plane_fmt[0].bytesperline; - - if (format->num_planes > 1) { - offset = (height / fmtinfo->vsub - 1) - * format->plane_fmt[1].bytesperline; - mem.addr[1] += offset; - mem.addr[2] += offset; - } - } - - if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { - unsigned int hoffset = max(0, (int)format->width - 16); - - /* - * Compute the output coordinate. The partition - * horizontal (left) offset becomes a vertical offset. - */ - for (i = 0; i < format->num_planes; ++i) { - unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; - - mem.addr[i] += hoffset / hsub - * fmtinfo->bpp[i] / 8; - } - } - - /* - * On Gen3 hardware the SPUVS bit has no effect on 3-planar - * formats. Swap the U and V planes manually in that case. - */ - if (vsp1->info->gen == 3 && format->num_planes == 3 && - fmtinfo->swap_uv) - swap(mem.addr[1], mem.addr[2]); - - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); - return; - } - /* Format */ if (!pipe->lif) { const struct v4l2_pix_format_mplane *format = &wpf->format; @@ -410,17 +268,17 @@ static void wpf_configure(struct vsp1_entity *entity, outfmt |= VI6_WPF_OUTFMT_SPUVS; /* Destination stride and byte swapping. */ - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_Y, + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y, format->plane_fmt[0].bytesperline); if (format->num_planes > 1) - vsp1_wpf_write(wpf, dl, VI6_WPF_DSTM_STRIDE_C, + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C, format->plane_fmt[1].bytesperline); - vsp1_wpf_write(wpf, dl, VI6_WPF_DSWAP, fmtinfo->swap); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); if (vsp1->info->features & VSP1_HAS_WPF_HFLIP && wpf->entity.index == 0) - vsp1_wpf_write(wpf, dl, VI6_WPF_ROT_CTRL, + vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, VI6_WPF_ROT_CTRL_LN16 | (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); } @@ -430,13 +288,13 @@ static void wpf_configure(struct vsp1_entity *entity, wpf->outfmt = outfmt; - vsp1_dl_list_write(dl, VI6_DPR_WPF_FPORCH(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(wpf->entity.index), VI6_DPR_WPF_FPORCH_FP_WPFN); - vsp1_dl_list_write(dl, VI6_WPF_WRBCK_CTRL, 0); + vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL, 0); /* - * Sources. If the pipeline has a single input and BRU is not used, + * 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. @@ -447,24 +305,180 @@ static void wpf_configure(struct vsp1_entity *entity, if (!input) continue; - srcrpf |= (!pipe->bru && pipe->num_inputs == 1) + srcrpf |= (!pipe->brx && pipe->num_inputs == 1) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } - if (pipe->bru) - srcrpf |= pipe->bru->type == VSP1_ENTITY_BRU + if (pipe->brx) + srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU ? VI6_WPF_SRCRPF_VIRACT_MST : VI6_WPF_SRCRPF_VIRACT2_MST; - vsp1_wpf_write(wpf, dl, VI6_WPF_SRCRPF, srcrpf); + vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf); /* Enable interrupts */ - vsp1_dl_list_write(dl, VI6_WPF_IRQ_STA(wpf->entity.index), 0); - vsp1_dl_list_write(dl, VI6_WPF_IRQ_ENB(wpf->entity.index), + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(wpf->entity.index), 0); + vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(wpf->entity.index), VI6_WFP_IRQ_ENB_DFEE); } +static void wpf_configure_frame(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + struct vsp1_dl_list *dl, + struct vsp1_dl_body *dlb) +{ + const unsigned int mask = BIT(WPF_CTRL_VFLIP) + | BIT(WPF_CTRL_HFLIP); + struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); + unsigned long flags; + u32 outfmt; + + spin_lock_irqsave(&wpf->flip.lock, flags); + wpf->flip.active = (wpf->flip.active & ~mask) + | (wpf->flip.pending & mask); + spin_unlock_irqrestore(&wpf->flip.lock, flags); + + outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; + + if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) + outfmt |= VI6_WPF_OUTFMT_FLP; + if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) + outfmt |= VI6_WPF_OUTFMT_HFLP; + + vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt); +} + +static void wpf_configure_partition(struct vsp1_entity *entity, + struct vsp1_pipeline *pipe, + 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; + unsigned int height; + unsigned int offset; + unsigned int flip; + unsigned int i; + + sink_format = vsp1_entity_get_pad_format(&wpf->entity, + wpf->entity.config, + RWPF_PAD_SINK); + width = sink_format->width; + height = sink_format->height; + + /* + * Cropping. The partition algorithm can split the image into + * multiple slices. + */ + if (pipe->partitions > 1) + width = pipe->partition->wpf.width; + + vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); + vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | + (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | + (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); + + if (pipe->lif) + return; + + /* + * Update the memory offsets based on flipping configuration. + * The destination addresses point to the locations where the + * VSP starts writing to memory, which can be any corner of the + * image depending on the combination of flipping and rotation. + */ + + /* + * First take the partition left coordinate into account. + * Compute the offset to order the partitions correctly on the + * output based on whether flipping is enabled. Consider + * horizontal flipping when rotation is disabled but vertical + * flipping when rotation is enabled, as rotating the image + * switches the horizontal and vertical directions. The offset + * is applied horizontally or vertically accordingly. + */ + flip = wpf->flip.active; + + if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) + offset = format->width - pipe->partition->wpf.left + - pipe->partition->wpf.width; + else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) + offset = format->height - pipe->partition->wpf.left + - pipe->partition->wpf.width; + else + offset = pipe->partition->wpf.left; + + for (i = 0; i < format->num_planes; ++i) { + unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; + unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; + + if (wpf->flip.rotate) + mem.addr[i] += offset / vsub + * format->plane_fmt[i].bytesperline; + else + mem.addr[i] += offset / hsub + * fmtinfo->bpp[i] / 8; + } + + if (flip & BIT(WPF_CTRL_VFLIP)) { + /* + * When rotating the output (after rotation) image + * height is equal to the partition width (before + * rotation). Otherwise it is equal to the output + * image height. + */ + if (wpf->flip.rotate) + height = pipe->partition->wpf.width; + else + height = format->height; + + mem.addr[0] += (height - 1) + * format->plane_fmt[0].bytesperline; + + if (format->num_planes > 1) { + offset = (height / fmtinfo->vsub - 1) + * format->plane_fmt[1].bytesperline; + mem.addr[1] += offset; + mem.addr[2] += offset; + } + } + + if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { + unsigned int hoffset = max(0, (int)format->width - 16); + + /* + * Compute the output coordinate. The partition + * horizontal (left) offset becomes a vertical offset. + */ + for (i = 0; i < format->num_planes; ++i) { + unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; + + mem.addr[i] += hoffset / hsub + * fmtinfo->bpp[i] / 8; + } + } + + /* + * On Gen3 hardware the SPUVS bit has no effect on 3-planar + * formats. Swap the U and V planes manually in that case. + */ + if (vsp1->info->gen == 3 && format->num_planes == 3 && + fmtinfo->swap_uv) + swap(mem.addr[1], mem.addr[2]); + + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); + vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); +} + static unsigned int wpf_max_width(struct vsp1_entity *entity, struct vsp1_pipeline *pipe) { @@ -484,7 +498,9 @@ static void wpf_partition(struct vsp1_entity *entity, static const struct vsp1_entity_operations wpf_entity_ops = { .destroy = vsp1_wpf_destroy, - .configure = wpf_configure, + .configure_stream = wpf_configure_stream, + .configure_frame = wpf_configure_frame, + .configure_partition = wpf_configure_partition, .max_width = wpf_max_width, .partition = wpf_partition, }; |