18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Analog Devices ADV748X CSI-2 Transmitter 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Renesas Electronics Corp. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/mutex.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 128c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 138c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "adv748x.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, 188c2ecf20Sopenharmony_ci unsigned int vc) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci return tx_write(tx, ADV748X_CSI_VC_REF, vc << ADV748X_CSI_VC_REF_SHIFT); 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * adv748x_csi2_register_link : Register and link internal entities 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * @tx: CSI2 private entity 278c2ecf20Sopenharmony_ci * @v4l2_dev: Video registration device 288c2ecf20Sopenharmony_ci * @src: Source subdevice to establish link 298c2ecf20Sopenharmony_ci * @src_pad: Pad number of source to link to this @tx 308c2ecf20Sopenharmony_ci * @enable: Link enabled flag 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * Ensure that the subdevice is registered against the v4l2_device, and link the 338c2ecf20Sopenharmony_ci * source pad to the sink pad of the CSI2 bus entity. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic int adv748x_csi2_register_link(struct adv748x_csi2 *tx, 368c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev, 378c2ecf20Sopenharmony_ci struct v4l2_subdev *src, 388c2ecf20Sopenharmony_ci unsigned int src_pad, 398c2ecf20Sopenharmony_ci bool enable) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int ret; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!src->v4l2_dev) { 448c2ecf20Sopenharmony_ci ret = v4l2_device_register_subdev(v4l2_dev, src); 458c2ecf20Sopenharmony_ci if (ret) 468c2ecf20Sopenharmony_ci return ret; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci ret = media_create_pad_link(&src->entity, src_pad, 508c2ecf20Sopenharmony_ci &tx->sd.entity, ADV748X_CSI2_SINK, 518c2ecf20Sopenharmony_ci enable ? MEDIA_LNK_FL_ENABLED : 0); 528c2ecf20Sopenharmony_ci if (ret) 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (enable) 568c2ecf20Sopenharmony_ci tx->src = src; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return 0; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 628c2ecf20Sopenharmony_ci * v4l2_subdev_internal_ops 638c2ecf20Sopenharmony_ci * 648c2ecf20Sopenharmony_ci * We use the internal registered operation to be able to ensure that our 658c2ecf20Sopenharmony_ci * incremental subdevices (not connected in the forward path) can be registered 668c2ecf20Sopenharmony_ci * against the resulting video path and media device. 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int adv748x_csi2_registered(struct v4l2_subdev *sd) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 728c2ecf20Sopenharmony_ci struct adv748x_state *state = tx->state; 738c2ecf20Sopenharmony_ci int ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci adv_dbg(state, "Registered %s (%s)", is_txa(tx) ? "TXA":"TXB", 768c2ecf20Sopenharmony_ci sd->name); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* 798c2ecf20Sopenharmony_ci * Link TXA to AFE and HDMI, and TXB to AFE only as TXB cannot output 808c2ecf20Sopenharmony_ci * HDMI. 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * The HDMI->TXA link is enabled by default, as is the AFE->TXB one. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci if (is_afe_enabled(state)) { 858c2ecf20Sopenharmony_ci ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, 868c2ecf20Sopenharmony_ci &state->afe.sd, 878c2ecf20Sopenharmony_ci ADV748X_AFE_SOURCE, 888c2ecf20Sopenharmony_ci is_txb(tx)); 898c2ecf20Sopenharmony_ci if (ret) 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* TXB can output AFE signals only. */ 938c2ecf20Sopenharmony_ci if (is_txb(tx)) 948c2ecf20Sopenharmony_ci state->afe.tx = tx; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* Register link to HDMI for TXA only. */ 988c2ecf20Sopenharmony_ci if (is_txb(tx) || !is_hdmi_enabled(state)) 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, 1028c2ecf20Sopenharmony_ci ADV748X_HDMI_SOURCE, true); 1038c2ecf20Sopenharmony_ci if (ret) 1048c2ecf20Sopenharmony_ci return ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* The default HDMI output is TXA. */ 1078c2ecf20Sopenharmony_ci state->hdmi.tx = tx; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { 1138c2ecf20Sopenharmony_ci .registered = adv748x_csi2_registered, 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1178c2ecf20Sopenharmony_ci * v4l2_subdev_video_ops 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int adv748x_csi2_s_stream(struct v4l2_subdev *sd, int enable) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 1238c2ecf20Sopenharmony_ci struct v4l2_subdev *src; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci src = adv748x_get_remote_sd(&tx->pads[ADV748X_CSI2_SINK]); 1268c2ecf20Sopenharmony_ci if (!src) 1278c2ecf20Sopenharmony_ci return -EPIPE; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return v4l2_subdev_call(src, video, s_stream, enable); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops adv748x_csi2_video_ops = { 1338c2ecf20Sopenharmony_ci .s_stream = adv748x_csi2_s_stream, 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1378c2ecf20Sopenharmony_ci * v4l2_subdev_pad_ops 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * The CSI2 bus pads are ignorant to the data sizes or formats. 1408c2ecf20Sopenharmony_ci * But we must support setting the pad formats for format propagation. 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt * 1448c2ecf20Sopenharmony_ciadv748x_csi2_get_pad_format(struct v4l2_subdev *sd, 1458c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1468c2ecf20Sopenharmony_ci unsigned int pad, u32 which) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 1518c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_format(sd, cfg, pad); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return &tx->format; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int adv748x_csi2_get_format(struct v4l2_subdev *sd, 1578c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1588c2ecf20Sopenharmony_ci struct v4l2_subdev_format *sdformat) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 1618c2ecf20Sopenharmony_ci struct adv748x_state *state = tx->state; 1628c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbusformat; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, 1658c2ecf20Sopenharmony_ci sdformat->which); 1668c2ecf20Sopenharmony_ci if (!mbusformat) 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci mutex_lock(&state->mutex); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sdformat->format = *mbusformat; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci mutex_unlock(&state->mutex); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int adv748x_csi2_set_format(struct v4l2_subdev *sd, 1798c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 1808c2ecf20Sopenharmony_ci struct v4l2_subdev_format *sdformat) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 1838c2ecf20Sopenharmony_ci struct adv748x_state *state = tx->state; 1848c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbusformat; 1858c2ecf20Sopenharmony_ci int ret = 0; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci mbusformat = adv748x_csi2_get_pad_format(sd, cfg, sdformat->pad, 1888c2ecf20Sopenharmony_ci sdformat->which); 1898c2ecf20Sopenharmony_ci if (!mbusformat) 1908c2ecf20Sopenharmony_ci return -EINVAL; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mutex_lock(&state->mutex); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (sdformat->pad == ADV748X_CSI2_SOURCE) { 1958c2ecf20Sopenharmony_ci const struct v4l2_mbus_framefmt *sink_fmt; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci sink_fmt = adv748x_csi2_get_pad_format(sd, cfg, 1988c2ecf20Sopenharmony_ci ADV748X_CSI2_SINK, 1998c2ecf20Sopenharmony_ci sdformat->which); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!sink_fmt) { 2028c2ecf20Sopenharmony_ci ret = -EINVAL; 2038c2ecf20Sopenharmony_ci goto unlock; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci sdformat->format = *sink_fmt; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci *mbusformat = sdformat->format; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ciunlock: 2128c2ecf20Sopenharmony_ci mutex_unlock(&state->mutex); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int adv748x_csi2_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, 2188c2ecf20Sopenharmony_ci struct v4l2_mbus_config *config) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (pad != ADV748X_CSI2_SOURCE) 2238c2ecf20Sopenharmony_ci return -EINVAL; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci config->type = V4L2_MBUS_CSI2_DPHY; 2268c2ecf20Sopenharmony_ci switch (tx->active_lanes) { 2278c2ecf20Sopenharmony_ci case 1: 2288c2ecf20Sopenharmony_ci config->flags = V4L2_MBUS_CSI2_1_LANE; 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci case 2: 2328c2ecf20Sopenharmony_ci config->flags = V4L2_MBUS_CSI2_2_LANE; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci case 3: 2368c2ecf20Sopenharmony_ci config->flags = V4L2_MBUS_CSI2_3_LANE; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci case 4: 2408c2ecf20Sopenharmony_ci config->flags = V4L2_MBUS_CSI2_4_LANE; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops adv748x_csi2_pad_ops = { 2488c2ecf20Sopenharmony_ci .get_fmt = adv748x_csi2_get_format, 2498c2ecf20Sopenharmony_ci .set_fmt = adv748x_csi2_set_format, 2508c2ecf20Sopenharmony_ci .get_mbus_config = adv748x_csi2_get_mbus_config, 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2548c2ecf20Sopenharmony_ci * v4l2_subdev_ops 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops adv748x_csi2_ops = { 2588c2ecf20Sopenharmony_ci .video = &adv748x_csi2_video_ops, 2598c2ecf20Sopenharmony_ci .pad = &adv748x_csi2_pad_ops, 2608c2ecf20Sopenharmony_ci}; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 2638c2ecf20Sopenharmony_ci * Subdev module and controls 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciint adv748x_csi2_set_pixelrate(struct v4l2_subdev *sd, s64 rate) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct adv748x_csi2 *tx = adv748x_sd_to_csi2(sd); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (!tx->pixel_rate) 2718c2ecf20Sopenharmony_ci return -EINVAL; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return v4l2_ctrl_s_ctrl_int64(tx->pixel_rate, rate); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int adv748x_csi2_s_ctrl(struct v4l2_ctrl *ctrl) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci switch (ctrl->id) { 2798c2ecf20Sopenharmony_ci case V4L2_CID_PIXEL_RATE: 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci return -EINVAL; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops adv748x_csi2_ctrl_ops = { 2878c2ecf20Sopenharmony_ci .s_ctrl = adv748x_csi2_s_ctrl, 2888c2ecf20Sopenharmony_ci}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int adv748x_csi2_init_controls(struct adv748x_csi2 *tx) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&tx->ctrl_hdl, 1); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci tx->pixel_rate = v4l2_ctrl_new_std(&tx->ctrl_hdl, 2968c2ecf20Sopenharmony_ci &adv748x_csi2_ctrl_ops, 2978c2ecf20Sopenharmony_ci V4L2_CID_PIXEL_RATE, 1, INT_MAX, 2988c2ecf20Sopenharmony_ci 1, 1); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci tx->sd.ctrl_handler = &tx->ctrl_hdl; 3018c2ecf20Sopenharmony_ci if (tx->ctrl_hdl.error) { 3028c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&tx->ctrl_hdl); 3038c2ecf20Sopenharmony_ci return tx->ctrl_hdl.error; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return v4l2_ctrl_handler_setup(&tx->ctrl_hdl); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciint adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (!is_tx_enabled(tx)) 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Initialise the virtual channel */ 3178c2ecf20Sopenharmony_ci adv748x_csi2_set_virtual_channel(tx, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci adv748x_subdev_init(&tx->sd, state, &adv748x_csi2_ops, 3208c2ecf20Sopenharmony_ci MEDIA_ENT_F_VID_IF_BRIDGE, 3218c2ecf20Sopenharmony_ci is_txa(tx) ? "txa" : "txb"); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Ensure that matching is based upon the endpoint fwnodes */ 3248c2ecf20Sopenharmony_ci tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Register internal ops for incremental subdev registration */ 3278c2ecf20Sopenharmony_ci tx->sd.internal_ops = &adv748x_csi2_internal_ops; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci tx->pads[ADV748X_CSI2_SINK].flags = MEDIA_PAD_FL_SINK; 3308c2ecf20Sopenharmony_ci tx->pads[ADV748X_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&tx->sd.entity, ADV748X_CSI2_NR_PADS, 3338c2ecf20Sopenharmony_ci tx->pads); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = adv748x_csi2_init_controls(tx); 3388c2ecf20Sopenharmony_ci if (ret) 3398c2ecf20Sopenharmony_ci goto err_free_media; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = v4l2_async_register_subdev(&tx->sd); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci goto err_free_ctrl; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return 0; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cierr_free_ctrl: 3488c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&tx->ctrl_hdl); 3498c2ecf20Sopenharmony_cierr_free_media: 3508c2ecf20Sopenharmony_ci media_entity_cleanup(&tx->sd.entity); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_civoid adv748x_csi2_cleanup(struct adv748x_csi2 *tx) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci if (!is_tx_enabled(tx)) 3588c2ecf20Sopenharmony_ci return; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci v4l2_async_unregister_subdev(&tx->sd); 3618c2ecf20Sopenharmony_ci media_entity_cleanup(&tx->sd.entity); 3628c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&tx->ctrl_hdl); 3638c2ecf20Sopenharmony_ci} 364