1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * vsp1_sru.c -- R-Car VSP1 Super Resolution Unit 4 * 5 * Copyright (C) 2013 Renesas Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10 #include <linux/device.h> 11 #include <linux/gfp.h> 12 13 #include <media/v4l2-subdev.h> 14 15 #include "vsp1.h" 16 #include "vsp1_dl.h" 17 #include "vsp1_entity.h" 18 #include "vsp1_pipe.h" 19 #include "vsp1_sru.h" 20 21 #define SRU_MIN_SIZE 4U 22 #define SRU_MAX_SIZE 8190U 23 24 /* ----------------------------------------------------------------------------- 25 * Device Access 26 */ 27 28 static inline void vsp1_sru_write(struct vsp1_sru *sru, 29 struct vsp1_dl_body *dlb, u32 reg, u32 data) 30 { 31 vsp1_dl_body_write(dlb, reg, data); 32 } 33 34 /* ----------------------------------------------------------------------------- 35 * Controls 36 */ 37 38 #define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE | 0x1001) 39 40 struct vsp1_sru_param { 41 u32 ctrl0; 42 u32 ctrl2; 43 }; 44 45 #define VI6_SRU_CTRL0_PARAMS(p0, p1) \ 46 (((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) | \ 47 ((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT)) 48 49 #define VI6_SRU_CTRL2_PARAMS(p6, p7, p8) \ 50 (((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) | \ 51 ((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) | \ 52 ((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT)) 53 54 static const struct vsp1_sru_param vsp1_sru_params[] = { 55 { 56 .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, 57 .ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255), 58 }, { 59 .ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN, 60 .ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255), 61 }, { 62 .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, 63 .ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255), 64 }, { 65 .ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN, 66 .ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255), 67 }, { 68 .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, 69 .ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255), 70 }, { 71 .ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN, 72 .ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255), 73 }, 74 }; 75 76 static int sru_s_ctrl(struct v4l2_ctrl *ctrl) 77 { 78 struct vsp1_sru *sru = 79 container_of(ctrl->handler, struct vsp1_sru, ctrls); 80 81 switch (ctrl->id) { 82 case V4L2_CID_VSP1_SRU_INTENSITY: 83 sru->intensity = ctrl->val; 84 break; 85 } 86 87 return 0; 88 } 89 90 static const struct v4l2_ctrl_ops sru_ctrl_ops = { 91 .s_ctrl = sru_s_ctrl, 92 }; 93 94 static const struct v4l2_ctrl_config sru_intensity_control = { 95 .ops = &sru_ctrl_ops, 96 .id = V4L2_CID_VSP1_SRU_INTENSITY, 97 .name = "Intensity", 98 .type = V4L2_CTRL_TYPE_INTEGER, 99 .min = 1, 100 .max = 6, 101 .def = 1, 102 .step = 1, 103 }; 104 105 /* ----------------------------------------------------------------------------- 106 * V4L2 Subdevice Operations 107 */ 108 109 static int sru_enum_mbus_code(struct v4l2_subdev *subdev, 110 struct v4l2_subdev_state *sd_state, 111 struct v4l2_subdev_mbus_code_enum *code) 112 { 113 static const unsigned int codes[] = { 114 MEDIA_BUS_FMT_ARGB8888_1X32, 115 MEDIA_BUS_FMT_AYUV8_1X32, 116 }; 117 118 return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, codes, 119 ARRAY_SIZE(codes)); 120 } 121 122 static int sru_enum_frame_size(struct v4l2_subdev *subdev, 123 struct v4l2_subdev_state *sd_state, 124 struct v4l2_subdev_frame_size_enum *fse) 125 { 126 struct vsp1_sru *sru = to_sru(subdev); 127 struct v4l2_subdev_state *state; 128 struct v4l2_mbus_framefmt *format; 129 int ret = 0; 130 131 state = vsp1_entity_get_state(&sru->entity, sd_state, fse->which); 132 if (!state) 133 return -EINVAL; 134 135 format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); 136 137 mutex_lock(&sru->entity.lock); 138 139 if (fse->index || fse->code != format->code) { 140 ret = -EINVAL; 141 goto done; 142 } 143 144 if (fse->pad == SRU_PAD_SINK) { 145 fse->min_width = SRU_MIN_SIZE; 146 fse->max_width = SRU_MAX_SIZE; 147 fse->min_height = SRU_MIN_SIZE; 148 fse->max_height = SRU_MAX_SIZE; 149 } else { 150 fse->min_width = format->width; 151 fse->min_height = format->height; 152 if (format->width <= SRU_MAX_SIZE / 2 && 153 format->height <= SRU_MAX_SIZE / 2) { 154 fse->max_width = format->width * 2; 155 fse->max_height = format->height * 2; 156 } else { 157 fse->max_width = format->width; 158 fse->max_height = format->height; 159 } 160 } 161 162 done: 163 mutex_unlock(&sru->entity.lock); 164 return ret; 165 } 166 167 static void sru_try_format(struct vsp1_sru *sru, 168 struct v4l2_subdev_state *sd_state, 169 unsigned int pad, struct v4l2_mbus_framefmt *fmt) 170 { 171 struct v4l2_mbus_framefmt *format; 172 unsigned int input_area; 173 unsigned int output_area; 174 175 switch (pad) { 176 case SRU_PAD_SINK: 177 /* Default to YUV if the requested format is not supported. */ 178 if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 && 179 fmt->code != MEDIA_BUS_FMT_AYUV8_1X32) 180 fmt->code = MEDIA_BUS_FMT_AYUV8_1X32; 181 182 vsp1_entity_adjust_color_space(fmt); 183 184 fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE); 185 fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE); 186 break; 187 188 case SRU_PAD_SOURCE: 189 /* The SRU can't perform format conversion. */ 190 format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK); 191 fmt->code = format->code; 192 193 fmt->colorspace = format->colorspace; 194 fmt->xfer_func = format->xfer_func; 195 fmt->ycbcr_enc = format->ycbcr_enc; 196 fmt->quantization = format->quantization; 197 198 /* 199 * We can upscale by 2 in both direction, but not independently. 200 * Compare the input and output rectangles areas (avoiding 201 * integer overflows on the output): if the requested output 202 * area is larger than 1.5^2 the input area upscale by two, 203 * otherwise don't scale. 204 */ 205 input_area = format->width * format->height; 206 output_area = min(fmt->width, SRU_MAX_SIZE) 207 * min(fmt->height, SRU_MAX_SIZE); 208 209 if (fmt->width <= SRU_MAX_SIZE / 2 && 210 fmt->height <= SRU_MAX_SIZE / 2 && 211 output_area > input_area * 9 / 4) { 212 fmt->width = format->width * 2; 213 fmt->height = format->height * 2; 214 } else { 215 fmt->width = format->width; 216 fmt->height = format->height; 217 } 218 break; 219 } 220 221 fmt->field = V4L2_FIELD_NONE; 222 } 223 224 static int sru_set_format(struct v4l2_subdev *subdev, 225 struct v4l2_subdev_state *sd_state, 226 struct v4l2_subdev_format *fmt) 227 { 228 struct vsp1_sru *sru = to_sru(subdev); 229 struct v4l2_subdev_state *state; 230 struct v4l2_mbus_framefmt *format; 231 int ret = 0; 232 233 mutex_lock(&sru->entity.lock); 234 235 state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which); 236 if (!state) { 237 ret = -EINVAL; 238 goto done; 239 } 240 241 sru_try_format(sru, state, fmt->pad, &fmt->format); 242 243 format = v4l2_subdev_state_get_format(state, fmt->pad); 244 *format = fmt->format; 245 246 if (fmt->pad == SRU_PAD_SINK) { 247 /* Propagate the format to the source pad. */ 248 format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); 249 *format = fmt->format; 250 251 sru_try_format(sru, state, SRU_PAD_SOURCE, format); 252 } 253 254 done: 255 mutex_unlock(&sru->entity.lock); 256 return ret; 257 } 258 259 static const struct v4l2_subdev_pad_ops sru_pad_ops = { 260 .enum_mbus_code = sru_enum_mbus_code, 261 .enum_frame_size = sru_enum_frame_size, 262 .get_fmt = vsp1_subdev_get_pad_format, 263 .set_fmt = sru_set_format, 264 }; 265 266 static const struct v4l2_subdev_ops sru_ops = { 267 .pad = &sru_pad_ops, 268 }; 269 270 /* ----------------------------------------------------------------------------- 271 * VSP1 Entity Operations 272 */ 273 274 static void sru_configure_stream(struct vsp1_entity *entity, 275 struct v4l2_subdev_state *state, 276 struct vsp1_pipeline *pipe, 277 struct vsp1_dl_list *dl, 278 struct vsp1_dl_body *dlb) 279 { 280 const struct vsp1_sru_param *param; 281 struct vsp1_sru *sru = to_sru(&entity->subdev); 282 struct v4l2_mbus_framefmt *input; 283 struct v4l2_mbus_framefmt *output; 284 u32 ctrl0; 285 286 input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); 287 output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); 288 289 if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32) 290 ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3 291 | VI6_SRU_CTRL0_PARAM4; 292 else 293 ctrl0 = VI6_SRU_CTRL0_PARAM3; 294 295 if (input->width != output->width) 296 ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE; 297 298 param = &vsp1_sru_params[sru->intensity - 1]; 299 300 ctrl0 |= param->ctrl0; 301 302 vsp1_sru_write(sru, dlb, VI6_SRU_CTRL0, ctrl0); 303 vsp1_sru_write(sru, dlb, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5); 304 vsp1_sru_write(sru, dlb, VI6_SRU_CTRL2, param->ctrl2); 305 } 306 307 static unsigned int sru_max_width(struct vsp1_entity *entity, 308 struct v4l2_subdev_state *state, 309 struct vsp1_pipeline *pipe) 310 { 311 struct v4l2_mbus_framefmt *input; 312 struct v4l2_mbus_framefmt *output; 313 314 input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); 315 output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); 316 317 /* 318 * The maximum input width of the SRU is 288 input pixels, but 32 319 * pixels are reserved to support overlapping partition windows when 320 * scaling. 321 */ 322 if (input->width != output->width) 323 return 512; 324 else 325 return 256; 326 } 327 328 static void sru_partition(struct vsp1_entity *entity, 329 struct v4l2_subdev_state *state, 330 struct vsp1_pipeline *pipe, 331 struct vsp1_partition *partition, 332 unsigned int partition_idx, 333 struct v4l2_rect *window) 334 { 335 struct v4l2_mbus_framefmt *input; 336 struct v4l2_mbus_framefmt *output; 337 338 input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK); 339 output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE); 340 341 /* Adapt if SRUx2 is enabled. */ 342 if (input->width != output->width) { 343 window->width /= 2; 344 window->left /= 2; 345 window->height /= 2; 346 window->top /= 2; 347 } 348 349 partition->sru = *window; 350 } 351 352 static const struct vsp1_entity_operations sru_entity_ops = { 353 .configure_stream = sru_configure_stream, 354 .max_width = sru_max_width, 355 .partition = sru_partition, 356 }; 357 358 /* ----------------------------------------------------------------------------- 359 * Initialization and Cleanup 360 */ 361 362 struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1) 363 { 364 struct vsp1_sru *sru; 365 int ret; 366 367 sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL); 368 if (sru == NULL) 369 return ERR_PTR(-ENOMEM); 370 371 sru->entity.ops = &sru_entity_ops; 372 sru->entity.type = VSP1_ENTITY_SRU; 373 374 ret = vsp1_entity_init(vsp1, &sru->entity, "sru", 2, &sru_ops, 375 MEDIA_ENT_F_PROC_VIDEO_SCALER); 376 if (ret < 0) 377 return ERR_PTR(ret); 378 379 /* Initialize the control handler. */ 380 v4l2_ctrl_handler_init(&sru->ctrls, 1); 381 v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL); 382 383 sru->intensity = 1; 384 385 sru->entity.subdev.ctrl_handler = &sru->ctrls; 386 387 if (sru->ctrls.error) { 388 dev_err(vsp1->dev, "sru: failed to initialize controls\n"); 389 ret = sru->ctrls.error; 390 vsp1_entity_destroy(&sru->entity); 391 return ERR_PTR(ret); 392 } 393 394 return sru; 395 } 396