1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters 4 * 5 * Copyright (C) 2013-2014 Renesas Electronics Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10#include <media/v4l2-subdev.h> 11 12#include "vsp1.h" 13#include "vsp1_rwpf.h" 14#include "vsp1_video.h" 15 16#define RWPF_MIN_WIDTH 1 17#define RWPF_MIN_HEIGHT 1 18 19struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, 20 struct v4l2_subdev_pad_config *config) 21{ 22 return v4l2_subdev_get_try_crop(&rwpf->entity.subdev, config, 23 RWPF_PAD_SINK); 24} 25 26/* ----------------------------------------------------------------------------- 27 * V4L2 Subdevice Pad Operations 28 */ 29 30static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, 31 struct v4l2_subdev_pad_config *cfg, 32 struct v4l2_subdev_mbus_code_enum *code) 33{ 34 static const unsigned int codes[] = { 35 MEDIA_BUS_FMT_ARGB8888_1X32, 36 MEDIA_BUS_FMT_AHSV8888_1X32, 37 MEDIA_BUS_FMT_AYUV8_1X32, 38 }; 39 40 if (code->index >= ARRAY_SIZE(codes)) 41 return -EINVAL; 42 43 code->code = codes[code->index]; 44 45 return 0; 46} 47 48static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, 49 struct v4l2_subdev_pad_config *cfg, 50 struct v4l2_subdev_frame_size_enum *fse) 51{ 52 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 53 54 return vsp1_subdev_enum_frame_size(subdev, cfg, fse, RWPF_MIN_WIDTH, 55 RWPF_MIN_HEIGHT, rwpf->max_width, 56 rwpf->max_height); 57} 58 59static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, 60 struct v4l2_subdev_pad_config *cfg, 61 struct v4l2_subdev_format *fmt) 62{ 63 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 64 struct v4l2_subdev_pad_config *config; 65 struct v4l2_mbus_framefmt *format; 66 int ret = 0; 67 68 mutex_lock(&rwpf->entity.lock); 69 70 config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, fmt->which); 71 if (!config) { 72 ret = -EINVAL; 73 goto done; 74 } 75 76 /* Default to YUV if the requested format is not supported. */ 77 if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 && 78 fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32 && 79 fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32) 80 fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32; 81 82 format = vsp1_entity_get_pad_format(&rwpf->entity, config, fmt->pad); 83 84 if (fmt->pad == RWPF_PAD_SOURCE) { 85 /* 86 * The RWPF performs format conversion but can't scale, only the 87 * format code can be changed on the source pad. 88 */ 89 format->code = fmt->format.code; 90 fmt->format = *format; 91 goto done; 92 } 93 94 format->code = fmt->format.code; 95 format->width = clamp_t(unsigned int, fmt->format.width, 96 RWPF_MIN_WIDTH, rwpf->max_width); 97 format->height = clamp_t(unsigned int, fmt->format.height, 98 RWPF_MIN_HEIGHT, rwpf->max_height); 99 format->field = V4L2_FIELD_NONE; 100 format->colorspace = V4L2_COLORSPACE_SRGB; 101 102 fmt->format = *format; 103 104 if (rwpf->entity.type == VSP1_ENTITY_RPF) { 105 struct v4l2_rect *crop; 106 107 /* Update the sink crop rectangle. */ 108 crop = vsp1_rwpf_get_crop(rwpf, config); 109 crop->left = 0; 110 crop->top = 0; 111 crop->width = fmt->format.width; 112 crop->height = fmt->format.height; 113 } 114 115 /* Propagate the format to the source pad. */ 116 format = vsp1_entity_get_pad_format(&rwpf->entity, config, 117 RWPF_PAD_SOURCE); 118 *format = fmt->format; 119 120 if (rwpf->flip.rotate) { 121 format->width = fmt->format.height; 122 format->height = fmt->format.width; 123 } 124 125done: 126 mutex_unlock(&rwpf->entity.lock); 127 return ret; 128} 129 130static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, 131 struct v4l2_subdev_pad_config *cfg, 132 struct v4l2_subdev_selection *sel) 133{ 134 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 135 struct v4l2_subdev_pad_config *config; 136 struct v4l2_mbus_framefmt *format; 137 int ret = 0; 138 139 /* 140 * Cropping is only supported on the RPF and is implemented on the sink 141 * pad. 142 */ 143 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) 144 return -EINVAL; 145 146 mutex_lock(&rwpf->entity.lock); 147 148 config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); 149 if (!config) { 150 ret = -EINVAL; 151 goto done; 152 } 153 154 switch (sel->target) { 155 case V4L2_SEL_TGT_CROP: 156 sel->r = *vsp1_rwpf_get_crop(rwpf, config); 157 break; 158 159 case V4L2_SEL_TGT_CROP_BOUNDS: 160 format = vsp1_entity_get_pad_format(&rwpf->entity, config, 161 RWPF_PAD_SINK); 162 sel->r.left = 0; 163 sel->r.top = 0; 164 sel->r.width = format->width; 165 sel->r.height = format->height; 166 break; 167 168 default: 169 ret = -EINVAL; 170 break; 171 } 172 173done: 174 mutex_unlock(&rwpf->entity.lock); 175 return ret; 176} 177 178static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, 179 struct v4l2_subdev_pad_config *cfg, 180 struct v4l2_subdev_selection *sel) 181{ 182 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 183 struct v4l2_subdev_pad_config *config; 184 struct v4l2_mbus_framefmt *format; 185 struct v4l2_rect *crop; 186 int ret = 0; 187 188 /* 189 * Cropping is only supported on the RPF and is implemented on the sink 190 * pad. 191 */ 192 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) 193 return -EINVAL; 194 195 if (sel->target != V4L2_SEL_TGT_CROP) 196 return -EINVAL; 197 198 mutex_lock(&rwpf->entity.lock); 199 200 config = vsp1_entity_get_pad_config(&rwpf->entity, cfg, sel->which); 201 if (!config) { 202 ret = -EINVAL; 203 goto done; 204 } 205 206 /* Make sure the crop rectangle is entirely contained in the image. */ 207 format = vsp1_entity_get_pad_format(&rwpf->entity, config, 208 RWPF_PAD_SINK); 209 210 /* 211 * Restrict the crop rectangle coordinates to multiples of 2 to avoid 212 * shifting the color plane. 213 */ 214 if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { 215 sel->r.left = ALIGN(sel->r.left, 2); 216 sel->r.top = ALIGN(sel->r.top, 2); 217 sel->r.width = round_down(sel->r.width, 2); 218 sel->r.height = round_down(sel->r.height, 2); 219 } 220 221 sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); 222 sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); 223 sel->r.width = min_t(unsigned int, sel->r.width, 224 format->width - sel->r.left); 225 sel->r.height = min_t(unsigned int, sel->r.height, 226 format->height - sel->r.top); 227 228 crop = vsp1_rwpf_get_crop(rwpf, config); 229 *crop = sel->r; 230 231 /* Propagate the format to the source pad. */ 232 format = vsp1_entity_get_pad_format(&rwpf->entity, config, 233 RWPF_PAD_SOURCE); 234 format->width = crop->width; 235 format->height = crop->height; 236 237done: 238 mutex_unlock(&rwpf->entity.lock); 239 return ret; 240} 241 242const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { 243 .init_cfg = vsp1_entity_init_cfg, 244 .enum_mbus_code = vsp1_rwpf_enum_mbus_code, 245 .enum_frame_size = vsp1_rwpf_enum_frame_size, 246 .get_fmt = vsp1_subdev_get_pad_format, 247 .set_fmt = vsp1_rwpf_set_format, 248 .get_selection = vsp1_rwpf_get_selection, 249 .set_selection = vsp1_rwpf_set_selection, 250}; 251 252/* ----------------------------------------------------------------------------- 253 * Controls 254 */ 255 256static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl) 257{ 258 struct vsp1_rwpf *rwpf = 259 container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 260 261 switch (ctrl->id) { 262 case V4L2_CID_ALPHA_COMPONENT: 263 rwpf->alpha = ctrl->val; 264 break; 265 } 266 267 return 0; 268} 269 270static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { 271 .s_ctrl = vsp1_rwpf_s_ctrl, 272}; 273 274int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) 275{ 276 v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); 277 v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, 278 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); 279 280 rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls; 281 282 return rwpf->ctrls.error; 283} 284