From 01c76fc995cdb1e701fa721c5b373777f0469b37 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Aug 2024 16:24:08 +0200 Subject: [PATCH 01/16] media: v4l2-core: add v4l2_debugfs_if_alloc/free() Add new helpers to export received or transmitted HDMI InfoFrames to debugfs. This complements similar code in drm where the transmitted HDMI infoframes are exported to debugfs. The same names have been used as in drm, so this is consistent. The exported infoframes can be parsed with the edid-decode utility. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dv-timings.c | 67 +++++++++++++++++++++++ include/media/v4l2-dv-timings.h | 48 ++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-dv-timings.c b/drivers/media/v4l2-core/v4l2-dv-timings.c index 942d0005c55e..39b5fc1807c4 100644 --- a/drivers/media/v4l2-core/v4l2-dv-timings.c +++ b/drivers/media/v4l2-core/v4l2-dv-timings.c @@ -1154,3 +1154,70 @@ int v4l2_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) return 0; } EXPORT_SYMBOL_GPL(v4l2_phys_addr_validate); + +#ifdef CONFIG_DEBUG_FS + +#define DEBUGFS_FOPS(type, flag) \ +static ssize_t \ +infoframe_read_##type(struct file *filp, \ + char __user *ubuf, size_t count, loff_t *ppos) \ +{ \ + struct v4l2_debugfs_if *infoframes = filp->private_data; \ + \ + return infoframes->if_read((flag), infoframes->priv, filp, \ + ubuf, count, ppos); \ +} \ + \ +static const struct file_operations infoframe_##type##_fops = { \ + .owner = THIS_MODULE, \ + .open = simple_open, \ + .read = infoframe_read_##type, \ +} + +DEBUGFS_FOPS(avi, V4L2_DEBUGFS_IF_AVI); +DEBUGFS_FOPS(audio, V4L2_DEBUGFS_IF_AUDIO); +DEBUGFS_FOPS(spd, V4L2_DEBUGFS_IF_SPD); +DEBUGFS_FOPS(hdmi, V4L2_DEBUGFS_IF_HDMI); + +struct v4l2_debugfs_if *v4l2_debugfs_if_alloc(struct dentry *root, u32 if_types, + void *priv, + v4l2_debugfs_if_read_t if_read) +{ + struct v4l2_debugfs_if *infoframes; + + if (IS_ERR_OR_NULL(root) || !if_types || !if_read) + return NULL; + + infoframes = kzalloc(sizeof(*infoframes), GFP_KERNEL); + if (!infoframes) + return NULL; + + infoframes->if_dir = debugfs_create_dir("infoframes", root); + infoframes->priv = priv; + infoframes->if_read = if_read; + if (if_types & V4L2_DEBUGFS_IF_AVI) + debugfs_create_file("avi", 0400, infoframes->if_dir, + infoframes, &infoframe_avi_fops); + if (if_types & V4L2_DEBUGFS_IF_AUDIO) + debugfs_create_file("audio", 0400, infoframes->if_dir, + infoframes, &infoframe_audio_fops); + if (if_types & V4L2_DEBUGFS_IF_SPD) + debugfs_create_file("spd", 0400, infoframes->if_dir, + infoframes, &infoframe_spd_fops); + if (if_types & V4L2_DEBUGFS_IF_HDMI) + debugfs_create_file("hdmi", 0400, infoframes->if_dir, + infoframes, &infoframe_hdmi_fops); + return infoframes; +} +EXPORT_SYMBOL_GPL(v4l2_debugfs_if_alloc); + +void v4l2_debugfs_if_free(struct v4l2_debugfs_if *infoframes) +{ + if (infoframes) { + debugfs_remove_recursive(infoframes->if_dir); + kfree(infoframes); + } +} +EXPORT_SYMBOL_GPL(v4l2_debugfs_if_free); + +#endif diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h index 8fa963326bf6..13830411bd6c 100644 --- a/include/media/v4l2-dv-timings.h +++ b/include/media/v4l2-dv-timings.h @@ -8,6 +8,7 @@ #ifndef __V4L2_DV_TIMINGS_H #define __V4L2_DV_TIMINGS_H +#include #include /** @@ -251,4 +252,51 @@ void v4l2_set_edid_phys_addr(u8 *edid, unsigned int size, u16 phys_addr); u16 v4l2_phys_addr_for_input(u16 phys_addr, u8 input); int v4l2_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port); +/* Add support for exporting InfoFrames to debugfs */ + +/* + * HDMI InfoFrames start with a 3 byte header, then a checksum, + * followed by the actual IF payload. + * + * The payload length is limited to 30 bytes according to the HDMI spec, + * but since the length is encoded in 5 bits, it can be 31 bytes theoretically. + * So set the max length as 31 + 3 (header) + 1 (checksum) = 35. + */ +#define V4L2_DEBUGFS_IF_MAX_LEN (35) + +#define V4L2_DEBUGFS_IF_AVI BIT(0) +#define V4L2_DEBUGFS_IF_AUDIO BIT(1) +#define V4L2_DEBUGFS_IF_SPD BIT(2) +#define V4L2_DEBUGFS_IF_HDMI BIT(3) + +typedef ssize_t (*v4l2_debugfs_if_read_t)(u32 type, void *priv, + struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos); + +struct v4l2_debugfs_if { + struct dentry *if_dir; + void *priv; + + v4l2_debugfs_if_read_t if_read; +}; + +#ifdef CONFIG_DEBUG_FS +struct v4l2_debugfs_if *v4l2_debugfs_if_alloc(struct dentry *root, u32 if_types, + void *priv, + v4l2_debugfs_if_read_t if_read); +void v4l2_debugfs_if_free(struct v4l2_debugfs_if *infoframes); +#else +static inline +struct v4l2_debugfs_if *v4l2_debugfs_if_alloc(struct dentry *root, u32 if_types, + void *priv, + v4l2_debugfs_if_read_t if_read) +{ + return NULL; +} + +static inline void v4l2_debugfs_if_free(struct v4l2_debugfs_if *infoframes) +{ +} +#endif + #endif -- 2.51.0 From c6a38df9162c8bf002b9a41e8ce00e178c3ca869 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Aug 2024 16:24:09 +0200 Subject: [PATCH 02/16] media: i2c: adv7511-v4l2: export InfoFrames to debugfs Export InfoFrames to debugfs. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7511-v4l2.c | 91 ++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c index e9406d552699..4036972af3a6 100644 --- a/drivers/media/i2c/adv7511-v4l2.c +++ b/drivers/media/i2c/adv7511-v4l2.c @@ -116,6 +116,9 @@ struct adv7511_state { unsigned edid_detect_counter; struct workqueue_struct *work_queue; struct delayed_work edid_handler; /* work entry */ + + struct dentry *debugfs_dir; + struct v4l2_debugfs_if *infoframes; }; static void adv7511_check_monitor_present_status(struct v4l2_subdev *sd); @@ -483,27 +486,25 @@ static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) return 256 - csum; } -static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_infoframe *cri) +static int read_infoframe(struct v4l2_subdev *sd, + const struct adv7511_cfg_read_infoframe *cri, + u8 *buffer) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct device *dev = &client->dev; - union hdmi_infoframe frame; - u8 buffer[32]; u8 len; int i; if (!(adv7511_rd(sd, cri->present_reg) & cri->present_mask)) { v4l2_info(sd, "%s infoframe not transmitted\n", cri->desc); - return; + return 0; } memcpy(buffer, cri->header, sizeof(cri->header)); len = buffer[2]; - if (len + 4 > sizeof(buffer)) { + if (len + 4 > V4L2_DEBUGFS_IF_MAX_LEN) { v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len); - return; + return 0; } if (cri->payload_addr >= 0x100) { @@ -516,21 +517,38 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_ buffer[3] = 0; buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); - if (hdmi_infoframe_unpack(&frame, buffer, len + 4) < 0) { - v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); + return len + 4; +} + +static void log_infoframe(struct v4l2_subdev *sd, + const struct adv7511_cfg_read_infoframe *cri) +{ + union hdmi_infoframe frame; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + int len = read_infoframe(sd, cri, buffer); + + if (len <= 0) + return; + + if (hdmi_infoframe_unpack(&frame, buffer, len) < 0) { + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", + __func__, cri->desc); return; } hdmi_infoframe_log(KERN_INFO, dev, &frame); } +static const struct adv7511_cfg_read_infoframe cri[] = { + { "AVI", 0x44, 0x10, { 0x82, 2, 13 }, 0x55 }, + { "Audio", 0x44, 0x08, { 0x84, 1, 10 }, 0x73 }, + { "SDP", 0x40, 0x40, { 0x83, 1, 25 }, 0x103 }, +}; + static void adv7511_log_infoframes(struct v4l2_subdev *sd) { - static const struct adv7511_cfg_read_infoframe cri[] = { - { "AVI", 0x44, 0x10, { 0x82, 2, 13 }, 0x55 }, - { "Audio", 0x44, 0x08, { 0x84, 1, 10 }, 0x73 }, - { "SDP", 0x40, 0x40, { 0x83, 1, 25 }, 0x103 }, - }; int i; for (i = 0; i < ARRAY_SIZE(cri); i++) @@ -1693,6 +1711,34 @@ static bool adv7511_check_edid_status(struct v4l2_subdev *sd) return false; } +static ssize_t +adv7511_debugfs_if_read(u32 type, void *priv, + struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) +{ + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + struct v4l2_subdev *sd = priv; + int index; + int len; + + switch (type) { + case V4L2_DEBUGFS_IF_AVI: + index = 0; + break; + case V4L2_DEBUGFS_IF_AUDIO: + index = 1; + break; + case V4L2_DEBUGFS_IF_SPD: + index = 2; + break; + default: + return 0; + } + len = read_infoframe(sd, &cri[index], buf); + if (len > 0) + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + return len < 0 ? 0 : len; +} + static int adv7511_registered(struct v4l2_subdev *sd) { struct adv7511_state *state = get_adv7511_state(sd); @@ -1700,9 +1746,16 @@ static int adv7511_registered(struct v4l2_subdev *sd) int err; err = cec_register_adapter(state->cec_adap, &client->dev); - if (err) + if (err) { cec_delete_adapter(state->cec_adap); - return err; + return err; + } + + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); + state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, + V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO | + V4L2_DEBUGFS_IF_SPD, sd, adv7511_debugfs_if_read); + return 0; } static void adv7511_unregistered(struct v4l2_subdev *sd) @@ -1710,6 +1763,10 @@ static void adv7511_unregistered(struct v4l2_subdev *sd) struct adv7511_state *state = get_adv7511_state(sd); cec_unregister_adapter(state->cec_adap); + v4l2_debugfs_if_free(state->infoframes); + state->infoframes = NULL; + debugfs_remove_recursive(state->debugfs_dir); + state->debugfs_dir = NULL; } static const struct v4l2_subdev_internal_ops adv7511_int_ops = { -- 2.51.0 From 60bdc2ffe53db44c3bdc8c2979ae46973e87cf8f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Aug 2024 16:24:10 +0200 Subject: [PATCH 03/16] media: i2c: adv7604: export InfoFrames to debugfs Export InfoFrames to debugfs. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7604.c | 90 ++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 48230d5109f0..3184a2fa1532 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -193,6 +193,9 @@ struct adv76xx_state { struct delayed_work delayed_work_enable_hotplug; bool restart_stdi_once; + struct dentry *debugfs_dir; + struct v4l2_debugfs_if *infoframes; + /* CEC */ struct cec_adapter *cec_adap; u8 cec_addr[ADV76XX_MAX_ADDRS]; @@ -2458,10 +2461,9 @@ static const struct adv76xx_cfg_read_infoframe adv76xx_cri[] = { { "Vendor", 0x10, 0xec, 0x54 } }; -static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, - union hdmi_infoframe *frame) +static int adv76xx_read_infoframe_buf(struct v4l2_subdev *sd, int index, + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN]) { - uint8_t buffer[32]; u8 len; int i; @@ -2472,27 +2474,20 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, } for (i = 0; i < 3; i++) - buffer[i] = infoframe_read(sd, - adv76xx_cri[index].head_addr + i); + buf[i] = infoframe_read(sd, adv76xx_cri[index].head_addr + i); - len = buffer[2] + 1; + len = buf[2] + 1; - if (len + 3 > sizeof(buffer)) { + if (len + 3 > V4L2_DEBUGFS_IF_MAX_LEN) { v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, adv76xx_cri[index].desc, len); return -ENOENT; } for (i = 0; i < len; i++) - buffer[i + 3] = infoframe_read(sd, - adv76xx_cri[index].payload_addr + i); - - if (hdmi_infoframe_unpack(frame, buffer, len + 3) < 0) { - v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, - adv76xx_cri[index].desc); - return -ENOENT; - } - return 0; + buf[i + 3] = infoframe_read(sd, + adv76xx_cri[index].payload_addr + i); + return len + 3; } static void adv76xx_log_infoframes(struct v4l2_subdev *sd) @@ -2505,10 +2500,19 @@ static void adv76xx_log_infoframes(struct v4l2_subdev *sd) } for (i = 0; i < ARRAY_SIZE(adv76xx_cri); i++) { - union hdmi_infoframe frame; struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + union hdmi_infoframe frame; + int len; - if (!adv76xx_read_infoframe(sd, i, &frame)) + len = adv76xx_read_infoframe_buf(sd, i, buffer); + if (len < 0) + continue; + + if (hdmi_infoframe_unpack(&frame, buffer, len) < 0) + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", + __func__, adv76xx_cri[i].desc); + else hdmi_infoframe_log(KERN_INFO, &client->dev, &frame); } } @@ -2686,6 +2690,41 @@ static int adv76xx_subscribe_event(struct v4l2_subdev *sd, } } +static ssize_t +adv76xx_debugfs_if_read(u32 type, void *priv, struct file *filp, + char __user *ubuf, size_t count, loff_t *ppos) +{ + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + struct v4l2_subdev *sd = priv; + int index; + int len; + + if (!is_hdmi(sd)) + return 0; + + switch (type) { + case V4L2_DEBUGFS_IF_AVI: + index = 0; + break; + case V4L2_DEBUGFS_IF_AUDIO: + index = 1; + break; + case V4L2_DEBUGFS_IF_SPD: + index = 2; + break; + case V4L2_DEBUGFS_IF_HDMI: + index = 3; + break; + default: + return 0; + } + + len = adv76xx_read_infoframe_buf(sd, index, buf); + if (len > 0) + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + return len < 0 ? 0 : len; +} + static int adv76xx_registered(struct v4l2_subdev *sd) { struct adv76xx_state *state = to_state(sd); @@ -2693,9 +2732,16 @@ static int adv76xx_registered(struct v4l2_subdev *sd) int err; err = cec_register_adapter(state->cec_adap, &client->dev); - if (err) + if (err) { cec_delete_adapter(state->cec_adap); - return err; + return err; + } + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); + state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, + V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO | + V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI, sd, + adv76xx_debugfs_if_read); + return 0; } static void adv76xx_unregistered(struct v4l2_subdev *sd) @@ -2703,6 +2749,10 @@ static void adv76xx_unregistered(struct v4l2_subdev *sd) struct adv76xx_state *state = to_state(sd); cec_unregister_adapter(state->cec_adap); + v4l2_debugfs_if_free(state->infoframes); + state->infoframes = NULL; + debugfs_remove_recursive(state->debugfs_dir); + state->debugfs_dir = NULL; } /* ----------------------------------------------------------------------- */ -- 2.51.0 From 42765902bd4c8c47effb88cbf78a5d2a107177d5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Aug 2024 16:24:11 +0200 Subject: [PATCH 04/16] media: i2c: adv7842: export InfoFrames to debugfs Export InfoFrames to debugfs. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/adv7842.c | 120 ++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 32 deletions(-) diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 014fc913225c..e445699da85b 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -114,6 +114,9 @@ struct adv7842_state { bool restart_stdi_once; bool hdmi_port_a; + struct dentry *debugfs_dir; + struct v4l2_debugfs_if *infoframes; + /* i2c clients */ struct i2c_client *i2c_sdp_io; struct i2c_client *i2c_sdp; @@ -2565,58 +2568,65 @@ struct adv7842_cfg_read_infoframe { u8 payload_addr; }; -static void log_infoframe(struct v4l2_subdev *sd, const struct adv7842_cfg_read_infoframe *cri) +static const struct adv7842_cfg_read_infoframe adv7842_cri[] = { + { "AVI", 0x01, 0xe0, 0x00 }, + { "Audio", 0x02, 0xe3, 0x1c }, + { "SDP", 0x04, 0xe6, 0x2a }, + { "Vendor", 0x10, 0xec, 0x54 } +}; + +static int adv7842_read_infoframe_buf(struct v4l2_subdev *sd, int index, + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN]) { - int i; - u8 buffer[32]; - union hdmi_infoframe frame; - u8 len; - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct device *dev = &client->dev; + const struct adv7842_cfg_read_infoframe *cri = &adv7842_cri[index]; + int len, i; if (!(io_read(sd, 0x60) & cri->present_mask)) { - v4l2_info(sd, "%s infoframe not received\n", cri->desc); - return; + v4l2_dbg(1, debug, sd, + "%s infoframe not received\n", cri->desc); + return -ENOENT; } for (i = 0; i < 3; i++) - buffer[i] = infoframe_read(sd, cri->head_addr + i); + buf[i] = infoframe_read(sd, cri->head_addr + i); - len = buffer[2] + 1; + len = buf[2] + 1; - if (len + 3 > sizeof(buffer)) { - v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len); - return; + if (len + 3 > V4L2_DEBUGFS_IF_MAX_LEN) { + v4l2_err(sd, "%s: invalid %s infoframe length %d\n", + __func__, cri->desc, len); + return -ENOENT; } for (i = 0; i < len; i++) - buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - - if (hdmi_infoframe_unpack(&frame, buffer, len + 3) < 0) { - v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); - return; - } - - hdmi_infoframe_log(KERN_INFO, dev, &frame); + buf[i + 3] = infoframe_read(sd, cri->payload_addr + i); + return len + 3; } static void adv7842_log_infoframes(struct v4l2_subdev *sd) { - int i; - static const struct adv7842_cfg_read_infoframe cri[] = { - { "AVI", 0x01, 0xe0, 0x00 }, - { "Audio", 0x02, 0xe3, 0x1c }, - { "SDP", 0x04, 0xe6, 0x2a }, - { "Vendor", 0x10, 0xec, 0x54 } - }; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; + union hdmi_infoframe frame; + u8 buffer[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + int len, i; if (!(hdmi_read(sd, 0x05) & 0x80)) { v4l2_info(sd, "receive DVI-D signal, no infoframes\n"); return; } - for (i = 0; i < ARRAY_SIZE(cri); i++) - log_infoframe(sd, &cri[i]); + for (i = 0; i < ARRAY_SIZE(adv7842_cri); i++) { + len = adv7842_read_infoframe_buf(sd, i, buffer); + if (len < 0) + continue; + + if (hdmi_infoframe_unpack(&frame, buffer, len) < 0) + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", + __func__, adv7842_cri[i].desc); + else + hdmi_infoframe_log(KERN_INFO, dev, &frame); + } } #if 0 @@ -3263,6 +3273,41 @@ static int adv7842_subscribe_event(struct v4l2_subdev *sd, } } +static ssize_t +adv7842_debugfs_if_read(u32 type, void *priv, struct file *filp, + char __user *ubuf, size_t count, loff_t *ppos) +{ + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + struct v4l2_subdev *sd = priv; + int index; + int len; + + if (!is_hdmi(sd)) + return 0; + + switch (type) { + case V4L2_DEBUGFS_IF_AVI: + index = 0; + break; + case V4L2_DEBUGFS_IF_AUDIO: + index = 1; + break; + case V4L2_DEBUGFS_IF_SPD: + index = 2; + break; + case V4L2_DEBUGFS_IF_HDMI: + index = 3; + break; + default: + return 0; + } + + len = adv7842_read_infoframe_buf(sd, index, buf); + if (len > 0) + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + return len < 0 ? 0 : len; +} + static int adv7842_registered(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); @@ -3270,8 +3315,15 @@ static int adv7842_registered(struct v4l2_subdev *sd) int err; err = cec_register_adapter(state->cec_adap, &client->dev); - if (err) + if (err) { cec_delete_adapter(state->cec_adap); + } else { + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); + state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, + V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO | + V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI, sd, + adv7842_debugfs_if_read); + } return err; } @@ -3280,6 +3332,10 @@ static void adv7842_unregistered(struct v4l2_subdev *sd) struct adv7842_state *state = to_state(sd); cec_unregister_adapter(state->cec_adap); + v4l2_debugfs_if_free(state->infoframes); + state->infoframes = NULL; + debugfs_remove_recursive(state->debugfs_dir); + state->debugfs_dir = NULL; } /* ----------------------------------------------------------------------- */ -- 2.51.0 From 23788ceb87818393dfaae7e8ba20d6200f9e8678 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 28 Aug 2024 16:24:12 +0200 Subject: [PATCH 05/16] media: i2c: tc358743: export InfoFrames to debugfs Export InfoFrames to debugfs. Signed-off-by: Hans Verkuil Tested-by: Dave Stevenson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tc358743.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 65d58ddf0287..fd49bf824051 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -87,6 +87,10 @@ struct tc358743_state { struct timer_list timer; struct work_struct work_i2c_poll; + /* debugfs */ + struct dentry *debugfs_dir; + struct v4l2_debugfs_if *infoframes; + /* edid */ u8 edid_blocks_written; @@ -430,12 +434,35 @@ static void tc358743_erase_bksv(struct v4l2_subdev *sd) /* --------------- AVI infoframe --------------- */ +static ssize_t +tc358743_debugfs_if_read(u32 type, void *priv, struct file *filp, + char __user *ubuf, size_t count, loff_t *ppos) +{ + u8 buf[V4L2_DEBUGFS_IF_MAX_LEN] = {}; + struct v4l2_subdev *sd = priv; + int len; + + if (!is_hdmi(sd)) + return 0; + + if (type != V4L2_DEBUGFS_IF_AVI) + return 0; + + i2c_rd(sd, PK_AVI_0HEAD, buf, PK_AVI_16BYTE - PK_AVI_0HEAD + 1); + len = buf[2] + 4; + if (len > V4L2_DEBUGFS_IF_MAX_LEN) + len = -ENOENT; + if (len > 0) + len = simple_read_from_buffer(ubuf, count, ppos, buf, len); + return len < 0 ? 0 : len; +} + static void print_avi_infoframe(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; union hdmi_infoframe frame; - u8 buffer[HDMI_INFOFRAME_SIZE(AVI)]; + u8 buffer[HDMI_INFOFRAME_SIZE(AVI)] = {}; if (!is_hdmi(sd)) { v4l2_info(sd, "DVI-D signal - AVI infoframe not supported\n"); @@ -2161,6 +2188,11 @@ static int tc358743_probe(struct i2c_client *client) if (err < 0) goto err_work_queues; + state->debugfs_dir = debugfs_create_dir(sd->name, v4l2_debugfs_root()); + state->infoframes = v4l2_debugfs_if_alloc(state->debugfs_dir, + V4L2_DEBUGFS_IF_AVI, sd, + tc358743_debugfs_if_read); + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); @@ -2188,6 +2220,8 @@ static void tc358743_remove(struct i2c_client *client) flush_work(&state->work_i2c_poll); } cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + v4l2_debugfs_if_free(state->infoframes); + debugfs_remove_recursive(state->debugfs_dir); cec_unregister_adapter(state->cec_adap); v4l2_async_unregister_subdev(sd); v4l2_device_unregister_subdev(sd); -- 2.51.0 From 6ce4c5dc5dd2706d1821d8ebdc53afad8182c2d5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 10:31:22 +0200 Subject: [PATCH 06/16] media: omap3isp: add missing wait_prepare/finish ops Without these ops the v4l2-compliance blocking wait test will fail. These ops are required to ensure that when VIDIOC_DQBUF has to wait for buffers to arrive, the queue lock is correctly released and retaken. Otherwise the wait for a buffer would block all other queue ioctls. Signed-off-by: Hans Verkuil Cc: Laurent Pinchart Acked-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/ti/omap3isp/ispvideo.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c index daca689dc082..5c9aa80023fd 100644 --- a/drivers/media/platform/ti/omap3isp/ispvideo.c +++ b/drivers/media/platform/ti/omap3isp/ispvideo.c @@ -480,11 +480,29 @@ static int isp_video_start_streaming(struct vb2_queue *queue, return 0; } +static void omap3isp_wait_prepare(struct vb2_queue *vq) +{ + struct isp_video_fh *vfh = vb2_get_drv_priv(vq); + struct isp_video *video = vfh->video; + + mutex_unlock(&video->queue_lock); +} + +static void omap3isp_wait_finish(struct vb2_queue *vq) +{ + struct isp_video_fh *vfh = vb2_get_drv_priv(vq); + struct isp_video *video = vfh->video; + + mutex_lock(&video->queue_lock); +} + static const struct vb2_ops isp_video_queue_ops = { .queue_setup = isp_video_queue_setup, .buf_prepare = isp_video_buffer_prepare, .buf_queue = isp_video_buffer_queue, .start_streaming = isp_video_start_streaming, + .wait_prepare = omap3isp_wait_prepare, + .wait_finish = omap3isp_wait_finish, }; /* -- 2.51.0 From 5e53592063f344658d0876b448a2e5ec09a9883d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 10:31:23 +0200 Subject: [PATCH 07/16] media: pisp_be: add missing wait_prepare/finish ops Without these ops the v4l2-compliance blocking wait test will fail. These ops are required to ensure that when VIDIOC_DQBUF has to wait for buffers to arrive, the queue lock is correctly released and retaken. Otherwise the wait for a buffer would block all other queue ioctls. Signed-off-by: Hans Verkuil Cc: Laurent Pinchart Acked-by: Jacopo Mondi Tested-by: Jacopo Mondi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/raspberrypi/pisp_be/pisp_be.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c index 65ff2382cffe..7ce3be626c4a 100644 --- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c +++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c @@ -964,6 +964,8 @@ static const struct vb2_ops pispbe_node_queue_ops = { .buf_queue = pispbe_node_buffer_queue, .start_streaming = pispbe_node_start_streaming, .stop_streaming = pispbe_node_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static const struct v4l2_file_operations pispbe_fops = { -- 2.51.0 From 547629f7b167a5f27a040e80aa7991b12172ef0e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 10:31:24 +0200 Subject: [PATCH 08/16] media: venus: add missing wait_prepare/finish ops Without these ops the v4l2-compliance blocking wait test will fail. These ops are required to ensure that when VIDIOC_DQBUF has to wait for buffers to arrive, the queue lock is correctly released and retaken. Otherwise the wait for a buffer would block all other queue ioctls. Signed-off-by: Hans Verkuil Cc: Stanimir Varbanov Reviewed-by: Dikshita Agarwal Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/qcom/venus/vdec.c | 2 ++ drivers/media/platform/qcom/venus/venc.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index d12089370d91..666309f684f1 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -1390,6 +1390,8 @@ static const struct vb2_ops vdec_vb2_ops = { .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = vdec_start_streaming, .stop_streaming = vdec_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_queue = vdec_vb2_buf_queue, }; diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 3ec2fb8d9fab..5804a4911537 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -1319,6 +1319,8 @@ static const struct vb2_ops venc_vb2_ops = { .buf_prepare = venus_helper_vb2_buf_prepare, .start_streaming = venc_start_streaming, .stop_streaming = venus_helper_vb2_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_queue = venc_vb2_buf_queue, }; -- 2.51.0 From 3653f14841dc2a76d1fb9997e249715899bf4fb1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:48 +0200 Subject: [PATCH 09/16] media: pwc: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/pwc/pwc-if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index e342199711d3..4a8485ea0c81 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -1054,6 +1054,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vb_queue.ops = &pwc_vb_queue_ops; pdev->vb_queue.mem_ops = &vb2_vmalloc_memops; pdev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + pdev->vb_queue.lock = &pdev->vb_queue_lock; rc = vb2_queue_init(&pdev->vb_queue); if (rc < 0) { PWC_ERROR("Oops, could not initialize vb2 queue.\n"); @@ -1064,7 +1065,6 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->vdev = pwc_template; strscpy(pdev->vdev.name, name, sizeof(pdev->vdev.name)); pdev->vdev.queue = &pdev->vb_queue; - pdev->vdev.queue->lock = &pdev->vb_queue_lock; video_set_drvdata(&pdev->vdev, pdev); pdev->release = le16_to_cpu(udev->descriptor.bcdDevice); -- 2.51.0 From fee248fb86ae229b82dee5c4764681078f0668c5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:49 +0200 Subject: [PATCH 10/16] media: msi2500: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/msi2500/msi2500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c index 5138486abfa0..c42fad1e3204 100644 --- a/drivers/media/usb/msi2500/msi2500.c +++ b/drivers/media/usb/msi2500/msi2500.c @@ -1199,6 +1199,7 @@ static int msi2500_probe(struct usb_interface *intf, dev->vb_queue.ops = &msi2500_vb2_ops; dev->vb_queue.mem_ops = &vb2_vmalloc_memops; dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dev->vb_queue.lock = &dev->vb_queue_lock; ret = vb2_queue_init(&dev->vb_queue); if (ret) { dev_err(dev->dev, "Could not initialize vb2 queue\n"); @@ -1208,7 +1209,6 @@ static int msi2500_probe(struct usb_interface *intf, /* Init video_device structure */ dev->vdev = msi2500_template; dev->vdev.queue = &dev->vb_queue; - dev->vdev.queue->lock = &dev->vb_queue_lock; video_set_drvdata(&dev->vdev, dev); /* Register the v4l2_device structure */ -- 2.51.0 From cf78f3d3a6766f7f846320e32ff3109f1e192aad Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:50 +0200 Subject: [PATCH 11/16] media: hackrf: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/hackrf/hackrf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c index 9c0ecd5f056c..a1a7690a344a 100644 --- a/drivers/media/usb/hackrf/hackrf.c +++ b/drivers/media/usb/hackrf/hackrf.c @@ -1398,6 +1398,7 @@ static int hackrf_probe(struct usb_interface *intf, dev->rx_vb2_queue.drv_priv = dev; dev->rx_vb2_queue.buf_struct_size = sizeof(struct hackrf_buffer); dev->rx_vb2_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dev->rx_vb2_queue.lock = &dev->vb_queue_lock; ret = vb2_queue_init(&dev->rx_vb2_queue); if (ret) { dev_err(dev->dev, "Could not initialize rx vb2 queue\n"); @@ -1413,6 +1414,7 @@ static int hackrf_probe(struct usb_interface *intf, dev->tx_vb2_queue.drv_priv = dev; dev->tx_vb2_queue.buf_struct_size = sizeof(struct hackrf_buffer); dev->tx_vb2_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + dev->tx_vb2_queue.lock = &dev->vb_queue_lock; ret = vb2_queue_init(&dev->tx_vb2_queue); if (ret) { dev_err(dev->dev, "Could not initialize tx vb2 queue\n"); @@ -1474,7 +1476,6 @@ static int hackrf_probe(struct usb_interface *intf, /* Init video_device structure for receiver */ dev->rx_vdev = hackrf_template; dev->rx_vdev.queue = &dev->rx_vb2_queue; - dev->rx_vdev.queue->lock = &dev->vb_queue_lock; dev->rx_vdev.v4l2_dev = &dev->v4l2_dev; dev->rx_vdev.ctrl_handler = &dev->rx_ctrl_handler; dev->rx_vdev.lock = &dev->v4l2_lock; @@ -1494,7 +1495,6 @@ static int hackrf_probe(struct usb_interface *intf, /* Init video_device structure for transmitter */ dev->tx_vdev = hackrf_template; dev->tx_vdev.queue = &dev->tx_vb2_queue; - dev->tx_vdev.queue->lock = &dev->vb_queue_lock; dev->tx_vdev.v4l2_dev = &dev->v4l2_dev; dev->tx_vdev.ctrl_handler = &dev->tx_ctrl_handler; dev->tx_vdev.lock = &dev->v4l2_lock; -- 2.51.0 From 8d04fcc76f82e7025444b1d79d5a00dd1d2388b9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:51 +0200 Subject: [PATCH 12/16] media: airspy: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/airspy/airspy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index e24e655fb1db..3e2a3099a8fe 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -1017,6 +1017,7 @@ static int airspy_probe(struct usb_interface *intf, s->vb_queue.ops = &airspy_vb2_ops; s->vb_queue.mem_ops = &vb2_vmalloc_memops; s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + s->vb_queue.lock = &s->vb_queue_lock; ret = vb2_queue_init(&s->vb_queue); if (ret) { dev_err(s->dev, "Could not initialize vb2 queue\n"); @@ -1026,7 +1027,6 @@ static int airspy_probe(struct usb_interface *intf, /* Init video_device structure */ s->vdev = airspy_template; s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; video_set_drvdata(&s->vdev, s); /* Register the v4l2_device structure */ -- 2.51.0 From 922b739445f39b31e243f517fd09630f5a406f0a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:52 +0200 Subject: [PATCH 13/16] media: rcar_drif.c: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Cc: Ramesh Shanmugasundaram Cc: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/renesas/rcar_drif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c index f21d05054341..5d3109940ded 100644 --- a/drivers/media/platform/renesas/rcar_drif.c +++ b/drivers/media/platform/renesas/rcar_drif.c @@ -1071,7 +1071,6 @@ static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr) sdr->vdev->release = video_device_release; sdr->vdev->lock = &sdr->v4l2_mutex; sdr->vdev->queue = &sdr->vb_queue; - sdr->vdev->queue->lock = &sdr->vb_queue_mutex; sdr->vdev->ctrl_handler = &sdr->ctrl_hdl; sdr->vdev->v4l2_dev = &sdr->v4l2_dev; sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER | @@ -1316,6 +1315,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) sdr->vb_queue.ops = &rcar_drif_vb2_ops; sdr->vb_queue.mem_ops = &vb2_vmalloc_memops; sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + sdr->vb_queue.lock = &sdr->vb_queue_mutex; /* Init videobuf2 queue */ ret = vb2_queue_init(&sdr->vb_queue); -- 2.51.0 From 7e8ca483c0a96da6564bf7c57390e7c071fb5bae Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:53 +0200 Subject: [PATCH 14/16] media: video-i2c: set lock before calling vb2_queue_init() The vb2_queue_init() will expect the vb2_queue lock pointer to be set in the future. So for those drivers that set the lock later, move it up to before the vb2_queue_init() call. Signed-off-by: Hans Verkuil Acked-by: Matt Ranostay Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/video-i2c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 56dbe07a1c99..ffb3d9d61a74 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -798,13 +798,13 @@ static int video_i2c_probe(struct i2c_client *client) queue->min_queued_buffers = 1; queue->ops = &video_i2c_video_qops; queue->mem_ops = &vb2_vmalloc_memops; + queue->lock = &data->queue_lock; ret = vb2_queue_init(queue); if (ret < 0) goto error_unregister_device; data->vdev.queue = queue; - data->vdev.queue->lock = &data->queue_lock; snprintf(data->vdev.name, sizeof(data->vdev.name), "I2C %d-%d Transport Video", -- 2.51.0 From c780d01cf1a68182d515ea46a7c85746435a4004 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:54 +0200 Subject: [PATCH 15/16] media: vb2: vb2_core_queue_init(): sanity check lock and wait_prepare/finish Add two new checks: 1) wait_prepare and wait_finish callbacks are either both present or both unset, you can't mix. 2) if lock == NULL, then wait_prepare (and due to check 1 also wait_finish) must be present. These checks should prevent the case where lock == NULL, but there is no way to release/reacquire whatever lock is used when waiting for a buffer to arrive in VIDIOC_DQBUF. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 29a8d876e6c2..6335ac7b771a 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2644,6 +2644,14 @@ int vb2_core_queue_init(struct vb2_queue *q) if (WARN_ON(q->min_reqbufs_allocation > q->max_num_buffers)) return -EINVAL; + /* Either both or none are set */ + if (WARN_ON(!q->ops->wait_prepare ^ !q->ops->wait_finish)) + return -EINVAL; + + /* Warn if q->lock is NULL and no custom wait_prepare is provided */ + if (WARN_ON(!q->lock && !q->ops->wait_prepare)) + return -EINVAL; + INIT_LIST_HEAD(&q->queued_list); INIT_LIST_HEAD(&q->done_list); spin_lock_init(&q->done_lock); -- 2.51.0 From 88785982a19daa765e30ab3a605680202cfaee4e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 2 Sep 2024 16:04:55 +0200 Subject: [PATCH 16/16] media: vb2: use lock if wait_prepare/finish are NULL If the wait_prepare or wait_finish callback is set, then call it. If it is NULL and the queue lock pointer is not NULL, then just unlock/lock that mutex. This allows simplifying drivers by dropping the wait_prepare and wait_finish ops (and eventually the vb2_ops_wait_prepare/finish helpers). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/videobuf2/videobuf2-core.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 6335ac7b771a..d064e0664851 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2035,7 +2035,10 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) * become ready or for streamoff. Driver's lock is released to * allow streamoff or qbuf to be called while waiting. */ - call_void_qop(q, wait_prepare, q); + if (q->ops->wait_prepare) + call_void_qop(q, wait_prepare, q); + else if (q->lock) + mutex_unlock(q->lock); /* * All locks have been released, it is safe to sleep now. @@ -2045,12 +2048,16 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) !list_empty(&q->done_list) || !q->streaming || q->error); + if (q->ops->wait_finish) + call_void_qop(q, wait_finish, q); + else if (q->lock) + mutex_lock(q->lock); + + q->waiting_in_dqbuf = 0; /* * We need to reevaluate both conditions again after reacquiring * the locks or return an error if one occurred. */ - call_void_qop(q, wait_finish, q); - q->waiting_in_dqbuf = 0; if (ret) { dprintk(q, 1, "sleep was interrupted\n"); return ret; -- 2.51.0