File Index
Files and directories
Sign in
FILE

mei_csi.c

File preview with syntax highlighting.

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