Skip to content

Commit bf3cfaa

Browse files
slongerbeammchehab
authored andcommitted
media: staging/imx: get CSI bus type from nearest upstream entity
The imx-media driver currently supports a device tree graph of limited complexity. This patch is a first step in allowing imx-media to work with more general OF graphs. The CSI subdevice assumes the originating upstream subdevice (the "sensor") is connected directly to either the CSI mux or the MIPI CSI-2 receiver. But for more complex graphs, the sensor can be distant, with possible bridge entities in between. Thus the sensor's bus type could be quite different from what is entering the CSI. For example a distant sensor could have a parallel interface, but the stream entering the i.MX is MIPI CSI-2. To remove this assumption, get the entering bus config from the entity that is directly upstream from either the CSI mux, or the CSI-2 receiver. If the CSI-2 receiver is not in the enabled pipeline, the bus type to the CSI is parallel, otherwise the CSI is receiving MIPI CSI-2. Note that we can't use the direct upstream source connected to CSI (which is either the CSI mux or the CSI-2 receiver) to determine bus type. The bus entering the CSI from the CSI-2 receiver is a 32-bit parallel bus containing the demultiplexed MIPI CSI-2 virtual channels. But the CSI and its IDMAC channels must be configured based on whether it is receiving data from the CSI-2 receiver or from the CSI mux's parallel interface pins. The function csi_get_upstream_endpoint() is used to find this endpoint. It makes use of a new utility function imx_media_find_upstream_pad(), that if given a grp_id of 0, will return the closest upstream pad from start_entity. With these changes, imx_media_find_sensor() is no longer used and is removed. As a result there is also no longer a need to identify any sensor or set the sensor subdev's group id as a method to search for it. So IMX_MEDIA_GRP_ID_SENSOR is removed. Also the video-mux group id IMX_MEDIA_GRP_ID_VIDMUX was never used so that is removed as well. The remaining IMX_MEDIA_GRP_ID_* definitions are entities internal to the i.MX. Another use of imx_media_find_sensor() in the CSI was to call the sensor's g_skip_frames op to determine if a delay was needed before enabling the CSI at stream on. If necessary this will have to be re-addressed at a later time. Signed-off-by: Steve Longerbeam <[email protected]> Signed-off-by: Hans Verkuil <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent 3cd890d commit bf3cfaa

File tree

5 files changed

+150
-161
lines changed

5 files changed

+150
-161
lines changed

drivers/staging/media/imx/imx-media-csi.c

Lines changed: 108 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/gcd.h>
1414
#include <linux/interrupt.h>
1515
#include <linux/module.h>
16+
#include <linux/of_graph.h>
1617
#include <linux/pinctrl/consumer.h>
1718
#include <linux/platform_device.h>
1819
#include <media/v4l2-ctrls.h>
@@ -99,8 +100,8 @@ struct csi_priv {
99100
/* the mipi virtual channel number at link validate */
100101
int vc_num;
101102

102-
/* the attached sensor at stream on */
103-
struct imx_media_subdev *sensor;
103+
/* the upstream endpoint CSI is receiving from */
104+
struct v4l2_fwnode_endpoint upstream_ep;
104105

105106
spinlock_t irqlock; /* protect eof_irq handler */
106107
struct timer_list eof_timeout_timer;
@@ -120,6 +121,71 @@ static inline struct csi_priv *sd_to_dev(struct v4l2_subdev *sdev)
120121
return container_of(sdev, struct csi_priv, sd);
121122
}
122123

