404 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/bug.h>7#include <linux/device.h>8#include <linux/minmax.h>9 10#include <media/media-entity.h>11#include <media/mipi-csi2.h>12#include <media/v4l2-ctrls.h>13#include <media/v4l2-subdev.h>14 15#include "ipu6-bus.h"16#include "ipu6-isys.h"17#include "ipu6-isys-subdev.h"18 19unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)20{21 switch (code) {22 case MEDIA_BUS_FMT_RGB888_1X24:23 case MEDIA_BUS_FMT_META_24:24 return 24;25 case MEDIA_BUS_FMT_RGB565_1X16:26 case MEDIA_BUS_FMT_UYVY8_1X16:27 case MEDIA_BUS_FMT_YUYV8_1X16:28 case MEDIA_BUS_FMT_META_16:29 return 16;30 case MEDIA_BUS_FMT_SBGGR12_1X12:31 case MEDIA_BUS_FMT_SGBRG12_1X12:32 case MEDIA_BUS_FMT_SGRBG12_1X12:33 case MEDIA_BUS_FMT_SRGGB12_1X12:34 case MEDIA_BUS_FMT_META_12:35 return 12;36 case MEDIA_BUS_FMT_SBGGR10_1X10:37 case MEDIA_BUS_FMT_SGBRG10_1X10:38 case MEDIA_BUS_FMT_SGRBG10_1X10:39 case MEDIA_BUS_FMT_SRGGB10_1X10:40 case MEDIA_BUS_FMT_META_10:41 return 10;42 case MEDIA_BUS_FMT_SBGGR8_1X8:43 case MEDIA_BUS_FMT_SGBRG8_1X8:44 case MEDIA_BUS_FMT_SGRBG8_1X8:45 case MEDIA_BUS_FMT_SRGGB8_1X8:46 case MEDIA_BUS_FMT_META_8:47 return 8;48 default:49 WARN_ON(1);50 return 8;51 }52}53 54unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)55{56 switch (code) {57 case MEDIA_BUS_FMT_RGB565_1X16:58 return MIPI_CSI2_DT_RGB565;59 case MEDIA_BUS_FMT_RGB888_1X24:60 return MIPI_CSI2_DT_RGB888;61 case MEDIA_BUS_FMT_UYVY8_1X16:62 case MEDIA_BUS_FMT_YUYV8_1X16:63 return MIPI_CSI2_DT_YUV422_8B;64 case MEDIA_BUS_FMT_SBGGR16_1X16:65 case MEDIA_BUS_FMT_SGBRG16_1X16:66 case MEDIA_BUS_FMT_SGRBG16_1X16:67 case MEDIA_BUS_FMT_SRGGB16_1X16:68 return MIPI_CSI2_DT_RAW16;69 case MEDIA_BUS_FMT_SBGGR12_1X12:70 case MEDIA_BUS_FMT_SGBRG12_1X12:71 case MEDIA_BUS_FMT_SGRBG12_1X12:72 case MEDIA_BUS_FMT_SRGGB12_1X12:73 return MIPI_CSI2_DT_RAW12;74 case MEDIA_BUS_FMT_SBGGR10_1X10:75 case MEDIA_BUS_FMT_SGBRG10_1X10:76 case MEDIA_BUS_FMT_SGRBG10_1X10:77 case MEDIA_BUS_FMT_SRGGB10_1X10:78 return MIPI_CSI2_DT_RAW10;79 case MEDIA_BUS_FMT_SBGGR8_1X8:80 case MEDIA_BUS_FMT_SGBRG8_1X8:81 case MEDIA_BUS_FMT_SGRBG8_1X8:82 case MEDIA_BUS_FMT_SRGGB8_1X8:83 return MIPI_CSI2_DT_RAW8;84 default:85 /* return unavailable MIPI data type - 0x3f */86 WARN_ON(1);87 return 0x3f;88 }89}90 91bool ipu6_isys_is_bayer_format(u32 code)92{93 switch (ipu6_isys_mbus_code_to_mipi(code)) {94 case MIPI_CSI2_DT_RAW8:95 case MIPI_CSI2_DT_RAW10:96 case MIPI_CSI2_DT_RAW12:97 case MIPI_CSI2_DT_RAW14:98 case MIPI_CSI2_DT_RAW16:99 case MIPI_CSI2_DT_RAW20:100 case MIPI_CSI2_DT_RAW24:101 case MIPI_CSI2_DT_RAW28:102 return true;103 default:104 return false;105 }106}107 108u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)109{110 static const u32 code_map[] = {111 MEDIA_BUS_FMT_SRGGB8_1X8,112 MEDIA_BUS_FMT_SGRBG8_1X8,113 MEDIA_BUS_FMT_SGBRG8_1X8,114 MEDIA_BUS_FMT_SBGGR8_1X8,115 MEDIA_BUS_FMT_SRGGB10_1X10,116 MEDIA_BUS_FMT_SGRBG10_1X10,117 MEDIA_BUS_FMT_SGBRG10_1X10,118 MEDIA_BUS_FMT_SBGGR10_1X10,119 MEDIA_BUS_FMT_SRGGB12_1X12,120 MEDIA_BUS_FMT_SGRBG12_1X12,121 MEDIA_BUS_FMT_SGBRG12_1X12,122 MEDIA_BUS_FMT_SBGGR12_1X12,123 MEDIA_BUS_FMT_SRGGB16_1X16,124 MEDIA_BUS_FMT_SGRBG16_1X16,125 MEDIA_BUS_FMT_SGBRG16_1X16,126 MEDIA_BUS_FMT_SBGGR16_1X16,127 };128 u32 i;129 130 for (i = 0; i < ARRAY_SIZE(code_map); i++)131 if (code_map[i] == code)132 break;133 134 if (WARN_ON(i == ARRAY_SIZE(code_map)))135 return code;136 137 return code_map[i ^ (((y & 1) << 1) | (x & 1))];138}139 140int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,141 struct v4l2_subdev_state *state,142 struct v4l2_subdev_format *format)143{144 struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);145 struct v4l2_mbus_framefmt *fmt;146 struct v4l2_rect *crop;147 u32 code = asd->supported_codes[0];148 u32 other_pad, other_stream;149 unsigned int i;150 int ret;151 152 /* No transcoding, source and sink formats must match. */153 if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&154 sd->entity.num_pads > 1)155 return v4l2_subdev_get_fmt(sd, state, format);156 157 format->format.width = clamp(format->format.width, IPU6_ISYS_MIN_WIDTH,158 IPU6_ISYS_MAX_WIDTH);159 format->format.height = clamp(format->format.height,160 IPU6_ISYS_MIN_HEIGHT,161 IPU6_ISYS_MAX_HEIGHT);162 163 for (i = 0; asd->supported_codes[i]; i++) {164 if (asd->supported_codes[i] == format->format.code) {165 code = asd->supported_codes[i];166 break;167 }168 }169 format->format.code = code;170 format->format.field = V4L2_FIELD_NONE;171 172 /* Store the format and propagate it to the source pad. */173 fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);174 if (!fmt)175 return -EINVAL;176 177 *fmt = format->format;178 179 if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))180 return 0;181 182 /* propagate format to following source pad */183 fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,184 format->stream);185 if (!fmt)186 return -EINVAL;187 188 *fmt = format->format;189 190 ret = v4l2_subdev_routing_find_opposite_end(&state->routing,191 format->pad,192 format->stream,193 &other_pad,194 &other_stream);195 if (ret)196 return -EINVAL;197 198 crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);199 /* reset crop */200 crop->left = 0;201 crop->top = 0;202 crop->width = fmt->width;203 crop->height = fmt->height;204 205 return 0;206}207 208int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,209 struct v4l2_subdev_state *state,210 struct v4l2_subdev_mbus_code_enum *code)211{212 struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);213 const u32 *supported_codes = asd->supported_codes;214 u32 index;215 216 for (index = 0; supported_codes[index]; index++) {217 if (index == code->index) {218 code->code = supported_codes[index];219 return 0;220 }221 }222 223 return -EINVAL;224}225 226static int subdev_set_routing(struct v4l2_subdev *sd,227 struct v4l2_subdev_state *state,228 struct v4l2_subdev_krouting *routing)229{230 static const struct v4l2_mbus_framefmt format = {231 .width = 4096,232 .height = 3072,233 .code = MEDIA_BUS_FMT_SGRBG10_1X10,234 .field = V4L2_FIELD_NONE,235 };236 int ret;237 238 ret = v4l2_subdev_routing_validate(sd, routing,239 V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);240 if (ret)241 return ret;242 243 return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);244}245 246int ipu6_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,247 struct v4l2_mbus_framefmt *format)248{249 struct v4l2_mbus_framefmt *fmt;250 struct v4l2_subdev_state *state;251 252 if (!sd || !format)253 return -EINVAL;254 255 state = v4l2_subdev_lock_and_get_active_state(sd);256 fmt = v4l2_subdev_state_get_format(state, pad, stream);257 if (fmt)258 *format = *fmt;259 v4l2_subdev_unlock_state(state);260 261 return fmt ? 0 : -EINVAL;262}263 264int ipu6_isys_get_stream_pad_crop(struct v4l2_subdev *sd, u32 pad, u32 stream,265 struct v4l2_rect *crop)266{267 struct v4l2_subdev_state *state;268 struct v4l2_rect *rect;269 270 if (!sd || !crop)271 return -EINVAL;272 273 state = v4l2_subdev_lock_and_get_active_state(sd);274 rect = v4l2_subdev_state_get_crop(state, pad, stream);275 if (rect)276 *crop = *rect;277 v4l2_subdev_unlock_state(state);278 279 return rect ? 0 : -EINVAL;280}281 282u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)283{284 struct v4l2_subdev_state *state;285 struct v4l2_subdev_route *routes;286 unsigned int i;287 u32 source_stream = 0;288 289 state = v4l2_subdev_lock_and_get_active_state(sd);290 if (!state)291 return 0;292 293 routes = state->routing.routes;294 for (i = 0; i < state->routing.num_routes; i++) {295 if (routes[i].source_pad == pad) {296 source_stream = routes[i].source_stream;297 break;298 }299 }300 301 v4l2_subdev_unlock_state(state);302 303 return source_stream;304}305 306static int ipu6_isys_subdev_init_state(struct v4l2_subdev *sd,307 struct v4l2_subdev_state *state)308{309 struct v4l2_subdev_route route = {310 .sink_pad = 0,311 .sink_stream = 0,312 .source_pad = 1,313 .source_stream = 0,314 .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,315 };316 struct v4l2_subdev_krouting routing = {317 .num_routes = 1,318 .routes = &route,319 };320 321 return subdev_set_routing(sd, state, &routing);322}323 324int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,325 struct v4l2_subdev_state *state,326 enum v4l2_subdev_format_whence which,327 struct v4l2_subdev_krouting *routing)328{329 return subdev_set_routing(sd, state, routing);330}331 332static const struct v4l2_subdev_internal_ops ipu6_isys_subdev_internal_ops = {333 .init_state = ipu6_isys_subdev_init_state,334};335 336int ipu6_isys_subdev_init(struct ipu6_isys_subdev *asd,337 const struct v4l2_subdev_ops *ops,338 unsigned int nr_ctrls,339 unsigned int num_sink_pads,340 unsigned int num_source_pads)341{342 unsigned int num_pads = num_sink_pads + num_source_pads;343 unsigned int i;344 int ret;345 346 v4l2_subdev_init(&asd->sd, ops);347 348 asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |349 V4L2_SUBDEV_FL_HAS_EVENTS |350 V4L2_SUBDEV_FL_STREAMS;351 asd->sd.owner = THIS_MODULE;352 asd->sd.dev = &asd->isys->adev->auxdev.dev;353 asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;354 asd->sd.internal_ops = &ipu6_isys_subdev_internal_ops;355 356 asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,357 sizeof(*asd->pad), GFP_KERNEL);358 if (!asd->pad)359 return -ENOMEM;360 361 for (i = 0; i < num_sink_pads; i++)362 asd->pad[i].flags = MEDIA_PAD_FL_SINK |363 MEDIA_PAD_FL_MUST_CONNECT;364 365 for (i = num_sink_pads; i < num_pads; i++)366 asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;367 368 ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);369 if (ret)370 return ret;371 372 if (asd->ctrl_init) {373 ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);374 if (ret)375 goto out_media_entity_cleanup;376 377 asd->ctrl_init(&asd->sd);378 if (asd->ctrl_handler.error) {379 ret = asd->ctrl_handler.error;380 goto out_v4l2_ctrl_handler_free;381 }382 383 asd->sd.ctrl_handler = &asd->ctrl_handler;384 }385 386 asd->source = -1;387 388 return 0;389 390out_v4l2_ctrl_handler_free:391 v4l2_ctrl_handler_free(&asd->ctrl_handler);392 393out_media_entity_cleanup:394 media_entity_cleanup(&asd->sd.entity);395 396 return ret;397}398 399void ipu6_isys_subdev_cleanup(struct ipu6_isys_subdev *asd)400{401 media_entity_cleanup(&asd->sd.entity);402 v4l2_ctrl_handler_free(&asd->ctrl_handler);403}404