816 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2023 Intel Corporation. All rights reserved.4 * Intel Visual Sensing Controller CSI Linux driver5 */6 7/*8 * To set ownership of CSI-2 link and to configure CSI-2 link, there9 * are specific commands, which are sent via MEI protocol. The send10 * command function uses "completion" as a synchronization mechanism.11 * The response for command is received via a mei callback which wakes12 * up the caller. There can be only one outstanding command at a time.13 */14 15#include <linux/completion.h>16#include <linux/delay.h>17#include <linux/kernel.h>18#include <linux/math64.h>19#include <linux/mei_cl_bus.h>20#include <linux/module.h>21#include <linux/mutex.h>22#include <linux/pci.h>23#include <linux/pm_runtime.h>24#include <linux/slab.h>25#include <linux/units.h>26#include <linux/uuid.h>27#include <linux/workqueue.h>28 29#include <media/ipu-bridge.h>30#include <media/ipu6-pci-table.h>31#include <media/v4l2-async.h>32#include <media/v4l2-ctrls.h>33#include <media/v4l2-fwnode.h>34#include <media/v4l2-subdev.h>35 36#define MEI_CSI_ENTITY_NAME "Intel IVSC CSI"37 38#define MEI_CSI_LINK_FREQ_400MHZ 400000000ULL39 40/* the 5s used here is based on experiment */41#define CSI_CMD_TIMEOUT (5 * HZ)42/* to setup CSI-2 link an extra delay needed and determined experimentally */43#define CSI_FW_READY_DELAY_MS 10044/* link frequency unit is 100kHz */45#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))46 47/*48 * identify the command id supported by firmware49 * IPC, as well as the privacy notification id50 * used when processing privacy event.51 */52enum csi_cmd_id {53 /* used to set csi ownership */54 CSI_SET_OWNER = 0,55 56 /* used to configure CSI-2 link */57 CSI_SET_CONF = 2,58 59 /* privacy notification id used when privacy state changes */60 CSI_PRIVACY_NOTIF = 6,61};62 63/* CSI-2 link ownership definition */64enum csi_link_owner {65 CSI_LINK_IVSC,66 CSI_LINK_HOST,67};68 69/* privacy status definition */70enum ivsc_privacy_status {71 CSI_PRIVACY_OFF,72 CSI_PRIVACY_ON,73 CSI_PRIVACY_MAX,74};75 76enum csi_pads {77 CSI_PAD_SINK,78 CSI_PAD_SOURCE,79 CSI_NUM_PADS80};81 82/* configuration of the CSI-2 link between host and IVSC */83struct csi_link_cfg {84 /* number of data lanes used on the CSI-2 link */85 u32 nr_of_lanes;86 87 /* frequency of the CSI-2 link */88 u32 link_freq;89 90 /* for future use */91 u32 rsvd[2];92} __packed;93 94/* CSI command structure */95struct csi_cmd {96 u32 cmd_id;97 union _cmd_param {98 u32 param;99 struct csi_link_cfg conf;100 } param;101} __packed;102 103/* CSI notification structure */104struct csi_notif {105 u32 cmd_id;106 int status;107 union _resp_cont {108 u32 cont;109 struct csi_link_cfg conf;110 } cont;111} __packed;112 113struct mei_csi {114 struct mei_cl_device *cldev;115 116 /* command response */117 struct csi_notif cmd_response;118 /* used to wait for command response from firmware */119 struct completion cmd_completion;120 /* protect command download */121 struct mutex lock;122 123 struct v4l2_subdev subdev;124 struct v4l2_subdev *remote;125 struct v4l2_async_notifier notifier;126 struct v4l2_ctrl_handler ctrl_handler;127 struct v4l2_ctrl *freq_ctrl;128 struct v4l2_ctrl *privacy_ctrl;129 /* lock for v4l2 controls */130 struct mutex ctrl_lock;131 unsigned int remote_pad;132 /* start streaming or not */133 int streaming;134 135 struct media_pad pads[CSI_NUM_PADS];136 137 /* number of data lanes used on the CSI-2 link */138 u32 nr_of_lanes;139 /* frequency of the CSI-2 link */140 u64 link_freq;141};142 143static const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = {144 .width = 1,145 .height = 1,146 .code = MEDIA_BUS_FMT_Y8_1X8,147 .field = V4L2_FIELD_NONE,148};149 150static s64 link_freq_menu_items[] = {151 MEI_CSI_LINK_FREQ_400MHZ152};153 154static inline struct mei_csi *notifier_to_csi(struct v4l2_async_notifier *n)155{156 return container_of(n, struct mei_csi, notifier);157}158 159static inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd)160{161 return container_of(sd, struct mei_csi, subdev);162}163 164static inline struct mei_csi *ctrl_to_csi(struct v4l2_ctrl *ctrl)165{166 return container_of(ctrl->handler, struct mei_csi, ctrl_handler);167}168 169/* send a command to firmware and mutex must be held by caller */170static int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len)171{172 struct csi_cmd *cmd = (struct csi_cmd *)buf;173 int ret;174 175 reinit_completion(&csi->cmd_completion);176 177 ret = mei_cldev_send(csi->cldev, buf, len);178 if (ret < 0)179 goto out;180 181 ret = wait_for_completion_killable_timeout(&csi->cmd_completion,182 CSI_CMD_TIMEOUT);183 if (ret < 0) {184 goto out;185 } else if (!ret) {186 ret = -ETIMEDOUT;187 goto out;188 }189 190 /* command response status */191 ret = csi->cmd_response.status;192 if (ret == -1) {193 /* notify privacy on instead of reporting error */194 ret = 0;195 v4l2_ctrl_s_ctrl(csi->privacy_ctrl, 1);196 } else if (ret) {197 ret = -EINVAL;198 goto out;199 }200 201 if (csi->cmd_response.cmd_id != cmd->cmd_id)202 ret = -EINVAL;203 204out:205 return ret;206}207 208/* set CSI-2 link ownership */209static int csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner)210{211 struct csi_cmd cmd = { 0 };212 size_t cmd_size;213 int ret;214 215 cmd.cmd_id = CSI_SET_OWNER;216 cmd.param.param = owner;217 cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);218 219 mutex_lock(&csi->lock);220 221 ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);222 223 mutex_unlock(&csi->lock);224 225 return ret;226}227 228/* configure CSI-2 link between host and IVSC */229static int csi_set_link_cfg(struct mei_csi *csi)230{231 struct csi_cmd cmd = { 0 };232 size_t cmd_size;233 int ret;234 235 cmd.cmd_id = CSI_SET_CONF;236 cmd.param.conf.nr_of_lanes = csi->nr_of_lanes;237 cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq);238 cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);239 240 mutex_lock(&csi->lock);241 242 ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size);243 /*244 * wait configuration ready if download success. placing245 * delay under mutex is to make sure current command flow246 * completed before starting a possible new one.247 */248 if (!ret)249 msleep(CSI_FW_READY_DELAY_MS);250 251 mutex_unlock(&csi->lock);252 253 return ret;254}255 256/* callback for receive */257static void mei_csi_rx(struct mei_cl_device *cldev)258{259 struct mei_csi *csi = mei_cldev_get_drvdata(cldev);260 struct csi_notif notif = { 0 };261 int ret;262 263 ret = mei_cldev_recv(cldev, (u8 *)¬if, sizeof(notif));264 if (ret < 0) {265 dev_err(&cldev->dev, "recv error: %d\n", ret);266 return;267 }268 269 switch (notif.cmd_id) {270 case CSI_PRIVACY_NOTIF:271 if (notif.cont.cont < CSI_PRIVACY_MAX)272 v4l2_ctrl_s_ctrl(csi->privacy_ctrl,273 notif.cont.cont == CSI_PRIVACY_ON);274 break;275 case CSI_SET_OWNER:276 case CSI_SET_CONF:277 memcpy(&csi->cmd_response, ¬if, ret);278 279 complete(&csi->cmd_completion);280 break;281 default:282 break;283 }284}285 286static int mei_csi_set_stream(struct v4l2_subdev *sd, int enable)287{288 struct mei_csi *csi = sd_to_csi(sd);289 s64 freq;290 int ret;291 292 if (enable && csi->streaming == 0) {293 freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0);294 if (freq < 0) {295 dev_err(&csi->cldev->dev,296 "error %lld, invalid link_freq\n", freq);297 ret = freq;298 goto err;299 }300 csi->link_freq = freq;301 302 /* switch CSI-2 link to host */303 ret = csi_set_link_owner(csi, CSI_LINK_HOST);304 if (ret < 0)305 goto err;306 307 /* configure CSI-2 link */308 ret = csi_set_link_cfg(csi);309 if (ret < 0)310 goto err_switch;311 312 ret = v4l2_subdev_call(csi->remote, video, s_stream, 1);313 if (ret)314 goto err_switch;315 } else if (!enable && csi->streaming == 1) {316 v4l2_subdev_call(csi->remote, video, s_stream, 0);317 318 /* switch CSI-2 link to IVSC */319 ret = csi_set_link_owner(csi, CSI_LINK_IVSC);320 if (ret < 0)321 dev_warn(&csi->cldev->dev,322 "failed to switch CSI2 link: %d\n", ret);323 }324 325 csi->streaming = enable;326 327 return 0;328 329err_switch:330 csi_set_link_owner(csi, CSI_LINK_IVSC);331 332err:333 return ret;334}335 336static int mei_csi_init_state(struct v4l2_subdev *sd,337 struct v4l2_subdev_state *sd_state)338{339 struct v4l2_mbus_framefmt *mbusformat;340 unsigned int i;341 342 for (i = 0; i < sd->entity.num_pads; i++) {343 mbusformat = v4l2_subdev_state_get_format(sd_state, i);344 *mbusformat = mei_csi_format_mbus_default;345 }346 347 return 0;348}349 350static int mei_csi_set_fmt(struct v4l2_subdev *sd,351 struct v4l2_subdev_state *sd_state,352 struct v4l2_subdev_format *format)353{354 struct v4l2_mbus_framefmt *source_fmt;355 struct v4l2_mbus_framefmt *sink_fmt;356 357 sink_fmt = v4l2_subdev_state_get_format(sd_state, CSI_PAD_SINK);358 source_fmt = v4l2_subdev_state_get_format(sd_state, CSI_PAD_SOURCE);359 360 if (format->pad) {361 *source_fmt = *sink_fmt;362 363 return 0;364 }365 366 v4l_bound_align_image(&format->format.width, 1, 65536, 0,367 &format->format.height, 1, 65536, 0, 0);368 369 switch (format->format.code) {370 case MEDIA_BUS_FMT_RGB444_1X12:371 case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:372 case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:373 case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:374 case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:375 case MEDIA_BUS_FMT_RGB565_1X16:376 case MEDIA_BUS_FMT_BGR565_2X8_BE:377 case MEDIA_BUS_FMT_BGR565_2X8_LE:378 case MEDIA_BUS_FMT_RGB565_2X8_BE:379 case MEDIA_BUS_FMT_RGB565_2X8_LE:380 case MEDIA_BUS_FMT_RGB666_1X18:381 case MEDIA_BUS_FMT_RBG888_1X24:382 case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:383 case MEDIA_BUS_FMT_BGR888_1X24:384 case MEDIA_BUS_FMT_GBR888_1X24:385 case MEDIA_BUS_FMT_RGB888_1X24:386 case MEDIA_BUS_FMT_RGB888_2X12_BE:387 case MEDIA_BUS_FMT_RGB888_2X12_LE:388 case MEDIA_BUS_FMT_ARGB8888_1X32:389 case MEDIA_BUS_FMT_RGB888_1X32_PADHI:390 case MEDIA_BUS_FMT_RGB101010_1X30:391 case MEDIA_BUS_FMT_RGB121212_1X36:392 case MEDIA_BUS_FMT_RGB161616_1X48:393 case MEDIA_BUS_FMT_Y8_1X8:394 case MEDIA_BUS_FMT_UV8_1X8:395 case MEDIA_BUS_FMT_UYVY8_1_5X8:396 case MEDIA_BUS_FMT_VYUY8_1_5X8:397 case MEDIA_BUS_FMT_YUYV8_1_5X8:398 case MEDIA_BUS_FMT_YVYU8_1_5X8:399 case MEDIA_BUS_FMT_UYVY8_2X8:400 case MEDIA_BUS_FMT_VYUY8_2X8:401 case MEDIA_BUS_FMT_YUYV8_2X8:402 case MEDIA_BUS_FMT_YVYU8_2X8:403 case MEDIA_BUS_FMT_Y10_1X10:404 case MEDIA_BUS_FMT_UYVY10_2X10:405 case MEDIA_BUS_FMT_VYUY10_2X10:406 case MEDIA_BUS_FMT_YUYV10_2X10:407 case MEDIA_BUS_FMT_YVYU10_2X10:408 case MEDIA_BUS_FMT_Y12_1X12:409 case MEDIA_BUS_FMT_UYVY12_2X12:410 case MEDIA_BUS_FMT_VYUY12_2X12:411 case MEDIA_BUS_FMT_YUYV12_2X12:412 case MEDIA_BUS_FMT_YVYU12_2X12:413 case MEDIA_BUS_FMT_UYVY8_1X16:414 case MEDIA_BUS_FMT_VYUY8_1X16:415 case MEDIA_BUS_FMT_YUYV8_1X16:416 case MEDIA_BUS_FMT_YVYU8_1X16:417 case MEDIA_BUS_FMT_YDYUYDYV8_1X16:418 case MEDIA_BUS_FMT_UYVY10_1X20:419 case MEDIA_BUS_FMT_VYUY10_1X20:420 case MEDIA_BUS_FMT_YUYV10_1X20:421 case MEDIA_BUS_FMT_YVYU10_1X20:422 case MEDIA_BUS_FMT_VUY8_1X24:423 case MEDIA_BUS_FMT_YUV8_1X24:424 case MEDIA_BUS_FMT_UYYVYY8_0_5X24:425 case MEDIA_BUS_FMT_UYVY12_1X24:426 case MEDIA_BUS_FMT_VYUY12_1X24:427 case MEDIA_BUS_FMT_YUYV12_1X24:428 case MEDIA_BUS_FMT_YVYU12_1X24:429 case MEDIA_BUS_FMT_YUV10_1X30:430 case MEDIA_BUS_FMT_UYYVYY10_0_5X30:431 case MEDIA_BUS_FMT_AYUV8_1X32:432 case MEDIA_BUS_FMT_UYYVYY12_0_5X36:433 case MEDIA_BUS_FMT_YUV12_1X36:434 case MEDIA_BUS_FMT_YUV16_1X48:435 case MEDIA_BUS_FMT_UYYVYY16_0_5X48:436 case MEDIA_BUS_FMT_JPEG_1X8:437 case MEDIA_BUS_FMT_AHSV8888_1X32:438 case MEDIA_BUS_FMT_SBGGR8_1X8:439 case MEDIA_BUS_FMT_SGBRG8_1X8:440 case MEDIA_BUS_FMT_SGRBG8_1X8:441 case MEDIA_BUS_FMT_SRGGB8_1X8:442 case MEDIA_BUS_FMT_SBGGR10_1X10:443 case MEDIA_BUS_FMT_SGBRG10_1X10:444 case MEDIA_BUS_FMT_SGRBG10_1X10:445 case MEDIA_BUS_FMT_SRGGB10_1X10:446 case MEDIA_BUS_FMT_SBGGR12_1X12:447 case MEDIA_BUS_FMT_SGBRG12_1X12:448 case MEDIA_BUS_FMT_SGRBG12_1X12:449 case MEDIA_BUS_FMT_SRGGB12_1X12:450 case MEDIA_BUS_FMT_SBGGR14_1X14:451 case MEDIA_BUS_FMT_SGBRG14_1X14:452 case MEDIA_BUS_FMT_SGRBG14_1X14:453 case MEDIA_BUS_FMT_SRGGB14_1X14:454 case MEDIA_BUS_FMT_SBGGR16_1X16:455 case MEDIA_BUS_FMT_SGBRG16_1X16:456 case MEDIA_BUS_FMT_SGRBG16_1X16:457 case MEDIA_BUS_FMT_SRGGB16_1X16:458 break;459 default:460 format->format.code = MEDIA_BUS_FMT_Y8_1X8;461 break;462 }463 464 if (format->format.field == V4L2_FIELD_ANY)465 format->format.field = V4L2_FIELD_NONE;466 467 *sink_fmt = format->format;468 *source_fmt = *sink_fmt;469 470 return 0;471}472 473static int mei_csi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)474{475 struct mei_csi *csi = ctrl_to_csi(ctrl);476 s64 freq;477 478 if (ctrl->id == V4L2_CID_LINK_FREQ) {479 if (!csi->remote)480 return -EINVAL;481 482 freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0);483 if (freq < 0) {484 dev_err(&csi->cldev->dev,485 "error %lld, invalid link_freq\n", freq);486 return -EINVAL;487 }488 489 link_freq_menu_items[0] = freq;490 ctrl->val = 0;491 492 return 0;493 }494 495 return -EINVAL;496}497 498static const struct v4l2_ctrl_ops mei_csi_ctrl_ops = {499 .g_volatile_ctrl = mei_csi_g_volatile_ctrl,500};501 502static const struct v4l2_subdev_video_ops mei_csi_video_ops = {503 .s_stream = mei_csi_set_stream,504};505 506static const struct v4l2_subdev_pad_ops mei_csi_pad_ops = {507 .get_fmt = v4l2_subdev_get_fmt,508 .set_fmt = mei_csi_set_fmt,509};510 511static const struct v4l2_subdev_ops mei_csi_subdev_ops = {512 .video = &mei_csi_video_ops,513 .pad = &mei_csi_pad_ops,514};515 516static const struct v4l2_subdev_internal_ops mei_csi_internal_ops = {517 .init_state = mei_csi_init_state,518};519 520static const struct media_entity_operations mei_csi_entity_ops = {521 .link_validate = v4l2_subdev_link_validate,522};523 524static int mei_csi_notify_bound(struct v4l2_async_notifier *notifier,525 struct v4l2_subdev *subdev,526 struct v4l2_async_connection *asd)527{528 struct mei_csi *csi = notifier_to_csi(notifier);529 int pad;530 531 pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,532 MEDIA_PAD_FL_SOURCE);533 if (pad < 0)534 return pad;535 536 csi->remote = subdev;537 csi->remote_pad = pad;538 539 return media_create_pad_link(&subdev->entity, pad,540 &csi->subdev.entity, CSI_PAD_SINK,541 MEDIA_LNK_FL_ENABLED |542 MEDIA_LNK_FL_IMMUTABLE);543}544 545static void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier,546 struct v4l2_subdev *subdev,547 struct v4l2_async_connection *asd)548{549 struct mei_csi *csi = notifier_to_csi(notifier);550 551 csi->remote = NULL;552}553 554static const struct v4l2_async_notifier_operations mei_csi_notify_ops = {555 .bound = mei_csi_notify_bound,556 .unbind = mei_csi_notify_unbind,557};558 559static int mei_csi_init_controls(struct mei_csi *csi)560{561 u32 max;562 int ret;563 564 mutex_init(&csi->ctrl_lock);565 566 ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 2);567 if (ret)568 return ret;569 570 csi->ctrl_handler.lock = &csi->ctrl_lock;571 572 max = ARRAY_SIZE(link_freq_menu_items) - 1;573 csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler,574 &mei_csi_ctrl_ops,575 V4L2_CID_LINK_FREQ,576 max,577 0,578 link_freq_menu_items);579 if (csi->freq_ctrl)580 csi->freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY |581 V4L2_CTRL_FLAG_VOLATILE;582 583 csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL,584 V4L2_CID_PRIVACY, 0, 1, 1, 0);585 if (csi->privacy_ctrl)586 csi->privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;587 588 if (csi->ctrl_handler.error)589 return csi->ctrl_handler.error;590 591 csi->subdev.ctrl_handler = &csi->ctrl_handler;592 593 return 0;594}595 596static int mei_csi_parse_firmware(struct mei_csi *csi)597{598 struct v4l2_fwnode_endpoint v4l2_ep = {599 .bus_type = V4L2_MBUS_CSI2_DPHY,600 };601 struct device *dev = &csi->cldev->dev;602 struct v4l2_async_connection *asd;603 struct fwnode_handle *sink_ep, *source_ep;604 int ret;605 606 sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);607 if (!sink_ep) {608 dev_err(dev, "can't obtain sink endpoint\n");609 return -EINVAL;610 }611 612 v4l2_async_subdev_nf_init(&csi->notifier, &csi->subdev);613 csi->notifier.ops = &mei_csi_notify_ops;614 615 ret = v4l2_fwnode_endpoint_parse(sink_ep, &v4l2_ep);616 if (ret) {617 dev_err(dev, "could not parse v4l2 sink endpoint\n");618 goto out_nf_cleanup;619 }620 621 csi->nr_of_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;622 623 source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0);624 if (!source_ep) {625 ret = -ENOTCONN;626 dev_err(dev, "can't obtain source endpoint\n");627 goto out_nf_cleanup;628 }629 630 ret = v4l2_fwnode_endpoint_parse(source_ep, &v4l2_ep);631 fwnode_handle_put(source_ep);632 if (ret) {633 dev_err(dev, "could not parse v4l2 source endpoint\n");634 goto out_nf_cleanup;635 }636 637 if (csi->nr_of_lanes != v4l2_ep.bus.mipi_csi2.num_data_lanes) {638 ret = -EINVAL;639 dev_err(dev,640 "the number of lanes does not match (%u vs. %u)\n",641 csi->nr_of_lanes, v4l2_ep.bus.mipi_csi2.num_data_lanes);642 goto out_nf_cleanup;643 }644 645 asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, sink_ep,646 struct v4l2_async_connection);647 if (IS_ERR(asd)) {648 ret = PTR_ERR(asd);649 goto out_nf_cleanup;650 }651 652 ret = v4l2_async_nf_register(&csi->notifier);653 if (ret)654 goto out_nf_cleanup;655 656 fwnode_handle_put(sink_ep);657 658 return 0;659 660out_nf_cleanup:661 v4l2_async_nf_cleanup(&csi->notifier);662 fwnode_handle_put(sink_ep);663 664 return ret;665}666 667static int mei_csi_probe(struct mei_cl_device *cldev,668 const struct mei_cl_device_id *id)669{670 struct device *dev = &cldev->dev;671 struct pci_dev *ipu;672 struct mei_csi *csi;673 unsigned int i;674 int ret;675 676 for (i = 0, ipu = NULL; !ipu && ipu6_pci_tbl[i].vendor; i++)677 ipu = pci_get_device(ipu6_pci_tbl[i].vendor,678 ipu6_pci_tbl[i].device, NULL);679 680 if (!ipu)681 return -ENODEV;682 683 ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb);684 put_device(&ipu->dev);685 if (ret < 0)686 return ret;687 if (!dev_fwnode(dev)) {688 dev_err(dev, "mei-csi probed without device fwnode!\n");689 return -ENXIO;690 }691 692 csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL);693 if (!csi)694 return -ENOMEM;695 696 csi->cldev = cldev;697 mutex_init(&csi->lock);698 init_completion(&csi->cmd_completion);699 700 mei_cldev_set_drvdata(cldev, csi);701 702 ret = mei_cldev_enable(cldev);703 if (ret < 0) {704 dev_err(dev, "mei_cldev_enable failed: %d\n", ret);705 goto destroy_mutex;706 }707 708 ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx);709 if (ret) {710 dev_err(dev, "event cb registration failed: %d\n", ret);711 goto err_disable;712 }713 714 ret = mei_csi_parse_firmware(csi);715 if (ret)716 goto err_disable;717 718 csi->subdev.dev = &cldev->dev;719 csi->subdev.state_lock = &csi->lock;720 v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops);721 csi->subdev.internal_ops = &mei_csi_internal_ops;722 v4l2_set_subdevdata(&csi->subdev, csi);723 csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |724 V4L2_SUBDEV_FL_HAS_EVENTS;725 csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;726 csi->subdev.entity.ops = &mei_csi_entity_ops;727 728 snprintf(csi->subdev.name, sizeof(csi->subdev.name),729 MEI_CSI_ENTITY_NAME);730 731 ret = mei_csi_init_controls(csi);732 if (ret)733 goto err_ctrl_handler;734 735 csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;736 csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;737 ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS,738 csi->pads);739 if (ret)740 goto err_ctrl_handler;741 742 ret = v4l2_subdev_init_finalize(&csi->subdev);743 if (ret < 0)744 goto err_entity;745 746 ret = v4l2_async_register_subdev(&csi->subdev);747 if (ret < 0)748 goto err_subdev;749 750 pm_runtime_enable(&cldev->dev);751 752 return 0;753 754err_subdev:755 v4l2_subdev_cleanup(&csi->subdev);756 757err_entity:758 media_entity_cleanup(&csi->subdev.entity);759 760err_ctrl_handler:761 v4l2_ctrl_handler_free(&csi->ctrl_handler);762 mutex_destroy(&csi->ctrl_lock);763 v4l2_async_nf_unregister(&csi->notifier);764 v4l2_async_nf_cleanup(&csi->notifier);765 766err_disable:767 mei_cldev_disable(cldev);768 769destroy_mutex:770 mutex_destroy(&csi->lock);771 772 return ret;773}774 775static void mei_csi_remove(struct mei_cl_device *cldev)776{777 struct mei_csi *csi = mei_cldev_get_drvdata(cldev);778 779 v4l2_async_nf_unregister(&csi->notifier);780 v4l2_async_nf_cleanup(&csi->notifier);781 v4l2_ctrl_handler_free(&csi->ctrl_handler);782 mutex_destroy(&csi->ctrl_lock);783 v4l2_async_unregister_subdev(&csi->subdev);784 v4l2_subdev_cleanup(&csi->subdev);785 media_entity_cleanup(&csi->subdev.entity);786 787 pm_runtime_disable(&cldev->dev);788 789 mutex_destroy(&csi->lock);790}791 792#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \793 0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA)794 795static const struct mei_cl_device_id mei_csi_tbl[] = {796 { .uuid = MEI_CSI_UUID, .version = MEI_CL_VERSION_ANY },797 { /* sentinel */ }798};799MODULE_DEVICE_TABLE(mei, mei_csi_tbl);800 801static struct mei_cl_driver mei_csi_driver = {802 .id_table = mei_csi_tbl,803 .name = KBUILD_MODNAME,804 805 .probe = mei_csi_probe,806 .remove = mei_csi_remove,807};808 809module_mei_cl_driver(mei_csi_driver);810 811MODULE_IMPORT_NS(INTEL_IPU_BRIDGE);812MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");813MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");814MODULE_DESCRIPTION("Device driver for IVSC CSI");815MODULE_LICENSE("GPL");816