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_entity.h" 14 #include "vsp1_rwpf.h" 15 #include "vsp1_video.h" 16 17 #define RWPF_MIN_WIDTH 1 18 #define RWPF_MIN_HEIGHT 1 19 20 /* ----------------------------------------------------------------------------- 21 * V4L2 Subdevice Operations 22 */ 23 24 static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, 25 struct v4l2_subdev_state *sd_state, 26 struct v4l2_subdev_mbus_code_enum *code) 27 { 28 static const unsigned int codes[] = { 29 MEDIA_BUS_FMT_ARGB8888_1X32, 30 MEDIA_BUS_FMT_AHSV8888_1X32, 31 MEDIA_BUS_FMT_AYUV8_1X32, 32 }; 33 34 if (code->index >= ARRAY_SIZE(codes)) 35 return -EINVAL; 36 37 code->code = codes[code->index]; 38 39 if (code->pad == RWPF_PAD_SOURCE && 40 code->code == MEDIA_BUS_FMT_AYUV8_1X32) 41 code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC 42 | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION; 43 44 return 0; 45 } 46 47 static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev, 48 struct v4l2_subdev_state *sd_state, 49 struct v4l2_subdev_frame_size_enum *fse) 50 { 51 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 52 53 return vsp1_subdev_enum_frame_size(subdev, sd_state, fse, 54 RWPF_MIN_WIDTH, 55 RWPF_MIN_HEIGHT, rwpf->max_width, 56 rwpf->max_height); 57 } 58 59 static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, 60 struct v4l2_subdev_state *sd_state, 61 struct v4l2_subdev_format *fmt) 62 { 63 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 64 struct v4l2_subdev_state *state; 65 struct v4l2_mbus_framefmt *format; 66 int ret = 0; 67 68 mutex_lock(&rwpf->entity.lock); 69 70 state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which); 71 if (!state) { 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 = v4l2_subdev_state_get_format(state, fmt->pad); 83 84 if (fmt->pad == RWPF_PAD_SOURCE) { 85 const struct v4l2_mbus_framefmt *sink_format = 86 v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); 87 u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC; 88 bool csc; 89 90 /* 91 * The RWPF performs format conversion but can't scale, only the 92 * format code, encoding and quantization can be changed on the 93 * source pad when converting between RGB and YUV. 94 */ 95 if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 && 96 fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32) 97 format->code = fmt->format.code; 98 else 99 format->code = sink_format->code; 100 101 /* 102 * Encoding and quantization can only be configured when YCbCr 103 * <-> RGB is enabled. The V4L2 API requires userspace to set 104 * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these 105 * conditions is not met, use the encoding and quantization 106 * values from the sink pad. 107 */ 108 csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) != 109 (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32); 110 111 if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) { 112 format->ycbcr_enc = fmt->format.ycbcr_enc; 113 format->quantization = fmt->format.quantization; 114 } else { 115 format->ycbcr_enc = sink_format->ycbcr_enc; 116 format->quantization = sink_format->quantization; 117 } 118 119 vsp1_entity_adjust_color_space(format); 120 121 fmt->format = *format; 122 fmt->format.flags = flags; 123 124 goto done; 125 } 126 127 format->code = fmt->format.code; 128 format->width = clamp_t(unsigned int, fmt->format.width, 129 RWPF_MIN_WIDTH, rwpf->max_width); 130 format->height = clamp_t(unsigned int, fmt->format.height, 131 RWPF_MIN_HEIGHT, rwpf->max_height); 132 format->field = V4L2_FIELD_NONE; 133 134 format->colorspace = fmt->format.colorspace; 135 format->xfer_func = fmt->format.xfer_func; 136 format->ycbcr_enc = fmt->format.ycbcr_enc; 137 format->quantization = fmt->format.quantization; 138 139 vsp1_entity_adjust_color_space(format); 140 141 fmt->format = *format; 142 143 if (rwpf->entity.type == VSP1_ENTITY_RPF) { 144 struct v4l2_rect *crop; 145 146 /* Update the sink crop rectangle. */ 147 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); 148 crop->left = 0; 149 crop->top = 0; 150 crop->width = fmt->format.width; 151 crop->height = fmt->format.height; 152 } 153 154 /* Propagate the format to the source pad. */ 155 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); 156 *format = fmt->format; 157 158 if (rwpf->flip.rotate) { 159 format->width = fmt->format.height; 160 format->height = fmt->format.width; 161 } 162 163 done: 164 mutex_unlock(&rwpf->entity.lock); 165 return ret; 166 } 167 168 static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, 169 struct v4l2_subdev_state *sd_state, 170 struct v4l2_subdev_selection *sel) 171 { 172 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 173 struct v4l2_subdev_state *state; 174 struct v4l2_mbus_framefmt *format; 175 int ret = 0; 176 177 /* 178 * Cropping is only supported on the RPF and is implemented on the sink 179 * pad. 180 */ 181 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) 182 return -EINVAL; 183 184 mutex_lock(&rwpf->entity.lock); 185 186 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); 187 if (!state) { 188 ret = -EINVAL; 189 goto done; 190 } 191 192 switch (sel->target) { 193 case V4L2_SEL_TGT_CROP: 194 sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); 195 break; 196 197 case V4L2_SEL_TGT_CROP_BOUNDS: 198 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); 199 sel->r.left = 0; 200 sel->r.top = 0; 201 sel->r.width = format->width; 202 sel->r.height = format->height; 203 break; 204 205 default: 206 ret = -EINVAL; 207 break; 208 } 209 210 done: 211 mutex_unlock(&rwpf->entity.lock); 212 return ret; 213 } 214 215 static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, 216 struct v4l2_subdev_state *sd_state, 217 struct v4l2_subdev_selection *sel) 218 { 219 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 220 struct v4l2_subdev_state *state; 221 struct v4l2_mbus_framefmt *format; 222 struct v4l2_rect *crop; 223 int ret = 0; 224 225 /* 226 * Cropping is only supported on the RPF and is implemented on the sink 227 * pad. 228 */ 229 if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK) 230 return -EINVAL; 231 232 if (sel->target != V4L2_SEL_TGT_CROP) 233 return -EINVAL; 234 235 mutex_lock(&rwpf->entity.lock); 236 237 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); 238 if (!state) { 239 ret = -EINVAL; 240 goto done; 241 } 242 243 /* Make sure the crop rectangle is entirely contained in the image. */ 244 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK); 245 246 /* 247 * Restrict the crop rectangle coordinates to multiples of 2 to avoid 248 * shifting the color plane. 249 */ 250 if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) { 251 sel->r.left = ALIGN(sel->r.left, 2); 252 sel->r.top = ALIGN(sel->r.top, 2); 253 sel->r.width = round_down(sel->r.width, 2); 254 sel->r.height = round_down(sel->r.height, 2); 255 } 256 257 sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2); 258 sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2); 259 sel->r.width = min_t(unsigned int, sel->r.width, 260 format->width - sel->r.left); 261 sel->r.height = min_t(unsigned int, sel->r.height, 262 format->height - sel->r.top); 263 264 crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK); 265 *crop = sel->r; 266 267 /* Propagate the format to the source pad. */ 268 format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE); 269 format->width = crop->width; 270 format->height = crop->height; 271 272 done: 273 mutex_unlock(&rwpf->entity.lock); 274 return ret; 275 } 276 277 static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { 278 .enum_mbus_code = vsp1_rwpf_enum_mbus_code, 279 .enum_frame_size = vsp1_rwpf_enum_frame_size, 280 .get_fmt = vsp1_subdev_get_pad_format, 281 .set_fmt = vsp1_rwpf_set_format, 282 .get_selection = vsp1_rwpf_get_selection, 283 .set_selection = vsp1_rwpf_set_selection, 284 }; 285 286 const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = { 287 .pad = &vsp1_rwpf_pad_ops, 288 }; 289 290 /* ----------------------------------------------------------------------------- 291 * Controls 292 */ 293 294 static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl) 295 { 296 struct vsp1_rwpf *rwpf = 297 container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 298 299 switch (ctrl->id) { 300 case V4L2_CID_ALPHA_COMPONENT: 301 rwpf->alpha = ctrl->val; 302 break; 303 } 304 305 return 0; 306 } 307 308 static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { 309 .s_ctrl = vsp1_rwpf_s_ctrl, 310 }; 311 312 int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) 313 { 314 v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); 315 v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, 316 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); 317 318 rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls; 319 320 return rwpf->ctrls.error; 321 } 322