18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013-2014 Renesas Electronics Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/device.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "vsp1.h" 158c2ecf20Sopenharmony_ci#include "vsp1_dl.h" 168c2ecf20Sopenharmony_ci#include "vsp1_pipe.h" 178c2ecf20Sopenharmony_ci#include "vsp1_rwpf.h" 188c2ecf20Sopenharmony_ci#include "vsp1_video.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define WPF_GEN2_MAX_WIDTH 2048U 218c2ecf20Sopenharmony_ci#define WPF_GEN2_MAX_HEIGHT 2048U 228c2ecf20Sopenharmony_ci#define WPF_GEN3_MAX_WIDTH 8190U 238c2ecf20Sopenharmony_ci#define WPF_GEN3_MAX_HEIGHT 8190U 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 268c2ecf20Sopenharmony_ci * Device Access 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, 308c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb, u32 reg, u32 data) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, reg + wpf->entity.index * VI6_WPF_OFFSET, data); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 368c2ecf20Sopenharmony_ci * Controls 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cienum wpf_flip_ctrl { 408c2ecf20Sopenharmony_ci WPF_CTRL_VFLIP = 0, 418c2ecf20Sopenharmony_ci WPF_CTRL_HFLIP = 1, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct vsp1_video *video = wpf->video; 478c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *sink_format; 488c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *source_format; 498c2ecf20Sopenharmony_ci bool rotate; 508c2ecf20Sopenharmony_ci int ret = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Only consider the 0°/180° from/to 90°/270° modifications, the rest 548c2ecf20Sopenharmony_ci * is taken care of by the flipping configuration. 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci rotate = rotation == 90 || rotation == 270; 578c2ecf20Sopenharmony_ci if (rotate == wpf->flip.rotate) 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* Changing rotation isn't allowed when buffers are allocated. */ 618c2ecf20Sopenharmony_ci mutex_lock(&video->lock); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (vb2_is_busy(&video->queue)) { 648c2ecf20Sopenharmony_ci ret = -EBUSY; 658c2ecf20Sopenharmony_ci goto done; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci sink_format = vsp1_entity_get_pad_format(&wpf->entity, 698c2ecf20Sopenharmony_ci wpf->entity.config, 708c2ecf20Sopenharmony_ci RWPF_PAD_SINK); 718c2ecf20Sopenharmony_ci source_format = vsp1_entity_get_pad_format(&wpf->entity, 728c2ecf20Sopenharmony_ci wpf->entity.config, 738c2ecf20Sopenharmony_ci RWPF_PAD_SOURCE); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci mutex_lock(&wpf->entity.lock); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (rotate) { 788c2ecf20Sopenharmony_ci source_format->width = sink_format->height; 798c2ecf20Sopenharmony_ci source_format->height = sink_format->width; 808c2ecf20Sopenharmony_ci } else { 818c2ecf20Sopenharmony_ci source_format->width = sink_format->width; 828c2ecf20Sopenharmony_ci source_format->height = sink_format->height; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci wpf->flip.rotate = rotate; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci mutex_unlock(&wpf->entity.lock); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cidone: 908c2ecf20Sopenharmony_ci mutex_unlock(&video->lock); 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = 978c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 988c2ecf20Sopenharmony_ci unsigned int rotation; 998c2ecf20Sopenharmony_ci u32 flip = 0; 1008c2ecf20Sopenharmony_ci int ret; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Update the rotation. */ 1038c2ecf20Sopenharmony_ci rotation = wpf->flip.ctrls.rotate ? wpf->flip.ctrls.rotate->val : 0; 1048c2ecf20Sopenharmony_ci ret = vsp1_wpf_set_rotation(wpf, rotation); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* 1098c2ecf20Sopenharmony_ci * Compute the flip value resulting from all three controls, with 1108c2ecf20Sopenharmony_ci * rotation by 180° flipping the image in both directions. Store the 1118c2ecf20Sopenharmony_ci * result in the pending flip field for the next frame that will be 1128c2ecf20Sopenharmony_ci * processed. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci if (wpf->flip.ctrls.vflip->val) 1158c2ecf20Sopenharmony_ci flip |= BIT(WPF_CTRL_VFLIP); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (wpf->flip.ctrls.hflip && wpf->flip.ctrls.hflip->val) 1188c2ecf20Sopenharmony_ci flip |= BIT(WPF_CTRL_HFLIP); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (rotation == 180 || rotation == 270) 1218c2ecf20Sopenharmony_ci flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irq(&wpf->flip.lock); 1248c2ecf20Sopenharmony_ci wpf->flip.pending = flip; 1258c2ecf20Sopenharmony_ci spin_unlock_irq(&wpf->flip.lock); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops vsp1_wpf_ctrl_ops = { 1318c2ecf20Sopenharmony_ci .s_ctrl = vsp1_wpf_s_ctrl, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int wpf_init_controls(struct vsp1_rwpf *wpf) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = wpf->entity.vsp1; 1378c2ecf20Sopenharmony_ci unsigned int num_flip_ctrls; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci spin_lock_init(&wpf->flip.lock); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (wpf->entity.index != 0) { 1428c2ecf20Sopenharmony_ci /* Only WPF0 supports flipping. */ 1438c2ecf20Sopenharmony_ci num_flip_ctrls = 0; 1448c2ecf20Sopenharmony_ci } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP)) { 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * When horizontal flip is supported the WPF implements three 1478c2ecf20Sopenharmony_ci * controls (horizontal flip, vertical flip and rotation). 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci num_flip_ctrls = 3; 1508c2ecf20Sopenharmony_ci } else if (vsp1_feature(vsp1, VSP1_HAS_WPF_VFLIP)) { 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * When only vertical flip is supported the WPF implements a 1538c2ecf20Sopenharmony_ci * single control (vertical flip). 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci num_flip_ctrls = 1; 1568c2ecf20Sopenharmony_ci } else { 1578c2ecf20Sopenharmony_ci /* Otherwise flipping is not supported. */ 1588c2ecf20Sopenharmony_ci num_flip_ctrls = 0; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (num_flip_ctrls >= 1) { 1648c2ecf20Sopenharmony_ci wpf->flip.ctrls.vflip = 1658c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, 1668c2ecf20Sopenharmony_ci V4L2_CID_VFLIP, 0, 1, 1, 0); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (num_flip_ctrls == 3) { 1708c2ecf20Sopenharmony_ci wpf->flip.ctrls.hflip = 1718c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, 1728c2ecf20Sopenharmony_ci V4L2_CID_HFLIP, 0, 1, 1, 0); 1738c2ecf20Sopenharmony_ci wpf->flip.ctrls.rotate = 1748c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&wpf->ctrls, &vsp1_wpf_ctrl_ops, 1758c2ecf20Sopenharmony_ci V4L2_CID_ROTATE, 0, 270, 90, 0); 1768c2ecf20Sopenharmony_ci v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (wpf->ctrls.error) { 1808c2ecf20Sopenharmony_ci dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", 1818c2ecf20Sopenharmony_ci wpf->entity.index); 1828c2ecf20Sopenharmony_ci return wpf->ctrls.error; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1898c2ecf20Sopenharmony_ci * V4L2 Subdevice Core Operations 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int wpf_s_stream(struct v4l2_subdev *subdev, int enable) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = to_rwpf(subdev); 1958c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = wpf->entity.vsp1; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (enable) 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * Write to registers directly when stopping the stream as there will be 2028c2ecf20Sopenharmony_ci * no pipeline run to apply the display list. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0); 2058c2ecf20Sopenharmony_ci vsp1_write(vsp1, wpf->entity.index * VI6_WPF_OFFSET + 2068c2ecf20Sopenharmony_ci VI6_WPF_SRCRPF, 0); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2128c2ecf20Sopenharmony_ci * V4L2 Subdevice Operations 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops wpf_video_ops = { 2168c2ecf20Sopenharmony_ci .s_stream = wpf_s_stream, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops wpf_ops = { 2208c2ecf20Sopenharmony_ci .video = &wpf_video_ops, 2218c2ecf20Sopenharmony_ci .pad = &vsp1_rwpf_pad_ops, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2258c2ecf20Sopenharmony_ci * VSP1 Entity Operations 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void vsp1_wpf_destroy(struct vsp1_entity *entity) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = entity_to_rwpf(entity); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci vsp1_dlm_destroy(wpf->dlm); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf, 2368c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci unsigned int index = wpf->entity.index; 2398c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl_next; 2408c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci dl_next = vsp1_dl_list_get(wpf->dlm); 2438c2ecf20Sopenharmony_ci if (!dl_next) { 2448c2ecf20Sopenharmony_ci dev_err(wpf->entity.vsp1->dev, 2458c2ecf20Sopenharmony_ci "Failed to obtain a dl list, disabling writeback\n"); 2468c2ecf20Sopenharmony_ci return -ENOMEM; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci dlb = vsp1_dl_list_get_body0(dl_next); 2508c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 0); 2518c2ecf20Sopenharmony_ci vsp1_dl_list_add_chain(dl, dl_next); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void wpf_configure_stream(struct vsp1_entity *entity, 2578c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 2588c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 2598c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); 2628c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = wpf->entity.vsp1; 2638c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *source_format; 2648c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *sink_format; 2658c2ecf20Sopenharmony_ci unsigned int index = wpf->entity.index; 2668c2ecf20Sopenharmony_ci unsigned int i; 2678c2ecf20Sopenharmony_ci u32 outfmt = 0; 2688c2ecf20Sopenharmony_ci u32 srcrpf = 0; 2698c2ecf20Sopenharmony_ci int ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci sink_format = vsp1_entity_get_pad_format(&wpf->entity, 2728c2ecf20Sopenharmony_ci wpf->entity.config, 2738c2ecf20Sopenharmony_ci RWPF_PAD_SINK); 2748c2ecf20Sopenharmony_ci source_format = vsp1_entity_get_pad_format(&wpf->entity, 2758c2ecf20Sopenharmony_ci wpf->entity.config, 2768c2ecf20Sopenharmony_ci RWPF_PAD_SOURCE); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* Format */ 2798c2ecf20Sopenharmony_ci if (!pipe->lif || wpf->writeback) { 2808c2ecf20Sopenharmony_ci const struct v4l2_pix_format_mplane *format = &wpf->format; 2818c2ecf20Sopenharmony_ci const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (wpf->flip.rotate) 2868c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_ROT; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (fmtinfo->alpha) 2898c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_PXA; 2908c2ecf20Sopenharmony_ci if (fmtinfo->swap_yc) 2918c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_SPYCS; 2928c2ecf20Sopenharmony_ci if (fmtinfo->swap_uv) 2938c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_SPUVS; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Destination stride and byte swapping. */ 2968c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_Y, 2978c2ecf20Sopenharmony_ci format->plane_fmt[0].bytesperline); 2988c2ecf20Sopenharmony_ci if (format->num_planes > 1) 2998c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_STRIDE_C, 3008c2ecf20Sopenharmony_ci format->plane_fmt[1].bytesperline); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSWAP, fmtinfo->swap); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (vsp1_feature(vsp1, VSP1_HAS_WPF_HFLIP) && index == 0) 3058c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_ROT_CTRL, 3068c2ecf20Sopenharmony_ci VI6_WPF_ROT_CTRL_LN16 | 3078c2ecf20Sopenharmony_ci (256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT)); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (sink_format->code != source_format->code) 3118c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_CSC; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci wpf->outfmt = outfmt; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, VI6_DPR_WPF_FPORCH(index), 3168c2ecf20Sopenharmony_ci VI6_DPR_WPF_FPORCH_FP_WPFN); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * Sources. If the pipeline has a single input and BRx is not used, 3208c2ecf20Sopenharmony_ci * configure it as the master layer. Otherwise configure all 3218c2ecf20Sopenharmony_ci * inputs as sub-layers and select the virtual RPF as the master 3228c2ecf20Sopenharmony_ci * layer. 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci for (i = 0; i < vsp1->info->rpf_count; ++i) { 3258c2ecf20Sopenharmony_ci struct vsp1_rwpf *input = pipe->inputs[i]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!input) 3288c2ecf20Sopenharmony_ci continue; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci srcrpf |= (!pipe->brx && pipe->num_inputs == 1) 3318c2ecf20Sopenharmony_ci ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) 3328c2ecf20Sopenharmony_ci : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (pipe->brx) 3368c2ecf20Sopenharmony_ci srcrpf |= pipe->brx->type == VSP1_ENTITY_BRU 3378c2ecf20Sopenharmony_ci ? VI6_WPF_SRCRPF_VIRACT_MST 3388c2ecf20Sopenharmony_ci : VI6_WPF_SRCRPF_VIRACT2_MST; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_SRCRPF, srcrpf); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Enable interrupts. */ 3438c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, VI6_WPF_IRQ_STA(index), 0); 3448c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index), 3458c2ecf20Sopenharmony_ci VI6_WFP_IRQ_ENB_DFEE); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * Configure writeback for display pipelines (the wpf writeback flag is 3498c2ecf20Sopenharmony_ci * never set for memory-to-memory pipelines). Start by adding a chained 3508c2ecf20Sopenharmony_ci * display list to disable writeback after a single frame, and process 3518c2ecf20Sopenharmony_ci * to enable writeback. If the display list allocation fails don't 3528c2ecf20Sopenharmony_ci * enable writeback as we wouldn't be able to safely disable it, 3538c2ecf20Sopenharmony_ci * resulting in possible memory corruption. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci if (wpf->writeback) { 3568c2ecf20Sopenharmony_ci ret = wpf_configure_writeback_chain(wpf, dl); 3578c2ecf20Sopenharmony_ci if (ret < 0) 3588c2ecf20Sopenharmony_ci wpf->writeback = false; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci vsp1_dl_body_write(dlb, VI6_WPF_WRBCK_CTRL(index), 3628c2ecf20Sopenharmony_ci wpf->writeback ? VI6_WPF_WRBCK_CTRL_WBMD : 0); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void wpf_configure_frame(struct vsp1_entity *entity, 3668c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 3678c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 3688c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci const unsigned int mask = BIT(WPF_CTRL_VFLIP) 3718c2ecf20Sopenharmony_ci | BIT(WPF_CTRL_HFLIP); 3728c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); 3738c2ecf20Sopenharmony_ci unsigned long flags; 3748c2ecf20Sopenharmony_ci u32 outfmt; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci spin_lock_irqsave(&wpf->flip.lock, flags); 3778c2ecf20Sopenharmony_ci wpf->flip.active = (wpf->flip.active & ~mask) 3788c2ecf20Sopenharmony_ci | (wpf->flip.pending & mask); 3798c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wpf->flip.lock, flags); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (wpf->flip.active & BIT(WPF_CTRL_VFLIP)) 3848c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_FLP; 3858c2ecf20Sopenharmony_ci if (wpf->flip.active & BIT(WPF_CTRL_HFLIP)) 3868c2ecf20Sopenharmony_ci outfmt |= VI6_WPF_OUTFMT_HFLP; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_OUTFMT, outfmt); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void wpf_configure_partition(struct vsp1_entity *entity, 3928c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 3938c2ecf20Sopenharmony_ci struct vsp1_dl_list *dl, 3948c2ecf20Sopenharmony_ci struct vsp1_dl_body *dlb) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); 3978c2ecf20Sopenharmony_ci struct vsp1_device *vsp1 = wpf->entity.vsp1; 3988c2ecf20Sopenharmony_ci struct vsp1_rwpf_memory mem = wpf->mem; 3998c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *sink_format; 4008c2ecf20Sopenharmony_ci const struct v4l2_pix_format_mplane *format = &wpf->format; 4018c2ecf20Sopenharmony_ci const struct vsp1_format_info *fmtinfo = wpf->fmtinfo; 4028c2ecf20Sopenharmony_ci unsigned int width; 4038c2ecf20Sopenharmony_ci unsigned int height; 4048c2ecf20Sopenharmony_ci unsigned int left; 4058c2ecf20Sopenharmony_ci unsigned int offset; 4068c2ecf20Sopenharmony_ci unsigned int flip; 4078c2ecf20Sopenharmony_ci unsigned int i; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci sink_format = vsp1_entity_get_pad_format(&wpf->entity, 4108c2ecf20Sopenharmony_ci wpf->entity.config, 4118c2ecf20Sopenharmony_ci RWPF_PAD_SINK); 4128c2ecf20Sopenharmony_ci width = sink_format->width; 4138c2ecf20Sopenharmony_ci height = sink_format->height; 4148c2ecf20Sopenharmony_ci left = 0; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * Cropping. The partition algorithm can split the image into 4188c2ecf20Sopenharmony_ci * multiple slices. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci if (pipe->partitions > 1) { 4218c2ecf20Sopenharmony_ci width = pipe->partition->wpf.width; 4228c2ecf20Sopenharmony_ci left = pipe->partition->wpf.left; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN | 4268c2ecf20Sopenharmony_ci (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | 4278c2ecf20Sopenharmony_ci (width << VI6_WPF_SZCLIP_SIZE_SHIFT)); 4288c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN | 4298c2ecf20Sopenharmony_ci (0 << VI6_WPF_SZCLIP_OFST_SHIFT) | 4308c2ecf20Sopenharmony_ci (height << VI6_WPF_SZCLIP_SIZE_SHIFT)); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* 4338c2ecf20Sopenharmony_ci * For display pipelines without writeback enabled there's no memory 4348c2ecf20Sopenharmony_ci * address to configure, return now. 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci if (pipe->lif && !wpf->writeback) 4378c2ecf20Sopenharmony_ci return; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* 4408c2ecf20Sopenharmony_ci * Update the memory offsets based on flipping configuration. 4418c2ecf20Sopenharmony_ci * The destination addresses point to the locations where the 4428c2ecf20Sopenharmony_ci * VSP starts writing to memory, which can be any corner of the 4438c2ecf20Sopenharmony_ci * image depending on the combination of flipping and rotation. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * First take the partition left coordinate into account. 4488c2ecf20Sopenharmony_ci * Compute the offset to order the partitions correctly on the 4498c2ecf20Sopenharmony_ci * output based on whether flipping is enabled. Consider 4508c2ecf20Sopenharmony_ci * horizontal flipping when rotation is disabled but vertical 4518c2ecf20Sopenharmony_ci * flipping when rotation is enabled, as rotating the image 4528c2ecf20Sopenharmony_ci * switches the horizontal and vertical directions. The offset 4538c2ecf20Sopenharmony_ci * is applied horizontally or vertically accordingly. 4548c2ecf20Sopenharmony_ci */ 4558c2ecf20Sopenharmony_ci flip = wpf->flip.active; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (flip & BIT(WPF_CTRL_HFLIP) && !wpf->flip.rotate) 4588c2ecf20Sopenharmony_ci offset = format->width - left - width; 4598c2ecf20Sopenharmony_ci else if (flip & BIT(WPF_CTRL_VFLIP) && wpf->flip.rotate) 4608c2ecf20Sopenharmony_ci offset = format->height - left - width; 4618c2ecf20Sopenharmony_ci else 4628c2ecf20Sopenharmony_ci offset = left; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0; i < format->num_planes; ++i) { 4658c2ecf20Sopenharmony_ci unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; 4668c2ecf20Sopenharmony_ci unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (wpf->flip.rotate) 4698c2ecf20Sopenharmony_ci mem.addr[i] += offset / vsub 4708c2ecf20Sopenharmony_ci * format->plane_fmt[i].bytesperline; 4718c2ecf20Sopenharmony_ci else 4728c2ecf20Sopenharmony_ci mem.addr[i] += offset / hsub 4738c2ecf20Sopenharmony_ci * fmtinfo->bpp[i] / 8; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (flip & BIT(WPF_CTRL_VFLIP)) { 4778c2ecf20Sopenharmony_ci /* 4788c2ecf20Sopenharmony_ci * When rotating the output (after rotation) image 4798c2ecf20Sopenharmony_ci * height is equal to the partition width (before 4808c2ecf20Sopenharmony_ci * rotation). Otherwise it is equal to the output 4818c2ecf20Sopenharmony_ci * image height. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci if (wpf->flip.rotate) 4848c2ecf20Sopenharmony_ci height = width; 4858c2ecf20Sopenharmony_ci else 4868c2ecf20Sopenharmony_ci height = format->height; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci mem.addr[0] += (height - 1) 4898c2ecf20Sopenharmony_ci * format->plane_fmt[0].bytesperline; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (format->num_planes > 1) { 4928c2ecf20Sopenharmony_ci offset = (height / fmtinfo->vsub - 1) 4938c2ecf20Sopenharmony_ci * format->plane_fmt[1].bytesperline; 4948c2ecf20Sopenharmony_ci mem.addr[1] += offset; 4958c2ecf20Sopenharmony_ci mem.addr[2] += offset; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (wpf->flip.rotate && !(flip & BIT(WPF_CTRL_HFLIP))) { 5008c2ecf20Sopenharmony_ci unsigned int hoffset = max(0, (int)format->width - 16); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* 5038c2ecf20Sopenharmony_ci * Compute the output coordinate. The partition 5048c2ecf20Sopenharmony_ci * horizontal (left) offset becomes a vertical offset. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_ci for (i = 0; i < format->num_planes; ++i) { 5078c2ecf20Sopenharmony_ci unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mem.addr[i] += hoffset / hsub 5108c2ecf20Sopenharmony_ci * fmtinfo->bpp[i] / 8; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * On Gen3 hardware the SPUVS bit has no effect on 3-planar 5168c2ecf20Sopenharmony_ci * formats. Swap the U and V planes manually in that case. 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci if (vsp1->info->gen == 3 && format->num_planes == 3 && 5198c2ecf20Sopenharmony_ci fmtinfo->swap_uv) 5208c2ecf20Sopenharmony_ci swap(mem.addr[1], mem.addr[2]); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_Y, mem.addr[0]); 5238c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C0, mem.addr[1]); 5248c2ecf20Sopenharmony_ci vsp1_wpf_write(wpf, dlb, VI6_WPF_DSTM_ADDR_C1, mem.addr[2]); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* 5278c2ecf20Sopenharmony_ci * Writeback operates in single-shot mode and lasts for a single frame, 5288c2ecf20Sopenharmony_ci * reset the writeback flag to false for the next frame. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ci wpf->writeback = false; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic unsigned int wpf_max_width(struct vsp1_entity *entity, 5348c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return wpf->flip.rotate ? 256 : wpf->max_width; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_cistatic void wpf_partition(struct vsp1_entity *entity, 5428c2ecf20Sopenharmony_ci struct vsp1_pipeline *pipe, 5438c2ecf20Sopenharmony_ci struct vsp1_partition *partition, 5448c2ecf20Sopenharmony_ci unsigned int partition_idx, 5458c2ecf20Sopenharmony_ci struct vsp1_partition_window *window) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci partition->wpf = *window; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic const struct vsp1_entity_operations wpf_entity_ops = { 5518c2ecf20Sopenharmony_ci .destroy = vsp1_wpf_destroy, 5528c2ecf20Sopenharmony_ci .configure_stream = wpf_configure_stream, 5538c2ecf20Sopenharmony_ci .configure_frame = wpf_configure_frame, 5548c2ecf20Sopenharmony_ci .configure_partition = wpf_configure_partition, 5558c2ecf20Sopenharmony_ci .max_width = wpf_max_width, 5568c2ecf20Sopenharmony_ci .partition = wpf_partition, 5578c2ecf20Sopenharmony_ci}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 5608c2ecf20Sopenharmony_ci * Initialization and Cleanup 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistruct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct vsp1_rwpf *wpf; 5668c2ecf20Sopenharmony_ci char name[6]; 5678c2ecf20Sopenharmony_ci int ret; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL); 5708c2ecf20Sopenharmony_ci if (wpf == NULL) 5718c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (vsp1->info->gen == 2) { 5748c2ecf20Sopenharmony_ci wpf->max_width = WPF_GEN2_MAX_WIDTH; 5758c2ecf20Sopenharmony_ci wpf->max_height = WPF_GEN2_MAX_HEIGHT; 5768c2ecf20Sopenharmony_ci } else { 5778c2ecf20Sopenharmony_ci wpf->max_width = WPF_GEN3_MAX_WIDTH; 5788c2ecf20Sopenharmony_ci wpf->max_height = WPF_GEN3_MAX_HEIGHT; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci wpf->entity.ops = &wpf_entity_ops; 5828c2ecf20Sopenharmony_ci wpf->entity.type = VSP1_ENTITY_WPF; 5838c2ecf20Sopenharmony_ci wpf->entity.index = index; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci sprintf(name, "wpf.%u", index); 5868c2ecf20Sopenharmony_ci ret = vsp1_entity_init(vsp1, &wpf->entity, name, 2, &wpf_ops, 5878c2ecf20Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER); 5888c2ecf20Sopenharmony_ci if (ret < 0) 5898c2ecf20Sopenharmony_ci return ERR_PTR(ret); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* Initialize the display list manager. */ 5928c2ecf20Sopenharmony_ci wpf->dlm = vsp1_dlm_create(vsp1, index, 64); 5938c2ecf20Sopenharmony_ci if (!wpf->dlm) { 5948c2ecf20Sopenharmony_ci ret = -ENOMEM; 5958c2ecf20Sopenharmony_ci goto error; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* Initialize the control handler. */ 5998c2ecf20Sopenharmony_ci ret = wpf_init_controls(wpf); 6008c2ecf20Sopenharmony_ci if (ret < 0) { 6018c2ecf20Sopenharmony_ci dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n", 6028c2ecf20Sopenharmony_ci index); 6038c2ecf20Sopenharmony_ci goto error; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci v4l2_ctrl_handler_setup(&wpf->ctrls); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return wpf; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cierror: 6118c2ecf20Sopenharmony_ci vsp1_entity_destroy(&wpf->entity); 6128c2ecf20Sopenharmony_ci return ERR_PTR(ret); 6138c2ecf20Sopenharmony_ci} 614