On Tue Aug 19 16:47:42 2025 +0200, Benjamin Mugnier wrote:
> The vd65g4 is the bayer version of the vd55g1.
> As opposed to the vd55g1, the vd65g4 does not need any patch. Check the
> sensor id at probe and choose to patch or not on power_on() according to
> it.
> It's bayer matrix's order is RGGB. This commit handles hflip and vflip
> by switching the bayer pattern accordingly.
>
> Signed-off-by: Benjamin Mugnier <[email protected]>
> Signed-off-by: Sakari Ailus <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>
Patch committed.
Thanks,
Hans Verkuil
drivers/media/i2c/vd55g1.c | 234 ++++++++++++++++++++++++++++++++-------------
1 file changed, 167 insertions(+), 67 deletions(-)
---
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index f09d6bf32641..78d18c028154 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -29,9 +29,11 @@
/* Register Map */
#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000)
-#define VD55G1_MODEL_ID 0x53354731
+#define VD55G1_MODEL_ID_VD55G1 0x53354731 /* Mono */
+#define VD55G1_MODEL_ID_VD65G4 0x53354733 /* RGB */
#define VD55G1_REG_REVISION CCI_REG16_LE(0x0004)
#define VD55G1_REVISION_CCB 0x2020
+#define VD55G1_REVISION_BAYER 0x3030
#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012)
#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c)
@@ -39,7 +41,8 @@
#define VD55G1_SYSTEM_FSM_SW_STBY 0x02
#define VD55G1_SYSTEM_FSM_STREAMING 0x03
#define VD55G1_REG_BOOT CCI_REG8(0x0200)
-#define VD55G1_BOOT_PATCH_SETUP 2
+#define VD55G1_BOOT_BOOT 1
+#define VD55G1_BOOT_PATCH_AND_BOOT 2
#define VD55G1_REG_STBY CCI_REG8(0x0201)
#define VD55G1_STBY_START_STREAM 1
#define VD55G1_REG_STREAMING CCI_REG8(0x0202)
@@ -132,7 +135,10 @@
#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
-static const u8 patch_array[] = {
+#define VD55G1_MODEL_ID_NAME(id) \
+ ((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4")
+
+static const u8 vd55g1_patch_array[] = {
0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -466,22 +472,24 @@ struct vd55g1_mode {
u32 height;
};
-struct vd55g1_fmt_desc {
- u32 code;
- u8 bpp;
- u8 data_type;
+static const u32 vd55g1_mbus_formats_mono[] = {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
};
-static const struct vd55g1_fmt_desc vd55g1_mbus_codes[] = {
+/* Format order is : no flip, hflip, vflip, both */
+static const u32 vd55g1_mbus_formats_bayer[][4] = {
{
- .code = MEDIA_BUS_FMT_Y8_1X8,
- .bpp = 8,
- .data_type = MIPI_CSI2_DT_RAW8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
},
{
- .code = MEDIA_BUS_FMT_Y10_1X10,
- .bpp = 10,
- .data_type = MIPI_CSI2_DT_RAW10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
},
};
@@ -524,6 +532,7 @@ struct vd55g1_vblank_limits {
struct vd55g1 {
struct device *dev;
+ unsigned int id;
struct v4l2_subdev sd;
struct media_pad pad;
struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)];
@@ -572,27 +581,78 @@ static inline struct vd55g1 *ctrl_to_vd55g1(struct
v4l2_ctrl *ctrl)
return to_vd55g1(sd);
}
-static const struct vd55g1_fmt_desc *vd55g1_get_fmt_desc(struct vd55g1 *sensor,
- u32 code)
+static unsigned int vd55g1_get_fmt_bpp(u32 code)
{
- unsigned int i;
+ switch (code) {
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ default:
+ return 8;
+
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return 10;
+ }
+}
+
+static unsigned int vd55g1_get_fmt_data_type(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ default:
+ return MIPI_CSI2_DT_RAW8;
+
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return MIPI_CSI2_DT_RAW10;
+ }
+}
+
+static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code)
+{
+ unsigned int i, j;
- for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_codes); i++) {
- if (vd55g1_mbus_codes[i].code == code)
- return &vd55g1_mbus_codes[i];
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1)
+ return code;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) {
+ for (j = 0; j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]); j++) {
+ if (vd55g1_mbus_formats_bayer[i][j] == code)
+ goto adapt_bayer_pattern;
+ }
}
+ dev_warn(sensor->dev, "Unsupported mbus format\n");
- /* Should never happen */
- dev_warn(sensor->dev, "Unsupported code %d. default to 8 bpp\n", code);
+ return code;
+
+adapt_bayer_pattern:
+ j = 0;
+ /* In first init_state() call, controls might not be initialized yet */
+ if (sensor->hflip_ctrl && sensor->vflip_ctrl) {
+ j = (sensor->hflip_ctrl->val ? 1 : 0) +
+ (sensor->vflip_ctrl->val ? 2 : 0);
+ }
- return &vd55g1_mbus_codes[0];
+ return vd55g1_mbus_formats_bayer[i][j];
}
static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor,
struct v4l2_mbus_framefmt *format)
{
- return sensor->mipi_rate /
- vd55g1_get_fmt_desc(sensor, format->code)->bpp;
+ return sensor->mipi_rate / vd55g1_get_fmt_bpp(format->code);
}
static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor,
@@ -605,7 +665,7 @@ static unsigned int vd55g1_get_hblank_min(struct vd55g1
*sensor,
/* MIPI required time */
mipi_req_line_time = (crop->width *
- vd55g1_get_fmt_desc(sensor, format->code)->bpp +
+ vd55g1_get_fmt_bpp(format->code) +
VD55G1_MIPI_MARGIN) /
(sensor->mipi_rate / MEGA);
mipi_req_line_length = mipi_req_line_time * sensor->pixel_clock /
@@ -887,7 +947,7 @@ static void vd55g1_update_pad_fmt(struct vd55g1 *sensor,
const struct vd55g1_mode *mode, u32 code,
struct v4l2_mbus_framefmt *fmt)
{
- fmt->code = code;
+ fmt->code = vd55g1_get_fmt_code(sensor, code);
fmt->width = mode->width;
fmt->height = mode->height;
fmt->colorspace = V4L2_COLORSPACE_RAW;
@@ -951,10 +1011,9 @@ static int vd55g1_set_framefmt(struct vd55g1 *sensor,
int ret = 0;
vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL,
- vd55g1_get_fmt_desc(sensor, format->code)->bpp, &ret);
+ vd55g1_get_fmt_bpp(format->code), &ret);
vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL,
- vd55g1_get_fmt_desc(sensor, format->code)->data_type,
- &ret);
+ vd55g1_get_fmt_data_type(format->code), &ret);
switch (crop->width / format->width) {
case 1:
@@ -1114,26 +1173,45 @@ static int vd55g1_patch(struct vd55g1 *sensor)
u64 patch;
int ret = 0;
- vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
- sizeof(patch_array), patch_array, &ret);
- vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_PATCH_SETUP, &ret);
- vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
- if (ret) {
- dev_err(sensor->dev, "Failed to apply patch\n");
- return ret;
- }
+ /* vd55g1 needs a patch while vd65g4 does not */
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
+ vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
+ sizeof(vd55g1_patch_array),
+ vd55g1_patch_array, &ret);
+ vd55g1_write(sensor, VD55G1_REG_BOOT,
+ VD55G1_BOOT_PATCH_AND_BOOT, &ret);
+ vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
+ if (ret) {
+ dev_err(sensor->dev, "Failed to apply patch\n");
+ return ret;
+ }
- vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret);
- if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) +
- VD55G1_FWPATCH_REVISION_MINOR) {
- dev_err(sensor->dev, "Bad patch version expected %d.%d got
%d.%d\n",
- VD55G1_FWPATCH_REVISION_MAJOR,
- VD55G1_FWPATCH_REVISION_MINOR,
+ vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret);
+ if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) +
+ VD55G1_FWPATCH_REVISION_MINOR) {
+ dev_err(sensor->dev, "Bad patch version expected %d.%d
got %d.%d\n",
+ VD55G1_FWPATCH_REVISION_MAJOR,
+ VD55G1_FWPATCH_REVISION_MINOR,
+ (u8)(patch >> 8), (u8)(patch & 0xff));
+ return -ENODEV;
+ }
+ dev_dbg(sensor->dev, "patch %d.%d applied\n",
(u8)(patch >> 8), (u8)(patch & 0xff));
- return -ENODEV;
+
+ } else {
+ vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_BOOT, &ret);
+ vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
+ if (ret) {
+ dev_err(sensor->dev, "Failed to boot\n");
+ return ret;
+ }
+ }
+
+ ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL);
+ if (ret) {
+ dev_err(sensor->dev, "Sensor waiting after boot failed\n");
+ return ret;
}
- dev_dbg(sensor->dev, "patch %d.%d applied\n",
- (u8)(patch >> 8), (u8)(patch & 0xff));
return 0;
}
@@ -1165,10 +1243,19 @@ static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->index >= ARRAY_SIZE(vd55g1_mbus_codes))
- return -EINVAL;
+ struct vd55g1 *sensor = to_vd55g1(sd);
+ u32 base_code;
- code->code = vd55g1_mbus_codes[code->index].code;
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
+ if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono))
+ return -EINVAL;
+ base_code = vd55g1_mbus_formats_mono[code->index];
+ } else {
+ if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_bayer))
+ return -EINVAL;
+ base_code = vd55g1_mbus_formats_bayer[code->index][0];
+ }
+ code->code = vd55g1_get_fmt_code(sensor, base_code);
return 0;
}
@@ -1275,7 +1362,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
return ret;
vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
- vd55g1_mbus_codes[VD55G1_MBUS_CODE_DEF].code,
+ vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF),
&fmt.format);
return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
@@ -1285,9 +1372,16 @@ static int vd55g1_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct vd55g1 *sensor = to_vd55g1(sd);
+ u32 code;
+
if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes))
return -EINVAL;
+ code = vd55g1_get_fmt_code(sensor, fse->code);
+ if (fse->code != code)
+ return -EINVAL;
+
fse->min_width = vd55g1_supported_modes[fse->index].width;
fse->max_width = fse->min_width;
fse->min_height = vd55g1_supported_modes[fse->index].height;
@@ -1463,8 +1557,12 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor)
/* Flip cluster */
sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
0, 1, 1, 0);
+ if (sensor->hflip_ctrl)
+ sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
0, 1, 1, 0);
+ if (sensor->vflip_ctrl)
+ sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
v4l2_ctrl_cluster(2, &sensor->hflip_ctrl);
/* Exposition cluster */
@@ -1548,26 +1646,34 @@ unlock_state:
static int vd55g1_detect(struct vd55g1 *sensor)
{
- u64 device_rev;
- u64 id;
+ unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev);
+ u64 rev, id;
int ret;
ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL);
if (ret)
return ret;
- if (id != VD55G1_MODEL_ID) {
- dev_warn(sensor->dev, "Unsupported sensor id %x\n", (u32)id);
+ if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) {
+ dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n",
+ (u32)id);
+ return -ENODEV;
+ }
+ if (id != dt_id) {
+ dev_err(sensor->dev, "Probed sensor %s and device tree
definition (%s) mismatch",
+ VD55G1_MODEL_ID_NAME(id), VD55G1_MODEL_ID_NAME(dt_id));
return -ENODEV;
}
+ sensor->id = id;
- ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &device_rev, NULL);
+ ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL);
if (ret)
return ret;
- if (device_rev != VD55G1_REVISION_CCB) {
- dev_err(sensor->dev, "Unsupported sensor revision (0x%x)\n",
- (u16)device_rev);
+ if ((id == VD55G1_MODEL_ID_VD55G1 && rev != VD55G1_REVISION_CCB) &&
+ (id == VD55G1_MODEL_ID_VD65G4 && rev != VD55G1_REVISION_BAYER)) {
+ dev_err(sensor->dev, "Unsupported sensor revision 0x%x for
sensor %s\n",
+ (u16)rev, VD55G1_MODEL_ID_NAME(id));
return -ENODEV;
}
@@ -1616,13 +1722,6 @@ static int vd55g1_power_on(struct device *dev)
goto disable_clock;
}
- ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL);
- if (ret) {
- dev_err(dev, "Sensor waiting after patch failed %d\n",
- ret);
- goto disable_clock;
- }
-
return 0;
disable_clock:
@@ -1934,7 +2033,8 @@ static void vd55g1_remove(struct i2c_client *client)
}
static const struct of_device_id vd55g1_dt_ids[] = {
- { .compatible = "st,vd55g1" },
+ { .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 },
+ { .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vd55g1_dt_ids);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]