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