124+
static inline bool is_parallel_16bit_bus(struct v4l2_fwnode_endpoint *ep)
125+
{
126+
return ep->bus_type != V4L2_MBUS_CSI2 &&
127+
ep->bus.parallel.bus_width >= 16;
128+
}
129+
130+
/*
131+
* Parses the fwnode endpoint from the source pad of the entity
132+
* connected to this CSI. This will either be the entity directly
133+
* upstream from the CSI-2 receiver, or directly upstream from the
134+
* video mux. The endpoint is needed to determine the bus type and
135+
* bus config coming into the CSI.
136+
*/
137+
static int csi_get_upstream_endpoint(struct csi_priv *priv,
138+
struct v4l2_fwnode_endpoint *ep)
139+
{
140+
struct device_node *endpoint, *port;
141+
struct imx_media_subdev *imxsd;
142+
struct media_entity *src;
143+
struct v4l2_subdev *sd;
144+
struct media_pad *pad;
145+
146+
if (!priv->src_sd)
147+
return -EPIPE;
148+
149+
src = &priv->src_sd->entity;
150+
151+
if (src->function == MEDIA_ENT_F_VID_MUX) {
152+
/*
153+
* CSI is connected directly to video mux, skip up to
154+
* CSI-2 receiver if it is in the path, otherwise stay
155+
* with video mux.
156+
*/
157+
imxsd = imx_media_find_upstream_subdev(priv->md, src,
158+
IMX_MEDIA_GRP_ID_CSI2);
159+
if (!IS_ERR(imxsd))
160+
src = &imxsd->sd->entity;
161+
}
162+
163+
/* get source pad of entity directly upstream from src */
164+
pad = imx_media_find_upstream_pad(priv->md, src, 0);
165+
if (IS_ERR(pad))
166+
return PTR_ERR(pad);
167+
168+
sd = media_entity_to_v4l2_subdev(pad->entity);
169+
170+
/*
171+
* NOTE: this assumes an OF-graph port id is the same as a
172+
* media pad index.
173+
*/
174+
port = of_graph_get_port_by_id(sd->dev->of_node, pad->index);
175+
if (!port)
176+
return -ENODEV;
177+
178+
endpoint = of_get_next_child(port, NULL);
179+
of_node_put(port);
180+
if (!endpoint)
181+
return -ENODEV;
182+
183+
v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), ep);
184+
of_node_put(endpoint);
185+
186+
return 0;
187+
}
188+
123189
static void csi_idmac_put_ipu_resources(struct csi_priv *priv)
124190
{
125191
if (priv->idmac_ch)
@@ -302,7 +368,6 @@ static void csi_idmac_unsetup_vb2_buf(struct csi_priv *priv,
302368
static int csi_idmac_setup_channel(struct csi_priv *priv)
303369
{
304370
struct imx_media_video_dev *vdev = priv->vdev;
305-
struct v4l2_fwnode_endpoint *sensor_ep;
306371
struct v4l2_mbus_framefmt *infmt;
307372
struct ipu_image image;
308373
u32 passthrough_bits;
@@ -312,7 +377,6 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
312377
int ret;
313378

314379
infmt = &priv->format_mbus[CSI_SINK_PAD];
315-
sensor_ep = &priv->sensor->sensor_ep;
316380

317381
ipu_cpmem_zero(priv->idmac_ch);
318382

@@ -330,7 +394,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
330394
* Check for conditions that require the IPU to handle the
331395
* data internally as generic data, aka passthrough mode:
332396
* - raw bayer formats
333-
* - the sensor bus is 16-bit parallel
397+
* - the CSI is receiving from a 16-bit parallel bus
334398
*/
335399
switch (image.pix.pixelformat) {
336400
case V4L2_PIX_FMT_SBGGR8:
@@ -354,8 +418,7 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
354418
burst_size = (image.pix.width & 0x3f) ?
355419
((image.pix.width & 0x1f) ?
356420
((image.pix.width & 0xf) ? 8 : 16) : 32) : 64;
357-
passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
358-
sensor_ep->bus.parallel.bus_width >= 16);
421+
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
359422
passthrough_bits = 16;
360423
/* Skip writing U and V components to odd rows */
361424
ipu_cpmem_skip_odd_chroma_rows(priv->idmac_ch);
@@ -364,14 +427,12 @@ static int csi_idmac_setup_channel(struct csi_priv *priv)
364427
case V4L2_PIX_FMT_UYVY:
365428
burst_size = (image.pix.width & 0x1f) ?
366429
((image.pix.width & 0xf) ? 8 : 16) : 32;
367-
passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
368-
sensor_ep->bus.parallel.bus_width >= 16);
430+
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
369431
passthrough_bits = 16;
370432
break;
371433
default:
372434
burst_size = (image.pix.width & 0xf) ? 8 : 16;
373-
passthrough = (sensor_ep->bus_type != V4L2_MBUS_CSI2 &&
374-
sensor_ep->bus.parallel.bus_width >= 16);
435+
passthrough = is_parallel_16bit_bus(&priv->upstream_ep);
375436
passthrough_bits = 16;
376437
break;
377438
}
@@ -568,22 +629,20 @@ static void csi_idmac_stop(struct csi_priv *priv)
568629
static int csi_setup(struct csi_priv *priv)
569630
{
570631
struct v4l2_mbus_framefmt *infmt, *outfmt;
571-
struct v4l2_mbus_config sensor_mbus_cfg;
572-
struct v4l2_fwnode_endpoint *sensor_ep;
632+
struct v4l2_mbus_config mbus_cfg;
573633
struct v4l2_mbus_framefmt if_fmt;
574634

575635
infmt = &priv->format_mbus[CSI_SINK_PAD];
576636
outfmt = &priv->format_mbus[priv->active_output_pad];
577-
sensor_ep = &priv->sensor->sensor_ep;
578637

579-
/* compose mbus_config from sensor endpoint */
580-
sensor_mbus_cfg.type = sensor_ep->bus_type;
581-
sensor_mbus_cfg.flags = (sensor_ep->bus_type == V4L2_MBUS_CSI2) ?
582-
sensor_ep->bus.mipi_csi2.flags :
583-
sensor_ep->bus.parallel.flags;
638+
/* compose mbus_config from the upstream endpoint */
639+
mbus_cfg.type = priv->upstream_ep.bus_type;
640+
mbus_cfg.flags = (priv->upstream_ep.bus_type == V4L2_MBUS_CSI2) ?
641+
priv->upstream_ep.bus.mipi_csi2.flags :
642+
priv->upstream_ep.bus.parallel.flags;
584643

585644
/*
586-
* we need to pass input sensor frame to CSI interface, but
645+
* we need to pass input frame to CSI interface, but
587646
* with translated field type from output format
588647
*/
589648
if_fmt = *infmt;
@@ -595,7 +654,7 @@ static int csi_setup(struct csi_priv *priv)
595654
priv->crop.width == 2 * priv->compose.width,
596655
priv->crop.height == 2 * priv->compose.height);
597656

598-
ipu_csi_init_interface(priv->csi, &sensor_mbus_cfg, &if_fmt);
657+
ipu_csi_init_interface(priv->csi, &mbus_cfg, &if_fmt);
599658

600659
ipu_csi_set_dest(priv->csi, priv->dest);
601660

@@ -611,35 +670,11 @@ static int csi_setup(struct csi_priv *priv)
611670
static int csi_start(struct csi_priv *priv)
612671
{
613672
struct v4l2_fract *output_fi, *input_fi;
614-
u32 bad_frames = 0;
615673
int ret;
616674

617-
if (!priv->sensor) {
618-
v4l2_err(&priv->sd, "no sensor attached\n");
619-
return -EINVAL;
620-
}
621-
622675
output_fi = &priv->frame_interval[priv->active_output_pad];
623676
input_fi = &priv->frame_interval[CSI_SINK_PAD];
624677

625-
ret = v4l2_subdev_call(priv->sensor->sd, sensor,
626-
g_skip_frames, &bad_frames);
627-
if (!ret && bad_frames) {
628-
u32 delay_usec;
629-
630-
/*
631-
* This sensor has bad frames when it is turned on,
632-
* add a delay to avoid them before enabling the CSI
633-
* hardware. Especially for sensors with a bt.656 interface,
634-
* any shifts in the SAV/EAV sync codes will cause the CSI
635-
* to lose vert/horiz sync.
636-
*/
637-
delay_usec = DIV_ROUND_UP_ULL(
638-
(u64)USEC_PER_SEC * input_fi->numerator * bad_frames,
639-
input_fi->denominator);
640-
usleep_range(delay_usec, delay_usec + 1000);
641-
}
642-
643678
if (priv->dest == IPU_CSI_DEST_IDMAC) {
644679
ret = csi_idmac_start(priv);
645680
if (ret)
@@ -971,9 +1006,8 @@ static int csi_link_validate(struct v4l2_subdev *sd,
9711006
struct v4l2_subdev_format *sink_fmt)
9721007
{
9731008
struct csi_priv *priv = v4l2_get_subdevdata(sd);
974-
struct v4l2_fwnode_endpoint *sensor_ep;
1009+
struct v4l2_fwnode_endpoint upstream_ep;
9751010
const struct imx_media_pixfmt *incc;
976-
struct imx_media_subdev *sensor;
9771011
bool is_csi2;
9781012
int ret;
9791013

@@ -982,22 +1016,20 @@ static int csi_link_validate(struct v4l2_subdev *sd,
9821016
if (ret)
9831017
return ret;
9841018

985-
sensor = __imx_media_find_sensor(priv->md, &priv->sd.entity);
986-
if (IS_ERR(sensor)) {
987-
v4l2_err(&priv->sd, "no sensor attached\n");
988-
return PTR_ERR(sensor);
1019+
ret = csi_get_upstream_endpoint(priv, &upstream_ep);
1020+
if (ret) {
1021+
v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
1022+
return ret;
9891023
}
9901024

9911025
mutex_lock(&priv->lock);
9921026

993-
priv->sensor = sensor;
994-
sensor_ep = &priv->sensor->sensor_ep;
995-
is_csi2 = (sensor_ep->bus_type == V4L2_MBUS_CSI2);
1027+
priv->upstream_ep = upstream_ep;
1028+
is_csi2 = (upstream_ep.bus_type == V4L2_MBUS_CSI2);
9961029
incc = priv->cc[CSI_SINK_PAD];
9971030

9981031
if (priv->dest != IPU_CSI_DEST_IDMAC &&
999-
(incc->bayer || (!is_csi2 &&
1000-
sensor_ep->bus.parallel.bus_width >= 16))) {
1032+
(incc->bayer || is_parallel_16bit_bus(&upstream_ep))) {
10011033
v4l2_err(&priv->sd,
10021034
"bayer/16-bit parallel buses must go to IDMAC pad\n");
10031035
ret = -EINVAL;
@@ -1067,12 +1099,8 @@ static void csi_try_crop(struct csi_priv *priv,
10671099
struct v4l2_rect *crop,
10681100
struct v4l2_subdev_pad_config *cfg,
10691101
struct v4l2_mbus_framefmt *infmt,
1070-
struct imx_media_subdev *sensor)
1102+
struct v4l2_fwnode_endpoint *upstream_ep)
10711103
{
1072-
struct v4l2_fwnode_endpoint *sensor_ep;
1073-
1074-
sensor_ep = &sensor->sensor_ep;
1075-
10761104
crop->width = min_t(__u32, infmt->width, crop->width);
10771105
if (crop->left + crop->width > infmt->width)
10781106
crop->left = infmt->width - crop->width;
@@ -1086,7 +1114,7 @@ static void csi_try_crop(struct csi_priv *priv,
10861114
* sync, so fix it to NTSC/PAL active lines. NTSC contains
10871115
* 2 extra lines of active video that need to be cropped.
10881116
*/
1089-
if (sensor_ep->bus_type == V4L2_MBUS_BT656 &&
1117+
if (upstream_ep->bus_type == V4L2_MBUS_BT656 &&
10901118
(V4L2_FIELD_HAS_BOTH(infmt->field) ||
10911119
infmt->field == V4L2_FIELD_ALTERNATE)) {
10921120
crop->height = infmt->height;
@@ -1236,7 +1264,7 @@ static int csi_get_fmt(struct v4l2_subdev *sd,
12361264
}
12371265

12381266
static void csi_try_fmt(struct csi_priv *priv,
1239-
struct imx_media_subdev *sensor,
1267+
struct v4l2_fwnode_endpoint *upstream_ep,
12401268
struct v4l2_subdev_pad_config *cfg,
12411269
struct v4l2_subdev_format *sdformat,
12421270
struct v4l2_rect *crop,
@@ -1304,7 +1332,7 @@ static void csi_try_fmt(struct csi_priv *priv,
13041332
crop->top = 0;
13051333
crop->width = sdformat->format.width;
13061334
crop->height = sdformat->format.height;
1307-
csi_try_crop(priv, crop, cfg, &sdformat->format, sensor);
1335+
csi_try_crop(priv, crop, cfg, &sdformat->format, upstream_ep);
13081336
compose->left = 0;
13091337
compose->top = 0;
13101338
compose->width = crop->width;
@@ -1333,20 +1361,20 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
13331361
{
13341362
struct csi_priv *priv = v4l2_get_subdevdata(sd);
13351363
struct imx_media_video_dev *vdev = priv->vdev;
1364+
struct v4l2_fwnode_endpoint upstream_ep;
13361365
const struct imx_media_pixfmt *cc;
1337-
struct imx_media_subdev *sensor;
13381366
struct v4l2_pix_format vdev_fmt;
13391367
struct v4l2_mbus_framefmt *fmt;
13401368
struct v4l2_rect *crop, *compose;
1341-
int ret = 0;
1369+
int ret;
13421370

13431371
if (sdformat->pad >= CSI_NUM_PADS)
13441372
return -EINVAL;
13451373

1346-
sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
1347-
if (IS_ERR(sensor)) {
1348-
v4l2_err(&priv->sd, "no sensor attached\n");
1349-
return PTR_ERR(sensor);
1374+
ret = csi_get_upstream_endpoint(priv, &upstream_ep);
1375+
if (ret) {
1376+
v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
1377+
return ret;
13501378
}
13511379

13521380
mutex_lock(&priv->lock);
@@ -1359,7 +1387,7 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
13591387
crop = __csi_get_crop(priv, cfg, sdformat->which);
13601388
compose = __csi_get_compose(priv, cfg, sdformat->which);
13611389

1362-
csi_try_fmt(priv, sensor, cfg, sdformat, crop, compose, &cc);
1390+
csi_try_fmt(priv, &upstream_ep, cfg, sdformat, crop, compose, &cc);
13631391

13641392
fmt = __csi_get_fmt(priv, cfg, sdformat->pad, sdformat->which);
13651393
*fmt = sdformat->format;
@@ -1376,8 +1404,8 @@ static int csi_set_fmt(struct v4l2_subdev *sd,
13761404
format.pad = pad;
13771405
format.which = sdformat->which;
13781406
format.format = sdformat->format;
1379-
csi_try_fmt(priv, sensor, cfg, &format, NULL, compose,
1380-
&outcc);
1407+
csi_try_fmt(priv, &upstream_ep, cfg, &format,
1408+
NULL, compose, &outcc);
13811409

13821410
outfmt = __csi_get_fmt(priv, cfg, pad, sdformat->which);
13831411
*outfmt = format.format;
@@ -1472,18 +1500,18 @@ static int csi_set_selection(struct v4l2_subdev *sd,
14721500
struct v4l2_subdev_selection *sel)
14731501
{
14741502
struct csi_priv *priv = v4l2_get_subdevdata(sd);
1503+
struct v4l2_fwnode_endpoint upstream_ep;
14751504
struct v4l2_mbus_framefmt *infmt;
14761505
struct v4l2_rect *crop, *compose;
1477-
struct imx_media_subdev *sensor;
1478-
int pad, ret = 0;
1506+
int pad, ret;
14791507

14801508
if (sel->pad != CSI_SINK_PAD)
14811509
return -EINVAL;
14821510

1483-
sensor = imx_media_find_sensor(priv->md, &priv->sd.entity);
1484-
if (IS_ERR(sensor)) {
1485-
v4l2_err(&priv->sd, "no sensor attached\n");
1486-
return PTR_ERR(sensor);
1511+
ret = csi_get_upstream_endpoint(priv, &upstream_ep);
1512+
if (ret) {
1513+
v4l2_err(&priv->sd, "failed to find upstream endpoint\n");
1514+
return ret;
14871515
}
14881516

14891517
mutex_lock(&priv->lock);
@@ -1511,7 +1539,7 @@ static int csi_set_selection(struct v4l2_subdev *sd,
15111539
goto out;
15121540
}
15131541

1514-
csi_try_crop(priv, &sel->r, cfg, infmt, sensor);
1542+
csi_try_crop(priv, &sel->r, cfg, infmt, &upstream_ep);
15151543

15161544
*crop = sel->r;
15171545

drivers/staging/media/imx/imx-media-dev.c

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,6 @@ static int imx_media_subdev_bound(struct v4l2_async_notifier *notifier,
222222
ret = imx_media_get_ipu(imxmd, sd);
223223
if (ret)
224224
goto out_unlock;
225-
} else if (sd->entity.function == MEDIA_ENT_F_VID_MUX) {
226-
/* this is a video mux */
227-
sd->grp_id = IMX_MEDIA_GRP_ID_VIDMUX;
228-
} else if (imxsd->num_sink_pads == 0) {
229-
/*
230-
* this is an original source of video frames, it
231-
* could be a camera sensor, an analog decoder, or
232-
* a bridge device (HDMI -> MIPI CSI-2 for example).
233-
* This group ID is used to locate the entity that
234-
* is the original source of video in a pipeline.
235-
*/
236-
sd->grp_id = IMX_MEDIA_GRP_ID_SENSOR;
237225
}
238226

239227
/* attach the subdev */

0 commit comments

Comments
 (0)