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