1 From 2c51b8e533a8b43bde18072c9dbbd0fc5084bbe7 Mon Sep 17 00:00:00 2001
2 From: Dave Stevenson <dave.stevenson@raspberrypi.org>
3 Date: Wed, 2 Oct 2019 17:40:38 +0100
4 Subject: [PATCH] media: bcm2835-unicam: Rework to not cache the list
7 Some sensors will change Bayer order based on H & V flips,
8 therefore collecting the list of formats at async_bound has
11 Enumerate the formats from the sensor every time.
13 Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.org>
15 .../media/platform/bcm2835/bcm2835-unicam.c | 246 ++++++++++--------
16 1 file changed, 136 insertions(+), 110 deletions(-)
18 --- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
19 +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
20 @@ -363,8 +363,6 @@ struct unicam_device {
21 /* Used to store current mbus frame format */
22 struct v4l2_mbus_framefmt m_fmt;
24 - struct unicam_fmt active_fmts[MAX_POSSIBLE_PIX_FMTS];
26 unsigned int virtual_channel;
27 enum v4l2_mbus_type bus_type;
29 @@ -455,48 +453,30 @@ static int find_mbus_depth_by_code(u32 c
33 -static const struct unicam_fmt *find_format_by_code(struct unicam_device *dev,
35 +static const struct unicam_fmt *find_format_by_code(u32 code)
37 - const struct unicam_fmt *fmt;
40 - for (k = 0; k < dev->num_active_fmt; k++) {
41 - fmt = &dev->active_fmts[k];
42 - if (fmt->code == code)
44 + for (k = 0; k < ARRAY_SIZE(formats); k++) {
45 + if (formats[k].code == code)
52 -static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev,
54 +static const struct unicam_fmt *find_format_by_pix(u32 pixelformat)
56 - const struct unicam_fmt *fmt;
59 - for (k = 0; k < dev->num_active_fmt; k++) {
60 - fmt = &dev->active_fmts[k];
61 - if (fmt->fourcc == pixelformat)
63 + for (k = 0; k < ARRAY_SIZE(formats); k++) {
64 + if (formats[k].fourcc == pixelformat)
71 -static void dump_active_formats(struct unicam_device *dev)
75 - for (i = 0; i < dev->num_active_fmt; i++) {
76 - unicam_dbg(3, dev, "active_fmt[%d] (%p) is code %04x, fourcc " V4L2_FOURCC_CONV ", depth %d\n",
77 - i, &dev->active_fmts[i], dev->active_fmts[i].code,
78 - V4L2_FOURCC_CONV_ARGS(dev->active_fmts[i].fourcc),
79 - dev->active_fmts[i].depth);
83 static inline unsigned int bytes_per_line(u32 width,
84 const struct unicam_fmt *fmt)
86 @@ -726,14 +706,40 @@ static int unicam_enum_fmt_vid_cap(struc
87 struct v4l2_fmtdesc *f)
89 struct unicam_device *dev = video_drvdata(file);
90 + struct v4l2_subdev_mbus_code_enum mbus_code;
91 const struct unicam_fmt *fmt = NULL;
96 - if (f->index >= dev->num_active_fmt)
98 + /* Loop whilst the sensor driver says it has more formats, but add a
99 + * failsafe against a dodgy driver at 128 (more than any sensor will
100 + * ever sensibly advertise)
102 + for (i = 0; !ret && i < 128 ; i++) {
103 + memset(&mbus_code, 0, sizeof(mbus_code));
104 + mbus_code.index = i;
106 - fmt = &dev->active_fmts[f->index];
107 + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code,
111 + "subdev->enum_mbus_code idx %d returned %d - index invalid\n",
116 - f->pixelformat = fmt->fourcc;
117 + fmt = find_format_by_code(mbus_code.code);
120 + if (index == f->index) {
121 + f->pixelformat = fmt->fourcc;
131 @@ -748,6 +754,39 @@ static int unicam_g_fmt_vid_cap(struct f
136 +const struct unicam_fmt *get_first_supported_format(struct unicam_device *dev)
138 + struct v4l2_subdev_mbus_code_enum mbus_code;
139 + const struct unicam_fmt *fmt = NULL;
143 + for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
144 + memset(&mbus_code, 0, sizeof(mbus_code));
145 + mbus_code.index = j;
146 + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL,
150 + "subdev->enum_mbus_code idx %d returned %d - continue\n",
155 + unicam_dbg(2, dev, "subdev %s: code: %04x idx: %d\n",
156 + dev->sensor->name, mbus_code.code, j);
158 + fmt = find_format_by_code(mbus_code.code);
159 + unicam_dbg(2, dev, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
160 + mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
161 + fmt ? fmt->csi_dt : 0);
168 static int unicam_try_fmt_vid_cap(struct file *file, void *priv,
169 struct v4l2_format *f)
171 @@ -759,13 +798,15 @@ static int unicam_try_fmt_vid_cap(struct
172 struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
175 - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
176 + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
178 - unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use default of %08X\n",
179 - f->fmt.pix.pixelformat, dev->active_fmts[0].fourcc);
180 + /* Pixel format not supported by unicam. Choose the first
181 + * supported format, and let the sensor choose something else.
183 + unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n",
184 + f->fmt.pix.pixelformat);
186 - /* Just get the first one enumerated */
187 - fmt = &dev->active_fmts[0];
189 f->fmt.pix.pixelformat = fmt->fourcc;
192 @@ -785,6 +826,40 @@ static int unicam_try_fmt_vid_cap(struct
193 unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
195 v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
196 + if (mbus_fmt->code != fmt->code) {
197 + /* Sensor has returned an alternate format */
198 + fmt = find_format_by_code(mbus_fmt->code);
200 + /* The alternate format is one unicam can't support.
201 + * Find the first format that is supported by both, and
204 + fmt = get_first_supported_format(dev);
205 + mbus_fmt->code = fmt->code;
207 + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt,
208 + dev->sensor_config, &sd_fmt);
209 + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV)
212 + if (mbus_fmt->field != V4L2_FIELD_NONE)
213 + unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n");
215 + v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format);
217 + if (mbus_fmt->code != fmt->code) {
218 + /* We've set a format that the sensor reports
219 + * as being supported, but it refuses to set it.
220 + * Not much else we can do.
221 + * Assume that the sensor driver may accept the
222 + * format when it is set (rather than tried).
224 + unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n");
228 + f->fmt.pix.pixelformat = fmt->fourcc;
231 return unicam_calc_format_size_bpl(dev, fmt, f);
233 @@ -805,10 +880,18 @@ static int unicam_s_fmt_vid_cap(struct f
237 - fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat);
238 + fmt = find_format_by_pix(f->fmt.pix.pixelformat);
240 - /* Unknown pixel format - adopt a default */
241 - fmt = &dev->active_fmts[0];
242 + /* Unknown pixel format - adopt a default.
243 + * This shouldn't happen as try_fmt should have resolved any
246 + fmt = get_first_supported_format(dev);
248 + /* It shouldn't be possible to get here with no
249 + * supported formats
252 f->fmt.pix.pixelformat = fmt->fourcc;
255 @@ -944,6 +1027,7 @@ static void unicam_set_packing_config(st
256 unpack = UNICAM_PUM_NONE;
260 switch (v4l2_depth) {
262 pack = UNICAM_PPM_PACK8;
263 @@ -1439,7 +1523,7 @@ static int unicam_enum_framesizes(struct
266 /* check for valid format */
267 - fmt = find_format_by_pix(dev, fsize->pixel_format);
268 + fmt = find_format_by_pix(fsize->pixel_format);
270 unicam_dbg(3, dev, "Invalid pixel code: %x\n",
271 fsize->pixel_format);
272 @@ -1478,7 +1562,7 @@ static int unicam_enum_frameintervals(st
276 - fmt = find_format_by_pix(dev, fival->pixel_format);
277 + fmt = find_format_by_pix(fival->pixel_format);
281 @@ -1742,27 +1826,6 @@ static const struct v4l2_ioctl_ops unica
282 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
286 - * Adds an entry to the active_fmts array
287 - * Returns non-zero if attempting to write off the end of the array.
289 -static int unicam_add_active_format(struct unicam_device *unicam,
290 - const struct unicam_fmt *fmt)
292 - //Ensure we don't run off the end of the array.
293 - if (unicam->num_active_fmt >= MAX_POSSIBLE_PIX_FMTS)
296 - unicam->active_fmts[unicam->num_active_fmt] = *fmt;
297 - unicam_dbg(2, unicam,
298 - "matched fourcc: " V4L2_FOURCC_CONV ": code: %04x idx: %d\n",
299 - V4L2_FOURCC_CONV_ARGS(fmt->fourcc),
300 - fmt->code, unicam->num_active_fmt);
301 - unicam->num_active_fmt++;
307 unicam_async_bound(struct v4l2_async_notifier *notifier,
308 struct v4l2_subdev *subdev,
309 @@ -1770,9 +1833,6 @@ unicam_async_bound(struct v4l2_async_not
311 struct unicam_device *unicam = container_of(notifier->v4l2_dev,
312 struct unicam_device, v4l2_dev);
313 - struct v4l2_subdev_mbus_code_enum mbus_code;
317 if (unicam->sensor) {
318 unicam_info(unicam, "Rejecting subdev %s (Already set!!)",
319 @@ -1783,47 +1843,6 @@ unicam_async_bound(struct v4l2_async_not
320 unicam->sensor = subdev;
321 unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name);
323 - /* Enumerate sub device formats and enable all matching local formats */
324 - unicam->num_active_fmt = 0;
325 - unicam_dbg(2, unicam, "Get supported formats...\n");
326 - for (j = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++j) {
327 - const struct unicam_fmt *fmt = NULL;
330 - memset(&mbus_code, 0, sizeof(mbus_code));
331 - mbus_code.index = j;
332 - ret = v4l2_subdev_call(subdev, pad, enum_mbus_code,
335 - unicam_dbg(2, unicam,
336 - "subdev->enum_mbus_code idx %d returned %d - continue\n",
341 - unicam_dbg(2, unicam, "subdev %s: code: %04x idx: %d\n",
342 - subdev->name, mbus_code.code, j);
344 - for (k = 0; k < ARRAY_SIZE(formats); k++) {
345 - if (mbus_code.code == formats[k].code) {
350 - unicam_dbg(2, unicam, "fmt %04x returned as %p, V4L2 FOURCC %04x, csi_dt %02X\n",
351 - mbus_code.code, fmt, fmt ? fmt->fourcc : 0,
352 - fmt ? fmt->csi_dt : 0);
354 - if (unicam_add_active_format(unicam, fmt)) {
355 - unicam_dbg(1, unicam, "Active fmt list truncated\n");
360 - unicam_dbg(2, unicam,
361 - "Done all formats\n");
362 - dump_active_formats(unicam);
367 @@ -1849,10 +1868,17 @@ static int unicam_probe_complete(struct
371 - fmt = find_format_by_code(unicam, mbus_fmt.code);
372 + fmt = find_format_by_code(mbus_fmt.code);
374 - /* Default image format not valid. Choose first active fmt. */
375 - fmt = &unicam->active_fmts[0];
376 + /* Find the first format that the sensor and unicam both
379 + fmt = get_first_supported_format(unicam);
382 + /* No compatible formats */
385 mbus_fmt.code = fmt->code;
386 ret = __subdev_set_format(unicam, &mbus_fmt);