18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_brx.c -- R-Car VSP1 Blend ROP Unit (BRU and BRS) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Renesas Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci#include <linux/gfp.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "vsp1.h" 168c2ecf20Sopenharmony_ci#include "vsp1_brx.h" 178c2ecf20Sopenharmony_ci#include "vsp1_dl.h" 188c2ecf20Sopenharmony_ci#include "vsp1_pipe.h" 198c2ecf20Sopenharmony_ci#include "vsp1_rwpf.h" 208c2ecf20Sopenharmony_ci#include "vsp1_video.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define BRX_MIN_SIZE 1U 238c2ecf20Sopenharmony_ci#define BRX_MAX_SIZE 8190U 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 268c2ecf20Sopenharmony_ci * Device Access 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline void vsp1_brx_write(struct vsp1_brx *brx, 308c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb, u32 reg, u32 data) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, brx->base + reg, data); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 368c2ecf20Sopenharmony_ci * Controls 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int brx_s_ctrl(struct v4l2_ctrl *ctrl) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct vsp1_brx *brx = 428c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct vsp1_brx, ctrls); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci switch (ctrl->id) { 458c2ecf20Sopenharmony_ci case V4L2_CID_BG_COLOR: 468c2ecf20Sopenharmony_ci brx->bgcolor = ctrl->val; 478c2ecf20Sopenharmony_ci break; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops brx_ctrl_ops = { 548c2ecf20Sopenharmony_ci .s_ctrl = brx_s_ctrl, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 588c2ecf20Sopenharmony_ci * V4L2 Subdevice Operations 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* 628c2ecf20Sopenharmony_ci * The BRx can't perform format conversion, all sink and source formats must be 638c2ecf20Sopenharmony_ci * identical. We pick the format on the first sink pad (pad 0) and propagate it 648c2ecf20Sopenharmony_ci * to all other pads. 658c2ecf20Sopenharmony_ci */ 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int brx_enum_mbus_code(struct v4l2_subdev *subdev, 688c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 698c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci static const unsigned int codes[] = { 728c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_ARGB8888_1X32, 738c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_AYUV8_1X32, 748c2ecf20Sopenharmony_ci }; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return vsp1_subdev_enum_mbus_code(subdev, cfg, code, codes, 778c2ecf20Sopenharmony_ci ARRAY_SIZE(codes)); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int brx_enum_frame_size(struct v4l2_subdev *subdev, 818c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 828c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci if (fse->index) 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (fse->code != MEDIA_BUS_FMT_ARGB8888_1X32 && 888c2ecf20Sopenharmony_ci fse->code != MEDIA_BUS_FMT_AYUV8_1X32) 898c2ecf20Sopenharmony_ci return -EINVAL; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci fse->min_width = BRX_MIN_SIZE; 928c2ecf20Sopenharmony_ci fse->max_width = BRX_MAX_SIZE; 938c2ecf20Sopenharmony_ci fse->min_height = BRX_MIN_SIZE; 948c2ecf20Sopenharmony_ci fse->max_height = BRX_MAX_SIZE; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx, 1008c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1018c2ecf20Sopenharmony_ci unsigned int pad) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_compose(&brx->entity.subdev, cfg, pad); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void brx_try_format(struct vsp1_brx *brx, 1078c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config, 1088c2ecf20Sopenharmony_ci unsigned int pad, struct v4l2_mbus_framefmt *fmt) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci switch (pad) { 1138c2ecf20Sopenharmony_ci case BRX_PAD_SINK(0): 1148c2ecf20Sopenharmony_ci /* Default to YUV if the requested format is not supported. */ 1158c2ecf20Sopenharmony_ci if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && 1168c2ecf20Sopenharmony_ci fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) 1178c2ecf20Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; 1188c2ecf20Sopenharmony_ci break; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci /* The BRx can't perform format conversion. */ 1228c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, config, 1238c2ecf20Sopenharmony_ci BRX_PAD_SINK(0)); 1248c2ecf20Sopenharmony_ci fmt->code = format->code; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE); 1298c2ecf20Sopenharmony_ci fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE); 1308c2ecf20Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 1318c2ecf20Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int brx_set_format(struct v4l2_subdev *subdev, 1358c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1368c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct vsp1_brx *brx = to_brx(subdev); 1398c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config; 1408c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 1418c2ecf20Sopenharmony_ci int ret = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mutex_lock(&brx->entity.lock); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci config = vsp1_entity_get_pad_config(&brx->entity, cfg, fmt->which); 1468c2ecf20Sopenharmony_ci if (!config) { 1478c2ecf20Sopenharmony_ci ret = -EINVAL; 1488c2ecf20Sopenharmony_ci goto done; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci brx_try_format(brx, config, fmt->pad, &fmt->format); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, config, fmt->pad); 1548c2ecf20Sopenharmony_ci *format = fmt->format; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* Reset the compose rectangle. */ 1578c2ecf20Sopenharmony_ci if (fmt->pad != brx->entity.source_pad) { 1588c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci compose = brx_get_compose(brx, config, fmt->pad); 1618c2ecf20Sopenharmony_ci compose->left = 0; 1628c2ecf20Sopenharmony_ci compose->top = 0; 1638c2ecf20Sopenharmony_ci compose->width = format->width; 1648c2ecf20Sopenharmony_ci compose->height = format->height; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Propagate the format code to all pads. */ 1688c2ecf20Sopenharmony_ci if (fmt->pad == BRX_PAD_SINK(0)) { 1698c2ecf20Sopenharmony_ci unsigned int i; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (i = 0; i <= brx->entity.source_pad; ++i) { 1728c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, 1738c2ecf20Sopenharmony_ci config, i); 1748c2ecf20Sopenharmony_ci format->code = fmt->format.code; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cidone: 1798c2ecf20Sopenharmony_ci mutex_unlock(&brx->entity.lock); 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int brx_get_selection(struct v4l2_subdev *subdev, 1848c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1858c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct vsp1_brx *brx = to_brx(subdev); 1888c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (sel->pad == brx->entity.source_pad) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci switch (sel->target) { 1948c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 1958c2ecf20Sopenharmony_ci sel->r.left = 0; 1968c2ecf20Sopenharmony_ci sel->r.top = 0; 1978c2ecf20Sopenharmony_ci sel->r.width = BRX_MAX_SIZE; 1988c2ecf20Sopenharmony_ci sel->r.height = BRX_MAX_SIZE; 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 2028c2ecf20Sopenharmony_ci config = vsp1_entity_get_pad_config(&brx->entity, cfg, 2038c2ecf20Sopenharmony_ci sel->which); 2048c2ecf20Sopenharmony_ci if (!config) 2058c2ecf20Sopenharmony_ci return -EINVAL; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_lock(&brx->entity.lock); 2088c2ecf20Sopenharmony_ci sel->r = *brx_get_compose(brx, config, sel->pad); 2098c2ecf20Sopenharmony_ci mutex_unlock(&brx->entity.lock); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci default: 2138c2ecf20Sopenharmony_ci return -EINVAL; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int brx_set_selection(struct v4l2_subdev *subdev, 2188c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 2198c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct vsp1_brx *brx = to_brx(subdev); 2228c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *config; 2238c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 2248c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 2258c2ecf20Sopenharmony_ci int ret = 0; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (sel->pad == brx->entity.source_pad) 2288c2ecf20Sopenharmony_ci return -EINVAL; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (sel->target != V4L2_SEL_TGT_COMPOSE) 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci mutex_lock(&brx->entity.lock); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci config = vsp1_entity_get_pad_config(&brx->entity, cfg, sel->which); 2368c2ecf20Sopenharmony_ci if (!config) { 2378c2ecf20Sopenharmony_ci ret = -EINVAL; 2388c2ecf20Sopenharmony_ci goto done; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* 2428c2ecf20Sopenharmony_ci * The compose rectangle top left corner must be inside the output 2438c2ecf20Sopenharmony_ci * frame. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, config, 2468c2ecf20Sopenharmony_ci brx->entity.source_pad); 2478c2ecf20Sopenharmony_ci sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1); 2488c2ecf20Sopenharmony_ci sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * Scaling isn't supported, the compose rectangle size must be identical 2528c2ecf20Sopenharmony_ci * to the sink format size. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, config, sel->pad); 2558c2ecf20Sopenharmony_ci sel->r.width = format->width; 2568c2ecf20Sopenharmony_ci sel->r.height = format->height; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci compose = brx_get_compose(brx, config, sel->pad); 2598c2ecf20Sopenharmony_ci *compose = sel->r; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cidone: 2628c2ecf20Sopenharmony_ci mutex_unlock(&brx->entity.lock); 2638c2ecf20Sopenharmony_ci return ret; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops brx_pad_ops = { 2678c2ecf20Sopenharmony_ci .init_cfg = vsp1_entity_init_cfg, 2688c2ecf20Sopenharmony_ci .enum_mbus_code = brx_enum_mbus_code, 2698c2ecf20Sopenharmony_ci .enum_frame_size = brx_enum_frame_size, 2708c2ecf20Sopenharmony_ci .get_fmt = vsp1_subdev_get_pad_format, 2718c2ecf20Sopenharmony_ci .set_fmt = brx_set_format, 2728c2ecf20Sopenharmony_ci .get_selection = brx_get_selection, 2738c2ecf20Sopenharmony_ci .set_selection = brx_set_selection, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops brx_ops = { 2778c2ecf20Sopenharmony_ci .pad = &brx_pad_ops, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2818c2ecf20Sopenharmony_ci * VSP1 Entity Operations 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void brx_configure_stream(struct vsp1_entity *entity, 2858c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 2868c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 2878c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct vsp1_brx *brx = to_brx(&entity->subdev); 2908c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 2918c2ecf20Sopenharmony_ci unsigned int flags; 2928c2ecf20Sopenharmony_ci unsigned int i; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.config, 2958c2ecf20Sopenharmony_ci brx->entity.source_pad); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* 2988c2ecf20Sopenharmony_ci * The hardware is extremely flexible but we have no userspace API to 2998c2ecf20Sopenharmony_ci * expose all the parameters, nor is it clear whether we would have use 3008c2ecf20Sopenharmony_ci * cases for all the supported modes. Let's just hardcode the parameters 3018c2ecf20Sopenharmony_ci * to sane default values for now. 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* 3058c2ecf20Sopenharmony_ci * Disable dithering and enable color data normalization unless the 3068c2ecf20Sopenharmony_ci * format at the pipeline output is premultiplied. 3078c2ecf20Sopenharmony_ci */ 3088c2ecf20Sopenharmony_ci flags = pipe->output ? pipe->output->format.flags : 0; 3098c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_INCTRL, 3108c2ecf20Sopenharmony_ci flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ? 3118c2ecf20Sopenharmony_ci 0 : VI6_BRU_INCTRL_NRM); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * Set the background position to cover the whole output image and 3158c2ecf20Sopenharmony_ci * configure its color. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_SIZE, 3188c2ecf20Sopenharmony_ci (format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) | 3198c2ecf20Sopenharmony_ci (format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT)); 3208c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_LOC, 0); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_VIRRPF_COL, brx->bgcolor | 3238c2ecf20Sopenharmony_ci (0xff << VI6_BRU_VIRRPF_COL_A_SHIFT)); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Route BRU input 1 as SRC input to the ROP unit and configure the ROP 3278c2ecf20Sopenharmony_ci * unit with a NOP operation to make BRU input 1 available as the 3288c2ecf20Sopenharmony_ci * Blend/ROP unit B SRC input. Only needed for BRU, the BRS has no ROP 3298c2ecf20Sopenharmony_ci * unit. 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci if (entity->type == VSP1_ENTITY_BRU) 3328c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_ROP, 3338c2ecf20Sopenharmony_ci VI6_BRU_ROP_DSTSEL_BRUIN(1) | 3348c2ecf20Sopenharmony_ci VI6_BRU_ROP_CROP(VI6_ROP_NOP) | 3358c2ecf20Sopenharmony_ci VI6_BRU_ROP_AROP(VI6_ROP_NOP)); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci for (i = 0; i < brx->entity.source_pad; ++i) { 3388c2ecf20Sopenharmony_ci bool premultiplied = false; 3398c2ecf20Sopenharmony_ci u32 ctrl = 0; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* 3428c2ecf20Sopenharmony_ci * Configure all Blend/ROP units corresponding to an enabled BRx 3438c2ecf20Sopenharmony_ci * input for alpha blending. Blend/ROP units corresponding to 3448c2ecf20Sopenharmony_ci * disabled BRx inputs are used in ROP NOP mode to ignore the 3458c2ecf20Sopenharmony_ci * SRC input. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci if (brx->inputs[i].rpf) { 3488c2ecf20Sopenharmony_ci ctrl |= VI6_BRU_CTRL_RBC; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci premultiplied = brx->inputs[i].rpf->format.flags 3518c2ecf20Sopenharmony_ci & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA; 3528c2ecf20Sopenharmony_ci } else { 3538c2ecf20Sopenharmony_ci ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP) 3548c2ecf20Sopenharmony_ci | VI6_BRU_CTRL_AROP(VI6_ROP_NOP); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* 3588c2ecf20Sopenharmony_ci * Select the virtual RPF as the Blend/ROP unit A DST input to 3598c2ecf20Sopenharmony_ci * serve as a background color. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci if (i == 0) 3628c2ecf20Sopenharmony_ci ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Route inputs 0 to 3 as SRC inputs to Blend/ROP units A to D 3668c2ecf20Sopenharmony_ci * in that order. In the BRU the Blend/ROP unit B SRC is 3678c2ecf20Sopenharmony_ci * hardwired to the ROP unit output, the corresponding register 3688c2ecf20Sopenharmony_ci * bits must be set to 0. The BRS has no ROP unit and doesn't 3698c2ecf20Sopenharmony_ci * need any special processing. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci if (!(entity->type == VSP1_ENTITY_BRU && i == 1)) 3728c2ecf20Sopenharmony_ci ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_CTRL(i), ctrl); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Hardcode the blending formula to 3788c2ecf20Sopenharmony_ci * 3798c2ecf20Sopenharmony_ci * DSTc = DSTc * (1 - SRCa) + SRCc * SRCa 3808c2ecf20Sopenharmony_ci * DSTa = DSTa * (1 - SRCa) + SRCa 3818c2ecf20Sopenharmony_ci * 3828c2ecf20Sopenharmony_ci * when the SRC input isn't premultiplied, and to 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * DSTc = DSTc * (1 - SRCa) + SRCc 3858c2ecf20Sopenharmony_ci * DSTa = DSTa * (1 - SRCa) + SRCa 3868c2ecf20Sopenharmony_ci * 3878c2ecf20Sopenharmony_ci * otherwise. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci vsp1_brx_write(brx, dlb, VI6_BRU_BLD(i), 3908c2ecf20Sopenharmony_ci VI6_BRU_BLD_CCMDX_255_SRC_A | 3918c2ecf20Sopenharmony_ci (premultiplied ? VI6_BRU_BLD_CCMDY_COEFY : 3928c2ecf20Sopenharmony_ci VI6_BRU_BLD_CCMDY_SRC_A) | 3938c2ecf20Sopenharmony_ci VI6_BRU_BLD_ACMDX_255_SRC_A | 3948c2ecf20Sopenharmony_ci VI6_BRU_BLD_ACMDY_COEFY | 3958c2ecf20Sopenharmony_ci (0xff << VI6_BRU_BLD_COEFY_SHIFT)); 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic const struct vsp1_entity_operations brx_entity_ops = { 4008c2ecf20Sopenharmony_ci .configure_stream = brx_configure_stream, 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 4048c2ecf20Sopenharmony_ci * Initialization and Cleanup 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistruct vsp1_brx *vsp1_brx_create(struct vsp1_device *vsp1, 4088c2ecf20Sopenharmony_ci enum vsp1_entity_type type) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct vsp1_brx *brx; 4118c2ecf20Sopenharmony_ci unsigned int num_pads; 4128c2ecf20Sopenharmony_ci const char *name; 4138c2ecf20Sopenharmony_ci int ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci brx = devm_kzalloc(vsp1->dev, sizeof(*brx), GFP_KERNEL); 4168c2ecf20Sopenharmony_ci if (brx == NULL) 4178c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci brx->base = type == VSP1_ENTITY_BRU ? VI6_BRU_BASE : VI6_BRS_BASE; 4208c2ecf20Sopenharmony_ci brx->entity.ops = &brx_entity_ops; 4218c2ecf20Sopenharmony_ci brx->entity.type = type; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (type == VSP1_ENTITY_BRU) { 4248c2ecf20Sopenharmony_ci num_pads = vsp1->info->num_bru_inputs + 1; 4258c2ecf20Sopenharmony_ci name = "bru"; 4268c2ecf20Sopenharmony_ci } else { 4278c2ecf20Sopenharmony_ci num_pads = 3; 4288c2ecf20Sopenharmony_ci name = "brs"; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ret = vsp1_entity_init(vsp1, &brx->entity, name, num_pads, &brx_ops, 4328c2ecf20Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_COMPOSER); 4338c2ecf20Sopenharmony_ci if (ret < 0) 4348c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Initialize the control handler. */ 4378c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&brx->ctrls, 1); 4388c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&brx->ctrls, &brx_ctrl_ops, V4L2_CID_BG_COLOR, 4398c2ecf20Sopenharmony_ci 0, 0xffffff, 1, 0); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci brx->bgcolor = 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci brx->entity.subdev.ctrl_handler = &brx->ctrls; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (brx->ctrls.error) { 4468c2ecf20Sopenharmony_ci dev_err(vsp1->dev, "%s: failed to initialize controls\n", name); 4478c2ecf20Sopenharmony_ci ret = brx->ctrls.error; 4488c2ecf20Sopenharmony_ci vsp1_entity_destroy(&brx->entity); 4498c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return brx; 4538c2ecf20Sopenharmony_ci} 454