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_state_get_crop(sd_state, RWPF_PAD_SINK); 23 } 24 25 /* ----------------------------------------------------------------------------- 26 * V4L2 Subdevice Operations 27 */ 28 29 static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev, 30 struct v4l2_subdev_state *sd_state, 31 struct v4l2_subdev_mbus_code_enum *code) 32 { 33 static const unsigned int codes[] = { 34 MEDIA_BUS_FMT_ARGB8888_1X32, 35 MEDIA_BUS_FMT_AHSV8888_1X32, 36 MEDIA_BUS_FMT_AYUV8_1X32, 37 }; 38 39 if (code->index >= ARRAY_SIZE(codes)) 40 return -EINVAL; 41 42 code->code = codes[code->index]; 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 = vsp1_entity_get_pad_format(&rwpf->entity, state, 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, state); 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, state, 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 125 done: 126 mutex_unlock(&rwpf->entity.lock); 127 return ret; 128 } 129 130 static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev, 131 struct v4l2_subdev_state *sd_state, 132 struct v4l2_subdev_selection *sel) 133 { 134 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 135 struct v4l2_subdev_state *state; 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 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); 149 if (!state) { 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, state); 157 break; 158 159 case V4L2_SEL_TGT_CROP_BOUNDS: 160 format = vsp1_entity_get_pad_format(&rwpf->entity, state, 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 173 done: 174 mutex_unlock(&rwpf->entity.lock); 175 return ret; 176 } 177 178 static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev, 179 struct v4l2_subdev_state *sd_state, 180 struct v4l2_subdev_selection *sel) 181 { 182 struct vsp1_rwpf *rwpf = to_rwpf(subdev); 183 struct v4l2_subdev_state *state; 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 state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which); 201 if (!state) { 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, state, 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, state); 229 *crop = sel->r; 230 231 /* Propagate the format to the source pad. */ 232 format = vsp1_entity_get_pad_format(&rwpf->entity, state, 233 RWPF_PAD_SOURCE); 234 format->width = crop->width; 235 format->height = crop->height; 236 237 done: 238 mutex_unlock(&rwpf->entity.lock); 239 return ret; 240 } 241 242 static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = { 243 .enum_mbus_code = vsp1_rwpf_enum_mbus_code, 244 .enum_frame_size = vsp1_rwpf_enum_frame_size, 245 .get_fmt = vsp1_subdev_get_pad_format, 246 .set_fmt = vsp1_rwpf_set_format, 247 .get_selection = vsp1_rwpf_get_selection, 248 .set_selection = vsp1_rwpf_set_selection, 249 }; 250 251 const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops = { 252 .pad = &vsp1_rwpf_pad_ops, 253 }; 254 255 /* ----------------------------------------------------------------------------- 256 * Controls 257 */ 258 259 static int vsp1_rwpf_s_ctrl(struct v4l2_ctrl *ctrl) 260 { 261 struct vsp1_rwpf *rwpf = 262 container_of(ctrl->handler, struct vsp1_rwpf, ctrls); 263 264 switch (ctrl->id) { 265 case V4L2_CID_ALPHA_COMPONENT: 266 rwpf->alpha = ctrl->val; 267 break; 268 } 269 270 return 0; 271 } 272 273 static const struct v4l2_ctrl_ops vsp1_rwpf_ctrl_ops = { 274 .s_ctrl = vsp1_rwpf_s_ctrl, 275 }; 276 277 int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols) 278 { 279 v4l2_ctrl_handler_init(&rwpf->ctrls, ncontrols + 1); 280 v4l2_ctrl_new_std(&rwpf->ctrls, &vsp1_rwpf_ctrl_ops, 281 V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 255); 282 283 rwpf->entity.subdev.ctrl_handler = &rwpf->ctrls; 284 285 return rwpf->ctrls.error; 286 } 287