185411d17SMichael Riesch // SPDX-License-Identifier: GPL-2.0 285411d17SMichael Riesch /* 385411d17SMichael Riesch * Rockchip Camera Interface (CIF) Driver 485411d17SMichael Riesch * 585411d17SMichael Riesch * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net> 685411d17SMichael Riesch * Copyright (C) 2025 Collabora, Ltd. 785411d17SMichael Riesch */ 885411d17SMichael Riesch 985411d17SMichael Riesch #include <media/v4l2-common.h> 1085411d17SMichael Riesch #include <media/v4l2-fwnode.h> 1185411d17SMichael Riesch #include <media/v4l2-mc.h> 1285411d17SMichael Riesch #include <media/v4l2-subdev.h> 1385411d17SMichael Riesch 1485411d17SMichael Riesch #include "rkcif-common.h" 1585411d17SMichael Riesch #include "rkcif-interface.h" 1685411d17SMichael Riesch 1785411d17SMichael Riesch static inline struct rkcif_interface *to_rkcif_interface(struct v4l2_subdev *sd) 1885411d17SMichael Riesch { 1985411d17SMichael Riesch return container_of(sd, struct rkcif_interface, sd); 2085411d17SMichael Riesch } 2185411d17SMichael Riesch 2285411d17SMichael Riesch static const struct media_entity_operations rkcif_interface_media_ops = { 2385411d17SMichael Riesch .link_validate = v4l2_subdev_link_validate, 2485411d17SMichael Riesch .has_pad_interdep = v4l2_subdev_has_pad_interdep, 2585411d17SMichael Riesch }; 2685411d17SMichael Riesch 2785411d17SMichael Riesch static int rkcif_interface_set_fmt(struct v4l2_subdev *sd, 2885411d17SMichael Riesch struct v4l2_subdev_state *state, 2985411d17SMichael Riesch struct v4l2_subdev_format *format) 3085411d17SMichael Riesch { 3185411d17SMichael Riesch struct rkcif_interface *interface = to_rkcif_interface(sd); 3285411d17SMichael Riesch const struct rkcif_input_fmt *input; 3385411d17SMichael Riesch struct v4l2_mbus_framefmt *sink, *src; 3485411d17SMichael Riesch struct v4l2_rect *crop; 3585411d17SMichael Riesch u32 other_pad, other_stream; 3685411d17SMichael Riesch int ret; 3785411d17SMichael Riesch 3885411d17SMichael Riesch /* the format on the source pad always matches the sink pad */ 3985411d17SMichael Riesch if (format->pad == RKCIF_IF_PAD_SRC) 4085411d17SMichael Riesch return v4l2_subdev_get_fmt(sd, state, format); 4185411d17SMichael Riesch 4285411d17SMichael Riesch input = rkcif_interface_find_input_fmt(interface, true, 4385411d17SMichael Riesch format->format.code); 4485411d17SMichael Riesch format->format.code = input->mbus_code; 4585411d17SMichael Riesch 4685411d17SMichael Riesch sink = v4l2_subdev_state_get_format(state, format->pad, format->stream); 4785411d17SMichael Riesch if (!sink) 4885411d17SMichael Riesch return -EINVAL; 4985411d17SMichael Riesch 5085411d17SMichael Riesch *sink = format->format; 5185411d17SMichael Riesch 5285411d17SMichael Riesch /* propagate the format to the source pad */ 5385411d17SMichael Riesch src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 5485411d17SMichael Riesch format->stream); 5585411d17SMichael Riesch if (!src) 5685411d17SMichael Riesch return -EINVAL; 5785411d17SMichael Riesch 5885411d17SMichael Riesch *src = *sink; 5985411d17SMichael Riesch 6085411d17SMichael Riesch ret = v4l2_subdev_routing_find_opposite_end(&state->routing, 6185411d17SMichael Riesch format->pad, format->stream, 6285411d17SMichael Riesch &other_pad, &other_stream); 6385411d17SMichael Riesch if (ret) 6485411d17SMichael Riesch return -EINVAL; 6585411d17SMichael Riesch 6685411d17SMichael Riesch crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream); 6785411d17SMichael Riesch if (!crop) 6885411d17SMichael Riesch return -EINVAL; 6985411d17SMichael Riesch 7085411d17SMichael Riesch /* reset crop */ 7185411d17SMichael Riesch crop->left = 0; 7285411d17SMichael Riesch crop->top = 0; 7385411d17SMichael Riesch crop->width = sink->width; 7485411d17SMichael Riesch crop->height = sink->height; 7585411d17SMichael Riesch 7685411d17SMichael Riesch return 0; 7785411d17SMichael Riesch } 7885411d17SMichael Riesch 7985411d17SMichael Riesch static int rkcif_interface_get_sel(struct v4l2_subdev *sd, 8085411d17SMichael Riesch struct v4l2_subdev_state *state, 8185411d17SMichael Riesch struct v4l2_subdev_selection *sel) 8285411d17SMichael Riesch { 8385411d17SMichael Riesch struct v4l2_mbus_framefmt *sink; 8485411d17SMichael Riesch struct v4l2_rect *crop; 8585411d17SMichael Riesch int ret = 0; 8685411d17SMichael Riesch 8785411d17SMichael Riesch if (sel->pad != RKCIF_IF_PAD_SRC) 8885411d17SMichael Riesch return -EINVAL; 8985411d17SMichael Riesch 9085411d17SMichael Riesch sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad, 9185411d17SMichael Riesch sel->stream); 9285411d17SMichael Riesch if (!sink) 9385411d17SMichael Riesch return -EINVAL; 9485411d17SMichael Riesch 9585411d17SMichael Riesch crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); 9685411d17SMichael Riesch if (!crop) 9785411d17SMichael Riesch return -EINVAL; 9885411d17SMichael Riesch 9985411d17SMichael Riesch switch (sel->target) { 10085411d17SMichael Riesch case V4L2_SEL_TGT_CROP_DEFAULT: 10185411d17SMichael Riesch case V4L2_SEL_TGT_CROP_BOUNDS: 10285411d17SMichael Riesch sel->r.left = 0; 10385411d17SMichael Riesch sel->r.top = 0; 10485411d17SMichael Riesch sel->r.width = sink->width; 10585411d17SMichael Riesch sel->r.height = sink->height; 10685411d17SMichael Riesch break; 10785411d17SMichael Riesch case V4L2_SEL_TGT_CROP: 10885411d17SMichael Riesch sel->r = *crop; 10985411d17SMichael Riesch break; 11085411d17SMichael Riesch default: 11185411d17SMichael Riesch ret = -EINVAL; 11285411d17SMichael Riesch } 11385411d17SMichael Riesch 11485411d17SMichael Riesch return ret; 11585411d17SMichael Riesch } 11685411d17SMichael Riesch 11785411d17SMichael Riesch static int rkcif_interface_set_sel(struct v4l2_subdev *sd, 11885411d17SMichael Riesch struct v4l2_subdev_state *state, 11985411d17SMichael Riesch struct v4l2_subdev_selection *sel) 12085411d17SMichael Riesch { 12185411d17SMichael Riesch struct v4l2_mbus_framefmt *sink, *src; 12285411d17SMichael Riesch struct v4l2_rect *crop; 12385411d17SMichael Riesch 12485411d17SMichael Riesch if (sel->pad != RKCIF_IF_PAD_SRC || sel->target != V4L2_SEL_TGT_CROP) 12585411d17SMichael Riesch return -EINVAL; 12685411d17SMichael Riesch 12785411d17SMichael Riesch sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad, 12885411d17SMichael Riesch sel->stream); 12985411d17SMichael Riesch if (!sink) 13085411d17SMichael Riesch return -EINVAL; 13185411d17SMichael Riesch 13285411d17SMichael Riesch src = v4l2_subdev_state_get_format(state, sel->pad, sel->stream); 13385411d17SMichael Riesch if (!src) 13485411d17SMichael Riesch return -EINVAL; 13585411d17SMichael Riesch 13685411d17SMichael Riesch crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream); 13785411d17SMichael Riesch if (!crop) 13885411d17SMichael Riesch return -EINVAL; 13985411d17SMichael Riesch 14085411d17SMichael Riesch *crop = sel->r; 14185411d17SMichael Riesch 14285411d17SMichael Riesch src->height = sel->r.height; 14385411d17SMichael Riesch src->width = sel->r.width; 14485411d17SMichael Riesch 14585411d17SMichael Riesch return 0; 14685411d17SMichael Riesch } 14785411d17SMichael Riesch 14885411d17SMichael Riesch static int rkcif_interface_set_routing(struct v4l2_subdev *sd, 14985411d17SMichael Riesch struct v4l2_subdev_state *state, 15085411d17SMichael Riesch enum v4l2_subdev_format_whence which, 15185411d17SMichael Riesch struct v4l2_subdev_krouting *routing) 15285411d17SMichael Riesch { 15385411d17SMichael Riesch int ret; 15485411d17SMichael Riesch 15585411d17SMichael Riesch ret = v4l2_subdev_routing_validate(sd, routing, 15685411d17SMichael Riesch V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 15785411d17SMichael Riesch if (ret) 15885411d17SMichael Riesch return ret; 15985411d17SMichael Riesch 160*501802e2SMichael Riesch for (unsigned int i = 0; i < routing->num_routes; i++) { 161*501802e2SMichael Riesch const struct v4l2_subdev_route *route = &routing->routes[i]; 162*501802e2SMichael Riesch 163*501802e2SMichael Riesch if (route->source_stream >= RKCIF_ID_MAX) 164*501802e2SMichael Riesch return -EINVAL; 165*501802e2SMichael Riesch } 166*501802e2SMichael Riesch 16785411d17SMichael Riesch ret = v4l2_subdev_set_routing(sd, state, routing); 16885411d17SMichael Riesch 16985411d17SMichael Riesch return ret; 17085411d17SMichael Riesch } 17185411d17SMichael Riesch 172*501802e2SMichael Riesch static int rkcif_interface_apply_crop(struct rkcif_stream *stream, 173*501802e2SMichael Riesch struct v4l2_subdev_state *state) 174*501802e2SMichael Riesch { 175*501802e2SMichael Riesch struct rkcif_interface *interface = stream->interface; 176*501802e2SMichael Riesch struct v4l2_rect *crop; 177*501802e2SMichael Riesch 178*501802e2SMichael Riesch crop = v4l2_subdev_state_get_crop(state, RKCIF_IF_PAD_SRC, stream->id); 179*501802e2SMichael Riesch if (!crop) 180*501802e2SMichael Riesch return -EINVAL; 181*501802e2SMichael Riesch 182*501802e2SMichael Riesch if (interface->set_crop) 183*501802e2SMichael Riesch interface->set_crop(stream, crop->left, crop->top); 184*501802e2SMichael Riesch 185*501802e2SMichael Riesch return 0; 186*501802e2SMichael Riesch } 187*501802e2SMichael Riesch 18885411d17SMichael Riesch static int rkcif_interface_enable_streams(struct v4l2_subdev *sd, 18985411d17SMichael Riesch struct v4l2_subdev_state *state, 19085411d17SMichael Riesch u32 pad, u64 streams_mask) 19185411d17SMichael Riesch { 192*501802e2SMichael Riesch struct rkcif_interface *interface = to_rkcif_interface(sd); 193*501802e2SMichael Riesch struct rkcif_stream *stream; 194*501802e2SMichael Riesch struct v4l2_subdev_route *route; 19585411d17SMichael Riesch struct v4l2_subdev *remote_sd; 19685411d17SMichael Riesch struct media_pad *remote_pad; 19785411d17SMichael Riesch u64 mask; 19885411d17SMichael Riesch 19985411d17SMichael Riesch remote_pad = 20085411d17SMichael Riesch media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]); 20185411d17SMichael Riesch remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); 20285411d17SMichael Riesch 203*501802e2SMichael Riesch /* DVP has one crop setting for all IDs */ 204*501802e2SMichael Riesch if (interface->type == RKCIF_IF_DVP) { 205*501802e2SMichael Riesch stream = &interface->streams[RKCIF_ID0]; 206*501802e2SMichael Riesch rkcif_interface_apply_crop(stream, state); 207*501802e2SMichael Riesch } else { 208*501802e2SMichael Riesch for_each_active_route(&state->routing, route) { 209*501802e2SMichael Riesch stream = &interface->streams[route->sink_stream]; 210*501802e2SMichael Riesch rkcif_interface_apply_crop(stream, state); 211*501802e2SMichael Riesch } 212*501802e2SMichael Riesch } 213*501802e2SMichael Riesch 21485411d17SMichael Riesch mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK, 21585411d17SMichael Riesch RKCIF_IF_PAD_SRC, &streams_mask); 21685411d17SMichael Riesch 21785411d17SMichael Riesch return v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask); 21885411d17SMichael Riesch } 21985411d17SMichael Riesch 22085411d17SMichael Riesch static int rkcif_interface_disable_streams(struct v4l2_subdev *sd, 22185411d17SMichael Riesch struct v4l2_subdev_state *state, 22285411d17SMichael Riesch u32 pad, u64 streams_mask) 22385411d17SMichael Riesch { 22485411d17SMichael Riesch struct v4l2_subdev *remote_sd; 22585411d17SMichael Riesch struct media_pad *remote_pad; 22685411d17SMichael Riesch u64 mask; 22785411d17SMichael Riesch 22885411d17SMichael Riesch remote_pad = 22985411d17SMichael Riesch media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]); 23085411d17SMichael Riesch remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity); 23185411d17SMichael Riesch 23285411d17SMichael Riesch mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK, 23385411d17SMichael Riesch RKCIF_IF_PAD_SRC, &streams_mask); 23485411d17SMichael Riesch 23585411d17SMichael Riesch return v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask); 23685411d17SMichael Riesch } 23785411d17SMichael Riesch 23885411d17SMichael Riesch static const struct v4l2_subdev_pad_ops rkcif_interface_pad_ops = { 23985411d17SMichael Riesch .get_fmt = v4l2_subdev_get_fmt, 24085411d17SMichael Riesch .set_fmt = rkcif_interface_set_fmt, 24185411d17SMichael Riesch .get_selection = rkcif_interface_get_sel, 24285411d17SMichael Riesch .set_selection = rkcif_interface_set_sel, 24385411d17SMichael Riesch .set_routing = rkcif_interface_set_routing, 24485411d17SMichael Riesch .enable_streams = rkcif_interface_enable_streams, 24585411d17SMichael Riesch .disable_streams = rkcif_interface_disable_streams, 24685411d17SMichael Riesch }; 24785411d17SMichael Riesch 24885411d17SMichael Riesch static const struct v4l2_subdev_ops rkcif_interface_ops = { 24985411d17SMichael Riesch .pad = &rkcif_interface_pad_ops, 25085411d17SMichael Riesch }; 25185411d17SMichael Riesch 25285411d17SMichael Riesch static int rkcif_interface_init_state(struct v4l2_subdev *sd, 25385411d17SMichael Riesch struct v4l2_subdev_state *state) 25485411d17SMichael Riesch { 25585411d17SMichael Riesch struct rkcif_interface *interface = to_rkcif_interface(sd); 25685411d17SMichael Riesch struct v4l2_subdev_route routes[] = { 25785411d17SMichael Riesch { 25885411d17SMichael Riesch .sink_pad = RKCIF_IF_PAD_SINK, 25985411d17SMichael Riesch .sink_stream = 0, 26085411d17SMichael Riesch .source_pad = RKCIF_IF_PAD_SRC, 26185411d17SMichael Riesch .source_stream = 0, 26285411d17SMichael Riesch .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 26385411d17SMichael Riesch }, 26485411d17SMichael Riesch }; 26585411d17SMichael Riesch struct v4l2_subdev_krouting routing = { 26685411d17SMichael Riesch .len_routes = ARRAY_SIZE(routes), 26785411d17SMichael Riesch .num_routes = ARRAY_SIZE(routes), 26885411d17SMichael Riesch .routes = routes, 26985411d17SMichael Riesch }; 27085411d17SMichael Riesch const struct v4l2_mbus_framefmt dvp_default_format = { 27185411d17SMichael Riesch .width = 3840, 27285411d17SMichael Riesch .height = 2160, 27385411d17SMichael Riesch .code = MEDIA_BUS_FMT_YUYV8_1X16, 27485411d17SMichael Riesch .field = V4L2_FIELD_NONE, 27585411d17SMichael Riesch .colorspace = V4L2_COLORSPACE_REC709, 27685411d17SMichael Riesch .ycbcr_enc = V4L2_YCBCR_ENC_709, 27785411d17SMichael Riesch .quantization = V4L2_QUANTIZATION_LIM_RANGE, 27885411d17SMichael Riesch .xfer_func = V4L2_XFER_FUNC_NONE, 27985411d17SMichael Riesch }; 28085411d17SMichael Riesch const struct v4l2_mbus_framefmt mipi_default_format = { 28185411d17SMichael Riesch .width = 3840, 28285411d17SMichael Riesch .height = 2160, 28385411d17SMichael Riesch .code = MEDIA_BUS_FMT_SRGGB10_1X10, 28485411d17SMichael Riesch .field = V4L2_FIELD_NONE, 28585411d17SMichael Riesch .colorspace = V4L2_COLORSPACE_RAW, 28685411d17SMichael Riesch .ycbcr_enc = V4L2_YCBCR_ENC_601, 28785411d17SMichael Riesch .quantization = V4L2_QUANTIZATION_FULL_RANGE, 28885411d17SMichael Riesch .xfer_func = V4L2_XFER_FUNC_NONE, 28985411d17SMichael Riesch }; 29085411d17SMichael Riesch const struct v4l2_mbus_framefmt *default_format; 29185411d17SMichael Riesch int ret; 29285411d17SMichael Riesch 29385411d17SMichael Riesch default_format = (interface->type == RKCIF_IF_DVP) ? 29485411d17SMichael Riesch &dvp_default_format : 29585411d17SMichael Riesch &mipi_default_format; 29685411d17SMichael Riesch 29785411d17SMichael Riesch ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing, 29885411d17SMichael Riesch default_format); 29985411d17SMichael Riesch 30085411d17SMichael Riesch return ret; 30185411d17SMichael Riesch } 30285411d17SMichael Riesch 30385411d17SMichael Riesch static const struct v4l2_subdev_internal_ops rkcif_interface_internal_ops = { 30485411d17SMichael Riesch .init_state = rkcif_interface_init_state, 30585411d17SMichael Riesch }; 30685411d17SMichael Riesch 30785411d17SMichael Riesch static int rkcif_interface_add(struct rkcif_interface *interface) 30885411d17SMichael Riesch { 30985411d17SMichael Riesch struct rkcif_device *rkcif = interface->rkcif; 31085411d17SMichael Riesch struct rkcif_remote *remote; 31185411d17SMichael Riesch struct v4l2_async_notifier *ntf = &rkcif->notifier; 31285411d17SMichael Riesch struct v4l2_fwnode_endpoint *vep = &interface->vep; 31385411d17SMichael Riesch struct device *dev = rkcif->dev; 31485411d17SMichael Riesch struct fwnode_handle *ep; 31585411d17SMichael Riesch u32 dvp_clk_delay = 0; 31685411d17SMichael Riesch int ret; 31785411d17SMichael Riesch 31885411d17SMichael Riesch ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), interface->index, 31985411d17SMichael Riesch 0, 0); 32085411d17SMichael Riesch if (!ep) 32185411d17SMichael Riesch return -ENODEV; 32285411d17SMichael Riesch 32385411d17SMichael Riesch vep->bus_type = V4L2_MBUS_UNKNOWN; 32485411d17SMichael Riesch ret = v4l2_fwnode_endpoint_parse(ep, vep); 32585411d17SMichael Riesch if (ret) 32685411d17SMichael Riesch goto complete; 32785411d17SMichael Riesch 32885411d17SMichael Riesch if (interface->type == RKCIF_IF_DVP) { 32985411d17SMichael Riesch if (vep->bus_type != V4L2_MBUS_BT656 && 33085411d17SMichael Riesch vep->bus_type != V4L2_MBUS_PARALLEL) { 33185411d17SMichael Riesch ret = dev_err_probe(dev, -EINVAL, 33285411d17SMichael Riesch "unsupported bus type\n"); 33385411d17SMichael Riesch goto complete; 33485411d17SMichael Riesch } 33585411d17SMichael Riesch 33685411d17SMichael Riesch fwnode_property_read_u32(ep, "rockchip,dvp-clk-delay", 33785411d17SMichael Riesch &dvp_clk_delay); 33885411d17SMichael Riesch interface->dvp.dvp_clk_delay = dvp_clk_delay; 33985411d17SMichael Riesch } 34085411d17SMichael Riesch 34185411d17SMichael Riesch remote = v4l2_async_nf_add_fwnode_remote(ntf, ep, struct rkcif_remote); 34285411d17SMichael Riesch if (IS_ERR(remote)) { 34385411d17SMichael Riesch ret = PTR_ERR(remote); 34485411d17SMichael Riesch goto complete; 34585411d17SMichael Riesch } 34685411d17SMichael Riesch 34785411d17SMichael Riesch remote->interface = interface; 34885411d17SMichael Riesch interface->remote = remote; 34985411d17SMichael Riesch interface->status = RKCIF_IF_ACTIVE; 35085411d17SMichael Riesch ret = 0; 35185411d17SMichael Riesch 35285411d17SMichael Riesch complete: 35385411d17SMichael Riesch fwnode_handle_put(ep); 35485411d17SMichael Riesch 35585411d17SMichael Riesch return ret; 35685411d17SMichael Riesch } 35785411d17SMichael Riesch 35885411d17SMichael Riesch int rkcif_interface_register(struct rkcif_device *rkcif, 35985411d17SMichael Riesch struct rkcif_interface *interface) 36085411d17SMichael Riesch { 36185411d17SMichael Riesch struct media_pad *pads = interface->pads; 36285411d17SMichael Riesch struct v4l2_subdev *sd = &interface->sd; 36385411d17SMichael Riesch int ret; 36485411d17SMichael Riesch 36585411d17SMichael Riesch interface->rkcif = rkcif; 36685411d17SMichael Riesch 36785411d17SMichael Riesch v4l2_subdev_init(sd, &rkcif_interface_ops); 36885411d17SMichael Riesch sd->dev = rkcif->dev; 36985411d17SMichael Riesch sd->entity.ops = &rkcif_interface_media_ops; 37085411d17SMichael Riesch sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 37185411d17SMichael Riesch sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; 37285411d17SMichael Riesch sd->internal_ops = &rkcif_interface_internal_ops; 37385411d17SMichael Riesch sd->owner = THIS_MODULE; 37485411d17SMichael Riesch 37585411d17SMichael Riesch if (interface->type == RKCIF_IF_DVP) 37685411d17SMichael Riesch snprintf(sd->name, sizeof(sd->name), "rkcif-dvp0"); 37785411d17SMichael Riesch else if (interface->type == RKCIF_IF_MIPI) 37885411d17SMichael Riesch snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d", 37985411d17SMichael Riesch interface->index - RKCIF_MIPI_BASE); 38085411d17SMichael Riesch 38185411d17SMichael Riesch pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 38285411d17SMichael Riesch pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 38385411d17SMichael Riesch ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads); 38485411d17SMichael Riesch if (ret) 38585411d17SMichael Riesch goto err; 38685411d17SMichael Riesch 38785411d17SMichael Riesch ret = v4l2_subdev_init_finalize(sd); 38885411d17SMichael Riesch if (ret) 38985411d17SMichael Riesch goto err_entity_cleanup; 39085411d17SMichael Riesch 39185411d17SMichael Riesch ret = v4l2_device_register_subdev(&rkcif->v4l2_dev, sd); 39285411d17SMichael Riesch if (ret) { 39385411d17SMichael Riesch dev_err(sd->dev, "failed to register subdev\n"); 39485411d17SMichael Riesch goto err_subdev_cleanup; 39585411d17SMichael Riesch } 39685411d17SMichael Riesch 39785411d17SMichael Riesch ret = rkcif_interface_add(interface); 39885411d17SMichael Riesch if (ret) 39985411d17SMichael Riesch goto err_subdev_unregister; 40085411d17SMichael Riesch 40185411d17SMichael Riesch return 0; 40285411d17SMichael Riesch 40385411d17SMichael Riesch err_subdev_unregister: 40485411d17SMichael Riesch v4l2_device_unregister_subdev(sd); 40585411d17SMichael Riesch err_subdev_cleanup: 40685411d17SMichael Riesch v4l2_subdev_cleanup(sd); 40785411d17SMichael Riesch err_entity_cleanup: 40885411d17SMichael Riesch media_entity_cleanup(&sd->entity); 40985411d17SMichael Riesch err: 41085411d17SMichael Riesch return ret; 41185411d17SMichael Riesch } 41285411d17SMichael Riesch 41385411d17SMichael Riesch void rkcif_interface_unregister(struct rkcif_interface *interface) 41485411d17SMichael Riesch { 41585411d17SMichael Riesch struct v4l2_subdev *sd = &interface->sd; 41685411d17SMichael Riesch 41785411d17SMichael Riesch if (interface->status != RKCIF_IF_ACTIVE) 41885411d17SMichael Riesch return; 41985411d17SMichael Riesch 42085411d17SMichael Riesch v4l2_device_unregister_subdev(sd); 42185411d17SMichael Riesch v4l2_subdev_cleanup(sd); 42285411d17SMichael Riesch media_entity_cleanup(&sd->entity); 42385411d17SMichael Riesch } 42485411d17SMichael Riesch 42585411d17SMichael Riesch const struct rkcif_input_fmt * 42685411d17SMichael Riesch rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def, 42785411d17SMichael Riesch u32 mbus_code) 42885411d17SMichael Riesch { 42985411d17SMichael Riesch const struct rkcif_input_fmt *fmt; 43085411d17SMichael Riesch 43185411d17SMichael Riesch WARN_ON(interface->in_fmts_num == 0); 43285411d17SMichael Riesch 43385411d17SMichael Riesch for (unsigned int i = 0; i < interface->in_fmts_num; i++) { 43485411d17SMichael Riesch fmt = &interface->in_fmts[i]; 43585411d17SMichael Riesch if (fmt->mbus_code == mbus_code) 43685411d17SMichael Riesch return fmt; 43785411d17SMichael Riesch } 43885411d17SMichael Riesch if (ret_def) 43985411d17SMichael Riesch return &interface->in_fmts[0]; 44085411d17SMichael Riesch else 44185411d17SMichael Riesch return NULL; 44285411d17SMichael Riesch } 443