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