[PATCH v2 10/11] media: atomisp: allow raw Bayer capture

From: Maurizio Casciano

Date: Thu Aug 27 2026 - 14:20:06 EST


AtomISP currently rejects all raw formats and silently substitutes
YUV420. This prevents userspace camera processing stacks from obtaining
unprocessed sensor frames.

Enumerate only the raw format matching the sensor media-bus code and
reconcile raw format requests with the code selected by the sensor.
Userspace explicitly opts into raw capture by selecting that raw V4L2
pixel format with VIDIOC_S_FMT; no separate module parameter is needed.

Also apply the per-sensor padding discovered by the CSI-2 bridge when
enumerating and negotiating processed frame sizes. Raw formats expose
the full sensor transport frame so ISP2401 can use its copy pipeline.
Existing systems retain the global padding defaults.

Tested on a Lenovo Yoga Book YB1-X91L with raw capture from its OV2740
and OV8858 sensors.

Signed-off-by: Maurizio Casciano <mauriziocasciano7@xxxxxxxxx>
Assisted-by: Codex:gpt-5.6-sol sparse
---
.../staging/media/atomisp/pci/atomisp_cmd.c | 33 +++++++++++++++----
.../media/atomisp/pci/atomisp_internal.h | 3 ++
.../staging/media/atomisp/pci/atomisp_ioctl.c | 22 +++++++++++--
.../staging/media/atomisp/pci/atomisp_v4l2.c | 9 +++++
4 files changed, 57 insertions(+), 10 deletions(-)

diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 6cd500d9fd26..70ff9b55b52e 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -3572,10 +3572,17 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
u32 min_pad_w = ISP2400_MIN_PAD_W;
u32 min_pad_h = ISP2400_MIN_PAD_H;
struct v4l2_mbus_framefmt *sink;
+ u32 input_padding_w = pad_w;
+ u32 input_padding_h = pad_h;
+
+ if (input->padding_override) {
+ input_padding_w = input->padding.width;
+ input_padding_h = input->padding.height;
+ }

if (!input->crop_support) {
- *padding_w = pad_w;
- *padding_h = pad_h;
+ *padding_w = input_padding_w;
+ *padding_h = input_padding_h;
return;
}

@@ -3588,8 +3595,10 @@ void atomisp_get_padding(struct atomisp_device *isp, u32 width, u32 height,
native_rect.height /= 2;
}

- *padding_w = min_t(u32, (native_rect.width - width) & ~1, pad_w);
- *padding_h = min_t(u32, (native_rect.height - height) & ~1, pad_h);
+ *padding_w = min_t(u32, (native_rect.width - width) & ~1,
+ input_padding_w);
+ *padding_h = min_t(u32, (native_rect.height - height) & ~1,
+ input_padding_h);

/* The below minimum padding requirements are for BYT / ISP2400 only */
if (IS_ISP2401)
@@ -3804,8 +3813,7 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
int ret;

fmt = atomisp_get_format_bridge(f->pixelformat);
- /* Currently, raw formats are broken!!! */
- if (!fmt || fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ if (!fmt) {
f->pixelformat = V4L2_PIX_FMT_YUV420;

fmt = atomisp_get_format_bridge(f->pixelformat);
@@ -3827,7 +3835,13 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
* resolution + padding. Add padding here and remove it again after
* the set_fmt call, like atomisp_set_fmt_to_snr() does.
*/
- atomisp_get_padding(isp, f->width, f->height, &padding_w, &padding_h);
+ if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ padding_w = 0;
+ padding_h = 0;
+ } else {
+ atomisp_get_padding(isp, f->width, f->height,
+ &padding_w, &padding_h);
+ }
v4l2_fill_mbus_format(&ffmt, f, fmt->mbus_code);
ffmt.width += padding_w;
ffmt.height += padding_h;
@@ -3846,6 +3860,11 @@ int atomisp_try_fmt(struct atomisp_device *isp, struct v4l2_pix_format *f,
ffmt.code);
return -EINVAL;
}
+ if (fmt->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ fmt->mbus_code != snr_fmt->mbus_code) {
+ fmt = snr_fmt;
+ f->pixelformat = fmt->pixelformat;
+ }

f->width = ffmt.width - padding_w;
f->height = ffmt.height - padding_h;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp_internal.h
index 5a69580b8251..66b12461bd40 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_internal.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_internal.h
@@ -98,6 +98,7 @@
#define ATOMISP_CSS_SUPPORT_YUVPP 1

#define ATOMISP_CSS_OUTPUT_SECOND_INDEX 1
+
#define ATOMISP_CSS_OUTPUT_DEFAULT_INDEX 0

/* ISP2401 */
@@ -116,6 +117,8 @@
struct atomisp_input_subdev {
enum atomisp_camera_port port;
u32 code; /* MEDIA_BUS_FMT_* */
+ struct v4l2_area padding;
+ bool padding_override;
bool binning_support;
bool crop_support;
bool sensor_on;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index 50366bf10f32..cf3e68983e0d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -536,11 +536,19 @@ static int atomisp_enum_framesizes(struct file *file, void *priv,
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.code = input->code,
};
+ const struct atomisp_format_bridge *format;
struct v4l2_subdev_state *act_sd_state;
+ struct v4l2_area padding = {
+ .width = pad_w,
+ .height = pad_h,
+ };
int ret;

if (!input->sensor)
return -EINVAL;
+ format = atomisp_get_format_bridge(fsize->pixel_format);
+ if (!format)
+ return -EINVAL;

if (input->crop_support)
return atomisp_enum_framesizes_crop(isp, fsize);
@@ -553,9 +561,16 @@ static int atomisp_enum_framesizes(struct file *file, void *priv,
if (ret)
return ret;

+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW) {
+ padding.width = 0;
+ padding.height = 0;
+ } else if (input->padding_override) {
+ padding = input->padding;
+ }
+
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.max_width - pad_w;
- fsize->discrete.height = fse.max_height - pad_h;
+ fsize->discrete.width = fse.max_width - padding.width;
+ fsize->discrete.height = fse.max_height - padding.height;

return 0;
}
@@ -629,7 +644,8 @@ static int atomisp_enum_fmt_cap(struct file *file, void *fh,
*
* FIXME: fix the pipeline to allow sensor format too.
*/
- if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW)
+ if (format->sh_fmt == IA_CSS_FRAME_FORMAT_RAW &&
+ format->mbus_code != code.code)
continue;

/* Found a match. Now let's pick f->index'th one. */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 812230397409..7b4c1c6c1295 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -25,6 +25,7 @@

#include "atomisp_cmd.h"
#include "atomisp_common.h"
+#include "atomisp_csi2.h"
#include "atomisp_fops.h"
#include "atomisp_ioctl.h"
#include "atomisp_internal.h"
@@ -889,6 +890,14 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
struct v4l2_subdev_state *try_sd_state, *act_sd_state;
int i, err;

+ input->padding_override =
+ atomisp_csi2_get_sensor_padding(input->sensor->dev,
+ &input->padding);
+ if (input->padding_override)
+ dev_info(input->sensor->dev,
+ "using ISP input padding %ux%u\n",
+ input->padding.width, input->padding.height);
+
/*
* FIXME: Drivers are not supposed to use __v4l2_subdev_state_alloc()
* but atomisp needs this for try_fmt on its /dev/video# node since
--
2.53.0