566 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for STM32 Digital Camera Memory Interface Pixel Processor4 *5 * Copyright (C) STMicroelectronics SA 20236 * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>7 * Alain Volmat <alain.volmat@foss.st.com>8 * for STMicroelectronics.9 */10 11#include <linux/vmalloc.h>12#include <linux/v4l2-mediabus.h>13#include <media/v4l2-rect.h>14#include <media/v4l2-subdev.h>15 16#include "dcmipp-common.h"17 18#define DCMIPP_P0FCTCR 0x50019#define DCMIPP_P0FCTCR_FRATE_MASK GENMASK(1, 0)20#define DCMIPP_P0SCSTR 0x50421#define DCMIPP_P0SCSTR_HSTART_SHIFT 022#define DCMIPP_P0SCSTR_VSTART_SHIFT 1623#define DCMIPP_P0SCSZR 0x50824#define DCMIPP_P0SCSZR_ENABLE BIT(31)25#define DCMIPP_P0SCSZR_HSIZE_SHIFT 026#define DCMIPP_P0SCSZR_VSIZE_SHIFT 1627#define DCMIPP_P0PPCR 0x5c028#define DCMIPP_P0PPCR_BSM_1_2 0x129#define DCMIPP_P0PPCR_BSM_1_4 0x230#define DCMIPP_P0PPCR_BSM_2_4 0x331#define DCMIPP_P0PPCR_BSM_MASK GENMASK(8, 7)32#define DCMIPP_P0PPCR_BSM_SHIFT 0x733#define DCMIPP_P0PPCR_LSM BIT(10)34#define DCMIPP_P0PPCR_OELS BIT(11)35 36#define IS_SINK(pad) (!(pad))37#define IS_SRC(pad) ((pad))38 39struct dcmipp_byteproc_pix_map {40 unsigned int code;41 unsigned int bpp;42};43 44#define PIXMAP_MBUS_BPP(mbus, byteperpixel) \45 { \46 .code = MEDIA_BUS_FMT_##mbus, \47 .bpp = byteperpixel, \48 }49static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {50 PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2),51 PIXMAP_MBUS_BPP(YUYV8_2X8, 2),52 PIXMAP_MBUS_BPP(YVYU8_2X8, 2),53 PIXMAP_MBUS_BPP(UYVY8_2X8, 2),54 PIXMAP_MBUS_BPP(VYUY8_2X8, 2),55 PIXMAP_MBUS_BPP(Y8_1X8, 1),56 PIXMAP_MBUS_BPP(SBGGR8_1X8, 1),57 PIXMAP_MBUS_BPP(SGBRG8_1X8, 1),58 PIXMAP_MBUS_BPP(SGRBG8_1X8, 1),59 PIXMAP_MBUS_BPP(SRGGB8_1X8, 1),60 PIXMAP_MBUS_BPP(JPEG_1X8, 1),61};62 63static const struct dcmipp_byteproc_pix_map *64dcmipp_byteproc_pix_map_by_code(u32 code)65{66 unsigned int i;67 68 for (i = 0; i < ARRAY_SIZE(dcmipp_byteproc_pix_map_list); i++) {69 if (dcmipp_byteproc_pix_map_list[i].code == code)70 return &dcmipp_byteproc_pix_map_list[i];71 }72 73 return NULL;74}75 76struct dcmipp_byteproc_device {77 struct dcmipp_ent_device ved;78 struct v4l2_subdev sd;79 struct device *dev;80 void __iomem *regs;81 bool streaming;82};83 84static const struct v4l2_mbus_framefmt fmt_default = {85 .width = DCMIPP_FMT_WIDTH_DEFAULT,86 .height = DCMIPP_FMT_HEIGHT_DEFAULT,87 .code = MEDIA_BUS_FMT_RGB565_2X8_LE,88 .field = V4L2_FIELD_NONE,89 .colorspace = DCMIPP_COLORSPACE_DEFAULT,90 .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,91 .quantization = DCMIPP_QUANTIZATION_DEFAULT,92 .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,93};94 95static const struct v4l2_rect crop_min = {96 .width = DCMIPP_FRAME_MIN_WIDTH,97 .height = DCMIPP_FRAME_MIN_HEIGHT,98 .top = 0,99 .left = 0,100};101 102static void dcmipp_byteproc_adjust_crop(struct v4l2_rect *r,103 struct v4l2_rect *compose)104{105 /* Disallow rectangles smaller than the minimal one. */106 v4l2_rect_set_min_size(r, &crop_min);107 v4l2_rect_map_inside(r, compose);108}109 110static void dcmipp_byteproc_adjust_compose(struct v4l2_rect *r,111 const struct v4l2_mbus_framefmt *fmt)112{113 r->top = 0;114 r->left = 0;115 116 /* Compose is not possible for JPEG or Bayer formats */117 if (fmt->code == MEDIA_BUS_FMT_JPEG_1X8 ||118 fmt->code == MEDIA_BUS_FMT_SBGGR8_1X8 ||119 fmt->code == MEDIA_BUS_FMT_SGBRG8_1X8 ||120 fmt->code == MEDIA_BUS_FMT_SGRBG8_1X8 ||121 fmt->code == MEDIA_BUS_FMT_SRGGB8_1X8) {122 r->width = fmt->width;123 r->height = fmt->height;124 return;125 }126 127 /* Adjust height - we can only perform 1/2 decimation */128 if (r->height <= (fmt->height / 2))129 r->height = fmt->height / 2;130 else131 r->height = fmt->height;132 133 /* Adjust width /2 or /4 for 8bits formats and /2 for 16bits formats */134 if (fmt->code == MEDIA_BUS_FMT_Y8_1X8 && r->width <= (fmt->width / 4))135 r->width = fmt->width / 4;136 else if (r->width <= (fmt->width / 2))137 r->width = fmt->width / 2;138 else139 r->width = fmt->width;140}141 142static void dcmipp_byteproc_adjust_fmt(struct v4l2_mbus_framefmt *fmt)143{144 const struct dcmipp_byteproc_pix_map *vpix;145 146 /* Only accept code in the pix map table */147 vpix = dcmipp_byteproc_pix_map_by_code(fmt->code);148 if (!vpix)149 fmt->code = fmt_default.code;150 151 fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,152 DCMIPP_FRAME_MAX_WIDTH) & ~1;153 fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,154 DCMIPP_FRAME_MAX_HEIGHT) & ~1;155 156 if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)157 fmt->field = fmt_default.field;158 159 dcmipp_colorimetry_clamp(fmt);160}161 162static int dcmipp_byteproc_init_state(struct v4l2_subdev *sd,163 struct v4l2_subdev_state *sd_state)164{165 unsigned int i;166 167 for (i = 0; i < sd->entity.num_pads; i++) {168 struct v4l2_mbus_framefmt *mf;169 struct v4l2_rect *r;170 171 mf = v4l2_subdev_state_get_format(sd_state, i);172 *mf = fmt_default;173 174 if (IS_SINK(i))175 r = v4l2_subdev_state_get_compose(sd_state, i);176 else177 r = v4l2_subdev_state_get_crop(sd_state, i);178 179 r->top = 0;180 r->left = 0;181 r->width = DCMIPP_FMT_WIDTH_DEFAULT;182 r->height = DCMIPP_FMT_HEIGHT_DEFAULT;183 }184 185 return 0;186}187 188static int189dcmipp_byteproc_enum_mbus_code(struct v4l2_subdev *sd,190 struct v4l2_subdev_state *sd_state,191 struct v4l2_subdev_mbus_code_enum *code)192{193 const struct dcmipp_byteproc_pix_map *vpix;194 struct v4l2_mbus_framefmt *sink_fmt;195 196 if (IS_SINK(code->pad)) {197 if (code->index >= ARRAY_SIZE(dcmipp_byteproc_pix_map_list))198 return -EINVAL;199 vpix = &dcmipp_byteproc_pix_map_list[code->index];200 code->code = vpix->code;201 } else {202 /* byteproc doesn't support transformation on format */203 if (code->index > 0)204 return -EINVAL;205 206 sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);207 code->code = sink_fmt->code;208 }209 210 return 0;211}212 213static int214dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd,215 struct v4l2_subdev_state *sd_state,216 struct v4l2_subdev_frame_size_enum *fse)217{218 struct v4l2_rect *compose;219 220 if (fse->index)221 return -EINVAL;222 223 fse->min_width = DCMIPP_FRAME_MIN_WIDTH;224 fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;225 226 if (IS_SINK(fse->pad)) {227 fse->max_width = DCMIPP_FRAME_MAX_WIDTH;228 fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;229 } else {230 compose = v4l2_subdev_state_get_compose(sd_state, 0);231 fse->max_width = compose->width;232 fse->max_height = compose->height;233 }234 235 return 0;236}237 238static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,239 struct v4l2_subdev_state *sd_state,240 struct v4l2_subdev_format *fmt)241{242 struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);243 struct v4l2_mbus_framefmt *mf;244 struct v4l2_rect *crop, *compose;245 246 if (byteproc->streaming)247 return -EBUSY;248 249 mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);250 251 crop = v4l2_subdev_state_get_crop(sd_state, 1);252 compose = v4l2_subdev_state_get_compose(sd_state, 0);253 254 if (IS_SRC(fmt->pad)) {255 fmt->format = *v4l2_subdev_state_get_format(sd_state, 0);256 fmt->format.width = crop->width;257 fmt->format.height = crop->height;258 } else {259 dcmipp_byteproc_adjust_fmt(&fmt->format);260 crop->top = 0;261 crop->left = 0;262 crop->width = fmt->format.width;263 crop->height = fmt->format.height;264 *compose = *crop;265 /* Set the same format on SOURCE pad as well */266 *v4l2_subdev_state_get_format(sd_state, 1) = fmt->format;267 }268 *mf = fmt->format;269 270 return 0;271}272 273static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd,274 struct v4l2_subdev_state *sd_state,275 struct v4l2_subdev_selection *s)276{277 struct v4l2_mbus_framefmt *sink_fmt;278 struct v4l2_rect *crop, *compose;279 280 /*281 * In the HW, the decimation block is located prior to the crop hence:282 * Compose is done on the sink pad283 * Crop is done on the src pad284 */285 if (IS_SINK(s->pad) &&286 (s->target == V4L2_SEL_TGT_CROP ||287 s->target == V4L2_SEL_TGT_CROP_BOUNDS ||288 s->target == V4L2_SEL_TGT_CROP_DEFAULT))289 return -EINVAL;290 291 if (IS_SRC(s->pad) &&292 (s->target == V4L2_SEL_TGT_COMPOSE ||293 s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||294 s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT))295 return -EINVAL;296 297 sink_fmt = v4l2_subdev_state_get_format(sd_state, 0);298 crop = v4l2_subdev_state_get_crop(sd_state, 1);299 compose = v4l2_subdev_state_get_compose(sd_state, 0);300 301 switch (s->target) {302 case V4L2_SEL_TGT_CROP:303 s->r = *crop;304 break;305 case V4L2_SEL_TGT_CROP_BOUNDS:306 case V4L2_SEL_TGT_CROP_DEFAULT:307 s->r = *compose;308 break;309 case V4L2_SEL_TGT_COMPOSE:310 s->r = *compose;311 break;312 case V4L2_SEL_TGT_COMPOSE_BOUNDS:313 case V4L2_SEL_TGT_COMPOSE_DEFAULT:314 s->r.top = 0;315 s->r.left = 0;316 s->r.width = sink_fmt->width;317 s->r.height = sink_fmt->height;318 break;319 default:320 return -EINVAL;321 }322 323 return 0;324}325 326static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,327 struct v4l2_subdev_state *sd_state,328 struct v4l2_subdev_selection *s)329{330 struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);331 struct v4l2_mbus_framefmt *mf;332 struct v4l2_rect *crop, *compose;333 334 /*335 * In the HW, the decimation block is located prior to the crop hence:336 * Compose is done on the sink pad337 * Crop is done on the src pad338 */339 if ((s->target == V4L2_SEL_TGT_CROP ||340 s->target == V4L2_SEL_TGT_CROP_BOUNDS ||341 s->target == V4L2_SEL_TGT_CROP_DEFAULT) && IS_SINK(s->pad))342 return -EINVAL;343 344 if ((s->target == V4L2_SEL_TGT_COMPOSE ||345 s->target == V4L2_SEL_TGT_COMPOSE_BOUNDS ||346 s->target == V4L2_SEL_TGT_COMPOSE_DEFAULT) && IS_SRC(s->pad))347 return -EINVAL;348 349 crop = v4l2_subdev_state_get_crop(sd_state, 1);350 compose = v4l2_subdev_state_get_compose(sd_state, 0);351 352 switch (s->target) {353 case V4L2_SEL_TGT_CROP:354 dcmipp_byteproc_adjust_crop(&s->r, compose);355 356 *crop = s->r;357 mf = v4l2_subdev_state_get_format(sd_state, 1);358 mf->width = s->r.width;359 mf->height = s->r.height;360 361 dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n",362 crop->width, crop->height, crop->left, crop->top);363 break;364 case V4L2_SEL_TGT_COMPOSE:365 mf = v4l2_subdev_state_get_format(sd_state, 0);366 dcmipp_byteproc_adjust_compose(&s->r, mf);367 *compose = s->r;368 *crop = s->r;369 370 mf = v4l2_subdev_state_get_format(sd_state, 1);371 mf->width = s->r.width;372 mf->height = s->r.height;373 374 dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n",375 compose->width, compose->height,376 compose->left, compose->top);377 break;378 default:379 return -EINVAL;380 }381 382 return 0;383}384 385static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = {386 .enum_mbus_code = dcmipp_byteproc_enum_mbus_code,387 .enum_frame_size = dcmipp_byteproc_enum_frame_size,388 .get_fmt = v4l2_subdev_get_fmt,389 .set_fmt = dcmipp_byteproc_set_fmt,390 .get_selection = dcmipp_byteproc_get_selection,391 .set_selection = dcmipp_byteproc_set_selection,392};393 394static int dcmipp_byteproc_configure_scale_crop395 (struct dcmipp_byteproc_device *byteproc)396{397 const struct dcmipp_byteproc_pix_map *vpix;398 struct v4l2_subdev_state *state;399 struct v4l2_mbus_framefmt *sink_fmt;400 u32 hprediv, vprediv;401 struct v4l2_rect *compose, *crop;402 u32 val = 0;403 404 state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd);405 sink_fmt = v4l2_subdev_state_get_format(state, 0);406 compose = v4l2_subdev_state_get_compose(state, 0);407 crop = v4l2_subdev_state_get_crop(state, 1);408 v4l2_subdev_unlock_state(state);409 410 /* find output format bpp */411 vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code);412 if (!vpix)413 return -EINVAL;414 415 /* clear decimation/crop */416 reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK);417 reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM);418 reg_write(byteproc, DCMIPP_P0SCSTR, 0);419 reg_write(byteproc, DCMIPP_P0SCSZR, 0);420 421 /* Ignore decimation/crop with JPEG */422 if (vpix->code == MEDIA_BUS_FMT_JPEG_1X8)423 return 0;424 425 /* decimation */426 hprediv = sink_fmt->width / compose->width;427 if (hprediv == 4)428 val |= DCMIPP_P0PPCR_BSM_1_4 << DCMIPP_P0PPCR_BSM_SHIFT;429 else if ((vpix->code == MEDIA_BUS_FMT_Y8_1X8) && (hprediv == 2))430 val |= DCMIPP_P0PPCR_BSM_1_2 << DCMIPP_P0PPCR_BSM_SHIFT;431 else if (hprediv == 2)432 val |= DCMIPP_P0PPCR_BSM_2_4 << DCMIPP_P0PPCR_BSM_SHIFT;433 434 vprediv = sink_fmt->height / compose->height;435 if (vprediv == 2)436 val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;437 438 /* decimate using bytes and lines skipping */439 if (val) {440 reg_set(byteproc, DCMIPP_P0PPCR, val);441 442 dev_dbg(byteproc->dev, "decimate to %dx%d [prediv=%dx%d]\n",443 compose->width, compose->height,444 hprediv, vprediv);445 }446 447 dev_dbg(byteproc->dev, "crop to %dx%d\n", crop->width, crop->height);448 449 /* expressed in 32-bits words on X axis, lines on Y axis */450 reg_write(byteproc, DCMIPP_P0SCSTR,451 (((crop->left * vpix->bpp) / 4) <<452 DCMIPP_P0SCSTR_HSTART_SHIFT) |453 (crop->top << DCMIPP_P0SCSTR_VSTART_SHIFT));454 reg_write(byteproc, DCMIPP_P0SCSZR,455 DCMIPP_P0SCSZR_ENABLE |456 (((crop->width * vpix->bpp) / 4) <<457 DCMIPP_P0SCSZR_HSIZE_SHIFT) |458 (crop->height << DCMIPP_P0SCSZR_VSIZE_SHIFT));459 460 return 0;461}462 463static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable)464{465 struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);466 struct v4l2_subdev *s_subdev;467 struct media_pad *pad;468 int ret = 0;469 470 /* Get source subdev */471 pad = media_pad_remote_pad_first(&sd->entity.pads[0]);472 if (!pad || !is_media_entity_v4l2_subdev(pad->entity))473 return -EINVAL;474 s_subdev = media_entity_to_v4l2_subdev(pad->entity);475 476 if (enable) {477 ret = dcmipp_byteproc_configure_scale_crop(byteproc);478 if (ret)479 return ret;480 481 ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);482 if (ret < 0) {483 dev_err(byteproc->dev,484 "failed to start source subdev streaming (%d)\n",485 ret);486 return ret;487 }488 } else {489 ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);490 if (ret < 0) {491 dev_err(byteproc->dev,492 "failed to stop source subdev streaming (%d)\n",493 ret);494 return ret;495 }496 }497 498 byteproc->streaming = enable;499 500 return 0;501}502 503static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = {504 .s_stream = dcmipp_byteproc_s_stream,505};506 507static const struct v4l2_subdev_ops dcmipp_byteproc_ops = {508 .pad = &dcmipp_byteproc_pad_ops,509 .video = &dcmipp_byteproc_video_ops,510};511 512static void dcmipp_byteproc_release(struct v4l2_subdev *sd)513{514 struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);515 516 kfree(byteproc);517}518 519static const struct v4l2_subdev_internal_ops dcmipp_byteproc_int_ops = {520 .init_state = dcmipp_byteproc_init_state,521 .release = dcmipp_byteproc_release,522};523 524void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved)525{526 struct dcmipp_byteproc_device *byteproc =527 container_of(ved, struct dcmipp_byteproc_device, ved);528 529 dcmipp_ent_sd_unregister(ved, &byteproc->sd);530}531 532struct dcmipp_ent_device *533dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,534 struct v4l2_device *v4l2_dev, void __iomem *regs)535{536 struct dcmipp_byteproc_device *byteproc;537 const unsigned long pads_flag[] = {538 MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,539 };540 int ret;541 542 /* Allocate the byteproc struct */543 byteproc = kzalloc(sizeof(*byteproc), GFP_KERNEL);544 if (!byteproc)545 return ERR_PTR(-ENOMEM);546 547 byteproc->regs = regs;548 549 /* Initialize ved and sd */550 ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd,551 v4l2_dev, entity_name,552 MEDIA_ENT_F_PROC_VIDEO_SCALER,553 ARRAY_SIZE(pads_flag), pads_flag,554 &dcmipp_byteproc_int_ops,555 &dcmipp_byteproc_ops,556 NULL, NULL);557 if (ret) {558 kfree(byteproc);559 return ERR_PTR(ret);560 }561 562 byteproc->dev = dev;563 564 return &byteproc->ved;565}566