diff options
Diffstat (limited to 'drivers/staging/media/imx')
-rw-r--r-- | drivers/staging/media/imx/TODO | 63 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-ic-prp.c | 4 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-capture.c | 8 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-csi.c | 189 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-dev.c | 400 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-internal-sd.c | 253 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-of.c | 278 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media-utils.c | 122 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx-media.h | 189 | ||||
-rw-r--r-- | drivers/staging/media/imx/imx6-mipi-csi2.c | 4 |
10 files changed, 726 insertions, 784 deletions
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO index 0bee3132b26f..9eb7326f3fc6 100644 --- a/drivers/staging/media/imx/TODO +++ b/drivers/staging/media/imx/TODO @@ -1,19 +1,14 @@ -- Clean up and move the ov5642 subdev driver to drivers/media/i2c, or - merge support for OV5642 into drivers/media/i2c/ov5640.c, and create - the binding docs for it. - - The Frame Interval Monitor could be exported to v4l2-core for general use. -- At driver load time, the device-tree node that is the original source - (the "sensor"), is parsed to record its media bus configuration, and - this info is required in imx-media-csi.c to setup the CSI. - Laurent Pinchart argues that instead the CSI subdev should call its - neighbor's g_mbus_config op (which should be propagated if necessary) - to get this info. However Hans Verkuil is planning to remove the - g_mbus_config op. For now this driver uses the parsed DT mbus config - method until this issue is resolved. +- The CSI subdevice parses its nearest upstream neighbor's device-tree + bus config in order to setup the CSI. Laurent Pinchart argues that + instead the CSI subdev should call its neighbor's g_mbus_config op + (which should be propagated if necessary) to get this info. However + Hans Verkuil is planning to remove the g_mbus_config op. For now this + driver uses the parsed DT bus config method until this issue is + resolved. - This media driver supports inheriting V4L2 controls to the video capture devices, from the subdevices in the capture device's @@ -21,3 +16,47 @@ link_notify callback when the pipeline is modified. It should be decided whether this feature is useful enough to make it generally available by exporting to v4l2-core. + +- The OF graph is walked at probe time to form the list of fwnodes to + be passed to v4l2_async_notifier_register(), starting from the IPU + CSI ports. And after all async subdevices have been bound, + v4l2_fwnode_parse_link() is used to form the media links between + the entities discovered by walking the OF graph. + + While this approach allows support for arbitrary OF graphs, there + are some assumptions for this to work: + + 1. All port parent nodes reachable in the graph from the IPU CSI + ports bind to V4L2 async subdevice drivers. + + If a device has mixed-use ports such as video plus audio, the + endpoints from the audio ports are followed to devices that must + bind to V4L2 subdevice drivers, and not for example, to an ALSA + driver or a non-V4L2 media driver. If the device were bound to + such a driver, imx-media would never get an async completion + notification because the device fwnode was added to the async + list, but the driver does not interface with the V4L2 async + framework. + + 2. Every port reachable in the graph is treated as a media pad, + owned by the V4L2 subdevice that is bound to the port's parent. + + This presents problems for devices that don't make this port = pad + assumption. Examples are SMIAPP compatible cameras which define only + a single output port node, but which define multiple pads owned + by multiple subdevices (pixel-array, binner, scaler). Or video + decoders (entity function MEDIA_ENT_F_ATV_DECODER), which also define + only a single output port node, but define multiple pads for video, + VBI, and audio out. + + A workaround at present is to set the port reg properties to + correspond to the media pad index that the port represents. A + possible long-term solution is to implement a subdev API that + maps a port id to a media pad index. + + 3. Every endpoint of a port reachable in the graph is treated as + a media link, between V4L2 subdevices that are bound to the + port parents of the local and remote endpoints. + + Which means a port must not contain mixed-use endpoints, they + must all refer to media links between V4L2 subdevices. diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c index 9e41987f9884..c6d7e80932ad 100644 --- a/drivers/staging/media/imx/imx-ic-prp.c +++ b/drivers/staging/media/imx/imx-ic-prp.c @@ -300,7 +300,7 @@ static int prp_link_validate(struct v4l2_subdev *sd, { struct imx_ic_priv *ic_priv = v4l2_get_subdevdata(sd); struct prp_priv *priv = ic_priv->prp_priv; - struct imx_media_subdev *csi; + struct v4l2_subdev *csi; int ret; ret = v4l2_subdev_link_validate_default(sd, link, @@ -333,7 +333,7 @@ static int prp_link_validate(struct v4l2_subdev *sd, } if (csi) { - switch (csi->sd->grp_id) { + switch (csi->grp_id) { case IMX_MEDIA_GRP_ID_CSI0: priv->csi_id = 0; break; diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index ea145bafb880..576bdc7e9c42 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -449,9 +449,6 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count) unsigned long flags; int ret; - if (vb2_is_streaming(vq)) - return 0; - ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity, true); if (ret) { @@ -480,9 +477,6 @@ static void capture_stop_streaming(struct vb2_queue *vq) unsigned long flags; int ret; - if (!vb2_is_streaming(vq)) - return; - spin_lock_irqsave(&priv->q_lock, flags); priv->stop = true; spin_unlock_irqrestore(&priv->q_lock, flags); @@ -754,6 +748,8 @@ imx_media_capture_device_init(struct v4l2_subdev *src_sd, int pad) vfd->queue = &priv->q; priv->vdev.vfd = vfd; + INIT_LIST_HEAD(&priv->vdev.list); + video_set_drvdata(vfd, priv); v4l2_ctrl_handler_init(&priv->ctrl_hdlr, 0); diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c index 26994b429cf2..eb7be5093a9d 100644 --- a/drivers/staging/media/imx/imx-media-csi.c +++ b/drivers/staging/media/imx/imx-media-csi.c @@ -13,6 +13,7 @@ #include <linux/gcd.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <media/v4l2-ctrls.h> @@ -99,8 +100,8 @@ struct csi_priv { /* the mipi virtual channel number at link validate */ int vc_num; - /* the attached sensor at stream on */ - struct imx_media_subdev *sensor; + /* the upstream endpoint CSI is receiving from */ + struct v4l2_fwnode_endpoint upstream_ep; spinlock_t irqlock; /* protect eof_irq handler */ struct timer_list eof_timeout_timer; @@ -120,6 +121,70 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev) return container_of(sdev, struct csi_priv, sd); } +static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep) +{ + return ep->bus_type != V4L2_MBUS_CSI2 && + ep->bus.parallel.bus_width >= 16; +} + +/* + * Parses the fwnode endpoint from the source pad of the entity + * connected to this CSI. This will either be the entity directly + * upstream from the CSI-2 receiver, or directly upstream from the + * video mux. The endpoint is needed to determine the bus type and + * bus config coming into the CSI. + */ +static int csi_get_upstream_endpoint(struct csi_priv *priv, + struct v4l2_fwnode_endpoint *ep) +{ + struct device_node *endpoint, *port; + struct media_entity *src; + struct v4l2_subdev *sd; + struct media_pad *pad; + + if (!priv->src_sd) + return -EPIPE; + + src = &priv->src_sd->entity; + + if (src->function == MEDIA_ENT_F_VID_MUX) { + /* + * CSI is connected directly to video mux, skip up to + * CSI-2 receiver if it is in the path, otherwise stay + * with video mux. + */ + sd = imx_media_find_upstream_subdev(priv->md, src, + IMX_MEDIA_GRP_ID_CSI2); + if (!IS_ERR(sd)) + src = &sd->entity; + } + + /* get source pad of entity directly upstream from src */ + pad = imx_media_find_upstream_pad(priv->md, src, 0); + if (IS_ERR(pad)) + return PTR_ERR(pad); + + sd = media_entity_to_v4l2_subdev(pad->entity); + + /* + * NOTE: this assumes an OF-graph port id is the same as a + * media pad index. + */ + port = of_graph_get_port_by_id(sd->dev->of_node, pad->index); + if (!port) + return -ENODEV; + + endpoint = of_get_next_child(port, NULL); + of_node_put(port); + if (!endpoint) + return -ENODEV; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep); + of_node_put(endpoint); + + return 0; +} + static void csi_idmac_put_ipu_resources(struct csi_priv *priv) { if (priv->idmac_ch) @@ -302,7 +367,6 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv, static int csi_idmac_setup_channel(struct csi_priv *priv) { struct imx_media_video_dev *vdev = priv->vdev; - struct v4l2_fwnode_endpoint *sensor_ep; struct v4l2_mbus_framefmt *infmt; struct ipu_image image; u32 passthrough_bits; @@ -312,7 +376,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) int ret; infmt = &priv->format_mbus[CSI_SINK_PAD]; - sensor_ep = &priv->sensor->sensor_ep; ipu_cpmem_zero(priv->idmac_ch); @@ -330,14 +393,14 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) * Check for conditions that require the IPU to handle the * data internally as generic data, aka passthrough mode: * - raw bayer formats - * - the sensor bus is 16-bit parallel + * - the CSI is receiving from a 16-bit parallel bus */ switch (image.pix.pixelformat) { case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SRGGB8: - burst_size = 8; + burst_size = 16; passthrough = true; passthrough_bits = 8; break; @@ -354,8 +417,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) burst_size = (image.pix.width & 0x3f) ? ((image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32) : 64; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; /* Skip writing U and V components to odd rows */ ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch); @@ -364,14 +426,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv) case V4L2_PIX_FMT_UYVY: burst_size = (image.pix.width & 0x1f) ? ((image.pix.width & 0xf) ? 8 : 16) : 32; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; default: burst_size = (image.pix.width & 0xf) ? 8 : 16; - passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 && - sensor_ep->bus.parallel.bus_width >= 16); + passthrough = is_parallel_16bit_bus(&priv->upstream_ep); passthrough_bits = 16; break; } @@ -568,22 +628,20 @@ static void csi_idmac_stop(struct csi_priv *priv) static int csi_setup(struct csi_priv *priv) { struct v4l2_mbus_framefmt *infmt, *outfmt; - struct v4l2_mbus_config sensor_mbus_cfg; - struct v4l2_fwnode_endpoint *sensor_ep; + struct v4l2_mbus_config mbus_cfg; struct v4l2_mbus_framefmt if_fmt; infmt = &priv->format_mbus[CSI_SINK_PAD]; outfmt = &priv->format_mbus[priv->active_output_pad]; - sensor_ep = &priv->sensor->sensor_ep; - /* compose mbus_config from sensor endpoint */ - sensor_mbus_cfg.type = sensor_ep->bus_type; - sensor_mbus_cfg.flags = (sensor_ep->bus_type == V4L2_MBUS_CSI2) ? - sensor_ep->bus.mipi_csi2.flags : - sensor_ep->bus.parallel.flags; + /* compose mbus_config from the upstream endpoint */ + mbus_cfg.type = priv->upstream_ep.bus_type; + mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ? + priv->upstream_ep.bus.mipi_csi2.flags : + priv->upstream_ep.bus.parallel.flags; /* - * we need to pass input sensor frame to CSI interface, but + * we need to pass input frame to CSI interface, but * with translated field type from output format */ if_fmt = *infmt; @@ -595,7 +653,7 @@ static int csi_setup(struct csi_priv *priv) priv->crop.width == 2 * priv->compose.width, priv->crop.height == 2 * priv->compose.height); - ipu_csi_init_interface(priv->csi, &sensor_mbus_cfg, &if_fmt); + ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt); ipu_csi_set_dest(priv->csi, priv->dest); @@ -611,35 +669,11 @@ static int csi_setup(struct csi_priv *priv) static int csi_start(struct csi_priv *priv) { struct v4l2_fract *output_fi, *input_fi; - u32 bad_frames = 0; int ret; - if (!priv->sensor) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return -EINVAL; - } - output_fi = &priv->frame_interval[priv->active_output_pad]; input_fi = &priv->frame_interval[CSI_SINK_PAD]; - ret = v4l2_subdev_call(priv->sensor->sd, sensor, - g_skip_frames, &bad_frames); - if (!ret && bad_frames) { - u32 delay_usec; - - /* - * This sensor has bad frames when it is turned on, - * add a delay to avoid them before enabling the CSI - * hardware. Especially for sensors with a bt.656 interface, - * any shifts in the SAV/EAV sync codes will cause the CSI - * to lose vert/horiz sync. - */ - delay_usec = DIV_ROUND_UP_ULL( - (u64)USEC_PER_SEC * input_fi->numerator * bad_frames, - input_fi->denominator); - usleep_range(delay_usec, delay_usec + 1000); - } - if (priv->dest == IPU_CSI_DEST_IDMAC) { ret = csi_idmac_start(priv); if (ret) @@ -971,9 +1005,8 @@ static int csi_link_validate(struct v4l2_subdev *sd, struct v4l2_subdev_format *sink_fmt) { struct csi_priv *priv = v4l2_get_subdevdata(sd); - struct v4l2_fwnode_endpoint *sensor_ep; + struct v4l2_fwnode_endpoint upstream_ep; const struct imx_media_pixfmt *incc; - struct imx_media_subdev *sensor; bool is_csi2; int ret; @@ -982,22 +1015,20 @@ static int csi_link_validate(struct v4l2_subdev *sd, if (ret) return ret; - sensor = __imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(priv->sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); - priv->sensor = sensor; - sensor_ep = &priv->sensor->sensor_ep; - is_csi2 = (sensor_ep->bus_type == V4L2_MBUS_CSI2); + priv->upstream_ep = upstream_ep; + is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2); incc = priv->cc[CSI_SINK_PAD]; if (priv->dest != IPU_CSI_DEST_IDMAC && - (incc->bayer || (!is_csi2 && - sensor_ep->bus.parallel.bus_width >= 16))) { + (incc->bayer || is_parallel_16bit_bus(&upstream_ep))) { v4l2_err(&priv->sd, "bayer/16-bit parallel buses must go to IDMAC pad\n"); ret = -EINVAL; @@ -1067,12 +1098,8 @@ static void csi_try_crop(struct csi_priv *priv, struct v4l2_rect *crop, struct v4l2_subdev_pad_config *cfg, struct v4l2_mbus_framefmt *infmt, - struct imx_media_subdev *sensor) + struct v4l2_fwnode_endpoint *upstream_ep) { - struct v4l2_fwnode_endpoint *sensor_ep; - - sensor_ep = &sensor->sensor_ep; - crop->width = min_t(__u32, infmt->width, crop->width); if (crop->left + crop->width > infmt->width) crop->left = infmt->width - crop->width; @@ -1086,7 +1113,7 @@ static void csi_try_crop(struct csi_priv *priv, * sync, so fix it to NTSC/PAL active lines. NTSC contains * 2 extra lines of active video that need to be cropped. */ - if (sensor_ep->bus_type == V4L2_MBUS_BT656 && + if (upstream_ep->bus_type == V4L2_MBUS_BT656 && (V4L2_FIELD_HAS_BOTH(infmt->field) || infmt->field == V4L2_FIELD_ALTERNATE)) { crop->height = infmt->height; @@ -1236,7 +1263,7 @@ out: } static void csi_try_fmt(struct csi_priv *priv, - struct imx_media_subdev *sensor, + struct v4l2_fwnode_endpoint *upstream_ep, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *sdformat, struct v4l2_rect *crop, @@ -1304,7 +1331,7 @@ static void csi_try_fmt(struct csi_priv *priv, crop->top = 0; crop->width = sdformat->format.width; crop->height = sdformat->format.height; - csi_try_crop(priv, crop, cfg, &sdformat->format, sensor); + csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep); compose->left = 0; compose->top = 0; compose->width = crop->width; @@ -1333,20 +1360,20 @@ static int csi_set_fmt(struct v4l2_subdev *sd, { struct csi_priv *priv = v4l2_get_subdevdata(sd); struct imx_media_video_dev *vdev = priv->vdev; + struct v4l2_fwnode_endpoint upstream_ep; const struct imx_media_pixfmt *cc; - struct imx_media_subdev *sensor; struct v4l2_pix_format vdev_fmt; struct v4l2_mbus_framefmt *fmt; struct v4l2_rect *crop, *compose; - int ret = 0; + int ret; if (sdformat->pad >= CSI_NUM_PADS) return -EINVAL; - sensor = imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); @@ -1359,7 +1386,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd, crop = __csi_get_crop(priv, cfg, sdformat->which); compose = __csi_get_compose(priv, cfg, sdformat->which); - csi_try_fmt(priv, sensor, cfg, sdformat, crop, compose, &cc); + csi_try_fmt(priv, &upstream_ep, cfg, sdformat, crop, compose, &cc); fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which); *fmt = sdformat->format; @@ -1376,8 +1403,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd, format.pad = pad; format.which = sdformat->which; format.format = sdformat->format; - csi_try_fmt(priv, sensor, cfg, &format, NULL, compose, - &outcc); + csi_try_fmt(priv, &upstream_ep, cfg, &format, + NULL, compose, &outcc); outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which); *outfmt = format.format; @@ -1472,18 +1499,18 @@ static int csi_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_selection *sel) { struct csi_priv *priv = v4l2_get_subdevdata(sd); + struct v4l2_fwnode_endpoint upstream_ep; struct v4l2_mbus_framefmt *infmt; struct v4l2_rect *crop, *compose; - struct imx_media_subdev *sensor; - int pad, ret = 0; + int pad, ret; if (sel->pad != CSI_SINK_PAD) return -EINVAL; - sensor = imx_media_find_sensor(priv->md, &priv->sd.entity); - if (IS_ERR(sensor)) { - v4l2_err(&priv->sd, "no sensor attached\n"); - return PTR_ERR(sensor); + ret = csi_get_upstream_endpoint(priv, &upstream_ep); + if (ret) { + v4l2_err(&priv->sd, "failed to find upstream endpoint\n"); + return ret; } mutex_lock(&priv->lock); @@ -1511,7 +1538,7 @@ static int csi_set_selection(struct v4l2_subdev *sd, goto out; } - csi_try_crop(priv, &sel->r, cfg, infmt, sensor); + csi_try_crop(priv, &sel->r, cfg, infmt, &upstream_ep); *crop = sel->r; diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index 47c4c954fed5..289d775c4820 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/fs.h> #include <linux/module.h> +#include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> @@ -32,29 +33,28 @@ static inline struct imx_media_dev *notifier2dev(struct v4l2_async_notifier *n) } /* - * Find a subdev by device node or device name. This is called during + * Find an asd by fwnode or device name. This is called during * driver load to form the async subdev list and bind them. */ -struct imx_media_subdev * -imx_media_find_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *devname) +static struct v4l2_async_subdev * +find_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + const char *devname) { - struct fwnode_handle *fwnode = np ? of_fwnode_handle(np) : NULL; - struct imx_media_subdev *imxsd; - int i; + struct imx_media_async_subdev *imxasd; + struct v4l2_async_subdev *asd; - for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - switch (imxsd->asd.match_type) { + list_for_each_entry(imxasd, &imxmd->asd_list, list) { + asd = &imxasd->asd; + switch (asd->match_type) { case V4L2_ASYNC_MATCH_FWNODE: - if (fwnode && imxsd->asd.match.fwnode.fwnode == fwnode) - return imxsd; + if (fwnode && asd->match.fwnode == fwnode) + return asd; break; case V4L2_ASYNC_MATCH_DEVNAME: - if (devname && - !strcmp(imxsd->asd.match.device_name.name, devname)) - return imxsd; + if (devname && !strcmp(asd->match.device_name, + devname)) + return asd; break; default: break; @@ -66,57 +66,53 @@ imx_media_find_async_subdev(struct imx_media_dev *imxmd, /* - * Adds a subdev to the async subdev list. If np is non-NULL, adds + * Adds a subdev to the async subdev list. If fwnode is non-NULL, adds * the async as a V4L2_ASYNC_MATCH_FWNODE match type, otherwise as * a V4L2_ASYNC_MATCH_DEVNAME match type using the dev_name of the * given platform_device. This is called during driver load when * forming the async subdev list. */ -struct imx_media_subdev * -imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - struct platform_device *pdev) +int imx_media_add_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + struct platform_device *pdev) { - struct imx_media_subdev *imxsd; + struct device_node *np = to_of_node(fwnode); + struct imx_media_async_subdev *imxasd; struct v4l2_async_subdev *asd; const char *devname = NULL; - int sd_idx; + int ret = 0; mutex_lock(&imxmd->mutex); if (pdev) devname = dev_name(&pdev->dev); - /* return -EEXIST if this subdev already added */ - if (imx_media_find_async_subdev(imxmd, np, devname)) { + /* return -EEXIST if this asd already added */ + if (find_async_subdev(imxmd, fwnode, devname)) { dev_dbg(imxmd->md.dev, "%s: already added %s\n", __func__, np ? np->name : devname); - imxsd = ERR_PTR(-EEXIST); + ret = -EEXIST; goto out; } - sd_idx = imxmd->subdev_notifier.num_subdevs; - if (sd_idx >= IMX_MEDIA_MAX_SUBDEVS) { - dev_err(imxmd->md.dev, "%s: too many subdevs! can't add %s\n", - __func__, np ? np->name : devname); - imxsd = ERR_PTR(-ENOSPC); + imxasd = devm_kzalloc(imxmd->md.dev, sizeof(*imxasd), GFP_KERNEL); + if (!imxasd) { + ret = -ENOMEM; goto out; } + asd = &imxasd->asd; - imxsd = &imxmd->subdev[sd_idx]; - - asd = &imxsd->asd; - if (np) { + if (fwnode) { asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode.fwnode = of_fwnode_handle(np); + asd->match.fwnode = fwnode; } else { asd->match_type = V4L2_ASYNC_MATCH_DEVNAME; - strncpy(imxsd->devname, devname, sizeof(imxsd->devname)); - asd->match.device_name.name = imxsd->devname; - imxsd->pdev = pdev; + asd->match.device_name = devname; + imxasd->pdev = pdev; } - imxmd->async_ptrs[sd_idx] = asd; + list_add_tail(&imxasd->list, &imxmd->asd_list); + imxmd->subdev_notifier.num_subdevs++; dev_dbg(imxmd->md.dev, "%s: added %s, match type %s\n", @@ -124,50 +120,6 @@ imx_media_add_async_subdev(struct imx_media_dev *imxmd, out: mutex_unlock(&imxmd->mutex); - return imxsd; -} - -/* - * Adds an imx-media link to a subdev pad's link list. This is called - * during driver load when forming the links between subdevs. - * - * @pad: the local pad - * @remote_node: the device node of the remote subdev - * @remote_devname: the device name of the remote subdev - * @local_pad: local pad index - * @remote_pad: remote pad index - */ -int imx_media_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *remote_node, - const char *remote_devname, - int local_pad, int remote_pad) -{ - struct imx_media_link *link; - int link_idx, ret = 0; - - mutex_lock(&imxmd->mutex); - - link_idx = pad->num_links; - if (link_idx >= IMX_MEDIA_MAX_LINKS) { - dev_err(imxmd->md.dev, "%s: too many links!\n", __func__); - ret = -ENOSPC; - goto out; - } - - link = &pad->link[link_idx]; - - link->remote_sd_node = remote_node; - if (remote_devname) - strncpy(link->remote_devname, remote_devname, - sizeof(link->remote_devname)); - - link->local_pad = local_pad; - link->remote_pad = remote_pad; - - pad->num_links++; -out: - mutex_unlock(&imxmd->mutex); return ret; } @@ -206,122 +158,66 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier, struct v4l2_async_subdev *asd) { struct imx_media_dev *imxmd = notifier2dev(notifier); - struct device_node *np = to_of_node(sd->fwnode); - struct imx_media_subdev *imxsd; int ret = 0; mutex_lock(&imxmd->mutex); - imxsd = imx_media_find_async_subdev(imxmd, np, dev_name(sd->dev)); - if (!imxsd) { - ret = -EINVAL; - goto out; - } - if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) { ret = imx_media_get_ipu(imxmd, sd); if (ret) - goto out_unlock; - } else if (sd->entity.function == MEDIA_ENT_F_VID_MUX) { - /* this is a video mux */ - sd->grp_id = IMX_MEDIA_GRP_ID_VIDMUX; - } else if (imxsd->num_sink_pads == 0) { - /* - * this is an original source of video frames, it - * could be a camera sensor, an analog decoder, or - * a bridge device (HDMI -> MIPI CSI-2 for example). - * This group ID is used to locate the entity that - * is the original source of video in a pipeline. - */ - sd->grp_id = IMX_MEDIA_GRP_ID_SENSOR; + goto out; } - /* attach the subdev */ - imxsd->sd = sd; + v4l2_info(&imxmd->v4l2_dev, "subdev %s bound\n", sd->name); out: - if (ret) - v4l2_warn(&imxmd->v4l2_dev, - "Received unknown subdev %s\n", sd->name); - else - v4l2_info(&imxmd->v4l2_dev, - "Registered subdev %s\n", sd->name); - -out_unlock: mutex_unlock(&imxmd->mutex); return ret; } /* - * Create a single source->sink media link given a subdev and a single - * link from one of its source pads. Called after all subdevs have - * registered. + * create the media links for all subdevs that registered async. + * Called after all async subdevs have bound. */ -static int imx_media_create_link(struct imx_media_dev *imxmd, - struct imx_media_subdev *src, - struct imx_media_link *link) +static int imx_media_create_links(struct v4l2_async_notifier *notifier) { - struct imx_media_subdev *sink; - u16 source_pad, sink_pad; + struct imx_media_dev *imxmd = notifier2dev(notifier); + struct v4l2_subdev *sd; int ret; - sink = imx_media_find_async_subdev(imxmd, link->remote_sd_node, - link->remote_devname); - if (!sink) { - v4l2_warn(&imxmd->v4l2_dev, "%s: no sink for %s:%d\n", - __func__, src->sd->name, link->local_pad); - return 0; - } - - source_pad = link->local_pad; - sink_pad = link->remote_pad; - - v4l2_info(&imxmd->v4l2_dev, "%s: %s:%d -> %s:%d\n", __func__, - src->sd->name, source_pad, sink->sd->name, sink_pad); - - ret = media_create_pad_link(&src->sd->entity, source_pad, - &sink->sd->entity, sink_pad, 0); - if (ret) - v4l2_err(&imxmd->v4l2_dev, - "create_pad_link failed: %d\n", ret); - - return ret; -} - -/* - * create the media links from all imx-media pads and their links. - * Called after all subdevs have registered. - */ -static int imx_media_create_links(struct imx_media_dev *imxmd) -{ - struct imx_media_subdev *imxsd; - struct imx_media_link *link; - struct imx_media_pad *pad; - int num_pads, i, j, k; - int ret = 0; - - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - num_pads = imxsd->num_sink_pads + imxsd->num_src_pads; - - for (j = 0; j < num_pads; j++) { - pad = &imxsd->pad[j]; - - /* only create the source->sink links */ - if (!(pad->pad.flags & MEDIA_PAD_FL_SOURCE)) - continue; - - for (k = 0; k < pad->num_links; k++) { - link = &pad->link[k]; - - ret = imx_media_create_link(imxmd, imxsd, link); - if (ret) - goto out; - } + /* + * Only links are created between subdevices that are known + * to the async notifier. If there are other non-async subdevices, + * they were created internally by some subdevice (smiapp is one + * example). In those cases it is expected the subdevice is + * responsible for creating those internal links. + */ + list_for_each_entry(sd, ¬ifier->done, async_list) { + switch (sd->grp_id) { + case IMX_MEDIA_GRP_ID_VDIC: + case IMX_MEDIA_GRP_ID_IC_PRP: + case IMX_MEDIA_GRP_ID_IC_PRPENC: + case IMX_MEDIA_GRP_ID_IC_PRPVF: + case IMX_MEDIA_GRP_ID_CSI0: + case IMX_MEDIA_GRP_ID_CSI1: + ret = imx_media_create_internal_links(imxmd, sd); + if (ret) + return ret; + /* + * the CSIs straddle between the external and the IPU + * internal entities, so create the external links + * to the CSI sink pads. + */ + if (sd->grp_id & IMX_MEDIA_GRP_ID_CSI) + imx_media_create_csi_of_links(imxmd, sd); + break; + default: + /* this is an external fwnode subdev */ + imx_media_create_of_links(imxmd, sd); + break; } } -out: - return ret; + return 0; } /* @@ -333,39 +229,45 @@ static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, struct media_pad *srcpad) { struct media_entity *entity = srcpad->entity; - struct imx_media_subdev *imxsd; - struct imx_media_pad *imxpad; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; struct media_link *link; struct v4l2_subdev *sd; - int i, vdev_idx, ret; + int i, ret; /* skip this entity if not a v4l2_subdev */ if (!is_media_entity_v4l2_subdev(entity)) return 0; sd = media_entity_to_v4l2_subdev(entity); - imxsd = imx_media_find_subdev_by_sd(imxmd, sd); - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - imxpad = &imxsd->pad[srcpad->index]; - vdev_idx = imxpad->num_vdevs; + pad_vdev_list = to_pad_vdev_list(sd, srcpad->index); + if (!pad_vdev_list) { + v4l2_warn(&imxmd->v4l2_dev, "%s:%u has no vdev list!\n", + entity->name, srcpad->index); + /* + * shouldn't happen, but no reason to fail driver load, + * just skip this entity. + */ + return 0; + } /* just return if we've been here before */ - for (i = 0; i < vdev_idx; i++) - if (vdev == imxpad->vdev[i]) + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + if (pad_vdev->vdev == vdev) return 0; - - if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) { - dev_err(imxmd->md.dev, "can't add %s to pad %s:%u\n", - vdev->vfd->entity.name, entity->name, srcpad->index); - return -ENOSPC; } dev_dbg(imxmd->md.dev, "adding %s to pad %s:%u\n", vdev->vfd->entity.name, entity->name, srcpad->index); - imxpad->vdev[vdev_idx] = vdev; - imxpad->num_vdevs++; + + pad_vdev = devm_kzalloc(imxmd->md.dev, sizeof(*pad_vdev), GFP_KERNEL); + if (!pad_vdev) + return -ENOMEM; + + /* attach this vdev to this pad */ + pad_vdev->vdev = vdev; + list_add_tail(&pad_vdev->list, pad_vdev_list); /* move upstream from this entity's sink pads */ for (i = 0; i < entity->num_pads; i++) { @@ -387,15 +289,49 @@ static int imx_media_add_vdev_to_pad(struct imx_media_dev *imxmd, return 0; } +/* + * For every subdevice, allocate an array of list_head's, one list_head + * for each pad, to hold the list of video devices reachable from that + * pad. + */ +static int imx_media_alloc_pad_vdev_lists(struct imx_media_dev *imxmd) +{ + struct list_head *vdev_lists; + struct media_entity *entity; + struct v4l2_subdev *sd; + int i; + + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + entity = &sd->entity; + vdev_lists = devm_kzalloc( + imxmd->md.dev, + entity->num_pads * sizeof(*vdev_lists), + GFP_KERNEL); + if (!vdev_lists) + return -ENOMEM; + + /* attach to the subdev's host private pointer */ + sd->host_priv = vdev_lists; + + for (i = 0; i < entity->num_pads; i++) + INIT_LIST_HEAD(to_pad_vdev_list(sd, i)); + } + + return 0; +} + /* form the vdev lists in all imx-media source pads */ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) { struct imx_media_video_dev *vdev; struct media_link *link; - int i, ret; + int ret; + + ret = imx_media_alloc_pad_vdev_lists(imxmd); + if (ret) + return ret; - for (i = 0; i < imxmd->num_vdevs; i++) { - vdev = imxmd->vdev[i]; + list_for_each_entry(vdev, &imxmd->vdev_list, list) { link = list_first_entry(&vdev->vfd->entity.links, struct media_link, list); ret = imx_media_add_vdev_to_pad(imxmd, vdev, link->source); @@ -410,20 +346,11 @@ static int imx_media_create_pad_vdev_lists(struct imx_media_dev *imxmd) static int imx_media_probe_complete(struct v4l2_async_notifier *notifier) { struct imx_media_dev *imxmd = notifier2dev(notifier); - int i, ret; + int ret; mutex_lock(&imxmd->mutex); - /* make sure all subdevs were bound */ - for (i = 0; i < imxmd->num_subdevs; i++) { - if (!imxmd->subdev[i].sd) { - v4l2_err(&imxmd->v4l2_dev, "unbound subdev!\n"); - ret = -ENODEV; - goto unlock; - } - } - - ret = imx_media_create_links(imxmd); + ret = imx_media_create_links(notifier); if (ret) goto unlock; @@ -492,12 +419,12 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, unsigned int notification) { struct media_entity *source = link->source->entity; - struct imx_media_subdev *imxsd; - struct imx_media_pad *imxpad; + struct imx_media_pad_vdev *pad_vdev; + struct list_head *pad_vdev_list; struct imx_media_dev *imxmd; struct video_device *vfd; struct v4l2_subdev *sd; - int i, pad_idx, ret; + int pad_idx, ret; ret = v4l2_pipeline_link_notify(link, flags, notification); if (ret) @@ -512,10 +439,11 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, imxmd = dev_get_drvdata(sd->v4l2_dev->dev); - imxsd = imx_media_find_subdev_by_sd(imxmd, sd); - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - imxpad = &imxsd->pad[pad_idx]; + pad_vdev_list = to_pad_vdev_list(sd, pad_idx); + if (!pad_vdev_list) { + /* shouldn't happen, but no reason to fail link setup */ + return 0; + } /* * Before disabling a link, reset controls for all video @@ -526,8 +454,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, */ if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH && !(flags & MEDIA_LNK_FL_ENABLED)) { - for (i = 0; i < imxpad->num_vdevs; i++) { - vfd = imxpad->vdev[i]->vfd; + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; dev_dbg(imxmd->md.dev, "reset controls for %s\n", vfd->entity.name); @@ -536,8 +464,8 @@ static int imx_media_link_notify(struct media_link *link, u32 flags, } } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && (link->flags & MEDIA_LNK_FL_ENABLED)) { - for (i = 0; i < imxpad->num_vdevs; i++) { - vfd = imxpad->vdev[i]->vfd; + list_for_each_entry(pad_vdev, pad_vdev_list, list) { + vfd = pad_vdev->vdev->vfd; dev_dbg(imxmd->md.dev, "refresh controls for %s\n", vfd->entity.name); @@ -559,9 +487,10 @@ static int imx_media_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *node = dev->of_node; - struct imx_media_subdev *csi[4] = {0}; + struct imx_media_async_subdev *imxasd; + struct v4l2_async_subdev **subdevs; struct imx_media_dev *imxmd; - int ret; + int num_subdevs, i, ret; imxmd = devm_kzalloc(dev, sizeof(*imxmd), GFP_KERNEL); if (!imxmd) @@ -590,29 +519,44 @@ static int imx_media_probe(struct platform_device *pdev) dev_set_drvdata(imxmd->v4l2_dev.dev, imxmd); - ret = imx_media_of_parse(imxmd, &csi, node); + INIT_LIST_HEAD(&imxmd->asd_list); + INIT_LIST_HEAD(&imxmd->vdev_list); + + ret = imx_media_add_of_subdevs(imxmd, node); if (ret) { v4l2_err(&imxmd->v4l2_dev, - "imx_media_of_parse failed with %d\n", ret); + "add_of_subdevs failed with %d\n", ret); goto unreg_dev; } - ret = imx_media_add_internal_subdevs(imxmd, csi); + ret = imx_media_add_internal_subdevs(imxmd); if (ret) { v4l2_err(&imxmd->v4l2_dev, "add_internal_subdevs failed with %d\n", ret); goto unreg_dev; } + num_subdevs = imxmd->subdev_notifier.num_subdevs; + /* no subdevs? just bail */ - imxmd->num_subdevs = imxmd->subdev_notifier.num_subdevs; - if (imxmd->num_subdevs == 0) { + if (num_subdevs == 0) { ret = -ENODEV; goto unreg_dev; } + subdevs = devm_kzalloc(imxmd->md.dev, sizeof(*subdevs) * num_subdevs, + GFP_KERNEL); + if (!subdevs) { + ret = -ENOMEM; + goto unreg_dev; + } + + i = 0; + list_for_each_entry(imxasd, &imxmd->asd_list, list) + subdevs[i++] = &imxasd->asd; + /* prepare the async subdev notifier and register it */ - imxmd->subdev_notifier.subdevs = imxmd->async_ptrs; + imxmd->subdev_notifier.subdevs = subdevs; imxmd->subdev_notifier.ops = &imx_media_subdev_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, &imxmd->subdev_notifier); diff --git a/drivers/staging/media/imx/imx-media-internal-sd.c b/drivers/staging/media/imx/imx-media-internal-sd.c index cdfbf40dfcbe..70833fe503b5 100644 --- a/drivers/staging/media/imx/imx-media-internal-sd.c +++ b/drivers/staging/media/imx/imx-media-internal-sd.c @@ -60,73 +60,68 @@ static const struct internal_subdev_id { }, }; +struct internal_subdev; + struct internal_link { - const struct internal_subdev_id *remote_id; + const struct internal_subdev *remote; + int local_pad; int remote_pad; }; +/* max pads per internal-sd */ +#define MAX_INTERNAL_PADS 8 +/* max links per internal-sd pad */ +#define MAX_INTERNAL_LINKS 8 + struct internal_pad { - bool devnode; /* does this pad link to a device node */ - struct internal_link link[IMX_MEDIA_MAX_LINKS]; + struct internal_link link[MAX_INTERNAL_LINKS]; }; static const struct internal_subdev { const struct internal_subdev_id *id; - struct internal_pad pad[IMX_MEDIA_MAX_PADS]; - int num_sink_pads; - int num_src_pads; -} internal_subdev[num_isd] = { + struct internal_pad pad[MAX_INTERNAL_PADS]; +} int_subdev[num_isd] = { [isd_csi0] = { .id = &isd_id[isd_csi0], - .num_sink_pads = CSI_NUM_SINK_PADS, - .num_src_pads = CSI_NUM_SRC_PADS, .pad[CSI_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, { - .remote_id = &isd_id[isd_vdic], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_vdic], .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, - .pad[CSI_SRC_PAD_IDMAC] = { - .devnode = true, - }, }, [isd_csi1] = { .id = &isd_id[isd_csi1], - .num_sink_pads = CSI_NUM_SINK_PADS, - .num_src_pads = CSI_NUM_SRC_PADS, .pad[CSI_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, { - .remote_id = &isd_id[isd_vdic], + .local_pad = CSI_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_vdic], .remote_pad = VDIC_SINK_PAD_DIRECT, }, }, }, - .pad[CSI_SRC_PAD_IDMAC] = { - .devnode = true, - }, }, [isd_vdic] = { .id = &isd_id[isd_vdic], - .num_sink_pads = VDIC_NUM_SINK_PADS, - .num_src_pads = VDIC_NUM_SRC_PADS, - .pad[VDIC_SINK_PAD_IDMAC] = { - .devnode = true, - }, .pad[VDIC_SRC_PAD_DIRECT] = { .link = { { - .remote_id = &isd_id[isd_ic_prp], + .local_pad = VDIC_SRC_PAD_DIRECT, + .remote = &int_subdev[isd_ic_prp], .remote_pad = PRP_SINK_PAD, }, }, @@ -135,12 +130,11 @@ static const struct internal_subdev { [isd_ic_prp] = { .id = &isd_id[isd_ic_prp], - .num_sink_pads = PRP_NUM_SINK_PADS, - .num_src_pads = PRP_NUM_SRC_PADS, .pad[PRP_SRC_PAD_PRPENC] = { .link = { { - .remote_id = &isd_id[isd_ic_prpenc], + .local_pad = PRP_SRC_PAD_PRPENC, + .remote = &int_subdev[isd_ic_prpenc], .remote_pad = 0, }, }, @@ -148,7 +142,8 @@ static const struct internal_subdev { .pad[PRP_SRC_PAD_PRPVF] = { .link = { { - .remote_id = &isd_id[isd_ic_prpvf], + .local_pad = PRP_SRC_PAD_PRPVF, + .remote = &int_subdev[isd_ic_prpvf], .remote_pad = 0, }, }, @@ -157,70 +152,111 @@ static const struct internal_subdev { [isd_ic_prpenc] = { .id = &isd_id[isd_ic_prpenc], - .num_sink_pads = PRPENCVF_NUM_SINK_PADS, - .num_src_pads = PRPENCVF_NUM_SRC_PADS, - .pad[PRPENCVF_SRC_PAD] = { - .devnode = true, - }, }, [isd_ic_prpvf] = { .id = &isd_id[isd_ic_prpvf], - .num_sink_pads = PRPENCVF_NUM_SINK_PADS, - .num_src_pads = PRPENCVF_NUM_SRC_PADS, - .pad[PRPENCVF_SRC_PAD] = { - .devnode = true, - }, }, }; -/* form a device name given a group id and ipu id */ -static inline void isd_id_to_devname(char *devname, int sz, - const struct internal_subdev_id *id, - int ipu_id) +/* form a device name given an internal subdev and ipu id */ +static inline void isd_to_devname(char *devname, int sz, + const struct internal_subdev *isd, + int ipu_id) { - int pdev_id = ipu_id * num_isd + id->index; + int pdev_id = ipu_id * num_isd + isd->id->index; - snprintf(devname, sz, "%s.%d", id->name, pdev_id); + snprintf(devname, sz, "%s.%d", isd->id->name, pdev_id); } -/* adds the links from given internal subdev */ -static int add_internal_links(struct imx_media_dev *imxmd, - const struct internal_subdev *isd, - struct imx_media_subdev *imxsd, - int ipu_id) +static const struct internal_subdev *find_intsd_by_grp_id(u32 grp_id) { - int i, num_pads, ret; + enum isd_enum i; - num_pads = isd->num_sink_pads + isd->num_src_pads; + for (i = 0; i < num_isd; i++) { + const struct internal_subdev *isd = &int_subdev[i]; - for (i = 0; i < num_pads; i++) { - const struct internal_pad *intpad = &isd->pad[i]; - struct imx_media_pad *pad = &imxsd->pad[i]; - int j; + if (isd->id->grp_id == grp_id) + return isd; + } - /* init the pad flags for this internal subdev */ - pad->pad.flags = (i < isd->num_sink_pads) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - /* export devnode pad flag to the subdevs */ - pad->devnode = intpad->devnode; + return NULL; +} - for (j = 0; ; j++) { - const struct internal_link *link; - char remote_devname[32]; +static struct v4l2_subdev *find_sink(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + const struct internal_link *link) +{ + char sink_devname[32]; + int ipu_id; + + /* + * retrieve IPU id from subdev name, note: can't get this from + * struct imx_media_internal_sd_platformdata because if src is + * a CSI, it has different struct ipu_client_platformdata which + * does not contain IPU id. + */ + if (sscanf(src->name, "ipu%d", &ipu_id) != 1) + return NULL; + + isd_to_devname(sink_devname, sizeof(sink_devname), + link->remote, ipu_id - 1); + + return imx_media_find_subdev_by_devname(imxmd, sink_devname); +} + +static int create_ipu_internal_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *src, + const struct internal_link *link) +{ + struct v4l2_subdev *sink; + int ret; + + sink = find_sink(imxmd, src, link); + if (!sink) + return -ENODEV; + + v4l2_info(&imxmd->v4l2_dev, "%s:%d -> %s:%d\n", + src->name, link->local_pad, + sink->name, link->remote_pad); + ret = media_create_pad_link(&src->entity, link->local_pad, + &sink->entity, link->remote_pad, 0); + if (ret) + v4l2_err(&imxmd->v4l2_dev, + "create_pad_link failed: %d\n", ret); + + return ret; +} + +int imx_media_create_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) +{ + const struct internal_subdev *intsd; + const struct internal_pad *intpad; + const struct internal_link *link; + struct media_pad *pad; + int i, j, ret; + + intsd = find_intsd_by_grp_id(sd->grp_id); + if (!intsd) + return -ENODEV; + + /* create the source->sink links */ + for (i = 0; i < sd->entity.num_pads; i++) { + intpad = &intsd->pad[i]; + pad = &sd->entity.pads[i]; + + if (!(pad->flags & MEDIA_PAD_FL_SOURCE)) + continue; + + for (j = 0; ; j++) { link = &intpad->link[j]; - if (!link->remote_id) + if (!link->remote) break; - isd_id_to_devname(remote_devname, - sizeof(remote_devname), - link->remote_id, ipu_id); - - ret = imx_media_add_pad_link(imxmd, pad, - NULL, remote_devname, - i, link->remote_pad); + ret = create_ipu_internal_link(imxmd, sd, link); if (ret) return ret; } @@ -230,14 +266,12 @@ static int add_internal_links(struct imx_media_dev *imxmd, } /* register an internal subdev as a platform device */ -static struct imx_media_subdev * -add_internal_subdev(struct imx_media_dev *imxmd, - const struct internal_subdev *isd, - int ipu_id) +static int add_internal_subdev(struct imx_media_dev *imxmd, + const struct internal_subdev *isd, + int ipu_id) { struct imx_media_internal_sd_platformdata pdata; struct platform_device_info pdevinfo = {0}; - struct imx_media_subdev *imxsd; struct platform_device *pdev; pdata.grp_id = isd->id->grp_id; @@ -258,73 +292,51 @@ add_internal_subdev(struct imx_media_dev *imxmd, pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(pdev)) - return ERR_CAST(pdev); + return PTR_ERR(pdev); - imxsd = imx_media_add_async_subdev(imxmd, NULL, pdev); - if (IS_ERR(imxsd)) - return imxsd; - - imxsd->num_sink_pads = isd->num_sink_pads; - imxsd->num_src_pads = isd->num_src_pads; - - return imxsd; + return imx_media_add_async_subdev(imxmd, NULL, pdev); } /* adds the internal subdevs in one ipu */ -static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi0, - struct imx_media_subdev *csi1, - int ipu_id) +static int add_ipu_internal_subdevs(struct imx_media_dev *imxmd, int ipu_id) { enum isd_enum i; - int ret; for (i = 0; i < num_isd; i++) { - const struct internal_subdev *isd = &internal_subdev[i]; - struct imx_media_subdev *imxsd; + const struct internal_subdev *isd = &int_subdev[i]; + int ret; /* * the CSIs are represented in the device-tree, so those - * devices are added already, and are added to the async - * subdev list by of_parse_subdev(), so we are given those - * subdevs as csi0 and csi1. + * devices are already added to the async subdev list by + * of_parse_subdev(). */ switch (isd->id->grp_id) { case IMX_MEDIA_GRP_ID_CSI0: - imxsd = csi0; - break; case IMX_MEDIA_GRP_ID_CSI1: - imxsd = csi1; + ret = 0; break; default: - imxsd = add_internal_subdev(imxmd, isd, ipu_id); + ret = add_internal_subdev(imxmd, isd, ipu_id); break; } - if (IS_ERR(imxsd)) - return PTR_ERR(imxsd); - - /* add the links from this subdev */ - if (imxsd) { - ret = add_internal_links(imxmd, isd, imxsd, ipu_id); - if (ret) - return ret; - } + if (ret) + return ret; } return 0; } -int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi[4]) +int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd) { int ret; - ret = add_ipu_internal_subdevs(imxmd, csi[0], csi[1], 0); + ret = add_ipu_internal_subdevs(imxmd, 0); if (ret) goto remove; - ret = add_ipu_internal_subdevs(imxmd, csi[2], csi[3], 1); + ret = add_ipu_internal_subdevs(imxmd, 1); if (ret) goto remove; @@ -337,13 +349,12 @@ remove: void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd) { - struct imx_media_subdev *imxsd; - int i; + struct imx_media_async_subdev *imxasd; - for (i = 0; i < imxmd->subdev_notifier.num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (!imxsd->pdev) + list_for_each_entry(imxasd, &imxmd->asd_list, list) { + if (!imxasd->pdev) continue; - platform_device_unregister(imxsd->pdev); + + platform_device_unregister(imxasd->pdev); } } diff --git a/drivers/staging/media/imx/imx-media-of.c b/drivers/staging/media/imx/imx-media-of.c index 12df09f52490..acde372c6795 100644 --- a/drivers/staging/media/imx/imx-media-of.c +++ b/drivers/staging/media/imx/imx-media-of.c @@ -20,34 +20,6 @@ #include <video/imx-ipu-v3.h> #include "imx-media.h" -static int of_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *local_sd_node, - struct device_node *remote_sd_node, - int local_pad, int remote_pad) -{ - dev_dbg(imxmd->md.dev, "%s: adding %s:%d -> %s:%d\n", __func__, - local_sd_node->name, local_pad, - remote_sd_node->name, remote_pad); - - return imx_media_add_pad_link(imxmd, pad, remote_sd_node, NULL, - local_pad, remote_pad); -} - -static void of_parse_sensor(struct imx_media_dev *imxmd, - struct imx_media_subdev *sensor, - struct device_node *sensor_np) -{ - struct device_node *endpoint; - - endpoint = of_graph_get_next_endpoint(sensor_np, NULL); - if (endpoint) { - v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), - &sensor->sensor_ep); - of_node_put(endpoint); - } -} - static int of_get_port_count(const struct device_node *np) { struct device_node *ports, *child; @@ -67,15 +39,14 @@ static int of_get_port_count(const struct device_node *np) } /* - * find the remote device node and remote port id (remote pad #) - * given local endpoint node + * find the remote device node given local endpoint node */ -static void of_get_remote_pad(struct device_node *epnode, - struct device_node **remote_node, - int *remote_pad) +static bool of_get_remote(struct device_node *epnode, + struct device_node **remote_node) { struct device_node *rp, *rpp; struct device_node *remote; + bool is_csi_port; rp = of_graph_get_remote_port(epnode); rpp = of_graph_get_remote_port_parent(epnode); @@ -83,13 +54,12 @@ static void of_get_remote_pad(struct device_node *epnode, if (of_device_is_compatible(rpp, "fsl,imx6q-ipu")) { /* the remote is one of the CSI ports */ remote = rp; - *remote_pad = 0; of_node_put(rpp); + is_csi_port = true; } else { remote = rpp; - if (of_property_read_u32(rp, "reg", remote_pad)) - *remote_pad = 0; of_node_put(rp); + is_csi_port = false; } if (!of_device_is_available(remote)) { @@ -98,130 +68,66 @@ static void of_get_remote_pad(struct device_node *epnode, } else { *remote_node = remote; } + + return is_csi_port; } static int of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, - bool is_csi_port, struct imx_media_subdev **subdev) + bool is_csi_port) { - struct imx_media_subdev *imxsd; - int i, num_pads, ret; + int i, num_ports, ret; if (!of_device_is_available(sd_np)) { dev_dbg(imxmd->md.dev, "%s: %s not enabled\n", __func__, sd_np->name); - *subdev = NULL; /* unavailable is not an error */ return 0; } /* register this subdev with async notifier */ - imxsd = imx_media_add_async_subdev(imxmd, sd_np, NULL); - ret = PTR_ERR_OR_ZERO(imxsd); + ret = imx_media_add_async_subdev(imxmd, of_fwnode_handle(sd_np), + NULL); if (ret) { if (ret == -EEXIST) { /* already added, everything is fine */ - *subdev = NULL; return 0; } /* other error, can't continue */ return ret; } - *subdev = imxsd; - - if (is_csi_port) { - /* - * the ipu-csi has one sink port and two source ports. - * The source ports are not represented in the device tree, - * but are described by the internal pads and links later. - */ - num_pads = CSI_NUM_PADS; - imxsd->num_sink_pads = CSI_NUM_SINK_PADS; - } else if (of_device_is_compatible(sd_np, "fsl,imx6-mipi-csi2")) { - num_pads = of_get_port_count(sd_np); - /* the mipi csi2 receiver has only one sink port */ - imxsd->num_sink_pads = 1; - } else if (of_device_is_compatible(sd_np, "video-mux")) { - num_pads = of_get_port_count(sd_np); - /* for the video mux, all but the last port are sinks */ - imxsd->num_sink_pads = num_pads - 1; - } else { - num_pads = of_get_port_count(sd_np); - if (num_pads != 1) { - /* confused, but no reason to give up here */ - dev_warn(imxmd->md.dev, - "%s: unknown device %s with %d ports\n", - __func__, sd_np->name, num_pads); - return 0; - } - - /* - * we got to this node from this single source port, - * there are no sink pads. - */ - imxsd->num_sink_pads = 0; - } - - if (imxsd->num_sink_pads >= num_pads) - return -EINVAL; - - imxsd->num_src_pads = num_pads - imxsd->num_sink_pads; - - dev_dbg(imxmd->md.dev, "%s: %s has %d pads (%d sink, %d src)\n", - __func__, sd_np->name, num_pads, - imxsd->num_sink_pads, imxsd->num_src_pads); /* - * With no sink, this subdev node is the original source - * of video, parse it's media bus for use by the pipeline. + * the ipu-csi has one sink port. The source pads are not + * represented in the device tree by port nodes, but are + * described by the internal pads and links later. */ - if (imxsd->num_sink_pads == 0) - of_parse_sensor(imxmd, imxsd, sd_np); + num_ports = is_csi_port ? 1 : of_get_port_count(sd_np); - for (i = 0; i < num_pads; i++) { + for (i = 0; i < num_ports; i++) { struct device_node *epnode = NULL, *port, *remote_np; - struct imx_media_subdev *remote_imxsd; - struct imx_media_pad *pad; - int remote_pad; - - /* init this pad */ - pad = &imxsd->pad[i]; - pad->pad.flags = (i < imxsd->num_sink_pads) ? - MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; - - if (is_csi_port) - port = (i < imxsd->num_sink_pads) ? sd_np : NULL; - else - port = of_graph_get_port_by_id(sd_np, i); + + port = is_csi_port ? sd_np : of_graph_get_port_by_id(sd_np, i); if (!port) continue; for_each_child_of_node(port, epnode) { - of_get_remote_pad(epnode, &remote_np, &remote_pad); + bool remote_is_csi; + + remote_is_csi = of_get_remote(epnode, &remote_np); if (!remote_np) continue; - ret = of_add_pad_link(imxmd, pad, sd_np, remote_np, - i, remote_pad); + ret = of_parse_subdev(imxmd, remote_np, remote_is_csi); + of_node_put(remote_np); if (ret) break; - - if (i < imxsd->num_sink_pads) { - /* follow sink endpoints upstream */ - ret = of_parse_subdev(imxmd, remote_np, - false, &remote_imxsd); - if (ret) - break; - } - - of_node_put(remote_np); } if (port != sd_np) of_node_put(port); if (ret) { - of_node_put(remote_np); of_node_put(epnode); break; } @@ -230,13 +136,10 @@ of_parse_subdev(struct imx_media_dev *imxmd, struct device_node *sd_np, return ret; } -int imx_media_of_parse(struct imx_media_dev *imxmd, - struct imx_media_subdev *(*csi)[4], - struct device_node *np) +int imx_media_add_of_subdevs(struct imx_media_dev *imxmd, + struct device_node *np) { - struct imx_media_subdev *lcsi; struct device_node *csi_np; - u32 ipu_id, csi_id; int i, ret; for (i = 0; ; i++) { @@ -244,33 +147,120 @@ int imx_media_of_parse(struct imx_media_dev *imxmd, if (!csi_np) break; - ret = of_parse_subdev(imxmd, csi_np, true, &lcsi); + ret = of_parse_subdev(imxmd, csi_np, true); + of_node_put(csi_np); if (ret) - goto err_put; + return ret; + } - ret = of_property_read_u32(csi_np, "reg", &csi_id); - if (ret) { - dev_err(imxmd->md.dev, - "%s: csi port missing reg property!\n", - __func__); - goto err_put; - } + return 0; +} - ipu_id = of_alias_get_id(csi_np->parent, "ipu"); - of_node_put(csi_np); +/* + * Create a single media link to/from sd using a fwnode link. + * + * NOTE: this function assumes an OF port node is equivalent to + * a media pad (port id equal to media pad index), and that an + * OF endpoint node is equivalent to a media link. + */ +static int create_of_link(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd, + struct v4l2_fwnode_link *link) +{ + struct v4l2_subdev *remote, *src, *sink; + int src_pad, sink_pad; - if (ipu_id > 1 || csi_id > 1) { - dev_err(imxmd->md.dev, - "%s: invalid ipu/csi id (%u/%u)\n", - __func__, ipu_id, csi_id); - return -EINVAL; - } + if (link->local_port >= sd->entity.num_pads) + return -EINVAL; + + remote = imx_media_find_subdev_by_fwnode(imxmd, link->remote_node); + if (!remote) + return 0; + + if (sd->entity.pads[link->local_port].flags & MEDIA_PAD_FL_SINK) { + src = remote; + src_pad = link->remote_port; + sink = sd; + sink_pad = link->local_port; + } else { + src = sd; + src_pad = link->local_port; + sink = remote; + sink_pad = link->remote_port; + } + + /* make sure link doesn't already exist before creating */ + if (media_entity_find_link(&src->entity.pads[src_pad], + &sink->entity.pads[sink_pad])) + return 0; + + v4l2_info(sd->v4l2_dev, "%s:%d -> %s:%d\n", + src->name, src_pad, sink->name, sink_pad); + + return media_create_pad_link(&src->entity, src_pad, + &sink->entity, sink_pad, 0); +} + +/* + * Create media links to/from sd using its device-tree endpoints. + */ +int imx_media_create_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd) +{ + struct v4l2_fwnode_link link; + struct device_node *ep; + int ret; + + for_each_endpoint_of_node(sd->dev->of_node, ep) { + ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link); + if (ret) + continue; - (*csi)[ipu_id * 2 + csi_id] = lcsi; + ret = create_of_link(imxmd, sd, &link); + v4l2_fwnode_put_link(&link); + if (ret) + return ret; + } + + return 0; +} + +/* + * Create media links to the given CSI subdevice's sink pads, + * using its device-tree endpoints. + */ +int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi) +{ + struct device_node *csi_np = csi->dev->of_node; + struct fwnode_handle *fwnode, *csi_ep; + struct v4l2_fwnode_link link; + struct device_node *ep; + int ret; + + link.local_node = of_fwnode_handle(csi_np); + link.local_port = CSI_SINK_PAD; + + for_each_child_of_node(csi_np, ep) { + csi_ep = of_fwnode_handle(ep); + + fwnode = fwnode_graph_get_remote_endpoint(csi_ep); + if (!fwnode) + continue; + + fwnode = fwnode_get_parent(fwnode); + fwnode_property_read_u32(fwnode, "reg", &link.remote_port); + fwnode = fwnode_get_next_parent(fwnode); + if (is_of_node(fwnode) && + of_node_cmp(to_of_node(fwnode)->name, "ports") == 0) + fwnode = fwnode_get_next_parent(fwnode); + link.remote_node = fwnode; + + ret = create_of_link(imxmd, csi, &link); + fwnode_handle_put(link.remote_node); + if (ret) + return ret; } return 0; -err_put: - of_node_put(csi_np); - return ret; } diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c index 59523872a886..13dafa77a2eb 100644 --- a/drivers/staging/media/imx/imx-media-utils.c +++ b/drivers/staging/media/imx/imx-media-utils.c @@ -668,38 +668,35 @@ void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id) } EXPORT_SYMBOL_GPL(imx_media_grp_id_to_sd_name); -struct imx_media_subdev * -imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd) +struct v4l2_subdev * +imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode) { - struct imx_media_subdev *imxsd; - int i; + struct v4l2_subdev *sd; - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (sd == imxsd->sd) - return imxsd; + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + if (sd->fwnode == fwnode) + return sd; } - return ERR_PTR(-ENODEV); + return NULL; } -EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_sd); +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_fwnode); -struct imx_media_subdev * -imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, u32 grp_id) +struct v4l2_subdev * +imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, + const char *devname) { - struct imx_media_subdev *imxsd; - int i; + struct v4l2_subdev *sd; - for (i = 0; i < imxmd->num_subdevs; i++) { - imxsd = &imxmd->subdev[i]; - if (imxsd->sd && imxsd->sd->grp_id == grp_id) - return imxsd; + list_for_each_entry(sd, &imxmd->v4l2_dev.subdevs, list) { + if (!strcmp(devname, dev_name(sd->dev))) + return sd; } - return ERR_PTR(-ENODEV); + return NULL; } -EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id); +EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_devname); /* * Adds a video device to the master video device list. This is called by @@ -708,32 +705,21 @@ EXPORT_SYMBOL_GPL(imx_media_find_subdev_by_id); int imx_media_add_video_device(struct imx_media_dev *imxmd, struct imx_media_video_dev *vdev) { - int vdev_idx, ret = 0; - mutex_lock(&imxmd->mutex); - vdev_idx = imxmd->num_vdevs; - if (vdev_idx >= IMX_MEDIA_MAX_VDEVS) { - dev_err(imxmd->md.dev, - "%s: too many video devices! can't add %s\n", - __func__, vdev->vfd->name); - ret = -ENOSPC; - goto out; - } + list_add_tail(&vdev->list, &imxmd->vdev_list); - imxmd->vdev[vdev_idx] = vdev; - imxmd->num_vdevs++; -out: mutex_unlock(&imxmd->mutex); - return ret; + return 0; } EXPORT_SYMBOL_GPL(imx_media_add_video_device); /* - * Search upstream or downstream for a subdevice in the current pipeline + * Search upstream/downstream for a subdevice in the current pipeline * with given grp_id, starting from start_entity. Returns the subdev's - * source/sink pad that it was reached from. Must be called with - * mdev->graph_mutex held. + * source/sink pad that it was reached from. If grp_id is zero, just + * returns the nearest source/sink pad to start_entity. Must be called + * with mdev->graph_mutex held. */ static struct media_pad * find_pipeline_pad(struct imx_media_dev *imxmd, @@ -756,11 +742,16 @@ find_pipeline_pad(struct imx_media_dev *imxmd, if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) continue; - sd = media_entity_to_v4l2_subdev(pad->entity); - if (sd->grp_id & grp_id) - return pad; + if (grp_id != 0) { + sd = media_entity_to_v4l2_subdev(pad->entity); + if (sd->grp_id & grp_id) + return pad; - return find_pipeline_pad(imxmd, pad->entity, grp_id, upstream); + return find_pipeline_pad(imxmd, pad->entity, + grp_id, upstream); + } else { + return pad; + } } return NULL; @@ -789,7 +780,6 @@ find_upstream_subdev(struct imx_media_dev *imxmd, return pad ? media_entity_to_v4l2_subdev(pad->entity) : NULL; } - /* * Find the upstream mipi-csi2 virtual channel reached from the given * start entity in the current pipeline. @@ -814,11 +804,30 @@ int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, EXPORT_SYMBOL_GPL(imx_media_find_mipi_csi2_channel); /* + * Find a source pad reached upstream from the given start entity in + * the current pipeline. Must be called with mdev->graph_mutex held. + */ +struct media_pad * +imx_media_find_upstream_pad(struct imx_media_dev *imxmd, + struct media_entity *start_entity, + u32 grp_id) +{ + struct media_pad *pad; + + pad = find_pipeline_pad(imxmd, start_entity, grp_id, true); + if (!pad) + return ERR_PTR(-ENODEV); + + return pad; +} +EXPORT_SYMBOL_GPL(imx_media_find_upstream_pad); + +/* * Find a subdev reached upstream from the given start entity in * the current pipeline. * Must be called with mdev->graph_mutex held. */ -struct imx_media_subdev * +struct v4l2_subdev * imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, struct media_entity *start_entity, u32 grp_id) @@ -829,33 +838,10 @@ imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, if (!sd) return ERR_PTR(-ENODEV); - return imx_media_find_subdev_by_sd(imxmd, sd); + return sd; } EXPORT_SYMBOL_GPL(imx_media_find_upstream_subdev); -struct imx_media_subdev * -__imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity) -{ - return imx_media_find_upstream_subdev(imxmd, start_entity, - IMX_MEDIA_GRP_ID_SENSOR); -} -EXPORT_SYMBOL_GPL(__imx_media_find_sensor); - -struct imx_media_subdev * -imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity) -{ - struct imx_media_subdev *sensor; - - mutex_lock(&imxmd->md.graph_mutex); - sensor = __imx_media_find_sensor(imxmd, start_entity); - mutex_unlock(&imxmd->md.graph_mutex); - - return sensor; -} -EXPORT_SYMBOL_GPL(imx_media_find_sensor); - /* * Turn current pipeline streaming on/off starting from entity. */ diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h index ac3ab115394f..2fd6dfdf37d6 100644 --- a/drivers/staging/media/imx/imx-media.h +++ b/drivers/staging/media/imx/imx-media.h @@ -11,6 +11,7 @@ #ifndef _IMX_MEDIA_H #define _IMX_MEDIA_H +#include <linux/platform_device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> #include <media/v4l2-fwnode.h> @@ -19,26 +20,6 @@ #include <video/imx-ipu-v3.h> /* - * This is somewhat arbitrary, but we need at least: - * - 4 video devices per IPU - * - 3 IC subdevs per IPU - * - 1 VDIC subdev per IPU - * - 2 CSI subdevs per IPU - * - 1 mipi-csi2 receiver subdev - * - 2 video-mux subdevs - * - 2 camera sensor subdevs per IPU (1 parallel, 1 mipi-csi2) - * - */ -/* max video devices */ -#define IMX_MEDIA_MAX_VDEVS 8 -/* max subdevices */ -#define IMX_MEDIA_MAX_SUBDEVS 32 -/* max pads per subdev */ -#define IMX_MEDIA_MAX_PADS 16 -/* max links per pad */ -#define IMX_MEDIA_MAX_LINKS 8 - -/* * Pad definitions for the subdevs with multiple source or * sink pads */ @@ -51,9 +32,6 @@ enum { CSI_NUM_PADS, }; -#define CSI_NUM_SINK_PADS 1 -#define CSI_NUM_SRC_PADS 2 - /* ipu_vdic */ enum { VDIC_SINK_PAD_DIRECT = 0, @@ -62,9 +40,6 @@ enum { VDIC_NUM_PADS, }; -#define VDIC_NUM_SINK_PADS 2 -#define VDIC_NUM_SRC_PADS 1 - /* ipu_ic_prp */ enum { PRP_SINK_PAD = 0, @@ -73,9 +48,6 @@ enum { PRP_NUM_PADS, }; -#define PRP_NUM_SINK_PADS 1 -#define PRP_NUM_SRC_PADS 2 - /* ipu_ic_prpencvf */ enum { PRPENCVF_SINK_PAD = 0, @@ -83,9 +55,6 @@ enum { PRPENCVF_NUM_PADS, }; -#define PRPENCVF_NUM_SINK_PADS 1 -#define PRPENCVF_NUM_SRC_PADS 1 - /* How long to wait for EOF interrupts in the buffer-capture subdevs */ #define IMX_MEDIA_EOF_TIMEOUT 1000 @@ -110,6 +79,9 @@ struct imx_media_video_dev { /* the user format */ struct v4l2_format fmt; const struct imx_media_pixfmt *cc; + + /* links this vdev to master list */ + struct list_head list; }; static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb) @@ -119,25 +91,24 @@ static inline struct imx_media_buffer *to_imx_media_vb(struct vb2_buffer *vb) return container_of(vbuf, struct imx_media_buffer, vbuf); } -struct imx_media_link { - struct device_node *remote_sd_node; - char remote_devname[32]; - int local_pad; - int remote_pad; -}; +/* + * to support control inheritance to video devices, this + * retrieves a pad's list_head of video devices that can + * be reached from the pad. Note that only the lists in + * source pads get populated, sink pads have empty lists. + */ +static inline struct list_head * +to_pad_vdev_list(struct v4l2_subdev *sd, int pad_index) +{ + struct list_head *vdev_list = sd->host_priv; -struct imx_media_pad { - struct media_pad pad; - struct imx_media_link link[IMX_MEDIA_MAX_LINKS]; - bool devnode; /* does this pad link to a device node */ - int num_links; - - /* - * list of video devices that can be reached from this pad, - * list is only valid for source pads. - */ - struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS]; - int num_vdevs; + return vdev_list ? &vdev_list[pad_index] : NULL; +} + +/* an entry in a pad's video device list */ +struct imx_media_pad_vdev { + struct imx_media_video_dev *vdev; + struct list_head list; }; struct imx_media_internal_sd_platformdata { @@ -146,23 +117,20 @@ struct imx_media_internal_sd_platformdata { int ipu_id; }; -struct imx_media_subdev { - struct v4l2_async_subdev asd; - struct v4l2_subdev *sd; /* set when bound */ - - struct imx_media_pad pad[IMX_MEDIA_MAX_PADS]; - int num_sink_pads; - int num_src_pads; - /* the platform device if this is an internal subdev */ +struct imx_media_async_subdev { + struct v4l2_async_subdev asd; + /* the platform device of IPU-internal subdevs */ struct platform_device *pdev; - /* the devname is needed for async devname match */ - char devname[32]; - - /* if this is a sensor */ - struct v4l2_fwnode_endpoint sensor_ep; + struct list_head list; }; +static inline struct imx_media_async_subdev * +to_imx_media_asd(struct v4l2_async_subdev *asd) +{ + return container_of(asd, struct imx_media_async_subdev, asd); +} + struct imx_media_dev { struct media_device md; struct v4l2_device v4l2_dev; @@ -172,19 +140,14 @@ struct imx_media_dev { struct mutex mutex; /* protect elements below */ - /* master subdevice list */ - struct imx_media_subdev subdev[IMX_MEDIA_MAX_SUBDEVS]; - int num_subdevs; - /* master video device list */ - struct imx_media_video_dev *vdev[IMX_MEDIA_MAX_VDEVS]; - int num_vdevs; + struct list_head vdev_list; /* IPUs this media driver control, valid after subdevs bound */ struct ipu_soc *ipu[2]; /* for async subdev registration */ - struct v4l2_async_subdev *async_ptrs[IMX_MEDIA_MAX_SUBDEVS]; + struct list_head asd_list; struct v4l2_async_notifier subdev_notifier; }; @@ -194,6 +157,7 @@ enum codespace_sel { CS_SEL_ANY, }; +/* imx-media-utils.c */ const struct imx_media_pixfmt * imx_media_find_format(u32 fourcc, enum codespace_sel cs_sel, bool allow_bayer); int imx_media_enum_format(u32 *fourcc, u32 index, enum codespace_sel cs_sel); @@ -205,7 +169,6 @@ int imx_media_enum_mbus_format(u32 *code, u32 index, enum codespace_sel cs_sel, const struct imx_media_pixfmt * imx_media_find_ipu_format(u32 code, enum codespace_sel cs_sel); int imx_media_enum_ipu_format(u32 *code, u32 index, enum codespace_sel cs_sel); - int imx_media_init_mbus_fmt(struct v4l2_mbus_framefmt *mbus, u32 width, u32 height, u32 code, u32 field, const struct imx_media_pixfmt **cc); @@ -219,48 +182,26 @@ int imx_media_mbus_fmt_to_ipu_image(struct ipu_image *image, struct v4l2_mbus_framefmt *mbus); int imx_media_ipu_image_to_mbus_fmt(struct v4l2_mbus_framefmt *mbus, struct ipu_image *image); - -struct imx_media_subdev * -imx_media_find_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *devname); -struct imx_media_subdev * -imx_media_add_async_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - struct platform_device *pdev); -int imx_media_add_pad_link(struct imx_media_dev *imxmd, - struct imx_media_pad *pad, - struct device_node *remote_node, - const char *remote_devname, - int local_pad, int remote_pad); - void imx_media_grp_id_to_sd_name(char *sd_name, int sz, u32 grp_id, int ipu_id); - -int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd, - struct imx_media_subdev *csi[4]); -void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd); - -struct imx_media_subdev * -imx_media_find_subdev_by_sd(struct imx_media_dev *imxmd, - struct v4l2_subdev *sd); -struct imx_media_subdev * -imx_media_find_subdev_by_id(struct imx_media_dev *imxmd, - u32 grp_id); +struct v4l2_subdev * +imx_media_find_subdev_by_fwnode(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode); +struct v4l2_subdev * +imx_media_find_subdev_by_devname(struct imx_media_dev *imxmd, + const char *devname); int imx_media_add_video_device(struct imx_media_dev *imxmd, struct imx_media_video_dev *vdev); int imx_media_find_mipi_csi2_channel(struct imx_media_dev *imxmd, struct media_entity *start_entity); -struct imx_media_subdev * +struct media_pad * +imx_media_find_upstream_pad(struct imx_media_dev *imxmd, + struct media_entity *start_entity, + u32 grp_id); +struct v4l2_subdev * imx_media_find_upstream_subdev(struct imx_media_dev *imxmd, struct media_entity *start_entity, u32 grp_id); -struct imx_media_subdev * -__imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity); -struct imx_media_subdev * -imx_media_find_sensor(struct imx_media_dev *imxmd, - struct media_entity *start_entity); struct imx_media_dma_buf { void *virt; @@ -278,6 +219,11 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd, struct media_entity *entity, bool on); +/* imx-media-dev.c */ +int imx_media_add_async_subdev(struct imx_media_dev *imxmd, + struct fwnode_handle *fwnode, + struct platform_device *pdev); + /* imx-media-fim.c */ struct imx_media_fim; void imx_media_fim_eof_monitor(struct imx_media_fim *fim, ktime_t timestamp); @@ -288,14 +234,19 @@ int imx_media_fim_add_controls(struct imx_media_fim *fim); struct imx_media_fim *imx_media_fim_init(struct v4l2_subdev *sd); void imx_media_fim_free(struct imx_media_fim *fim); +/* imx-media-internal-sd.c */ +int imx_media_add_internal_subdevs(struct imx_media_dev *imxmd); +int imx_media_create_internal_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd); +void imx_media_remove_internal_subdevs(struct imx_media_dev *imxmd); + /* imx-media-of.c */ -struct imx_media_subdev * -imx_media_of_find_subdev(struct imx_media_dev *imxmd, - struct device_node *np, - const char *name); -int imx_media_of_parse(struct imx_media_dev *dev, - struct imx_media_subdev *(*csi)[4], - struct device_node *np); +int imx_media_add_of_subdevs(struct imx_media_dev *dev, + struct device_node *np); +int imx_media_create_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *sd); +int imx_media_create_csi_of_links(struct imx_media_dev *imxmd, + struct v4l2_subdev *csi); /* imx-media-capture.c */ struct imx_media_video_dev * @@ -310,16 +261,14 @@ void imx_media_capture_device_set_format(struct imx_media_video_dev *vdev, void imx_media_capture_device_error(struct imx_media_video_dev *vdev); /* subdev group ids */ -#define IMX_MEDIA_GRP_ID_SENSOR (1 << 8) -#define IMX_MEDIA_GRP_ID_VIDMUX (1 << 9) -#define IMX_MEDIA_GRP_ID_CSI2 (1 << 10) -#define IMX_MEDIA_GRP_ID_CSI_BIT 11 +#define IMX_MEDIA_GRP_ID_CSI2 BIT(8) +#define IMX_MEDIA_GRP_ID_CSI_BIT 9 #define IMX_MEDIA_GRP_ID_CSI (0x3 << IMX_MEDIA_GRP_ID_CSI_BIT) -#define IMX_MEDIA_GRP_ID_CSI0 (1 << IMX_MEDIA_GRP_ID_CSI_BIT) +#define IMX_MEDIA_GRP_ID_CSI0 BIT(IMX_MEDIA_GRP_ID_CSI_BIT) #define IMX_MEDIA_GRP_ID_CSI1 (2 << IMX_MEDIA_GRP_ID_CSI_BIT) -#define IMX_MEDIA_GRP_ID_VDIC (1 << 13) -#define IMX_MEDIA_GRP_ID_IC_PRP (1 << 14) -#define IMX_MEDIA_GRP_ID_IC_PRPENC (1 << 15) -#define IMX_MEDIA_GRP_ID_IC_PRPVF (1 << 16) +#define IMX_MEDIA_GRP_ID_VDIC BIT(11) +#define IMX_MEDIA_GRP_ID_IC_PRP BIT(12) +#define IMX_MEDIA_GRP_ID_IC_PRPENC BIT(13) +#define IMX_MEDIA_GRP_ID_IC_PRPVF BIT(14) #endif diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c index 5061f3f524fd..477d191c568b 100644 --- a/drivers/staging/media/imx/imx6-mipi-csi2.c +++ b/drivers/staging/media/imx/imx6-mipi-csi2.c @@ -252,8 +252,8 @@ static int csi2_dphy_wait_stopstate(struct csi2_dev *csi2) u32 mask, reg; int ret; - mask = PHY_STOPSTATECLK | - ((csi2->bus.num_data_lanes - 1) << PHY_STOPSTATEDATA_BIT); + mask = PHY_STOPSTATECLK | (((1 << csi2->bus.num_data_lanes) - 1) << + PHY_STOPSTATEDATA_BIT); ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg, (reg & mask) == mask, 0, 500000); |