From 5d4d0de2c445e74b6748899f5dccfa4c6f5dcf12 Mon Sep 17 00:00:00 2001 From: Bert Outtier Date: Tue, 27 Oct 2026 13:18:35 +0100 Subject: [PATCH 1/2] media: rp1_cfe: Factor out the remap lookup cfe_find_16bit_code() and cfe_find_compressed_code() do the same lookup with a different remap type. Factor it out into cfe_find_remap_fourcc(), which gives the remapped pixel format of a given type, and cfe_find_remap_code(), which gives that format's code, so that other remap types can use it. No functional change. Signed-off-by: Bert Outtier --- .../media/platform/raspberrypi/rp1_cfe/cfe.c | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c index 6bda6515714fd1..4dff25dd05804f 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c @@ -480,43 +480,52 @@ const struct cfe_fmt *find_format_by_pix(u32 pixelformat) } /* - * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap - * possible. + * Given the mbus code, find its remapped pixel format of the given type. + * Returns 0 if no remap possible. */ -u32 cfe_find_16bit_code(u32 code) +static u32 cfe_find_remap_fourcc(u32 code, enum cfe_remap_types type) { - const struct cfe_fmt *cfe_fmt; + const struct cfe_fmt *cfe_fmt = find_format_by_code(code); - cfe_fmt = find_format_by_code(code); + return cfe_fmt ? cfe_fmt->remap[type] : 0; +} + +/* + * Given the mbus code, find the code of its remapped pixel format of the given + * type. Returns 0 if no remap possible. + */ +static u32 cfe_find_remap_code(u32 code, enum cfe_remap_types type) +{ + const struct cfe_fmt *cfe_fmt; + u32 fourcc; - if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT]) + fourcc = cfe_find_remap_fourcc(code, type); + if (!fourcc) return 0; - cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]); + cfe_fmt = find_format_by_pix(fourcc); if (!cfe_fmt) return 0; return cfe_fmt->code; } +/* + * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap + * possible. + */ +u32 cfe_find_16bit_code(u32 code) +{ + return cfe_find_remap_code(code, CFE_REMAP_16BIT); +} + /* * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap * possible. */ u32 cfe_find_compressed_code(u32 code) { - const struct cfe_fmt *cfe_fmt; - - cfe_fmt = find_format_by_code(code); - - if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED]) - return 0; - - cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]); - if (!cfe_fmt) - return 0; - - return cfe_fmt->code; + return cfe_find_remap_code(code, CFE_REMAP_COMPRESSED); } static int cfe_calc_format_size_bpl(struct cfe_device *cfe, From e9e996e988406faca98490155e2a950f822fbff6 Mon Sep 17 00:00:00 2001 From: Bert Outtier Date: Tue, 27 Oct 2026 13:18:36 +0100 Subject: [PATCH 2/2] media: rp1_cfe: Add LSB-aligned unpacked Bayer formats to the FE outputs The PiSP Front End can shift its 16-bit output right (PISP_FE_OUTPUT0_FORMAT.SHIFT), which writes unpacked 10 and 12-bit samples aligned to the LSB: the layout of the standard V4L2_PIX_FMT_S*10 and V4L2_PIX_FMT_S*12 formats. Today the FE output nodes only offer the 16-bit and compressed formats, so userspace has to report SBGGR16 for such a stream and carry the shift on the side. Offer V4L2_PIX_FMT_S{BGGR,GBRG,GRBG,RGGB}{10,12} on the FE output nodes, with the FE source pad's 16-bit media bus code and a stride of width * 2: - a new CFE_REMAP_LSB16 remap of the 10 and 12-bit Bayer codes, next to the 16-bit and compressed ones, names the LSB-aligned format the FE output can write for that code; the FE output shift of such a format is 16 - the bit depth of that code (cfe_find_lsb16_shift()); - the new formats are kept off the CSI-2 nodes, whose REMAP unpacking is MSB-aligned; - link validation on the FE output nodes accepts them for the 16-bit code of the FE source pad, and only if the format is the CFE_REMAP_LSB16 remap of the code on the CSI-2 sink pad of the channel that feeds the FE, so that a 10-bit format can't drop bits of 12-bit data; - pisp_fe_validate_output() requires the FE config to match the node format: 16 bits per sample, uncompressed, shifted right by 16 - bit depth. The existing formats and lookups are unchanged: the new entries come after the 16-bit ones, as find_format_by_code() returns the first match. Tested on a Raspberry Pi 5 with a 10-bit and a 12-bit sensor through libcamera: the LSB-aligned RAW stream equals the MSB-aligned one shifted down, the Back End output from it is bit-identical, and a format of the wrong bit depth fails to start streaming. Signed-off-by: Bert Outtier --- .../media/platform/raspberrypi/rp1_cfe/cfe.c | 67 +++++++++++++++- .../media/platform/raspberrypi/rp1_cfe/cfe.h | 3 + .../platform/raspberrypi/rp1_cfe/cfe_fmts.h | 77 +++++++++++++++++-- .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 17 ++++ 4 files changed, 155 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c index 4dff25dd05804f..67b7abe4448db0 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c +++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c @@ -519,6 +519,28 @@ u32 cfe_find_16bit_code(u32 code) return cfe_find_remap_code(code, CFE_REMAP_16BIT); } +/* + * Given an unpacked LSB-aligned 16-bit pixel format, find the right shift of the + * FE output that writes it: 16 - the bit depth of the code it is the + * CFE_REMAP_LSB16 remap of. Returns 0 for any other pixel format. These formats + * are only available on the FE output nodes, as the CSI-2 REMAP mode is + * MSB-aligned. + */ +unsigned int cfe_find_lsb16_shift(u32 pixelformat) +{ + unsigned int i; + + if (!pixelformat) + return 0; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].remap[CFE_REMAP_LSB16] == pixelformat) + return 16 - formats[i].depth; + } + + return 0; +} + /* * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap * possible. @@ -1303,6 +1325,9 @@ static int cfe_enum_fmt_vid_cap(struct file *file, void *priv, !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT)) continue; + if (is_csi2_node(node) && cfe_find_lsb16_shift(formats[i].fourcc)) + continue; + if (j == f->index) { f->pixelformat = formats[i].fourcc; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1347,7 +1372,7 @@ static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f) * Default to a format that works for both CSI2 and FE. */ fmt = find_format_by_pix(f->fmt.pix.pixelformat); - if (!fmt) + if (!fmt || (is_csi2_node(node) && cfe_find_lsb16_shift(fmt->fourcc))) fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10); f->fmt.pix.pixelformat = fmt->fourcc; @@ -1672,6 +1697,25 @@ static const struct v4l2_file_operations cfe_fops = { .mmap = vb2_fop_mmap, }; +/* The mbus code of the CSI-2 data feeding the FE, 0 if none. */ +static u32 cfe_fe_input_code(struct cfe_device *cfe) +{ + struct v4l2_subdev_state *state; + struct v4l2_mbus_framefmt *fmt; + u32 code = 0; + + if (!is_fe_enabled(cfe)) + return 0; + + state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd); + fmt = v4l2_subdev_state_get_format(state, cfe->fe_csi2_channel); + if (fmt) + code = fmt->code; + v4l2_subdev_unlock_state(state); + + return code; +} + static int cfe_video_link_validate(struct cfe_node *node, struct v4l2_subdev *remote_sd, struct v4l2_mbus_framefmt *remote_fmt) @@ -1703,6 +1747,27 @@ static int cfe_video_link_validate(struct cfe_node *node, return -EINVAL; } + if (is_fe_node(node)) { + const struct cfe_fmt *pix = find_format_by_pix(pix_fmt->pixelformat); + + /* + * An LSB-aligned format of the FE source pad's code, at + * the bit depth of the CSI-2 data feeding the FE. + */ + if (pix && cfe_find_lsb16_shift(pix->fourcc) && + pix->code == remote_fmt->code) { + u32 code = cfe_fe_input_code(cfe); + + if (cfe_find_remap_fourcc(code, CFE_REMAP_LSB16) == + pix_fmt->pixelformat) + return 0; + + cfe_err("Format %p4cc doesn't match the CSI-2 input code 0x%04x\n", + &pix_fmt->pixelformat, code); + return -EINVAL; + } + } + if (fmt->fourcc != pix_fmt->pixelformat && fmt->remap[CFE_REMAP_16BIT] != pix_fmt->pixelformat && fmt->remap[CFE_REMAP_COMPRESSED] != pix_fmt->pixelformat) { diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h index 637b63a838c407..1b53c17c9e95d7 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h +++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h @@ -16,6 +16,8 @@ extern bool cfe_debug_verbose; enum cfe_remap_types { CFE_REMAP_16BIT, CFE_REMAP_COMPRESSED, + /* Unpacked, LSB-aligned 16-bit output of the FE */ + CFE_REMAP_LSB16, CFE_NUM_REMAP, }; @@ -39,5 +41,6 @@ const struct cfe_fmt *find_format_by_code(u32 code); const struct cfe_fmt *find_format_by_pix(u32 pixelformat); u32 cfe_find_16bit_code(u32 code); u32 cfe_find_compressed_code(u32 code); +unsigned int cfe_find_lsb16_shift(u32 pixelformat); #endif diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h index d4ecb6be80f82b..26461d0896c132 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h +++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h @@ -114,56 +114,64 @@ static const struct cfe_fmt formats[] = { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .depth = 10, .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR, + V4L2_PIX_FMT_SBGGR10 }, }, { .fourcc = V4L2_PIX_FMT_SGBRG10P, .code = MEDIA_BUS_FMT_SGBRG10_1X10, .depth = 10, .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG, + V4L2_PIX_FMT_SGBRG10 }, }, { .fourcc = V4L2_PIX_FMT_SGRBG10P, .code = MEDIA_BUS_FMT_SGRBG10_1X10, .depth = 10, .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG, + V4L2_PIX_FMT_SGRBG10 }, }, { .fourcc = V4L2_PIX_FMT_SRGGB10P, .code = MEDIA_BUS_FMT_SRGGB10_1X10, .depth = 10, .csi_dt = MIPI_CSI2_DT_RAW10, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB, + V4L2_PIX_FMT_SRGGB10 }, }, { .fourcc = V4L2_PIX_FMT_SBGGR12P, .code = MEDIA_BUS_FMT_SBGGR12_1X12, .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR }, + .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR, + V4L2_PIX_FMT_SBGGR12 }, }, { .fourcc = V4L2_PIX_FMT_SGBRG12P, .code = MEDIA_BUS_FMT_SGBRG12_1X12, .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG }, + .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG, + V4L2_PIX_FMT_SGBRG12 }, }, { .fourcc = V4L2_PIX_FMT_SGRBG12P, .code = MEDIA_BUS_FMT_SGRBG12_1X12, .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG }, + .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG, + V4L2_PIX_FMT_SGRBG12 }, }, { .fourcc = V4L2_PIX_FMT_SRGGB12P, .code = MEDIA_BUS_FMT_SRGGB12_1X12, .depth = 12, .csi_dt = MIPI_CSI2_DT_RAW12, - .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, + .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB, + V4L2_PIX_FMT_SRGGB12 }, }, { .fourcc = V4L2_PIX_FMT_SBGGR14P, @@ -225,6 +233,59 @@ static const struct cfe_fmt formats[] = { .flags = CFE_FORMAT_FLAG_FE_OUT, .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB }, }, + /* + * Unpacked LSB-aligned Bayer FE output: code is the FE source pad code, so + * these come after the 16-bit entries (find_format_by_code() returns + * the first match). + */ + { + .fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, + { + .fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB16_1X16, + .depth = 16, + .flags = CFE_FORMAT_FLAG_FE_OUT, + }, /* PiSP Compressed Mode 1 */ { .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB, diff --git a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c index 627b9e43d2eb47..c260cea929a71b 100644 --- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c +++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c @@ -222,6 +222,7 @@ void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof) static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, unsigned int c, struct v4l2_format const *f) { + unsigned int shift; unsigned int wbytes; wbytes = cfg->ch[c].output.format.width; @@ -236,6 +237,22 @@ static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg, wbytes > f->fmt.pix.bytesperline) return false; + /* + * An LSB-aligned node format needs the FE output to match it: 16 bits per + * sample, uncompressed, shifted right by 16 - bit depth. + */ + shift = cfe_find_lsb16_shift(f->fmt.pix.pixelformat); + if (shift) { + u32 format = cfg->ch[c].output.format.format; + + if ((format & PISP_IMAGE_FORMAT_BPS_MASK) != + PISP_IMAGE_FORMAT_BPS_16 || + (format & PISP_IMAGE_FORMAT_COMPRESSION_MASK) || + (format & PISP_IMAGE_FORMAT_SHIFT_MASK) != + shift * PISP_IMAGE_FORMAT_SHIFT_1) + return false; + } + /* Check for zero-sized crops, which could cause lockup */ if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) && ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||