brintos

brintos / linux-shallow public Read only

0
0
Text · 19.6 KiB · 2a9c12c Raw
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 *)&notif, 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, &notif, 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