From 5ecdd416b87ca133c098cde2a9082692fe150e0f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 10 Jun 2026 16:20:06 +0000 Subject: media: ov9282: Remove unused kerneldoc description The field has been removed, but the kerneldoc not. Let's take care of this. This patch fixes the following warning: Warning: drivers/media/i2c/ov9282.c:223 Excess struct member 'mutex' description in 'ov9282' Fixes: 0c69a963a300 ("media: i2c: ov9282: Switch to using the sub-device state lock") Signed-off-by: Ricardo Ribalda Reviewed-by: Daniel Scally Reviewed-by: Laurent Pinchart Reviewed-by: Dave Stevenson Signed-off-by: Hans Verkuil --- drivers/media/i2c/ov9282.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 5b6f897a74fc..3ba630ec4f2a 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -197,7 +197,6 @@ struct ov9282_mode { * @noncontinuous_clock: Selection of CSI2 noncontinuous clock mode * @cur_mode: Pointer to current selected sensor mode * @code: Mbus code currently selected - * @mutex: Mutex for serializing sensor controls */ struct ov9282 { struct device *dev; -- cgit From 9ec2b44b2ec155cc1496d8470b349fd484529e81 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 10 Jun 2026 16:20:07 +0000 Subject: media: rzg2l-cru: Remove unused kerneldoc description The variable has been previously removed but not its kerneldoc. Also remove the leftover reference to state in the description of qlock. This patch fixes the following warning: Warning: drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h:164 Excess struct member 'state' description in 'rzg2l_cru_dev' Fixes: 85d8820d4869 ("media: rzg2l-cru: Remove the 'state' variable") Signed-off-by: Ricardo Ribalda Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Reviewed-by: Lad Prabhakar Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h index 5bf334e173d2..b426bc7898bf 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h @@ -116,11 +116,9 @@ struct rzg2l_cru_info { * @scratch_phys: physical address of the scratch buffer * * @qlock: protects @queue_buf, @buf_list, @sequence - * @state * @queue_buf: Keeps track of buffers given to HW slot * @buf_list: list of queued buffers * @sequence: V4L2 buffers sequence number - * @state: keeps track of operation state * * @format: active V4L2 pixel format */ -- cgit From e9c44d4eccc735b9b3a2c7e6324207d5ccb2d821 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Wed, 10 Jun 2026 16:20:08 +0000 Subject: media: mc-entity: Add missing kerneldoc The argument args is not documented, and the latest kernel version complains about that. This fixes the following warning: Warning: include/media/media-entity.h:1394 function parameter 'args' not described in 'media_entity_call' Fixes: 48a7c4bac94d ("[media] docs-rst: improve the kAPI documentation for the mediactl") Signed-off-by: Ricardo Ribalda Reviewed-by: Daniel Scally Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- include/media/media-entity.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/media/media-entity.h b/include/media/media-entity.h index d9b72cd87d52..fa393e840669 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -1387,6 +1387,7 @@ void media_remove_intf_links(struct media_interface *intf); * @entity: entity where the @operation will be called * @operation: type of the operation. Should be the name of a member of * struct &media_entity_operations. + * @args: arguments for the operation. * * This helper function will check if @operation is not %NULL. On such case, * it will issue a call to @operation\(@entity, @args\). -- cgit From 479aa6fa8c50f1052f1451326ef7d4d586d340c3 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 13 May 2026 16:11:03 +0900 Subject: media: rc: sunxi-cir: Unregister rc device on probe failure After rc_register_device() succeeds, later probe failures must undo the registration with rc_unregister_device(). The current error path jumps to the allocation cleanup label and only calls rc_free_device(), leaving the rc device registration and resources created by rc_register_device() behind. Add a registered-device unwind label for the IRQ lookup, IRQ request, and hardware initialization failure paths. Keep rc_free_device() for failures before rc_register_device() succeeds. Fixes: b4e3e59fb59c ("[media] rc: add sunxi-ir driver") Cc: stable@vger.kernel.org Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Signed-off-by: Sean Young --- drivers/media/rc/sunxi-cir.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index cb4c56bf0752..28e840a7e5b8 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -344,22 +344,25 @@ static int sunxi_ir_probe(struct platform_device *pdev) ir->irq = platform_get_irq(pdev, 0); if (ir->irq < 0) { ret = ir->irq; - goto exit_free_dev; + goto exit_unregister_dev; } ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir); if (ret) { dev_err(dev, "failed request irq\n"); - goto exit_free_dev; + goto exit_unregister_dev; } ret = sunxi_ir_hw_init(dev); if (ret) - goto exit_free_dev; + goto exit_unregister_dev; dev_info(dev, "initialized sunXi IR driver\n"); return 0; +exit_unregister_dev: + rc_unregister_device(ir->rc); + exit_free_dev: rc_free_device(ir->rc); -- cgit From 2cf8593cc8d7f31f5f96339054b43ddc458b4008 Mon Sep 17 00:00:00 2001 From: "Uwe Kleine-König (The Capable Hub)" Date: Tue, 9 Jun 2026 19:00:09 +0200 Subject: media: Drop unused assignments from pnp_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly assigning .driver_data in drivers that don't use this member is silly and a bit irritating. Drop it. Also simplify the list terminator entry to be just empty to match what most other device_id tables do. While touching these arrays convert them to use named initializers, empty list terminators and no comma after the terminator. There is no changed semantic, not even a change in the compiled result. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Sean Young --- drivers/media/radio/radio-cadet.c | 4 ++-- drivers/media/radio/radio-gemtek.c | 4 ++-- drivers/media/rc/fintek-cir.c | 4 ++-- drivers/media/rc/nuvoton-cir.c | 6 +++--- drivers/media/rc/winbond-cir.c | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c index 5110754e1a31..53c1d1061cf4 100644 --- a/drivers/media/radio/radio-cadet.c +++ b/drivers/media/radio/radio-cadet.c @@ -527,8 +527,8 @@ static const struct v4l2_ctrl_ops cadet_ctrl_ops = { static const struct pnp_device_id cadet_pnp_devices[] = { /* ADS Cadet AM/FM Radio Card */ - {.id = "MSM0c24", .driver_data = 0}, - {.id = ""} + { .id = "MSM0c24" }, + { } }; MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices); diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 3d1d0b46195b..7e4257e4b71d 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -284,8 +284,8 @@ static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c } #ifdef CONFIG_PNP static const struct pnp_device_id gemtek_pnp_devices[] = { /* AOpen FX-3D/Pro Radio */ - {.id = "ADS7183", .driver_data = 0}, - {.id = ""} + { .id = "ADS7183" }, + { } }; MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 5055dfc3f465..c196ee923ecd 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -642,8 +642,8 @@ static void fintek_shutdown(struct pnp_dev *pdev) } static const struct pnp_device_id fintek_ids[] = { - { "FIT0002", 0 }, /* CIR */ - { "", 0 }, + { .id = "FIT0002" }, /* CIR */ + { } }; static struct pnp_driver fintek_driver = { diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 4e5a0c8dc9a0..918258dadd69 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1092,9 +1092,9 @@ static void nvt_shutdown(struct pnp_dev *pdev) } static const struct pnp_device_id nvt_ids[] = { - { "WEC0530", 0 }, /* CIR */ - { "NTN0530", 0 }, /* CIR for new chip's pnp id*/ - { "", 0 }, + { .id = "WEC0530" }, /* CIR */ + { .id = "NTN0530" }, /* CIR for new chip's pnp id*/ + { } }; static struct pnp_driver nvt_driver = { diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 8e804661a621..1ae036f8a296 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -1179,8 +1179,8 @@ wbcir_remove(struct pnp_dev *device) } static const struct pnp_device_id wbcir_ids[] = { - { "WEC1022", 0 }, - { "", 0 } + { .id = "WEC1022" }, + { } }; MODULE_DEVICE_TABLE(pnp, wbcir_ids); -- cgit From 253355887a1ab0ac8f33b356c7c1140eee554d18 Mon Sep 17 00:00:00 2001 From: "Uwe Kleine-König (The Capable Hub)" Date: Wed, 10 Jun 2026 17:27:09 +0200 Subject: media: ite-cir: Use an enum for the different supported device types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using magic constants define an enum that that makes it easier to keep .driver_data and the array of different types in sync. While touching the pnp_device_id array simplify the list terminator and use named initializers which better show which struct members are actually assigned to. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Sean Young --- drivers/media/rc/ite-cir.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index bde2a7051231..1fbafcd8219e 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1203,9 +1203,16 @@ static void ite_close(struct rc_dev *rcdev) spin_unlock_irqrestore(&dev->lock, flags); } +enum ite_model_type { + ITE8704, + ITE8713, + ITE8708, + ITE8709, +}; + /* supported models and their parameters */ static const struct ite_dev_params ite_dev_descs[] = { - { /* 0: ITE8704 */ + [ITE8704] = { .model = "ITE8704 CIR transceiver", .io_region_size = IT87_IOREG_LENGTH, .io_rsrc_no = 0, @@ -1224,7 +1231,7 @@ static const struct ite_dev_params ite_dev_descs[] = { .init_hardware = it87_init_hardware, .set_carrier_params = it87_set_carrier_params, }, - { /* 1: ITE8713 */ + [ITE8713] = { .model = "ITE8713 CIR transceiver", .io_region_size = IT87_IOREG_LENGTH, .io_rsrc_no = 0, @@ -1243,7 +1250,7 @@ static const struct ite_dev_params ite_dev_descs[] = { .init_hardware = it87_init_hardware, .set_carrier_params = it87_set_carrier_params, }, - { /* 2: ITE8708 */ + [ITE8708] = { .model = "ITE8708 CIR transceiver", .io_region_size = IT8708_IOREG_LENGTH, .io_rsrc_no = 0, @@ -1263,7 +1270,7 @@ static const struct ite_dev_params ite_dev_descs[] = { .init_hardware = it8708_init_hardware, .set_carrier_params = it8708_set_carrier_params, }, - { /* 3: ITE8709 */ + [ITE8709] = { .model = "ITE8709 CIR transceiver", .io_region_size = IT8709_IOREG_LENGTH, .io_rsrc_no = 2, @@ -1286,11 +1293,11 @@ static const struct ite_dev_params ite_dev_descs[] = { }; static const struct pnp_device_id ite_ids[] = { - {"ITE8704", 0}, /* Default model */ - {"ITE8713", 1}, /* CIR found in EEEBox 1501U */ - {"ITE8708", 2}, /* Bridged IT8512 */ - {"ITE8709", 3}, /* SRAM-Bridged IT8512 */ - {"", 0}, + { .id = "ITE8704", .driver_data = ITE8704 }, /* Default model */ + { .id = "ITE8713", .driver_data = ITE8713 }, /* CIR found in EEEBox 1501U */ + { .id = "ITE8708", .driver_data = ITE8708 }, /* Bridged IT8512 */ + { .id = "ITE8709", .driver_data = ITE8709 }, /* SRAM-Bridged IT8512 */ + { } }; /* allocate memory, probe hardware, and initialize everything */ @@ -1301,7 +1308,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id struct ite_dev *itdev = NULL; struct rc_dev *rdev = NULL; int ret = -ENOMEM; - int model_no; + enum ite_model_type model_no; int io_rsrc_no; itdev = kzalloc_obj(struct ite_dev); @@ -1317,7 +1324,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id ret = -ENODEV; /* get the model number */ - model_no = (int)dev_id->driver_data; + model_no = dev_id->driver_data; dev_dbg(&pdev->dev, "Auto-detected model: %s\n", ite_dev_descs[model_no].model); -- cgit From 172c5a7d81deb922ddedd1bc920751c7fed6c43c Mon Sep 17 00:00:00 2001 From: Yi Ding Date: Thu, 28 May 2026 23:00:05 -0700 Subject: media: cec: meson: ao-cec-g12a: name the CEC core regmap to avoid debugfs clash The driver registers two regmaps on the same platform device: an MMIO regmap for the AO CEC registers, and an indirect regmap (using reg_read()/reg_write() callbacks) for the CEC controller core registers. Neither regmap_config sets a .name, so both default their debugfs directory to the device name and collide: debugfs: 'ff800280.cec' already exists in 'regmap' Because of the clash the second regmap's debugfs directory fails to register, so its registers can no longer be inspected via debugfs. Give the indirect CEC core regmap a distinct name. The two debugfs directories then become ".cec" and ".cec-core". This only affects debugfs naming; register access is unchanged. Tested on an ODROID-N2 (Amlogic S922X): the warning is gone and both /sys/kernel/debug/regmap/ff800280.cec and ff800280.cec-core are present. Fixes: b7778c46683c ("media: platform: meson: Add Amlogic Meson G12A AO CEC Controller driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Yi Ding Reviewed-by: Neil Armstrong Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/meson/ao-cec-g12a.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c index 41f5b8669cb0..2c914f0008b5 100644 --- a/drivers/media/cec/platform/meson/ao-cec-g12a.c +++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c @@ -405,6 +405,7 @@ static int meson_ao_cec_g12a_write(void *context, unsigned int addr, } static const struct regmap_config meson_ao_cec_g12a_cec_regmap_conf = { + .name = "core", .reg_bits = 8, .val_bits = 8, .reg_read = meson_ao_cec_g12a_read, -- cgit From a24ba0653f7154e671dc8d2bf64682ab2d042792 Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Fri, 29 May 2026 09:59:26 +0000 Subject: media: cec: core: Fix kmemleak due to missed rc_free_device() call The commit dccc0c3ddf8f ("media: rc: fix race between unregister and urb/irq callbacks") removed the implicit call to rc_free_device() from rc_unregister_device(). However, the commit missed to remove the NULL assignment of adap->rc that is now causing rc_free_device() to never be called on an allocated rc device. kmemleak reports following after e.g. dw-hdmi unbind: unreferenced object 0xffff00010ac10000 (size 4096): comm "kworker/u16:1", pid 39, jiffies 4294897739 hex dump (first 32 bytes): 20 23 4b 0a 01 00 ff ff 08 00 c1 0a 01 00 ff ff #K............. 08 00 c1 0a 01 00 ff ff 00 00 00 00 00 00 00 00 ................ backtrace (crc e11baccc): kmemleak_alloc+0x38/0x44 __kmalloc_cache_noprof+0x4a8/0x5e0 rc_allocate_device+0x48/0x2a0 cec_allocate_adapter+0x3ac/0x800 dw_hdmi_cec_probe+0x264/0x634 platform_probe+0xc0/0x188 really_probe+0x4a4/0x8e0 __driver_probe_device+0x2f8/0x440 driver_probe_device+0x60/0x160 __device_attach_driver+0x1a0/0x2a0 bus_for_each_drv+0x100/0x1a0 __device_attach+0x174/0x350 device_initial_probe+0x90/0xb0 bus_probe_device+0x4c/0x120 device_add+0xdec/0x116c platform_device_add+0x354/0x598 Remove the assignment of adap->rc to NULL to let cec_delete_adapter() free the allocated rc device after last user of the cec device exits to fix the kmemleak. Fixes: dccc0c3ddf8f ("media: rc: fix race between unregister and urb/irq callbacks") Cc: stable@vger.kernel.org Signed-off-by: Jonas Karlman Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0fcd3b5e60c8..90a98f322f5e 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -371,9 +371,7 @@ void cec_unregister_adapter(struct cec_adapter *adap) return; #ifdef CONFIG_MEDIA_CEC_RC - /* Note: rc_unregister also calls rc_free */ rc_unregister_device(adap->rc); - adap->rc = NULL; #endif debugfs_remove_recursive(adap->cec_dir); #ifdef CONFIG_CEC_NOTIFIER -- cgit From fb9dda38d4b9e90db07ed9a0ee2d35bf85494035 Mon Sep 17 00:00:00 2001 From: Weigang He Date: Thu, 11 Jun 2026 23:22:48 +1000 Subject: media: cec: stm32: prevent out-of-bounds write on RX overflow stm32_rx_done() appends each received CEC byte to rx_msg.msg[] using rx_msg.len as the write index, incrementing it on every RXBR (receive-byte-ready) interrupt without checking it against the buffer size: cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; rx_msg.msg[] is a fixed CEC_MAX_MSG_SIZE (16) byte array in struct cec_msg, and rx_msg.len is only reset on RXACKE/RXOVR or after a completed message (RXEND). The number of bytes received before RXEND is decided by the remote CEC device (it sets EOM), not by the driver. A peer that keeps sending bytes without ending the message drives RXBR repeatedly, pushing rx_msg.len past 16 and writing peer-controlled bytes out of bounds into the surrounding memory. This is reachable in normal operation once the driver has probed and receiving is enabled, from the IRQ thread, without any local privilege. The length check in the CEC core runs on the consumer side, after the byte has been stored, so it does not prevent the overflow. Bound the index in the driver before the store, as the other platform CEC drivers already do (e.g. tegra_cec), dropping the excess bytes of an overlong frame. Found by static analysis tool CodeQL. Fixes: d69ae57453c8 ("[media] cec: add STM32 cec driver") Cc: stable@vger.kernel.org Signed-off-by: Weigang He Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/stm32/stm32-cec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index 1ec0cece0a5b..8c2fc232202d 100644 --- a/drivers/media/cec/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c @@ -132,7 +132,8 @@ static void stm32_rx_done(struct stm32_cec *cec, u32 status) u32 val; regmap_read(cec->regmap, CEC_RXDR, &val); - cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; + if (cec->rx_msg.len < CEC_MAX_MSG_SIZE) + cec->rx_msg.msg[cec->rx_msg.len++] = val & 0xFF; } if (cec->irq_status & RXEND) { -- cgit From abac9820b26b5cfcb01eb79efe2abdd0ac7e07c3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 18 Jun 2026 13:03:19 +0200 Subject: media: cec: extron-da-hd-4k-plus: add sanity check Add check to prevent overflowing msg.msg[] in case the incoming data is malformed. Signed-off-by: Hans Verkuil Reviewed-by: Sean Young Fixes: 056f2821b631 ("media: cec: extron-da-hd-4k-plus: add the Extron DA HD 4K Plus CEC driver") Cc: stable@vger.kernel.org --- drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c index 3381d86096a1..3c6ce6f3d93e 100644 --- a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c +++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c @@ -657,7 +657,8 @@ static void extron_process_received(struct extron_port *port, const char *data) if (!port || port->disconnected) return; - if (len < 5 || (len - 2) % 3 || data[len - 2] != '*') + if (len < 5 || ((len - 2) / 3 > sizeof(msg.msg)) || + (len - 2) % 3 || data[len - 2] != '*') goto malformed; while (*data != '*') { -- cgit From 0fbd5c2327020858c45b2d1c65775d64cdeca523 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 21 Jun 2026 10:04:37 +0530 Subject: media: cec: disable delayed work before freeing an interrupted transmit cec_transmit_msg_fh() drops adap->lock to wait for a blocking transmit in wait_for_completion_killable(). If that wait is interrupted by a signal, cancel_delayed_work_sync() can run before the CEC kthread arms the reply timeout via schedule_delayed_work(&data->work) in cec_transmit_done_ts(). The work is then armed after the cancel, and the data is freed with its delayed_work still pending: ODEBUG: free active (active state 0) object: ... hint: cec_wait_timeout Use disable_delayed_work_sync(): it cancels the work and disables it, so the later schedule_delayed_work() becomes a no-op and the work cannot be re-armed. The data is freed right after, so it need not be re-enabled. Fixes: 490d84f6d73c ("media: cec: forgot to cancel delayed work") Reported-by: syzbot+051024d603432b4ab395@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=051024d603432b4ab395 Suggested-by: Hillf Danton Cc: stable@vger.kernel.org Signed-off-by: Biren Pandya Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-adap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index a90cb84a4b4d..829ee4861bf7 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -965,7 +965,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, */ mutex_unlock(&adap->lock); err = wait_for_completion_killable(&data->c); - cancel_delayed_work_sync(&data->work); + disable_delayed_work_sync(&data->work); mutex_lock(&adap->lock); if (err) -- cgit From 3bc2e4a26465386332ee1a40219d8dafba335b0b Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 24 Jun 2026 22:40:15 +0800 Subject: media: cec: stm32: return an error when log-address wait times out stm32_cec_adap_log_addr() waits for TXSOM to clear before disabling CEC and updating the logical address registers. The wait result is ignored, so a timeout can still be reported as a successful logical address update. Return the polling error before touching the address registers. Compute the address mask only for valid logical addresses so the invalid-address path does not evaluate a shift based on CEC_LOG_ADDR_INVALID. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/stm32/stm32-cec.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/media/cec/platform/stm32/stm32-cec.c b/drivers/media/cec/platform/stm32/stm32-cec.c index 8c2fc232202d..29418966a98e 100644 --- a/drivers/media/cec/platform/stm32/stm32-cec.c +++ b/drivers/media/cec/platform/stm32/stm32-cec.c @@ -194,18 +194,24 @@ static int stm32_cec_adap_enable(struct cec_adapter *adap, bool enable) static int stm32_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) { struct stm32_cec *cec = adap->priv; - u32 oar = (1 << logical_addr) << 16; u32 val; + int ret; /* Poll every 100µs the register CEC_CR to wait end of transmission */ - regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM), - 100, CEC_XFER_TIMEOUT_MS * 1000); + ret = regmap_read_poll_timeout(cec->regmap, CEC_CR, val, !(val & TXSOM), + 100, CEC_XFER_TIMEOUT_MS * 1000); + if (ret) + return ret; + regmap_update_bits(cec->regmap, CEC_CR, CECEN, 0); - if (logical_addr == CEC_LOG_ADDR_INVALID) + if (logical_addr == CEC_LOG_ADDR_INVALID) { regmap_update_bits(cec->regmap, CEC_CFGR, OAR, 0); - else + } else { + u32 oar = BIT(logical_addr) << 16; + regmap_update_bits(cec->regmap, CEC_CFGR, oar, oar); + } regmap_update_bits(cec->regmap, CEC_CR, CECEN, CECEN); -- cgit From a1d83d1b810665bd53ce8a7b7867e054d68676c7 Mon Sep 17 00:00:00 2001 From: Gui-Dong Han Date: Fri, 26 Jun 2026 16:08:06 +0800 Subject: media: cec-pin: Fix event FIFO ordering cec_pin_update() fills work_pin_events[] and work_pin_ts[], then increments work_pin_num_events. cec_pin_thread_func() uses that counter to decide when to read the FIFO entries. Do not let the counter update be observed without the event update. Also do not let a freed slot be reused before the thread has finished reading it. Use release operations when publishing an entry and releasing a slot, and acquire operations when consuming those counter updates. Leave the other work_pin_num_events users as they do not participate in this FIFO publication path. Fixes: ea5c8ef29668 ("media: cec-pin: add low-level pin hardware support") Signed-off-by: Gui-Dong Han Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-pin.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 6e1c39102832..085fc12067af 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -115,7 +115,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force) return; pin->adap->cec_pin_is_high = v; - if (atomic_read(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) { + if (atomic_read_acquire(&pin->work_pin_num_events) < CEC_NUM_PIN_EVENTS) { u8 ev = v; if (pin->work_pin_events_dropped) { @@ -126,7 +126,7 @@ static void cec_pin_update(struct cec_pin *pin, bool v, bool force) pin->work_pin_ts[pin->work_pin_events_wr] = ktime_get(); pin->work_pin_events_wr = (pin->work_pin_events_wr + 1) % CEC_NUM_PIN_EVENTS; - atomic_inc(&pin->work_pin_num_events); + atomic_inc_return_release(&pin->work_pin_num_events); } else { pin->work_pin_events_dropped = true; pin->work_pin_events_dropped_cnt++; @@ -1101,7 +1101,7 @@ static int cec_pin_thread_func(void *_adap) pin->work_tx_ts); } - while (atomic_read(&pin->work_pin_num_events)) { + while (atomic_read_acquire(&pin->work_pin_num_events)) { unsigned int idx = pin->work_pin_events_rd; u8 v = pin->work_pin_events[idx]; @@ -1110,7 +1110,7 @@ static int cec_pin_thread_func(void *_adap) v & CEC_PIN_EVENT_FL_DROPPED, pin->work_pin_ts[idx]); pin->work_pin_events_rd = (idx + 1) % CEC_NUM_PIN_EVENTS; - atomic_dec(&pin->work_pin_num_events); + atomic_dec_return_release(&pin->work_pin_num_events); } switch (atomic_xchg(&pin->work_irq_change, -- cgit From 5370facb7b4461166a4610d456fefeb92ef50a82 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 24 Jun 2026 10:55:51 +0100 Subject: media: keymaps: Remove obsolete RC_MAP_RC5_TV keymap define Since commit 206241069ecf ("[media] rc/keymaps: Remove the obsolete rc-rc5-tv keymap"), the rc-rc5-tv keymap is no longer in the tree. Fixes: 206241069ecf ("[media] rc/keymaps: Remove the obsolete rc-rc5-tv keymap") Signed-off-by: Sean Young Acked-by: Mauro Carvalho Chehab --- include/media/rc-map.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d90e4611b066..950d702aee3b 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -309,7 +309,6 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_PROTEUS_2309 "rc-proteus-2309" #define RC_MAP_PURPLETV "rc-purpletv" #define RC_MAP_PV951 "rc-pv951" -#define RC_MAP_RC5_TV "rc-rc5-tv" #define RC_MAP_RC6_MCE "rc-rc6-mce" #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" #define RC_MAP_REDDO "rc-reddo" -- cgit From 6e5deb2923b0d1b73c77a1a77c30b0da43d9e022 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Wed, 24 Jun 2026 11:05:47 +0100 Subject: media: keymaps: Remove obsolete RC_MAP_HAUPPAUGE_NEW keymap define Since commit af86ce79f020 ("[media] remove the old RC_MAP_HAUPPAUGE_NEW RC map"), the RC_MAP_HAUPPAUGE_NEW define is no longer used. Fixes: af86ce79f020 ("[media] remove the old RC_MAP_HAUPPAUGE_NEW RC map") Signed-off-by: Sean Young Acked-by: Mauro Carvalho Chehab --- include/media/rc-map.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 950d702aee3b..d95ed3e96de2 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -262,7 +262,6 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce" #define RC_MAP_GOTVIEW7135 "rc-gotview7135" #define RC_MAP_HAUPPAUGE "rc-hauppauge" -#define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge" #define RC_MAP_HISI_POPLAR "rc-hisi-poplar" #define RC_MAP_HISI_TV_DEMO "rc-hisi-tv-demo" #define RC_MAP_IMON_MCE "rc-imon-mce" -- cgit From af191a7d4783747cbc388d4573433b2911cf555c Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 19 Jun 2026 11:42:24 +0100 Subject: media: keymaps: Ensure module name matches RC_MAP_VIDEOMATE_K100 When loading a rc keymap, rc_map_get() attempts to load the kernel module using the keymap name. The rc_map RC_MAP_VIDEOMATE_K100 is defined to "rc-videomate-k100" but the module is called rc-videomate-m1f.ko, so therefore automagic loading of the keymap will fail. Rename the module to match. Signed-off-by: Sean Young Acked-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 2 +- drivers/media/rc/keymaps/rc-videomate-k100.c | 90 ++++++++++++++++++++++++++++ drivers/media/rc/keymaps/rc-videomate-m1f.c | 90 ---------------------------- 3 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 drivers/media/rc/keymaps/rc-videomate-k100.c delete mode 100644 drivers/media/rc/keymaps/rc-videomate-m1f.c diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index d04572627cdd..38b84e34b225 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -129,7 +129,7 @@ obj-$(CONFIG_RC_MAP) += \ rc-twinhan1027.o \ rc-twinhan-dtv-cab-ci.o \ rc-vega-s9x.o \ - rc-videomate-m1f.o \ + rc-videomate-k100.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ rc-videostrong-kii-pro.o \ diff --git a/drivers/media/rc/keymaps/rc-videomate-k100.c b/drivers/media/rc/keymaps/rc-videomate-k100.c new file mode 100644 index 000000000000..6664cfd0b7f3 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-videomate-k100.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* videomate-k100.h - Keytable for videomate_k100 Remote Controller + * + * keymap imported from ir-keymaps.c + * + * Copyright (c) 2010 by Pavel Osnova + */ + +#include +#include + +static struct rc_map_table videomate_k100[] = { + { 0x01, KEY_POWER }, + { 0x31, KEY_TUNER }, + { 0x33, KEY_VIDEO }, + { 0x2f, KEY_RADIO }, + { 0x30, KEY_CAMERA }, + { 0x2d, KEY_NEW }, /* TV record button */ + { 0x17, KEY_CYCLEWINDOWS }, + { 0x2c, KEY_ANGLE }, + { 0x2b, KEY_LANGUAGE }, + { 0x32, KEY_SEARCH }, /* '...' button */ + { 0x11, KEY_UP }, + { 0x13, KEY_LEFT }, + { 0x15, KEY_OK }, + { 0x14, KEY_RIGHT }, + { 0x12, KEY_DOWN }, + { 0x16, KEY_BACKSPACE }, + { 0x02, KEY_ZOOM }, /* WIN key */ + { 0x04, KEY_INFO }, + { 0x05, KEY_VOLUMEUP }, + { 0x03, KEY_MUTE }, + { 0x07, KEY_CHANNELUP }, + { 0x06, KEY_VOLUMEDOWN }, + { 0x08, KEY_CHANNELDOWN }, + { 0x0c, KEY_RECORD }, + { 0x0e, KEY_STOP }, + { 0x0a, KEY_BACK }, + { 0x0b, KEY_PLAY }, + { 0x09, KEY_FORWARD }, + { 0x10, KEY_PREVIOUS }, + { 0x0d, KEY_PAUSE }, + { 0x0f, KEY_NEXT }, + { 0x1e, KEY_NUMERIC_1 }, + { 0x1f, KEY_NUMERIC_2 }, + { 0x20, KEY_NUMERIC_3 }, + { 0x21, KEY_NUMERIC_4 }, + { 0x22, KEY_NUMERIC_5 }, + { 0x23, KEY_NUMERIC_6 }, + { 0x24, KEY_NUMERIC_7 }, + { 0x25, KEY_NUMERIC_8 }, + { 0x26, KEY_NUMERIC_9 }, + { 0x2a, KEY_NUMERIC_STAR }, /* * key */ + { 0x1d, KEY_NUMERIC_0 }, + { 0x29, KEY_SUBTITLE }, /* # key */ + { 0x27, KEY_CLEAR }, + { 0x34, KEY_SCREEN }, + { 0x28, KEY_ENTER }, + { 0x19, KEY_RED }, + { 0x1a, KEY_GREEN }, + { 0x1b, KEY_YELLOW }, + { 0x1c, KEY_BLUE }, + { 0x18, KEY_TEXT }, +}; + +static struct rc_map_list videomate_k100_map = { + .map = { + .scan = videomate_k100, + .size = ARRAY_SIZE(videomate_k100), + .rc_proto = RC_PROTO_UNKNOWN, /* Legacy IR type */ + .name = RC_MAP_VIDEOMATE_K100, + } +}; + +static int __init init_rc_map_videomate_k100(void) +{ + return rc_map_register(&videomate_k100_map); +} + +static void __exit exit_rc_map_videomate_k100(void) +{ + rc_map_unregister(&videomate_k100_map); +} + +module_init(init_rc_map_videomate_k100) +module_exit(exit_rc_map_videomate_k100) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pavel Osnova "); +MODULE_DESCRIPTION("videomate-k100 remote controller keytable"); diff --git a/drivers/media/rc/keymaps/rc-videomate-m1f.c b/drivers/media/rc/keymaps/rc-videomate-m1f.c deleted file mode 100644 index 1f9be84ff27b..000000000000 --- a/drivers/media/rc/keymaps/rc-videomate-m1f.c +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* videomate-k100.h - Keytable for videomate_k100 Remote Controller - * - * keymap imported from ir-keymaps.c - * - * Copyright (c) 2010 by Pavel Osnova - */ - -#include -#include - -static struct rc_map_table videomate_k100[] = { - { 0x01, KEY_POWER }, - { 0x31, KEY_TUNER }, - { 0x33, KEY_VIDEO }, - { 0x2f, KEY_RADIO }, - { 0x30, KEY_CAMERA }, - { 0x2d, KEY_NEW }, /* TV record button */ - { 0x17, KEY_CYCLEWINDOWS }, - { 0x2c, KEY_ANGLE }, - { 0x2b, KEY_LANGUAGE }, - { 0x32, KEY_SEARCH }, /* '...' button */ - { 0x11, KEY_UP }, - { 0x13, KEY_LEFT }, - { 0x15, KEY_OK }, - { 0x14, KEY_RIGHT }, - { 0x12, KEY_DOWN }, - { 0x16, KEY_BACKSPACE }, - { 0x02, KEY_ZOOM }, /* WIN key */ - { 0x04, KEY_INFO }, - { 0x05, KEY_VOLUMEUP }, - { 0x03, KEY_MUTE }, - { 0x07, KEY_CHANNELUP }, - { 0x06, KEY_VOLUMEDOWN }, - { 0x08, KEY_CHANNELDOWN }, - { 0x0c, KEY_RECORD }, - { 0x0e, KEY_STOP }, - { 0x0a, KEY_BACK }, - { 0x0b, KEY_PLAY }, - { 0x09, KEY_FORWARD }, - { 0x10, KEY_PREVIOUS }, - { 0x0d, KEY_PAUSE }, - { 0x0f, KEY_NEXT }, - { 0x1e, KEY_NUMERIC_1 }, - { 0x1f, KEY_NUMERIC_2 }, - { 0x20, KEY_NUMERIC_3 }, - { 0x21, KEY_NUMERIC_4 }, - { 0x22, KEY_NUMERIC_5 }, - { 0x23, KEY_NUMERIC_6 }, - { 0x24, KEY_NUMERIC_7 }, - { 0x25, KEY_NUMERIC_8 }, - { 0x26, KEY_NUMERIC_9 }, - { 0x2a, KEY_NUMERIC_STAR }, /* * key */ - { 0x1d, KEY_NUMERIC_0 }, - { 0x29, KEY_SUBTITLE }, /* # key */ - { 0x27, KEY_CLEAR }, - { 0x34, KEY_SCREEN }, - { 0x28, KEY_ENTER }, - { 0x19, KEY_RED }, - { 0x1a, KEY_GREEN }, - { 0x1b, KEY_YELLOW }, - { 0x1c, KEY_BLUE }, - { 0x18, KEY_TEXT }, -}; - -static struct rc_map_list videomate_k100_map = { - .map = { - .scan = videomate_k100, - .size = ARRAY_SIZE(videomate_k100), - .rc_proto = RC_PROTO_UNKNOWN, /* Legacy IR type */ - .name = RC_MAP_VIDEOMATE_K100, - } -}; - -static int __init init_rc_map_videomate_k100(void) -{ - return rc_map_register(&videomate_k100_map); -} - -static void __exit exit_rc_map_videomate_k100(void) -{ - rc_map_unregister(&videomate_k100_map); -} - -module_init(init_rc_map_videomate_k100) -module_exit(exit_rc_map_videomate_k100) - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pavel Osnova "); -MODULE_DESCRIPTION("videomate-m1f remote controller keytable"); -- cgit From 8dac27bfa2f994ecb11f01a63641527d17d48fc1 Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 19 Jun 2026 11:56:33 +0100 Subject: media: dt-bindings: rc: Sync keymap list with latest list The list is out of sync, so add missing entries, remove obsolete entries and sort. Signed-off-by: Sean Young Acked-by: Rob Herring (Arm) Acked-by: Mauro Carvalho Chehab --- Documentation/devicetree/bindings/media/rc.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index dedc5a4b81ec..43d84296e70d 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -122,10 +122,10 @@ properties: - rc-proteus-2309 - rc-purpletv - rc-pv951 - - rc-rc5-tv - rc-rc6-mce - rc-real-audio-220-32-keys - rc-reddo + - rc-siemens-gigaset-rc20 - rc-snapstream-firefly - rc-streamzap - rc-su3000 @@ -147,11 +147,11 @@ properties: - rc-tt-1500 - rc-twinhan-dtv-cab-ci - rc-twinhan1027 + - rc-vega-s9x - rc-videomate-k100 - rc-videomate-s350 - rc-videomate-tv-pvr - rc-videostrong-kii-pro - - rc-vega-s9x - rc-wetek-hub - rc-wetek-play2 - rc-winfast -- cgit From f4a018ea58518827e3fdbfc5526c16edf5e3a574 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Mon, 29 Jun 2026 03:40:23 -0400 Subject: media: ipu-bridge: Add DMI information of Lenovo X9 to the image upside-down list The Lenovo X9 has an upside-down-mounted Sony IMX471 sensor so the image was displayed upside-down. Add the DMI information of Lenovo X9 to resolve the issue. Signed-off-by: Kate Hsuan Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 88581a4c081d..2474452b3015 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -134,6 +134,45 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + /* + * The first four characters of DMI_BOARD_NAME identify the Lenovo + * machine type/model. For example, a DMI_BOARD_NAME starting with + * "21Q6" indicates a ThinkPad X9-15. + * + * Reference: https://psref.lenovo.com/ + */ + { + /* Lenovo X9-14 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21QA"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-14 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21QB"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-15 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21Q6"), + }, + .driver_data = "SONY471A", + }, + { + /* Lenovo X9-15 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_BOARD_NAME, "21Q7"), + }, + .driver_data = "SONY471A", + }, {} /* Terminating entry */ }; -- cgit From f6ef7f97175e97622c2e65d44005660fe1787e19 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Mon, 29 Jun 2026 03:40:24 -0400 Subject: media: ipu-bridge: Add Sony IMX471 for Lenovo X1 Carbon G14 The HID for Sony IMX471 is TBE20A0 on Lenovo X1 Carbon G14. Signed-off-by: Kate Hsuan Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 2474452b3015..8ddfab357922 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -97,6 +97,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000), /* Sony IMX471 */ IPU_SENSOR_CONFIG("SONY471A", 1, 200000000), + /* Sony IMX471 (found on Lenovo X1 Carbon G14) */ + IPU_SENSOR_CONFIG("TBE20A0", 1, 200000000), /* Toshiba T4KA3 */ IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000), }; -- cgit From f8ae10fe4b81efb93e890e6601a92624e4b4a576 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Mon, 29 Jun 2026 03:40:25 -0400 Subject: platform: int3472: discrete: con_id vana for Sony IMX471 as power enable Update the con_id for the Sony IMX471 sensor to "vana" to serve as the power enable. Additionally, the HID values SONY471A and TBE20A0, both associated with the IMX471 image sensor, have been identified on Lenovo laptops. Signed-off-by: Kate Hsuan Reviewed-by: Tarang Raval Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/discrete.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 115bb37577a1..adff564bf3fd 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -164,6 +164,24 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { .con_id = "dvdd", .enable_time_us = 45 * USEC_PER_MSEC, }, + { /* imx471 expects "vana" as con_id for power enable */ + .hid = "SONY471A", + .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, + .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, + .con_id = "vana", + .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, + }, + { + /* + * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id + * for power enable + */ + .hid = "TBE20A0", + .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, + .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, + .con_id = "vana", + .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, + }, }; static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, -- cgit From be1589e567aeea5d32d9dd7027501657787d3ad3 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Mon, 29 Jun 2026 03:40:26 -0400 Subject: media: i2c: imx471: Add Sony IMX471 image sensor driver Add a new driver for Sony imx471 camera sensor. It is based on Jimmy Su implementation and the driver can be found in the following URL. https://github.com/intel/ipu6-drivers/commits/master/drivers/media/i2c/imx471.c This sensor can be found on Lenovo X1 Carbon G14, X9-14 and X9-15 laptops and it is a part of IPU7 solution. The driver was tested on Lenovo X1 Carbon G14, X9-14 and X9-15 laptops. Signed-off-by: Kate Hsuan Tested-by: computman Reviewed-by: Tarang Raval Signed-off-by: Sakari Ailus --- MAINTAINERS | 6 + drivers/media/i2c/Kconfig | 10 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx471.c | 957 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 974 insertions(+) create mode 100644 drivers/media/i2c/imx471.c diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..2aec6afa8108 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25260,6 +25260,12 @@ T: git git://linuxtv.org/media.git F: Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml F: drivers/media/i2c/imx415.c +SONY IMX471 SENSOR DRIVER +M: Kate Hsuan +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/i2c/imx471.c + SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky M: Alex Dubov diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 5d173e0ecf42..b7199f9f5a0c 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -287,6 +287,16 @@ config VIDEO_IMX415 To compile this driver as a module, choose M here: the module will be called imx415. +config VIDEO_IMX471 + tristate "Sony IMX471 sensor support" + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX471 camera. + + To compile this driver as a module, choose M here: the + module will be called imx471. + config VIDEO_MAX9271_LIB tristate diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index e45359efe0e4..acbd321fc12e 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o +obj-$(CONFIG_VIDEO_IMX471) += imx471.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c new file mode 100644 index 000000000000..6d358b11e96d --- /dev/null +++ b/drivers/media/i2c/imx471.c @@ -0,0 +1,957 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx471.c - imx471 sensor driver + * + * Copyright (C) 2025 Intel Corporation + * Copyright (C) 2026 Kate Hsuan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX471_REG_MODE_SELECT CCI_REG8(0x0100) +#define IMX471_MODE_STANDBY 0x00 +#define IMX471_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX471_REG_CHIP_ID CCI_REG16(0x0016) +#define IMX471_CHIP_ID 0x0471 + +/* V_TIMING internal */ +#define IMX471_REG_FLL CCI_REG16(0x0340) +#define IMX471_FLL_MAX 0xffff + +/* Exposure control */ +#define IMX471_REG_EXPOSURE CCI_REG16(0x0202) +#define IMX471_EXPOSURE_MIN 1 +#define IMX471_EXPOSURE_STEP 1 +#define IMX471_EXPOSURE_DEFAULT 1270 + +/* Default exposure margin */ +#define IMX471_EXPOSURE_MARGIN 18 + +/* Analog gain control */ +#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204) +#define IMX471_ANA_GAIN_MIN 0 +#define IMX471_ANA_GAIN_MAX 800 +#define IMX471_ANA_GAIN_STEP 1 +#define IMX471_ANA_GAIN_DEFAULT 0 + +/* Digital gain control */ +#define IMX471_REG_DPGA_USE_GLOBAL_GAIN CCI_REG16(0x3ff9) +#define IMX471_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) +#define IMX471_DGTL_GAIN_MIN 256 +#define IMX471_DGTL_GAIN_MAX 4095 +#define IMX471_DGTL_GAIN_STEP 1 +#define IMX471_DGTL_GAIN_DEFAULT 256 + +/* HFLIP and VFLIP control */ +#define IMX471_REG_ORIENTATION CCI_REG8(0x0101) + +/* Test Pattern Control */ +#define IMX471_REG_TEST_PATTERN CCI_REG8(0x0600) + +/* default link frequency and external clock */ +#define IMX471_LINK_FREQ_DEFAULT 200000000LL +#define IMX471_EXT_CLK 19200000 + +/* PLL */ +#define IMX471_REG_VTPXCK_DIV CCI_REG8(0x0301) +#define IMX471_REG_VTSYCK_DIV CCI_REG8(0x0303) +#define IMX471_REG_PREPLLCK_VT_DIV CCI_REG8(0x0305) +#define IMX471_REG_PLL_VT_MPY CCI_REG16(0x0306) +#define IMX471_REG_OPPXCK_DIV CCI_REG8(0x0309) +#define IMX471_REG_OPSYCK_DIV CCI_REG8(0x030b) +#define IMX471_REG_PLL_MULT_DRIV CCI_REG8(0x0310) +#define IMX471_PLL_SINGLE 0 +#define IMX471_PLL_DUAL 1 + +/* IMX471 native and active pixel array size */ +#define IMX471_NATIVE_WIDTH 4672 +#define IMX471_NATIVE_HEIGHT 3512 +#define IMX471_PIXEL_ARRAY_LEFT 8 +#define IMX471_PIXEL_ARRAY_TOP 8 +#define IMX471_PIXEL_ARRAY_WIDTH 4656 +#define IMX471_PIXEL_ARRAY_HEIGHT 3496 + +#define IMX471_REG_EXCK_FREQ CCI_REG16(0x0136) + +#define IMX471_REG_CSI_DATA_FORMAT CCI_REG16(0x0112) +#define IMX471_CSI_DATA_FORMAT_RAW10 0x0a0a + +#define IMX471_REG_CSI_LANE_MODE CCI_REG8(0x0114) +#define IMX471_CSI_2_LANE_MODE 1 +#define IMX471_CSI_4_LANE_MODE 3 + +#define IMX471_REG_X_ADD_STA CCI_REG16(0x0344) +#define IMX471_REG_Y_ADD_STA CCI_REG16(0x0346) +#define IMX471_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX471_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX471_REG_X_OUTPUT_SIZE CCI_REG16(0x034c) +#define IMX471_REG_Y_OUTPUT_SIZE CCI_REG16(0x034e) +#define IMX471_REG_X_EVEN_INC CCI_REG8(0x0381) +#define IMX471_REG_X_ODD_INC CCI_REG8(0x0383) +#define IMX471_REG_Y_EVEN_INC CCI_REG8(0x0385) +#define IMX471_REG_Y_ODD_INC CCI_REG8(0x0387) + +#define IMX471_REG_DIG_CROP_X_OFFSET CCI_REG16(0x0408) +#define IMX471_REG_DIG_CROP_Y_OFFSET CCI_REG16(0x040a) +#define IMX471_REG_DIG_CROP_WIDTH CCI_REG16(0x040c) +#define IMX471_REG_DIG_CROP_HEIGHT CCI_REG16(0x040e) + +/* Binning mode */ +#define IMX471_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX471_BINNING_NONE 0 +#define IMX471_BINNING_ENABLE 1 +#define IMX471_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX471_REG_BINNING_WEIGHTING CCI_REG8(0x0902) + +#define to_imx471(_sd) container_of_const(_sd, struct imx471, sd) + +static const char * const imx471_supply_name[] = { + "vana", +}; + +struct imx471_mode { + u32 width; + u32 height; + + /* V-timing */ + u32 fll_def; + u32 fll_min; + + /* H-timing */ + u32 llp; + + const struct cci_reg_sequence *default_mode_regs; + unsigned int default_mode_regs_length; +}; + +struct imx471 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *exposure; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx471_supply_name)]; + struct clk *img_clk; + + struct device *dev; + struct regmap *regmap; +}; + +static const struct cci_reg_sequence imx471_global_regs[] = { + { IMX471_REG_EXCK_FREQ, 0x1333 }, + { CCI_REG8(0x3c7e), 0x08 }, + { CCI_REG8(0x3c7f), 0x05 }, + { CCI_REG8(0x3e35), 0x00 }, + { CCI_REG8(0x3e36), 0x00 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3f7f), 0x01 }, + { CCI_REG8(0x4431), 0x04 }, + { CCI_REG8(0x531c), 0x01 }, + { CCI_REG8(0x531d), 0x02 }, + { CCI_REG8(0x531e), 0x04 }, + { CCI_REG8(0x5928), 0x00 }, + { CCI_REG8(0x5929), 0x2f }, + { CCI_REG8(0x592a), 0x00 }, + { CCI_REG8(0x592b), 0x85 }, + { CCI_REG8(0x592c), 0x00 }, + { CCI_REG8(0x592d), 0x32 }, + { CCI_REG8(0x592e), 0x00 }, + { CCI_REG8(0x592f), 0x88 }, + { CCI_REG8(0x5930), 0x00 }, + { CCI_REG8(0x5931), 0x3d }, + { CCI_REG8(0x5932), 0x00 }, + { CCI_REG8(0x5933), 0x93 }, + { CCI_REG8(0x5938), 0x00 }, + { CCI_REG8(0x5939), 0x24 }, + { CCI_REG8(0x593a), 0x00 }, + { CCI_REG8(0x593b), 0x7a }, + { CCI_REG8(0x593c), 0x00 }, + { CCI_REG8(0x593d), 0x24 }, + { CCI_REG8(0x593e), 0x00 }, + { CCI_REG8(0x593f), 0x7a }, + { CCI_REG8(0x5940), 0x00 }, + { CCI_REG8(0x5941), 0x2f }, + { CCI_REG8(0x5942), 0x00 }, + { CCI_REG8(0x5943), 0x85 }, + { CCI_REG8(0x5f0e), 0x6e }, + { CCI_REG8(0x5f11), 0xc6 }, + { CCI_REG8(0x5f17), 0x5e }, + { CCI_REG8(0x7990), 0x01 }, + { CCI_REG8(0x7993), 0x5d }, + { CCI_REG8(0x7994), 0x5d }, + { CCI_REG8(0x7995), 0xa1 }, + { CCI_REG8(0x799a), 0x01 }, + { CCI_REG8(0x799d), 0x00 }, + { CCI_REG8(0x8169), 0x01 }, + { CCI_REG8(0x8359), 0x01 }, + { CCI_REG8(0x9302), 0x1e }, + { CCI_REG8(0x9306), 0x1f }, + { CCI_REG8(0x930a), 0x26 }, + { CCI_REG8(0x930e), 0x23 }, + { CCI_REG8(0x9312), 0x23 }, + { CCI_REG8(0x9316), 0x2c }, + { CCI_REG8(0x9317), 0x19 }, + { CCI_REG8(0xb046), 0x01 }, + { CCI_REG8(0xb048), 0x01 }, +}; + +static const struct cci_reg_sequence mode_1928x1088_regs[] = { + { IMX471_REG_CSI_DATA_FORMAT, IMX471_CSI_DATA_FORMAT_RAW10 }, + { IMX471_REG_CSI_LANE_MODE, IMX471_CSI_4_LANE_MODE }, + { IMX471_REG_X_ADD_STA, 8 }, + { IMX471_REG_Y_ADD_STA, 408 }, + { IMX471_REG_X_ADD_END, 4647 }, + { IMX471_REG_Y_ADD_END, 3051 }, + { IMX471_REG_X_EVEN_INC, 1 }, + { IMX471_REG_X_ODD_INC, 1 }, + { IMX471_REG_Y_EVEN_INC, 1 }, + { IMX471_REG_Y_ODD_INC, 1 }, + { IMX471_REG_BINNING_MODE, IMX471_BINNING_ENABLE }, + { IMX471_REG_BINNING_TYPE, 0x22 }, + { IMX471_REG_BINNING_WEIGHTING, 0x08 }, + { IMX471_REG_DIG_CROP_X_OFFSET, 208 }, + { IMX471_REG_DIG_CROP_Y_OFFSET, 108 }, + { IMX471_REG_DIG_CROP_WIDTH, 1928 }, + { IMX471_REG_DIG_CROP_HEIGHT, 1088 }, + { IMX471_REG_X_OUTPUT_SIZE, 1928 }, + { IMX471_REG_Y_OUTPUT_SIZE, 1088 }, + { IMX471_REG_VTPXCK_DIV, 0x06 }, + { IMX471_REG_VTSYCK_DIV, 0x02 }, + { IMX471_REG_PREPLLCK_VT_DIV, 0x02 }, + { IMX471_REG_PLL_VT_MPY, 0x0079 }, + { IMX471_REG_OPSYCK_DIV, 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x030e), 0x00 }, + { CCI_REG8(0x030f), 0x53 }, + { IMX471_REG_PLL_MULT_DRIV, IMX471_PLL_DUAL }, + { CCI_REG8(0x3f4c), 0x81 }, + { CCI_REG8(0x3f4d), 0x81 }, + { CCI_REG8(0x3f78), 0x01 }, + { CCI_REG8(0x3f79), 0x31 }, + { CCI_REG8(0x3ffe), 0x00 }, + { CCI_REG8(0x3fff), 0x8a }, + { CCI_REG8(0x5f0a), 0xb6 }, +}; + +static const char * const imx471_test_pattern_menu[] = { + "Disabled", + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", +}; + +static const s64 link_freq_menu_items[] = { + IMX471_LINK_FREQ_DEFAULT, +}; + +/* + * The Bayer formats for the flipping. + * - no flip + * - h flip + * - v flip + * - h and v flips + */ +static const u32 imx471_hv_flips_bayer_order[] = { + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, +}; + +static const struct imx471_mode imx471_modes[] = { + { + .width = 1928, + .height = 1088, + .fll_def = 1308, + .fll_min = 1308, + .llp = 2328, + .default_mode_regs = mode_1928x1088_regs, + .default_mode_regs_length = ARRAY_SIZE(mode_1928x1088_regs), + }, +}; + +static int imx471_get_regulators(struct device *dev, struct imx471 *sensor) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(imx471_supply_name); i++) + sensor->supplies[i].supply = imx471_supply_name[i]; + + return devm_regulator_bulk_get(dev, ARRAY_SIZE(imx471_supply_name), + sensor->supplies); +} + +static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx471 *sensor = container_of_const(ctrl->handler, + struct imx471, + ctrl_handler); + struct v4l2_subdev_state *state = + v4l2_subdev_get_locked_active_state(&sensor->sd); + const struct v4l2_mbus_framefmt *format = + v4l2_subdev_state_get_format(state, 0); + int ret; + + if (ctrl->id == V4L2_CID_VBLANK) { + s64 exposure_max = format->height + ctrl->val - + IMX471_EXPOSURE_MARGIN; + ret = __v4l2_ctrl_modify_range(sensor->exposure, + sensor->exposure->minimum, + exposure_max, + sensor->exposure->step, + exposure_max); + if (ret) + return ret; + } + + if (!pm_runtime_get_if_in_use(sensor->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_ANALOG_GAIN, + ctrl->val, NULL); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = cci_write(sensor->regmap, IMX471_REG_DIG_GAIN_GLOBAL, + ctrl->val, NULL); + break; + case V4L2_CID_EXPOSURE: + ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, + ctrl->val, &ret); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = cci_write(sensor->regmap, IMX471_REG_FLL, + format->height + ctrl->val, &ret); + break; + case V4L2_CID_TEST_PATTERN: + ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, + ctrl->val, NULL); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = cci_write(sensor->regmap, IMX471_REG_ORIENTATION, + sensor->hflip->val | sensor->vflip->val << 1, + NULL); + break; + default: + ret = -EINVAL; + dev_err(sensor->dev, "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(sensor->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx471_ctrl_ops = { + .s_ctrl = imx471_set_ctrl, +}; + +static u32 imx471_get_format_code(struct imx471 *sensor) +{ + unsigned int i; + + i = (sensor->vflip->val ? 2 : 0) | (sensor->hflip->val ? 1 : 0); + + return imx471_hv_flips_bayer_order[i]; +} + +static int imx471_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx471 *sensor = to_imx471(sd); + + if (code->index >= (ARRAY_SIZE(imx471_hv_flips_bayer_order) / 4)) + return -EINVAL; + + code->code = imx471_get_format_code(sensor); + + return 0; +} + +static int imx471_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->index >= ARRAY_SIZE(imx471_modes)) + return -EINVAL; + + fse->min_width = imx471_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = imx471_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx471_update_pad_format(struct imx471 *sensor, + const struct imx471_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.code = imx471_get_format_code(sensor); + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; +} + +static int imx471_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + int h_blank, ret; + + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->format.width, + fmt->format.height); + + imx471_update_pad_format(sensor, mode, fmt); + + *v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (media_entity_is_streaming(&sensor->sd.entity)) + return -EBUSY; + + ret = __v4l2_ctrl_modify_range(sensor->vblank, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + if (ret) + return ret; + + h_blank = mode->llp - mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ + return __v4l2_ctrl_modify_range(sensor->hblank, h_blank, + h_blank, 1, h_blank); +} + +static int imx471_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); + break; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = IMX471_NATIVE_WIDTH; + sel->r.height = IMX471_NATIVE_HEIGHT; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = IMX471_PIXEL_ARRAY_TOP; + sel->r.left = IMX471_PIXEL_ARRAY_LEFT; + sel->r.width = IMX471_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX471_PIXEL_ARRAY_HEIGHT; + return 0; + } + + return -EINVAL; +} + +static int imx471_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .format = { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .width = imx471_modes[0].width, + .height = imx471_modes[0].height, + }, + }; + + return imx471_set_pad_format(sd, sd_state, &fmt); +} + +static int imx471_identify_module(struct imx471 *sensor) +{ + int ret; + u64 val; + + ret = cci_read(sensor->regmap, IMX471_REG_CHIP_ID, &val, NULL); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to read chip id\n"); + + if (val != IMX471_CHIP_ID) + return dev_err_probe(sensor->dev, -EIO, + "chip id mismatch: %x!=%llx\n", + IMX471_CHIP_ID, val); + + return 0; +} + +static int imx471_power_off(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + + clk_disable_unprepare(sensor->img_clk); + gpiod_set_value_cansleep(sensor->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + + return 0; +} + +static int imx471_power_on(struct device *dev) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + if (ret < 0) { + dev_err(dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(sensor->img_clk); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(imx471_supply_name), + sensor->supplies); + dev_err(dev, "failed to enable imaging clock: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(sensor->reset_gpio, 0); + + usleep_range(10000, 15000); + + return 0; +} + +static int imx471_enable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + const struct imx471_mode *mode; + struct v4l2_mbus_framefmt *fmt; + int ret; + + ret = pm_runtime_resume_and_get(sensor->dev); + if (ret) + return ret; + + ret = imx471_identify_module(sensor); + if (ret) + goto error_powerdown; + + ret = cci_multi_reg_write(sensor->regmap, imx471_global_regs, + ARRAY_SIZE(imx471_global_regs), NULL); + if (ret) { + dev_err(sensor->dev, "failed to set global settings: %d\n", + ret); + goto error_powerdown; + } + + fmt = v4l2_subdev_state_get_format(state, 0); + mode = v4l2_find_nearest_size(imx471_modes, ARRAY_SIZE(imx471_modes), + width, height, fmt->width, fmt->height); + + ret = cci_multi_reg_write(sensor->regmap, mode->default_mode_regs, + mode->default_mode_regs_length, NULL); + if (ret) { + dev_err(sensor->dev, "failed to set mode: %d\n", ret); + goto error_powerdown; + } + + ret = cci_write(sensor->regmap, IMX471_REG_DPGA_USE_GLOBAL_GAIN, 1, + NULL); + if (ret) + goto error_powerdown; + + ret = __v4l2_ctrl_handler_setup(&sensor->ctrl_handler); + if (ret) + goto error_powerdown; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STREAMING, NULL); + if (ret) + goto error_powerdown; + + __v4l2_ctrl_grab(sensor->vflip, true); + __v4l2_ctrl_grab(sensor->hflip, true); + + return ret; + +error_powerdown: + pm_runtime_put(sensor->dev); + + return ret; +} + +static int imx471_disable_stream(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) +{ + struct imx471 *sensor = to_imx471(sd); + int ret; + + ret = cci_write(sensor->regmap, IMX471_REG_MODE_SELECT, + IMX471_MODE_STANDBY, NULL); + pm_runtime_put(sensor->dev); + + if (ret) + dev_err(sensor->dev, + "failed to disable stream with return value: %d\n", + ret); + + __v4l2_ctrl_grab(sensor->vflip, false); + __v4l2_ctrl_grab(sensor->hflip, false); + + return 0; +} + +static const struct v4l2_subdev_video_ops imx471_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx471_pad_ops = { + .enum_mbus_code = imx471_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = imx471_set_pad_format, + .get_selection = imx471_get_selection, + .enum_frame_size = imx471_enum_frame_size, + .enable_streams = imx471_enable_stream, + .disable_streams = imx471_disable_stream, +}; + +static const struct v4l2_subdev_ops imx471_subdev_ops = { + .video = &imx471_video_ops, + .pad = &imx471_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx471_internal_ops = { + .init_state = imx471_init_state, +}; + +static int imx471_init_controls(struct imx471 *sensor) +{ + const struct imx471_mode *mode = &imx471_modes[0]; + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_ctrl *link_freq; + s64 exposure_max, hblank; + u64 pixel_rate; + int ret; + + ret = v4l2_fwnode_device_parse(sensor->dev, &props); + if (ret) { + dev_err(sensor->dev, "failed to parse fwnode: %d\n", ret); + return ret; + } + + ctrl_hdlr = &sensor->ctrl_handler; + v4l2_ctrl_handler_init(ctrl_hdlr, 12); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx471_ctrl_ops, &props); + + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq_menu_items) - 1, + 0, + link_freq_menu_items); + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = div_u64(IMX471_LINK_FREQ_DEFAULT * 2 * 4, 10); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + sensor->vblank = v4l2_ctrl_new_std(ctrl_hdlr, + &imx471_ctrl_ops, + V4L2_CID_VBLANK, + mode->fll_min - mode->height, + IMX471_FLL_MAX - mode->height, + 1, + mode->fll_def - mode->height); + + hblank = mode->llp - mode->width; + sensor->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + + /* fll >= exposure time + adjust parameter (default value is 18) */ + exposure_max = mode->fll_def - IMX471_EXPOSURE_MARGIN; + sensor->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX471_EXPOSURE_MIN, exposure_max, + IMX471_EXPOSURE_STEP, + IMX471_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX471_ANA_GAIN_MIN, IMX471_ANA_GAIN_MAX, + IMX471_ANA_GAIN_STEP, IMX471_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX471_DGTL_GAIN_MIN, IMX471_DGTL_GAIN_MAX, + IMX471_DGTL_GAIN_STEP, IMX471_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx471_test_pattern_menu) - 1, + 0, 0, imx471_test_pattern_menu); + + sensor->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + sensor->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx471_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (ctrl_hdlr->error) { + dev_err(sensor->dev, "%s control init failed: %d\n", + __func__, ctrl_hdlr->error); + goto error; + } + + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + sensor->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ctrl_hdlr->error; +} + +static int imx471_check_hwcfg(struct imx471 *sensor) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + struct fwnode_handle *ep, *fwnode = dev_fwnode(sensor->dev); + unsigned long link_freq_bitmap; + struct clk *clk; + int ret; + + clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(sensor->dev, PTR_ERR(clk), + "can't get clock frequency\n"); + + if (clk_get_rate(clk) != IMX471_EXT_CLK) + return dev_err_probe(sensor->dev, -EINVAL, + "external clock %lu is not supported\n", + clk_get_rate(clk)); + + ep = fwnode_graph_get_endpoint_by_id(fwnode, 0, 0, 0); + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + fwnode_handle_put(ep); + if (ret) + return dev_err_probe(sensor->dev, ret, + "parsing endpoint failed\n"); + + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { + ret = dev_err_probe(sensor->dev, -EINVAL, + "number of CSI2 data lanes %u is not supported\n", + bus_cfg.bus.mipi_csi2.num_data_lanes); + goto done_endpoint_free; + } + + ret = v4l2_link_freq_to_bitmap(sensor->dev, bus_cfg.link_frequencies, + bus_cfg.nr_of_link_frequencies, + link_freq_menu_items, + ARRAY_SIZE(link_freq_menu_items), + &link_freq_bitmap); + +done_endpoint_free: + v4l2_fwnode_endpoint_free(&bus_cfg); + + return ret; +} + +static int imx471_probe(struct i2c_client *client) +{ + struct imx471 *sensor; + int ret; + + sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); + if (!sensor) + return dev_err_probe(&client->dev, -ENOMEM, + "failed to allocate memory\n"); + + sensor->dev = &client->dev; + + ret = imx471_check_hwcfg(sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to check hwcfg: %d\n", ret); + + ret = imx471_get_regulators(sensor->dev, sensor); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to get regulators\n"); + + sensor->reset_gpio = devm_gpiod_get_optional(sensor->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(sensor->reset_gpio)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset_gpio), + "failed to get reset gpio\n"); + + sensor->img_clk = devm_v4l2_sensor_clk_get(sensor->dev, NULL); + if (IS_ERR(sensor->img_clk)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->img_clk), + "failed to get imaging clock\n"); + + v4l2_i2c_subdev_init(&sensor->sd, client, &imx471_subdev_ops); + + sensor->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(sensor->regmap)) + return dev_err_probe(sensor->dev, PTR_ERR(sensor->regmap), + "failed to initialize CCI\n"); + + ret = imx471_power_on(sensor->dev); + if (ret) + return dev_err_probe(sensor->dev, ret, + "failed to power on\n"); + + ret = imx471_identify_module(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to find sensor: %d\n", + ret); + goto error_power_off; + } + + ret = imx471_init_controls(sensor); + if (ret) { + dev_err_probe(sensor->dev, ret, "failed to init controls: %d\n", + ret); + goto error_power_off; + } + + sensor->sd.internal_ops = &imx471_internal_ops; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->pad.flags = MEDIA_PAD_FL_SOURCE; + sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); + if (ret) { + dev_err_probe(sensor->dev, ret, + "failed to init entity pads: %d\n", ret); + goto error_v4l2_ctrl_handler_free; + } + + sensor->sd.state_lock = sensor->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&sensor->sd); + if (ret < 0) { + dev_err_probe(sensor->dev, ret, "failed to init subdev: %d\n", + ret); + goto error_media_entity_pm; + } + + pm_runtime_set_active(sensor->dev); + pm_runtime_enable(sensor->dev); + + ret = v4l2_async_register_subdev_sensor(&sensor->sd); + if (ret < 0) + goto error_v4l2_subdev_cleanup; + + pm_runtime_idle(sensor->dev); + + return 0; + +error_v4l2_subdev_cleanup: + pm_runtime_disable(sensor->dev); + pm_runtime_set_suspended(sensor->dev); + v4l2_subdev_cleanup(&sensor->sd); + +error_media_entity_pm: + media_entity_cleanup(&sensor->sd.entity); + +error_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); + +error_power_off: + imx471_power_off(sensor->dev); + + return ret; +} + +static void imx471_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + + if (!pm_runtime_status_suspended(&client->dev)) { + imx471_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); + } +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx471_pm_ops, imx471_power_off, + imx471_power_on, NULL); + +static const struct acpi_device_id imx471_acpi_ids[] __maybe_unused = { + { "SONY471A" }, + { "TBE20A0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, imx471_acpi_ids); + +static struct i2c_driver imx471_i2c_driver = { + .driver = { + .name = "imx471", + .acpi_match_table = ACPI_PTR(imx471_acpi_ids), + .pm = pm_sleep_ptr(&imx471_pm_ops), + }, + .probe = imx471_probe, + .remove = imx471_remove, +}; +module_i2c_driver(imx471_i2c_driver); + +MODULE_AUTHOR("Jimmy Su "); +MODULE_AUTHOR("Serin Yeh "); +MODULE_AUTHOR("Kate Hsuan "); +MODULE_DESCRIPTION("Sony imx471 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit From 2e7c49b8ba0aea7c3dff68c2919a5f4a402bc23e Mon Sep 17 00:00:00 2001 From: Arun T Date: Sat, 4 Jul 2026 22:48:52 +0530 Subject: ACPI: scan: Honor ACPI _DEP dependency for NVL CVS Add INTC10FA to the ACPI dependency honor list so the CVS driver is loaded before dependent camera devices are probed on NVL platforms. This matches the camera dependency handling already used for IVSC-based platforms and ensures the camera streaming path is initialized before sensor access or pipeline setup depends on it. Signed-off-by: Arun T Acked-by: Rafael J. Wysocki (Intel) Signed-off-by: Sakari Ailus --- drivers/acpi/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 9a7ac2eb9ce0..21937912a1a5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -862,6 +862,7 @@ static const char * const acpi_honor_dep_ids[] = { "INTC10DE", /* CVS (LNL) driver must be loaded to allow camera streaming */ "INTC10E0", /* CVS (ARL) driver must be loaded to allow camera streaming */ "INTC10E1", /* CVS (PTL) driver must be loaded to allow camera streaming */ + "INTC10FA", /* CVS (NVL) driver must be loaded to allow camera streaming */ "RSCV0001", /* RISC-V PLIC */ "RSCV0002", /* RISC-V APLIC */ "RSCV0005", /* RISC-V SBI MPXY MBOX */ -- cgit From 73ebbc5cf0a06783150d3fbf03fabce7b1e0ab58 Mon Sep 17 00:00:00 2001 From: Arun T Date: Sat, 4 Jul 2026 22:48:53 +0530 Subject: media: pci: ipu-bridge: Add NVL CVS ACPI ID Add INTC10FA to the IPU bridge ACPI match table so the bridge can recognize NVL CVS devices. This enables the camera bridge path to bind on NVL platforms where the CVS device is exposed through the INTC10FA ACPI ID. Signed-off-by: Arun T Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 8ddfab357922..50818a10168e 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -212,6 +212,7 @@ static const struct acpi_device_id ivsc_acpi_ids[] = { { "INTC10DE" }, /* LNL */ { "INTC10E0" }, /* ARL */ { "INTC10E1" }, /* PTL */ + { "INTC10FA" }, /* NVL */ }; static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev) -- cgit From a8fec4b13dd50bd2abbfa549b02684970b200a2b Mon Sep 17 00:00:00 2001 From: Arun T Date: Sat, 4 Jul 2026 22:48:54 +0530 Subject: media: i2c: cvs: Add IPU8 PCI device ID Add the NVL IPU8 PCI device ID to the CVS driver match table so the CVS driver can bind to IPU8 camera hardware. Signed-off-by: Arun T Signed-off-by: Sakari Ailus --- drivers/media/i2c/cvs/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c index 4282f33c7295..8270d93f1abe 100644 --- a/drivers/media/i2c/cvs/core.c +++ b/drivers/media/i2c/cvs/core.c @@ -32,6 +32,7 @@ #define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */ #define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */ +#define PCI_DEVICE_ID_INTEL_IPU8 0xd719 /* NVL */ /* * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h. @@ -40,6 +41,7 @@ static const struct pci_device_id icvs_ipu7_tbl[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU8) }, { } }; -- cgit From 4ececf7d42f7b97698cf1b3317ec849074551f4f Mon Sep 17 00:00:00 2001 From: Arun T Date: Sat, 4 Jul 2026 22:48:55 +0530 Subject: media: i2c: cvs: Add NVL ACPI ID Add INTC10FA to the CVS ACPI match table so the driver can bind to CVS devices exposed on NVL platforms. Signed-off-by: Arun T Signed-off-by: Sakari Ailus --- drivers/media/i2c/cvs/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c index 8270d93f1abe..7486815b16bd 100644 --- a/drivers/media/i2c/cvs/core.c +++ b/drivers/media/i2c/cvs/core.c @@ -965,6 +965,7 @@ static const struct acpi_device_id intel_cvs_acpi_match[] = { { "INTC10DE" }, /* LNL */ { "INTC10E0" }, /* ARL */ { "INTC10E1" }, /* PTL */ + { "INTC10FA" }, /* NVL */ { } }; MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match); -- cgit From 58ca8a0bff9e78841a39863474b96e59ef60da19 Mon Sep 17 00:00:00 2001 From: Martin Hecht Date: Fri, 22 May 2026 13:41:12 +0200 Subject: media: i2c: alvium: Fix: Correct name of register in alvium_set_ctrl_auto_exposure Write value for auto-exposure into correct register REG_BCRM_EXPOSURE_AUTO_RW instead of wrong register REG_BCRM_WHITE_BALANCE_AUTO_RW. Fixes: 0a7af872915e ("media: i2c: Add support for alvium camera") Cc: stable@vger.kernel.org Signed-off-by: Martin Hecht Signed-off-by: Sakari Ailus --- drivers/media/i2c/alvium-csi2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c index dd991c2ee700..f51f9b987759 100644 --- a/drivers/media/i2c/alvium-csi2.c +++ b/drivers/media/i2c/alvium-csi2.c @@ -1290,7 +1290,7 @@ static int alvium_set_ctrl_auto_exposure(struct alvium_dev *alvium, bool on) struct device *dev = &alvium->i2c_client->dev; int ret; - ret = alvium_write_hshake(alvium, REG_BCRM_WHITE_BALANCE_AUTO_RW, + ret = alvium_write_hshake(alvium, REG_BCRM_EXPOSURE_AUTO_RW, on ? 0x02 : 0x00); if (ret) { dev_err(dev, "Fail to set autoexposure reg\n"); -- cgit From 843644e1c3347670498d247d7cd20dff1569181c Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 14:00:30 +0530 Subject: staging: media: ipu7: fix pm_runtime refcount leak in ipu7_init_fw_code_region_by_sys() ipu7_init_fw_code_region_by_sys() calls pm_runtime_get_sync() before accessing the firmware code region. If resuming the device fails, pm_runtime_get_sync() leaves the runtime PM usage count incremented, but the error path returns without dropping the reference. Use pm_runtime_resume_and_get() instead, which balances the usage count automatically on failure and avoids the leak. The ipu6 driver uses pm_runtime_resume_and_get() in the equivalent location. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Signed-off-by: Vidhu Sarwal Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index 310e3f24e571..056af3a07507 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2343,7 +2343,7 @@ static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys, return ret; } - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { dev_err(dev, "Failed to get runtime PM\n"); return ret; -- cgit From b298b80814dd0fc3cb1c8c0e0082fc14fdb5fecf Mon Sep 17 00:00:00 2001 From: Vidhu Sarwal Date: Sat, 4 Jul 2026 14:00:31 +0530 Subject: staging: media: ipu7: fix pm_runtime refcount leak in ipu7_resume() ipu7_resume() calls pm_runtime_get_sync() before resuming the device. If the runtime PM resume fails, the usage count remains incremented, but the error path returns without dropping the reference. Use pm_runtime_resume_and_get() instead, which balances the usage count on failure and avoids the leak. Keep returning 0 on error, as resume callbacks should not propagate failures to the PM core, matching the behaviour of the ipu6 driver. Fixes: b7fe4c0019b1 ("media: staging/ipu7: add Intel IPU7 PCI device driver") Signed-off-by: Vidhu Sarwal Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c index 056af3a07507..48a35bda4237 100644 --- a/drivers/staging/media/ipu7/ipu7.c +++ b/drivers/staging/media/ipu7/ipu7.c @@ -2702,7 +2702,7 @@ static int ipu7_resume(struct device *dev) if (ret) dev_err(dev, "IPC reset protocol failed!\n"); - ret = pm_runtime_get_sync(&isp->psys->auxdev.dev); + ret = pm_runtime_resume_and_get(&isp->psys->auxdev.dev); if (ret < 0) { dev_err(dev, "Failed to get runtime PM\n"); return 0; -- cgit From 94971ba0592ca77ec99b292226a4b398763315b8 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 8 Jul 2026 18:35:33 +0530 Subject: media: i2c: ov02a10: fix endpoint parsing use-after-free The ov02a10_check_hwcfg() function calls fwnode_handle_put(ep) immediately after allocating and parsing the endpoint. However, it subsequently calls fwnode_property_read_u32() using the same 'ep' handle, leading to a potential use-after-free. Additionally, reading the optional 'ovti,mipi-clock-voltage' property used to overwrite the 'ret' variable. If the property was missing, 'ret' would become negative, and this failure code would be incorrectly returned at the end of the function, causing probe to fail entirely. Fix the use-after-free by moving fwnode_property_read_u32() before the endpoint is parsed and freed. Avoid the error leak by not assigning the result of fwnode_property_read_u32() to 'ret'. Fixes: 91807efbe8ec ("media: i2c: add OV02A10 image sensor driver") Cc: stable@vger.kernel.org Signed-off-by: Biren Pandya Reviewed-by: Vladimir Zapolskiy Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov02a10.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c index 143dcfe10445..0150e4d296af 100644 --- a/drivers/media/i2c/ov02a10.c +++ b/drivers/media/i2c/ov02a10.c @@ -820,18 +820,16 @@ static int ov02a10_check_hwcfg(struct device *dev, struct ov02a10 *ov02a10) if (!ep) return -ENXIO; + /* Optional indication of MIPI clock voltage unit */ + if (!fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", + &clk_volt)) + ov02a10->mipi_clock_voltage = clk_volt; + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); fwnode_handle_put(ep); if (ret) return ret; - /* Optional indication of MIPI clock voltage unit */ - ret = fwnode_property_read_u32(ep, "ovti,mipi-clock-voltage", - &clk_volt); - - if (!ret) - ov02a10->mipi_clock_voltage = clk_volt; - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { if (link_freq_menu_items[i] == -- cgit From 22790c57c64068677efffc52b1c9f0005e1cc512 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 8 Jul 2026 18:27:22 +0530 Subject: media: i2c: ov7740: fix unbalanced pm_runtime_get_sync in remove The ov7740_remove() function unconditionally called pm_runtime_get_sync() but completely ignored the return value. If the device was already in an error state or disconnected, this could lead to an unbalanced PM runtime usage count or attempt to communicate with an unresponsive device. Disable Runtime PM first, then see if the device is active, and if so, power it off. Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov7740.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index c2e02f191816..2d29147c0f64 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1122,12 +1122,10 @@ static void ov7740_remove(struct i2c_client *client) v4l2_async_unregister_subdev(sd); ov7740_free_controls(ov7740); - pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + ov7740_set_power(ov7740, 0); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); - - ov7740_set_power(ov7740, 0); } static int __maybe_unused ov7740_runtime_suspend(struct device *dev) -- cgit From 5d1b3dea5a44124bab6c14a2d71b977dabed54e7 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 8 Jul 2026 18:27:23 +0530 Subject: media: i2c: ov7740: fix use-after-destroy in remove The ov7740_remove() function had a severe teardown order bug where it destroyed the driver's mutex before freeing the V4L2 control handler which relies on that mutex, leading to a use-after-destroy kernel panic. Furthermore, the driver explicitly called v4l2_ctrl_handler_free() and mutex_destroy() sequentially, but then called ov7740_free_controls() which invokes both of them a second time, resulting in a double-free. This patch fixes the issue by unregistering the subdevice first, and relying exclusively on ov7740_free_controls() to safely tear down the mutex and control handler in the correct order. Fixes: 39c5c4471b8d ("media: i2c: Add the ov7740 image sensor driver") Cc: stable@vger.kernel.org Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov7740.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 2d29147c0f64..b4e14171556f 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -1116,10 +1116,8 @@ static void ov7740_remove(struct i2c_client *client) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov7740 *ov7740 = container_of(sd, struct ov7740, subdev); - mutex_destroy(&ov7740->mutex); - v4l2_ctrl_handler_free(ov7740->subdev.ctrl_handler); - media_entity_cleanup(&ov7740->subdev.entity); v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&ov7740->subdev.entity); ov7740_free_controls(ov7740); pm_runtime_disable(&client->dev); -- cgit From 447ea0c26e57c1b423426e6c46d7ff89fd66eb6b Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 3 Jul 2026 14:49:16 +0530 Subject: dt-bindings: media: i2c: Add Sony IMX678 Sony IMX678 is an 8.4 Megapixel (3856x2180) CMOS sensor, that can output pixels over MIPI CSI-2 bus. Add bindings for it. Runtime detection for mono vs color variant of this sensor is feasible by reading a register, but it's not always desirable given the sensor needs to be powered on for it, potentially flashing a privacy LED or wasting precious boot time. The manufacturer or vendor knows which sensor variant they are putting on their board, so we define the compatible property to expect the (color/mono) specific name of the sensor, along with a generic fallback. Reviewed-by: Conor Dooley Signed-off-by: Jai Luthra Signed-off-by: Sakari Ailus --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2aec6afa8108..239bff3e7528 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25266,6 +25266,12 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/i2c/imx471.c +SONY IMX678 SENSOR DRIVER +M: Jai Luthra +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml + SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky M: Alex Dubov -- cgit From bf40cc53b1e00c312046f6dd88e9054bc91865af Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Fri, 3 Jul 2026 14:49:17 +0530 Subject: media: i2c: imx678: Add driver for Sony IMX678 Add a V4L2 subdev driver for the Sony IMX678 image sensor. IMX678 is a diagonal 8.86 mm (Type 1/1.8) CMOS active pixel type solid-state image sensor with a square pixel array and 8.40 M effective pixels. The following features are supported by this driver: - MIPI RAW12 output - Monochrome and Color (Bayer filter) variants - Multiple input clock frequencies - Multiple link frequencies - VBLANK and HBLANK control for variable framerate - VFLIP and HFLIP control for flipping readout - Exposure and analogue gain control - Test pattern control Following features are not currently supported: - MIPI RAW10 output - Pixel-perfect crop reporting, accounting for the shift-by-1 when doing HFLIP/VFLIP where the sensor maintains RGGB bayer ordering Along with the ones below which depend on the new raw sensor model: - Embedded data stream - Freely configurable cropping - Increased framerate when cropping - 2x2 binning support Signed-off-by: Jai Luthra Reviewed-by: Tarang Raval [Sakari Ailus: Add an extra newline to the enum_mbus_code op.] Signed-off-by: Sakari Ailus --- MAINTAINERS | 1 + drivers/media/i2c/Kconfig | 11 + drivers/media/i2c/Makefile | 1 + drivers/media/i2c/imx678.c | 1447 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1460 insertions(+) create mode 100644 drivers/media/i2c/imx678.c diff --git a/MAINTAINERS b/MAINTAINERS index 239bff3e7528..b083e44ffce4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25271,6 +25271,7 @@ M: Jai Luthra L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml +F: drivers/media/i2c/imx678.c SONY MEMORYSTICK SUBSYSTEM M: Maxim Levitsky diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index b7199f9f5a0c..7ffccba897ae 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -297,6 +297,17 @@ config VIDEO_IMX471 To compile this driver as a module, choose M here: the module will be called imx471. +config VIDEO_IMX678 + tristate "Sony IMX678 sensor support" + depends on GPIOLIB + select V4L2_CCI_I2C + help + This is a Video4Linux2 sensor driver for the Sony + IMX678 camera. + + To compile this driver as a module, choose M here: the + module will be called imx678. + config VIDEO_MAX9271_LIB tristate diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index acbd321fc12e..d04bd5724552 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_IMX335) += imx335.o obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_VIDEO_IMX412) += imx412.o obj-$(CONFIG_VIDEO_IMX415) += imx415.o +obj-$(CONFIG_VIDEO_IMX678) += imx678.o obj-$(CONFIG_VIDEO_IMX471) += imx471.o obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o diff --git a/drivers/media/i2c/imx678.c b/drivers/media/i2c/imx678.c new file mode 100644 index 000000000000..0efbf43d2fe6 --- /dev/null +++ b/drivers/media/i2c/imx678.c @@ -0,0 +1,1447 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * V4L2 driver for Sony IMX678 + * + * Diagonal 8.86 mm (Type 1/1.8) CMOS image sensor with 8.40 M effective pixels. + * + * Copyright (C) 2026 Ideas On Board Oy. + * + * Based on Sony IMX678 driver prepared by Will Whang & Soho Enterprise Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Standby or streaming mode */ +#define IMX678_REG_MODE_SELECT CCI_REG8(0x3000) +#define IMX678_MODE_STANDBY 0x01 +#define IMX678_MODE_STREAMING 0x00 +#define IMX678_STREAM_DELAY_US 25000 +#define IMX678_STREAM_DELAY_RANGE_US 1000 + +/* XVS/XHS sync control */ +#define IMX678_REG_XMSTA CCI_REG8(0x3002) +#define IMX678_REG_XXS_DRV CCI_REG8(0x30a6) +#define IMX678_REG_XXS_OUTSEL CCI_REG8(0x30a4) + +/* Clk selection */ +#define IMX678_REG_INCK_SEL CCI_REG8(0x3014) + +/* Link Speed */ +#define IMX678_REG_DATARATE_SEL CCI_REG8(0x3015) + +/* Lane Count */ +#define IMX678_REG_LANEMODE CCI_REG8(0x3040) + +/* + * The internal readout clock runs at 74.25 MHz. In one cycle the AD reads 8 + * pixels, thus giving us a rate of 74.25 * 8 = 594 MPix/s + */ +#define IMX678_PIXEL_RATE 594000000 +#define IMX678_PIX_PER_CLK 8 + +/* VMAX - Frame Length in Lines */ +#define IMX678_REG_VMAX CCI_REG24_LE(0x3028) +#define IMX678_VMAX_MAX 0xfffff +#define IMX678_VMAX_DEFAULT 2250 + +/* HMAX - Line Length in Cycles (8 Pixels) */ +#define IMX678_REG_HMAX CCI_REG16_LE(0x302c) +#define IMX678_HMAX_MAX 0xffff + +/* SHR internal */ +#define IMX678_REG_SHR CCI_REG24_LE(0x3050) +#define IMX678_SHR_MIN 8 + +/* Exposure control */ +#define IMX678_EXPOSURE_MIN 2 +#define IMX678_EXPOSURE_STEP 1 +#define IMX678_EXPOSURE_DEFAULT 1000 + +/* + * Analogue gain control + * Range is from 0 to 100 (0dB - 30dB) with 0.3dB step size + * Values from 101 to 240 are valid but correspond to additional digital gain + * (0.3dB - 42dB) so don't expose it to userspace + */ +#define IMX678_REG_GAIN CCI_REG16_LE(0x3070) +#define IMX678_ANA_GAIN_MIN_NORMAL 0 +#define IMX678_ANA_GAIN_MAX_NORMAL 100 +#define IMX678_ANA_GAIN_STEP 1 +#define IMX678_ANA_GAIN_DEFAULT 0 + +/* Crop */ +#define IMX678_REG_WINMODE CCI_REG8(0x3018) +#define IMX678_REG_PIX_HST CCI_REG16_LE(0x303c) +#define IMX678_REG_PIX_HWIDTH CCI_REG16_LE(0x303e) +#define IMX678_REG_PIX_VST CCI_REG16_LE(0x3044) +#define IMX678_REG_PIX_VWIDTH CCI_REG16_LE(0x3046) + +/* Flip */ +#define IMX678_REG_WINMODEH CCI_REG8(0x3020) +#define IMX678_REG_WINMODEV CCI_REG8(0x3021) + +/* Sensor Identification */ +#define IMX678_REG_MONOCHROME CCI_REG8(0x4d18) +#define IMX678_TYPE BIT(0) +#define IMX678_REG_MODULE_ID CCI_REG16_LE(0x4d1c) +#define IMX678_ID 0x02a6 +#define IMX678_MODULE_ID_DELAY 80000 + +/* Common configuration registers */ +#define IMX678_REG_WDMODE CCI_REG8(0x301a) +#define IMX678_REG_ADDMODE CCI_REG8(0x301b) +#define IMX678_REG_THIN_V_EN CCI_REG8(0x301c) +#define IMX678_REG_VCMODE CCI_REG8(0x301e) +#define IMX678_REG_ADBIT CCI_REG8(0x3022) +#define IMX678_REG_MDBIT CCI_REG8(0x3023) +#define IMX678_REG_GAIN_PGC_FIDMD CCI_REG8(0x3400) + +/* Test pattern generator */ +#define IMX678_REG_TPG_EN_DUOUT CCI_REG8(0x30e0) +#define IMX678_REG_TPG_PATSEL_DUOUT CCI_REG8(0x30e2) +#define IMX678_TPG_ALL_000 0 +#define IMX678_TPG_ALL_FFF 1 +#define IMX678_TPG_ALL_555 2 +#define IMX678_TPG_ALL_AAA 3 +#define IMX678_TPG_TOG_555_AAA 4 +#define IMX678_TPG_TOG_AAA_555 5 +#define IMX678_TPG_TOG_000_555 6 +#define IMX678_TPG_TOG_555_000 7 +#define IMX678_TPG_TOG_000_FFF 8 +#define IMX678_TPG_TOG_FFF_000 9 +#define IMX678_TPG_H_COLOR_BARS 10 +#define IMX678_TPG_V_COLOR_BARS 11 +#define IMX678_REG_TPG_COLORWIDTH CCI_REG8(0x30e4) +#define IMX678_TPG_COLORWIDTH_80PIX 0 +#define IMX678_TPG_COLORWIDTH_160PIX 1 +#define IMX678_TPG_COLORWIDTH_320PIX 2 +#define IMX678_TPG_COLORWIDTH_640PIX 3 + +#define IMX678_REG_INTERFACE_SEL CCI_REG8(0x4e3c) +#define IMX678_INTERFACE_2L_4L 0x07 +#define IMX678_INTERFACE_8L_2x4L 0x7f + +/* Minimum output resolution */ +#define IMX678_PIXEL_ARRAY_MIN_WIDTH 1040 +#define IMX678_PIXEL_ARRAY_MIN_HEIGHT 956 + +/* Sensor windowing register alignment */ +#define IMX678_CROP_HWIDTH_ALIGN 16 +#define IMX678_CROP_VWIDTH_ALIGN 4 +#define IMX678_CROP_HST_ALIGN 4 +#define IMX678_CROP_VST_ALIGN 4 + +/* Subdev pads */ +#define IMX678_SOURCE_PAD 0 + +/* IMX678 native and active pixel array size. */ +static const struct v4l2_rect imx678_native_area = { + .top = 0, + .left = 0, + .width = 3857, + .height = 2201, +}; + +static const struct v4l2_rect imx678_active_area = { + .top = 20, + .left = 0, + .width = 3856, + .height = 2180, +}; + +enum imx678_type { + IMX678_COLOR = 0, + IMX678_MONOCHROME = 1, +}; + +struct imx678_model_info { + enum imx678_type type; + const u32 *codes; + unsigned int num_codes; +}; + +enum imx678_lanemode { + IMX678_LANEMODE_2L = 1, + IMX678_LANEMODE_4L = 3, +}; + +/* Link frequency setup (DDR: lane rate = 2 x link freq) */ +enum { + IMX678_LINK_FREQ_297MHZ, + IMX678_LINK_FREQ_360MHZ, + IMX678_LINK_FREQ_445MHZ, + IMX678_LINK_FREQ_594MHZ, + IMX678_LINK_FREQ_720MHZ, + IMX678_LINK_FREQ_891MHZ, + IMX678_LINK_FREQ_1039MHZ, + IMX678_LINK_FREQ_1188MHZ, +}; + +static const u8 link_freqs_reg_value[] = { + [IMX678_LINK_FREQ_297MHZ] = 0x07, + [IMX678_LINK_FREQ_360MHZ] = 0x06, + [IMX678_LINK_FREQ_445MHZ] = 0x05, + [IMX678_LINK_FREQ_594MHZ] = 0x04, + [IMX678_LINK_FREQ_720MHZ] = 0x03, + [IMX678_LINK_FREQ_891MHZ] = 0x02, + [IMX678_LINK_FREQ_1039MHZ] = 0x01, + [IMX678_LINK_FREQ_1188MHZ] = 0x00, +}; + +static const u64 link_freqs[] = { + [IMX678_LINK_FREQ_297MHZ] = 297000000, + [IMX678_LINK_FREQ_360MHZ] = 360000000, + [IMX678_LINK_FREQ_445MHZ] = 445500000, + [IMX678_LINK_FREQ_594MHZ] = 594000000, + [IMX678_LINK_FREQ_720MHZ] = 720000000, + [IMX678_LINK_FREQ_891MHZ] = 891000000, + [IMX678_LINK_FREQ_1039MHZ] = 1039500000, + [IMX678_LINK_FREQ_1188MHZ] = 1188000000, +}; + +static const u16 min_hmax_4lane[] = { + [IMX678_LINK_FREQ_297MHZ] = 1584, + [IMX678_LINK_FREQ_360MHZ] = 1320, + [IMX678_LINK_FREQ_445MHZ] = 1100, + [IMX678_LINK_FREQ_594MHZ] = 792, + [IMX678_LINK_FREQ_720MHZ] = 660, + [IMX678_LINK_FREQ_891MHZ] = 550, + [IMX678_LINK_FREQ_1039MHZ] = 550, + [IMX678_LINK_FREQ_1188MHZ] = 550, +}; + +struct imx678_inck_cfg { + u32 xclk_hz; /* platform clock rate */ + u8 inck_sel; /* value for reg */ +}; + +static const struct imx678_inck_cfg imx678_inck_table[] = { + { 74250000, 0x00 }, + { 37125000, 0x01 }, + { 72000000, 0x02 }, + { 27000000, 0x03 }, + { 24000000, 0x04 }, + { 36000000, 0x05 }, + { 18000000, 0x06 }, + { 13500000, 0x07 }, +}; + +static const char * const imx678_tpg_menu[] = { + "Disabled", + "All 000h", + "All FFFh", + "All 555h", + "All AAAh", + "Toggle 555/AAAh", + "Toggle AAA/555h", + "Toggle 000/555h", + "Toggle 555/000h", + "Toggle 000/FFFh", + "Toggle FFF/000h", + "Horizontal color bars", + "Vertical color bars", +}; + +static const int imx678_tpg_val[] = { + IMX678_TPG_ALL_000, + IMX678_TPG_ALL_000, + IMX678_TPG_ALL_FFF, + IMX678_TPG_ALL_555, + IMX678_TPG_ALL_AAA, + IMX678_TPG_TOG_555_AAA, + IMX678_TPG_TOG_AAA_555, + IMX678_TPG_TOG_000_555, + IMX678_TPG_TOG_555_000, + IMX678_TPG_TOG_000_FFF, + IMX678_TPG_TOG_FFF_000, + IMX678_TPG_H_COLOR_BARS, + IMX678_TPG_V_COLOR_BARS, +}; + +/* Common configuration */ +static const struct cci_reg_sequence common_regs[] = { + { IMX678_REG_THIN_V_EN, 0x00 }, + { IMX678_REG_VCMODE, 0x01 }, + { CCI_REG8(0x306b), 0x00 }, + { IMX678_REG_GAIN_PGC_FIDMD, 0x01 }, + { CCI_REG8(0x3460), 0x22 }, + { CCI_REG8(0x355a), 0x64 }, + { CCI_REG8(0x3a02), 0x7a }, + { CCI_REG8(0x3a10), 0xec }, + { CCI_REG8(0x3a12), 0x71 }, + { CCI_REG8(0x3a14), 0xde }, + { CCI_REG8(0x3a20), 0x2b }, + { CCI_REG8(0x3a24), 0x22 }, + { CCI_REG8(0x3a25), 0x25 }, + { CCI_REG8(0x3a26), 0x2a }, + { CCI_REG8(0x3a27), 0x2c }, + { CCI_REG8(0x3a28), 0x39 }, + { CCI_REG8(0x3a29), 0x38 }, + { CCI_REG8(0x3a30), 0x04 }, + { CCI_REG8(0x3a31), 0x04 }, + { CCI_REG8(0x3a32), 0x03 }, + { CCI_REG8(0x3a33), 0x03 }, + { CCI_REG8(0x3a34), 0x09 }, + { CCI_REG8(0x3a35), 0x06 }, + { CCI_REG8(0x3a38), 0xcd }, + { CCI_REG8(0x3a3a), 0x4c }, + { CCI_REG8(0x3a3c), 0xb9 }, + { CCI_REG8(0x3a3e), 0x30 }, + { CCI_REG8(0x3a40), 0x2c }, + { CCI_REG8(0x3a42), 0x39 }, + { CCI_REG8(0x3a4e), 0x00 }, + { CCI_REG8(0x3a52), 0x00 }, + { CCI_REG8(0x3a56), 0x00 }, + { CCI_REG8(0x3a5a), 0x00 }, + { CCI_REG8(0x3a5e), 0x00 }, + { CCI_REG8(0x3a62), 0x00 }, + { CCI_REG8(0x3a64), 0x00 }, + { CCI_REG8(0x3a6e), 0xa0 }, + { CCI_REG8(0x3a70), 0x50 }, + { CCI_REG8(0x3a8c), 0x04 }, + { CCI_REG8(0x3a8d), 0x03 }, + { CCI_REG8(0x3a8e), 0x09 }, + { CCI_REG8(0x3a90), 0x38 }, + { CCI_REG8(0x3a91), 0x42 }, + { CCI_REG8(0x3a92), 0x3c }, + { CCI_REG8(0x3b0e), 0xf3 }, + { CCI_REG8(0x3b12), 0xe5 }, + { CCI_REG8(0x3b27), 0xc0 }, + { CCI_REG8(0x3b2e), 0xef }, + { CCI_REG8(0x3b30), 0x6a }, + { CCI_REG8(0x3b32), 0xf6 }, + { CCI_REG8(0x3b36), 0xe1 }, + { CCI_REG8(0x3b3a), 0xe8 }, + { CCI_REG8(0x3b5a), 0x17 }, + { CCI_REG8(0x3b5e), 0xef }, + { CCI_REG8(0x3b60), 0x6a }, + { CCI_REG8(0x3b62), 0xf6 }, + { CCI_REG8(0x3b66), 0xe1 }, + { CCI_REG8(0x3b6a), 0xe8 }, + { CCI_REG8(0x3b88), 0xec }, + { CCI_REG8(0x3b8a), 0xed }, + { CCI_REG8(0x3b94), 0x71 }, + { CCI_REG8(0x3b96), 0x72 }, + { CCI_REG8(0x3b98), 0xde }, + { CCI_REG8(0x3b9a), 0xdf }, + { CCI_REG8(0x3c0f), 0x06 }, + { CCI_REG8(0x3c10), 0x06 }, + { CCI_REG8(0x3c11), 0x06 }, + { CCI_REG8(0x3c12), 0x06 }, + { CCI_REG8(0x3c13), 0x06 }, + { CCI_REG8(0x3c18), 0x20 }, + { CCI_REG8(0x3c37), 0x10 }, + { CCI_REG8(0x3c3a), 0x7a }, + { CCI_REG8(0x3c40), 0xf4 }, + { CCI_REG8(0x3c48), 0xe6 }, + { CCI_REG8(0x3c54), 0xce }, + { CCI_REG8(0x3c56), 0xd0 }, + { CCI_REG8(0x3c6c), 0x53 }, + { CCI_REG8(0x3c6e), 0x55 }, + { CCI_REG8(0x3c70), 0xc0 }, + { CCI_REG8(0x3c72), 0xc2 }, + { CCI_REG8(0x3c7e), 0xce }, + { CCI_REG8(0x3c8c), 0xcf }, + { CCI_REG8(0x3c8e), 0xeb }, + { CCI_REG8(0x3c98), 0x54 }, + { CCI_REG8(0x3c9a), 0x70 }, + { CCI_REG8(0x3c9c), 0xc1 }, + { CCI_REG8(0x3c9e), 0xdd }, + { CCI_REG8(0x3cb0), 0x7a }, + { CCI_REG8(0x3cb2), 0xba }, + { CCI_REG8(0x3cc8), 0xbc }, + { CCI_REG8(0x3cca), 0x7c }, + { CCI_REG8(0x3cd4), 0xea }, + { CCI_REG8(0x3cd5), 0x01 }, + { CCI_REG8(0x3cd6), 0x4a }, + { CCI_REG8(0x3cd8), 0x00 }, + { CCI_REG8(0x3cd9), 0x00 }, + { CCI_REG8(0x3cda), 0xff }, + { CCI_REG8(0x3cdb), 0x03 }, + { CCI_REG8(0x3cdc), 0x00 }, + { CCI_REG8(0x3cdd), 0x00 }, + { CCI_REG8(0x3cde), 0xff }, + { CCI_REG8(0x3cdf), 0x03 }, + { CCI_REG8(0x3ce4), 0x4c }, + { CCI_REG8(0x3ce6), 0xec }, + { CCI_REG8(0x3ce7), 0x01 }, + { CCI_REG8(0x3ce8), 0xff }, + { CCI_REG8(0x3ce9), 0x03 }, + { CCI_REG8(0x3cea), 0x00 }, + { CCI_REG8(0x3ceb), 0x00 }, + { CCI_REG8(0x3cec), 0xff }, + { CCI_REG8(0x3ced), 0x03 }, + { CCI_REG8(0x3cee), 0x00 }, + { CCI_REG8(0x3cef), 0x00 }, + { CCI_REG8(0x3cf2), 0xff }, + { CCI_REG8(0x3cf3), 0x03 }, + { CCI_REG8(0x3cf4), 0x00 }, + { CCI_REG8(0x3e28), 0x82 }, + { CCI_REG8(0x3e2a), 0x80 }, + { CCI_REG8(0x3e30), 0x85 }, + { CCI_REG8(0x3e32), 0x7d }, + { CCI_REG8(0x3e5c), 0xce }, + { CCI_REG8(0x3e5e), 0xd3 }, + { CCI_REG8(0x3e70), 0x53 }, + { CCI_REG8(0x3e72), 0x58 }, + { CCI_REG8(0x3e74), 0xc0 }, + { CCI_REG8(0x3e76), 0xc5 }, + { CCI_REG8(0x3e78), 0xc0 }, + { CCI_REG8(0x3e79), 0x01 }, + { CCI_REG8(0x3e7a), 0xd4 }, + { CCI_REG8(0x3e7b), 0x01 }, + { CCI_REG8(0x3eb4), 0x0b }, + { CCI_REG8(0x3eb5), 0x02 }, + { CCI_REG8(0x3eb6), 0x4d }, + { CCI_REG8(0x3eb7), 0x42 }, + { CCI_REG8(0x3eec), 0xf3 }, + { CCI_REG8(0x3eee), 0xe7 }, + { CCI_REG8(0x3f01), 0x01 }, + { CCI_REG8(0x3f24), 0x10 }, + { CCI_REG8(0x3f28), 0x2d }, + { CCI_REG8(0x3f2a), 0x2d }, + { CCI_REG8(0x3f2c), 0x2d }, + { CCI_REG8(0x3f2e), 0x2d }, + { CCI_REG8(0x3f30), 0x23 }, + { CCI_REG8(0x3f38), 0x2d }, + { CCI_REG8(0x3f3a), 0x2d }, + { CCI_REG8(0x3f3c), 0x2d }, + { CCI_REG8(0x3f3e), 0x28 }, + { CCI_REG8(0x3f40), 0x1e }, + { CCI_REG8(0x3f48), 0x2d }, + { CCI_REG8(0x3f4a), 0x2d }, + { CCI_REG8(0x3f4c), 0x00 }, + { CCI_REG8(0x4004), 0xe4 }, + { CCI_REG8(0x4006), 0xff }, + { CCI_REG8(0x4018), 0x69 }, + { CCI_REG8(0x401a), 0x84 }, + { CCI_REG8(0x401c), 0xd6 }, + { CCI_REG8(0x401e), 0xf1 }, + { CCI_REG8(0x4038), 0xde }, + { CCI_REG8(0x403a), 0x00 }, + { CCI_REG8(0x403b), 0x01 }, + { CCI_REG8(0x404c), 0x63 }, + { CCI_REG8(0x404e), 0x85 }, + { CCI_REG8(0x4050), 0xd0 }, + { CCI_REG8(0x4052), 0xf2 }, + { CCI_REG8(0x4108), 0xdd }, + { CCI_REG8(0x410a), 0xf7 }, + { CCI_REG8(0x411c), 0x62 }, + { CCI_REG8(0x411e), 0x7c }, + { CCI_REG8(0x4120), 0xcf }, + { CCI_REG8(0x4122), 0xe9 }, + { CCI_REG8(0x4138), 0xe6 }, + { CCI_REG8(0x413a), 0xf1 }, + { CCI_REG8(0x414c), 0x6b }, + { CCI_REG8(0x414e), 0x76 }, + { CCI_REG8(0x4150), 0xd8 }, + { CCI_REG8(0x4152), 0xe3 }, + { CCI_REG8(0x417e), 0x03 }, + { CCI_REG8(0x417f), 0x01 }, + { CCI_REG8(0x4186), 0xe0 }, + { CCI_REG8(0x4190), 0xf3 }, + { CCI_REG8(0x4192), 0xf7 }, + { CCI_REG8(0x419c), 0x78 }, + { CCI_REG8(0x419e), 0x7c }, + { CCI_REG8(0x41a0), 0xe5 }, + { CCI_REG8(0x41a2), 0xe9 }, + { CCI_REG8(0x41c8), 0xe2 }, + { CCI_REG8(0x41ca), 0xfd }, + { CCI_REG8(0x41dc), 0x67 }, + { CCI_REG8(0x41de), 0x82 }, + { CCI_REG8(0x41e0), 0xd4 }, + { CCI_REG8(0x41e2), 0xef }, + { CCI_REG8(0x4200), 0xde }, + { CCI_REG8(0x4202), 0xda }, + { CCI_REG8(0x4218), 0x63 }, + { CCI_REG8(0x421a), 0x5f }, + { CCI_REG8(0x421c), 0xd0 }, + { CCI_REG8(0x421e), 0xcc }, + { CCI_REG8(0x425a), 0x82 }, + { CCI_REG8(0x425c), 0xef }, + { CCI_REG8(0x4348), 0xfe }, + { CCI_REG8(0x4349), 0x06 }, + { CCI_REG8(0x4352), 0xce }, + { CCI_REG8(0x4420), 0x0b }, + { CCI_REG8(0x4421), 0x02 }, + { CCI_REG8(0x4422), 0x4d }, + { CCI_REG8(0x4423), 0x0a }, + { CCI_REG8(0x4426), 0xf5 }, + { CCI_REG8(0x442a), 0xe7 }, + { CCI_REG8(0x4432), 0xf5 }, + { CCI_REG8(0x4436), 0xe7 }, + { CCI_REG8(0x4466), 0xb4 }, + { CCI_REG8(0x446e), 0x32 }, + { CCI_REG8(0x449f), 0x1c }, + { CCI_REG8(0x44a4), 0x2c }, + { CCI_REG8(0x44a6), 0x2c }, + { CCI_REG8(0x44a8), 0x2c }, + { CCI_REG8(0x44aa), 0x2c }, + { CCI_REG8(0x44b4), 0x2c }, + { CCI_REG8(0x44b6), 0x2c }, + { CCI_REG8(0x44b8), 0x2c }, + { CCI_REG8(0x44ba), 0x2c }, + { CCI_REG8(0x44c4), 0x2c }, + { CCI_REG8(0x44c6), 0x2c }, + { CCI_REG8(0x44c8), 0x2c }, + { CCI_REG8(0x4506), 0xf3 }, + { CCI_REG8(0x450e), 0xe5 }, + { CCI_REG8(0x4516), 0xf3 }, + { CCI_REG8(0x4522), 0xe5 }, + { CCI_REG8(0x4524), 0xf3 }, + { CCI_REG8(0x452c), 0xe5 }, + { CCI_REG8(0x453c), 0x22 }, + { CCI_REG8(0x453d), 0x1b }, + { CCI_REG8(0x453e), 0x1b }, + { CCI_REG8(0x453f), 0x15 }, + { CCI_REG8(0x4540), 0x15 }, + { CCI_REG8(0x4541), 0x15 }, + { CCI_REG8(0x4542), 0x15 }, + { CCI_REG8(0x4543), 0x15 }, + { CCI_REG8(0x4544), 0x15 }, + { CCI_REG8(0x4548), 0x00 }, + { CCI_REG8(0x4549), 0x01 }, + { CCI_REG8(0x454a), 0x01 }, + { CCI_REG8(0x454b), 0x06 }, + { CCI_REG8(0x454c), 0x06 }, + { CCI_REG8(0x454d), 0x06 }, + { CCI_REG8(0x454e), 0x06 }, + { CCI_REG8(0x454f), 0x06 }, + { CCI_REG8(0x4550), 0x06 }, + { CCI_REG8(0x4554), 0x55 }, + { CCI_REG8(0x4555), 0x02 }, + { CCI_REG8(0x4556), 0x42 }, + { CCI_REG8(0x4557), 0x05 }, + { CCI_REG8(0x4558), 0xfd }, + { CCI_REG8(0x4559), 0x05 }, + { CCI_REG8(0x455a), 0x94 }, + { CCI_REG8(0x455b), 0x06 }, + { CCI_REG8(0x455d), 0x06 }, + { CCI_REG8(0x455e), 0x49 }, + { CCI_REG8(0x455f), 0x07 }, + { CCI_REG8(0x4560), 0x7f }, + { CCI_REG8(0x4561), 0x07 }, + { CCI_REG8(0x4562), 0xa5 }, + { CCI_REG8(0x4564), 0x55 }, + { CCI_REG8(0x4565), 0x02 }, + { CCI_REG8(0x4566), 0x42 }, + { CCI_REG8(0x4567), 0x05 }, + { CCI_REG8(0x4568), 0xfd }, + { CCI_REG8(0x4569), 0x05 }, + { CCI_REG8(0x456a), 0x94 }, + { CCI_REG8(0x456b), 0x06 }, + { CCI_REG8(0x456d), 0x06 }, + { CCI_REG8(0x456e), 0x49 }, + { CCI_REG8(0x456f), 0x07 }, + { CCI_REG8(0x4572), 0xa5 }, + { CCI_REG8(0x460c), 0x7d }, + { CCI_REG8(0x460e), 0xb1 }, + { CCI_REG8(0x4614), 0xa8 }, + { CCI_REG8(0x4616), 0xb2 }, + { CCI_REG8(0x461c), 0x7e }, + { CCI_REG8(0x461e), 0xa7 }, + { CCI_REG8(0x4624), 0xa8 }, + { CCI_REG8(0x4626), 0xb2 }, + { CCI_REG8(0x462c), 0x7e }, + { CCI_REG8(0x462e), 0x8a }, + { CCI_REG8(0x4630), 0x94 }, + { CCI_REG8(0x4632), 0xa7 }, + { CCI_REG8(0x4634), 0xfb }, + { CCI_REG8(0x4636), 0x2f }, + { CCI_REG8(0x4638), 0x81 }, + { CCI_REG8(0x4639), 0x01 }, + { CCI_REG8(0x463a), 0xb5 }, + { CCI_REG8(0x463b), 0x01 }, + { CCI_REG8(0x463c), 0x26 }, + { CCI_REG8(0x463e), 0x30 }, + { CCI_REG8(0x4640), 0xac }, + { CCI_REG8(0x4641), 0x01 }, + { CCI_REG8(0x4642), 0xb6 }, + { CCI_REG8(0x4643), 0x01 }, + { CCI_REG8(0x4644), 0xfc }, + { CCI_REG8(0x4646), 0x25 }, + { CCI_REG8(0x4648), 0x82 }, + { CCI_REG8(0x4649), 0x01 }, + { CCI_REG8(0x464a), 0xab }, + { CCI_REG8(0x464b), 0x01 }, + { CCI_REG8(0x464c), 0x26 }, + { CCI_REG8(0x464e), 0x30 }, + { CCI_REG8(0x4654), 0xfc }, + { CCI_REG8(0x4656), 0x08 }, + { CCI_REG8(0x4658), 0x12 }, + { CCI_REG8(0x465a), 0x25 }, + { CCI_REG8(0x4662), 0xfc }, + { CCI_REG8(0x46a2), 0xfb }, + { CCI_REG8(0x46d6), 0xf3 }, + { CCI_REG8(0x46e6), 0x00 }, + { CCI_REG8(0x46e8), 0xff }, + { CCI_REG8(0x46e9), 0x03 }, + { CCI_REG8(0x46ec), 0x7a }, + { CCI_REG8(0x46ee), 0xe5 }, + { CCI_REG8(0x46f4), 0xee }, + { CCI_REG8(0x46f6), 0xf2 }, + { CCI_REG8(0x470c), 0xff }, + { CCI_REG8(0x470d), 0x03 }, + { CCI_REG8(0x470e), 0x00 }, + { CCI_REG8(0x4714), 0xe0 }, + { CCI_REG8(0x4716), 0xe4 }, + { CCI_REG8(0x471e), 0xed }, + { CCI_REG8(0x472e), 0x00 }, + { CCI_REG8(0x4730), 0xff }, + { CCI_REG8(0x4731), 0x03 }, + { CCI_REG8(0x4734), 0x7b }, + { CCI_REG8(0x4736), 0xdf }, + { CCI_REG8(0x4754), 0x7d }, + { CCI_REG8(0x4756), 0x8b }, + { CCI_REG8(0x4758), 0x93 }, + { CCI_REG8(0x475a), 0xb1 }, + { CCI_REG8(0x475c), 0xfb }, + { CCI_REG8(0x475e), 0x09 }, + { CCI_REG8(0x4760), 0x11 }, + { CCI_REG8(0x4762), 0x2f }, + { CCI_REG8(0x4766), 0xcc }, + { CCI_REG8(0x4776), 0xcb }, + { CCI_REG8(0x477e), 0x4a }, + { CCI_REG8(0x478e), 0x49 }, + { CCI_REG8(0x4794), 0x7c }, + { CCI_REG8(0x4796), 0x8f }, + { CCI_REG8(0x4798), 0xb3 }, + { CCI_REG8(0x4799), 0x00 }, + { CCI_REG8(0x479a), 0xcc }, + { CCI_REG8(0x479c), 0xc1 }, + { CCI_REG8(0x479e), 0xcb }, + { CCI_REG8(0x47a4), 0x7d }, + { CCI_REG8(0x47a6), 0x8e }, + { CCI_REG8(0x47a8), 0xb4 }, + { CCI_REG8(0x47a9), 0x00 }, + { CCI_REG8(0x47aa), 0xc0 }, + { CCI_REG8(0x47ac), 0xfa }, + { CCI_REG8(0x47ae), 0x0d }, + { CCI_REG8(0x47b0), 0x31 }, + { CCI_REG8(0x47b1), 0x01 }, + { CCI_REG8(0x47b2), 0x4a }, + { CCI_REG8(0x47b3), 0x01 }, + { CCI_REG8(0x47b4), 0x3f }, + { CCI_REG8(0x47b6), 0x49 }, + { CCI_REG8(0x47bc), 0xfb }, + { CCI_REG8(0x47be), 0x0c }, + { CCI_REG8(0x47c0), 0x32 }, + { CCI_REG8(0x47c1), 0x01 }, + { CCI_REG8(0x47c2), 0x3e }, + { CCI_REG8(0x47c3), 0x01 }, + { IMX678_REG_WDMODE, 0x00 }, + { IMX678_REG_MDBIT, 0x01 }, + { IMX678_REG_XXS_DRV, 0x00 }, +}; + +static const u32 codes_bayer[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, +}; + +static const u32 codes_monochrome[] = { + MEDIA_BUS_FMT_Y12_1X12, +}; + +static const struct imx678_model_info imx678_aaqr_info = { + .type = IMX678_COLOR, + .codes = codes_bayer, + .num_codes = ARRAY_SIZE(codes_bayer), +}; + +static const struct imx678_model_info imx678_aamr_info = { + .type = IMX678_MONOCHROME, + .codes = codes_monochrome, + .num_codes = ARRAY_SIZE(codes_monochrome), +}; + +static const char * const imx678_supply_name[] = { + "avdd", /* Analog (3.3V) supply */ + "dvdd", /* Digital Core (1.1V) supply */ + "ovdd", /* IF (1.8V) supply */ +}; + +struct imx678 { + struct v4l2_subdev sd; + struct media_pad pad; + struct regmap *cci; + + const struct imx678_model_info *info; + + struct clk *xclk; + u32 xclk_freq; + + /* chosen INCK_SEL register value */ + u8 inck_sel_val; + + /* Link configurations */ + enum imx678_lanemode lane_mode; + unsigned long link_freq_bitmap; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[ARRAY_SIZE(imx678_supply_name)]; + + struct v4l2_ctrl_handler ctrl_handler; + + /* V4L2 Controls */ + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + + /* Track VMAX for exposure updates */ + u32 vmax; +}; + +static inline struct imx678 *to_imx678(struct v4l2_subdev *_sd) +{ + return container_of_const(_sd, struct imx678, sd); +} + +static u32 imx678_default_mbus_code(struct imx678 *imx678) +{ + return imx678->info->codes[0]; +} + +static bool imx678_mbus_code_supported(struct imx678 *imx678, u32 code) +{ + for (unsigned int i = 0; i < imx678->info->num_codes; i++) { + if (imx678->info->codes[i] == code) + return true; + } + + return false; +} + +static int imx678_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx678 *imx678 = container_of_const(ctrl->handler, struct + imx678, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + const struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_state *state; + int ret = 0; + + state = v4l2_subdev_get_locked_active_state(&imx678->sd); + format = v4l2_subdev_state_get_format(state, IMX678_SOURCE_PAD); + + if (ctrl->id == V4L2_CID_VBLANK) { + u32 current_exposure = imx678->exposure->cur.val; + + imx678->vmax = format->height + ctrl->val; + + current_exposure = clamp_t(u32, current_exposure, + IMX678_EXPOSURE_MIN, + imx678->vmax - IMX678_SHR_MIN); + ret = __v4l2_ctrl_modify_range(imx678->exposure, + IMX678_EXPOSURE_MIN, + imx678->vmax - IMX678_SHR_MIN, + 1, current_exposure); + if (ret) + return ret; + } + + /* + * Only apply control values when device is powered on (RPM ACTIVE) + * and streaming (usage count != 0) + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_VBLANK: + cci_write(imx678->cci, IMX678_REG_VMAX, imx678->vmax, &ret); + fallthrough; /* SHR = VMAX - exposure, so update it */ + case V4L2_CID_EXPOSURE: { + u32 shr = imx678->vmax - imx678->exposure->val; + + cci_write(imx678->cci, IMX678_REG_SHR, shr, &ret); + break; + } + case V4L2_CID_ANALOGUE_GAIN: + cci_write(imx678->cci, IMX678_REG_GAIN, ctrl->val, &ret); + break; + case V4L2_CID_HBLANK: { + u32 hmax = (format->width + ctrl->val) / IMX678_PIX_PER_CLK; + + cci_write(imx678->cci, IMX678_REG_HMAX, hmax, &ret); + break; + } + case V4L2_CID_TEST_PATTERN: { + cci_write(imx678->cci, IMX678_REG_TPG_COLORWIDTH, + IMX678_TPG_COLORWIDTH_160PIX, &ret); + cci_write(imx678->cci, IMX678_REG_TPG_PATSEL_DUOUT, + imx678_tpg_val[ctrl->val], &ret); + cci_write(imx678->cci, IMX678_REG_TPG_EN_DUOUT, + (ctrl->val) ? 1 : 0, + &ret); + break; + } + case V4L2_CID_HFLIP: + cci_write(imx678->cci, IMX678_REG_WINMODEH, ctrl->val, &ret); + break; + case V4L2_CID_VFLIP: + cci_write(imx678->cci, IMX678_REG_WINMODEV, ctrl->val, &ret); + break; + default: + dev_warn(&client->dev, + "ctrl(id:0x%x,val:0x%x) is not handled\n", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx678_ctrl_ops = { + .s_ctrl = imx678_set_ctrl, +}; + +static int imx678_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx678 *imx678 = to_imx678(sd); + + if (code->index >= imx678->info->num_codes) + return -EINVAL; + + code->code = imx678->info->codes[code->index]; + + return 0; +} + +static int imx678_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx678 *imx678 = to_imx678(sd); + const struct v4l2_rect *crop; + + if (fse->index) + return -EINVAL; + + if (!imx678_mbus_code_supported(imx678, fse->code)) + return -EINVAL; + + crop = v4l2_subdev_state_get_crop(sd_state, fse->pad); + + fse->min_width = crop->width; + fse->max_width = fse->min_width; + fse->min_height = crop->height; + fse->max_height = fse->min_height; + + return 0; +} + +static int imx678_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, sel->pad); + return 0; + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r = imx678_native_area; + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r = imx678_active_area; + return 0; + } + + return -EINVAL; +} + +static int imx678_init_state(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct imx678 *imx678 = to_imx678(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; + + crop = v4l2_subdev_state_get_crop(state, IMX678_SOURCE_PAD); + *crop = imx678_active_area; + + format = v4l2_subdev_state_get_format(state, IMX678_SOURCE_PAD); + format->code = imx678_default_mbus_code(imx678); + format->width = imx678_active_area.width; + format->height = imx678_active_area.height; + format->field = V4L2_FIELD_NONE; + format->colorspace = V4L2_COLORSPACE_RAW; + format->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + format->quantization = V4L2_QUANTIZATION_FULL_RANGE; + format->xfer_func = V4L2_XFER_FUNC_NONE; + + return 0; +} + +static int imx678_write_common(struct imx678 *imx678) +{ + int ret = 0; + + cci_multi_reg_write(imx678->cci, common_regs, ARRAY_SIZE(common_regs), + &ret); + + cci_write(imx678->cci, IMX678_REG_INCK_SEL, imx678->inck_sel_val, &ret); + cci_write(imx678->cci, IMX678_REG_DATARATE_SEL, + link_freqs_reg_value[__ffs(imx678->link_freq_bitmap)], &ret); + cci_write(imx678->cci, IMX678_REG_LANEMODE, imx678->lane_mode, &ret); + + cci_write(imx678->cci, IMX678_REG_INTERFACE_SEL, IMX678_INTERFACE_2L_4L, + &ret); + + return ret; +} + +static int imx678_program_window(struct imx678 *imx678, + const struct v4l2_rect *crop) +{ + int ret = 0; + + cci_write(imx678->cci, IMX678_REG_ADDMODE, 0x00, &ret); + cci_write(imx678->cci, IMX678_REG_WINMODE, + v4l2_rect_equal(crop, &imx678_active_area) ? 0x00 : 0x04, + &ret); + cci_write(imx678->cci, IMX678_REG_PIX_HST, + crop->left - imx678_active_area.left, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_HWIDTH, crop->width, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_VST, + crop->top - imx678_active_area.top, &ret); + cci_write(imx678->cci, IMX678_REG_PIX_VWIDTH, crop->height, &ret); + cci_write(imx678->cci, IMX678_REG_ADBIT, 0x01, &ret); + + return ret; +} + +static int imx678_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, u32 pad, + u64 mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx678 *imx678 = to_imx678(sd); + const struct v4l2_rect *crop; + int ret; + + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; + + crop = v4l2_subdev_state_get_crop(state, pad); + ret = imx678_program_window(imx678, crop); + if (ret) { + dev_err(&client->dev, "%s failed to set mode\n", __func__); + goto err_rpm_put; + } + + ret = __v4l2_ctrl_handler_setup(imx678->sd.ctrl_handler); + if (ret) { + dev_err(&client->dev, "%s failed to apply user values\n", + __func__); + goto err_rpm_put; + } + + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, IMX678_MODE_STREAMING, + &ret); + usleep_range(IMX678_STREAM_DELAY_US, IMX678_STREAM_DELAY_US + + IMX678_STREAM_DELAY_RANGE_US); + cci_write(imx678->cci, IMX678_REG_XMSTA, 0x00, &ret); + + if (ret) { + dev_err(&client->dev, "%s failed to start streaming\n", + __func__); + goto err_rpm_put; + } + + return 0; + +err_rpm_put: + pm_runtime_put(&client->dev); + + return ret; +} + +static int imx678_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 mask) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct imx678 *imx678 = to_imx678(sd); + int ret = 0; + + /* Master mode disable */ + cci_write(imx678->cci, IMX678_REG_XMSTA, 0x01, &ret); + /* Standby */ + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, IMX678_MODE_STANDBY, + &ret); + if (ret) + dev_err(&client->dev, "%s failed to stop stream\n", __func__); + + pm_runtime_put(&client->dev); + + return ret; +} + +static int imx678_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + if (ret) { + dev_err(&client->dev, "%s: failed to enable regulators\n", + __func__); + return ret; + } + + fsleep(1); /* Tlow > 500ns */ + + gpiod_set_value_cansleep(imx678->reset_gpio, 0); + + fsleep(1); /* T3 > 1us */ + + ret = clk_prepare_enable(imx678->xclk); + if (ret) { + dev_err(&client->dev, "%s: failed to enable clock\n", + __func__); + goto reg_off; + } + + fsleep(20); /* T4 > 20us */ + + ret = imx678_write_common(imx678); + if (ret) { + dev_err(&client->dev, "%s failed to write registers\n", + __func__); + goto clk_off; + } + + return 0; + +clk_off: + clk_disable_unprepare(imx678->xclk); + +reg_off: + gpiod_set_value_cansleep(imx678->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + + return ret; +} + +static int imx678_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + clk_disable_unprepare(imx678->xclk); + gpiod_set_value_cansleep(imx678->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + + return 0; +} + +static int imx678_identify_model(struct imx678 *imx678) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + const struct imx678_model_info *info; + enum imx678_type detected; + int ret = 0; + u64 val = 0; + + info = device_get_match_data(&client->dev); + + /* + * This sensor's ID registers become accessible 80ms after coming out + * of STANDBY mode. + */ + cci_write(imx678->cci, IMX678_REG_MODE_SELECT, 0, &ret); + fsleep(IMX678_MODULE_ID_DELAY); + + cci_read(imx678->cci, IMX678_REG_MODULE_ID, &val, &ret); + + if (ret) { + dev_err(&client->dev, + "I2C transaction failed ret = %d\n", ret); + return ret; + } + + if (val != IMX678_ID) { + dev_err(&client->dev, + "Chip ID mismatch: %x!=%llx\n", IMX678_ID, val); + return -ENXIO; + } + + cci_read(imx678->cci, IMX678_REG_MONOCHROME, &val, &ret); + + if (ret) { + dev_err(&client->dev, + "I2C transaction failed ret = %d\n", ret); + return ret; + } + + detected = val & IMX678_TYPE; + + /* Prefer to use sensor type specified in device tree */ + if (info) { + imx678->info = info; + if (detected != info->type) + dev_err(&client->dev, + "detected %s sensor, DT specifies %s; using DT value\n", + detected == IMX678_COLOR ? "color" : "mono", + info->type == IMX678_COLOR ? "color" : "mono"); + } else { + imx678->info = detected == IMX678_MONOCHROME ? + &imx678_aamr_info : &imx678_aaqr_info; + dev_info(&client->dev, + "sensor type missing in DT; detected %s sensor\n", + detected == IMX678_MONOCHROME ? "mono" : "color"); + } + + return 0; +} + +static const struct v4l2_subdev_video_ops imx678_video_ops = { + .s_stream = v4l2_subdev_s_stream_helper, +}; + +static const struct v4l2_subdev_pad_ops imx678_pad_ops = { + .enum_mbus_code = imx678_enum_mbus_code, + .get_fmt = v4l2_subdev_get_fmt, + .set_fmt = v4l2_subdev_get_fmt, + .get_selection = imx678_get_selection, + .enum_frame_size = imx678_enum_frame_size, + .enable_streams = imx678_enable_streams, + .disable_streams = imx678_disable_streams, +}; + +static const struct v4l2_subdev_ops imx678_subdev_ops = { + .video = &imx678_video_ops, + .pad = &imx678_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops imx678_internal_ops = { + .init_state = imx678_init_state, +}; + +static int imx678_init_controls(struct imx678 *imx678) +{ + struct v4l2_ctrl_handler *ctrl_hdlr; + const u32 hmax_4lane = min_hmax_4lane[__ffs(imx678->link_freq_bitmap)]; + const u32 lane_scale = imx678->lane_mode == IMX678_LANEMODE_2L ? 2 : 1; + struct i2c_client *client = v4l2_get_subdevdata(&imx678->sd); + struct v4l2_fwnode_device_properties props; + struct v4l2_ctrl *link_freq; + s32 hblank, max_hblank, vblank, max_vblank; + u32 hmax; + int ret; + + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret < 0) + return ret; + + ctrl_hdlr = &imx678->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 11); + if (ret) + return ret; + + imx678->vmax = IMX678_VMAX_DEFAULT; + hmax = hmax_4lane * lane_scale; + + /* PIXEL_RATE is fixed and read-only */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX678_PIXEL_RATE, IMX678_PIXEL_RATE, 1, + IMX678_PIXEL_RATE); + + /* LINK_FREQ is also read only */ + link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freqs) - 1, + __ffs(imx678->link_freq_bitmap), + link_freqs); + + if (link_freq) + link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vblank = imx678->vmax - imx678_active_area.height; + max_vblank = IMX678_VMAX_MAX - imx678_active_area.height; + imx678->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_VBLANK, vblank, max_vblank, + 2, vblank); + + hblank = hmax * IMX678_PIX_PER_CLK - imx678_active_area.width; + max_hblank = IMX678_HMAX_MAX * IMX678_PIX_PER_CLK - + imx678_active_area.width; + imx678->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_HBLANK, hblank, max_hblank, + IMX678_PIX_PER_CLK, hblank); + + imx678->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX678_EXPOSURE_MIN, + IMX678_VMAX_DEFAULT - + IMX678_SHR_MIN, + IMX678_EXPOSURE_STEP, + IMX678_EXPOSURE_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX678_ANA_GAIN_MIN_NORMAL, + IMX678_ANA_GAIN_MAX_NORMAL, IMX678_ANA_GAIN_STEP, + IMX678_ANA_GAIN_DEFAULT); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_HFLIP, + 0, 1, 1, 0); + v4l2_ctrl_new_std(ctrl_hdlr, &imx678_ctrl_ops, V4L2_CID_VFLIP, + 0, 1, 1, 0); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx678_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx678_tpg_menu) - 1, 0, 0, + imx678_tpg_menu); + + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx678_ctrl_ops, &props); + + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; + } + + imx678->sd.ctrl_handler = ctrl_hdlr; + + return 0; +} + +static int imx678_check_hwcfg(struct device *dev, struct imx678 *imx678) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint ep_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + int ret = -EINVAL; + + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep_cfg)) { + dev_err(dev, "could not parse endpoint\n"); + goto error_out; + } + + switch (ep_cfg.bus.mipi_csi2.num_data_lanes) { + case 2: + imx678->lane_mode = IMX678_LANEMODE_2L; + break; + case 4: + imx678->lane_mode = IMX678_LANEMODE_4L; + break; + default: + dev_err(dev, + "only 2 or 4 CSI2 data lanes are currently supported\n"); + goto error_out; + } + + ret = v4l2_link_freq_to_bitmap(dev, ep_cfg.link_frequencies, + ep_cfg.nr_of_link_frequencies, + link_freqs, ARRAY_SIZE(link_freqs), + &imx678->link_freq_bitmap); + +error_out: + v4l2_fwnode_endpoint_free(&ep_cfg); + fwnode_handle_put(endpoint); + + return ret; +} + +static int imx678_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx678 *imx678; + int ret, i; + + imx678 = devm_kzalloc(&client->dev, sizeof(*imx678), GFP_KERNEL); + if (!imx678) + return -ENOMEM; + + v4l2_i2c_subdev_init(&imx678->sd, client, &imx678_subdev_ops); + + imx678->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx678->cci)) + return dev_err_probe(dev, PTR_ERR(imx678->cci), + "failed to init CCI\n"); + + if (imx678_check_hwcfg(dev, imx678)) + return -EINVAL; + + imx678->xclk = devm_v4l2_sensor_clk_get(dev, NULL); + if (IS_ERR(imx678->xclk)) + return dev_err_probe(dev, PTR_ERR(imx678->xclk), + "failed to get xclk\n"); + + imx678->xclk_freq = clk_get_rate(imx678->xclk); + + for (i = 0; i < ARRAY_SIZE(imx678_inck_table); ++i) { + if (imx678_inck_table[i].xclk_hz == imx678->xclk_freq) { + imx678->inck_sel_val = imx678_inck_table[i].inck_sel; + break; + } + } + + if (i == ARRAY_SIZE(imx678_inck_table)) + return dev_err_probe(dev, -EINVAL, + "unsupported XCLK rate %u Hz\n", + imx678->xclk_freq); + + for (i = 0; i < ARRAY_SIZE(imx678_supply_name); i++) + imx678->supplies[i].supply = imx678_supply_name[i]; + + ret = devm_regulator_bulk_get(&client->dev, + ARRAY_SIZE(imx678_supply_name), + imx678->supplies); + if (ret) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + imx678->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(imx678->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(imx678->reset_gpio), + "failed to get reset GPIO\n"); + + ret = imx678_power_on(dev); + if (ret) + return ret; + + ret = imx678_identify_model(imx678); + if (ret) + goto error_power_off; + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = imx678_init_controls(imx678); + if (ret) + goto error_pm_runtime; + + imx678->sd.internal_ops = &imx678_internal_ops; + imx678->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx678->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + imx678->pad.flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&imx678->sd.entity, 1, &imx678->pad); + if (ret) { + dev_err_probe(dev, ret, "failed to init entity pads\n"); + goto error_handler_free; + } + + imx678->sd.state_lock = imx678->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx678->sd); + if (ret < 0) { + dev_err_probe(dev, ret, "subdev init error\n"); + goto error_media_entity; + } + + ret = v4l2_async_register_subdev_sensor(&imx678->sd); + if (ret < 0) { + dev_err_probe(dev, ret, + "failed to register sensor sub-device\n"); + goto error_subdev_cleanup; + } + + pm_runtime_idle(dev); + + return 0; + +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx678->sd); + +error_media_entity: + media_entity_cleanup(&imx678->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx678->sd.ctrl_handler); + +error_pm_runtime: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + +error_power_off: + imx678_power_off(&client->dev); + + return ret; +} + +static void imx678_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx678 *imx678 = to_imx678(sd); + + v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(imx678->sd.ctrl_handler); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + imx678_power_off(&client->dev); + pm_runtime_set_suspended(&client->dev); +} + +static const struct dev_pm_ops imx678_pm_ops = { + SET_RUNTIME_PM_OPS(imx678_power_off, imx678_power_on, NULL) +}; + +static const struct of_device_id imx678_of_match[] = { + { .compatible = "sony,imx678-aamr", .data = &imx678_aamr_info }, + { .compatible = "sony,imx678-aaqr", .data = &imx678_aaqr_info }, + /* for non-conforming DTs that rely on runtime check */ + { .compatible = "sony,imx678" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, imx678_of_match); + +static struct i2c_driver imx678_i2c_driver = { + .driver = { + .name = "imx678", + .of_match_table = imx678_of_match, + .pm = pm_ptr(&imx678_pm_ops), + }, + .probe = imx678_probe, + .remove = imx678_remove, +}; + +module_i2c_driver(imx678_i2c_driver); + +MODULE_AUTHOR("Will Whang "); +MODULE_AUTHOR("Tetsuya NOMURA "); +MODULE_AUTHOR("Jai Luthra "); +MODULE_DESCRIPTION("Sony imx678 sensor driver"); +MODULE_LICENSE("GPL"); -- cgit From fa17e189bb88a8f0153c7a588d4de9465d276d16 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:53 +0200 Subject: media: cec: core: consolidate error path in cec_allocate_adapter() Consolidate error path in cec_allocate_adapter() so next changes will be less verbose. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 90a98f322f5e..55011e555d35 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -267,8 +267,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, if (IS_ERR(adap->kthread)) { pr_err("cec-%s: kernel_thread() failed\n", name); res = PTR_ERR(adap->kthread); - kfree(adap); - return ERR_PTR(res); + goto err_free_adap; } #ifdef CONFIG_MEDIA_CEC_RC @@ -281,8 +280,8 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, pr_err("cec-%s: failed to allocate memory for rc_dev\n", name); kthread_stop(adap->kthread); - kfree(adap); - return ERR_PTR(-ENOMEM); + res = -ENOMEM; + goto err_free_adap; } snprintf(adap->input_phys, sizeof(adap->input_phys), @@ -301,6 +300,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->rc->timeout = MS_TO_US(550); #endif return adap; + +err_free_adap: + kfree(adap); + return ERR_PTR(res); } EXPORT_SYMBOL_GPL(cec_allocate_adapter); -- cgit From b2b9a296b7a5a4c1d0d75b94ce5668a46741c43b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:54 +0200 Subject: media: cec: core: add missing mutex_destroy to error path and remove Add missing mutex_destroy() call to error path and remove to properly clean up the mutex initialized during adapter allocation. Ensure proper resource cleanup and follows kernel practices. Found by code review. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 55011e555d35..0c89fac58349 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -302,6 +302,11 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, return adap; err_free_adap: + mutex_destroy(&adap->devnode.lock); + mutex_destroy(&adap->devnode.lock_fhs); + + mutex_destroy(&adap->lock); + kfree(adap); return ERR_PTR(res); } @@ -388,14 +393,23 @@ void cec_delete_adapter(struct cec_adapter *adap) { if (IS_ERR_OR_NULL(adap)) return; + if (adap->kthread_config) kthread_stop(adap->kthread_config); kthread_stop(adap->kthread); + if (adap->ops->adap_free) adap->ops->adap_free(adap); + #ifdef CONFIG_MEDIA_CEC_RC rc_free_device(adap->rc); #endif + + mutex_destroy(&adap->devnode.lock); + mutex_destroy(&adap->devnode.lock_fhs); + + mutex_destroy(&adap->lock); + kfree(adap); } EXPORT_SYMBOL_GPL(cec_delete_adapter); -- cgit From dbcaf39698f3ee85d7e2ca7a93379e6fcbccbbb4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:55 +0200 Subject: media: cec: core: Use DEFINE_SHOW_STORE_ATTRIBUTE() helper for debugfs Use DEFINE_SHOW_STORE_ATTRIBUTE() helper for read-write file to reduce some duplicated code. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 0c89fac58349..674bcb6450dc 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -205,19 +205,7 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused) return call_op(adap, error_inj_show, sf); } - -static int cec_error_inj_open(struct inode *inode, struct file *file) -{ - return single_open(file, cec_error_inj_show, inode->i_private); -} - -static const struct file_operations cec_error_inj_fops = { - .open = cec_error_inj_open, - .write = cec_error_inj_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj); #endif struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, -- cgit From 15a09a13cba8680864675916580ff2a835d409ee Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:56 +0200 Subject: media: cec: core: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 674bcb6450dc..1f12fb0caca7 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -5,16 +5,30 @@ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ +#include +#include +#include +#include #include -#include +#include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include #include +#include +#include +#include #include +#include #include #include +#include + +#include #include "cec-priv.h" -- cgit From 09da2a7df0c41585e2b47d9c6c10e0e5a34db32a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:57 +0200 Subject: media: cec: core: Add pr_fmt() Several prints inconsistently use cec: or cec-%s: or nothing. To make it clear which prints come from cec-core.c, add a pr_fmt() macro. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil [hverkuil: also change kernel_thread to kthread_run in comment] --- drivers/media/cec/core/cec-core.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index 1f12fb0caca7..af566d9217dd 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -5,6 +5,8 @@ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -107,7 +109,7 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, minor = find_first_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES); if (minor == CEC_NUM_DEVICES) { mutex_unlock(&cec_devnode_lock); - pr_err("could not get a free minor\n"); + pr_err("Could not get a free minor\n"); return -ENFILE; } @@ -130,7 +132,7 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, ret = cdev_device_add(&devnode->cdev, &devnode->dev); if (ret) { devnode->registered = false; - pr_err("%s: cdev_device_add failed\n", __func__); + pr_err("cdev_device_add() failed\n"); goto clr_bit; } @@ -267,7 +269,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); if (IS_ERR(adap->kthread)) { - pr_err("cec-%s: kernel_thread() failed\n", name); + pr_err("%s: kthread_run() failed\n", name); res = PTR_ERR(adap->kthread); goto err_free_adap; } @@ -279,8 +281,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, /* Prepare the RC input device */ adap->rc = rc_allocate_device(RC_DRIVER_SCANCODE); if (!adap->rc) { - pr_err("cec-%s: failed to allocate memory for rc_dev\n", - name); + pr_err("%s: failed to allocate memory for rc_dev\n", name); kthread_stop(adap->kthread); res = -ENOMEM; goto err_free_adap; @@ -336,8 +337,7 @@ int cec_register_adapter(struct cec_adapter *adap, res = rc_register_device(adap->rc); if (res) { - pr_err("cec-%s: failed to prepare input device\n", - adap->name); + pr_err("%s: failed to prepare input device\n", adap->name); rc_free_device(adap->rc); adap->rc = NULL; return res; @@ -424,14 +424,14 @@ static int __init cec_devnode_init(void) int ret = alloc_chrdev_region(&cec_dev_t, 0, CEC_NUM_DEVICES, CEC_NAME); if (ret < 0) { - pr_warn("cec: unable to allocate major\n"); + pr_warn("Unable to allocate major\n"); return ret; } #ifdef CONFIG_DEBUG_FS top_cec_dir = debugfs_create_dir("cec", NULL); if (IS_ERR_OR_NULL(top_cec_dir)) { - pr_warn("cec: Failed to create debugfs cec dir\n"); + pr_warn("Failed to create debugfs cec dir\n"); top_cec_dir = NULL; } #endif @@ -440,7 +440,7 @@ static int __init cec_devnode_init(void) if (ret < 0) { debugfs_remove_recursive(top_cec_dir); unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES); - pr_warn("cec: bus_register failed\n"); + pr_warn("bus_register() failed\n"); return -EIO; } -- cgit From 5f41e11e5ffe455b00e8d7ecdb9da3e8ddc12df0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:58 +0200 Subject: media: cec: core: Consistently use CEC_NAME where it matters When create a debugfs folder or device nodes we use actual subsystem name. But in some cases it's hardcoded, while in other it uses predefined string literal. Make it consistently use CEC_NAME. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index af566d9217dd..c57f691b5a09 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -120,13 +120,13 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, devnode->dev.bus = &cec_bus_type; devnode->dev.devt = MKDEV(MAJOR(cec_dev_t), minor); devnode->dev.release = cec_devnode_release; - dev_set_name(&devnode->dev, "cec%d", devnode->minor); + dev_set_name(&devnode->dev, "%s%d", CEC_NAME, devnode->minor); device_initialize(&devnode->dev); /* Part 2: Initialize and register the character device */ cdev_init(&devnode->cdev, &cec_devnode_fops); devnode->cdev.owner = owner; - kobject_set_name(&devnode->cdev.kobj, "cec%d", devnode->minor); + kobject_set_name(&devnode->cdev.kobj, "%s%d", CEC_NAME, devnode->minor); devnode->registered = true; ret = cdev_device_add(&devnode->cdev, &devnode->dev); @@ -267,7 +267,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, mutex_init(&adap->devnode.lock_fhs); mutex_init(&adap->devnode.lock); - adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); + adap->kthread = kthread_run(cec_thread_func, adap, "%s-%s", CEC_NAME, name); if (IS_ERR(adap->kthread)) { pr_err("%s: kthread_run() failed\n", name); res = PTR_ERR(adap->kthread); @@ -429,9 +429,9 @@ static int __init cec_devnode_init(void) } #ifdef CONFIG_DEBUG_FS - top_cec_dir = debugfs_create_dir("cec", NULL); + top_cec_dir = debugfs_create_dir(CEC_NAME, NULL); if (IS_ERR_OR_NULL(top_cec_dir)) { - pr_warn("Failed to create debugfs cec dir\n"); + pr_warn("Failed to create debugfs " CEC_NAME " dir\n"); top_cec_dir = NULL; } #endif -- cgit From a6bc7e89f9fbb03ec7aca42dc2514a3f4214c4e3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:20:59 +0200 Subject: media: cec: core: Use predefined time multiplier Instead of relying on custom MS_TO_US() macro, use existing time multiplier. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index c57f691b5a09..c51e769b4e34 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -300,7 +301,7 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, adap->rc->allowed_protocols = RC_PROTO_BIT_CEC; adap->rc->priv = adap; adap->rc->map_name = RC_MAP_CEC; - adap->rc->timeout = MS_TO_US(550); + adap->rc->timeout = 550 * USEC_PER_MSEC; #endif return adap; -- cgit From e24bde838d85ddea086909ca1dd17a8cf8737a0c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 9 Jul 2026 10:21:00 +0200 Subject: media: cec: seco: Use predefined time multiplier Instead of relying on custom MS_TO_US() macro, use existing time multiplier. Signed-off-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/seco/seco-cec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index 97ed9654c78a..654c70503dd5 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -7,14 +7,15 @@ * Copyright (C) 2018, Aidilab Srl. */ -#include #include #include #include #include #include +#include #include #include +#include /* CEC Framework */ #include @@ -356,7 +357,7 @@ static int secocec_ir_probe(void *priv) cec->ir->allowed_protocols = RC_PROTO_BIT_RC5; cec->ir->priv = cec; cec->ir->map_name = RC_MAP_HAUPPAUGE; - cec->ir->timeout = MS_TO_US(100); + cec->ir->timeout = 100 * USEC_PER_MSEC; /* Clear the status register */ status = smb_rd16(SECOCEC_STATUS_REG_1, &val); -- cgit From 8dcf23edaf4bdf22259f6a47cfddc8bb1467425c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:26:31 +0800 Subject: media: cec: tegra: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/tegra/tegra_cec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index 3ed50097262f..fe66336e734f 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -458,6 +458,7 @@ static const struct of_device_id tegra_cec_of_match[] = { { .compatible = "nvidia,tegra210-cec", }, {}, }; +MODULE_DEVICE_TABLE(of, tegra_cec_of_match); static struct platform_driver tegra_cec_driver = { .driver = { -- cgit From 1924d0788caa6c66fd320dd4704fae99487fd2c7 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 11:01:02 +0800 Subject: media: cec: Serialize exclusive follower delivery cec_receive_notify() reads the exclusive follower pointer without the adapter lock. Serialize the no-follower check and message delivery against mode changes and release. Fixes: 9881fe0ca187 ("[media] cec: add HDMI CEC framework (adapter)") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-adap.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 829ee4861bf7..de0c4fcd8dfe 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -2219,9 +2219,13 @@ static int cec_receive_notify(struct cec_adapter *adap, struct cec_msg *msg, * Unprocessed messages are aborted if userspace isn't doing * any processing either. */ + mutex_lock(&adap->lock); if (!is_broadcast && !is_reply && !adap->follower_cnt && - !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) + !adap->cec_follower && msg->msg[1] != CEC_MSG_FEATURE_ABORT) { + mutex_unlock(&adap->lock); return cec_feature_abort(adap, msg); + } + mutex_unlock(&adap->lock); break; } @@ -2234,10 +2238,12 @@ skip_processing: * Send to the exclusive follower if there is one, otherwise send * to all followers. */ + mutex_lock(&adap->lock); if (adap->cec_follower) cec_queue_msg_fh(adap->cec_follower, msg); else cec_queue_msg_followers(adap, msg); + mutex_unlock(&adap->lock); return 0; } -- cgit From bedd5ea6b718741fff04ffbfe6ec803ca62841cd Mon Sep 17 00:00:00 2001 From: Luciano Ciccariello Date: Wed, 20 May 2026 15:20:10 +0100 Subject: media: em28xx: Add StarTech SVID2USB232 Add support for the StarTech SVID2USB232 USB analog video grabber (USB ID eb1a:8286). The device uses the Empia EM28281 bridge, a member of the em2828X family with an integrated video decoder. Reuses the EM28XX_BUILTIN decoder path introduced in commit 8e53399c63c3 ("media: em28xx: Add support for Empia em2828X bridge"). The standard PAL/NTSC switching and composite/S-Video input switching in em2828X_decoder_set_std() handle this board without board-specific code. Inputs: - Composite video - S-Video - Analog stereo audio (line in) Tested on hardware with a PAL signal on both S-Video and composite inputs (ffplay -f v4l2 /dev/video0). This supersedes an earlier RFC posting from before EM28XX_BUILTIN was available, which proposed a custom EM28XX_I2C_ALGO_EM28281_INTEGRATED TVP5150-bridge algorithm. That approach is no longer necessary now that the em2828X bridge support landed. Link: https://lore.kernel.org/all/20260119185921.575666-1-xeeynamo@hotmail.com/ Signed-off-by: Luciano Ciccariello Signed-off-by: Hans Verkuil --- .../admin-guide/media/em28xx-cardlist.rst | 4 +++ drivers/media/usb/em28xx/em28xx-cards.c | 29 ++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx-reg.h | 1 + drivers/media/usb/em28xx/em28xx.h | 1 + 4 files changed, 35 insertions(+) diff --git a/Documentation/admin-guide/media/em28xx-cardlist.rst b/Documentation/admin-guide/media/em28xx-cardlist.rst index 7dac07986d91..741aa7f7c882 100644 --- a/Documentation/admin-guide/media/em28xx-cardlist.rst +++ b/Documentation/admin-guide/media/em28xx-cardlist.rst @@ -446,3 +446,7 @@ EM28xx cards list - MyGica UTV3 Analog USB2.0 TV Box - em2860 - eb1a:2860 + * - 113 + - StarTech SVID2USB232 + - em28281 + - eb1a:8286 diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index fbfb74eab475..e3c0f5e491e3 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2677,6 +2677,28 @@ const struct em28xx_board em28xx_boards[] = { .gpio = mygica_utv3_tuner_audio_gpio, } }, }, + /* eb1a:8286 StarTech SVID2USB232 + * Empia EM28281 with integrated TVP5150-compatible video decoder. + * Composite and S-Video inputs, stereo line-in audio. + */ + [EM28281_BOARD_STARTECH_SVID2USB232] = { + .name = "StarTech SVID2USB232", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .has_dvb = 0, + .decoder = EM28XX_BUILTIN, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_COMPOSITE, + .vmux = EM2828X_COMPOSITE, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = EM2828X_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2828X_BOARD_HAUPPAUGE_USB_LIVE2] = { .name = "Hauppauge USB Live2", .vchannels = 2, @@ -2946,6 +2968,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2874_BOARD_HAUPPAUGE_USB_QUADHD }, { USB_DEVICE(0x2040, 0xc220), .driver_info = EM2828X_BOARD_HAUPPAUGE_USB_LIVE2 }, + { USB_DEVICE(0xeb1a, 0x8286), + .driver_info = EM28281_BOARD_STARTECH_SVID2USB232 }, { USB_DEVICE(0x2040, 0x0360), .driver_info = EM2828X_BOARD_HAUPPAUGE_935_V2 }, { USB_DEVICE(0x2040, 0x8360), @@ -3859,6 +3883,11 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->wait_after_write = 0; dev->eeprom_addrwidth_16bit = 1; break; + case CHIP_ID_EM28281: + chip_name = "em28281"; + dev->wait_after_write = 0; + dev->eeprom_addrwidth_16bit = 1; + break; case CHIP_ID_EM2883: chip_name = "em2882/3"; dev->wait_after_write = 0; diff --git a/drivers/media/usb/em28xx/em28xx-reg.h b/drivers/media/usb/em28xx/em28xx-reg.h index 68a0fcc2fa72..8931733a8e24 100644 --- a/drivers/media/usb/em28xx/em28xx-reg.h +++ b/drivers/media/usb/em28xx/em28xx-reg.h @@ -283,6 +283,7 @@ enum em28xx_chip_id { CHIP_ID_EM2884 = 68, CHIP_ID_EM28174 = 113, CHIP_ID_EM28178 = 114, + CHIP_ID_EM28281 = 145, CHIP_ID_EM2828X = 148, }; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 21c912403efc..2b9b95b53307 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -149,6 +149,7 @@ #define EM2828X_BOARD_HAUPPAUGE_955_V2 110 #define EM2828X_BOARD_HAUPPAUGE_975_V2 111 #define EM28178_BOARD_PCTV_461E_V3 112 +#define EM28281_BOARD_STARTECH_SVID2USB232 113 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 -- cgit From ab8c3ed895586f933bdd134fe7765a2e9f303ffd Mon Sep 17 00:00:00 2001 From: Ashwin Gundarapu Date: Fri, 22 May 2026 21:16:52 +0530 Subject: media: cx231xx: fix null pointer deref in snd_cx231xx_pcm_close Add a null check for the dev pointer after retrieving it from the substream. Without this, a use-after-free or null pointer dereference can occur when closing the audio device, causing a kernel page fault. Link: https://bugzilla.redhat.com/show_bug.cgi?id=2365068 Signed-off-by: Ashwin Gundarapu Signed-off-by: Hans Verkuil --- drivers/media/usb/cx231xx/cx231xx-audio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index 9c71b32552df..b24ceef497e4 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -443,6 +443,11 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) int ret; struct cx231xx *dev = snd_pcm_substream_chip(substream); + if (!dev) { + pr_err("cx231xx: called with null device\n"); + return -ENODEV; + } + dev_dbg(dev->dev, "closing device\n"); /* inform hardware to stop streaming */ -- cgit From dabb047c62668f280998e29117c55e41aabac336 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Sat, 23 May 2026 19:53:37 +0300 Subject: media: rtl2832_sdr: use vb2_video_unregister_device() on remove to fix DMA leak rtl2832_sdr_remove() runs on USB disconnect and clears dev->udev to NULL before any pending streaming teardown has run. When user space later closes its file descriptor, vb2 calls rtl2832_sdr_stop_streaming() which in turn calls rtl2832_sdr_free_stream_bufs(). That helper releases each coherent buffer with: usb_free_coherent(dev->udev, dev->buf_size, dev->buf_list[dev->buf_num], dev->dma_addr[dev->buf_num]); usb_free_coherent() returns immediately when its dev argument is NULL, so every DMA stream buffer that was live at disconnect is silently leaked. The URBs allocated in rtl2832_sdr_alloc_urbs() outlive the device for the same reason. The rtl2832_sdr driver uses vb2_fop_release() in its file_operations, so replace video_unregister_device(&dev->vdev) with vb2_video_unregister_device(&dev->vdev) and move it before clearing dev->udev. vb2_video_unregister_device() releases the vb2 queue, which synchronously runs rtl2832_sdr_stop_streaming() if streaming is active, so URBs and coherent DMA stream buffers are freed while dev->udev is still valid. vb2_video_unregister_device() locks vdev->queue->lock (vb_queue_lock) internally, and stop_streaming() locks v4l2_lock, so the previous outer mutex_lock(&dev->vb_queue_lock) / mutex_lock(&dev->v4l2_lock) pair around the unregister sequence would self-deadlock and has been removed. A short v4l2_lock critical section around dev->udev = NULL remains so any ioctl path that still holds the file descriptor sees coherent state. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module") Cc: stable@vger.kernel.org Suggested-by: Hans Verkuil Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832_sdr.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c564485e3bbb..c1f5f07c42a8 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -1477,14 +1477,22 @@ static void rtl2832_sdr_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "\n"); - mutex_lock(&dev->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers rtl2832_sdr_stop_streaming() if streaming is active. + * stop_streaming() uses dev->udev to free URBs and coherent DMA + * stream buffers via usb_free_coherent(), so it must run before + * dev->udev is cleared. vb2_video_unregister_device() locks + * vb_queue_lock internally and stop_streaming() locks v4l2_lock, + * so neither may be held by the caller. + */ + v4l2_device_disconnect(&dev->v4l2_dev); + vb2_video_unregister_device(&dev->vdev); + mutex_lock(&dev->v4l2_lock); - /* No need to keep the urbs around after disconnection */ dev->udev = NULL; - v4l2_device_disconnect(&dev->v4l2_dev); - video_unregister_device(&dev->vdev); mutex_unlock(&dev->v4l2_lock); - mutex_unlock(&dev->vb_queue_lock); + v4l2_device_put(&dev->v4l2_dev); module_put(pdev->dev.parent->driver->owner); } -- cgit From 2f378dc45e685fc825d2dd08e7864666d6fcc009 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Sat, 23 May 2026 19:53:49 +0300 Subject: media: airspy: use vb2_video_unregister_device() on disconnect to fix NULL deref airspy_disconnect() clears s->udev under v4l2_lock, but airspy_stop_streaming() unconditionally calls airspy_ctrl_msg() and airspy_free_stream_bufs() afterwards. If a streaming user closes the device after disconnect, stop_streaming() runs and dereferences the NULL s->udev: airspy_stop_streaming() airspy_ctrl_msg(s, CMD_RECEIVER_MODE, 0, 0, NULL, 0) usb_sndctrlpipe(s->udev, 0) /* NULL deref */ airspy_free_stream_bufs(s) usb_free_coherent(s->udev, ...) /* NULL deref */ The airspy driver uses vb2_fop_release() in its file_operations, so replace video_unregister_device(&s->vdev) with vb2_video_unregister_device(&s->vdev) and move it before clearing s->udev. vb2_video_unregister_device() releases the vb2 queue, which synchronously runs airspy_stop_streaming() if streaming is active, so the URBs, coherent DMA stream buffers and the hardware stop control message all execute while s->udev is still valid. vb2_video_unregister_device() locks vdev->queue->lock (vb_queue_lock) internally, and stop_streaming() locks v4l2_lock, so the previous outer mutex_lock(&s->vb_queue_lock) / mutex_lock(&s->v4l2_lock) pair around the unregister sequence would self-deadlock and has been removed. A short v4l2_lock critical section around s->udev = NULL remains so any ioctl path that still holds the file descriptor sees coherent state. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 634fe5033951 ("[media] airspy: AirSpy SDR driver") Cc: stable@vger.kernel.org Suggested-by: Hans Verkuil Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/usb/airspy/airspy.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c index 57edb42463e8..358a66ab8e48 100644 --- a/drivers/media/usb/airspy/airspy.c +++ b/drivers/media/usb/airspy/airspy.c @@ -464,14 +464,21 @@ static void airspy_disconnect(struct usb_interface *intf) dev_dbg(s->dev, "\n"); - mutex_lock(&s->vb_queue_lock); + /* + * vb2_video_unregister_device() releases the vb2 queue, which + * triggers airspy_stop_streaming() if streaming is active. + * stop_streaming() dereferences s->udev via airspy_ctrl_msg() and + * airspy_free_stream_bufs(), so it must run before s->udev is + * cleared. vb2_video_unregister_device() locks vb_queue_lock + * internally and stop_streaming() locks v4l2_lock, so neither may + * be held by the caller. + */ + v4l2_device_disconnect(&s->v4l2_dev); + vb2_video_unregister_device(&s->vdev); + mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ s->udev = NULL; - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); v4l2_device_put(&s->v4l2_dev); } -- cgit From fe50cdaebf12cd32ff9a44d92bfd6fbc2300dbd4 Mon Sep 17 00:00:00 2001 From: Valery Borovsky Date: Sat, 23 May 2026 19:53:58 +0300 Subject: media: rtl2832_sdr: release URBs and stream buffers on start_streaming() failure rtl2832_sdr_start_streaming() calls rtl2832_sdr_alloc_stream_bufs(), rtl2832_sdr_alloc_urbs() and rtl2832_sdr_submit_urbs() in sequence and shares a single err: label that only unlocks the mutex and returns. When alloc_urbs() succeeds but submit_urbs() fails, or when alloc_urbs() itself returns -ENOMEM after alloc_stream_bufs() has already succeeded, the URBs and/or the coherent DMA stream buffers stay allocated while streaming reports failure to vb2. Two latent defects follow on the next VIDIOC_STREAMON: 1) rtl2832_sdr_alloc_stream_bufs() unconditionally resets dev->buf_num to 0 and overwrites dev->buf_list[]/dev->dma_addr[], permanently leaking the coherent DMA memory allocated by the previous attempt. 2) rtl2832_sdr_alloc_urbs() never resets dev->urbs_initialized and only increments it. After a second successful pass urbs_initialized can exceed MAX_BULK_BUFS, so the subsequent rtl2832_sdr_free_urbs() walks from urbs_initialized - 1 down to 0 and reads past the end of dev->urb_list[], passing garbage pointers to usb_free_urb(). Mirror the teardown that stop_streaming() already performs: on the error path call rtl2832_sdr_free_urbs() and rtl2832_sdr_free_stream_bufs() before unlocking. Both helpers are idempotent (free_urbs kills and zeros urbs_initialized; free_stream_bufs is gated on URB_BUF and clears the buf_num counter), so partial-failure paths and the no-allocation paths remain safe. Issue identified by automated review of the INV-003 series at https://sashiko.dev/ Fixes: 771138920eaf ("[media] rtl2832_sdr: Realtek RTL2832 SDR driver module") Cc: stable@vger.kernel.org Signed-off-by: Valery Borovsky Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/rtl2832_sdr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index c1f5f07c42a8..0330e7f0881a 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -906,9 +906,12 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; mutex_unlock(&dev->v4l2_lock); + return 0; err: + rtl2832_sdr_free_urbs(dev); + rtl2832_sdr_free_stream_bufs(dev); rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED); mutex_unlock(&dev->v4l2_lock); -- cgit From 07d4abbe766aafb4c75c788db172d2d69f9d7612 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 25 May 2026 09:55:42 +0200 Subject: media: camif-core: Drop GPIO handling The driver and platform data refers to the legacy GPIO API, doesn't really use it, and also calls some GPIO-related functions passed as platform data. This platform data is not used anywhere in the kernel. I'm not outright deleting the driver, just deleting the bogus GPIO handling. If outoftree developers want to fix up the driver the GPIOs should be defined in some kind of firmware node and be obtained using the gpiod_get*() APIs directly in the driver, but I don't even know what these GPIOs are since they are hidden in platform data that is not in the mainline kernel, so what can I do. Signed-off-by: Linus Walleij Reviewed-by: Bartosz Golaszewski Signed-off-by: Hans Verkuil --- drivers/media/platform/samsung/s3c-camif/camif-core.c | 10 ++-------- include/media/drv-intf/s3c_camif.h | 2 -- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index 221e3c447f36..14eedd1ceb27 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -412,7 +411,7 @@ static int s3c_camif_probe(struct platform_device *pdev) camif->dev = dev; - if (!pdata || !pdata->gpio_get || !pdata->gpio_put) { + if (!pdata) { dev_err(dev, "wrong platform data\n"); return -EINVAL; } @@ -429,9 +428,7 @@ static int s3c_camif_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = pdata->gpio_get(); - if (ret < 0) - return ret; + /* FIXME: get GPIOs here */ ret = s3c_camif_create_subdev(camif); if (ret < 0) @@ -504,14 +501,12 @@ err_disable: err_clk: s3c_camif_unregister_subdev(camif); err_sd: - pdata->gpio_put(); return ret; } static void s3c_camif_remove(struct platform_device *pdev) { struct camif_dev *camif = platform_get_drvdata(pdev); - struct s3c_camif_plat_data *pdata = &camif->pdata; media_device_unregister(&camif->media_dev); media_device_cleanup(&camif->media_dev); @@ -521,7 +516,6 @@ static void s3c_camif_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); camif_clk_put(camif); s3c_camif_unregister_subdev(camif); - pdata->gpio_put(); } static int s3c_camif_runtime_resume(struct device *dev) diff --git a/include/media/drv-intf/s3c_camif.h b/include/media/drv-intf/s3c_camif.h index f746851a5ce6..00d83620ea5f 100644 --- a/include/media/drv-intf/s3c_camif.h +++ b/include/media/drv-intf/s3c_camif.h @@ -31,8 +31,6 @@ struct s3c_camif_sensor_info { struct s3c_camif_plat_data { struct s3c_camif_sensor_info sensor; - int (*gpio_get)(void); - int (*gpio_put)(void); }; #endif /* MEDIA_S3C_CAMIF_ */ -- cgit From 86de1c762966e4be773bcdb63220679fcb61c9b8 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Thu, 28 May 2026 07:16:49 +0000 Subject: media: Documentation: Add myself as maintainer for media-ci Add myself as maintainer of media-ci. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- Documentation/driver-api/media/maintainer-entry-profile.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/driver-api/media/maintainer-entry-profile.rst b/Documentation/driver-api/media/maintainer-entry-profile.rst index c5c00c66d85c..7806d83128d4 100644 --- a/Documentation/driver-api/media/maintainer-entry-profile.rst +++ b/Documentation/driver-api/media/maintainer-entry-profile.rst @@ -432,6 +432,10 @@ The Media Driver Maintainers responsible for specific areas are: - Qualcomm drivers + - Ricardo Ribalda + + - Media-CI + Submit Checklist Addendum ------------------------- -- cgit From 23b8b4cbd88617bd8610bab54588f24bd8d1e2a9 Mon Sep 17 00:00:00 2001 From: Ben Hoff Date: Sat, 6 Jun 2026 00:05:49 -0400 Subject: media: hws: Remove stale control ioctl prototypes The HWS driver uses the V4L2 control handler callbacks provided by hws_ctrl_ops and does not implement legacy vidioc_g_ctrl, vidioc_s_ctrl, or vidioc_queryctrl callbacks. Drop the stale prototypes from hws_v4l2_ioctl.h. Signed-off-by: Ben Hoff Signed-off-by: Hans Verkuil --- drivers/media/pci/hws/hws_v4l2_ioctl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h index 53044f78d6fa..9a81e940e33e 100644 --- a/drivers/media/pci/hws/hws_v4l2_ioctl.h +++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h @@ -17,14 +17,10 @@ int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfp int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i); int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i); int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i); -int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a); -int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a); int hws_vidioc_dv_timings_cap(struct file *file, void *fh, struct v4l2_dv_timings_cap *cap); int hws_vidioc_s_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings); - -int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a); int hws_vidioc_g_dv_timings(struct file *file, void *fh, struct v4l2_dv_timings *timings); int hws_vidioc_enum_dv_timings(struct file *file, void *fh, -- cgit From 627a121c15fe05a541f44d86016294b80bada75d Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 15 Jun 2026 18:40:48 -0500 Subject: media: cx231xx: reject geometry changes while the VBI queue is busy vidioc_s_fmt_vid_cap() and vidioc_s_std() change the device-wide dev->width / dev->norm but only refuse the change when the *video* queue (dev->vidq) is busy. The VBI queue (dev->vbiq) shares that same geometry: cx231xx_init_vbi_isoc() latches dma_q->lines_per_field from dev->norm, the VBI videobuf2 plane is sized from dev->width / dev->norm in vbi_queue_setup() and vbi_buf_prepare(), and cx231xx_do_vbi_copy() then recomputes the destination offset from the *live* dev->width and the latched lines_per_field on every URB completion: offset = lines_completed * (dev->width << 1) + ...; if (dma_q->current_field == 2) offset += dev->width * 2 * dma_q->lines_per_field; memcpy(plane + offset, p_buffer, lencopy); Because the VBI node shares video_ioctl_ops with the video node, an application can size a small VBI plane (REQBUFS/QBUF with a small width, or with the NTSC standard), then enlarge dev->width (or switch dev->norm to PAL) through the video node while the VBI stream is running -- the change is allowed because only dev->vidq is checked -- and let the device deliver a field-2 VBI payload. cx231xx_do_vbi_copy() now computes the offset with the larger geometry and memcpy()s past the end of the smaller plane that was already allocated, a heap out-of-bounds write whose offset is attacker-chosen and whose contents come from the device. The per-field guard in cx231xx_copy_vbi_line() does not help: it bounds the copy against the latched lines_per_field, not the plane's real capacity, and vb2 does not re-run buf_prepare() for an already prepared buffer. Refuse the format/standard change when the VBI queue is busy as well, so the geometry cannot change underneath an allocated VBI buffer. Fixes: 7c617138b825 ("media: cx231xx: convert to the vb2 framework") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas Signed-off-by: Hans Verkuil --- drivers/media/usb/cx231xx/cx231xx-video.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 2cd4e333bc4b..70aa99fead27 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -898,7 +898,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, if (rc) return rc; - if (vb2_is_busy(&dev->vidq)) { + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) { dev_err(dev->dev, "%s: queue busy\n", __func__); return -EBUSY; } @@ -933,7 +933,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) if (dev->norm == norm) return 0; - if (vb2_is_busy(&dev->vidq)) + if (vb2_is_busy(&dev->vidq) || vb2_is_busy(&dev->vbiq)) return -EBUSY; dev->norm = norm; -- cgit From c49cdaafa443ccf861a72d3191121294742c28c7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Jun 2026 16:32:47 +0200 Subject: media: v4l2-tpg: reduce stack usage for kasan builds tpg_fill_plane_buffer() is a rather complex function. While there is nothing wrong with it per se, I have run into corner cases with clang-22 on s390 using KASAN that makes it run out of registers and blow the stack warning limit from excessive spills: drivers/media/common/v4l2-tpg/v4l2-tpg-core.c:2629:6: error: stack frame size (1560) exceeds limit (1536) in 'tpg_fill_plane_buffer' [-Werror,-Wframe-larger-than] 2629 | void tpg_fill_plane_buffer(struct tpg_data *tpg, v4l2_std_id std, Forcing the two largest callees out of line completely avoids the problem and prevents all the register spills, with the stack usage for each function going down to a few bytes for the local variables. Arguably this is a problem caused by clang rather than the code, but a noinline_for_stack annotation is an easy workaround. Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil --- drivers/media/common/v4l2-tpg/v4l2-tpg-core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c index 931e5dc453b9..e1d5c220f738 100644 --- a/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c +++ b/drivers/media/common/v4l2-tpg/v4l2-tpg-core.c @@ -2346,9 +2346,11 @@ static void tpg_fill_params_extras(const struct tpg_data *tpg, (params->is_60hz ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM); } -static void tpg_fill_plane_extras(const struct tpg_data *tpg, - const struct tpg_draw_params *params, - unsigned p, unsigned h, u8 *vbuf) +/* noinline to work around clang KASAN issues */ +static noinline_for_stack void +tpg_fill_plane_extras(const struct tpg_data *tpg, + const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) { unsigned twopixsize = params->twopixsize; unsigned img_width = params->img_width; @@ -2483,9 +2485,9 @@ static void tpg_fill_plane_extras(const struct tpg_data *tpg, } } -static void tpg_fill_plane_pattern(const struct tpg_data *tpg, - const struct tpg_draw_params *params, - unsigned p, unsigned h, u8 *vbuf) +static noinline_for_stack void +tpg_fill_plane_pattern(const struct tpg_data *tpg, const struct tpg_draw_params *params, + unsigned p, unsigned h, u8 *vbuf) { unsigned twopixsize = params->twopixsize; unsigned img_width = params->img_width; -- cgit From fb11735b957fd9c9ba5cec38329cd7c9a1ca51eb Mon Sep 17 00:00:00 2001 From: Binglin Zhao <1311165354@qq.com> Date: Thu, 25 Jun 2026 23:57:55 -0400 Subject: media: v4l2-ioctl: fix video_ioctl2 kernel-doc The video_ioctl2() kernel-doc comment says "hancle" and refers to v4l2_ioctl_ops.unlocked_ioctl. Fix the typo and refer to v4l2_file_operations.unlocked_ioctl instead. The unlocked_ioctl member belongs to struct v4l2_file_operations. Signed-off-by: Binglin Zhao <1311165354@qq.com> Signed-off-by: Hans Verkuil --- include/media/v4l2-ioctl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 54c83b18d555..a13dca7c609f 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -736,8 +736,8 @@ long int video_usercopy(struct file *file, unsigned int cmd, * @cmd: Ioctl name. * @arg: Ioctl argument. * - * Method used to hancle an ioctl. Should be used to fill the - * &v4l2_ioctl_ops.unlocked_ioctl on all V4L2 drivers. + * Method used to handle an ioctl. Should be used to fill the + * &v4l2_file_operations.unlocked_ioctl on all V4L2 drivers. */ long int video_ioctl2(struct file *file, unsigned int cmd, unsigned long int arg); -- cgit From 38d9078f8bb3e07973fbc7152a1f6f1318fedee2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 29 Jun 2026 15:26:30 +0200 Subject: media: replace linux/gpio.h inclusions linux/gpio.h should no longer be used, convert these instead to either linux/gpio/consumer.h or linux/gpio/legacy.h as needed. Signed-off-by: Arnd Bergmann Signed-off-by: Hans Verkuil --- drivers/media/pci/ddbridge/ddbridge.h | 2 +- drivers/media/usb/em28xx/em28xx-dvb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/pci/ddbridge/ddbridge.h b/drivers/media/pci/ddbridge/ddbridge.h index f01ecdb0b627..cf50898f9a92 100644 --- a/drivers/media/pci/ddbridge/ddbridge.h +++ b/drivers/media/pci/ddbridge/ddbridge.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 938f1980d448..8482fc4045ea 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -29,7 +29,7 @@ #include #include #include "tuner-simple.h" -#include +#include #include "lgdt330x.h" #include "lgdt3305.h" -- cgit From 3438f11372332cd46995f7ccda7eace0269bf221 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:42 +0000 Subject: media: em28xx-video: Remove unneeded semicolons There is no need to add a semicolon after a switch statement. It also makes cocci a bit uneasy. It triggers the following warnings: ./usb/em28xx/em28xx-cards.c:4085:2-3: Unneeded semicolon ./usb/em28xx/em28xx-core.c:635:2-3: Unneeded semicolon Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- drivers/media/usb/em28xx/em28xx-core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e3c0f5e491e3..c71cce963850 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -4111,7 +4111,7 @@ static void em28xx_check_usb_descriptor(struct em28xx *dev, dev->analog_ep_bulk = e->bEndpointAddress; } return; - }; + } } /* diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 5bbb082dbed9..d4197e37f637 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -632,7 +632,7 @@ void em2828X_decoder_vmux(struct em28xx *dev, unsigned int vin) default: dev_dbg(&dev->intf->dev, "EM2828X_SVIDEO\n"); break; - }; + } em28xx_write_reg(dev, 0x24, 0x00); em28xx_write_reg(dev, 0x25, 0x02); -- cgit From c19f5b0a979815491a55e8932f59b693aab6672e Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:43 +0000 Subject: media: iris: Replace ternary conditionals with max() The max() macro is simpler to read than the current construction, it also makes cocci happier, which currently throws these warnings: ./platform/qcom/iris/iris_vpu_buffer.c:703:13-15: WARNING opportunity for max() ./platform/qcom/iris/iris_vpu_buffer.c:583:23-25: WARNING opportunity for max() Signed-off-by: Ricardo Ribalda Reviewed-by: Bryan O'Donoghue Signed-off-by: Hans Verkuil --- drivers/media/platform/qcom/iris/iris_vpu_buffer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c index fb6f1016415e..faebb5472866 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c @@ -580,7 +580,7 @@ static u32 hfi_buffer_line_av1d(u32 frame_width, u32 frame_height, ALIGN(size_av1d_qp(frame_width, frame_height), DMA_ALIGNMENT); opbwr8 = size_av1d_lb_opb_wr1_nv12_ubwc(frame_width, frame_height); opbwr10 = size_av1d_lb_opb_wr1_tp10_ubwc(frame_width, frame_height); - opbwrbufsize = opbwr8 >= opbwr10 ? opbwr8 : opbwr10; + opbwrbufsize = max(opbwr8, opbwr10); size = ALIGN((size + opbwrbufsize), DMA_ALIGNMENT); if (is_opb) { vpss_lb_size = size_vpss_lb(frame_width, frame_height); @@ -700,7 +700,7 @@ static u32 hfi_buffer_ibc_av1d(u32 frame_width, u32 frame_height) ibc8 = size_av1d_ibc_nv12_ubwc(frame_width, frame_height); ibc10 = size_av1d_ibc_tp10_ubwc(frame_width, frame_height); - size = ibc8 >= ibc10 ? ibc8 : ibc10; + size = max(ibc8, ibc10); return ALIGN(size, DMA_ALIGNMENT); } -- cgit From 91624e58060476381f74c4e3da3fad017fba24d9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:44 +0000 Subject: media: vimc: Fix prototype of vimc_sensor_update_frame_timing The function does not return any value, make it into a void function. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-sensor.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index 5deebcc78a33..d125a79fec8c 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -92,8 +92,8 @@ static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor, tpg_s_xfer_func(&vsensor->tpg, format->xfer_func); } -static int vimc_sensor_update_frame_timing(struct v4l2_subdev *sd, - u32 width, u32 height) +static void vimc_sensor_update_frame_timing(struct v4l2_subdev *sd, + u32 width, u32 height) { struct vimc_sensor_device *vsensor = container_of(sd, struct vimc_sensor_device, sd); @@ -108,8 +108,6 @@ static int vimc_sensor_update_frame_timing(struct v4l2_subdev *sd, vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns); if (vsensor->hw.fps_jiffies == 0) vsensor->hw.fps_jiffies = 1; - - return 0; } static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt) -- cgit From 6b8ff734124a0ecccdbe3e315f6eb37315dbe5ee Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:45 +0000 Subject: media: vimc: Ensure that pixel_rate fits in 32 bits pixel_rate is set to VIMC_PIXEL_RATE_FIXED, which the code expects to fit in 32 bits. Make that constraint into a WARN_ON, so if we ever break that constraint a kernel warning will be triggered. It also fixes the following cocci warning: ./test-drivers/vimc/vimc-sensor.c:107:1-7: WARNING: do_div() does a 64-by-32 division, please consider using div64_u64 instead. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-sensor.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c index d125a79fec8c..83dcc9d61ee0 100644 --- a/drivers/media/test-drivers/vimc/vimc-sensor.c +++ b/drivers/media/test-drivers/vimc/vimc-sensor.c @@ -103,8 +103,12 @@ static void vimc_sensor_update_frame_timing(struct v4l2_subdev *sd, u64 total_pixels = (u64)hts * vts; u64 frame_interval_ns; + /* Sanity check, pixel rate is fixed and fits in 32 bits. */ + if (WARN_ON(pixel_rate >= 0x100000000)) + return; + frame_interval_ns = total_pixels * NSEC_PER_SEC; - do_div(frame_interval_ns, pixel_rate); + do_div(frame_interval_ns, (u32)pixel_rate); vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns); if (vsensor->hw.fps_jiffies == 0) vsensor->hw.fps_jiffies = 1; -- cgit From 0baf8f42110b7c361bb3f6a8a78c0958a23e4e32 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:46 +0000 Subject: media: platform: amd: use refcount_t instead of atomic_t We are using the refcnt variable for refcounting. Use the refcount_t type instead, as it has support for saturation and underflow. This also makes cocci happier, as it will fix the following warning: ./platform/amd/isp4/isp4_subdev.c:394:6-25: WARNING: atomic_dec_and_test variation before object free at line 395. Fixes: 4c5feef6a62c ("media: platform: amd: Add isp4 fw and hw interface") Signed-off-by: Ricardo Ribalda Reviewed-by: Pratap Nirujogi Reviewed-by: Bin Du Signed-off-by: Hans Verkuil --- drivers/media/platform/amd/isp4/isp4_interface.c | 4 ++-- drivers/media/platform/amd/isp4/isp4_interface.h | 2 +- drivers/media/platform/amd/isp4/isp4_subdev.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index 8d73f66bb42c..00a817909292 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -375,7 +375,7 @@ static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id, return -ENOMEM; /* Get two references: one for the resp thread, one for us */ - atomic_set(&ele->refcnt, 2); + refcount_set(&ele->refcnt, 2); init_completion(&ele->cmd_done); } @@ -455,7 +455,7 @@ err_dequeue_ele: put_ele_ref: /* Don't free the command if we didn't put the last reference */ - if (ele && atomic_dec_return(&ele->refcnt)) + if (ele && !refcount_dec_and_test(&ele->refcnt)) ele = NULL; free_ele: diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h index ce3ac9b9e5cd..04db71cd54e6 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.h +++ b/drivers/media/platform/amd/isp4/isp4_interface.h @@ -68,7 +68,7 @@ struct isp4if_cmd_element { u32 seq_num; u32 cmd_id; struct completion cmd_done; - atomic_t refcnt; + refcount_t refcnt; }; struct isp4_interface { diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 48deea79ce6c..2a8bc1207843 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -391,7 +391,7 @@ static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev, if (ele) { complete(&ele->cmd_done); - if (atomic_dec_and_test(&ele->refcnt)) + if (refcount_dec_and_test(&ele->refcnt)) kfree(ele); } } -- cgit From 5b8af852cd0303fc2faafd80d76ca7b19d15ca54 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:47 +0000 Subject: media: dvb-frontends/helene: Rename priv variable Coccinelle triggers a false positive where it thinks that the priv variable in helene_attach_s and helene_attach is the same variable as helene_probe. This is due to a bad heuristic in cocci. We have reported it to cocci, but until/if this is fixed, renaming a local variable is a good compromise to fix this warning: ./dvb-frontends/helene.c:1049:2-7: WARNING: invalid free of devm_ allocated data ./dvb-frontends/helene.c:1013:2-7: WARNING: invalid free of devm_ allocated data Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-frontends/helene.c | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c index 993280fefc2c..5fbb466cc8af 100644 --- a/drivers/media/dvb-frontends/helene.c +++ b/drivers/media/dvb-frontends/helene.c @@ -995,22 +995,22 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, const struct helene_config *config, struct i2c_adapter *i2c) { - struct helene_priv *priv = NULL; + struct helene_priv *pr = NULL; - priv = kzalloc_obj(struct helene_priv); - if (priv == NULL) + pr = kzalloc_obj(struct helene_priv); + if (!pr) return NULL; - priv->i2c_address = (config->i2c_address >> 1); - priv->i2c = i2c; - priv->set_tuner_data = config->set_tuner_priv; - priv->set_tuner = config->set_tuner_callback; - priv->xtal = config->xtal; + pr->i2c_address = (config->i2c_address >> 1); + pr->i2c = i2c; + pr->set_tuner_data = config->set_tuner_priv; + pr->set_tuner = config->set_tuner_callback; + pr->xtal = config->xtal; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (helene_x_pon(priv) != 0) { - kfree(priv); + if (helene_x_pon(pr) != 0) { + kfree(pr); return NULL; } @@ -1019,10 +1019,10 @@ struct dvb_frontend *helene_attach_s(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_s, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = priv; - dev_info(&priv->i2c->dev, - "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", - priv->i2c_address, priv->i2c); + fe->tuner_priv = pr; + dev_info(&pr->i2c->dev, + "Sony HELENE Sat attached on addr=%x at I2C adapter %p\n", + pr->i2c_address, pr->i2c); return fe; } EXPORT_SYMBOL_GPL(helene_attach_s); @@ -1031,22 +1031,22 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, const struct helene_config *config, struct i2c_adapter *i2c) { - struct helene_priv *priv = NULL; + struct helene_priv *pr = NULL; - priv = kzalloc_obj(struct helene_priv); - if (priv == NULL) + pr = kzalloc_obj(struct helene_priv); + if (!pr) return NULL; - priv->i2c_address = (config->i2c_address >> 1); - priv->i2c = i2c; - priv->set_tuner_data = config->set_tuner_priv; - priv->set_tuner = config->set_tuner_callback; - priv->xtal = config->xtal; + pr->i2c_address = (config->i2c_address >> 1); + pr->i2c = i2c; + pr->set_tuner_data = config->set_tuner_priv; + pr->set_tuner = config->set_tuner_callback; + pr->xtal = config->xtal; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (helene_x_pon(priv) != 0) { - kfree(priv); + if (helene_x_pon(pr) != 0) { + kfree(pr); return NULL; } @@ -1055,10 +1055,10 @@ struct dvb_frontend *helene_attach(struct dvb_frontend *fe, memcpy(&fe->ops.tuner_ops, &helene_tuner_ops_t, sizeof(struct dvb_tuner_ops)); - fe->tuner_priv = priv; - dev_info(&priv->i2c->dev, - "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", - priv->i2c_address, priv->i2c); + fe->tuner_priv = pr; + dev_info(&pr->i2c->dev, + "Sony HELENE Ter attached on addr=%x at I2C adapter %p\n", + pr->i2c_address, pr->i2c); return fe; } EXPORT_SYMBOL_GPL(helene_attach); -- cgit From e8123c85a6fd15951298182ffa66bcf006f6707b Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:48 +0000 Subject: media: drivers/media/dvb-core: Split dvb_frontend_open() Move the actual opening to its own function. Not intended code change. This is a preparation for the next patch. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-core/dvb_frontend.c | 148 ++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 68 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index d082b6c57c76..d99b0348df54 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2760,77 +2760,25 @@ static __poll_t dvb_frontend_poll(struct file *file, struct poll_table_struct *w return 0; } -static int dvb_frontend_open(struct inode *inode, struct file *file) +static int __dvb_frontend_open(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; - struct dvb_adapter *adapter = fe->dvb; int ret; - dev_dbg(fe->dvb->device, "%s:\n", __func__); - if (fe->exit == DVB_FE_DEVICE_REMOVED) - return -ENODEV; - - if (adapter->mfe_shared == 2) { - mutex_lock(&adapter->mfe_lock); - if ((file->f_flags & O_ACCMODE) != O_RDONLY) { - if (adapter->mfe_dvbdev && - !adapter->mfe_dvbdev->writers) { - mutex_unlock(&adapter->mfe_lock); - return -EBUSY; - } - adapter->mfe_dvbdev = dvbdev; - } - } else if (adapter->mfe_shared) { - mutex_lock(&adapter->mfe_lock); - - if (!adapter->mfe_dvbdev) - adapter->mfe_dvbdev = dvbdev; - - else if (adapter->mfe_dvbdev != dvbdev) { - struct dvb_device - *mfedev = adapter->mfe_dvbdev; - struct dvb_frontend - *mfe = mfedev->priv; - struct dvb_frontend_private - *mfepriv = mfe->frontend_priv; - int mferetry = (dvb_mfe_wait_time << 1); - - mutex_unlock(&adapter->mfe_lock); - while (mferetry-- && (mfedev->users != -1 || - mfepriv->thread)) { - if (msleep_interruptible(500)) { - if (signal_pending(current)) - return -EINTR; - } - } - - mutex_lock(&adapter->mfe_lock); - if (adapter->mfe_dvbdev != dvbdev) { - mfedev = adapter->mfe_dvbdev; - mfe = mfedev->priv; - mfepriv = mfe->frontend_priv; - if (mfedev->users != -1 || - mfepriv->thread) { - mutex_unlock(&adapter->mfe_lock); - return -EBUSY; - } - adapter->mfe_dvbdev = dvbdev; - } - } - } - if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) { if ((ret = fe->ops.ts_bus_ctrl(fe, 1)) < 0) - goto err0; - - /* If we took control of the bus, we need to force - reinitialization. This is because many ts_bus_ctrl() - functions strobe the RESET pin on the demod, and if the - frontend thread already exists then the dvb_init() routine - won't get called (which is what usually does initial - register configuration). */ + return ret; + + /* + * If we took control of the bus, we need to force + * reinitialization. This is because many ts_bus_ctrl() + * functions strobe the RESET pin on the demod, and if the + * frontend thread already exists then the dvb_init() routine + * won't get called (which is what usually does initial + * register configuration). + */ fepriv->reinitialise = 1; } @@ -2871,8 +2819,6 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) dvb_frontend_get(fe); - if (adapter->mfe_shared) - mutex_unlock(&adapter->mfe_lock); return ret; err3: @@ -2891,9 +2837,75 @@ err2: err1: if (dvbdev->users == -1 && fe->ops.ts_bus_ctrl) fe->ops.ts_bus_ctrl(fe, 0); -err0: - if (adapter->mfe_shared) - mutex_unlock(&adapter->mfe_lock); + + return ret; +} + +static int dvb_frontend_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_frontend *fe = dvbdev->priv; + struct dvb_adapter *adapter = fe->dvb; + int ret; + + dev_dbg(fe->dvb->device, "%s:\n", __func__); + if (fe->exit == DVB_FE_DEVICE_REMOVED) + return -ENODEV; + + if (!adapter->mfe_shared) + return __dvb_frontend_open(inode, file); + + if (adapter->mfe_shared == 2) { + mutex_lock(&adapter->mfe_lock); + if ((file->f_flags & O_ACCMODE) != O_RDONLY) { + if (adapter->mfe_dvbdev && + !adapter->mfe_dvbdev->writers) { + mutex_unlock(&adapter->mfe_lock); + return -EBUSY; + } + adapter->mfe_dvbdev = dvbdev; + } + } else { + mutex_lock(&adapter->mfe_lock); + + if (!adapter->mfe_dvbdev) { + adapter->mfe_dvbdev = dvbdev; + } else if (adapter->mfe_dvbdev != dvbdev) { + struct dvb_device + *mfedev = adapter->mfe_dvbdev; + struct dvb_frontend + *mfe = mfedev->priv; + struct dvb_frontend_private + *mfepriv = mfe->frontend_priv; + int mferetry = (dvb_mfe_wait_time << 1); + + mutex_unlock(&adapter->mfe_lock); + while (mferetry-- && (mfedev->users != -1 || + mfepriv->thread)) { + if (msleep_interruptible(500)) { + if (signal_pending(current)) + return -EINTR; + } + } + + mutex_lock(&adapter->mfe_lock); + if (adapter->mfe_dvbdev != dvbdev) { + mfedev = adapter->mfe_dvbdev; + mfe = mfedev->priv; + mfepriv = mfe->frontend_priv; + if (mfedev->users != -1 || + mfepriv->thread) { + mutex_unlock(&adapter->mfe_lock); + return -EBUSY; + } + adapter->mfe_dvbdev = dvbdev; + } + } + } + + ret = __dvb_frontend_open(inode, file); + mutex_unlock(&adapter->mfe_lock); + return ret; } -- cgit From 245019a0836cb5f24efae05cb7b3b0d9c10519d9 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:49 +0000 Subject: media: drivers/media/dvb-core: Refactor dvb_frontend_open locking Split out the wait function, and introduce some new toys: guard and lockdep. This fixes the following cocci warnings: drivers/media/dvb-core/dvb_frontend.c:2897:1-7: preceding lock on line 2776 drivers/media/dvb-core/dvb_frontend.c:2897:1-7: preceding lock on line 2786 drivers/media/dvb-core/dvb_frontend.c:2897:1-7: preceding lock on line 2809 Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-core/dvb_frontend.c | 58 ++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index d99b0348df54..791834e088b6 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -2841,6 +2842,34 @@ err1: return ret; } +static int wait_dvb_frontend(struct dvb_adapter *adapter, + struct dvb_device *mfedev) +{ + struct dvb_frontend *mfe = mfedev->priv; + struct dvb_frontend_private *mfepriv = mfe->frontend_priv; + int mferetry = (dvb_mfe_wait_time << 1); + int ret = 0; + + lockdep_assert_held(&adapter->mfe_lock); + + if (mfedev->users == -1 && !mfepriv->thread) + return 0; + + mutex_unlock(&adapter->mfe_lock); + + while (mferetry-- && (mfedev->users != -1 || mfepriv->thread)) { + if (msleep_interruptible(500)) + if (signal_pending(current)) { + ret = -EINTR; + break; + } + } + + mutex_lock(&adapter->mfe_lock); + + return ret; +} + static int dvb_frontend_open(struct inode *inode, struct file *file) { struct dvb_device *dvbdev = file->private_data; @@ -2855,19 +2884,16 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) if (!adapter->mfe_shared) return __dvb_frontend_open(inode, file); + guard(mutex)(&adapter->mfe_lock); + if (adapter->mfe_shared == 2) { - mutex_lock(&adapter->mfe_lock); if ((file->f_flags & O_ACCMODE) != O_RDONLY) { if (adapter->mfe_dvbdev && - !adapter->mfe_dvbdev->writers) { - mutex_unlock(&adapter->mfe_lock); + !adapter->mfe_dvbdev->writers) return -EBUSY; - } adapter->mfe_dvbdev = dvbdev; } } else { - mutex_lock(&adapter->mfe_lock); - if (!adapter->mfe_dvbdev) { adapter->mfe_dvbdev = dvbdev; } else if (adapter->mfe_dvbdev != dvbdev) { @@ -2877,34 +2903,24 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) *mfe = mfedev->priv; struct dvb_frontend_private *mfepriv = mfe->frontend_priv; - int mferetry = (dvb_mfe_wait_time << 1); - - mutex_unlock(&adapter->mfe_lock); - while (mferetry-- && (mfedev->users != -1 || - mfepriv->thread)) { - if (msleep_interruptible(500)) { - if (signal_pending(current)) - return -EINTR; - } - } - mutex_lock(&adapter->mfe_lock); + ret = wait_dvb_frontend(adapter, mfedev); + if (ret) + return ret; + if (adapter->mfe_dvbdev != dvbdev) { mfedev = adapter->mfe_dvbdev; mfe = mfedev->priv; mfepriv = mfe->frontend_priv; if (mfedev->users != -1 || - mfepriv->thread) { - mutex_unlock(&adapter->mfe_lock); + mfepriv->thread) return -EBUSY; - } adapter->mfe_dvbdev = dvbdev; } } } ret = __dvb_frontend_open(inode, file); - mutex_unlock(&adapter->mfe_lock); return ret; } -- cgit From 85aa8122459eb96d2f3e0d32af436cf8d0961f8f Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:30:50 +0000 Subject: media: drivers/media/dvb-core: CodeStyle for dvb_frontend_open() We can rearrange a bit the function to reduce the indentation levels. No functional change added to this patch. Signed-off-by: Ricardo Ribalda Signed-off-by: Hans Verkuil --- drivers/media/dvb-core/dvb_frontend.c | 49 ++++++++++++++++------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index 791834e088b6..0286da57f382 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2875,7 +2875,6 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_adapter *adapter = fe->dvb; - int ret; dev_dbg(fe->dvb->device, "%s:\n", __func__); if (fe->exit == DVB_FE_DEVICE_REMOVED) @@ -2893,36 +2892,32 @@ static int dvb_frontend_open(struct inode *inode, struct file *file) return -EBUSY; adapter->mfe_dvbdev = dvbdev; } - } else { - if (!adapter->mfe_dvbdev) { + return __dvb_frontend_open(inode, file); + } + + if (!adapter->mfe_dvbdev) { + adapter->mfe_dvbdev = dvbdev; + } else if (adapter->mfe_dvbdev != dvbdev) { + struct dvb_device *mfedev = adapter->mfe_dvbdev; + struct dvb_frontend *mfe = mfedev->priv; + struct dvb_frontend_private *mfepriv = mfe->frontend_priv; + int ret; + + ret = wait_dvb_frontend(adapter, mfedev); + if (ret) + return ret; + + if (adapter->mfe_dvbdev != dvbdev) { + mfedev = adapter->mfe_dvbdev; + mfe = mfedev->priv; + mfepriv = mfe->frontend_priv; + if (mfedev->users != -1 || mfepriv->thread) + return -EBUSY; adapter->mfe_dvbdev = dvbdev; - } else if (adapter->mfe_dvbdev != dvbdev) { - struct dvb_device - *mfedev = adapter->mfe_dvbdev; - struct dvb_frontend - *mfe = mfedev->priv; - struct dvb_frontend_private - *mfepriv = mfe->frontend_priv; - - ret = wait_dvb_frontend(adapter, mfedev); - if (ret) - return ret; - - if (adapter->mfe_dvbdev != dvbdev) { - mfedev = adapter->mfe_dvbdev; - mfe = mfedev->priv; - mfepriv = mfe->frontend_priv; - if (mfedev->users != -1 || - mfepriv->thread) - return -EBUSY; - adapter->mfe_dvbdev = dvbdev; - } } } - ret = __dvb_frontend_open(inode, file); - - return ret; + return __dvb_frontend_open(inode, file); } static int dvb_frontend_release(struct inode *inode, struct file *file) -- cgit From 4e72f13d58c4245c177a9d5f54579345554f354d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 29 Jun 2026 08:47:05 +0200 Subject: media: v4l2-async: Unregister sub-device if asc_list is empty When my em28xx USB device that uses the i2c tvp5150 driver is disconnected, it crashes. The cause is that the tvp5150 i2c module uses v4l2_async, but the em28xx driver does not since it predates v4l2_async. In that corner case sd->asc_list is empty, so v4l2_async_unregister_subdev() never calls v4l2_device_unregister_subdev(). Modify the code so that, if sd->asc_list is empty, v4l2_device_unregister_subdev() is still called. Fixes: 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities") Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 888a2e213b08..f36d60e6ff41 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -898,9 +898,18 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) sd->subdev_notifier = NULL; if (sd->asc_list.next) { - list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list, - asc_subdev_entry) { - v4l2_async_unbind_subdev_one(asc->notifier, asc); + if (list_empty(&sd->asc_list)) { + /* + * If the sub-device was registered through other means + * than v4l2-async, there are no async connections but + * the sub-device may still well be registered. + * Unregister it now. + */ + v4l2_device_unregister_subdev(sd); + } else { + list_for_each_entry_safe(asc, asc_tmp, &sd->asc_list, + asc_subdev_entry) + v4l2_async_unbind_subdev_one(asc->notifier, asc); } } -- cgit From 288254383674be25b26cf137bc12bfe38fefbad6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 19 May 2026 12:07:59 +0200 Subject: media: em28xx: use v4l2_device release callback The em28xx driver creates a lot of video devices, but life-time management is really bad. Instead use the struct v4l2_device release() callback to have a single place where memory can be freed once the last user has gone. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 66 ++++++++++++++++++++------------- drivers/media/usb/em28xx/em28xx.h | 1 - 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index da0422c65e5f..42f5e7547cc4 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2275,18 +2275,27 @@ static int radio_s_tuner(struct file *file, void *priv, } /* - * em28xx_free_v4l2() - Free struct em28xx_v4l2 + * em28xx_free_v4l2() - v4l2_device release callback * - * @ref: struct kref for struct em28xx_v4l2 + * @v4l2_dev: pointer to struct v4l2_device embedded in struct em28xx_v4l2 * - * Called when all users of struct em28xx_v4l2 are gone + * Called by the v4l2 core when the last reference to the v4l2_device is + * released. At this point no userspace file handle nor video_device node + * keeps the v4l2 instance alive anymore, so it is safe to release all + * v4l2-related resources and drop the em28xx device reference taken when + * the v4l2 extension was initialized. */ -static void em28xx_free_v4l2(struct kref *ref) +static void em28xx_free_v4l2(struct v4l2_device *v4l2_dev) { - struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref); + struct em28xx_v4l2 *v4l2 = + container_of(v4l2_dev, struct em28xx_v4l2, v4l2_dev); + struct em28xx *dev = v4l2->dev; - v4l2->dev->v4l2 = NULL; + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); + v4l2_device_unregister(v4l2_dev); + dev->v4l2 = NULL; kfree(v4l2); + kref_put(&dev->ref, em28xx_free_device); } /* @@ -2354,8 +2363,6 @@ static int em28xx_v4l2_open(struct file *filp) v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); } - kref_get(&dev->ref); - kref_get(&v4l2->ref); v4l2->users++; mutex_unlock(&dev->lock); @@ -2411,14 +2418,14 @@ static int em28xx_v4l2_fini(struct em28xx *dev) video_unregister_device(&v4l2->vdev); } - v4l2_ctrl_handler_free(&v4l2->ctrl_handler); - v4l2_device_unregister(&v4l2->v4l2_dev); - - kref_put(&v4l2->ref, em28xx_free_v4l2); - mutex_unlock(&dev->lock); - kref_put(&dev->ref, em28xx_free_device); + /* + * Drop the initial reference taken at v4l2_device_register() time. + * The em28xx_free_v4l2() release callback will be invoked once all + * userspace file handles to the video device nodes are closed. + */ + v4l2_device_put(&v4l2->v4l2_dev); return 0; } @@ -2490,9 +2497,7 @@ static int em28xx_v4l2_close(struct file *filp) exit: v4l2->users--; - kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); - kref_put(&dev->ref, em28xx_free_device); return 0; } @@ -2711,7 +2716,6 @@ static int em28xx_v4l2_init(struct em28xx *dev) mutex_unlock(&dev->lock); return -ENOMEM; } - kref_init(&v4l2->ref); v4l2->dev = dev; dev->v4l2 = v4l2; @@ -2722,9 +2726,21 @@ static int em28xx_v4l2_init(struct em28xx *dev) if (ret < 0) { dev_err(&dev->intf->dev, "Call to v4l2_device_register() failed!\n"); - goto err; + dev->v4l2 = NULL; + kfree(v4l2); + mutex_unlock(&dev->lock); + return ret; } + /* + * From this point on, em28xx_free_v4l2() will be used to release + * v4l2-related resources when the v4l2_device refcount reaches + * zero. Take a reference to the em28xx device so that it cannot + * be freed before the v4l2 instance is released. + */ + v4l2->v4l2_dev.release = em28xx_free_v4l2; + kref_get(&dev->ref); + hdl = &v4l2->ctrl_handler; v4l2_ctrl_handler_init(hdl, 9); v4l2->v4l2_dev.ctrl_handler = hdl; @@ -3048,8 +3064,6 @@ static int em28xx_v4l2_init(struct em28xx *dev) dev_info(&dev->intf->dev, "V4L2 extension successfully initialized\n"); - kref_get(&dev->ref); - mutex_unlock(&dev->lock); return 0; @@ -3073,12 +3087,14 @@ unregister_dev: video_unregister_device(&v4l2->vdev); } - v4l2_ctrl_handler_free(&v4l2->ctrl_handler); - v4l2_device_unregister(&v4l2->v4l2_dev); -err: - dev->v4l2 = NULL; - kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); + + /* + * Drop the initial reference. em28xx_free_v4l2() will be called + * once the last video_device node release has decremented the + * v4l2_device refcount to zero. + */ + v4l2_device_put(&v4l2->v4l2_dev); return ret; } diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 2b9b95b53307..7d70f37665a3 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -558,7 +558,6 @@ struct em28xx_eeprom { #define EM28XX_RESOURCE_VBI 0x02 struct em28xx_v4l2 { - struct kref ref; struct em28xx *dev; struct v4l2_device v4l2_dev; -- cgit From de626073dfcbb06441f1a9755715941c0c375fe3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 19 May 2026 13:30:50 +0200 Subject: media: em28xx: drop 'users' field Drop the em28xx_v4l2 'users' field, use v4l2_fh_is_singular_file() instead. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 18 +++++++----------- drivers/media/usb/em28xx/em28xx.h | 1 - 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 42f5e7547cc4..6c726764a5f3 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2332,9 +2332,8 @@ static int em28xx_v4l2_open(struct file *filp) return -ENODEV; } - em28xx_videodbg("open dev=%s type=%s users=%d\n", - video_device_node_name(vdev), v4l2_type_names[fh_type], - v4l2->users); + em28xx_videodbg("open dev=%s type=%s\n", + video_device_node_name(vdev), v4l2_type_names[fh_type]); ret = v4l2_fh_open(filp); if (ret) { @@ -2345,7 +2344,7 @@ static int em28xx_v4l2_open(struct file *filp) return ret; } - if (v4l2->users == 0) { + if (v4l2_fh_is_singular_file(filp)) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (vdev->vfl_type != VFL_TYPE_RADIO) @@ -2363,8 +2362,6 @@ static int em28xx_v4l2_open(struct file *filp) v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); } - v4l2->users++; - mutex_unlock(&dev->lock); return 0; @@ -2467,13 +2464,13 @@ static int em28xx_v4l2_close(struct file *filp) struct em28xx_v4l2 *v4l2 = dev->v4l2; struct usb_device *udev = interface_to_usbdev(dev->intf); int err; + bool last_user; - em28xx_videodbg("users=%d\n", v4l2->users); - - vb2_fop_release(filp); mutex_lock(&dev->lock); + last_user = v4l2_fh_is_singular_file(filp); + _vb2_fop_release(filp, NULL); - if (v4l2->users == 1) { + if (last_user) { /* No sense to try to write to the device */ if (dev->disconnected) goto exit; @@ -2496,7 +2493,6 @@ static int em28xx_v4l2_close(struct file *filp) } exit: - v4l2->users--; mutex_unlock(&dev->lock); return 0; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 7d70f37665a3..5694d99716ad 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -581,7 +581,6 @@ struct em28xx_v4l2 { int sensor_yres; int sensor_xtal; - int users; /* user count for exclusive use */ int streaming_users; /* number of actively streaming users */ u32 frequency; /* selected tuner frequency */ -- cgit From 7c8bf63b79371f5b57da1bd1401bd420180ac6e6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 19 May 2026 13:33:38 +0200 Subject: media: em28xx: use vb2_video_unregister_device Use vb2_video_unregister_device instead of video_unregister_device to ensure any streaming is correctly stopped at unregister time. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 6c726764a5f3..e4554d015944 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -2402,17 +2402,17 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (video_is_registered(&v4l2->radio_dev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->radio_dev)); - video_unregister_device(&v4l2->radio_dev); + vb2_video_unregister_device(&v4l2->radio_dev); } if (video_is_registered(&v4l2->vbi_dev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->vbi_dev)); - video_unregister_device(&v4l2->vbi_dev); + vb2_video_unregister_device(&v4l2->vbi_dev); } if (video_is_registered(&v4l2->vdev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->vdev)); - video_unregister_device(&v4l2->vdev); + vb2_video_unregister_device(&v4l2->vdev); } mutex_unlock(&dev->lock); @@ -3068,19 +3068,19 @@ unregister_dev: dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->radio_dev)); - video_unregister_device(&v4l2->radio_dev); + vb2_video_unregister_device(&v4l2->radio_dev); } if (video_is_registered(&v4l2->vbi_dev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->vbi_dev)); - video_unregister_device(&v4l2->vbi_dev); + vb2_video_unregister_device(&v4l2->vbi_dev); } if (video_is_registered(&v4l2->vdev)) { dev_info(&dev->intf->dev, "V4L2 device %s deregistered\n", video_device_node_name(&v4l2->vdev)); - video_unregister_device(&v4l2->vdev); + vb2_video_unregister_device(&v4l2->vdev); } mutex_unlock(&dev->lock); -- cgit From c132b07f7e5853c0df35a9b94b8e204cdc6afdef Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 16 Jun 2026 14:00:12 +0200 Subject: media: em28xx: dev_info->pr_info since dev has been freed In em28xx_free_device() dev_info passed &dev->intf->dev, but that device can be freed already. Just use pr_info instead. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index c71cce963850..b94b00456bcd 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3782,7 +3782,7 @@ void em28xx_free_device(struct kref *ref) { struct em28xx *dev = kref_to_dev(ref); - dev_info(&dev->intf->dev, "Freeing device\n"); + pr_info("%s: Freeing device\n", dev->name); if (!dev->disconnected) em28xx_release_resources(dev); -- cgit From 67f87b104f4ef3108607ac9d8fa2075b13ed314d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 16 Jun 2026 14:42:16 +0200 Subject: media: em28xx: requeue buffers if start_streaming fails If start_streaming fails, then all queued buffers must be returned to vb2 in state QUEUED. Otherwise it will trigger a WARN_ON. Signed-off-by: Hans Verkuil Tested-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-video.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index e4554d015944..c418add65bb5 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1213,6 +1213,9 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { struct em28xx *dev = vb2_get_drv_priv(vq); struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_dmaqueue *dmaq = vq->type == V4L2_BUF_TYPE_VBI_CAPTURE ? + &dev->vbiq : &dev->vidq; + unsigned long flags = 0; struct v4l2_frequency f; struct v4l2_fh *owner; int rc = 0; @@ -1227,7 +1230,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) */ rc = res_get(dev, vq->type); if (rc) - return rc; + goto exit; if (v4l2->streaming_users == 0) { /* First active streaming user, so allocate all the URBs */ @@ -1250,7 +1253,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) em28xx_urb_data_copy); if (rc < 0) { res_free(dev, vq->type); - return rc; + goto exit; } /* @@ -1275,7 +1278,18 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) } v4l2->streaming_users++; + return 0; +exit: + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct em28xx_buffer *buf; + + buf = list_entry(dmaq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); + } + spin_unlock_irqrestore(&dev->slock, flags); return rc; } -- cgit From 3c8dd2cf6235a5d724b42776f4912e98c22fdab0 Mon Sep 17 00:00:00 2001 From: Dave Hansen Date: Tue, 26 May 2026 14:41:31 -0700 Subject: MAINTAINERS: Camera sensor and Intel IPU driver changes Tian Shu Qiu and Bingbu Cao are maintainers and reviewers of a bunch of media drivers (7 and 9 respectively). Bingbu's e-mail address has changed and Tian Shu's is bouncing. Update Bingbu's e-mail address, remove Bingbu as a maintainer from Intel specific drivers and remove Tian Shu as maintainer. Also add Dave Stevenson as a maintainer and David Heidelberg as a reviewer for the imx355 driver. Also add Bingbu and Tian Shu to CREDITS. Signed-off-by: Dave Hansen Cc: Yong Zhi Cc: Dan Scally Cc: Mauro Carvalho Chehab Cc: Greg Kroah-Hartman Cc: Lixu Zhang Cc: linux-media@vger.kernel.org Cc: linux-kernel@vger.kernel.org Cc: linux-staging@lists.linux.dev Co-developed-by: Sakari Ailus Signed-off-by: Sakari Ailus Acked-by: Dave Stevenson Acked-by: David Heidelberg Reviewed-by: Bingbu Cao --- CREDITS | 7 +++++++ MAINTAINERS | 29 ++++++++++++----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/CREDITS b/CREDITS index 84793a967a0b..7456c8bbffc2 100644 --- a/CREDITS +++ b/CREDITS @@ -640,6 +640,10 @@ S: 25-29 St Giles S: Oxford S: United Kingdom +N: Bingbu Cao +E: bingbu.cao@amd.com +D: Ipu6, ipu7 and camera sensor drivers + N: Luiz Fernando N. Capitulino E: lcapitulino@mandriva.com.br E: lcapitulino@gmail.com @@ -3334,6 +3338,9 @@ E: rpurdie@rpsys.net D: Backlight subsystem maintainer S: United Kingdom +N: Tian Shu Qiu +D: Ipu6, ipu7 and camera sensor drivers + N: Daniel Quinlan E: quinlan@pathname.com W: https://www.pathname.com/~quinlan/ diff --git a/MAINTAINERS b/MAINTAINERS index b083e44ffce4..05b783f429e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3946,7 +3946,7 @@ F: Documentation/devicetree/bindings/leds/ams,as3668.yaml F: drivers/leds/leds-as3668.c ASAHI KASEI AK7375 LENS VOICE COIL DRIVER -M: Tianshu Qiu +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -13135,9 +13135,7 @@ F: drivers/iommu/intel/ INTEL IPU3 CSI-2 CIO2 DRIVER M: Yong Zhi M: Sakari Ailus -M: Bingbu Cao M: Dan Scally -R: Tianshu Qiu L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -13146,8 +13144,6 @@ F: drivers/media/pci/intel/ipu3/ INTEL IPU3 CSI-2 IMGU DRIVER M: Sakari Ailus -R: Bingbu Cao -R: Tianshu Qiu L: linux-media@vger.kernel.org S: Maintained F: Documentation/admin-guide/media/ipu3.rst @@ -13157,8 +13153,6 @@ F: drivers/staging/media/ipu3/ INTEL IPU6 INPUT SYSTEM DRIVER M: Sakari Ailus -M: Bingbu Cao -R: Tianshu Qiu L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -13167,7 +13161,6 @@ F: drivers/media/pci/intel/ipu6/ INTEL IPU7 INPUT SYSTEM DRIVER M: Sakari Ailus -R: Bingbu Cao L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -13469,7 +13462,6 @@ F: drivers/net/wireless/intel/iwlwifi/ INTEL VISION SENSING CONTROLLER DRIVER M: Sakari Ailus -R: Bingbu Cao R: Lixu Zhang L: linux-media@vger.kernel.org S: Maintained @@ -19902,7 +19894,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,os05b10.yaml F: drivers/media/i2c/os05b10.c OMNIVISION OV01A10 SENSOR DRIVER -M: Bingbu Cao +M: Bingbu Cao +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -19995,9 +19988,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov2735.yaml F: drivers/media/i2c/ov2735.c OMNIVISION OV2740 SENSOR DRIVER -M: Tianshu Qiu -R: Sakari Ailus -R: Bingbu Cao +M: Sakari Ailus +R: Bingbu Cao L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -20139,8 +20131,8 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov9650.txt F: drivers/media/i2c/ov9650.c OMNIVISION OV9734 SENSOR DRIVER -M: Tianshu Qiu -R: Bingbu Cao +M: Sakari Ailus +R: Bingbu Cao L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -25217,7 +25209,8 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx296.yaml F: drivers/media/i2c/imx296.c SONY IMX319 SENSOR DRIVER -M: Bingbu Cao +M: Bingbu Cao +M: Sakari Ailus L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git @@ -25239,7 +25232,9 @@ F: Documentation/devicetree/bindings/media/i2c/sony,imx335.yaml F: drivers/media/i2c/imx335.c SONY IMX355 SENSOR DRIVER -M: Tianshu Qiu +M: Sakari Ailus +M: Dave Stevenson +R: David Heidelberg L: linux-media@vger.kernel.org S: Maintained T: git git://linuxtv.org/media.git -- cgit From 6c3a86e563b5c79e4429d8aa4cde39b2485bf969 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 8 Jul 2026 14:26:31 +0300 Subject: MAINTAINERS: Merge int3472 driver patches via the media tree by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The int3472 driver (under drivers/platform/x86/intel/int3472) implements an effective kernel API (via instantiating regulators, clocks and GPIOs) that is primarily used by the drivers under drivers/media. This means that there are often dependencies between patches to the int3472 driver and the camera sensor drivers whereas dependencies to other parts of the x86 platform tree are fairly rare. Assign the int3472 driver tree to the Media tree. Patches that have dependencies elsewhere than in the Media tree will be coordinated with the respective maintainers as usual. Signed-off-by: Sakari Ailus Acked-by: Ilpo Järvinen Acked-by: Daniel Scally --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 05b783f429e6..febe918d6316 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13372,6 +13372,7 @@ INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER M: Daniel Scally M: Sakari Ailus S: Maintained +T: git git://linuxtv.org/media.git F: drivers/platform/x86/intel/int3472/ F: include/linux/platform_data/x86/int3472.h -- cgit From cf4500ebf6fb57bf4ab83c3dd349a40257dbe2a9 Mon Sep 17 00:00:00 2001 From: Junrui Luo Date: Tue, 14 Jul 2026 15:24:37 +0800 Subject: media: vicodec: fix out-of-bounds write in FWHT encoder vidioc_s_fmt_vid_out() sizes the encoder CAPTURE buffer from the compressed descriptor pixfmt_fwht, whose sizeimage_mult is 3: coded_w * coded_h * 3 + sizeof(struct fwht_cframe_hdr). fwht_encode_frame() encodes one plane per component, and an incompressible plane takes the FWHT_FRAME_UNENCODED path in encode_plane(), copying the plane verbatim. For a 4-component pixel format all four planes are full resolution (width_div == height_div == 1), so a frame that forces every plane through the unencoded fallback writes sizeof(struct fwht_cframe_hdr) + 4 * coded_w * coded_h bytes, overrunning the plane by coded_w * coded_h, which can result in corruption of adjacent kernel heap memory. Bump pixfmt_fwht.sizeimage_mult from 3 to 4, matching the largest components_num among the supported raw formats, so the capture buffer is always large enough for the unencoded fallback. Fixes: 16ecf6dff97c ("media: vicodec: Add support for 4 planes formats") Reported-by: Yuhao Jiang Cc: stable@vger.kernel.org Signed-off-by: Junrui Luo Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vicodec/vicodec-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c index 318e8330f16a..ff9d50fb05fd 100644 --- a/drivers/media/test-drivers/vicodec/vicodec-core.c +++ b/drivers/media/test-drivers/vicodec/vicodec-core.c @@ -63,11 +63,11 @@ struct pixfmt_info { }; static const struct v4l2_fwht_pixfmt_info pixfmt_fwht = { - V4L2_PIX_FMT_FWHT, 0, 3, 1, 1, 1, 1, 1, 0, 1 + V4L2_PIX_FMT_FWHT, 0, 4, 1, 1, 1, 1, 1, 0, 1 }; static const struct v4l2_fwht_pixfmt_info pixfmt_stateless_fwht = { - V4L2_PIX_FMT_FWHT_STATELESS, 0, 3, 1, 1, 1, 1, 1, 0, 1 + V4L2_PIX_FMT_FWHT_STATELESS, 0, 4, 1, 1, 1, 1, 1, 0, 1 }; static void vicodec_dev_release(struct device *dev) -- cgit From 19b160af0f5fe3391f9dc9ed6ce03e30707f0270 Mon Sep 17 00:00:00 2001 From: Elgin Perumbilly Date: Mon, 16 Mar 2026 14:30:54 +0530 Subject: media: i2c: imx412: Convert to CCI register access helpers Use the new common CCI register access helpers to replace the private register access helpers in the imx412 driver. This simplifies the driver by reducing the amount of code. Signed-off-by: Elgin Perumbilly Reviewed-by: Tarang Raval Signed-off-by: Sakari Ailus --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx412.c | 635 +++++++++++++++++++-------------------------- 2 files changed, 271 insertions(+), 365 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 7ffccba897ae..e8b7422bc3d5 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -269,6 +269,7 @@ config VIDEO_IMX355 config VIDEO_IMX412 tristate "Sony IMX412 sensor support" depends on OF + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX412 camera. diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index 2705af2f16c0..fe97b4c97fd9 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -13,38 +13,39 @@ #include #include +#include #include #include #include /* Streaming Mode */ -#define IMX412_REG_MODE_SELECT 0x0100 +#define IMX412_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX412_MODE_STANDBY 0x00 #define IMX412_MODE_STREAMING 0x01 /* Lines per frame */ -#define IMX412_REG_LPFR 0x0340 +#define IMX412_REG_LPFR CCI_REG16(0x0340) /* Chip ID */ -#define IMX412_REG_ID 0x0016 +#define IMX412_REG_ID CCI_REG16(0x0016) #define IMX412_ID 0x577 /* Exposure control */ -#define IMX412_REG_EXPOSURE_CIT 0x0202 +#define IMX412_REG_EXPOSURE_CIT CCI_REG16(0x0202) #define IMX412_EXPOSURE_MIN 8 #define IMX412_EXPOSURE_OFFSET 22 #define IMX412_EXPOSURE_STEP 1 #define IMX412_EXPOSURE_DEFAULT 0x0648 /* Analog gain control */ -#define IMX412_REG_AGAIN 0x0204 +#define IMX412_REG_AGAIN CCI_REG16(0x0204) #define IMX412_AGAIN_MIN 0 #define IMX412_AGAIN_MAX 978 #define IMX412_AGAIN_STEP 1 #define IMX412_AGAIN_DEFAULT 0 /* Group hold register */ -#define IMX412_REG_HOLD 0x0104 +#define IMX412_REG_HOLD CCI_REG8(0x0104) /* Input clock rate */ #define IMX412_INCLK_RATE 24000000 @@ -56,16 +57,6 @@ #define IMX412_REG_MIN 0x00 #define IMX412_REG_MAX 0xffff -/** - * struct imx412_reg - imx412 sensor register - * @address: Register address - * @val: Register value - */ -struct imx412_reg { - u16 address; - u8 val; -}; - /** * struct imx412_reg_list - imx412 sensor register list * @num_of_regs: Number of registers in the list @@ -73,7 +64,7 @@ struct imx412_reg { */ struct imx412_reg_list { u32 num_of_regs; - const struct imx412_reg *regs; + const struct cci_reg_sequence *regs; }; /** @@ -111,6 +102,7 @@ static const char * const imx412_supply_names[] = { /** * struct imx412 - imx412 sensor device structure * @dev: Pointer to generic device + * @cci: CCI register map * @client: Pointer to i2c client * @sd: V4L2 sub-device * @pad: Media pad. Only one pad supported @@ -130,6 +122,7 @@ static const char * const imx412_supply_names[] = { */ struct imx412 { struct device *dev; + struct regmap *cci; struct i2c_client *client; struct v4l2_subdev sd; struct media_pad pad; @@ -155,238 +148,238 @@ static const s64 link_freq[] = { }; /* Sensor mode registers */ -static const struct imx412_reg mode_4056x3040_regs[] = { - {0x0136, 0x18}, - {0x0137, 0x00}, - {0x3c7e, 0x08}, - {0x3c7f, 0x02}, - {0x38a8, 0x1f}, - {0x38a9, 0xff}, - {0x38aa, 0x1f}, - {0x38ab, 0xff}, - {0x55d4, 0x00}, - {0x55d5, 0x00}, - {0x55d6, 0x07}, - {0x55d7, 0xff}, - {0x55e8, 0x07}, - {0x55e9, 0xff}, - {0x55ea, 0x00}, - {0x55eb, 0x00}, - {0x575c, 0x07}, - {0x575d, 0xff}, - {0x575e, 0x00}, - {0x575f, 0x00}, - {0x5764, 0x00}, - {0x5765, 0x00}, - {0x5766, 0x07}, - {0x5767, 0xff}, - {0x5974, 0x04}, - {0x5975, 0x01}, - {0x5f10, 0x09}, - {0x5f11, 0x92}, - {0x5f12, 0x32}, - {0x5f13, 0x72}, - {0x5f14, 0x16}, - {0x5f15, 0xba}, - {0x5f17, 0x13}, - {0x5f18, 0x24}, - {0x5f19, 0x60}, - {0x5f1a, 0xe3}, - {0x5f1b, 0xad}, - {0x5f1c, 0x74}, - {0x5f2d, 0x25}, - {0x5f5c, 0xd0}, - {0x6a22, 0x00}, - {0x6a23, 0x1d}, - {0x7ba8, 0x00}, - {0x7ba9, 0x00}, - {0x886b, 0x00}, - {0x9002, 0x0a}, - {0x9004, 0x1a}, - {0x9214, 0x93}, - {0x9215, 0x69}, - {0x9216, 0x93}, - {0x9217, 0x6b}, - {0x9218, 0x93}, - {0x9219, 0x6d}, - {0x921a, 0x57}, - {0x921b, 0x58}, - {0x921c, 0x57}, - {0x921d, 0x59}, - {0x921e, 0x57}, - {0x921f, 0x5a}, - {0x9220, 0x57}, - {0x9221, 0x5b}, - {0x9222, 0x93}, - {0x9223, 0x02}, - {0x9224, 0x93}, - {0x9225, 0x03}, - {0x9226, 0x93}, - {0x9227, 0x04}, - {0x9228, 0x93}, - {0x9229, 0x05}, - {0x922a, 0x98}, - {0x922b, 0x21}, - {0x922c, 0xb2}, - {0x922d, 0xdb}, - {0x922e, 0xb2}, - {0x922f, 0xdc}, - {0x9230, 0xb2}, - {0x9231, 0xdd}, - {0x9232, 0xe2}, - {0x9233, 0xe1}, - {0x9234, 0xb2}, - {0x9235, 0xe2}, - {0x9236, 0xb2}, - {0x9237, 0xe3}, - {0x9238, 0xb7}, - {0x9239, 0xb9}, - {0x923a, 0xb7}, - {0x923b, 0xbb}, - {0x923c, 0xb7}, - {0x923d, 0xbc}, - {0x923e, 0xb7}, - {0x923f, 0xc5}, - {0x9240, 0xb7}, - {0x9241, 0xc7}, - {0x9242, 0xb7}, - {0x9243, 0xc9}, - {0x9244, 0x98}, - {0x9245, 0x56}, - {0x9246, 0x98}, - {0x9247, 0x55}, - {0x9380, 0x00}, - {0x9381, 0x62}, - {0x9382, 0x00}, - {0x9383, 0x56}, - {0x9384, 0x00}, - {0x9385, 0x52}, - {0x9388, 0x00}, - {0x9389, 0x55}, - {0x938a, 0x00}, - {0x938b, 0x55}, - {0x938c, 0x00}, - {0x938d, 0x41}, - {0x5078, 0x01}, - {0x0112, 0x0a}, - {0x0113, 0x0a}, - {0x0114, 0x03}, - {0x0342, 0x11}, - {0x0343, 0xa0}, - {0x0340, 0x0d}, - {0x0341, 0xda}, - {0x3210, 0x00}, - {0x0344, 0x00}, - {0x0345, 0x00}, - {0x0346, 0x00}, - {0x0347, 0x00}, - {0x0348, 0x0f}, - {0x0349, 0xd7}, - {0x034a, 0x0b}, - {0x034b, 0xdf}, - {0x00e3, 0x00}, - {0x00e4, 0x00}, - {0x00e5, 0x01}, - {0x00fc, 0x0a}, - {0x00fd, 0x0a}, - {0x00fe, 0x0a}, - {0x00ff, 0x0a}, - {0xe013, 0x00}, - {0x0220, 0x00}, - {0x0221, 0x11}, - {0x0381, 0x01}, - {0x0383, 0x01}, - {0x0385, 0x01}, - {0x0387, 0x01}, - {0x0900, 0x00}, - {0x0901, 0x11}, - {0x0902, 0x00}, - {0x3140, 0x02}, - {0x3241, 0x11}, - {0x3250, 0x03}, - {0x3e10, 0x00}, - {0x3e11, 0x00}, - {0x3f0d, 0x00}, - {0x3f42, 0x00}, - {0x3f43, 0x00}, - {0x0401, 0x00}, - {0x0404, 0x00}, - {0x0405, 0x10}, - {0x0408, 0x00}, - {0x0409, 0x00}, - {0x040a, 0x00}, - {0x040b, 0x00}, - {0x040c, 0x0f}, - {0x040d, 0xd8}, - {0x040e, 0x0b}, - {0x040f, 0xe0}, - {0x034c, 0x0f}, - {0x034d, 0xd8}, - {0x034e, 0x0b}, - {0x034f, 0xe0}, - {0x0301, 0x05}, - {0x0303, 0x02}, - {0x0305, 0x04}, - {0x0306, 0x00}, - {0x0307, 0xc8}, - {0x0309, 0x0a}, - {0x030b, 0x01}, - {0x030d, 0x02}, - {0x030e, 0x01}, - {0x030f, 0x5e}, - {0x0310, 0x00}, - {0x0820, 0x12}, - {0x0821, 0xc0}, - {0x0822, 0x00}, - {0x0823, 0x00}, - {0x3e20, 0x01}, - {0x3e37, 0x00}, - {0x3f50, 0x00}, - {0x3f56, 0x00}, - {0x3f57, 0xe2}, - {0x3c0a, 0x5a}, - {0x3c0b, 0x55}, - {0x3c0c, 0x28}, - {0x3c0d, 0x07}, - {0x3c0e, 0xff}, - {0x3c0f, 0x00}, - {0x3c10, 0x00}, - {0x3c11, 0x02}, - {0x3c12, 0x00}, - {0x3c13, 0x03}, - {0x3c14, 0x00}, - {0x3c15, 0x00}, - {0x3c16, 0x0c}, - {0x3c17, 0x0c}, - {0x3c18, 0x0c}, - {0x3c19, 0x0a}, - {0x3c1a, 0x0a}, - {0x3c1b, 0x0a}, - {0x3c1c, 0x00}, - {0x3c1d, 0x00}, - {0x3c1e, 0x00}, - {0x3c1f, 0x00}, - {0x3c20, 0x00}, - {0x3c21, 0x00}, - {0x3c22, 0x3f}, - {0x3c23, 0x0a}, - {0x3e35, 0x01}, - {0x3f4a, 0x03}, - {0x3f4b, 0xbf}, - {0x3f26, 0x00}, - {0x0202, 0x0d}, - {0x0203, 0xc4}, - {0x0204, 0x00}, - {0x0205, 0x00}, - {0x020e, 0x01}, - {0x020f, 0x00}, - {0x0210, 0x01}, - {0x0211, 0x00}, - {0x0212, 0x01}, - {0x0213, 0x00}, - {0x0214, 0x01}, - {0x0215, 0x00}, - {0xbcf1, 0x00}, +static const struct cci_reg_sequence mode_4056x3040_regs[] = { + { CCI_REG8(0x0136), 0x18 }, + { CCI_REG8(0x0137), 0x00 }, + { CCI_REG8(0x3c7e), 0x08 }, + { CCI_REG8(0x3c7f), 0x02 }, + { CCI_REG8(0x38a8), 0x1f }, + { CCI_REG8(0x38a9), 0xff }, + { CCI_REG8(0x38aa), 0x1f }, + { CCI_REG8(0x38ab), 0xff }, + { CCI_REG8(0x55d4), 0x00 }, + { CCI_REG8(0x55d5), 0x00 }, + { CCI_REG8(0x55d6), 0x07 }, + { CCI_REG8(0x55d7), 0xff }, + { CCI_REG8(0x55e8), 0x07 }, + { CCI_REG8(0x55e9), 0xff }, + { CCI_REG8(0x55ea), 0x00 }, + { CCI_REG8(0x55eb), 0x00 }, + { CCI_REG8(0x575c), 0x07 }, + { CCI_REG8(0x575d), 0xff }, + { CCI_REG8(0x575e), 0x00 }, + { CCI_REG8(0x575f), 0x00 }, + { CCI_REG8(0x5764), 0x00 }, + { CCI_REG8(0x5765), 0x00 }, + { CCI_REG8(0x5766), 0x07 }, + { CCI_REG8(0x5767), 0xff }, + { CCI_REG8(0x5974), 0x04 }, + { CCI_REG8(0x5975), 0x01 }, + { CCI_REG8(0x5f10), 0x09 }, + { CCI_REG8(0x5f11), 0x92 }, + { CCI_REG8(0x5f12), 0x32 }, + { CCI_REG8(0x5f13), 0x72 }, + { CCI_REG8(0x5f14), 0x16 }, + { CCI_REG8(0x5f15), 0xba }, + { CCI_REG8(0x5f17), 0x13 }, + { CCI_REG8(0x5f18), 0x24 }, + { CCI_REG8(0x5f19), 0x60 }, + { CCI_REG8(0x5f1a), 0xe3 }, + { CCI_REG8(0x5f1b), 0xad }, + { CCI_REG8(0x5f1c), 0x74 }, + { CCI_REG8(0x5f2d), 0x25 }, + { CCI_REG8(0x5f5c), 0xd0 }, + { CCI_REG8(0x6a22), 0x00 }, + { CCI_REG8(0x6a23), 0x1d }, + { CCI_REG8(0x7ba8), 0x00 }, + { CCI_REG8(0x7ba9), 0x00 }, + { CCI_REG8(0x886b), 0x00 }, + { CCI_REG8(0x9002), 0x0a }, + { CCI_REG8(0x9004), 0x1a }, + { CCI_REG8(0x9214), 0x93 }, + { CCI_REG8(0x9215), 0x69 }, + { CCI_REG8(0x9216), 0x93 }, + { CCI_REG8(0x9217), 0x6b }, + { CCI_REG8(0x9218), 0x93 }, + { CCI_REG8(0x9219), 0x6d }, + { CCI_REG8(0x921a), 0x57 }, + { CCI_REG8(0x921b), 0x58 }, + { CCI_REG8(0x921c), 0x57 }, + { CCI_REG8(0x921d), 0x59 }, + { CCI_REG8(0x921e), 0x57 }, + { CCI_REG8(0x921f), 0x5a }, + { CCI_REG8(0x9220), 0x57 }, + { CCI_REG8(0x9221), 0x5b }, + { CCI_REG8(0x9222), 0x93 }, + { CCI_REG8(0x9223), 0x02 }, + { CCI_REG8(0x9224), 0x93 }, + { CCI_REG8(0x9225), 0x03 }, + { CCI_REG8(0x9226), 0x93 }, + { CCI_REG8(0x9227), 0x04 }, + { CCI_REG8(0x9228), 0x93 }, + { CCI_REG8(0x9229), 0x05 }, + { CCI_REG8(0x922a), 0x98 }, + { CCI_REG8(0x922b), 0x21 }, + { CCI_REG8(0x922c), 0xb2 }, + { CCI_REG8(0x922d), 0xdb }, + { CCI_REG8(0x922e), 0xb2 }, + { CCI_REG8(0x922f), 0xdc }, + { CCI_REG8(0x9230), 0xb2 }, + { CCI_REG8(0x9231), 0xdd }, + { CCI_REG8(0x9232), 0xe2 }, + { CCI_REG8(0x9233), 0xe1 }, + { CCI_REG8(0x9234), 0xb2 }, + { CCI_REG8(0x9235), 0xe2 }, + { CCI_REG8(0x9236), 0xb2 }, + { CCI_REG8(0x9237), 0xe3 }, + { CCI_REG8(0x9238), 0xb7 }, + { CCI_REG8(0x9239), 0xb9 }, + { CCI_REG8(0x923a), 0xb7 }, + { CCI_REG8(0x923b), 0xbb }, + { CCI_REG8(0x923c), 0xb7 }, + { CCI_REG8(0x923d), 0xbc }, + { CCI_REG8(0x923e), 0xb7 }, + { CCI_REG8(0x923f), 0xc5 }, + { CCI_REG8(0x9240), 0xb7 }, + { CCI_REG8(0x9241), 0xc7 }, + { CCI_REG8(0x9242), 0xb7 }, + { CCI_REG8(0x9243), 0xc9 }, + { CCI_REG8(0x9244), 0x98 }, + { CCI_REG8(0x9245), 0x56 }, + { CCI_REG8(0x9246), 0x98 }, + { CCI_REG8(0x9247), 0x55 }, + { CCI_REG8(0x9380), 0x00 }, + { CCI_REG8(0x9381), 0x62 }, + { CCI_REG8(0x9382), 0x00 }, + { CCI_REG8(0x9383), 0x56 }, + { CCI_REG8(0x9384), 0x00 }, + { CCI_REG8(0x9385), 0x52 }, + { CCI_REG8(0x9388), 0x00 }, + { CCI_REG8(0x9389), 0x55 }, + { CCI_REG8(0x938a), 0x00 }, + { CCI_REG8(0x938b), 0x55 }, + { CCI_REG8(0x938c), 0x00 }, + { CCI_REG8(0x938d), 0x41 }, + { CCI_REG8(0x5078), 0x01 }, + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0342), 0x11 }, + { CCI_REG8(0x0343), 0xa0 }, + { CCI_REG8(0x0340), 0x0d }, + { CCI_REG8(0x0341), 0xda }, + { CCI_REG8(0x3210), 0x00 }, + { CCI_REG8(0x0344), 0x00 }, + { CCI_REG8(0x0345), 0x00 }, + { CCI_REG8(0x0346), 0x00 }, + { CCI_REG8(0x0347), 0x00 }, + { CCI_REG8(0x0348), 0x0f }, + { CCI_REG8(0x0349), 0xd7 }, + { CCI_REG8(0x034a), 0x0b }, + { CCI_REG8(0x034b), 0xdf }, + { CCI_REG8(0x00e3), 0x00 }, + { CCI_REG8(0x00e4), 0x00 }, + { CCI_REG8(0x00e5), 0x01 }, + { CCI_REG8(0x00fc), 0x0a }, + { CCI_REG8(0x00fd), 0x0a }, + { CCI_REG8(0x00fe), 0x0a }, + { CCI_REG8(0x00ff), 0x0a }, + { CCI_REG8(0xe013), 0x00 }, + { CCI_REG8(0x0220), 0x00 }, + { CCI_REG8(0x0221), 0x11 }, + { CCI_REG8(0x0381), 0x01 }, + { CCI_REG8(0x0383), 0x01 }, + { CCI_REG8(0x0385), 0x01 }, + { CCI_REG8(0x0387), 0x01 }, + { CCI_REG8(0x0900), 0x00 }, + { CCI_REG8(0x0901), 0x11 }, + { CCI_REG8(0x0902), 0x00 }, + { CCI_REG8(0x3140), 0x02 }, + { CCI_REG8(0x3241), 0x11 }, + { CCI_REG8(0x3250), 0x03 }, + { CCI_REG8(0x3e10), 0x00 }, + { CCI_REG8(0x3e11), 0x00 }, + { CCI_REG8(0x3f0d), 0x00 }, + { CCI_REG8(0x3f42), 0x00 }, + { CCI_REG8(0x3f43), 0x00 }, + { CCI_REG8(0x0401), 0x00 }, + { CCI_REG8(0x0404), 0x00 }, + { CCI_REG8(0x0405), 0x10 }, + { CCI_REG8(0x0408), 0x00 }, + { CCI_REG8(0x0409), 0x00 }, + { CCI_REG8(0x040a), 0x00 }, + { CCI_REG8(0x040b), 0x00 }, + { CCI_REG8(0x040c), 0x0f }, + { CCI_REG8(0x040d), 0xd8 }, + { CCI_REG8(0x040e), 0x0b }, + { CCI_REG8(0x040f), 0xe0 }, + { CCI_REG8(0x034c), 0x0f }, + { CCI_REG8(0x034d), 0xd8 }, + { CCI_REG8(0x034e), 0x0b }, + { CCI_REG8(0x034f), 0xe0 }, + { CCI_REG8(0x0301), 0x05 }, + { CCI_REG8(0x0303), 0x02 }, + { CCI_REG8(0x0305), 0x04 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x0307), 0xc8 }, + { CCI_REG8(0x0309), 0x0a }, + { CCI_REG8(0x030b), 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x030e), 0x01 }, + { CCI_REG8(0x030f), 0x5e }, + { CCI_REG8(0x0310), 0x00 }, + { CCI_REG8(0x0820), 0x12 }, + { CCI_REG8(0x0821), 0xc0 }, + { CCI_REG8(0x0822), 0x00 }, + { CCI_REG8(0x0823), 0x00 }, + { CCI_REG8(0x3e20), 0x01 }, + { CCI_REG8(0x3e37), 0x00 }, + { CCI_REG8(0x3f50), 0x00 }, + { CCI_REG8(0x3f56), 0x00 }, + { CCI_REG8(0x3f57), 0xe2 }, + { CCI_REG8(0x3c0a), 0x5a }, + { CCI_REG8(0x3c0b), 0x55 }, + { CCI_REG8(0x3c0c), 0x28 }, + { CCI_REG8(0x3c0d), 0x07 }, + { CCI_REG8(0x3c0e), 0xff }, + { CCI_REG8(0x3c0f), 0x00 }, + { CCI_REG8(0x3c10), 0x00 }, + { CCI_REG8(0x3c11), 0x02 }, + { CCI_REG8(0x3c12), 0x00 }, + { CCI_REG8(0x3c13), 0x03 }, + { CCI_REG8(0x3c14), 0x00 }, + { CCI_REG8(0x3c15), 0x00 }, + { CCI_REG8(0x3c16), 0x0c }, + { CCI_REG8(0x3c17), 0x0c }, + { CCI_REG8(0x3c18), 0x0c }, + { CCI_REG8(0x3c19), 0x0a }, + { CCI_REG8(0x3c1a), 0x0a }, + { CCI_REG8(0x3c1b), 0x0a }, + { CCI_REG8(0x3c1c), 0x00 }, + { CCI_REG8(0x3c1d), 0x00 }, + { CCI_REG8(0x3c1e), 0x00 }, + { CCI_REG8(0x3c1f), 0x00 }, + { CCI_REG8(0x3c20), 0x00 }, + { CCI_REG8(0x3c21), 0x00 }, + { CCI_REG8(0x3c22), 0x3f }, + { CCI_REG8(0x3c23), 0x0a }, + { CCI_REG8(0x3e35), 0x01 }, + { CCI_REG8(0x3f4a), 0x03 }, + { CCI_REG8(0x3f4b), 0xbf }, + { CCI_REG8(0x3f26), 0x00 }, + { CCI_REG8(0x0202), 0x0d }, + { CCI_REG8(0x0203), 0xc4 }, + { CCI_REG8(0x0204), 0x00 }, + { CCI_REG8(0x0205), 0x00 }, + { CCI_REG8(0x020e), 0x01 }, + { CCI_REG8(0x020f), 0x00 }, + { CCI_REG8(0x0210), 0x01 }, + { CCI_REG8(0x0211), 0x00 }, + { CCI_REG8(0x0212), 0x01 }, + { CCI_REG8(0x0213), 0x00 }, + { CCI_REG8(0x0214), 0x01 }, + { CCI_REG8(0x0215), 0x00 }, + { CCI_REG8(0xbcf1), 0x00 }, }; /* Supported sensor mode configurations */ @@ -417,97 +410,6 @@ static inline struct imx412 *to_imx412(struct v4l2_subdev *subdev) return container_of(subdev, struct imx412, sd); } -/** - * imx412_read_reg() - Read registers. - * @imx412: pointer to imx412 device - * @reg: register address - * @len: length of bytes to read. Max supported bytes is 4 - * @val: pointer to register value to be filled. - * - * Return: 0 if successful, error code otherwise. - */ -static int imx412_read_reg(struct imx412 *imx412, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd); - struct i2c_msg msgs[2] = {0}; - u8 addr_buf[2] = {0}; - u8 data_buf[4] = {0}; - int ret; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/** - * imx412_write_reg() - Write register - * @imx412: pointer to imx412 device - * @reg: register address - * @len: length of bytes. Max supported bytes is 4 - * @val: register value - * - * Return: 0 if successful, error code otherwise. - */ -static int imx412_write_reg(struct imx412 *imx412, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx412->sd); - u8 buf[6] = {0}; - - if (WARN_ON(len > 4)) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/** - * imx412_write_regs() - Write a list of registers - * @imx412: pointer to imx412 device - * @regs: list of registers to be written - * @len: length of registers array - * - * Return: 0 if successful, error code otherwise. - */ -static int imx412_write_regs(struct imx412 *imx412, - const struct imx412_reg *regs, u32 len) -{ - unsigned int i; - int ret; - - for (i = 0; i < len; i++) { - ret = imx412_write_reg(imx412, regs[i].address, 1, regs[i].val); - if (ret) - return ret; - } - - return 0; -} - /** * imx412_update_controls() - Update control ranges based on streaming mode * @imx412: pointer to imx412 device @@ -543,29 +445,25 @@ static int imx412_update_controls(struct imx412 *imx412, static int imx412_update_exp_gain(struct imx412 *imx412, u32 exposure, u32 gain) { u32 lpfr; - int ret; + int ret = 0; + int ret_hold; lpfr = imx412->vblank + imx412->cur_mode->height; dev_dbg(imx412->dev, "Set exp %u, analog gain %u, lpfr %u\n", exposure, gain, lpfr); - ret = imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 1); - if (ret) - return ret; + cci_write(imx412->cci, IMX412_REG_HOLD, 1, &ret); - ret = imx412_write_reg(imx412, IMX412_REG_LPFR, 2, lpfr); - if (ret) - goto error_release_group_hold; + cci_write(imx412->cci, IMX412_REG_LPFR, lpfr, &ret); - ret = imx412_write_reg(imx412, IMX412_REG_EXPOSURE_CIT, 2, exposure); - if (ret) - goto error_release_group_hold; + cci_write(imx412->cci, IMX412_REG_EXPOSURE_CIT, exposure, &ret); - ret = imx412_write_reg(imx412, IMX412_REG_AGAIN, 2, gain); + cci_write(imx412->cci, IMX412_REG_AGAIN, gain, &ret); -error_release_group_hold: - imx412_write_reg(imx412, IMX412_REG_HOLD, 1, 0); + ret_hold = cci_write(imx412->cci, IMX412_REG_HOLD, 0, NULL); + if (ret_hold) + return ret_hold; return ret; } @@ -749,8 +647,8 @@ static int imx412_start_streaming(struct imx412 *imx412) /* Write sensor mode registers */ reg_list = &imx412->cur_mode->reg_list; - ret = imx412_write_regs(imx412, reg_list->regs, - reg_list->num_of_regs); + ret = cci_multi_reg_write(imx412->cci, reg_list->regs, + reg_list->num_of_regs, NULL); if (ret) { dev_err(imx412->dev, "fail to write initial registers\n"); return ret; @@ -767,8 +665,8 @@ static int imx412_start_streaming(struct imx412 *imx412) usleep_range(7400, 8000); /* Start streaming */ - ret = imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, - 1, IMX412_MODE_STREAMING); + ret = cci_write(imx412->cci, IMX412_REG_MODE_SELECT, + IMX412_MODE_STREAMING, NULL); if (ret) { dev_err(imx412->dev, "fail to start streaming\n"); return ret; @@ -785,8 +683,8 @@ static int imx412_start_streaming(struct imx412 *imx412) */ static int imx412_stop_streaming(struct imx412 *imx412) { - return imx412_write_reg(imx412, IMX412_REG_MODE_SELECT, - 1, IMX412_MODE_STANDBY); + return cci_write(imx412->cci, IMX412_REG_MODE_SELECT, + IMX412_MODE_STANDBY, NULL); } static int imx412_set_stream(struct v4l2_subdev *sd, int enable) @@ -830,16 +728,18 @@ error_unlock: static int imx412_detect(struct imx412 *imx412) { int ret; - u32 val; + u64 val; - ret = imx412_read_reg(imx412, IMX412_REG_ID, 2, &val); + ret = cci_read(imx412->cci, IMX412_REG_ID, &val, NULL); if (ret) - return ret; + return dev_err_probe(imx412->dev, ret, + "failed to read chip id %x\n", + IMX412_ID); if (val != IMX412_ID) { - dev_err(imx412->dev, "chip id mismatch: %x!=%x\n", - IMX412_ID, val); - return -ENXIO; + return dev_err_probe(imx412->dev, -ENODEV, + "chip id mismatch: %x!=%llx", + IMX412_ID, val); } return 0; @@ -1104,6 +1004,11 @@ static int imx412_probe(struct i2c_client *client) if (!name) return -ENODEV; + imx412->cci = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx412->cci)) + return dev_err_probe(imx412->dev, PTR_ERR(imx412->cci), + "Failed to init CCI\n"); + /* Initialize subdev */ v4l2_i2c_subdev_init(&imx412->sd, client, &imx412_subdev_ops); imx412->sd.internal_ops = &imx412_internal_ops; -- cgit From 42e682e6925070e0dd1d9afb1735d9dd61c3686d Mon Sep 17 00:00:00 2001 From: Elgin Perumbilly Date: Mon, 16 Mar 2026 14:30:55 +0530 Subject: media: i2c: imx412: Switch to using the sub-device state lock Switch to using the sub-device state lock and properly call v4l2_subdev_init_finalize() / v4l2_subdev_cleanup() on probe() / remove(). Signed-off-by: Elgin Perumbilly Reviewed-by: Tarang Raval Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx412.c | 53 ++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index fe97b4c97fd9..f75a22703aae 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -118,7 +118,6 @@ static const char * const imx412_supply_names[] = { * @again_ctrl: Pointer to analog gain control * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode - * @mutex: Mutex for serializing sensor controls */ struct imx412 { struct device *dev; @@ -140,7 +139,6 @@ struct imx412 { }; u32 vblank; const struct imx412_mode *cur_mode; - struct mutex mutex; }; static const s64 link_freq[] = { @@ -577,8 +575,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, { struct imx412 *imx412 = to_imx412(sd); - mutex_lock(&imx412->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; @@ -588,8 +584,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd, imx412_fill_pad_format(imx412, imx412->cur_mode, fmt); } - mutex_unlock(&imx412->mutex); - return 0; } @@ -601,8 +595,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, const struct imx412_mode *mode; int ret = 0; - mutex_lock(&imx412->mutex); - mode = &supported_mode; imx412_fill_pad_format(imx412, mode, fmt); @@ -617,8 +609,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd, imx412->cur_mode = mode; } - mutex_unlock(&imx412->mutex); - return ret; } @@ -690,9 +680,10 @@ static int imx412_stop_streaming(struct imx412 *imx412) static int imx412_set_stream(struct v4l2_subdev *sd, int enable) { struct imx412 *imx412 = to_imx412(sd); + struct v4l2_subdev_state *state; int ret; - mutex_lock(&imx412->mutex); + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { ret = pm_runtime_resume_and_get(imx412->dev); @@ -707,14 +698,14 @@ static int imx412_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put(imx412->dev); } - mutex_unlock(&imx412->mutex); + v4l2_subdev_unlock_state(state); return 0; error_power_off: pm_runtime_put(imx412->dev); error_unlock: - mutex_unlock(&imx412->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -921,9 +912,6 @@ static int imx412_init_controls(struct imx412 *imx412) if (ret) return ret; - /* Serialize controls with sensor device */ - ctrl_hdlr->lock = &imx412->mutex; - /* Initialize exposure and gain */ lpfr = mode->vblank + mode->height; imx412->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, @@ -1019,13 +1007,10 @@ static int imx412_probe(struct i2c_client *client) return ret; } - mutex_init(&imx412->mutex); - ret = imx412_power_on(imx412->dev); - if (ret) { - dev_err(imx412->dev, "failed to power-on the sensor\n"); - goto error_mutex_destroy; - } + if (ret) + return dev_err_probe(imx412->dev, ret, + "failed to power-on the sensor\n"); /* Check module identity */ ret = imx412_detect(imx412); @@ -1058,27 +1043,37 @@ static int imx412_probe(struct i2c_client *client) goto error_handler_free; } - ret = v4l2_async_register_subdev_sensor(&imx412->sd); + imx412->sd.state_lock = imx412->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx412->sd); if (ret < 0) { - dev_err(imx412->dev, - "failed to register async subdev: %d\n", ret); + dev_err_probe(imx412->dev, ret, "subdev init error\n"); goto error_media_entity; } pm_runtime_set_active(imx412->dev); pm_runtime_enable(imx412->dev); + + ret = v4l2_async_register_subdev_sensor(&imx412->sd); + if (ret < 0) { + dev_err_probe(imx412->dev, ret, + "failed to register sub-device\n"); + goto error_subdev_cleanup; + } + pm_runtime_idle(imx412->dev); return 0; +error_subdev_cleanup: + v4l2_subdev_cleanup(&imx412->sd); + pm_runtime_disable(imx412->dev); + pm_runtime_set_suspended(imx412->dev); error_media_entity: media_entity_cleanup(&imx412->sd.entity); error_handler_free: v4l2_ctrl_handler_free(imx412->sd.ctrl_handler); error_power_off: imx412_power_off(imx412->dev); -error_mutex_destroy: - mutex_destroy(&imx412->mutex); return ret; } @@ -1086,9 +1081,9 @@ error_mutex_destroy: static void imx412_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct imx412 *imx412 = to_imx412(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1096,8 +1091,6 @@ static void imx412_remove(struct i2c_client *client) if (!pm_runtime_status_suspended(&client->dev)) imx412_power_off(&client->dev); pm_runtime_set_suspended(&client->dev); - - mutex_destroy(&imx412->mutex); } static const struct dev_pm_ops imx412_pm_ops = { -- cgit From b845e0f35383606b4713c231a486a4113b455fa2 Mon Sep 17 00:00:00 2001 From: Elgin Perumbilly Date: Mon, 16 Mar 2026 14:30:56 +0530 Subject: media: i2c: imx412: switch to {enable,disable}_streams Switch from s_stream to enable_streams and disable_streams callbacks. Signed-off-by: Elgin Perumbilly Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx412.c | 75 +++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c index f75a22703aae..d2760e9e6194 100644 --- a/drivers/media/i2c/imx412.c +++ b/drivers/media/i2c/imx412.c @@ -625,30 +625,40 @@ static int imx412_init_state(struct v4l2_subdev *sd, } /** - * imx412_start_streaming() - Start sensor stream - * @imx412: pointer to imx412 device + * imx412_enable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are enabled + * @streams_mask: bitmask specifying the streams to enable * * Return: 0 if successful, error code otherwise. */ -static int imx412_start_streaming(struct imx412 *imx412) +static int imx412_enable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { const struct imx412_reg_list *reg_list; + struct imx412 *imx412 = to_imx412(sd); int ret; + ret = pm_runtime_resume_and_get(imx412->dev); + if (ret < 0) + return ret; + /* Write sensor mode registers */ reg_list = &imx412->cur_mode->reg_list; ret = cci_multi_reg_write(imx412->cci, reg_list->regs, reg_list->num_of_regs, NULL); if (ret) { dev_err(imx412->dev, "fail to write initial registers\n"); - return ret; + goto err_rpm_put; } /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx412->sd.ctrl_handler); if (ret) { dev_err(imx412->dev, "fail to setup handler\n"); - return ret; + goto err_rpm_put; } /* Delay is required before streaming*/ @@ -659,55 +669,42 @@ static int imx412_start_streaming(struct imx412 *imx412) IMX412_MODE_STREAMING, NULL); if (ret) { dev_err(imx412->dev, "fail to start streaming\n"); - return ret; + goto err_rpm_put; } return 0; + +err_rpm_put: + pm_runtime_put(imx412->dev); + + return ret; } /** - * imx412_stop_streaming() - Stop sensor stream - * @imx412: pointer to imx412 device + * imx412_disable_streams() - Enable specified streams for the sensor + * @sd: pointer to the V4L2 subdevice + * @state: pointer to the subdevice state + * @pad: pad number for which streams are disabled + * @streams_mask: bitmask specifying the streams to disable * * Return: 0 if successful, error code otherwise. */ -static int imx412_stop_streaming(struct imx412 *imx412) -{ - return cci_write(imx412->cci, IMX412_REG_MODE_SELECT, - IMX412_MODE_STANDBY, NULL); -} - -static int imx412_set_stream(struct v4l2_subdev *sd, int enable) +static int imx412_disable_streams(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + u32 pad, u64 streams_mask) { struct imx412 *imx412 = to_imx412(sd); - struct v4l2_subdev_state *state; int ret; - state = v4l2_subdev_lock_and_get_active_state(sd); - - if (enable) { - ret = pm_runtime_resume_and_get(imx412->dev); - if (ret) - goto error_unlock; - - ret = imx412_start_streaming(imx412); - if (ret) - goto error_power_off; - } else { - imx412_stop_streaming(imx412); - pm_runtime_put(imx412->dev); - } - - v4l2_subdev_unlock_state(state); + ret = cci_write(imx412->cci, IMX412_REG_MODE_SELECT, + IMX412_MODE_STANDBY, NULL); - return 0; + if (ret) + dev_err(imx412->dev, "failed to set stream off\n"); -error_power_off: pm_runtime_put(imx412->dev); -error_unlock: - v4l2_subdev_unlock_state(state); - return ret; + return 0; } /** @@ -824,7 +821,7 @@ done_endpoint_free: /* V4l2 subdevice ops */ static const struct v4l2_subdev_video_ops imx412_video_ops = { - .s_stream = imx412_set_stream, + .s_stream = v4l2_subdev_s_stream_helper, }; static const struct v4l2_subdev_pad_ops imx412_pad_ops = { @@ -832,6 +829,8 @@ static const struct v4l2_subdev_pad_ops imx412_pad_ops = { .enum_frame_size = imx412_enum_frame_size, .get_fmt = imx412_get_pad_format, .set_fmt = imx412_set_pad_format, + .enable_streams = imx412_enable_streams, + .disable_streams = imx412_disable_streams, }; static const struct v4l2_subdev_ops imx412_subdev_ops = { -- cgit From 46f563185b23443894c4d6aa86942851bda6f5dc Mon Sep 17 00:00:00 2001 From: Yemike Abhilash Chandra Date: Sun, 12 Jul 2026 21:42:56 +0530 Subject: media: ti: vpe: Select V4L2_FWNODE for VIP The VIP driver uses v4l2_fwnode_endpoint_parse() and the v4l2_async_nf_*() notifier APIs, but its Kconfig entry does not select V4L2_FWNODE. Hence kernel test robot reports: vip.c:3236: undefined reference to `v4l2_async_nf_unregister' vip.c:3237: undefined reference to `v4l2_async_nf_cleanup' vip.c:3339: undefined reference to `v4l2_fwnode_endpoint_parse' vip.c:3346: undefined reference to `v4l2_async_nf_init' vip.c:3348: undefined reference to `__v4l2_async_nf_add_fwnode' vip.c:3357: undefined reference to `v4l2_async_nf_register' Select V4L2_FWNODE, which in turn selects V4L2_ASYNC, providing all the missing symbols. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607031826.vOPABT39-lkp@intel.com/ Signed-off-by: Yemike Abhilash Chandra Signed-off-by: Sakari Ailus --- drivers/media/platform/ti/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig index d0cb05481bd8..1a020b2bbb4f 100644 --- a/drivers/media/platform/ti/Kconfig +++ b/drivers/media/platform/ti/Kconfig @@ -50,6 +50,7 @@ config VIDEO_TI_VIP select VIDEO_TI_VPDMA select VIDEO_TI_SC select VIDEO_TI_CSC + select V4L2_FWNODE help Driver support for VIP module on certain TI SoC's VIP = Video Input Port. -- cgit From f4d4912e7f061e42c328e3c2db6e4e70a8fb19cd Mon Sep 17 00:00:00 2001 From: Samuele Angioli Date: Mon, 6 Jul 2026 10:00:02 +0200 Subject: media: ipu-bridge: Add DMI quirk for Dell 16 Premium DA16250 The Dell 16 Premium DA16250 (marketed as XPS 16) has its OV02C10 front sensor mounted upside down, like the other Dell XPS models already present in upside_down_sensor_dmi_ids[]. The rotation is reported as 0 in both the SSDB and the _PLD, so without a DMI quirk the image is presented upside down. Add a DMI quirk entry for this model so that a rotation of 180 degrees is reported for its OVTI02C1 sensor. Signed-off-by: Samuele Angioli Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 50818a10168e..9e37f24c8a7d 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -136,6 +136,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 16 (Dell 16 Premium) DA16250"), + }, + .driver_data = "OVTI02C1", + }, /* * The first four characters of DMI_BOARD_NAME identify the Lenovo * machine type/model. For example, a DMI_BOARD_NAME starting with -- cgit From 04c053379c3a33460b581953c4f5b36de39439ac Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Sun, 5 Jul 2026 01:46:28 +0530 Subject: media: i2c: rdacm21: Fix missing media_entity_cleanup() The driver misses calling media_entity_cleanup() on the probe error path and during remove, leaking resources if probe fails after entity initialization or when the driver is unloaded. Fix this by adding media_entity_cleanup() to the rdacm21_probe() error handling path and to rdacm21_remove(). Fixes: a59f853b3b4b ("media: i2c: Add driver for RDACM21 camera module") Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/rdacm21.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c index bcab462708c7..ece8a410e7ce 100644 --- a/drivers/media/i2c/rdacm21.c +++ b/drivers/media/i2c/rdacm21.c @@ -588,10 +588,12 @@ static int rdacm21_probe(struct i2c_client *client) ret = v4l2_async_register_subdev(&dev->sd); if (ret) - goto error_free_ctrls; + goto error_entity_cleanup; return 0; +error_entity_cleanup: + media_entity_cleanup(&dev->sd.entity); error_free_ctrls: v4l2_ctrl_handler_free(&dev->ctrls); error: @@ -606,6 +608,7 @@ static void rdacm21_remove(struct i2c_client *client) v4l2_async_unregister_subdev(&dev->sd); v4l2_ctrl_handler_free(&dev->ctrls); + media_entity_cleanup(&dev->sd.entity); i2c_unregister_device(dev->isp); } -- cgit From a6e86efd7f85e519bf48417f41923f8bd51f1597 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Fri, 19 Jun 2026 13:41:30 +0530 Subject: media: v4l2-fwnode: Fix fwnode leak in v4l2_fwnode_parse_link In v4l2_fwnode_parse_link(), the remote endpoint fwnode reference is acquired using fwnode_graph_get_remote_endpoint(). This reference is properly released in the error paths, but it is leaked on the success path. Add the missing fwnode_handle_put() before returning 0 to prevent the reference leak. Signed-off-by: Biren Pandya Fixes: ca50c197bd96 ("[media] v4l: fwnode: Support generic fwnode for parsing standardised properties") Cc: stable@vger.kernel.org [Sakari Ailus: Fix subject prefix and coding style a little.] Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-fwnode.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 62a3a452f788..570df3c415b0 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -633,6 +633,8 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, if (!link->remote_node) goto err_put_remote_endpoint; + fwnode_handle_put(fwnode); + return 0; err_put_remote_endpoint: -- cgit From 95a99456ff354a1bad4b4032ed1d044d608bd438 Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 12 Jul 2026 00:59:39 +0100 Subject: media: platform: amd: fix unmet dependency for VIDEO_V4L2_SUBDEV_API Currently, VIDEO_AMD_ISP4_CAPTURE selects VIDEO_V4L2_SUBDEV_API without ensuring MEDIA_CONTROLLER is enabled, causing an unmet dependency: WARNING: unmet direct dependencies detected for VIDEO_V4L2_SUBDEV_API Depends on [n]: MEDIA_SUPPORT [=m] && VIDEO_DEV [=m] && MEDIA_CONTROLLER [=n] Selected by [m]: - VIDEO_AMD_ISP4_CAPTURE [=m] && MEDIA_SUPPORT [=m] && MEDIA_PLATFORM_SUPPORT [=y] && MEDIA_PLATFORM_DRIVERS [=y] && DRM_AMDGPU [=m] && DRM_AMD_ISP [=y] && HAS_DMA [=y] && VIDEO_DEV [=m] Many other options in this subsystem select MEDIA_CONTROLLER, let's do the same here. This unmet dependency bug was detected by kconfirm, a static analysis tool for Kconfig. Fixes: 9a54c285630c ("media: platform: amd: Introduce amd isp4 capture driver") Signed-off-by: Julian Braha Reviewed-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig index 9d1927af1cb8..77b61fae82ba 100644 --- a/drivers/media/platform/amd/isp4/Kconfig +++ b/drivers/media/platform/amd/isp4/Kconfig @@ -5,6 +5,7 @@ config VIDEO_AMD_ISP4_CAPTURE depends on DRM_AMDGPU && DRM_AMD_ISP depends on HAS_DMA depends on VIDEO_DEV + select MEDIA_CONTROLLER select VIDEOBUF2_CORE select VIDEOBUF2_MEMOPS select VIDEOBUF2_V4L2 -- cgit From 15b8b49933507c9f437af51c2da626dc5840ef26 Mon Sep 17 00:00:00 2001 From: Sanjay Chitroda Date: Fri, 10 Jul 2026 10:55:23 +0530 Subject: media: i2c: gc0310: Use devm_v4l2_sensor_clk_get() Several camera sensor drivers access the "clock-frequency" property directly to retrieve the external clock rate or handle the external clock manually in the driver. While this is valid on a subset of ACPI platforms, implementing this logic directly in drivers is deprecated and can lead to inconsistent behaviour across drivers. This driver supports ACPI platforms only. It currently retrieves the external clock rate from the "clock-frequency" property and fails probing if the rate does not match the expected value, which is the correct policy for ACPI platforms. Switch to using the devm_v4l2_sensor_clk_get() helper to standardise clock handling. This preserves the existing behaviour on ACPI platforms that specify a clock-frequency property without providing a clock. On platforms that provide a clock, the helper will program the clock to the rate specified by clock-frequency, which is also consistent with the driver's expectations. Reviewed-by: Hans de Goede Signed-off-by: Sanjay Chitroda Signed-off-by: Sakari Ailus --- drivers/media/i2c/gc0310.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/media/i2c/gc0310.c b/drivers/media/i2c/gc0310.c index 7af4d66f42a0..754e82ad50ae 100644 --- a/drivers/media/i2c/gc0310.c +++ b/drivers/media/i2c/gc0310.c @@ -6,6 +6,7 @@ * Copyright (c) 2023-2025 Hans de Goede */ +#include #include #include #include @@ -84,6 +85,8 @@ #define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd) struct gc0310_device { + struct clk *clk; + struct v4l2_subdev sd; struct media_pad pad; @@ -635,7 +638,6 @@ static int gc0310_check_hwcfg(struct device *dev) }; struct fwnode_handle *ep_fwnode; unsigned long link_freq_bitmap; - u32 mclk; int ret; /* @@ -647,21 +649,6 @@ static int gc0310_check_hwcfg(struct device *dev) return dev_err_probe(dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n"); - ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", - &mclk); - if (ret) { - fwnode_handle_put(ep_fwnode); - return dev_err_probe(dev, ret, - "reading clock-frequency property\n"); - } - - if (mclk != GC0310_MCLK_FREQ) { - fwnode_handle_put(ep_fwnode); - return dev_err_probe(dev, -EINVAL, - "external clock %u is not supported\n", - mclk); - } - ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg); fwnode_handle_put(ep_fwnode); if (ret) @@ -685,6 +672,7 @@ static int gc0310_check_hwcfg(struct device *dev) static int gc0310_probe(struct i2c_client *client) { struct gc0310_device *sensor; + unsigned long freq; int ret; ret = gc0310_check_hwcfg(&client->dev); @@ -695,6 +683,16 @@ static int gc0310_probe(struct i2c_client *client) if (!sensor) return -ENOMEM; + sensor->clk = devm_v4l2_sensor_clk_get(&client->dev, NULL); + if (IS_ERR(sensor->clk)) + return dev_err_probe(&client->dev, PTR_ERR(sensor->clk), + "failed to get clock\n"); + + freq = clk_get_rate(sensor->clk); + if (freq != GC0310_MCLK_FREQ) + return dev_err_probe(&client->dev, -EINVAL, + "external clock %lu is not supported\n", freq); + sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(sensor->reset)) { return dev_err_probe(&client->dev, PTR_ERR(sensor->reset), -- cgit From 9fc25c7b9a6ba2fe941450407950959df982a8bf Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Sun, 7 Jun 2026 23:52:40 +0000 Subject: media: intel: ipu-bridge: add blank line after declarations Add a blank line between the local variable declarations and the following statement in ipu_bridge_get_ivsc_acpi_dev(). Also remove an unnecessary blank line within the declaration block. This addresses a checkpatch.pl warning. No functional change intended. Signed-off-by: Hungyu Lin Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 9e37f24c8a7d..42a309e7218e 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -229,8 +229,8 @@ static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev for (i = 0; i < ARRAY_SIZE(ivsc_acpi_ids); i++) { const struct acpi_device_id *acpi_id = &ivsc_acpi_ids[i]; struct acpi_device *consumer, *ivsc_adev; - acpi_handle handle = acpi_device_handle(ACPI_PTR(adev)); + for_each_acpi_dev_match(ivsc_adev, acpi_id->id, NULL, -1) /* camera sensor depends on IVSC in DSDT if exist */ for_each_acpi_consumer_dev(ivsc_adev, consumer) -- cgit From 253c9659e25131b0169f718e7d094ac1aa0d9279 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Tue, 9 Jun 2026 20:05:23 +0300 Subject: media: bcm2835-unicam: Fix asc leaked in error/remove path v4l2_async_nf_add_fwnode_remote() allocates the asc, which is freed when v4l2_async_nf_cleanup() is called. Call v4l2_async_nf_cleanup() properly in the driver paths. Discovered with kmemleak after rmmod: unreferenced object 0xffff000084526b80 (size 64): comm "modprobe", pid 185, jiffies 4295013512 hex dump (first 32 bytes): 01 00 00 00 00 00 00 00 e8 0d ff bf 00 00 ff ff ................ 40 83 bc 84 00 00 ff ff 60 83 bc 84 00 00 ff ff @.......`....... backtrace (crc ac584083): [<00000000ffb081a7>] kmemleak_alloc+0x38/0x44 [<00000000d2fd9301>] __kmalloc+0x1b0/0x250 [<000000004dd5354d>] __v4l2_async_nf_add_fwnode+0x28/0x9c [<0000000067587657>] __v4l2_async_nf_add_fwnode_remote+0x3c/0x64 Fixes: 392cd78d495f ("media: bcm2835-unicam: Add support for CCP2/CSI2 camera interface") Signed-off-by: Eugen Hristev Reviewed-by: Laurent Pinchart Signed-off-by: Sakari Ailus --- drivers/media/platform/broadcom/bcm2835-unicam.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 8d28ba0b59a3..1508843ae58c 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -2613,6 +2613,7 @@ static int unicam_async_nf_init(struct unicam_device *unicam) return 0; error: + v4l2_async_nf_cleanup(&unicam->notifier); fwnode_handle_put(ep_handle); return ret; } @@ -2745,6 +2746,7 @@ static void unicam_remove(struct platform_device *pdev) v4l2_device_unregister(&unicam->v4l2_dev); media_device_unregister(&unicam->mdev); v4l2_async_nf_unregister(&unicam->notifier); + v4l2_async_nf_cleanup(&unicam->notifier); unicam_subdev_cleanup(unicam); -- cgit From 3fe797a12bfa9a49aa998ee722e5f25c957b9b15 Mon Sep 17 00:00:00 2001 From: Jurison Murati Date: Wed, 10 Jun 2026 12:22:36 +0200 Subject: media: i2c: ov8865: Drop the runtime PM usage count on stream failure ov8865_s_stream() takes a runtime PM reference when enabling the stream, but returns without releasing it if ov8865_sw_standby() fails, leaving the reference unbalanced and the sensor powered indefinitely. The same applies to a failure while disabling the stream, in which case the reference acquired at stream start is never dropped. Drop the reference in a single place, both when disabling the stream and on failure, and only update the streaming state on success. Signed-off-by: Jurison Murati Reviewed-by: Daniel Scally Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov8865.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index a8586df14f77..5b909a856128 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2621,15 +2621,13 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) ret = ov8865_sw_standby(sensor, !enable); mutex_unlock(&sensor->mutex); - if (ret) - return ret; - - state->streaming = !!enable; - - if (!enable) + if (ret || !enable) pm_runtime_put(sensor->dev); - return 0; + if (!ret) + state->streaming = enable; + + return ret; } static const struct v4l2_subdev_video_ops ov8865_subdev_video_ops = { -- cgit From 4e1446ac169a1a621ba90502ab8db3be821fe726 Mon Sep 17 00:00:00 2001 From: Jurison Murati Date: Wed, 10 Jun 2026 12:22:37 +0200 Subject: media: i2c: ov8865: Program the sensor on stream start The sensor registers are only written in the runtime PM resume handler; ov8865_set_fmt() merely stores the requested mode, relying on the sensor being runtime suspended between uses so that the next resume applies it. That assumption breaks when something keeps the sensor powered. On IPU3 platforms, ipu_bridge instantiates the VCM device with a DL_FLAG_PM_RUNTIME device link to the sensor, so a userspace process holding the VCM subdev open (e.g. wireplumber's camera monitor) pins the sensor runtime-active. A subsequent set_fmt() then never reaches the hardware: the sensor keeps streaming the mode programmed on the last resume while the CSI-2 receiver expects the newly negotiated format. On a Surface Book 2 (IPU3, ov8865 + dw9719 VCM), requesting the 3264x2448 mode while the hardware was left programmed for the 1632x1224 binned mode makes ipu3-cio2 report "frame sync error" and "payload length is 10340352, received 2585088" (exactly one binned frame) for every frame, and the inverse case stalls the stream after a single frame. Camera applications end up displaying one bogus frame forever. Program the sensor configuration and apply the control values on stream start instead, where the negotiated mode is always current, and only write the configuration in the runtime PM resume handler when resuming with the stream already started. Signed-off-by: Jurison Murati Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov8865.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c index 5b909a856128..c6d53c3d55ca 100644 --- a/drivers/media/i2c/ov8865.c +++ b/drivers/media/i2c/ov8865.c @@ -2609,7 +2609,7 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) { struct ov8865_sensor *sensor = ov8865_subdev_sensor(subdev); struct ov8865_state *state = &sensor->state; - int ret; + int ret = 0; if (enable) { ret = pm_runtime_resume_and_get(sensor->dev); @@ -2618,7 +2618,23 @@ static int ov8865_s_stream(struct v4l2_subdev *subdev, int enable) } mutex_lock(&sensor->mutex); - ret = ov8865_sw_standby(sensor, !enable); + + /* + * The sensor may have been kept powered by something else (e.g. the + * VCM's runtime PM device link on IPU3 platforms), in which case + * runtime resume did not run and the hardware may still be + * configured for a previous mode. Always program the negotiated + * configuration on stream start. + */ + if (enable) { + ret = ov8865_sensor_init(sensor); + if (!ret) + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + } + + if (!ret) + ret = ov8865_sw_standby(sensor, !enable); + mutex_unlock(&sensor->mutex); if (ret || !enable) @@ -2914,15 +2930,15 @@ static int ov8865_resume(struct device *dev) if (ret) goto complete; - ret = ov8865_sensor_init(sensor); - if (ret) - goto error_power; + if (state->streaming) { + ret = ov8865_sensor_init(sensor); + if (ret) + goto error_power; - ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); - if (ret) - goto error_power; + ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler); + if (ret) + goto error_power; - if (state->streaming) { ret = ov8865_sw_standby(sensor, false); if (ret) goto error_power; -- cgit From 9be07216af4cfc4813e1a46ce26407d31ea845de Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:33:43 +0800 Subject: media: ipu6: Do not free aux device pdata after init ipu6_bus_initialize_device() stores the isys/psys pdata pointer in struct ipu6_bus_device and initializes the auxiliary device. After that point, error unwinding must drop the auxiliary device reference and let ipu6_bus_release() free both the bus device and adev->pdata. The isys and psys init paths already call put_device() when MMU initialization fails, and ipu6_bus_add_device() calls auxiliary_device_uninit() on auxiliary_device_add() failure. Both paths therefore run the bus release callback. The extra kfree(pdata) in the callers can release the same object a second time. Remove the manual pdata frees after the auxiliary device has been initialized. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: cb3117b074ae ("media: intel/ipu6: add IPU auxiliary devices") Signed-off-by: Ruoyu Wang Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu6/ipu6.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c index d033d4618169..5449a2006bcc 100644 --- a/drivers/media/pci/intel/ipu6/ipu6.c +++ b/drivers/media/pci/intel/ipu6/ipu6.c @@ -400,7 +400,6 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, &ipdata->hw_variant); if (IS_ERR(isys_adev->mmu)) { put_device(&isys_adev->auxdev.dev); - kfree(pdata); return dev_err_cast_probe(dev, isys_adev->mmu, "ipu6_mmu_init(isys_adev->mmu) failed\n"); } @@ -408,10 +407,8 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent, isys_adev->mmu->dev = &isys_adev->auxdev.dev; ret = ipu6_bus_add_device(isys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return isys_adev; } @@ -444,7 +441,6 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, &ipdata->hw_variant); if (IS_ERR(psys_adev->mmu)) { put_device(&psys_adev->auxdev.dev); - kfree(pdata); return dev_err_cast_probe(&pdev->dev, psys_adev->mmu, "ipu6_mmu_init(psys_adev->mmu) failed\n"); } @@ -452,10 +448,8 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent, psys_adev->mmu->dev = &psys_adev->auxdev.dev; ret = ipu6_bus_add_device(psys_adev); - if (ret) { - kfree(pdata); + if (ret) return ERR_PTR(ret); - } return psys_adev; } -- cgit From e4a4f2e3b9bc65e0b82ae8c3a2945955870df36f Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Mon, 29 Jun 2026 12:51:46 +0200 Subject: media: i2c: vd55g1: Fix media bus code initialization In the driver initialization, the index of the default media bus code from the supported media bus code array is passed directly to the vd55g1_get_fmt_code() function instead of the proper media bus code. This works correctly as a proper media bus code is set after initialization but could not have been the case. This also resulted in mutliple "Unsupported mbus format" error messages. Retrieve the media bus code from the media bus code array, and pass this media bus code to vd55g1_get_fmt_code() instead of the code index. Rename VD55G1_MBUS_CODE_DEF to VD55G1_MBUS_CODE_IDX_DEF and VD55G1_MODE_DEF to VD55G1_MODE_IDX_DEF while at it to avoid future confusions. Display the guilty error code in warning message. Cc: stable@vger.kernel.org Fixes: e138e7f00042 ("media: i2c: vd55g1: Add support for vd65g4 RGB variant") Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- drivers/media/i2c/vd55g1.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 78d18c028154..fceb437e19be 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -114,9 +114,8 @@ #define VD55G1_WIDTH 804 #define VD55G1_HEIGHT 704 -#define VD55G1_MODE_DEF 0 +#define VD55G1_MODE_IDX_DEF 0 #define VD55G1_NB_GPIOS 4 -#define VD55G1_MBUS_CODE_DEF 0 #define VD55G1_DGAIN_DEF 256 #define VD55G1_AGAIN_DEF 19 #define VD55G1_EXPO_MAX_TERM 64 @@ -634,7 +633,7 @@ static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code) goto adapt_bayer_pattern; } } - dev_warn(sensor->dev, "Unsupported mbus format\n"); + dev_warn(sensor->dev, "Unsupported mbus format: 0x%x\n", code); return code; @@ -1347,6 +1346,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, { struct vd55g1 *sensor = to_vd55g1(sd); struct v4l2_subdev_format fmt = { 0 }; + int code; struct v4l2_subdev_route routes[] = { { .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE } }; @@ -1361,9 +1361,13 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, if (ret) return ret; - vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF], - vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF), - &fmt.format); + if (sensor->id == VD55G1_MODEL_ID_VD55G1) + code = vd55g1_mbus_formats_mono[0]; + else + code = vd55g1_mbus_formats_bayer[0][0]; + vd55g1_update_pad_fmt(sensor, + &vd55g1_supported_modes[VD55G1_MODE_IDX_DEF], + vd55g1_get_fmt_code(sensor, code), &fmt.format); return vd55g1_set_pad_fmt(sd, sd_state, &fmt); } -- cgit From 3e275097e206a67f2b691c8167313871ee421cd7 Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Mon, 29 Jun 2026 12:51:47 +0200 Subject: media: i2c: vd55g1: Remove spurious pad format update on init_state() vd55g1_update_pad_fmt() is called in vd55g1_init_state(). But vd55g1_set_pad_fmt(), called at the end of vd55g1_init_state(), also calls vd55g1_update_pad_fmt() itself. Enhance readability and clear confusion by only preparing the format in vd55g1_init_state() and let vd55g1_set_pad_fmt() update it instead, effectively calling it only 1 time instead of 2. Reviewed-by: Jacopo Mondi Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- drivers/media/i2c/vd55g1.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index fceb437e19be..22464fe31562 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -1365,9 +1365,9 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, code = vd55g1_mbus_formats_mono[0]; else code = vd55g1_mbus_formats_bayer[0][0]; - vd55g1_update_pad_fmt(sensor, - &vd55g1_supported_modes[VD55G1_MODE_IDX_DEF], - vd55g1_get_fmt_code(sensor, code), &fmt.format); + fmt.format.code = vd55g1_get_fmt_code(sensor, code); + fmt.format.width = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].width; + fmt.format.height = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].height; return vd55g1_set_pad_fmt(sd, sd_state, &fmt); } -- cgit From 113a84d6ba739d333eef2a595e3ccd4f97a306c2 Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Mon, 29 Jun 2026 12:51:48 +0200 Subject: media: i2c: vd55g1: Fix manual digital gain on color variant Apply digital gain to all channels, each channel representing a color. Cc: stable@vger.kernel.org Fixes: e138e7f00042 ("media: i2c: vd55g1: Add support for vd65g4 RGB variant") Reviewed-by: Jacopo Mondi Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- drivers/media/i2c/vd55g1.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 22464fe31562..37d44abd1435 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -60,7 +60,10 @@ #define VD55G1_PATGEN_ENABLE BIT(0) #define VD55G1_REG_MANUAL_ANALOG_GAIN CCI_REG8(0x0501) #define VD55G1_REG_MANUAL_COARSE_EXPOSURE CCI_REG16_LE(0x0502) -#define VD55G1_REG_MANUAL_DIGITAL_GAIN CCI_REG16_LE(0x0504) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0 CCI_REG16_LE(0x0504) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1 CCI_REG16_LE(0x0506) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2 CCI_REG16_LE(0x0508) +#define VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3 CCI_REG16_LE(0x050a) #define VD55G1_REG_APPLIED_COARSE_EXPOSURE CCI_REG16_LE(0x00e8) #define VD55G1_REG_APPLIED_ANALOG_GAIN CCI_REG16_LE(0x00ea) #define VD55G1_REG_APPLIED_DIGITAL_GAIN CCI_REG16_LE(0x00ec) @@ -849,9 +852,16 @@ static int vd55g1_update_expo_cluster(struct vd55g1 *sensor, bool is_auto) vd55g1_write(sensor, VD55G1_REG_MANUAL_ANALOG_GAIN, sensor->again_ctrl->val, &ret); - if (!is_auto && sensor->dgain_ctrl->is_new) - vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN, + if (!is_auto && sensor->dgain_ctrl->is_new) { + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH0, sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH1, + sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH2, + sensor->dgain_ctrl->val, &ret); + vd55g1_write(sensor, VD55G1_REG_MANUAL_DIGITAL_GAIN_CH3, + sensor->dgain_ctrl->val, &ret); + } return ret; } -- cgit From eaa329aff7bb272f7ba6881de14eb3a4c6626ed5 Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Mon, 29 Jun 2026 12:51:49 +0200 Subject: media: dt-bindings: vd55g1: Add vd55g4 compatible Define it as a new monochrome variant of vd65g4. Acked-by: Krzysztof Kozlowski Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml index 060ac6829b66..58b1f9e85a9d 100644 --- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml +++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml @@ -27,9 +27,10 @@ properties: compatible: enum: - st,vd55g1 + - st,vd55g4 - st,vd65g4 description: - VD55G1 is the monochrome variant, while VD65G4 is the color one. + VD55G1 and VD55G4 are monochrome variants, while VD65G4 is a color one. reg: maxItems: 1 -- cgit From 7a1e5239ae63c68035ac5b8902ff779b394adfd4 Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Mon, 29 Jun 2026 12:51:50 +0200 Subject: media: i2c: vd55g1: Add support for vd55g4 vd55g4 is the same device as vd65g4 but outputs in monochrome instead of RGB. Adapt the driver structure according to this new variant, and add its support. Reviewed-by: Jacopo Mondi Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- drivers/media/i2c/vd55g1.c | 130 ++++++++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 43 deletions(-) diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index 37d44abd1435..c4142d771e7c 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -29,11 +29,7 @@ /* Register Map */ #define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000) -#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_COLOR_VERSION CCI_REG32_LE(0x0670) #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) @@ -137,8 +133,39 @@ #define VD55G1_MIPI_RATE_MIN (250 * MEGA) #define VD55G1_MIPI_RATE_MAX (1200 * MEGA) -#define VD55G1_MODEL_ID_NAME(id) \ - ((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4") +enum vd55g1_model_id { + VD55G1_MODEL_ID_2 = 0x53354731, + VD55G1_MODEL_ID_3 = 0x53354733, +}; + +enum vd55g1_color_version { + VD55G1_COLOR_VERSION_MONO, + VD55G1_COLOR_VERSION_BAYER, +}; + +struct vd55g1_version { + char *name; + enum vd55g1_model_id id; + enum vd55g1_color_version color; +}; + +static const struct vd55g1_version vd55g1_versions[] = { + { + .name = "vd55g1", + .id = VD55G1_MODEL_ID_2, + .color = VD55G1_COLOR_VERSION_MONO, + }, + { + .name = "vd55g4", + .id = VD55G1_MODEL_ID_3, + .color = VD55G1_COLOR_VERSION_MONO, + }, + { + .name = "vd65g4", + .id = VD55G1_MODEL_ID_3, + .color = VD55G1_COLOR_VERSION_BAYER, + }, +}; static const u8 vd55g1_patch_array[] = { 0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00, @@ -534,7 +561,7 @@ struct vd55g1_vblank_limits { struct vd55g1 { struct device *dev; - unsigned int id; + const struct vd55g1_version *version; struct v4l2_subdev sd; struct media_pad pad; struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)]; @@ -625,20 +652,28 @@ static unsigned int vd55g1_get_fmt_data_type(u32 code) static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code) { + u32 fallback_code; unsigned int i, j; - 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; + if (sensor->version->color == VD55G1_COLOR_VERSION_MONO) { + fallback_code = vd55g1_mbus_formats_mono[0]; + for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_mono); i++) + if (vd55g1_mbus_formats_mono[i] == code) + return code; + } else { + fallback_code = vd55g1_mbus_formats_bayer[0][0]; + 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: 0x%x\n", code); - return code; + return fallback_code; adapt_bayer_pattern: j = 0; @@ -1182,8 +1217,8 @@ static int vd55g1_patch(struct vd55g1 *sensor) u64 patch; int ret = 0; - /* vd55g1 needs a patch while vd65g4 does not */ - if (sensor->id == VD55G1_MODEL_ID_VD55G1) { + /* Version 2 needs a patch while version 3 does not */ + if (sensor->version->id == VD55G1_MODEL_ID_2) { vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR, sizeof(vd55g1_patch_array), vd55g1_patch_array, &ret); @@ -1255,7 +1290,7 @@ static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd, struct vd55g1 *sensor = to_vd55g1(sd); u32 base_code; - if (sensor->id == VD55G1_MODEL_ID_VD55G1) { + if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER) { if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono)) return -EINVAL; base_code = vd55g1_mbus_formats_mono[code->index]; @@ -1371,7 +1406,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd, if (ret) return ret; - if (sensor->id == VD55G1_MODEL_ID_VD55G1) + if (sensor->version->color != VD55G1_COLOR_VERSION_BAYER) code = vd55g1_mbus_formats_mono[0]; else code = vd55g1_mbus_formats_bayer[0][0]; @@ -1658,38 +1693,46 @@ unlock_state: return ret; } +static const struct vd55g1_version * +vd55g1_get_version(enum vd55g1_model_id id, + enum vd55g1_color_version color) +{ + for (unsigned int i = 0; i < ARRAY_SIZE(vd55g1_versions); i++) { + if (vd55g1_versions[i].id == id && + vd55g1_versions[i].color == color) + return &vd55g1_versions[i]; + } + + return NULL; +} + static int vd55g1_detect(struct vd55g1 *sensor) { - unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev); - u64 rev, id; - int ret; + const struct vd55g1_version *dt_version = + device_get_match_data(sensor->dev); + const struct vd55g1_version *version; + u64 color, id; + int ret = 0; - ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL); + vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, &ret); + vd55g1_read(sensor, VD55G1_REG_COLOR_VERSION, &color, &ret); if (ret) return ret; - if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) { - dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n", - (u32)id); + version = vd55g1_get_version(id, color); + if (!version) { + dev_warn(sensor->dev, "Unsupported sensor version, expected %s\n", + dt_version->name); 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)); + if (version->id != dt_version->id || + version->color != dt_version->color) { + dev_err(sensor->dev, "Probed sensor version %s and device tree definition %s mismatch", + version->name, dt_version->name); return -ENODEV; } - sensor->id = id; - ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL); - if (ret) - return ret; - - 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; - } + sensor->version = version; return 0; } @@ -2047,8 +2090,9 @@ static void vd55g1_remove(struct i2c_client *client) } static const struct of_device_id vd55g1_dt_ids[] = { - { .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 }, - { .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 }, + { .compatible = "st,vd55g1", .data = (void *)&vd55g1_versions[0] }, + { .compatible = "st,vd55g4", .data = (void *)&vd55g1_versions[1] }, + { .compatible = "st,vd65g4", .data = (void *)&vd55g1_versions[2] }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, vd55g1_dt_ids); -- cgit From ee737bc3ccae7dc713ccaa83ffa46080c6031b3e Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:14 +0100 Subject: media: imx355: Avoid calling imx355_power_off twice in error path If v4l2_async_register_subdev_sensor failed, then the sensor had already been powered down by pm_runtime_idle, but the error path then also explicitly called imx355_power_off as well. That left an imbalance in the regulator and clock calls. Call pm_runtime_idle only after v4l2_async_register_subdev_sensor succeeds to avoid this. Fixes: efa5fe19c0a9 ("media: imx355: Enable runtime PM before registering async sub-device") Cc: stable@vger.kernel.org Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 27a5c212a527..ac59908f57aa 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1822,12 +1822,13 @@ static int imx355_probe(struct i2c_client *client) */ pm_runtime_set_active(imx355->dev); pm_runtime_enable(imx355->dev); - pm_runtime_idle(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) goto error_media_entity_runtime_pm; + pm_runtime_idle(imx355->dev); + return 0; error_media_entity_runtime_pm: -- cgit From 64faa6aa51bd7dadde3ce81b31ff4fce91042f14 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Wed, 15 Jul 2026 12:43:15 +0100 Subject: media: imx355: Add support for 24 MHz external clock The IMX355 sensor supports multiple external clock frequencies, including 19.2 MHz and 24 MHz. The driver currently supports only fixed 19.2 MHz input clock. Refactor the clock handling to make the PLL configuration dependent on the external clock frequency and add support for 24 MHz. Introduce a table of clock parameter sets and program the corresponding EXTCLK frequency and PLL multipliers to maintain consistent internal VCO frequencies across supported inputs. The PLL settings are adjusted so that OP VCO remains at 720 MHz This preserves existing timing characteristics while allowing systems using a 24 MHz clock to operate correctly. No functional change for existing 19.2 MHz users. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Heidelberg Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 77 +++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index ac59908f57aa..a6ee7532018f 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -25,6 +25,10 @@ #define IMX355_REG_CHIP_ID 0x0016 #define IMX355_CHIP_ID 0x0355 +/* PLL registers that depend on the external clock frequency */ +#define IMX355_REG_EXTCLK_FREQ 0x0136 +#define IMX355_REG_PLL_OP_MUL 0x030e + /* V_TIMING internal */ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff @@ -63,7 +67,6 @@ /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL -#define IMX355_EXT_CLK 19200000 #define IMX355_LINK_FREQ_INDEX 0 /* number of data lanes */ @@ -100,6 +103,29 @@ struct imx355_mode { struct imx355_reg_list reg_list; }; +struct imx355_clk_params { + u32 ext_clk; + u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ + u16 pll_op_mpy; /* OP system PLL multiplier */ +}; + +/* + * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. + * All modes use the same PLL setup for OP, with IOPCK being 720MHz. + */ +static const struct imx355_clk_params imx355_clk_params[] = { + { + .ext_clk = 19200000, + .extclk_freq = 0x1333, /* 19.2 MHz */ + .pll_op_mpy = 75, /* 19.2 / 2 * 75 = 720 MHz */ + }, + { + .ext_clk = 24000000, + .extclk_freq = 0x1800, /* 24.0 MHz */ + .pll_op_mpy = 60, /* 24.0 / 2 * 60 = 720 MHz */ + }, +}; + struct imx355_hwcfg { unsigned long link_freq_bitmap; }; @@ -125,6 +151,7 @@ struct imx355 { const struct imx355_mode *cur_mode; struct imx355_hwcfg *hwcfg; + const struct imx355_clk_params *clk_params; /* * Mutex for serialized access: @@ -144,8 +171,6 @@ static const struct regulator_bulk_data imx355_supplies[] = { }; static const struct imx355_reg imx355_global_regs[] = { - { 0x0136, 0x13 }, - { 0x0137, 0x33 }, { 0x304e, 0x03 }, { 0x4348, 0x16 }, { 0x4350, 0x19 }, @@ -235,8 +260,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -284,8 +307,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -333,8 +354,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -382,8 +401,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -431,8 +448,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -480,8 +495,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -529,8 +542,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -578,8 +589,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -627,8 +636,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -676,8 +683,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -725,8 +730,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -774,8 +777,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -823,8 +824,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, @@ -872,8 +871,6 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x0307, 0x78 }, { 0x030b, 0x01 }, { 0x030d, 0x02 }, - { 0x030e, 0x00 }, - { 0x030f, 0x4b }, { 0x0310, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, @@ -1422,6 +1419,16 @@ static int imx355_start_streaming(struct imx355 *imx355) return ret; } + /* Set PLL registers for the external clock frequency */ + ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, + imx355->clk_params->extclk_freq); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_PLL_OP_MUL, 2, + imx355->clk_params->pll_op_mpy); + if (ret) + return ret; + /* set digital gain control to all color mode */ ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); if (ret) @@ -1749,7 +1756,13 @@ static int imx355_probe(struct i2c_client *client) "failed to get clock\n"); freq = clk_get_rate(imx355->clk); - if (freq != IMX355_EXT_CLK) + for (unsigned int i = 0; i < ARRAY_SIZE(imx355_clk_params); i++) { + if (freq == imx355_clk_params[i].ext_clk) { + imx355->clk_params = &imx355_clk_params[i]; + break; + } + } + if (!imx355->clk_params) return dev_err_probe(imx355->dev, -EINVAL, "external clock %lu is not supported\n", freq); -- cgit From 1b3f2170a0c4703c297cc26b7e2b9d58ae0c3192 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:16 +0100 Subject: dt-bindings: media: imx355: Allow 2 CSI2 data lane output The sensor supports output over either 2 or 4 CSI2 data lanes. The binding only permitted the 4 lane configuration. Add the 2 lane configuration as an option. Signed-off-by: Dave Stevenson Reviewed-by: Krzysztof Kozlowski Signed-off-by: Sakari Ailus --- Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml index 6050d7e7dcfe..d9cdfda699bf 100644 --- a/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml @@ -50,8 +50,15 @@ properties: properties: data-lanes: - minItems: 4 - maxItems: 4 + oneOf: + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 required: - link-frequencies -- cgit From 5ac4c9b680af9f01559d08bed63561d0e3312b4f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:17 +0100 Subject: media: imx355: Remove duplicated registers from the mode tables A large number of registers are identical within all the modes. Move those to imx355_global_regs. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 345 +++------------------------------------------ 1 file changed, 23 insertions(+), 322 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index a6ee7532018f..01d6c28a2656 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -221,6 +221,29 @@ static const struct imx355_reg imx355_global_regs[] = { { 0x68b0, 0x00 }, { 0x3058, 0x00 }, { 0x305a, 0x00 }, + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x0310, 0x00 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, }; static const struct imx355_reg_list imx355_global_setting = { @@ -229,9 +252,6 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -244,8 +264,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0349, 0xcb }, { 0x034a, 0x09 }, { 0x034b, 0x97 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -253,32 +271,11 @@ static const struct imx355_reg mode_3268x2448_regs[] = { { 0x034d, 0xc4 }, { 0x034e, 0x09 }, { 0x034f, 0x90 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -291,8 +288,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0349, 0xc7 }, { 0x034a, 0x09 }, { 0x034b, 0x97 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -300,32 +295,11 @@ static const struct imx355_reg mode_3264x2448_regs[] = { { 0x034d, 0xc0 }, { 0x034e, 0x09 }, { 0x034f, 0x90 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x0a }, @@ -338,8 +312,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -347,32 +319,11 @@ static const struct imx355_reg mode_3280x2464_regs[] = { { 0x034d, 0xd0 }, { 0x034e, 0x09 }, { 0x034f, 0xa0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -385,8 +336,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0349, 0x33 }, { 0x034a, 0x06 }, { 0x034b, 0xf3 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -394,32 +343,11 @@ static const struct imx355_reg mode_1940x1096_regs[] = { { 0x034d, 0x94 }, { 0x034e, 0x04 }, { 0x034f, 0x48 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -432,8 +360,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0349, 0x2f }, { 0x034a, 0x06 }, { 0x034b, 0xf3 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -441,32 +367,11 @@ static const struct imx355_reg mode_1936x1096_regs[] = { { 0x034d, 0x90 }, { 0x034e, 0x04 }, { 0x034f, 0x48 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -479,8 +384,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0349, 0x2b }, { 0x034a, 0x06 }, { 0x034b, 0xeb }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -488,32 +391,11 @@ static const struct imx355_reg mode_1924x1080_regs[] = { { 0x034d, 0x84 }, { 0x034e, 0x04 }, { 0x034f, 0x38 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x05 }, @@ -526,8 +408,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0349, 0x27 }, { 0x034a, 0x06 }, { 0x034b, 0xeb }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -535,32 +415,11 @@ static const struct imx355_reg mode_1920x1080_regs[] = { { 0x034d, 0x80 }, { 0x034e, 0x04 }, { 0x034f, 0x38 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -573,8 +432,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -582,32 +439,11 @@ static const struct imx355_reg mode_1640x1232_regs[] = { { 0x034d, 0x68 }, { 0x034e, 0x04 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -620,8 +456,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x08 }, { 0x034b, 0x63 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -629,32 +463,11 @@ static const struct imx355_reg mode_1640x922_regs[] = { { 0x034d, 0x68 }, { 0x034e, 0x03 }, { 0x034f, 0x9a }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -667,8 +480,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x0349, 0x7f }, { 0x034a, 0x07 }, { 0x034b, 0xaf }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -676,32 +487,11 @@ static const struct imx355_reg mode_1300x736_regs[] = { { 0x034d, 0x14 }, { 0x034e, 0x02 }, { 0x034f, 0xe0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -714,8 +504,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x0349, 0x77 }, { 0x034a, 0x07 }, { 0x034b, 0xaf }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -723,32 +511,11 @@ static const struct imx355_reg mode_1296x736_regs[] = { { 0x034d, 0x10 }, { 0x034e, 0x02 }, { 0x034f, 0xe0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -761,8 +528,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x0349, 0x6f }, { 0x034a, 0x07 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -770,32 +535,11 @@ static const struct imx355_reg mode_1284x720_regs[] = { { 0x034d, 0x04 }, { 0x034e, 0x02 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x07 }, { 0x0343, 0x2c }, { 0x0340, 0x05 }, @@ -808,8 +552,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x0349, 0x67 }, { 0x034a, 0x07 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -817,32 +559,11 @@ static const struct imx355_reg mode_1280x720_regs[] = { { 0x034d, 0x00 }, { 0x034e, 0x02 }, { 0x034f, 0xd0 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, { 0x0342, 0x0e }, { 0x0343, 0x58 }, { 0x0340, 0x02 }, @@ -855,8 +576,6 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x0349, 0xcf }, { 0x034a, 0x09 }, { 0x034b, 0x9f }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, @@ -864,26 +583,8 @@ static const struct imx355_reg mode_820x616_regs[] = { { 0x034d, 0x34 }, { 0x034e, 0x02 }, { 0x034f, 0x68 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, }; static const char * const imx355_test_pattern_menu[] = { -- cgit From c8703ce868acf9b60de6a50502895d5c68855d35 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:18 +0100 Subject: media: imx355: Remove setting FRM_LENGTH_LINES in the mode regs Registers 0x0340 and 0x0341 (FRM_LENGTH_LINES) are already written from the set_ctrl(V4L2_CID_VBLANK) handler, so don't write them from the mode register list. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 01d6c28a2656..b40d89d86156 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -254,8 +254,6 @@ static const struct imx355_reg_list imx355_global_setting = { static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x08 }, { 0x0346, 0x00 }, @@ -278,8 +276,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x08 }, { 0x0346, 0x00 }, @@ -302,8 +298,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x0a }, - { 0x0341, 0x37 }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -326,8 +320,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa0 }, { 0x0346, 0x02 }, @@ -350,8 +342,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa0 }, { 0x0346, 0x02 }, @@ -374,8 +364,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa8 }, { 0x0346, 0x02 }, @@ -398,8 +386,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x02 }, { 0x0345, 0xa8 }, { 0x0346, 0x02 }, @@ -422,8 +408,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, @@ -446,8 +430,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { static const struct imx355_reg mode_1640x922_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x01 }, @@ -470,8 +452,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { static const struct imx355_reg mode_1300x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x58 }, { 0x0346, 0x01 }, @@ -494,8 +474,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { static const struct imx355_reg mode_1296x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x58 }, { 0x0346, 0x01 }, @@ -518,8 +496,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { static const struct imx355_reg mode_1284x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x68 }, { 0x0346, 0x02 }, @@ -542,8 +518,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { static const struct imx355_reg mode_1280x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0340, 0x05 }, - { 0x0341, 0x1a }, { 0x0344, 0x01 }, { 0x0345, 0x68 }, { 0x0346, 0x02 }, @@ -566,8 +540,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { static const struct imx355_reg mode_820x616_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0340, 0x02 }, - { 0x0341, 0x8c }, { 0x0344, 0x00 }, { 0x0345, 0x00 }, { 0x0346, 0x00 }, -- cgit From f8ec1f0c06e4b82e5601aa8dc3c1c6514a83ab57 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:19 +0100 Subject: media: imx355: Programmatically set the crop parameters for each mode Currently the cropping is set via register entries in the per mode register lists. Add the crop information to the mode structure as a v4l2_rect, and set the registers programmatically. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 290 +++++++++++++++++++-------------------------- 1 file changed, 121 insertions(+), 169 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index b40d89d86156..123ceed26d05 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -33,6 +33,13 @@ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_REG_X_ADD_START 0x0344 +#define IMX355_REG_Y_ADD_START 0x0346 +#define IMX355_REG_X_ADD_END 0x0348 +#define IMX355_REG_Y_ADD_END 0x034a +#define IMX355_REG_X_OUT_SIZE 0x034c +#define IMX355_REG_Y_OUT_SIZE 0x034e + /* Exposure control */ #define IMX355_REG_EXPOSURE 0x0202 #define IMX355_EXPOSURE_MIN 1 @@ -88,6 +95,7 @@ struct imx355_mode { u32 width; /* Frame height */ u32 height; + struct v4l2_rect crop; /* V-timing */ u32 fll_def; @@ -254,21 +262,9 @@ static const struct imx355_reg_list imx355_global_setting = { static const struct imx355_reg mode_3268x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x08 }, - { 0x0346, 0x00 }, - { 0x0347, 0x08 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcb }, - { 0x034a, 0x09 }, - { 0x034b, 0x97 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xc4 }, - { 0x034e, 0x09 }, - { 0x034f, 0x90 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -276,21 +272,9 @@ static const struct imx355_reg mode_3268x2448_regs[] = { static const struct imx355_reg mode_3264x2448_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x08 }, - { 0x0346, 0x00 }, - { 0x0347, 0x08 }, - { 0x0348, 0x0c }, - { 0x0349, 0xc7 }, - { 0x034a, 0x09 }, - { 0x034b, 0x97 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xc0 }, - { 0x034e, 0x09 }, - { 0x034f, 0x90 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -298,21 +282,9 @@ static const struct imx355_reg mode_3264x2448_regs[] = { static const struct imx355_reg mode_3280x2464_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x0c }, - { 0x034d, 0xd0 }, - { 0x034e, 0x09 }, - { 0x034f, 0xa0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -320,21 +292,9 @@ static const struct imx355_reg mode_3280x2464_regs[] = { static const struct imx355_reg mode_1940x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa0 }, - { 0x0346, 0x02 }, - { 0x0347, 0xac }, - { 0x0348, 0x0a }, - { 0x0349, 0x33 }, - { 0x034a, 0x06 }, - { 0x034b, 0xf3 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x94 }, - { 0x034e, 0x04 }, - { 0x034f, 0x48 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -342,21 +302,9 @@ static const struct imx355_reg mode_1940x1096_regs[] = { static const struct imx355_reg mode_1936x1096_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa0 }, - { 0x0346, 0x02 }, - { 0x0347, 0xac }, - { 0x0348, 0x0a }, - { 0x0349, 0x2f }, - { 0x034a, 0x06 }, - { 0x034b, 0xf3 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x90 }, - { 0x034e, 0x04 }, - { 0x034f, 0x48 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -364,21 +312,9 @@ static const struct imx355_reg mode_1936x1096_regs[] = { static const struct imx355_reg mode_1924x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa8 }, - { 0x0346, 0x02 }, - { 0x0347, 0xb4 }, - { 0x0348, 0x0a }, - { 0x0349, 0x2b }, - { 0x034a, 0x06 }, - { 0x034b, 0xeb }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x84 }, - { 0x034e, 0x04 }, - { 0x034f, 0x38 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -386,21 +322,9 @@ static const struct imx355_reg mode_1924x1080_regs[] = { static const struct imx355_reg mode_1920x1080_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x02 }, - { 0x0345, 0xa8 }, - { 0x0346, 0x02 }, - { 0x0347, 0xb4 }, - { 0x0348, 0x0a }, - { 0x0349, 0x27 }, - { 0x034a, 0x06 }, - { 0x034b, 0xeb }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, - { 0x034c, 0x07 }, - { 0x034d, 0x80 }, - { 0x034e, 0x04 }, - { 0x034f, 0x38 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -408,21 +332,9 @@ static const struct imx355_reg mode_1920x1080_regs[] = { static const struct imx355_reg mode_1640x1232_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x06 }, - { 0x034d, 0x68 }, - { 0x034e, 0x04 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -430,21 +342,9 @@ static const struct imx355_reg mode_1640x1232_regs[] = { static const struct imx355_reg mode_1640x922_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x01 }, - { 0x0347, 0x30 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x08 }, - { 0x034b, 0x63 }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x06 }, - { 0x034d, 0x68 }, - { 0x034e, 0x03 }, - { 0x034f, 0x9a }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -452,21 +352,9 @@ static const struct imx355_reg mode_1640x922_regs[] = { static const struct imx355_reg mode_1300x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x58 }, - { 0x0346, 0x01 }, - { 0x0347, 0xf0 }, - { 0x0348, 0x0b }, - { 0x0349, 0x7f }, - { 0x034a, 0x07 }, - { 0x034b, 0xaf }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x14 }, - { 0x034e, 0x02 }, - { 0x034f, 0xe0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -474,21 +362,9 @@ static const struct imx355_reg mode_1300x736_regs[] = { static const struct imx355_reg mode_1296x736_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x58 }, - { 0x0346, 0x01 }, - { 0x0347, 0xf0 }, - { 0x0348, 0x0b }, - { 0x0349, 0x77 }, - { 0x034a, 0x07 }, - { 0x034b, 0xaf }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x10 }, - { 0x034e, 0x02 }, - { 0x034f, 0xe0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -496,21 +372,9 @@ static const struct imx355_reg mode_1296x736_regs[] = { static const struct imx355_reg mode_1284x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x68 }, - { 0x0346, 0x02 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0b }, - { 0x0349, 0x6f }, - { 0x034a, 0x07 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x04 }, - { 0x034e, 0x02 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -518,21 +382,9 @@ static const struct imx355_reg mode_1284x720_regs[] = { static const struct imx355_reg mode_1280x720_regs[] = { { 0x0342, 0x07 }, { 0x0343, 0x2c }, - { 0x0344, 0x01 }, - { 0x0345, 0x68 }, - { 0x0346, 0x02 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0b }, - { 0x0349, 0x67 }, - { 0x034a, 0x07 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, - { 0x034c, 0x05 }, - { 0x034d, 0x00 }, - { 0x034e, 0x02 }, - { 0x034f, 0xd0 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; @@ -540,21 +392,9 @@ static const struct imx355_reg mode_1280x720_regs[] = { static const struct imx355_reg mode_820x616_regs[] = { { 0x0342, 0x0e }, { 0x0343, 0x58 }, - { 0x0344, 0x00 }, - { 0x0345, 0x00 }, - { 0x0346, 0x00 }, - { 0x0347, 0x00 }, - { 0x0348, 0x0c }, - { 0x0349, 0xcf }, - { 0x034a, 0x09 }, - { 0x034b, 0x9f }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, - { 0x034c, 0x03 }, - { 0x034d, 0x34 }, - { 0x034e, 0x02 }, - { 0x034f, 0x68 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, }; @@ -580,6 +420,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3280, .height = 2464, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -592,6 +438,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3268, .height = 2448, + .crop = { + .width = 3268, + .height = 2448, + .left = 8, + .top = 8, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -604,6 +456,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 3264, .height = 2448, + .crop = { + .width = 3264, + .height = 2448, + .left = 8, + .top = 8, + }, .fll_def = 2615, .fll_min = 2615, .llp = 3672, @@ -616,6 +474,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1940, .height = 1096, + .crop = { + .width = 1940, + .height = 1096, + .left = 672, + .top = 684, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -628,6 +492,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1936, .height = 1096, + .crop = { + .width = 1936, + .height = 1096, + .left = 672, + .top = 684, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -640,6 +510,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1924, .height = 1080, + .crop = { + .width = 1924, + .height = 1080, + .left = 680, + .top = 692, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -652,6 +528,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1920, .height = 1080, + .crop = { + .width = 1920, + .height = 1080, + .left = 680, + .top = 692, + }, .fll_def = 1306, .fll_min = 1306, .llp = 3672, @@ -664,6 +546,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1640, .height = 1232, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -676,6 +564,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1640, .height = 922, + .crop = { + .width = 3280, + .height = 1844, + .left = 0, + .top = 304, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -688,6 +582,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1300, .height = 736, + .crop = { + .width = 2600, + .height = 1472, + .left = 344, + .top = 496, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -700,6 +600,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1296, .height = 736, + .crop = { + .width = 2592, + .height = 1472, + .left = 344, + .top = 496, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -712,6 +618,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1284, .height = 720, + .crop = { + .width = 2568, + .height = 1440, + .left = 360, + .top = 512, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -724,6 +636,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 1280, .height = 720, + .crop = { + .width = 2560, + .height = 1440, + .left = 360, + .top = 512, + }, .fll_def = 1306, .fll_min = 1306, .llp = 1836, @@ -736,6 +654,12 @@ static const struct imx355_mode supported_modes[] = { { .width = 820, .height = 616, + .crop = { + .width = 3280, + .height = 2464, + .left = 0, + .top = 0, + }, .fll_def = 652, .fll_min = 652, .llp = 3672, @@ -1074,6 +998,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; + const struct imx355_mode *mode; int ret; /* Global Setting */ @@ -1085,13 +1010,40 @@ static int imx355_start_streaming(struct imx355 *imx355) } /* Apply default values of current mode */ - reg_list = &imx355->cur_mode->reg_list; + mode = imx355->cur_mode; + reg_list = &mode->reg_list; ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); if (ret) { dev_err(imx355->dev, "failed to set mode"); return ret; } + /* Set readout crop and size registers */ + ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_START, 2, + mode->crop.left); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_START, 2, + mode->crop.top); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_END, 2, + mode->crop.width + mode->crop.left - 1); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_END, 2, + mode->crop.height + mode->crop.top - 1); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_X_OUT_SIZE, 2, + mode->width); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_Y_OUT_SIZE, 2, + mode->height); + if (ret) + return ret; + /* Set PLL registers for the external clock frequency */ ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, imx355->clk_params->extclk_freq); -- cgit From 8722701663b9170d3a63c668f03a46a304169d3d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:20 +0100 Subject: media: imx355: Set register LINE_LENGTH_PCK programmatically The driver already has the LLP value stored in the mode structure, but also had the same value set via register writes in the mode's register list. Remove this duplication. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 123ceed26d05..e9f4bea67d3a 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -33,6 +33,9 @@ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_REG_LLP 0x0342 +#define IMX355_LLP_MAX 0xffff + #define IMX355_REG_X_ADD_START 0x0344 #define IMX355_REG_Y_ADD_START 0x0346 #define IMX355_REG_X_ADD_END 0x0348 @@ -260,8 +263,6 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -270,8 +271,6 @@ static const struct imx355_reg mode_3268x2448_regs[] = { }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -280,8 +279,6 @@ static const struct imx355_reg mode_3264x2448_regs[] = { }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -290,8 +287,6 @@ static const struct imx355_reg mode_3280x2464_regs[] = { }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -300,8 +295,6 @@ static const struct imx355_reg mode_1940x1096_regs[] = { }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -310,8 +303,6 @@ static const struct imx355_reg mode_1936x1096_regs[] = { }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -320,8 +311,6 @@ static const struct imx355_reg mode_1924x1080_regs[] = { }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x00 }, { 0x0901, 0x11 }, { 0x0902, 0x00 }, @@ -330,8 +319,6 @@ static const struct imx355_reg mode_1920x1080_regs[] = { }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -340,8 +327,6 @@ static const struct imx355_reg mode_1640x1232_regs[] = { }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -350,8 +335,6 @@ static const struct imx355_reg mode_1640x922_regs[] = { }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -360,8 +343,6 @@ static const struct imx355_reg mode_1300x736_regs[] = { }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -370,8 +351,6 @@ static const struct imx355_reg mode_1296x736_regs[] = { }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -380,8 +359,6 @@ static const struct imx355_reg mode_1284x720_regs[] = { }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0342, 0x07 }, - { 0x0343, 0x2c }, { 0x0900, 0x01 }, { 0x0901, 0x22 }, { 0x0902, 0x00 }, @@ -390,8 +367,6 @@ static const struct imx355_reg mode_1280x720_regs[] = { }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0342, 0x0e }, - { 0x0343, 0x58 }, { 0x0900, 0x01 }, { 0x0901, 0x44 }, { 0x0902, 0x00 }, @@ -1059,6 +1034,12 @@ static int imx355_start_streaming(struct imx355 *imx355) if (ret) return ret; + /* set line length */ + ret = imx355_write_reg(imx355, IMX355_REG_LLP, 2, + imx355->hblank->val + imx355->cur_mode->width); + if (ret) + return ret; + /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); if (ret) -- cgit From 70a99e08c47383acf4f2d7c82917375113b8d940 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:21 +0100 Subject: media: imx355: Set binning mode registers programmatically Compute the binning registers based on the difference between the mode width/height vs the crop width/height. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 61 +++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index e9f4bea67d3a..95487b12939d 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -72,6 +72,10 @@ #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 +#define IMX355_REG_BINNING_MODE 0x0900 +#define IMX355_REG_BINNING_TYPE 0x0901 +#define IMX355_REG_BINNING_WEIGHTING 0x0902 + /* Flip Control */ #define IMX355_REG_ORIENTATION 0x0101 @@ -263,113 +267,71 @@ static const struct imx355_reg_list imx355_global_setting = { }; static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0900, 0x00 }, - { 0x0901, 0x11 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x22 }, - { 0x0902, 0x00 }, { 0x0700, 0x00 }, { 0x0701, 0x10 }, }; static const struct imx355_reg mode_820x616_regs[] = { - { 0x0900, 0x01 }, - { 0x0901, 0x44 }, - { 0x0902, 0x00 }, { 0x0700, 0x02 }, { 0x0701, 0x78 }, }; @@ -974,6 +936,7 @@ static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; + u8 binning_mode; int ret; /* Global Setting */ @@ -1019,6 +982,20 @@ static int imx355_start_streaming(struct imx355 *imx355) if (ret) return ret; + binning_mode = ((mode->crop.width / mode->width) << 4) | + (mode->crop.height / mode->height); + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_MODE, 1, + binning_mode == 0x11 ? 0x00 : 0x01); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_TYPE, 1, + binning_mode); + if (ret) + return ret; + ret = imx355_write_reg(imx355, IMX355_REG_BINNING_WEIGHTING, 1, 0x00); + if (ret) + return ret; + /* Set PLL registers for the external clock frequency */ ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, imx355->clk_params->extclk_freq); -- cgit From d8c0dd60e923dbf9e58c8b293160b024b01a40b9 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:22 +0100 Subject: media: imx355: Remove link_freq_index from each mode as ununsed The link_freq_index value in imx355_mode is unused, so remove it. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 95487b12939d..b329da85ddc0 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -81,7 +81,6 @@ /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL -#define IMX355_LINK_FREQ_INDEX 0 /* number of data lanes */ #define IMX355_DATA_LANES 4 @@ -111,9 +110,6 @@ struct imx355_mode { /* H-timing */ u32 llp; - /* index of link frequency */ - u32 link_freq_index; - /* Default register values */ struct imx355_reg_list reg_list; }; @@ -366,7 +362,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), .regs = mode_3280x2464_regs, @@ -384,7 +379,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), .regs = mode_3268x2448_regs, @@ -402,7 +396,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 2615, .fll_min = 2615, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), .regs = mode_3264x2448_regs, @@ -420,7 +413,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), .regs = mode_1940x1096_regs, @@ -438,7 +430,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), .regs = mode_1936x1096_regs, @@ -456,7 +447,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), .regs = mode_1924x1080_regs, @@ -474,7 +464,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), .regs = mode_1920x1080_regs, @@ -492,7 +481,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), .regs = mode_1640x1232_regs, @@ -510,7 +498,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), .regs = mode_1640x922_regs, @@ -528,7 +515,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), .regs = mode_1300x736_regs, @@ -546,7 +532,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), .regs = mode_1296x736_regs, @@ -564,7 +549,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), .regs = mode_1284x720_regs, @@ -582,7 +566,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 1306, .fll_min = 1306, .llp = 1836, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), .regs = mode_1280x720_regs, @@ -600,7 +583,6 @@ static const struct imx355_mode supported_modes[] = { .fll_def = 652, .fll_min = 652, .llp = 3672, - .link_freq_index = IMX355_LINK_FREQ_INDEX, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_820x616_regs), .regs = mode_820x616_regs, -- cgit From 2ed5e717a47f06a84c738ee67d373fe587d7341f Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:23 +0100 Subject: media: imx355: pixel_rate never changes, so don't recompute The pixel rate is always the same, so there is no need to try and recompute it in imx355_set_pad_format, and then no need to have the pointer to it stored. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index b329da85ddc0..879e98b8a8ef 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -151,7 +151,6 @@ struct imx355 { struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *vblank; struct v4l2_ctrl *hblank; struct v4l2_ctrl *exposure; @@ -867,7 +866,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, s32 vblank_def; s32 vblank_min; s64 h_blank; - u64 pixel_rate; u32 height; mutex_lock(&imx355->mutex); @@ -888,9 +886,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, *framefmt = fmt->format; } else { imx355->cur_mode = mode; - pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; - do_div(pixel_rate, 10); - __v4l2_ctrl_s_ctrl_int64(imx355->pixel_rate, pixel_rate); /* Update limits and set FPS to default */ height = imx355->cur_mode->height; vblank_def = imx355->cur_mode->fll_def - height; @@ -1180,9 +1175,8 @@ static int imx355_init_controls(struct imx355 *imx355) pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; do_div(pixel_rate, 10); /* By default, PIXEL_RATE is read only */ - imx355->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_PIXEL_RATE, pixel_rate, - pixel_rate, 1, pixel_rate); + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, + pixel_rate, pixel_rate, 1, pixel_rate); /* Initialize vblank/hblank/exposure parameters based on current mode */ mode = imx355->cur_mode; -- cgit From ac2d97c65db49a41d411041dec0bf6ec18314a19 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:24 +0100 Subject: media: imx355: Remove redundant fll_min, and implement fixed offset fll_min (Frame Length Lines) is set to the same value as fll_def for all modes, which makes it redundant. The actual value is also erroneous as the sensor works in all the defined modes with FLL set at the mode height + 20 lines, so set the vblank control minimum to 20 rather than varying it. This also improves the maximum frame rate achievable. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 879e98b8a8ef..649a6fc9abe9 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -32,6 +32,7 @@ /* V_TIMING internal */ #define IMX355_REG_FLL 0x0340 #define IMX355_FLL_MAX 0xffff +#define IMX355_VBLANK_MIN 20 #define IMX355_REG_LLP 0x0342 #define IMX355_LLP_MAX 0xffff @@ -105,7 +106,6 @@ struct imx355_mode { /* V-timing */ u32 fll_def; - u32 fll_min; /* H-timing */ u32 llp; @@ -359,7 +359,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), @@ -376,7 +375,6 @@ static const struct imx355_mode supported_modes[] = { .top = 8, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), @@ -393,7 +391,6 @@ static const struct imx355_mode supported_modes[] = { .top = 8, }, .fll_def = 2615, - .fll_min = 2615, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), @@ -410,7 +407,6 @@ static const struct imx355_mode supported_modes[] = { .top = 684, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), @@ -427,7 +423,6 @@ static const struct imx355_mode supported_modes[] = { .top = 684, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), @@ -444,7 +439,6 @@ static const struct imx355_mode supported_modes[] = { .top = 692, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), @@ -461,7 +455,6 @@ static const struct imx355_mode supported_modes[] = { .top = 692, }, .fll_def = 1306, - .fll_min = 1306, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), @@ -478,7 +471,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), @@ -495,7 +487,6 @@ static const struct imx355_mode supported_modes[] = { .top = 304, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), @@ -512,7 +503,6 @@ static const struct imx355_mode supported_modes[] = { .top = 496, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), @@ -529,7 +519,6 @@ static const struct imx355_mode supported_modes[] = { .top = 496, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), @@ -546,7 +535,6 @@ static const struct imx355_mode supported_modes[] = { .top = 512, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), @@ -563,7 +551,6 @@ static const struct imx355_mode supported_modes[] = { .top = 512, }, .fll_def = 1306, - .fll_min = 1306, .llp = 1836, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), @@ -580,7 +567,6 @@ static const struct imx355_mode supported_modes[] = { .top = 0, }, .fll_def = 652, - .fll_min = 652, .llp = 3672, .reg_list = { .num_of_regs = ARRAY_SIZE(mode_820x616_regs), @@ -864,7 +850,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; s32 vblank_def; - s32 vblank_min; s64 h_blank; u32 height; @@ -889,10 +874,9 @@ imx355_set_pad_format(struct v4l2_subdev *sd, /* Update limits and set FPS to default */ height = imx355->cur_mode->height; vblank_def = imx355->cur_mode->fll_def - height; - vblank_min = imx355->cur_mode->fll_min - height; height = IMX355_FLL_MAX - height; - __v4l2_ctrl_modify_range(imx355->vblank, vblank_min, height, 1, - vblank_def); + __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, + height, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); h_blank = mode->llp - imx355->cur_mode->width; /* @@ -1151,7 +1135,6 @@ static int imx355_init_controls(struct imx355 *imx355) struct v4l2_ctrl_handler *ctrl_hdlr; s64 exposure_max; s64 vblank_def; - s64 vblank_min; s64 hblank; u64 pixel_rate; const struct imx355_mode *mode; @@ -1181,9 +1164,8 @@ static int imx355_init_controls(struct imx355 *imx355) /* Initialize vblank/hblank/exposure parameters based on current mode */ mode = imx355->cur_mode; vblank_def = mode->fll_def - mode->height; - vblank_min = mode->fll_min - mode->height; imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_VBLANK, vblank_min, + V4L2_CID_VBLANK, IMX355_VBLANK_MIN, IMX355_FLL_MAX - mode->height, 1, vblank_def); -- cgit From 8a52937caec744247fa31ea923d83c0b7fb7645d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:25 +0100 Subject: media: imx355: Add support for get_selection Provide all the cropping information via get_selection. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 649a6fc9abe9..120eda46d982 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -86,6 +86,11 @@ /* number of data lanes */ #define IMX355_DATA_LANES 4 +#define IMX355_PIXEL_ARRAY_TOP 0 +#define IMX355_PIXEL_ARRAY_LEFT 0 +#define IMX355_PIXEL_ARRAY_WIDTH 3280 +#define IMX355_PIXEL_ARRAY_HEIGHT 2464 + struct imx355_reg { u16 address; u8 val; @@ -677,6 +682,7 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) struct imx355 *imx355 = to_imx355(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_state_get_format(fh->state, 0); + struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); mutex_lock(&imx355->mutex); @@ -686,6 +692,8 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->code = imx355_get_format_code(imx355); try_fmt->field = V4L2_FIELD_NONE; + *crop = imx355->cur_mode->crop; + mutex_unlock(&imx355->mutex); return 0; @@ -849,6 +857,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, struct imx355 *imx355 = to_imx355(sd); const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; + struct v4l2_rect *crop; s32 vblank_def; s64 h_blank; u32 height; @@ -869,6 +878,12 @@ imx355_set_pad_format(struct v4l2_subdev *sd, if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); *framefmt = fmt->format; + + crop = v4l2_subdev_state_get_crop(sd_state, 0); + crop->width = mode->crop.width; + crop->height = mode->crop.height; + crop->left = mode->crop.left; + crop->top = mode->crop.top; } else { imx355->cur_mode = mode; /* Update limits and set FPS to default */ @@ -892,6 +907,50 @@ imx355_set_pad_format(struct v4l2_subdev *sd, return 0; } +static void +__imx355_get_pad_crop(struct imx355 *imx355, + struct v4l2_subdev_state *sd_state, unsigned int pad, + enum v4l2_subdev_format_whence which, struct v4l2_rect *r) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + *r = *v4l2_subdev_state_get_crop(sd_state, pad); + break; + case V4L2_SUBDEV_FORMAT_ACTIVE: + *r = imx355->cur_mode->crop; + break; + } +} + +static int imx355_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct imx355 *imx355 = to_imx355(sd); + + mutex_lock(&imx355->mutex); + __imx355_get_pad_crop(imx355, sd_state, sel->pad, sel->which, + &sel->r); + mutex_unlock(&imx355->mutex); + + return 0; + } + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = IMX355_PIXEL_ARRAY_TOP; + sel->r.left = IMX355_PIXEL_ARRAY_LEFT; + sel->r.width = IMX355_PIXEL_ARRAY_WIDTH; + sel->r.height = IMX355_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { @@ -1066,6 +1125,7 @@ static const struct v4l2_subdev_pad_ops imx355_pad_ops = { .get_fmt = imx355_get_pad_format, .set_fmt = imx355_set_pad_format, .enum_frame_size = imx355_enum_frame_size, + .get_selection = imx355_get_selection, }; static const struct v4l2_subdev_ops imx355_subdev_ops = { -- cgit From a21872acb74290a5dc62c7e50137012d08078b29 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:26 +0100 Subject: media: imx355: Use pm_runtime autosuspend_delay Avoid powering the sensor up and down unnecessarily by using pm_runtime's autosuspend_delay feature. Signed-off-by: Dave Stevenson Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 120eda46d982..17a2acc1152d 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1074,7 +1074,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) goto err_rpm_put; } else { imx355_stop_streaming(imx355); - pm_runtime_put(imx355->dev); + pm_runtime_put_autosuspend(imx355->dev); } /* vflip and hflip cannot change during streaming */ @@ -1086,7 +1086,7 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) return ret; err_rpm_put: - pm_runtime_put(imx355->dev); + pm_runtime_put_autosuspend(imx355->dev); err_unlock: mutex_unlock(&imx355->mutex); @@ -1436,6 +1436,8 @@ static int imx355_probe(struct i2c_client *client) */ pm_runtime_set_active(imx355->dev); pm_runtime_enable(imx355->dev); + pm_runtime_set_autosuspend_delay(imx355->dev, 1000); + pm_runtime_use_autosuspend(imx355->dev); ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) @@ -1448,6 +1450,7 @@ static int imx355_probe(struct i2c_client *client) error_media_entity_runtime_pm: pm_runtime_disable(imx355->dev); pm_runtime_set_suspended(imx355->dev); + pm_runtime_dont_use_autosuspend(imx355->dev); media_entity_cleanup(&imx355->sd.entity); error_handler_free: @@ -1478,6 +1481,8 @@ static void imx355_remove(struct i2c_client *client) pm_runtime_set_suspended(imx355->dev); } + pm_runtime_dont_use_autosuspend(imx355->dev); + mutex_destroy(&imx355->mutex); } -- cgit From 314a6743938789c9c2937b052ddb5cff1e70d1ad Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:27 +0100 Subject: media: imx355: Convert to new CCI register access helpers Use the new comon CCI register access helpers to replace the private register access helpers in the imx355 driver. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/Kconfig | 1 + drivers/media/i2c/imx355.c | 500 ++++++++++++++++++--------------------------- 2 files changed, 196 insertions(+), 305 deletions(-) diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index e8b7422bc3d5..5c52007f9cbe 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -259,6 +259,7 @@ config VIDEO_IMX335 config VIDEO_IMX355 tristate "Sony IMX355 sensor support" + select V4L2_CCI_I2C help This is a Video4Linux2 sensor driver for the Sony IMX355 camera. diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 17a2acc1152d..52bdfe636231 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -9,76 +9,78 @@ #include #include #include +#include #include #include +#include #include #include #include #include -#define IMX355_REG_MODE_SELECT 0x0100 +#define IMX355_REG_MODE_SELECT CCI_REG8(0x0100) #define IMX355_MODE_STANDBY 0x00 #define IMX355_MODE_STREAMING 0x01 /* Chip ID */ -#define IMX355_REG_CHIP_ID 0x0016 +#define IMX355_REG_CHIP_ID CCI_REG16(0x0016) #define IMX355_CHIP_ID 0x0355 /* PLL registers that depend on the external clock frequency */ -#define IMX355_REG_EXTCLK_FREQ 0x0136 -#define IMX355_REG_PLL_OP_MUL 0x030e +#define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) +#define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) /* V_TIMING internal */ -#define IMX355_REG_FLL 0x0340 +#define IMX355_REG_FLL CCI_REG16(0x0340) #define IMX355_FLL_MAX 0xffff #define IMX355_VBLANK_MIN 20 -#define IMX355_REG_LLP 0x0342 +#define IMX355_REG_LLP CCI_REG16(0x0342) #define IMX355_LLP_MAX 0xffff -#define IMX355_REG_X_ADD_START 0x0344 -#define IMX355_REG_Y_ADD_START 0x0346 -#define IMX355_REG_X_ADD_END 0x0348 -#define IMX355_REG_Y_ADD_END 0x034a -#define IMX355_REG_X_OUT_SIZE 0x034c -#define IMX355_REG_Y_OUT_SIZE 0x034e +#define IMX355_REG_X_ADD_START CCI_REG16(0x0344) +#define IMX355_REG_Y_ADD_START CCI_REG16(0x0346) +#define IMX355_REG_X_ADD_END CCI_REG16(0x0348) +#define IMX355_REG_Y_ADD_END CCI_REG16(0x034a) +#define IMX355_REG_X_OUT_SIZE CCI_REG16(0x034c) +#define IMX355_REG_Y_OUT_SIZE CCI_REG16(0x034e) /* Exposure control */ -#define IMX355_REG_EXPOSURE 0x0202 +#define IMX355_REG_EXPOSURE CCI_REG16(0x0202) #define IMX355_EXPOSURE_MIN 1 #define IMX355_EXPOSURE_STEP 1 #define IMX355_EXPOSURE_DEFAULT 0x0282 /* Analog gain control */ -#define IMX355_REG_ANALOG_GAIN 0x0204 +#define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) #define IMX355_ANA_GAIN_MIN 0 #define IMX355_ANA_GAIN_MAX 960 #define IMX355_ANA_GAIN_STEP 1 #define IMX355_ANA_GAIN_DEFAULT 0 /* Digital gain control */ -#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 -#define IMX355_REG_DIG_GAIN_GLOBAL 0x020e +#define IMX355_REG_DPGA_USE_GLOBAL_GAIN CCI_REG8(0x3070) +#define IMX355_REG_DIG_GAIN_GLOBAL CCI_REG16(0x020e) #define IMX355_DGTL_GAIN_MIN 256 #define IMX355_DGTL_GAIN_MAX 4095 #define IMX355_DGTL_GAIN_STEP 1 #define IMX355_DGTL_GAIN_DEFAULT 256 /* Test Pattern Control */ -#define IMX355_REG_TEST_PATTERN 0x0600 +#define IMX355_REG_TEST_PATTERN CCI_REG16(0x0600) #define IMX355_TEST_PATTERN_DISABLED 0 #define IMX355_TEST_PATTERN_SOLID_COLOR 1 #define IMX355_TEST_PATTERN_COLOR_BARS 2 #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 -#define IMX355_REG_BINNING_MODE 0x0900 -#define IMX355_REG_BINNING_TYPE 0x0901 -#define IMX355_REG_BINNING_WEIGHTING 0x0902 +#define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) +#define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) +#define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) /* Flip Control */ -#define IMX355_REG_ORIENTATION 0x0101 +#define IMX355_REG_ORIENTATION CCI_REG8(0x0101) /* default link frequency and external clock */ #define IMX355_LINK_FREQ_DEFAULT 360000000LL @@ -91,14 +93,9 @@ #define IMX355_PIXEL_ARRAY_WIDTH 3280 #define IMX355_PIXEL_ARRAY_HEIGHT 2464 -struct imx355_reg { - u16 address; - u8 val; -}; - struct imx355_reg_list { u32 num_of_regs; - const struct imx355_reg *regs; + const struct cci_reg_sequence *regs; }; /* Mode : resolution and related config&values */ @@ -149,6 +146,7 @@ struct imx355_hwcfg { struct imx355 { struct device *dev; struct clk *clk; + struct regmap *regmap; struct v4l2_subdev sd; struct media_pad pad; @@ -185,155 +183,150 @@ static const struct regulator_bulk_data imx355_supplies[] = { { .supply = "dovdd" }, }; -static const struct imx355_reg imx355_global_regs[] = { - { 0x304e, 0x03 }, - { 0x4348, 0x16 }, - { 0x4350, 0x19 }, - { 0x4408, 0x0a }, - { 0x440c, 0x0b }, - { 0x4411, 0x5f }, - { 0x4412, 0x2c }, - { 0x4623, 0x00 }, - { 0x462c, 0x0f }, - { 0x462d, 0x00 }, - { 0x462e, 0x00 }, - { 0x4684, 0x54 }, - { 0x480a, 0x07 }, - { 0x4908, 0x07 }, - { 0x4909, 0x07 }, - { 0x490d, 0x0a }, - { 0x491e, 0x0f }, - { 0x4921, 0x06 }, - { 0x4923, 0x28 }, - { 0x4924, 0x28 }, - { 0x4925, 0x29 }, - { 0x4926, 0x29 }, - { 0x4927, 0x1f }, - { 0x4928, 0x20 }, - { 0x4929, 0x20 }, - { 0x492a, 0x20 }, - { 0x492c, 0x05 }, - { 0x492d, 0x06 }, - { 0x492e, 0x06 }, - { 0x492f, 0x06 }, - { 0x4930, 0x03 }, - { 0x4931, 0x04 }, - { 0x4932, 0x04 }, - { 0x4933, 0x05 }, - { 0x595e, 0x01 }, - { 0x5963, 0x01 }, - { 0x3030, 0x01 }, - { 0x3031, 0x01 }, - { 0x3045, 0x01 }, - { 0x4010, 0x00 }, - { 0x4011, 0x00 }, - { 0x4012, 0x00 }, - { 0x4013, 0x01 }, - { 0x68a8, 0xfe }, - { 0x68a9, 0xff }, - { 0x6888, 0x00 }, - { 0x6889, 0x00 }, - { 0x68b0, 0x00 }, - { 0x3058, 0x00 }, - { 0x305a, 0x00 }, - { 0x0112, 0x0a }, - { 0x0113, 0x0a }, - { 0x0114, 0x03 }, - { 0x0301, 0x05 }, - { 0x0303, 0x01 }, - { 0x0305, 0x02 }, - { 0x0306, 0x00 }, - { 0x0307, 0x78 }, - { 0x030b, 0x01 }, - { 0x030d, 0x02 }, - { 0x0310, 0x00 }, - { 0x0220, 0x00 }, - { 0x0222, 0x01 }, - { 0x0820, 0x0b }, - { 0x0821, 0x40 }, - { 0x3088, 0x04 }, - { 0x6813, 0x02 }, - { 0x6835, 0x07 }, - { 0x6836, 0x01 }, - { 0x6837, 0x04 }, - { 0x684d, 0x07 }, - { 0x684e, 0x01 }, - { 0x684f, 0x04 }, +static const struct cci_reg_sequence imx355_global_regs[] = { + { CCI_REG8(0x304e), 0x03 }, + { CCI_REG8(0x4348), 0x16 }, + { CCI_REG8(0x4350), 0x19 }, + { CCI_REG8(0x4408), 0x0a }, + { CCI_REG8(0x440c), 0x0b }, + { CCI_REG8(0x4411), 0x5f }, + { CCI_REG8(0x4412), 0x2c }, + { CCI_REG8(0x4623), 0x00 }, + { CCI_REG8(0x462c), 0x0f }, + { CCI_REG8(0x462d), 0x00 }, + { CCI_REG8(0x462e), 0x00 }, + { CCI_REG8(0x4684), 0x54 }, + { CCI_REG8(0x480a), 0x07 }, + { CCI_REG8(0x4908), 0x07 }, + { CCI_REG8(0x4909), 0x07 }, + { CCI_REG8(0x490d), 0x0a }, + { CCI_REG8(0x491e), 0x0f }, + { CCI_REG8(0x4921), 0x06 }, + { CCI_REG8(0x4923), 0x28 }, + { CCI_REG8(0x4924), 0x28 }, + { CCI_REG8(0x4925), 0x29 }, + { CCI_REG8(0x4926), 0x29 }, + { CCI_REG8(0x4927), 0x1f }, + { CCI_REG8(0x4928), 0x20 }, + { CCI_REG8(0x4929), 0x20 }, + { CCI_REG8(0x492a), 0x20 }, + { CCI_REG8(0x492c), 0x05 }, + { CCI_REG8(0x492d), 0x06 }, + { CCI_REG8(0x492e), 0x06 }, + { CCI_REG8(0x492f), 0x06 }, + { CCI_REG8(0x4930), 0x03 }, + { CCI_REG8(0x4931), 0x04 }, + { CCI_REG8(0x4932), 0x04 }, + { CCI_REG8(0x4933), 0x05 }, + { CCI_REG8(0x595e), 0x01 }, + { CCI_REG8(0x5963), 0x01 }, + { CCI_REG8(0x3030), 0x01 }, + { CCI_REG8(0x3031), 0x01 }, + { CCI_REG8(0x3045), 0x01 }, + { CCI_REG8(0x4010), 0x00 }, + { CCI_REG8(0x4011), 0x00 }, + { CCI_REG8(0x4012), 0x00 }, + { CCI_REG8(0x4013), 0x01 }, + { CCI_REG8(0x68a8), 0xfe }, + { CCI_REG8(0x68a9), 0xff }, + { CCI_REG8(0x6888), 0x00 }, + { CCI_REG8(0x6889), 0x00 }, + { CCI_REG8(0x68b0), 0x00 }, + { CCI_REG8(0x3058), 0x00 }, + { CCI_REG8(0x305a), 0x00 }, + { CCI_REG8(0x0112), 0x0a }, + { CCI_REG8(0x0113), 0x0a }, + { CCI_REG8(0x0114), 0x03 }, + { CCI_REG8(0x0301), 0x05 }, + { CCI_REG8(0x0303), 0x01 }, + { CCI_REG8(0x0305), 0x02 }, + { CCI_REG8(0x0306), 0x00 }, + { CCI_REG8(0x0307), 0x78 }, + { CCI_REG8(0x030b), 0x01 }, + { CCI_REG8(0x030d), 0x02 }, + { CCI_REG8(0x0310), 0x00 }, + { CCI_REG8(0x0220), 0x00 }, + { CCI_REG8(0x0222), 0x01 }, + { CCI_REG8(0x0820), 0x0b }, + { CCI_REG8(0x0821), 0x40 }, + { CCI_REG8(0x3088), 0x04 }, + { CCI_REG8(0x6813), 0x02 }, + { CCI_REG8(0x6835), 0x07 }, + { CCI_REG8(0x6836), 0x01 }, + { CCI_REG8(0x6837), 0x04 }, + { CCI_REG8(0x684d), 0x07 }, + { CCI_REG8(0x684e), 0x01 }, + { CCI_REG8(0x684f), 0x04 }, }; -static const struct imx355_reg_list imx355_global_setting = { - .num_of_regs = ARRAY_SIZE(imx355_global_regs), - .regs = imx355_global_regs, +static const struct cci_reg_sequence mode_3268x2448_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3268x2448_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_3264x2448_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3264x2448_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_3280x2464_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_3280x2464_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1940x1096_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1940x1096_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1936x1096_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1936x1096_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1924x1080_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1924x1080_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1920x1080_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1920x1080_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1640x1232_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1640x1232_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1640x922_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1640x922_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1300x736_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1300x736_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1296x736_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1296x736_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1284x720_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1284x720_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, +static const struct cci_reg_sequence mode_1280x720_regs[] = { + { CCI_REG8(0x0700), 0x00 }, + { CCI_REG8(0x0701), 0x10 }, }; -static const struct imx355_reg mode_1280x720_regs[] = { - { 0x0700, 0x00 }, - { 0x0701, 0x10 }, -}; - -static const struct imx355_reg mode_820x616_regs[] = { - { 0x0700, 0x02 }, - { 0x0701, 0x78 }, +static const struct cci_reg_sequence mode_820x616_regs[] = { + { CCI_REG8(0x0700), 0x02 }, + { CCI_REG8(0x0701), 0x78 }, }; static const char * const imx355_test_pattern_menu[] = { @@ -604,78 +597,6 @@ static u32 imx355_get_format_code(struct imx355 *imx355) return code; } -/* Read registers up to 4 at a time */ -static int imx355_read_reg(struct imx355 *imx355, u16 reg, u32 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = { 0 }; - int ret; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, addr_buf); - /* Write register address */ - msgs[0].addr = client->addr; - msgs[0].flags = 0; - msgs[0].len = ARRAY_SIZE(addr_buf); - msgs[0].buf = addr_buf; - - /* Read data from register */ - msgs[1].addr = client->addr; - msgs[1].flags = I2C_M_RD; - msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; - - ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) - return -EIO; - - *val = get_unaligned_be32(data_buf); - - return 0; -} - -/* Write registers up to 4 at a time */ -static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << (8 * (4 - len)), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; -} - -/* Write a list of registers */ -static int imx355_write_regs(struct imx355 *imx355, - const struct imx355_reg *regs, u32 len) -{ - int ret; - u32 i; - - for (i = 0; i < len; i++) { - ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); - if (ret) { - dev_err_ratelimited(imx355->dev, - "write reg 0x%4.4x return err %d", - regs[i].address, ret); - - return ret; - } - } - - return 0; -} - /* Open sub-device */ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { @@ -727,31 +648,31 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_ANALOGUE_GAIN: /* Analog gain = 1024/(1024 - ctrl->val) times */ - ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_ANALOG_GAIN, + ctrl->val, NULL); break; case V4L2_CID_DIGITAL_GAIN: - ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_DIG_GAIN_GLOBAL, + ctrl->val, NULL); break; case V4L2_CID_EXPOSURE: - ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, - ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_EXPOSURE, + ctrl->val, NULL); break; case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ - ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, - imx355->cur_mode->height + ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_FLL, + imx355->cur_mode->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: - ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, - 2, ctrl->val); + ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, + ctrl->val, NULL); break; case V4L2_CID_HFLIP: case V4L2_CID_VFLIP: - ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, - imx355->hflip->val | - imx355->vflip->val << 1); + ret = cci_write(imx355->regmap, IMX355_REG_ORIENTATION, + imx355->hflip->val | imx355->vflip->val << 1, + NULL); break; default: ret = -EINVAL; @@ -957,100 +878,64 @@ static int imx355_start_streaming(struct imx355 *imx355) const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; u8 binning_mode; - int ret; + int ret = 0; /* Global Setting */ - reg_list = &imx355_global_setting; - ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(imx355->dev, "failed to set global settings"); - return ret; - } + cci_multi_reg_write(imx355->regmap, imx355_global_regs, + ARRAY_SIZE(imx355_global_regs), &ret); /* Apply default values of current mode */ mode = imx355->cur_mode; reg_list = &mode->reg_list; - ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); - if (ret) { - dev_err(imx355->dev, "failed to set mode"); - return ret; - } + cci_multi_reg_write(imx355->regmap, reg_list->regs, + reg_list->num_of_regs, &ret); /* Set readout crop and size registers */ - ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_START, 2, - mode->crop.left); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_START, 2, - mode->crop.top); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_X_ADD_END, 2, - mode->crop.width + mode->crop.left - 1); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_ADD_END, 2, - mode->crop.height + mode->crop.top - 1); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_X_OUT_SIZE, 2, - mode->width); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_Y_OUT_SIZE, 2, - mode->height); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_X_ADD_START, mode->crop.left, + &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, mode->crop.top, &ret); + cci_write(imx355->regmap, IMX355_REG_X_ADD_END, + mode->crop.width + mode->crop.left - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, + mode->crop.height + mode->crop.top - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, mode->width, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, mode->height, &ret); binning_mode = ((mode->crop.width / mode->width) << 4) | (mode->crop.height / mode->height); - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_MODE, 1, - binning_mode == 0x11 ? 0x00 : 0x01); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_TYPE, 1, - binning_mode); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_BINNING_WEIGHTING, 1, 0x00); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, + binning_mode == 0x11 ? 0x00 : 0x01, &ret); + cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); + cci_write(imx355->regmap, IMX355_REG_BINNING_WEIGHTING, 0x00, &ret); /* Set PLL registers for the external clock frequency */ - ret = imx355_write_reg(imx355, IMX355_REG_EXTCLK_FREQ, 2, - imx355->clk_params->extclk_freq); - if (ret) - return ret; - ret = imx355_write_reg(imx355, IMX355_REG_PLL_OP_MUL, 2, - imx355->clk_params->pll_op_mpy); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, + imx355->clk_params->extclk_freq, &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, + imx355->clk_params->pll_op_mpy, &ret); /* set digital gain control to all color mode */ - ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); /* set line length */ - ret = imx355_write_reg(imx355, IMX355_REG_LLP, 2, - imx355->hblank->val + imx355->cur_mode->width); - if (ret) - return ret; + cci_write(imx355->regmap, IMX355_REG_LLP, + imx355->hblank->val + imx355->cur_mode->width, &ret); /* Apply customized values from user */ - ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); - if (ret) - return ret; + if (!ret) + ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + + cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, IMX355_MODE_STREAMING, + &ret); - return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, - 1, IMX355_MODE_STREAMING); + return ret; } /* Stop streaming */ static int imx355_stop_streaming(struct imx355 *imx355) { - return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, - 1, IMX355_MODE_STANDBY); + return cci_write(imx355->regmap, IMX355_REG_MODE_SELECT, + IMX355_MODE_STANDBY, NULL); } static int imx355_set_stream(struct v4l2_subdev *sd, int enable) @@ -1097,14 +982,14 @@ err_unlock: static int imx355_identify_module(struct imx355 *imx355) { int ret; - u32 val; + u64 val; - ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); + ret = cci_read(imx355->regmap, IMX355_REG_CHIP_ID, &val, NULL); if (ret) return ret; if (val != IMX355_CHIP_ID) { - dev_err(imx355->dev, "chip id mismatch: %x!=%x", + dev_err(imx355->dev, "chip id mismatch: %x!=%llx", IMX355_CHIP_ID, val); return -EIO; } @@ -1351,6 +1236,11 @@ static int imx355_probe(struct i2c_client *client) mutex_init(&imx355->mutex); + imx355->regmap = devm_cci_regmap_init_i2c(client, 16); + if (IS_ERR(imx355->regmap)) + return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), + "Unable to initialize I2C\n"); + imx355->clk = devm_v4l2_sensor_clk_get(imx355->dev, NULL); if (IS_ERR(imx355->clk)) return dev_err_probe(imx355->dev, PTR_ERR(imx355->clk), -- cgit From ba533bb043a8af516f863d5319daa57ec475c178 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:28 +0100 Subject: media: imx355: Set the colorspace in the format The colorspace fields were left set to 0 when they should be set appropriately for a raw image sensor. Add those values. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 52bdfe636231..fa8a6851b44e 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -612,6 +612,10 @@ static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) try_fmt->height = imx355->cur_mode->height; try_fmt->code = imx355_get_format_code(imx355); try_fmt->field = V4L2_FIELD_NONE; + try_fmt->colorspace = V4L2_COLORSPACE_RAW; + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; + try_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; + try_fmt->xfer_func = V4L2_XFER_FUNC_NONE; *crop = imx355->cur_mode->crop; @@ -738,6 +742,10 @@ static void imx355_update_pad_format(struct imx355 *imx355, fmt->format.height = mode->height; fmt->format.code = imx355_get_format_code(imx355); fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } static int imx355_do_get_pad_format(struct imx355 *imx355, -- cgit From f31cb77bb1d141eab71d4aae96f5fa51efcd3ca0 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:29 +0100 Subject: media: imx355: Define the exposure offset, and use that define The difference between frame length and max exposure time in lines is 10. That had been used as a bare value in calculations. Move it to a define, and use that define. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index fa8a6851b44e..cc27a193cd81 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -51,6 +51,7 @@ #define IMX355_EXPOSURE_MIN 1 #define IMX355_EXPOSURE_STEP 1 #define IMX355_EXPOSURE_DEFAULT 0x0282 +#define IMX355_EXPOSURE_OFFSET 10 /* Analog gain control */ #define IMX355_REG_ANALOG_GAIN CCI_REG16(0x0204) @@ -635,7 +636,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = imx355->cur_mode->height + ctrl->val - 10; + max = imx355->cur_mode->height + ctrl->val - IMX355_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -1130,7 +1131,7 @@ static int imx355_init_controls(struct imx355 *imx355) imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* fll >= exposure time + adjust parameter (default value is 10) */ - exposure_max = mode->fll_def - 10; + exposure_max = mode->fll_def - IMX355_EXPOSURE_OFFSET; imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_EXPOSURE, IMX355_EXPOSURE_MIN, exposure_max, -- cgit From 2ae189fc1c2cdc53f6fa525c01b1026db01cc532 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:30 +0100 Subject: media: imx355: Use NULL ctrl_ops for HBLANK as it is a read-only control This avoids the set_ctrl handler being called under any circumstances, as it will return an error for the unhandled ctrl. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index cc27a193cd81..3b3bf41485c2 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -1124,9 +1124,8 @@ static int imx355_init_controls(struct imx355 *imx355) 1, vblank_def); hblank = mode->llp - mode->width; - imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_HBLANK, hblank, hblank, - 1, hblank); + imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_HBLANK, + hblank, hblank, 1, hblank); if (imx355->hblank) imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; -- cgit From b8d87e9bdb8815b15e097765b65bca2ec857155d Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:31 +0100 Subject: media: imx355: Compute link frequency from PLL setup In preparation for additional options in the PLL setup, compute the link frequency rather than using a hardcoded value. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 3b3bf41485c2..d8f8efc0c927 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -29,7 +29,11 @@ /* PLL registers that depend on the external clock frequency */ #define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) +#define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) #define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) +#define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) +#define IMX355_PLL_OP_PREDIV 2 +#define IMX355_PLL_IVT_PCK_DIV 5 /* V_TIMING internal */ #define IMX355_REG_FLL CCI_REG16(0x0340) @@ -83,9 +87,6 @@ /* Flip Control */ #define IMX355_REG_ORIENTATION CCI_REG8(0x0101) -/* default link frequency and external clock */ -#define IMX355_LINK_FREQ_DEFAULT 360000000LL - /* number of data lanes */ #define IMX355_DATA_LANES 4 @@ -141,6 +142,7 @@ static const struct imx355_clk_params imx355_clk_params[] = { }; struct imx355_hwcfg { + s64 link_freq_menu; unsigned long link_freq_bitmap; }; @@ -238,13 +240,13 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, { CCI_REG8(0x0114), 0x03 }, - { CCI_REG8(0x0301), 0x05 }, + { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, { CCI_REG8(0x0303), 0x01 }, { CCI_REG8(0x0305), 0x02 }, { CCI_REG8(0x0306), 0x00 }, { CCI_REG8(0x0307), 0x78 }, { CCI_REG8(0x030b), 0x01 }, - { CCI_REG8(0x030d), 0x02 }, + { IMX355_REG_PLL_OP_PREDIV, IMX355_PLL_OP_PREDIV }, { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, @@ -338,14 +340,6 @@ static const char * const imx355_test_pattern_menu[] = { "Pseudorandom Sequence (PN9)", }; -/* - * When adding more than the one below, make sure the disallowed ones will - * actually be disabled in the LINK_FREQ control. - */ -static const s64 link_freq_menu_items[] = { - IMX355_LINK_FREQ_DEFAULT, -}; - /* Mode configs */ static const struct imx355_mode supported_modes[] = { { @@ -1092,7 +1086,6 @@ static int imx355_init_controls(struct imx355 *imx355) s64 hblank; u64 pixel_rate; const struct imx355_mode *mode; - u32 max; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1101,15 +1094,14 @@ static int imx355_init_controls(struct imx355 *imx355) return ret; ctrl_hdlr->lock = &imx355->mutex; - max = ARRAY_SIZE(link_freq_menu_items) - 1; imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, - V4L2_CID_LINK_FREQ, max, 0, - link_freq_menu_items); + V4L2_CID_LINK_FREQ, 0, 0, + &imx355->hwcfg->link_freq_menu); if (imx355->link_freq) imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = IMX355_LINK_FREQ_DEFAULT * 2 * 4; + pixel_rate = imx355->hwcfg->link_freq_menu * 2 * 4; do_div(pixel_rate, 10); /* By default, PIXEL_RATE is read only */ v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, @@ -1184,12 +1176,14 @@ error: return ret; } -static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) +static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) { + struct device *dev = imx355->dev; struct imx355_hwcfg *cfg; struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY }; + const struct imx355_clk_params *clk = imx355->clk_params; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); int ret; @@ -1212,10 +1206,11 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) goto out_err; + cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy) / + (IMX355_PLL_OP_PREDIV * 2); ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, - link_freq_menu_items, - ARRAY_SIZE(link_freq_menu_items), + &cfg->link_freq_menu, 1, &cfg->link_freq_bitmap); if (ret) goto out_err; @@ -1286,7 +1281,7 @@ static int imx355_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); - imx355->hwcfg = imx355_get_hwcfg(imx355->dev); + imx355->hwcfg = imx355_get_hwcfg(imx355); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); ret = -ENODEV; -- cgit From 7c3aad6fbaff06694a77b46ec8600bee6c0b7f42 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:32 +0100 Subject: media: imx355: Support 2 lane readout The sensor supports 2 or 4 lane readout, but the driver only allowed for 4 lanes. Add 2 lane support. The clock tree only supports single PLL mode to feed both IOP (MIPI) and IVT (Pixel array). 2 lane mode supports a MIPI link frequency of up to 445MHz (890Mbit/s) cf 360MHz (720Mbit/s) for 4lane. Update clock setup and pixel rates to match. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 67 +++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index d8f8efc0c927..0462e0feee36 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -27,11 +27,14 @@ #define IMX355_REG_CHIP_ID CCI_REG16(0x0016) #define IMX355_CHIP_ID 0x0355 +#define IMX355_REG_LANE_SEL CCI_REG8(0x0114) + /* PLL registers that depend on the external clock frequency */ #define IMX355_REG_EXTCLK_FREQ CCI_REG16(0x0136) #define IMX355_REG_PLL_OP_PREDIV CCI_REG8(0x030d) #define IMX355_REG_PLL_OP_MUL CCI_REG16(0x030e) #define IMX355_REG_PLL_IVT_PCK_DIV CCI_REG8(0x0301) +#define IMX355_REG_PLL_IVT_SYSCK_DIV CCI_REG8(0x0303) #define IMX355_PLL_OP_PREDIV 2 #define IMX355_PLL_IVT_PCK_DIV 5 @@ -80,6 +83,8 @@ #define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 #define IMX355_TEST_PATTERN_PN9 4 +#define IMX355_REG_REQ_LINK_BIT_RATE CCI_REG16(0x0820) + #define IMX355_REG_BINNING_MODE CCI_REG8(0x0900) #define IMX355_REG_BINNING_TYPE CCI_REG8(0x0901) #define IMX355_REG_BINNING_WEIGHTING CCI_REG8(0x0902) @@ -87,9 +92,6 @@ /* Flip Control */ #define IMX355_REG_ORIENTATION CCI_REG8(0x0101) -/* number of data lanes */ -#define IMX355_DATA_LANES 4 - #define IMX355_PIXEL_ARRAY_TOP 0 #define IMX355_PIXEL_ARRAY_LEFT 0 #define IMX355_PIXEL_ARRAY_WIDTH 3280 @@ -120,30 +122,38 @@ struct imx355_mode { struct imx355_clk_params { u32 ext_clk; - u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ - u16 pll_op_mpy; /* OP system PLL multiplier */ + u16 extclk_freq; /* External clock (MHz) in 8.8 fixed point) */ + u16 pll_op_mpy[2]; /* OP system PLL multiplier */ + u8 pll_op_prediv[2]; /* OP system pre PLL d */ }; /* * The clock tree is in single PLL mode, so PREDIV_VT and MPY_IVT do nothing. - * All modes use the same PLL setup for OP, with IOPCK being 720MHz. + * In 4 lane mode the MIPI rate is 360Mhz (720Mbit/s) and pixel rate is + * 288MPix/s. + * In 2 lane mode the MIPI rate is 444MHz (888Mbit/s) and pixel rate + * 177.6MPix/s with a 24MHz clock, and 441.6MHz (883.2Mbit/s) and 176.6MPix/s + * with a 19.2MHz clock. */ static const struct imx355_clk_params imx355_clk_params[] = { { .ext_clk = 19200000, - .extclk_freq = 0x1333, /* 19.2 MHz */ - .pll_op_mpy = 75, /* 19.2 / 2 * 75 = 720 MHz */ + .extclk_freq = 0x1333, + .pll_op_mpy = { 75, 92 }, + .pll_op_prediv = { 2, 2 } }, { .ext_clk = 24000000, - .extclk_freq = 0x1800, /* 24.0 MHz */ - .pll_op_mpy = 60, /* 24.0 / 2 * 60 = 720 MHz */ + .extclk_freq = 0x1800, + .pll_op_mpy = { 60, 111 }, + .pll_op_prediv = { 2, 3 } }, }; struct imx355_hwcfg { s64 link_freq_menu; unsigned long link_freq_bitmap; + unsigned int num_lanes; }; struct imx355 { @@ -239,7 +249,6 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x305a), 0x00 }, { CCI_REG8(0x0112), 0x0a }, { CCI_REG8(0x0113), 0x0a }, - { CCI_REG8(0x0114), 0x03 }, { IMX355_REG_PLL_IVT_PCK_DIV, IMX355_PLL_IVT_PCK_DIV }, { CCI_REG8(0x0303), 0x01 }, { CCI_REG8(0x0305), 0x02 }, @@ -250,8 +259,6 @@ static const struct cci_reg_sequence imx355_global_regs[] = { { CCI_REG8(0x0310), 0x00 }, { CCI_REG8(0x0220), 0x00 }, { CCI_REG8(0x0222), 0x01 }, - { CCI_REG8(0x0820), 0x0b }, - { CCI_REG8(0x0821), 0x40 }, { CCI_REG8(0x3088), 0x04 }, { CCI_REG8(0x6813), 0x02 }, { CCI_REG8(0x6835), 0x07 }, @@ -817,6 +824,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, height, 1, vblank_def); __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); + h_blank = mode->llp - imx355->cur_mode->width; /* * Currently hblank is not changeable. @@ -880,6 +888,8 @@ static int imx355_start_streaming(struct imx355 *imx355) { const struct imx355_reg_list *reg_list; const struct imx355_mode *mode; + int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; + u64 link_bitrate; u8 binning_mode; int ret = 0; @@ -915,7 +925,21 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_write(imx355->regmap, IMX355_REG_EXTCLK_FREQ, imx355->clk_params->extclk_freq, &ret); cci_write(imx355->regmap, IMX355_REG_PLL_OP_MUL, - imx355->clk_params->pll_op_mpy, &ret); + imx355->clk_params->pll_op_mpy[lane_idx], &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_OP_PREDIV, + imx355->clk_params->pll_op_prediv[lane_idx], &ret); + cci_write(imx355->regmap, IMX355_REG_PLL_IVT_SYSCK_DIV, + lane_idx ? 2 : 1, &ret); + + /* Set MIPI configuration */ + cci_write(imx355->regmap, IMX355_REG_LANE_SEL, + imx355->hwcfg->num_lanes - 1, &ret); + + link_bitrate = imx355->link_freq->qmenu_int[imx355->link_freq->val] * + imx355->hwcfg->num_lanes * 2; + do_div(link_bitrate, 1000000); + cci_write(imx355->regmap, IMX355_REG_REQ_LINK_BIT_RATE, link_bitrate, + &ret); /* set digital gain control to all color mode */ cci_write(imx355->regmap, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, &ret); @@ -1101,9 +1125,9 @@ static int imx355_init_controls(struct imx355 *imx355) imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = imx355->hwcfg->link_freq_menu * 2 * 4; + pixel_rate = imx355->hwcfg->link_freq_menu * 2 * imx355->hwcfg->num_lanes; do_div(pixel_rate, 10); - /* By default, PIXEL_RATE is read only */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_PIXEL_RATE, pixel_rate, pixel_rate, 1, pixel_rate); @@ -1186,6 +1210,7 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) const struct imx355_clk_params *clk = imx355->clk_params; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); + int lane_idx; int ret; if (!fwnode) @@ -1203,11 +1228,15 @@ static struct imx355_hwcfg *imx355_get_hwcfg(struct imx355 *imx355) if (!cfg) goto out_err; - if (bus_cfg.bus.mipi_csi2.num_data_lanes != IMX355_DATA_LANES) + if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && + bus_cfg.bus.mipi_csi2.num_data_lanes != 4) goto out_err; - cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy) / - (IMX355_PLL_OP_PREDIV * 2); + cfg->num_lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + + lane_idx = cfg->num_lanes == 4 ? 0 : 1; + cfg->link_freq_menu = (clk->ext_clk * clk->pll_op_mpy[lane_idx]) / + (clk->pll_op_prediv[lane_idx] * 2); ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies, bus_cfg.nr_of_link_frequencies, &cfg->link_freq_menu, 1, -- cgit From 9f36cdacfdc4f91f2f2902ad073bc9e9fbc60ae8 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:33 +0100 Subject: media: imx355: Switch to using the subdev state The subdev state is now preferred rather than handling crop and format within the driver state, so switch the driver to using it. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 183 ++++++++++++++------------------------------- 1 file changed, 55 insertions(+), 128 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 0462e0feee36..97f4594f7ed9 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -179,13 +179,6 @@ struct imx355 { struct imx355_hwcfg *hwcfg; const struct imx355_clk_params *clk_params; - /* - * Mutex for serialized access: - * Protect sensor set pad format and start/stop streaming safely. - * Protect access to sensor v4l2 controls. - */ - struct mutex mutex; - struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; }; @@ -593,46 +586,23 @@ static u32 imx355_get_format_code(struct imx355 *imx355) { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, }; - lockdep_assert_held(&imx355->mutex); code = codes[imx355->vflip->val][imx355->hflip->val]; return code; } -/* Open sub-device */ -static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct imx355 *imx355 = to_imx355(sd); - struct v4l2_mbus_framefmt *try_fmt = - v4l2_subdev_state_get_format(fh->state, 0); - struct v4l2_rect *crop = v4l2_subdev_state_get_crop(fh->state, 0); - - mutex_lock(&imx355->mutex); - - /* Initialize try_fmt */ - try_fmt->width = imx355->cur_mode->width; - try_fmt->height = imx355->cur_mode->height; - try_fmt->code = imx355_get_format_code(imx355); - try_fmt->field = V4L2_FIELD_NONE; - try_fmt->colorspace = V4L2_COLORSPACE_RAW; - try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601; - try_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - try_fmt->xfer_func = V4L2_XFER_FUNC_NONE; - - *crop = imx355->cur_mode->crop; - - mutex_unlock(&imx355->mutex); - - return 0; -} - static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) { struct imx355 *imx355 = container_of(ctrl->handler, struct imx355, ctrl_handler); + const struct v4l2_mbus_framefmt *format = NULL; + struct v4l2_subdev_state *state; s64 max; int ret; + state = v4l2_subdev_get_locked_active_state(&imx355->sd); + format = v4l2_subdev_state_get_format(state, 0); + /* Propagate change of current control to all related controls */ switch (ctrl->id) { case V4L2_CID_VBLANK: @@ -705,9 +675,7 @@ static int imx355_enum_mbus_code(struct v4l2_subdev *sd, if (code->index > 0) return -EINVAL; - mutex_lock(&imx355->mutex); code->code = imx355_get_format_code(imx355); - mutex_unlock(&imx355->mutex); return 0; } @@ -721,12 +689,9 @@ static int imx355_enum_frame_size(struct v4l2_subdev *sd, if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - mutex_lock(&imx355->mutex); if (fse->code != imx355_get_format_code(imx355)) { - mutex_unlock(&imx355->mutex); return -EINVAL; } - mutex_unlock(&imx355->mutex); fse->min_width = supported_modes[fse->index].width; fse->max_width = fse->min_width; @@ -750,36 +715,6 @@ static void imx355_update_pad_format(struct imx355 *imx355, fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; } -static int imx355_do_get_pad_format(struct imx355 *imx355, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - fmt->format = *framefmt; - } else { - imx355_update_pad_format(imx355, imx355->cur_mode, fmt); - } - - return 0; -} - -static int imx355_get_pad_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct imx355 *imx355 = to_imx355(sd); - int ret; - - mutex_lock(&imx355->mutex); - ret = imx355_do_get_pad_format(imx355, sd_state, fmt); - mutex_unlock(&imx355->mutex); - - return ret; -} - static int imx355_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, @@ -793,8 +728,6 @@ imx355_set_pad_format(struct v4l2_subdev *sd, s64 h_blank; u32 height; - mutex_lock(&imx355->mutex); - /* * Only one bayer order is supported. * It depends on the flip settings. @@ -806,16 +739,17 @@ imx355_set_pad_format(struct v4l2_subdev *sd, width, height, fmt->format.width, fmt->format.height); imx355_update_pad_format(imx355, mode, fmt); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad); - *framefmt = fmt->format; - - crop = v4l2_subdev_state_get_crop(sd_state, 0); - crop->width = mode->crop.width; - crop->height = mode->crop.height; - crop->left = mode->crop.left; - crop->top = mode->crop.top; - } else { + framefmt = v4l2_subdev_state_get_format(sd_state, 0); + + *framefmt = fmt->format; + + crop = v4l2_subdev_state_get_crop(sd_state, 0); + crop->width = mode->crop.width; + crop->height = mode->crop.height; + crop->left = mode->crop.left; + crop->top = mode->crop.top; + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { imx355->cur_mode = mode; /* Update limits and set FPS to default */ height = imx355->cur_mode->height; @@ -834,41 +768,17 @@ imx355_set_pad_format(struct v4l2_subdev *sd, h_blank, 1, h_blank); } - mutex_unlock(&imx355->mutex); - return 0; } -static void -__imx355_get_pad_crop(struct imx355 *imx355, - struct v4l2_subdev_state *sd_state, unsigned int pad, - enum v4l2_subdev_format_whence which, struct v4l2_rect *r) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - *r = *v4l2_subdev_state_get_crop(sd_state, pad); - break; - case V4L2_SUBDEV_FORMAT_ACTIVE: - *r = imx355->cur_mode->crop; - break; - } -} - static int imx355_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { switch (sel->target) { - case V4L2_SEL_TGT_CROP: { - struct imx355 *imx355 = to_imx355(sd); - - mutex_lock(&imx355->mutex); - __imx355_get_pad_crop(imx355, sd_state, sel->pad, sel->which, - &sel->r); - mutex_unlock(&imx355->mutex); - + case V4L2_SEL_TGT_CROP: + sel->r = *v4l2_subdev_state_get_crop(sd_state, 0); return 0; - } case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP_BOUNDS: case V4L2_SEL_TGT_NATIVE_SIZE: @@ -883,6 +793,21 @@ static int imx355_get_selection(struct v4l2_subdev *sd, return -EINVAL; } +static int imx355_entity_init_state(struct v4l2_subdev *subdev, + struct v4l2_subdev_state *sd_state) +{ + struct v4l2_subdev_format fmt = { }; + + fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.code = MEDIA_BUS_FMT_SRGGB10_1X10; + fmt.format.width = supported_modes[0].width; + fmt.format.height = supported_modes[0].height; + + imx355_set_pad_format(subdev, sd_state, &fmt); + + return 0; +} + /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { @@ -968,9 +893,10 @@ static int imx355_stop_streaming(struct imx355 *imx355) static int imx355_set_stream(struct v4l2_subdev *sd, int enable) { struct imx355 *imx355 = to_imx355(sd); + struct v4l2_subdev_state *state; int ret = 0; - mutex_lock(&imx355->mutex); + state = v4l2_subdev_lock_and_get_active_state(sd); if (enable) { ret = pm_runtime_resume_and_get(imx355->dev); @@ -993,14 +919,14 @@ static int imx355_set_stream(struct v4l2_subdev *sd, int enable) __v4l2_ctrl_grab(imx355->vflip, enable); __v4l2_ctrl_grab(imx355->hflip, enable); - mutex_unlock(&imx355->mutex); + v4l2_subdev_unlock_state(state); return ret; err_rpm_put: pm_runtime_put_autosuspend(imx355->dev); err_unlock: - mutex_unlock(&imx355->mutex); + v4l2_subdev_unlock_state(state); return ret; } @@ -1034,7 +960,7 @@ static const struct v4l2_subdev_video_ops imx355_video_ops = { static const struct v4l2_subdev_pad_ops imx355_pad_ops = { .enum_mbus_code = imx355_enum_mbus_code, - .get_fmt = imx355_get_pad_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx355_set_pad_format, .enum_frame_size = imx355_enum_frame_size, .get_selection = imx355_get_selection, @@ -1051,7 +977,7 @@ static const struct media_entity_operations imx355_subdev_entity_ops = { }; static const struct v4l2_subdev_internal_ops imx355_internal_ops = { - .open = imx355_open, + .init_state = imx355_entity_init_state, }; static int imx355_power_off(struct device *dev) @@ -1117,7 +1043,6 @@ static int imx355_init_controls(struct imx355 *imx355) if (ret) return ret; - ctrl_hdlr->lock = &imx355->mutex; imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_LINK_FREQ, 0, 0, &imx355->hwcfg->link_freq_menu); @@ -1266,8 +1191,6 @@ static int imx355_probe(struct i2c_client *client) imx355->dev = &client->dev; - mutex_init(&imx355->mutex); - imx355->regmap = devm_cci_regmap_init_i2c(client, 16); if (IS_ERR(imx355->regmap)) return dev_err_probe(imx355->dev, PTR_ERR(imx355->regmap), @@ -1296,7 +1219,7 @@ static int imx355_probe(struct i2c_client *client) &imx355->supplies); if (ret) { dev_err_probe(imx355->dev, ret, "could not get regulators"); - goto error_probe; + return ret; } imx355->reset_gpio = devm_gpiod_get_optional(imx355->dev, "reset", @@ -1304,7 +1227,7 @@ static int imx355_probe(struct i2c_client *client) if (IS_ERR(imx355->reset_gpio)) { ret = dev_err_probe(imx355->dev, PTR_ERR(imx355->reset_gpio), "failed to get gpios"); - goto error_probe; + return ret; } /* Initialize subdev */ @@ -1313,13 +1236,12 @@ static int imx355_probe(struct i2c_client *client) imx355->hwcfg = imx355_get_hwcfg(imx355); if (!imx355->hwcfg) { dev_err(imx355->dev, "failed to get hwcfg"); - ret = -ENODEV; - goto error_probe; + return -ENODEV; } ret = imx355_power_on(imx355->dev); if (ret) - goto error_probe; + return ret; /* Check module identity */ ret = imx355_identify_module(imx355); @@ -1352,6 +1274,13 @@ static int imx355_probe(struct i2c_client *client) goto error_handler_free; } + imx355->sd.state_lock = imx355->ctrl_handler.lock; + ret = v4l2_subdev_init_finalize(&imx355->sd); + if (ret < 0) { + dev_err_probe(imx355->dev, ret, "subdev init error\n"); + goto error_media_entity_free; + } + /* * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. @@ -1363,16 +1292,18 @@ static int imx355_probe(struct i2c_client *client) ret = v4l2_async_register_subdev_sensor(&imx355->sd); if (ret < 0) - goto error_media_entity_runtime_pm; + goto error_subdev_cleanup_runtime_pm; pm_runtime_idle(imx355->dev); return 0; -error_media_entity_runtime_pm: +error_subdev_cleanup_runtime_pm: pm_runtime_disable(imx355->dev); pm_runtime_set_suspended(imx355->dev); pm_runtime_dont_use_autosuspend(imx355->dev); + v4l2_subdev_cleanup(&imx355->sd); +error_media_entity_free: media_entity_cleanup(&imx355->sd.entity); error_handler_free: @@ -1381,9 +1312,6 @@ error_handler_free: error_power_off: imx355_power_off(imx355->dev); -error_probe: - mutex_destroy(&imx355->mutex); - return ret; } @@ -1393,6 +1321,7 @@ static void imx355_remove(struct i2c_client *client) struct imx355 *imx355 = to_imx355(sd); v4l2_async_unregister_subdev(sd); + v4l2_subdev_cleanup(sd); media_entity_cleanup(&sd->entity); v4l2_ctrl_handler_free(sd->ctrl_handler); @@ -1404,8 +1333,6 @@ static void imx355_remove(struct i2c_client *client) } pm_runtime_dont_use_autosuspend(imx355->dev); - - mutex_destroy(&imx355->mutex); } static const struct acpi_device_id imx355_acpi_ids[] __maybe_unused = { -- cgit From b7f17b9fe0ec14607a7acd0141a1ee00e967a0de Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Wed, 15 Jul 2026 12:43:34 +0100 Subject: media: imx355: Remove storing cur_mode in the state All the information for the mode is now stored within the subdev state, so configure the sensor based on that. Signed-off-by: Dave Stevenson Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx355.c | 63 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 97f4594f7ed9..8eb8588cb71b 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -173,9 +173,6 @@ struct imx355 { struct v4l2_ctrl *vflip; struct v4l2_ctrl *hflip; - /* Current mode */ - const struct imx355_mode *cur_mode; - struct imx355_hwcfg *hwcfg; const struct imx355_clk_params *clk_params; @@ -607,7 +604,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - max = imx355->cur_mode->height + ctrl->val - IMX355_EXPOSURE_OFFSET; + max = format->height + ctrl->val - IMX355_EXPOSURE_OFFSET; __v4l2_ctrl_modify_range(imx355->exposure, imx355->exposure->minimum, max, imx355->exposure->step, max); @@ -638,7 +635,7 @@ static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ ret = cci_write(imx355->regmap, IMX355_REG_FLL, - imx355->cur_mode->height + ctrl->val, NULL); + format->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: ret = cci_write(imx355->regmap, IMX355_REG_TEST_PATTERN, @@ -724,9 +721,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd, const struct imx355_mode *mode; struct v4l2_mbus_framefmt *framefmt; struct v4l2_rect *crop; - s32 vblank_def; s64 h_blank; - u32 height; /* * Only one bayer order is supported. @@ -750,16 +745,14 @@ imx355_set_pad_format(struct v4l2_subdev *sd, crop->top = mode->crop.top; if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - imx355->cur_mode = mode; /* Update limits and set FPS to default */ - height = imx355->cur_mode->height; - vblank_def = imx355->cur_mode->fll_def - height; - height = IMX355_FLL_MAX - height; __v4l2_ctrl_modify_range(imx355->vblank, IMX355_VBLANK_MIN, - height, 1, vblank_def); - __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); + IMX355_FLL_MAX - mode->height, 1, + mode->fll_def - mode->height); + __v4l2_ctrl_s_ctrl(imx355->vblank, mode->fll_def - mode->height); + + h_blank = mode->llp - mode->width; - h_blank = mode->llp - imx355->cur_mode->width; /* * Currently hblank is not changeable. * So FPS control is done only by vblank. @@ -811,9 +804,11 @@ static int imx355_entity_init_state(struct v4l2_subdev *subdev, /* Start streaming */ static int imx355_start_streaming(struct imx355 *imx355) { - const struct imx355_reg_list *reg_list; + const struct v4l2_mbus_framefmt *fmt; + struct v4l2_subdev_state *state; const struct imx355_mode *mode; int lane_idx = imx355->hwcfg->num_lanes == 4 ? 0 : 1; + struct v4l2_rect *crop; u64 link_bitrate; u8 binning_mode; int ret = 0; @@ -822,25 +817,29 @@ static int imx355_start_streaming(struct imx355 *imx355) cci_multi_reg_write(imx355->regmap, imx355_global_regs, ARRAY_SIZE(imx355_global_regs), &ret); - /* Apply default values of current mode */ - mode = imx355->cur_mode; - reg_list = &mode->reg_list; - cci_multi_reg_write(imx355->regmap, reg_list->regs, - reg_list->num_of_regs, &ret); + /* Apply values of current mode */ + state = v4l2_subdev_get_locked_active_state(&imx355->sd); + fmt = v4l2_subdev_state_get_format(state, 0); + crop = v4l2_subdev_state_get_crop(state, 0); + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, fmt->width, fmt->height); + cci_multi_reg_write(imx355->regmap, mode->reg_list.regs, + mode->reg_list.num_of_regs, &ret); /* Set readout crop and size registers */ - cci_write(imx355->regmap, IMX355_REG_X_ADD_START, mode->crop.left, + cci_write(imx355->regmap, IMX355_REG_X_ADD_START, crop->left, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, mode->crop.top, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_ADD_START, crop->top, &ret); cci_write(imx355->regmap, IMX355_REG_X_ADD_END, - mode->crop.width + mode->crop.left - 1, &ret); + crop->width + crop->left - 1, &ret); cci_write(imx355->regmap, IMX355_REG_Y_ADD_END, - mode->crop.height + mode->crop.top - 1, &ret); - cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, mode->width, &ret); - cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, mode->height, &ret); + crop->height + crop->top - 1, &ret); + cci_write(imx355->regmap, IMX355_REG_X_OUT_SIZE, fmt->width, &ret); + cci_write(imx355->regmap, IMX355_REG_Y_OUT_SIZE, fmt->height, &ret); - binning_mode = ((mode->crop.width / mode->width) << 4) | - (mode->crop.height / mode->height); + binning_mode = ((crop->width / fmt->width) << 4) | + (crop->height / fmt->height); cci_write(imx355->regmap, IMX355_REG_BINNING_MODE, binning_mode == 0x11 ? 0x00 : 0x01, &ret); cci_write(imx355->regmap, IMX355_REG_BINNING_TYPE, binning_mode, &ret); @@ -871,7 +870,7 @@ static int imx355_start_streaming(struct imx355 *imx355) /* set line length */ cci_write(imx355->regmap, IMX355_REG_LLP, - imx355->hblank->val + imx355->cur_mode->width, &ret); + imx355->hblank->val + fmt->width, &ret); /* Apply customized values from user */ if (!ret) @@ -1031,11 +1030,11 @@ static int imx355_init_controls(struct imx355 *imx355) { struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; + const struct imx355_mode *mode = &supported_modes[0]; s64 exposure_max; s64 vblank_def; s64 hblank; u64 pixel_rate; - const struct imx355_mode *mode; int ret; ctrl_hdlr = &imx355->ctrl_handler; @@ -1057,7 +1056,6 @@ static int imx355_init_controls(struct imx355 *imx355) pixel_rate, pixel_rate, 1, pixel_rate); /* Initialize vblank/hblank/exposure parameters based on current mode */ - mode = imx355->cur_mode; vblank_def = mode->fll_def - mode->height; imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_VBLANK, IMX355_VBLANK_MIN, @@ -1250,9 +1248,6 @@ static int imx355_probe(struct i2c_client *client) goto error_power_off; } - /* Set default mode to max resolution */ - imx355->cur_mode = &supported_modes[0]; - ret = imx355_init_controls(imx355); if (ret) { dev_err(imx355->dev, "failed to init controls: %d", ret); -- cgit From 635590417a89466234aa8a8c47a63274fb7fc523 Mon Sep 17 00:00:00 2001 From: André Moreira Date: Sat, 20 Jun 2026 14:21:38 -0300 Subject: staging: media: av7110: refactor av7110_start_feed to reduce indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The av7110_start_feed function contains heavily nested if-statements, causing excessive indentation and violating code style guidelines. Refactor the logic inside the DMX_MEMORY_FE case by inverting the conditional check to break early. Additionally, remove the inner 'if (feed->ts_type & TS_DECODER)' check, as it is redundant since the outer block already validates this condition. This reduces the indentation level and cleans up redundant checks without altering the underlying driver behavior. Signed-off-by: André Moreira Signed-off-by: Hans Verkuil --- drivers/staging/media/av7110/av7110.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c index 862aee993889..06c43b5f8a87 100644 --- a/drivers/staging/media/av7110/av7110.c +++ b/drivers/staging/media/av7110/av7110.c @@ -942,16 +942,16 @@ static int av7110_start_feed(struct dvb_demux_feed *feed) (feed->pes_type <= DMX_PES_PCR)) { switch (demux->dmx.frontend->source) { case DMX_MEMORY_FE: - if (feed->ts_type & TS_DECODER) - if (feed->pes_type < 2 && - !(demux->pids[0] & 0x8000) && - !(demux->pids[1] & 0x8000)) { - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); - dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); - ret = av7110_av_start_play(av7110, RP_AV); - if (!ret) - demux->playing = 1; - } + if (feed->pes_type >= 2 || + (demux->pids[0] & 0x8000) || + (demux->pids[1] & 0x8000)) + break; + + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->avout); + dvb_ringbuffer_flush_spinlock_wakeup(&av7110->aout); + ret = av7110_av_start_play(av7110, RP_AV); + if (!ret) + demux->playing = 1; break; default: ret = dvb_feed_start_pid(feed); -- cgit From e1880f52cbc69de83e9d22873b83cd38d20da5a2 Mon Sep 17 00:00:00 2001 From: "Uwe Kleine-König (The Capable Hub)" Date: Wed, 24 Jun 2026 10:34:48 +0200 Subject: media: ti: vpe: #include explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver uses several symbols and structs defined in that header. The header is currently included transitively via "vip.h" -> -> -> -> which seems to be on the lower end of the scale between random and reliable. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index cb0a5a07a3d4..e56a95f53ea9 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include -- cgit From dd5f981d0256893dd38dbee44c5f03fc6c084313 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 25 Jun 2026 11:33:02 +0300 Subject: staging: media: tegra-video: vi: Improve media graph building logic The existing tegra_vi_graph_build function relies heavily on a one-to-one match between Device Tree nodes and media pad links. While this works for simpler configurations, it causes issues when Device Tree nodes do not match media pad link logic (e.g., mt9m114). Switch to the media_entity_get_fwnode_pad helper to verify and retrieve the correct pad linked to an endpoint, rather than assuming the endpoint ID matches the pad ID. Tested-by: Luca Ceresoli # tegra20, parallel camera Signed-off-by: Svyatoslav Ryhel Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 78 +++++++++++++++------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index 456134a9e8cf..ce09949178f6 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1468,7 +1468,6 @@ static int tegra_vi_graph_build(struct tegra_vi_channel *chan, struct tegra_vi *vi = chan->vi; struct tegra_vi_graph_entity *ent; struct fwnode_handle *ep = NULL; - struct v4l2_fwnode_link link; struct media_entity *local = entity->entity; struct media_entity *remote; struct media_pad *local_pad; @@ -1478,70 +1477,64 @@ static int tegra_vi_graph_build(struct tegra_vi_channel *chan, dev_dbg(vi->dev, "creating links for entity %s\n", local->name); - while (1) { - ep = fwnode_graph_get_next_endpoint(entity->asd.match.fwnode, - ep); - if (!ep) - break; + fwnode_graph_for_each_endpoint(entity->asd.match.fwnode, ep) { + struct fwnode_handle *remote_parent __free(fwnode_handle) = NULL; + struct fwnode_handle *sink_ep __free(fwnode_handle) = NULL; + int src_idx, sink_idx; - ret = v4l2_fwnode_parse_link(ep, &link); - if (ret < 0) { - dev_err(vi->dev, "failed to parse link for %pOF: %d\n", - to_of_node(ep), ret); + src_idx = media_entity_get_fwnode_pad(local, ep, + MEDIA_PAD_FL_SOURCE); + if (src_idx < 0) { + dev_dbg(vi->dev, "no source pad found for %pfw\n", ep); continue; } - if (link.local_port >= local->num_pads) { - dev_err(vi->dev, "invalid port number %u on %pOF\n", - link.local_port, to_of_node(link.local_node)); - v4l2_fwnode_put_link(&link); - ret = -EINVAL; - break; + remote_parent = fwnode_graph_get_remote_port_parent(ep); + if (!remote_parent) { + dev_dbg(vi->dev, "no remote parent found for %pfw\n", + ep); + continue; } - local_pad = &local->pads[link.local_port]; + local_pad = &local->pads[src_idx]; /* Remote node is vi node. So use channel video entity and pad * as remote/sink. */ - if (link.remote_node == of_fwnode_handle(vi->dev->of_node)) { + if (remote_parent == of_fwnode_handle(vi->dev->of_node)) { remote = &chan->video.entity; remote_pad = &chan->pad; goto create_link; } - /* - * Skip sink ports, they will be processed from the other end - * of the link. - */ - if (local_pad->flags & MEDIA_PAD_FL_SINK) { - dev_dbg(vi->dev, "skipping sink port %pOF:%u\n", - to_of_node(link.local_node), link.local_port); - v4l2_fwnode_put_link(&link); - continue; - } - /* find the remote entity from notifier list */ ent = tegra_vi_graph_find_entity(&chan->notifier.done_list, - link.remote_node); + remote_parent); if (!ent) { - dev_err(vi->dev, "no entity found for %pOF\n", - to_of_node(link.remote_node)); - v4l2_fwnode_put_link(&link); + fwnode_handle_put(ep); + dev_err(vi->dev, "no entity found for %pfw\n", + remote_parent); ret = -ENODEV; break; } remote = ent->entity; - if (link.remote_port >= remote->num_pads) { - dev_err(vi->dev, "invalid port number %u on %pOF\n", - link.remote_port, - to_of_node(link.remote_node)); - v4l2_fwnode_put_link(&link); - ret = -EINVAL; - break; + + sink_ep = fwnode_graph_get_remote_endpoint(ep); + if (!sink_ep) { + dev_dbg(vi->dev, "no sink ep found for %pfw\n", + ep); + continue; + } + + sink_idx = media_entity_get_fwnode_pad(remote, sink_ep, + MEDIA_PAD_FL_SINK); + if (sink_idx < 0) { + dev_dbg(vi->dev, "no sink pad found for %pfw\n", + sink_ep); + continue; } - remote_pad = &remote->pads[link.remote_port]; + remote_pad = &remote->pads[sink_idx]; create_link: dev_dbg(vi->dev, "creating %s:%u -> %s:%u link\n", @@ -1551,8 +1544,8 @@ create_link: ret = media_create_pad_link(local, local_pad->index, remote, remote_pad->index, link_flags); - v4l2_fwnode_put_link(&link); if (ret < 0) { + fwnode_handle_put(ep); dev_err(vi->dev, "failed to create %s:%u -> %s:%u link: %d\n", local->name, local_pad->index, @@ -1561,7 +1554,6 @@ create_link: } } - fwnode_handle_put(ep); return ret; } -- cgit From 007b61981aa970d314a6042cedcf7ef2cf34bf23 Mon Sep 17 00:00:00 2001 From: "Uwe Kleine-König (The Capable Hub)" Date: Mon, 29 Jun 2026 19:12:54 +0200 Subject: media: pci: Use PCI_DEVICE macros and drop zeros from pci_device_id arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some pci_device_id arrays already used the PCI_DEVICE macros, others used named initializers or list initializers. Unify all to use the PCI_DEVICE macros and (if applicable) a named initializer for .driver_data. Also drop all zeros that the compiler can care for and unify indention and spacing. Signed-off-by: Uwe Kleine-König (The Capable Hub) Signed-off-by: Hans Verkuil --- drivers/media/pci/bt8xx/bttv-driver.c | 12 +- drivers/media/pci/cobalt/cobalt-driver.c | 5 +- drivers/media/pci/cx18/cx18-driver.c | 5 +- drivers/media/pci/cx23885/cx23885-core.c | 10 +- drivers/media/pci/cx25821/cx25821-alsa.c | 4 +- drivers/media/pci/cx25821/cx25821-core.c | 10 +- drivers/media/pci/cx88/cx88-alsa.c | 6 +- drivers/media/pci/cx88/cx88-mpeg.c | 5 +- drivers/media/pci/cx88/cx88-video.c | 5 +- drivers/media/pci/ddbridge/ddbridge-main.c | 2 +- drivers/media/pci/dm1105/dm1105.c | 10 +- drivers/media/pci/dt3155/dt3155.c | 4 +- drivers/media/pci/intel/ipu3/ipu3-cio2.c | 2 +- drivers/media/pci/ivtv/ivtv-driver.c | 8 +- drivers/media/pci/mantis/mantis_common.h | 8 +- drivers/media/pci/mgb4/mgb4_core.c | 6 +- drivers/media/pci/netup_unidvb/netup_unidvb_core.c | 2 +- drivers/media/pci/ngene/ngene-cards.c | 5 +- drivers/media/pci/pluto2/pluto2.c | 5 +- drivers/media/pci/saa7134/saa7134-cards.c | 2047 +++++++------------- drivers/media/pci/saa7146/hexium_gemini.c | 22 +- drivers/media/pci/saa7146/hexium_orion.c | 33 +- drivers/media/pci/saa7146/mxb.c | 10 +- drivers/media/pci/saa7164/saa7164-core.c | 5 +- drivers/media/pci/smipcie/smipcie-main.c | 5 +- drivers/media/pci/solo6x10/solo6x10-core.c | 2 +- drivers/media/pci/ttpci/budget-ci.c | 4 +- drivers/media/pci/ttpci/budget.c | 4 +- drivers/media/pci/tw5864/tw5864-core.c | 4 +- drivers/media/pci/tw68/tw68-core.c | 16 +- drivers/media/pci/tw686x/tw686x-core.c | 23 +- drivers/media/pci/zoran/zoran_card.c | 7 +- include/media/drv-intf/saa7146.h | 11 +- 33 files changed, 839 insertions(+), 1468 deletions(-) diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 663469208271..c631b8bbd386 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -3563,12 +3563,12 @@ static int __maybe_unused bttv_resume(struct device *dev) } static const struct pci_device_id bttv_pci_tbl[] = { - {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848), 0}, - {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849), 0}, - {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878), 0}, - {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879), 0}, - {PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879), 0}, - {0,} + { PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT848) }, + { PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT849) }, + { PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT878) }, + { PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_BT879) }, + { PCI_VDEVICE(BROOKTREE, PCI_DEVICE_ID_FUSION879) }, + { } }; MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c index 9b9f69ff4016..7b1ca1238c8d 100644 --- a/drivers/media/pci/cobalt/cobalt-driver.c +++ b/drivers/media/pci/cobalt/cobalt-driver.c @@ -26,9 +26,8 @@ /* add your revision and whatnot here */ static const struct pci_device_id cobalt_pci_tbl[] = { - {PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_COBALT, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_VDEVICE(CISCO, PCI_DEVICE_ID_COBALT) }, + { } }; MODULE_DEVICE_TABLE(pci, cobalt_pci_tbl); diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c index f778f79b921d..214fac7af61e 100644 --- a/drivers/media/pci/cx18/cx18-driver.c +++ b/drivers/media/pci/cx18/cx18-driver.c @@ -40,9 +40,8 @@ EXPORT_SYMBOL(cx18_ext_init); /* add your revision and whatnot here */ static const struct pci_device_id cx18_pci_tbl[] = { - {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_VDEVICE(CX, PCI_DEVICE_ID_CX23418) }, + { } }; MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 9b92e8db494c..5fb26285e4af 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2279,16 +2279,10 @@ static int __maybe_unused cx23885_resume(struct device *dev_d) static const struct pci_device_id cx23885_pci_tbl[] = { { /* CX23885 */ - .vendor = 0x14f1, - .device = 0x8852, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(0x14f1, 0x8852), }, { /* CX23887 Rev 2 */ - .vendor = 0x14f1, - .device = 0x8880, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(0x14f1, 0x8880), }, { /* --- end of list --- */ } diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 4fdc59aaed8e..f963ae79f685 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -681,8 +681,8 @@ static int snd_cx25821_pcm(struct cx25821_audio_dev *chip, int device, */ static const struct pci_device_id __maybe_unused cx25821_audio_pci_tbl[] = { - {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_DEVICE(0x14f1, 0x0920) }, + { } }; MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 5acb1dc00ae8..f883a779b570 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -1347,16 +1347,10 @@ static void cx25821_finidev(struct pci_dev *pci_dev) static const struct pci_device_id cx25821_pci_tbl[] = { { /* CX25821 Athena */ - .vendor = 0x14f1, - .device = 0x8210, - .subvendor = 0x14f1, - .subdevice = 0x0920, + PCI_DEVICE_SUB(0x14f1, 0x8210, 0x14f1, 0x0920), }, { /* CX25821 No Brand */ - .vendor = 0x14f1, - .device = 0x8210, - .subvendor = 0x0000, - .subdevice = 0x0000, + PCI_DEVICE_SUB(0x14f1, 0x8210, 0x0000, 0x0000), }, { /* --- end of list --- */ } diff --git a/drivers/media/pci/cx88/cx88-alsa.c b/drivers/media/pci/cx88/cx88-alsa.c index dce041a5e47a..d2534f90cfba 100644 --- a/drivers/media/pci/cx88/cx88-alsa.c +++ b/drivers/media/pci/cx88/cx88-alsa.c @@ -809,9 +809,9 @@ static const struct snd_kcontrol_new snd_cx88_alc_switch = { */ static const struct pci_device_id cx88_audio_pci_tbl[] = { - {0x14f1, 0x8801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0x14f1, 0x8811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0, } + { PCI_DEVICE(0x14f1, 0x8801) }, + { PCI_DEVICE(0x14f1, 0x8811) }, + { } }; MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index 676160e9554d..a399a9bf3b92 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -787,10 +787,7 @@ static void cx8802_remove(struct pci_dev *pci_dev) static const struct pci_device_id cx8802_pci_tbl[] = { { - .vendor = 0x14f1, - .device = 0x8802, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(0x14f1, 0x8802), }, { /* --- end of list --- */ } diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c index c78b156c5cda..eaa46a2f92e7 100644 --- a/drivers/media/pci/cx88/cx88-video.c +++ b/drivers/media/pci/cx88/cx88-video.c @@ -1610,10 +1610,7 @@ static int __maybe_unused cx8800_resume(struct device *dev_d) static const struct pci_device_id cx8800_pci_tbl[] = { { - .vendor = 0x14f1, - .device = 0x8800, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(0x14f1, 0x8800), }, { /* --- end of list --- */ } diff --git a/drivers/media/pci/ddbridge/ddbridge-main.c b/drivers/media/pci/ddbridge/ddbridge-main.c index 363badab7cf0..248ace29e5c8 100644 --- a/drivers/media/pci/ddbridge/ddbridge-main.c +++ b/drivers/media/pci/ddbridge/ddbridge-main.c @@ -269,7 +269,7 @@ static const struct pci_device_id ddb_id_table[] = { DDB_DEVICE_ANY(0x0323), DDB_DEVICE_ANY(0x0328), DDB_DEVICE_ANY(0x0329), - {0} + { } }; MODULE_DEVICE_TABLE(pci, ddb_id_table); diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c index 80d2e143384b..ae4544042a74 100644 --- a/drivers/media/pci/dm1105/dm1105.c +++ b/drivers/media/pci/dm1105/dm1105.c @@ -1209,15 +1209,9 @@ static void dm1105_remove(struct pci_dev *pdev) static const struct pci_device_id dm1105_id_table[] = { { - .vendor = PCI_VENDOR_ID_TRIGEM, - .device = PCI_DEVICE_ID_DM1105, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_VDEVICE(TRIGEM, PCI_DEVICE_ID_DM1105), }, { - .vendor = PCI_VENDOR_ID_AXESS, - .device = PCI_DEVICE_ID_DM05, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_VDEVICE(AXESS, PCI_DEVICE_ID_DM05), }, { /* empty */ }, diff --git a/drivers/media/pci/dt3155/dt3155.c b/drivers/media/pci/dt3155/dt3155.c index 7bddcbba4cf1..660aab6b62e0 100644 --- a/drivers/media/pci/dt3155/dt3155.c +++ b/drivers/media/pci/dt3155/dt3155.c @@ -577,8 +577,8 @@ static void dt3155_remove(struct pci_dev *pdev) } static const struct pci_device_id pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, DT3155_DEVICE_ID) }, - { 0, /* zero marks the end */ }, + { PCI_VDEVICE(INTEL, DT3155_DEVICE_ID) }, + { /* zero marks the end */ }, }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 986b9afd7cb5..eb1824ee86fd 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1961,7 +1961,7 @@ static const struct dev_pm_ops cio2_pm_ops = { }; static const struct pci_device_id cio2_pci_id_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, CIO2_PCI_ID) }, + { PCI_VDEVICE(INTEL, CIO2_PCI_ID) }, { } }; diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c index e0091a8e5c88..b9ea56ec9593 100644 --- a/drivers/media/pci/ivtv/ivtv-driver.c +++ b/drivers/media/pci/ivtv/ivtv-driver.c @@ -60,11 +60,9 @@ EXPORT_SYMBOL(ivtv_ext_init); /* add your revision and whatnot here */ static const struct pci_device_id ivtv_pci_tbl[] = { - {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {0,} + { PCI_VDEVICE(ICOMP, PCI_DEVICE_ID_IVTV15) }, + { PCI_VDEVICE(ICOMP, PCI_DEVICE_ID_IVTV16) }, + { } }; MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); diff --git a/drivers/media/pci/mantis/mantis_common.h b/drivers/media/pci/mantis/mantis_common.h index 6e563ecd94e8..f357ca268dd1 100644 --- a/drivers/media/pci/mantis/mantis_common.h +++ b/drivers/media/pci/mantis/mantis_common.h @@ -58,11 +58,9 @@ #define TERRATEC 0x153b #define MAKE_ENTRY(__subven, __subdev, __configptr, __rc) { \ - .vendor = TWINHAN_TECHNOLOGIES, \ - .device = MANTIS, \ - .subvendor = (__subven), \ - .subdevice = (__subdev), \ - .driver_data = (unsigned long) \ + PCI_DEVICE_SUB(TWINHAN_TECHNOLOGIES, MANTIS, \ + (__subven), (__subdev)), \ + .driver_data = (unsigned long) \ &(struct mantis_pci_drvdata){__configptr, __rc} \ } diff --git a/drivers/media/pci/mgb4/mgb4_core.c b/drivers/media/pci/mgb4/mgb4_core.c index d23d854581c5..cbd193e06536 100644 --- a/drivers/media/pci/mgb4/mgb4_core.c +++ b/drivers/media/pci/mgb4/mgb4_core.c @@ -694,9 +694,9 @@ static void mgb4_remove(struct pci_dev *pdev) } static const struct pci_device_id mgb4_pci_ids[] = { - { PCI_DEVICE(DIGITEQ_VID, T100_DID), }, - { PCI_DEVICE(DIGITEQ_VID, T200_DID), }, - { 0, } + { PCI_DEVICE(DIGITEQ_VID, T100_DID) }, + { PCI_DEVICE(DIGITEQ_VID, T200_DID) }, + { } }; MODULE_DEVICE_TABLE(pci, mgb4_pci_ids); diff --git a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c index ec08023b5d5e..00f81e4c8e8d 100644 --- a/drivers/media/pci/netup_unidvb/netup_unidvb_core.c +++ b/drivers/media/pci/netup_unidvb/netup_unidvb_core.c @@ -1014,7 +1014,7 @@ static void netup_unidvb_finidev(struct pci_dev *pci_dev) static const struct pci_device_id netup_unidvb_pci_tbl[] = { { PCI_DEVICE(0x1b55, 0x18f6) }, /* hw rev. 1.3 */ { PCI_DEVICE(0x1b55, 0x18f7) }, /* hw rev. 1.4 */ - { 0, } + { } }; MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); diff --git a/drivers/media/pci/ngene/ngene-cards.c b/drivers/media/pci/ngene/ngene-cards.c index 7dbc21e1a2ca..a1a83f3bb6b4 100644 --- a/drivers/media/pci/ngene/ngene-cards.c +++ b/drivers/media/pci/ngene/ngene-cards.c @@ -1155,8 +1155,7 @@ static const struct ngene_info ngene_info_terratec = { /****************************************************************************/ #define NGENE_ID(_subvend, _subdev, _driverdata) { \ - .vendor = NGENE_VID, .device = NGENE_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ + PCI_DEVICE_SUB(NGENE_VID, NGENE_PID, (_subvend), (_subdev)), \ .driver_data = (unsigned long) &_driverdata } /****************************************************************************/ @@ -1173,7 +1172,7 @@ static const struct pci_device_id ngene_id_tbl[] = { NGENE_ID(0x18c3, 0xdd20, ngene_info_duoFlex), NGENE_ID(0x1461, 0x062e, ngene_info_m780), NGENE_ID(0x153b, 0x1167, ngene_info_terratec), - {0} + { } }; MODULE_DEVICE_TABLE(pci, ngene_id_tbl); diff --git a/drivers/media/pci/pluto2/pluto2.c b/drivers/media/pci/pluto2/pluto2.c index 22c2222d5c4d..77b3e4eec54d 100644 --- a/drivers/media/pci/pluto2/pluto2.c +++ b/drivers/media/pci/pluto2/pluto2.c @@ -762,10 +762,7 @@ static void pluto2_remove(struct pci_dev *pdev) static const struct pci_device_id pluto2_id_table[] = { { - .vendor = PCI_VENDOR_ID_SCM, - .device = PCI_DEVICE_ID_PLUTO2, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_VDEVICE(SCM, PCI_DEVICE_ID_PLUTO2), }, { /* empty */ }, diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c index e80fb4ebfda6..a7d665407460 100644 --- a/drivers/media/pci/saa7134/saa7134-cards.c +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -5800,1317 +5800,758 @@ const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); struct pci_device_id saa7134_pci_tbl[] = { { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2001, - .driver_data = SAA7134_BOARD_PROTEUS_PRO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2001, - .driver_data = SAA7134_BOARD_PROTEUS_PRO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x6752, - .driver_data = SAA7134_BOARD_EMPRESS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1131, - .subdevice = 0x4e85, - .driver_data = SAA7134_BOARD_MONSTERTV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x153b, - .subdevice = 0x1142, - .driver_data = SAA7134_BOARD_CINERGY400, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x153b, - .subdevice = 0x1143, - .driver_data = SAA7134_BOARD_CINERGY600, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x153b, - .subdevice = 0x1158, - .driver_data = SAA7134_BOARD_CINERGY600_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x153b, - .subdevice = 0x1162, - .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5169, - .subdevice = 0x0138, - .driver_data = SAA7134_BOARD_FLYVIDEO3000_NTSC, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5168, - .subdevice = 0x0138, - .driver_data = SAA7134_BOARD_FLYVIDEO3000, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x4e42, /* "Typhoon PCI Capture TV Card" Art.No. 50673 */ - .subdevice = 0x0138, - .driver_data = SAA7134_BOARD_FLYVIDEO3000, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x5168, - .subdevice = 0x0138, - .driver_data = SAA7134_BOARD_FLYVIDEO2000, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x4e42, /* Typhoon */ - .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ - .driver_data = SAA7134_BOARD_FLYVIDEO2000, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x0212, /* minipci, LR212 */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x14c0, - .subdevice = 0x1212, /* minipci, LR1212 */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI2, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x4e42, - .subdevice = 0x0212, /* OEM minipci, LR212 */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, /* Animation Technologies (LifeView) */ - .subdevice = 0x0214, /* Standard PCI, LR214 Rev E and earlier (SAA7135) */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, /* Animation Technologies (LifeView) */ - .subdevice = 0x5214, /* Standard PCI, LR214 Rev F onwards (SAA7131) */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1489, /* KYE */ - .subdevice = 0x0214, /* Genius VideoWonder ProTV */ - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */ - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x16be, - .subdevice = 0x0003, - .driver_data = SAA7134_BOARD_MD7134, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */ - .subdevice = 0x5000, /* only analog TV and DVB-T for now */ - .driver_data = SAA7134_BOARD_MD7134, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1048, - .subdevice = 0x226b, - .driver_data = SAA7134_BOARD_ELSA, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1048, - .subdevice = 0x226a, - .driver_data = SAA7134_BOARD_ELSA_500TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1048, - .subdevice = 0x226c, - .driver_data = SAA7134_BOARD_ELSA_700TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_ASUSTEK, - .subdevice = 0x4842, - .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_VENDOR_ID_ASUSTEK, - .subdevice = 0x4845, - .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7135, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_ASUSTEK, - .subdevice = 0x4830, - .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_VENDOR_ID_ASUSTEK, - .subdevice = 0x4843, - .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_ASUSTEK, - .subdevice = 0x4840, - .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0xfe01, - .driver_data = SAA7134_BOARD_TVSTATION_RDS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1894, - .subdevice = 0xfe01, - .driver_data = SAA7134_BOARD_TVSTATION_RDS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1894, - .subdevice = 0xa006, - .driver_data = SAA7134_BOARD_TVSTATION_DVR, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1131, - .subdevice = 0x7133, - .driver_data = SAA7134_BOARD_VA1000POWER, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2001, - .driver_data = SAA7134_BOARD_10MOONSTVMASTER, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x185b, - .subdevice = 0xc100, - .driver_data = SAA7134_BOARD_VIDEOMATE_TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x185b, - .subdevice = 0xc100, - .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_MATROX, - .subdevice = 0x48d0, - .driver_data = SAA7134_BOARD_CRONOS_PLUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa70b, - .driver_data = SAA7134_BOARD_MD2819, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa7a1, - .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa7a2, - .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x2115, - .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa115, - .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x2108, - .driver_data = SAA7134_BOARD_AVERMEDIA_305, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x10ff, - .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER, - },{ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_PHILIPS, 0x2001), + .driver_data = SAA7134_BOARD_PROTEUS_PRO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, + PCI_VENDOR_ID_PHILIPS, 0x2001), + .driver_data = SAA7134_BOARD_PROTEUS_PRO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_PHILIPS, 0x6752), + .driver_data = SAA7134_BOARD_EMPRESS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1131, 0x4e85), + .driver_data = SAA7134_BOARD_MONSTERTV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x153b, 0x1142), + .driver_data = SAA7134_BOARD_CINERGY400, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x153b, 0x1143), + .driver_data = SAA7134_BOARD_CINERGY600, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x153b, 0x1158), + .driver_data = SAA7134_BOARD_CINERGY600_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x153b, 0x1162), + .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5169, 0x0138), + .driver_data = SAA7134_BOARD_FLYVIDEO3000_NTSC, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5168, 0x0138), + .driver_data = SAA7134_BOARD_FLYVIDEO3000, + }, { + /* "Typhoon PCI Capture TV Card" Art.No. 50673 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x4e42, 0x0138), + .driver_data = SAA7134_BOARD_FLYVIDEO3000, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x5168, 0x0138), + .driver_data = SAA7134_BOARD_FLYVIDEO2000, + }, { + /* Typhoon LifeView FlyTV Prime30 OEM */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x4e42, 0x0138), + .driver_data = SAA7134_BOARD_FLYVIDEO2000, + }, { + /* minipci, LR212 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0212), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + }, { + /* minipci, LR1212 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x14c0, 0x1212), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI2, + }, { + /* OEM minipci, LR212 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x4e42, 0x0212), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + }, { + /* + * Animation Technologies (LifeView) + * Standard PCI, LR214 Rev E and earlier (SAA7135) + */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0214), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + }, { + /* + * Animation Technologies (LifeView) + * Standard PCI, LR214 Rev F onwards (SAA7131) + */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x5214), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + }, { + /* KYE Genius VideoWonder ProTV */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1489, 0x0214), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */ + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x16be, 0x0003), + .driver_data = SAA7134_BOARD_MD7134, + }, { + /* CTX946 analog TV, HW mpeg, DVB-T; only analog TV and DVB-T for now */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x16be, 0x5000), + .driver_data = SAA7134_BOARD_MD7134, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1048, 0x226b), + .driver_data = SAA7134_BOARD_ELSA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1048, 0x226a), + .driver_data = SAA7134_BOARD_ELSA_500TV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1048, 0x226c), + .driver_data = SAA7134_BOARD_ELSA_700TV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_ASUSTEK, 0x4842), + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, + PCI_VENDOR_ID_ASUSTEK, 0x4845), + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7135, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_ASUSTEK, 0x4830), + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, + PCI_VENDOR_ID_ASUSTEK, 0x4843), + .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_ASUSTEK, 0x4840), + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_PHILIPS, 0xfe01), + .driver_data = SAA7134_BOARD_TVSTATION_RDS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1894, 0xfe01), + .driver_data = SAA7134_BOARD_TVSTATION_RDS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1894, 0xa006), + .driver_data = SAA7134_BOARD_TVSTATION_DVR, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1131, 0x7133), + .driver_data = SAA7134_BOARD_VA1000POWER, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_PHILIPS, 0x2001), + .driver_data = SAA7134_BOARD_10MOONSTVMASTER, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x185b, 0xc100), + .driver_data = SAA7134_BOARD_VIDEOMATE_TV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x185b, 0xc100), + .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_MATROX, 0x48d0), + .driver_data = SAA7134_BOARD_CRONOS_PLUS, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0xa70b), + .driver_data = SAA7134_BOARD_MD2819, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xa7a1), + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xa7a2), + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0x2115), + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0xa115), + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0x2108), + .driver_data = SAA7134_BOARD_AVERMEDIA_305, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0x10ff), + .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER, + }, { /* AVerMedia CardBus */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xd6ee, - .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS, - },{ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0xd6ee), + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS, + }, { /* AVerMedia CardBus */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xb7e9, - .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501, + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0xb7e9), + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501, }, { - /* TransGear 3000TV */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x050c, - .driver_data = SAA7134_BOARD_TG3000TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x11bd, - .subdevice = 0x002b, - .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x11bd, - .subdevice = 0x002d, - .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1019, - .subdevice = 0x4cb4, - .driver_data = SAA7134_BOARD_ECS_TVP3XP, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1019, - .subdevice = 0x4cb5, - .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1019, - .subdevice = 0x4cb6, - .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB6, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x12ab, - .subdevice = 0x0800, - .driver_data = SAA7134_BOARD_UPMOST_PURPLE_TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x153b, - .subdevice = 0x1152, - .driver_data = SAA7134_BOARD_CINERGY200, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x185b, - .subdevice = 0xc100, - .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x9715, - .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa70a, - .driver_data = SAA7134_BOARD_AVERMEDIA_307, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x185b, - .subdevice = 0xc200, - .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1540, - .subdevice = 0x9524, - .driver_data = SAA7134_BOARD_PROVIDEO_PV952, - - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x0502, /* Cardbus version */ - .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x0306, /* PCI version */ - .driver_data = SAA7134_BOARD_FLYDVBTDUO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf31f, - .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM, - - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf11d, - .driver_data = SAA7134_BOARD_AVERMEDIA_M135A, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x4155, - .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x4255, - .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2004, - .driver_data = SAA7134_BOARD_PHILIPS_TOUGH, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1421, - .subdevice = 0x0350, /* PCI version */ - .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1421, - .subdevice = 0x0351, /* PCI version, new revision */ - .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1421, - .subdevice = 0x0370, /* cardbus version */ - .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1421, - .subdevice = 0x1370, /* cardbus version */ - .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, - - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x4e42, /* Typhoon */ - .subdevice = 0x0502, /* LifeView LR502 OEM */ - .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x0210, /* mini pci NTSC version */ - .driver_data = SAA7134_BOARD_FLYTV_DIGIMATRIX, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1043, - .subdevice = 0x0210, /* mini pci PAL/SECAM version */ - .driver_data = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV, - - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0000, /* It shouldn't break anything, since subdevice id seems unique */ - .subdevice = 0x4091, - .driver_data = SAA7134_BOARD_BEHOLD_409FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5456, /* GoTView */ - .subdevice = 0x7135, - .driver_data = SAA7134_BOARD_GOTVIEW_7135, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2004, - .driver_data = SAA7134_BOARD_PHILIPS_EUROPA, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x185b, - .subdevice = 0xc900, - .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_300, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x185b, - .subdevice = 0xc901, - .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_200, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1435, - .subdevice = 0x7350, - .driver_data = SAA7134_BOARD_RTD_VFG7350, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1435, - .subdevice = 0x7330, - .driver_data = SAA7134_BOARD_RTD_VFG7330, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, - .subdevice = 0x1044, - .driver_data = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1131, - .subdevice = 0x4ee9, - .driver_data = SAA7134_BOARD_MONSTERTV_MOBILE, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x11bd, - .subdevice = 0x002e, - .driver_data = SAA7134_BOARD_PINNACLE_PCTV_110i, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x4862, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2018, - .driver_data = SAA7134_BOARD_PHILIPS_TIGER, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1462, - .subdevice = 0x6231, /* tda8275a, ks003 IR */ - .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1462, - .subdevice = 0x8624, /* tda8275, ks003 IR */ - .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x153b, - .subdevice = 0x1160, - .driver_data = SAA7134_BOARD_CINERGY250PCI, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA 7131E */ - .subvendor = 0x5168, - .subdevice = 0x0319, - .driver_data = SAA7134_BOARD_FLYDVB_TRIO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, - .subdevice = 0x2c05, - .driver_data = SAA7134_BOARD_AVERMEDIA_777, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5168, - .subdevice = 0x0301, - .driver_data = SAA7134_BOARD_FLYDVBT_LR301, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0331, - .subdevice = 0x1421, - .driver_data = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x17de, - .subdevice = 0x7201, - .driver_data = SAA7134_BOARD_TEVION_DVBT_220RF, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x17de, - .subdevice = 0x7250, - .driver_data = SAA7134_BOARD_KWORLD_DVBT_210, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ - .subvendor = 0x17de, - .subdevice = 0x7350, - .driver_data = SAA7134_BOARD_KWORLD_ATSC110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ - .subvendor = 0x17de, - .subdevice = 0x7352, - .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ - .subvendor = 0x17de, - .subdevice = 0xa134, - .driver_data = SAA7134_BOARD_KWORLD_PC150U, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, - .subdevice = 0x7360, - .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, - .subdevice = 0x6360, - .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B1, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x16be, - .subdevice = 0x0005, - .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5168, - .subdevice = 0x0300, - .driver_data = SAA7134_BOARD_FLYDVBS_LR300, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x4e42, - .subdevice = 0x0300,/* LR300 */ - .driver_data = SAA7134_BOARD_FLYDVBS_LR300, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1489, - .subdevice = 0x0301, + /* AVerMedia TransGear 3000TV */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0x050c), + .driver_data = SAA7134_BOARD_TG3000TV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x11bd, 0x002b), + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x11bd, 0x002d), + .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1019, 0x4cb4), + .driver_data = SAA7134_BOARD_ECS_TVP3XP, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1019, 0x4cb5), + .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1019, 0x4cb6), + .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB6, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x12ab, 0x0800), + .driver_data = SAA7134_BOARD_UPMOST_PURPLE_TV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x153b, 0x1152), + .driver_data = SAA7134_BOARD_CINERGY200, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x185b, 0xc100), + .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0x9715), + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0xa70a), + .driver_data = SAA7134_BOARD_AVERMEDIA_307, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x185b, 0xc200), + .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1540, 0x9524), + .driver_data = SAA7134_BOARD_PROVIDEO_PV952, + }, { + /* Cardbus version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0502), + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + }, { + /* PCI version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0306), + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf31f), + .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf11d), + .driver_data = SAA7134_BOARD_AVERMEDIA_M135A, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x4155), + .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x4255), + .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_PHILIPS, 0x2004), + .driver_data = SAA7134_BOARD_PHILIPS_TOUGH, + }, { + /* PCI version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1421, 0x0350), + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + }, { + /* PCI version, new revision */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1421, 0x0351), + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + }, { + /* cardbus version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1421, 0x0370), + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + }, { + /* cardbus version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1421, 0x1370), + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + }, { + /* Typhoon LifeView LR502 OEM */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x4e42, 0x0502), + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + }, { + /* mini pci NTSC version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x0210), + .driver_data = SAA7134_BOARD_FLYTV_DIGIMATRIX, + }, { + /* mini pci PAL/SECAM version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1043, 0x0210), + .driver_data = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV, + }, { + /* subvendor == 0 shouldn't break anything, since subdevice id seems unique */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0000, 0x4091), + .driver_data = SAA7134_BOARD_BEHOLD_409FM, + }, { + /* GoTView */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5456, 0x7135), + .driver_data = SAA7134_BOARD_GOTVIEW_7135, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_PHILIPS, 0x2004), + .driver_data = SAA7134_BOARD_PHILIPS_EUROPA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x185b, 0xc900), + .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_300, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x185b, 0xc901), + .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_200, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1435, 0x7350), + .driver_data = SAA7134_BOARD_RTD_VFG7350, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1435, 0x7330), + .driver_data = SAA7134_BOARD_RTD_VFG7330, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x1044), + .driver_data = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1131, 0x4ee9), + .driver_data = SAA7134_BOARD_MONSTERTV_MOBILE, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x11bd, 0x002e), + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_110i, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x4862), + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, + PCI_VENDOR_ID_PHILIPS, 0x2018), + .driver_data = SAA7134_BOARD_PHILIPS_TIGER, + }, { + /* tda8275a, ks003 IR */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1462, 0x6231), + .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, + }, { + /* tda8275, ks003 IR */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1462, 0x8624), + .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x153b, 0x1160), + .driver_data = SAA7134_BOARD_CINERGY250PCI, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0319), + .driver_data = SAA7134_BOARD_FLYDVB_TRIO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0x2c05), + .driver_data = SAA7134_BOARD_AVERMEDIA_777, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5168, 0x0301), .driver_data = SAA7134_BOARD_FLYDVBT_LR301, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, /* Animation Technologies (LifeView) */ - .subdevice = 0x0304, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0331, 0x1421), + .driver_data = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0x7201), + .driver_data = SAA7134_BOARD_TEVION_DVBT_220RF, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0x7250), + .driver_data = SAA7134_BOARD_KWORLD_DVBT_210, + }, { + /* SAA7135HL */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0x7350), + .driver_data = SAA7134_BOARD_KWORLD_ATSC110, + }, { + /* SAA7135HL */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0x7352), + .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ + }, { + /* SAA7135HL */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0xa134), + .driver_data = SAA7134_BOARD_KWORLD_PC150U, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0x7360), + .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0x6360), + .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B1, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x16be, 0x0005), + .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5168, 0x0300), + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + }, { + /* LR300 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x4e42, 0x0300), + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1489, 0x0301), + .driver_data = SAA7134_BOARD_FLYDVBT_LR301, + }, { + /* Animation Technologies (LifeView) */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x0304), .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x3306, - .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x3502, /* what's the difference to 0x3306 ?*/ - .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5168, - .subdevice = 0x3307, /* FlyDVB-T Hybrid Mini PCI */ - .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x16be, - .subdevice = 0x0007, - .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x16be, - .subdevice = 0x0008, - .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x16be, - .subdevice = 0x000d, /* triple CTX948_V1.1.1 */ - .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, - .subdevice = 0x2c05, - .driver_data = SAA7134_BOARD_AVERMEDIA_777, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1489, - .subdevice = 0x0502, /* Cardbus version */ - .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0919, /* Philips Proteus PRO 2309 */ - .subdevice = 0x2003, - .driver_data = SAA7134_BOARD_PROTEUS_2309, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, - .subdevice = 0x2c00, - .driver_data = SAA7134_BOARD_AVERMEDIA_A16AR, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1043, - .subdevice = 0x4860, - .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x11bd, - .subdevice = 0x002f, - .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x9715, - .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa11b, - .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x4876, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6700, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6701, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6702, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6703, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6704, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6705, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6706, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6707, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6708, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x6709, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0070, - .subdevice = 0x670a, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x153b, - .subdevice = 0x1172, - .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2342, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1131, - .subdevice = 0x2341, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x3016, - .subdevice = 0x2344, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1131, - .subdevice = 0x230f, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1a7f, - .subdevice = 0x2008, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1a7f, - .subdevice = 0x2108, - .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x153b, - .subdevice = 0x1175, - .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf31e, - .driver_data = SAA7134_BOARD_AVERMEDIA_M102, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x4E42, /* MSI */ - .subdevice = 0x0306, /* TV@nywhere DUO */ - .driver_data = SAA7134_BOARD_FLYDVBTDUO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x4871, - .driver_data = SAA7134_BOARD_ASUS_P7131_4871, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x4857, /* REV:1.00 */ - .driver_data = SAA7134_BOARD_ASUSTeK_TIGER, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x0919, /* SinoVideo PCI 2309 Proteus (7134) */ - .subdevice = 0x2003, /* OEM cardbus */ - .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2304, - .driver_data = SAA7134_BOARD_10MOONSTVMASTER3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf01d, /* AVerTV DVB-T Super 007 */ - .driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, - .subdevice = 0x4016, - .driver_data = SAA7134_BOARD_BEHOLD_401, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x0000, - .subdevice = 0x4036, - .driver_data = SAA7134_BOARD_BEHOLD_403, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x0000, - .subdevice = 0x4037, - .driver_data = SAA7134_BOARD_BEHOLD_403FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, - .subdevice = 0x4050, - .driver_data = SAA7134_BOARD_BEHOLD_405, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, - .subdevice = 0x4051, - .driver_data = SAA7134_BOARD_BEHOLD_405FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x0000, - .subdevice = 0x4070, - .driver_data = SAA7134_BOARD_BEHOLD_407, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x0000, - .subdevice = 0x4071, - .driver_data = SAA7134_BOARD_BEHOLD_407FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0000, - .subdevice = 0x4090, - .driver_data = SAA7134_BOARD_BEHOLD_409, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, - .subdevice = 0x505B, - .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, - .subdevice = 0x5051, - .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x5ace, - .subdevice = 0x5050, - .driver_data = SAA7134_BOARD_BEHOLD_505FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0000, - .subdevice = 0x5071, - .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0000, - .subdevice = 0x507B, - .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5ace, - .subdevice = 0x5070, - .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x5090, - .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x0000, - .subdevice = 0x5201, - .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5ace, - .subdevice = 0x6070, - .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5ace, - .subdevice = 0x6071, - .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5ace, - .subdevice = 0x6072, - .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x5ace, - .subdevice = 0x6073, - .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6090, - .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6091, - .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6092, - .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6093, - .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6190, - .driver_data = SAA7134_BOARD_BEHOLD_M6, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6193, - .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6191, - .driver_data = SAA7134_BOARD_BEHOLD_M63, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x4e42, - .subdevice = 0x3502, - .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1822, /*Twinhan Technology Co. Ltd*/ - .subdevice = 0x0022, - .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x16be, - .subdevice = 0x0010, /* Medion version CTX953_V.1.4.3 */ - .driver_data = SAA7134_BOARD_CREATIX_CTX953, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1462, /* MSI */ - .subdevice = 0x8625, /* TV@nywhere A/D v1.1 */ - .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf436, - .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf936, - .driver_data = SAA7134_BOARD_AVERMEDIA_A16D, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa836, - .driver_data = SAA7134_BOARD_AVERMEDIA_M115, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x185b, - .subdevice = 0xc900, - .driver_data = SAA7134_BOARD_VIDEOMATE_T750, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ - .subvendor = 0x1421, - .subdevice = 0x0380, - .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5169, - .subdevice = 0x1502, - .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x6290, - .driver_data = SAA7134_BOARD_BEHOLD_H6, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf636, - .driver_data = SAA7134_BOARD_AVERMEDIA_M103, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf736, - .driver_data = SAA7134_BOARD_AVERMEDIA_M103, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x4878, /* REV:1.02G */ - .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1043, - .subdevice = 0x48cd, - .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x17de, - .subdevice = 0x7128, - .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x17de, - .subdevice = 0xb136, - .driver_data = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xf31d, - .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x185b, - .subdevice = 0xc900, - .driver_data = SAA7134_BOARD_VIDEOMATE_S350, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ - .subdevice = 0x7595, - .driver_data = SAA7134_BOARD_BEHOLD_X7, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x19d1, /* RoverMedia */ - .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ - .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0x2004, - .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x1043, - .subdevice = 0x4847, - .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x107d, - .subdevice = 0x6655, - .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x13c2, - .subdevice = 0x2804, - .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ - .subdevice = 0x7190, - .driver_data = SAA7134_BOARD_BEHOLD_H7, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ - .subdevice = 0x7090, - .driver_data = SAA7134_BOARD_BEHOLD_A7, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7135, - .subvendor = 0x185b, - .subdevice = 0xc900, - .driver_data = SAA7134_BOARD_VIDEOMATE_M1F, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x5ace, - .subdevice = 0x5030, - .driver_data = SAA7134_BOARD_BEHOLD_503FM, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x5ace, - .subdevice = 0x5010, - .driver_data = SAA7134_BOARD_BEHOLD_501, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = 0x17de, - .subdevice = 0xd136, - .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x6000, - .subdevice = 0x0811, - .driver_data = SAA7134_BOARD_SENSORAY811_911, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x6000, - .subdevice = 0x0911, - .driver_data = SAA7134_BOARD_SENSORAY811_911, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0x2055, /* AverTV Satellite Hybrid+FM A706 */ - .driver_data = SAA7134_BOARD_AVERMEDIA_A706, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1905, /* WIS */ - .subdevice = 0x7007, - .driver_data = SAA7134_BOARD_WIS_VOYAGER, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x1461, /* Avermedia Technologies Inc */ - .subdevice = 0xa10a, - .driver_data = SAA7134_BOARD_AVERMEDIA_505, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x107d, - .subdevice = 0x6f3a, - .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x1779, /* V One Multimedia PTE Ltd */ - .subdevice = 0x13cf, - .driver_data = SAA7134_BOARD_SNAZIO_TVPVR_PRO, - }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = 0x107d, - .subdevice = 0x6f2e, - .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_HDTV200_H, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x3306), + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + /* what's the difference to .subdevice = 0x3306 above? */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x3502), + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + /* FlyDVB-T Hybrid Mini PCI */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5168, 0x3307), + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x16be, 0x0007), + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x16be, 0x0008), + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + }, { + /* triple CTX948_V1.1.1 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x16be, 0x000d), + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x2c05), + .driver_data = SAA7134_BOARD_AVERMEDIA_777, + }, { + /* Cardbus version */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1489, 0x0502), + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + }, { + /* Philips Proteus PRO 2309 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0919, 0x2003), + .driver_data = SAA7134_BOARD_PROTEUS_2309, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0x2c00), + .driver_data = SAA7134_BOARD_AVERMEDIA_A16AR, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1043, 0x4860), + .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x11bd, 0x002f), + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x9715), + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1461, 0xa11b), + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x4876), + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6700), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6701), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6702), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6703), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6704), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6705), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6706), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6707), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6708), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x6709), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0070, 0x670a), + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x153b, 0x1172), + .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_PHILIPS, 0x2342), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1131, 0x2341), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x3016, 0x2344), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1131, 0x230f), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1a7f, 0x2008), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1a7f, 0x2108), + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x153b, 0x1175), + .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf31e), + .driver_data = SAA7134_BOARD_AVERMEDIA_M102, + }, { + /* MSI TV@nywhere DUO */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x4E42, 0x0306), + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x4871), + .driver_data = SAA7134_BOARD_ASUS_P7131_4871, + }, { + /* REV:1.00 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x4857), + .driver_data = SAA7134_BOARD_ASUSTeK_TIGER, + }, { + /* SinoVideo PCI 2309 Proteus (7134) OEM cardbus */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x0919, 0x2003), + .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_PHILIPS, 0x2304), + .driver_data = SAA7134_BOARD_10MOONSTVMASTER3, + }, { + /* Avermedia Technologies Inc AVerTV DVB-T Super 007 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf01d), + .driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0000, 0x4016), + .driver_data = SAA7134_BOARD_BEHOLD_401, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x0000, 0x4036), + .driver_data = SAA7134_BOARD_BEHOLD_403, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x0000, 0x4037), + .driver_data = SAA7134_BOARD_BEHOLD_403FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0000, 0x4050), + .driver_data = SAA7134_BOARD_BEHOLD_405, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0000, 0x4051), + .driver_data = SAA7134_BOARD_BEHOLD_405FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x0000, 0x4070), + .driver_data = SAA7134_BOARD_BEHOLD_407, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x0000, 0x4071), + .driver_data = SAA7134_BOARD_BEHOLD_407FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0000, 0x4090), + .driver_data = SAA7134_BOARD_BEHOLD_409, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0000, 0x505B), + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x0000, 0x5051), + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x5ace, 0x5050), + .driver_data = SAA7134_BOARD_BEHOLD_505FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0000, 0x5071), + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0000, 0x507B), + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5ace, 0x5070), + .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x5090), + .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x0000, 0x5201), + .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5ace, 0x6070), + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5ace, 0x6071), + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5ace, 0x6072), + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x5ace, 0x6073), + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6090), + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6091), + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6092), + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6093), + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6190), + .driver_data = SAA7134_BOARD_BEHOLD_M6, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6193), + .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6191), + .driver_data = SAA7134_BOARD_BEHOLD_M63, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x4e42, 0x3502), + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + /*Twinhan Technology Co. Ltd*/ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1822, 0x0022), + .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056, + }, { + /* Medion version CTX953_V.1.4.3 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x16be, 0x0010), + .driver_data = SAA7134_BOARD_CREATIX_CTX953, + }, { + /* MSI TV@nywhere A/D v1.1 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1462, 0x8625), + .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf436), + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf936), + .driver_data = SAA7134_BOARD_AVERMEDIA_A16D, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xa836), + .driver_data = SAA7134_BOARD_AVERMEDIA_M115, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x185b, 0xc900), + .driver_data = SAA7134_BOARD_VIDEOMATE_T750, + }, { + /* SAA7135HL */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1421, 0x0380), + .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5169, 0x1502), + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x6290), + .driver_data = SAA7134_BOARD_BEHOLD_H6, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf636), + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf736), + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { + /* REV:1.02G */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x4878), + .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1043, 0x48cd), + .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x17de, 0x7128), + .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x17de, 0xb136), + .driver_data = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0xf31d), + .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x185b, 0xc900), + .driver_data = SAA7134_BOARD_VIDEOMATE_S350, + }, { + /* Beholder Intl. Ltd. */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x7595), + .driver_data = SAA7134_BOARD_BEHOLD_X7, + }, { + /* RoverMedia LifeView FlyTV Prime30 OEM */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x19d1, 0x0138), + .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, + PCI_VENDOR_ID_PHILIPS, 0x2004), + .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x1043, 0x4847), + .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x107d, 0x6655), + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x13c2, 0x2804), + .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, + }, { + /* Beholder Intl. Ltd. */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x7190), + .driver_data = SAA7134_BOARD_BEHOLD_H7, + }, { + /* Beholder Intl. Ltd. */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x7090), + .driver_data = SAA7134_BOARD_BEHOLD_A7, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7135, 0x185b, 0xc900), + .driver_data = SAA7134_BOARD_VIDEOMATE_M1F, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x5ace, 0x5030), + .driver_data = SAA7134_BOARD_BEHOLD_503FM, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x5ace, 0x5010), + .driver_data = SAA7134_BOARD_BEHOLD_501, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, 0x17de, 0xd136), + .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x6000, 0x0811), + .driver_data = SAA7134_BOARD_SENSORAY811_911, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x6000, 0x0911), + .driver_data = SAA7134_BOARD_SENSORAY811_911, + }, { + /* Avermedia Technologies Inc AverTV Satellite Hybrid+FM A706 */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1461, 0x2055), + .driver_data = SAA7134_BOARD_AVERMEDIA_A706, + }, { + /* WIS */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1905, 0x7007), + .driver_data = SAA7134_BOARD_WIS_VOYAGER, + }, { + /* Avermedia Technologies Inc */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x1461, 0xa10a), + .driver_data = SAA7134_BOARD_AVERMEDIA_505, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, 0x107d, 0x6f3a), + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_TV2100_FM, + }, { + /* V One Multimedia PTE Ltd */ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x1779, 0x13cf), + .driver_data = SAA7134_BOARD_SNAZIO_TVPVR_PRO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133, 0x107d, 0x6f2e), + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_HDTV200_H, }, { /* --- boards without eeprom + subsystem ID --- */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0, - .driver_data = SAA7134_BOARD_NOAUTO, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_VENDOR_ID_PHILIPS, - .subdevice = 0, - .driver_data = SAA7134_BOARD_NOAUTO, - },{ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134, + PCI_VENDOR_ID_PHILIPS, 0), + .driver_data = SAA7134_BOARD_NOAUTO, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130, + PCI_VENDOR_ID_PHILIPS, 0), + .driver_data = SAA7134_BOARD_NOAUTO, + }, { /* --- default catch --- */ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SAA7134_BOARD_UNKNOWN, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7133, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SAA7134_BOARD_UNKNOWN, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7134, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SAA7134_BOARD_UNKNOWN, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7135, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SAA7134_BOARD_UNKNOWN, - },{ + PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7130), + .driver_data = SAA7134_BOARD_UNKNOWN, + }, { + PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7133), + .driver_data = SAA7134_BOARD_UNKNOWN, + }, { + PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7134), + .driver_data = SAA7134_BOARD_UNKNOWN, + }, { + PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7135), + .driver_data = SAA7134_BOARD_UNKNOWN, + }, { /* --- end of list --- */ } }; diff --git a/drivers/media/pci/saa7146/hexium_gemini.c b/drivers/media/pci/saa7146/hexium_gemini.c index 1eef4e102972..f2298e8bb96d 100644 --- a/drivers/media/pci/saa7146/hexium_gemini.c +++ b/drivers/media/pci/saa7146/hexium_gemini.c @@ -361,22 +361,14 @@ static struct saa7146_pci_extension_data hexium_gemini_dual_4bnc = { static const struct pci_device_id pci_tbl[] = { { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2401, - .driver_data = (unsigned long) &hexium_gemini_4bnc, - }, + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2401), + .driver_data = (unsigned long)&hexium_gemini_4bnc, + }, { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2402, - .driver_data = (unsigned long) &hexium_gemini_dual_4bnc, - }, - { - .vendor = 0, - } + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2402), + .driver_data = (unsigned long)&hexium_gemini_dual_4bnc, + }, + { } }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/media/pci/saa7146/hexium_orion.c b/drivers/media/pci/saa7146/hexium_orion.c index 6f1b4bff7596..8ef2a4e8235d 100644 --- a/drivers/media/pci/saa7146/hexium_orion.c +++ b/drivers/media/pci/saa7146/hexium_orion.c @@ -425,29 +425,16 @@ static struct saa7146_pci_extension_data hexium_orion_4bnc = { static const struct pci_device_id pci_tbl[] = { { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, - .driver_data = (unsigned long) &hexium_hv_pci6, - }, - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x0101, - .driver_data = (unsigned long) &hexium_orion_1svhs_3bnc, - }, - { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x17c8, - .subdevice = 0x2101, - .driver_data = (unsigned long) &hexium_orion_4bnc, - }, - { - .vendor = 0, - } + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x0000, 0x0000), + .driver_data = (unsigned long)&hexium_hv_pci6, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x0101), + .driver_data = (unsigned long)&hexium_orion_1svhs_3bnc, + }, { + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x17c8, 0x2101), + .driver_data = (unsigned long)&hexium_orion_4bnc, + }, + { } }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c index d931b4e3052f..d759e8a87e24 100644 --- a/drivers/media/pci/saa7146/mxb.c +++ b/drivers/media/pci/saa7146/mxb.c @@ -821,14 +821,10 @@ static struct saa7146_pci_extension_data mxb = { static const struct pci_device_id pci_tbl[] = { { - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, - .subvendor = 0x0000, - .subdevice = 0x0000, + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, 0x0000, 0x0000), .driver_data = (unsigned long)&mxb, - }, { - .vendor = 0, - } + }, + { } }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 6bcde506adf5..683bd78288b3 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -1535,10 +1535,7 @@ static void saa7164_finidev(struct pci_dev *pci_dev) static const struct pci_device_id saa7164_pci_tbl[] = { { /* SAA7164 */ - .vendor = 0x1131, - .device = 0x7164, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, + PCI_DEVICE(0x1131, 0x7164), }, { /* --- end of list --- */ } diff --git a/drivers/media/pci/smipcie/smipcie-main.c b/drivers/media/pci/smipcie/smipcie-main.c index 387c29958c98..5f18abb48b17 100644 --- a/drivers/media/pci/smipcie/smipcie-main.c +++ b/drivers/media/pci/smipcie/smipcie-main.c @@ -1097,8 +1097,7 @@ static const struct smi_cfg_info technotrend_s2_4200_cfg = { /* PCI IDs */ #define SMI_ID(_subvend, _subdev, _driverdata) { \ - .vendor = SMI_VID, .device = SMI_PID, \ - .subvendor = _subvend, .subdevice = _subdev, \ + PCI_DEVICE_SUB(SMI_VID, SMI_PID, (_subvend), (_subdev)), \ .driver_data = (unsigned long)&_driverdata } static const struct pci_device_id smi_id_table[] = { @@ -1106,7 +1105,7 @@ static const struct pci_device_id smi_id_table[] = { SMI_ID(0x4254, 0x0552, dvbsky_s952_cfg), SMI_ID(0x4254, 0x5580, dvbsky_t9580_cfg), SMI_ID(0x13c2, 0x3016, technotrend_s2_4200_cfg), - {0} + { } }; MODULE_DEVICE_TABLE(pci, smi_id_table); diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 11cddf4b4312..282c2c87151f 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -656,7 +656,7 @@ static const struct pci_device_id solo_id_table[] = { .driver_data = SOLO_DEV_6110 }, { PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16), .driver_data = SOLO_DEV_6110 }, - {0,} + { } }; MODULE_DEVICE_TABLE(pci, solo_id_table); diff --git a/drivers/media/pci/ttpci/budget-ci.c b/drivers/media/pci/ttpci/budget-ci.c index 8b496b959d7e..eb82a670c5bf 100644 --- a/drivers/media/pci/ttpci/budget-ci.c +++ b/drivers/media/pci/ttpci/budget-ci.c @@ -1537,9 +1537,7 @@ static const struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(ttc1501, 0x13c2, 0x101a), MAKE_EXTENSION_PCI(tt3200, 0x13c2, 0x1019), MAKE_EXTENSION_PCI(ttbs1500b, 0x13c2, 0x101b), - { - .vendor = 0, - } + { } }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/media/pci/ttpci/budget.c b/drivers/media/pci/ttpci/budget.c index 95370156aff5..c9f419fc07ad 100644 --- a/drivers/media/pci/ttpci/budget.c +++ b/drivers/media/pci/ttpci/budget.c @@ -858,9 +858,7 @@ static const struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(fsact, 0x1131, 0x5f61), MAKE_EXTENSION_PCI(omicom, 0x14c4, 0x1020), MAKE_EXTENSION_PCI(sylt, 0x1131, 0x4f52), - { - .vendor = 0, - } + { } }; MODULE_DEVICE_TABLE(pci, pci_tbl); diff --git a/drivers/media/pci/tw5864/tw5864-core.c b/drivers/media/pci/tw5864/tw5864-core.c index 832788603f88..6ec8bce4ac4f 100644 --- a/drivers/media/pci/tw5864/tw5864-core.c +++ b/drivers/media/pci/tw5864/tw5864-core.c @@ -72,8 +72,8 @@ MODULE_PARM_DESC(video_nr, "video devices numbers array"); * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ static const struct pci_device_id tw5864_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_5864)}, - {0,} + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_5864) }, + { } }; void tw5864_irqmask_apply(struct tw5864_dev *dev) diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c index 08b7ce1043aa..509d7ddec150 100644 --- a/drivers/media/pci/tw68/tw68-core.c +++ b/drivers/media/pci/tw68/tw68-core.c @@ -62,14 +62,14 @@ static atomic_t tw68_instance = ATOMIC_INIT(0); * added under vendor 0x1797 (Techwell Inc.) as subsystem IDs. */ static const struct pci_device_id tw68_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6800)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6801)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6804)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_1)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_2)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_3)}, - {PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_4)}, - {0,} + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6800) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6801) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6804) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_1) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_2) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_3) }, + { PCI_VDEVICE(TECHWELL, PCI_DEVICE_ID_TECHWELL_6816_4) }, + { } }; /* ------------------------------------------------------------------ */ diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index a10e38221817..5a4ab329c06e 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -416,25 +416,24 @@ static void tw686x_remove(struct pci_dev *pci_dev) /* driver_data is number of A/V channels */ static const struct pci_device_id tw686x_pci_tbl[] = { { - PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6864), - .driver_data = 4 - }, - { - PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6865), /* not tested */ - .driver_data = 4 | TYPE_SECOND_GEN + PCI_VDEVICE(TECHWELL, 0x6864), + .driver_data = 4, + }, { + PCI_VDEVICE(TECHWELL, 0x6865), /* not tested */ + .driver_data = 4 | TYPE_SECOND_GEN, }, /* * TW6868 supports 8 A/V channels with an external TW2865 chip; * not supported by the driver. */ { - PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6868), /* not tested */ - .driver_data = 4 + PCI_VDEVICE(TECHWELL, 0x6868), /* not tested */ + .driver_data = 4, + }, { + PCI_VDEVICE(TECHWELL, 0x6869), + .driver_data = 8 | TYPE_SECOND_GEN }, - { - PCI_DEVICE(PCI_VENDOR_ID_TECHWELL, 0x6869), - .driver_data = 8 | TYPE_SECOND_GEN}, - {} + { } }; MODULE_DEVICE_TABLE(pci, tw686x_pci_tbl); diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index f707bdc1fb0f..8bcf355a5418 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -75,8 +75,9 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(ZORAN_VERSION); #define ZR_DEVICE(subven, subdev, data) { \ - .vendor = PCI_VENDOR_ID_ZORAN, .device = PCI_DEVICE_ID_ZORAN_36057, \ - .subvendor = (subven), .subdevice = (subdev), .driver_data = (data) } + PCI_DEVICE_SUB(PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, \ + (subven), (subdev)), \ + .driver_data = (data) } static const struct pci_device_id zr36067_pci_tbl[] = { ZR_DEVICE(PCI_VENDOR_ID_MIRO, PCI_DEVICE_ID_MIRO_DC10PLUS, DC10_PLUS), @@ -84,7 +85,7 @@ static const struct pci_device_id zr36067_pci_tbl[] = { ZR_DEVICE(PCI_VENDOR_ID_ELECTRONICDESIGNGMBH, PCI_DEVICE_ID_LML_33R10, LML33R10), ZR_DEVICE(PCI_VENDOR_ID_IOMEGA, PCI_DEVICE_ID_IOMEGA_BUZ, BUZ), ZR_DEVICE(PCI_ANY_ID, PCI_ANY_ID, NUM_CARDS), - {0} + { } }; MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl); diff --git a/include/media/drv-intf/saa7146.h b/include/media/drv-intf/saa7146.h index 71ce63c99cb4..b8f2fe5680f0 100644 --- a/include/media/drv-intf/saa7146.h +++ b/include/media/drv-intf/saa7146.h @@ -77,13 +77,10 @@ struct saa7146_pci_extension_data { void *ext_priv; /* most likely a name string */ }; -#define MAKE_EXTENSION_PCI(x_var, x_vendor, x_device) \ - { \ - .vendor = PCI_VENDOR_ID_PHILIPS, \ - .device = PCI_DEVICE_ID_PHILIPS_SAA7146, \ - .subvendor = x_vendor, \ - .subdevice = x_device, \ - .driver_data = (unsigned long)& x_var, \ +#define MAKE_EXTENSION_PCI(x_var, x_vendor, x_device) \ + { \ + PCI_VDEVICE_SUB(PHILIPS, PCI_DEVICE_ID_PHILIPS_SAA7146, (x_vendor), (x_device)),\ + .driver_data = (unsigned long)&(x_var), \ } struct saa7146_extension -- cgit From bf533acaaa7dee21d49bcaabf8e813ea36bddcd8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 30 Jun 2026 14:56:36 +0800 Subject: media: cx18: stop VBI start-code scan before short tail compress_sliced_buf() scans one byte at a time while testing a four-byte VBI start code. The final iterations can read beyond the remaining buffer tail. Stop the scan once fewer than four bytes remain. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/pci/cx18/cx18-vbi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/cx18/cx18-vbi.c b/drivers/media/pci/cx18/cx18-vbi.c index 8dc4ce325935..3a8430f3259e 100644 --- a/drivers/media/pci/cx18/cx18-vbi.c +++ b/drivers/media/pci/cx18/cx18-vbi.c @@ -135,7 +135,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, : VBI_HBLANK_SAMPLES_50HZ; /* find the first valid line */ - for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { + for (i = hdr_size, buf += hdr_size; i + 3 < size; i++, buf++) { if (buf[0] == 0xff && !buf[1] && !buf[2] && (buf[3] == sliced_vbi_eav_rp[0] || buf[3] == sliced_vbi_eav_rp[1])) -- cgit From f373cf17fb8e7a530bc6e1e36fc41cb5dab2cdaa Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 30 Jun 2026 15:19:33 +0800 Subject: media: ivtv: stop VBI start-code scan before short tail compress_sliced_buf() scans one byte at a time while testing a four-byte VBI start code. The final iterations can read beyond the remaining buffer tail. Stop the scan once fewer than four bytes remain. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/pci/ivtv/ivtv-vbi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/ivtv/ivtv-vbi.c b/drivers/media/pci/ivtv/ivtv-vbi.c index ae7a00f46257..af086c4cbe1a 100644 --- a/drivers/media/pci/ivtv/ivtv-vbi.c +++ b/drivers/media/pci/ivtv/ivtv-vbi.c @@ -330,7 +330,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 unsigned lines = 0; /* find the first valid line */ - for (i = 0; i < size; i++, buf++) { + for (i = 0; i + 3 < size; i++, buf++) { if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) break; } -- cgit From 41f82777e547732460290ceb28fcc7c1b95ef192 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:52:57 +0200 Subject: media: go7007: avoid undefined shifts in MPEG-4 headers The byte-alignment paths can call CODE_ADD() with a zero bit length. The macro then shifts an int by name.b, which can be 32 after the preceding bit was flushed. vti_bitlen() can likewise shift a signed int into its sign bit while searching for the bit length, and the package mask construction can shift by the full type width. Make zero-length additions a no-op, derive the bit length with fls(), and build the mask with GENMASK(). Signed-off-by: Yousef Alhouseen Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/go7007-fw.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/media/usb/go7007/go7007-fw.c b/drivers/media/usb/go7007/go7007-fw.c index 86ce593e0c54..8cf959c20e30 100644 --- a/drivers/media/usb/go7007/go7007-fw.c +++ b/drivers/media/usb/go7007/go7007-fw.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -58,14 +59,16 @@ struct code_gen { #define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 } #define CODE_ADD(name, val, length) do { \ - name.b -= (length); \ - name.a |= (val) << name.b; \ - while (name.b <= 24) { \ - *name.p = name.a >> 24; \ - ++name.p; \ - name.a <<= 8; \ - name.b += 8; \ - name.len += 8; \ + if (length) { \ + name.b -= (length); \ + name.a |= (val) << name.b; \ + while (name.b <= 24) { \ + *name.p = name.a >> 24; \ + ++name.p; \ + name.a <<= 8; \ + name.b += 8; \ + name.len += 8; \ + } \ } \ } while (0) @@ -707,11 +710,10 @@ done: static int vti_bitlen(struct go7007 *go) { - unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale; + unsigned int max_time_incr = go->sensor_framerate / go->fps_scale; + int bitlen = fls(max_time_incr); - for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i) - ; - return i + 1; + return bitlen ?: 1; } static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf, @@ -1209,7 +1211,7 @@ static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, 0xbf08, fps, 0xbf09, 0, 0xbff2, vop_time_increment_bitlength, - 0xbff3, (1 << vop_time_increment_bitlength) - 1, + 0xbff3, GENMASK(vop_time_increment_bitlength - 1, 0), 0xbfe6, 0, 0xbfe7, (fps / 1000) << 8, 0, 0, -- cgit From 826915b6b65e2d3251e7248ea54289a22d748c84 Mon Sep 17 00:00:00 2001 From: "Jiangong.Han" Date: Tue, 30 Jun 2026 22:54:34 +0800 Subject: media: em28xx: fix use-after-free of dev_next->devlist on disconnect When a device with has_dual_ts=1 is probed and the is_audio_only path is taken, both dev and dev->dev_next are added to the global em28xx_devlist via em28xx_init_extension(). However, during disconnect, em28xx_close_extension(dev) only calls list_del(&dev->devlist), leaving dev->dev_next->devlist still linked in the global list. When dev_next is subsequently freed via kref_put(), its devlist entry becomes a dangling pointer in em28xx_devlist. The next device probe that calls em28xx_init_extension() triggers a list corruption BUG when list_add_tail detects the freed node. This bug was exposed by commit a368ecde8a50 ("USB: core: Fix duplicate endpoint bug by clearing reserved bits in the descriptor") which clears reserved bits in bEndpointAddress during endpoint parsing. This causes fuzzed endpoint addresses like 0xf3 to be normalized to 0x83, which em28xx interprets as a vendor audio endpoint, enabling the is_audio_only + has_dual_ts code path that was previously unreachable with such descriptors. Fix this by removing dev->dev_next->devlist from the global list in em28xx_close_extension() before the device is freed. Fixes: f410b4093fdd ("media: em28xx: split up em28xx_dvb_init to reduce stack size") Cc: stable@vger.kernel.org Reported-by: syzbot+99d6c66dbbc484f50e1c@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=99d6c66dbbc484f50e1c Signed-off-by: Jiangong.Han Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index d4197e37f637..0b998e7cafef 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1265,6 +1265,8 @@ void em28xx_close_extension(struct em28xx *dev) ops->fini(dev); } } + if (dev->dev_next) + list_del(&dev->dev_next->devlist); list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); } -- cgit From da929031d6eb0bf7e0087eb3223f6f71fd90291e Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:16:38 +0800 Subject: media: stm32: dcmi: Propagate runtime resume errors dcmi_runtime_resume() returns clock enable failures, but dcmi_resume() currently ignores the result of pm_runtime_force_resume() and always returns success. Return the pm_runtime_force_resume() result so system resume does not hide a failed runtime resume. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/platform/st/stm32/stm32-dcmi.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index eeb0199864dd..b1f9af8edf6e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2273,9 +2273,7 @@ static int dcmi_resume(struct device *dev) pinctrl_pm_select_default_state(dev); /* clock enable */ - pm_runtime_force_resume(dev); - - return 0; + return pm_runtime_force_resume(dev); } static const struct dev_pm_ops dcmi_pm_ops = { -- cgit From 0a617f11c866c31dce12204f1da277c1335001c0 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 15:17:40 +0800 Subject: media: stm32: dcmipp: Propagate runtime resume errors dcmipp_runtime_resume() can fail while preparing the device clocks, but dcmipp_resume() currently ignores the result of pm_runtime_force_resume() and always returns success. Return the pm_runtime_force_resume() result so system resume does not hide a failed runtime resume. Signed-off-by: Pengpeng Hou Signed-off-by: Hans Verkuil --- drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c index 49398d077764..516334541b2c 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c +++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c @@ -649,9 +649,7 @@ static int dcmipp_resume(struct device *dev) pinctrl_pm_select_default_state(dev); /* clock enable */ - pm_runtime_force_resume(dev); - - return 0; + return pm_runtime_force_resume(dev); } static const struct dev_pm_ops dcmipp_pm_ops = { -- cgit From 4e1d07cfc12fe5e2a15199b53fb8bdd077f1afbf Mon Sep 17 00:00:00 2001 From: Joseph Breihan Date: Sun, 5 Jul 2026 01:10:43 -0400 Subject: media: au0828: add support for Monoprice 106456 USB ATSC/QAM tuner The Monoprice 106456 (board rev TV22AD-A, also sold as the AnyTV AUTV002) is a USB ATSC/QAM tuner that enumerates as 05e1:0400 and reports itself as a "Syntek Semiconductor USB 2.0 Video Capture Controller". The hardware is an Auvitek AU0828A bridge with an AU8522 demodulator and an NXP TDA18271HDC2 tuner at I2C address 0x60 -- the same demod/tuner combination as the existing Hauppauge Woodbury board. Adds board profile and the USB ID so the device binds. The digital frontend is electrically identical to the Woodbury, so share that board's au8522/tda18271 configuration and attach path rather than duplicating it. Tested with over-the-air 8VSB ATSC: it locks and streams a live 720p transport stream. Some 05e1:0400 units reportedly ship a Microtune MT2131 tuner with an AU8502 demodulator instead. This patch does not handle that variant, but it can't regress it either: 05e1:0400 matches no driver today, and on the MT2131 units tda18271_attach() just fails, so no frontend is registered. Signed-off-by: Joseph Breihan Signed-off-by: Hans Verkuil --- drivers/media/usb/au0828/au0828-cards.c | 14 ++++++++++++++ drivers/media/usb/au0828/au0828-cards.h | 1 + drivers/media/usb/au0828/au0828-dvb.c | 1 + 3 files changed, 16 insertions(+) diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index 9929ce5dcdcd..8ab100db01ab 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -124,6 +124,17 @@ struct au0828_board au0828_boards[] = { }, }, }, + [AU0828_BOARD_MONOPRICE_106456] = { + /* + * Monoprice 106456 USB ATSC/QAM tuner (board rev TV22AD-A), + * a.k.a. AnyTV AUTV002, USB ID 05e1:0400. Same AU8522 demod + + * NXP TDA18271HDC2 tuner @ 0x60 as the Hauppauge Woodbury. + */ + .name = "Monoprice 106456 USB ATSC/QAM (TV22AD-A)", + .tuner_type = TUNER_NXP_TDA18271, + .tuner_addr = 0x60, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, + }, [AU0828_BOARD_HAUPPAUGE_HVR1265] = { .name = "Hauppauge HVR1265", .tuner_type = TUNER_XC5000, @@ -294,6 +305,7 @@ void au0828_gpio_setup(struct au0828_dev *dev) case AU0828_BOARD_HAUPPAUGE_WOODBURY: case AU0828_BOARD_HAUPPAUGE_HVR1265: case AU0828_BOARD_HAUPPAUGE_IMPACTVCBE: + case AU0828_BOARD_MONOPRICE_106456: /* GPIO's * 4 - CS5340 * 5 - AU8522 Demodulator @@ -378,6 +390,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, { USB_DEVICE(0x05e1, 0x0480), .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, + { USB_DEVICE(0x05e1, 0x0400), + .driver_info = AU0828_BOARD_MONOPRICE_106456 }, { USB_DEVICE(0x2040, 0x8200), .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, { USB_DEVICE(0x2040, 0x7260), diff --git a/drivers/media/usb/au0828/au0828-cards.h b/drivers/media/usb/au0828/au0828-cards.h index a438aeb334a0..a4a283731f7d 100644 --- a/drivers/media/usb/au0828/au0828-cards.h +++ b/drivers/media/usb/au0828/au0828-cards.h @@ -13,3 +13,4 @@ #define AU0828_BOARD_HAUPPAUGE_WOODBURY 5 #define AU0828_BOARD_HAUPPAUGE_IMPACTVCBE 6 #define AU0828_BOARD_HAUPPAUGE_HVR1265 7 +#define AU0828_BOARD_MONOPRICE_106456 8 diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c index 31123e6f9fc3..9c95b7ceaecd 100644 --- a/drivers/media/usb/au0828/au0828-dvb.c +++ b/drivers/media/usb/au0828/au0828-dvb.c @@ -600,6 +600,7 @@ int au0828_dvb_register(struct au0828_dev *dev) &mxl5007t_hvr950q_config); break; case AU0828_BOARD_HAUPPAUGE_WOODBURY: + case AU0828_BOARD_MONOPRICE_106456: dvb->frontend = dvb_attach(au8522_attach, &hauppauge_woodbury_config, &dev->i2c_adap); -- cgit From 76e379754ba618989f6215be608d5c04774a611d Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 6 Jul 2026 20:58:33 +0530 Subject: media: video-i2c: fix kthread error pointer left in kthread_vid_cap on failure kthread_run() returns an ERR_PTR on failure, not NULL. When start_streaming() fails, data->kthread_vid_cap is left holding this error pointer instead of being cleared. This causes two subsequent bugs: 1. A future call to start_streaming() sees a non-NULL kthread_vid_cap and returns 0 (success) immediately, without actually starting the capture thread. 2. A call to stop_streaming() checks 'kthread_vid_cap == NULL' which is false for an error pointer, and proceeds to call kthread_stop() on the error pointer, leading to a kernel crash. Fix this by resetting kthread_vid_cap to NULL on failure before jumping to the error path. Fixes: 5cebaac60974 ("media: video-i2c: add video-i2c driver") Cc: stable@vger.kernel.org Signed-off-by: Uday Khare Signed-off-by: Hans Verkuil --- drivers/media/i2c/video-i2c.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 56b99eea54a1..7f971645c0b5 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -523,8 +523,12 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, "%s-vid-cap", data->v4l2_dev.name); ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap); - if (!ret) - return 0; + if (ret) { + data->kthread_vid_cap = NULL; + goto error_rpm_put; + } + + return 0; error_rpm_put: pm_runtime_put_autosuspend(dev); -- cgit From fc530fe168bb2b745a93f553ad21fc25fd9cba3d Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Mon, 6 Jul 2026 16:24:06 -0400 Subject: media: usbtv: keep device alive while ALSA card exists The ALSA PCM callbacks store the driver state in pcm->private_data. An open PCM file can outlive USB disconnect because usbtv_audio_free() uses snd_card_free_when_closed(). The disconnect path can then drop the V4L2 device reference and free struct usbtv before ALSA releases the substream, so a later close dereferences freed memory in snd_usbtv_pcm_close(). Take a V4L2 device reference for the ALSA card and drop it from the card private_free callback. This keeps struct usbtv valid until ALSA has closed the remaining files and freed the card. Closes: https://lore.kernel.org/r/178144969601.60470.4852887710381872458@gmail.com Fixes: 63ddf68de52e ("[media] usbtv: add audio support") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Signed-off-by: Hans Verkuil --- drivers/media/usb/usbtv/usbtv-audio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/media/usb/usbtv/usbtv-audio.c b/drivers/media/usb/usbtv/usbtv-audio.c index 333bd305a4f9..ae0a14e5ed2a 100644 --- a/drivers/media/usb/usbtv/usbtv-audio.c +++ b/drivers/media/usb/usbtv/usbtv-audio.c @@ -317,6 +317,13 @@ static const struct snd_pcm_ops snd_usbtv_pcm_ops = { .pointer = snd_usbtv_pointer, }; +static void usbtv_audio_card_free(struct snd_card *card) +{ + struct usbtv *usbtv = card->private_data; + + v4l2_device_put(&usbtv->v4l2_dev); +} + int usbtv_audio_init(struct usbtv *usbtv) { int rv; @@ -331,6 +338,10 @@ int usbtv_audio_init(struct usbtv *usbtv) if (rv < 0) return rv; + v4l2_device_get(&usbtv->v4l2_dev); + card->private_data = usbtv; + card->private_free = usbtv_audio_card_free; + strscpy(card->driver, usbtv->dev->driver->name, sizeof(card->driver)); strscpy(card->shortname, "usbtv", sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), -- cgit From 1bd456afeb8a515137e567967069fce6f8fcd23e Mon Sep 17 00:00:00 2001 From: Shuangpeng Bai Date: Mon, 6 Jul 2026 16:50:15 -0400 Subject: media: go7007: defer the ALSA v4l2 put until card release go7007_snd_init() already takes a v4l2_device reference for the ALSA side, but go7007_snd_remove() drops it immediately after calling snd_card_free_when_closed(). That is too early when a userspace process still has the capture PCM open. The ALSA card and its PCM callbacks remain alive until the last file is closed, so the release path can still reach struct go7007 through pcm->private_data and call go7007_snd_hw_free() after the V4L2 release path has freed the object. Move the matching v4l2_device_put() to the ALSA card private_free callback so the existing ALSA reference covers the whole deferred card lifetime. Closes: https://lore.kernel.org/r/178144969601.60470.6005237146425573205@gmail.com Fixes: d5d3a7cc127d ("[media] go7007: fix unregister/disconnect handling") Cc: stable@vger.kernel.org Signed-off-by: Shuangpeng Bai Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/snd-go7007.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/usb/go7007/snd-go7007.c b/drivers/media/usb/go7007/snd-go7007.c index e4b9f37be77b..01d547ad678d 100644 --- a/drivers/media/usb/go7007/snd-go7007.c +++ b/drivers/media/usb/go7007/snd-go7007.c @@ -195,6 +195,13 @@ static const struct snd_device_ops go7007_snd_device_ops = { .dev_free = go7007_snd_free, }; +static void go7007_snd_card_free(struct snd_card *card) +{ + struct go7007 *go = card->private_data; + + v4l2_device_put(&go->v4l2_dev); +} + int go7007_snd_init(struct go7007 *go) { static int dev; @@ -245,6 +252,8 @@ int go7007_snd_init(struct go7007 *go) gosnd->substream = NULL; go->snd_context = gosnd; v4l2_device_get(&go->v4l2_dev); + gosnd->card->private_data = go; + gosnd->card->private_free = go7007_snd_card_free; ++dev; return 0; @@ -263,7 +272,6 @@ int go7007_snd_remove(struct go7007 *go) snd_card_disconnect(gosnd->card); snd_card_free_when_closed(gosnd->card); - v4l2_device_put(&go->v4l2_dev); return 0; } EXPORT_SYMBOL(go7007_snd_remove); -- cgit From 7393372f79db940acff206b43e2905685a0c57ad Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Tue, 7 Jul 2026 23:03:26 +0800 Subject: staging: media: tegra-video: fix of_node_put() on VIP parse errors tegra_vip_channel_of_parse() initializes np from dev->of_node without taking a reference, but its error paths drop one through the err_node_put label. This underflows the refcount of the VIP device's OF node when endpoint parsing fails on a malformed device tree. The only reference the function takes on np is the success-path of_node_get() stored in vip->chan.of_node, and that one is already released by the tegra_vip_init() error path and by tegra_vip_exit(). Return errors directly instead of jumping to the bogus cleanup label. Fixes: e740d199cf0f ("staging: media: tegra-video: add support for Tegra20 parallel input") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vip.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/staging/media/tegra-video/vip.c b/drivers/staging/media/tegra-video/vip.c index 9ff1f1750a15..5fba11e31e1d 100644 --- a/drivers/staging/media/tegra-video/vip.c +++ b/drivers/staging/media/tegra-video/vip.c @@ -126,7 +126,7 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) if (!ep) { err = -EINVAL; dev_err_probe(dev, err, "%pOF: error getting endpoint node\n", np); - goto err_node_put; + return err; } fwh = of_fwnode_handle(ep); @@ -134,14 +134,14 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) of_node_put(ep); if (err) { dev_err_probe(dev, err, "%pOF: failed to parse v4l2 endpoint\n", np); - goto err_node_put; + return err; } num_pads = of_graph_get_endpoint_count(np); if (num_pads != TEGRA_VIP_PADS_NUM) { err = -EINVAL; dev_err_probe(dev, err, "%pOF: need 2 pads, got %d\n", np, num_pads); - goto err_node_put; + return err; } vip->chan.of_node = of_node_get(np); @@ -149,10 +149,6 @@ static int tegra_vip_channel_of_parse(struct tegra_vip *vip) vip->chan.pads[TEGRA_VIP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; return 0; - -err_node_put: - of_node_put(np); - return err; } static int tegra_vip_channel_init(struct tegra_vip *vip) -- cgit From ae15adeed9f7ec54989175fe3c9e0815186821bc Mon Sep 17 00:00:00 2001 From: Hao-Qun Huang Date: Tue, 7 Jul 2026 23:24:25 +0800 Subject: staging: media: tegra-video: vi: fix probe failure on skipped last port tegra_vi_channels_alloc() iterates over port nodes and skips those whose reg property cannot be read or whose remote endpoint fails v4l2_fwnode_endpoint_parse(), leaving the negative result of the failed call in ret. If that happens on the last port node, the loop ends with ret still negative and tegra_vi_init() fails the whole VI probe. The same defective port earlier in the ports node is skipped silently, so probing succeeds or fails depending on the order of the port nodes. The CSI equivalent, tegra_csi_channels_alloc(), returns 0 unconditionally after its loop and does not have this problem. Use a separate variable for the per-port checks so that only fatal errors end up in ret. Fixes: 1ebaeb09830f ("media: tegra-video: Add support for external sensor capture") Fixes: 2ac4035a78c9 ("media: tegra-video: Add support for x8 captures with gang ports") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5 Signed-off-by: Hao-Qun Huang Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/vi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c index ce09949178f6..01622013c109 100644 --- a/drivers/staging/media/tegra-video/vi.c +++ b/drivers/staging/media/tegra-video/vi.c @@ -1257,6 +1257,7 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) struct device_node *parent; struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; unsigned int lanes; + int err; int ret = 0; ports = of_get_child_by_name(node, "ports"); @@ -1267,8 +1268,8 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) if (!of_node_name_eq(port, "port")) continue; - ret = of_property_read_u32(port, "reg", &port_num); - if (ret < 0) + err = of_property_read_u32(port, "reg", &port_num); + if (err < 0) continue; if (port_num > vi->soc->vi_max_channels) { @@ -1289,10 +1290,10 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi) ep = of_graph_get_endpoint_by_regs(parent, 0, 0); of_node_put(parent); - ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), + err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep); of_node_put(ep); - if (ret) + if (err) continue; lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; -- cgit From 3a7d6b9c4cb5ac18cbd3f1c7f8c7b159c42ba0b1 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:33:38 +0800 Subject: media: cobalt: Avoid freeing ALSA private data twice snd_cobalt_card_create() stores cobsc in sc->private_data and installs snd_cobalt_card_private_free() as sc->private_free. From that point, snd_card_free(sc) releases cobsc through the ALSA card cleanup path. If cobalt_alsa_init() fails after snd_cobalt_card_create(), the err_exit_free path calls snd_card_free(sc) and then kfree(cobsc). That second free releases the same object again. Remove the explicit kfree(cobsc) and leave ownership with the ALSA card. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 85756a069c55 ("[media] cobalt: add new driver") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Hans Verkuil --- drivers/media/pci/cobalt/cobalt-alsa-main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/pci/cobalt/cobalt-alsa-main.c b/drivers/media/pci/cobalt/cobalt-alsa-main.c index 7bb7f13c70c0..9ed547cd40af 100644 --- a/drivers/media/pci/cobalt/cobalt-alsa-main.c +++ b/drivers/media/pci/cobalt/cobalt-alsa-main.c @@ -135,7 +135,6 @@ int cobalt_alsa_init(struct cobalt_stream *s) err_exit_free: if (sc != NULL) snd_card_free(sc); - kfree(cobsc); err_exit: return ret; } -- cgit From 083362373ef4a301a333080bf601484071c79ea9 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 9 Jul 2026 15:21:23 +0200 Subject: media: stk1160: cleanup error handling in probe Do not replicate cleanup code. Signed-off-by: Oliver Neukum Signed-off-by: Hans Verkuil --- drivers/media/usb/stk1160/stk1160-core.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/stk1160/stk1160-core.c b/drivers/media/usb/stk1160/stk1160-core.c index f9462a9ca761..5058cada0d0a 100644 --- a/drivers/media/usb/stk1160/stk1160-core.c +++ b/drivers/media/usb/stk1160/stk1160-core.c @@ -263,7 +263,7 @@ static int stk1160_scan_usb(struct usb_interface *intf, struct usb_device *udev, static int stk1160_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int rc = 0; + int rc; unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ struct usb_device *udev; @@ -290,15 +290,13 @@ static int stk1160_probe(struct usb_interface *interface, * Also, check if device speed is fast enough. */ rc = stk1160_scan_usb(interface, udev, alt_max_pkt_size); - if (rc < 0) { - kfree(alt_max_pkt_size); - return rc; - } + if (rc < 0) + goto free_array; dev = kzalloc_obj(struct stk1160); if (dev == NULL) { - kfree(alt_max_pkt_size); - return -ENOMEM; + rc = -ENOMEM; + goto free_array; } dev->alt_max_pkt_size = alt_max_pkt_size; @@ -379,8 +377,9 @@ unreg_v4l2: free_ctrl: v4l2_ctrl_handler_free(&dev->ctrl_handler); free_err: - kfree(alt_max_pkt_size); kfree(dev); +free_array: + kfree(alt_max_pkt_size); return rc; } -- cgit From 3839365cf0d4ca08bf1026d5adaa50b9c7ffca41 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Thu, 9 Jul 2026 15:21:24 +0200 Subject: media: stk1160: handle ESHUTDOWN gracefully Give it its own error message. Signed-off-by: Oliver Neukum Signed-off-by: Hans Verkuil --- drivers/media/usb/stk1160/stk1160-video.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/usb/stk1160/stk1160-video.c b/drivers/media/usb/stk1160/stk1160-video.c index f4baf9263286..020b75fc5b2d 100644 --- a/drivers/media/usb/stk1160/stk1160-video.c +++ b/drivers/media/usb/stk1160/stk1160-video.c @@ -48,6 +48,9 @@ static inline void print_err_status(struct stk1160 *dev, case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; + case -ESHUTDOWN: + errmsg = "host controller removed"; + break; case -ETIME: errmsg = "Device does not respond"; break; -- cgit From cd05b6174e505c09304bee49894096706df4cc99 Mon Sep 17 00:00:00 2001 From: Tharit Tangkijwanichakul Date: Mon, 13 Jul 2026 23:24:03 +0700 Subject: media: videobuf2: move vb2_querybuf() kernel-doc to header Move the function documentation for vb2_querybuf() from videobuf2-v4l2.c to videobuf2-v4l2.h to match other functions. Leaving the comment in the source file prevents the kernel-doc parser from extracting it - thus become missing from the generated HTML. Additionally, format the comment block to be the same style as other functions in the header. Signed-off-by: Tharit Tangkijwanichakul Signed-off-by: Hans Verkuil --- drivers/media/common/videobuf2/videobuf2-v4l2.c | 13 ------------- include/media/videobuf2-v4l2.h | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c index 4baded4fd3b8..5a1ee5eb9c0f 100644 --- a/drivers/media/common/videobuf2/videobuf2-v4l2.c +++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c @@ -635,19 +635,6 @@ struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp) } EXPORT_SYMBOL_GPL(vb2_find_buffer); -/* - * vb2_querybuf() - query video buffer information - * @q: vb2 queue - * @b: buffer struct passed from userspace to vidioc_querybuf handler - * in driver - * - * Should be called from vidioc_querybuf ioctl handler in driver. - * This function will verify the passed v4l2_buffer structure and fill the - * relevant information for the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_querybuf handler in driver. - */ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) { struct vb2_buffer *vb; diff --git a/include/media/videobuf2-v4l2.h b/include/media/videobuf2-v4l2.h index 71d2864fb235..d51e10c10fc0 100644 --- a/include/media/videobuf2-v4l2.h +++ b/include/media/videobuf2-v4l2.h @@ -72,6 +72,19 @@ struct vb2_v4l2_buffer { */ struct vb2_buffer *vb2_find_buffer(struct vb2_queue *q, u64 timestamp); +/** + * vb2_querybuf() - Query video buffer information + * @q: pointer to &struct vb2_queue with videobuf2 queue. + * @b: buffer structure passed from userspace to + * &v4l2_ioctl_ops->vidioc_querybuf handler in driver + * + * Should be called from vidioc_querybuf ioctl handler in driver. + * This function will verify the passed v4l2_buffer structure and fill the + * relevant information for the userspace. + * + * The return values from this function are intended to be directly returned + * from vidioc_querybuf handler in driver. + */ int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); /** -- cgit From 330f2936ab768c7215322a476f033143e8891d28 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Tue, 14 Jul 2026 14:52:02 +0800 Subject: media: s2255: check firmware size before reading trailing marker s2255_probe() reads a 4-byte marker and version from the last 8 bytes of the firmware blob (fw->data[fw_size - 8] and [fw_size - 4]). If the firmware file is shorter than 8 bytes, fw_size - 8 underflows and the access reads out of bounds. Validate the firmware size before indexing. Fixes: 14d962602c8b ("V4L/DVB (8752): s2255drv: firmware improvement patch") Cc: stable@vger.kernel.org Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 0b8182edf8e4..3917913c3b47 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2277,6 +2277,11 @@ static int s2255_probe(struct usb_interface *interface, } /* check the firmware is valid */ fw_size = dev->fw_data->fw->size; + if (fw_size < 8) { + dev_err(&interface->dev, "Firmware invalid: too small.\n"); + retval = -ENODEV; + goto errorFWMARKER; + } pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; if (*pdata != S2255_FW_MARKER) { -- cgit From bf2909f85c8cfb817d14a86129a579bd77595f3f Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Wed, 15 Jul 2026 09:56:00 +0800 Subject: media: go7007: fix memory leaks on probe error paths In go7007_register_encoder(), if any step after v4l2_device_register() fails (go7007_init_encoder, go7007_v4l2_ctrl_init, go7007_i2c_init, go7007_v4l2_init), the function returns directly without cleanup, leaking the registered V4L2 device and the control handler (if already initialized). Add cascade error labels that properly release resources according to how far initialization has progressed: - err_free_controls: frees the control handler - err_unregister_v4l2_dev: unregisters the V4L2 device (reached from any failure after v4l2_device_register) Signed-off-by: Chen Changcheng Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/go7007-driver.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c index 25b3ee25aaa4..453ab5c3aa03 100644 --- a/drivers/media/usb/go7007/go7007-driver.c +++ b/drivers/media/usb/go7007/go7007-driver.c @@ -264,17 +264,17 @@ int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) ret = go7007_init_encoder(go); mutex_unlock(&go->hw_lock); if (ret < 0) - return ret; + goto err_unregister_v4l2_dev; ret = go7007_v4l2_ctrl_init(go); if (ret < 0) - return ret; + goto err_free_controls; if (!go->i2c_adapter_online && go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { ret = go7007_i2c_init(go); if (ret < 0) - return ret; + goto err_free_controls; go->i2c_adapter_online = 1; } if (go->i2c_adapter_online) { @@ -304,13 +304,19 @@ int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs) ret = go7007_v4l2_init(go); if (ret < 0) - return ret; + goto err_free_controls; if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { go->audio_enabled = 1; go7007_snd_init(go); } return 0; + +err_free_controls: + v4l2_ctrl_handler_free(&go->hdl); +err_unregister_v4l2_dev: + v4l2_device_unregister(&go->v4l2_dev); + return ret; } EXPORT_SYMBOL(go7007_register_encoder); -- cgit From bb03a00d68c147fe7b1fd1a8475c078613c30ec4 Mon Sep 17 00:00:00 2001 From: Ricardo Ribalda Date: Mon, 29 Jun 2026 11:48:28 +0000 Subject: media: atomisp: Demote all broken kernel-doc comments The atomisp code has been very heavily annotated in doxygen format. Unfortunately, both doxygen and kernel-doc use the same mark for signify the beginning of a comment (/**). As a result of this, kernel-doc gets confused when it parses atomisp and throws tons of warnings, which made us completely disable atomisp kernel-doc checks in Media-CI. Demote all the broken comments so we can have 100% coverage. Future patches can convert the doxygen annotations to kernel-doc if this is deemed useful. Signed-off-by: Ricardo Ribalda Reviewed-by: Andy Shevchenko Signed-off-by: Hans Verkuil --- .../pci/base/circbuf/interface/ia_css_circbuf.h | 40 ++++++++++----------- .../base/circbuf/interface/ia_css_circbuf_comm.h | 4 +-- .../base/circbuf/interface/ia_css_circbuf_desc.h | 14 ++++---- .../pci/camera/util/interface/ia_css_util.h | 2 +- .../pci/css_2401_system/host/csi_rx_private.h | 18 +++++----- .../pci/css_2401_system/host/isys_irq_private.h | 4 +-- .../host/isys_stream2mmio_private.h | 12 +++---- .../pci/css_2401_system/host/pixelgen_private.h | 8 ++--- .../atomisp/pci/css_2401_system/pixelgen_global.h | 2 +- .../pci/hive_isp_css_common/timed_ctrl_global.h | 2 +- .../device_access/device_access.h | 2 +- .../pci/hive_isp_css_include/host/csi_rx_public.h | 18 +++++----- .../host/isys_stream2mmio_public.h | 12 +++---- .../hive_isp_css_include/host/pixelgen_public.h | 8 ++--- .../pci/hive_isp_css_include/host/tag_public.h | 4 +-- .../pci/hive_isp_css_include/platform_support.h | 2 +- .../pci/hive_isp_css_include/type_support.h | 2 +- .../media/atomisp/pci/ia_css_frame_public.h | 2 +- .../staging/media/atomisp/pci/ia_css_host_data.h | 4 +-- .../staging/media/atomisp/pci/ia_css_pipe_public.h | 8 ++--- drivers/staging/media/atomisp/pci/ia_css_prbs.h | 4 +-- drivers/staging/media/atomisp/pci/ia_css_stream.h | 2 +- drivers/staging/media/atomisp/pci/ia_css_timer.h | 7 ++-- drivers/staging/media/atomisp/pci/ia_css_types.h | 6 ++-- .../conversion_1.0/ia_css_conversion_types.h | 2 +- .../pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h | 2 +- .../atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h | 42 +++++++++++----------- .../ipu2_io_ls/common/ia_css_common_io_param.h | 2 +- .../ipu2_io_ls/common/ia_css_common_io_types.h | 2 +- .../yuv444_io_ls/ia_css_yuv444_io.host.h | 2 +- .../yuv444_io_ls/ia_css_yuv444_io_param.h | 2 +- .../yuv444_io_ls/ia_css_yuv444_io_types.h | 2 +- .../pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h | 4 +-- .../isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h | 14 ++++---- .../pci/isp/modes/interface/input_buf.isp.h | 2 +- .../pci/runtime/binary/interface/ia_css_binary.h | 2 +- .../pci/runtime/bufq/interface/ia_css_bufq.h | 26 +++++++------- .../pci/runtime/debug/interface/ia_css_debug.h | 10 +++--- .../runtime/debug/interface/ia_css_debug_pipe.h | 10 +++--- .../pci/runtime/eventq/interface/ia_css_eventq.h | 4 +-- .../isp_param/interface/ia_css_isp_param_types.h | 2 +- .../pci/runtime/isys/interface/ia_css_isys.h | 8 ++--- .../runtime/pipeline/interface/ia_css_pipeline.h | 10 +++--- .../pci/runtime/rmgr/interface/ia_css_rmgr.h | 4 +-- .../pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h | 18 +++++----- .../tagger/interface/ia_css_tagger_common.h | 4 +-- .../staging/media/atomisp/pci/sh_css_internal.h | 4 +-- drivers/staging/media/atomisp/pci/sh_css_sp.h | 14 ++++---- drivers/staging/media/atomisp/pci/system_global.h | 4 +-- 49 files changed, 194 insertions(+), 189 deletions(-) diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h index 4732b45b25ee..fbcd73b711ba 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h @@ -20,7 +20,7 @@ * Data structures. * ****************************************************************/ -/** +/* * @brief Data structure for the circular buffer. */ typedef struct ia_css_circbuf_s ia_css_circbuf_t; @@ -29,7 +29,7 @@ struct ia_css_circbuf_s { ia_css_circbuf_elem_t *elems; /* an array of elements */ }; -/** +/* * @brief Create the circular buffer. * * @param cb The pointer to the circular buffer. @@ -41,7 +41,7 @@ void ia_css_circbuf_create( ia_css_circbuf_elem_t *elems, ia_css_circbuf_desc_t *desc); -/** +/* * @brief Destroy the circular buffer. * * @param cb The pointer to the circular buffer. @@ -49,7 +49,7 @@ void ia_css_circbuf_create( void ia_css_circbuf_destroy( ia_css_circbuf_t *cb); -/** +/* * @brief Pop a value out of the circular buffer. * Get a value at the head of the circular buffer. * The user should call "ia_css_circbuf_is_empty()" @@ -62,7 +62,7 @@ void ia_css_circbuf_destroy( uint32_t ia_css_circbuf_pop( ia_css_circbuf_t *cb); -/** +/* * @brief Extract a value out of the circular buffer. * Get a value at an arbitrary position in the circular * buffer. The user should call "ia_css_circbuf_is_empty()" @@ -82,7 +82,7 @@ uint32_t ia_css_circbuf_extract( * Inline functions. * ****************************************************************/ -/** +/* * @brief Set the "val" field in the element. * * @param elem The pointer to the element. @@ -97,7 +97,7 @@ static inline void ia_css_circbuf_elem_set_val( elem->val = val; } -/** +/* * @brief Initialize the element. * * @param elem The pointer to the element. @@ -109,7 +109,7 @@ static inline void ia_css_circbuf_elem_init( ia_css_circbuf_elem_set_val(elem, 0); } -/** +/* * @brief Copy an element. * * @param src The element as the copy source. @@ -125,7 +125,7 @@ static inline void ia_css_circbuf_elem_cpy( ia_css_circbuf_elem_set_val(dest, src->val); } -/** +/* * @brief Get position in the circular buffer. * * @param cb The pointer to the circular buffer. @@ -151,7 +151,7 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset( return (base + offset) % cb->desc->size; } -/** +/* * @brief Get the offset between two positions in the circular buffer. * Get the offset from the source position to the terminal position, * along the direction in which the new elements come in. @@ -178,7 +178,7 @@ static inline int ia_css_circbuf_get_offset( return offset; } -/** +/* * @brief Get the maximum number of elements. * * @param cb The pointer to the circular buffer. @@ -196,7 +196,7 @@ static inline uint32_t ia_css_circbuf_get_size( return cb->desc->size; } -/** +/* * @brief Get the number of available elements. * * @param cb The pointer to the circular buffer. @@ -216,7 +216,7 @@ static inline uint32_t ia_css_circbuf_get_num_elems( return (uint32_t)num; } -/** +/* * @brief Test if the circular buffer is empty. * * @param cb The pointer to the circular buffer. @@ -234,7 +234,7 @@ static inline bool ia_css_circbuf_is_empty( return ia_css_circbuf_desc_is_empty(cb->desc); } -/** +/* * @brief Test if the circular buffer is full. * * @param cb The pointer to the circular buffer. @@ -251,7 +251,7 @@ static inline bool ia_css_circbuf_is_full(ia_css_circbuf_t *cb) return ia_css_circbuf_desc_is_full(cb->desc); } -/** +/* * @brief Write a new element into the circular buffer. * Write a new element WITHOUT checking whether the * circular buffer is full or not. So it also overwrites @@ -275,7 +275,7 @@ static inline void ia_css_circbuf_write( cb->desc->end = ia_css_circbuf_get_pos_at_offset(cb, cb->desc->end, 1); } -/** +/* * @brief Push a value in the circular buffer. * Put a new value at the tail of the circular buffer. * The user should call "ia_css_circbuf_is_full()" @@ -300,7 +300,7 @@ static inline void ia_css_circbuf_push( ia_css_circbuf_write(cb, elem); } -/** +/* * @brief Get the number of free elements. * * @param cb The pointer to the circular buffer. @@ -316,7 +316,7 @@ static inline uint32_t ia_css_circbuf_get_free_elems( return ia_css_circbuf_desc_get_free_elems(cb->desc); } -/** +/* * @brief Peek an element in Circular Buffer. * * @param cb The pointer to the circular buffer. @@ -328,7 +328,7 @@ uint32_t ia_css_circbuf_peek( ia_css_circbuf_t *cb, int offset); -/** +/* * @brief Get an element in Circular Buffer. * * @param cb The pointer to the circular buffer. @@ -340,7 +340,7 @@ uint32_t ia_css_circbuf_peek_from_start( ia_css_circbuf_t *cb, int offset); -/** +/* * @brief Increase Size of a Circular Buffer. * Use 'CAUTION' before using this function, This was added to * support / fix issue with increasing size for tagger only diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h index 971e07f2acc5..090e2e764042 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_comm.h @@ -24,7 +24,7 @@ * Portable Data structures * ****************************************************************/ -/** +/* * @brief Data structure for the circular descriptor. */ typedef struct ia_css_circbuf_desc_s ia_css_circbuf_desc_t; @@ -40,7 +40,7 @@ struct ia_css_circbuf_desc_s { static_assert(sizeof(struct ia_css_circbuf_desc_s) == SIZE_OF_IA_CSS_CIRCBUF_DESC_S_STRUCT); -/** +/* * @brief Data structure for the circular buffer element. */ typedef struct ia_css_circbuf_elem_s ia_css_circbuf_elem_t; diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h index 64f754f1d49b..3306a7a0fa5e 100644 --- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h +++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h @@ -17,7 +17,7 @@ * Inline functions. * ****************************************************************/ -/** +/* * @brief Test if the circular buffer is empty. * * @param cb_desc The pointer to the circular buffer descriptor. @@ -33,7 +33,7 @@ static inline bool ia_css_circbuf_desc_is_empty( return (cb_desc->end == cb_desc->start); } -/** +/* * @brief Test if the circular buffer descriptor is full. * * @param cb_desc The pointer to the circular buffer @@ -50,7 +50,7 @@ static inline bool ia_css_circbuf_desc_is_full( return ((cb_desc->end + 1) % cb_desc->size) == cb_desc->start; } -/** +/* * @brief Initialize the circular buffer descriptor * * @param cb_desc The pointer circular buffer descriptor @@ -64,7 +64,7 @@ static inline void ia_css_circbuf_desc_init( cb_desc->size = size; } -/** +/* * @brief Get a position in the circular buffer descriptor. * * @param cb The pointer to the circular buffer descriptor. @@ -89,7 +89,7 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset( return (base + offset) % cb_desc->size; } -/** +/* * @brief Get the offset between two positions in the circular buffer * descriptor. * Get the offset from the source position to the terminal position, @@ -116,7 +116,7 @@ static inline int ia_css_circbuf_desc_get_offset( return offset; } -/** +/* * @brief Get the number of available elements. * * @param cb_desc The pointer to the circular buffer. @@ -137,7 +137,7 @@ static inline uint32_t ia_css_circbuf_desc_get_num_elems( return (uint32_t)num; } -/** +/* * @brief Get the number of free elements. * * @param cb_desc The pointer to the circular buffer descriptor. diff --git a/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h b/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h index 24cd99a659ca..dce8dd23b47c 100644 --- a/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h +++ b/drivers/staging/media/atomisp/pci/camera/util/interface/ia_css_util.h @@ -81,7 +81,7 @@ bool ia_css_util_res_leq( struct ia_css_resolution b); /* ISP2401 */ -/** +/* * @brief Check if resolution is zero * * @param[in] resolution The resolution to check diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_private.h index 989f55bec519..43bb352a8e05 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/csi_rx_private.h @@ -21,7 +21,7 @@ * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Refer to "csi_rx_public.h" for details. */ @@ -35,7 +35,7 @@ static inline hrt_data csi_rx_fe_ctrl_reg_load( hrt_data)); } -/** +/* * @brief Store a value to the register. * Refer to "ibuf_ctrl_public.h" for details. */ @@ -51,7 +51,7 @@ static inline void csi_rx_fe_ctrl_reg_store( value); } -/** +/* * @brief Load the register value. * Refer to "csi_rx_public.h" for details. */ @@ -65,7 +65,7 @@ static inline hrt_data csi_rx_be_ctrl_reg_load( hrt_data)); } -/** +/* * @brief Store a value to the register. * Refer to "ibuf_ctrl_public.h" for details. */ @@ -88,7 +88,7 @@ static inline void csi_rx_be_ctrl_reg_store( * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the state of the csi rx fe dlane process. * Refer to "csi_rx_public.h" for details. */ @@ -103,7 +103,7 @@ static inline void csi_rx_fe_ctrl_get_dlane_state( csi_rx_fe_ctrl_reg_load(ID, _HRT_CSI_RX_DLY_CNT_SETTLE_DLANE_REG_IDX(lane)); } -/** +/* * @brief Get the csi rx fe state. * Refer to "csi_rx_public.h" for details. */ @@ -142,7 +142,7 @@ static inline void csi_rx_fe_ctrl_get_state( } } -/** +/* * @brief dump the csi rx fe state. * Refer to "csi_rx_public.h" for details. */ @@ -180,7 +180,7 @@ static inline void csi_rx_fe_ctrl_dump_state( } } -/** +/* * @brief Get the csi rx be state. * Refer to "csi_rx_public.h" for details. */ @@ -243,7 +243,7 @@ static inline void csi_rx_be_ctrl_get_state( } } -/** +/* * @brief Dump the csi rx be state. * Refer to "csi_rx_public.h" for details. */ diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_private.h index 4bd8209aaa01..85439d561ba3 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_irq_private.h @@ -15,7 +15,7 @@ | Native command interface (NCI) | + -------------------------------------------------------*/ -/** +/* * @brief Get the isys irq status. * Refer to "isys_irq.h" for details. */ @@ -34,7 +34,7 @@ void isys_irqc_state_get( */ } -/** +/* * @brief Dump the isys irq status. * Refer to "isys_irq.h" for details. */ diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h index 8e295cd78129..88a763365631 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h @@ -27,7 +27,7 @@ * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the stream2mmio-controller state. * Refer to "stream2mmio_public.h" for details. */ @@ -45,7 +45,7 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_state( stream2mmio_get_sid_state(ID, i, &state->sid_state[i]); } -/** +/* * @brief Get the state of the stream2mmio-controller sidess. * Refer to "stream2mmio_public.h" for details. */ @@ -76,7 +76,7 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_sid_state( stream2mmio_reg_load(ID, sid_id, STREAM2MMIO_BLOCK_WHEN_NO_CMD_REG_ID); } -/** +/* * @brief Dump the state of the stream2mmio-controller sidess. * Refer to "stream2mmio_public.h" for details. */ @@ -92,7 +92,7 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_print_sid_state( ia_css_print("\t \t block when no cmd 0x%x\n", state->block_when_no_cmd); } -/** +/* * @brief Dump the ibuf-controller state. * Refer to "stream2mmio_public.h" for details. */ @@ -119,7 +119,7 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_dump_state( * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Refer to "stream2mmio_public.h" for details. */ @@ -137,7 +137,7 @@ STORAGE_CLASS_STREAM2MMIO_C hrt_data stream2mmio_reg_load( (reg_bank_offset + reg_idx) * sizeof(hrt_data)); } -/** +/* * @brief Store a value to the register. * Refer to "stream2mmio_public.h" for details. */ diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h index b8b98106bd31..0faf6ffdf55d 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/pixelgen_private.h @@ -16,7 +16,7 @@ * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Refer to "pixelgen_public.h" for details. */ @@ -30,7 +30,7 @@ STORAGE_CLASS_PIXELGEN_C hrt_data pixelgen_ctrl_reg_load( hrt_data)); } -/** +/* * @brief Store a value to the register. * Refer to "pixelgen_ctrl_public.h" for details. */ @@ -53,7 +53,7 @@ STORAGE_CLASS_PIXELGEN_C void pixelgen_ctrl_reg_store( * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the pixelgen state. * Refer to "pixelgen_public.h" for details. */ @@ -117,7 +117,7 @@ STORAGE_CLASS_PIXELGEN_C void pixelgen_ctrl_get_state( pixelgen_ctrl_reg_load(ID, _PXG_TPG_B2_REG_IDX); } -/** +/* * @brief Dump the pixelgen state. * Refer to "pixelgen_public.h" for details. */ diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/pixelgen_global.h b/drivers/staging/media/atomisp/pci/css_2401_system/pixelgen_global.h index 59e0b44bfdc3..b5a6a8f0c3d2 100644 --- a/drivers/staging/media/atomisp/pci/css_2401_system/pixelgen_global.h +++ b/drivers/staging/media/atomisp/pci/css_2401_system/pixelgen_global.h @@ -9,7 +9,7 @@ #include -/** +/* * Pixel-generator. ("pixelgen_global.h") */ /* diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/timed_ctrl_global.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/timed_ctrl_global.h index 2dc0fb88399f..e4c136660b1e 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/timed_ctrl_global.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/timed_ctrl_global.h @@ -11,7 +11,7 @@ #include "timed_controller_defs.h" -/** +/* * Order of the input bits for the timed controller taken from * ISP_CSS_2401 System Architecture Description valid for * 2400, 2401. diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/device_access/device_access.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/device_access/device_access.h index ca33a1e03d8d..f5b0ff414395 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/device_access/device_access.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/device_access/device_access.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h index 2002960f078e..e8ce556bfd6a 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/csi_rx_public.h @@ -12,7 +12,7 @@ * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the csi rx frontend state. * Get the state of the csi rx frontend regiester-set. * @@ -22,7 +22,7 @@ void csi_rx_fe_ctrl_get_state( const csi_rx_frontend_ID_t ID, csi_rx_fe_ctrl_state_t *state); -/** +/* * @brief Dump the csi rx frontend state. * Dump the state of the csi rx frontend regiester-set. * @@ -32,7 +32,7 @@ void csi_rx_fe_ctrl_get_state( void csi_rx_fe_ctrl_dump_state( const csi_rx_frontend_ID_t ID, csi_rx_fe_ctrl_state_t *state); -/** +/* * @brief Get the state of the csi rx fe dlane. * Get the state of the register set per dlane process. * @@ -44,7 +44,7 @@ void csi_rx_fe_ctrl_get_dlane_state( const csi_rx_frontend_ID_t ID, const u32 lane, csi_rx_fe_ctrl_lane_t *dlane_state); -/** +/* * @brief Get the csi rx backend state. * Get the state of the csi rx backend regiester-set. * @@ -54,7 +54,7 @@ void csi_rx_fe_ctrl_get_dlane_state( void csi_rx_be_ctrl_get_state( const csi_rx_backend_ID_t ID, csi_rx_be_ctrl_state_t *state); -/** +/* * @brief Dump the csi rx backend state. * Dump the state of the csi rx backend regiester-set. * @@ -71,7 +71,7 @@ void csi_rx_be_ctrl_dump_state( * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Load the value of the register of the csi rx fe. * @@ -83,7 +83,7 @@ void csi_rx_be_ctrl_dump_state( hrt_data csi_rx_fe_ctrl_reg_load( const csi_rx_frontend_ID_t ID, const hrt_address reg); -/** +/* * @brief Store a value to the register. * Store a value to the register of the csi rx fe. * @@ -96,7 +96,7 @@ void csi_rx_fe_ctrl_reg_store( const csi_rx_frontend_ID_t ID, const hrt_address reg, const hrt_data value); -/** +/* * @brief Load the register value. * Load the value of the register of the csirx be. * @@ -108,7 +108,7 @@ void csi_rx_fe_ctrl_reg_store( hrt_data csi_rx_be_ctrl_reg_load( const csi_rx_backend_ID_t ID, const hrt_address reg); -/** +/* * @brief Store a value to the register. * Store a value to the register of the csi rx be. * diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_stream2mmio_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_stream2mmio_public.h index 7ed55d427cf6..e4f77e975f4e 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_stream2mmio_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/isys_stream2mmio_public.h @@ -12,7 +12,7 @@ * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the stream2mmio-controller state. * Get the state of the stream2mmio-controller regiester-set. * @@ -23,7 +23,7 @@ STORAGE_CLASS_STREAM2MMIO_H void stream2mmio_get_state( const stream2mmio_ID_t ID, stream2mmio_state_t *state); -/** +/* * @brief Get the state of the stream2mmio-controller sidess. * Get the state of the register set per buf-controller sidess. * @@ -42,7 +42,7 @@ STORAGE_CLASS_STREAM2MMIO_H void stream2mmio_get_sid_state( * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Load the value of the register of the stream2mmio-controller. * @@ -57,7 +57,7 @@ STORAGE_CLASS_STREAM2MMIO_H hrt_data stream2mmio_reg_load( const stream2mmio_sid_ID_t sid_id, const uint32_t reg_idx); -/** +/* * @brief Dump the SID processor state. * Dump the state of the sid regiester-set. * @@ -65,7 +65,7 @@ STORAGE_CLASS_STREAM2MMIO_H hrt_data stream2mmio_reg_load( */ STORAGE_CLASS_STREAM2MMIO_H void stream2mmio_print_sid_state( stream2mmio_sid_state_t *state); -/** +/* * @brief Dump the stream2mmio state. * Dump the state of the ibuf-controller regiester-set. * @@ -75,7 +75,7 @@ STORAGE_CLASS_STREAM2MMIO_H void stream2mmio_print_sid_state( STORAGE_CLASS_STREAM2MMIO_H void stream2mmio_dump_state( const stream2mmio_ID_t ID, stream2mmio_state_t *state); -/** +/* * @brief Store a value to the register. * Store a value to the registe of the stream2mmio-controller. * diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/pixelgen_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/pixelgen_public.h index dc31ce3cd741..6aabf0b1995f 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/pixelgen_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/pixelgen_public.h @@ -12,7 +12,7 @@ * Native command interface (NCI). * *****************************************************/ -/** +/* * @brief Get the pixelgen state. * Get the state of the pixelgen regiester-set. * @@ -22,7 +22,7 @@ STORAGE_CLASS_PIXELGEN_H void pixelgen_ctrl_get_state( const pixelgen_ID_t ID, pixelgen_ctrl_state_t *state); -/** +/* * @brief Dump the pixelgen state. * Dump the state of the pixelgen regiester-set. * @@ -39,7 +39,7 @@ STORAGE_CLASS_PIXELGEN_H void pixelgen_ctrl_dump_state( * Device level interface (DLI). * *****************************************************/ -/** +/* * @brief Load the register value. * Load the value of the register of the pixelgen * @@ -51,7 +51,7 @@ STORAGE_CLASS_PIXELGEN_H void pixelgen_ctrl_dump_state( STORAGE_CLASS_PIXELGEN_H hrt_data pixelgen_ctrl_reg_load( const pixelgen_ID_t ID, const hrt_address reg); -/** +/* * @brief Store a value to the register. * Store a value to the registe of the pixelgen * diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/tag_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/tag_public.h index ad83ff97bbd6..1652c9f79349 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/tag_public.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/tag_public.h @@ -7,7 +7,7 @@ #ifndef __TAG_PUBLIC_H_INCLUDED__ #define __TAG_PUBLIC_H_INCLUDED__ -/** +/* * @brief Creates the tag description from the given parameters. * @param[in] num_captures * @param[in] skip @@ -21,7 +21,7 @@ sh_css_create_tag_descr(int num_captures, unsigned int exp_id, struct sh_css_tag_descr *tag_descr); -/** +/* * @brief Encodes the members of tag description into a 32-bit value. * @param[in] tag Pointer to the tag description * @return (unsigned int) Encoded 32-bit tag-info diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/platform_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/platform_support.h index 473d8d4fb9ba..20a5faad0716 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/platform_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/platform_support.h @@ -7,7 +7,7 @@ #ifndef __PLATFORM_SUPPORT_H_INCLUDED__ #define __PLATFORM_SUPPORT_H_INCLUDED__ -/** +/* * @file * Platform specific includes and functionality. */ diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h index 097be6bd3cb5..cbd290121a28 100644 --- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h +++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/type_support.h @@ -7,7 +7,7 @@ #ifndef __TYPE_SUPPORT_H_INCLUDED__ #define __TYPE_SUPPORT_H_INCLUDED__ -/** +/* * @file * Platform specific types. * diff --git a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h index 7acfedb541d8..936b17950b3a 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_frame_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_frame_public.h @@ -119,7 +119,7 @@ struct ia_css_frame_info { .raw_bayer_order = IA_CSS_BAYER_ORDER_NUM, \ } -/** +/* * Specifies the DVS loop delay in "frame periods" */ enum ia_css_frame_delay { diff --git a/drivers/staging/media/atomisp/pci/ia_css_host_data.h b/drivers/staging/media/atomisp/pci/ia_css_host_data.h index 0e45650cc1ab..547542577902 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_host_data.h +++ b/drivers/staging/media/atomisp/pci/ia_css_host_data.h @@ -10,7 +10,7 @@ #include /* ia_css_pipe */ -/** +/* * @brief Allocate structure ia_css_host_data. * * @param[in] size Size of the requested host data @@ -22,7 +22,7 @@ struct ia_css_host_data * ia_css_host_data_allocate(size_t size); -/** +/* * @brief Free structure ia_css_host_data. * * @param[in] me Pointer to structure, if a NULL is passed functions diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h index 2bb06b0ff5db..1d1b704b2bc6 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h +++ b/drivers/staging/media/atomisp/pci/ia_css_pipe_public.h @@ -44,7 +44,7 @@ enum ia_css_pipe_mode { /* Temporary define */ #define IA_CSS_PIPE_MODE_NUM (IA_CSS_PIPE_MODE_YUVPP + 1) -/** +/* * Enumeration of pipe versions. * the order should match with definition in sh_css_defs.h */ @@ -55,7 +55,7 @@ enum ia_css_pipe_version { IA_CSS_PIPE_VERSION_2_7 = 4 /** ISP2.7 pipe */ }; -/** +/* * Pipe configuration structure. * Resolution properties are filled by Driver, kernel configurations are * set by AIC @@ -123,7 +123,7 @@ struct ia_css_pipe_config { to retrieve shading gains which correspond to bayer data. */ }; -/** +/* * Default settings for newly created pipe configurations. */ #define DEFAULT_PIPE_CONFIG { \ @@ -175,7 +175,7 @@ struct ia_css_pipe_info { output at the first valid frame. */ }; -/** +/* * Defaults for ia_css_pipe_info structs. */ #define DEFAULT_PIPE_INFO {\ diff --git a/drivers/staging/media/atomisp/pci/ia_css_prbs.h b/drivers/staging/media/atomisp/pci/ia_css_prbs.h index abdbcb8fda53..3bb1e5dbb4fc 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_prbs.h +++ b/drivers/staging/media/atomisp/pci/ia_css_prbs.h @@ -19,7 +19,7 @@ enum ia_css_prbs_id { IA_CSS_PRBS_ID2 }; -/** +/* * Maximum number of PRBS IDs. * * Make sure the value of this define gets changed to reflect the correct @@ -27,7 +27,7 @@ enum ia_css_prbs_id { */ #define N_CSS_PRBS_IDS (IA_CSS_PRBS_ID2 + 1) -/** +/* * PRBS configuration structure. * * Seed the for the Pseudo Random Bit Sequence. diff --git a/drivers/staging/media/atomisp/pci/ia_css_stream.h b/drivers/staging/media/atomisp/pci/ia_css_stream.h index c8de632a8e12..578dc45785fc 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_stream.h +++ b/drivers/staging/media/atomisp/pci/ia_css_stream.h @@ -13,7 +13,7 @@ #include "ia_css_types.h" #include "ia_css_stream_public.h" -/** +/* * structure to hold all internal stream related information */ struct ia_css_stream { diff --git a/drivers/staging/media/atomisp/pci/ia_css_timer.h b/drivers/staging/media/atomisp/pci/ia_css_timer.h index da752834adf4..5c73e8c61588 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_timer.h +++ b/drivers/staging/media/atomisp/pci/ia_css_timer.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. @@ -42,7 +42,10 @@ struct ia_css_time_meas { clock_value_t end_timer_value; /** measured time in ticks */ }; -/**@brief SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT checks to ensure correct alignment for struct ia_css_clock_tick. */ +/* + * @brief SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT checks to ensure correct alignment + * for struct ia_css_clock_tick. + */ #define SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT sizeof(clock_value_t) /* @brief checks to ensure correct alignment for ia_css_time_meas. */ #define SIZE_OF_IA_CSS_TIME_MEAS_STRUCT (sizeof(clock_value_t) \ diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h index 2b7db9cda23a..7065eded7a33 100644 --- a/drivers/staging/media/atomisp/pci/ia_css_types.h +++ b/drivers/staging/media/atomisp/pci/ia_css_types.h @@ -53,7 +53,7 @@ #include "isp/kernels/output/output_1.0/ia_css_output_types.h" #define IA_CSS_DVS_STAT_GRID_INFO_SUPPORTED -/** Should be removed after Driver adaptation will be done */ +/* Should be removed after Driver adaptation will be done */ #define IA_CSS_VERSION_MAJOR 2 #define IA_CSS_VERSION_MINOR 0 @@ -421,7 +421,7 @@ struct ia_css_dvs_6axis_config { u32 *ycoords_uv; }; -/** +/* * This specifies the coordinates (x,y) */ struct ia_css_point { @@ -429,7 +429,7 @@ struct ia_css_point { s32 y; /** y coordinate */ }; -/** +/* * Digital zoom: * This feature is currently available only for video, but will become * available for preview and capture as well. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_types.h index 374261d25520..ee2cf2877b7d 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/conversion/conversion_1.0/ia_css_conversion_types.h @@ -7,7 +7,7 @@ #ifndef __IA_CSS_CONVERSION_TYPES_H #define __IA_CSS_CONVERSION_TYPES_H -/** +/* * Conversion Kernel parameters. * Deinterleave bayer quad into isys format * diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h index 0b977eb7ad71..bbd97c71a6fd 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_types.h @@ -13,7 +13,7 @@ #include "type_support.h" -/** +/* * \brief EED1_8 public parameters. * \details Struct with all parameters for the EED1.8 kernel that can be set * from the CSS API. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h index e32290d1c86b..d6785724fe97 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/hdr/ia_css_hdr_types.h @@ -10,44 +10,46 @@ #define IA_CSS_HDR_MAX_NUM_INPUT_FRAMES (3) -/** +/* * \brief HDR Irradiance Parameters * \detail Currently HDR parameters are used only for testing purposes */ struct ia_css_hdr_irradiance_params { - int test_irr; /** Test parameter */ - int match_shift[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Histogram matching shift parameter */ - int match_mul[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Histogram matching multiplication parameter */ - int thr_low[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Weight map soft threshold low bound parameter */ - int thr_high[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Weight map soft threshold high bound parameter */ - int thr_coeff[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Soft threshold linear function coefficien */ - int thr_shift[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - - 1]; /** Soft threshold precision shift parameter */ - int weight_bpp; /** Weight map bits per pixel */ + /* Test parameter */ + int test_irr; + /* Histogram matching shift parameter */ + int match_shift[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Histogram matching multiplication parameter */ + int match_mul[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Weight map soft threshold low bound parameter */ + int thr_low[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Weight map soft threshold high bound parameter */ + int thr_high[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Soft threshold linear function coefficien */ + int thr_coeff[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Soft threshold precision shift parameter */ + int thr_shift[IA_CSS_HDR_MAX_NUM_INPUT_FRAMES - 1]; + /* Weight map bits per pixel */ + int weight_bpp; }; -/** +/* * \brief HDR Deghosting Parameters * \detail Currently HDR parameters are used only for testing purposes */ struct ia_css_hdr_deghost_params { - int test_deg; /** Test parameter */ + int test_deg; /* Test parameter */ }; -/** +/* * \brief HDR Exclusion Parameters * \detail Currently HDR parameters are used only for testing purposes */ struct ia_css_hdr_exclusion_params { - int test_excl; /** Test parameter */ + int test_excl; /* Test parameter */ }; -/** +/* * \brief HDR public parameters. * \details Struct with all parameters for HDR that can be seet from * the CSS API. Currently, only test parameters are defined. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_param.h index c9a3f7bfaa90..de535503b70e 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_param.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_types.h index c1c93b2245c5..69a40a15264b 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/common/ia_css_common_io_types.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.h index e6ce0cba44b9..72310781cf7e 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io.host.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_param.h index 429fcdb73f60..9fabb8b2d3b8 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_param.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_param.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_types.h index 485ad17cb29b..1daafd96a0cd 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/ipu2_io_ls/yuv444_io_ls/ia_css_yuv444_io_types.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h index 42b760ffac67..676a633336a2 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/tnr/tnr3/ia_css_tnr3_types.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. @@ -12,7 +12,7 @@ Copyright (c) 2010 - 2015, Intel Corporation. * CSS-API header file for Temporal Noise Reduction v3 (TNR3) kernel */ -/** +/* * \brief Number of piecewise linear segments. * \details The parameters to TNR3 are specified as a piecewise linear segment. * The number of such segments is fixed at 3. diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h index c012c1865d1e..fc4b9033951c 100644 --- a/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h +++ b/drivers/staging/media/atomisp/pci/isp/kernels/xnr/xnr_3.0/ia_css_xnr3_types.h @@ -11,25 +11,25 @@ * CSS-API header file for Extra Noise Reduction (XNR) parameters. */ -/** +/* * \brief Scale of the XNR sigma parameters. * \details The define specifies which fixed-point value represents 1.0. */ #define IA_CSS_XNR3_SIGMA_SCALE BIT(10) -/** +/* * \brief Scale of the XNR coring parameters. * \details The define specifies which fixed-point value represents 1.0. */ #define IA_CSS_XNR3_CORING_SCALE BIT(15) -/** +/* * \brief Scale of the XNR blending parameter. * \details The define specifies which fixed-point value represents 1.0. */ #define IA_CSS_XNR3_BLENDING_SCALE BIT(11) -/** +/* * \brief XNR3 Sigma Parameters. * \details Sigma parameters define the strength of the XNR filter. * A higher number means stronger filtering. There are two values for each of @@ -46,7 +46,7 @@ struct ia_css_xnr3_sigma_params { int v1; /** Sigma for V range similarity in bright area */ }; -/** +/* * \brief XNR3 Coring Parameters * \details Coring parameters define the "coring" strength, which is a soft * thresholding technique to avoid false coloring. There are two values for @@ -61,7 +61,7 @@ struct ia_css_xnr3_coring_params { int v1; /** Coring threshold of V channel in bright area */ }; -/** +/* * \brief XNR3 Blending Parameters * \details Blending parameters define the blending strength of filtered * output pixels with the original chroma pixels from before xnr3. The @@ -75,7 +75,7 @@ struct ia_css_xnr3_blending_params { int strength; /** Blending strength */ }; -/** +/* * \brief XNR3 public parameters. * \details Struct with all parameters for the XNR3 kernel that can be set * from the CSS API. diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h index 6a0257359e69..d6ed1fbb0e8e 100644 --- a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h +++ b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/interface/ia_css_binary.h b/drivers/staging/media/atomisp/pci/runtime/binary/interface/ia_css_binary.h index 9c682f2ecbb2..5f768af9bc1d 100644 --- a/drivers/staging/media/atomisp/pci/runtime/binary/interface/ia_css_binary.h +++ b/drivers/staging/media/atomisp/pci/runtime/binary/interface/ia_css_binary.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq.h b/drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq.h index 3e7dadca6d70..618172f17b63 100644 --- a/drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq.h +++ b/drivers/staging/media/atomisp/pci/runtime/bufq/interface/ia_css_bufq.h @@ -13,7 +13,7 @@ #include "ia_css_err.h" #define BUFQ_EVENT_SIZE 4 -/** +/* * @brief Query the internal frame ID. * * @param[in] key The query key. @@ -29,7 +29,7 @@ bool ia_css_query_internal_queue_id( enum sh_css_queue_id *val ); -/** +/* * @brief Map buffer type to a internal queue id. * * @param[in] thread id Thread in which the buffer type has to be mapped or unmapped @@ -43,13 +43,13 @@ void ia_css_queue_map( bool map ); -/** +/* * @brief Initialize buffer type to a queue id mapping * @return none */ void ia_css_queue_map_init(void); -/** +/* * @brief initializes bufq module * It create instances of * -host to SP buffer queue which is a list with predefined size, @@ -62,7 +62,7 @@ void ia_css_queue_map_init(void); */ void ia_css_bufq_init(void); -/** +/* * @brief Enqueues an item into host to SP buffer queue * * @param thread_index[in] Thread in which the item to be enqueued @@ -77,7 +77,7 @@ int ia_css_bufq_enqueue_buffer( int queue_id, uint32_t item); -/** +/* * @brief Dequeues an item from SP to host buffer queue. * * @param queue_id[in] Specifies the index of the queue in the list where @@ -90,7 +90,7 @@ int ia_css_bufq_dequeue_buffer( int queue_id, uint32_t *item); -/** +/* * @brief Enqueue an event item into host to SP communication event queue. * * @param[in] evt_id The event ID. @@ -107,7 +107,7 @@ int ia_css_bufq_enqueue_psys_event( uint8_t evt_payload_2 ); -/** +/* * @brief Dequeue an item from SP to host communication event queue. * * @param item Object to be dequeued into this item. @@ -119,7 +119,7 @@ int ia_css_bufq_dequeue_psys_event( ); -/** +/* * @brief Enqueue an event item into host to SP EOF event queue. * * @param[in] evt_id The event ID. @@ -129,7 +129,7 @@ int ia_css_bufq_dequeue_psys_event( int ia_css_bufq_enqueue_isys_event( uint8_t evt_id); -/** +/* * @brief Dequeue an item from SP to host communication EOF event queue. * @@ -140,7 +140,7 @@ int ia_css_bufq_enqueue_isys_event( int ia_css_bufq_dequeue_isys_event( u8 item[BUFQ_EVENT_SIZE]); -/** +/* * @brief Enqueue a tagger command item into tagger command queue.. * * @param item Object to be enqueue. @@ -150,7 +150,7 @@ int ia_css_bufq_dequeue_isys_event( int ia_css_bufq_enqueue_tag_cmd( uint32_t item); -/** +/* * @brief Uninitializes bufq module. * * @return 0 or error code upon error. @@ -158,7 +158,7 @@ int ia_css_bufq_enqueue_tag_cmd( */ int ia_css_bufq_deinit(void); -/** +/* * @brief Dump queue states * * @return None diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h index 2d0e906530af..b7ff809eeeab 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h +++ b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug.h @@ -312,7 +312,7 @@ void ia_css_debug_dump_stream_config( const struct ia_css_stream_config *config, int num_pipes); -/** +/* * @brief Initialize the debug mode. * * WARNING: @@ -324,7 +324,7 @@ void ia_css_debug_dump_stream_config( */ bool ia_css_debug_mode_init(void); -/** +/* * @brief Disable the DMA channel. * * @param[in] dma_ID The ID of the target DMA. @@ -344,7 +344,7 @@ bool ia_css_debug_mode_disable_dma_channel( int dma_ID, int channel_id, int request_type); -/** +/* * @brief Enable the DMA channel. * * @param[in] dma_ID The ID of the target DMA. @@ -363,7 +363,7 @@ bool ia_css_debug_mode_enable_dma_channel( int channel_id, int request_type); -/** +/* * @brief Dump tracer data. * [Currently support is only for SKC] * @@ -373,7 +373,7 @@ bool ia_css_debug_mode_enable_dma_channel( void ia_css_debug_dump_trace(void); /* ISP2401 */ -/** +/* * @brief Program counter dumping (in loop) * * @param[in] id The ID of the SP diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_pipe.h b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_pipe.h index 80c58cb934d5..6f6cd77ce91d 100644 --- a/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_pipe.h +++ b/drivers/staging/media/atomisp/pci/runtime/debug/interface/ia_css_debug_pipe.h @@ -13,21 +13,21 @@ #include #include "ia_css_pipeline.h" -/** +/* * @brief Internal debug support for constructing a pipe graph. * * @return None */ void ia_css_debug_pipe_graph_dump_prologue(void); -/** +/* * @brief Internal debug support for constructing a pipe graph. * * @return None */ void ia_css_debug_pipe_graph_dump_epilogue(void); -/** +/* * @brief Internal debug support for constructing a pipe graph. * @param[in] stage Pipeline stage. * @param[in] id Pipe id. @@ -38,7 +38,7 @@ void ia_css_debug_pipe_graph_dump_stage( struct ia_css_pipeline_stage *stage, enum ia_css_pipe_id id); -/** +/* * @brief Internal debug support for constructing a pipe graph. * @param[in] out_frame Output frame of SP raw copy. * @@ -47,7 +47,7 @@ void ia_css_debug_pipe_graph_dump_stage( void ia_css_debug_pipe_graph_dump_sp_raw_copy( struct ia_css_frame *out_frame); -/** +/* * @brief Internal debug support for constructing a pipe graph. * @param[in] stream_config info about sensor and input formatter. * diff --git a/drivers/staging/media/atomisp/pci/runtime/eventq/interface/ia_css_eventq.h b/drivers/staging/media/atomisp/pci/runtime/eventq/interface/ia_css_eventq.h index 9b058e296c93..f8ce32e9b90d 100644 --- a/drivers/staging/media/atomisp/pci/runtime/eventq/interface/ia_css_eventq.h +++ b/drivers/staging/media/atomisp/pci/runtime/eventq/interface/ia_css_eventq.h @@ -9,7 +9,7 @@ #include "ia_css_queue.h" /* queue APIs */ -/** +/* * @brief HOST receives event from SP. * * @param[in] eventq_handle eventq_handle. @@ -22,7 +22,7 @@ int ia_css_eventq_recv( ia_css_queue_t *eventq_handle, uint8_t *payload); -/** +/* * @brief The Host sends the event to SP. * The caller of this API will be blocked until the event * is sent. diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param_types.h b/drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param_types.h index d6d60508c1bf..095d1853670f 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param_types.h +++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/interface/ia_css_isp_param_types.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/** +/* Support for Intel Camera Imaging ISP subsystem. Copyright (c) 2010 - 2015, Intel Corporation. diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys.h b/drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys.h index 29eebe8f9078..8870ff294c1e 100644 --- a/drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys.h +++ b/drivers/staging/media/atomisp/pci/runtime/isys/interface/ia_css_isys.h @@ -15,7 +15,7 @@ #include #include "ia_css_isys_comm.h" -/** +/* * Virtual Input System. (Input System 2401) */ typedef isp2401_input_system_cfg_t ia_css_isys_descr_t; @@ -28,7 +28,7 @@ enum mipi_port_id ia_css_isys_port_to_mipi_port( enum mipi_port_id api_port); -/** +/* * @brief Register one (virtual) stream. This is used to track when all * virtual streams are configured inside the input system. The CSI RX is * only started when all registered streams are configured. @@ -43,7 +43,7 @@ int ia_css_isys_csi_rx_register_stream( enum mipi_port_id port, uint32_t isys_stream_id); -/** +/* * @brief Unregister one (virtual) stream. This is used to track when all * virtual streams are configured inside the input system. The CSI RX is * only started when all registered streams are configured. @@ -97,7 +97,7 @@ int ia_css_isys_convert_stream_format_to_mipi_format( mipi_predictor_t compression, unsigned int *fmt_type); -/** +/* * Virtual Input System. (Input System 2401) */ ia_css_isys_error_t ia_css_isys_stream_create( diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h index 8b7cbf31a1a2..04d9f1fdb5b0 100644 --- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h +++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h @@ -219,7 +219,7 @@ int ia_css_pipeline_get_output_stage( */ bool ia_css_pipeline_uses_params(struct ia_css_pipeline *pipeline); -/** +/* * @brief get the SP thread ID. * * @param[in] key The query key, typical use is pipe_num. @@ -231,7 +231,7 @@ bool ia_css_pipeline_uses_params(struct ia_css_pipeline *pipeline); */ bool ia_css_pipeline_get_sp_thread_id(unsigned int key, unsigned int *val); -/** +/* * @brief Get the pipeline io status * * @param[in] None @@ -240,7 +240,7 @@ bool ia_css_pipeline_get_sp_thread_id(unsigned int key, unsigned int *val); */ struct sh_css_sp_pipeline_io_status *ia_css_pipeline_get_pipe_io_status(void); -/** +/* * @brief Map an SP thread to this pipeline * * @param[in] pipe_num @@ -249,7 +249,7 @@ struct sh_css_sp_pipeline_io_status *ia_css_pipeline_get_pipe_io_status(void); */ void ia_css_pipeline_map(unsigned int pipe_num, bool map); -/** +/* * @brief Checks whether the pipeline is mapped to SP threads * * @param[in] Query key, typical use is pipe_num @@ -260,7 +260,7 @@ void ia_css_pipeline_map(unsigned int pipe_num, bool map); */ bool ia_css_pipeline_is_mapped(unsigned int key); -/** +/* * @brief Print pipeline thread mapping * * @param[in] none diff --git a/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr.h b/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr.h index 00b903d950df..4c86c19811bb 100644 --- a/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr.h +++ b/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr.h @@ -17,12 +17,12 @@ #define STORAGE_CLASS_RMGR_C static inline #endif /* __INLINE_RMGR__ */ -/** +/* * @brief Initialize resource manager (host/common) */ int ia_css_rmgr_init(void); -/** +/* * @brief Uninitialize resource manager (host/common) */ void ia_css_rmgr_uninit(void); diff --git a/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h b/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h index 6820bfc77432..68285334bc88 100644 --- a/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h +++ b/drivers/staging/media/atomisp/pci/runtime/rmgr/interface/ia_css_rmgr_vbuf.h @@ -12,7 +12,7 @@ #include #include -/** +/* * @brief Data structure for the resource handle (host, vbuf) */ struct ia_css_rmgr_vbuf_handle { @@ -21,7 +21,7 @@ struct ia_css_rmgr_vbuf_handle { u32 size; }; -/** +/* * @brief Data structure for the resource pool (host, vbuf) */ struct ia_css_rmgr_vbuf_pool { @@ -32,14 +32,14 @@ struct ia_css_rmgr_vbuf_pool { struct ia_css_rmgr_vbuf_handle **handles; }; -/** +/* * @brief VBUF resource pools */ extern struct ia_css_rmgr_vbuf_pool *vbuf_ref; extern struct ia_css_rmgr_vbuf_pool *vbuf_write; extern struct ia_css_rmgr_vbuf_pool *hmm_buffer_pool; -/** +/* * @brief Initialize the resource pool (host, vbuf) * * @param pool The pointer to the pool @@ -47,7 +47,7 @@ extern struct ia_css_rmgr_vbuf_pool *hmm_buffer_pool; STORAGE_CLASS_RMGR_H int ia_css_rmgr_init_vbuf( struct ia_css_rmgr_vbuf_pool *pool); -/** +/* * @brief Uninitialize the resource pool (host, vbuf) * * @param pool The pointer to the pool @@ -55,7 +55,7 @@ STORAGE_CLASS_RMGR_H int ia_css_rmgr_init_vbuf( STORAGE_CLASS_RMGR_H void ia_css_rmgr_uninit_vbuf( struct ia_css_rmgr_vbuf_pool *pool); -/** +/* * @brief Acquire a handle from the pool (host, vbuf) * * @param pool The pointer to the pool @@ -65,7 +65,7 @@ STORAGE_CLASS_RMGR_H void ia_css_rmgr_acq_vbuf( struct ia_css_rmgr_vbuf_pool *pool, struct ia_css_rmgr_vbuf_handle **handle); -/** +/* * @brief Release a handle to the pool (host, vbuf) * * @param pool The pointer to the pool @@ -75,14 +75,14 @@ STORAGE_CLASS_RMGR_H void ia_css_rmgr_rel_vbuf( struct ia_css_rmgr_vbuf_pool *pool, struct ia_css_rmgr_vbuf_handle **handle); -/** +/* * @brief Retain the reference count for a handle (host, vbuf) * * @param handle The pointer to the handle */ void ia_css_rmgr_refcount_retain_vbuf(struct ia_css_rmgr_vbuf_handle **handle); -/** +/* * @brief Release the reference count for a handle (host, vbuf) * * @param handle The pointer to the handle diff --git a/drivers/staging/media/atomisp/pci/runtime/tagger/interface/ia_css_tagger_common.h b/drivers/staging/media/atomisp/pci/runtime/tagger/interface/ia_css_tagger_common.h index 79006c325de6..9208c650fde7 100644 --- a/drivers/staging/media/atomisp/pci/runtime/tagger/interface/ia_css_tagger_common.h +++ b/drivers/staging/media/atomisp/pci/runtime/tagger/interface/ia_css_tagger_common.h @@ -10,14 +10,14 @@ #include #include -/** +/* * @brief The tagger's circular buffer. * * Should be one less than NUM_CONTINUOUS_FRAMES in sh_css_internal.h */ #define MAX_CB_ELEMS_FOR_TAGGER 14 -/** +/* * @brief Data structure for the tagger buffer element. */ typedef struct { diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h index 9155a83fcc03..ec68c16d4272 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_internal.h +++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h @@ -80,7 +80,7 @@ #define SH_CSS_MAX_SP_THREADS 5 -/** +/* * The C99 standard does not specify the exact object representation of structs; * the representation is compiler dependent. * @@ -638,7 +638,7 @@ struct sh_css_sp_output { unsigned int sw_interrupt_value[SH_CSS_NUM_SDW_IRQS]; }; -/** +/* * @brief Data structure for the circular buffer. * The circular buffer is empty if "start == end". The * circular buffer is full if "(end + 1) % size == start". diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.h b/drivers/staging/media/atomisp/pci/sh_css_sp.h index 78aec5b7e8fa..6985ca200f09 100644 --- a/drivers/staging/media/atomisp/pci/sh_css_sp.h +++ b/drivers/staging/media/atomisp/pci/sh_css_sp.h @@ -68,7 +68,7 @@ sh_css_read_host2sp_command(void); void sh_css_init_host2sp_frame_data(void); -/** +/* * @brief Update the offline frame information in host_sp_communication. * * @param[in] frame_num The offline frame number. @@ -80,7 +80,7 @@ sh_css_update_host2sp_offline_frame( struct ia_css_frame *frame, struct ia_css_metadata *metadata); -/** +/* * @brief Update the mipi frame information in host_sp_communication. * * @param[in] frame_num The mipi frame number. @@ -91,7 +91,7 @@ sh_css_update_host2sp_mipi_frame( unsigned int frame_num, struct ia_css_frame *frame); -/** +/* * @brief Update the mipi metadata information in host_sp_communication. * * @param[in] frame_num The mipi frame number. @@ -102,7 +102,7 @@ sh_css_update_host2sp_mipi_metadata( unsigned int frame_num, struct ia_css_metadata *metadata); -/** +/* * @brief Update the nr of mipi frames to use in host_sp_communication. * * @param[in] num_frames The number of mipi frames to use. @@ -110,7 +110,7 @@ sh_css_update_host2sp_mipi_metadata( void sh_css_update_host2sp_num_mipi_frames(unsigned int num_frames); -/** +/* * @brief Update the nr of offline frames to use in host_sp_communication. * * @param[in] num_frames The number of raw frames to use. @@ -170,7 +170,7 @@ sh_css_sp_set_disable_continuous_viewfinder(bool flag); void sh_css_sp_reset_global_vars(void); -/** +/* * @brief Initialize the DMA software-mask in the debug mode. * This API should be ONLY called in the debugging mode. * And it should be always called before the first call of @@ -185,7 +185,7 @@ sh_css_sp_reset_global_vars(void); bool sh_css_sp_init_dma_sw_reg(int dma_id); -/** +/* * @brief Set the DMA software-mask in the debug mode. * This API should be ONLYL called in the debugging mode. Must * call "sh_css_set_dma_sw_reg(...)" before this diff --git a/drivers/staging/media/atomisp/pci/system_global.h b/drivers/staging/media/atomisp/pci/system_global.h index e8a29f73d67a..ee1d9b66ccb1 100644 --- a/drivers/staging/media/atomisp/pci/system_global.h +++ b/drivers/staging/media/atomisp/pci/system_global.h @@ -293,7 +293,7 @@ typedef enum { } stream2mmio_sid_ID_t; /* end of Stream2MMIO */ -/** +/* * Input System 2401: CSI-MIPI recevier. */ typedef enum { @@ -324,7 +324,7 @@ typedef enum { N_ISYS2401_DMA_ID } isys2401_dma_ID_t; -/** +/* * Pixel-generator. ("system_global.h") */ typedef enum { -- cgit From 42f83a32259a82c4481d3cccf560c3bfeb32c892 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 15 Jul 2026 16:54:08 +0300 Subject: dt-bindings: media: i2c: Add Sony IMX678 Really add Sony IMX678 bindings this time. Those were accidentally missed from commit 447ea0c26e57 ("dt-bindings: media: i2c: Add Sony IMX678") even though the original patch included them. Fixes: 447ea0c26e57 ("dt-bindings: media: i2c: Add Sony IMX678") Signed-off-by: Sakari Ailus Acked-by: Rob Herring (Arm) Acked-by: Jai Luthra --- .../devicetree/bindings/media/i2c/sony,imx678.yaml | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml new file mode 100644 index 000000000000..098f72544216 --- /dev/null +++ b/Documentation/devicetree/bindings/media/i2c/sony,imx678.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2026 Ideas on Board Oy +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/i2c/sony,imx678.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sony IMX678 Sensor + +maintainers: + - Jai Luthra + +description: + Sony IMX678 diagonal 8.86 mm (Type 1/1.8) CMOS active pixel type solid-state + image sensor with a square pixel array and 8.40M (3856x2180) effective pixels. + +allOf: + - $ref: /schemas/media/video-interface-devices.yaml# + +properties: + compatible: + items: + - enum: + - sony,imx678-aamr + - sony,imx678-aaqr + - const: sony,imx678 + description: + The IMX678 sensor exists in a colour variant (IMX678-AAQR) and a mono + variant (IMX678-AAMR). The variant-specific compatible describes the + exact sensor variant, with the generic IMX678 compatible as a fallback. + + reg: + maxItems: 1 + + clocks: + description: | + Input clock (INCK). Only the rates listed in the datasheet are supported: + 13.5, 18, 24, 27, 36, 37.125, 72, and 74.25 MHz. + maxItems: 1 + + avdd-supply: + description: Analog power supply (3.3V) + + ovdd-supply: + description: Interface power supply (1.8V) + + dvdd-supply: + description: Digital power supply (1.1V) + + reset-gpios: + description: Sensor RESETN (XCLR) GPIO + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + additionalProperties: false + + properties: + endpoint: + $ref: /schemas/media/video-interfaces.yaml# + unevaluatedProperties: false + + properties: + data-lanes: + oneOf: + - items: + - const: 1 + - const: 2 + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + - const: 5 + - const: 6 + - const: 7 + - const: 8 + + required: + - data-lanes + - link-frequencies + + required: + - endpoint + +required: + - compatible + - reg + - clocks + - avdd-supply + - ovdd-supply + - dvdd-supply + - port + +unevaluatedProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + camera@1a { + compatible = "sony,imx678-aaqr", "sony,imx678"; + reg = <0x1a>; + clocks = <&imx678_clk>; + + avdd-supply = <&camera_avdd_3v3>; + ovdd-supply = <&camera_ovdd_1v8>; + dvdd-supply = <&camera_dvdd_1v1>; + + reset-gpios = <&gpio 0 GPIO_ACTIVE_LOW>; + + port { + imx678: endpoint { + remote-endpoint = <&cam>; + data-lanes = <1 2 3 4>; + link-frequencies = /bits/ 64 <720000000>; + }; + }; + }; + }; +... -- cgit From d590d26710f8cb5b6d2c990f08384d0c82a87755 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 1 Jul 2026 13:10:33 +0200 Subject: Documentation: media: vidioc-g-ext-ctrls: clarify try/set behavior It was not clearly stated in the documentation that calling VIDIOC_S_EXT_CTRLS implies an internal VIDIOC_TRY_EXT_CTRLS call. Clarify this. Signed-off-by: Hans Verkuil Acked-by: Linus Walleij Link: https://lore.kernel.org/linux-media/20260701-v4l2-doc-v1-1-9cce64b7a1c2@kernel.org/ Signed-off-by: Mauro Carvalho Chehab --- .../userspace-api/media/v4l/vidioc-g-ext-ctrls.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst index b8698b85bd80..d78328152b75 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-g-ext-ctrls.rst @@ -72,12 +72,6 @@ partial array, all elements have to be set or retrieved. The total size is calculated as ``elems`` * ``elem_size``. These values can be obtained by calling :ref:`VIDIOC_QUERY_EXT_CTRL `. -To change the value of a set of controls applications initialize the -``id``, ``size``, ``reserved2`` and ``value/value64/string/ptr`` fields -of each struct :c:type:`v4l2_ext_control` and call -the :ref:`VIDIOC_S_EXT_CTRLS ` ioctl. The controls will only be set if *all* -control values are valid. - To check if a set of controls have correct values applications initialize the ``id``, ``size``, ``reserved2`` and ``value/value64/string/ptr`` fields of each struct @@ -86,6 +80,15 @@ initialize the ``id``, ``size``, ``reserved2`` and values are automatically adjusted to a valid value or if an error is returned. +To change the value of a set of controls applications initialize the +``id``, ``size``, ``reserved2`` and ``value/value64/string/ptr`` fields +of each struct :c:type:`v4l2_ext_control` and call +the :ref:`VIDIOC_S_EXT_CTRLS ` ioctl. +:ref:`VIDIOC_S_EXT_CTRLS ` will implicitly call +:ref:`VIDIOC_TRY_EXT_CTRLS ` first and return an +error if that fails. So the controls will only be set if *all* +control values are valid. + When the ``id`` or ``which`` is invalid drivers return an ``EINVAL`` error code. When the value is out of bounds drivers can choose to take the closest valid value or return an ``ERANGE`` error code, whatever seems more -- cgit From 6eac7371089f0de61df274f4809b6a0e273572f7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 1 Jul 2026 16:25:03 +0200 Subject: media: vivid: always allow meta output streaming The vivid metadata output emulation only works if the video input is configured for a webcam. That makes no sense, and it is in fact a copy-and-paste from the metadata capture support. Always allow metadata output, just make sure that when the metadata is processed it only sets the brightness/saturation/ hue/contrast controls if they have been defined: if vivid was instantiation without video capture support, then those controls are missing. This change ensures that the test-media regression script correctly tests metadata output streaming. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/test-drivers/vivid/vivid-meta-out.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/media/test-drivers/vivid/vivid-meta-out.c b/drivers/media/test-drivers/vivid/vivid-meta-out.c index 55e5e5dec2f2..ca913b808225 100644 --- a/drivers/media/test-drivers/vivid/vivid-meta-out.c +++ b/drivers/media/test-drivers/vivid/vivid-meta-out.c @@ -17,12 +17,8 @@ static int meta_out_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], struct device *alloc_devs[]) { - struct vivid_dev *dev = vb2_get_drv_priv(vq); unsigned int size = sizeof(struct vivid_meta_out_buf); - if (!vivid_is_webcam(dev)) - return -EINVAL; - if (*nplanes) { if (sizes[0] < size) return -EINVAL; @@ -127,11 +123,6 @@ const struct vb2_ops vivid_meta_out_qops = { int vidioc_enum_fmt_meta_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct vivid_dev *dev = video_drvdata(file); - - if (!vivid_is_webcam(dev)) - return -EINVAL; - if (f->index > 0) return -EINVAL; @@ -143,12 +134,8 @@ int vidioc_enum_fmt_meta_out(struct file *file, void *priv, int vidioc_g_fmt_meta_out(struct file *file, void *priv, struct v4l2_format *f) { - struct vivid_dev *dev = video_drvdata(file); struct v4l2_meta_format *meta = &f->fmt.meta; - if (!vivid_is_webcam(dev) || !dev->has_meta_out) - return -EINVAL; - meta->dataformat = V4L2_META_FMT_VIVID; meta->buffersize = sizeof(struct vivid_meta_out_buf); return 0; @@ -159,10 +146,12 @@ void vivid_meta_out_process(struct vivid_dev *dev, { struct vivid_meta_out_buf *meta = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - v4l2_ctrl_s_ctrl(dev->brightness, meta->brightness); - v4l2_ctrl_s_ctrl(dev->contrast, meta->contrast); - v4l2_ctrl_s_ctrl(dev->saturation, meta->saturation); - v4l2_ctrl_s_ctrl(dev->hue, meta->hue); + if (dev->brightness) { + v4l2_ctrl_s_ctrl(dev->brightness, meta->brightness); + v4l2_ctrl_s_ctrl(dev->contrast, meta->contrast); + v4l2_ctrl_s_ctrl(dev->saturation, meta->saturation); + v4l2_ctrl_s_ctrl(dev->hue, meta->hue); + } dprintk(dev, 2, " %s brightness %u contrast %u saturation %u hue %d\n", __func__, meta->brightness, meta->contrast, -- cgit From be627f257ce283c95fd2988080dc0db3a20d0d2a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 11:53:17 +0200 Subject: media: cec: tegra_cec: don't break off msg on NACK The Tegra CEC hardware has a bug where, if the first attempt to transmit a message is NACKed so the transmit is aborted, then the second attempt can contain corrupt data. Ensure that the full message is always transmitted to avoid hitting this bug. I suspect some internal state is not reset in the case of aborting a message due to a NACK. Signed-off-by: Hans Verkuil Reviewed-by: Mikko Perttunen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/platform/tegra/tegra_cec.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index fe66336e734f..f8ffaeb78118 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -243,7 +243,18 @@ static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable) TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED); - cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE); + /* + * TX_NAK_MODE ensures that the whole message is transmitted even + * if each byte is NACKed. Without this flag the retransmit of the + * messages after a NACK can be corrupt. This is a bug in the hardware. + * + * While less efficient, in practice you rarely transmit messages + * that can be NACKed, with the exception of POLL messages which + * are just one byte anyway. + */ + cec_write(cec, TEGRA_CEC_HW_CONTROL, + TEGRA_CEC_HWCTRL_TX_RX_MODE | + TEGRA_CEC_HWCTRL_TX_NAK_MODE); return 0; } -- cgit From bce7236c16c2c6fb99857d90b928db135acf68bb Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 11:53:18 +0200 Subject: media: cec: tegra_cec: keep track of number of Rx Low Drives Keep track of Rx Low Drive conditions. Useful for detecting potential hardware/cable problems as it suggests unstable or incorrect voltage levels. Note that the Tx Low Drive conditions are already logged in the CEC core. Signed-off-by: Hans Verkuil Reviewed-by: Mikko Perttunen Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/platform/tegra/tegra_cec.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index f8ffaeb78118..e22a4625f192 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ struct tegra_cec { u32 tx_buf[CEC_MAX_MSG_SIZE]; u8 tx_buf_cur; u8 tx_buf_cnt; + u32 rx_total_low_drives; }; static inline u32 cec_read(struct tegra_cec *cec, u32 reg) @@ -116,6 +118,13 @@ static irqreturn_t tegra_cec_irq_handler(int irq, void *data) return IRQ_WAKE_THREAD; } + if (status & TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED) { + dev_warn_ratelimited(dev, "RX bus error detected, generated low drive\n"); + cec->rx_total_low_drives++; + cec_write(cec, TEGRA_CEC_INT_STAT, + TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED); + } + if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { tegra_cec_error_recovery(cec); @@ -241,6 +250,7 @@ static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable) TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED | TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED | TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED); /* @@ -318,11 +328,20 @@ static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, return 0; } +static void tegra_cec_adap_status(struct cec_adapter *adap, struct seq_file *file) +{ + struct tegra_cec *cec = adap->priv; + + seq_printf(file, "receive low drive count: %u\n", + cec->rx_total_low_drives); +} + static const struct cec_adap_ops tegra_cec_ops = { .adap_enable = tegra_cec_adap_enable, .adap_log_addr = tegra_cec_adap_log_addr, .adap_transmit = tegra_cec_adap_transmit, .adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable, + .adap_status = tegra_cec_adap_status, }; static int tegra_cec_probe(struct platform_device *pdev) -- cgit From e6dcc43e5174d685e4e9685d4f241130f5f5f020 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:29 +0200 Subject: media: cec/core: max_retries -> max_attempts Rename max_retries to max_attempts. This counter really is for the number of attempts, not the number of retries. Also increase the number of attempts from 2 to 3. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index de0c4fcd8dfe..e9dce0dfd102 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1317,7 +1317,7 @@ static int cec_config_log_addr(struct cec_adapter *adap, { struct cec_log_addrs *las = &adap->log_addrs; struct cec_msg msg = { }; - const unsigned int max_retries = 2; + const unsigned int max_attempts = 3; unsigned int i; int err; @@ -1328,7 +1328,7 @@ static int cec_config_log_addr(struct cec_adapter *adap, msg.len = 1; msg.msg[0] = (log_addr << 4) | log_addr; - for (i = 0; i < max_retries; i++) { + for (i = 0; i < max_attempts; i++) { err = cec_transmit_msg_fh(adap, &msg, NULL, true); /* @@ -1357,19 +1357,19 @@ static int cec_config_log_addr(struct cec_adapter *adap, if (msg.tx_status & CEC_TX_STATUS_NACK) break; /* - * Retry up to max_retries times if the message was neither + * Do up to max_attempts if the message was neither * OKed or NACKed. This can happen due to e.g. a Lost * Arbitration condition. */ } /* - * If we are unable to get an OK or a NACK after max_retries attempts + * If we are unable to get an OK or a NACK after max_attempts * (and note that each attempt already consists of four polls), then * we assume that something is really weird and that it is not a * good idea to try and claim this logical address. */ - if (i == max_retries) { + if (i == max_attempts) { dprintk(0, "polling for LA %u failed with tx_status=0x%04x\n", log_addr, msg.tx_status); return 0; -- cgit From 0c138a5d5045f06618ff581f7a65f3149c033902 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:30 +0200 Subject: media: cec/core: drop ABORTED/TIMEOUT check When claiming a logical address, don't break off the process when the transmit returns ABORTED or TIMEOUT. Just do another attempt in that case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index e9dce0dfd102..0c2f4660a7bd 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1345,13 +1345,6 @@ static int cec_config_log_addr(struct cec_adapter *adap, if (err) return err; - /* - * The message was aborted or timed out due to a disconnect or - * unconfigure, just bail out. - */ - if (msg.tx_status & - (CEC_TX_STATUS_ABORTED | CEC_TX_STATUS_TIMEOUT)) - return -EINTR; if (msg.tx_status & CEC_TX_STATUS_OK) return 0; if (msg.tx_status & CEC_TX_STATUS_NACK) -- cgit From 67d95987201591a91067a3e49ebef265fd968df6 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:31 +0200 Subject: media: cec/core: handle core events like normal events Currently there is a distinction between core events (CEC_EVENT_STATE_CHANGE and CEC_EVENT_LOST_MSGS) and other events. The core events do not require memory allocations, so are a bit faster, but they are also limited to just a single event: if a new event comes in, then that replaces the old one. It's all overly complicated, and with only one state change event it is easy to miss state changes. So just drop that optimization, and allow for up to 3 state change events. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 34 ++++++++++++++++------------------ drivers/media/cec/core/cec-api.c | 5 ++--- include/media/cec.h | 2 -- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 0c2f4660a7bd..bbd5395fa67d 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -80,9 +80,9 @@ void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { static const u16 max_events[CEC_NUM_EVENTS] = { - 1, 1, 800, 800, 8, 8, 8, 8 + 3, 1, 800, 800, 8, 8, 8, 8 }; - struct cec_event_entry *entry; + struct cec_event_entry *new_entry, *entry; unsigned int ev_idx = new_ev->event - 1; if (WARN_ON(ev_idx >= ARRAY_SIZE(fh->events))) @@ -92,36 +92,34 @@ void cec_queue_event_fh(struct cec_fh *fh, ts = ktime_get_ns(); mutex_lock(&fh->lock); - if (ev_idx < CEC_NUM_CORE_EVENTS) - entry = &fh->core_events[ev_idx]; - else - entry = kmalloc_obj(*entry); - if (entry) { + new_entry = kmalloc_obj(*new_entry); + if (new_entry) { if (new_ev->event == CEC_EVENT_LOST_MSGS && fh->queued_events[ev_idx]) { + entry = list_first_entry(&fh->events[ev_idx], + struct cec_event_entry, list); entry->ev.lost_msgs.lost_msgs += new_ev->lost_msgs.lost_msgs; + kfree(new_entry); goto unlock; } - entry->ev = *new_ev; - entry->ev.ts = ts; + new_entry->ev = *new_ev; + new_entry->ev.ts = ts; if (fh->queued_events[ev_idx] < max_events[ev_idx]) { /* Add new msg at the end of the queue */ - list_add_tail(&entry->list, &fh->events[ev_idx]); + list_add_tail(&new_entry->list, &fh->events[ev_idx]); fh->queued_events[ev_idx]++; fh->total_queued_events++; goto unlock; } - if (ev_idx >= CEC_NUM_CORE_EVENTS) { - list_add_tail(&entry->list, &fh->events[ev_idx]); - /* drop the oldest event */ - entry = list_first_entry(&fh->events[ev_idx], - struct cec_event_entry, list); - list_del(&entry->list); - kfree(entry); - } + list_add_tail(&new_entry->list, &fh->events[ev_idx]); + /* drop the oldest event */ + entry = list_first_entry(&fh->events[ev_idx], + struct cec_event_entry, list); + list_del(&entry->list); + kfree(entry); } /* Mark that events were lost */ entry = list_first_entry_or_null(&fh->events[ev_idx], diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index 103ded79526f..a491d7fc8a71 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -345,8 +345,7 @@ static long cec_dqevent(struct cec_adapter *adap, struct cec_fh *fh, if (copy_to_user(parg, &ev->ev, sizeof(ev->ev))) err = -EFAULT; - if (ev_idx >= CEC_NUM_CORE_EVENTS) - kfree(ev); + kfree(ev); fh->queued_events[ev_idx]--; fh->total_queued_events--; @@ -673,7 +672,7 @@ static int cec_release(struct inode *inode, struct file *filp) list_del(&entry->list); kfree(entry); } - for (i = CEC_NUM_CORE_EVENTS; i < CEC_NUM_EVENTS; i++) { + for (i = 0; i < CEC_NUM_EVENTS; i++) { while (!list_empty(&fh->events[i])) { struct cec_event_entry *entry = list_first_entry(&fh->events[i], diff --git a/include/media/cec.h b/include/media/cec.h index 0c8e86115b6f..5aff399e69e6 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -85,7 +85,6 @@ struct cec_event_entry { struct cec_event ev; }; -#define CEC_NUM_CORE_EVENTS 2 #define CEC_NUM_EVENTS CEC_EVENT_PIN_5V_HIGH struct cec_fh { @@ -101,7 +100,6 @@ struct cec_fh { struct list_head events[CEC_NUM_EVENTS]; /* queued events */ u16 queued_events[CEC_NUM_EVENTS]; unsigned int total_queued_events; - struct cec_event_entry core_events[CEC_NUM_CORE_EVENTS]; struct list_head msgs; /* queued messages */ unsigned int queued_msgs; }; -- cgit From 2ff4b91e82bd76e1a6e1ff22bfc9aeda08e12a61 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:32 +0200 Subject: media: cec/core: drop cec_post_state_event call No need to call cec_post_state_event() if the next call is to cec_adap_unconfigure() since that will also call cec_post_state_event(). This fixes the case when, when the physical address is invalidated, you get two CEC_EVENT_STATE_CHANGE events: one with a non-zero log_addr_mask and one with a zero log_addr_mask. You just want to see the event with phys_addr set to f.f.f.f and log_addr_mask set to 0. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index bbd5395fa67d..5c7ede88c3df 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1708,7 +1708,6 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) cec_phys_addr_exp(phys_addr)); if (becomes_invalid || !is_invalid) { adap->phys_addr = CEC_PHYS_ADDR_INVALID; - cec_post_state_event(adap); cec_adap_unconfigure(adap); if (becomes_invalid) { cec_adap_enable(adap); -- cgit From 350308a05f7dd5e03ab8d966dd672288749efb64 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:33 +0200 Subject: media: cec/core: flush stale STATE_CHANGE events If the physical address becomes invalid, then flush any old STATE_CHANGE events since those are no longer relevant. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 5c7ede88c3df..4d14186bfef6 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -103,9 +103,32 @@ void cec_queue_event_fh(struct cec_fh *fh, kfree(new_entry); goto unlock; } + new_entry->ev = *new_ev; new_entry->ev.ts = ts; + /* + * If the physical address becomes invalid (HPD went low), + * then just flush all pending STATE_CHANGE events since + * those are all obsoleted. + * + * This ensures you will not see stale STATE_CHANGE events. + */ + if (new_ev->event == CEC_EVENT_STATE_CHANGE && + new_ev->state_change.phys_addr == CEC_PHYS_ADDR_INVALID && + fh->queued_events[ev_idx]) { + /* drop all events */ + while (!list_empty(&fh->events[ev_idx])) { + entry = list_first_entry(&fh->events[ev_idx], + struct cec_event_entry, list); + list_del(&entry->list); + kfree(entry); + fh->total_queued_events--; + fh->queued_events[ev_idx]--; + } + new_entry->ev.flags |= CEC_EVENT_FL_DROPPED_EVENTS; + } + if (fh->queued_events[ev_idx] < max_events[ev_idx]) { /* Add new msg at the end of the queue */ list_add_tail(&new_entry->list, &fh->events[ev_idx]); -- cgit From cba172820898140dc1fd4c2223ddf3ded978d2b2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:34 +0200 Subject: media: cec/core: add a new CEC_LOG_ADDRS_FL_CONFIG_FAILED flag If claiming a logical address fails, then set the CEC_LOG_ADDRS_FL_CONFIG_FAILED flag. This makes it possible for userspace to detect this corner case. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst | 7 +++++++ drivers/media/cec/core/cec-adap.c | 3 +++ include/uapi/linux/cec.h | 2 ++ 3 files changed, 12 insertions(+) diff --git a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst index f3293a589dd6..8397bf573798 100644 --- a/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst +++ b/Documentation/userspace-api/media/cec/cec-ioc-adap-g-log-addrs.rst @@ -185,6 +185,13 @@ logical address types are already defined will return with error ``EBUSY``. are CEC devices that can only handle CDC messages. All other messages are ignored. + * .. _`CEC-LOG-ADDRS-FL-CONFIG-FAILED`: + + - ``CEC_LOG_ADDRS_FL_CONFIG_FAILED`` + - 8 + - If this flag is set, then the CEC device failed to claim a free logical + address and is in the unconfigured state. This can never happen if + ``CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK`` was set as well. .. tabularcolumns:: |p{7.8cm}|p{1.0cm}|p{8.5cm}| diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 4d14186bfef6..774bf9099183 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1482,6 +1482,7 @@ static int cec_config_thread_func(void *arg) dprintk(1, "physical address: %x.%x.%x.%x, claim %d logical addresses\n", cec_phys_addr_exp(adap->phys_addr), las->num_log_addrs); las->log_addr_mask = 0; + las->flags &= ~CEC_LOG_ADDRS_FL_CONFIG_FAILED; if (las->log_addr_type[0] == CEC_LOG_ADDR_TYPE_UNREGISTERED) goto configured; @@ -1614,6 +1615,8 @@ configured: unconfigure: for (i = 0; i < las->num_log_addrs; i++) las->log_addr[i] = CEC_LOG_ADDR_INVALID; + if (adap->phys_addr != CEC_PHYS_ADDR_INVALID) + las->flags |= CEC_LOG_ADDRS_FL_CONFIG_FAILED; cec_adap_unconfigure(adap); adap->is_configuring = false; adap->must_reconfigure = false; diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 81a05c9c0706..fdfc97a6e4ec 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -403,6 +403,8 @@ struct cec_log_addrs { #define CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU (1 << 1) /* CDC-Only device: supports only CDC messages */ #define CEC_LOG_ADDRS_FL_CDC_ONLY (1 << 2) +/* Configuration failed */ +#define CEC_LOG_ADDRS_FL_CONFIG_FAILED (1 << 3) /** * struct cec_drm_connector_info - tells which drm connector is -- cgit From cefce07c6ca56c826e046feaa70b82a0d708bc71 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:35 +0200 Subject: media: cec/core: cec-pin: toggle rx_toggle when arb lost If we inject an Arbitration Lost error, then manually toggle rx_toggle instead of waiting for cec_pin_to_idle(). When handling the Arbitration Lost error injection we are switching to TX mode, and as a result when cec_pin_to_idle() is called when the transmit ends it would never toggle rx_toggle since it is no longer in RX mode. Without this change the 'any,toggle rx-arb-lost' error injection would, once it is on, always stay on. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-pin.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c index 085fc12067af..6a0ee32e8401 100644 --- a/drivers/media/cec/core/cec-pin.c +++ b/drivers/media/cec/core/cec-pin.c @@ -692,7 +692,6 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) v = cec_pin_read(pin); if (!v) break; - pin->state = CEC_ST_RX_START_BIT_HIGH; delta = ktime_us_delta(ts, pin->ts); /* Start bit low is too short, go back to idle */ if (delta < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_IDLE_SAMPLE) { @@ -703,7 +702,16 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts) cec_pin_to_idle(pin); break; } + pin->state = CEC_ST_RX_START_BIT_HIGH; if (rx_arb_lost(pin, &poll)) { + /* + * Normally rx_toggle is toggled in cec_pin_to_idle() + * when we're in an RX state, but here we switch to TX + * mode, so cec_pin_to_idle() sees a TX mode and never + * toggles rx_toggle. So toggle it here as a special + * corner case. + */ + pin->rx_toggle ^= 1; cec_msg_init(&pin->tx_msg, poll >> 4, poll & 0xf); pin->tx_generated_poll = true; pin->tx_extra_bytes = 0; -- cgit From 22c0f8e2d9fa156ac25ff1854142956f2464e5c8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 10 Jul 2026 13:07:36 +0200 Subject: media: cec/core: add error-inj-tx-timeouts debugfs entry Add a new debugfs entry that makes it possible to do error injection of failing the next N transmits by a timeout. This can be used to test what happens in that case during the claiming of a free logical address. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/core/cec-adap.c | 7 +++++++ drivers/media/cec/core/cec-core.c | 31 +++++++++++++++++++++++++++++++ include/media/cec.h | 4 ++++ 3 files changed, 42 insertions(+) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 774bf9099183..ee8a200b9803 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -629,6 +629,13 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, attempts_made = 1; mutex_lock(&adap->lock); + if (adap->error_inj_tx_timeouts) { + dprintk(2, "%s: error_inj_tx_timeouts %u\n", + __func__, adap->error_inj_tx_timeouts); + adap->error_inj_tx_timeouts--; + mutex_unlock(&adap->lock); + return; + } data = adap->transmitting; if (!data) { /* diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c index c51e769b4e34..230f8a413e74 100644 --- a/drivers/media/cec/core/cec-core.c +++ b/drivers/media/cec/core/cec-core.c @@ -223,6 +223,34 @@ static int cec_error_inj_show(struct seq_file *sf, void *unused) return call_op(adap, error_inj_show, sf); } DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj); + +static ssize_t cec_error_inj_tx_timeouts_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *sf = file->private_data; + struct cec_adapter *adap = sf->private; + int ret; + + if (count > 5) + return -EINVAL; + + mutex_lock(&adap->lock); + ret = kstrtou32_from_user(ubuf, count, 0, &adap->error_inj_tx_timeouts); + if (ret) + adap->error_inj_tx_timeouts = 0; + mutex_unlock(&adap->lock); + return ret ? : count; +} + +static int cec_error_inj_tx_timeouts_show(struct seq_file *sf, void *unused) +{ + struct cec_adapter *adap = sf->private; + + seq_printf(sf, "%u", adap->error_inj_tx_timeouts); + return 0; +} + +DEFINE_SHOW_STORE_ATTRIBUTE(cec_error_inj_tx_timeouts); #endif struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, @@ -367,6 +395,9 @@ int cec_register_adapter(struct cec_adapter *adap, debugfs_create_devm_seqfile(&adap->devnode.dev, "status", adap->cec_dir, cec_adap_status); + debugfs_create_file("error-inj-tx-timeouts", 0644, adap->cec_dir, adap, + &cec_error_inj_tx_timeouts_fops); + if (!adap->ops->error_inj_show || !adap->ops->error_inj_parse_line) return 0; debugfs_create_file("error-inj", 0644, adap->cec_dir, adap, diff --git a/include/media/cec.h b/include/media/cec.h index 5aff399e69e6..4c8b0950b377 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -221,6 +221,8 @@ struct cec_adap_ops { * @tx_error_log_cnt: number of logged Error transmits since the adapter was * enabled. Used to avoid flooding the kernel log if this * happens a lot. + * @error_inj_tx_timeouts: error injection: the next @error_inj_tx_timeouts + * transmits will time out. * @notifier: CEC notifier * @pin: CEC pin status struct * @cec_dir: debugfs cec directory @@ -281,6 +283,8 @@ struct cec_adapter { u32 tx_low_drive_log_cnt; u32 tx_error_log_cnt; + u32 error_inj_tx_timeouts; + #ifdef CONFIG_CEC_NOTIFIER struct cec_notifier *notifier; #endif -- cgit From 7acc334cf038b5ac1a7ee480d3ec59d288d2f5f3 Mon Sep 17 00:00:00 2001 From: Sven Püschel Date: Fri, 5 Jun 2026 15:32:01 +0200 Subject: media: rockchip: rga: don't change RGB quantization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't change the quantization of an RGB format when converting between RGB and YUV with the RGA3. As the RGA3 only supports doing conversions to full range YUV with BT601, it wants to announce it through try_fmt/s_fmt. As it is only relevant, when converting between RGB and YUV, it's guarded by a given condition. But the condition also causes the RGB format quantization to be adjusted to limited range, which is incorrect. Therefore simplify the condition to only apply when the current format is a YUV format. Also document the reason for checking if the other format is an RGB format. Fixes: 24a63d4c9d3c ("media: rockchip: rga: add rga3 support") Cc: stable@vger.kernel.org Signed-off-by: Sven Püschel Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rga/rga3-hw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c index ca1c268303dd..c4a71306278b 100644 --- a/drivers/media/platform/rockchip/rga/rga3-hw.c +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c @@ -450,10 +450,14 @@ static void *rga3_adjust_and_map_format(struct rga_ctx *ctx, other_format = is_output ? &ctx->out.pix : &ctx->in.pix; other_format_info = v4l2_format_info(other_format->pixelformat); - if ((v4l2_is_format_rgb(format_info) && - v4l2_is_format_yuv(other_format_info)) || - (v4l2_is_format_yuv(format_info) && - v4l2_is_format_rgb(other_format_info))) { + /* + * Only apply the quantization restrictions when we need to + * convert between RGB and YUV. Otherwise there is no point + * to limit the quantization for operations like scaling or + * rotations. + */ + if (v4l2_is_format_yuv(format_info) && + v4l2_is_format_rgb(other_format_info)) { /* * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions * Additionally BT709 and BT2020 only support limited range YUV. -- cgit From 307673410774d5708232d05a977f047c03e2a4bb Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Wed, 25 Mar 2026 11:57:41 +0800 Subject: media: platform: mtk-mdp3: Refactor CMDQ writes for CMDQ API change Update CMDQ register writes to use subsys-aware APIs, cmdq_pkt_write_subsys() and cmdq_pkt_write_mask_subsys(). This conforms to recent CMDQ API changes that split access by subsys ID support. Since all current MDP SoCs support subsys ID, and future MDP deployments will not run on SoCs without subsys ID, only subsys-specific API calls are needed. No logic for non-subsys ID hardware is required. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c | 4 ++-- drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c index d30a05782ab9..8dff981f6720 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -321,7 +321,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Enable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value); + cmdq_pkt_write_subsys(&cmd->pkt, set->subsys_id, set->reg, set->reg, set->value); } /* Config sub-frame information */ for (index = (num_comp - 1); index >= 0; index--) { @@ -376,7 +376,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd, /* Disable mux settings */ for (index = 0; index < ctrl->num_sets; index++) { set = &ctrl->sets[index]; - cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0); + cmdq_pkt_write_subsys(&cmd->pkt, set->subsys_id, set->reg, set->reg, 0); } return 0; diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h index 681906c16419..c6fc180950f2 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h @@ -12,14 +12,14 @@ #define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \ do { \ typeof(mask) (m) = (mask); \ - cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \ - (val), \ + cmdq_pkt_write_mask_subsys(&((cmd)->pkt), (id), (base), \ + (base) + (ofst), (val), \ (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \ (0xffffffff) : (m)); \ } while (0) #define MM_REG_WRITE(cmd, id, base, ofst, val) \ - cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val)) + cmdq_pkt_write_subsys(&((cmd)->pkt), (id), (base), (base) + (ofst), (val)) #define MM_REG_WAIT(cmd, evt) \ do { \ -- cgit From 8d00b49be7f75246bcd081d04af9757ff9ff7d0c Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Wed, 25 Mar 2026 11:57:42 +0800 Subject: media: platform: mtk-mdp3: Change cmdq_pkt_jump_rel() to cmdq_pkt_jump_rel_temp() To facilitate the removal of the shift_pa parameter from cmdq_pkt_jump_rel(), current users of cmdq_pkt_jump_rel() need to transition to using cmdq_pkt_jump_rel_temp() before the API change is implemented. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c index 8dff981f6720..96a66aadf0cd 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c @@ -628,7 +628,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp, goto err_free_path; } cmdq_pkt_eoc(&cmd->pkt); - cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]); + cmdq_pkt_jump_rel_temp(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]); for (i = 0; i < num_comp; i++) { s32 inner_id = MDP_COMP_NONE; -- cgit From 55793e4665b7f15151e6f5ab51ca980e73abed5d Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sun, 5 Jul 2026 01:31:19 +0800 Subject: media: platform: mtk-mdp3: Fix SCP device refcounting mdp_probe() first tries to get the SCP handle with scp_get(). When that fails, it falls back to looking up the SCP platform device with __get_pdev_by_id() and then reads its driver data. The fallback lookup returns the platform device with a reference, just like scp_get() does. However, the fallback path currently drops that reference immediately after platform_get_drvdata(). The driver later still calls scp_put(mdp->scp) unconditionally from the probe error path and from mdp_video_device_release(), which drops the SCP device reference again. Keep the fallback reference until the existing scp_put() call, so that the fallback path follows the same ownership rules as the scp_get() path. Fixes: 8f6f3aa21517 ("media: platform: mtk-mdp3: fix device leaks at probe") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Johan Hovold Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 8f4da4cf55d2..121b1006ffdc 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -294,7 +294,6 @@ static int mdp_probe(struct platform_device *pdev) goto err_destroy_clock_wq; } mdp->scp = platform_get_drvdata(mm_pdev); - put_device(&mm_pdev->dev); } mdp->rproc_handle = scp_get_rproc(mdp->scp); -- cgit From 90368323fb244da0504e3da37a182f8e89bcc3b9 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 6 Jul 2026 08:59:27 +0200 Subject: media: platform: mtk-mdp3: fix NULL deref on failed SCP lookup Add the missing sanity check after looking up the SCP to avoid dereferencing a NULL-pointer in case its driver has not yet been bound. Fixes: 61890ccaefaf ("media: platform: mtk-mdp3: add MediaTek MDP3 driver") Cc: stable@vger.kernel.org # 6.1 Cc: Moudy Ho Signed-off-by: Johan Hovold Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c index 121b1006ffdc..f412aadb5996 100644 --- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c +++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c @@ -296,6 +296,11 @@ static int mdp_probe(struct platform_device *pdev) mdp->scp = platform_get_drvdata(mm_pdev); } + if (!mdp->scp) { + ret = -EPROBE_DEFER; + goto err_destroy_clock_wq; + } + mdp->rproc_handle = scp_get_rproc(mdp->scp); dev_dbg(&pdev->dev, "MDP rproc_handle: %p", mdp->rproc_handle); -- cgit From 1e77971dbe79120eeb47ee115858b9749285a96b Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 6 May 2026 16:42:00 +0800 Subject: media: mediatek: vcodec: remove redundant return value of vdec_vp9_slice_setup_lat_buffer() vdec_vp9_slice_setup_lat_buffer() never fails, remove the 'return 0' from vdec_vp9_slice_setup_lat_buffer() and modify its signature. Also, remove the return value check in vdec_vp9_slice_setup_lat(). Signed-off-by: Haoxiang Li Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index adbacabdbebc..e5316b8c9b26 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -974,10 +974,10 @@ static int vdec_vp9_slice_setup_pfc(struct vdec_vp9_slice_instance *instance, return 0; } -static int vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *instance, - struct vdec_vp9_slice_vsi *vsi, - struct mtk_vcodec_mem *bs, - struct vdec_lat_buf *lat_buf) +static void vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *instance, + struct vdec_vp9_slice_vsi *vsi, + struct mtk_vcodec_mem *bs, + struct vdec_lat_buf *lat_buf) { int i; @@ -1011,8 +1011,6 @@ static int vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *insta vsi->row_info.buf = 0; vsi->row_info.size = 0; - - return 0; } static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance, @@ -1166,9 +1164,7 @@ static int vdec_vp9_slice_setup_lat(struct vdec_vp9_slice_instance *instance, if (ret) goto err; - ret = vdec_vp9_slice_setup_lat_buffer(instance, vsi, bs, lat_buf); - if (ret) - goto err; + vdec_vp9_slice_setup_lat_buffer(instance, vsi, bs, lat_buf); vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]); -- cgit From 2a577b0bd604dd1173c8453a594f780a2a33c90e Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Wed, 6 May 2026 16:42:01 +0800 Subject: media: mediatek: vcodec: remove redundant return value of vdec_vp9_slice_setup_prob_buffer() vdec_vp9_slice_setup_prob_buffer() never fails, remove 'return 0' from vdec_vp9_slice_setup_prob_buffer() and modify its signature. Also, remove the return value check in vdec_vp9_slice_setup_single() and vdec_vp9_slice_setup_lat(). Signed-off-by: Haoxiang Li Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c index e5316b8c9b26..ba7a1cd998b9 100644 --- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c @@ -1013,8 +1013,8 @@ static void vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *inst vsi->row_info.size = 0; } -static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance, - struct vdec_vp9_slice_vsi *vsi) +static void vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance, + struct vdec_vp9_slice_vsi *vsi) { struct vdec_vp9_slice_frame_ctx *frame_ctx; struct vdec_vp9_slice_uncompressed_header *uh; @@ -1030,8 +1030,6 @@ static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *inst else frame_ctx = vdec_vp9_slice_default_frame_ctx; memcpy(instance->prob.va, frame_ctx, sizeof(*frame_ctx)); - - return 0; } static void vdec_vp9_slice_setup_seg_buffer(struct vdec_vp9_slice_instance *instance, @@ -1170,9 +1168,7 @@ static int vdec_vp9_slice_setup_lat(struct vdec_vp9_slice_instance *instance, /* setup prob/tile buffers for LAT */ - ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi); - if (ret) - goto err; + vdec_vp9_slice_setup_prob_buffer(instance, vsi); ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs); if (ret) @@ -1804,10 +1800,7 @@ static int vdec_vp9_slice_setup_single(struct vdec_vp9_slice_instance *instance, vdec_vp9_slice_setup_single_buffer(instance, pfc, vsi, bs, fb); vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]); - - ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi); - if (ret) - goto err; + vdec_vp9_slice_setup_prob_buffer(instance, vsi); ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs); if (ret) -- cgit From f0b9d7e5be061b4858279d451f5a6ad0ed20b1be Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 15:47:35 -0400 Subject: media: rkvdec: hevc: tighten EXT SPS RPS control dimensions The VDPU381 HEVC driver registers V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS and V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS with .cfg.dims = { 65 }, but the HEVC spec caps num_short_term_ref_pic_sets at 64 (ITU-T H.265 7.4.8) and num_long_term_ref_pics_sps at 32 (7.4.3.2.1). The hardware descriptor table (struct rkvdec_rps) sizes match those spec limits: refs[32] and short_term_ref_sets[64]. Reduce the dims to { 64 } and { 32 } respectively so the V4L2 control framework rejects oversized payloads before any driver code runs. Fixes: c9a59dc2acc7 ("media: rkvdec: Add HEVC support for the VDPU381 variant") Cc: stable@vger.kernel.org Suggested-by: Detlev Casanova Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rkvdec/rkvdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 1d1e9bfef8e9..4fd06f4c04db 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -278,12 +278,12 @@ static const struct rkvdec_ctrl_desc vdpu38x_hevc_ctrl_descs[] = { { .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS, .cfg.ops = &rkvdec_ctrl_ops, - .cfg.dims = { 65 }, + .cfg.dims = { 64 }, }, { .cfg.id = V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS, .cfg.ops = &rkvdec_ctrl_ops, - .cfg.dims = { 65 }, + .cfg.dims = { 32 }, }, }; -- cgit From 796b5c6d4f1615d59d5d8fe5a38fae6bfdfe878e Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 15:47:36 -0400 Subject: media: v4l2-ctrls: validate HEVC EXT SPS RPS counts The HEVC SPS control carries the short-term and long-term RPS counts that decoder drivers use to walk the matching EXT SPS dynamic arrays. Reject SPS values that exceed the HEVC limits of 64 short-term sets and 32 long-term references so drivers cannot later index beyond those controls. Also reject EXT SPS ST RPS entries whose negative or positive picture counts exceed the 16-entry arrays, or whose combined delta-POC count exceeds the HEVC DPB maximum. Fixes: c9a59dc2acc7 ("media: rkvdec: Add HEVC support for the VDPU381 variant") Cc: stable@vger.kernel.org Suggested-by: Detlev Casanova Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-ctrls-core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index ba047d7d8601..1214e7744ac0 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -16,6 +16,9 @@ static const union v4l2_ctrl_ptr ptr_null; +#define V4L2_HEVC_MAX_SHORT_TERM_REF_PIC_SETS 64 +#define V4L2_HEVC_MAX_LONG_TERM_REF_PICS_SPS 32 + static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { @@ -1214,6 +1217,10 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_HEVC_SPS: p_hevc_sps = p; + if (p_hevc_sps->num_short_term_ref_pic_sets > + V4L2_HEVC_MAX_SHORT_TERM_REF_PIC_SETS) + return -EINVAL; + if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED)) { p_hevc_sps->pcm_sample_bit_depth_luma_minus1 = 0; p_hevc_sps->pcm_sample_bit_depth_chroma_minus1 = 0; @@ -1224,6 +1231,9 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, if (!(p_hevc_sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT)) p_hevc_sps->num_long_term_ref_pics_sps = 0; + else if (p_hevc_sps->num_long_term_ref_pics_sps > + V4L2_HEVC_MAX_LONG_TERM_REF_PICS_SPS) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_PPS: @@ -1280,6 +1290,11 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, if (p_hevc_st_rps->flags & ~V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED) return -EINVAL; + if (p_hevc_st_rps->num_negative_pics > 16 || + p_hevc_st_rps->num_positive_pics > 16 || + p_hevc_st_rps->num_negative_pics + + p_hevc_st_rps->num_positive_pics > 16) + return -EINVAL; break; case V4L2_CTRL_TYPE_HEVC_EXT_SPS_LT_RPS: -- cgit From 052c5ed5a1d96a6b24fd50ccda16fc6841ee7ca3 Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Wed, 27 May 2026 15:47:37 -0400 Subject: media: rkvdec: hevc: guard INTER_REF_PIC_SET_PRED index underflow st_ref_pic_set_prediction() computes the reference RPS index as st_rps_idx - (delta_idx_minus1 + 1) per HEVC spec equation 7-59. Both operands are u8, so when delta_idx_minus1 + 1 exceeds the current index the subtraction wraps and the subsequent array access at calculated_rps_st_sets[ref_rps_idx] reads far out of bounds. A userspace V4L2 client that can open the RKVDEC m2m decoder can submit an EXT_SPS_ST_RPS control with INTER_REF_PIC_SET_PRED set and delta_idx_minus1 crafted to trigger the underflow. Reject the entry early when the reference index would underflow. Fixes: c9a59dc2acc7 ("media: rkvdec: Add HEVC support for the VDPU381 variant") Cc: stable@vger.kernel.org Suggested-by: Detlev Casanova Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c index f89602075121..9c4a6093af32 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c @@ -209,6 +209,9 @@ static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx, int i, j; int dPoc; + if ((unsigned int)rps_data->delta_idx_minus1 + 1 > idx) + return; + ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */ delta_rps = (1 - 2 * rps_data->delta_rps_sign) * (rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */ -- cgit From c37aca64206fafe938119e801a3fd10a537a051f Mon Sep 17 00:00:00 2001 From: Narasimharao Vadlamudi Date: Wed, 1 Jul 2026 16:42:03 +0530 Subject: media: rkvdec: Propagate platform_get_irq() errors platform_get_irq() returns a positive IRQ number on success and a negative error code on failure. It no longer returns zero. The driver currently returns -ENXIO for all failures, which loses useful errors such as -EPROBE_DEFER. Return the error from platform_get_irq() directly. Fixes: cd33c830448b ("media: rkvdec: Add the rkvdec driver") Cc: stable@vger.kernel.org Signed-off-by: Narasimharao Vadlamudi Reviewed-by: Detlev Casanova Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rkvdec/rkvdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c index 4fd06f4c04db..061281f903f3 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c @@ -1818,8 +1818,8 @@ static int rkvdec_probe(struct platform_device *pdev) vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + if (irq < 0) + return irq; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, rkvdec_irq_handler, IRQF_ONESHOT, -- cgit From 28ceb7eb73c90ef6f59c24a7fcc4fe8e42183b2c Mon Sep 17 00:00:00 2001 From: Hiroki Nakajima <3na7nanana@gmail.com> Date: Sun, 12 Jul 2026 16:26:52 +0900 Subject: media: rkvdec: use DIV_ROUND_UP() for CTB counts Use DIV_ROUND_UP() when computing HEVC coding tree block counts instead of open-coding the same rounding expression. This keeps the rounding intent explicit without changing behavior. Found using a Coccinelle rule generated from the DIV_ROUND_UP() macro definition. Signed-off-by: Hiroki Nakajima <3na7nanana@gmail.com> Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c | 4 ++-- drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c | 8 ++++---- drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c index 87abf93dfd5e..ff3942f91c5d 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c @@ -258,9 +258,9 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, for (i = 0; i <= pps->num_tile_rows_minus1; i++) WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i)); } else { - WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1, + WRITE_PPS(DIV_ROUND_UP(sps->pic_width_in_luma_samples, ctb_size_y) - 1, COLUMN_WIDTH(0)); - WRITE_PPS(((sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1, + WRITE_PPS(DIV_ROUND_UP(sps->pic_height_in_luma_samples, ctb_size_y) - 1, ROW_HEIGHT(0)); } diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c index fe6414a17551..d07c74679552 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c @@ -261,8 +261,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(row_height, 0, sizeof(row_height)); max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size); - pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width; - pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width; + pic_in_cts_width = DIV_ROUND_UP(width, max_cu_width); + pic_in_cts_height = DIV_ROUND_UP(height, max_cu_width); if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) { if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) { @@ -275,8 +275,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, column_width, row_height); } } else { - column_width[0] = (width + max_cu_width - 1) / max_cu_width; - row_height[0] = (height + max_cu_width - 1) / max_cu_width; + column_width[0] = DIV_ROUND_UP(width, max_cu_width); + row_height[0] = DIV_ROUND_UP(height, max_cu_width); } for (i = 0; i < 20; i++) { diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c index 3575338a531a..3462d995d4cf 100644 --- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c +++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c @@ -287,8 +287,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, memset(row_height, 0, sizeof(row_height)); max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size); - pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width; - pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width; + pic_in_cts_width = DIV_ROUND_UP(width, max_cu_width); + pic_in_cts_height = DIV_ROUND_UP(height, max_cu_width); if (tiles_enabled) { if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) { @@ -301,8 +301,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx, column_width, row_height); } } else { - column_width[0] = (width + max_cu_width - 1) / max_cu_width; - row_height[0] = (height + max_cu_width - 1) / max_cu_width; + column_width[0] = DIV_ROUND_UP(width, max_cu_width); + row_height[0] = DIV_ROUND_UP(height, max_cu_width); } for (i = 0; i < 20; i++) -- cgit From 2be6ee86385badab95b1bace984735bde6e0fec0 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Thu, 11 Jun 2026 17:17:00 +0900 Subject: media: amphion: Remove obsolete frame_count check in venc_start_session The dev_err() log warning about no input when starting was originally meaningful when min_queued_buffers was set, as it indicated an abnormal condition. However, since commit 5633ec763a2a ("media: amphion: Drop min_queued_buffers assignment") removed the min_queued_buffers assignment, having frame_count == 0 at start is a normal condition. Remove this misleading log that no longer serves any purpose. Fixes: 5633ec763a2a ("media: amphion: Drop min_queued_buffers assignment") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Reviewed-by: Frank Li Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/amphion/venc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c index 0b3d58b9f2f7..2ba4ae20f829 100644 --- a/drivers/media/platform/amphion/venc.c +++ b/drivers/media/platform/amphion/venc.c @@ -973,8 +973,6 @@ static int venc_start_session(struct vpu_inst *inst, u32 type) venc->ready_count = 0; venc->stopped = false; vpu_process_output_buffer(inst); - if (venc->frame_count == 0) - dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id); return 0; error: -- cgit From 7504c2463632a509f6085545578fa4058cef537e Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Mon, 15 Jun 2026 01:58:16 +0530 Subject: media: platform: hantro_drv: Add missing media_entity_cleanup() The media_entity_pads_init() (or media_entity_init()) function initializes the media entity, but the driver forgets to call media_entity_cleanup() in the error paths and remove function. Add the missing calls to fix the API violation and prevent potential future memory leaks. Signed-off-by: Biren Pandya Reviewed-by: Benjamin Gaignard Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/verisilicon/hantro_drv.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c index 2e81877f640f..ad71c0402ef3 100644 --- a/drivers/media/platform/verisilicon/hantro_drv.c +++ b/drivers/media/platform/verisilicon/hantro_drv.c @@ -771,8 +771,10 @@ static int hantro_register_entity(struct media_device *mdev, return ret; ret = media_device_register_entity(mdev, entity); - if (ret) + if (ret) { + media_entity_cleanup(entity); return ret; + } return 0; } @@ -860,12 +862,13 @@ err_rm_links0: err_rel_entity2: media_device_unregister_entity(&func->sink); - + media_entity_cleanup(&func->sink); err_rel_entity1: media_device_unregister_entity(&func->proc); - + media_entity_cleanup(&func->proc); err_rel_entity0: media_device_unregister_entity(&func->vdev.entity); + media_entity_cleanup(&func->vdev.entity); return ret; } @@ -878,6 +881,9 @@ static void hantro_detach_func(struct hantro_func *func) media_device_unregister_entity(&func->sink); media_device_unregister_entity(&func->proc); media_device_unregister_entity(&func->vdev.entity); + media_entity_cleanup(&func->sink); + media_entity_cleanup(&func->proc); + media_entity_cleanup(&func->vdev.entity); } static int hantro_add_func(struct hantro_dev *vpu, unsigned int funcid) -- cgit From 20aa934ace6917262ff579a73ec018d06a7bad1c Mon Sep 17 00:00:00 2001 From: Christian Hewitt Date: Wed, 24 Jun 2026 06:58:08 +0000 Subject: media: meson: vdec: fix NULL pointer deref in vdec_try_fmt_common When VIDIOC_TRY_FMT is called with an unsupported pixel format on the OUTPUT queue, vdec_try_fmt_common() falls back to V4L2_PIX_FMT_MPEG2. However, if a distro has locally patched MPEG2 support out (as it has been broken for some time) the platform format table does not contain MPEG2 so find_format() returns NULL and the subsequent dereference of fmt_out->max_width triggers a NULL pointer dereference. Fix this by falling back to the first format in the platform's format array instead of hardcoding V4L2_PIX_FMT_MPEG2. This is always valid since every platform defines at least one format. Fixes: 3e7f51bd9607 ("media: meson: add v4l2 m2m video decoder driver") Cc: stable@vger.kernel.org Signed-off-by: Christian Hewitt Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/meson/vdec/vdec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index a039d925c0fe..c18bf352b199 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -504,8 +504,8 @@ vdec_try_fmt_common(struct amvdec_session *sess, u32 size, case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: fmt_out = find_format(fmts, size, pixmp->pixelformat); if (!fmt_out) { - pixmp->pixelformat = V4L2_PIX_FMT_MPEG2; - fmt_out = find_format(fmts, size, pixmp->pixelformat); + pixmp->pixelformat = fmts[0].pixfmt; + fmt_out = &fmts[0]; } break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: -- cgit From 9df2fbe563194da1967a5db083442186c1323efe Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Wed, 24 Jun 2026 16:59:20 +0800 Subject: media: cedrus: fix memory leak in cedrus_init_ctrls() In cedrus_init_ctrls(), the V4L2 control handler is initialized before allocating memory for ctx->ctrls. If this allocation fails, the function returns -ENOMEM without freeing the previously allocated handler resources, leading to a memory leak. Fix this by calling v4l2_ctrl_handler_free() on the ctx->ctrls allocation failure path. The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. As we do not have an Allwinner SoC or board with a Cedrus VPU available to test with, no runtime testing was able to be performed. Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver") Cc: stable@vger.kernel.org Signed-off-by: Dawei Feng Acked-by: Jernej Skrabec Reviewed-by: Dan Carpenter Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/staging/media/sunxi/cedrus/cedrus.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index bbd186b8035b..96acd52e380c 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -285,8 +285,10 @@ static int cedrus_init_ctrls(struct cedrus_dev *dev, struct cedrus_ctx *ctx) ctrl_size = sizeof(ctrl) * CEDRUS_CONTROLS_COUNT + 1; ctx->ctrls = kzalloc(ctrl_size, GFP_KERNEL); - if (!ctx->ctrls) + if (!ctx->ctrls) { + v4l2_ctrl_handler_free(hdl); return -ENOMEM; + } j = 0; for (i = 0; i < CEDRUS_CONTROLS_COUNT; i++) { -- cgit From 1551386934ad43d934c3bb7317929207e1edcd6a Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:26 +0900 Subject: media: chips-media: wave5: Guard bit depth check with initial_info_obtained When CAPTURE STREAMON is called before the VPU has completed sequence initialization (initial_info_obtained == false), the initial_info fields contain uninitialized data. The driver checks luma_bitdepth and rejects anything other than 8-bit, so garbage values (e.g. 15) cause STREAMON to fail spuriously. This is reproducible with the following multi-threaded test scenario: 1. Allocate 2 CAPTURE buffers. 2. Call STREAMON on the CAPTURE queue. 3. Call DQBUF, which blocks waiting for a decoded frame. 4. A second thread calls STREAMOFF on the CAPTURE queue. 5. The blocked DQBUF should be released, allowing graceful termination. At step 2, STREAMON reads uninitialized luma_bitdepth and rejects the stream, causing the test to fail. Fix this by checking initial_info_obtained before accessing the bit depth fields, so the validation is only performed when the sequence info has actually been parsed by the VPU. Fixes: 035371c9e509 ("media: chips-media: wave5: Fix timeout while testing 10bit hevc fluster") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index bb2ba9204a83..01d1368b2965 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1403,6 +1403,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count } else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { struct dec_initial_info *initial_info = &inst->codec_info->dec_info.initial_info; + struct dec_info *p_dec_info = &inst->codec_info->dec_info; if (inst->state == VPU_INST_STATE_STOP) ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ); @@ -1410,6 +1411,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count goto return_buffers; if (inst->state == VPU_INST_STATE_INIT_SEQ && + p_dec_info->initial_info_obtained && inst->dev->product_code == WAVE521C_CODE) { if (initial_info->luma_bitdepth != 8) { dev_info(inst->dev->dev, "%s: no support for %d bit depth", @@ -1418,7 +1420,6 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count goto return_buffers; } } - } pm_runtime_put_autosuspend(inst->dev->dev); return ret; -- cgit From cfcefc5a996f6a00b310c963b5f811430f28a30a Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:27 +0900 Subject: media: chips-media: wave5: Set inst->std during default format initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the encoder is opened, wave5_set_default_format() sets up the default capture format (e.g. H.264) but does not initialize inst->std. As a result, inst->std remains zero, which does not match any valid encoder codec. If STREAMON is called before the user explicitly calls S_FMT on the capture queue — as v4l2-compliance does in testBlockingDQBuf — the codec/product check in wave5_vpu_enc_init_seq() fails with "Unsupported encoder-codec & product combination" because inst->std is neither W_HEVC_ENC nor W_AVC_ENC, returning -EOPNOTSUPP. Fix this by setting inst->std via wave5_to_vpu_std() in wave5_set_default_format(), so that the codec type is always consistent with the default capture pixel format from the moment the instance is opened. Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c index e6c94b6f2671..f9fcdf4c224b 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c @@ -1494,7 +1494,8 @@ static const struct vb2_ops wave5_vpu_enc_vb2_ops = { .stop_streaming = wave5_vpu_enc_stop_streaming, }; -static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, +static void wave5_set_default_format(struct vpu_instance *inst, + struct v4l2_pix_format_mplane *src_fmt, struct v4l2_pix_format_mplane *dst_fmt) { src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt; @@ -1506,6 +1507,7 @@ static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt, wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC, W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT, &enc_frmsize[VPU_FMT_TYPE_CODEC]); + inst->std = wave5_to_vpu_std(dst_fmt->pixelformat, inst->type); } static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) @@ -1770,7 +1772,7 @@ static int wave5_vpu_open_enc(struct file *filp) inst->v4l2_fh.ctrl_handler = v4l2_ctrl_hdl; v4l2_ctrl_handler_setup(v4l2_ctrl_hdl); - wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt); + wave5_set_default_format(inst, &inst->src_fmt, &inst->dst_fmt); inst->conf_win.width = inst->dst_fmt.width; inst->conf_win.height = inst->dst_fmt.height; inst->colorspace = V4L2_COLORSPACE_REC709; -- cgit From 8c5a74a24cbbba9142e38c463c96435d316149ce Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:28 +0900 Subject: media: chips-media: wave5: avoid skipping device_run while VPU has work The decoder stalls because empty_queue is set to true even when the m2m context still has pending commands. As a result, device_run is never invoked, the m2m source queue fills up, and userspace (e.g. Chromium) can no longer queue new bitstream buffers to the V4L2 driver. Fix this by querying the VPU queue status via DEC_GET_QUEUE_STATUS before deciding whether to skip device_run. Only skip when the VPU's instance_queue_count equals the number of ready source buffers in the v4l2-m2m context, which indicates that there is genuinely no new work to perform. Otherwise, proceed with issuing a decode command so that the VPU can continue draining its internal queue. Fixes: a176ac5e701f ("media: chips-media: wave5: Improve performance of decoder") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 01d1368b2965..6c6e86b09b40 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1663,9 +1663,13 @@ static void wave5_vpu_dec_device_run(void *priv) } else if (!inst->eos && inst->queuing_num == 0 && inst->state == VPU_INST_STATE_PIC_RUN) { - dev_dbg(inst->dev->dev, "%s: no bitstream for feeding, so skip ", __func__); - inst->empty_queue = true; - goto finish_job_and_return; + wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); + if (q_status.instance_queue_count == v4l2_m2m_num_src_bufs_ready(m2m_ctx)) { + dev_dbg(inst->dev->dev, "%s: no bitstream, skip\n", + __func__); + inst->empty_queue = true; + goto finish_job_and_return; + } } } -- cgit From 2ae7faed2e60d6d07d9efdd962d20dcb15330ced Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:29 +0900 Subject: media: chips-media: wave5: Add timeout while stop_streaming When stop_streaming is called, an infinite loop may occur in some cases. Add a bounded poll of the queue status: loop until the queues drain, sleeping briefly between polls, and bail out once VPU_DEC_STOP_TIMEOUT elapses. Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c | 15 +++++++++------ .../media/platform/chips-media/wave5/wave5-vpuconfig.h | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 6c6e86b09b40..93f7b724d86c 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -5,6 +5,7 @@ * Copyright (C) 2021-2023 CHIPS&MEDIA INC */ +#include #include #include "wave5-helper.h" @@ -1537,15 +1538,15 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) { struct vpu_instance *inst = vb2_get_drv_priv(q); struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx; - - bool check_cmd = TRUE; + unsigned long timeout; dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type); pm_runtime_resume_and_get(inst->dev->dev); inst->empty_queue = true; - while (check_cmd) { + + timeout = jiffies + msecs_to_jiffies(VPU_DEC_STOP_TIMEOUT); + while (true) { struct queue_status_info q_status; - struct dec_output_info dec_output_info; wave5_vpu_dec_give_command(inst, DEC_GET_QUEUE_STATUS, &q_status); if ((inst->state == VPU_INST_STATE_STOP || @@ -1554,8 +1555,10 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q) q_status.report_queue_count == 0) break; - if (wave5_vpu_dec_get_output_info(inst, &dec_output_info)) - dev_dbg(inst->dev->dev, "there is no output info\n"); + if (time_after(jiffies, timeout)) + break; + + usleep_range(1000, 2000); } v4l2_m2m_update_stop_streaming_state(m2m_ctx, q); diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h index 4ebd48d5550e..e04f2dbf3b65 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h +++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h @@ -59,7 +59,7 @@ // application specific configuration #define VPU_ENC_TIMEOUT 60000 #define VPU_DEC_TIMEOUT 60000 -#define VPU_DEC_STOP_TIMEOUT 10 +#define VPU_DEC_STOP_TIMEOUT 300 // for WAVE encoder #define USE_SRC_PRP_AXI 0 -- cgit From b694ba0a5526a69f78a6924982b1553154ccfd73 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:30 +0900 Subject: media: chips-media: wave5: Defer job_finish() only when a DEC_PIC was queued Decoder instances sharing a VPU also share one v4l2_m2m job slot, released when the running context calls v4l2_m2m_job_finish(). While draining, device_run() defers job_finish() once EOS is sent (sent_eos), expecting a later finish_decode() (from a DEC_PIC completion IRQ) to release the slot. But the m2m core checks job_ready() only when a job is queued, not when it is dispatched. A job queued while draining can run after finish_decode() has already moved the instance to STOP and sent EOS. device_run() then runs in STOP, issues no DEC_PIC, yet still skips job_finish() - so no IRQ, no finish_decode(), and the shared slot is leaked, stalling every instance. With several v4l2h264dec instances in parallel, GStreamer hangs at EOS. Track whether the run actually queued a DEC_PIC (cmd_issued) and defer job_finish() only then. Otherwise finish the job immediately Fixes: a176ac5e701f ("media: chips-media: wave5: Improve performance of decoder") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 93f7b724d86c..f33c00cb801b 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -1655,6 +1655,7 @@ static void wave5_vpu_dec_device_run(void *priv) struct queue_status_info q_status; u32 fail_res = 0; int ret = 0; + bool cmd_issued = false; dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__); pm_runtime_resume_and_get(inst->dev->dev); @@ -1752,6 +1753,7 @@ static void wave5_vpu_dec_device_run(void *priv) inst->retry = false; if (!inst->eos) inst->queuing_num--; + cmd_issued = true; } break; default: @@ -1769,8 +1771,16 @@ finish_job_and_return: * in power and CPU time. * If EOS is passed, device_run will not call job_finish no more, it is called * only if HW is idle status in order to reduce overhead. + * + * Deferring job_finish() is only safe when this run actually queued a + * DEC_PIC command (cmd_issued): that guarantees a completion IRQ, and + * thus a later finish_decode(), will release the shared job slot. When + * device_run() is entered with no command to issue (e.g. a job that was + * queued while draining but reached the STOP state by the time it ran), + * no IRQ follows, so finish the job here to avoid leaking the slot and + * stalling every instance sharing the VPU. */ - if (!inst->sent_eos) + if (!inst->sent_eos || !cmd_issued) v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } -- cgit From e3a80073d99c376176d81013335cd355af576be2 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:31 +0900 Subject: media: chips-media: wave5: Fix pipeline stall when queuing fails The Wave5 decoder calls v4l2_m2m_job_finish() immediately in device_run() after submitting frames to firmware. When the firmware completes those frames and the queue drains to zero, finish_decode() has no active M2M job to finish, so v4l2_m2m_schedule_next_job() is never called and the decoder stalls. Call v4l2_m2m_try_schedule() in finish_decode() when the firmware queue empties to ensure the framework always schedules the next device_run(). Fixes: a176ac5e701f ("media: chips-media: wave5: Improve performance of decoder") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index f33c00cb801b..1817b83c5884 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -475,7 +475,10 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst) v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } - inst->queuing_fail = false; + if (inst->queuing_fail) { + inst->queuing_fail = false; + v4l2_m2m_try_schedule(m2m_ctx); + } } static int wave5_vpu_dec_querycap(struct file *file, void *fh, struct v4l2_capability *cap) -- cgit From a52e6f7923c17a672135b485ffd96fbd72f46267 Mon Sep 17 00:00:00 2001 From: Jackson Lee Date: Fri, 26 Jun 2026 10:22:32 +0900 Subject: media: chips-media: wave5: Resume device before setting EOS flag Setting the EOS flag talks to the firmware via send_firmware_command(), which accesses VPU registers. Both the STREAMOFF path (wave5_vpu_dec_job_abort()) and the V4L2_DEC_CMD_STOP path (wave5_vpu_dec_stop()) can run while the device is runtime suspended, so those register accesses hit powered-down hardware and the SoC raises an asynchronous SError, panicking the kernel: SError Interrupt on CPU3, code 0x00000000bf000000 -- SError send_firmware_command+0x2c/0x160 [wave5] wave5_vpu_dec_set_bitstream_flag+0x6c/0x80 [wave5] wave5_vpu_dec_update_bitstream_buffer+0x80/0xec [wave5] wave5_vpu_dec_job_abort+0x44/0xa0 [wave5] v4l2_m2m_cancel_job+0x110/0x19c [v4l2_mem2mem] v4l2_m2m_streamoff+0x24/0x140 [v4l2_mem2mem] Resume the device with pm_runtime_resume_and_get() around the EOS firmware command and release it with pm_runtime_put_autosuspend(), matching the runtime PM handling already done in wave5_vpu_dec_device_run(). Fixes: 9707a6254a8a ("media: chips-media: wave5: Add the v4l2 layer") Cc: stable@vger.kernel.org Signed-off-by: Jackson Lee Signed-off-by: Nas Chung Reviewed-by: Nicolas Dufresne Signed-off-by: Nicolas Dufresne Signed-off-by: Hans Verkuil --- .../media/platform/chips-media/wave5/wave5-vpu-dec.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c index 1817b83c5884..6564cf3ec739 100644 --- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c +++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c @@ -823,7 +823,15 @@ static int wave5_vpu_dec_stop(struct vpu_instance *inst) * calls do not block on a mutex while inside this spinlock. */ spin_unlock_irqrestore(&inst->state_spinlock, flags); + /* + * V4L2_DEC_CMD_STOP can arrive while the device is runtime + * suspended (e.g. on pipeline teardown). Setting the EOS flag + * accesses VPU registers via send_firmware_command(), so the + * device must be resumed first to avoid an asynchronous SError. + */ + pm_runtime_resume_and_get(inst->dev->dev); ret = wave5_vpu_dec_set_eos_on_firmware(inst); + pm_runtime_put_autosuspend(inst->dev->dev); if (ret) return ret; @@ -1797,11 +1805,22 @@ static void wave5_vpu_dec_job_abort(void *priv) if (ret) return; + /* + * job_abort() runs from the STREAMOFF path and may be called while the + * device is runtime suspended. Setting the EOS flag talks to the + * firmware (send_firmware_command() accesses VPU registers), so the + * device must be resumed first; otherwise the register access faults + * with an asynchronous SError. + */ + pm_runtime_resume_and_get(inst->dev->dev); + ret = wave5_vpu_dec_set_eos_on_firmware(inst); if (ret) dev_warn(inst->dev->dev, "Setting EOS for the bitstream, fail: %d\n", ret); + pm_runtime_put_autosuspend(inst->dev->dev); + v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx); } -- cgit From d41db9138a71daf6ff8b00d637fffeae25e0707d Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:30 +0800 Subject: media: s2255: Rename CamelCase goto labels to snake_case Rename the error-path goto labels in s2255_probe() from CamelCase to snake_case to comply with the Linux kernel coding style: errorBOARDINIT -> err_boardinit errorFWMARKER -> err_fwmarker errorREQFW -> err_reqfw errorFWDATA2 -> err_fwdata2 errorFWURB -> err_fwurb errorEP -> err_ep errorUDEV -> err_udev errorFWDATA1 -> err_fwdata1 No functional changes; all label definitions and goto references are updated consistently. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 3917913c3b47..64140b780e63 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2216,14 +2216,14 @@ static int s2255_probe(struct usb_interface *interface, dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); if (dev->cmdbuf == NULL) { s2255_dev_err(&interface->dev, "out of memory\n"); - goto errorFWDATA1; + goto err_fwdata1; } refcount_set(&dev->num_channels, 0); dev->pid = id->idProduct; dev->fw_data = kzalloc_obj(struct s2255_fw); if (!dev->fw_data) - goto errorFWDATA1; + goto err_fwdata1; mutex_init(&dev->lock); mutex_init(&dev->cmdlock); /* grab usb_device and save it */ @@ -2231,7 +2231,7 @@ static int s2255_probe(struct usb_interface *interface, if (dev->udev == NULL) { dev_err(&interface->dev, "null usb device\n"); retval = -ENODEV; - goto errorUDEV; + goto err_udev; } dev_dbg(&interface->dev, "dev: %p, udev %p interface %p\n", dev, dev->udev, interface); @@ -2243,7 +2243,7 @@ static int s2255_probe(struct usb_interface *interface, if (usb_find_bulk_in_endpoint(iface_desc, &endpoint)) { dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); - goto errorEP; + goto err_ep; } dev->read_endpoint = endpoint->bEndpointAddress; @@ -2262,32 +2262,32 @@ static int s2255_probe(struct usb_interface *interface, dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->fw_data->fw_urb) - goto errorFWURB; + goto err_fwurb; dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); if (!dev->fw_data->pfw_data) { dev_err(&interface->dev, "out of memory!\n"); - goto errorFWDATA2; + goto err_fwdata2; } /* load the first chunk */ if (request_firmware(&dev->fw_data->fw, FIRMWARE_FILE_NAME, &dev->udev->dev)) { dev_err(&interface->dev, "sensoray 2255 failed to get firmware\n"); - goto errorREQFW; + goto err_reqfw; } /* check the firmware is valid */ fw_size = dev->fw_data->fw->size; if (fw_size < 8) { dev_err(&interface->dev, "Firmware invalid: too small.\n"); retval = -ENODEV; - goto errorFWMARKER; + goto err_fwmarker; } pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; if (*pdata != S2255_FW_MARKER) { dev_err(&interface->dev, "Firmware invalid.\n"); retval = -ENODEV; - goto errorFWMARKER; + goto err_fwmarker; } else { /* make sure firmware is the latest */ __le32 *pRel; @@ -2305,30 +2305,30 @@ static int s2255_probe(struct usb_interface *interface, /* load 2255 board specific */ retval = s2255_board_init(dev); if (retval) - goto errorBOARDINIT; + goto err_boardinit; s2255_fwload_start(dev); /* loads v4l specific */ retval = s2255_probe_v4l(dev); if (retval) - goto errorBOARDINIT; + goto err_boardinit; dev_info(&interface->dev, "Sensoray 2255 detected\n"); return 0; -errorBOARDINIT: +err_boardinit: s2255_board_shutdown(dev); -errorFWMARKER: +err_fwmarker: release_firmware(dev->fw_data->fw); -errorREQFW: +err_reqfw: kfree(dev->fw_data->pfw_data); -errorFWDATA2: +err_fwdata2: usb_free_urb(dev->fw_data->fw_urb); -errorFWURB: +err_fwurb: timer_shutdown_sync(&dev->timer); -errorEP: +err_ep: usb_put_dev(dev->udev); -errorUDEV: +err_udev: kfree(dev->fw_data); mutex_destroy(&dev->lock); -errorFWDATA1: +err_fwdata1: kfree(dev->cmdbuf); kfree(dev); pr_warn("Sensoray 2255 driver load failed: 0x%x\n", retval); -- cgit From e8485e3b9b211c0b8ee9a081c5ba223bb5fc3df8 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:31 +0800 Subject: media: s2255: Rename remaining CamelCase identifiers to snake_case Rename CamelCase identifiers throughout s2255drv.c to comply with the Linux kernel coding style. All identifiers are local to this file (no header file or cross-file references). Struct members: ulState -> state dwFrames -> num_frames Function parameters (s2255_vendor_req, also updating the forward declaration to match): Request -> req Index -> index Value -> value TransferBuffer -> xfer_buf TransferBufferLength -> xfer_buf_len bOut -> is_out Local variables: pY/pCb/pCr -> p_y/p_cb/p_cr linesPerFrame -> lines_per_frame pixelsPerLine -> pixels_per_line outImageSize -> out_image_size usbInSize -> usb_in_size transBuffer -> trans_buf pRel -> p_rel No functional changes. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 139 +++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 69 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 64140b780e63..0f98a4cc18a4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -151,14 +151,14 @@ struct s2255_mode { /* frame structure */ struct s2255_framei { unsigned long size; - unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ + unsigned long state; /* state:S2255_READ_IDLE, S2255_READ_FRAME*/ void *lpvbits; /* image data */ unsigned long cur_size; /* current data copied to it */ }; /* image buffer structure */ struct s2255_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ + unsigned long num_frames; /* number of frames in buffer */ struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ }; @@ -343,8 +343,8 @@ static int s2255_board_shutdown(struct s2255_dev *dev); static void s2255_fwload_start(struct s2255_dev *dev); static void s2255_destroy(struct s2255_dev *dev); static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, - u16 index, u16 value, void *buf, - s32 buf_len, int bOut); + u16 index, u16 value, void *xfer_buf, + s32 xfer_buf_len, int is_out); /* dev_err macro with driver name */ #define S2255_DRIVER_NAME "s2255" @@ -440,19 +440,19 @@ static void planar422p_to_yuv_packed(const unsigned char *in, int width, int height, int fmt) { - unsigned char *pY; - unsigned char *pCb; - unsigned char *pCr; + unsigned char *p_y; + unsigned char *p_cb; + unsigned char *p_cr; unsigned long size = height * width; unsigned int i; - pY = (unsigned char *)in; - pCr = (unsigned char *)in + height * width; - pCb = (unsigned char *)in + height * width + (height * width / 2); + p_y = (unsigned char *)in; + p_cr = (unsigned char *)in + height * width; + p_cb = (unsigned char *)in + height * width + (height * width / 2); for (i = 0; i < size * 2; i += 4) { - out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; - out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; - out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; - out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; + out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_y++ : *p_cr++; + out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_cr++ : *p_y++; + out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_y++ : *p_cb++; + out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_cb++ : *p_y++; } return; } @@ -896,10 +896,10 @@ static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, static u32 get_transfer_size(struct s2255_mode *mode) { - int linesPerFrame = LINE_SZ_DEF; - int pixelsPerLine = NUM_LINES_DEF; - u32 outImageSize; - u32 usbInSize; + int lines_per_frame = LINE_SZ_DEF; + int pixels_per_line = NUM_LINES_DEF; + u32 out_image_size; + u32 usb_in_size; unsigned int mask_mult; if (mode == NULL) @@ -909,16 +909,16 @@ static u32 get_transfer_size(struct s2255_mode *mode) switch (mode->scale) { case SCALE_4CIFS: case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; - pixelsPerLine = LINE_SZ_4CIFS_NTSC; + lines_per_frame = NUM_LINES_4CIFS_NTSC * 2; + pixels_per_line = LINE_SZ_4CIFS_NTSC; break; case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_NTSC; - pixelsPerLine = LINE_SZ_2CIFS_NTSC; + lines_per_frame = NUM_LINES_2CIFS_NTSC; + pixels_per_line = LINE_SZ_2CIFS_NTSC; break; case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_NTSC; - pixelsPerLine = LINE_SZ_1CIFS_NTSC; + lines_per_frame = NUM_LINES_1CIFS_NTSC; + pixels_per_line = LINE_SZ_1CIFS_NTSC; break; default: break; @@ -927,35 +927,35 @@ static u32 get_transfer_size(struct s2255_mode *mode) switch (mode->scale) { case SCALE_4CIFS: case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_PAL * 2; - pixelsPerLine = LINE_SZ_4CIFS_PAL; + lines_per_frame = NUM_LINES_4CIFS_PAL * 2; + pixels_per_line = LINE_SZ_4CIFS_PAL; break; case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_PAL; - pixelsPerLine = LINE_SZ_2CIFS_PAL; + lines_per_frame = NUM_LINES_2CIFS_PAL; + pixels_per_line = LINE_SZ_2CIFS_PAL; break; case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_PAL; - pixelsPerLine = LINE_SZ_1CIFS_PAL; + lines_per_frame = NUM_LINES_1CIFS_PAL; + pixels_per_line = LINE_SZ_1CIFS_PAL; break; default: break; } } - outImageSize = linesPerFrame * pixelsPerLine; + out_image_size = lines_per_frame * pixels_per_line; if ((mode->color & MASK_COLOR) != COLOR_Y8) { /* 2 bytes/pixel if not monochrome */ - outImageSize *= 2; + out_image_size *= 2; } /* total bytes to send including prefix and 4K padding; must be a multiple of USB_READ_SIZE */ - usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ + usb_in_size = out_image_size + PREFIX_SIZE; /* always send prefix */ mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; /* if size not a multiple of USB_READ_SIZE */ - if (usbInSize & ~mask_mult) - usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); - return usbInSize; + if (usb_in_size & ~mask_mult) + usb_in_size = (usb_in_size & mask_mult) + (DEF_USB_BLOCK); + return usb_in_size; } static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) @@ -1066,7 +1066,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) vc->cur_frame = 0; vc->frame_count = 0; for (j = 0; j < SYS_FRAMES; j++) { - vc->buffer.frame[j].ulState = S2255_READ_IDLE; + vc->buffer.frame[j].state = S2255_READ_IDLE; vc->buffer.frame[j].cur_size = 0; } return s2255_start_acquire(vc); @@ -1701,7 +1701,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) vc = &dev->vc[dev->cc]; idx = vc->cur_frame; frm = &vc->buffer.frame[idx]; - if (frm->ulState == S2255_READ_IDLE) { + if (frm->state == S2255_READ_IDLE) { int jj; unsigned int cc; __le32 *pdword; /*data from dsp is little endian */ @@ -1790,12 +1790,12 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) /* search done. now find out if should be acquiring on this channel */ if (!vb2_is_streaming(&vc->vb_vidq)) { /* we found a frame, but this channel is turned off */ - frm->ulState = S2255_READ_IDLE; + frm->state = S2255_READ_IDLE; return -EINVAL; } - if (frm->ulState == S2255_READ_IDLE) { - frm->ulState = S2255_READ_FRAME; + if (frm->state == S2255_READ_IDLE) { + frm->state = S2255_READ_FRAME; frm->cur_size = 0; } @@ -1829,13 +1829,13 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) vc->cur_frame++; /* end of system frame ring buffer, start at zero */ if ((vc->cur_frame == SYS_FRAMES) || - (vc->cur_frame == vc->buffer.dwFrames)) + (vc->cur_frame == vc->buffer.num_frames)) vc->cur_frame = 0; /* frame ready */ if (vb2_is_streaming(&vc->vb_vidq)) s2255_got_frame(vc, vc->jpg_size); vc->frame_count++; - frm->ulState = S2255_READ_IDLE; + frm->state = S2255_READ_IDLE; frm->cur_size = 0; } @@ -1863,33 +1863,33 @@ static void s2255_read_video_callback(struct s2255_dev *dev, return; } -static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, - u16 Index, u16 Value, void *TransferBuffer, - s32 TransferBufferLength, int bOut) +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, + u16 index, u16 value, void *xfer_buf, + s32 xfer_buf_len, int is_out) { int r; unsigned char *buf; - buf = kmalloc(TransferBufferLength, GFP_KERNEL); + buf = kmalloc(xfer_buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; - if (!bOut) { + if (!is_out) { r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - Request, + req, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - Value, Index, buf, - TransferBufferLength, USB_CTRL_SET_TIMEOUT); + value, index, buf, + xfer_buf_len, USB_CTRL_SET_TIMEOUT); if (r >= 0) - memcpy(TransferBuffer, buf, TransferBufferLength); + memcpy(xfer_buf, buf, xfer_buf_len); } else { - memcpy(buf, TransferBuffer, TransferBufferLength); + memcpy(buf, xfer_buf, xfer_buf_len); r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - Value, Index, buf, - TransferBufferLength, USB_CTRL_SET_TIMEOUT); + req, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, buf, + xfer_buf_len, USB_CTRL_SET_TIMEOUT); } kfree(buf); return r; @@ -1904,14 +1904,14 @@ static int s2255_get_fx2fw(struct s2255_dev *dev) { int fw; int ret; - u8 transBuffer[2] = {}; + u8 trans_buf[2] = {}; - ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, - sizeof(transBuffer), S2255_VR_IN); + ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, trans_buf, + sizeof(trans_buf), S2255_VR_IN); if (ret < 0) dprintk(dev, 2, "get fw error: %x\n", ret); - fw = transBuffer[0] + (transBuffer[1] << 8); - dprintk(dev, 2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); + fw = trans_buf[0] + (trans_buf[1] << 8); + dprintk(dev, 2, "Get FW %x %x\n", trans_buf[0], trans_buf[1]); return fw; } @@ -1923,7 +1923,7 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) { unsigned long i; unsigned long reqsize; - vc->buffer.dwFrames = SYS_FRAMES; + vc->buffer.num_frames = SYS_FRAMES; /* always allocate maximum size(PAL) for system buffers */ reqsize = SYS_FRAMES_MAXSIZE; @@ -1936,14 +1936,14 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) vc->buffer.frame[i].size = reqsize; if (vc->buffer.frame[i].lpvbits == NULL) { pr_info("out of memory. using less frames\n"); - vc->buffer.dwFrames = i; + vc->buffer.num_frames = i; break; } } /* make sure internal states are set */ for (i = 0; i < SYS_FRAMES; i++) { - vc->buffer.frame[i].ulState = 0; + vc->buffer.frame[i].state = 0; vc->buffer.frame[i].cur_size = 0; } @@ -2123,7 +2123,7 @@ static int s2255_start_acquire(struct s2255_vc *vc) vc->bad_payload = 0; vc->cur_frame = 0; for (j = 0; j < SYS_FRAMES; j++) { - vc->buffer.frame[j].ulState = 0; + vc->buffer.frame[j].state = 0; vc->buffer.frame[j].cur_size = 0; } @@ -2290,10 +2290,11 @@ static int s2255_probe(struct usb_interface *interface, goto err_fwmarker; } else { /* make sure firmware is the latest */ - __le32 *pRel; - pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; - pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*pRel)); - dev->dsp_fw_ver = le32_to_cpu(*pRel); + __le32 *p_rel; + + p_rel = (__le32 *)&dev->fw_data->fw->data[fw_size - 4]; + pr_info("s2255 dsp fw version %x\n", le32_to_cpu(*p_rel)); + dev->dsp_fw_ver = le32_to_cpu(*p_rel); if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) pr_info("s2255: f2255usb.bin out of date.\n"); if (dev->pid == 0x2257 && -- cgit From 67f869721d9a2dfeccd731f62e47017c75fcac09 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:32 +0800 Subject: media: s2255: Remove unnecessary spaces after casts Fix all 8 'CHECK: No space is necessary after a cast' issues reported by checkpatch.pl in s2255drv.c. Remove the space between the cast type and the value being cast. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 0f98a4cc18a4..78011f5fe99c 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -521,7 +521,7 @@ static void s2255_fwchunk_complete(struct urb *urb) memset(data->pfw_data, 0, CHUNK_SIZE); memcpy(data->pfw_data, - (char *) data->fw->data + data->fw_loaded, len); + (char *)data->fw->data + data->fw_loaded, len); usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), data->pfw_data, CHUNK_SIZE, @@ -875,7 +875,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, else if (mode.format != vc->mode.format) mode.restart = 1; vc->mode = mode; - (void) s2255_set_mode(vc, &mode); + (void)s2255_set_mode(vc, &mode); return 0; } @@ -1001,7 +1001,7 @@ static int s2255_set_mode(struct s2255_vc *vc, dprintk(dev, 1, "%s: reqsize %ld\n", __func__, vc->req_image_size); /* set the mode */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[1] = (__le32)cpu_to_le32(chn_rev); buffer[2] = CMD_SET_MODE; for (i = 0; i < sizeof(struct s2255_mode) / sizeof(u32); i++) buffer[3 + i] = cpu_to_le32(((u32 *)&vc->mode)[i]); @@ -1038,7 +1038,7 @@ static int s2255_cmd_status(struct s2255_vc *vc, u32 *pstatus) dprintk(dev, 4, "%s chan %d\n", __func__, vc->idx); /* form the get vid status command */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[1] = (__le32)cpu_to_le32(chn_rev); buffer[2] = CMD_STATUS; *pstatus = 0; vc->vidstatus_ready = 0; @@ -1078,7 +1078,7 @@ static void stop_streaming(struct vb2_queue *vq) struct s2255_vc *vc = vb2_get_drv_priv(vq); struct s2255_buffer *buf, *node; unsigned long flags; - (void) s2255_stop_acquire(vc); + (void)s2255_stop_acquire(vc); spin_lock_irqsave(&vc->qlock, flags); list_for_each_entry_safe(buf, node, &vc->buf_list, list) { list_del(&buf->list); @@ -2129,7 +2129,7 @@ static int s2255_start_acquire(struct s2255_vc *vc) /* send the start command */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[1] = (__le32)cpu_to_le32(chn_rev); buffer[2] = CMD_START; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (res != 0) @@ -2151,7 +2151,7 @@ static int s2255_stop_acquire(struct s2255_vc *vc) chn_rev = G_chnmap[vc->idx]; /* send the stop command */ buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[1] = (__le32)cpu_to_le32(chn_rev); buffer[2] = CMD_STOP; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); @@ -2282,7 +2282,7 @@ static int s2255_probe(struct usb_interface *interface, retval = -ENODEV; goto err_fwmarker; } - pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; + pdata = (__le32 *)&dev->fw_data->fw->data[fw_size - 8]; if (*pdata != S2255_FW_MARKER) { dev_err(&interface->dev, "Firmware invalid.\n"); -- cgit From 0235f4da3e18895d48925674d6c1487c97c21bdd Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:33 +0800 Subject: media: s2255: Use consistent braces on all arms of if/else statements Fix all 5 'CHECK: braces {} should be used on all arms of this statement' issues reported by checkpatch.pl in s2255drv.c. Add braces to else and else-if branches that were missing them for consistency with other branches. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 78011f5fe99c..a759ba9a6d51 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -534,8 +534,9 @@ static void s2255_fwchunk_complete(struct urb *urb) return; } data->fw_loaded += len; - } else + } else { atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + } return; } @@ -839,8 +840,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, mode.scale = SCALE_4CIFSI; else mode.scale = SCALE_4CIFS; - } else + } else { mode.scale = SCALE_2CIFS; + } } else { mode.scale = SCALE_1CIFS; @@ -1122,8 +1124,9 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id i) vc->width = LINE_SZ_4CIFS_PAL; vc->height = NUM_LINES_4CIFS_PAL * 2; } - } else + } else { return -EINVAL; + } vc->std = i; if (mode.restart) s2255_set_mode(vc, &mode); @@ -1306,11 +1309,11 @@ static int vidioc_s_parm(struct file *file, void *priv, return -EBUSY; def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; - if (def_dem != sp->parm.capture.timeperframe.denominator) + if (def_dem != sp->parm.capture.timeperframe.denominator) { sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= def_num) + } else if (sp->parm.capture.timeperframe.numerator <= def_num) { sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { + } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { sp->parm.capture.timeperframe.numerator = def_num * 2; fdec = FDEC_2; } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { @@ -2058,9 +2061,9 @@ static void read_pipe_completion(struct urb *purb) return; } - if (status == 0) + if (status == 0) { s2255_read_video_callback(dev, pipe_info); - else { + } else { pipe_info->err_count++; dprintk(dev, 1, "%s: failed URB %d\n", __func__, status); } -- cgit From 236720f3436d7fe05925f635bd4450f139d6d0b0 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:34 +0800 Subject: media: s2255: Replace NULL comparisons with ! notation Fix all 15 'CHECK: Comparison to NULL could be written' issues reported by checkpatch.pl in s2255drv.c. Replace 'x == NULL' with '!x' for pointer NULL checks. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index a759ba9a6d51..4df558a7b8b3 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -501,7 +501,7 @@ static void s2255_fwchunk_complete(struct urb *urb) wake_up(&data->wait_fw); return; } - if (data->fw_urb == NULL) { + if (!data->fw_urb) { s2255_dev_err(&udev->dev, "disconnected\n"); atomic_set(&data->fw_state, S2255_FW_FAILED); /* wake up anything waiting for the firmware */ @@ -664,7 +664,7 @@ static int buffer_prepare(struct vb2_buffer *vb) unsigned long size; dprintk(vc->dev, 4, "%s\n", __func__); - if (vc->fmt == NULL) + if (!vc->fmt) return -EINVAL; if ((w < norm_minw(vc)) || @@ -763,7 +763,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (fmt == NULL) + if (!fmt) return -EINVAL; dprintk(vc->dev, 50, "%s NTSC: %d suggested width: %d, height: %d\n", @@ -820,7 +820,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (fmt == NULL) + if (!fmt) return -EINVAL; if (vb2_is_busy(q)) { @@ -904,7 +904,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) u32 usb_in_size; unsigned int mask_mult; - if (mode == NULL) + if (!mode) return 0; if (mode->format == FORMAT_NTSC) { @@ -1358,7 +1358,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, return -EINVAL; fmt = format_by_fourcc(fe->pixel_format); - if (fmt == NULL) + if (!fmt) return -EINVAL; fe->type = V4L2_FRMSIZE_TYPE_DISCRETE; fe->discrete = is_ntsc ? ntsc_sizes[fe->index] : pal_sizes[fe->index]; @@ -1380,7 +1380,7 @@ static int vidioc_enum_frameintervals(struct file *file, void *priv, return -EINVAL; fmt = format_by_fourcc(fe->pixel_format); - if (fmt == NULL) + if (!fmt) return -EINVAL; sizes = is_ntsc ? ntsc_sizes : pal_sizes; @@ -1806,7 +1806,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) psrc = (u8 *)pipe_info->transfer_buffer + offset; - if (frm->lpvbits == NULL) { + if (!frm->lpvbits) { dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d", frm, dev, dev->cc, idx); return -ENOMEM; @@ -1937,7 +1937,7 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) /* allocate the frames */ vc->buffer.frame[i].lpvbits = vmalloc(reqsize); vc->buffer.frame[i].size = reqsize; - if (vc->buffer.frame[i].lpvbits == NULL) { + if (!vc->buffer.frame[i].lpvbits) { pr_info("out of memory. using less frames\n"); vc->buffer.num_frames = i; break; @@ -1979,7 +1979,7 @@ static int s2255_board_init(struct s2255_dev *dev) pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { + if (!pipe->transfer_buffer) { dprintk(dev, 1, "out of memory!\n"); return -ENOMEM; } @@ -2039,12 +2039,12 @@ static void read_pipe_completion(struct urb *purb) int status; int pipe; pipe_info = purb->context; - if (pipe_info == NULL) { + if (!pipe_info) { dev_err(&purb->dev->dev, "no context!\n"); return; } dev = pipe_info->dev; - if (dev == NULL) { + if (!dev) { dev_err(&purb->dev->dev, "no context!\n"); return; } @@ -2211,13 +2211,13 @@ static int s2255_probe(struct usb_interface *interface, /* allocate memory for our device state and initialize it to zero */ dev = kzalloc_obj(struct s2255_dev); - if (dev == NULL) { + if (!dev) { s2255_dev_err(&interface->dev, "out of memory\n"); return -ENOMEM; } dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); - if (dev->cmdbuf == NULL) { + if (!dev->cmdbuf) { s2255_dev_err(&interface->dev, "out of memory\n"); goto err_fwdata1; } @@ -2231,7 +2231,7 @@ static int s2255_probe(struct usb_interface *interface, mutex_init(&dev->cmdlock); /* grab usb_device and save it */ dev->udev = usb_get_dev(interface_to_usbdev(interface)); - if (dev->udev == NULL) { + if (!dev->udev) { dev_err(&interface->dev, "null usb device\n"); retval = -ENODEV; goto err_udev; -- cgit From e9bf991b9db5d813aa4f5bd23225c94161a407f2 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:35 +0800 Subject: media: s2255: Fix alignment to match open parenthesis Fix all 16 'CHECK: Alignment should match open parenthesis' issues reported by checkpatch.pl in s2255drv.c. Adjust continuation line indentation in function declarations, if-conditions, function calls, and block comments to properly align with the opening parenthesis. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 4df558a7b8b3..f3e4bd069f1c 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -720,21 +720,21 @@ static int vidioc_querycap(struct file *file, void *priv, } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) + struct v4l2_fmtdesc *f) { int index = f->index; if (index >= ARRAY_SIZE(formats)) return -EINVAL; if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) return -EINVAL; f->pixelformat = formats[index].fourcc; return 0; } static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct s2255_vc *vc = video_drvdata(file); int is_ntsc = vc->std & V4L2_STD_525_60; @@ -754,7 +754,7 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, } static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { const struct s2255_fmt *fmt; enum v4l2_field field; @@ -805,7 +805,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *f) { struct s2255_vc *vc = video_drvdata(file); const struct s2255_fmt *fmt; @@ -1234,7 +1234,7 @@ static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) } static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) + struct v4l2_jpegcompression *jc) { struct s2255_vc *vc = video_drvdata(file); @@ -1245,7 +1245,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, } static int vidioc_s_jpegcomp(struct file *file, void *priv, - const struct v4l2_jpegcompression *jc) + const struct v4l2_jpegcompression *jc) { struct s2255_vc *vc = video_drvdata(file); @@ -1348,7 +1348,7 @@ static const struct v4l2_frmsize_discrete pal_sizes[] = { }; static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fe) + struct v4l2_frmsizeenum *fe) { struct s2255_vc *vc = video_drvdata(file); int is_ntsc = vc->std & V4L2_STD_525_60; @@ -1366,7 +1366,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, } static int vidioc_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fe) + struct v4l2_frmivalenum *fe) { struct s2255_vc *vc = video_drvdata(file); const struct s2255_fmt *fmt; @@ -1418,7 +1418,7 @@ static int s2255_open(struct file *file) return -ENODEV; case S2255_FW_FAILED: s2255_dev_err(&dev->udev->dev, - "firmware load failed. retrying.\n"); + "firmware load failed. retrying.\n"); s2255_fwload_start(dev); wait_event_timeout(dev->fw_data->wait_fw, ((atomic_read(&dev->fw_data->fw_state) @@ -1601,17 +1601,17 @@ static int s2255_probe_v4l(struct s2255_dev *dev) v4l2_ctrl_handler_init(&vc->hdl, 6); v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, - V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); + V4L2_CID_BRIGHTNESS, -127, 127, 1, DEF_BRIGHT); v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); + V4L2_CID_CONTRAST, 0, 255, 1, DEF_CONTRAST); v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); + V4L2_CID_SATURATION, 0, 255, 1, DEF_SATURATION); v4l2_ctrl_new_std(&vc->hdl, &s2255_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, DEF_HUE); + V4L2_CID_HUE, 0, 255, 1, DEF_HUE); vc->jpegqual_ctrl = v4l2_ctrl_new_std(&vc->hdl, - &s2255_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, - 0, 100, 1, S2255_DEF_JPEG_QUAL); + &s2255_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + 0, 100, 1, S2255_DEF_JPEG_QUAL); if (dev->dsp_fw_ver >= S2255_MIN_DSP_COLORFILTER && (dev->pid != 0x2257 || vc->idx <= 1)) v4l2_ctrl_new_custom(&vc->hdl, &color_filter_ctrl, @@ -2301,7 +2301,7 @@ static int s2255_probe(struct usb_interface *interface, if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) pr_info("s2255: f2255usb.bin out of date.\n"); if (dev->pid == 0x2257 && - dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) pr_warn("2257 needs firmware %d or above.\n", S2255_MIN_DSP_COLORFILTER); } -- cgit From 9f6c8775b2dcd8cdaa65173ea918ebd9f4e019d6 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:36 +0800 Subject: media: s2255: Remove multiple blank lines and before close braces Remove all 9 instances of double blank lines: 'CHECK: Please don't use multiple blank lines' reported by checkpatch.pl in s2255drv.c. Fix all 3 'CHECK: Blank lines aren't necessary before a close brace' issues reported by checkpatch.pl in s2255drv.c. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index f3e4bd069f1c..dc3a9d69789c 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -86,7 +86,6 @@ #define LINE_SZ_DEF 640 #define NUM_LINES_DEF 240 - /* predefined settings */ #define FORMAT_NTSC 1 #define FORMAT_PAL 2 @@ -144,7 +143,6 @@ struct s2255_mode { u32 restart; /* if DSP requires restart */ }; - #define S2255_READ_IDLE 0 #define S2255_READ_FRAME 1 @@ -243,7 +241,6 @@ struct s2255_vc { spinlock_t qlock; }; - struct s2255_dev { struct s2255_vc vc[MAX_CHANNELS]; struct v4l2_device v4l2_dev; @@ -284,7 +281,6 @@ struct s2255_buffer { struct list_head list; }; - /* current cypress EEPROM firmware version */ #define S2255_CUR_USB_FWVER ((3 << 8) | 12) /* current DSP FW version */ @@ -430,7 +426,6 @@ static int norm_minh(struct s2255_vc *vc) (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); } - /* * TODO: fixme: move YUV reordering to hardware * converts 2255 planar format to yuyv or uyvy @@ -482,7 +477,6 @@ static void s2255_timer(struct timer_list *t) } } - /* this loads the firmware asynchronously. Originally this was done synchronously in probe. But it is better to load it asynchronously here than block @@ -538,7 +532,6 @@ static void s2255_fwchunk_complete(struct urb *urb) atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); } return; - } static void s2255_got_frame(struct s2255_vc *vc, int jpgsize) @@ -637,7 +630,6 @@ static void s2255_fillbuff(struct s2255_vc *vc, vbuf, pos); } - /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -881,7 +873,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } - /* write to the configuration pipe, synchronously */ static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, int size) @@ -1663,7 +1654,6 @@ static int s2255_probe_v4l(struct s2255_dev *dev) refcount_inc(&dev->num_channels); v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", video_device_node_name(&vc->vdev)); - } pr_info("Sensoray 2255 V4L driver Revision: %s\n", S2255_VERSION); @@ -1805,7 +1795,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) /* skip the marker 512 bytes (and offset if out of sync) */ psrc = (u8 *)pipe_info->transfer_buffer + offset; - if (!frm->lpvbits) { dprintk(dev, 1, "s2255 frame buffer == NULL.%p %p %d %d", frm, dev, dev->cc, idx); @@ -1840,7 +1829,6 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) vc->frame_count++; frm->state = S2255_READ_IDLE; frm->cur_size = 0; - } /* done successfully */ return 0; -- cgit From f770ad3ca0fca0a17317c4caabc3d974aa4ad65f Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:37 +0800 Subject: media: s2255: Add blank lines after function/struct/enum declarations Fix all 2 'CHECK: Please use a blank line after function/struct/union/enum declarations' issues reported by checkpatch.pl in s2255drv.c. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index dc3a9d69789c..3aa6270358fc 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1178,6 +1178,7 @@ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) *i = 0; return 0; } + static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { if (i > 0) @@ -1332,6 +1333,7 @@ static const struct v4l2_frmsize_discrete ntsc_sizes[] = { { 640, 240 }, { 320, 240 }, }; + static const struct v4l2_frmsize_discrete pal_sizes[] = { { 704, 576 }, { 704, 288 }, -- cgit From f7b05885a2f12b8fa05a24f685a159cb2ad7ddad Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:54:38 +0800 Subject: media: s2255: Add spaces around multiplication operators Fix all 3 'CHECK: spaces preferred around that '*' (ctx:VxV)' issues reported by checkpatch.pl in s2255drv.c. Add spaces around the multiplication operators in the SYS_FRAMES_MAXSIZE macro. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 3aa6270358fc..ee69492ba8b9 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -69,7 +69,7 @@ #define MAX_CHANNELS 4 #define SYS_FRAMES 4 /* maximum size is PAL full size plus room for the marker header(s) */ -#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) +#define SYS_FRAMES_MAXSIZE (720 * 288 * 2 * 2 + 4096) #define DEF_USB_BLOCK S2255_USB_XFER_SIZE #define LINE_SZ_4CIFS_NTSC 640 #define LINE_SZ_2CIFS_NTSC 640 -- cgit From fcd708ee65f526c6c796079dba6ef70bb2538516 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:01 +0800 Subject: media: s2255: Remove unnecessary parentheses around conditions Fix all 11 'CHECK: Unnecessary parentheses around' issues reported by checkpatch.pl in s2255drv.c. Remove redundant inner parentheses from if-condition expressions where operator precedence already guarantees the correct evaluation order. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index ee69492ba8b9..c65cb794599b 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -566,8 +566,8 @@ static const struct s2255_fmt *format_by_fourcc(int fourcc) for (i = 0; i < ARRAY_SIZE(formats); i++) { if (-1 == formats[i].fourcc) continue; - if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) + if (!jpeg_enable && (formats[i].fourcc == V4L2_PIX_FMT_JPEG || + formats[i].fourcc == V4L2_PIX_FMT_MJPEG)) continue; if (formats[i].fourcc == fourcc) return formats + i; @@ -659,10 +659,10 @@ static int buffer_prepare(struct vb2_buffer *vb) if (!vc->fmt) return -EINVAL; - if ((w < norm_minw(vc)) || - (w > norm_maxw(vc)) || - (h < norm_minh(vc)) || - (h > norm_maxh(vc))) { + if (w < norm_minw(vc) || + w > norm_maxw(vc) || + h < norm_minh(vc) || + h > norm_maxh(vc)) { dprintk(vc->dev, 4, "invalid buffer prepare\n"); return -EINVAL; } @@ -718,8 +718,8 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, if (index >= ARRAY_SIZE(formats)) return -EINVAL; - if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + if (!jpeg_enable && (formats[index].fourcc == V4L2_PIX_FMT_JPEG || + formats[index].fourcc == V4L2_PIX_FMT_MJPEG)) return -EINVAL; f->pixelformat = formats[index].fourcc; return 0; @@ -1296,8 +1296,8 @@ static int vidioc_s_parm(struct file *file, void *priv, return -EINVAL; mode = vc->mode; /* high quality capture mode requires a stream restart */ - if ((vc->cap_parm.capturemode != sp->parm.capture.capturemode) - && vb2_is_streaming(&vc->vb_vidq)) + if (vc->cap_parm.capturemode != sp->parm.capture.capturemode && + vb2_is_streaming(&vc->vb_vidq)) return -EBUSY; def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; @@ -1822,8 +1822,8 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) vc->last_frame = vc->cur_frame; vc->cur_frame++; /* end of system frame ring buffer, start at zero */ - if ((vc->cur_frame == SYS_FRAMES) || - (vc->cur_frame == vc->buffer.num_frames)) + if (vc->cur_frame == SYS_FRAMES || + vc->cur_frame == vc->buffer.num_frames) vc->cur_frame = 0; /* frame ready */ if (vb2_is_streaming(&vc->vb_vidq)) -- cgit From f29d0d8219f0b9eb537b92abc3041758133deb77 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:02 +0800 Subject: media: s2255: Parenthesize macro argument to avoid precedence issues Fix 'CHECK: Macro argument 'dev' may be better as '(dev)' to avoid precedence issues' reported by checkpatch.pl in s2255drv.c. Wrap the 'dev' macro argument in parentheses in the dprintk macro. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index c65cb794599b..e5242247a118 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -348,7 +348,7 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) #define dprintk(dev, level, fmt, arg...) \ - v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg) + v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ## arg) static struct usb_driver s2255_driver; -- cgit From e30eafc9b85642c3b3aaa7790dfe44bf70b4c831 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:03 +0800 Subject: media: s2255: Add blank lines after variable declarations Fix all 22 'WARNING: Missing a blank line after declarations' issues reported by checkpatch.pl in s2255drv.c. Insert a blank line between local variable declarations and the first statement in each function. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index e5242247a118..501ac7cf31b3 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -440,6 +440,7 @@ static void planar422p_to_yuv_packed(const unsigned char *in, unsigned char *p_cr; unsigned long size = height * width; unsigned int i; + p_y = (unsigned char *)in; p_cr = (unsigned char *)in + height * width; p_cb = (unsigned char *)in + height * width + (height * width / 2); @@ -468,6 +469,7 @@ static void s2255_timer(struct timer_list *t) { struct s2255_dev *dev = timer_container_of(dev, t, timer); struct s2255_fw *data = dev->fw_data; + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { pr_err("s2255: can't submit urb\n"); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -488,6 +490,7 @@ static void s2255_fwchunk_complete(struct urb *urb) struct s2255_fw *data = urb->context; struct usb_device *udev = urb->dev; int len; + if (urb->status) { dev_err(&udev->dev, "URB failed with status %d\n", urb->status); atomic_set(&data->fw_state, S2255_FW_FAILED); @@ -563,6 +566,7 @@ static void s2255_got_frame(struct s2255_vc *vc, int jpgsize) static const struct s2255_fmt *format_by_fourcc(int fourcc) { unsigned int i; + for (i = 0; i < ARRAY_SIZE(formats); i++) { if (-1 == formats[i].fourcc) continue; @@ -639,6 +643,7 @@ static int queue_setup(struct vb2_queue *vq, unsigned int sizes[], struct device *alloc_devs[]) { struct s2255_vc *vc = vb2_get_drv_priv(vq); + if (*nbuffers < S2255_MIN_BUFS) *nbuffers = S2255_MIN_BUFS; *nplanes = 1; @@ -682,6 +687,7 @@ static void buffer_queue(struct vb2_buffer *vb) struct s2255_buffer *buf = container_of(vbuf, struct s2255_buffer, vb); struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); unsigned long flags = 0; + dprintk(vc->dev, 1, "%s\n", __func__); spin_lock_irqsave(&vc->qlock, flags); list_add_tail(&buf->list, &vc->buf_list); @@ -880,6 +886,7 @@ static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, int pipe; int done; long retval = -1; + if (udev) { pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); @@ -954,6 +961,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) { struct device *dev = &sdev->udev->dev; + dev_info(dev, "------------------------------------------------\n"); dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); @@ -1153,6 +1161,7 @@ static int vidioc_enum_input(struct file *file, void *priv, inp->status = 0; if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { int rc; + rc = s2255_cmd_status(vc, &status); dprintk(dev, 4, "s2255_cmd_status rc: %d status %x\n", rc, status); @@ -1191,6 +1200,7 @@ static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) struct s2255_vc *vc = container_of(ctrl->handler, struct s2255_vc, hdl); struct s2255_mode mode; + mode = vc->mode; /* update the mode to the corresponding value */ switch (ctrl->id) { @@ -1292,6 +1302,7 @@ static int vidioc_s_parm(struct file *file, void *priv, struct s2255_mode mode; int fdec = FDEC_1; __u32 def_num, def_dem; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; mode = vc->mode; @@ -1692,6 +1703,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) struct s2255_framei *frm; unsigned char *pdata; struct s2255_vc *vc; + dprintk(dev, 100, "buffer to user\n"); vc = &dev->vc[dev->cc]; idx = vc->cur_frame; @@ -1840,6 +1852,7 @@ static void s2255_read_video_callback(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) { int res; + dprintk(dev, 50, "callback read video\n"); if (dev->cc >= MAX_CHANNELS) { @@ -1916,6 +1929,7 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) { unsigned long i; unsigned long reqsize; + vc->buffer.num_frames = SYS_FRAMES; /* always allocate maximum size(PAL) for system buffers */ reqsize = SYS_FRAMES_MAXSIZE; @@ -1948,6 +1962,7 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) static int s2255_release_sys_buffers(struct s2255_vc *vc) { unsigned long i; + for (i = 0; i < SYS_FRAMES; i++) { vfree(vc->buffer.frame[i].lpvbits); vc->buffer.frame[i].lpvbits = NULL; @@ -1961,6 +1976,7 @@ static int s2255_board_init(struct s2255_dev *dev) int fw_ver; int j; struct s2255_pipeinfo *pipe = &dev->pipe; + dprintk(dev, 4, "board init: %p", dev); memset(pipe, 0, sizeof(*pipe)); pipe->dev = dev; @@ -1985,6 +2001,7 @@ static int s2255_board_init(struct s2255_dev *dev) for (j = 0; j < MAX_CHANNELS; j++) { struct s2255_vc *vc = &dev->vc[j]; + vc->mode = mode_def; if (dev->pid == 0x2257 && j > 1) vc->mode.color |= (1 << 16); @@ -2008,6 +2025,7 @@ static int s2255_board_init(struct s2255_dev *dev) static int s2255_board_shutdown(struct s2255_dev *dev) { u32 i; + dprintk(dev, 1, "%s: dev: %p", __func__, dev); for (i = 0; i < MAX_CHANNELS; i++) { @@ -2028,6 +2046,7 @@ static void read_pipe_completion(struct urb *purb) struct s2255_dev *dev; int status; int pipe; + pipe_info = purb->context; if (!pipe_info) { dev_err(&purb->dev->dev, "no context!\n"); @@ -2080,6 +2099,7 @@ static int s2255_start_readpipe(struct s2255_dev *dev) int pipe; int retval; struct s2255_pipeinfo *pipe_info = &dev->pipe; + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); dprintk(dev, 2, "%s: IN %d\n", __func__, dev->read_endpoint); pipe_info->state = 1; @@ -2245,6 +2265,7 @@ static int s2255_probe(struct usb_interface *interface, init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { struct s2255_vc *vc = &dev->vc[i]; + vc->idx = i; vc->dev = dev; init_waitqueue_head(&vc->wait_setmode); @@ -2335,6 +2356,7 @@ static void s2255_disconnect(struct usb_interface *interface) struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); int i; int channels = refcount_read(&dev->num_channels); + mutex_lock(&dev->lock); v4l2_device_disconnect(&dev->v4l2_dev); mutex_unlock(&dev->lock); -- cgit From 24e4846a5110a2a7b7ed1b05099a3e2d12de27bf Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:04 +0800 Subject: media: s2255: Fix block comment style to kernel format Fix all 'WARNING: Block comments use * on subsequent lines' and 'WARNING: Block comments use a trailing */ on a separate line' issues reported by checkpatch.pl in s2255drv.c. Convert block comments to kernel-style format with leading '*' on continuation lines and '*/' on its own line. Convert banner-style separator comments to single-line format. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 50 ++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 501ac7cf31b3..543155fe75a9 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -110,9 +110,9 @@ #define FDEC_3 3 /* capture every 3rd frame */ #define FDEC_5 5 /* capture every 5th frame */ -/*------------------------------------------------------- - * Default mode parameters. - *-------------------------------------------------------*/ +/*-------------------------------------------------------*/ +/* Default mode parameters. */ +/*-------------------------------------------------------*/ #define DEF_SCALE SCALE_4CIFS #define DEF_COLOR COLOR_YUVPL #define DEF_FDEC FDEC_1 @@ -480,11 +480,11 @@ static void s2255_timer(struct timer_list *t) } /* this loads the firmware asynchronously. - Originally this was done synchronously in probe. - But it is better to load it asynchronously here than block - inside the probe function. Blocking inside probe affects boot time. - FW loading is triggered by the timer in the probe function -*/ + * Originally this was done synchronously in probe. + * But it is better to load it asynchronously here than block + * inside the probe function. Blocking inside probe affects boot time. + * FW loading is triggered by the timer in the probe function + */ static void s2255_fwchunk_complete(struct urb *urb) { struct s2255_fw *data = urb->context; @@ -507,8 +507,8 @@ static void s2255_fwchunk_complete(struct urb *urb) } #define CHUNK_SIZE 512 /* all USB transfers must be done with continuous kernel memory. - can't allocate more than 128k in current linux kernel, so - upload the firmware in chunks + * can't allocate more than 128k in current linux kernel, so + * upload the firmware in chunks */ if (data->fw_loaded < data->fw_size) { len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? @@ -634,9 +634,9 @@ static void s2255_fillbuff(struct s2255_vc *vc, vbuf, pos); } -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ +/* ------------------------------------------------------------------ */ +/* Videobuf operations */ +/* ------------------------------------------------------------------ */ static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes, @@ -949,7 +949,8 @@ static u32 get_transfer_size(struct s2255_mode *mode) } /* total bytes to send including prefix and 4K padding; - must be a multiple of USB_READ_SIZE */ + * must be a multiple of USB_READ_SIZE + */ usb_in_size = out_image_size + PREFIX_SIZE; /* always send prefix */ mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; /* if size not a multiple of USB_READ_SIZE */ @@ -1141,12 +1142,12 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *i) } /* Sensoray 2255 is a multiple channel capture device. - It does not have a "crossbar" of inputs. - We use one V4L device per channel. The user must - be aware that certain combinations are not allowed. - For instance, you cannot do full FPS on more than 2 channels(2 videodevs) - at once in color(you can do full fps on 4 channels with greyscale. -*/ + * It does not have a "crossbar" of inputs. + * We use one V4L device per channel. The user must + * be aware that certain combinations are not allowed. + * For instance, you cannot do full FPS on more than 2 channels(2 videodevs) + * at once in color(you can do full fps on 4 channels with greyscale. + */ static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { @@ -1228,9 +1229,9 @@ static int s2255_s_ctrl(struct v4l2_ctrl *ctrl) } mode.restart = 0; /* set mode here. Note: stream does not need restarted. - some V4L programs restart stream unnecessarily - after a s_crtl. - */ + * some V4L programs restart stream unnecessarily + * after a s_crtl. + */ s2255_set_mode(vc, &mode); return 0; } @@ -1436,7 +1437,8 @@ static int s2255_open(struct file *file) case S2255_FW_NOTLOADED: case S2255_FW_LOADED_DSPWAIT: /* give S2255_LOAD_TIMEOUT time for firmware to load in case - driver loaded and then device immediately opened */ + * driver loaded and then device immediately opened + */ pr_info("%s waiting for firmware load\n", __func__); wait_event_timeout(dev->fw_data->wait_fw, ((atomic_read(&dev->fw_data->fw_state) -- cgit From 98f0eda9a71de1f78609451285a4b84993b809fd Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:05 +0800 Subject: media: s2255: Remove unnecessary return statements from void functions Fix all 7 'WARNING: void function return statements are not generally useful' issues reported by checkpatch.pl in s2255drv.c. Remove trailing 'return;' statements that immediately precede the closing brace in void functions. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 543155fe75a9..78c78e55d180 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -450,7 +450,6 @@ static void planar422p_to_yuv_packed(const unsigned char *in, out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_y++ : *p_cb++; out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *p_cb++ : *p_y++; } - return; } static void s2255_reset_dsppower(struct s2255_dev *dev) @@ -460,7 +459,6 @@ static void s2255_reset_dsppower(struct s2255_dev *dev) s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); msleep(600); s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); - return; } /* kickstarts the firmware loading. from probe @@ -534,7 +532,6 @@ static void s2255_fwchunk_complete(struct urb *urb) } else { atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); } - return; } static void s2255_got_frame(struct s2255_vc *vc, int jpgsize) @@ -1563,7 +1560,6 @@ static void s2255_video_device_release(struct video_device *vdev) if (refcount_dec_and_test(&dev->num_channels)) s2255_destroy(dev); - return; } static const struct video_device template = { @@ -1868,7 +1864,6 @@ static void s2255_read_video_callback(struct s2255_dev *dev, dprintk(dev, 4, "s2255: read callback failed\n"); dprintk(dev, 50, "callback read video done\n"); - return; } static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, @@ -2093,7 +2088,6 @@ static void read_pipe_completion(struct urb *purb) } else { dprintk(dev, 2, "%s :complete state 0\n", __func__); } - return; } static int s2255_start_readpipe(struct s2255_dev *dev) @@ -2190,7 +2184,6 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) pipe->stream_urb = NULL; } dprintk(dev, 4, "%s", __func__); - return; } static void s2255_fwload_start(struct s2255_dev *dev) -- cgit From 71bb625ce24adaf59f343182b8a592de86a6b39f Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:06 +0800 Subject: media: s2255: Remove unnecessary debug logging and out-of-memory messages Fix all 5 'WARNING: Unnecessary ftrace-like logging - prefer using ftrace' issues reported by checkpatch.pl in s2255drv.c. Remove dprintk/dev_info calls that only print the function name, as ftrace can provide the same function tracing information. Fix all 3 'WARNING: Possible unnecessary 'out of memory' message' issues reported by checkpatch.pl in s2255drv.c. The kernel's memory allocator already prints a warning on allocation failure, making these messages redundant. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 78c78e55d180..a9ed663cece4 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -657,7 +657,6 @@ static int buffer_prepare(struct vb2_buffer *vb) int h = vc->height; unsigned long size; - dprintk(vc->dev, 4, "%s\n", __func__); if (!vc->fmt) return -EINVAL; @@ -685,7 +684,6 @@ static void buffer_queue(struct vb2_buffer *vb) struct s2255_vc *vc = vb2_get_drv_priv(vb->vb2_queue); unsigned long flags = 0; - dprintk(vc->dev, 1, "%s\n", __func__); spin_lock_irqsave(&vc->qlock, flags); list_add_tail(&buf->list, &vc->buf_list); spin_unlock_irqrestore(&vc->qlock, flags); @@ -1487,7 +1485,6 @@ static int s2255_open(struct file *file) static void s2255_destroy(struct s2255_dev *dev) { - dprintk(dev, 1, "%s", __func__); /* board shutdown stops the read pipe if it is running */ s2255_board_shutdown(dev); /* make sure firmware still not trying to load */ @@ -1939,7 +1936,6 @@ static int s2255_create_sys_buffers(struct s2255_vc *vc) vc->buffer.frame[i].lpvbits = vmalloc(reqsize); vc->buffer.frame[i].size = reqsize; if (!vc->buffer.frame[i].lpvbits) { - pr_info("out of memory. using less frames\n"); vc->buffer.num_frames = i; break; } @@ -1983,7 +1979,6 @@ static int s2255_board_init(struct s2255_dev *dev) pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, GFP_KERNEL); if (!pipe->transfer_buffer) { - dprintk(dev, 1, "out of memory!\n"); return -ENOMEM; } /* query the firmware */ @@ -2183,7 +2178,6 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) usb_free_urb(pipe->stream_urb); pipe->stream_urb = NULL; } - dprintk(dev, 4, "%s", __func__); } static void s2255_fwload_start(struct s2255_dev *dev) @@ -2217,13 +2211,11 @@ static int s2255_probe(struct usb_interface *interface, /* allocate memory for our device state and initialize it to zero */ dev = kzalloc_obj(struct s2255_dev); if (!dev) { - s2255_dev_err(&interface->dev, "out of memory\n"); return -ENOMEM; } dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); if (!dev->cmdbuf) { - s2255_dev_err(&interface->dev, "out of memory\n"); goto err_fwdata1; } @@ -2275,7 +2267,6 @@ static int s2255_probe(struct usb_interface *interface, dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); if (!dev->fw_data->pfw_data) { - dev_err(&interface->dev, "out of memory!\n"); goto err_fwdata2; } /* load the first chunk */ @@ -2371,7 +2362,6 @@ static void s2255_disconnect(struct usb_interface *interface) } if (refcount_dec_and_test(&dev->num_channels)) s2255_destroy(dev); - dev_info(&interface->dev, "%s\n", __func__); } static struct usb_driver s2255_driver = { -- cgit From 5e62e53289f5a9fba98013bb4143e3277b254260 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:07 +0800 Subject: media: s2255: Remove unnecessary braces from single-statement blocks Fix all 4 'WARNING: braces {} are not necessary for single statement blocks' issues reported by checkpatch.pl in s2255drv.c. Remove braces from single-statement if blocks where they are not needed. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index a9ed663cece4..c66cca2e76dd 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -1978,9 +1978,8 @@ static int s2255_board_init(struct s2255_dev *dev) pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, GFP_KERNEL); - if (!pipe->transfer_buffer) { + if (!pipe->transfer_buffer) return -ENOMEM; - } /* query the firmware */ fw_ver = s2255_get_fx2fw(dev); @@ -2210,14 +2209,12 @@ static int s2255_probe(struct usb_interface *interface, /* allocate memory for our device state and initialize it to zero */ dev = kzalloc_obj(struct s2255_dev); - if (!dev) { + if (!dev) return -ENOMEM; - } dev->cmdbuf = kzalloc(S2255_CMDBUF_SIZE, GFP_KERNEL); - if (!dev->cmdbuf) { + if (!dev->cmdbuf) goto err_fwdata1; - } refcount_set(&dev->num_channels, 0); dev->pid = id->idProduct; @@ -2266,9 +2263,8 @@ static int s2255_probe(struct usb_interface *interface, goto err_fwurb; dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); - if (!dev->fw_data->pfw_data) { + if (!dev->fw_data->pfw_data) goto err_fwdata2; - } /* load the first chunk */ if (request_firmware(&dev->fw_data->fw, FIRMWARE_FILE_NAME, &dev->udev->dev)) { -- cgit From e260d1d7713b92f7156f5de7716c68438f49ae37 Mon Sep 17 00:00:00 2001 From: Lei Huang Date: Mon, 20 Jul 2026 10:55:08 +0800 Subject: media: s2255: Replace bare 'unsigned' with 'unsigned int' Fix 'WARNING: Prefer 'unsigned int' to bare use of 'unsigned'' reported by checkpatch.pl in s2255drv.c. Signed-off-by: Lei Huang Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index c66cca2e76dd..68cc4ea5b459 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -210,7 +210,7 @@ struct s2255_vc { struct s2255_mode mode; v4l2_std_id std; /* jpeg compression */ - unsigned jpegqual; + unsigned int jpegqual; /* capture parameters (for high quality mode full size) */ struct v4l2_captureparm cap_parm; int cur_frame; -- cgit From 59dc14a6cb5ff1ae5ce2f6ebf51fe0139e1c0623 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Tue, 30 Jun 2026 12:50:15 +0200 Subject: media: go7007: s2250: propagate control I/O failures The brightness and contrast paths use oldvalue after read_reg_fp() without checking whether the read succeeded. A failed read leaves oldvalue uninitialized and can write arbitrary reserved register bits. Return read and write errors from every control path so failed hardware access cannot be reported as a successful control update. Signed-off-by: Yousef Alhouseen Signed-off-by: Hans Verkuil --- drivers/media/usb/go7007/s2250-board.c | 54 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c index 0901d79e827d..d11f8e723624 100644 --- a/drivers/media/usb/go7007/s2250-board.c +++ b/drivers/media/usb/go7007/s2250-board.c @@ -365,36 +365,48 @@ static int s2250_s_ctrl(struct v4l2_ctrl *ctrl) struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl); struct i2c_client *client = v4l2_get_subdevdata(&state->sd); u16 oldvalue; + int ret; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - ctrl->val | (oldvalue & ~0xff)); - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - ctrl->val | (oldvalue & ~0xff)); - write_reg_fp(client, 0x140, 0x60); - break; + ret = read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); + if (ret) + return ret; + ret = write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, + ctrl->val | (oldvalue & ~0xff)); + if (ret) + return ret; + ret = read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); + if (ret) + return ret; + ret = write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, + ctrl->val | (oldvalue & ~0xff)); + if (ret) + return ret; + return write_reg_fp(client, 0x140, 0x60); case V4L2_CID_CONTRAST: - read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST0, - ctrl->val | (oldvalue & ~0x3f)); - read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST1, - ctrl->val | (oldvalue & ~0x3f)); - write_reg_fp(client, 0x140, 0x60); - break; + ret = read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); + if (ret) + return ret; + ret = write_reg_fp(client, VPX322_ADDR_CONTRAST0, + ctrl->val | (oldvalue & ~0x3f)); + if (ret) + return ret; + ret = read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); + if (ret) + return ret; + ret = write_reg_fp(client, VPX322_ADDR_CONTRAST1, + ctrl->val | (oldvalue & ~0x3f)); + if (ret) + return ret; + return write_reg_fp(client, 0x140, 0x60); case V4L2_CID_SATURATION: - write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val); - break; + return write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val); case V4L2_CID_HUE: - write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val); - break; + return write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val); default: return -EINVAL; } - return 0; } static int s2250_set_fmt(struct v4l2_subdev *sd, -- cgit From e504cc888f42999dd76b6a43788c422610f2aad2 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Wed, 1 Jul 2026 20:45:36 +0900 Subject: media: s2255: bound JPEG frame size before copying into the buffer s2255_fillbuff() memcpy()s vc->jpg_size bytes of a captured JPEG/MJPEG frame into the vb2 plane. vc->jpg_size is taken verbatim from the S2255_MARKER_FRAME header the device sends (pdword[4] in save_frame()) and, unlike the frame payload length just above it, is never bounded: payload = le32_to_cpu(pdword[3]); if (payload > vc->req_image_size) /* payload is checked ... */ return -EINVAL; vc->pkt_size = payload; vc->jpg_size = le32_to_cpu(pdword[4]); /* ... jpg_size is not */ A malicious or malfunctioning device can therefore report a jpg_size larger than the destination vb2 plane, and the memcpy() writes past it. jpg_size is a signed int, so a value with the top bit set also turns into a huge length. Reject a frame whose jpg_size is negative or exceeds the plane size before copying it. Fixes: 38f993ad8b1f ("V4L/DVB (8125): This driver adds support for the Sensoray 2255 devices.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Signed-off-by: Hans Verkuil --- drivers/media/usb/s2255/s2255drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index 68cc4ea5b459..15012d73975c 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -612,6 +612,12 @@ static void s2255_fillbuff(struct s2255_vc *vc, break; case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: + if (jpgsize < 0 || + jpgsize > vb2_plane_size(&buf->vb.vb2_buf, 0)) { + dprintk(dev, 1, "bad JPEG frame size %d\n", + jpgsize); + break; + } vb2_set_plane_payload(&buf->vb.vb2_buf, 0, jpgsize); memcpy(vbuf, tmpbuf, jpgsize); break; -- cgit From 9fa26c971ca310dd10a3bc0014f2fc7ea7167bba Mon Sep 17 00:00:00 2001 From: Rituparna Warwatkar Date: Tue, 14 Jul 2026 16:10:59 +0200 Subject: media: dvb-core: fix feed leak on failed DMX_ADD_PID dvb_dmxdev_add_pid() allocates a new dmxdev_feed, links it into filter->feed.ts and, when the filter is already running, immediately starts the feed. If starting the feed fails, the newly allocated feed remains on the list. Subsequent restart and rollback paths may then operate on this stale entry, leaving feed resources allocated and causing leaks in drivers that allocate resources from ->start_feed() and release them from ->stop_feed(). Remove the feed from the list and free it when dvb_dmxdev_start_feed() fails. Reported-by: syzbot+e9a1f5e196de6663631b@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e9a1f5e196de6663631b Signed-off-by: Rituparna Warwatkar Link: https://patch.msgid.link/20260714141059.63582-1-rwarwatkar@gmail.com Signed-off-by: Mauro Carvalho Chehab Message-ID: <20260714141059.63582-1-rwarwatkar@gmail.com> --- drivers/media/dvb-core/dmxdev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c index 3c8bc75e4d6c..6a825d9bae43 100644 --- a/drivers/media/dvb-core/dmxdev.c +++ b/drivers/media/dvb-core/dmxdev.c @@ -884,6 +884,7 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, struct dmxdev_filter *filter, u16 pid) { struct dmxdev_feed *feed; + int ret; if ((filter->type != DMXDEV_TYPE_PES) || (filter->state < DMXDEV_STATE_SET)) @@ -901,8 +902,14 @@ static int dvb_dmxdev_add_pid(struct dmxdev *dmxdev, feed->pid = pid; list_add(&feed->next, &filter->feed.ts); - if (filter->state >= DMXDEV_STATE_GO) - return dvb_dmxdev_start_feed(dmxdev, filter, feed); + if (filter->state >= DMXDEV_STATE_GO) { + ret = dvb_dmxdev_start_feed(dmxdev, filter, feed); + if (ret < 0) { + list_del(&feed->next); + kfree(feed); + return ret; + } + } return 0; } -- cgit From 634be628b5ccde3dcc9ba1a89c305cad0c286dd7 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 15 Jul 2026 09:08:20 +0530 Subject: media: i2c: cx25840-core: Add missing media_entity_cleanup() The remove function is missing a call to media_entity_cleanup(). Add it. Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/cx25840/cx25840-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c index 8110d40931d9..8b7dd43ed208 100644 --- a/drivers/media/i2c/cx25840/cx25840-core.c +++ b/drivers/media/i2c/cx25840/cx25840-core.c @@ -3962,6 +3962,7 @@ static int cx25840_probe(struct i2c_client *client) int err = state->hdl.error; v4l2_ctrl_handler_free(&state->hdl); + media_entity_cleanup(&sd->entity); return err; } if (!is_cx2583x(state)) @@ -3986,6 +3987,7 @@ static void cx25840_remove(struct i2c_client *client) cx25840_ir_remove(sd); v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&state->hdl); + media_entity_cleanup(&sd->entity); } static const struct i2c_device_id cx25840_id[] = { -- cgit From d2bc4752548652d9de002e9ca93c162ac54e115a Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 15 Jul 2026 09:08:21 +0530 Subject: media: i2c: max9286: Add missing media_entity_cleanup() The remove function is missing a call to media_entity_cleanup(). Add it. Reviewed-by: Jacopo Mondi Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/max9286.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index ac0712ce1e65..79eab9045e24 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -1062,7 +1062,7 @@ static int max9286_v4l2_register(struct max9286_priv *priv) priv->sd.state_lock = priv->ctrls.lock; ret = v4l2_subdev_init_finalize(&priv->sd); if (ret) - goto err_async; + goto err_entity; ret = v4l2_async_register_subdev(&priv->sd); if (ret < 0) { @@ -1074,6 +1074,8 @@ static int max9286_v4l2_register(struct max9286_priv *priv) err_subdev: v4l2_subdev_cleanup(&priv->sd); +err_entity: + media_entity_cleanup(&priv->sd.entity); err_async: v4l2_ctrl_handler_free(&priv->ctrls); max9286_v4l2_notifier_unregister(priv); @@ -1084,6 +1086,7 @@ err_async: static void max9286_v4l2_unregister(struct max9286_priv *priv) { v4l2_subdev_cleanup(&priv->sd); + media_entity_cleanup(&priv->sd.entity); v4l2_ctrl_handler_free(&priv->ctrls); v4l2_async_unregister_subdev(&priv->sd); max9286_v4l2_notifier_unregister(priv); -- cgit From 87d1307b6791eb3910836a66ea5d3a5b1f72f500 Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 15 Jul 2026 09:08:22 +0530 Subject: media: i2c: msp3400-driver: Add missing media_entity_cleanup() The remove function and the probe error path are missing calls to media_entity_cleanup(). Add them. Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/msp3400-driver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 413cfbc2dd94..a540b211f7d7 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -717,6 +717,7 @@ static int msp_probe(struct i2c_client *client) if (state->rev1 == -1 || (state->rev1 == 0 && state->rev2 == 0)) { dev_dbg_lvl(&client->dev, 1, msp_debug, "not an msp3400 (cannot read chip version)\n"); + media_entity_cleanup(&sd->entity); return -ENODEV; } @@ -812,6 +813,7 @@ static int msp_probe(struct i2c_client *client) int err = hdl->error; v4l2_ctrl_handler_free(hdl); + media_entity_cleanup(&sd->entity); return err; } @@ -865,6 +867,7 @@ static void msp_remove(struct i2c_client *client) msp_reset(client); v4l2_ctrl_handler_free(&state->hdl); + media_entity_cleanup(&state->sd.entity); } /* ----------------------------------------------------------------------- */ -- cgit From ff340ae9b1b0589db374d3da64d1566d8bfc5fdb Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 15 Jul 2026 09:08:23 +0530 Subject: media: i2c: mt9v011: Add missing media_entity_cleanup() The remove function is missing a call to media_entity_cleanup(). Add it. Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/mt9v011.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c index 985517f1cff7..ff55a8ff32b4 100644 --- a/drivers/media/i2c/mt9v011.c +++ b/drivers/media/i2c/mt9v011.c @@ -520,6 +520,7 @@ static int mt9v011_probe(struct i2c_client *c) (version != MT9V011_REV_B_VERSION)) { v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", version); + media_entity_cleanup(&sd->entity); return -EINVAL; } @@ -542,6 +543,7 @@ static int mt9v011_probe(struct i2c_client *c) v4l2_err(sd, "control initialization error %d\n", ret); v4l2_ctrl_handler_free(&core->ctrls); + media_entity_cleanup(&sd->entity); return ret; } core->sd.ctrl_handler = &core->ctrls; @@ -577,6 +579,7 @@ static void mt9v011_remove(struct i2c_client *c) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(&core->ctrls); + media_entity_cleanup(&sd->entity); } /* ----------------------------------------------------------------------- */ -- cgit From 6787d5189e32097a36d54d354439e560f5d22bfc Mon Sep 17 00:00:00 2001 From: Biren Pandya Date: Wed, 15 Jul 2026 09:08:24 +0530 Subject: media: i2c: saa7115: Add missing media_entity_cleanup() The remove function is missing a call to media_entity_cleanup(). Add it. Signed-off-by: Biren Pandya Signed-off-by: Sakari Ailus --- drivers/media/i2c/saa7115.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index 7cce90750c93..d20695238b48 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -1867,6 +1867,7 @@ static int saa711x_probe(struct i2c_client *client) int err = hdl->error; v4l2_ctrl_handler_free(hdl); + media_entity_cleanup(&sd->entity); return err; } v4l2_ctrl_auto_cluster(2, &state->agc, 0, true); @@ -1925,6 +1926,7 @@ static void saa711x_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); } static const struct i2c_device_id saa711x_id[] = { -- cgit From 4f26c8345dbfa92f19078c788be1b9bfd425ed60 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Wed, 20 May 2026 18:37:00 +0300 Subject: media: bcm2835-unicam: Fix pipeline wrong validation for unpacked formats The commit 08f9794d9b79 ("media: bcm2835-unicam: Fix RGB format / mbus code association") introduced a check to see whether the format requested is the same as the fourcc in the format list. However, this breaks the case when userspace requested an unpacked fourcc, e.g. RG10. Unicam can work with or without unpacking pixels, e.g. pRAA or RG10, depending on what userspace requests. In the unpacking case, a dedicated register is being set. If the userspace requests pRAA, this works, because the check validates the pipeline: v4l2-ctl -d /dev/video0 --set-fmt-video=width=3280,height=2464,pixelformat=pRAA \ --stream-mmap --stream-count=1 --stream-to=frame.raw but, with v4l2-ctl -d /dev/video0 --set-fmt-video=width=3280,height=2464,pixelformat=RG10 \ --stream-mmap --stream-count=1 --stream-to=frame.raw unicam complains at validation level: image: format mismatch: 0x300f <=> RG10 little-endian (0x30314752) This should work, because MEDIA_BUS_FMT_SRGGB10_1X10 can be packed into either RG10 or pRAA depending on the packing register. To fix this, modified the condition check to also allow in the case when requested format (fmt->pixelformat) is equal to fmtinfo->unpacked_fourcc. Fixes: 08f9794d9b79 ("media: bcm2835-unicam: Fix RGB format / mbus code association") Cc: stable@vger.kernel.org Signed-off-by: Eugen Hristev Reviewed-by: Jai Luthra Reviewed-by: Dave Stevenson [Sakari Ailus: Added Cc: stable.] Signed-off-by: Sakari Ailus --- drivers/media/platform/broadcom/bcm2835-unicam.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c index 1508843ae58c..14bb916dd7b1 100644 --- a/drivers/media/platform/broadcom/bcm2835-unicam.c +++ b/drivers/media/platform/broadcom/bcm2835-unicam.c @@ -2158,7 +2158,8 @@ static int unicam_video_link_validate(struct media_link *link) * In order to allow the applications using the old behaviour to * run, let's accept the old combination, but warn about it. */ - if (fmtinfo->fourcc != fmt->pixelformat) { + if (fmt->pixelformat != fmtinfo->fourcc && + fmt->pixelformat != fmtinfo->unpacked_fourcc) { if ((fmt->pixelformat == V4L2_PIX_FMT_BGR24 && format->code == MEDIA_BUS_FMT_BGR888_1X24) || (fmt->pixelformat == V4L2_PIX_FMT_RGB24 && -- cgit From fb9b87145624eedaf3d50edac9b11d707494cb5b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 2 Jun 2026 19:53:05 +0100 Subject: media: mali-c55: fix dropped last AEC histogram zone weight The 15x15 AEC histogram metering grid has 225 per-zone weights, packed by userspace as a u8 array. The driver writes the first 56 registers (zones 0 through 223) in a loop, then handles the final register on its own to keep static analysers from flagging the array access. That separate path computes the address and value for the 225th weight (the bottom-right zone) but never issues the register write, so the zone keeps its stale or default weight. Any non-default weight userspace sets for the last zone is silently ignored, skewing auto-exposure metering. Both the AEXP_HIST_WEIGHTS and AEXP_IHIST_WEIGHTS blocks are affected as they share this handler. Issue the missing write, masking the value as the loop does. Fixes: 01535ea08674 ("media: platform: Add mali-c55 parameters video node") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-params.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index de0e9d898db7..33e2232ec8f5 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -212,6 +212,7 @@ mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55, val = params->zone_weights[MALI_C55_MAX_ZONES - 1]; addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56); + mali_c55_ctx_write(mali_c55, addr, val & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK); } static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55, -- cgit From 9a2925b823d541a23b8330f80056e6cca78c7677 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 9 Jun 2026 06:32:31 +0100 Subject: media: mali-c55: Fix AEXP IHIST disable bit shift The post-Iridix auto-exposure histogram disable bit in MALI_C55_REG_METERING_CONFIG is bit 16, but MALI_C55_AEXP_IHIST_DISABLE was defined with a shift of 12, copied from the AEXP_HIST definition above it. As the value is masked with the BIT(16) disable mask when it is programmed, the result is always zero and the disable bit is never set. The IHIST can therefore never be disabled, neither at ISP init nor via a parameters block flagged V4L2_ISP_PARAMS_FL_BLOCK_DISABLE, and the hardware keeps producing histogram statistics that userspace believes are switched off. Use a shift of 16 so the disable request takes effect. Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Reviewed-by: Daniel Scally Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-registers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h index f098effde7b4..4cd13b702d9f 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h +++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h @@ -173,7 +173,7 @@ enum mali_c55_interrupts { #define MALI_C55_AEXP_HIST_SWITCH_MASK GENMASK(14, 13) #define MALI_C55_AEXP_HIST_SWITCH(x) ((x) << 13) #define MALI_C55_AEXP_IHIST_DISABLE_MASK BIT(16) -#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 12) +#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 16) #define MALI_C55_AEXP_SRC_MASK BIT(24) #define MALI_C55_REG_TPG_CH0 0x18ed8 -- cgit From 2447c768cb5dfb9f52630b82d419e2b24fee27a3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 18 Jun 2026 20:52:54 +0100 Subject: media: mali-c55: Fix scaler factor overflow for large crop sizes The horizontal and vertical scaling factors multiply the crop dimensions by MALI_C55_RSZ_SCALER_FACTOR, a Q4.20 factor of (1 << 20). Both operands are 32-bit, so the multiplication wraps before the result is stored in the u64 scale variables. For any crop dimension of 4096 or more (the maximum is 8192) the value overflows; an 8192 to 4096 downscale yields a TINC of zero, so the scaler never advances and the output is corrupted. Define MALI_C55_RSZ_SCALER_FACTOR as a 64-bit constant so the multiplication is performed in 64-bit. Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Daniel Scally Reviewed-by: Linus Walleij Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-resizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c index c4f46651dcee..6706939b4a90 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c @@ -15,7 +15,7 @@ #include "mali-c55-registers.h" /* Scaling factor in Q4.20 format. */ -#define MALI_C55_RSZ_SCALER_FACTOR (1U << 20) +#define MALI_C55_RSZ_SCALER_FACTOR BIT_ULL(20) #define MALI_C55_RSZ_COEFS_BANKS 8 #define MALI_C55_RSZ_COEFS_ENTRIES 64 -- cgit From 876006e994c4ac8f2af553364b5a2282d6dee9ab Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 30 Jun 2026 21:40:28 +0100 Subject: media: mali-c55: Drop redundant mutex_destroy in capture register error path In mali_c55_register_cap_dev(), a failure of media_entity_pads_init() destroys cap_dev->lock inline and then jumps to err_destroy_mutex, which destroys the same mutex a second time. Calling mutex_destroy() twice is harmless, so this is not a bugfix, but the inline call is redundant: the err_destroy_mutex label already covers this path, just like the switch default case immediately above. Drop the inline mutex_destroy() and rely solely on the err_destroy_mutex label, so the mutex is destroyed exactly once on every error path. Signed-off-by: David Carlier Reviewed-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-capture.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c index 7aaa5c3f7354..ff01553026fb 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c @@ -857,10 +857,8 @@ static int mali_c55_register_cap_dev(struct mali_c55 *mali_c55, cap_dev->pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_pads_init(&cap_dev->vdev.entity, 1, &cap_dev->pad); - if (ret) { - mutex_destroy(&cap_dev->lock); + if (ret) goto err_destroy_mutex; - } vb2q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; vb2q->io_modes = VB2_MMAP | VB2_DMABUF; -- cgit From f499befeb668436b67bdb2b610b34db57732703f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 3 Jul 2026 17:35:03 +0100 Subject: media: mali-c55: Fix clock leak on reset deassert failure __mali_c55_power_on() enables the clocks before deasserting the resets, but bails out on a deassert failure without disabling them again. Both callers treat a failed power-on as already cleaned up, so the clocks are left enabled. Disable them on the error path. Fixes: d5f281f3dd29 ("media: mali-c55: Add Mali-C55 ISP driver") Cc: stable@vger.kernel.org Signed-off-by: David Carlier Reviewed-by: Daniel Scally Reviewed-by: Jacopo Mondi Signed-off-by: Hans Verkuil --- drivers/media/platform/arm/mali-c55/mali-c55-core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c index ee4a4267415e..fb81141d1653 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c @@ -699,6 +699,8 @@ static int __mali_c55_power_on(struct mali_c55 *mali_c55) mali_c55->resets); if (ret) { dev_err(mali_c55->dev, "failed to deassert resets\n"); + clk_bulk_disable_unprepare(ARRAY_SIZE(mali_c55->clks), + mali_c55->clks); return ret; } -- cgit From bb401df68c06b13db7f1f4de97d7c588b9e22a03 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 30 Jun 2026 09:41:27 +0200 Subject: media: mali-c55: Add support for CCM Add support for the CCM (Color Correction Matrix) for the Mali C55 ISP. Define a new block in the uAPI using the extensible v4l2-isp format and implement support for configuring the CCM parameters in the mali-c55 ISP driver. Signed-off-by: Jacopo Mondi Reviewed-by: Vincenzo Frascino Reviewed-by: Linus Walleij Signed-off-by: Hans Verkuil --- .../media/platform/arm/mali-c55/mali-c55-params.c | 52 ++++++++++++++++++++++ include/uapi/linux/media/arm/mali-c55-config.h | 41 ++++++++++++++++- 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index 33e2232ec8f5..98f780cd46fe 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -46,6 +46,7 @@ * @awb_config: For header->type == MALI_C55_PARAM_BLOCK_AWB_CONFIG * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG * @shading_selection: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION + * @ccm: For header->type == MALI_C55_PARAM_BLOCK_CCM * @data: Allows easy initialisation of a union variable with a * pointer into a __u8 array. */ @@ -59,6 +60,7 @@ union mali_c55_params_block { const struct mali_c55_params_awb_config *awb_config; const struct mali_c55_params_mesh_shading_config *shading_config; const struct mali_c55_params_mesh_shading_selection *shading_selection; + const struct mali_c55_params_ccm *ccm; const __u8 *data; }; @@ -415,6 +417,52 @@ static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55, params->mesh_strength); } +static void mali_c55_params_ccm(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + const struct mali_c55_params_ccm *params = block.ccm; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0); + return; + } + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_R, + MALI_C55_CCM_COEF_MASK, params->coeffs[0][0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_G, + MALI_C55_CCM_COEF_MASK, params->coeffs[0][1]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_R_B, + MALI_C55_CCM_COEF_MASK, params->coeffs[0][2]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_R, + MALI_C55_CCM_COEF_MASK, params->coeffs[1][0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_G, + MALI_C55_CCM_COEF_MASK, params->coeffs[1][1]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_G_B, + MALI_C55_CCM_COEF_MASK, params->coeffs[1][2]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_R, + MALI_C55_CCM_COEF_MASK, params->coeffs[2][0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_G, + MALI_C55_CCM_COEF_MASK, params->coeffs[2][1]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_COEF_B_B, + MALI_C55_CCM_COEF_MASK, params->coeffs[2][2]); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_R, + MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_G, + MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[1]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_GAIN_B, + MALI_C55_CCM_ANTIFOG_GAIN_MASK, params->gains[2]); + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_R, + MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_G, + MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[1]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_CCM_ANTIFOG_OFFSET_B, + MALI_C55_CCM_ANTIFOG_OFFSET_MASK, params->offs[2]); + + mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 1); +} + static const mali_c55_params_handler mali_c55_params_handlers[] = { [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs, [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist, @@ -427,6 +475,7 @@ static const mali_c55_params_handler mali_c55_params_handlers[] = { [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains, [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config, [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection, + [MALI_C55_PARAM_BLOCK_CCM] = &mali_c55_params_ccm, }; static const struct v4l2_isp_params_block_type_info @@ -464,6 +513,9 @@ mali_c55_params_block_types_info[] = { [MALI_C55_PARAM_MESH_SHADING_SELECTION] = { .size = sizeof(struct mali_c55_params_mesh_shading_selection), }, + [MALI_C55_PARAM_BLOCK_CCM] = { + .size = sizeof(struct mali_c55_params_ccm), + }, }; static_assert(ARRAY_SIZE(mali_c55_params_handlers) == diff --git a/include/uapi/linux/media/arm/mali-c55-config.h b/include/uapi/linux/media/arm/mali-c55-config.h index 3d335f950eeb..0b18863f973d 100644 --- a/include/uapi/linux/media/arm/mali-c55-config.h +++ b/include/uapi/linux/media/arm/mali-c55-config.h @@ -219,6 +219,7 @@ struct mali_c55_stats_buffer { * @MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP: Auto-white balance gains for AEXP-0 tap * @MALI_C55_PARAM_MESH_SHADING_CONFIG : Mesh shading tables configuration * @MALI_C55_PARAM_MESH_SHADING_SELECTION: Mesh shading table selection + * @MALI_C55_PARAM_BLOCK_CCM: Colour correction matrix */ enum mali_c55_param_block_type { MALI_C55_PARAM_BLOCK_SENSOR_OFFS, @@ -232,6 +233,7 @@ enum mali_c55_param_block_type { MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP, MALI_C55_PARAM_MESH_SHADING_CONFIG, MALI_C55_PARAM_MESH_SHADING_SELECTION, + MALI_C55_PARAM_BLOCK_CCM, }; /** @@ -757,6 +759,42 @@ struct mali_c55_params_mesh_shading_selection { __u16 mesh_strength; }; +/** + * struct mali_c55_params_ccm - Coefficients, offsets and gains for the colour + * correction matrix + * + * The colour correction module converts images data from a sensor-specific + * colour space to known one. + * + * Colour correction is applied after demosaicing and each pixel is represented + * as a column vector of the three RGB colour channels on which the following + * operations take place: + * 1) An offset is subtracted from each colour channel + * 2) Each colour channel is multiplied by a gain + * 3) The pixel column vector is multiplied by the colour correction matrix + * + * This struct allows users to configure the coefficients for CCM and the + * per-channel offsets and gains. The nine matrix coefficients are expressed as + * 13 bits signed Q4.8 Sign/Magnitude fixed-point numbers, the three gain + * multipliers are expressed as 12 bits unsigned Q4.8 fixed-point numbers and + * the three offsets are expressed as a 12 bits unsigned integers. + * + * header.type should be set to MALI_C55_PARAM_BLOCK_CCM from + * :c:type:`mali_c55_param_block_type`. + * + * @header: The Mali-C55 parameters block header + * @coeffs: 3x3 color conversion matrix coefficients in sign/magnitude + * Q4.8 format + * @gains: Gains for red, green and blue channels in unsigned Q4.8 format + * @offs: Offsets for red, green and blue channels + */ +struct mali_c55_params_ccm { + struct v4l2_isp_params_block_header header; + __u16 coeffs[3][3]; + __u16 gains[3]; + __u16 offs[3]; +}; + /** * define MALI_C55_PARAMS_MAX_SIZE - Maximum size of all Mali C55 Parameters * @@ -780,6 +818,7 @@ struct mali_c55_params_mesh_shading_selection { sizeof(struct mali_c55_params_awb_config) + \ sizeof(struct mali_c55_params_awb_gains) + \ sizeof(struct mali_c55_params_mesh_shading_config) + \ - sizeof(struct mali_c55_params_mesh_shading_selection)) + sizeof(struct mali_c55_params_mesh_shading_selection) + \ + sizeof(struct mali_c55_params_ccm)) #endif /* __UAPI_MALI_C55_CONFIG_H */ -- cgit From 6962703102c4a65e8d2f34fd43aeb8c3df45869d Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Tue, 30 Jun 2026 09:41:28 +0200 Subject: media: mali-c55: Add support for RGB Gamma Add support for Gamma curve correction for the Mali C55 ISP. Define a new block in the uAPI using the extensible v4l2-isp format and implement support for configuring the RGB Gamma parameters in the mali-c55 parameters handler. While at it, rename the MALI_C55_REG_GAMMA_GAINS_[1|2] register name to MALI_C55_REG_GAMMA_GAINS_[RG|B] and the MALI_C55_REG_GAMMA_OFFSETS_[1|2] register name to MALI_C55_REG_GAMMA_OFFSETS_[RG|B] to better clarify their intent. Signed-off-by: Jacopo Mondi Reviewed-by: Vincenzo Frascino Reviewed-by: Linus Walleij Signed-off-by: Hans Verkuil --- .../media/platform/arm/mali-c55/mali-c55-params.c | 75 ++++++++++++++++++++++ .../platform/arm/mali-c55/mali-c55-registers.h | 13 ++-- include/uapi/linux/media/arm/mali-c55-config.h | 47 +++++++++++++- 3 files changed, 130 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index 98f780cd46fe..e53c7b512dbd 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -47,6 +47,8 @@ * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG * @shading_selection: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION * @ccm: For header->type == MALI_C55_PARAM_BLOCK_CCM + * @gamma: For header->type == MALI_C55_PARAM_BLOCK_GAMMA_FR and + * header->type = MALI_C55_PARAM_BLOCK_GAMMA_DS * @data: Allows easy initialisation of a union variable with a * pointer into a __u8 array. */ @@ -61,6 +63,7 @@ union mali_c55_params_block { const struct mali_c55_params_mesh_shading_config *shading_config; const struct mali_c55_params_mesh_shading_selection *shading_selection; const struct mali_c55_params_ccm *ccm; + const struct mali_c55_params_gamma *gamma; const __u8 *data; }; @@ -463,6 +466,70 @@ static void mali_c55_params_ccm(struct mali_c55 *mali_c55, mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 1); } +static void mali_c55_params_gamma(struct mali_c55 *mali_c55, + union mali_c55_params_block block, + __u32 offset, __u32 lut_base) +{ + const struct mali_c55_params_gamma *params = block.gamma; + + if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_GAMMA_RGB_ENABLE + offset, + MALI_C55_GAMMA_ENABLE_MASK, 0x00); + return; + } + + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_RG + offset, + MALI_C55_GAMMA_GAIN_R_MASK, params->gains[0]); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_RG + offset, + MALI_C55_GAMMA_GAIN_G_MASK, + MALI_C55_GAMMA_GAIN_G(params->gains[1])); + mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_GAMMA_GAINS_B + offset, + MALI_C55_GAMMA_GAIN_B_MASK, params->gains[2]); + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_GAMMA_OFFSETS_RG + offset, + MALI_C55_GAMMA_OFFSET_R_MASK, + params->offs[0]); + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_GAMMA_OFFSETS_RG + offset, + MALI_C55_GAMMA_OFFSET_G_MASK, + MALI_C55_GAMMA_OFFSET_G(params->offs[1])); + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_GAMMA_OFFSETS_B + offset, + MALI_C55_GAMMA_OFFSET_B_MASK, + params->offs[2]); + + for (unsigned int i = 0; i < MALI_C55_NUM_GAMMA_LUT_ELEMENTS; i++) { + __u32 addr = lut_base + (i * 4); + + mali_c55_ctx_write(mali_c55, addr, params->lut[i]); + } + + mali_c55_ctx_update_bits(mali_c55, + MALI_C55_REG_GAMMA_RGB_ENABLE + offset, + MALI_C55_GAMMA_ENABLE_MASK, 0x1); +} + +static void mali_c55_params_gamma_fr(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + return mali_c55_params_gamma(mali_c55, block, + MALI_C55_CAP_DEV_FR_REG_OFFSET, + MALI_C55_REG_FR_GAMMA_RGB_MEM); +} + +static void mali_c55_params_gamma_ds(struct mali_c55 *mali_c55, + union mali_c55_params_block block) +{ + /* We cannot apply parameters to DS if it is not fitted. */ + if (!(mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED)) + return; + + return mali_c55_params_gamma(mali_c55, block, + MALI_C55_CAP_DEV_DS_REG_OFFSET, + MALI_C55_REG_DS_GAMMA_RGB_MEM); +} + static const mali_c55_params_handler mali_c55_params_handlers[] = { [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs, [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist, @@ -476,6 +543,8 @@ static const mali_c55_params_handler mali_c55_params_handlers[] = { [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config, [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection, [MALI_C55_PARAM_BLOCK_CCM] = &mali_c55_params_ccm, + [MALI_C55_PARAM_BLOCK_GAMMA_FR] = &mali_c55_params_gamma_fr, + [MALI_C55_PARAM_BLOCK_GAMMA_DS] = &mali_c55_params_gamma_ds, }; static const struct v4l2_isp_params_block_type_info @@ -516,6 +585,12 @@ mali_c55_params_block_types_info[] = { [MALI_C55_PARAM_BLOCK_CCM] = { .size = sizeof(struct mali_c55_params_ccm), }, + [MALI_C55_PARAM_BLOCK_GAMMA_FR] = { + .size = sizeof(struct mali_c55_params_gamma), + }, + [MALI_C55_PARAM_BLOCK_GAMMA_DS] = { + .size = sizeof(struct mali_c55_params_gamma), + }, }; static_assert(ARRAY_SIZE(mali_c55_params_handlers) == diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h index 4cd13b702d9f..8a7c3bfe7051 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h +++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h @@ -422,15 +422,17 @@ enum mali_c55_interrupts { #define MALI_C55_REG_GAMMA_RGB_ENABLE 0x1c064 #define MALI_C55_GAMMA_ENABLE_MASK BIT(0) -#define MALI_C55_REG_GAMMA_GAINS_1 0x1c068 +#define MALI_C55_REG_GAMMA_GAINS_RG 0x1c068 #define MALI_C55_GAMMA_GAIN_R_MASK GENMASK(11, 0) #define MALI_C55_GAMMA_GAIN_G_MASK GENMASK(27, 16) -#define MALI_C55_REG_GAMMA_GAINS_2 0x1c06c +#define MALI_C55_GAMMA_GAIN_G(x) ((x) << 16) +#define MALI_C55_REG_GAMMA_GAINS_B 0x1c06c #define MALI_C55_GAMMA_GAIN_B_MASK GENMASK(11, 0) -#define MALI_C55_REG_GAMMA_OFFSETS_1 0x1c070 +#define MALI_C55_REG_GAMMA_OFFSETS_RG 0x1c070 #define MALI_C55_GAMMA_OFFSET_R_MASK GENMASK(11, 0) #define MALI_C55_GAMMA_OFFSET_G_MASK GENMASK(27, 16) -#define MALI_C55_REG_GAMMA_OFFSETS_2 0x1c074 +#define MALI_C55_GAMMA_OFFSET_G(x) ((x) << 16) +#define MALI_C55_REG_GAMMA_OFFSETS_B 0x1c074 #define MALI_C55_GAMMA_OFFSET_B_MASK GENMASK(11, 0) /* @@ -441,6 +443,9 @@ enum mali_c55_interrupts { #define MALI_C55_REG_FR_GAMMA_RGB_ENABLE 0x1c064 #define MALI_C55_REG_DS_GAMMA_RGB_ENABLE 0x1c1d8 +#define MALI_C55_REG_FR_GAMMA_RGB_MEM 0x18280 +#define MALI_C55_REG_DS_GAMMA_RGB_MEM 0x18484 + #define MALI_C55_REG_FR_SCALER_HFILT 0x34a8 #define MALI_C55_REG_FR_SCALER_VFILT 0x44a8 #define MALI_C55_REG_DS_SCALER_HFILT 0x14a8 diff --git a/include/uapi/linux/media/arm/mali-c55-config.h b/include/uapi/linux/media/arm/mali-c55-config.h index 0b18863f973d..ef0fe17df96e 100644 --- a/include/uapi/linux/media/arm/mali-c55-config.h +++ b/include/uapi/linux/media/arm/mali-c55-config.h @@ -36,6 +36,9 @@ */ #define MALI_C55_MAX_ZONES (15 * 15) +/* Number of RGB gamma LUT entries. */ +#define MALI_C55_NUM_GAMMA_LUT_ELEMENTS 129 + /** * struct mali_c55_ae_1024bin_hist - Auto Exposure 1024-bin histogram statistics * @@ -220,6 +223,8 @@ struct mali_c55_stats_buffer { * @MALI_C55_PARAM_MESH_SHADING_CONFIG : Mesh shading tables configuration * @MALI_C55_PARAM_MESH_SHADING_SELECTION: Mesh shading table selection * @MALI_C55_PARAM_BLOCK_CCM: Colour correction matrix + * @MALI_C55_PARAM_BLOCK_GAMMA_FR: Gamma gain and offset for FR pipe + * @MALI_C55_PARAM_BLOCK_GAMMA_DS: Gamma gain and offset for DS pipe */ enum mali_c55_param_block_type { MALI_C55_PARAM_BLOCK_SENSOR_OFFS, @@ -234,6 +239,8 @@ enum mali_c55_param_block_type { MALI_C55_PARAM_MESH_SHADING_CONFIG, MALI_C55_PARAM_MESH_SHADING_SELECTION, MALI_C55_PARAM_BLOCK_CCM, + MALI_C55_PARAM_BLOCK_GAMMA_FR, + MALI_C55_PARAM_BLOCK_GAMMA_DS, }; /** @@ -795,6 +802,42 @@ struct mali_c55_params_ccm { __u16 offs[3]; }; +/** + * struct mali_c55_params_gamma - RGB Gamma correction + * + * Gamma correction is used to program a standard gamma curve such as the sRGB + * one. It provides gains and offsets to implement contrast adjustments. + * + * Gamma correction is applied on both the FR and DS pipes separately in the RGB + * colour domain where the following operations take place: + * 1) An offset is subtracted from each colour channel + * 2) Each colour channel is multiplied by a gain + * 3) The Gamma LUT is applied to each colour channel + * + * The Gamma LUT has 129 entries where each node is an unsigned 12 bit number. + * It is expected that LUT[0]=0 and LUT[128]=0xfff, with the other 127 values + * defining the Gamma correction curve. The three gain multipliers are expressed + * as 12-bits unsigned Q4.8 fixed-point numbers and the three offsets are + * expressed as a 12-bits unsigned integers. + * + * As one Gamma correction block is available on both the FR and DS pipes, the + * header.type field should be set to one of either + * MALI_C55_PARAM_BLOCK_GAMMA_FR or MALI_C55_PARAM_BLOCK_GAMMA_DS from + * :c:type:`mali_c55_param_block_type`. + * + * @header: The Mali-C55 parameters block header + * @gains: Gains for the red, green and blue channel in unsigned Q4.8 format + * @offs: Offsets subtracted from the red, green and blue channels + * in unsigned 12 bits format + * @lut: 129-node Gamma LUT in unsigned 12 bits format + */ +struct mali_c55_params_gamma { + struct v4l2_isp_params_block_header header; + __u16 gains[3]; + __u16 offs[3]; + __u32 lut[MALI_C55_NUM_GAMMA_LUT_ELEMENTS]; +}; + /** * define MALI_C55_PARAMS_MAX_SIZE - Maximum size of all Mali C55 Parameters * @@ -819,6 +862,8 @@ struct mali_c55_params_ccm { sizeof(struct mali_c55_params_awb_gains) + \ sizeof(struct mali_c55_params_mesh_shading_config) + \ sizeof(struct mali_c55_params_mesh_shading_selection) + \ - sizeof(struct mali_c55_params_ccm)) + sizeof(struct mali_c55_params_ccm) + \ + sizeof(struct mali_c55_params_gamma) + \ + sizeof(struct mali_c55_params_gamma)) #endif /* __UAPI_MALI_C55_CONFIG_H */ -- cgit From 1bc5946763067d5b8dde2e5c11d7a7ddb89c8937 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 21 Jul 2026 15:38:37 +0200 Subject: media: stm32: dcmi: fix error handling on MDMA pool alloc failure Properly return an error if of_gen_pool_get or gen_pool_dma_zalloc fails during the chained DMA probing. Fixes: 87ebce19aa03 ("media: stm32: dcmi: addition of DMA-MDMA chaining support") Signed-off-by: Alain Volmat Signed-off-by: Hans Verkuil --- drivers/media/platform/st/stm32/stm32-dcmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index b1f9af8edf6e..ad716cf0633e 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2050,6 +2050,7 @@ static int dcmi_probe(struct platform_device *pdev) dcmi->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0); if (!dcmi->sram_pool) { dev_info(&pdev->dev, "No SRAM pool, can't use MDMA chaining\n"); + ret = -ENOMEM; goto err_dma_slave_config; } @@ -2061,6 +2062,7 @@ static int dcmi_probe(struct platform_device *pdev) &dcmi->sram_dma_buf); if (!dcmi->sram_buf) { dev_err(dcmi->dev, "Failed to allocate from SRAM\n"); + ret = -ENOMEM; goto err_dma_slave_config; } -- cgit From 0735e0b5a96761a9ce277a238e834008ad92a0a5 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Wed, 8 Jul 2026 22:33:49 +0800 Subject: media: zoran: Avoid freeing a registered video_device twice zoran_init_video_device() installs zoran_vdev_release() as the video_device release callback through zoran_template. After video_register_device() succeeds, video_unregister_device() drops the registered video_device reference and the V4L2 core eventually invokes that release callback, which kfree()s the video_device. zoran_exit_video_devices() called video_unregister_device() and then kfree(zr->video_dev), so device teardown could free the same video_device twice. Remove the direct kfree() and clear the cached pointer after unregistering. The pre-registration failure path keeps its manual free because the video_device was not registered there. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 82e3a496eb56 ("media: staging: media: zoran: move videodev alloc") Cc: stable@vger.kernel.org Signed-off-by: Ruoyu Wang Signed-off-by: Hans Verkuil --- drivers/media/pci/zoran/zoran_card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/pci/zoran/zoran_card.c b/drivers/media/pci/zoran/zoran_card.c index 8bcf355a5418..e5ff407318df 100644 --- a/drivers/media/pci/zoran/zoran_card.c +++ b/drivers/media/pci/zoran/zoran_card.c @@ -886,7 +886,7 @@ static int zoran_init_video_device(struct zoran *zr, struct video_device *video_ static void zoran_exit_video_devices(struct zoran *zr) { video_unregister_device(zr->video_dev); - kfree(zr->video_dev); + zr->video_dev = NULL; } static int zoran_init_video_devices(struct zoran *zr) -- cgit From bbd4218310cc9fc8bba677e8c35abd03524ad474 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Tue, 14 Jul 2026 23:16:48 +0800 Subject: media: usbtv: Fix V4L2 refcount leak on probe failure usbtv_probe() allocates usbtv before usbtv_video_init() registers its embedded v4l2_device. v4l2_device_register() initializes the reference count to one, with usbtv_release() providing the final cleanup. If video_register_device() fails, usbtv_video_init() unregisters the V4L2 device and returns an error without dropping the initial v4l2_device reference. The probe error path then calls kfree() on usbtv directly, leaving the reference stranded and bypassing usbtv_release(). Leave the initialized V4L2 device intact on this failure path. After releasing the USB reference, call v4l2_device_put() so the final reference invokes usbtv_release(). Retain the direct kfree() path for failures that occur before v4l2_device_register(). This issue was found by a static analysis tool I am developing. Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/usb/usbtv/usbtv-core.c | 5 ++++- drivers/media/usb/usbtv/usbtv-video.c | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c index 6c4facf4f41a..4f10f6613bc4 100644 --- a/drivers/media/usb/usbtv/usbtv-core.c +++ b/drivers/media/usb/usbtv/usbtv-core.c @@ -119,7 +119,10 @@ usbtv_audio_fail: usbtv_video_fail: usb_set_intfdata(intf, NULL); - kfree(usbtv); + if (usbtv->v4l2_dev.dev) + v4l2_device_put(&usbtv->v4l2_dev); + else + kfree(usbtv); return ret; } diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c index de0328100a60..92bc7a2509c3 100644 --- a/drivers/media/usb/usbtv/usbtv-video.c +++ b/drivers/media/usb/usbtv/usbtv-video.c @@ -949,13 +949,11 @@ int usbtv_video_init(struct usbtv *usbtv) ret = video_register_device(&usbtv->vdev, VFL_TYPE_VIDEO, -1); if (ret < 0) { dev_warn(usbtv->dev, "Could not register video device\n"); - goto vdev_fail; + return ret; } return 0; -vdev_fail: - v4l2_device_unregister(&usbtv->v4l2_dev); v4l2_fail: ctrl_fail: v4l2_ctrl_handler_free(&usbtv->ctrl); -- cgit From aeaacc3001449d44b4ab7da56331121d1f3b137b Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 16 Jul 2026 11:32:30 +0000 Subject: media: ti: vpe: quiesce overflow recovery before freeing streams The VIP overflow recovery worker is armed from the hardirq handler when a FIFO overflow is detected, and the list-complete path looks the stream up through the VPDMA list private pointer. Both keep touching stream, port and device state; the recovery worker also resets the parser and VPDMA, repopulates the descriptor list, and re-enables the per-list IRQs. vip_stop_streaming() masks and clears the per-list IRQs, but it neither synchronizes the hardirq handler nor disables recovery_work. An overflow IRQ that has already queued recovery_work, or a list-complete IRQ in flight when the stream is torn down, can therefore still dereference the stream after its resources are released: the descriptor list is freed by vip_release_stream() on file release, and the stream itself by free_stream() on unbind/remove. Drain the recovery worker and the IRQ handler at both teardown points through a shared vip_quiesce_stream() helper, before any stream-owned resource is released. disable_work_sync() cancels pending recovery_work, drains a running instance, and raises its disable depth, so a subsequent schedule_work() issued by a racing IRQ handler is rejected at the workqueue scheduler: recovery_work cannot be requeued after disable_work_sync() takes effect. The worker may still re-enable the per-list IRQs before disable_work_sync() returns; disable_irqs() then masks those sources and synchronize_irq() waits for any in-flight handler that still dereferences stream state. In vip_stop_streaming() the helper runs before the parser is stopped, since a worker drained by disable_work_sync() may re-enable the parser before exiting and would otherwise undo the stop. recovery_work is created disabled and enabled in vip_start_streaming() before IRQs, pairing the enable with the teardown disable across the streaming lifecycle. This issue was found by an in-house static analysis tool and confirmed by manual code review. Fixes: fc2873aa4a21 ("media: ti: vpe: Add the VIP driver") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Reviewed-by: Yemike Abhilash Chandra Tested-by: Yemike Abhilash Chandra Signed-off-by: Hans Verkuil --- drivers/media/platform/ti/vpe/vip.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c index e56a95f53ea9..ccb688f4d7af 100644 --- a/drivers/media/platform/ti/vpe/vip.c +++ b/drivers/media/platform/ti/vpe/vip.c @@ -815,6 +815,22 @@ static void clear_irqs(struct vip_dev *dev, int irq_num, int list_num) vpdma_clear_list_stat(dev->shared->vpdma, irq_num, dev->slice_id); } +/* + * Quiesce recovery work and per-list IRQs before releasing stream resources. + * disable_work_sync() prevents the overflow handler from requeueing recovery + * work. Mask and synchronize IRQs afterwards because a running worker may + * have re-enabled them before exiting. + */ +static void vip_quiesce_stream(struct vip_stream *stream) +{ + struct vip_dev *dev = stream->port->dev; + + disable_work_sync(&stream->recovery_work); + disable_irqs(dev, dev->slice_id, stream->list_num); + clear_irqs(dev, dev->slice_id, stream->list_num); + synchronize_irq(dev->irq); +} + static void populate_desc_list(struct vip_stream *stream) { struct vip_port *port = stream->port; @@ -2429,6 +2445,7 @@ static int vip_start_streaming(struct vb2_queue *vq, unsigned int count) goto err; stream->num_recovery = 0; + enable_work(&stream->recovery_work); clear_irqs(dev, dev->slice_id, stream->list_num); enable_irqs(dev, dev->slice_id, stream->list_num); @@ -2453,13 +2470,17 @@ static void vip_stop_streaming(struct vb2_queue *vq) struct vip_dev *dev = port->dev; int ret; + /* + * A running recovery worker may re-enable the parser, so quiesce it + * and its IRQ handler before stopping the parser or releasing the + * descriptor list. + */ + vip_quiesce_stream(stream); + vip_parser_stop_imm(port, true); vip_enable_parser(port, false); unset_fmt_params(stream); - disable_irqs(dev, dev->slice_id, stream->list_num); - clear_irqs(dev, dev->slice_id, stream->list_num); - if (port->subdev) { ret = v4l2_subdev_call(port->subdev, video, s_stream, 0); if (ret) @@ -3075,6 +3096,8 @@ static int alloc_stream(struct vip_port *port, int stream_id, int vfl_type) goto do_free_hwlist; INIT_WORK(&stream->recovery_work, vip_overflow_recovery_work); + /* Start disabled; vip_start_streaming() enables it before IRQs. */ + disable_work(&stream->recovery_work); INIT_LIST_HEAD(&stream->vidq); @@ -3140,6 +3163,13 @@ static void free_stream(struct vip_stream *stream) return; dev = stream->port->dev; + /* + * Quiesce the IRQ handler and recovery worker, then drop the stream + * from cap_streams[], before releasing stream-owned resources. + */ + vip_quiesce_stream(stream); + stream->port->cap_streams[stream->stream_id] = NULL; + /* Free up the Drop queue */ list_for_each_safe(pos, q, &stream->dropq) { buf = list_entry(pos, @@ -3151,7 +3181,6 @@ static void free_stream(struct vip_stream *stream) video_unregister_device(stream->vfd); vpdma_hwlist_release(dev->shared->vpdma, stream->list_num); - stream->port->cap_streams[stream->stream_id] = NULL; kfree(stream); } -- cgit From e7600f5cee5de14065f950807931d6e6d40fb2d7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2026 15:42:45 +0200 Subject: Revert "media: v4l2-dev: fix error handling in __video_register_device()" This reverts commit 2a934fdb01db6458288fc9386d3d8ceba6dd551a. The intentions of that patch were good, but it doesn't work. The idea is that if device_register fails, you have to do a put_device to let the ref counter release resources. However, the V4L2 API says that if video_register_device() fails, then you have to call video_device_release(), which kfree()s the video_device struct. But the put_device() will already have freed the struct, so you end up in a double-free scenario. There is not really a good way of fixing this without breaking video_register_device() into two parts, one that initializes everything, and one that does the actual device_register, and then converting all V4L2 drivers to this new model. That is a massive job, and it is very unlikely that device_register will fail. So rather than ending up in a double-free scenario, just revert this patch, and in that case we'll have a small memory leak. Which is a lot more robust. Reviewed-by: Laurent Pinchart Fixes: 2a934fdb01db ("media: v4l2-dev: fix error handling in __video_register_device()") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-media/20260520090624.1071139-1-lgs201920130244@gmail.com/ Link: https://lore.kernel.org/all/2026042058-charm-storable-4ad8@gregkh/ Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-dev.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 5516b2bbb08f..d750bf10febe 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1071,25 +1071,25 @@ int __video_register_device(struct video_device *vdev, vdev->dev.class = &video_class; vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); vdev->dev.parent = vdev->dev_parent; - vdev->dev.release = v4l2_device_release; dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); - - /* Increase v4l2_device refcount */ - v4l2_device_get(vdev->v4l2_dev); - mutex_lock(&videodev_lock); ret = device_register(&vdev->dev); if (ret < 0) { mutex_unlock(&videodev_lock); pr_err("%s: device_register failed\n", __func__); - put_device(&vdev->dev); - return ret; + goto cleanup; } + /* Register the release callback that will be called when the last + reference to the device goes away. */ + vdev->dev.release = v4l2_device_release; if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) pr_warn("%s: requested %s%d, got %s\n", __func__, name_base, nr, video_device_node_name(vdev)); + /* Increase v4l2_device refcount */ + v4l2_device_get(vdev->v4l2_dev); + /* Part 5: Register the entity. */ ret = video_register_media_controller(vdev); -- cgit From 1d21691540a1872710c2deb9bc8e555bd189a181 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jul 2026 15:42:46 +0200 Subject: media: v4l2-core: v4l2-dev: add comments on device_register fail. If device_register fails, then we are supposed to call put_device. Explain why we do not do that. Reviewed-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/v4l2-core/v4l2-dev.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index d750bf10febe..fd267fb74905 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -1075,6 +1075,20 @@ int __video_register_device(struct video_device *vdev, mutex_lock(&videodev_lock); ret = device_register(&vdev->dev); if (ret < 0) { + /* + * We should do a put_device() here, but the problem is that + * the V4L2 API expects drivers to call video_device_release() + * on error, and so both put_device() and video_device_release + * would kfree vdev. + * + * The proper solution would be to split this function into + * two parts: initialization and registration, and then rework + * all drivers. + * + * Until then just skip the put_device and free everything. + * This will result in a small memory leak, which is better + * than a double-free. + */ mutex_unlock(&videodev_lock); pr_err("%s: device_register failed\n", __func__); goto cleanup; -- cgit From 28e84c6e2e6753ed238ea097b2842a32a6a6879b Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 18 Jul 2026 14:10:11 +0800 Subject: media: saa7164: fix cleanup on resource allocation failure saa7164_dev_setup() adds the device to the global saa7164_devlist before requesting the PCI BAR memory regions. If get_resources() fails, saa7164_dev_setup() decrements the device count and returns an error, but leaves the device on saa7164_devlist. The probe error path then frees the device, leaving a dangling entry on the global list. Reuse the existing MMIO mapping error path to remove the device from saa7164_devlist and decrement the device count before returning. Also release BAR0 if it was successfully requested but the BAR2 request fails. Fixes: 443c1228d505 ("V4L/DVB (12923): SAA7164: Add support for the NXP SAA7164 silicon") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Signed-off-by: Hans Verkuil --- drivers/media/pci/saa7164/saa7164-core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c index 683bd78288b3..ac5eb6b923e2 100644 --- a/drivers/media/pci/saa7164/saa7164-core.c +++ b/drivers/media/pci/saa7164/saa7164-core.c @@ -878,6 +878,9 @@ static int get_resources(struct saa7164_dev *dev) if (request_mem_region(pci_resource_start(dev->pci, 2), pci_resource_len(dev->pci, 2), dev->name)) return 0; + + release_mem_region(pci_resource_start(dev->pci, 0), + pci_resource_len(dev->pci, 0)); } printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", @@ -1000,8 +1003,7 @@ static int saa7164_dev_setup(struct saa7164_dev *dev) dev->name, dev->pci->subsystem_vendor, dev->pci->subsystem_device); - saa7164_devcount--; - return -ENODEV; + goto err_devlist; } /* PCI/e allocations */ @@ -1039,7 +1041,7 @@ err_ioremap_bar2: iounmap(dev->lmmio); err_ioremap_bar0: release_resources(dev); - +err_devlist: scoped_guard(mutex, &devlist) { list_del(&dev->devlist); } -- cgit From 6dd8e257f7cafda7fbf10d81b3c55c9bba4825f4 Mon Sep 17 00:00:00 2001 From: Ilya Krutskih Date: Sun, 19 Jul 2026 13:08:35 +0000 Subject: media: tda18250: fix possible integer overflow Integer overflow may occur, when variable exp equals to zero. Result of shift 1 << (exp - 1) may then leads to undefined behavior. Fixes: 148abd3b5b14 ("media: tda18250: support for new silicon tuner") Cc: stable@vger.kernel.org Signed-off-by: Ilya Krutskih Signed-off-by: Hans Verkuil --- drivers/media/tuners/tda18250.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c index 7bb945ba0989..bd7ed50fb6e8 100644 --- a/drivers/media/tuners/tda18250.c +++ b/drivers/media/tuners/tda18250.c @@ -440,8 +440,8 @@ static int tda18250_pll_calc(struct dvb_frontend *fe, u8 *rdiv, goto err; exp = (uval & 0x70) >> 4; - if (exp > 5) - exp = 0; + if (exp == 0 || exp > 5) + exp = 1; lopd = 1 << (exp - 1); scale = uval & 0x0f; fvco = lopd * scale * ((c->frequency / 1000) + dev->if_frequency); -- cgit From 466a78e1c9aa69e7a0611659113601d5b3e61162 Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Mon, 20 Jul 2026 02:43:23 +0530 Subject: dt-bindings: media: ti,am437x-vpfe: Convert to DT schema Convert Texas Instruments AM437x CAMERA Video Processing Front End (VPFE) from legacy text to DT schema. Signed-off-by: Bhargav Joshi Reviewed-by: Rob Herring (Arm) Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/ti,am437x-vpfe.yaml | 93 ++++++++++++++++++++++ .../devicetree/bindings/media/ti-am437x-vpfe.txt | 61 -------------- 2 files changed, 93 insertions(+), 61 deletions(-) create mode 100644 Documentation/devicetree/bindings/media/ti,am437x-vpfe.yaml delete mode 100644 Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt diff --git a/Documentation/devicetree/bindings/media/ti,am437x-vpfe.yaml b/Documentation/devicetree/bindings/media/ti,am437x-vpfe.yaml new file mode 100644 index 000000000000..983e705ab1e8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/ti,am437x-vpfe.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/ti,am437x-vpfe.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments AM437x CAMERA Video Processing Front End (VPFE) + +maintainers: + - Benoit Parrot + - Bhargav Joshi + +description: + The Video Processing Front End (VPFE) is a key component for image capture + applications. The capture module provides the system interface and the + processing capability to connect RAW image-sensor modules and video decoders + to the AM437x device. + +properties: + compatible: + const: ti,am437x-vpfe + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + port: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + A single parallel input port connecting to an external image sensor or + video decoder. The VPFE supports only one input port per instance. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + ti,am437x-vpfe-interface: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4] + description: + Selects the VPFE input interface type can be one of following + 0 - Raw Bayer (VPFE_RAW_BAYER) + 1 - BT.656 8-bit (VPFE_BT656) + 2 - BT.656 10-bit (VPFE_BT656_10BIT) + 3 - YCbCr 8-bit sync (VPFE_YCBCR_SYNC_8) + 4 - YCbCr 16-bit sync (VPFE_YCBCR_SYNC_16) + + bus-width: + minimum: 8 + maximum: 16 + + required: + - remote-endpoint + - ti,am437x-vpfe-interface + - bus-width + +required: + - compatible + - reg + - interrupts + - port + +additionalProperties: false + +examples: + - | + #include + #include + + vpfe@48328000{ + compatible = "ti,am437x-vpfe"; + reg = <0x48328000 0x2000>; + interrupts = ; + + pinctrl-names = "default", "sleep"; + pinctrl-0 = <&vpfe_pins_default>; + pinctrl-1 = <&vpfe_pins_sleep>; + + port { + vpfe0_ep: endpoint { + remote-endpoint = <&ov2659_1>; + ti,am437x-vpfe-interface = <0>; + bus-width = <8>; + hsync-active = <0>; + vsync-active = <0>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt b/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt deleted file mode 100644 index 3932e766553a..000000000000 --- a/Documentation/devicetree/bindings/media/ti-am437x-vpfe.txt +++ /dev/null @@ -1,61 +0,0 @@ -Texas Instruments AM437x CAMERA (VPFE) --------------------------------------- - -The Video Processing Front End (VPFE) is a key component for image capture -applications. The capture module provides the system interface and the -processing capability to connect RAW image-sensor modules and video decoders -to the AM437x device. - -Required properties: -- compatible: must be "ti,am437x-vpfe" -- reg: physical base address and length of the registers set for the device; -- interrupts: should contain IRQ line for the VPFE; -- ti,am437x-vpfe-interface: can be one of the following, - 0 - Raw Bayer Interface. - 1 - 8 Bit BT656 Interface. - 2 - 10 Bit BT656 Interface. - 3 - YCbCr 8 Bit Interface. - 4 - YCbCr 16 Bit Interface. - -VPFE supports a single port node with parallel bus. It should contain one -'port' child node with child 'endpoint' node. Please refer to the bindings -defined in Documentation/devicetree/bindings/media/video-interfaces.txt. - -Example: - vpfe: vpfe@f0034000 { - compatible = "ti,am437x-vpfe"; - reg = <0x48328000 0x2000>; - interrupts = ; - - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&vpfe_pins_default>; - pinctrl-1 = <&vpfe_pins_sleep>; - - port { - #address-cells = <1>; - #size-cells = <0>; - - vpfe0_ep: endpoint { - remote-endpoint = <&ov2659_1>; - ti,am437x-vpfe-interface = <0>; - bus-width = <8>; - hsync-active = <0>; - vsync-active = <0>; - }; - }; - }; - - i2c1: i2c@4802a000 { - - ov2659@30 { - compatible = "ti,ov2659"; - reg = <0x30>; - - port { - ov2659_1: endpoint { - remote-endpoint = <&vpfe0_ep>; - bus-width = <8>; - mclk-frequency = <12000000>; - }; - }; - }; -- cgit From 49af0c7cad889f7dabe5cf080b019392066122a3 Mon Sep 17 00:00:00 2001 From: Ming Qian Date: Mon, 20 Jul 2026 18:17:26 +0900 Subject: media: v4l2-ctrls: Allow unknown HDR10 white point and luminance SMPTE ST 2086 defines the nominal ranges for mastering display chromaticity and luminance values. Its Annex A also documents that CTA 861-G uses zero maximum and minimum luminance values to signal that the corresponding values are unknown, and the xy chromaticity coordinate (0, 0) to signal that the white point chromaticity is unknown. The V4L2 HDR10 mastering display compound control currently rejects these values. Consequently, an unknown white point or luminance value prevents the entire compound control from being updated, making the other valid mastering display metadata unavailable to userspace. Accept (0, 0) as an unknown white point and zero as an unknown maximum or minimum mastering luminance. Continue to reject partially zero white point coordinates and non-zero values outside the nominal ranges. Display primary validation remains unchanged. Document the newly accepted unknown values in the V4L2 userspace API. Fixes: 1ad0de78e794 ("media: v4l: Add HDR10 static metadata controls") Cc: stable@vger.kernel.org Signed-off-by: Ming Qian Signed-off-by: Hans Verkuil --- .../media/v4l/ext-ctrls-colorimetry.rst | 12 ++++- drivers/media/v4l2-core/v4l2-ctrls-core.c | 53 ++++++++++++++-------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst index 38a4136d7220..0a7da2f16850 100644 --- a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst +++ b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst @@ -80,15 +80,25 @@ Colorimetry Control IDs - ``white_point_x`` - Specifies the normalized x chromaticity coordinate of the white point of the mastering display in increments of 0.00002. + When both ``white_point_x`` and ``white_point_y`` are zero, + the white point chromaticity is unknown. If either coordinate is + non-zero, both coordinates shall be within their valid ranges. * - __u16 - ``white_point_y`` - Specifies the normalized y chromaticity coordinate of the white point of the mastering display in increments of 0.00002. + When both ``white_point_x`` and ``white_point_y`` are zero, + the white point chromaticity is unknown. If either coordinate is + non-zero, both coordinates shall be within their valid ranges. * - __u32 - ``max_luminance`` - Specifies the nominal maximum display luminance of the mastering display in units of 0.0001 cd/m\ :sup:`2`. + A value of zero indicates that the nominal maximum display + luminance is unknown. * - __u32 - ``min_luminance`` - - specifies the nominal minimum display luminance of the mastering + - Specifies the nominal minimum display luminance of the mastering display in units of 0.0001 cd/m\ :sup:`2`. + A value of zero indicates that the nominal minimum display + luminance is unknown. diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c index 1214e7744ac0..5b8a594fb9e2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c @@ -1322,24 +1322,41 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx, return -EINVAL; } - if (p_hdr10_mastering->white_point_x < - V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW || - p_hdr10_mastering->white_point_x > - V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH || - p_hdr10_mastering->white_point_y < - V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW || - p_hdr10_mastering->white_point_y > - V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH) - return -EINVAL; - - if (p_hdr10_mastering->max_display_mastering_luminance < - V4L2_HDR10_MASTERING_MAX_LUMA_LOW || - p_hdr10_mastering->max_display_mastering_luminance > - V4L2_HDR10_MASTERING_MAX_LUMA_HIGH || - p_hdr10_mastering->min_display_mastering_luminance < - V4L2_HDR10_MASTERING_MIN_LUMA_LOW || - p_hdr10_mastering->min_display_mastering_luminance > - V4L2_HDR10_MASTERING_MIN_LUMA_HIGH) + /* + * SMPTE ST 2086 Annex A documents that CTA 861-G uses + * (0, 0) to indicate that the white point chromaticity + * is unknown. + */ + if (p_hdr10_mastering->white_point_x || + p_hdr10_mastering->white_point_y) { + if (p_hdr10_mastering->white_point_x < + V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW || + p_hdr10_mastering->white_point_x > + V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH || + p_hdr10_mastering->white_point_y < + V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW || + p_hdr10_mastering->white_point_y > + V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH) + return -EINVAL; + } + + /* + * SMPTE ST 2086 Annex A documents that CTA 861-G uses zero + * maximum and minimum luminance values to indicate that + * the corresponding values are unknown. + */ + if (p_hdr10_mastering->max_display_mastering_luminance && + (p_hdr10_mastering->max_display_mastering_luminance < + V4L2_HDR10_MASTERING_MAX_LUMA_LOW || + p_hdr10_mastering->max_display_mastering_luminance > + V4L2_HDR10_MASTERING_MAX_LUMA_HIGH)) + return -EINVAL; + + if (p_hdr10_mastering->min_display_mastering_luminance && + (p_hdr10_mastering->min_display_mastering_luminance < + V4L2_HDR10_MASTERING_MIN_LUMA_LOW || + p_hdr10_mastering->min_display_mastering_luminance > + V4L2_HDR10_MASTERING_MIN_LUMA_HIGH)) return -EINVAL; /* The following restriction comes from ITU-T Rec. H.265 spec */ -- cgit From 4e143d662ca94888b494b2427fc9e34494eb933a Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Mon, 20 Jul 2026 13:17:08 +0000 Subject: media: cx23885: cancel NetUP CI work before teardown netup_ci_exit() frees a netup_ci_state while its work item, netup_read_ci_status(), may still be pending or running on the system workqueue. The worker obtains the state with container_of() and dereferences it, so it must not outlive the state. netup_ci_init() queues the initial status read, and CI GPIO interrupts subsequently queue the same work from netup_ci_slot_status(). During remove, cx23885_finidev() calls free_irq() before the CI device is unregistered. free_irq() prevents further IRQ handlers from running, but does not drain work queued previously, so the worker can run after netup_ci_exit() frees the state. Call cancel_work_sync() before dvb_ca_en50221_release() and kfree(). This issue was found by an in-house static analysis tool. Fixes: c184dcd28233 ("V4L/DVB (10798): Add CIMax(R) SP2 Common Interface code for NetUP Dual DVB-S2 CI card") Cc: stable@vger.kernel.org Assisted-by: Codex:gpt-5.6 Signed-off-by: Fan Wu Signed-off-by: Hans Verkuil --- drivers/media/pci/cx23885/cimax2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/cx23885/cimax2.c b/drivers/media/pci/cx23885/cimax2.c index 154918984534..723509fa1e06 100644 --- a/drivers/media/pci/cx23885/cimax2.c +++ b/drivers/media/pci/cx23885/cimax2.c @@ -528,6 +528,7 @@ void netup_ci_exit(struct cx23885_tsport *port) if (NULL == state->ca.data) return; + cancel_work_sync(&state->work); dvb_ca_en50221_release(&state->ca); kfree(state); } -- cgit From ad4c65fa30cfb00e2e06adae9a8eb407086eaa66 Mon Sep 17 00:00:00 2001 From: Arash Golgol Date: Wed, 22 Jul 2026 20:14:51 +0330 Subject: media: vimc: fix pixel format lookup in enum_framesizes vimc_capture_enum_framesizes() looks up the requested format using vimc_pix_map_by_code(), which searches the pix map table by media bus code (MEDIA_BUS_FMT_*). However, v4l2_frmsizeenum::pixel_format holds a V4L2 pixel format (V4L2_PIX_FMT_*), not a media bus code, so valid pixel formats end up being rejected with -EINVAL. Fix this by using vimc_pix_map_by_pixelformat() instead, which performs the lookup by pixel format as the ioctl expects. Fixes: 09c41a23a2e2 ("media: Revert "media: vimc: propagate pixel format in the stream"") Cc: stable@vger.kernel.org Signed-off-by: Arash Golgol Signed-off-by: Hans Verkuil --- drivers/media/test-drivers/vimc/vimc-capture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/test-drivers/vimc/vimc-capture.c b/drivers/media/test-drivers/vimc/vimc-capture.c index e0c6bc9f8f20..ac60c9b5418e 100644 --- a/drivers/media/test-drivers/vimc/vimc-capture.c +++ b/drivers/media/test-drivers/vimc/vimc-capture.c @@ -175,8 +175,8 @@ static int vimc_capture_enum_framesizes(struct file *file, void *priv, if (fsize->index) return -EINVAL; - /* Only accept code in the pix map table */ - vpix = vimc_pix_map_by_code(fsize->pixel_format); + /* Only accept pixel_format in the pix map table */ + vpix = vimc_pix_map_by_pixelformat(fsize->pixel_format); if (!vpix) return -EINVAL; -- cgit From 4dac3652c60278a3586444ebfa38e2201f1d0f67 Mon Sep 17 00:00:00 2001 From: Faisal Mukhtar Date: Wed, 22 Jul 2026 22:47:22 +0500 Subject: media: tegra-video: Fix length warnings in tegra20.c Wrap long function arguments under the starting parentheses because of line length style warning reported by checkpatch.pl Signed-off-by: Faisal Mukhtar Signed-off-by: Hans Verkuil --- drivers/staging/media/tegra-video/tegra20.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/staging/media/tegra-video/tegra20.c b/drivers/staging/media/tegra-video/tegra20.c index e513e6ccb776..eb1fc5b7e2cd 100644 --- a/drivers/staging/media/tegra-video/tegra20.c +++ b/drivers/staging/media/tegra-video/tegra20.c @@ -552,7 +552,8 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_1), base); - tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), base + chan->start_offset); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_1), + base + chan->start_offset); break; /* RAW8 */ case V4L2_PIX_FMT_SRGGB8: @@ -565,7 +566,8 @@ static void tegra20_channel_vi_buffer_setup(struct tegra_vi_channel *chan, case V4L2_PIX_FMT_SGBRG10: case V4L2_PIX_FMT_SBGGR10: tegra20_vi_write(chan, TEGRA_VI_VB0_BASE_ADDRESS(TEGRA_VI_OUT_2), base); - tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_2), base + chan->start_offset); + tegra20_vi_write(chan, TEGRA_VI_VB0_START_ADDRESS(TEGRA_VI_OUT_2), + base + chan->start_offset); break; } } -- cgit From 32b322e749d0664be41c3777ee0ecb6325240387 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 17:54:57 +0800 Subject: media: cec: ao-cec: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/meson/ao-cec-g12a.c | 4 +--- drivers/media/cec/platform/meson/ao-cec.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c index 2c914f0008b5..b175be3f2bf4 100644 --- a/drivers/media/cec/platform/meson/ao-cec-g12a.c +++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c @@ -689,10 +689,8 @@ static int meson_ao_cec_g12a_probe(struct platform_device *pdev) meson_ao_cec_g12a_irq, meson_ao_cec_g12a_irq_thread, 0, NULL, ao_cec); - if (ret) { - dev_err(&pdev->dev, "irq request failed\n"); + if (ret) goto out_probe_adapter; - } ao_cec->oscin = devm_clk_get(&pdev->dev, "oscin"); if (IS_ERR(ao_cec->oscin)) { diff --git a/drivers/media/cec/platform/meson/ao-cec.c b/drivers/media/cec/platform/meson/ao-cec.c index 145efd9af6ac..2df0d3743077 100644 --- a/drivers/media/cec/platform/meson/ao-cec.c +++ b/drivers/media/cec/platform/meson/ao-cec.c @@ -636,10 +636,8 @@ static int meson_ao_cec_probe(struct platform_device *pdev) meson_ao_cec_irq, meson_ao_cec_irq_thread, 0, NULL, ao_cec); - if (ret) { - dev_err(&pdev->dev, "irq request failed\n"); + if (ret) goto out_probe_adapter; - } ao_cec->core = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(ao_cec->core)) { -- cgit From 7861669593205dfcccdf7f2f0a9e18684331415d Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 17:54:58 +0800 Subject: media: cec: seco: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/seco/seco-cec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c index 654c70503dd5..12db6dca602b 100644 --- a/drivers/media/cec/platform/seco/seco-cec.c +++ b/drivers/media/cec/platform/seco/seco-cec.c @@ -619,7 +619,6 @@ static int secocec_probe(struct platform_device *pdev) dev_name(&pdev->dev), secocec); if (ret) { - dev_err(dev, "Cannot request IRQ %d\n", secocec->irq); ret = -EIO; goto err; } -- cgit From a45a81a790b960af42f615964f7bbafd63b30a16 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 17:54:59 +0800 Subject: media: cec: tegra: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Signed-off-by: Hans Verkuil --- drivers/media/cec/platform/tegra/tegra_cec.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c index e22a4625f192..d64e1662249e 100644 --- a/drivers/media/cec/platform/tegra/tegra_cec.c +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -411,11 +411,8 @@ static int tegra_cec_probe(struct platform_device *pdev) tegra_cec_irq_handler, tegra_cec_irq_thread_handler, 0, "cec_irq", &pdev->dev); - if (ret) { - dev_err(&pdev->dev, - "Unable to request interrupt for device\n"); + if (ret) goto err_clk; - } cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME, CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | -- cgit From 9508676ad8562368bb1aa7d1991fbd50611ddacb Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:14 +0900 Subject: media: remove conditional return with no effect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Reviewed-by: Niklas Söderlund Signed-off-by: Hans Verkuil --- drivers/media/i2c/mt9p031.c | 6 +----- drivers/media/platform/microchip/microchip-sama7g5-isc.c | 7 +------ drivers/media/platform/qcom/iris/iris_resources.c | 6 +----- drivers/media/platform/qcom/venus/pm_helpers.c | 7 +------ drivers/media/platform/renesas/rcar-csi2.c | 6 +----- drivers/media/platform/samsung/s3c-camif/camif-core.c | 7 +------ drivers/media/usb/dvb-usb-v2/mxl111sf.c | 12 ++---------- drivers/media/usb/gspca/jl2005bcd.c | 7 +------ 8 files changed, 9 insertions(+), 49 deletions(-) diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index 8dc57eeba606..cf064c34c38c 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -453,11 +453,7 @@ static int mt9p031_set_params(struct mt9p031 *mt9p031) ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1); if (ret < 0) return ret; - ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1); - if (ret < 0) - return ret; - - return ret; + return mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1); } static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c index b0302dfc3278..7383341ec51d 100644 --- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c +++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c @@ -598,13 +598,8 @@ static int __maybe_unused xisc_runtime_suspend(struct device *dev) static int __maybe_unused xisc_runtime_resume(struct device *dev) { struct isc_device *isc = dev_get_drvdata(dev); - int ret; - - ret = clk_prepare_enable(isc->hclock); - if (ret) - return ret; - return ret; + return clk_prepare_enable(isc->hclock); } static const struct dev_pm_ops microchip_xisc_dev_pm_ops = { diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c index 773f6548370a..872bd09656b1 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.c +++ b/drivers/media/platform/qcom/iris/iris_resources.c @@ -78,11 +78,7 @@ int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev) if (ret) return ret; - ret = pm_runtime_get_sync(pd_dev); - if (ret < 0) - return ret; - - return ret; + return pm_runtime_get_sync(pd_dev); } int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev) diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index be1cbd5cfe84..e88e66be4f6d 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -781,7 +781,6 @@ static int decide_core(struct venus_inst *inst) unsigned long max_freq = ULONG_MAX; struct device *dev = core->dev; struct dev_pm_opp *opp; - int ret = 0; if (legacy_binding) { if (inst->session_type == VIDC_SESSION_TYPE_DEC) @@ -829,11 +828,7 @@ static int decide_core(struct venus_inst *inst) } done: - ret = hfi_session_set_property(inst, ptype, &cu); - if (ret) - return ret; - - return ret; + return hfi_session_set_property(inst, ptype, &cu); } static int acquire_core(struct venus_inst *inst) diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c index 7305cc4a04cb..6635f5782175 100644 --- a/drivers/media/platform/renesas/rcar-csi2.c +++ b/drivers/media/platform/renesas/rcar-csi2.c @@ -2273,11 +2273,7 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv, return ret; } - ret = rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4)); - if (ret) - return ret; - - return ret; + return rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4)); } /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c index 14eedd1ceb27..bb06847f3a63 100644 --- a/drivers/media/platform/samsung/s3c-camif/camif-core.c +++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c @@ -301,7 +301,6 @@ static int camif_media_dev_init(struct camif_dev *camif) struct media_device *md = &camif->media_dev; struct v4l2_device *v4l2_dev = &camif->v4l2_dev; unsigned int ip_rev = camif->variant->ip_revision; - int ret; memset(md, 0, sizeof(*md)); snprintf(md->model, sizeof(md->model), "Samsung S3C%s CAMIF", @@ -316,11 +315,7 @@ static int camif_media_dev_init(struct camif_dev *camif) media_device_init(md); - ret = v4l2_device_register(camif->dev, v4l2_dev); - if (ret < 0) - return ret; - - return ret; + return v4l2_device_register(camif->dev, v4l2_dev); } static void camif_clk_put(struct camif_dev *camif) diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index 870ac3c8b085..6404eb74db32 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -987,11 +987,7 @@ static int mxl111sf_frontend_attach_atsc_mh(struct dvb_usb_adapter *adap) if (ret < 0) return ret; - ret = mxl111sf_lg2160_frontend_attach(adap, 2); - if (ret < 0) - return ret; - - return ret; + return mxl111sf_lg2160_frontend_attach(adap, 2); } static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap) @@ -1007,11 +1003,7 @@ static int mxl111sf_frontend_attach_mercury(struct dvb_usb_adapter *adap) if (ret < 0) return ret; - ret = mxl111sf_lg2161_ep6_frontend_attach(adap, 2); - if (ret < 0) - return ret; - - return ret; + return mxl111sf_lg2161_ep6_frontend_attach(adap, 2); } static int mxl111sf_frontend_attach_mercury_mh(struct dvb_usb_adapter *adap) diff --git a/drivers/media/usb/gspca/jl2005bcd.c b/drivers/media/usb/gspca/jl2005bcd.c index a408fcc3a060..4988fbf5005e 100644 --- a/drivers/media/usb/gspca/jl2005bcd.c +++ b/drivers/media/usb/gspca/jl2005bcd.c @@ -148,17 +148,12 @@ static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev) static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg, unsigned char value) { - int retval; u8 instruction[2]; instruction[0] = reg; instruction[1] = value; - retval = jl2005c_write2(gspca_dev, instruction); - if (retval < 0) - return retval; - - return retval; + return jl2005c_write2(gspca_dev, instruction); } static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev) -- cgit From e228b8b1a5b47a72b265671dc6d864c0da5528d0 Mon Sep 17 00:00:00 2001 From: Chen Changcheng Date: Fri, 24 Jul 2026 10:52:18 +0800 Subject: media: cx88: fix memory leak in cx8802_register_driver() error path In cx8802_register_driver(), when drv->probe(driver) fails (non-zero), the allocated cx8802_driver struct is freed neither in the else branch nor later in cx8802_unregister_driver() (which only frees entries that were added to dev->drvlist on success). Each failed probe leaks the driver struct. Add kfree(driver) in the else branch to fix the leak. Signed-off-by: Chen Changcheng Signed-off-by: Hans Verkuil --- drivers/media/pci/cx88/cx88-mpeg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index a399a9bf3b92..0c07ad335799 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -640,6 +640,7 @@ int cx8802_register_driver(struct cx8802_driver *drv) list_add_tail(&driver->drvlist, &dev->drvlist); } else { pr_err("cx8802 probe failed, err = %d\n", err); + kfree(driver); } mutex_unlock(&drv->core->lock); } -- cgit From 81f0403d4eda80ad304758da16174044d7f8055b Mon Sep 17 00:00:00 2001 From: "shaikh.kamal" Date: Sun, 26 Jul 2026 12:24:07 +0530 Subject: Documentation: media: fix spelling errors Fix spelling errors reported by codespell: thare -> there (vidioc-subdev-g-routing.rst) stuct -> struct (legacy_dvb_video.rst) No functional change. Signed-off-by: shaikh.kamal Signed-off-by: Hans Verkuil --- Documentation/userspace-api/media/dvb/legacy_dvb_video.rst | 2 +- Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/userspace-api/media/dvb/legacy_dvb_video.rst b/Documentation/userspace-api/media/dvb/legacy_dvb_video.rst index b9fd5cadae24..71d044605315 100644 --- a/Documentation/userspace-api/media/dvb/legacy_dvb_video.rst +++ b/Documentation/userspace-api/media/dvb/legacy_dvb_video.rst @@ -344,7 +344,7 @@ Variables - .. - - :rspan:`5` ``stuct play`` + - :rspan:`5` ``struct play`` - :rspan:`4` ``__s32 speed`` diff --git a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst index 6f66ca38589e..164cf0ad3593 100644 --- a/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst +++ b/Documentation/userspace-api/media/v4l/vidioc-subdev-g-routing.rst @@ -67,7 +67,7 @@ subdevice routing table. This may be smaller or larger than the value of drivers may adjust the requested routing table. The kernel can return a ``num_routes`` value larger than ``len_routes`` from -both ioctls. This indicates thare are more routes in the routing table than fits +both ioctls. This indicates there are more routes in the routing table than fits the ``routes`` array. In this case, the ``routes`` array is filled by the kernel with the first ``len_routes`` entries of the subdevice routing table. This is not considered to be an error, and the ioctl call succeeds. If the applications -- cgit From 95f76f51937fdfb0fc1e14cae606b1ef574a56f3 Mon Sep 17 00:00:00 2001 From: Diego Fernando Mancera Gomez Date: Thu, 23 Jul 2026 12:22:42 -0600 Subject: media: em28xx: defer audio-only extension registration The audio-only path registers extensions while probing the primary device. For a dual-TS board, this happens before dev_next is created. The duplicate device inherits is_audio_only and is then independently inserted into em28xx_devlist. The list is intended to contain only primary devices: extension operations reach the secondary device through dev_next. The independently linked secondary can be freed during disconnect while its list node remains reachable, resulting in a use-after-free. Defer audio-only extension registration to the module-request work item. It runs only after probing has completed construction of the optional secondary device, so only the primary is registered and extension callbacks reach the secondary through dev_next. Fixes: 4a089668ef22 ("media: em28xx-cards: rework the em28xx probing code") Cc: stable@vger.kernel.org Reported-by: syzbot+a11c46f37ee083a73deb@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/66ec3c83.050a0220.29194.002f.GAE@google.com/T/ Suggested-by: Fedor Pchelkin Signed-off-by: Diego Fernando Mancera Gomez Signed-off-by: Hans Verkuil --- drivers/media/usb/em28xx/em28xx-cards.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index b94b00456bcd..b7c534fc8a21 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -3675,6 +3675,7 @@ static void request_module_async(struct work_struct *work) * intf. Don't register extensions twice on those devices. */ if (dev->is_audio_only) { + em28xx_init_extension(dev); #if defined(CONFIG_MODULES) && defined(MODULE) request_module("em28xx-alsa"); #endif @@ -3913,8 +3914,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, retval = -ENODEV; goto err_deinit_media; } - em28xx_init_extension(dev); - return 0; } -- cgit From 92a59c24cb688b5faecdb6047a377fc76bc093da Mon Sep 17 00:00:00 2001 From: Benjamin Mugnier Date: Wed, 1 Jul 2026 16:10:27 +0200 Subject: media: i2c: vd55g1: Fix wrong output FPS value Running the sensor on 10 bits mode resulted in FPS being 25% faster than it should be. This issue did not appear in 8 bits mode. This is mostly noticeable with libcamera as it can set a target FPS value directly. This is caused by the V4L2_CID_PIXEL_RATE control being dynamically changed according to the media bus code, while it should be the same regardless of the code. Since we already compute the pixel_clock, use it directly for V4L2_CID_PIXEL_RATE instead. Suggested-by: Dave Stevenson Signed-off-by: Benjamin Mugnier Signed-off-by: Sakari Ailus --- drivers/media/i2c/vd55g1.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c index c4142d771e7c..6f458f611f63 100644 --- a/drivers/media/i2c/vd55g1.c +++ b/drivers/media/i2c/vd55g1.c @@ -686,12 +686,6 @@ adapt_bayer_pattern: 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_bpp(format->code); -} - static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor, struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop) @@ -1329,12 +1323,6 @@ static int vd55g1_new_format_change_controls(struct vd55g1 *sensor, if (ret) return ret; - /* Update pixel rate to reflect new bpp */ - ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate_ctrl, - vd55g1_get_pixel_rate(sensor, format)); - if (ret) - return ret; - /* Update hblank according to new width */ hblank = vd55g1_get_hblank_min(sensor, format, crop); ret = __v4l2_ctrl_modify_range(sensor->hblank_ctrl, hblank, hblank, 1, @@ -1598,7 +1586,6 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) v4l2_subdev_state_get_crop(state, 0); struct v4l2_mbus_framefmt *format = v4l2_subdev_state_get_format(state, 0); - s32 pixel_rate = vd55g1_get_pixel_rate(sensor, format); int ret; v4l2_ctrl_handler_init(hdl, 16); @@ -1640,7 +1627,7 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor) sensor->pixel_rate_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, - pixel_rate); + sensor->pixel_clock); if (sensor->pixel_rate_ctrl) sensor->pixel_rate_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; sensor->ae_lock_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_3A_LOCK, -- cgit From 84ea27af18695ab5f240ff57acb1d51ad216e89e Mon Sep 17 00:00:00 2001 From: Tharit Tangkijwanichakul Date: Wed, 1 Jul 2026 19:57:38 +0700 Subject: media: imx219: add test patterns Add 5 missing test patterns to complete the available hardware test patterns. The original mapping for color bars and solid color in the menu is preserved to prevent existing userspace application and automated vendor capture tests. The changes were validated on a raspberry pi 5 with an imx219 sensor using v4l2-ctl and rpicam-still. Signed-off-by: Tharit Tangkijwanichakul Reviewed-by: Jai Luthra Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx219.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c index 223d3753cc93..9571f3622d2d 100644 --- a/drivers/media/i2c/imx219.c +++ b/drivers/media/i2c/imx219.c @@ -110,11 +110,16 @@ /* Test Pattern Control */ #define IMX219_REG_TEST_PATTERN CCI_REG16(0x0600) -#define IMX219_TEST_PATTERN_DISABLE 0 -#define IMX219_TEST_PATTERN_SOLID_COLOR 1 -#define IMX219_TEST_PATTERN_COLOR_BARS 2 -#define IMX219_TEST_PATTERN_GREY_COLOR 3 -#define IMX219_TEST_PATTERN_PN9 4 +#define IMX219_TEST_PATTERN_DISABLE 0 +#define IMX219_TEST_PATTERN_SOLID_COLOR 1 +#define IMX219_TEST_PATTERN_COLOR_BARS 2 +#define IMX219_TEST_PATTERN_GREY_COLOR 3 +#define IMX219_TEST_PATTERN_PN9 4 +#define IMX219_TEST_PATTERN_16SPLIT_COLOR_BARS 5 +#define IMX219_TEST_PATTERN_16SPLIT_INV_COLOR_BARS 6 +#define IMX219_TEST_PATTERN_COLUMN_COUNTER 7 +#define IMX219_TEST_PATTERN_INV_COLUMN_COUNTER 8 +#define IMX219_TEST_PATTERN_PN31 9 /* Test pattern colour components */ #define IMX219_REG_TESTP_RED CCI_REG16(0x0602) @@ -238,7 +243,12 @@ static const char * const imx219_test_pattern_menu[] = { "Color Bars", "Solid Color", "Grey Color Bars", - "PN9" + "PN9", + "16 Split Color Bars", + "16 Split Inverted Color Bars", + "Column Counter", + "Inverted Column Counter", + "PN31" }; static const int imx219_test_pattern_val[] = { @@ -247,6 +257,11 @@ static const int imx219_test_pattern_val[] = { IMX219_TEST_PATTERN_SOLID_COLOR, IMX219_TEST_PATTERN_GREY_COLOR, IMX219_TEST_PATTERN_PN9, + IMX219_TEST_PATTERN_16SPLIT_COLOR_BARS, + IMX219_TEST_PATTERN_16SPLIT_INV_COLOR_BARS, + IMX219_TEST_PATTERN_COLUMN_COUNTER, + IMX219_TEST_PATTERN_INV_COLUMN_COUNTER, + IMX219_TEST_PATTERN_PN31 }; /* regulator supplies */ -- cgit From 665cf662a04b0d87c0095665bc412752144451d0 Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Sun, 26 Jul 2026 14:45:30 +0200 Subject: media: ov2740: define most fields on variable declaration This also removes initializing reg_write to NULL, which will help with changing its signature in nvmem-provider[1]. Thanks to Sakari Ailus for their suggestion[2]! [1] https://lore.kernel.org/rust-for-linux/20260716142415.28718-1-linkmauve@linkmauve.fr/ [2] https://lore.kernel.org/rust-for-linux/al3sxMUgeK49dwNs@kekkonen.localdomain/ Signed-off-by: Link Mauve Suggested-by: Sakari Ailus Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov2740.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index fb590dfadda1..fbcbadcecebe 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1246,9 +1246,21 @@ static int ov2740_register_nvmem(struct i2c_client *client, { struct nvm_data *nvm; struct regmap_config regmap_config = { }; - struct nvmem_config nvmem_config = { }; struct regmap *regmap; struct device *dev = ov2740->dev; + struct nvmem_config nvmem_config = { + .name = dev_name(dev), + .dev = dev, + .read_only = true, + .root_only = true, + .owner = THIS_MODULE, + .compat = true, + .base_dev = dev, + .reg_read = ov2740_nvmem_read, + .stride = 1, + .word_size = 1, + .size = CUSTOMER_USE_OTP_SIZE, + }; nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL); if (!nvm) @@ -1262,20 +1274,7 @@ static int ov2740_register_nvmem(struct i2c_client *client, return PTR_ERR(regmap); nvm->regmap = regmap; - - nvmem_config.name = dev_name(dev); - nvmem_config.dev = dev; - nvmem_config.read_only = true; - nvmem_config.root_only = true; - nvmem_config.owner = THIS_MODULE; - nvmem_config.compat = true; - nvmem_config.base_dev = dev; - nvmem_config.reg_read = ov2740_nvmem_read; - nvmem_config.reg_write = NULL; nvmem_config.priv = nvm; - nvmem_config.stride = 1; - nvmem_config.word_size = 1; - nvmem_config.size = CUSTOMER_USE_OTP_SIZE; nvm->nvmem = devm_nvmem_register(dev, &nvmem_config); if (IS_ERR(nvm->nvmem)) -- cgit From eeafd9e12144fb6249e1510f9db8a4aaf8155a0a Mon Sep 17 00:00:00 2001 From: Link Mauve Date: Sun, 26 Jul 2026 14:45:31 +0200 Subject: media: ov2740: move regmap_config to a static const variable This one was fully static, so no need to fill it in the register function manually. Signed-off-by: Link Mauve Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov2740.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c index fbcbadcecebe..39003c1632ad 100644 --- a/drivers/media/i2c/ov2740.c +++ b/drivers/media/i2c/ov2740.c @@ -1241,11 +1241,16 @@ exit: return ret; } +static const struct regmap_config regmap_config = { + .val_bits = 8, + .reg_bits = 16, + .disable_locking = true, +}; + static int ov2740_register_nvmem(struct i2c_client *client, struct ov2740 *ov2740) { struct nvm_data *nvm; - struct regmap_config regmap_config = { }; struct regmap *regmap; struct device *dev = ov2740->dev; struct nvmem_config nvmem_config = { @@ -1266,9 +1271,6 @@ static int ov2740_register_nvmem(struct i2c_client *client, if (!nvm) return -ENOMEM; - regmap_config.val_bits = 8; - regmap_config.reg_bits = 16; - regmap_config.disable_locking = true; regmap = devm_regmap_init_i2c(client, ®map_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); -- cgit From 11ccf31a657f9f95260a22848b7d324d3c6cf113 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Mon, 27 Jul 2026 18:22:39 +0700 Subject: media: staging/ipu7: fix async notifier leak on init error isys_notifier_init() initialises a v4l2 async notifier and then, for each CSI-2 port, adds a remote sensor subdev to the notifier's waiting_list via v4l2_async_nf_add_fwnode_remote(), which allocates a sensor_async_sd descriptor and takes a fwnode reference. If parsing or adding a later port fails, the code jumps to the "err_parse" label, which only drops the current endpoint fwnode reference and returns, without calling v4l2_async_nf_cleanup(). Any descriptors already added to the notifier for earlier ports are therefore leaked, and the caller's error path does not clean up the notifier either. Call v4l2_async_nf_cleanup() on the error path, matching the cleanup already performed when v4l2_async_nf_register() fails. This is safe as the notifier is always initialised before the loop is entered. Fixes: a516d36bdc3d ("media: staging/ipu7: add IPU7 input system device driver") Signed-off-by: Cong Nguyen Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7-isys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c index cb2f49f3e0fa..c626de051898 100644 --- a/drivers/staging/media/ipu7/ipu7-isys.c +++ b/drivers/staging/media/ipu7/ipu7-isys.c @@ -233,6 +233,7 @@ static int isys_notifier_init(struct ipu7_isys *isys) err_parse: fwnode_handle_put(ep); + v4l2_async_nf_cleanup(&isys->notifier); return ret; } -- cgit From d836f57e8ab971c1cd3b607d9e466513f654e644 Mon Sep 17 00:00:00 2001 From: Richard Leitner Date: Mon, 20 Jul 2026 20:31:00 +0200 Subject: media: i2c: ov9282: restore flash duration calculation The migration to CCI helpers made V4L2_CID_FLASH_DURATION write the requested duration directly to OV9282_REG_STROBE_FRAME_SPAN, dropping the conversion from microseconds to the register value. This made flash strobes too long and produced overly bright frames. Restore the missing calculation so flash duration is programmed correctly again. Fixes: 273f52f3cbec ("media: i2c: ov9282: Convert to CCI register access helpers") Cc: Xiaolei Wang Cc: stable@vger.kernel.org Signed-off-by: Richard Leitner Signed-off-by: Sakari Ailus --- drivers/media/i2c/ov9282.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c index 3ba630ec4f2a..5d301660a87d 100644 --- a/drivers/media/i2c/ov9282.c +++ b/drivers/media/i2c/ov9282.c @@ -647,7 +647,8 @@ static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl) ctrl->val ? OV9282_OUTPUT_ENABLE6_STROBE : 0, NULL); break; case V4L2_CID_FLASH_DURATION: - ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, ctrl->val, NULL); + ret = cci_write(ov9282->regmap, OV9282_REG_STROBE_FRAME_SPAN, + ov9282_us_to_flash_duration(ov9282, ctrl->val), NULL); break; default: dev_err(ov9282->dev, "Invalid control %d", ctrl->id); -- cgit From 642f8aa04c0bf6f6399acde52c0c15235180d204 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 17:18:31 +0530 Subject: media: i2c: mt9t112: fix incorrect PTR_ERR() call on non-error pointer In mt9t112_probe(), the clock error check after devm_v4l2_sensor_clk_get() calls PTR_ERR(priv->clk) unconditionally, before testing IS_ERR(). On a successful lookup, priv->clk is a valid pointer and calling PTR_ERR() on it is incorrect API usage. While the comparison against -ENOENT happens to be harmless in practice (valid kernel pointers never fall in the error range), this is still a violation of the IS_ERR()/PTR_ERR() contract that can mislead readers. Restructure the check to guard PTR_ERR() inside IS_ERR(), using the simpler != -ENOENT form to avoid an unnecessary else clause. Signed-off-by: Uday Khare Signed-off-by: Sakari Ailus --- drivers/media/i2c/mt9t112.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index bd2268154ca7..b3a6c6d76632 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -1079,11 +1079,12 @@ static int mt9t112_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); priv->clk = devm_v4l2_sensor_clk_get(&client->dev, "extclk"); - if (PTR_ERR(priv->clk) == -ENOENT) + if (IS_ERR(priv->clk)) { + if (PTR_ERR(priv->clk) != -ENOENT) + return dev_err_probe(&client->dev, PTR_ERR(priv->clk), + "Unable to get clock \"extclk\"\n"); priv->clk = NULL; - else if (IS_ERR(priv->clk)) - return dev_err_probe(&client->dev, PTR_ERR(priv->clk), - "Unable to get clock \"extclk\"\n"); + } priv->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", GPIOD_OUT_HIGH); -- cgit From 2f9a7ac90fed8bab1242ab75efb2f172a3933ba2 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 20 Jul 2026 10:36:42 +0300 Subject: media: i2c: cvs: Pass link frequency explicitly to csi_set_link_cfg() The link frequency, retrieved in cvs_csi_enable_streams(), is stored in the icvs structure to then be used right after in csi_set_link_cfg(), called only from the same function. Pass it as a function parameter instead to improve readability. Signed-off-by: Laurent Pinchart Tested-by: Antti Laakso Signed-off-by: Sakari Ailus --- drivers/media/i2c/cvs/icvs.h | 2 -- drivers/media/i2c/cvs/v4l2.c | 17 ++++++++--------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h index cfa8ef5d975c..17beb0920dd2 100644 --- a/drivers/media/i2c/cvs/icvs.h +++ b/drivers/media/i2c/cvs/icvs.h @@ -432,7 +432,6 @@ enum icvs_state { * @freq_ctrl: (future) frequency control pointer * @pads: Local media pads (sink/source) * @nr_of_lanes: Active CSI-2 lane count - * @link_freq: Current link frequency (Hz) * @ipu_link: PM runtime device link (IPU consumer, CVS supplier) * @res: Resource capability (light/full) * @caps: Reported device protocol capabilities @@ -458,7 +457,6 @@ struct icvs { struct v4l2_ctrl *freq_ctrl; struct media_pad pads[ICVS_CSI_NUM_PADS]; u32 nr_of_lanes; - u64 link_freq; struct device_link *ipu_link; enum icvs_resources res; struct icvs_dev_capabilities caps; diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c index 3a1ec0059ef7..9fadca7a3bee 100644 --- a/drivers/media/i2c/cvs/v4l2.c +++ b/drivers/media/i2c/cvs/v4l2.c @@ -46,6 +46,7 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { /** * csi_set_link_cfg - Program default CSI-2 link parameters * @ctx: CVS device context + * @link_freq: Link frequency (Hz) * * Populates a HOST_SET_MIPI_CONFIG command using current lane count and * link frequency, then submits it to the device. @@ -53,12 +54,12 @@ static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = { * * Return: 0 on success or negative errno. */ -static int csi_set_link_cfg(struct icvs *ctx) +static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq) { struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG), .param.conf.nr_of_lanes = ctx->nr_of_lanes, - .param.conf.link_freq = ctx->link_freq, + .param.conf.link_freq = link_freq, }; size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); @@ -91,7 +92,7 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev *remote_sd = media_entity_to_v4l2_subdev(ctx->remote->entity); struct device *dev = cvs_dev(ctx); - s64 freq; + s64 link_freq; int ret; /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */ @@ -99,15 +100,14 @@ static int cvs_csi_enable_streams(struct v4l2_subdev *sd, if (ret < 0) return ret; - freq = v4l2_get_link_freq(ctx->remote, 0, 0); - if (freq < 0) { - ret = freq; + link_freq = v4l2_get_link_freq(ctx->remote, 0, 0); + if (link_freq < 0) { + ret = link_freq; goto err_rpm_put; } - ctx->link_freq = freq; if (ctx->i2c_client) { - ret = csi_set_link_cfg(ctx); + ret = csi_set_link_cfg(ctx, link_freq); if (ret < 0) goto err_rpm_put_sync; } @@ -345,7 +345,6 @@ static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad, if (freq < 0) return -EINVAL; - ctx->link_freq = freq; cfg->link_freq = freq; return 0; -- cgit From 3a6dcbe36aafee331c33c9b3340756c519104103 Mon Sep 17 00:00:00 2001 From: Mihail Antsev Date: Sun, 26 Jul 2026 14:54:48 +0200 Subject: media: ipu-bridge: Add upside-down sensor DMI quirk for Samsung Galaxy Book5 Pro 360 The Samsung Galaxy Book5 Pro 360 (960QHA) has an upside-down mounted OV02E10 sensor. The 180 degree rotation is reported in neither the SSDB nor the _PLD for the sensor (both report a rotation of 0 degrees), so the image is rendered upside-down in userspace. Add a DMI quirk entry for this laptop so that the sensor fwnode gets a "rotation" property of 180 and userspace (e.g. libcamera) can compensate. Tested on the Galaxy Book5 Pro 360: with the sensor reporting V4L2_CID_CAMERA_SENSOR_ROTATION = 180 (verified with an equivalent local sensor-driver quirk), libcamera renders the image upright. Signed-off-by: Mihail Antsev Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 42a309e7218e..20cb133de826 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -182,6 +182,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "SONY471A", }, + { + /* Samsung Galaxy Book5 Pro 360 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960QHA"), + }, + .driver_data = "OVTI02E1", + }, {} /* Terminating entry */ }; -- cgit From 460d3257a6dffc7f0bf58009daeb7e0b6eb2d9d3 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:36 +0530 Subject: media: qcom: iris: fix state-change debug log printing stale value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The state‑change debug log in iris_inst_change_state() always prints the same value for the old and new state, rendering it useless for debugging. This happens because the state is updated before the log is emitted. Log the transition before updating the state so the previous value is preserved, consistent with the existing sub‑state handling. Fixes: 11712ce70f8e ("media: iris: implement vb2 streaming ops") Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c index d14472414750..e991f34916ec 100644 --- a/drivers/media/platform/qcom/iris/iris_state.c +++ b/drivers/media/platform/qcom/iris/iris_state.c @@ -60,9 +60,9 @@ int iris_inst_change_state(struct iris_inst *inst, return -EINVAL; change_state: - inst->state = request_state; dev_dbg(inst->core->dev, "state changed from %x to %x\n", inst->state, request_state); + inst->state = request_state; return 0; } -- cgit From f87d7eda07fca21efe4b96169ae59007db46e60e Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 8 Jun 2026 08:17:20 +0000 Subject: media: qcom: iris: fix runtime PM reference leaks Use pm_runtime_resume_and_get() in iris_enable_power_domains() to avoid leaking a runtime PM usage count on failure. Also ensure pm_runtime_put_sync() is always called in iris_disable_power_domains(), even when iris_opp_set_rate() fails, so runtime PM references remain balanced. Fixes: bb8a95aa038e ("media: iris: implement power management") Reviewed-by: Dmitry Baryshkov Signed-off-by: Hungyu Lin Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_resources.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c index 872bd09656b1..6d3339423eec 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.c +++ b/drivers/media/platform/qcom/iris/iris_resources.c @@ -78,20 +78,21 @@ int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev) if (ret) return ret; - return pm_runtime_get_sync(pd_dev); + return pm_runtime_resume_and_get(pd_dev); } int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev) { int ret; + int pm_ret; ret = iris_opp_set_rate(core->dev, 0); - if (ret) - return ret; - pm_runtime_put_sync(pd_dev); + pm_ret = pm_runtime_put_sync(pd_dev); + if (!ret) + ret = pm_ret; - return 0; + return ret; } static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type) -- cgit From 5eebacbc9a38e1019854ea5f86e367a4d5116387 Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Thu, 23 Jul 2026 10:55:54 +0800 Subject: media: qcom: iris: fix missing hfi_id in gen1 GOP_SIZE cap Add hfi_id to gen1 encoder GOP_SIZE cap and replace the set function, remove the redundant INTRA_PERIOD cap. Fixes: d22037f3fd33 ("media: iris: Set platform capabilities to firmware for encoder video device") Reviewed-by: Vishnu Reddy Signed-off-by: Wangao Wang Reviewed-by: Vikash Garodia Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 2 +- drivers/media/platform/qcom/iris/iris_hfi_gen1.c | 16 ++++------------ drivers/media/platform/qcom/iris/iris_platform_common.h | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 10e33b8a73f6..391e1fc5f6e2 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -1293,7 +1293,7 @@ int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; - u32 gop_size = inst->fw_caps[GOP_SIZE].value; + u32 gop_size = inst->fw_caps[cap_id].value; u32 b_frame = inst->fw_caps[B_FRAME].value; u32 hfi_id = inst->fw_caps[cap_id].hfi_id; struct hfi_intra_period intra_period; diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index ca1545d28b53..eff9216bb501 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -171,7 +171,9 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .max = (1 << 16) - 1, .step_or_mask = 1, .value = 30, - .set = iris_set_u32 + .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_intra_period, }, { .cap_id = ENTROPY_MODE, @@ -240,7 +242,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .step_or_mask = 1, .value = 0, .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH, - .flags = CAP_FLAG_OUTPUT_PORT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_ir_period_gen1, }, { @@ -281,16 +283,6 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { .value = 0, .flags = CAP_FLAG_OUTPUT_PORT, }, - { - .cap_id = INTRA_PERIOD, - .min = 0, - .max = 1, - .step_or_mask = 1, - .value = 0, - .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD, - .flags = CAP_FLAG_OUTPUT_PORT, - .set = iris_set_intra_period, - }, { .cap_id = LAYER_ENABLE, .min = 0, diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index c9256f2323dc..ff48333ad089 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -165,7 +165,6 @@ enum platform_inst_fw_cap_type { USE_LTR, MARK_LTR, B_FRAME, - INTRA_PERIOD, LAYER_ENABLE, LAYER_TYPE_H264, LAYER_TYPE_HEVC, -- cgit From a51cea23e409278f6e2ea072280aba93cc1dd75e Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Wed, 10 Jun 2026 13:56:54 +0100 Subject: media: venus: fix payload size returned by parse_caps() and parse_alloc_mode() parse_caps() and parse_alloc_mode() return only the size of their fixed header fields, excluding the flexible array payload. hfi_parser() uses this return value to advance through the firmware response buffer, so underreporting causes parser desynchronization. Return the full consumed size (header + entries), matching the correct pattern used by parse_profile_level(). Fixes: 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic") Cc: stable@vger.kernel.org Signed-off-by: Mohammed EL Kadiri Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/hfi_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index b1657443f23f..8284fcc78bb1 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -85,7 +85,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data) type++; } - return sizeof(*mode); + return mode->num_entries * sizeof(u32) + sizeof(*mode); } static void fill_profile_level(struct hfi_plat_caps *cap, const void *data, @@ -146,7 +146,7 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_caps, caps_arr, num_caps); - return sizeof(*caps); + return num_caps * sizeof(*cap) + sizeof(u32); } static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts, -- cgit From bd595b745eb770e80347c31ffc25351046935305 Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Wed, 10 Jun 2026 13:56:55 +0100 Subject: media: venus: fix payload size calculation in parse_raw_formats() The consumed size is computed after the loop using the num_planes value from the last iteration for all entries. When entries have different plane counts, this produces an incorrect total. Accumulate the actual size during the loop instead. Fixes: 9edaaa8e3e15 ("media: venus: hfi_parser: refactor hfi packet parsing logic") Cc: stable@vger.kernel.org Signed-off-by: Mohammed EL Kadiri Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/hfi_parser.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 8284fcc78bb1..d07845f37c55 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -171,7 +171,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) u32 entries = fmt->format_entries; unsigned int i = 0; u32 num_planes = 0; - u32 size; + u32 size = 2 * sizeof(u32); while (entries) { num_planes = pinfo->num_planes; @@ -186,6 +186,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) if (pinfo->num_planes > MAX_PLANES) break; + size += sizeof(*constr) * num_planes + 2 * sizeof(u32); pinfo = (void *)pinfo + sizeof(*constr) * num_planes + 2 * sizeof(u32); entries--; @@ -193,8 +194,6 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data) for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain, fill_raw_fmts, rawfmts, i); - size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32)) - + 2 * sizeof(u32); return size; } -- cgit From e1fb54b437c9eff2b6519216042c87026e8677e8 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:35 +0530 Subject: media: qcom: iris: Centralize internal buffer table selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Internal buffer table dispatch is duplicated across multiple Iris code paths, which is error‑prone and makes future changes harder to reason about. Consolidate the buffer dispatch logic into a single helper so that table selection is defined in exactly one place and keep call sites minimal. No functional change intended. Reviewed-by: Bryan O'Donoghue Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_buffer.c | 107 ++++++------------------- 1 file changed, 26 insertions(+), 81 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index 246ad0abbac3..bd922a12045c 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -492,39 +492,41 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst, buffers->min_count = iris_vpu_buf_count(inst, buffer_type); } -void iris_get_internal_buffers(struct iris_inst *inst, u32 plane) +static void iris_get_int_buf_tbl(struct iris_inst *inst, u32 plane, + const u32 **internal_buf_type, u32 *internal_buffer_count) { const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; - const u32 *internal_buf_type; - u32 internal_buffer_count, i; if (inst->domain == DECODER) { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->dec_ip_int_buf_tbl; - internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; - for (i = 0; i < internal_buffer_count; i++) - iris_fill_internal_buf_info(inst, internal_buf_type[i]); + *internal_buf_type = firmware_data->dec_ip_int_buf_tbl; + *internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; } else { - internal_buf_type = firmware_data->dec_op_int_buf_tbl; - internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; - for (i = 0; i < internal_buffer_count; i++) - iris_fill_internal_buf_info(inst, internal_buf_type[i]); + *internal_buf_type = firmware_data->dec_op_int_buf_tbl; + *internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; } } else { if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->enc_ip_int_buf_tbl; - internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; - for (i = 0; i < internal_buffer_count; i++) - iris_fill_internal_buf_info(inst, internal_buf_type[i]); + *internal_buf_type = firmware_data->enc_ip_int_buf_tbl; + *internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; } else { - internal_buf_type = firmware_data->enc_op_int_buf_tbl; - internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; - for (i = 0; i < internal_buffer_count; i++) - iris_fill_internal_buf_info(inst, internal_buf_type[i]); + *internal_buf_type = firmware_data->enc_op_int_buf_tbl; + *internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; } } } +void iris_get_internal_buffers(struct iris_inst *inst, u32 plane) +{ + const u32 *internal_buf_type; + u32 internal_buffer_count, i; + + iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count); + + for (i = 0; i < internal_buffer_count; i++) + iris_fill_internal_buf_info(inst, internal_buf_type[i]); +} + static int iris_create_internal_buffer(struct iris_inst *inst, enum iris_buffer_type buffer_type, u32 index) { @@ -559,29 +561,12 @@ static int iris_create_internal_buffer(struct iris_inst *inst, int iris_create_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; u32 internal_buffer_count, i, j; struct iris_buffers *buffers; const u32 *internal_buf_type; int ret; - if (inst->domain == DECODER) { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->dec_ip_int_buf_tbl; - internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->dec_op_int_buf_tbl; - internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; - } - } else { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->enc_ip_int_buf_tbl; - internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->enc_op_int_buf_tbl; - internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; - } - } + iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count); for (i = 0; i < internal_buffer_count; i++) { buffers = &inst->buffers[internal_buf_type[i]]; @@ -635,30 +620,13 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) { - const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buffer, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; u32 internal_buffer_count, i; int ret; - if (inst->domain == DECODER) { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->dec_ip_int_buf_tbl; - internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->dec_op_int_buf_tbl; - internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size; - } - } else { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->enc_ip_int_buf_tbl; - internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->enc_op_int_buf_tbl; - internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size; - } - } + iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &internal_buffer_count); for (i = 0; i < internal_buffer_count; i++) { buffers = &inst->buffers[internal_buf_type[i]]; @@ -694,30 +662,13 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) { - const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; struct iris_buffer *buf, *next; struct iris_buffers *buffers; const u32 *internal_buf_type; u32 i, len; int ret; - if (inst->domain == DECODER) { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->dec_ip_int_buf_tbl; - len = firmware_data->dec_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->dec_op_int_buf_tbl; - len = firmware_data->dec_op_int_buf_tbl_size; - } - } else { - if (V4L2_TYPE_IS_OUTPUT(plane)) { - internal_buf_type = firmware_data->enc_ip_int_buf_tbl; - len = firmware_data->enc_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->enc_op_int_buf_tbl; - len = firmware_data->enc_op_int_buf_tbl_size; - } - } + iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &len); for (i = 0; i < len; i++) { buffers = &inst->buffers[internal_buf_type[i]]; @@ -788,18 +739,12 @@ static int iris_release_internal_buffers(struct iris_inst *inst, static int iris_release_input_internal_buffers(struct iris_inst *inst) { - const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data; const u32 *internal_buf_type; u32 internal_buffer_count, i; int ret; - if (inst->domain == DECODER) { - internal_buf_type = firmware_data->dec_ip_int_buf_tbl; - internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size; - } else { - internal_buf_type = firmware_data->enc_ip_int_buf_tbl; - internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size; - } + iris_get_int_buf_tbl(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, + &internal_buf_type, &internal_buffer_count); for (i = 0; i < internal_buffer_count; i++) { ret = iris_release_internal_buffers(inst, internal_buf_type[i]); -- cgit From 0ac05c4d9f1fa25d0692fb154de36bd3baf2e7ce Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:37 +0530 Subject: media: qcom: iris: Fix bitmask test in iris_allow_cmd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iris_allow_cmd() incorrectly checks a sub‑state flag using a logical equality comparison. Since sub_state is a bitmask, this allows STOP to pass when IRIS_INST_SUB_DRAIN is set alongside other bits, violating the intended drain semantics. Fix this by using a proper bitmask test. Fixes: d09100763bed ("media: iris: add support for drain sequence") Reviewed-by: Bryan O'Donoghue Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c index e991f34916ec..5552725c614e 100644 --- a/drivers/media/platform/qcom/iris/iris_state.c +++ b/drivers/media/platform/qcom/iris/iris_state.c @@ -269,7 +269,7 @@ bool iris_allow_cmd(struct iris_inst *inst, u32 cmd) return true; } else if (cmd == V4L2_DEC_CMD_STOP || cmd == V4L2_ENC_CMD_STOP) { if (vb2_is_streaming(src_q)) - if (inst->sub_state != IRIS_INST_SUB_DRAIN) + if (!(inst->sub_state & IRIS_INST_SUB_DRAIN)) return true; } -- cgit From f6afe7ffb2984d8aa200d325cf9317517d68e926 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:38 +0530 Subject: media: qcom: iris: Remove dead assignment in iris_hfi_gen2_set_tier() Fold the ternary initialiser directly into the variable declaration, removing the dead store that was immediately overwritten. Reviewed-by: Bryan O'Donoghue Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index ca2954f8bd3a..62283bd448dc 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -603,10 +603,9 @@ static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane) { u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst); - u32 tier = inst->fw_caps[TIER].value; + u32 tier_cap = (inst->codec == V4L2_PIX_FMT_AV1) ? TIER_AV1 : TIER; + u32 tier = inst->fw_caps[tier_cap].value; - tier = (inst->codec == V4L2_PIX_FMT_AV1) ? inst->fw_caps[TIER_AV1].value : - inst->fw_caps[TIER].value; inst_hfi_gen2->src_subcr_params.tier = tier; return iris_hfi_gen2_session_set_property(inst, -- cgit From 727a87c71b4ef794cdf552dae585d6f67be690ee Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:39 +0530 Subject: media: qcom: iris: Remove duplicate HFI_PROP_OPB_ENABLE entry HFI_PROP_OPB_ENABLE/iris_hfi_gen2_set_opb_enable appeared twice in the dispatch table, causing the property to be sent to firmware twice on every config-params call. Fixes: 2af481a459a4 ("media: iris: Define AV1-specific platform capabilities and properties") Reviewed-by: Konrad Dybcio Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index 62283bd448dc..b3496db55ad5 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -691,7 +691,6 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p {HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate }, {HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain }, {HFI_PROP_AV1_SUPER_BLOCK_ENABLED, iris_hfi_gen2_set_super_block }, - {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable }, }; if (inst->domain == DECODER) { -- cgit From 75126861e6aec4d39fdc1cd892d579f2d1e05e80 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:40 +0530 Subject: media: qcom: iris: Add missing break in iris_hfi_gen2_session_set_codec() Without the break the AV1 case falls through, risking unintended behaviour if new cases are added after it. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c index b3496db55ad5..388a36ff2b07 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c @@ -764,6 +764,7 @@ static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst) break; case V4L2_PIX_FMT_AV1: codec = HFI_CODEC_DECODE_AV1; + break; } iris_hfi_gen2_packet_session_property(inst, -- cgit From d2f0ebbcde0b579cc6c8368174f76e5517fc6c69 Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Sun, 31 May 2026 11:20:41 +0530 Subject: media: qcom: iris: Make iris_destroy_internal_buffer() return void iris_destroy_internal_buffer() is guaranteed to succeed and never reports an error. Returning an int is misleading and forces callers to handle a meaningless status value. Convert it to return void to match its behavior and simplify callers. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Dikshita Agarwal Signed-off-by: Vishnu Reddy Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_buffer.c | 16 ++++------------ drivers/media/platform/qcom/iris/iris_buffer.h | 2 +- drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c | 2 +- .../media/platform/qcom/iris/iris_hfi_gen2_response.c | 4 +++- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c index bd922a12045c..eb8de60c1177 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.c +++ b/drivers/media/platform/qcom/iris/iris_buffer.c @@ -648,7 +648,7 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane) return 0; } -int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer) +void iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer) { struct iris_core *core = inst->core; @@ -656,8 +656,6 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr, buffer->device_addr, buffer->dma_attrs); kfree(buffer); - - return 0; } static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force) @@ -666,7 +664,6 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool struct iris_buffers *buffers; const u32 *internal_buf_type; u32 i, len; - int ret; iris_get_int_buf_tbl(inst, plane, &internal_buf_type, &len); @@ -681,9 +678,7 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool if (!force && buf->attr & BUF_ATTR_QUEUED) continue; - ret = iris_destroy_internal_buffer(inst, buf); - if (ret) - return ret; + iris_destroy_internal_buffer(inst, buf); } } @@ -693,11 +688,8 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool else buffers = &inst->buffers[BUF_ARP]; - list_for_each_entry_safe(buf, next, &buffers->list, list) { - ret = iris_destroy_internal_buffer(inst, buf); - if (ret) - return ret; - } + list_for_each_entry_safe(buf, next, &buffers->list, list) + iris_destroy_internal_buffer(inst, buf); } return 0; diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h index 75bb76776182..ab8e5d953101 100644 --- a/drivers/media/platform/qcom/iris/iris_buffer.h +++ b/drivers/media/platform/qcom/iris/iris_buffer.h @@ -112,7 +112,7 @@ void iris_get_internal_buffers(struct iris_inst *inst, u32 plane); int iris_create_internal_buffers(struct iris_inst *inst, u32 plane); int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane); int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buffer_type); -int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer); +void iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer); int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane); int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane); int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst, enum iris_buffer_type buf_type); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 7674b47ad6c4..1c02f2eebdf7 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -442,7 +442,7 @@ static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct ir ret = iris_wait_for_session_response(inst, false); if (!ret) - ret = iris_destroy_internal_buffer(inst, buf); + iris_destroy_internal_buffer(inst, buf); exit: kfree(pkt); diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c index 25162ae71357..8c2644c7f6e8 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c @@ -447,7 +447,9 @@ static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst, buf->attr &= ~BUF_ATTR_QUEUED; - return iris_destroy_internal_buffer(inst, buf); + iris_destroy_internal_buffer(inst, buf); + + return 0; } static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst, -- cgit From 94ef75095d5e76df848a2e5508d71a4532a6ce23 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Tue, 2 Jun 2026 14:59:21 +0100 Subject: media: iris: Enumerate cap->bus_info to differentiate between encoder and decoder commit 66c744e28b69 ("media: venus: assign unique bus_info strings for encoder and decoder") introduced the naming convention plat:node-addr:video-codec{enc|dec}. Right now Iris does not replicate this naming convention. When we do v4l2-ctrl --list -devices we see: Iris Decoder (platform:aa00000.video-codec): /dev/video0 /dev/video1 Enumerate the bus_info field of the capabilities structure for namespace parity and appropriate differentiation: Iris Decoder (plat:aa00000.video-codec:dec): /dev/video0 Iris Encoder (plat:aa00000.video-codec:enc): /dev/video1 Fixes: 5ad964ad5656 ("media: iris: Initialize and deinitialize encoder instance structure") Cc: stable@vger.kernel.org Signed-off-by: Bryan O'Donoghue Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vidc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 14d63dc76c9b..1c052ca235be 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -449,14 +449,21 @@ static int iris_enum_frameintervals(struct file *filp, void *fh, static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap) { + struct iris_core *core = video_drvdata(filp); struct iris_inst *inst = iris_get_inst(filp); + char *info; strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver)); - if (inst->domain == DECODER) + if (inst->domain == DECODER) { strscpy(cap->card, "Iris Decoder", sizeof(cap->card)); - else + info = "dec"; + } else { strscpy(cap->card, "Iris Encoder", sizeof(cap->card)); + info = "enc"; + } + snprintf(cap->bus_info, sizeof(cap->bus_info), + "plat:%s:%s", dev_name(core->dev), info); return 0; } -- cgit From 14634f3161e8996cbdf83fc7f36b33833a0015b1 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 4 Jun 2026 00:18:59 +0000 Subject: media: qcom: venus: add callback parameter names Naming the callback parameters improves readability and resolves checkpatch warnings about unnamed function pointer arguments. No functional change intended. Signed-off-by: Hungyu Lin Reviewed-by: Vishnu Reddy Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/hfi_msgs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 47b99d5b5af7..5c2025c5acc7 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -690,7 +690,7 @@ struct hfi_done_handler { u32 pkt; u32 pkt_sz; u32 pkt_sz2; - void (*done)(struct venus_core *, struct venus_inst *, void *); + void (*done)(struct venus_core *core, struct venus_inst *inst, void *packet); bool is_sys_pkt; }; -- cgit From 75d79879ec3cbfd288144b0ae4c3e3fa7700c5fc Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 4 Jun 2026 05:04:51 +0000 Subject: media: qcom: iris: handle runtime PM resume failure in core deinit Check the return value of pm_runtime_resume_and_get() in iris_core_deinit(). If runtime PM resume fails, skip hardware power-off operations but still perform software teardown and state transition. Also skip the corresponding pm_runtime_put_sync() call to avoid unbalanced runtime PM references. Fixes: bb8a95aa038e ("media: iris: implement power management") Signed-off-by: Hungyu Lin Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c index 52bf56e517f9..8c335dbfce16 100644 --- a/drivers/media/platform/qcom/iris/iris_core.c +++ b/drivers/media/platform/qcom/iris/iris_core.c @@ -12,18 +12,24 @@ void iris_core_deinit(struct iris_core *core) { - pm_runtime_resume_and_get(core->dev); + int ret; + + ret = pm_runtime_resume_and_get(core->dev); mutex_lock(&core->lock); if (core->state != IRIS_CORE_DEINIT) { iris_fw_unload(core); - iris_vpu_power_off(core); + + if (!ret) + iris_vpu_power_off(core); + iris_hfi_queues_deinit(core); core->state = IRIS_CORE_DEINIT; } mutex_unlock(&core->lock); - pm_runtime_put_sync(core->dev); + if (!ret) + pm_runtime_put_sync(core->dev); } static int iris_wait_for_system_response(struct iris_core *core) -- cgit From f5daac6eff7ebdd0b3058584502e579cd77601de Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Mon, 8 Jun 2026 12:05:31 +0000 Subject: media: qcom: iris: initialize OPP pointer at declaration Initialize the managed OPP pointer at declaration rather than assigning it in a separate statement. This avoids a checkpatch warning about a potentially uninitialized managed pointer. Signed-off-by: Hungyu Lin Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_resources.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c index 6d3339423eec..2c4d34c7bd77 100644 --- a/drivers/media/platform/qcom/iris/iris_resources.c +++ b/drivers/media/platform/qcom/iris/iris_resources.c @@ -61,9 +61,9 @@ int iris_unset_icc_bw(struct iris_core *core) int iris_opp_set_rate(struct device *dev, unsigned long freq) { - struct dev_pm_opp *opp __free(put_opp); + struct dev_pm_opp *opp __free(put_opp) = + devfreq_recommended_opp(dev, &freq, 0); - opp = devfreq_recommended_opp(dev, &freq, 0); if (IS_ERR(opp)) return PTR_ERR(opp); -- cgit From b9c2215bdedc9c532a7e9d57ec49ee1b6381f863 Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Tue, 30 Jun 2026 15:26:17 +0000 Subject: media: qcom: iris: use disable_irq() during power-off The IRQ is registered as a threaded IRQ. Using disable_irq_nosync() in iris_vpu_power_off() does not wait for an already queued threaded IRQ handler to complete before returning. As a result, a threaded IRQ handler may still run after the VPU has been powered down and access hardware registers after power-off. Replace disable_irq_nosync() with disable_irq() so the power-off path waits for any in-flight threaded IRQ handler to complete before returning. Fixes: bb8a95aa038e ("media: iris: implement power management") Cc: stable@vger.kernel.org Suggested-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Hungyu Lin Reviewed-by: Bryan O'Donoghue Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_vpu_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c index ab41da1f47c8..e4847c107709 100644 --- a/drivers/media/platform/qcom/iris/iris_vpu_common.c +++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c @@ -237,7 +237,7 @@ void iris_vpu_power_off(struct iris_core *core) iris_unset_icc_bw(core); if (!iris_vpu_watchdog(core, core->intr_status)) - disable_irq_nosync(core->irq); + disable_irq(core->irq); } int iris_vpu_power_on_controller(struct iris_core *core) -- cgit From 4bf9ae6229a02f3600af6b56f763f819110130ee Mon Sep 17 00:00:00 2001 From: Mohammed EL Kadiri Date: Sun, 7 Jun 2026 12:19:33 +0100 Subject: media: venus: Annotate flex arrays with __counted_by() Add __counted_by() annotations to flexible array members: - hfi_capabilities::data, counted by num_capabilities - hfi_profile_level_supported::profile_level, counted by profile_count - hfi_resource_ocmem_requirement_info::requirements, counted by num_entries This improves run-time bounds checking via CONFIG_UBSAN_BOUNDS and compile-time object size resolution via __builtin_dynamic_object_size(). Assisted-by: Claude:claude-opus-4 Reviewed-by: Dmitry Baryshkov Signed-off-by: Mohammed EL Kadiri Reviewed-by: Konrad Dybcio Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/venus/hfi_helper.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index f44059f19505..e96d458c52fa 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -616,7 +616,7 @@ struct hfi_capability { struct hfi_capabilities { u32 num_capabilities; - struct hfi_capability data[]; + struct hfi_capability data[] __counted_by(num_capabilities); }; #define HFI_DEBUG_MSG_LOW 0x01 @@ -802,7 +802,7 @@ struct hfi_profile_level { struct hfi_profile_level_supported { u32 profile_count; - struct hfi_profile_level profile_level[]; + struct hfi_profile_level profile_level[] __counted_by(profile_count); }; struct hfi_quality_vs_speed { @@ -1085,7 +1085,7 @@ struct hfi_resource_ocmem_requirement { struct hfi_resource_ocmem_requirement_info { u32 num_entries; - struct hfi_resource_ocmem_requirement requirements[]; + struct hfi_resource_ocmem_requirement requirements[] __counted_by(num_entries); }; struct hfi_property_sys_image_version_info_type { -- cgit From 412a2e5955e0631456b060f9a7c62783d6fb30fe Mon Sep 17 00:00:00 2001 From: Dikshita Agarwal Date: Wed, 10 Jun 2026 16:51:56 +0300 Subject: media: iris: Add Gen2 firmware autodetect and fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Iris platforms support both Gen1 and Gen2 HFI firmware images. Update the firmware loading logic to handle this generically by preferring Gen2 when available, while safely falling back to Gen1 when required. The firmware loading logic is updated with the following priority: 1. Device Tree (`firmware-name`): If specified, load unconditionally. 2. Gen2 default : If no DT override exists, select the Gen2 firmware descriptor when present and attempt to load the corresponding firmware image. 3. Gen1 Fallback: If loading the Gen2 firmware fails and a Gen1 descriptor is available, retry with the Gen1 firmware image. When a platform provides both Gen1 and Gen2 firmware descriptors and the firmware is loaded via a DT override, the driver detects the firmware generation at runtime before authentication by inspecting the firmware data. The firmware is classified as Gen2 if the QC_IMAGE_VERSION_STRING starts with "vfw" or matches the "video-firmware.N.M" format with N >= 2. If a Gen1 firmware image is detected in this case, the driver switches to the Gen1 firmware descriptor and associated platform data so that the correct HFI implementation is used. This change makes firmware generation detection platform‑agnostic, preserves DT overrides, prefers newer Gen2 firmware when available, and maintains compatibility with platforms that only support Gen1. Signed-off-by: Dikshita Agarwal Reviewed-by: Vikash Garodia Co-developed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_firmware.c | 119 ++++++++++++++++++--- .../platform/qcom/iris/iris_platform_common.h | 6 +- .../media/platform/qcom/iris/iris_platform_vpu2.c | 11 +- .../media/platform/qcom/iris/iris_platform_vpu3x.c | 10 +- drivers/media/platform/qcom/iris/iris_probe.c | 4 - drivers/media/platform/qcom/iris/iris_vidc.c | 3 + 6 files changed, 120 insertions(+), 33 deletions(-) diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 1a476146d758..965384fdd339 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -16,20 +16,109 @@ #define MAX_FIRMWARE_NAME_SIZE 128 -static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) +/* Detect Gen2 firmware by scanning the blob for: + * QC_IMAGE_VERSION_STRING= + * and then checking: + * - version starts with "vfw", OR + * - version matches "video-firmware.N.M" with N >= 2 + */ + +static bool iris_detect_gen2_from_fwdata(const u8 *data, size_t size) +{ + static const char *marker = "QC_IMAGE_VERSION_STRING="; + const size_t mlen = strlen(marker); + static const char *vfw = "vfw"; + const size_t vfwlen = strlen(vfw); + static const char *vf = "video-firmware."; + const size_t vflen = strlen(vf); + + for (size_t i = 0; i + mlen < size; i++) { + const char *found; + + if (memcmp(data + i, marker, mlen)) + continue; + + found = data + i + mlen; + size -= i + mlen; + + /* vfw => Gen2 */ + if (size > vfwlen && !memcmp(found, vfw, vfwlen)) + return true; + + if (size < vflen || + memcmp(found, vf, vflen)) + return false; + + found += vflen; + size -= vflen; + + /* + * video-firmware.1.x is Gen1. + * video-firmware.2.x and video-firmware.10.x are Gen2. + */ + return size >= 2 && + (*found >= '2' || (*found == '1' && found[1] != '.')); + } + + return false; +} + +static const struct firmware *iris_detect_firmware(struct iris_core *core, + const char **fw_name) +{ + const struct firmware *firmware; + bool has_both_gens; + int ret; + + *fw_name = NULL; + if (core->iris_platform_data->firmware_desc_gen2) + core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen2; + else if (core->iris_platform_data->firmware_desc_gen1) + core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1; + else + return ERR_PTR(-EINVAL); + + has_both_gens = core->iris_platform_data->firmware_desc_gen2 && + core->iris_platform_data->firmware_desc_gen1; + + ret = of_property_read_string_index(dev_of_node(core->dev), "firmware-name", 0, fw_name); + if (ret) { + *fw_name = core->iris_firmware_desc->fwname; + ret = request_firmware(&firmware, *fw_name, core->dev); + if (ret && has_both_gens) { + core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1; + *fw_name = core->iris_firmware_desc->fwname; + ret = request_firmware(&firmware, *fw_name, core->dev); + } + + return ret ? ERR_PTR(ret) : firmware; + } + + ret = request_firmware(&firmware, *fw_name, core->dev); + if (ret) + return ERR_PTR(ret); + + if (has_both_gens && + !iris_detect_gen2_from_fwdata((const u8 *)firmware->data, firmware->size)) { + dev_info(core->dev, "Gen1 FW detected in %s\n", *fw_name); + core->iris_firmware_desc = core->iris_platform_data->firmware_desc_gen1; + } + + return firmware; +} + +static int iris_load_fw_to_memory(struct iris_core *core) { const struct firmware *firmware = NULL; struct device *dev = core->dev; struct resource res; phys_addr_t mem_phys; + const char *fw_name; size_t res_size; ssize_t fw_size; void *mem_virt; int ret; - if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4) - return -EINVAL; - ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res); if (ret) return ret; @@ -37,9 +126,11 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name) mem_phys = res.start; res_size = resource_size(&res); - ret = request_firmware(&firmware, fw_name, dev); - if (ret) - return ret; + firmware = iris_detect_firmware(core, &fw_name); + if (IS_ERR(firmware)) + return PTR_ERR(firmware); + + core->iris_firmware_data = core->iris_firmware_desc->firmware_data; fw_size = qcom_mdt_get_size(firmware); if (fw_size < 0 || res_size < (size_t)fw_size) { @@ -66,18 +157,12 @@ err_release_fw: int iris_fw_load(struct iris_core *core) { const struct tz_cp_config *cp_config; - const char *fwpath = NULL; int i, ret; - ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0, - &fwpath); - if (ret) - fwpath = core->iris_firmware_desc->fwname; - - ret = iris_load_fw_to_memory(core, fwpath); + ret = iris_load_fw_to_memory(core); if (ret) { - dev_err(core->dev, "firmware download failed\n"); - return -ENOMEM; + dev_err(core->dev, "firmware download failed %d\n", ret); + return ret; } ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID); @@ -99,7 +184,7 @@ int iris_fw_load(struct iris_core *core) } } - return ret; + return 0; } int iris_fw_unload(struct iris_core *core) diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index ff48333ad089..980cbf5b5f2a 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -288,11 +288,7 @@ struct iris_firmware_desc { }; struct iris_platform_data { - /* - * XXX: replace with gen1 / gen2 pointers once we have platforms - * supporting both firmware kinds. - */ - const struct iris_firmware_desc *firmware_desc; + const struct iris_firmware_desc *firmware_desc_gen1, *firmware_desc_gen2; const struct vpu_ops *vpu_ops; const struct icc_info *icc_tbl; diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 6e06a32822bb..961dce2e6aa9 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -22,6 +22,12 @@ static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = { .fwname = "qcom/vpu/vpu20_p1.mbn", }; +static const struct iris_firmware_desc iris_vpu20_p1_gen2_s6_desc = { + .firmware_data = &iris_hfi_gen2_data, + .get_vpu_buffer_size = iris_vpu33_buf_size, + .fwname = "qcom/vpu/vpu20_p1_gen2_s6.mbn", +}; + static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = { .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, @@ -65,7 +71,8 @@ static const struct tz_cp_config tz_cp_config_vpu2[] = { }; const struct iris_platform_data sc7280_data = { - .firmware_desc = &iris_vpu20_p1_gen1_desc, + .firmware_desc_gen1 = &iris_vpu20_p1_gen1_desc, + .firmware_desc_gen2 = &iris_vpu20_p1_gen2_s6_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), @@ -94,7 +101,7 @@ const struct iris_platform_data sc7280_data = { }; const struct iris_platform_data sm8250_data = { - .firmware_desc = &iris_vpu20_p4_gen1_desc, + .firmware_desc_gen1 = &iris_vpu20_p4_gen1_desc, .vpu_ops = &iris_vpu2_ops, .icc_tbl = iris_icc_info_vpu2, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2), diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c index 2c63adbc5579..74626b35d9cb 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c @@ -90,7 +90,7 @@ static const struct tz_cp_config tz_cp_config_vpu3[] = { * - inst_caps to platform_inst_cap_qcs8300 */ const struct iris_platform_data qcs8300_data = { - .firmware_desc = &iris_vpu30_p4_s6_gen2_desc, + .firmware_desc_gen2 = &iris_vpu30_p4_s6_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -119,7 +119,7 @@ const struct iris_platform_data qcs8300_data = { }; const struct iris_platform_data sm8550_data = { - .firmware_desc = &iris_vpu30_p4_gen2_desc, + .firmware_desc_gen2 = &iris_vpu30_p4_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -154,7 +154,7 @@ const struct iris_platform_data sm8550_data = { * - controller_rst_tbl to sm8650_controller_reset_table */ const struct iris_platform_data sm8650_data = { - .firmware_desc = &iris_vpu33_p4_gen2_desc, + .firmware_desc_gen2 = &iris_vpu33_p4_gen2_desc, .vpu_ops = &iris_vpu33_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -185,7 +185,7 @@ const struct iris_platform_data sm8650_data = { }; const struct iris_platform_data sm8750_data = { - .firmware_desc = &iris_vpu35_p4_gen2_desc, + .firmware_desc_gen2 = &iris_vpu35_p4_gen2_desc, .vpu_ops = &iris_vpu35_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), @@ -220,7 +220,7 @@ const struct iris_platform_data sm8750_data = { * - different num_vpp_pipe */ const struct iris_platform_data x1p42100_data = { - .firmware_desc = &iris_vpu30_p1_gen2_desc, + .firmware_desc_gen2 = &iris_vpu30_p1_gen2_desc, .vpu_ops = &iris_vpu3_ops, .icc_tbl = iris_icc_info_vpu3x, .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x), diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index c2dcb50a2782..7fe31136df21 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -251,8 +251,6 @@ static int iris_probe(struct platform_device *pdev) return core->irq; core->iris_platform_data = of_device_get_match_data(core->dev); - core->iris_firmware_desc = core->iris_platform_data->firmware_desc; - core->iris_firmware_data = core->iris_firmware_desc->firmware_data; core->ubwc_cfg = qcom_ubwc_config_get_data(); if (IS_ERR(core->ubwc_cfg)) @@ -271,8 +269,6 @@ static int iris_probe(struct platform_device *pdev) if (ret) return ret; - iris_session_init_caps(core); - ret = v4l2_device_register(dev, &core->v4l2_dev); if (ret) return ret; diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c index 1c052ca235be..fcbc60016bee 100644 --- a/drivers/media/platform/qcom/iris/iris_vidc.c +++ b/drivers/media/platform/qcom/iris/iris_vidc.c @@ -9,6 +9,7 @@ #include #include +#include "iris_ctrls.h" #include "iris_vidc.h" #include "iris_instance.h" #include "iris_vdec.h" @@ -196,6 +197,8 @@ int iris_open(struct file *filp) goto fail_m2m_release; } + iris_session_init_caps(core); + if (inst->domain == DECODER) ret = iris_vdec_inst_init(inst); else if (inst->domain == ENCODER) -- cgit From 6f62dcefd2494aa9ac01538372353bf07755491e Mon Sep 17 00:00:00 2001 From: Wangao Wang Date: Thu, 23 Jul 2026 10:55:55 +0800 Subject: media: qcom: iris: Add request key frame support for encoder Add request key frame support for both gen1 and gen2 encoders by enabling V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME. Reviewed-by: Bryan O'Donoghue Reviewed-by: Vishnu Reddy Signed-off-by: Wangao Wang Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 22 ++++++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 1 + drivers/media/platform/qcom/iris/iris_hfi_gen1.c | 10 ++++++++++ .../platform/qcom/iris/iris_hfi_gen1_command.c | 3 +++ .../platform/qcom/iris/iris_hfi_gen1_defines.h | 1 + drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 12 +++++++++++- .../platform/qcom/iris/iris_hfi_gen2_defines.h | 7 +++++++ .../platform/qcom/iris/iris_platform_common.h | 1 + 8 files changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 391e1fc5f6e2..2918a7df0b40 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -154,6 +154,8 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id) return LAYER4_BITRATE_HEVC; case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR: return LAYER5_BITRATE_HEVC; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + return REQUEST_SYNC_FRAME; default: return INST_FW_CAP_MAX; } @@ -297,6 +299,8 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id) return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR; case LAYER5_BITRATE_HEVC: return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR; + case REQUEST_SYNC_FRAME: + return V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME; default: return 0; } @@ -1477,6 +1481,24 @@ int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_typ &bitrate, sizeof(u32)); } +int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 hfi_val = 0; + + if (inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value) + hfi_val = HFI_SYNC_FRAME_REQUEST_WITH_PREFIX_SEQ_HDR; + else + hfi_val = HFI_SYNC_FRAME_REQUEST_WITHOUT_SEQ_HDR; + + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32_ENUM, + &hfi_val, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 3c462ec9190b..8d8bbef96007 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -47,6 +47,7 @@ int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type c int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c index eff9216bb501..0f7e7d6b25f4 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c @@ -375,6 +375,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = { CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_bitrate_gen1, }, + { + .cap_id = REQUEST_SYNC_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_req_sync_frame, + }, }; static const u32 sm8250_vdec_input_config_param_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c index 1c02f2eebdf7..f6e9cbfccd09 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c @@ -741,6 +741,9 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p packet->shdr.hdr.size += sizeof(u32); break; } + case HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME: { + break; + } default: return -EINVAL; } diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h index 0e4dee192384..275f3fea3d2a 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h @@ -157,6 +157,7 @@ #define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026 #define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001 #define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003 +#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME 0x2006004 #define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009 #define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a #define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008 diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index acc0ed8adda1..7d6fde36f0e9 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -937,7 +937,17 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, .set = iris_set_layer_bitrate, - } + }, + { + .cap_id = REQUEST_SYNC_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_REQUEST_SYNC_FRAME, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_req_sync_frame, + }, }; static const u32 sm8550_vdec_input_config_params_default[] = { diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index 776b21cd11b2..a8b47e4ceba0 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -90,6 +90,13 @@ enum hfi_layer_encoding_type { #define HFI_PROP_BITRATE_LAYER4 0x0300013f #define HFI_PROP_BITRATE_LAYER5 0x03000140 #define HFI_PROP_BITRATE_LAYER6 0x03000141 + +enum hfi_syncframe_request_mode { + HFI_SYNC_FRAME_REQUEST_WITHOUT_SEQ_HDR = 0x00000001, + HFI_SYNC_FRAME_REQUEST_WITH_PREFIX_SEQ_HDR = 0x00000002, +}; + +#define HFI_PROP_REQUEST_SYNC_FRAME 0x03000145 #define HFI_PROP_MAX_GOP_FRAMES 0x03000146 #define HFI_PROP_MAX_B_FRAMES 0x03000147 #define HFI_PROP_QUALITY_MODE 0x03000148 diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 980cbf5b5f2a..4a4ad3a9033f 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -182,6 +182,7 @@ enum platform_inst_fw_cap_type { LAYER3_BITRATE_HEVC, LAYER4_BITRATE_HEVC, LAYER5_BITRATE_HEVC, + REQUEST_SYNC_FRAME, INST_FW_CAP_MAX, }; -- cgit From 305b0615b685f44805557089142cc94534ce8465 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Mon, 6 Jul 2026 22:05:49 +0000 Subject: dt-bindings: media: qcom,milos-iris: Add Milos video codec Add binding for Qualcomm Milos Iris video codec. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Alexander Koskovich Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,milos-iris.yaml | 166 +++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/qcom,milos-iris.yaml diff --git a/Documentation/devicetree/bindings/media/qcom,milos-iris.yaml b/Documentation/devicetree/bindings/media/qcom,milos-iris.yaml new file mode 100644 index 000000000000..b0b0355ad52e --- /dev/null +++ b/Documentation/devicetree/bindings/media/qcom,milos-iris.yaml @@ -0,0 +1,166 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/qcom,milos-iris.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Milos SoC Iris video encoder and decoder + +maintainers: + - Alexander Koskovich + +description: + The Iris video processing unit on Qualcomm Milos SoC is a video encode and + decode accelerator. + +properties: + compatible: + enum: + - qcom,milos-iris + + clocks: + maxItems: 3 + + clock-names: + items: + - const: iface + - const: core + - const: vcodec0_core + + dma-coherent: true + + interconnects: + maxItems: 2 + + interconnect-names: + items: + - const: cpu-cfg + - const: video-mem + + iommus: + maxItems: 2 + + operating-points-v2: true + opp-table: + type: object + + power-domains: + maxItems: 4 + + power-domain-names: + items: + - const: venus + - const: vcodec0 + - const: cx + - const: mx + + resets: + maxItems: 2 + + reset-names: + items: + - const: bus + - const: core + +required: + - compatible + - dma-coherent + - interconnects + - interconnect-names + - iommus + - power-domain-names + - resets + - reset-names + +allOf: + - $ref: qcom,venus-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + #include + #include + + video-codec@aa00000 { + compatible = "qcom,milos-iris"; + reg = <0x0aa00000 0xf0000>; + + clocks = <&gcc GCC_VIDEO_AXI0_CLK>, + <&videocc VIDEO_CC_MVS0C_CLK>, + <&videocc VIDEO_CC_MVS0_CLK>; + clock-names = "iface", + "core", + "vcodec0_core"; + + dma-coherent; + iommus = <&apps_smmu 0x1960 0>, + <&apps_smmu 0x1967 0>; + + interconnects = <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &cnoc_cfg SLAVE_VENUS_CFG QCOM_ICC_TAG_ACTIVE_ONLY>, + <&mmss_noc MASTER_VIDEO QCOM_ICC_TAG_ALWAYS + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; + interconnect-names = "cpu-cfg", + "video-mem"; + + interrupts = ; + + operating-points-v2 = <&iris_opp_table>; + + memory-region = <&video_mem>; + + power-domains = <&videocc VIDEO_CC_MVS0C_GDSC>, + <&videocc VIDEO_CC_MVS0_GDSC>, + <&rpmhpd RPMHPD_CX>, + <&rpmhpd RPMHPD_MX>; + power-domain-names = "venus", + "vcodec0", + "cx", + "mx"; + + resets = <&gcc GCC_VIDEO_AXI0_CLK_ARES>, + <&videocc VIDEO_CC_MVS0C_CLK_ARES>; + reset-names = "bus", + "core"; + + iris_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-240000000 { + opp-hz = /bits/ 64 <240000000>; + required-opps = <&rpmhpd_opp_low_svs>, + <&rpmhpd_opp_svs>; + }; + + opp-338000000 { + opp-hz = /bits/ 64 <338000000>; + required-opps = <&rpmhpd_opp_svs>, + <&rpmhpd_opp_svs>; + }; + + opp-366000000 { + opp-hz = /bits/ 64 <366000000>; + required-opps = <&rpmhpd_opp_svs_l1>, + <&rpmhpd_opp_svs>; + }; + + opp-444000000 { + opp-hz = /bits/ 64 <444000000>; + required-opps = <&rpmhpd_opp_nom>, + <&rpmhpd_opp_svs_l1>; + }; + + opp-552000000 { + opp-hz = /bits/ 64 <552000000>; + required-opps = <&rpmhpd_opp_turbo>, + <&rpmhpd_opp_nom>; + }; + }; + }; -- cgit From a9aba94a7ba6d003744f6ba418fa789aa14343c6 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Mon, 6 Jul 2026 22:05:57 +0000 Subject: media: iris: Add support for Milos (VPU v2.0) Add support for the Milos Iris codec. This only supports the variant found on the SM7635-AB that has half of it's pipes disabled via efuse. Signed-off-by: Alexander Koskovich Reviewed-by: Dmitry Baryshkov Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 883 +++++++++++++++++++++ .../platform/qcom/iris/iris_platform_common.h | 2 + .../media/platform/qcom/iris/iris_platform_milos.h | 27 + .../media/platform/qcom/iris/iris_platform_vpu2.c | 35 + drivers/media/platform/qcom/iris/iris_probe.c | 4 + 5 files changed, 951 insertions(+) create mode 100644 drivers/media/platform/qcom/iris/iris_platform_milos.h diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 7d6fde36f0e9..619a54edb09f 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -11,8 +11,832 @@ #include "iris_vpu_buffer.h" #define VIDEO_ARCH_LX 1 +#define MILOS_BITRATE_MAX 100000000 #define BITRATE_MAX 245000000 +static const struct platform_inst_fw_cap inst_fw_cap_milos_dec[] = { + { + .cap_id = PROFILE_H264, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH), + .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = PROFILE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), + .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = PROFILE_VP9, + .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) | + BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2), + .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_H264, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5), + .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = LEVEL_VP9, + .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_VP9_LEVEL_5_0, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0), + .value = V4L2_MPEG_VIDEO_VP9_LEVEL_5_0, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = TIER, + .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH), + .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH, + .hfi_id = HFI_PROP_TIER, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_u32_enum, + }, + { + .cap_id = INPUT_BUF_HOST_MAX_COUNT, + .min = DEFAULT_MAX_HOST_BUF_COUNT, + .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, + .step_or_mask = 1, + .value = DEFAULT_MAX_HOST_BUF_COUNT, + .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = STAGE, + .min = STAGE_1, + .max = STAGE_2, + .step_or_mask = 1, + .value = STAGE_2, + .hfi_id = HFI_PROP_STAGE, + .set = iris_set_stage, + }, + { + .cap_id = PIPE, + /* .max, .min and .value are set via platform data */ + .step_or_mask = 1, + .hfi_id = HFI_PROP_PIPE, + .set = iris_set_pipe, + }, + { + .cap_id = POC, + .min = 0, + .max = 2, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE, + }, + { + .cap_id = CODED_FRAMES, + .min = CODED_FRAMES_PROGRESSIVE, + .max = CODED_FRAMES_PROGRESSIVE, + .step_or_mask = 0, + .value = CODED_FRAMES_PROGRESSIVE, + .hfi_id = HFI_PROP_CODED_FRAMES, + }, + { + .cap_id = BIT_DEPTH, + .min = BIT_DEPTH_8, + .max = BIT_DEPTH_10, + .step_or_mask = 1, + .value = BIT_DEPTH_8, + .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH, + }, + { + .cap_id = RAP_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 1, + .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, +}; + +static const struct platform_inst_fw_cap inst_fw_cap_milos_enc[] = { + { + .cap_id = PROFILE_H264, + .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH), + .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_profile, + }, + { + .cap_id = PROFILE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) | + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10), + .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .hfi_id = HFI_PROP_PROFILE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_profile, + }, + { + .cap_id = LEVEL_H264, + .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0, + .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) | + BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1), + .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_0, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_level, + }, + { + .cap_id = LEVEL_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1, + .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) | + BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5), + .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5, + .hfi_id = HFI_PROP_LEVEL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_level, + }, + { + .cap_id = STAGE, + .min = STAGE_1, + .max = STAGE_2, + .step_or_mask = 1, + .value = STAGE_2, + .hfi_id = HFI_PROP_STAGE, + .set = iris_set_stage, + }, + { + .cap_id = HEADER_MODE, + .min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE, + .max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | + BIT(V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME), + .value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + .hfi_id = HFI_PROP_SEQ_HEADER_MODE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_header_mode_gen2, + }, + { + .cap_id = PREPEND_SPSPPS_TO_IDR, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + }, + { + .cap_id = BITRATE, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_TOTAL_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_bitrate_gen2, + }, + { + .cap_id = BITRATE_PEAK, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_TOTAL_PEAK_BITRATE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_peak_bitrate, + }, + { + .cap_id = BITRATE_MODE, + .min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) | + BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR), + .value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + .hfi_id = HFI_PROP_RATE_CONTROL, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_bitrate_mode_gen2, + }, + { + .cap_id = FRAME_SKIP_MODE, + .min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, + .max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) | + BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT) | + BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT), + .value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = FRAME_RC_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 1, + }, + { + .cap_id = GOP_SIZE, + .min = 0, + .max = INT_MAX, + .step_or_mask = 1, + .value = 2 * DEFAULT_FPS - 1, + .hfi_id = HFI_PROP_MAX_GOP_FRAMES, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_u32, + }, + { + .cap_id = ENTROPY_MODE, + .min = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC, + .max = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) | + BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC), + .value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC, + .hfi_id = HFI_PROP_CABAC_SESSION, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_entropy_mode_gen2, + }, + { + .cap_id = MIN_FRAME_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + .hfi_id = HFI_PROP_MIN_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_min_qp, + }, + { + .cap_id = MIN_FRAME_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + .hfi_id = HFI_PROP_MIN_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_min_qp, + }, + { + .cap_id = MAX_FRAME_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + .hfi_id = HFI_PROP_MAX_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_max_qp, + }, + { + .cap_id = MAX_FRAME_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + .hfi_id = HFI_PROP_MAX_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_max_qp, + }, + { + .cap_id = I_FRAME_MIN_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = I_FRAME_MIN_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = P_FRAME_MIN_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = P_FRAME_MIN_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = B_FRAME_MIN_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = B_FRAME_MIN_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MIN_QP_8BIT, + }, + { + .cap_id = I_FRAME_MAX_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = I_FRAME_MAX_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = P_FRAME_MAX_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = P_FRAME_MAX_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = B_FRAME_MAX_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = B_FRAME_MAX_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = MAX_QP, + }, + { + .cap_id = I_FRAME_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = I_FRAME_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = P_FRAME_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = P_FRAME_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = B_FRAME_QP_H264, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = B_FRAME_QP_HEVC, + .min = MIN_QP_8BIT, + .max = MAX_QP, + .step_or_mask = 1, + .value = DEFAULT_QP, + .hfi_id = HFI_PROP_QP_PACKED, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_frame_qp, + }, + { + .cap_id = INPUT_BUF_HOST_MAX_COUNT, + .min = DEFAULT_MAX_HOST_BUF_COUNT, + .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, + .step_or_mask = 1, + .value = DEFAULT_MAX_HOST_BUF_COUNT, + .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, + .flags = CAP_FLAG_INPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = OUTPUT_BUF_HOST_MAX_COUNT, + .min = DEFAULT_MAX_HOST_BUF_COUNT, + .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT, + .step_or_mask = 1, + .value = DEFAULT_MAX_HOST_BUF_COUNT, + .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = ROTATION, + .min = 0, + .max = 270, + .step_or_mask = 90, + .value = 0, + .hfi_id = HFI_PROP_ROTATION, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_rotation, + }, + { + .cap_id = HFLIP, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_FLIP, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_flip, + }, + { + .cap_id = VFLIP, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_FLIP, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_flip, + }, + { + .cap_id = IR_TYPE, + .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM), + .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + }, + { + .cap_id = IR_PERIOD, + .min = 0, + .max = INT_MAX, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_ir_period_gen2, + }, + { + .cap_id = LTR_COUNT, + .min = 0, + .max = MAX_LTR_FRAME_COUNT_GEN2, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LTR_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_ltr_count_gen2, + }, + { + .cap_id = USE_LTR, + .min = 0, + .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1), + .step_or_mask = 0, + .value = 0, + .hfi_id = HFI_PROP_LTR_USE, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = MARK_LTR, + .min = INVALID_DEFAULT_MARK_OR_USE_LTR, + .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1), + .step_or_mask = 1, + .value = INVALID_DEFAULT_MARK_OR_USE_LTR, + .hfi_id = HFI_PROP_LTR_MARK, + .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_use_and_mark_ltr, + }, + { + .cap_id = B_FRAME, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_MAX_B_FRAMES, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_u32, + }, + { + .cap_id = LAYER_ENABLE, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .flags = CAP_FLAG_OUTPUT_PORT, + }, + { + .cap_id = LAYER_TYPE_H264, + .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_TYPE_HEVC, + .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B, + .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) | + BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P), + .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P, + .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, + .set = iris_set_layer_type, + }, + { + .cap_id = LAYER_COUNT_H264, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER_COUNT_HEVC, + .min = 0, + .max = 5, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_LAYER_COUNT, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_count_gen2, + }, + { + .cap_id = LAYER0_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_H264, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER0_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER1, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER1_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER2, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER2_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER3, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER3_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER4, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER4_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER5, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + }, + { + .cap_id = LAYER5_BITRATE_HEVC, + .min = 1, + .max = MILOS_BITRATE_MAX, + .step_or_mask = 1, + .value = BITRATE_DEFAULT, + .hfi_id = HFI_PROP_BITRATE_LAYER6, + .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT | + CAP_FLAG_DYNAMIC_ALLOWED, + .set = iris_set_layer_bitrate, + } +}; + static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = { { .cap_id = PROFILE_H264, @@ -1128,3 +1952,62 @@ const struct iris_firmware_data iris_hfi_gen2_data = { .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), }; + +const struct iris_firmware_data iris_hfi_milos_data = { + .init_hfi_ops = iris_hfi_gen2_sys_ops_init, + + .core_arch = VIDEO_ARCH_LX, + + .inst_fw_caps_dec = inst_fw_cap_milos_dec, + .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_milos_dec), + .inst_fw_caps_enc = inst_fw_cap_milos_enc, + .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_milos_enc), + + .dec_input_config_params_default = + sm8550_vdec_input_config_params_default, + .dec_input_config_params_default_size = + ARRAY_SIZE(sm8550_vdec_input_config_params_default), + .dec_input_config_params_hevc = + sm8550_vdec_input_config_param_hevc, + .dec_input_config_params_hevc_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_hevc), + .dec_input_config_params_vp9 = + sm8550_vdec_input_config_param_vp9, + .dec_input_config_params_vp9_size = + ARRAY_SIZE(sm8550_vdec_input_config_param_vp9), + .dec_output_config_params = + sm8550_vdec_output_config_params, + .dec_output_config_params_size = + ARRAY_SIZE(sm8550_vdec_output_config_params), + + .enc_input_config_params = + sm8550_venc_input_config_params, + .enc_input_config_params_size = + ARRAY_SIZE(sm8550_venc_input_config_params), + .enc_output_config_params = + sm8550_venc_output_config_params, + .enc_output_config_params_size = + ARRAY_SIZE(sm8550_venc_output_config_params), + + .dec_input_prop = sm8550_vdec_subscribe_input_properties, + .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties), + .dec_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc, + .dec_output_prop_avc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc), + .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc, + .dec_output_prop_hevc_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc), + .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9, + .dec_output_prop_vp9_size = + ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9), + + .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl, + .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl), + .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl, + .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl), + + .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl, + .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl), + .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl, + .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl), +}; diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index 4a4ad3a9033f..d8481a9689d5 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -52,7 +52,9 @@ enum pipe_type { extern const struct iris_firmware_data iris_hfi_gen1_data; extern const struct iris_firmware_data iris_hfi_gen2_data; +extern const struct iris_firmware_data iris_hfi_milos_data; +extern const struct iris_platform_data milos_data; extern const struct iris_platform_data qcs8300_data; extern const struct iris_platform_data sc7280_data; extern const struct iris_platform_data sm8250_data; diff --git a/drivers/media/platform/qcom/iris/iris_platform_milos.h b/drivers/media/platform/qcom/iris/iris_platform_milos.h new file mode 100644 index 000000000000..8a45a4aa9e86 --- /dev/null +++ b/drivers/media/platform/qcom/iris/iris_platform_milos.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __IRIS_PLATFORM_MILOS_H__ +#define __IRIS_PLATFORM_MILOS_H__ + +#define MILOS_MAXIMUM_FPS 240 + +static const struct icc_info iris_icc_info_milos[] = { + { "cpu-cfg", 1000, 1000 }, + { "video-mem", 1000, 10000000 }, +}; + +static const char * const milos_opp_pd_table[] = { "cx", "mx" }; + +static struct platform_inst_caps platform_inst_cap_milos = { + .min_frame_width = 96, + .max_frame_width = 4096, + .min_frame_height = 96, + .max_frame_height = 4096, + .max_mbpf = (4096 * 2176) / 256, + .mb_cycles_vsp = 25, + .mb_cycles_vpp = 200, + .max_frame_rate = MILOS_MAXIMUM_FPS, + .max_operating_rate = MILOS_MAXIMUM_FPS, +}; + +#endif diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c index 961dce2e6aa9..bbdbf21961d7 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c +++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c @@ -13,9 +13,16 @@ #include "iris_vpu_common.h" #include "iris_instance.h" +#include "iris_platform_milos.h" #include "iris_platform_sc7280.h" #include "iris_platform_sm8250.h" +static const struct iris_firmware_desc iris_milos_desc = { + .firmware_data = &iris_hfi_milos_data, + .get_vpu_buffer_size = iris_vpu_buf_size, + .fwname = "qcom/vpu/vpu20_p2_gen2_s7.mbn", +}; + static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = { .firmware_data = &iris_hfi_gen1_data, .get_vpu_buffer_size = iris_vpu_buf_size, @@ -70,6 +77,34 @@ static const struct tz_cp_config tz_cp_config_vpu2[] = { }, }; +const struct iris_platform_data milos_data = { + .firmware_desc_gen1 = &iris_milos_desc, + .vpu_ops = &iris_vpu2_ops, + .icc_tbl = iris_icc_info_milos, + .icc_tbl_size = ARRAY_SIZE(iris_icc_info_milos), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .pmdomain_tbl = iris_pmdomain_table_vpu2, + .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2), + .opp_pd_tbl = milos_opp_pd_table, + .opp_pd_tbl_size = ARRAY_SIZE(milos_opp_pd_table), + .clk_tbl = sm8250_clk_table, + .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table), + .opp_clk_tbl = sm8250_opp_clk_table, + .clk_rst_tbl = iris_clk_reset_table_vpu2, + .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2), + .dma_mask = 0xe0000000 - 1, + .inst_iris_fmts = iris_fmts_vpu2_dec, + .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec), + .inst_caps = &platform_inst_cap_milos, + .tz_cp_config_data = tz_cp_config_vpu2, + .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2), + .num_vpp_pipe = 2, + .max_session_count = 16, + .max_core_mbpf = ((4096 * 2176) / 256) * 2, + .max_core_mbps = ((3840 * 2176) / 256) * 30 + ((1920 * 1088) / 256) * 30, +}; + const struct iris_platform_data sc7280_data = { .firmware_desc_gen1 = &iris_vpu20_p1_gen1_desc, .firmware_desc_gen2 = &iris_vpu20_p1_gen2_s6_desc, diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c index 7fe31136df21..e4acf4a74f94 100644 --- a/drivers/media/platform/qcom/iris/iris_probe.c +++ b/drivers/media/platform/qcom/iris/iris_probe.c @@ -356,6 +356,10 @@ static const struct dev_pm_ops iris_pm_ops = { }; static const struct of_device_id iris_dt_match[] = { + { + .compatible = "qcom,milos-iris", + .data = &milos_data, + }, { .compatible = "qcom,qcs8300-iris", .data = &qcs8300_data, -- cgit From 6eec0f9b5c31f92f2ad659af5984e89b722dfcfb Mon Sep 17 00:00:00 2001 From: Gourav Kumar Date: Fri, 10 Jul 2026 08:24:04 +0530 Subject: media: iris: disable time-delta-based rate control for VBR The iris encoder driver was not sending HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL to the firmware during encoder initialization. Without this property, the firmware defaults to time-delta-based rate control (enabled), which calculates the output bitrate from actual frame timing rather than following the configured bitrate target. This caused variable bitrate (VBR) encoding to produce ~5x configured bitrate. For example, with video_bitrate=896000 (896 Kbps), the output is ~4.4 Mbps instead of the expected ~896 Kbps. Time-delta-based rate control is designed for variable frame rate (VFR) scenarios where the encoder adapts to actual frame timing. However, when an application explicitly configures a bitrate target, the firmware must follow that target regardless of frame timing. Fix this by adding the TIME_DELTA_BASED_RC capability with a default value of 0 (disabled) and sending HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL = 0 to the firmware during stream-on, allowing the firmware to use the configured bitrate as the target. Signed-off-by: Gourav Kumar Signed-off-by: Vishnu Reddy Reviewed-by: Vikash Garodia Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/iris/iris_ctrls.c | 19 +++++++++++++++++++ drivers/media/platform/qcom/iris/iris_ctrls.h | 1 + drivers/media/platform/qcom/iris/iris_hfi_gen2.c | 10 ++++++++++ .../media/platform/qcom/iris/iris_hfi_gen2_defines.h | 1 + .../media/platform/qcom/iris/iris_platform_common.h | 1 + 5 files changed, 32 insertions(+) diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c index 2918a7df0b40..bf17d310eac0 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.c +++ b/drivers/media/platform/qcom/iris/iris_ctrls.c @@ -1499,6 +1499,25 @@ int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_ty &hfi_val, sizeof(u32)); } +int iris_set_time_delta_based_rc(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id) +{ + const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; + u32 hfi_id = inst->fw_caps[cap_id].hfi_id; + u32 value = inst->fw_caps[cap_id].value; + + /* + * Disable time-delta-based rate control (value = 0). + * This overrides the firmware's default (enabled), ensuring the + * firmware uses the configured bitrate target rather than calculating + * bitrate from frame timing. + */ + return hfi_ops->session_set_property(inst, hfi_id, + HFI_HOST_FLAGS_NONE, + iris_get_port_info(inst, cap_id), + HFI_PAYLOAD_U32, + &value, sizeof(u32)); +} + int iris_set_properties(struct iris_inst *inst, u32 plane) { const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops; diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h index 8d8bbef96007..5180d53d3c90 100644 --- a/drivers/media/platform/qcom/iris/iris_ctrls.h +++ b/drivers/media/platform/qcom/iris/iris_ctrls.h @@ -48,6 +48,7 @@ int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_ int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_req_sync_frame(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); +int iris_set_time_delta_based_rc(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id); int iris_set_properties(struct iris_inst *inst, u32 plane); #endif diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c index 619a54edb09f..3b8fbefb8b93 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.c +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c @@ -1240,6 +1240,16 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = { .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU, .set = iris_set_bitrate_mode_gen2, }, + { + .cap_id = TIME_DELTA_BASED_RC, + .min = 0, + .max = 1, + .step_or_mask = 1, + .value = 0, + .hfi_id = HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL, + .flags = CAP_FLAG_OUTPUT_PORT, + .set = iris_set_time_delta_based_rc, + }, { .cap_id = FRAME_SKIP_MODE, .min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED, diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h index a8b47e4ceba0..f43aea10090d 100644 --- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h +++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h @@ -67,6 +67,7 @@ enum hfi_rate_control { }; #define HFI_PROP_RATE_CONTROL 0x0300012a +#define HFI_PROP_TIME_DELTA_BASED_RATE_CONTROL 0x0300012b #define HFI_PROP_QP_PACKED 0x0300012e #define HFI_PROP_MIN_QP_PACKED 0x0300012f #define HFI_PROP_MAX_QP_PACKED 0x03000130 diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h index d8481a9689d5..974809509146 100644 --- a/drivers/media/platform/qcom/iris/iris_platform_common.h +++ b/drivers/media/platform/qcom/iris/iris_platform_common.h @@ -185,6 +185,7 @@ enum platform_inst_fw_cap_type { LAYER4_BITRATE_HEVC, LAYER5_BITRATE_HEVC, REQUEST_SYNC_FRAME, + TIME_DELTA_BASED_RC, INST_FW_CAP_MAX, }; -- cgit From 32179a552b8ee4e4194c863f3f90eb9ede74792a Mon Sep 17 00:00:00 2001 From: Nihal Kumar Gupta Date: Thu, 23 Jul 2026 10:13:55 +0530 Subject: dt-bindings: media: qcom: Add Shikra CAMSS compatible Shikra contains the same Camera Subsystem IP as QCM2290. Document the platform-specific compatible string, using qcom,qcm2290-camss as fallback. Unlike QCM2290, Shikra omits the CDM and OPE blocks, requiring only a single IOMMU context bank instead of four. Reviewed-by: Bryan O'Donoghue Reviewed-by: Krzysztof Kozlowski Reviewed-by: Vladimir Zapolskiy Signed-off-by: Nihal Kumar Gupta Signed-off-by: Bryan O'Donoghue --- .../devicetree/bindings/media/qcom,qcm2290-camss.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-camss.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-camss.yaml index 391d0f6f67ef..490a7f3a8c5f 100644 --- a/Documentation/devicetree/bindings/media/qcom,qcm2290-camss.yaml +++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-camss.yaml @@ -14,7 +14,11 @@ description: properties: compatible: - const: qcom,qcm2290-camss + oneOf: + - items: + - const: qcom,shikra-camss + - const: qcom,qcm2290-camss + - const: qcom,qcm2290-camss reg: maxItems: 9 @@ -76,7 +80,14 @@ properties: - const: sf_mnoc iommus: - maxItems: 4 + oneOf: + - items: + - description: S1 HLOS VFE non-protected (VFE only) + - items: + - description: S1 HLOS VFE non-protected + - description: S1 HLOS CDM non-protected + - description: S1 HLOS OPE read non-protected + - description: S1 HLOS OPE write non-protected power-domains: items: -- cgit From 7b1734e1761258d78651263706182f1d772c0d3b Mon Sep 17 00:00:00 2001 From: Hungyu Lin Date: Thu, 4 Jun 2026 00:55:05 +0000 Subject: media: qcom: camss: Rename unused macro parameter The ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN() macro declares a parameter named 'c' but uses 'cid' in the macro body instead. Rename the parameter to match the identifier used in the macro body and silence the checkpatch warning: WARNING: Argument 'c' is not used in function-like macro No functional change intended. Reviewed-by: Bryan O'Donoghue Reviewed-by: Vladimir Zapolskiy Signed-off-by: Hungyu Lin Signed-off-by: Bryan O'Donoghue --- drivers/media/platform/qcom/camss/camss-ispif.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index aaf3caa42d33..20ccd7b1f11f 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -83,8 +83,8 @@ (0x270 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(m, n) \ (0x27c + 0x200 * (m) + 0x4 * (n)) -#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(c) \ - (1 << ((cid % 8) * 4)) +#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(cid) \ + (1 << (((cid) % 8) * 4)) #define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \ (0x2c0 + 0x200 * (m) + 0x4 * (n)) #define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \ -- cgit From b272a1780e8515617fed2564edccb8da8431d707 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 28 Jul 2026 10:24:30 +0100 Subject: media: i2c: imx471: Fix uninitialized error value in imx471_set_ctrl() The exposure and vertical blanking writes pass the address of the local ret variable to cci_write() as its error pointer, but there is no earlier error to propagate: each case is a single standalone write, like the other controls in the same switch that already pass NULL. In the exposure case ret is still uninitialized, so a non-zero stack value makes cci_write() return early without programming the register, and the control write reports a bogus status. The vertical blanking case is benign today because ret is zero there, but the construct is equally wrong. Pass NULL as the error pointer in both cases. Fixes: be1589e567ae ("media: i2c: imx471: Add Sony IMX471 image sensor driver") Suggested-by: Kate Hsuan Signed-off-by: David Carlier Reviewed-by: Kate Hsuan Reviewed-by: Tarang Raval Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx471.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c index 6d358b11e96d..4053aed84340 100644 --- a/drivers/media/i2c/imx471.c +++ b/drivers/media/i2c/imx471.c @@ -334,12 +334,12 @@ static int imx471_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_EXPOSURE: ret = cci_write(sensor->regmap, IMX471_REG_EXPOSURE, - ctrl->val, &ret); + ctrl->val, NULL); break; case V4L2_CID_VBLANK: /* Update FLL that meets expected vertical blanking */ ret = cci_write(sensor->regmap, IMX471_REG_FLL, - format->height + ctrl->val, &ret); + format->height + ctrl->val, NULL); break; case V4L2_CID_TEST_PATTERN: ret = cci_write(sensor->regmap, IMX471_REG_TEST_PATTERN, -- cgit From f1d1ed39ced825615aeac61f0b6a322178756632 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 17 Jul 2026 12:12:29 +0300 Subject: media: stm32: dcmi: fix some error handling bugs in probe() There are a few issues here: 1) After we assign: chan = dma_request_chan(&pdev->dev, "tx"); Then the error paths need to clean up before returning. The first error path does a direct return. 2) The error paths check "dcmi->mdma_chan" but that is not assigned until later so it results in memory leaks. Test "mdma_chan" instead. 3) The error handling calls dma_release_channel(dcmi->dma_chan) before "dcmi->dma_chan" has been assigned which leads to a NULL pointer dereference. Use the "chan" variable instead. I also moved the call to dma_release_channel() after the call to dma_release_channel() so it mirrors the allocation code better. Fixes: bc901885fae0 ("media: stm32: dcmi: perform dmaengine_slave_config at probe") Signed-off-by: Dan Carpenter Acked-by: Alain Volmat Signed-off-by: Sakari Ailus --- drivers/media/platform/st/stm32/stm32-dcmi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c index ad716cf0633e..aa79fe60ff0c 100644 --- a/drivers/media/platform/st/stm32/stm32-dcmi.c +++ b/drivers/media/platform/st/stm32/stm32-dcmi.c @@ -2024,8 +2024,10 @@ static int dcmi_probe(struct platform_device *pdev) mdma_chan = dma_request_chan(&pdev->dev, "mdma_tx"); if (IS_ERR(mdma_chan)) { ret = PTR_ERR(mdma_chan); - if (ret != -ENODEV) - return dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n"); + if (ret != -ENODEV) { + dev_err_probe(&pdev->dev, ret, "Failed to request MDMA channel\n"); + goto err_release_chan; + } mdma_chan = NULL; } @@ -2208,12 +2210,13 @@ err_device_unregister: err_media_device_cleanup: media_device_cleanup(&dcmi->mdev); err_mdma_slave_config: - if (dcmi->mdma_chan) + if (mdma_chan) gen_pool_free(dcmi->sram_pool, (unsigned long)dcmi->sram_buf, dcmi->sram_buf_size); err_dma_slave_config: - dma_release_channel(dcmi->dma_chan); - if (dcmi->mdma_chan) + if (mdma_chan) dma_release_channel(mdma_chan); +err_release_chan: + dma_release_channel(chan); return ret; } -- cgit From 4fdb0342f05eed8bd134e84859a4e89e60bd5bc1 Mon Sep 17 00:00:00 2001 From: Jake Steinman Date: Thu, 30 Jul 2026 10:24:14 -0400 Subject: media: ipu-bridge: Add Himax HM1092 IR sensor The HM1092 is a mono IR sensor used for face unlock on Intel IPU6 and IPU7 laptops. Without an entry here the sensor endpoint is built with no link-frequencies property, the driver rejects every mode and the sensor never binds. Two frequencies are listed because the sensor is driven from a different external clock depending on the platform, with the PLL programmed to match: 180480000 Hz on Dell (19.2 MHz EXTCLK) and 180000000 Hz on ASUS (24 MHz). Assisted-by: Claude:claude-opus-5 Signed-off-by: Jake Steinman Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 20cb133de826..f9efa501e8cf 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -49,6 +49,8 @@ * Please keep the list sorted by ACPI HID. */ static const struct ipu_sensor_config ipu_supported_sensors[] = { + /* Himax HM1092 */ + IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000), /* Himax HM11B1 */ IPU_SENSOR_CONFIG("HIMX11B1", 1, 384000000), /* Himax HM2170 */ -- cgit From edd0dd59aca038b7f32cf893170928d4a3b2297e Mon Sep 17 00:00:00 2001 From: Tarang Raval Date: Wed, 29 Jul 2026 11:28:00 +0530 Subject: platform: int3472: discrete: Support multiple HIDs per GPIO map entry Each int3472_gpio_map entry currently maps exactly one ACPI HID to a GPIO quirk. As more sensors needing the same quirk are identified, this means adding a full duplicate table entry per HID, differing only in the HID string, which does not scale. Change int3472_gpio_map::hid to a NULL-terminated hids array so a single entry can match any number of ACPI HIDs, letting new HIDs be added to the relevant array instead of duplicating quirk entries. Cc: linux-media@vger.kernel.org Suggested-by: Sakari Ailus Signed-off-by: Tarang Raval Tested-by: Kate Hsuan Reviewed-by: Kate Hsuan Reviewed-by: Hans de Goede Reviewed-by: Daniel Scally Signed-off-by: Sakari Ailus --- drivers/platform/x86/intel/int3472/discrete.c | 59 ++++++++++++++++++--------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index adff564bf3fd..6c729fcfce5d 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -123,10 +123,31 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, return desc; } +/* + * Other vana-supply users (e.g. ST, Toshiba, Sony sensors) can be added to + * this array instead of adding new quirk table entries. + */ +static const char * const power_enable_hids_vana[] = { + "SONY471A", /* imx471 on Lenovo X9-14 and X9-15 */ + "TBE20A0", /* imx471 on Lenovo X1 Carbon G14 */ + NULL +}; + +static const char * const power_enable_hids_vdd[] = { + "INT33F0", /* mt9m114 */ + NULL +}; + +static const char * const power_enable_hids_enable[] = { + "INT347E", /* ov7251 */ + NULL +}; + /** * struct int3472_gpio_map - Map GPIOs to whatever is expected by the * sensor driver (as in DT bindings) - * @hid: The ACPI HID of the device without the instance number e.g. INT347E + * @hids: NULL-terminated array of ACPI HIDs of the devices without the + * instance number e.g. INT347E * @type_from: The GPIO type from ACPI ?SDT * @type_to: The assigned GPIO type, typically same as @type_from * @enable_time_us: Enable time in usec for GPIOs mapped to regulators @@ -135,7 +156,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, * GPIO_ACTIVE_HIGH otherwise */ struct int3472_gpio_map { - const char *hid; + const char * const *hids; u8 type_from; u8 type_to; bool polarity_low; @@ -145,38 +166,27 @@ struct int3472_gpio_map { static const struct int3472_gpio_map int3472_gpio_map[] = { { /* mt9m114 designs declare a powerdown pin which controls the regulators */ - .hid = "INT33F0", + .hids = power_enable_hids_vdd, .type_from = INT3472_GPIO_TYPE_POWERDOWN, .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, .con_id = "vdd", .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, }, { /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */ - .hid = "INT347E", + .hids = power_enable_hids_enable, .type_from = INT3472_GPIO_TYPE_RESET, .type_to = INT3472_GPIO_TYPE_RESET, .con_id = "enable", }, { /* ov08x40's handshake pin needs a 45 ms delay on some HP laptops */ - .hid = "OVTI08F4", + .hids = (const char * const[]) { "OVTI08F4", NULL }, .type_from = INT3472_GPIO_TYPE_HANDSHAKE, .type_to = INT3472_GPIO_TYPE_HANDSHAKE, .con_id = "dvdd", .enable_time_us = 45 * USEC_PER_MSEC, }, - { /* imx471 expects "vana" as con_id for power enable */ - .hid = "SONY471A", - .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, - .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, - .con_id = "vana", - .enable_time_us = GPIO_REGULATOR_ENABLE_TIME, - }, - { - /* - * imx471 (on Lenovo ThinkPads X1 G14) expects "vana" as con_id - * for power enable - */ - .hid = "TBE20A0", + { /* Sensors which expect "vana" as con_id for power enable */ + .hids = power_enable_hids_vana, .type_from = INT3472_GPIO_TYPE_POWER_ENABLE, .type_to = INT3472_GPIO_TYPE_POWER_ENABLE, .con_id = "vana", @@ -184,6 +194,17 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { }, }; +static bool int3472_gpio_map_hids_match(struct acpi_device *adev, + const char * const *hids) +{ + for (unsigned int i = 0; hids[i]; i++) { + if (acpi_dev_hid_uid_match(adev, hids[i], NULL)) + return true; + } + + return false; +} + static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, const char **con_id, unsigned long *gpio_flags, unsigned int *enable_time_us) @@ -200,7 +221,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 if (*type != int3472_gpio_map[i].type_from) continue; - if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL)) + if (!int3472_gpio_map_hids_match(adev, int3472_gpio_map[i].hids)) continue; dev_dbg(int3472->dev, "mapping type 0x%02x pin to 0x%02x %s\n", -- cgit From 01498beb8d8651c9b7b67fb6bda1ed0f091a4d23 Mon Sep 17 00:00:00 2001 From: Antoine Bouyer Date: Sat, 27 Jun 2026 15:41:58 +0200 Subject: media: uapi: v4l2-isp: Add extensible statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the v4l2-isp extensible format introduced for isp parameters buffers to the support buffers of ISP statistic. Like for ISP configuration purpose, that will help supporting various ISP hardware versions reporting different statistics data with less impact on userspace. Rename all 'v4l2_isp_params' types to generic 'v4l2_isp' types to prepare to use them for statistics as well and maintain the existing types for compatibility with existing userspace only. Signed-off-by: Antoine Bouyer Reviewed-by: Niklas Söderlund [Rework to remove 'v4l2_isp_stats' and unify types] Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- include/uapi/linux/media/v4l2-isp.h | 125 +++++++++++++++++++++++------------- 1 file changed, 79 insertions(+), 46 deletions(-) diff --git a/include/uapi/linux/media/v4l2-isp.h b/include/uapi/linux/media/v4l2-isp.h index 779168f9058e..e4607e1217e1 100644 --- a/include/uapi/linux/media/v4l2-isp.h +++ b/include/uapi/linux/media/v4l2-isp.h @@ -13,25 +13,33 @@ #include /** - * enum v4l2_isp_params_version - V4L2 ISP parameters versioning + * enum v4l2_isp_version - V4L2 ISP serialization format versioning * - * @V4L2_ISP_PARAMS_VERSION_V0: First version of the V4L2 ISP parameters format - * (for compatibility) - * @V4L2_ISP_PARAMS_VERSION_V1: First version of the V4L2 ISP parameters format + * @V4L2_ISP_VERSION_V0: First version of the V4L2 ISP serialization format + * (for compatibility) + * @V4L2_ISP_VERSION_V1: First version of the V4L2 ISP serialization format * * V0 and V1 are identical in order to support drivers compatible with the V4L2 - * ISP parameters format already upstreamed which use either 0 or 1 as their - * versioning identifier. Both V0 and V1 refers to the first version of the - * V4L2 ISP parameters format. + * ISP format already upstreamed which use either 0 or 1 as their versioning + * identifier. Both V0 and V1 refers to the first version of the V4L2 ISP + * serialization format. * - * Future revisions of the V4L2 ISP parameters format should start from the + * Future revisions of the V4L2 ISP serialization format should start from the * value of 2. */ -enum v4l2_isp_params_version { - V4L2_ISP_PARAMS_VERSION_V0 = 0, - V4L2_ISP_PARAMS_VERSION_V1 +enum v4l2_isp_version { + V4L2_ISP_VERSION_V0 = 0, + V4L2_ISP_VERSION_V1 }; +/* + * Compatibility with existing users of v4l2_isp_params which pre-date the + * introduction of v4l2_isp_stats. + */ +#define v4l2_isp_params_version v4l2_isp_version +#define V4L2_ISP_PARAMS_VERSION_V0 V4L2_ISP_VERSION_V0 +#define V4L2_ISP_PARAMS_VERSION_V1 V4L2_ISP_VERSION_V1 + #define V4L2_ISP_PARAMS_FL_BLOCK_DISABLE (1U << 0) #define V4L2_ISP_PARAMS_FL_BLOCK_ENABLE (1U << 1) @@ -39,64 +47,89 @@ enum v4l2_isp_params_version { * Reserve the first 8 bits for V4L2_ISP_PARAMS_FL_* flag. * * Driver-specific flags should be defined as: - * #define DRIVER_SPECIFIC_FLAG0 ((1U << V4L2_ISP_PARAMS_FL_DRIVER_FLAGS(0)) - * #define DRIVER_SPECIFIC_FLAG1 ((1U << V4L2_ISP_PARAMS_FL_DRIVER_FLAGS(1)) + * #define DRIVER_SPECIFIC_FLAG0 ((1U << V4L2_ISP_FL_DRIVER_FLAGS(0)) + * #define DRIVER_SPECIFIC_FLAG1 ((1U << V4L2_ISP_FL_DRIVER_FLAGS(1)) */ -#define V4L2_ISP_PARAMS_FL_DRIVER_FLAGS(n) ((n) + 8) +#define V4L2_ISP_FL_DRIVER_FLAGS(n) ((n) + 8) /** - * struct v4l2_isp_params_block_header - V4L2 extensible parameters block header - * @type: The parameters block type (driver-specific) + * struct v4l2_isp_block_header - V4L2 extensible block header + * @type: The parameters or statistics block type (driver-specific) * @flags: A bitmask of block flags (driver-specific) - * @size: Size (in bytes) of the parameters block, including this header + * @size: Size (in bytes) of the block, including this header * - * This structure represents the common part of all the ISP configuration - * blocks. Each parameters block shall embed an instance of this structure type - * as its first member, followed by the block-specific configuration data. + * This structure represents the common part of all the ISP configuration or + * statistic blocks. Each block shall embed an instance of this structure type + * as its first member, followed by the block-specific configuration or + * statistic data. * * The @type field is an ISP driver-specific value that identifies the block - * type. The @size field specifies the size of the parameters block. + * type. The @size field specifies the size of the block, including this + * header. * - * The @flags field is a bitmask of per-block flags V4L2_PARAMS_ISP_FL_* and - * driver-specific flags specified by the driver header. + * The @flags field is a bitmask of per-block flags. If a block is used for + * configuration parameters this field can be a combination of + * V4L2_ISP_PARAMS_FL_* and driver-specific flags. If a block is used + * for statistics this fields is used to report optional + * driver-specific flags, if any. */ -struct v4l2_isp_params_block_header { +struct v4l2_isp_block_header { __u16 type; __u16 flags; __u32 size; } __attribute__((aligned(8))); /** - * struct v4l2_isp_params_buffer - V4L2 extensible parameters configuration - * @version: The parameters buffer version (driver-specific) - * @data_size: The configuration data effective size, excluding this header - * @data: The configuration data + * v4l2_isp_params_block_header - V4L2 extensible parameters block header + * + * Compatibility with existing users of v4l2_isp_params_block_header + * which pre-date the introduction of v4l2_isp_block_header. + */ +#define v4l2_isp_params_block_header v4l2_isp_block_header + +/** + * struct v4l2_isp_buffer - V4L2 extensible buffer + * @version: The extensible buffer version (driver-specific) + * @data_size: The data effective size, excluding this header + * @data: The configuration or statistics data * - * This structure contains the configuration parameters of the ISP algorithms, - * serialized by userspace into a data buffer. Each configuration parameter - * block is represented by a block-specific structure which contains a - * :c:type:`v4l2_isp_params_block_header` entry as first member. Userspace - * populates the @data buffer with configuration parameters for the blocks that - * it intends to configure. As a consequence, the data buffer effective size - * changes according to the number of ISP blocks that userspace intends to - * configure and is set by userspace in the @data_size field. + * This structure contains ISP configuration parameters or ISP hardware + * statistics serialized into a data buffer. Each block is represented by a + * block-specific structure which contains a :c:type:`v4l2_isp_block_header` + * entry as first member. * - * The parameters buffer is versioned by the @version field to allow modifying - * and extending its definition. Userspace shall populate the @version field to - * inform the driver about the version it intends to use. The driver will parse - * and handle the @data buffer according to the data layout specific to the - * indicated version and return an error if the desired version is not + * When used for ISP parameters, userspace populates the @data buffer with + * configuration parameters for the blocks that it intends to configure. As a + * consequence, the data buffer effective size changes according to the number + * of ISP blocks that userspace intends to configure. + * + * When used to report ISP statistics, the driver populates the @data buffer + * with statistics for each supported measurement block. + * + * The buffer is versioned by the @version field to allow modifying + * and extending its definition. The writer shall populate the @version field + * to inform the reader about the version it intends to use. The reader will + * parse and handle the @data buffer according to the data layout specific to + * the indicated version and return an error if the desired version is not * supported. * - * For each ISP block that userspace wants to configure, a block-specific - * structure is appended to the @data buffer, one after the other without gaps - * in between. Userspace shall populate the @data_size field with the effective - * size, in bytes, of the @data buffer. + * For each ISP block, a block-specific structure is appended to the @data + * buffer, one after the other without gaps in between. The writer shall + * populate the @data_size field with the effective size, in bytes, of the + * @data buffer. */ -struct v4l2_isp_params_buffer { +struct v4l2_isp_buffer { __u32 version; __u32 data_size; __u8 data[] __counted_by(data_size); }; +/** + * v4l2_isp_params_buffer - V4L2 extensible parameters compatibility + * + * Compatibility with existing users of v4l2_isp_params_buffer which + * pre-date the introduction of v4l2_isp_buffer. + */ +#define v4l2_isp_params_buffer v4l2_isp_buffer + #endif /* _UAPI_V4L2_ISP_H_ */ -- cgit From 46479e54759d96153c66a5caf30fdb989e6f046d Mon Sep 17 00:00:00 2001 From: Antoine Bouyer Date: Sat, 27 Jun 2026 15:41:59 +0200 Subject: media: Documentation: uapi: Update V4L2 ISP for extensible stats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the userspace-api documentation of V4L2 ISP to support statistics. Update all occurences of 'v4l2_isp_param' types to match the uAPI changes and add a section to document the statistics serialization format. Signed-off-by: Antoine Bouyer Reviewed-by: Niklas Söderlund [Update on uAPI changes] Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- Documentation/userspace-api/media/v4l/v4l2-isp.rst | 45 ++++++++++++++++------ 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/Documentation/userspace-api/media/v4l/v4l2-isp.rst b/Documentation/userspace-api/media/v4l/v4l2-isp.rst index facf6dba1ca7..8a3f05b5e85f 100644 --- a/Documentation/userspace-api/media/v4l/v4l2-isp.rst +++ b/Documentation/userspace-api/media/v4l/v4l2-isp.rst @@ -18,22 +18,22 @@ single C structure that contains a header, followed by a binary buffer where userspace programs a variable number of ISP configuration data block, one for each supported ISP feature. -The :c:type:`v4l2_isp_params_buffer` structure defines the buffer header which -is followed by a binary buffer of ISP configuration data. Userspace shall -correctly populate the buffer header with the generic parameters format version -and with the size (in bytes) of the binary data buffer where it will store the -ISP blocks configuration. - -Each *ISP configuration block* is preceded by an header implemented by the -:c:type:`v4l2_isp_params_block_header` structure, followed by the configuration +The :c:type:`v4l2_isp_buffer` structure defines the buffer header which is +followed by a binary buffer of ISP configuration data. Userspace shall correctly +populate the buffer header with the serialization format version and with the +size (in bytes) of the binary data buffer where it will store the ISP blocks +configuration. + +Each *ISP configuration block* is preceded by a header implemented by the +:c:type:`v4l2_isp_block_header` structure, followed by the configuration parameters for that specific block, defined by the ISP driver specific data types. Userspace applications are responsible for correctly populating each block's header fields (type, flags and size) and the block-specific parameters. -ISP block enabling, disabling and configuration ------------------------------------------------ +ISP parameters block enabling, disabling and configuration +---------------------------------------------------------- When userspace wants to configure and enable an ISP block it shall fully populate the block configuration and set the V4L2_ISP_PARAMS_FL_BLOCK_ENABLE @@ -59,7 +59,30 @@ definition without invalidating the existing ones. ISP statistics ============== -Support for generic statistics format is not yet implemented in Video4Linux2. +The generic ISP statistics format is identical to the generic ISP configuration +parameters format. It is realized by defining a C structure that contains a +header, followed by binary buffer where the ISP driver copies a variable number +of ISP statistics blocks. + +Extensible statistics buffers have :c:type:`v4l2_isp_buffer` header followed by +a binary buffer of ISP statistics data. ISP drivers populate the buffer header +with the serialization format version and with the size (in bytes) of the binary +data buffer where ISP statistics data are serialized. Applications shall +validate that the serialization format version matches the expected one and that +the buffer size doesn't exceed the maximum size for a statistics buffer as +declared by the driver's uAPI header. + +Each *ISP statistics block* is preceded by a header implemented by the +:c:type:`v4l2_isp_block_header` structure, followed by the statistics data for +that specific block. The driver might optionally report platform-specific flags +associated with each statistics block. + +Applications inspect the statistics block type as reported in the header and +validates the reported size matches the block's expected size before accessing +the ISP statistics data. + +Extension to the statistics format can be implemented by adding new blocks +definition without invalidating the existing ones. V4L2 ISP uAPI data types ======================== -- cgit From 7041b0ebf178b03d1182c7a73c21e9adcb8deb6a Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 27 Jun 2026 15:42:00 +0200 Subject: media: v4l2-isp: Rename v4l2_isp_params_buffer_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename v4l2_isp_params_buffer_size() to v4l2_isp_buffer_size() to support statistics. Reviewed-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/arm/mali-c55/mali-c55-params.c | 12 ++++++------ include/media/v4l2-isp.h | 22 +++++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c index e53c7b512dbd..70106276b7e4 100644 --- a/drivers/media/platform/arm/mali-c55/mali-c55-params.c +++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c @@ -615,7 +615,7 @@ static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh, { static const struct v4l2_meta_format mfmt = { .dataformat = V4L2_META_FMT_MALI_C55_PARAMS, - .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), + .buffersize = v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE), }; f->fmt.meta = mfmt; @@ -668,13 +668,13 @@ mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, if (*num_planes && *num_planes > 1) return -EINVAL; - if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)) + if (sizes[0] && sizes[0] < v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE)) return -EINVAL; *num_planes = 1; if (!sizes[0]) - sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE); + sizes[0] = v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE); return 0; } @@ -684,7 +684,7 @@ static int mali_c55_params_buf_init(struct vb2_buffer *vb) struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf); - buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE), + buf->config = kvmalloc(v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE), GFP_KERNEL); if (!buf->config) return -ENOMEM; @@ -711,7 +711,7 @@ static int mali_c55_params_buf_prepare(struct vb2_buffer *vb) int ret; ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb, - v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); + v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); if (ret) return ret; @@ -721,7 +721,7 @@ static int mali_c55_params_buf_prepare(struct vb2_buffer *vb) * changed to the buffer content whilst the driver processes it. */ - memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); + memcpy(buf->config, config, v4l2_isp_buffer_size(MALI_C55_PARAMS_MAX_SIZE)); return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config, mali_c55_params_block_types_info, diff --git a/include/media/v4l2-isp.h b/include/media/v4l2-isp.h index f3a6d0edcb24..d70ed6b431e7 100644 --- a/include/media/v4l2-isp.h +++ b/include/media/v4l2-isp.h @@ -15,17 +15,21 @@ struct device; struct vb2_buffer; /** - * v4l2_isp_params_buffer_size - Calculate size of v4l2_isp_params_buffer - * @max_params_size: The total size of the ISP configuration blocks + * v4l2_isp_buffer_size - Calculate size of v4l2_isp_buffer + * @max_size: The total size of the ISP configuration or statistics blocks + * + * Users of v4l2-isp will have differing sized data arrays for parameters and + * statistics, depending on their specific blocks. Drivers need to be able to + * calculate the appropriate size of the buffer to accommodate all ISP blocks + * supported by the platform. This macro provides a convenient tool for the + * calculation. + * + * The intended users of this function are drivers initializing the size + * of their metadata (parameters and statistics) buffers. * - * Users of the v4l2 extensible parameters will have differing sized data arrays - * depending on their specific parameter buffers. Drivers and userspace will - * need to be able to calculate the appropriate size of the struct to - * accommodate all ISP configuration blocks provided by the platform. - * This macro provides a convenient tool for the calculation. */ -#define v4l2_isp_params_buffer_size(max_params_size) \ - (offsetof(struct v4l2_isp_params_buffer, data) + (max_params_size)) +#define v4l2_isp_buffer_size(max_size) \ + (offsetof(struct v4l2_isp_buffer, data) + (max_size)) /** * v4l2_isp_params_validate_buffer_size - Validate a V4L2 ISP buffer sizes -- cgit From cad05a2e235972aab82bcc5f4eb47e9176418a88 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 27 Jun 2026 15:42:01 +0200 Subject: media: v4l2-isp: Add per-block validation callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers are expected to provide to the helper function v4l2_isp_params_validate_buffer() a list of 'struct v4l2_isp_params_block_type_info' entries, one for each supported ISP block. The type 'struct v4l2_isp_params_block_type_info' so far only contained the expected block size for the core framework to validate the declared block size against the expected one. For some blocks, drivers might want to implement more precise per-block validations. Add a function pointer member to 'struct v4l2_isp_params_block_type_info' to allow drivers to register a callback and call it from the core framework during validation. Reviewed-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-isp.c | 3 +++ include/media/v4l2-isp.h | 11 ++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-isp.c b/drivers/media/v4l2-core/v4l2-isp.c index 29831f7032e9..f497471e9f18 100644 --- a/drivers/media/v4l2-core/v4l2-isp.c +++ b/drivers/media/v4l2-core/v4l2-isp.c @@ -114,6 +114,9 @@ int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb, return -EINVAL; } + if (info->block_validate && info->block_validate(dev, block)) + return -EINVAL; + block_offset += block->size; buffer_size -= block->size; } diff --git a/include/media/v4l2-isp.h b/include/media/v4l2-isp.h index d70ed6b431e7..1f35a52f978a 100644 --- a/include/media/v4l2-isp.h +++ b/include/media/v4l2-isp.h @@ -55,17 +55,22 @@ int v4l2_isp_params_validate_buffer_size(struct device *dev, /** * struct v4l2_isp_params_block_type_info - V4L2 ISP per-block-type info * @size: the block type expected size + * @block_validate: driver's callback to implement per-block validation * * The v4l2_isp_params_block_type_info collects information of the ISP - * configuration block types for validation purposes. It currently only contains - * the expected block type size. + * configuration block types for validation purposes. It contains the expected + * block type size and a function pointer where drivers can register a callback + * for additional per-block validation purposes. The validation function is + * expected to return 0 on success or a negative error number for errors. * * Drivers shall prepare a list of block type info, indexed by block type, one * for each supported ISP block type and correctly populate them with the - * expected block type size. + * expected block type size and the optional callback. */ struct v4l2_isp_params_block_type_info { size_t size; + int (*block_validate)(struct device *dev, + const struct v4l2_isp_block_header *block); }; /** -- cgit From 07a4324a81196f4b475b566a79f91f111b2fc4be Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 27 Jun 2026 15:42:02 +0200 Subject: media: amlogic-c3: Implement per-block validation Use the newly introduced per-block validation in v4l2-isp framework to implement a validation callback for the AWB and AE configuration blocks to make sure the configured metering zones do not exceed the hardware capabilities. Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/amlogic/c3/isp/c3-isp-params.c | 42 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c index aec3eed0e443..ae0777a20bda 100644 --- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c +++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c @@ -535,16 +535,52 @@ static const c3_isp_block_handler c3_isp_params_handlers[] = { [C3_ISP_PARAMS_BLOCK_BLC] = c3_isp_params_cfg_blc, }; -#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \ +static int +c3_isp_params_awb_config_validate(struct device *dev, + const struct v4l2_isp_block_header *block) +{ + const struct c3_isp_params_awb_config *cfg = + (const struct c3_isp_params_awb_config *)block; + + if (cfg->horiz_zones_num * cfg->vert_zones_num > C3_ISP_AWB_MAX_ZONES) { + dev_dbg(dev, "Invalid number of AWB measurement zones\n"); + return -EINVAL; + } + + return 0; +} + +static int +c3_isp_params_ae_config_validate(struct device *dev, + const struct v4l2_isp_block_header *block) +{ + const struct c3_isp_params_ae_config *cfg = + (const struct c3_isp_params_ae_config *)block; + + if (cfg->horiz_zones_num * cfg->vert_zones_num > C3_ISP_AE_MAX_ZONES) { + dev_dbg(dev, "Invalid number of AE measurement zones\n"); + return -EINVAL; + } + + return 0; +} + +#define C3_ISP_PARAMS_BLOCK_INFO_CBK(block, data, cbk) \ [C3_ISP_PARAMS_BLOCK_ ## block] = { \ .size = sizeof(struct c3_isp_params_ ## data), \ + .block_validate = (cbk)\ } +#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \ + C3_ISP_PARAMS_BLOCK_INFO_CBK(block, data, NULL) + static const struct v4l2_isp_params_block_type_info c3_isp_params_block_types_info[] = { C3_ISP_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains), - C3_ISP_PARAMS_BLOCK_INFO(AWB_CONFIG, awb_config), - C3_ISP_PARAMS_BLOCK_INFO(AE_CONFIG, ae_config), + C3_ISP_PARAMS_BLOCK_INFO_CBK(AWB_CONFIG, awb_config, + &c3_isp_params_awb_config_validate), + C3_ISP_PARAMS_BLOCK_INFO_CBK(AE_CONFIG, ae_config, + &c3_isp_params_ae_config_validate), C3_ISP_PARAMS_BLOCK_INFO(AF_CONFIG, af_config), C3_ISP_PARAMS_BLOCK_INFO(PST_GAMMA, pst_gamma), C3_ISP_PARAMS_BLOCK_INFO(CCM, ccm), -- cgit From 4ade3af89b2facff395467af1a5d953ea9dfc181 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Sat, 27 Jun 2026 15:42:03 +0200 Subject: media: v4l2-isp: Add helpers for stats buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two helper functions to v4l2-isp to handle statistics: - v4l2_isp_stats_init_buffer() to initialize a statistics buffer - v4l2_isp_stats_init_block() to initialize a statistics block in the next available memory location of a buffer The v4l2_isp_stats_init_buffer() resets the data size counter of the buffer and initializes its 'version' field. The v4l2_isp_stats_init_block() helper accepts the type of the stats block about to be populated, an array of per-block-type information and the maximum size of the v4l2-isp buffer. If enough space for the new block is available, the function increments the v4l2_isp_buffer.data_size counter, initializes the new stats block header and returns a pointer to the block for the driver to populate it. Reviewed-by: Niklas Söderlund Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-isp.c | 52 +++++++++++++++++++++++++++++++++ include/media/v4l2-isp.h | 59 +++++++++++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-isp.c b/drivers/media/v4l2-core/v4l2-isp.c index f497471e9f18..1eb46e080afa 100644 --- a/drivers/media/v4l2-core/v4l2-isp.c +++ b/drivers/media/v4l2-core/v4l2-isp.c @@ -130,6 +130,58 @@ int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb, } EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer); +void v4l2_isp_stats_init_buffer(struct v4l2_isp_buffer *buf, + enum v4l2_isp_version version) +{ + if (WARN_ON(!buf)) + return; + + if (WARN_ON(version > V4L2_ISP_VERSION_V1)) + return; + + buf->version = version; + buf->data_size = 0; +} +EXPORT_SYMBOL_GPL(v4l2_isp_stats_init_buffer); + +struct v4l2_isp_block_header * +v4l2_isp_stats_init_block(struct device *dev, struct v4l2_isp_buffer *buf, + const struct v4l2_isp_stats_block_type_info *type_info, + size_t num_block_types, unsigned int block_type, + size_t max_size) +{ + const struct v4l2_isp_stats_block_type_info *block_info; + struct v4l2_isp_block_header *header; + size_t used; + + if (WARN_ON(!dev || !buf || !type_info)) + return ERR_PTR(-EINVAL); + + if (block_type >= num_block_types) { + dev_err(dev, "Invalid block type %u\n", block_type); + return ERR_PTR(-EINVAL); + } + + block_info = &type_info[block_type]; + used = buf->data_size; + + if (used + block_info->size > max_size) { + dev_err(dev, "No space for stats block type %u of size %zu\n", + block_type, block_info->size); + return ERR_PTR(-ENOMEM); + } + + buf->data_size += block_info->size; + + header = (struct v4l2_isp_block_header *)&buf->data[used]; + header->type = block_type; + header->size = block_info->size; + header->flags = 0; + + return header; +} +EXPORT_SYMBOL_GPL(v4l2_isp_stats_init_block); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jacopo Mondi Date: Thu, 30 Jul 2026 19:12:29 +0200 Subject: media: Add RPPX1_PARAMS and RPPX1_STATS meta formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register V4L2 metadata fourcc codes for the Dreamchip RPP-X1 ISP parameters and statistics buffers. These formats are used by the driver to exchange ISP configuration and 3A statistics with userspace through the extensible parameters framework. Signed-off-by: Jai Luthra Reviewed-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Niklas Söderlund Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-ioctl.c | 2 ++ include/uapi/linux/videodev2.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index a2b650f4ec3c..17ba1ae70735 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1471,6 +1471,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break; case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break; case V4L2_META_FMT_MALI_C55_STATS: descr = "ARM Mali-C55 ISP 3A Statistics"; break; + case V4L2_META_FMT_RPPX1_PARAMS: descr = "Dreamchip RPP-X1 ISP Parameters"; break; + case V4L2_META_FMT_RPPX1_STATS: descr = "Dreamchip RPP-X1 ISP Statistics"; break; case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break; case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break; diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index eda4492e40dc..5373dba640fa 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -889,6 +889,10 @@ struct v4l2_pix_format { #define V4L2_META_FMT_MALI_C55_PARAMS v4l2_fourcc('C', '5', '5', 'P') /* ARM Mali-C55 Parameters */ #define V4L2_META_FMT_MALI_C55_STATS v4l2_fourcc('C', '5', '5', 'S') /* ARM Mali-C55 3A Statistics */ +/* Vendor specific - used for Dreamchip RPP-X1 ISP */ +#define V4L2_META_FMT_RPPX1_PARAMS v4l2_fourcc('D', 'R', '1', 'P') /* Dreamchip RPP-X1 Parameters */ +#define V4L2_META_FMT_RPPX1_STATS v4l2_fourcc('D', 'R', '1', 'S') /* Dreamchip RPP-X1 Statistics */ + #ifdef __KERNEL__ /* * Line-based metadata formats. Remember to update v4l_fill_fmtdesc() when -- cgit From d2655e8b788f2e1cd0c601c4bd582831f04de7d8 Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Thu, 30 Jul 2026 19:12:30 +0200 Subject: media: uapi: Add extensible param and stats blocks for RPPX1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the scaffolding for the parameters and statistics buffers for Dreamchip RPPX1. The parameters for each ISP function block will be added together with the logic for it. In addition to the scaffolding add a common struct that describes the measurement window. This struct is not specific to any block and will used by many of them. Signed-off-by: Jai Luthra [Niklas: Commit message] Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- include/uapi/linux/media/dreamchip/rppx1-config.h | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 include/uapi/linux/media/dreamchip/rppx1-config.h diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h new file mode 100644 index 000000000000..1f2548db73ad --- /dev/null +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Dreamchip RPP-X1 ISP Driver - Userspace API + * + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#ifndef __UAPI_RPP_X1_CONFIG_H +#define __UAPI_RPP_X1_CONFIG_H + +#include + +/** + * struct rppx1_window - Measurement window + * + * RPP-X1 measurement window. Different blocks use a window or multiple + * windows for measurement purposes. This defines a common type for all of + * them. The number of relevant bits depends on the block where the window is + * used and is specified in the per-block description + * + * @h_offs: horizontal offset from the left of the frame in pixels + * @v_offs: vertical offset from the top of the frame in pixels + * @h_size: horizontal size of the window in pixels + * @v_size: vertical size of the window in pixels + */ +struct rppx1_window { + __u16 h_offs; + __u16 v_offs; + __u16 h_size; + __u16 v_size; +}; + +/* --------------------------------------------------------------------------- + * Parameter Structures + * + * The same ISP block might be instantiated in multiple pipeliness and operate + * on a different bitdepth/precision. For fields of varying length among + * different instances of the same block, use a data type that can accommodate + * the larger bitdepth/precision. + */ + +/** + * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks + * + * Some types are reported twice as the same block might be instantiated in + * multiple pipes. + */ +#define RPPX1_PARAMS_MAX_SIZE 0 + +/* --------------------------------------------------------------------------- + * Statistics Structures + * + * The same ISP block might be instantiated in multiple pipeliness and operate + * on a different bitdepth/precision. For fields of varying length among + * different instances of the same block, use a data type that can accommodate + * the larger bitdepth/precision. + */ + +/** + * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics + * + * Some types are reported twice as the same block might be instantiated in + * multiple pipes. + */ +#define RPPX1_STATS_MAX_SIZE 0 + +#endif /* __UAPI_RPP_X1_CONFIG_H */ -- cgit From b562e72bdb987c9d67bb394c0f8c10f12504f947 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:31 +0200 Subject: media: rppx1: Add framework to support Dreamchip RPPX1 ISP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a framework driver for Dreamchip RPPX1 ISP. The driver aims to provide a framework for other V4L2 based drivers to drive the RPPX1 functionality. The reason for this split is that the RPPX1 IP itself do not provide any DMA engines to drive data to/from the device, instead it depends on other IP blocks to implement these features. While the peripherals around the RPPX1 ISP used in different designs and by different vendors are different the RPPX1 core itself is the same. For this reason the framework solution to be able to split the Dreamchip RPPX1 driver from vendors usage of it have been picked in hope to reduce duplication of the common parts. The design is to try and keep the surface of this framework as small as possible. The intention of this change is to be able to fill all needs of this. * Two functions to create and destroy a RPPX1 instance, rppx1_create() and rppx1_destory(). These are intended to be called in the users probe and remove code paths. * Two functions to start and stop the RPPX1 processing, rppx1_start() and rppx1_stop(). These are intended to be called in the users stream on and stream off code paths. * One function to ask the RPPX1 to process parameters buffer prepared by user space, rppx1_params(). The intention is to call this function when the parameter buffer is queued to the V4L2 driver and the result stored by the driver until the time it needs to be written to the RPPX1. It's the users responsibility to write it either using MMIO or other means. * One function to fill in a statistic buffer based on the current status of the RPPX1, rppx1_stats_fill_isr(). The intention is that the user call's this in its interrupt handler when it knows the RPPX1 is done processing a frame. * One function to ack and retrieve the interrupts generated by the RPPX1, rppx1_interrupt(). The intention is to call this function when the users interrupt handler detects the RPPX1 have raised and interrupt. There is no need for the user to understand, or act, on the actual RPPX1 interrupt, but it can if it wants too. The initial support in the framework is limited and do not implement any ISP processing algorithms other then configuring the RPPX1 to process any Bayer (8-, 10, or 12-bit) image and produce either a RGB or YUYV output. It do however probe all function blocks of the RPPX1 and provide an interface to interact with both parameter and statistic bufferers. The user of the framework will not change as algorithms for the different function blocks of the ISP are being added. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../userspace-api/media/v4l/meta-formats.rst | 1 + .../userspace-api/media/v4l/metafmt-rppx1.rst | 99 ++++++ MAINTAINERS | 10 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 1 + drivers/media/platform/dreamchip/Kconfig | 3 + drivers/media/platform/dreamchip/Makefile | 6 + drivers/media/platform/dreamchip/rppx1/Kconfig | 12 + drivers/media/platform/dreamchip/rppx1/Makefile | 33 ++ .../media/platform/dreamchip/rppx1/rpp_module.c | 39 +++ .../media/platform/dreamchip/rppx1/rpp_module.h | 107 ++++++ .../media/platform/dreamchip/rppx1/rpp_params.c | 71 ++++ drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 28 ++ drivers/media/platform/dreamchip/rppx1/rppx1.c | 381 +++++++++++++++++++++ drivers/media/platform/dreamchip/rppx1/rppx1.h | 105 ++++++ drivers/media/platform/dreamchip/rppx1/rppx1_acq.c | 148 ++++++++ .../media/platform/dreamchip/rppx1/rppx1_awbg.c | 31 ++ drivers/media/platform/dreamchip/rppx1/rppx1_bd.c | 53 +++ .../media/platform/dreamchip/rppx1/rppx1_bdrgb.c | 81 +++++ drivers/media/platform/dreamchip/rppx1/rppx1_bls.c | 60 ++++ drivers/media/platform/dreamchip/rppx1/rppx1_cac.c | 30 ++ .../media/platform/dreamchip/rppx1/rppx1_ccor.c | 105 ++++++ drivers/media/platform/dreamchip/rppx1/rppx1_db.c | 45 +++ .../media/platform/dreamchip/rppx1/rppx1_dpcc.c | 77 +++++ drivers/media/platform/dreamchip/rppx1/rppx1_exm.c | 51 +++ drivers/media/platform/dreamchip/rppx1/rppx1_ga.c | 50 +++ .../media/platform/dreamchip/rppx1/rppx1_hist.c | 77 +++++ .../media/platform/dreamchip/rppx1/rppx1_hist256.c | 47 +++ drivers/media/platform/dreamchip/rppx1/rppx1_is.c | 43 +++ drivers/media/platform/dreamchip/rppx1/rppx1_lin.c | 59 ++++ drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c | 69 ++++ drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c | 49 +++ .../media/platform/dreamchip/rppx1/rppx1_ltmmeas.c | 42 +++ .../media/platform/dreamchip/rppx1/rppx1_outif.c | 46 +++ .../media/platform/dreamchip/rppx1/rppx1_outregs.c | 76 ++++ .../media/platform/dreamchip/rppx1/rppx1_rmap.c | 63 ++++ .../platform/dreamchip/rppx1/rppx1_rmapmeas.c | 47 +++ .../media/platform/dreamchip/rppx1/rppx1_shrp.c | 65 ++++ .../media/platform/dreamchip/rppx1/rppx1_wbmeas.c | 62 ++++ .../media/platform/dreamchip/rppx1/rppx1_xyz2luv.c | 27 ++ include/media/rppx1.h | 36 ++ 41 files changed, 2436 insertions(+) create mode 100644 Documentation/userspace-api/media/v4l/metafmt-rppx1.rst create mode 100644 drivers/media/platform/dreamchip/Kconfig create mode 100644 drivers/media/platform/dreamchip/Makefile create mode 100644 drivers/media/platform/dreamchip/rppx1/Kconfig create mode 100644 drivers/media/platform/dreamchip/rppx1/Makefile create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_module.h create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_params.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rpp_stats.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1.h create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_acq.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bd.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_bls.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_cac.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_db.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_exm.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ga.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_is.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lin.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outif.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c create mode 100644 drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c create mode 100644 include/media/rppx1.h diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst index 3e0cab153f0a..b5455229837e 100644 --- a/Documentation/userspace-api/media/v4l/meta-formats.rst +++ b/Documentation/userspace-api/media/v4l/meta-formats.rst @@ -21,6 +21,7 @@ These formats are used for the :ref:`metadata` interface only. metafmt-pisp-be metafmt-pisp-fe metafmt-rkisp1 + metafmt-rppx1 metafmt-uvc metafmt-uvc-msxu-1-5 metafmt-vivid diff --git a/Documentation/userspace-api/media/v4l/metafmt-rppx1.rst b/Documentation/userspace-api/media/v4l/metafmt-rppx1.rst new file mode 100644 index 000000000000..a174e0a3e022 --- /dev/null +++ b/Documentation/userspace-api/media/v4l/metafmt-rppx1.rst @@ -0,0 +1,99 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. _v4l2-meta-fmt-rppx1-params: +.. _v4l2-meta-fmt-rppx1-stats: + +************************************************************************* +V4L2_META_FMT_RPPX1_PARAMS ('DR1P'), V4L2_META_FMT_RPPX1_STATS ('DR1S') +************************************************************************* + +Configuration Parameters +======================== + +The configuration parameters are passed to the metadata output video node, using +the :c:type:`v4l2_meta_format` interface. Rather than a single struct containing +sub-structs for each configurable area of the ISP, parameters for the Dreamchip +RPPX1 use the v4l2-isp parameters system, through which groups of parameters are +defined as distinct structs or "blocks" which may be added to the data member of +:c:type:`v4l2_isp_buffer`. Userspace is responsible for populating the data +member with the blocks that need to be configured by the driver. Each +block-specific struct embeds :c:type:`v4l2_isp_block_header` as its first member +and userspace must populate the type member with a value from +:c:type:`rppx1_params_block_type`. + +.. code-block:: c + + struct v4l2_isp_params_buffer *params = + (struct v4l2_isp_params_buffer *)buffer; + + params->version = V4L2_ISP_PARAMS_VERSION_V1; + params->data_size = 0; + + void *data = (void *)params->data; + + struct rppx1_ccor_params *ccor = + (struct rppx1_ccor_params *)data; + + ccor->header.type = RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST; + ccor->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE; + ccor->header.size = sizeof(struct rppx1_ccor_params); + + ccor->coeff[0][0] = 0x1000; + ccor->coeff[0][1] = 0x0000; + ccor->coeff[0][2] = 0x0000; + ccor->coeff[1][0] = 0x0000; + ccor->coeff[1][1] = 0x1000; + ccor->coeff[1][2] = 0x0000; + ccor->coeff[2][0] = 0x0000; + ccor->coeff[2][1] = 0x0000; + ccor->coeff[2][2] = 0x1000; + + ccor->offset[0] = 0x200000; + ccor->offset[1] = 0x200000; + ccor->offset[2] = 0x200000; + + data += sizeof(struct rppx1_ccor_params); + params->data_size += sizeof(struct rppx1_ccor_params); + +3A Statistics +============= + +The ISP device collects different statistics over an input bayer frame. Those +statistics can be obtained by userspace from the metadata capture video node, +using the :c:type:`v4l2_meta_format` interface. Rather than a single struct +containing sub-structs for each statistics area of the ISP, statistics for the +Dreamchip RPPX1 use the v4l2-isp statistics system, through which groups of +statistics are defined as distinct structs or "blocks" which may be added to the +data member of :c:type:`v4l2_isp_buffer`. Userspace is responsible for parsing +the buffer and extracting the blocks of statistics. Each block-specific struct +embeds :c:type:`v4l2_isp_block_header` as its first member and userspace must +interpret the type member with a value from :c:type:`rppx1_stats_block_type`. + +.. code-block:: C + + const struct v4l2_isp_buffer *stats = + (struct v4l2_isp_buffer *)buf; + size_t block_offset = 0; + + while (block_offset < stats->data_size) { + const struct v4l2_isp_stats_block_header *block = + (void*)(stats->data + block_offset); + + block_offset += block->size; + + switch (block->type) { + case RPPX1_STATS_BLOCK_TYPE_HIST_POST: + for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++) + printf("hist.hist_bins[%u] = 0x%08x\n", + i, hist.hist_bins[%i]); + break; + default: + printf("Unknown block type 0x%04x", block->type); + break; + } + } + +Dreamchip RPPX1 uAPI data types +=============================== + +.. kernel-doc:: include/uapi/linux/media/dreamchip/rppx1-config.h diff --git a/MAINTAINERS b/MAINTAINERS index febe918d6316..07177f98c4d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7878,6 +7878,16 @@ F: drivers/block/drbd/ F: include/linux/drbd* F: lib/lru_cache.c +DREAMCHIP RPPX1 ISP +M: Jacopo Mondi +M: Jai Luthra +M: Niklas Söderlund +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/userspace-api/media/v4l/metafmt-rppx1.rst +F: drivers/media/platform/dreamchip/rppx1/ +F: include/uapi/linux/media/dreamchip/rppx1-config.h + DRIVER COMPONENT FRAMEWORK L: dri-devel@lists.freedesktop.org F: drivers/base/component.c diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 0b33e927bd59..2c7699b6610b 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -72,6 +72,7 @@ source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/broadcom/Kconfig" source "drivers/media/platform/cadence/Kconfig" source "drivers/media/platform/chips-media/Kconfig" +source "drivers/media/platform/dreamchip/Kconfig" source "drivers/media/platform/imagination/Kconfig" source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 16c185752474..d47c47d817da 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -15,6 +15,7 @@ obj-y += atmel/ obj-y += broadcom/ obj-y += cadence/ obj-y += chips-media/ +obj-y += dreamchip/ obj-y += imagination/ obj-y += intel/ obj-y += marvell/ diff --git a/drivers/media/platform/dreamchip/Kconfig b/drivers/media/platform/dreamchip/Kconfig new file mode 100644 index 000000000000..d177d4ee79ae --- /dev/null +++ b/drivers/media/platform/dreamchip/Kconfig @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +source "drivers/media/platform/dreamchip/rppx1/Kconfig" diff --git a/drivers/media/platform/dreamchip/Makefile b/drivers/media/platform/dreamchip/Makefile new file mode 100644 index 000000000000..ba47ba2d136e --- /dev/null +++ b/drivers/media/platform/dreamchip/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Dreamchip device drivers. +# + +obj-y += rppx1/ diff --git a/drivers/media/platform/dreamchip/rppx1/Kconfig b/drivers/media/platform/dreamchip/rppx1/Kconfig new file mode 100644 index 000000000000..0998a7d10bf2 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_DCT_RPPX1 + tristate + depends on V4L_PLATFORM_DRIVERS + select V4L2_ISP + help + Support library for Dreamchip HDR RPP X1 High Dynamic Range Real-time + Pixel Processor (RPP). The library can be used by other drivers who + utilises the RPP as part of an ISP implementation. + + To compile this driver as a module, choose M here: the + module will be called rppx1. diff --git a/drivers/media/platform/dreamchip/rppx1/Makefile b/drivers/media/platform/dreamchip/rppx1/Makefile new file mode 100644 index 000000000000..b2bd6b5d68bc --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/Makefile @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0 +dct-rpp-x1-objs = \ + rpp_module.o \ + rpp_params.o \ + rpp_stats.o \ + rppx1.o \ + rppx1_acq.o \ + rppx1_awbg.o \ + rppx1_bd.o \ + rppx1_bdrgb.o \ + rppx1_bls.o \ + rppx1_cac.o \ + rppx1_ccor.o \ + rppx1_db.o \ + rppx1_dpcc.o \ + rppx1_exm.o \ + rppx1_ga.o \ + rppx1_hist.o \ + rppx1_hist256.o \ + rppx1_is.o \ + rppx1_lin.o \ + rppx1_lsc.o \ + rppx1_ltm.o \ + rppx1_ltmmeas.o \ + rppx1_outif.o \ + rppx1_outregs.o \ + rppx1_rmap.o \ + rppx1_rmapmeas.o \ + rppx1_shrp.o \ + rppx1_wbmeas.o \ + rppx1_xyz2luv.o + +obj-$(CONFIG_VIDEO_DCT_RPPX1) += dct-rpp-x1.o diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.c b/drivers/media/platform/dreamchip/rppx1/rpp_module.c new file mode 100644 index 000000000000..cb3e5a33d68c --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rppx1.h" +#include "rpp_module.h" + +int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp, + const struct rpp_module_ops *ops, u32 base) +{ + mod->rpp = rpp; + mod->base = base; + mod->ops = ops; + + if (ops->probe) + return ops->probe(mod); + + return 0; +} + +void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value) +{ + rppx1_write(mod->rpp, mod->base + offset, value); +} + +u32 rpp_module_read(struct rpp_module *mod, u32 offset) +{ + return rppx1_read(mod->rpp, mod->base + offset); +} + +void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value) +{ + u32 reg = rpp_module_read(mod, offset) & ~mask; + + rpp_module_write(mod, offset, reg | value); +} diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h new file mode 100644 index 000000000000..e92e77542980 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#ifndef __RPPX1_MODULE_H__ +#define __RPPX1_MODULE_H__ + +#include +#include +#include + +#include + +#include + +struct rpp_module_ops; + +enum rpp_raw_pattern { + RPP_RGGB = 0, + RPP_GRBG, + RPP_GBRG, + RPP_BGGR, +}; + +struct rpp_module { + struct rppx1 *rpp; + u32 base; + + const struct rpp_module_ops *ops; + + union { + struct { + enum rpp_raw_pattern raw_pattern; + } acq; + } info; +}; + +int rpp_module_probe(struct rpp_module *mod, struct rppx1 *rpp, + const struct rpp_module_ops *ops, u32 base); + +void rpp_module_write(struct rpp_module *mod, u32 offset, u32 value); +u32 rpp_module_read(struct rpp_module *mod, u32 offset); +void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); + +union rppx1_params_block { + struct v4l2_isp_block_header header; +}; + +union rppx1_stats_block { + struct v4l2_isp_block_header header; +}; + +struct rpp_module_ops { + int (*probe)(struct rpp_module *mod); + int (*start)(struct rpp_module *mod, const struct v4l2_mbus_framefmt *fmt); + + int (*fill_params)(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv); + int (*fill_stats)(struct rpp_module *mod, + union rppx1_stats_block *block); +}; + +extern const struct rpp_module_ops rppx1_acq_ops; +extern const struct rpp_module_ops rppx1_awbg_ops; +extern const struct rpp_module_ops rppx1_bd_ops; +extern const struct rpp_module_ops rppx1_bdrgb_ops; +extern const struct rpp_module_ops rppx1_bls_ops; +extern const struct rpp_module_ops rppx1_cac_ops; +extern const struct rpp_module_ops rppx1_ccor_ops; +extern const struct rpp_module_ops rppx1_ccor_csm_ops; +extern const struct rpp_module_ops rppx1_db_ops; +extern const struct rpp_module_ops rppx1_dpcc_ops; +extern const struct rpp_module_ops rppx1_exm_ops; +extern const struct rpp_module_ops rppx1_ga_ops; +extern const struct rpp_module_ops rppx1_hist256_ops; +extern const struct rpp_module_ops rppx1_hist_ops; +extern const struct rpp_module_ops rppx1_is_ops; +extern const struct rpp_module_ops rppx1_lin_ops; +extern const struct rpp_module_ops rppx1_lsc_ops; +extern const struct rpp_module_ops rppx1_ltm_ops; +extern const struct rpp_module_ops rppx1_ltmmeas_ops; +extern const struct rpp_module_ops rppx1_outif_ops; +extern const struct rpp_module_ops rppx1_outregs_ops; +extern const struct rpp_module_ops rppx1_rmapmeas_ops; +extern const struct rpp_module_ops rppx1_rmap_ops; +extern const struct rpp_module_ops rppx1_shrp_ops; +extern const struct rpp_module_ops rppx1_wbmeas_ops; +extern const struct rpp_module_ops rppx1_xyz2luv_ops; + +#define rpp_module_call(mod, op, args...) \ + ({ \ + struct rpp_module *__mod = (mod); \ + int __result; \ + if (!__mod) \ + __result = -ENODEV; \ + else if (!__mod->ops->op) \ + __result = 0; \ + else \ + __result = __mod->ops->op(__mod, ##args); \ + __result; \ + }) + +#endif /* __RPPX1_MODULE_H__ */ diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c new file mode 100644 index 000000000000..af45ad5750b3 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include +#include + +#include "rppx1.h" + +#define RPPX1_PARAMS_BLOCK_INFO(block, data) \ + [RPPX1_PARAMS_BLOCK_TYPE_ ## block] = { \ + .size = sizeof(struct rppx1_ ## data ## _params), \ + } + +static const struct v4l2_isp_params_block_type_info +rppx1_ext_params_blocks_info[] = { +}; + +int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, + rppx1_reg_write write, void *priv) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct v4l2_isp_buffer *cfg; + size_t block_offset; + int ret; + + ret = v4l2_isp_params_validate_buffer_size(rpp->dev, vb, max_size); + if (ret) + return ret; + + cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0); + + ret = v4l2_isp_params_validate_buffer(rpp->dev, vb, cfg, + rppx1_ext_params_blocks_info, + ARRAY_SIZE(rppx1_ext_params_blocks_info)); + if (ret) + return ret; + + /* Walk the list of parameter blocks and process them. */ + block_offset = 0; + while (block_offset < cfg->data_size) { + const union rppx1_params_block *block = + (const union rppx1_params_block *)&cfg->data[block_offset]; + struct rpp_module *module; + int ret; + + block_offset += block->header.size; + + switch (block->header.type) { + default: + dev_warn(rpp->dev, + "Not handled RPPX1 block type: 0x%04x\n", + block->header.type); + continue; + } + + ret = rpp_module_call(module, fill_params, block, write, priv); + if (ret) { + dev_err(rpp->dev, + "Error processing RPPX1 block type: 0x%04x\n", + block->header.type); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(rppx1_params); diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c new file mode 100644 index 000000000000..f7eb60deae10 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rppx1.h" +#include "rpp_module.h" + +#include + +#define RPPX1_STATS_BLOCK_INFO(type, block) \ + [RPPX1_STATS_BLOCK_TYPE_ ## type] = { \ + .size = sizeof(struct rppx1_ ## block ## _stats), \ + } + +#define rppx1_init_stats_block(rpp, buf, type) \ + ((union rppx1_stats_block *) \ + v4l2_isp_stats_init_block((rpp)->dev, (buf), \ + rppx1_stats_blocks_info, \ + ARRAY_SIZE(rppx1_stats_blocks_info), \ + (type), RPPX1_STATS_MAX_SIZE)) \ + +void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf) +{ +} +EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr); diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.c b/drivers/media/platform/dreamchip/rppx1/rppx1.c new file mode 100644 index 000000000000..c502be797914 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + * + * Support library for Dreamchip HDR RPPX1 High Dynamic Range Real-time Pixel + * Processor. + */ + +#include +#include +#include + +#include "rppx1.h" + +/* RPP_HDR Base Addresses */ +#define RPPX1_HDRREGS_BASE 0x0000 +#define RPPX1_HDR_IRQ_BASE 0x0200 +#define RPPX1_RPP_OUT_BASE 0x0800 +#define RPPX1_RPP_RMAP_BASE 0x0c00 +#define RPPX1_RPP_RMAP_MEAS_BASE 0x1000 +#define RPPX1_RPP_MAIN_PRE1_BASE 0x2000 +#define RPPX1_RPP_MAIN_PRE2_BASE 0x4000 +#define RPPX1_RPP_MAIN_POST_BASE 0xa000 +#define RPPX1_RPP_MVOUT_BASE 0xc000 +#define RPPX1_RPP_FUSA_BASE 0xf000 + +#define RPPX1_RPP_HDRREGS_VERSION_REG (RPPX1_HDRREGS_BASE + 0x0000) + +#define RPPX1_RPP_HDR_UPD_REG (RPPX1_HDRREGS_BASE + 0x0004) +#define RPPX1_RPP_HDR_UPD_REGS_GEN_CFG_UPD BIT(1) +#define RPPX1_RPP_HDR_UPD_REGS_CFG_UPD BIT(0) + +#define RPPX1_RESERVED_3_REG (RPPX1_HDRREGS_BASE + 0x0008) + +#define RPPX1_RPP_HDR_INFORM_ENABLE_REG (RPPX1_HDRREGS_BASE + 0x000c) +#define RPPX1_RPP_HDR_INFORM_ENABLE_ENABLE 1 +#define RPPX1_RPP_HDR_INFORM_ENABLE_DISABLE 0 + +#define RPPX1_RPP_HDR_OUT_IF_ON_REG (RPPX1_HDRREGS_BASE + 0x0010) +#define RPPX1_RPP_HDR_OUT_IF_OFF_REG (RPPX1_HDRREGS_BASE + 0x0014) + +#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG (RPPX1_HDRREGS_BASE + 0x0018) +#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_ENABLE 1 +#define RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_DISABLE 0 + +#define RPPX1_RPP_ISM (RPPX1_HDR_IRQ_BASE + 0x00) +#define RPPX1_RPP_RIS (RPPX1_HDR_IRQ_BASE + 0x04) +#define RPPX1_RPP_MIS (RPPX1_HDR_IRQ_BASE + 0x08) +#define RPPX1_RPP_ISC (RPPX1_HDR_IRQ_BASE + 0x0c) + +/* RPP_OUT/MV_OUT Pipelines - Base Addresses */ +#define RPPX1_GAMMA_OUT_BASE 0x0000 /* HV, MV */ +#define RPPX1_IS_BASE 0x00c0 /* HV, MV */ +#define RPPX1_CSM_BASE 0x0100 /* HV, MV */ +#define RPPX1_OUT_IF_BASE 0x0200 /* HV, MV */ +#define RPPX1_RPP_OUTREGS_BASE 0x02c0 /* HV, MV */ +#define RPPX1_LUV_BASE 0x0300 /* MV */ + +/* PRE1/PRE2/POST Pipelines - Base Addresses */ +#define RPPX1_ACQ_BASE 0x0080 /* PRE1, PRE2 */ +#define RPPX1_BLS_BASE 0x0100 /* PRE1, PRE2 */ +#define RPPX1_GAMMA_IN_BASE 0x0200 /* PRE1, PRE2 */ +#define RPPX1_LSC_BASE 0x0400 /* PRE1, PRE2 */ +#define RPPX1_AWB_GAIN_BASE 0x0500 /* PRE1, PRE2, POST */ +#define RPPX1_DPCC_BASE 0x0600 /* PRE1, PRE2 */ +#define RPPX1_DPF_BASE 0x0700 /* PRE1, PRE2 */ +#define RPPX1_FILT_BASE 0x0800 /* POST */ +#define RPPX1_CAC_BASE 0x0880 /* POST */ +#define RPPX1_CCOR_BASE 0x0900 /* POST */ +#define RPPX1_HIST_BASE 0x0a00 /* PRE1, PRE2, POST */ +#define RPPX1_HIST256_BASE 0x0b00 /* PRE1 */ +#define RPPX1_EXM_BASE 0x0c00 /* PRE1, PRE2 */ +#define RPPX1_LTM_BASE 0x1000 /* POST */ +#define RPPX1_LTM_MEAS_BASE 0x1200 /* POST */ +#define RPPX1_WBMEAS_BASE 0x1700 /* POST */ +#define RPPX1_BDRGB_BASE 0x1800 /* POST */ +#define RPPX1_SHRP_BASE 0x1a00 /* POST */ + +/* Functional Safety Module Base Addresses */ +#define RPPX1_FMU_BASE 0x0100 + +#define RPPX1_RPP_HDR_FMU_FSM_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x00) +#define RPPX1_RPP_HDR_FMU_FSM_FSM_IRQM_FAULT BIT(23) +#define RPPX1_RPP_HDR_FMU_FSM_PRE2_SIZE_FAULT BIT(20) +#define RPPX1_RPP_HDR_FMU_FSM_PRE2_TIME_FAULT BIT(19) +#define RPPX1_RPP_HDR_FMU_FSM_PRE1_SIZE_FAULT BIT(18) +#define RPPX1_RPP_HDR_FMU_FSM_PRE1_TIME_FAULT BIT(17) +#define RPPX1_RPP_HDR_FMU_FSM_SIZE_FAULT BIT(16) +#define RPPX1_RPP_HDR_FMU_FSM_TIME_FAULT BIT(15) +#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_SIZE_FAULT BIT(14) +#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_TIME_FAULT BIT(13) +#define RPPX1_RPP_HDR_FMU_FSM_HV_OUT_SIZE_FAULT BIT(12) +#define RPPX1_RPP_HDR_FMU_FSM_HV_OUT_TIME_FAULT BIT(11) +#define RPPX1_RPP_HDR_FMU_FSM_MV_OUT_SIZE_ERR BIT(10) +#define RPPX1_RPP_HDR_FMU_FSM_IS_OUT_SIZE_ERR BIT(9) +#define RPPX1_RPP_HDR_FMU_FSM_PRE2_FIFO_OVFLW BIT(7) +#define RPPX1_RPP_HDR_FMU_FSM_PRE1_FIFO_OVFLW BIT(6) +#define RPPX1_RPP_HDR_FMU_FSM_PRE1_INFORM_SIZE BIT(5) +#define RPPX1_RPP_HDR_FMU_FSM_PRE1_OUTFORM_SIZE BIT(4) +#define RPPX1_RPP_HDR_FMU_FSM_PRE2_INFORM_SIZE BIT(3) +#define RPPX1_RPP_HDR_FMU_FSM_PRE2_OUTFORM_SIZE BIT(2) + +#define RPPX1_RPP_HDR_FMU_RFS_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x04) +#define RPPX1_RPP_HDR_FMU_MFS_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x08) +#define RPPX1_RPP_HDR_FMU_FSC_REG (RPPX1_RPP_FUSA_BASE + RPPX1_FMU_BASE + 0x0c) + +void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value) +{ + iowrite32(value, rpp->base + offset); +} + +u32 rppx1_read(struct rppx1 *rpp, u32 offset) +{ + return ioread32(rpp->base + offset); +} + +bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc) +{ + u32 status, raw, fault; + + fault = rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS_REG); + if (fault) { + dev_err(rpp->dev, "%s: fault 0x%08x\n", __func__, fault); + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC_REG, fault); + } + + /* Read raw interrupt status. */ + raw = rppx1_read(rpp, RPPX1_RPP_RIS); + status = rppx1_read(rpp, RPPX1_RPP_MIS); + + /* Propagate the isc status. */ + if (isc) + *isc = status | raw; + + /* Clear enabled interrupts */ + rppx1_write(rpp, RPPX1_RPP_ISC, status); + + return !!(status & RPPX1_IRQ_ID_OUT_FRAME); +} +EXPORT_SYMBOL_GPL(rppx1_interrupt); + +void rppx1_destroy(struct rppx1 *rpp) +{ + kfree(rpp); +} +EXPORT_SYMBOL_GPL(rppx1_destroy); + +/* + * Allocate the private data structure and verify the hardware is present. + */ +struct rppx1 *rppx1_create(void __iomem *base, struct device *dev) +{ + struct rppx1 *rpp; + u32 reg; + + /* Allocate library structure */ + rpp = kzalloc_obj(*rpp); + if (!rpp) + return NULL; + + rpp->base = base; + rpp->dev = dev; + + /* Check communication with RPP and verify it truly is a X1. */ + reg = rppx1_read(rpp, RPPX1_RPP_HDRREGS_VERSION_REG); + if (reg != 3) { + dev_err(rpp->dev, "Unsupported HDR version (%u)\n", reg); + rppx1_destroy(rpp); + return NULL; + } + + /* Probe the PRE1 pipeline. */ + if (rpp_module_probe(&rpp->pre1.acq, rpp, &rppx1_acq_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_ACQ_BASE) || + rpp_module_probe(&rpp->pre1.bls, rpp, &rppx1_bls_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_BLS_BASE) || + rpp_module_probe(&rpp->pre1.lin, rpp, &rppx1_lin_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_GAMMA_IN_BASE) || + rpp_module_probe(&rpp->pre1.lsc, rpp, &rppx1_lsc_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_LSC_BASE) || + rpp_module_probe(&rpp->pre1.awbg, rpp, &rppx1_awbg_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_AWB_GAIN_BASE) || + rpp_module_probe(&rpp->pre1.dpcc, rpp, &rppx1_dpcc_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPCC_BASE) || + rpp_module_probe(&rpp->pre1.bd, rpp, &rppx1_bd_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_DPF_BASE) || + rpp_module_probe(&rpp->pre1.hist, rpp, &rppx1_hist_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST_BASE) || + rpp_module_probe(&rpp->pre1.hist256, rpp, &rppx1_hist256_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_HIST256_BASE) || + rpp_module_probe(&rpp->pre1.exm, rpp, &rppx1_exm_ops, + RPPX1_RPP_MAIN_PRE1_BASE + RPPX1_EXM_BASE)) + goto err; + + /* Probe the PRE2 pipeline. */ + if (rpp_module_probe(&rpp->pre2.acq, rpp, &rppx1_acq_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_ACQ_BASE) || + rpp_module_probe(&rpp->pre2.bls, rpp, &rppx1_bls_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_BLS_BASE) || + rpp_module_probe(&rpp->pre2.lin, rpp, &rppx1_lin_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_GAMMA_IN_BASE) || + rpp_module_probe(&rpp->pre2.lsc, rpp, &rppx1_lsc_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_LSC_BASE) || + rpp_module_probe(&rpp->pre2.awbg, rpp, &rppx1_awbg_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_AWB_GAIN_BASE) || + rpp_module_probe(&rpp->pre2.dpcc, rpp, &rppx1_dpcc_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPCC_BASE) || + rpp_module_probe(&rpp->pre2.bd, rpp, &rppx1_bd_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_DPF_BASE) || + rpp_module_probe(&rpp->pre2.hist, rpp, &rppx1_hist_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_HIST_BASE) || + rpp_module_probe(&rpp->pre2.exm, rpp, &rppx1_exm_ops, + RPPX1_RPP_MAIN_PRE2_BASE + RPPX1_EXM_BASE)) + goto err; + + /* Probe the POST pipeline. */ + if (rpp_module_probe(&rpp->post.awbg, rpp, &rppx1_awbg_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_AWB_GAIN_BASE) || + rpp_module_probe(&rpp->post.ccor, rpp, &rppx1_ccor_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_CCOR_BASE) || + rpp_module_probe(&rpp->post.hist, rpp, &rppx1_hist_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_HIST_BASE) || + rpp_module_probe(&rpp->post.db, rpp, &rppx1_db_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_FILT_BASE) || + rpp_module_probe(&rpp->post.cac, rpp, &rppx1_cac_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_CAC_BASE) || + rpp_module_probe(&rpp->post.ltm, rpp, &rppx1_ltm_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_BASE) || + rpp_module_probe(&rpp->post.ltmmeas, rpp, &rppx1_ltmmeas_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_LTM_MEAS_BASE) || + rpp_module_probe(&rpp->post.wbmeas, rpp, &rppx1_wbmeas_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_WBMEAS_BASE) || + rpp_module_probe(&rpp->post.bdrgb, rpp, &rppx1_bdrgb_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_BDRGB_BASE) || + rpp_module_probe(&rpp->post.shrp, rpp, &rppx1_shrp_ops, + RPPX1_RPP_MAIN_POST_BASE + RPPX1_SHRP_BASE)) + goto err; + + /* Probe the Human Vision pipeline. */ + if (rpp_module_probe(&rpp->hv.ga, rpp, &rppx1_ga_ops, + RPPX1_RPP_OUT_BASE + RPPX1_GAMMA_OUT_BASE) || + rpp_module_probe(&rpp->hv.is, rpp, &rppx1_is_ops, + RPPX1_RPP_OUT_BASE + RPPX1_IS_BASE) || + rpp_module_probe(&rpp->hv.ccor, rpp, &rppx1_ccor_csm_ops, + RPPX1_RPP_OUT_BASE + RPPX1_CSM_BASE) || + rpp_module_probe(&rpp->hv.outif, rpp, &rppx1_outif_ops, + RPPX1_RPP_OUT_BASE + RPPX1_OUT_IF_BASE) || + rpp_module_probe(&rpp->hv.outregs, rpp, &rppx1_outregs_ops, + RPPX1_RPP_OUT_BASE + RPPX1_RPP_OUTREGS_BASE)) + goto err; + + /* Probe the Machine Vision pipeline. */ + if (rpp_module_probe(&rpp->mv.ga, rpp, &rppx1_ga_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_GAMMA_OUT_BASE) || + rpp_module_probe(&rpp->mv.is, rpp, &rppx1_is_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_IS_BASE) || + rpp_module_probe(&rpp->mv.ccor, rpp, &rppx1_ccor_csm_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_CSM_BASE) || + rpp_module_probe(&rpp->mv.outif, rpp, &rppx1_outif_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_OUT_IF_BASE) || + rpp_module_probe(&rpp->mv.outregs, rpp, &rppx1_outregs_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_RPP_OUTREGS_BASE) || + rpp_module_probe(&rpp->mv.xyz2luv, rpp, &rppx1_xyz2luv_ops, + RPPX1_RPP_MVOUT_BASE + RPPX1_LUV_BASE)) + goto err; + + /* Probe the standalone Radiance Mapping modules. */ + if (rpp_module_probe(&rpp->rmap, rpp, &rppx1_rmap_ops, + RPPX1_RPP_RMAP_BASE) || + rpp_module_probe(&rpp->rmapmeas, rpp, &rppx1_rmapmeas_ops, + RPPX1_RPP_RMAP_MEAS_BASE)) + goto err; + + return rpp; +err: + rppx1_destroy(rpp); + + return NULL; +} +EXPORT_SYMBOL_GPL(rppx1_create); + +int rppx1_start(struct rppx1 *rpp, + const struct v4l2_mbus_framefmt *input, + const struct v4l2_mbus_framefmt *hv, + const struct v4l2_mbus_framefmt *mv) +{ + if (rpp_module_call(&rpp->pre1.acq, start, input) || + rpp_module_call(&rpp->pre1.bls, start, input) || + rpp_module_call(&rpp->pre1.lin, start, input) || + rpp_module_call(&rpp->pre1.lsc, start, input) || + rpp_module_call(&rpp->pre1.awbg, start, input) || + rpp_module_call(&rpp->pre1.dpcc, start, input) || + rpp_module_call(&rpp->pre1.bd, start, input) || + rpp_module_call(&rpp->pre1.hist, start, input) || + rpp_module_call(&rpp->pre1.exm, start, input) || + rpp_module_call(&rpp->pre1.hist256, start, input)) + return -EINVAL; + + if (rpp_module_call(&rpp->rmap, start, NULL) || + rpp_module_call(&rpp->rmapmeas, start, NULL)) + return -EINVAL; + + if (rpp_module_call(&rpp->post.awbg, start, input) || + rpp_module_call(&rpp->post.db, start, input) || + rpp_module_call(&rpp->post.cac, start, input) || + rpp_module_call(&rpp->post.ccor, start, input) || + rpp_module_call(&rpp->post.ltm, start, input) || + rpp_module_call(&rpp->post.bdrgb, start, input) || + rpp_module_call(&rpp->post.shrp, start, input) || + rpp_module_call(&rpp->post.ltmmeas, start, input) || + rpp_module_call(&rpp->post.wbmeas, start, input) || + rpp_module_call(&rpp->post.hist, start, input)) + return -EINVAL; + + if (hv && (rpp_module_call(&rpp->hv.ga, start, hv) || + rpp_module_call(&rpp->hv.ccor, start, hv) || + rpp_module_call(&rpp->hv.outregs, start, hv) || + rpp_module_call(&rpp->hv.is, start, hv) || + rpp_module_call(&rpp->hv.outif, start, hv))) + return -EINVAL; + + if (mv && (rpp_module_call(&rpp->mv.ga, start, mv) || + rpp_module_call(&rpp->mv.ccor, start, mv) || + rpp_module_call(&rpp->mv.xyz2luv, start, mv) || + rpp_module_call(&rpp->mv.outregs, start, mv) || + rpp_module_call(&rpp->mv.is, start, mv) || + rpp_module_call(&rpp->mv.outif, start, mv))) + return -EINVAL; + + /* Immediate update for shadows. */ + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_CFG_UPD); + + /* Clear fault interrupts. */ + rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, + RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_ENABLE); + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSM_REG, + RPPX1_RPP_HDR_FMU_FSM_PRE2_FIFO_OVFLW | + RPPX1_RPP_HDR_FMU_FSM_PRE1_FIFO_OVFLW); + rppx1_write(rpp, RPPX1_RPP_HDR_FMU_FSC_REG, + rppx1_read(rpp, RPPX1_RPP_HDR_FMU_MFS_REG)); + rppx1_write(rpp, RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_REG, + RPPX1_RPP_HDR_SAFETY_ACCESS_PROTECTION_DISABLE); + + /* Set interrupt mask. */ + rppx1_write(rpp, RPPX1_RPP_ISM, RPPX1_IRQ_ID_OUT_FRAME); + + /* Immediate commit update for shadows. */ + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_CFG_UPD); + + /* Then for operation update shadows with picture synchronization. */ + rppx1_write(rpp, RPPX1_RPP_HDR_UPD_REG, RPPX1_RPP_HDR_UPD_REGS_GEN_CFG_UPD); + + /* Clear any pending interrupts. */ + rppx1_interrupt(rpp, NULL); + + /* Enable input formatters. */ + rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, + RPPX1_RPP_HDR_INFORM_ENABLE_ENABLE); + + return 0; +} +EXPORT_SYMBOL_GPL(rppx1_start); + +int rppx1_stop(struct rppx1 *rpp) +{ + /* Disable input formatters. */ + rppx1_write(rpp, RPPX1_RPP_HDR_INFORM_ENABLE_REG, + RPPX1_RPP_HDR_INFORM_ENABLE_DISABLE); + + /* Clear any pending interrupts. */ + rppx1_interrupt(rpp, NULL); + + return 0; +} +EXPORT_SYMBOL_GPL(rppx1_stop); + +MODULE_AUTHOR("Niklas Söderlund "); +MODULE_DESCRIPTION("Dreamchip HDR RPPX1 support library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1.h b/drivers/media/platform/dreamchip/rppx1/rppx1.h new file mode 100644 index 000000000000..3bf3955afbae --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#ifndef __MEDIA_RPPX1_H__ +#define __MEDIA_RPPX1_H__ + +#include +#include + +#include "rpp_module.h" + +#define RPPX1_IRQ_ID_256HIST BIT(27) +#define RPPX1_IRQ_ID_PRE2_DPCC BIT(25) +#define RPPX1_IRQ_ID_PRE1_DPCC BIT(24) +#define RPPX1_IRQ_ID_MV_OUT_FRAME_OUT BIT(23) +#define RPPX1_IRQ_ID_MV_OUT_OFF BIT(22) +#define RPPX1_IRQ_ID_POST_AWB_MEAS BIT(21) +#define RPPX1_IRQ_ID_POST_HIST_MEAS BIT(20) +#define RPPX1_IRQ_ID_POST_TM BIT(19) +#define RPPX1_IRQ_ID_PRE1_EXM BIT(18) +#define RPPX1_IRQ_ID_PRE1_HIST BIT(17) +#define RPPX1_IRQ_ID_PRE1_FRAME_IN BIT(16) +#define RPPX1_IRQ_ID_PRE1_HSTART BIT(15) +#define RPPX1_IRQ_ID_PRE1_VSTART BIT(14) +#define RPPX1_IRQ_ID_PRE2_EXM BIT(13) +#define RPPX1_IRQ_ID_PRE2_HIST BIT(12) +#define RPPX1_IRQ_ID_PRE2_FRAME_IN BIT(11) +#define RPPX1_IRQ_ID_PRE2_HSTART BIT(10) +#define RPPX1_IRQ_ID_PRE2_VSTART BIT(9) +#define RPPX1_IRQ_ID_OUT_FRAME BIT(3) +#define RPPX1_IRQ_ID_OUT_OFF BIT(2) +#define RPPX1_IRQ_ID_RMAP_MEAS BIT(1) +#define RPPX1_IRQ_ID_RMAP_DONE BIT(0) + +struct rppx1 { + struct device *dev; + void __iomem *base; + + struct { + struct rpp_module acq; + struct rpp_module bls; + struct rpp_module lin; + struct rpp_module lsc; + struct rpp_module awbg; + struct rpp_module dpcc; + struct rpp_module bd; + struct rpp_module hist; + struct rpp_module hist256; + struct rpp_module exm; + } pre1; + + struct { + struct rpp_module acq; + struct rpp_module bls; + struct rpp_module lin; + struct rpp_module lsc; + struct rpp_module awbg; + struct rpp_module dpcc; + struct rpp_module bd; + struct rpp_module hist; + struct rpp_module exm; + } pre2; + + struct { + struct rpp_module awbg; + struct rpp_module ccor; + struct rpp_module hist; + struct rpp_module db; + struct rpp_module cac; + struct rpp_module ltm; + struct rpp_module ltmmeas; + struct rpp_module wbmeas; + struct rpp_module bdrgb; + struct rpp_module shrp; + } post; + + struct { + struct rpp_module ga; + struct rpp_module is; + struct rpp_module ccor; + struct rpp_module outif; + struct rpp_module outregs; + } hv; + + struct { + struct rpp_module ga; + struct rpp_module is; + struct rpp_module ccor; + struct rpp_module outif; + struct rpp_module outregs; + struct rpp_module xyz2luv; + } mv; + + struct rpp_module rmap; + struct rpp_module rmapmeas; +}; + +void rppx1_write(struct rppx1 *rpp, u32 offset, u32 value); +u32 rppx1_read(struct rppx1 *rpp, u32 offset); + +#endif /* __MEDIA_RPPX1_H__ */ diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c new file mode 100644 index 000000000000..d5624e3c76a9 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_acq.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define ACQ_VERSION_REG 0x0000 + +#define ACQ_CTRL_REG 0x0004 +#define ACQ_CTRL_ALTERNATIVE_CFG_MODE_ENABLE BIT(8) +#define ACQ_CTRL_RPP_MODE_MASK GENMASK(3, 1) +#define ACQ_CTRL_RPP_MODE_RAWBT601 (0 << 1) +#define ACQ_CTRL_RPP_MODE_BT656 (1 << 1) +#define ACQ_CTRL_RPP_MODE_BT601 (2 << 1) +#define ACQ_CTRL_RPP_MODE_BAYER (3 << 1) +#define ACQ_CTRL_RPP_MODE_DATA (4 << 1) +#define ACQ_CTRL_RPP_MODE_BAYERRGB (5 << 1) +#define ACQ_CTRL_RPP_MODE_RAWBT656 (6 << 1) +#define ACQ_CTRL_INFORM_EN_ENABLE BIT(0) + +#define ACQ_PROP_REG 0x0008 + +#define ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB BIT(30) +#define ACQ_PROP_YUV_OUT_SEL BIT(25) +#define ACQ_PROP_MUX_DMA_SEL BIT(24) +#define ACQ_PROP_SECOND_INPUT_TYPE BIT(18) +#define ACQ_PROP_LATENCY_FIFO_INPUT_SELECTION BIT(15) +#define ACQ_PROP_INPUT_SELECTION_MASK GENMASK(14, 12) +#define ACQ_PROP_INPUT_SELECTION_8BIT (0 << 12) +#define ACQ_PROP_INPUT_SELECTION_10BIT (1 << 12) +#define ACQ_PROP_INPUT_SELECTION_12BIT (2 << 12) +#define ACQ_PROP_BAYER_PAT_MASK GENMASK(4, 3) +#define ACQ_PROP_BAYER_PAT_RGRG (0 << 3) +#define ACQ_PROP_BAYER_PAT_GRGR (1 << 3) +#define ACQ_PROP_BAYER_PAT_GBGB (2 << 3) +#define ACQ_PROP_BAYER_PAT_BGBG (3 << 3) +#define ACQ_PROP_VSYNC_POL BIT(2) +#define ACQ_PROP_HSYNC_POL BIT(1) +#define ACQ_PROP_SAMPLE_EDGE BIT(0) + +#define ACQ_H_OFFS_REG 0x000c +#define ACQ_V_OFFS_REG 0x0010 +#define ACQ_H_SIZE_REG 0x0014 +#define ACQ_V_SIZE_REG 0x0018 +#define ACQ_OUT_H_OFFS_REG 0x001c +#define ACQ_OUT_V_OFFS_REG 0x0020 +#define ACQ_OUT_H_SIZE_REG 0x0024 +#define ACQ_OUT_V_SIZE_REG 0x0028 +#define FLAGS_SHD_REG 0x002c +#define ACQ_OUT_H_OFFS_SHD_REG 0x0030 +#define ACQ_OUT_V_OFFS_SHD_REG 0x0034 +#define ACQ_OUT_H_SIZE_SHD_REG 0x0038 +#define ACQ_OUT_V_SIZE_SHD_REG 0x003c + +static int rppx1_acq_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, ACQ_VERSION_REG) != 0x0b) + return -EINVAL; + + return 0; +} + +static int rppx1_acq_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + u32 bayerpat, selection; + + rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_RPP_MODE_MASK, + ACQ_CTRL_RPP_MODE_BAYER); + + rpp_module_write(mod, ACQ_H_OFFS_REG, 0); + rpp_module_write(mod, ACQ_V_OFFS_REG, 0); + rpp_module_write(mod, ACQ_H_SIZE_REG, fmt->width); + rpp_module_write(mod, ACQ_V_SIZE_REG, fmt->height); + rpp_module_write(mod, ACQ_OUT_H_OFFS_REG, 0); + rpp_module_write(mod, ACQ_OUT_V_OFFS_REG, 0); + rpp_module_write(mod, ACQ_OUT_H_SIZE_REG, fmt->width); + rpp_module_write(mod, ACQ_OUT_V_SIZE_REG, fmt->height); + + switch (fmt->code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: + mod->info.acq.raw_pattern = RPP_BGGR; + bayerpat = ACQ_PROP_BAYER_PAT_BGBG; + break; + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: + mod->info.acq.raw_pattern = RPP_GBRG; + bayerpat = ACQ_PROP_BAYER_PAT_GBGB; + break; + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG12_1X12: + mod->info.acq.raw_pattern = RPP_GRBG; + bayerpat = ACQ_PROP_BAYER_PAT_GRGR; + break; + case MEDIA_BUS_FMT_SRGGB8_1X8: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: + mod->info.acq.raw_pattern = RPP_RGGB; + bayerpat = ACQ_PROP_BAYER_PAT_RGRG; + break; + default: + return -EINVAL; + } + + switch (fmt->code) { + case MEDIA_BUS_FMT_SBGGR8_1X8: + case MEDIA_BUS_FMT_SGBRG8_1X8: + case MEDIA_BUS_FMT_SGRBG8_1X8: + case MEDIA_BUS_FMT_SRGGB8_1X8: + selection = ACQ_PROP_INPUT_SELECTION_8BIT; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SRGGB10_1X10: + selection = ACQ_PROP_INPUT_SELECTION_10BIT; + break; + case MEDIA_BUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGRBG12_1X12: + case MEDIA_BUS_FMT_SRGGB12_1X12: + selection = ACQ_PROP_INPUT_SELECTION_12BIT; + break; + default: + return -EINVAL; + } + + rpp_module_write(mod, ACQ_PROP_REG, bayerpat | selection | + ACQ_PROP_SENSOR_IN_LSB_ALIGNED_IN_LSB); + + rpp_module_clrset(mod, ACQ_CTRL_REG, ACQ_CTRL_INFORM_EN_ENABLE, + ACQ_CTRL_INFORM_EN_ENABLE); + + return 0; +} + +const struct rpp_module_ops rppx1_acq_ops = { + .probe = rppx1_acq_probe, + .start = rppx1_acq_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c new file mode 100644 index 000000000000..4a242b41142b --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define AWB_GAIN_VERSION_REG 0x0000 + +#define AWB_ENABLE_REG 0x0004 +#define AWB_ENABLE_AWB_GAIN_EN BIT(0) + +#define AWB_GAIN_GR_REG 0x0008 +#define AWB_GAIN_GB_REG 0x000c +#define AWB_GAIN_R_REG 0x0010 +#define AWB_GAIN_B_REG 0x0014 + +static int rppx1_awbg_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, AWB_GAIN_VERSION_REG) != 3) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_awbg_ops = { + .probe = rppx1_awbg_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c new file mode 100644 index 000000000000..fcbaa0ee9281 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bd.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define DPF_VERSION_REG 0x0000 + +#define DPF_MODE_REG 0x0004 +#define DPF_MODE_USE_NF_GAIN BIT(9) +#define DPF_MODE_LSC_GAIN_COMP BIT(8) +#define DPF_MODE_NLL_SEGMENTATION BIT(6) +#define DPF_MODE_RB_FILTER_SIZE BIT(5) +#define DPF_MODE_R_FILTER_OFF BIT(4) +#define DPF_MODE_GR_FILTER_OFF BIT(3) +#define DPF_MODE_GB_FILTER_OFF BIT(2) +#define DPF_MODE_B_FILTER_OFF BIT(1) +#define DPF_MODE_DPF_ENABLE BIT(0) + +#define DPF_STRENGTH_R_REG 0x0008 +#define DPF_STRENGTH_G_REG 0x000c +#define DPF_STRENGTH_B_REG 0x0010 +#define DPF_S_WEIGHT_G_1_4_REG 0x0014 +#define DPF_S_WEIGHT_G_5_6_REG 0x0018 +#define DPF_S_WEIGHT_RB_1_4_REG 0x001c +#define DPF_S_WEIGHT_RB_5_6_REG 0x0020 + +#define DPF_NLL_G_COEFF_REG_NUM 17 +#define DPF_NLL_G_COEFF_REG(n) (0x0024 + (4 * (n))) + +#define DPF_NLL_RB_COEFF_REG_NUM 17 +#define DPF_NLL_RB_COEFF_REG(n) (0x0068 + (4 * (n))) + +#define DPF_NF_GAIN_R_REG 0x00ac +#define DPF_NF_GAIN_GR_REG 0x00b0 +#define DPF_NF_GAIN_GB_REG 0x00b4 +#define DPF_NF_GAIN_B_REG 0x00b8 + +static int rppx1_bd_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, DPF_VERSION_REG) != 5) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_bd_ops = { + .probe = rppx1_bd_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c new file mode 100644 index 000000000000..1accc106d65b --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bdrgb.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define RGBDENOISE_VERSION_REG 0x0000 + +#define RGBDENOISE_HW_BYPASS_REG 0x0004 +#define RGBDENOISE_HW_BYPASS_BYPASS_EN BIT(0) + +#define RGBDENOISE_SPNR_CTRL_REG 0x0008 +#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_C_MASK GENMASK(11, 8) +#define RGBDENOISE_SPNR_CTRL_C2NR_INTENSITY_SHIFT_Y_MASK GENMASK(7, 4) +#define RGBDENOISE_SPNR_CTRL_C2NR_EN BIT(0) + +#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_REG 0x000c +#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_REG 0x0010 +#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_REG 0x0014 +#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_REG 0x0018 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_REG 0x001c +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_REG 0x0020 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_REG 0x0024 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_REG 0x0028 +#define RGBDENOISE_SPNR_SPATIAL_COEF_0_3_REG 0x002c +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_REG 0x0030 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_REG 0x0034 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_REG 0x0038 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_REG 0x003c +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_REG 0x0040 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_REG 0x0044 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_REG 0x0048 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_REG 0x004c +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_REG 0x0050 +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_REG 0x0054 +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_REG 0x0058 +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_REG 0x005c +#define RGBDENOISE_HW_BYPASS_SDW_REG 0x0060 +#define RGBDENOISE_SPNR_CTRL_SDW_REG 0x0064 +#define RGBDENOISE_SPNR_LUMA_IF_COEF_00_07_SDW_REG 0x0068 +#define RGBDENOISE_SPNR_LUMA_IF_COEF_08_15_SDW_REG 0x006c +#define RGBDENOISE_SPNR_LUMA_IF_COEF_16_23_SDW_REG 0x0070 +#define RGBDENOISE_SPNR_LUMA_IF_COEF_24_31_SDW_REG 0x0074 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_00_07_SDW_REG 0x0078 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_08_15_SDW_REG 0x007c +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_16_23_SDW_REG 0x0080 +#define RGBDENOISE_SPNR_CHROMA_IF_COEF_24_31_SDW_REG 0x0084 +#define RGBDENOISE_SPNR_SPATIAL_COEFF_0_3_SDW_REG 0x0088 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_0_SDW_REG 0x008c +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_1_SDW_REG 0x0090 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_2_SDW_REG 0x0094 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_3_SDW_REG 0x0098 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_4_SDW_REG 0x009c +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_5_SDW_REG 0x00a0 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_6_SDW_REG 0x00a4 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_7_SDW_REG 0x00a8 +#define RGBDENOISE_RGB2YUV_CCOR_COEFF_8_SDW_REG 0x00ac +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_R_SDW_REG 0x00b0 +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_G_SDW_REG 0x00b4 +#define RGBDENOISE_RGB2YUV_CCOR_OFFSET_B_SDW_REG 0x00b8 + +static int rppx1_bdrgb_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, RGBDENOISE_VERSION_REG)) { + case 6: + /* 12-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_bdrgb_ops = { + .probe = rppx1_bdrgb_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c new file mode 100644 index 000000000000..882a9a819229 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define BLS_VERSION_REG 0x0000 + +#define BLS_CTRL_REG 0x0004 +#define BLS_CTRL_BLS_WIN2 BIT(3) +#define BLS_CTRL_BLS_WIN1 BIT(2) +#define BLS_CTRL_BLS_MODE_MEASURED BIT(1) +#define BLS_CTRL_BLS_EN BIT(0) + +#define BLS_SAMPLES_REG 0x0008 +#define BLS_H1_START_REG 0x000c +#define BLS_H1_STOP_REG 0x0010 +#define BLS_V1_START_REG 0x0014 +#define BLS_V1_STOP_REG 0x0018 +#define BLS_H2_START_REG 0x001c +#define BLS_H2_STOP_REG 0x0020 +#define BLS_V2_START_REG 0x0024 +#define BLS_V2_STOP_REG 0x0028 +#define BLS_A_FIXED_REG 0x002c +#define BLS_B_FIXED_REG 0x0030 +#define BLS_C_FIXED_REG 0x0034 +#define BLS_D_FIXED_REG 0x0038 +#define BLS_A_MEASURED_REG 0x003c +#define BLS_B_MEASURED_REG 0x0040 +#define BLS_C_MEASURED_REG 0x0044 +#define BLS_D_MEASURED_REG 0x0048 + +static int rppx1_bls_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, BLS_VERSION_REG)) { + case 3: + case 5: + /* 12-bit. */ + break; + case 2: + case 4: + /* 20-bit. */ + break; + case 6: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_bls_ops = { + .probe = rppx1_bls_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c new file mode 100644 index 000000000000..fb3bfa668425 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_cac.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define CAC_VERSION_REG 0x0000 +#define CAC_CTRL_REG 0x0004 +#define CAC_COUNT_START_REG 0x0008 +#define CAC_A_REG 0x000c +#define CAC_B_REG 0x0010 +#define CAC_C_REG 0x0014 +#define CAC_X_NORM_REG 0x0018 +#define CAC_Y_NORM_REG 0x001c + +static int rppx1_cac_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, CAC_VERSION_REG) != 3) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_cac_ops = { + .probe = rppx1_cac_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c new file mode 100644 index 000000000000..3bfad3ba12e6 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define CCOR_VERSION_REG 0x0000 + +#define CCOR_COEFF_REG_NUM 9 +#define CCOR_COEFF_REG(n) (0x0004 + (4 * (n))) + +#define CCOR_OFFSET_R_REG 0x0028 +#define CCOR_OFFSET_G_REG 0x002c +#define CCOR_OFFSET_B_REG 0x0030 + +#define CCOR_CONFIG_TYPE_REG 0x0034 +#define CCOR_CONFIG_TYPE_USE_OFFSETS_AS_PRE_OFFSETS BIT(1) +#define CCOR_CONFIG_TYPE_CCOR_RANGE_AVAILABLE BIT(0) + +#define CCOR_RANGE_REG 0x0038 +#define CCOR_RANGE_CCOR_C_RANGE BIT(1) +#define CCOR_RANGE_CCOR_Y_RANGE BIT(0) + +static int rppx1_ccor_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, CCOR_VERSION_REG)) { + case 3: + /* 12-bit. */ + break; + case 4: + /* 20-bit. */ + break; + case 5: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rppx1_ccor_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + /* Configure matrix in bypass mode. */ + rpp_module_write(mod, CCOR_COEFF_REG(0), 0x1000); + rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0000); + rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0000); + + rpp_module_write(mod, CCOR_COEFF_REG(3), 0x0000); + rpp_module_write(mod, CCOR_COEFF_REG(4), 0x1000); + rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0000); + + rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0000); + rpp_module_write(mod, CCOR_COEFF_REG(7), 0x0000); + rpp_module_write(mod, CCOR_COEFF_REG(8), 0x1000); + + rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000); + rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000000); + rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000000); + + return 0; +} + +const struct rpp_module_ops rppx1_ccor_ops = { + .probe = rppx1_ccor_probe, + .start = rppx1_ccor_start, +}; + +static int rppx1_ccor_csm_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + /* Reuse bypass matrix setup. */ + if (fmt->code == MEDIA_BUS_FMT_RGB888_1X24) + return rppx1_ccor_start(mod, fmt); + + /* Color Transformation RGB to YUV according to ITU-R BT.709. */ + rpp_module_write(mod, CCOR_COEFF_REG(0), 0x0367); + rpp_module_write(mod, CCOR_COEFF_REG(1), 0x0b71); + rpp_module_write(mod, CCOR_COEFF_REG(2), 0x0128); + + rpp_module_write(mod, CCOR_COEFF_REG(3), 0xfe2b); + rpp_module_write(mod, CCOR_COEFF_REG(4), 0xf9d5); + rpp_module_write(mod, CCOR_COEFF_REG(5), 0x0800); + + rpp_module_write(mod, CCOR_COEFF_REG(6), 0x0800); + rpp_module_write(mod, CCOR_COEFF_REG(7), 0xf8bc); + rpp_module_write(mod, CCOR_COEFF_REG(8), 0xff44); + + rpp_module_write(mod, CCOR_OFFSET_R_REG, 0x00000000); + rpp_module_write(mod, CCOR_OFFSET_G_REG, 0x00000800); + rpp_module_write(mod, CCOR_OFFSET_B_REG, 0x00000800); + + return 0; +} + +const struct rpp_module_ops rppx1_ccor_csm_ops = { + .probe = rppx1_ccor_probe, + .start = rppx1_ccor_csm_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_db.c b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c new file mode 100644 index 000000000000..78a330066b7e --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_db.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define FILT_VERSION_REG 0x0000 + +#define DEMOSAIC_REG 0x0004 +#define DEMOSAIC_DEMOSAIC_BYPASS BIT(16) +#define DEMOSAIC_DEMOSAIC_TH_MASK GENMASK(15, 0) + +#define FILT_MODE_REG 0x0008 +#define FILT_MODE_FILT_LP_SELECT_MASK GENMASK(11, 8) +#define FILT_MODE_FILT_CHR_H_MODE_MASK GENMASK(7, 6) +#define FILT_MODE_FILT_CHR_V_MODE_MASK GENMASK(5, 4) +#define FILT_MODE_FILT_MODE BIT(1) +#define FILT_MODE_FILT_ENABLE BIT(0) + +#define FILT_THRESH_BL0_REG 0x000c +#define FILT_THRESH_BL1_REG 0x0010 +#define FILT_THRESH_SH0_REG 0x0014 +#define FILT_THRESH_SH1_REG 0x0018 +#define FILT_LUM_WEIGHT_REG 0x001c +#define FILT_FAC_SH1_REG 0x0020 +#define FILT_FAC_SH0_REG 0x0024 +#define FILT_FAC_MID_REG 0x0028 +#define FILT_FAC_BL0_REG 0x002c +#define FILT_FAC_BL1_REG 0x0030 + +static int rppx1_db_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, FILT_VERSION_REG) != 5) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_db_ops = { + .probe = rppx1_db_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c new file mode 100644 index 000000000000..3d5d9c0a7e72 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_dpcc.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define DPCC_VERSION_REG 0x0000 + +#define DPCC_MODE_REG 0x0004 +#define DPCC_MODE_STAGE1_ENABLE BIT(2) +#define DPCC_MODE_GRAYSCALE_MODE BIT(1) +#define DPCC_MODE_DPCC_ENABLE BIT(0) + +#define DPCC_OUTPUT_MODE_REG 0x0008 +#define DPCC_SET_USE_REG 0x000c +#define DPCC_METHODS_SET_1_REG 0x0010 +#define DPCC_METHODS_SET_2_REG 0x0014 +#define DPCC_METHODS_SET_3_REG 0x0018 +#define DPCC_LINE_THRESH_1_REG 0x001c +#define DPCC_LINE_MAD_FAC_1_REG 0x0020 +#define DPCC_PG_FAC_1_REG 0x0024 +#define DPCC_RND_THRESH_1_REG 0x0028 +#define DPCC_RG_FAC_1_REG 0x002c +#define DPCC_LINE_THRESH_2_REG 0x0030 +#define DPCC_LINE_MAD_FAC_2_REG 0x0034 +#define DPCC_PG_FAC_2_REG 0x0038 +#define DPCC_RND_THRESH_2_REG 0x003c +#define DPCC_RG_FAC_2_REG 0x0040 +#define DPCC_LINE_THRESH_3_REG 0x0044 +#define DPCC_LINE_MAD_FAC_3_REG 0x0048 +#define DPCC_PG_FAC_3_REG 0x004c +#define DPCC_RND_THRESH_3_REG 0x0050 +#define DPCC_RG_FAC_3_REG 0x0054 +#define DPCC_RO_LIMITS_REG 0x0058 +#define DPCC_RND_OFFS_REG 0x005c +#define DPCC_BPT_CTRL_REG 0x0060 +#define DPCC_BP_NUMBER_REG 0x0064 +#define DPCC_BP_TADDR_REG 0x0068 +#define DPCC_BP_POSITION_REG 0x006c + +static int rppx1_dpcc_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, DPCC_VERSION_REG)) { + case 2: + case 4: + case 6: + /* 12-bit. */ + break; + case 3: + case 5: + case 7: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rppx1_dpcc_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + /* Bypass stage1 and DPCC. */ + rpp_module_write(mod, DPCC_MODE_REG, 0); + + return 0; +} + +const struct rpp_module_ops rppx1_dpcc_ops = { + .probe = rppx1_dpcc_probe, + .start = rppx1_dpcc_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c new file mode 100644 index 000000000000..1dccb110d3e9 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define EXM_VERSION_REG 0x0000 +#define EXM_START_REG 0x0004 + +#define EXM_CTRL_REG 0x0008 +#define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0) + +#define EXM_MODE_REG 0x000c +#define EXM_CHANNEL_SEL_REG 0x0010 +#define EXM_LAST_MEAS_LINE_REG 0x0014 +#define EXM_COEFF_R_REG 0x0018 +#define EXM_COEFF_G_GR_REG 0x001c +#define EXM_COEFF_B_REG 0x0020 +#define EXM_COEFF_GB_REG 0x0024 +#define EXM_H_OFFS_REG 0x0028 +#define EXM_V_OFFS_REG 0x002c +#define EXM_H_SIZE_REG 0x0030 +#define EXM_V_SIZE_REG 0x0034 +#define EXM_FORCED_UPD_START_LINE_REG 0x0038 +#define EXM_VSTART_STATUS_REG 0x003c + +#define EXM_MEAN_REG(n) (0x0040 + (4 * (n))) + +static int rppx1_exm_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, EXM_VERSION_REG)) { + case 1: + /* 8-bit. */ + break; + case 3: + /* 20-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_exm_ops = { + .probe = rppx1_exm_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c new file mode 100644 index 000000000000..0667672b2694 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define GAMMA_OUT_VERSION_REG 0x0000 + +#define GAMMA_OUT_ENABLE_REG 0x0004 +#define GAMMA_OUT_ENABLE_GAMMA_OUT_EN BIT(0) + +#define GAMMA_OUT_MODE_REG 0x0008 +#define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0) + +#define GAMMA_OUT_Y_REG_NUM 17 +#define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n))) + +static int rppx1_ga_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, GAMMA_OUT_VERSION_REG)) { + case 1: + /* 12-bit. */ + break; + case 2: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rppx1_ga_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + /* Disable stage. */ + rpp_module_write(mod, GAMMA_OUT_ENABLE_REG, 0); + + return 0; +} + +const struct rpp_module_ops rppx1_ga_ops = { + .probe = rppx1_ga_probe, + .start = rppx1_ga_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c new file mode 100644 index 000000000000..7c1b42e96b96 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define HIST_VERSION_REG 0x0000 + +#define HIST_CTRL_REG 0x0004 +#define HIST_CTRL_HIST_UPDATE_ENABLE BIT(0) + +#define HIST_MODE_REG 0x0008 +#define HIST_MODE_HIST_MODE_MASK GENMASK(2, 0) +#define HIST_MODE_HIST_MODE_DISABLE 0 +#define HIST_MODE_HIST_MODE_YRGB 1 +#define HIST_MODE_HIST_MODE_R 2 +#define HIST_MODE_HIST_MODE_GR 3 +#define HIST_MODE_HIST_MODE_B 4 +#define HIST_MODE_HIST_MODE_GB 5 + +#define HIST_CHANNEL_SEL_REG 0x000c +#define HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0) + +#define HIST_LAST_MEAS_LINE_REG 0x0010 +#define HIST_SUBSAMPLING_REG 0x0014 +#define HIST_COEFF_R_REG 0x0018 +#define HIST_COEFF_G_REG 0x001c +#define HIST_COEFF_B_REG 0x0020 +#define HIST_H_OFFS_REG 0x0024 +#define HIST_V_OFFS_REG 0x0028 +#define HIST_H_SIZE_REG 0x002c +#define HIST_V_SIZE_REG 0x0030 + +#define HIST_SAMPLE_RANGE_REG 0x0034 +#define HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK GENMASK(28, 24) +#define HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK GENMASK(23, 0) + +#define HIST_WEIGHT_00TO30_REG 0x0038 +#define HIST_WEIGHT_40TO21_REG 0x003c +#define HIST_WEIGHT_31TO12_REG 0x0040 +#define HIST_WEIGHT_22TO03_REG 0x0044 +#define HIST_WEIGHT_13TO43_REG 0x0048 +#define HIST_WEIGHT_04TO34_REG 0x004c +#define HIST_WEIGHT_44_REG 0x0050 +#define HIST_FORCED_UPD_START_LINE_REG 0x0054 +#define HIST_FORCED_UPDATE_REG 0x0058 +#define HIST_VSTART_STATUS_REG 0x005c + +#define HIST_BIN_REG_NUM 32 +#define HIST_BIN_REG(n) (0x0060 + (4 * (n))) + +static int rppx1_hist_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, HIST_VERSION_REG)) { + case 3: + /* 12-bit. */ + break; + case 4: + /* 20-bit. */ + break; + case 5: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_hist_ops = { + .probe = rppx1_hist_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c new file mode 100644 index 000000000000..80b3244c77aa --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist256.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define HIST256_VERSION_REG 0x0000 +#define HIST256_MODE_REG 0x0004 +#define HIST256_MODE_HIST256_MODE BIT(0) + +#define HIST256_CHANNEL_SEL_REG 0x0008 +#define HIST256_CHANNEL_SEL_CHANNEL_SELECT GENMASK(2, 0) + +#define HIST256_H_OFFS_REG 0x000c +#define HIST256_V_OFFS_REG 0x0010 +#define HIST256_H_SIZE_REG 0x0014 +#define HIST256_V_SIZE_REG 0x0018 +#define HIST256_SAMPLE_OFFSET_REG 0x001c +#define HIST256_SAMPLE_SCALE_REG 0x0020 +#define HIST256_MEAS_RESULT_ADDR_AUTOINCR_REG 0x0024 +#define HIST256_MEAS_RESULT_ADDR_REG 0x0028 +#define HIST256_MEAS_RESULT_DATA_REG 0x002c + +#define HIST256_LOG_ENABLE_REG 0x0030 +#define HIST256_LOG_ENABLE_HIST256_LOG_EN BIT(0) + +#define HIST256_LOG_DX_LO_REG 0x0034 +#define HIST256_LOG_DX_HI_REG 0x0038 + +#define HIST256_Y_REG_NUM 17 +#define HIST256_Y_REG(n) (0x0040 + (4 * (n))) + +static int rppx1_hist256_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, HIST256_VERSION_REG) != 2) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_hist256_ops = { + .probe = rppx1_hist256_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_is.c b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c new file mode 100644 index 000000000000..7b1b6f6538cf --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_is.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define IS_VERSION 0x0000 +#define IS_H_OFFS 0x0008 +#define IS_V_OFFS 0x000c +#define IS_H_SIZE 0x0010 +#define IS_V_SIZE 0x0014 +#define IS_H_OFFS_SHD 0x0024 +#define IS_V_OFFS_SHD 0x0028 +#define IS_H_SIZE_SHD 0x002c +#define IS_V_SIZE_SHD 0x0030 + +static int rppx1_is_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, IS_VERSION) != 1) + return -EINVAL; + + return 0; +} + +static int rppx1_is_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + rpp_module_write(mod, IS_H_OFFS, 0); + rpp_module_write(mod, IS_V_OFFS, 0); + rpp_module_write(mod, IS_H_SIZE, fmt->width); + rpp_module_write(mod, IS_V_SIZE, fmt->height); + + return 0; +} + +const struct rpp_module_ops rppx1_is_ops = { + .probe = rppx1_is_probe, + .start = rppx1_is_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c new file mode 100644 index 000000000000..0a0e7ab2d1d6 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +/* NOTE: The module is called LIN the registers GAMMA_IN. */ +#define LIN_VERSION_REG 0x0000 + +#define LIN_ENABLE_REG 0x0004 +#define LIN_ENABLE_GAMMA_IN_EN BIT(0) + +#define LIN_DX_LO_REG 0x0008 +#define LIN_DX_HI_REG 0x000c + +#define LIN_R_Y_REG_NUM 17 +#define LIN_R_Y_REG(n) (0x0010 + (4 * (n))) + +#define LIN_G_Y_REG_NUM 17 +#define LIN_G_Y_REG(n) (0x0054 + (4 * (n))) + +#define LIN_B_Y_REG_NUM 17 +#define LIN_B_Y_REG(n) (0x0098 + (4 * (n))) + +static int rppx1_lin_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, LIN_VERSION_REG)) { + case 7: + /* 12-bit. */ + break; + case 8: + /* 20-bit. */ + break; + case 9: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rppx1_lin_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + rpp_module_clrset(mod, LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN, 0); + + return 0; +} + +const struct rpp_module_ops rppx1_lin_ops = { + .probe = rppx1_lin_probe, + .start = rppx1_lin_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c new file mode 100644 index 000000000000..be49fc17ea26 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define LSC_VERSION_REG 0x0000 + +#define LSC_CTRL_REG 0x0004 +#define LSC_CTRL_LSC_EN BIT(0) + +#define LSC_R_TABLE_ADDR_REG 0x0008 +#define LSC_GR_TABLE_ADDR_REG 0x000c +#define LSC_B_TABLE_ADDR_REG 0x0010 +#define LSC_GB_TABLE_ADDR_REG 0x0014 +#define LSC_R_TABLE_DATA_REG 0x0018 +#define LSC_GR_TABLE_DATA_REG 0x001c +#define LSC_B_TABLE_DATA_REG 0x0020 +#define LSC_GB_TABLE_DATA_REG 0x0024 +#define LSC_XGRAD_01_REG 0x0028 +#define LSC_XGRAD_23_REG 0x002c +#define LSC_XGRAD_45_REG 0x0030 +#define LSC_XGRAD_67_REG 0x0034 +#define LSC_XGRAD_89_REG 0x0038 +#define LSC_XGRAD_1011_REG 0x003c +#define LSC_XGRAD_1213_REG 0x0040 +#define LSC_XGRAD_1415_REG 0x0044 +#define LSC_YGRAD_01_REG 0x0048 +#define LSC_YGRAD_23_REG 0x004c +#define LSC_YGRAD_45_REG 0x0050 +#define LSC_YGRAD_67_REG 0x0054 +#define LSC_YGRAD_89_REG 0x0058 +#define LSC_YGRAD_1011_REG 0x005c +#define LSC_YGRAD_1213_REG 0x0060 +#define LSC_YGRAD_1415_REG 0x0064 +#define LSC_XSIZE_01_REG 0x0068 +#define LSC_XSIZE_23_REG 0x006c +#define LSC_XSIZE_45_REG 0x0070 +#define LSC_XSIZE_67_REG 0x0074 +#define LSC_XSIZE_89_REG 0x0078 +#define LSC_XSIZE_1011_REG 0x007c +#define LSC_XSIZE_1213_REG 0x0080 +#define LSC_XSIZE_1415_REG 0x0084 +#define LSC_YSIZE_01_REG 0x0088 +#define LSC_YSIZE_23_REG 0x008c +#define LSC_YSIZE_45_REG 0x0090 +#define LSC_YSIZE_67_REG 0x0094 +#define LSC_YSIZE_89_REG 0x0098 +#define LSC_YSIZE_1011_REG 0x009c +#define LSC_YSIZE_1213_REG 0x00a0 +#define LSC_YSIZE_1415_REG 0x00a4 +#define LSC_TABLE_SEL_REG 0x00a8 +#define LSC_STATUS_REG 0x00ac + +static int rppx1_lsc_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, LSC_VERSION_REG) != 0x04) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_lsc_ops = { + .probe = rppx1_lsc_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c new file mode 100644 index 000000000000..9a71155e7e13 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltm.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define LTM_VERSION_REG 0x0000 + +#define LTM_CTRL_REG 0x0004 +#define LTM_CTRL_LTM_ENABLE BIT(0) + +#define LTM_RGB_WEIGHTS_REG 0x0008 +#define LTM_CLB_LINESIZE_REG 0x000c +#define LTM_TONECURVE_1_REG 0x0010 +#define LTM_TONECURVE_2_REG 0x0014 +#define LTM_TONECURVE_3_REG 0x0018 +#define LTM_TONECURVE_4_REG 0x001c +#define LTM_TONECURVE_5_REG 0x0020 +#define LTM_TONECURVE_6_REG 0x0024 +#define LTM_TONECURVE_YM_REG(n) (0x0028 + (4 * (n))) +#define LTM_L0W_REG 0x00ec +#define LTM_L0W_R_REG 0x00f0 +#define LTM_L0D_REG 0x00f4 +#define LTM_L0D_R_REG 0x00f8 +#define LTM_KMIND_REG 0x00fc +#define LTM_KMAXD_REG 0x0100 +#define LTM_KDIFFD_REG 0x0104 +#define LTM_KDIFFD_R_REG 0x0108 +#define LTM_KW_REG 0x010c +#define LTM_KW_R_REG 0x0110 +#define LTM_CGAIN_REG 0x0114 +#define LTM_LPRCH_R_HIGH_REG 0x0118 +#define LTM_LPRCH_R_LOW_REG 0x011c + +static int rppx1_ltm_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, LTM_VERSION_REG) != 8) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_ltm_ops = { + .probe = rppx1_ltm_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c new file mode 100644 index 000000000000..c874b8fa8999 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ltmmeas.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define LTM_MEAS_VERSION_REG 0x0000 + +#define LTM_MEAS_CTRL_REG 0x0004 +#define LTM_MEAS_CTRL_LTM_MEAS_ENABLE BIT(0) + +#define LTM_MEAS_RGB_WEIGHTS_REG 0x0008 +#define LTM_MEAS_H_OFFS_REG 0x000c +#define LTM_MEAS_V_OFFS_REG 0x0010 +#define LTM_MEAS_H_SIZE_REG 0x0014 +#define LTM_MEAS_V_SIZE_REG 0x0018 + +#define LTM_MEAS_PRC_THRESH_NUM 8 +#define LTM_MEAS_PRC_THRESH_REG(n) (0x001c + (4 * (n))) + +#define LTM_MEAS_PRC_REG_NUM 8 +#define LTM_MEAS_PRC_REG(n) (0x003c + (4 * (n))) + +#define LTM_MEAS_L_MIN_REG 0x005c +#define LTM_MEAS_L_MAX_REG 0x0060 +#define LTM_MEAS_L_GMEAN_REG 0x0064 + +static int rppx1_ltmmeas_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, LTM_MEAS_VERSION_REG) != 1) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_ltmmeas_ops = { + .probe = rppx1_ltmmeas_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c new file mode 100644 index 000000000000..973b82a40d7c --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outif.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define OUT_IF_VERSION_REG 0x0000 + +#define OUT_IF_ON_REG 0x0004 +#define OUT_IF_ON_RPP_ON BIT(0) + +#define OUT_IF_OFF_REG 0x0008 + +#define OUT_IF_NR_FRAMES_REG 0x000c +#define OUT_IF_NR_FRAMES_NR_FRAMES GENMASK(9, 0) + +#define OUT_IF_NR_FRAMES_CNT_REG 0x0010 +#define FLAGS_SHD_REG 0x0018 + +static int rppx1_outif_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, OUT_IF_VERSION_REG) != 1) + return -EINVAL; + + return 0; +} + +static int rppx1_outif_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + rpp_module_clrset(mod, OUT_IF_NR_FRAMES_REG, + OUT_IF_NR_FRAMES_NR_FRAMES, 0); + + rpp_module_write(mod, OUT_IF_ON_REG, OUT_IF_ON_RPP_ON); + + return 0; +} + +const struct rpp_module_ops rppx1_outif_ops = { + .probe = rppx1_outif_probe, + .start = rppx1_outif_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c new file mode 100644 index 000000000000..ac8b71867137 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_outregs.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define OUTREGS_VERSION_REG 0x0000 + +#define OUT_MODE_REG 0x0004 +#define OUT_MODE_UNSELECTED_MODE_MASK GENMASK(11, 8) +#define OUT_MODE_UNSELECTED_MODE_MAIN (0x1 << 8) +#define OUT_MODE_UNSELECTED_MODE_PRE1 (0x2 << 8) +#define OUT_MODE_UNSELECTED_MODE_PRE2 (0x4 << 8) +#define OUT_MODE_IN_SEL_MASK GENMASK(3, 0) +#define OUT_MODE_IN_SEL_MAIN 1 +#define OUT_MODE_IN_SEL_PRE1 2 +#define OUT_MODE_IN_SEL_PRE2 4 + +#define OUT_CONV_422_METHOD_REG 0x0008 +#define OUT_CONV_422_METHOD_CONV_422_METHOD_MASK GENMASK(1, 0) +#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1 0 +#define OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED2 1 +#define OUT_CONV_422_METHOD_CONV_422_METHOD_NON_CO_SITED 2 + +#define OUTREGS_FORMAT_REG 0x000c +#define OUTREGS_FORMAT_OUTPUT_FORMAT_MASK GENMASK(1, 0) +#define OUTREGS_FORMAT_OUTPUT_FORMAT_RGB 0 +#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422 1 +#define OUTREGS_FORMAT_OUTPUT_FORMAT_YUV420 2 + +static int rppx1_outregs_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, OUTREGS_VERSION_REG) != 2) + return -EINVAL; + + return 0; +} + +static int rppx1_outregs_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + u32 format; + + switch (fmt->code) { + case MEDIA_BUS_FMT_YUYV12_1X24: + format = OUTREGS_FORMAT_OUTPUT_FORMAT_YUV422; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + format = OUTREGS_FORMAT_OUTPUT_FORMAT_RGB; + break; + default: + return -EINVAL; + } + + rpp_module_clrset(mod, OUT_MODE_REG, + OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MASK, + OUT_MODE_UNSELECTED_MODE_MASK | OUT_MODE_IN_SEL_MAIN); + + rpp_module_clrset(mod, OUT_CONV_422_METHOD_REG, + OUT_CONV_422_METHOD_CONV_422_METHOD_MASK, + OUT_CONV_422_METHOD_CONV_422_METHOD_CO_SITED1); + + rpp_module_clrset(mod, OUTREGS_FORMAT_REG, + OUTREGS_FORMAT_OUTPUT_FORMAT_MASK, format); + + return 0; +} + +const struct rpp_module_ops rppx1_outregs_ops = { + .probe = rppx1_outregs_probe, + .start = rppx1_outregs_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c new file mode 100644 index 000000000000..0a891caf3c52 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmap.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define RMAP_DATA_VERSION_REG 0x0000 + +#define RMAP_CTRL_REG 0x0004 +#define RMAP_CTRL_BYPASS_LONG BIT(2) + +#define RMAP_WBTHRESHOLD_LONG_REG 0x0008 +#define RMAP_WBTHRESHOLD_SHORT_REG 0x000c +#define RMAP_RESERVED_1_REG 0x0010 +#define RMAP_WBGAIN_LONG_RED_REG 0x0014 +#define RMAP_WBGAIN_LONG_BLUE_REG 0x0018 +#define RMAP_WBGAIN_SHORT_RED_REG 0x001c +#define RMAP_WBGAIN_SHORT_BLUE_REG 0x0020 +#define RMAP_RESERVED_2_REG 0x0024 +#define RMAP_RESERVED_3_REG 0x0028 +#define RMAP_MAP_FAC_SHORT_REG 0x002c +#define RMAP_RESERVED_4_REG 0x0030 +#define RMAP_MIN_THRES_SHORT_REG 0x0034 +#define RMAP_MAX_THRES_SHORT_REG 0x0038 +#define RMAP_STEPSIZE_SHORT_REG 0x003c +#define RMAP_MIN_THRES_LONG_REG 0x0040 +#define RMAP_MAX_THRES_LONG_REG 0x0044 +#define RMAP_STEPSIZE_LONG_REG 0x0048 +#define RMAP_CLB_LINESIZE_REG 0x004c + +static int rppx1_rmap_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, RMAP_DATA_VERSION_REG)) { + case 8: + /* low: 12-bit, high: 20-bit. */ + break; + case 9: + /* low: 12-bit, high: 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rppx1_rmap_start(struct rpp_module *mod, + const struct v4l2_mbus_framefmt *fmt) +{ + /* Bypass radiance mapping and use the long exposure channel (PRE1). */ + rpp_module_write(mod, RMAP_CTRL_REG, RMAP_CTRL_BYPASS_LONG); + + return 0; +} + +const struct rpp_module_ops rppx1_rmap_ops = { + .probe = rppx1_rmap_probe, + .start = rppx1_rmap_start, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c new file mode 100644 index 000000000000..ef709d2a6ee9 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_rmapmeas.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define RMAP_MEAS_VERSION_REG 0x0000 +#define RMAP_MEAS_MODE_REG 0x0004 +#define RMAP_MEAS_SUBSAMPLING_REG 0x0008 +#define RMAP_MEAS_RESERVED_1_REG 0x000c +#define RMAP_MEAS_MIN_THRES_SHORT_REG 0x0010 +#define RMAP_MEAS_MAX_THRES_SHORT_REG 0x0014 +#define RMAP_MEAS_MAX_THRES_LONG_REG 0x0018 +#define RMAP_MEAS_H_OFFS_REG 0x001c +#define RMAP_MEAS_V_OFFS_REG 0x0020 +#define RMAP_MEAS_H_SIZE_REG 0x0024 +#define RMAP_MEAS_V_SIZE_REG 0x0028 +#define RMAP_MEAS_LAST_MEAS_LINE_REG 0x002c +#define RMAP_MEAS_LS_RESULTSHORT0_REG 0x0030 +#define RMAP_MEAS_LS_RESULTLONG0_REG 0x0034 +#define RMAP_MEAS_RESERVED_2_REG 0x0038 +#define RMAP_MEAS_RESERVED_3_REG 0x003c +#define RMAP_MEAS_LS_RESULTSHORT1_REG 0x0040 +#define RMAP_MEAS_LS_RESULTLONG1_REG 0x0044 +#define RMAP_MEAS_RESERVED_4_REG 0x0048 +#define RMAP_MEAS_RESERVED_5_REG 0x004c + +static int rppx1_rmapmeas_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, RMAP_MEAS_VERSION_REG)) { + case 3: + /* low: 12-bit, high: 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_rmapmeas_ops = { + .probe = rppx1_rmapmeas_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c new file mode 100644 index 000000000000..c155418129e5 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_shrp.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define SHRPCNR_VERSION_REG 0x0000 + +#define SHRPCNR_CTRL_REG 0x0004 +#define SHRPCNR_CTRL_CAD_EN BIT(3) +#define SHRPCNR_CTRL_DESAT_EN BIT(2) +#define SHRPCNR_CTRL_CNR_EN BIT(1) +#define SHRPCNR_CTRL_SHARPEN_EN BIT(0) + +#define SHRPCNR_PARAM_REG 0x0008 +#define SHRPCNR_PARAM_SHARP_FACTOR_MASK GENMASK(19, 12) +#define SHRPCNR_PARAM_CORING_THR_MASK GENMASK(11, 0) + +#define SHRPCNR_MAT_1_REG 0x000c +#define SHRPCNR_MAT_2_REG 0x0010 +#define SHRPCNR_CLB_LINESIZE_REG 0x0014 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_0_REG 0x0018 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_1_REG 0x001c +#define SHRPCNR_YUV2RGB_CCOR_COEFF_2_REG 0x0020 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_3_REG 0x0024 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_4_REG 0x0028 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_5_REG 0x002c +#define SHRPCNR_YUV2RGB_CCOR_COEFF_6_REG 0x0030 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_7_REG 0x0034 +#define SHRPCNR_YUV2RGB_CCOR_COEFF_8_REG 0x0038 +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_R_REG 0x003c +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_G_REG 0x0040 +#define SHRPCNR_YUV2RGB_CCOR_OFFSET_B_REG 0x0044 + +#define SHRPCNR_CNR_THRES_REG 0x0048 +#define SHRPCNR_CNR_THRES_CNR_THRES_CR_MASK GENMASK(27, 16) +#define SHRPCNR_CNR_THRES_CNR_THRES_CB_MASK GENMASK(11, 0) + +#define SHRPCNR_CRED_THRES_REG 0x004c +#define SHRPCNR_CRED_SLOPE_REG 0x0050 +#define SHRPCNR_CAD_RESTORE_LVL_REG 0x0054 +#define SHRPCNR_CAD_THRESH_V_UNEG_REG 0x0058 +#define SHRPCNR_CAD_THRESH_V_UPOS_REG 0x005c +#define SHRPCNR_CAD_THRESH_U_REG 0x0060 + +static int rppx1_shrp_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, SHRPCNR_VERSION_REG)) { + case 2: + /* 12-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_shrp_ops = { + .probe = rppx1_shrp_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c new file mode 100644 index 000000000000..60b9c076ec42 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define AWB_MEAS_VERSION_REG 0x0000 + +#define AWB_MEAS_PROP_REG 0x0004 +#define AWB_MEAS_PROP_MEAS_MODE_RGB BIT(16) /* 0: YCbCr 1: RGB */ +#define AWB_MEAS_PROP_YMAX BIT(2) +#define AWB_MEAS_PROP_AWB_MODE_ON BIT(1) + +#define AWB_MEAS_H_OFFS_REG 0x0008 +#define AWB_MEAS_V_OFFS_REG 0x000c +#define AWB_MEAS_H_SIZE_REG 0x0010 +#define AWB_MEAS_V_SIZE_REG 0x0014 +#define AWB_MEAS_FRAMES_REG 0x0018 +#define AWB_MEAS_REF_CB_MAX_B_REG 0x001c +#define AWB_MEAS_REF_CR_MAX_R_REG 0x0020 +#define AWB_MEAS_MAX_Y_REG 0x0024 +#define AWB_MEAS_MIN_Y_MAX_G_REG 0x0028 +#define AWB_MEAS_MAX_CSUM_REG 0x002c +#define AWB_MEAS_MIN_C_REG 0x0030 +#define AWB_MEAS_WHITE_CNT_REG 0x0034 +#define AWB_MEAS_MEAN_Y_G_REG 0x0038 +#define AWB_MEAS_MEAN_CB_B_REG 0x003c +#define AWB_MEAS_MEAN_CR_R_REG 0x0040 + +#define AWB_MEAS_CCOR_COEFF_NUM 9 +#define AWB_MEAS_CCOR_COEFF_REG(n) (0x0044 + (4 * (n))) + +#define AWB_MEAS_CCOR_OFFSET_R_REG 0x0068 +#define AWB_MEAS_CCOR_OFFSET_G_REG 0x006c +#define AWB_MEAS_CCOR_OFFSET_B_REG 0x0070 + +static int rppx1_wbmeas_probe(struct rpp_module *mod) +{ + /* Version check. */ + switch (rpp_module_read(mod, AWB_MEAS_VERSION_REG)) { + case 1: + /* 8-bit. */ + break; + case 2: + /* 20-bit. */ + break; + case 3: + /* 24-bit. */ + break; + default: + return -EINVAL; + } + + return 0; +} + +const struct rpp_module_ops rppx1_wbmeas_ops = { + .probe = rppx1_wbmeas_probe, +}; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c new file mode 100644 index 000000000000..cc6f977c8a03 --- /dev/null +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_xyz2luv.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include "rpp_module.h" + +#define XYZ2LUV_VERSION_REG 0x0000 +#define XYZ2LUV_U_REF_REG 0x0004 +#define XYZ2LUV_V_REF_REG 0x0008 +#define XYZ2LUV_LUMA_OUT_FAC_REG 0x000c +#define XYZ2LUV_CHROMA_OUT_FAC_REG 0x0010 + +static int rppx1_xyz2luv_probe(struct rpp_module *mod) +{ + /* Version check. */ + if (rpp_module_read(mod, XYZ2LUV_VERSION_REG) != 4) + return -EINVAL; + + return 0; +} + +const struct rpp_module_ops rppx1_xyz2luv_ops = { + .probe = rppx1_xyz2luv_probe, +}; diff --git a/include/media/rppx1.h b/include/media/rppx1.h new file mode 100644 index 000000000000..2e761046db38 --- /dev/null +++ b/include/media/rppx1.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#ifndef __MEDIA_DCT_RPPX1_H__ +#define __MEDIA_DCT_RPPX1_H__ + +#include +#include + +#include + +struct rppx1; + +struct rppx1 *rppx1_create(void __iomem *base, struct device *dev); + +void rppx1_destroy(struct rppx1 *rpp); + +int rppx1_start(struct rppx1 *rpp, const struct v4l2_mbus_framefmt *input, + const struct v4l2_mbus_framefmt *hv, + const struct v4l2_mbus_framefmt *mv); + +int rppx1_stop(struct rppx1 *rpp); + +bool rppx1_interrupt(struct rppx1 *rpp, u32 *isc); + +typedef int (*rppx1_reg_write)(void *priv, u32 offset, u32 value); +int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, + rppx1_reg_write write, void *priv); + +void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf); + +#endif /* __MEDIA_DCT_RPPX1_H__ */ -- cgit From 2151350f60d14f3e734e879cf126af32a5c48d86 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:32 +0200 Subject: media: rcar-isp: Add support for ISPCORE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Renesas R-Car ISP block consists of two different IP blocks, one CSI-2 Channel Selector (CSISP) and one traditional ISP for image operation (ISPCORE). The R-Car ISP driver currently supports the CSISP functionality as part of the video capture pipeline, this change adds support for the ISPCORE functionality. The ISPCORE functionality is further split in two parts, a Renesas specific part and a Dream Chip Real-time Pixel Processor IP part (RPPX1). The Renesas part deals with I/O to/from the block while the RPPX1 part deals with the actual ISP functions. The RPPX1 functionality is implemented in a support framework (DCT RPPX1) as this block can be used by different vendors or setups. This change deals with the Renesas part of exposing the V4L2 elements needed for a user-space interface to the RPPX1 and deals with the DMA to/from the RPP block. It also facilitates the user-space V4L2 API to allow configuring the RPPX1 using the DCT RPPX1 support framework. The functionality exposed are one input video device where RAW bayer frames can be queued for processing, one output video device where the debayerd image can be read as either ABGR32 or NV16M format. Further more a video device to queue the image processing parameters to configure the RPPX1 IPS as well as a video device to read statistics about the processed image is available. There is no change in the operation of the CSISP functionality. Signed-off-by: Niklas Söderlund [ VSPX interfacing + locking sanitizing ] Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/platform/renesas/rcar-isp/Kconfig | 2 + drivers/media/platform/renesas/rcar-isp/Makefile | 2 +- drivers/media/platform/renesas/rcar-isp/core-io.c | 997 +++++++++++++++++++++ drivers/media/platform/renesas/rcar-isp/core.c | 901 +++++++++++++++++++ drivers/media/platform/renesas/rcar-isp/csisp.c | 46 +- .../media/platform/renesas/rcar-isp/risp-core.h | 178 ++++ 6 files changed, 2119 insertions(+), 7 deletions(-) create mode 100644 drivers/media/platform/renesas/rcar-isp/core-io.c create mode 100644 drivers/media/platform/renesas/rcar-isp/core.c create mode 100644 drivers/media/platform/renesas/rcar-isp/risp-core.h diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig index 242f6a23851f..bacc15c250fe 100644 --- a/drivers/media/platform/renesas/rcar-isp/Kconfig +++ b/drivers/media/platform/renesas/rcar-isp/Kconfig @@ -5,10 +5,12 @@ config VIDEO_RCAR_ISP depends on V4L_PLATFORM_DRIVERS depends on VIDEO_DEV && OF depends on ARCH_RENESAS || COMPILE_TEST + depends on VIDEO_RENESAS_VSP1 select MEDIA_CONTROLLER select VIDEO_V4L2_SUBDEV_API select RESET_CONTROLLER select V4L2_FWNODE + select VIDEO_DCT_RPPX1 help Support for Renesas R-Car Image Signal Processor (ISP). Enable this to support the Renesas R-Car Image Signal diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile index b542118c831e..c0c80303682c 100644 --- a/drivers/media/platform/renesas/rcar-isp/Makefile +++ b/drivers/media/platform/renesas/rcar-isp/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -rcar-isp-objs = csisp.o +rcar-isp-objs = csisp.o core.o core-io.o obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o diff --git a/drivers/media/platform/renesas/rcar-isp/core-io.c b/drivers/media/platform/renesas/rcar-isp/core-io.c new file mode 100644 index 000000000000..820af506f896 --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/core-io.c @@ -0,0 +1,997 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include +#include +#include +#include +#include + +#include + +#include "risp-core.h" + +#define risp_io_err(d, fmt, arg...) dev_err((d)->core->dev, fmt, ##arg) + +static struct risp_buffer *risp_io_vb2buf(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct risp_buffer, vb); +} + +static int risp_io_open(struct file *file) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + int ret; + + ret = mutex_lock_interruptible(&io->lock); + if (ret) + return ret; + + file->private_data = io; + + ret = v4l2_fh_open(file); + if (ret) + goto err_unlock; + + ret = v4l2_pipeline_pm_get(&io->vdev.entity); + if (ret < 0) + goto err_open; + + mutex_unlock(&io->lock); + + return 0; +err_open: + v4l2_fh_release(file); +err_unlock: + mutex_unlock(&io->lock); + + return ret; +} + +static int risp_io_release(struct file *file) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + int ret; + + mutex_lock(&io->lock); + + ret = _vb2_fop_release(file, NULL); + + v4l2_pipeline_pm_put(&io->vdev.entity); + + mutex_unlock(&io->lock); + + return ret; +} + +static const struct v4l2_file_operations risp_io_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = risp_io_open, + .release = risp_io_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * Common queue + */ + +static int risp_io_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) + +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq); + + if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { + const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp; + + if (*nplanes) { + if (*nplanes > pix->num_planes) + return -EINVAL; + + for (unsigned int i = 0; i < pix->num_planes; i++) + if (sizes[i] < pix->plane_fmt[i].sizeimage) + return -EINVAL; + + return 0; + } + + *nplanes = pix->num_planes; + for (unsigned int i = 0; i < pix->num_planes; i++) + sizes[i] = pix->plane_fmt[i].sizeimage; + } else { + if (*nplanes) { + if (sizes[0] < io->format.fmt.meta.buffersize) + return -EINVAL; + + return 0; + } + + *nplanes = 1; + sizes[0] = io->format.fmt.meta.buffersize; + } + + /* Initialize buffer queue */ + INIT_LIST_HEAD(&io->buffers); + + return 0; +}; + +static int risp_io_buffer_prepare_set(struct rcar_isp_core_io *io, + struct vb2_buffer *vb, unsigned int plane, + unsigned long size) +{ + if (vb2_plane_size(vb, plane) < size) { + risp_io_err(io, "Buffer too small (%lu < %lu)\n", + vb2_plane_size(vb, plane), size); + return -EINVAL; + } + + vb2_set_plane_payload(vb, plane, size); + + return 0; +} + +static int risp_io_buffer_prepare(struct vb2_buffer *vb) +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue); + + if (V4L2_TYPE_IS_MULTIPLANAR(vb->vb2_queue->type)) { + const struct v4l2_pix_format_mplane *pix = &io->format.fmt.pix_mp; + int ret = 0; + + for (unsigned int i = 0; i < pix->num_planes; i++) { + ret = risp_io_buffer_prepare_set(io, vb, i, + pix->plane_fmt[i].sizeimage); + if (ret) + break; + } + + return ret; + } + + return risp_io_buffer_prepare_set(io, vb, 0, + io->format.fmt.meta.buffersize); +} + +static void risp_io_buffer_queue(struct vb2_buffer *vb) +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct risp_buffer *buf = risp_io_vb2buf(vbuf); + + guard(mutex)(&io->core->io_lock); + + list_add_tail(&buf->list, &io->buffers); + + if (risp_core_job_prepare(io->core)) + risp_io_err(io, "Failed to prepare job\n"); +} + +static void risp_io_return_buffers(struct rcar_isp_core_io *io, + enum vb2_buffer_state state) +{ + struct risp_buffer *buf, *node; + + lockdep_assert_held(&io->core->io_lock); + + list_for_each_entry_safe(buf, node, &io->buffers, list) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->list); + } +} + +static int risp_io_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq); + int ret; + + scoped_guard(mutex, &io->core->io_lock) { + if (io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width != + io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width || + io->core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height != + io->core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height) { + risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED); + return -EPIPE; + } + + io->streaming = true; + } + + ret = risp_core_start_streaming(io->core); + if (ret) { + guard(mutex)(&io->core->io_lock); + + risp_io_return_buffers(io, VB2_BUF_STATE_QUEUED); + return ret; + } + + return 0; +} + +static void risp_io_stop_streaming(struct vb2_queue *vq) +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq); + + scoped_guard(mutex, &io->core->io_lock) { + io->streaming = false; + risp_core_stop_streaming(io->core); + risp_io_return_buffers(io, VB2_BUF_STATE_ERROR); + } + + /* + * Wait for buffers part of the jobs not yet processed. Note that this + * might complete buffers out of order. + */ + vb2_wait_for_all_buffers(&io->queue); +} + +/* ----------------------------------------------------------------------------- + * Common V4L2 IOCTLs + */ + +static int risp_io_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + + strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); + strscpy(cap->card, vdev->name, sizeof(cap->card)); + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Input Exposure + */ + +static int risp_io_input_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) + +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vq); + struct rcar_isp_core *core = io->core; + struct device *bus_master; + int ret; + + ret = risp_io_queue_setup(vq, nbuffers, nplanes, sizes, alloc_devs); + if (ret) + return ret; + + bus_master = vsp1_isp_get_bus_master(core->vspx.dev); + if (IS_ERR_OR_NULL(bus_master)) { + risp_io_err(io, "Missing reference to bus-master device\n"); + return -EINVAL; + } + + /* + * Allocate buffers using the bus_master device associated with the + * VSPX associated to this ISP instance. + */ + alloc_devs[0] = bus_master; + + return 0; +}; + +static const struct vb2_ops risp_io_input_qops = { + .queue_setup = risp_io_input_queue_setup, + .buf_prepare = risp_io_buffer_prepare, + .buf_queue = risp_io_buffer_queue, + .start_streaming = risp_io_start_streaming, + .stop_streaming = risp_io_stop_streaming, +}; + +static const struct v4l2_pix_format_mplane risp_io_input_default_format = { + .width = 1920, + .height = 1080, + .field = V4L2_FIELD_NONE, + .pixelformat = V4L2_PIX_FMT_SGRBG8, + .colorspace = V4L2_COLORSPACE_RAW, + .xfer_func = V4L2_XFER_FUNC_NONE, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .num_planes = 1, + .plane_fmt = { + [0] = { + .sizeimage = 1920 * 1080, + .bytesperline = 1920, + }, + }, +}; + +static const struct risp_io_input_format { + unsigned int fourcc; + unsigned int bpp; +} risp_io_input_formats[] = { + { .fourcc = V4L2_PIX_FMT_SBGGR8, .bpp = 1 }, + { .fourcc = V4L2_PIX_FMT_SGBRG8, .bpp = 1 }, + { .fourcc = V4L2_PIX_FMT_SGRBG8, .bpp = 1 }, + { .fourcc = V4L2_PIX_FMT_SRGGB8, .bpp = 1 }, + { .fourcc = V4L2_PIX_FMT_SBGGR10, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SGBRG10, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SGRBG10, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SRGGB10, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SBGGR12, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SGBRG12, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SGRBG12, .bpp = 2 }, + { .fourcc = V4L2_PIX_FMT_SRGGB12, .bpp = 2 }, +}; + +static void risp_io_input_try_format(struct rcar_isp_core_io *io, + struct v4l2_pix_format_mplane *pix) +{ + unsigned int bpp = 0; + + v4l_bound_align_image(&pix->width, 128, 5120, 2, + &pix->height, 128, 4096, 2, 0); + + for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) { + if (risp_io_input_formats[i].fourcc == pix->pixelformat) { + bpp = risp_io_input_formats[i].bpp; + break; + } + } + + if (!bpp) { + pix->pixelformat = risp_io_input_formats[0].fourcc; + bpp = risp_io_input_formats[0].bpp; + } + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_RAW; + + pix->num_planes = 1; + pix->plane_fmt[0].bytesperline = pix->width * bpp; + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height; +} + +static int risp_io_input_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= ARRAY_SIZE(risp_io_input_formats)) + return -EINVAL; + + f->pixelformat = risp_io_input_formats[f->index].fourcc; + + return 0; +} + +static int risp_io_input_g_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + f->fmt.pix_mp = io->format.fmt.pix_mp; + + return 0; +} + +static int risp_io_input_s_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (vb2_is_busy(&io->queue)) + return -EBUSY; + + if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + risp_io_input_try_format(io, &f->fmt.pix_mp); + + io->format.fmt.pix_mp = f->fmt.pix_mp; + + return 0; +} + +static int risp_io_input_try_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + risp_io_input_try_format(io, &f->fmt.pix_mp); + + return 0; +} + +static int risp_io_input_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + bool found = false; + + if (fsize->index != 0) + return -EINVAL; + + for (unsigned int i = 0; i < ARRAY_SIZE(risp_io_input_formats); i++) { + if (risp_io_input_formats[i].fourcc == fsize->pixel_format) { + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = 128; + fsize->stepwise.max_width = 5120; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = 128; + fsize->stepwise.max_height = 4096; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops risp_io_input_ioctl_ops = { + .vidioc_querycap = risp_io_querycap, + + .vidioc_enum_fmt_vid_out = risp_io_input_enum_fmt, + .vidioc_g_fmt_vid_out_mplane = risp_io_input_g_fmt, + .vidioc_s_fmt_vid_out_mplane = risp_io_input_s_fmt, + .vidioc_try_fmt_vid_out_mplane = risp_io_input_try_fmt, + .vidioc_enum_framesizes = risp_io_input_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * Parameters + * + */ + +static int risp_io_params_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct risp_buffer *buf = risp_io_vb2buf(vbuf); + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue); + struct rcar_isp_core *core = io->core; + size_t size; + int ret; + + memset(&buf->vsp_buffer, 0, sizeof(buf->vsp_buffer)); + + size = RISP_IO_PARAMS_BUF_SIZE; + ret = vsp1_isp_alloc_buffer(core->vspx.dev, size, &buf->vsp_buffer); + if (ret) + return -EINVAL; + + memset(buf->vsp_buffer.cpu_addr, 0, RISP_IO_PARAMS_BUF_SIZE); + + return 0; +} + +static void risp_io_params_buf_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct risp_buffer *buf = risp_io_vb2buf(vbuf); + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue); + struct rcar_isp_core *core = io->core; + + vsp1_isp_free_buffer(core->vspx.dev, &buf->vsp_buffer); +} + +struct risp_conf_dma_write_desc { + u32 *buf; + u32 base; + unsigned int count; +}; + +static int risp_conf_dma_prepare(void *priv, u32 offset, u32 value) +{ + struct risp_conf_dma_write_desc *desc = priv; + + /* Bounds check, 8 bytes = address (4)+ value (4). */ + if ((desc->count + 1) * 8 > RISP_IO_PARAMS_BUF_SIZE) + return -ENOMEM; + + (*desc->buf++) = desc->base | offset; + (*desc->buf++) = value; + + desc->count++; + + return 0; +} + +static int risp_io_params_buffer_prepare(struct vb2_buffer *vb) +{ + struct rcar_isp_core_io *io = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct risp_buffer *buf = risp_io_vb2buf(vbuf); + struct risp_conf_dma_write_desc desc; + u32 *cpu_addr; + int ret; + + /* Prepare params. */ + cpu_addr = (u32 *)buf->vsp_buffer.cpu_addr; + + desc.buf = cpu_addr + 2; + desc.base = io->core->rppaddr; + desc.count = 0; + + /* Fill params body. */ + ret = rppx1_params(io->core->rpp, vb, io->format.fmt.meta.buffersize, + risp_conf_dma_prepare, &desc); + if (ret) + return ret; + + /* Fill params header. */ + cpu_addr[0] = desc.count; + cpu_addr[1] = 0x0; + + return 0; +} + +static const struct vb2_ops risp_io_params_qops = { + .queue_setup = risp_io_queue_setup, + .buf_init = risp_io_params_buf_init, + .buf_cleanup = risp_io_params_buf_cleanup, + .buf_prepare = risp_io_params_buffer_prepare, + .buf_queue = risp_io_buffer_queue, + .start_streaming = risp_io_start_streaming, + .stop_streaming = risp_io_stop_streaming, +}; + +static const struct v4l2_meta_format risp_io_params_default_format = { + .dataformat = V4L2_META_FMT_RPPX1_PARAMS, + .buffersize = v4l2_isp_buffer_size(RPPX1_PARAMS_MAX_SIZE), +}; + +static int risp_io_params_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (f->type != V4L2_BUF_TYPE_META_OUTPUT || f->index) + return -EINVAL; + + f->pixelformat = io->format.fmt.meta.dataformat; + + return 0; +} + +static int risp_io_params_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != V4L2_BUF_TYPE_META_OUTPUT) + return -EINVAL; + + *meta = io->format.fmt.meta; + + return 0; +} + +static int risp_io_params_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (vb2_is_busy(&io->queue)) + return -EBUSY; + + return risp_io_params_g_fmt(file, priv, f); +} + +static const struct v4l2_ioctl_ops risp_io_params_ioctl_ops = { + .vidioc_querycap = risp_io_querycap, + + .vidioc_enum_fmt_meta_out = risp_io_params_enum_fmt, + .vidioc_g_fmt_meta_out = risp_io_params_g_fmt, + .vidioc_s_fmt_meta_out = risp_io_params_s_fmt, + .vidioc_try_fmt_meta_out = risp_io_params_g_fmt, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * Statistics + */ + +static const struct vb2_ops risp_io_stats_qops = { + .queue_setup = risp_io_queue_setup, + .buf_prepare = risp_io_buffer_prepare, + .buf_queue = risp_io_buffer_queue, + .start_streaming = risp_io_start_streaming, + .stop_streaming = risp_io_stop_streaming, +}; + +static const struct v4l2_meta_format risp_io_stats_default_format = { + .dataformat = V4L2_META_FMT_RPPX1_STATS, + .buffersize = v4l2_isp_buffer_size(RPPX1_STATS_MAX_SIZE), +}; + +static int risp_io_stats_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (f->type != V4L2_BUF_TYPE_META_CAPTURE || f->index) + return -EINVAL; + + f->pixelformat = io->format.fmt.meta.dataformat; + + return 0; +} + +static int risp_io_stats_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != V4L2_BUF_TYPE_META_CAPTURE) + return -EINVAL; + + *meta = io->format.fmt.meta; + + return 0; +} + +static int risp_io_stats_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (vb2_is_busy(&io->queue)) + return -EBUSY; + + return risp_io_stats_g_fmt(file, priv, f); +} + +static const struct v4l2_ioctl_ops risp_io_stats_ioctl_ops = { + .vidioc_querycap = risp_io_querycap, + + .vidioc_enum_fmt_meta_cap = risp_io_stats_enum_fmt, + .vidioc_g_fmt_meta_cap = risp_io_stats_g_fmt, + .vidioc_s_fmt_meta_cap = risp_io_stats_s_fmt, + .vidioc_try_fmt_meta_cap = risp_io_stats_g_fmt, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * Video capture + */ + +static const struct vb2_ops risp_io_capture_qops = { + .queue_setup = risp_io_queue_setup, + .buf_prepare = risp_io_buffer_prepare, + .buf_queue = risp_io_buffer_queue, + .start_streaming = risp_io_start_streaming, + .stop_streaming = risp_io_stop_streaming, +}; + +static const struct v4l2_pix_format_mplane risp_io_capture_default_format = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_XBGR32, + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + .num_planes = 1, + .plane_fmt = { + [0] = { + .bytesperline = ALIGN(1920 * 4, 256), + .sizeimage = ALIGN(1920 * 4, 256) * 1080, + }, + }, +}; + +static void risp_io_capture_try_format(struct rcar_isp_core_io *io, + struct v4l2_pix_format_mplane *pix) +{ + v4l_bound_align_image(&pix->width, 128, 5120, 2, + &pix->height, 128, 4096, 2, 0); + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->ycbcr_enc = V4L2_YCBCR_ENC_601; + pix->xfer_func = V4L2_XFER_FUNC_SRGB; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV16M: + pix->quantization = V4L2_QUANTIZATION_LIM_RANGE; + pix->num_planes = 2; + pix->plane_fmt[0].bytesperline = ALIGN(pix->width, 256); + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height; + pix->plane_fmt[1].bytesperline = ALIGN(pix->width, 256); + pix->plane_fmt[1].sizeimage = pix->plane_fmt[1].bytesperline * pix->height; + break; + default: + pix->pixelformat = V4L2_PIX_FMT_XBGR32; + pix->quantization = V4L2_QUANTIZATION_FULL_RANGE; + pix->num_planes = 1; + pix->plane_fmt[0].bytesperline = ALIGN(pix->width * 4, 256); + pix->plane_fmt[0].sizeimage = pix->plane_fmt[0].bytesperline * pix->height; + break; + } +} + +static int risp_io_capture_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_NV16M; + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_XBGR32; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int risp_io_capture_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + f->fmt.pix_mp = io->format.fmt.pix_mp; + + return 0; +} + +static int risp_io_capture_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + if (vb2_is_busy(&io->queue)) + return -EBUSY; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + risp_io_capture_try_format(io, &f->fmt.pix_mp); + + io->format.fmt.pix_mp = f->fmt.pix_mp; + + return 0; +} + +static int risp_io_capture_try_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rcar_isp_core_io *io = video_drvdata(file); + + risp_io_capture_try_format(io, &f->fmt.pix_mp); + + return 0; +} + +static int risp_io_capture_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + if (fsize->index != 0) + return -EINVAL; + + switch (fsize->pixel_format) { + case V4L2_PIX_FMT_NV16M: + case V4L2_PIX_FMT_XBGR32: + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + fsize->stepwise.min_width = 128; + fsize->stepwise.max_width = 5120; + fsize->stepwise.step_width = 2; + + fsize->stepwise.min_height = 128; + fsize->stepwise.max_height = 4096; + fsize->stepwise.step_height = 2; + + return 0; +} + +static const struct v4l2_ioctl_ops risp_io_capture_ioctl_ops = { + .vidioc_querycap = risp_io_querycap, + + .vidioc_enum_fmt_vid_cap = risp_io_capture_enum_fmt, + .vidioc_g_fmt_vid_cap_mplane = risp_io_capture_g_fmt, + .vidioc_s_fmt_vid_cap_mplane = risp_io_capture_s_fmt, + .vidioc_try_fmt_vid_cap_mplane = risp_io_capture_try_fmt, + .vidioc_enum_framesizes = risp_io_capture_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +/* ----------------------------------------------------------------------------- + * Create and remove IO video devices + */ + +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core, + struct rcar_isp_core_io *io, unsigned int pad) +{ + struct video_device *vdev = &io->vdev; + struct vb2_queue *q = &io->queue; + int ret; + + switch (pad) { + case RISP_CORE_INPUT1: + snprintf(vdev->name, sizeof(vdev->name), "%s %s input1", + KBUILD_MODNAME, dev_name(dev)); + vdev->vfl_dir = VFL_DIR_TX; + vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE; + vdev->ioctl_ops = &risp_io_input_ioctl_ops; + + q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + q->ops = &risp_io_input_qops; + + io->pad.flags = MEDIA_PAD_FL_SOURCE; + io->format.fmt.pix_mp = risp_io_input_default_format; + break; + + case RISP_CORE_PARAMS: + snprintf(vdev->name, sizeof(vdev->name), "%s %s params", + KBUILD_MODNAME, dev_name(dev)); + vdev->vfl_dir = VFL_DIR_TX; + vdev->device_caps = V4L2_CAP_META_OUTPUT; + vdev->ioctl_ops = &risp_io_params_ioctl_ops; + + q->type = V4L2_BUF_TYPE_META_OUTPUT; + q->ops = &risp_io_params_qops; + + io->pad.flags = MEDIA_PAD_FL_SOURCE; + io->format.fmt.meta = risp_io_params_default_format; + break; + + case RISP_CORE_STATS: + snprintf(vdev->name, sizeof(vdev->name), "%s %s stats", + KBUILD_MODNAME, dev_name(dev)); + vdev->vfl_dir = VFL_DIR_RX; + vdev->device_caps = V4L2_CAP_META_CAPTURE; + vdev->ioctl_ops = &risp_io_stats_ioctl_ops; + + q->type = V4L2_BUF_TYPE_META_CAPTURE; + q->ops = &risp_io_stats_qops; + + io->pad.flags = MEDIA_PAD_FL_SINK; + io->format.fmt.meta = risp_io_stats_default_format; + break; + + case RISP_CORE_OUTPUT1: + snprintf(vdev->name, sizeof(vdev->name), "%s %s output1", + KBUILD_MODNAME, dev_name(dev)); + vdev->vfl_dir = VFL_DIR_RX; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE; + vdev->ioctl_ops = &risp_io_capture_ioctl_ops; + + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->ops = &risp_io_capture_qops; + + io->pad.flags = MEDIA_PAD_FL_SINK; + io->format.fmt.pix_mp = risp_io_capture_default_format; + break; + } + + io->core = core; + + mutex_init(&io->lock); + INIT_LIST_HEAD(&io->buffers); + + /* Create media graph pad. */ + ret = media_entity_pads_init(&io->vdev.entity, 1, &io->pad); + if (ret) + return ret; + + /* Create queue */ + q->io_modes = VB2_MMAP | VB2_DMABUF; + q->lock = &io->lock; + q->drv_priv = io; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct risp_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->dev = dev; + + ret = vb2_queue_init(q); + if (ret < 0) { + risp_io_err(io, "Failed to initialize VB2 queue\n"); + return ret; + } + + /* Create video device */ + vdev->v4l2_dev = &core->v4l2_dev; + vdev->queue = &io->queue; + + vdev->release = video_device_release_empty; + vdev->lock = &io->lock; + vdev->fops = &risp_io_fops; + + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC; + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + risp_io_err(io, "Failed to register video device\n"); + return ret; + } + + video_set_drvdata(&io->vdev, io); + + v4l2_info(&core->v4l2_dev, "Device registered as %s\n", + video_device_node_name(vdev)); + + switch (pad) { + case RISP_CORE_INPUT1: + case RISP_CORE_PARAMS: + ret = media_create_pad_link(&io->vdev.entity, 0, + &core->subdev.entity, pad, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + break; + case RISP_CORE_STATS: + case RISP_CORE_OUTPUT1: + ret = media_create_pad_link(&core->subdev.entity, pad, + &io->vdev.entity, 0, + MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); + break; + } + + return ret; +} + +void risp_core_io_destroy(struct rcar_isp_core_io *io) +{ + if (!video_is_registered(&io->vdev)) + return; + + vb2_video_unregister_device(&io->vdev); +} diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c new file mode 100644 index 000000000000..f3dc52c13612 --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/core.c @@ -0,0 +1,901 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#include +#include +#include + +#include +#include +#include + +#include "risp-core.h" + +#define ISP_CS_STREAMER_MODE_REG 0x7000 +#define ISP_CS_STREAMER_MODE_STREAMER_EN 0xf + +#define ISP_CS_STREAMER_VBLANK_REG 0x7004 +#define ISP_CS_STREAMER_HBLANK_REG 0x7008 + +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG 0x7100 +#define ISP_CS_STREAMER_CONFIG_DMA_REG_ADDRESS_UPPER_8BIT_MASK GENMASK(31, 24) +#define ISP_CS_STREAMER_CONFIG_DMA_ENABLE0 BIT(0) + +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG 0x2100 +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 BIT(31) +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_CONFIG_DATA_START_REG_ADDRESS_MASK GENMASK(15, 0) + +#define ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG 0x2104 + +#define ISP_CORE_ISPCORE_INT_STATUS 0x80000 +#define ISP_CORE_ISPCORE_INT_ENABLE 0x80004 +#define ISPCORE_DMA_IMAGE_FRAME_MODE(i, f) (0x84000 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(i, f) (0x84004 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(i, f) (0x84008 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(i, f) (0x8400c + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(i, f) (0x84010 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(i, f) (0x84014 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP2(i, f) (0x84018 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP3(i, f) (0x8401c + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(i, f) (0x84020 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(i, f) (0x84024 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP2(i, f) (0x84028 + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP3(i, f) (0x8402c + 0x1000 * (i) + 0x100 * (f)) +#define ISPCORE_DMA_IMAGE_FRAME_AXI_ID(i, f) (0x84030 + 0x1000 * (i) + 0x100 * (f)) + +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(i) (0x84400 + 0x1000 * (i)) +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_MASK GENMASK(31, 16) +#define ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT 16 + +#define ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(i) (0x84800 + 0x1000 * (i)) + +static void risp_cs_write(struct rcar_isp_core *core, u32 offset, u32 value) +{ + iowrite32(value, core->csbase + offset); +} + +static u32 risp_cs_read(struct rcar_isp_core *core, u32 offset) +{ + return ioread32(core->csbase + offset); +} + +static void risp_core_write(struct rcar_isp_core *core, u32 offset, u32 value) +{ + iowrite32(value, core->base + offset); +} + +static u32 risp_core_read(struct rcar_isp_core *core, u32 offset) +{ + return ioread32(core->base + offset); +} + +static void risp_core_job_run_params(struct rcar_isp_core *core, + struct vsp1_isp_job_desc *vspx_job, + struct risp_buffer *buf) +{ + u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr; + bool have_config = !!params_buf[0]; + u32 ctrl0, ctrl1, ctrl2; + + /* + * If we have a configuration but not asked the VSPX to program it, + * use MMIO to write the configuration. This might be needed to work + * around limitations of the VSPX ConfigDMA, see comment in + * risp_core_job_prepare(). + */ + if (have_config && !vspx_job->config.pairs) { + for (unsigned int i = 0; i < params_buf[0]; i++) + risp_core_write(core, params_buf[2 + i * 2] & 0xffff, + params_buf[3 + i * 2]); + + /* Disable ConfigDMA. */ + have_config = false; + } + + ctrl0 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG) & + ~ISP_CS_STREAMER_CONFIG_DMA_ENABLE0; + ctrl1 = risp_cs_read(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG) & + ~(ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | 0xffff); + ctrl2 = 0; + + if (have_config) { + ctrl0 |= ISP_CS_STREAMER_CONFIG_DMA_ENABLE0; + ctrl1 |= ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_ENABLE1 | + (params_buf[2] & 0xffff); + ctrl2 = params_buf[3]; + } + + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL_REG, ctrl0); + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL1_REG, ctrl1); + risp_cs_write(core, ISP_CS_STREAMER_CONFIG_DMA_CONTROL2_REG, ctrl2); +} + +static void risp_core_job_run_output(struct rcar_isp_core *core, + struct risp_buffer *buf) +{ + const struct v4l2_format *fmt = &core->io[RISP_CORE_OUTPUT1].format; + dma_addr_t mem; + u32 reg; + + for (unsigned int frame = 0; frame < 4; frame++) { + reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP0(0, frame); + mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + risp_core_write(core, reg, mem); + + /* Only NV16 uses 2 planes. */ + if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV16M) + continue; + + reg = ISPCORE_DMA_IMAGE_FRAME_BASE_ADDRESS_COMP1(0, frame); + mem = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 1); + risp_core_write(core, reg, mem); + } +} + +static void risp_core_job_run(struct rcar_isp_core *core) +{ + struct rcar_isp_job *job; + + lockdep_assert_held(&core->lock); + + /* ISP not yet started, nothing to do. */ + if (!core->streaming) + return; + + /* If we have active buffers in the ISP core, nothing to do. */ + if (core->vspx.job) + return; + + job = list_first_entry_or_null(&core->risp_jobs, + struct rcar_isp_job, + job_queue); + if (!job) + return; + + list_del(&job->job_queue); + + core->vspx.job = job; + + /* Program the ISP register before kicking the VSPX. */ + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + struct risp_buffer *buf = job->buffers[i]; + + switch (i) { + case RISP_CORE_PARAMS: + risp_core_job_run_params(core, &job->vspx_job, buf); + break; + case RISP_CORE_OUTPUT1: + risp_core_job_run_output(core, buf); + break; + } + } + + if (vsp1_isp_job_run(core->vspx.dev, &job->vspx_job)) { + /* + * Release all buffers in this job if running on the VSPX + * failed. Userspace should recover from this, no new jobs are + * scheduled. + */ + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + struct risp_buffer *buf = job->buffers[i]; + + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + vsp1_isp_job_release(core->vspx.dev, &job->vspx_job); + core->vspx.job = NULL; + kfree(job); + + dev_err(core->dev, "Failed to run job"); + } +} + +static int risp_core_pixfmt_to_vspx(u32 pixfmt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: + case V4L2_PIX_FMT_SRGGB8: + return V4L2_PIX_FMT_GREY; + case V4L2_PIX_FMT_SBGGR10: + case V4L2_PIX_FMT_SGBRG10: + case V4L2_PIX_FMT_SGRBG10: + case V4L2_PIX_FMT_SRGGB10: + return V4L2_PIX_FMT_Y10; + case V4L2_PIX_FMT_SBGGR12: + case V4L2_PIX_FMT_SGBRG12: + case V4L2_PIX_FMT_SGRBG12: + case V4L2_PIX_FMT_SRGGB12: + return V4L2_PIX_FMT_Y12; + default: + return -EINVAL; + } +} + +int risp_core_job_prepare(struct rcar_isp_core *core) +{ + struct vsp1_isp_job_desc *vspx_job; + int vspx_pixfmt = -EINVAL; + struct rcar_isp_job *job; + int ret; + + lockdep_assert_held(&core->io_lock); + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + if (list_empty(&core->io[i].buffers)) + return 0; + } + + /* Memory is released when the job is consumed. */ + job = kzalloc(sizeof(*job), GFP_KERNEL); + if (!job) + return -ENOMEM; + + vspx_job = &job->vspx_job; + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + struct risp_buffer *buf; + + /* + * Extract buffer from the IO queue and save a reference in + * the job description. Buffers will be completed when the + * corresponding frame will be completed by the ISP. + */ + buf = list_first_entry_or_null(&core->io[i].buffers, + struct risp_buffer, list); + /* + * This should not happen as we have checked there is buffers, + * with the lock held, but check the return value anyhow. + */ + if (WARN_ON(!buf)) { + ret = -EINVAL; + goto error_return_buffers; + } + + switch (i) { + case RISP_CORE_INPUT1: { + u32 isp_pixfmt = core->io[i].format.fmt.pix_mp.pixelformat; + + vspx_pixfmt = risp_core_pixfmt_to_vspx(isp_pixfmt); + + vspx_job->img.fmt = core->io[i].format.fmt.pix_mp; + vspx_job->img.fmt.pixelformat = vspx_pixfmt; + vspx_job->img.mem = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, + 0); + break; + } + case RISP_CORE_PARAMS: { + u32 *params_buf = (u32 *)buf->vsp_buffer.cpu_addr; + u32 pairs = params_buf[0]; + + /* + * Check config pairs not larger then buffer. + * + * Remove 8 byte header and each pair is 16 bytes. + */ + if (pairs > (RISP_IO_PARAMS_BUF_SIZE - 8) / 16) { + ret = -EINVAL; + goto error_return_buffers; + } + + /* + * Work around undocumented behavior of the ConfigDMA + * interface by using MMIO if 16 or less pairs are to + * be programmed. + * + * Programming 15 or less pairs corrupts the image data + * following the config buffer, programming exactly 16 + * pairs freeze the whole VSPX. + */ + if (pairs <= 16) { + vspx_job->config.pairs = 0; + } else { + vspx_job->config.pairs = pairs; + vspx_job->config.mem = buf->vsp_buffer.dma_addr; + } + break; + } + } + + list_del(&buf->list); + job->buffers[i] = buf; + } + + if (vspx_pixfmt < 0) { + ret = -EINVAL; + goto error_return_buffers; + } + + ret = vsp1_isp_job_prepare(core->vspx.dev, vspx_job); + if (ret) + goto error_return_buffers; + + scoped_guard(spinlock_irqsave, &core->lock) { + list_add_tail(&job->job_queue, &core->risp_jobs); + risp_core_job_run(core); + } + + return 0; + +error_return_buffers: + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + if (!job->buffers[i]) + continue; + + vb2_buffer_done(&job->buffers[i]->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + } + kfree(job); + return ret; +} + +static int risp_core_config_output(struct rcar_isp_core *core, + unsigned int index, + const struct v4l2_pix_format_mplane *pix) +{ + /* For all frame capture slots. */ + for (unsigned int frame = 0; frame < 4; frame++) { + switch (pix->pixelformat) { + case V4L2_PIX_FMT_NV16M: + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame), + 1); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame), + 0 << 24 | 0 << 16 | 4 << 8 | 16 << 0); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame), + 0 << 24 | 0 << 16 | 7 << 8 | 7 << 0); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame), + 0 << 28 | 0 << 24 | + 0 << 20 | 0 << 16 | + 3 << 12 | 0 << 8 | + 3 << 4 | 0 << 0); + + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame), + pix->plane_fmt[0].bytesperline); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP1(index, frame), + pix->plane_fmt[1].bytesperline); + break; + case V4L2_PIX_FMT_XBGR32: + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_MODE(index, frame), + 0); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_POSITION(index, frame), + 0 << 24 | 0 << 16 | 0 << 8 | 0 << 0); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BITWIDTH_MINUS1(index, frame), + 0 << 24 | 0 << 16 | 0 << 8 | 23 << 0); + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_PIXEL_BPP(index, frame), + 0 << 28 | 0 << 24 | + 0 << 20 | 0 << 16 | + 0 << 12 | 0 << 8 | + 3 << 4 | 2 << 0); + + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_STRIDE_COMP0(index, frame), + pix->plane_fmt[0].bytesperline); + break; + default: + return -EINVAL; + } + + risp_core_write(core, + ISPCORE_DMA_IMAGE_FRAME_AXI_ID(index, frame), + 0); + } + + /* Set image out flush EOF. */ + risp_core_write(core, ISPCORE_DMA_IMAGE_FLUSH_OUT_REG(index), + pix->plane_fmt[0].bytesperline << + ISPCORE_DMA_IMAGE_FLUSH_OUT_PADDING_PIXEL_EOF_SHIFT); + + /* Enable DMA and set burst length. */ + risp_core_write(core, ISPCORE_DMA_IMAGE_AXI_CONFIG_REG(index), + BIT(31) | 7); + + return 0; +} + +static u32 risp_core_pix2bus(const struct rcar_isp_core_io *io) +{ + switch (io->format.fmt.pix_mp.pixelformat) { + case V4L2_PIX_FMT_SBGGR8: + return MEDIA_BUS_FMT_SBGGR8_1X8; + case V4L2_PIX_FMT_SGBRG8: + return MEDIA_BUS_FMT_SGBRG8_1X8; + case V4L2_PIX_FMT_SGRBG8: + return MEDIA_BUS_FMT_SGRBG8_1X8; + case V4L2_PIX_FMT_SRGGB8: + return MEDIA_BUS_FMT_SRGGB8_1X8; + case V4L2_PIX_FMT_SBGGR10: + return MEDIA_BUS_FMT_SBGGR10_1X10; + case V4L2_PIX_FMT_SGBRG10: + return MEDIA_BUS_FMT_SGBRG10_1X10; + case V4L2_PIX_FMT_SGRBG10: + return MEDIA_BUS_FMT_SGRBG10_1X10; + case V4L2_PIX_FMT_SRGGB10: + return MEDIA_BUS_FMT_SRGGB10_1X10; + case V4L2_PIX_FMT_SBGGR12: + return MEDIA_BUS_FMT_SBGGR12_1X12; + case V4L2_PIX_FMT_SGBRG12: + return MEDIA_BUS_FMT_SGBRG12_1X12; + case V4L2_PIX_FMT_SGRBG12: + return MEDIA_BUS_FMT_SGRBG12_1X12; + case V4L2_PIX_FMT_SRGGB12: + return MEDIA_BUS_FMT_SRGGB12_1X12; + case V4L2_PIX_FMT_XBGR32: + return MEDIA_BUS_FMT_RGB888_1X24; + case V4L2_PIX_FMT_NV16M: + return MEDIA_BUS_FMT_YUYV12_1X24; + default: + return 0; + } +} + +static void risp_core_try_next_job(struct rcar_isp_core *core) +{ + lockdep_assert_held(&core->lock); + + struct rcar_isp_job *job = core->vspx.job; + + /* If the ISP or the VSPX is not done with the job, wait. */ + if (!job || !job->done_isp || !job->done_vspx) + return; + + core->vspx.job = NULL; + kfree(job); + + core->sequence++; + + /* Kickoff processing of next frame (if any). */ + risp_core_job_run(core); +} + +static void risp_core_vspx_frame_end(void *data) +{ + struct rcar_isp_core *core = data; + + guard(spinlock_irqsave)(&core->lock); + + /* + * In tear-down the ISP may report a frame end event but we have already + * freed the job. It is safe to ignore the end of frame event. + */ + if (!core->vspx.job) + return; + + core->vspx.job->done_vspx = true; + risp_core_try_next_job(core); +} + +static int risp_core_power_on(struct rcar_isp_core *core) +{ + int ret; + + ret = pm_runtime_resume_and_get(core->dev); + if (ret < 0) + return ret; + + ret = reset_control_deassert(core->csrstc); + if (ret) + goto err_pm; + + ret = clk_prepare_enable(core->clk); + if (ret) + goto err_csrstc; + + return 0; + +err_csrstc: + reset_control_assert(core->csrstc); +err_pm: + pm_runtime_put(core->dev); + + return ret; +} + +static void risp_core_power_off(struct rcar_isp_core *core) +{ + clk_disable_unprepare(core->clk); + + reset_control_assert(core->csrstc); + + pm_runtime_put(core->dev); +} + +int risp_core_start_streaming(struct rcar_isp_core *core) +{ + struct vsp1_vspx_frame_end vspx_fe = { + .vspx_frame_end = risp_core_vspx_frame_end, + .frame_end_data = core, + }; + + struct v4l2_mbus_framefmt inputfmt = { + .width = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.width, + .height = core->io[RISP_CORE_INPUT1].format.fmt.pix_mp.height, + .code = risp_core_pix2bus(&core->io[RISP_CORE_INPUT1]), + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_RAW, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_XFER_FUNC_NONE, + }; + + struct v4l2_mbus_framefmt hvout = { + .width = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.width, + .height = core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.height, + .code = risp_core_pix2bus(&core->io[RISP_CORE_OUTPUT1]), + .field = V4L2_FIELD_NONE, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_YCBCR_ENC_601, + .quantization = + core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp.pixelformat == + V4L2_PIX_FMT_XBGR32 ? + V4L2_QUANTIZATION_FULL_RANGE : + V4L2_QUANTIZATION_LIM_RANGE, + .xfer_func = V4L2_XFER_FUNC_SRGB, + }; + int ret; + + scoped_guard(mutex, &core->io_lock) { + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + if (!core->io[i].streaming) + return 0; + } + + /* + * The state core->streaming is protected by core->lock, which + * is not held yet. It is however safe to read it here since + * core->io_lock is held both in risp_core_stop_streaming() and + * here, the only two places the variable is modified. + * + * With this small implied dependency on the two locks for write + * access, the interrupt handler can safely depend sole on the + * spinlock core->lock for read access to core->streaming. + * + * The gain is an interrupt handler which can hold the spinlock + * and a start/stop procedure which can reset the ISP using the + * reset_control_reset() API, The later which can not be called + * from a context that may sleep. + * + * All other locations core->streaming is read and _all_ + * locations where it is written core->lock is held. + */ + if (core->streaming) + return 0; + + ret = risp_core_power_on(core); + if (ret) + return ret; + + /* Reset and wait for ISP core to initialize itself. */ + reset_control_reset(core->rstc); + usleep_range(2000, 4000); + + scoped_guard(spinlock_irqsave, &core->lock) { + risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 1); + + /* Configure output DMA */ + risp_core_config_output(core, 0, + &core->io[RISP_CORE_OUTPUT1].format.fmt.pix_mp); + + risp_cs_write(core, ISP_CS_STREAMER_VBLANK_REG, inputfmt.width * 25); + risp_cs_write(core, ISP_CS_STREAMER_HBLANK_REG, 64); + + /* Enable ISP Streaming bridge. */ + risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, + ISP_CS_STREAMER_MODE_STREAMER_EN); + + /* Start RPP ISP */ + ret = rppx1_start(core->rpp, &inputfmt, &hvout, NULL); + if (ret) { + risp_core_power_off(core); + return ret; + } + + core->vspx.job = NULL; + core->sequence = 0; + core->streaming = true; + } + + /* Start VSPX */ + vsp1_isp_start_streaming(core->vspx.dev, &vspx_fe); + + scoped_guard(spinlock_irqsave, &core->lock) { + risp_core_job_run(core); + } + } + + return 0; +} + +void risp_core_stop_streaming(struct rcar_isp_core *core) +{ + struct rcar_isp_job *job, *tmp; + + /* + * This function releases buffers and jobs: make sure the queues mutex + * is held. + */ + lockdep_assert_held(&core->io_lock); + + scoped_guard(spinlock_irqsave, &core->lock) { + /* Stop is called by each vdev, only act on the first call. */ + if (!core->streaming) + return; + + /* Stop queueing jobs to VSPX. */ + core->streaming = false; + } + + /* Wait for active VSPX job to finish. */ + for (unsigned int retry = 0; retry <= 10; retry++) { + if (!core->vspx.job) + break; + + usleep_range(2000, 4000); + } + + if (core->vspx.job) + dev_err(core->dev, "Failed to complete running job"); + + /* Free all buffers and switch off the hardware. */ + scoped_guard(spinlock_irqsave, &core->lock) { + /* Free all jobs and buffers. */ + list_for_each_entry_safe(job, tmp, &core->risp_jobs, job_queue) { + vsp1_isp_job_release(core->vspx.dev, &job->vspx_job); + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + struct risp_buffer *buf = job->buffers[i]; + + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + list_del(&job->job_queue); + kfree(job); + } + + rppx1_stop(core->rpp); + risp_cs_write(core, ISP_CS_STREAMER_MODE_REG, 0); + risp_core_write(core, ISP_CORE_ISPCORE_INT_ENABLE, 0); + } + + vsp1_isp_stop_streaming(core->vspx.dev); + + risp_core_power_off(core); +} + +static irqreturn_t risp_core_irq(int irq, void *data) +{ + struct rcar_isp_core *core = data; + struct rcar_isp_job *job; + u32 status; + + status = risp_core_read(core, ISP_CORE_ISPCORE_INT_STATUS); + if (!(status & BIT(0))) + return IRQ_NONE; + + if (!rppx1_interrupt(core->rpp, &status)) + return IRQ_HANDLED; + + guard(spinlock_irqsave)(&core->lock); + + job = core->vspx.job; + if (!job) + return IRQ_HANDLED; + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + struct risp_buffer *buf; + + buf = job->buffers[i]; + + switch (i) { + case RISP_CORE_STATS: + rppx1_stats_fill_isr(core->rpp, status, + vb2_plane_vaddr(&buf->vb.vb2_buf, 0)); + fallthrough; + case RISP_CORE_OUTPUT1: + case RISP_CORE_INPUT1: + buf->vb.sequence = core->sequence; + buf->vb.vb2_buf.timestamp = ktime_get_ns(); + fallthrough; + case RISP_CORE_PARAMS: + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + break; + } + } + + core->vspx.job->done_isp = true; + risp_core_try_next_job(core); + + return IRQ_HANDLED; +} + +static const struct v4l2_subdev_ops risp_core_subdev_ops = { +}; + +static int risp_core_create_subdev(struct rcar_isp_core *core) +{ + struct v4l2_subdev *subdev = &core->subdev; + int ret; + + subdev->owner = THIS_MODULE; + subdev->dev = core->dev; + v4l2_subdev_init(subdev, &risp_core_subdev_ops); + v4l2_set_subdevdata(subdev, core->dev); + snprintf(subdev->name, sizeof(subdev->name), "%s %s core", + KBUILD_MODNAME, dev_name(core->dev)); + subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + + subdev->entity.function = MEDIA_ENT_F_VID_MUX; + + core->pads[RISP_CORE_INPUT1].flags = MEDIA_PAD_FL_SINK; + core->pads[RISP_CORE_PARAMS].flags = MEDIA_PAD_FL_SINK; + core->pads[RISP_CORE_STATS].flags = MEDIA_PAD_FL_SOURCE; + core->pads[RISP_CORE_OUTPUT1].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&subdev->entity, RISP_CORE_NUM_PADS, + core->pads); + if (ret) + return ret; + + return 0; +} + +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd) +{ + int ret; + + core->v4l2_dev.mdev = sd->v4l2_dev->mdev; + + /* Register ISP Core subdevice. */ + ret = v4l2_device_register_subdev(&core->v4l2_dev, &core->subdev); + if (ret) + return ret; + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) { + ret = risp_core_io_create(core->dev, core, &core->io[i], i); + if (ret) { + /* It is safe to destroy io node that is not created. */ + for (unsigned int n = 0; n < RISP_CORE_NUM_PADS; n++) + risp_core_io_destroy(&core->io[n]); + + v4l2_device_unregister_subdev(&core->subdev); + + return ret; + } + } + + return 0; +} + +static int risp_core_probe_resources(struct rcar_isp_core *core, + struct platform_device *pdev) +{ + struct platform_device *vspx; + struct device_node *of_vspx; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); + if (!res) + return -ENODEV; + + core->rppaddr = res->start; + core->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + ret = platform_get_irq_byname(pdev, "core"); + if (ret < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, ret, risp_core_irq, IRQF_SHARED, + KBUILD_MODNAME, core); + if (ret) + return ret; + + core->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(core->clk)) + return -ENODEV; + + core->rstc = devm_reset_control_get(&pdev->dev, "core"); + if (IS_ERR(core->rstc)) + return -ENODEV; + + of_vspx = of_parse_phandle(pdev->dev.of_node, "renesas,vspx", 0); + if (!of_vspx) + return -ENODEV; + + vspx = of_find_device_by_node(of_vspx); + if (!vspx) + return -ENODEV; + + /* Attach to VSP-X */ + core->vspx.dev = &vspx->dev; + + ret = vsp1_isp_init(&vspx->dev); + if (ret < 0) + return ret; + + /* Attach to the RPP library + * + * 1. Start and wait for the ISP to startup. + * 2. Attach the RPP library and talk with the RPP ISP. + * 3. Turn off ISP. + * 4. Fail if the RPP is unhappy with the hardware. + */ + ret = clk_prepare_enable(core->clk); + if (ret) + return ret; + + usleep_range(2000, 4000); + + core->rpp = rppx1_create(core->base, &pdev->dev); + + clk_disable_unprepare(core->clk); + + if (!core->rpp) + return -ENODEV; + + return 0; +} + +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev, + void __iomem *csbase, struct reset_control *csrstc) +{ + int ret; + + core->dev = &pdev->dev; + core->csrstc = csrstc; + core->csbase = csbase; + + ret = risp_core_probe_resources(core, pdev); + if (ret) { + core->base = NULL; + return ret; + } + + ret = v4l2_device_register(core->dev, &core->v4l2_dev); + if (ret) + return ret; + + ret = risp_core_create_subdev(core); + if (ret) + return ret; + + mutex_init(&core->io_lock); + spin_lock_init(&core->lock); + INIT_LIST_HEAD(&core->risp_jobs); + + return 0; +} + +void risp_core_remove(struct rcar_isp_core *core) +{ + /* If we did not probe the ISP core, nothing to do. */ + if (!core->base) + return; + + dev_info(core->dev, "Remove ISP Core\n"); + + for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) + risp_core_io_destroy(&core->io[i]); + + v4l2_device_unregister_subdev(&core->subdev); + + mutex_destroy(&core->io_lock); + rppx1_destroy(core->rpp); +} diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c index 8fb2cc3b5650..53ce47020d17 100644 --- a/drivers/media/platform/renesas/rcar-isp/csisp.c +++ b/drivers/media/platform/renesas/rcar-isp/csisp.c @@ -11,14 +11,13 @@ */ #include -#include #include -#include #include -#include #include -#include +#include + +#include "risp-core.h" #define ISPINPUTSEL0_REG 0x0008 #define ISPINPUTSEL0_SEL_CSI0 BIT(31) @@ -158,6 +157,7 @@ struct rcar_isp { struct device *dev; void __iomem *csbase; struct reset_control *rstc; + struct rcar_isp_core core; enum rcar_isp_input csi_input; @@ -451,6 +451,21 @@ static int risp_parse_dt(struct rcar_isp *isp) return ret; } +/* ----------------------------------------------------------------------------- + * ISP Core connection + */ + +static int risp_cs_registered(struct v4l2_subdev *sd) +{ + struct rcar_isp *isp = sd_to_isp(sd); + + return risp_core_registered(&isp->core, sd); +} + +static const struct v4l2_subdev_internal_ops risp_cs_internal_ops = { + .registered = risp_cs_registered, +}; + /* ----------------------------------------------------------------------------- * Platform Device Driver */ @@ -477,7 +492,7 @@ static int risp_probe_resources(struct rcar_isp *isp, if (IS_ERR(isp->csbase)) return PTR_ERR(isp->csbase); - isp->rstc = devm_reset_control_get(&pdev->dev, NULL); + isp->rstc = devm_reset_control_get_shared(&pdev->dev, NULL); return PTR_ERR_OR_ZERO(isp->rstc); } @@ -541,14 +556,31 @@ static int risp_probe(struct platform_device *pdev) if (ret) goto error_notifier; + ret = risp_core_probe(&isp->core, pdev, isp->csbase, isp->rstc); + switch (ret) { + case 0: + /* The device have an ISP core. */ + isp->subdev.internal_ops = &risp_cs_internal_ops; + break; + case -ENODEV: + /* The device don't have an ISP core, that is OK. */ + ret = 0; + break; + default: + /* Something went wrong registering the ISP core. */ + goto error_subdev; + } + ret = v4l2_async_register_subdev(&isp->subdev); if (ret < 0) - goto error_subdev; + goto error_core; dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input); return 0; +error_core: + risp_core_remove(&isp->core); error_subdev: v4l2_subdev_cleanup(&isp->subdev); error_notifier: @@ -564,6 +596,8 @@ static void risp_remove(struct platform_device *pdev) { struct rcar_isp *isp = platform_get_drvdata(pdev); + risp_core_remove(&isp->core); + v4l2_async_nf_unregister(&isp->notifier); v4l2_async_nf_cleanup(&isp->notifier); diff --git a/drivers/media/platform/renesas/rcar-isp/risp-core.h b/drivers/media/platform/renesas/rcar-isp/risp-core.h new file mode 100644 index 000000000000..627a762d6b19 --- /dev/null +++ b/drivers/media/platform/renesas/rcar-isp/risp-core.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2026 Renesas Electronics Corp. + * Copyright (C) 2026 Ideas on Board Oy + * Copyright (C) 2026 Ragnatech AB + */ + +#ifndef __RCAR_ISP__ +#define __RCAR_ISP__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* Max 2048 address + value pairs in one VSPX buffer, increase if needed. */ +#define RISP_IO_PARAMS_BUF_SIZE 16384 + +struct rcar_isp_core; + +enum risp_core_pads { + RISP_CORE_INPUT1, + RISP_CORE_PARAMS, + RISP_CORE_STATS, + RISP_CORE_OUTPUT1, + RISP_CORE_NUM_PADS, +}; + +/** + * struct risp_buffer - Describe an IO buffer + * @vb: The VB2 buffer + * @list: List of buffers queued to the IO queue + * @vsp_buffer: Buffer mapped from VSP-X, only used for params IO + */ +struct risp_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; + struct vsp1_isp_buffer_desc vsp_buffer; +}; + +/** + * struct rcar_isp_core_io - Information for a IO video devices + * @core: Backlink to the common ISP core structure + * + * @lock: Protects @vdev, @pad and @queue + open/close fops + * @vdev: V4L2 video device associated with this IO port + * @pad: Media pad for @vdev + * @queue: VB2 buffers queue for $@vdev + * + * @streaming: Flag to indicate if device is streaming, or not + * @buffers: List of buffers queued to the device + * + * @format: The active V4L2 format + */ +struct rcar_isp_core_io { + struct rcar_isp_core *core; + + struct mutex lock; /* See KDoc block. */ + struct video_device vdev; + struct media_pad pad; + struct vb2_queue queue; + + bool streaming; + struct list_head buffers; + + struct v4l2_format format; +}; + +/** + * struct rcar_isp_job - R-Car ISP job description + * + * Both done_isp and done_vspx shall be set before the job can be considered + * completely done. + * + * @buffers: IO buffers that form a job + * @vspx_job: VSPX job description + * @job_queue: list handle + * @done_isp: Flag to indicate the ISP is done with the job + * @done_vspx: Flag to indicate the VSPX is done with the job + */ +struct rcar_isp_job { + struct risp_buffer *buffers[RISP_CORE_NUM_PADS]; + struct vsp1_isp_job_desc vspx_job; + struct list_head job_queue; + bool done_isp; + bool done_vspx; +}; + +/** + * struct rcar_isp_vspx - R-Car ISP job description + * + * @dev: Device reference to VSPX + * @job: Job currently being processed by VSPX + */ +struct rcar_isp_vspx { + struct device *dev; + struct rcar_isp_job *job; +}; + +/** + * struct rcar_isp_core - ISP Core + * @dev: (OF) device + * @rppaddr: Hardware address of the RPP ISP (from OF) + * @clk: The clock for the ISP CORE + * @rstc: The reset for the ISP Core + * @csrstc: The reset for the ISP Channel Selector + * + * @base: MMIO base of the ISP CORE + * @csbase: MMIO base of the ISP CS + * + * @subdev: V4L2 subdevice to represent the ISP CORE + * @pads: Media pad for @subdev + * + * @v4l2_dev: V4L2 device + * @rpp: Handle to the RPP ISP connected to the ISP CORE + * + * @io_lock: Protect io[*].streaming and io[*].buffers + * @io: Array of IO ports to the ISP CORE + * + * @lock: Protects @vspx, @risp_jobs, @sequence and @streaming + * @vspx: Handle to the resources used by VSPX connected to the ISP CORE + * @risp_jobs: Queue of VSPX transfer jobs + * @sequence: V4L2 buffers sequence number + * @streaming: Tracks if the device is streaming + */ +struct rcar_isp_core { + struct device *dev; + + u32 rppaddr; + struct clk *clk; + + struct reset_control *rstc; + struct reset_control *csrstc; + + void __iomem *base; + void __iomem *csbase; + + struct v4l2_subdev subdev; + struct media_pad pads[RISP_CORE_NUM_PADS]; + + struct v4l2_device v4l2_dev; + struct rppx1 *rpp; + + struct mutex io_lock; /* See KDoc block. */ + struct rcar_isp_core_io io[RISP_CORE_NUM_PADS]; + + spinlock_t lock; /* See KDoc block. */ + struct rcar_isp_vspx vspx; + struct list_head risp_jobs; + unsigned int sequence; + bool streaming; +}; + +int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev, + void __iomem *csbase, struct reset_control *csrstc); +void risp_core_remove(struct rcar_isp_core *core); +int risp_core_registered(struct rcar_isp_core *core, struct v4l2_subdev *sd); + +int risp_core_job_prepare(struct rcar_isp_core *core); + +int risp_core_start_streaming(struct rcar_isp_core *core); +void risp_core_stop_streaming(struct rcar_isp_core *core); + +int risp_core_io_create(struct device *dev, struct rcar_isp_core *core, + struct rcar_isp_core_io *io, unsigned int pad); +void risp_core_io_destroy(struct rcar_isp_core_io *io); + +#endif -- cgit From 884d6794a816473070fa66a6b16b631861d15566 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:33 +0200 Subject: media: rppx1: wbmeas: Add support for white balance measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the white balance measurement configuration and consuming the resulting statistics. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 2 + .../media/platform/dreamchip/rppx1/rpp_params.c | 4 + drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 18 ++++ .../media/platform/dreamchip/rppx1/rppx1_wbmeas.c | 103 ++++++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 107 ++++++++++++++++++++- 5 files changed, 232 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index e92e77542980..64a1b848f1ea 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -47,10 +47,12 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; + struct rppx1_wbmeas_params wbmeas; }; union rppx1_stats_block { struct v4l2_isp_block_header header; + struct rppx1_wbmeas_stats wbmeas; }; struct rpp_module_ops { diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index af45ad5750b3..7325abeba92a 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -17,6 +17,7 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { + RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas), }; int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, @@ -50,6 +51,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, block_offset += block->header.size; switch (block->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: + module = &rpp->post.wbmeas; + break; default: dev_warn(rpp->dev, "Not handled RPPX1 block type: 0x%04x\n", diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c index f7eb60deae10..c99ea158abcf 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c @@ -15,6 +15,11 @@ .size = sizeof(struct rppx1_ ## block ## _stats), \ } +static const struct v4l2_isp_stats_block_type_info +rppx1_stats_blocks_info[] = { + RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas), +}; + #define rppx1_init_stats_block(rpp, buf, type) \ ((union rppx1_stats_block *) \ v4l2_isp_stats_init_block((rpp)->dev, (buf), \ @@ -24,5 +29,18 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf) { + struct v4l2_isp_buffer *stats = buf; + union rppx1_stats_block *block; + + v4l2_isp_stats_init_buffer(stats, V4L2_ISP_VERSION_V1); + + if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) { + block = rppx1_init_stats_block(rpp, stats, + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST); + if (IS_ERR(block)) + return; + + rpp_module_call(&rpp->post.wbmeas, fill_stats, block); + } } EXPORT_SYMBOL_GPL(rppx1_stats_fill_isr); diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c index 60b9c076ec42..f0a943556238 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_wbmeas.c @@ -57,6 +57,109 @@ static int rppx1_wbmeas_probe(struct rpp_module *mod) return 0; } +static int +rppx1_wbmeas_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_wbmeas_params *cfg = &block->wbmeas; + u32 awb_meas_props; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + AWB_MEAS_PROP_REG, 0); + return 0; + } + + /* Program measurement window. */ + write(priv, mod->base + AWB_MEAS_H_OFFS_REG, cfg->wnd.h_offs); + write(priv, mod->base + AWB_MEAS_V_OFFS_REG, cfg->wnd.v_offs); + write(priv, mod->base + AWB_MEAS_H_SIZE_REG, cfg->wnd.h_size); + write(priv, mod->base + AWB_MEAS_V_SIZE_REG, cfg->wnd.v_size); + + /* Set number of frames to sample. */ + write(priv, mod->base + AWB_MEAS_FRAMES_REG, cfg->frames); + + if (cfg->mode == RPPX1_WBMEAS_MODE_YCBCR) { + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG, + cfg->ref_cb_max_b); + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG, + cfg->ref_cr_max_r); + write(priv, mod->base + AWB_MEAS_MAX_Y_REG, cfg->max_y); + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG, + cfg->min_y_max_g); + write(priv, mod->base + AWB_MEAS_MAX_CSUM_REG, cfg->max_csum); + write(priv, mod->base + AWB_MEAS_MIN_C_REG, cfg->min_c); + + /* + * Program the color conversion matrix coefficients and the + * per-color channel offsets. + */ + for (unsigned int i = 0; i < 3; i++) { + for (unsigned int j = 0; j < 3; j++) { + unsigned int index = i * 3 + j; + + write(priv, + mod->base + AWB_MEAS_CCOR_COEFF_REG(index), + cfg->ccor_coeff[i][j]); + } + } + + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, + cfg->ccor_offs[0]); + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, + cfg->ccor_offs[1]); + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, + cfg->ccor_offs[2]); + + awb_meas_props = cfg->ymax_cmp ? AWB_MEAS_PROP_YMAX : 0; + } else { + write(priv, mod->base + AWB_MEAS_REF_CB_MAX_B_REG, + cfg->ref_cb_max_b); + write(priv, mod->base + AWB_MEAS_REF_CR_MAX_R_REG, + cfg->ref_cr_max_r); + write(priv, mod->base + AWB_MEAS_MIN_Y_MAX_G_REG, + cfg->min_y_max_g); + + /* Bypass color conversion matrix and color offsets. */ + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(0), 0x1000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(1), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(2), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(3), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(4), 0x1000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(5), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(6), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(7), 0x0000); + write(priv, mod->base + AWB_MEAS_CCOR_COEFF_REG(8), 0x1000); + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_R_REG, 0x00000000); + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_G_REG, 0x00000000); + write(priv, mod->base + AWB_MEAS_CCOR_OFFSET_B_REG, 0x00000000); + + awb_meas_props = AWB_MEAS_PROP_MEAS_MODE_RGB; + } + + write(priv, mod->base + AWB_MEAS_PROP_REG, + awb_meas_props | AWB_MEAS_PROP_AWB_MODE_ON); + + return 0; +} + +static int rppx1_wbmeas_fill_stats(struct rpp_module *mod, + union rppx1_stats_block *block) +{ + struct rppx1_wbmeas_stats *stats = &block->wbmeas; + + /* Return measurements at native hardware precision. */ + stats->cnt = rpp_module_read(mod, AWB_MEAS_WHITE_CNT_REG); + stats->mean_y_or_g = rpp_module_read(mod, AWB_MEAS_MEAN_Y_G_REG); + stats->mean_cb_or_b = rpp_module_read(mod, AWB_MEAS_MEAN_CB_B_REG); + stats->mean_cr_or_r = rpp_module_read(mod, AWB_MEAS_MEAN_CR_R_REG); + + return 0; +} + const struct rpp_module_ops rppx1_wbmeas_ops = { .probe = rppx1_wbmeas_probe, + .fill_params = rppx1_wbmeas_fill_params, + .fill_stats = rppx1_wbmeas_fill_stats }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index 1f2548db73ad..f674b041664e 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -41,13 +41,87 @@ struct rppx1_window { * the larger bitdepth/precision. */ +/** + * enum rppx1_params_block_type - RPP-X1 extensible params block types + * + * NOTE: Only append to the enumeration as the numbers are uAPI. + * + * @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration + */ +enum rppx1_params_block_type { + RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, +}; + +/** + * enum rppx1_wbmeas_mode - AWB measurement mode + * + * @RPPX1_WBMEAS_MODE_YCBCR: YCbCr measurement mode + * @RPPX1_WBMEAS_MODE_RGB: RGB measurement mode + */ +enum rppx1_wbmeas_mode { + RPPX1_WBMEAS_MODE_YCBCR, + RPPX1_WBMEAS_MODE_RGB, +}; + +/** + * struct rppx1_wbmeas_params - AWB measurement configuration + * + * The Auto-White Balance measurement module is available on the MAIN_POST pipe. + * It supports two measurement modes, selected by the @mode field. The + * measurement window is programmed through the @wnd field. + * + * To support measurement in YCbCr mode a color conversion matrix with + * programmable offset is available in the @ccor_coeff and @ccor_offs fields. + * The color conversion matrix coefficients are represented as 16 bits signed + * Q4.12 numbers ranging from -8 to +7.99. The per-color channel offsets are + * represented as 25 bits 2's complement integer numbers ranging from -16777216 + * to +16777215. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST) + * @wnd: measurement window + * @mode: measurement mode (from enum rppx1_wbmeas_mode) + * @ymax_cmp: enable Y_MAX compare using @max_y + * @frames: number of frames for mean value calculation (0 = 1 frame) + * @reserved: padding + * @ref_cr_max_r: reference Cr or max red value in RGB mode, 24 bits + * @ref_cb_max_b: reference Cb or max blue value in RGB mode, 24 bits + * @min_y_max_g: luminance minimum value or max green value in RGB mode, 24 bits + * @max_y: luminance maximum value, only valid if @mode is set to YCbCr and + * @ymax_cmp is set to enabled, 24 bits + * @max_csum: chrominance sum maximum value, 24 bits + * @min_c: chrominance minimum value, 24 bits + * @ccor_coeff: coefficients for color conversion matrix, signed 16 bits Q4.6 + * @reserved2: padding + * @ccor_offs: R-G-B color conversion coefficients, signed 25 bits 2's complement + * @reserved3: padding + */ +struct rppx1_wbmeas_params { + struct v4l2_isp_params_block_header header; + struct rppx1_window wnd; + __u8 mode; + __u8 ymax_cmp; + __u8 frames; + __u8 reserved; + __u32 ref_cr_max_r; + __u32 ref_cb_max_b; + __u32 min_y_max_g; + __u32 max_y; + __u32 max_csum; + __u32 min_c; + __u16 ccor_coeff[3][3]; + __u16 reserved2; + __u32 ccor_offs[3]; + __u32 reserved3; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * * Some types are reported twice as the same block might be instantiated in * multiple pipes. */ -#define RPPX1_PARAMS_MAX_SIZE 0 +#define RPPX1_PARAMS_MAX_SIZE \ + (sizeof(struct rppx1_wbmeas_params)) /* --------------------------------------------------------------------------- * Statistics Structures @@ -58,12 +132,41 @@ struct rppx1_window { * the larger bitdepth/precision. */ +/** + * enum rppx1_stats_block_type - RPP-X1 extensible stats block types + * + * NOTE: Only append to the enumeration as the numbers are uAPI. + * + * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement + */ +enum rppx1_stats_block_type { + RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST, +}; + +/** + * struct rppx1_wbmeas_stats - AWB statistics + * + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST) + * @cnt: Number of pixels matched + * @mean_y_or_g: mean Y (or G in RGB mode) value, 24-bit + * @mean_cb_or_b: mean Cb (or B in RGB mode) value, 24-bit + * @mean_cr_or_r: mean Cr (or R in RGB mode) value, 24-bit + */ +struct rppx1_wbmeas_stats { + struct v4l2_isp_block_header header; + __u32 cnt; + __u32 mean_y_or_g; + __u32 mean_cb_or_b; + __u32 mean_cr_or_r; +}; + /** * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics * * Some types are reported twice as the same block might be instantiated in * multiple pipes. */ -#define RPPX1_STATS_MAX_SIZE 0 +#define RPPX1_STATS_MAX_SIZE \ + (sizeof(struct rppx1_wbmeas_stats)) #endif /* __UAPI_RPP_X1_CONFIG_H */ -- cgit From 9ebf50010c686f77ff403d07548a8ad678184f2a Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:34 +0200 Subject: media: rppx1: awbg: Add support for white balance gain settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the white balance gain configuration parameters. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 +++ .../media/platform/dreamchip/rppx1/rppx1_awbg.c | 32 +++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 40 +++++++++++++++++++++- 4 files changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index 64a1b848f1ea..0e36b717b4ef 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -47,6 +47,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; + struct rppx1_awbg_params awbg; struct rppx1_wbmeas_params wbmeas; }; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 7325abeba92a..17bbcf366970 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -17,6 +17,8 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { + RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), + RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas), }; @@ -51,6 +53,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, block_offset += block->header.size; switch (block->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: + module = &rpp->pre1.awbg; + break; case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: module = &rpp->post.wbmeas; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c index 4a242b41142b..f30e12d6f880 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_awbg.c @@ -26,6 +26,38 @@ static int rppx1_awbg_probe(struct rpp_module *mod) return 0; } +static int +rppx1_awbg_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_awbg_params *cfg = &block->awbg; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + AWB_ENABLE_REG, 0); + return 0; + } + + /* + * RPP gains are 18-bit with 12 bit fractional part and 0x1000 = 1.0, + * giving a possible range of 0.0 to 64.0. NOTE: RPP documentation is + * contradictory this is the register definition, the function + * description states 0x400 = 1.0 AND 18-bit with 12 fractional bits, + * which is not possible... + */ + + write(priv, mod->base + AWB_GAIN_GR_REG, cfg->gain_green_r); + write(priv, mod->base + AWB_GAIN_GB_REG, cfg->gain_green_b); + write(priv, mod->base + AWB_GAIN_R_REG, cfg->gain_red); + write(priv, mod->base + AWB_GAIN_B_REG, cfg->gain_blue); + + write(priv, mod->base + AWB_ENABLE_REG, AWB_ENABLE_AWB_GAIN_EN); + + return 0; +} + const struct rpp_module_ops rppx1_awbg_ops = { .probe = rppx1_awbg_probe, + .fill_params = rppx1_awbg_fill_params, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index f674b041664e..b388a9003d8b 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -47,9 +47,15 @@ struct rppx1_window { * NOTE: Only append to the enumeration as the numbers are uAPI. * * @RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: AWB Measurement Configuration + * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: PRE1 pipe White Balance Gains + * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2: PRE2 White Balance Gains + * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, + RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2, + RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST, }; /** @@ -114,6 +120,35 @@ struct rppx1_wbmeas_params { __u32 reserved3; }; +/** + * struct rppx1_awbg_params - WB gain configuration + * + * The RPP-X1 White Balance Gain module is available in the PRE1 and PRE2 + * pre-fusion pipes and in the MAIN_POST post-fusion pipe. Userspace selects + * which pipe to operate by setting the @header.type field to + * RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1, RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2 + * or RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST. + * + * The White Balance module allows to specify per-color channel gains, expressed + * as unsigned fixed-point values as 18 bits unsigned integers in Q6.12 format + * with a maximum of 63.999. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1 or + * type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2 or + * type = RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST) + * @gain_red: gain for red component, 18-bit (unsigned Q6.12) + * @gain_green_r: gain for green component in red lines, 18-bit (unsigned Q6.12) + * @gain_blue: gain for blue component, 18-bit (unsigned Q6.12) + * @gain_green_b: gain for green component in blue lines, 18-bit (unsigned Q6.12) + */ +struct rppx1_awbg_params { + struct v4l2_isp_params_block_header header; + __u32 gain_red; + __u32 gain_green_r; + __u32 gain_blue; + __u32 gain_green_b; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -121,7 +156,10 @@ struct rppx1_wbmeas_params { * multiple pipes. */ #define RPPX1_PARAMS_MAX_SIZE \ - (sizeof(struct rppx1_wbmeas_params)) + (sizeof(struct rppx1_wbmeas_params) + \ + sizeof(struct rppx1_awbg_params) + \ + sizeof(struct rppx1_awbg_params) + \ + sizeof(struct rppx1_awbg_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From b020bec416932aa923739df7d2f2b37b85cfd318 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:35 +0200 Subject: media: rppx1: exm: Add support for exposure measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the exposure measurement configuration and consuming the resulting statistics. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 2 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 + drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 10 ++ drivers/media/platform/dreamchip/rppx1/rppx1_exm.c | 76 +++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 125 ++++++++++++++++++++- 5 files changed, 216 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index 0e36b717b4ef..e968ec67b9f5 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -48,11 +48,13 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; struct rppx1_awbg_params awbg; + struct rppx1_exm_params exm; struct rppx1_wbmeas_params wbmeas; }; union rppx1_stats_block { struct v4l2_isp_block_header header; + struct rppx1_exm_stats exm; struct rppx1_wbmeas_stats wbmeas; }; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 17bbcf366970..8c0f45e8066a 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -19,6 +19,8 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), + RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm), + RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm), RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas), }; @@ -56,6 +58,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: module = &rpp->pre1.awbg; break; + case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: + module = &rpp->pre1.exm; + break; case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: module = &rpp->post.wbmeas; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c index c99ea158abcf..388c758d805d 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c @@ -17,6 +17,7 @@ static const struct v4l2_isp_stats_block_type_info rppx1_stats_blocks_info[] = { + RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm), RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas), }; @@ -34,6 +35,15 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf) v4l2_isp_stats_init_buffer(stats, V4L2_ISP_VERSION_V1); + if (isc & RPPX1_IRQ_ID_PRE1_EXM) { + block = rppx1_init_stats_block(rpp, stats, + RPPX1_STATS_BLOCK_TYPE_EXM_PRE1); + if (IS_ERR(block)) + return; + + rpp_module_call(&rpp->pre1.exm, fill_stats, block); + } + if (isc & RPPX1_IRQ_ID_POST_AWB_MEAS) { block = rppx1_init_stats_block(rpp, stats, RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST); diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c index 1dccb110d3e9..c7b24c356222 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_exm.c @@ -11,10 +11,14 @@ #define EXM_START_REG 0x0004 #define EXM_CTRL_REG 0x0008 +#define EXM_CTRL_EXM_AUTOSTOP BIT(1) /* HW doc says not supported. */ #define EXM_CTRL_EXM_UPDATE_ENABLE BIT(0) #define EXM_MODE_REG 0x000c + #define EXM_CHANNEL_SEL_REG 0x0010 +#define EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK GENMASK(2, 0) + #define EXM_LAST_MEAS_LINE_REG 0x0014 #define EXM_COEFF_R_REG 0x0018 #define EXM_COEFF_G_GR_REG 0x001c @@ -46,6 +50,78 @@ static int rppx1_exm_probe(struct rpp_module *mod) return 0; } +static int +rppx1_exm_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_exm_params *cfg = &block->exm; + u32 h_offs, v_offs, h_size, v_size; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + EXM_MODE_REG, 0); + return 0; + } + + switch (cfg->mode) { + case RPPX1_EXP_MEASURING_MODE_RGB: + case RPPX1_EXP_MEASURING_MODE_BAYER: + write(priv, mod->base + EXM_MODE_REG, cfg->mode); + break; + default: + write(priv, mod->base + EXM_MODE_REG, 0); + return 0; + } + + write(priv, mod->base + EXM_COEFF_R_REG, cfg->coeff_r); + write(priv, mod->base + EXM_COEFF_G_GR_REG, cfg->coeff_g_gr); + write(priv, mod->base + EXM_COEFF_GB_REG, cfg->coeff_gb); + write(priv, mod->base + EXM_COEFF_B_REG, cfg->coeff_b); + + /* Select sample point */ + write(priv, mod->base + EXM_CHANNEL_SEL_REG, + cfg->channel_sel & EXM_CHANNEL_SEL_CHANNEL_SELECT_MASK); + + /* + * Adjust and set measurement window, + * - Offsets must be even. + * - Width and height must be even and divisible in 5 windows. + */ + h_offs = cfg->wnd.h_offs & 0x1ffe; + v_offs = cfg->wnd.v_offs & 0x1ffe; + h_size = (cfg->wnd.h_size - 1) - ((cfg->wnd.h_size - 1) % 10); + v_size = (cfg->wnd.v_size - 1) - ((cfg->wnd.v_size - 1) % 10); + + write(priv, mod->base + EXM_H_OFFS_REG, h_offs); + write(priv, mod->base + EXM_V_OFFS_REG, v_offs); + write(priv, mod->base + EXM_H_SIZE_REG, h_size / 5); + write(priv, mod->base + EXM_V_SIZE_REG, v_size / 5); + + /* + * Set last measurement line for ready interrupt. Ignore the value + * from the parameters as it is only useful for fast-channel switching. + */ + write(priv, mod->base + EXM_LAST_MEAS_LINE_REG, v_offs + v_size + 1); + + write(priv, mod->base + EXM_START_REG, 1); + + return 0; +} + +static int rppx1_exm_fill_stats(struct rpp_module *mod, + union rppx1_stats_block *block) +{ + struct rppx1_exm_stats *stats = &block->exm; + + for (unsigned int i = 0; i < RPPX1_EXM_NUM_WIN; i++) + stats->exp_mean[i] = rpp_module_read(mod, EXM_MEAN_REG(i)); + + return 0; +} + const struct rpp_module_ops rppx1_exm_ops = { .probe = rppx1_exm_probe, + .fill_params = rppx1_exm_fill_params, + .fill_stats = rppx1_exm_fill_stats, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index b388a9003d8b..6864df2d4436 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -32,6 +32,36 @@ struct rppx1_window { __u16 v_size; }; +/** + * enum rppx1_meas_chan - Measurement point for the Histogram and EXM Modules + * + * Measurement points for the RPP-X1 Histogram measurement module and Exposure + * measurement module. + * + * All tap points are available for the PRE1/PRE2 pipes. Only + * RPPX1_MEAS_CHAN_SEL4 and RPPX1_MEAS_CHAN_SEL7 are available for the + * MAIN_POST pipe. + * + * @RPPX1_MEAS_CHAN_SEL0: after input acquisition + * @RPPX1_MEAS_CHAN_SEL1: after black level subtraction + * @RPPX1_MEAS_CHAN_SEL2: after sensor gamma linearization + * @RPPX1_MEAS_CHAN_SEL3: after lens shading correction + * @RPPX1_MEAS_CHAN_SEL4: after auto white balance gains + * @RPPX1_MEAS_CHAN_SEL5: after defect pixel correction + * @RPPX1_MEAS_CHAN_SEL6: after denoise pre-filter + * @RPPX1_MEAS_CHAN_SEL7: after demosaicing + */ +enum rppx1_meas_chan { + RPPX1_MEAS_CHAN_SEL0, + RPPX1_MEAS_CHAN_SEL1, + RPPX1_MEAS_CHAN_SEL2, + RPPX1_MEAS_CHAN_SEL3, + RPPX1_MEAS_CHAN_SEL4, + RPPX1_MEAS_CHAN_SEL5, + RPPX1_MEAS_CHAN_SEL6, + RPPX1_MEAS_CHAN_SEL7, +}; + /* --------------------------------------------------------------------------- * Parameter Structures * @@ -50,12 +80,16 @@ struct rppx1_window { * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: PRE1 pipe White Balance Gains * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2: PRE2 White Balance Gains * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains + * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: PRE1 pipe Exposure Measurement + * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2: PRE2 pipe Exposure Measurement */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1, RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE2, RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST, + RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2, }; /** @@ -149,6 +183,66 @@ struct rppx1_awbg_params { __u32 gain_green_b; }; +/** + * enum rppx1_exm_mode - Exposure measurement mode + * + * Exaposure measurement mode selection (RGB/Bayer). + * + * @RPPX1_EXP_MEASURING_MODE_DISABLED: no measurement + * @RPPX1_EXP_MEASURING_MODE_RGB: Y/R/G/B measurement + * @RPPX1_EXP_MEASURING_MODE_BAYER: Bayer RGB measurement + */ +enum rppx1_exm_mode { + RPPX1_EXP_MEASURING_MODE_DISABLED, + RPPX1_EXP_MEASURING_MODE_RGB, + RPPX1_EXP_MEASURING_MODE_BAYER, +}; + +/** + * struct rppx1_exm_params - Exposure measurement configuration + * + * The RPP-X1 Exposure measurement unit is available on the PRE1 and PRE2 + * pre-fusion pipes. Userspace selects which pipe to operate by setting + * the @header.type field to RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1 or + * RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2. + * + * Exposure measurement is performed in the RGB or Bayer domain, according to + * the setting of the @mode field. The exposure measurement tap point is + * selected according to the value of @channel_sel. + * + * The exposure measurement is performed on an input window specified in @wnd. + * To each color component a programmable weight coefficient is associated. + * Coefficients are represented as unsigned 8 bits integer values in Q1.7 format + * ranging from 0 to 1.992. + * + * The @last_line fields controls when the exposure measurement completes. It + * is usually programmed to the value of (@wnd.v_offs + @wnd.v_size + 1). + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1 or + * type = RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2) + * @wnd: measurement window coordinates + * @mode: exposure measure mode (from enum rppx1_exm_mode) + * @last_line: line number for which the exposure measurement completes + * @channel_sel: exposure measurement point (see enum rppx1_meas_chan) + * @coeff_r: coefficient for the red Bayer sample or red color channel, Q1.7 + * @coeff_g_gr: coefficient for the green/red Bayer sample or green color channel, Q1.7 + * @coeff_b: coefficient for the blue Bayer sample or blue color channel, Q1.7 + * @coeff_gb: coefficient for the green/blue Bayer sample, unused in RGB mode, Q1.7 + * @reserved: padding + */ +struct rppx1_exm_params { + struct v4l2_isp_params_block_header header; + struct rppx1_window wnd; + __u32 mode; + __u32 last_line; + __u8 channel_sel; + __u8 coeff_r; + __u8 coeff_g_gr; + __u8 coeff_b; + __u8 coeff_gb; + __u8 reserved[3]; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -159,7 +253,9 @@ struct rppx1_awbg_params { (sizeof(struct rppx1_wbmeas_params) + \ sizeof(struct rppx1_awbg_params) + \ sizeof(struct rppx1_awbg_params) + \ - sizeof(struct rppx1_awbg_params)) + sizeof(struct rppx1_awbg_params) + \ + sizeof(struct rppx1_exm_params) + \ + sizeof(struct rppx1_exm_params)) /* --------------------------------------------------------------------------- * Statistics Structures @@ -176,9 +272,13 @@ struct rppx1_awbg_params { * NOTE: Only append to the enumeration as the numbers are uAPI. * * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement + * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE1: pre-fusion pipe1 exposure measurement + * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE2: pre-fusion pipe2 exposure measurement */ enum rppx1_stats_block_type { RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST, + RPPX1_STATS_BLOCK_TYPE_EXM_PRE1, + RPPX1_STATS_BLOCK_TYPE_EXM_PRE2, }; /** @@ -198,6 +298,25 @@ struct rppx1_wbmeas_stats { __u32 mean_cr_or_r; }; +/* Exposure Measurement */ +#define RPPX1_EXM_NUM_WIN 25 + +/** + * struct rppx1_exm_stats - Exposure measurement + * + * RPP-X1 exposure measurement calculates the mean value on 25 programmable + * windows on the input picture. + * + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_EXM_PRE1) + * @exp_mean: mean luminance values per block, up to 20-bit + * @reserved: padding + */ +struct rppx1_exm_stats { + struct v4l2_isp_block_header header; + __u32 exp_mean[RPPX1_EXM_NUM_WIN]; + __u32 reserved; +}; + /** * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics * @@ -205,6 +324,8 @@ struct rppx1_wbmeas_stats { * multiple pipes. */ #define RPPX1_STATS_MAX_SIZE \ - (sizeof(struct rppx1_wbmeas_stats)) + (sizeof(struct rppx1_wbmeas_stats) + \ + sizeof(struct rppx1_exm_stats) + \ + sizeof(struct rppx1_exm_stats)) #endif /* __UAPI_RPP_X1_CONFIG_H */ -- cgit From 9a52f7de7c20fe055b5cbb3c792e76a8562ccb2f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:36 +0200 Subject: media: rppx1: hist: Add support histogram measurement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the histogram measurement configuration and consuming the resulting statistics. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 2 + .../media/platform/dreamchip/rppx1/rpp_params.c | 6 ++ drivers/media/platform/dreamchip/rppx1/rpp_stats.c | 10 ++ .../media/platform/dreamchip/rppx1/rppx1_hist.c | 111 ++++++++++++++++++- include/uapi/linux/media/dreamchip/rppx1-config.h | 117 ++++++++++++++++++++- 5 files changed, 243 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index e968ec67b9f5..b134d140fe22 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -48,12 +48,14 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; struct rppx1_awbg_params awbg; + struct rppx1_hist_params hist; struct rppx1_exm_params exm; struct rppx1_wbmeas_params wbmeas; }; union rppx1_stats_block { struct v4l2_isp_block_header header; + struct rppx1_hist_stats hist; struct rppx1_exm_stats exm; struct rppx1_wbmeas_stats wbmeas; }; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 8c0f45e8066a..975ce3a42fb5 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -19,6 +19,9 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), + RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist), + RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist), + RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist), RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm), RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm), RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas), @@ -58,6 +61,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: module = &rpp->pre1.awbg; break; + case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: + module = &rpp->post.hist; + break; case RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: module = &rpp->pre1.exm; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c index 388c758d805d..4c7fe611d004 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_stats.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_stats.c @@ -17,6 +17,7 @@ static const struct v4l2_isp_stats_block_type_info rppx1_stats_blocks_info[] = { + RPPX1_STATS_BLOCK_INFO(HIST_POST, hist), RPPX1_STATS_BLOCK_INFO(EXM_PRE1, exm), RPPX1_STATS_BLOCK_INFO(WBMEAS_POST, wbmeas), }; @@ -35,6 +36,15 @@ void rppx1_stats_fill_isr(struct rppx1 *rpp, u32 isc, void *buf) v4l2_isp_stats_init_buffer(stats, V4L2_ISP_VERSION_V1); + if (isc & RPPX1_IRQ_ID_POST_HIST_MEAS) { + block = rppx1_init_stats_block(rpp, stats, + RPPX1_STATS_BLOCK_TYPE_HIST_POST); + if (IS_ERR(block)) + return; + + rpp_module_call(&rpp->post.hist, fill_stats, block); + } + if (isc & RPPX1_IRQ_ID_PRE1_EXM) { block = rppx1_init_stats_block(rpp, stats, RPPX1_STATS_BLOCK_TYPE_EXM_PRE1); diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c index 7c1b42e96b96..8fe8cbbc85df 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_hist.c @@ -7,6 +7,8 @@ #include "rpp_module.h" +#include + #define HIST_VERSION_REG 0x0000 #define HIST_CTRL_REG 0x0004 @@ -26,6 +28,9 @@ #define HIST_LAST_MEAS_LINE_REG 0x0010 #define HIST_SUBSAMPLING_REG 0x0014 +#define HIST_SUBSAMPLING_V_STEPSIZE(x) (((x) & 0x7f) << 24) +#define HIST_SUBSAMPLING_H_STEP_INC(x) (((x) & 0x1ffff)) + #define HIST_COEFF_R_REG 0x0018 #define HIST_COEFF_G_REG 0x001c #define HIST_COEFF_B_REG 0x0020 @@ -49,7 +54,6 @@ #define HIST_FORCED_UPDATE_REG 0x0058 #define HIST_VSTART_STATUS_REG 0x005c -#define HIST_BIN_REG_NUM 32 #define HIST_BIN_REG(n) (0x0060 + (4 * (n))) static int rppx1_hist_probe(struct rpp_module *mod) @@ -72,6 +76,111 @@ static int rppx1_hist_probe(struct rpp_module *mod) return 0; } +#define RPPX1_HIST_WEIGHT(v0, v1, v2, v3) \ + (((v0) & 0x1f) | (((v1) & 0x1f) << 8) | \ + (((v2) & 0x1f) << 16) | \ + (((v3) & 0x1f) << 24)) + +static int rppx1_hist_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_hist_params *cfg = &block->hist; + u32 h_offs, v_offs, h_size, v_size; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + HIST_MODE_REG, + HIST_MODE_HIST_MODE_DISABLE); + return 0; + } + + /* Select sample point */ + write(priv, mod->base + HIST_CHANNEL_SEL_REG, + cfg->channel_sel & HIST_CHANNEL_SEL_CHANNEL_SELECT_MASK); + + /* + * Configure the input subsampling. + * + * In Bayer mode the vertical and horizontal subsampling counters are + * only incremented for color channels selected by hist_mode. + */ + write(priv, mod->base + HIST_SUBSAMPLING_REG, + HIST_SUBSAMPLING_V_STEPSIZE(cfg->v_stepsize) | + HIST_SUBSAMPLING_H_STEP_INC(cfg->h_step_inc)); + + /* + * Adjust and set measurement window to hardware limitations, + * - Offsets must be even. + * - Width and height must be even and divisible in 5 windows. + */ + h_offs = cfg->wnd.h_offs & 0x1ffe; + v_offs = cfg->wnd.v_offs & 0x1ffe; + h_size = cfg->wnd.h_size - cfg->wnd.h_size % 10; + v_size = cfg->wnd.v_size - cfg->wnd.v_size % 10; + + write(priv, mod->base + HIST_H_OFFS_REG, h_offs); + write(priv, mod->base + HIST_V_OFFS_REG, v_offs); + write(priv, mod->base + HIST_H_SIZE_REG, h_size / 5); + write(priv, mod->base + HIST_V_SIZE_REG, v_size / 5); + + /* + * Set last measurement line for ready interrupt. Ignore the value + * from the parameters as it is only useful for fast-channel switching. + */ + write(priv, mod->base + HIST_LAST_MEAS_LINE_REG, v_offs + v_size + 1); + + /* Set measurement window weights. */ + write(priv, mod->base + HIST_WEIGHT_00TO30_REG, + RPPX1_HIST_WEIGHT(cfg->weights[0], cfg->weights[1], + cfg->weights[2], cfg->weights[3])); + write(priv, mod->base + HIST_WEIGHT_40TO21_REG, + RPPX1_HIST_WEIGHT(cfg->weights[4], cfg->weights[5], + cfg->weights[6], cfg->weights[7])); + write(priv, mod->base + HIST_WEIGHT_31TO12_REG, + RPPX1_HIST_WEIGHT(cfg->weights[8], cfg->weights[9], + cfg->weights[10], cfg->weights[11])); + write(priv, mod->base + HIST_WEIGHT_22TO03_REG, + RPPX1_HIST_WEIGHT(cfg->weights[12], cfg->weights[13], + cfg->weights[14], cfg->weights[15])); + write(priv, mod->base + HIST_WEIGHT_13TO43_REG, + RPPX1_HIST_WEIGHT(cfg->weights[16], cfg->weights[17], + cfg->weights[18], cfg->weights[19])); + write(priv, mod->base + HIST_WEIGHT_04TO34_REG, + RPPX1_HIST_WEIGHT(cfg->weights[20], cfg->weights[21], + cfg->weights[22], cfg->weights[23])); + write(priv, mod->base + HIST_WEIGHT_44_REG, + RPPX1_HIST_WEIGHT(cfg->weights[24], 0, 0, 0)); + + write(priv, mod->base + HIST_MODE_REG, cfg->mode); + write(priv, mod->base + HIST_COEFF_R_REG, cfg->coeff[0]); + write(priv, mod->base + HIST_COEFF_G_REG, cfg->coeff[1]); + write(priv, mod->base + HIST_COEFF_B_REG, cfg->coeff[2]); + + u32 sample_reg = FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_SHIFT_MASK, + cfg->sample_shift) | + FIELD_PREP(HIST_SAMPLE_RANGE_SAMPLE_OFFSET_MASK, + cfg->sample_offs); + write(priv, mod->base + HIST_SAMPLE_RANGE_REG, sample_reg); + + write(priv, mod->base + HIST_FORCED_UPDATE_REG, 1); + + return 0; +} + +static int rppx1_hist_fill_stats(struct rpp_module *mod, + union rppx1_stats_block *block) +{ + struct rppx1_hist_stats *stats = &block->hist; + + for (unsigned int i = 0; i < RPPX1_HIST_NUM_BINS; i++) + stats->hist_bins[i] = rpp_module_read(mod, HIST_BIN_REG(i)); + + return 0; +} + const struct rpp_module_ops rppx1_hist_ops = { .probe = rppx1_hist_probe, + .fill_params = rppx1_hist_fill_params, + .fill_stats = rppx1_hist_fill_stats, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index 6864df2d4436..934e18731b08 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -82,6 +82,9 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST: MAIN_POST White Balance Gains * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1: PRE1 pipe Exposure Measurement * @RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2: PRE2 pipe Exposure Measurement + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement + * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -90,6 +93,9 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_AWBG_POST, RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE1, RPPX1_PARAMS_BLOCK_TYPE_EXM_PRE2, + RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2, + RPPX1_PARAMS_BLOCK_TYPE_HIST_POST, }; /** @@ -243,6 +249,87 @@ struct rppx1_exm_params { __u8 reserved[3]; }; +/* Histogram */ +#define RPPX1_HIST_WEIGHT_GRIDS_SIZE 25 + +/** + * enum rppx1_hist_mode - Histogram measurement mode + * + * Histogram measurement mode. Select which channel or combination of channels + * the histogram measurement is performed on. + * + * @RPPX1_HIST_MODE_DISABLE: histogram disabled + * @RPPX1_HIST_MODE_RGB_COMBINED: combined RGB histogram + * @RPPX1_HIST_MODE_R_HISTOGRAM: red channel histogram + * @RPPX1_HIST_MODE_GR_HISTOGRAM: green/red channel histogram + * @RPPX1_HIST_MODE_B_HISTOGRAM: blue channel histogram + * @RPPX1_HIST_MODE_GB_HISTOGRAM: green/blue histogram + */ +enum rppx1_hist_mode { + RPPX1_HIST_MODE_DISABLE, + RPPX1_HIST_MODE_RGB_COMBINED, + RPPX1_HIST_MODE_R_HISTOGRAM, + RPPX1_HIST_MODE_GR_HISTOGRAM, + RPPX1_HIST_MODE_B_HISTOGRAM, + RPPX1_HIST_MODE_GB_HISTOGRAM, +}; + +/** + * struct rppx1_hist_params - Histogram measurement configuration + * + * The RPP-X1 Histogram measurement unit is available on the PRE1, PRE2 and + * MAIN_POST pipes. Userspace selects which pipe to operate by setting the + * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1, + * RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or + * RPPX1_PARAMS_BLOCK_TYPE_HIST_POST. + * + * The histogram measurement point is selected using the @channel field while + * histogram measurement mode is selected using the @mode field. + * + * Histogram measurement is performed by programming subsampling factors using + * the @v_stepsize and @h_step_inc fields and by weighted windowing, by + * programming the size of the measurement window @wnd with @weights associated + * to each cell of the 5x5 measurement grid. Weights are represented as 5 bits + * integer values ranging from 0 to 16. + * + * The @last_line fields controls when the histogram measurement completes. It + * is usually programmed to the value of (@wnd.v_offs + @wnd.v_size - 1). + * + * Histogram values are calculated by applying a per-color channel coefficient + * represented as an 8 bits unsigned Q1.7 integer value. The @sample_offs and + * @sample_shift fields allow to reduce the color dynamic range on which + * histogram data are produced. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1, + * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2 or + * type = RPPX1_PARAMS_BLOCK_TYPE_HIST_POST) + * @wnd: measurement window coordinates + * @last_line: line number for which the histogram measurement completes + * @v_stepsize: vertical subsampling divider, 7 bits + * @h_step_inc: horizontal subsampling step counter, 17 bits + * @sample_offs: sample offset, 24 bits + * @mode: histogram measurement mode (from enum rppx1_hist_mode) + * @channel_sel: histogram measurement point (see enum rppx1_meas_chan) + * @weights: weighting factors for each sub-window (5x5 grid) + * @coeff: R-G-B coefficients, 8 bits unsigned Q1.7 + * @sample_shift: sample shift, 4 bits + * @reserved: padding + */ +struct rppx1_hist_params { + struct v4l2_isp_params_block_header header; + struct rppx1_window wnd; + __u32 last_line; + __u32 v_stepsize; + __u32 h_step_inc; + __u32 sample_offs; + __u8 mode; + __u8 channel_sel; + __u8 weights[RPPX1_HIST_WEIGHT_GRIDS_SIZE]; + __u8 coeff[3]; + __u8 sample_shift; + __u8 reserved; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -255,7 +342,10 @@ struct rppx1_exm_params { sizeof(struct rppx1_awbg_params) + \ sizeof(struct rppx1_awbg_params) + \ sizeof(struct rppx1_exm_params) + \ - sizeof(struct rppx1_exm_params)) + sizeof(struct rppx1_exm_params) + \ + sizeof(struct rppx1_hist_params) + \ + sizeof(struct rppx1_hist_params) + \ + sizeof(struct rppx1_hist_params)) /* --------------------------------------------------------------------------- * Statistics Structures @@ -274,11 +364,17 @@ struct rppx1_exm_params { * @RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST: post-fusion white-balance measurement * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE1: pre-fusion pipe1 exposure measurement * @RPPX1_STATS_BLOCK_TYPE_EXM_PRE2: pre-fusion pipe2 exposure measurement + * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE1: pre-fusion pipe1 histogram + * @RPPX1_STATS_BLOCK_TYPE_HIST_PRE2: pre-fusion pipe2 histogram + * @RPPX1_STATS_BLOCK_TYPE_HIST_POST: post-fusion histogram */ enum rppx1_stats_block_type { RPPX1_STATS_BLOCK_TYPE_WBMEAS_POST, RPPX1_STATS_BLOCK_TYPE_EXM_PRE1, RPPX1_STATS_BLOCK_TYPE_EXM_PRE2, + RPPX1_STATS_BLOCK_TYPE_HIST_PRE1, + RPPX1_STATS_BLOCK_TYPE_HIST_PRE2, + RPPX1_STATS_BLOCK_TYPE_HIST_POST, }; /** @@ -317,6 +413,20 @@ struct rppx1_exm_stats { __u32 reserved; }; +/* Histogram */ +#define RPPX1_HIST_NUM_BINS 32 + +/** + * struct rppx1_hist_stats - Histogram statistics + * + * @header: block header (type = RPPX1_STATS_BLOCK_TYPE_HIST_POST) + * @hist_bins: accumulation histogram results in unsigned 20-bit Q16.4 format + */ +struct rppx1_hist_stats { + struct v4l2_isp_block_header header; + __u32 hist_bins[RPPX1_HIST_NUM_BINS]; +}; + /** * RPPX1_STATS_MAX_SIZE - Maximum size of all RPP-X1 statistics * @@ -326,6 +436,9 @@ struct rppx1_exm_stats { #define RPPX1_STATS_MAX_SIZE \ (sizeof(struct rppx1_wbmeas_stats) + \ sizeof(struct rppx1_exm_stats) + \ - sizeof(struct rppx1_exm_stats)) + sizeof(struct rppx1_exm_stats) + \ + sizeof(struct rppx1_hist_stats) + \ + sizeof(struct rppx1_hist_stats) + \ + sizeof(struct rppx1_hist_stats)) #endif /* __UAPI_RPP_X1_CONFIG_H */ -- cgit From 3f8a2a88365501998df6cd35ecd384be823bfa39 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:37 +0200 Subject: media: rppx1: bls: Add support for black level compensation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the black level measurement and gain configuration. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 + drivers/media/platform/dreamchip/rppx1/rppx1_bls.c | 97 +++++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 107 ++++++++++++++++++++- 4 files changed, 209 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index b134d140fe22..5e20fcdcbcc4 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -47,6 +47,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; + struct rppx1_bls_params bls; struct rppx1_awbg_params awbg; struct rppx1_hist_params hist; struct rppx1_exm_params exm; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 975ce3a42fb5..831cf7ca154c 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -17,6 +17,8 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { + RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls), + RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist), @@ -58,6 +60,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, block_offset += block->header.size; switch (block->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: + module = &rpp->pre1.bls; + break; case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: module = &rpp->pre1.awbg; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c index 882a9a819229..01a61db279bf 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_bls.c @@ -6,6 +6,7 @@ */ #include "rpp_module.h" +#include "rppx1.h" #define BLS_VERSION_REG 0x0000 @@ -32,6 +33,8 @@ #define BLS_B_MEASURED_REG 0x0040 #define BLS_C_MEASURED_REG 0x0044 #define BLS_D_MEASURED_REG 0x0048 +#define BLS_PRE1_FIXED_MASK GENMASK(24, 0) +#define BLS_PRE2_FIXED_MASK GENMASK(12, 0) static int rppx1_bls_probe(struct rpp_module *mod) { @@ -55,6 +58,100 @@ static int rppx1_bls_probe(struct rpp_module *mod) return 0; } +static void +rppx1_bls_swap_regs(struct rpp_module *mod, const u32 input[4], u32 output[4]) +{ + static const unsigned int swap[4][4] = { + [RPP_RGGB] = { 0, 1, 2, 3 }, + [RPP_GRBG] = { 1, 0, 3, 2 }, + [RPP_GBRG] = { 2, 3, 0, 1 }, + [RPP_BGGR] = { 3, 2, 1, 0 }, + }; + + /* Swap to pattern used in our path, PRE1 or PRE2. */ + struct rpp_module *acq = mod == &mod->rpp->pre1.bls ? + &mod->rpp->pre1.acq : &mod->rpp->pre2.bls; + enum rpp_raw_pattern pattern = acq->info.acq.raw_pattern; + + for (unsigned int i = 0; i < 4; ++i) + output[i] = input[swap[pattern][i]]; +} + +static int +rppx1_bls_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_bls_params *cfg = &block->bls; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + BLS_CTRL_REG, 0); + return 0; + } + + u32 ctrl = BLS_CTRL_BLS_EN; + + if (cfg->mode == RPPX1_BLS_MODE_FIXED) { + static const u32 regs[] = { + BLS_A_FIXED_REG, + BLS_B_FIXED_REG, + BLS_C_FIXED_REG, + BLS_D_FIXED_REG, + }; + u32 swapped[4]; + + rppx1_bls_swap_regs(mod, regs, swapped); + + /* + * The PRE1 pipe fixed values are 24-bits + 1 sign bit, while + * the PRE2 pipe values are 12-bits + 1 sign bit. + */ + u32 mask; + + switch (cfg->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: + mask = BLS_PRE1_FIXED_MASK; + break; + case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: + mask = BLS_PRE2_FIXED_MASK; + break; + default: + return -EINVAL; + } + + write(priv, mod->base + swapped[0], cfg->fixed.a & mask); + write(priv, mod->base + swapped[1], cfg->fixed.b & mask); + write(priv, mod->base + swapped[2], cfg->fixed.c & mask); + write(priv, mod->base + swapped[3], cfg->fixed.d & mask); + } else { + write(priv, mod->base + BLS_SAMPLES_REG, cfg->samples); + + if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN1) { + write(priv, mod->base + BLS_H1_START_REG, cfg->window1.h_offs); + write(priv, mod->base + BLS_H1_STOP_REG, cfg->window1.h_size); + write(priv, mod->base + BLS_V1_START_REG, cfg->window1.v_offs); + write(priv, mod->base + BLS_V1_STOP_REG, cfg->window1.v_size); + ctrl |= BLS_CTRL_BLS_WIN1; + } + + if (cfg->en_windows & RPPX1_BLS_WIN_EN_WIN2) { + write(priv, mod->base + BLS_H2_START_REG, cfg->window2.h_offs); + write(priv, mod->base + BLS_H2_STOP_REG, cfg->window2.h_size); + write(priv, mod->base + BLS_V2_START_REG, cfg->window2.v_offs); + write(priv, mod->base + BLS_V2_STOP_REG, cfg->window2.v_size); + ctrl |= BLS_CTRL_BLS_WIN2; + } + + ctrl |= BLS_CTRL_BLS_MODE_MEASURED; + } + + write(priv, mod->base + BLS_CTRL_REG, ctrl); + + return 0; +} + const struct rpp_module_ops rppx1_bls_ops = { .probe = rppx1_bls_probe, + .fill_params = rppx1_bls_fill_params, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index 934e18731b08..0049977870ad 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -85,6 +85,8 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1: PRE1 pipe Histogram Measurement * @RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2: PRE2 pipe Histogram Measurement * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement + * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction + * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -96,6 +98,8 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE1, RPPX1_PARAMS_BLOCK_TYPE_HIST_PRE2, RPPX1_PARAMS_BLOCK_TYPE_HIST_POST, + RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2, }; /** @@ -330,6 +334,105 @@ struct rppx1_hist_params { __u8 reserved; }; +/** + * struct rppx1_bls_fixed - BLS fixed subtraction values + * + * Fixed black level values subtracted from sensor data per Bayer channel. + * Negative values result in addition. + * + * The PRE1 pipe BLS module operates on a 24-bits input data and fixed black + * levels are stored as a signed 2's complement representation ranging from + * -2^24 to 2^24-1. + * + * The PRE2 pipe BLS module operates on a 12-bits input data and fixed black + * levels are stored as a signed 2's complement representation ranging from + * -2^12 to 2^12-1. + * + * Userspace is expected to provide fixed black level values with a bit-depth + * matching the one of pipe in use. + * + * These subtraction values are matched with the sensor native Bayer components + * ordering according to the cropping configuration on the input port. + * + * @a: subtraction value for channel A + * @b: subtraction value for channel B + * @c: subtraction value for channel C + * @d: subtraction value for channel D + */ +struct rppx1_bls_fixed { + __u32 a; + __u32 b; + __u32 c; + __u32 d; +}; + +/** + * enum rppx1_bls_mode - BLS subtraction mode + * + * Select if subtracted black level come from fixed or measured values. + * + * @RPPX1_BLS_MODE_FIXED: subtract fixed values + * @RPPX1_BLS_MODE_MEAS: subtract measured values + */ +enum rppx1_bls_mode { + RPPX1_BLS_MODE_FIXED, + RPPX1_BLS_MODE_MEAS, +}; + +/** + * enum rppx1_bls_win_en: BLS measurement configuration + * + * Select the measurement window to use for measured black level values. + * + * @RPPX1_BLS_WIN_EN_OFF: disable measurement + * @RPPX1_BLS_WIN_EN_WIN1: Enable measurement from window 1 + * @RPPX1_BLS_WIN_EN_WIN2: enable measurement from window 2 + * @RPPX1_BLS_WIN_EN_WIN12: enable measurement from window 1 and window 2 + */ +enum rppx1_bls_win_en { + RPPX1_BLS_WIN_EN_OFF, + RPPX1_BLS_WIN_EN_WIN1, + RPPX1_BLS_WIN_EN_WIN2, + RPPX1_BLS_WIN_EN_WIN12, +}; + +/** + * struct rppx1_bls_params - RPP-X1 Black Level Subtraction Module + * + * The RPP-X1 Black Level Subtraction module is available on the PRE1 and PRE2 + * pre-fusion pipes. Userspace selects which pipe to operate by setting the + * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or + * RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2. + * + * The BLS module operates on fixed or measured data according to the setting of + * the @mode field. When RPPX1_BLS_MODE_FIXED is used userspace shall provide + * the per-channel black levels in @fixed. When RPPX1_BLS_MODE_MEAS is used + * userspace shall configure the measurement windows @window1 and optionally + * @window2 to select the optically black pixels region in the input frame. The + * @samples fields controls how many measure samples are used for averaging the + * measured black levels. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1 or + * type == RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2) + * @window1: BLS measurement window 1 (14 bits) + * @window2: BLS measurement window 2 (14 bits) + * @fixed: fixed subtraction values (see enum rppx1_bls_fixed) + * @mode: BLS subtraction mode (see enum rppx1_bls_mode) + * @en_windows: BLS measurement mode (see rppx1_bls_win_en) + * @samples: log2 of the number of measured pixels per Bayer position + * @reserved: padding + */ +struct rppx1_bls_params { + struct v4l2_isp_params_block_header header; + struct rppx1_window window1; + struct rppx1_window window2; + struct rppx1_bls_fixed fixed; + __u8 mode; + __u8 en_windows; + __u8 samples; + __u8 reserved[5]; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -345,7 +448,9 @@ struct rppx1_hist_params { sizeof(struct rppx1_exm_params) + \ sizeof(struct rppx1_hist_params) + \ sizeof(struct rppx1_hist_params) + \ - sizeof(struct rppx1_hist_params)) + sizeof(struct rppx1_hist_params) + \ + sizeof(struct rppx1_bls_params) + \ + sizeof(struct rppx1_bls_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From aaeee9b7433c6015c0e5d5f3c4e2627330f23ae5 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:38 +0200 Subject: media: rppx1: ccor: Add support for color correction matrix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the color correction matrix configuration parameters. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 4 ++ .../media/platform/dreamchip/rppx1/rppx1_ccor.c | 61 ++++++++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 30 ++++++++++- 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index 5e20fcdcbcc4..e039746ac542 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -49,6 +49,7 @@ union rppx1_params_block { struct v4l2_isp_block_header header; struct rppx1_bls_params bls; struct rppx1_awbg_params awbg; + struct rppx1_ccor_params ccor; struct rppx1_hist_params hist; struct rppx1_exm_params exm; struct rppx1_wbmeas_params wbmeas; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 831cf7ca154c..a83d393d0504 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -21,6 +21,7 @@ rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), + RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor), RPPX1_PARAMS_BLOCK_INFO(HIST_PRE1, hist), RPPX1_PARAMS_BLOCK_INFO(HIST_PRE2, hist), RPPX1_PARAMS_BLOCK_INFO(HIST_POST, hist), @@ -66,6 +67,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: module = &rpp->pre1.awbg; break; + case RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: + module = &rpp->post.ccor; + break; case RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: module = &rpp->post.hist; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c index 3bfad3ba12e6..5ddc7edf6930 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ccor.c @@ -67,9 +67,70 @@ static int rppx1_ccor_start(struct rpp_module *mod, return 0; } +static int +rppx1_ccor_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_ccor_params *cfg = &block->ccor; + + /* If the modules is disabled, configure in bypass mode. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + CCOR_COEFF_REG(0), 0x1000); + write(priv, mod->base + CCOR_COEFF_REG(1), 0x0000); + write(priv, mod->base + CCOR_COEFF_REG(2), 0x0000); + + write(priv, mod->base + CCOR_COEFF_REG(3), 0x0000); + write(priv, mod->base + CCOR_COEFF_REG(4), 0x1000); + write(priv, mod->base + CCOR_COEFF_REG(5), 0x0000); + + write(priv, mod->base + CCOR_COEFF_REG(6), 0x0000); + write(priv, mod->base + CCOR_COEFF_REG(7), 0x0000); + write(priv, mod->base + CCOR_COEFF_REG(8), 0x1000); + + write(priv, mod->base + CCOR_OFFSET_R_REG, 0x00000000); + write(priv, mod->base + CCOR_OFFSET_G_REG, 0x00000000); + write(priv, mod->base + CCOR_OFFSET_B_REG, 0x00000000); + + return 0; + } + + /* + * Coefficient n for color correction matrix. + * + * RPP coefficients are 16-bit signed fixed-point numbers with 4 bit + * integer and 12 bit fractional part ranging from -8 (0x8000) to + * +7.9996 (0x7FFF). 0 is represented by 0x0000 and a coefficient + * value of 1 as 0x1000. + */ + write(priv, mod->base + CCOR_COEFF_REG(0), cfg->coeff[0][0]); + write(priv, mod->base + CCOR_COEFF_REG(1), cfg->coeff[0][1]); + write(priv, mod->base + CCOR_COEFF_REG(2), cfg->coeff[0][2]); + + write(priv, mod->base + CCOR_COEFF_REG(3), cfg->coeff[1][0]); + write(priv, mod->base + CCOR_COEFF_REG(4), cfg->coeff[1][1]); + write(priv, mod->base + CCOR_COEFF_REG(5), cfg->coeff[1][2]); + + write(priv, mod->base + CCOR_COEFF_REG(6), cfg->coeff[2][0]); + write(priv, mod->base + CCOR_COEFF_REG(7), cfg->coeff[2][1]); + write(priv, mod->base + CCOR_COEFF_REG(8), cfg->coeff[2][2]); + + /* + * Offset for color components correction matrix. + * + * Values are a two's complement integer with one sign bit. + */ + write(priv, mod->base + CCOR_OFFSET_R_REG, cfg->offset[0]); + write(priv, mod->base + CCOR_OFFSET_G_REG, cfg->offset[1]); + write(priv, mod->base + CCOR_OFFSET_B_REG, cfg->offset[2]); + + return 0; +} + const struct rpp_module_ops rppx1_ccor_ops = { .probe = rppx1_ccor_probe, .start = rppx1_ccor_start, + .fill_params = rppx1_ccor_fill_params, }; static int rppx1_ccor_csm_start(struct rpp_module *mod, diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index 0049977870ad..feea30585d4c 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -87,6 +87,7 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_HIST_POST: POST pipe Histogram Measurement * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction + * @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -100,6 +101,7 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_HIST_POST, RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1, RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2, + RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST, }; /** @@ -433,6 +435,31 @@ struct rppx1_bls_params { __u8 reserved[5]; }; +/** + * struct rppx1_ccor_params - Color CORrection configuration + * + * The CCOR (Color Correction) module is available on the MAIN_POST pipe. It + * performs color space correction on a pixel-per-pixel basis using a 3x3 matrix + * of coefficients and per-color channel offsets. + * + * The matrix coefficients are represented as 16 bits signed fixed point values + * in Q4.12 format ranging from -8 to +7.999. + * + * The per-channel color offsets are represented as 2's complement values + * stored in 25 bits ranging from -16777216 to 16777215. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST) + * @coeff: color correction matrix coefficients, 16 bits signed Q4.12 + * @reserved: padding + * @offset: R, G, B offsets, 2's complement 25 bits + */ +struct rppx1_ccor_params { + struct v4l2_isp_params_block_header header; + __u16 coeff[3][3]; + __u16 reserved; + __u32 offset[3]; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -450,7 +477,8 @@ struct rppx1_bls_params { sizeof(struct rppx1_hist_params) + \ sizeof(struct rppx1_hist_params) + \ sizeof(struct rppx1_bls_params) + \ - sizeof(struct rppx1_bls_params)) + sizeof(struct rppx1_bls_params) + \ + sizeof(struct rppx1_ccor_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From b39656efb71ad1e598b89e4bb1a39696d5c2be28 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:39 +0200 Subject: media: rppx1: lsc: Add support for lens shade correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the lens shade correction configuration parameters. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 + drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c | 119 +++++++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 54 +++++++++- 4 files changed, 178 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index e039746ac542..48b61b5c35b4 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -48,6 +48,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; struct rppx1_bls_params bls; + struct rppx1_lsc_params lsc; struct rppx1_awbg_params awbg; struct rppx1_ccor_params ccor; struct rppx1_hist_params hist; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index a83d393d0504..8d85d0c7bff1 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -19,6 +19,8 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls), RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls), + RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc), + RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE2, awbg), RPPX1_PARAMS_BLOCK_INFO(CCOR_POST, ccor), @@ -64,6 +66,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: module = &rpp->pre1.bls; break; + case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: + module = &rpp->pre1.lsc; + break; case RPPX1_PARAMS_BLOCK_TYPE_AWBG_PRE1: module = &rpp->pre1.awbg; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c index be49fc17ea26..8badeca23e24 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lsc.c @@ -55,6 +55,10 @@ #define LSC_TABLE_SEL_REG 0x00a8 #define LSC_STATUS_REG 0x00ac +#define LSC_R_TABLE_DATA_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 12)) +#define LSC_GRAD_VALUE(v1, v2) (((v1) & 0xfff) | (((v2) & 0xfff) << 16)) +#define LSC_SIZE_VALUE(v1, v2) (((v1) & 0x1ff) | (((v2) & 0x1ff) << 16)) + static int rppx1_lsc_probe(struct rpp_module *mod) { /* Version check. */ @@ -64,6 +68,121 @@ static int rppx1_lsc_probe(struct rpp_module *mod) return 0; } +static int +rppx1_lsc_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_lsc_params *cfg = &block->lsc; + const __u16 *v; + + /* Always disable module as it needs be disabled before configuring. */ + write(priv, mod->base + LSC_CTRL_REG, 0); + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) + return 0; + + /* + * Program the color correction sectors. + * + * There are two tables to one can program and switch between. As the + * RPPX1 supports preparing a buffer of commands to be applied later + * only use table 0. This works as long as the ISP is not used in + * inline-mode. + * + * For inline-mode support using DMA for configuration is not possible + * so this is not an issue, but needs to be address if inline-mode + * support is added to the driver. + */ + + /* Start writing at beginning of table 0. */ + write(priv, mod->base + LSC_R_TABLE_ADDR_REG, 0); + write(priv, mod->base + LSC_GR_TABLE_ADDR_REG, 0); + write(priv, mod->base + LSC_B_TABLE_ADDR_REG, 0); + write(priv, mod->base + LSC_GB_TABLE_ADDR_REG, 0); + + /* Program data tables. */ + for (unsigned int i = 0; i < RPPX1_LSC_SAMPLES_MAX; i++) { + const __u16 *r = cfg->r_data[i]; + const __u16 *gr = cfg->gr_data[i]; + const __u16 *b = cfg->b_data[i]; + const __u16 *gb = cfg->gb_data[i]; + unsigned int j; + + for (j = 0; j < RPPX1_LSC_SAMPLES_MAX - 1; j += 2) { + write(priv, mod->base + LSC_R_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(r[j], r[j + 1])); + write(priv, mod->base + LSC_GR_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(gr[j], gr[j + 1])); + write(priv, mod->base + LSC_B_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(b[j], b[j + 1])); + write(priv, mod->base + LSC_GB_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(gb[j], gb[j + 1])); + } + + write(priv, mod->base + LSC_R_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(r[j], 0)); + write(priv, mod->base + LSC_GR_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(gr[j], 0)); + write(priv, mod->base + LSC_B_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(b[j], 0)); + write(priv, mod->base + LSC_GB_TABLE_DATA_REG, + LSC_R_TABLE_DATA_VALUE(gb[j], 0)); + } + + /* Activate table 0. */ + write(priv, mod->base + LSC_TABLE_SEL_REG, 0); + + /* + * Program X- and Y- sizes, and gradients. + */ + + v = cfg->x_grad; + write(priv, mod->base + LSC_XGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1])); + write(priv, mod->base + LSC_XGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3])); + write(priv, mod->base + LSC_XGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5])); + write(priv, mod->base + LSC_XGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7])); + write(priv, mod->base + LSC_XGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9])); + write(priv, mod->base + LSC_XGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); + write(priv, mod->base + LSC_XGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); + write(priv, mod->base + LSC_XGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); + + v = cfg->y_grad; + write(priv, mod->base + LSC_YGRAD_01_REG, LSC_GRAD_VALUE(v[0], v[1])); + write(priv, mod->base + LSC_YGRAD_23_REG, LSC_GRAD_VALUE(v[2], v[3])); + write(priv, mod->base + LSC_YGRAD_45_REG, LSC_GRAD_VALUE(v[4], v[5])); + write(priv, mod->base + LSC_YGRAD_67_REG, LSC_GRAD_VALUE(v[6], v[7])); + write(priv, mod->base + LSC_YGRAD_89_REG, LSC_GRAD_VALUE(v[8], v[9])); + write(priv, mod->base + LSC_YGRAD_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); + write(priv, mod->base + LSC_YGRAD_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); + write(priv, mod->base + LSC_YGRAD_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); + + v = cfg->x_sect_size; + write(priv, mod->base + LSC_XSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1])); + write(priv, mod->base + LSC_XSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3])); + write(priv, mod->base + LSC_XSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5])); + write(priv, mod->base + LSC_XSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7])); + write(priv, mod->base + LSC_XSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9])); + write(priv, mod->base + LSC_XSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); + write(priv, mod->base + LSC_XSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); + write(priv, mod->base + LSC_XSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); + + v = cfg->y_sect_size; + write(priv, mod->base + LSC_YSIZE_01_REG, LSC_GRAD_VALUE(v[0], v[1])); + write(priv, mod->base + LSC_YSIZE_23_REG, LSC_GRAD_VALUE(v[2], v[3])); + write(priv, mod->base + LSC_YSIZE_45_REG, LSC_GRAD_VALUE(v[4], v[5])); + write(priv, mod->base + LSC_YSIZE_67_REG, LSC_GRAD_VALUE(v[6], v[7])); + write(priv, mod->base + LSC_YSIZE_89_REG, LSC_GRAD_VALUE(v[8], v[9])); + write(priv, mod->base + LSC_YSIZE_1011_REG, LSC_GRAD_VALUE(v[10], v[11])); + write(priv, mod->base + LSC_YSIZE_1213_REG, LSC_GRAD_VALUE(v[12], v[13])); + write(priv, mod->base + LSC_YSIZE_1415_REG, LSC_GRAD_VALUE(v[14], v[15])); + + /* Enable module. */ + write(priv, mod->base + LSC_CTRL_REG, LSC_CTRL_LSC_EN); + + return 0; +} + const struct rpp_module_ops rppx1_lsc_ops = { .probe = rppx1_lsc_probe, + .fill_params = rppx1_lsc_fill_params, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index feea30585d4c..eec8a9c50bbe 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -88,6 +88,8 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: PRE1 pipe Black Level Subtraction * @RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2: PRE2 pipe Black Level Subtraction * @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction + * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: PRE1 pipe Lens Shading Correction + * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 pipe Lens Shading Correction */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -102,6 +104,8 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1, RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE2, RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST, + RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2, }; /** @@ -460,6 +464,52 @@ struct rppx1_ccor_params { __u32 offset[3]; }; +/* Lens Shade Correction */ +#define RPPX1_LSC_SAMPLES_MAX 17 +#define RPPX1_LSC_NUM_SECTORS 16 + +/** + * struct rppx1_lsc_params - Lens Shading Correction configuration + * + * The RPP-X1 Lens shading correction module is available on the PRE1 and PRE2 + * pre-fusion pipes. Userspace selects which pipe to operate by setting the + * @header.type field to RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1 or + * RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2. + * + * The module applies per-color channel correction factors @r_data, @gr_data, + * @gb_data and @b_data as a 16x16 grid mapped on the image. The size of each + * grid segment is expressed by the @x_sect_size and @y_sect_size arrays. Each + * segment shall be at least 8 pixels in size and the sum of all horizontal + * segments @x_sect_size shall match the input frame size width. + * + * The correction factors values are expressed as unsigned Q2.10 integers + * ranging from 1 to 3.999. + * + * Pre-calculated interpolation factors shall be provided in the @x_grad + * and @y_grad fields, expressed as 12 bits integer values. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LSC) + * @r_data: correction factors for the red channel in Q2.10 format + * @gr_data: correction factors for the green (red) channel in Q2.10 format + * @gb_data: correction factors for the green (blue) channel in Q2.10 format + * @b_data: correction factors for the blue channel in Q2.10 format + * @x_grad: Interpolation gradients for each horizontal sector (12 bits) + * @y_grad: Interpolation gradients for each vertical sector (12 bits) + * @x_sect_size: Horizontal sectors sizes + * @y_sect_size: Vertical sectors sizes + */ +struct rppx1_lsc_params { + struct v4l2_isp_params_block_header header; + __u16 r_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX]; + __u16 gr_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX]; + __u16 gb_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX]; + __u16 b_data[RPPX1_LSC_SAMPLES_MAX][RPPX1_LSC_SAMPLES_MAX]; + __u16 x_grad[RPPX1_LSC_NUM_SECTORS]; + __u16 y_grad[RPPX1_LSC_NUM_SECTORS]; + __u16 x_sect_size[RPPX1_LSC_NUM_SECTORS]; + __u16 y_sect_size[RPPX1_LSC_NUM_SECTORS]; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -478,7 +528,9 @@ struct rppx1_ccor_params { sizeof(struct rppx1_hist_params) + \ sizeof(struct rppx1_bls_params) + \ sizeof(struct rppx1_bls_params) + \ - sizeof(struct rppx1_ccor_params)) + sizeof(struct rppx1_ccor_params) + \ + sizeof(struct rppx1_lsc_params) + \ + sizeof(struct rppx1_lsc_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From d011daefa1f1279e949a9b10a9439fc02f5093b1 Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Thu, 30 Jul 2026 19:12:40 +0200 Subject: media: rppx1: ga: Add support for gamma out correction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the gamma out correction configuration parameters. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Niklas Söderlund Co-developed-by: Jai Luthra Signed-off-by: Jai Luthra Co-developed-by: Jacopo Mondi Signed-off-by: Jacopo Mondi Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 ++ drivers/media/platform/dreamchip/rppx1/rppx1_ga.c | 43 ++++++++++++++++- include/uapi/linux/media/dreamchip/rppx1-config.h | 55 +++++++++++++++++++++- 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index 48b61b5c35b4..948e26d7361d 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -54,6 +54,7 @@ union rppx1_params_block { struct rppx1_hist_params hist; struct rppx1_exm_params exm; struct rppx1_wbmeas_params wbmeas; + struct rppx1_ga_params ga; }; union rppx1_stats_block { diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 8d85d0c7bff1..86d445b52504 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -30,6 +30,8 @@ rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(EXM_PRE1, exm), RPPX1_PARAMS_BLOCK_INFO(EXM_PRE2, exm), RPPX1_PARAMS_BLOCK_INFO(WBMEAS_POST, wbmeas), + RPPX1_PARAMS_BLOCK_INFO(GA_HV, ga), + RPPX1_PARAMS_BLOCK_INFO(GA_MV, ga), }; int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, @@ -84,6 +86,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST: module = &rpp->post.wbmeas; break; + case RPPX1_PARAMS_BLOCK_TYPE_GA_HV: + module = &rpp->hv.ga; + break; default: dev_warn(rpp->dev, "Not handled RPPX1 block type: 0x%04x\n", diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c index 0667672b2694..1d9c24c43f77 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_ga.c @@ -15,9 +15,11 @@ #define GAMMA_OUT_MODE_REG 0x0008 #define GAMMA_OUT_MODE_GAMMA_OUT_EQU_SEGM BIT(0) -#define GAMMA_OUT_Y_REG_NUM 17 #define GAMMA_OUT_Y_REG(n) (0x000c + (4 * (n))) +#define GAMMA_OUT_HV_GAMMA_CURVE_MASK GENMASK(11, 0) +#define GAMMA_OUT_MV_GAMMA_CURVE_MASK GENMASK(23, 0) + static int rppx1_ga_probe(struct rpp_module *mod) { /* Version check. */ @@ -44,7 +46,46 @@ static int rppx1_ga_start(struct rpp_module *mod, return 0; } +static int +rppx1_ga_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_ga_params *cfg = &block->ga; + u32 mask; + + /* If the modules is disabled, simply bypass it. */ + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + GAMMA_OUT_ENABLE_REG, 0); + return 0; + } + + switch (cfg->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_GA_HV: + mask = GAMMA_OUT_HV_GAMMA_CURVE_MASK; + break; + case RPPX1_PARAMS_BLOCK_TYPE_GA_MV: + mask = GAMMA_OUT_MV_GAMMA_CURVE_MASK; + break; + default: + return -EINVAL; + } + + write(priv, mod->base + GAMMA_OUT_MODE_REG, cfg->mode); + + for (unsigned int i = 0; i < RPPX1_GA_MAX_SAMPLES; i++) + write(priv, mod->base + GAMMA_OUT_Y_REG(i), + cfg->gamma_y[i] & mask); + + /* Enable module. */ + write(priv, mod->base + GAMMA_OUT_ENABLE_REG, + GAMMA_OUT_ENABLE_GAMMA_OUT_EN); + + return 0; +} + const struct rpp_module_ops rppx1_ga_ops = { .probe = rppx1_ga_probe, .start = rppx1_ga_start, + .fill_params = rppx1_ga_fill_params, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index eec8a9c50bbe..aa8a078493af 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -90,6 +90,8 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST: POST pipe Color Correction * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: PRE1 pipe Lens Shading Correction * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 pipe Lens Shading Correction + * @RPPX1_PARAMS_BLOCK_TYPE_GA_HV: Human Vision Pipe Gamma Out Correction + * @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -106,6 +108,8 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_CCOR_POST, RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1, RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2, + RPPX1_PARAMS_BLOCK_TYPE_GA_HV, + RPPX1_PARAMS_BLOCK_TYPE_GA_MV, }; /** @@ -510,6 +514,53 @@ struct rppx1_lsc_params { __u16 y_sect_size[RPPX1_LSC_NUM_SECTORS]; }; +/* Gamma Out */ +#define RPPX1_GA_MAX_SAMPLES 17 + +/** + * enum rppx1_ga_seg_mode - Gamma out curve segmentation mode + * + * Segmentation mode of the 16 input sampling points for the Gamma Out + * Correction module. + * + * @RPPX1_GA_SEG_MODE_LOGARITHMIC: logarithmic-like segmentation mode + * @RPPX1_GA_SEG_MODE_EQUIDISTANT: equidistant segmentation mode + */ +enum rppx1_ga_seg_mode { + RPPX1_GA_SEG_MODE_LOGARITHMIC, + RPPX1_GA_SEG_MODE_EQUIDISTANT +}; + +/** + * struct rppx1_ga_params - Gamma Out Correction configuration + * + * The Gamma Out Correction module is available on the Human Vision Output + * Pipe (HV) and the Machine Vision Output Pipe (MV). Userspace selects + * which pipe to operate by setting the @header.type field to + * RPPX1_PARAMS_BLOCK_TYPE_GA_HV or RPPX1_PARAMS_BLOCK_TYPE_GA_MV. + * + * The module allows to apply a @gamma_y gamma correction curve to RGB data + * represented as a table of 16 entries. The 16 input sampling points can be + * equidistant or segmented using a logarithmic scale according to the value of + * @mode. + * + * The gamma curve values are 12 bits on the HV output pipe and 24 bits on the + * MV output pipe. Userspace is expected to provide the curve values with a + * bit-depth matching the one of pipe in use. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_GA_HV or + * type = RPPX1_PARAMS_BLOCK_TYPE_GA_MV) + * @gamma_y: gamma out curve y-axis values + * @mode: gamma curve input segmentation mode (see rppx1_ga_seg_mode) + * @reserved: padding + */ +struct rppx1_ga_params { + struct v4l2_isp_params_block_header header; + __u32 gamma_y[RPPX1_GA_MAX_SAMPLES]; + __u8 mode; + __u8 reserved[3]; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -530,7 +581,9 @@ struct rppx1_lsc_params { sizeof(struct rppx1_bls_params) + \ sizeof(struct rppx1_ccor_params) + \ sizeof(struct rppx1_lsc_params) + \ - sizeof(struct rppx1_lsc_params)) + sizeof(struct rppx1_lsc_params) + \ + sizeof(struct rppx1_ga_params) + \ + sizeof(struct rppx1_ga_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From 465425926ff36b9f5fb1f069e4277918e6109f5a Mon Sep 17 00:00:00 2001 From: Jai Luthra Date: Thu, 30 Jul 2026 19:12:41 +0200 Subject: media: rppx1: lin: Add support for gamma sensor linearization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the RPPX1 driver to allow setting the gamma sensor linearization configuration parameters. It uses the RPPX1 framework for parameters and its writer abstraction to allow the user to control how, and when, configuration is applied to the RPPX1. Signed-off-by: Jai Luthra Signed-off-by: Niklas Söderlund Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- .../media/platform/dreamchip/rppx1/rpp_module.h | 1 + .../media/platform/dreamchip/rppx1/rpp_params.c | 5 ++ drivers/media/platform/dreamchip/rppx1/rppx1_lin.c | 53 ++++++++++++++++++++++ include/uapi/linux/media/dreamchip/rppx1-config.h | 50 +++++++++++++++++++- 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_module.h b/drivers/media/platform/dreamchip/rppx1/rpp_module.h index 948e26d7361d..072309d8662d 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_module.h +++ b/drivers/media/platform/dreamchip/rppx1/rpp_module.h @@ -48,6 +48,7 @@ void rpp_module_clrset(struct rpp_module *mod, u32 offset, u32 mask, u32 value); union rppx1_params_block { struct v4l2_isp_block_header header; struct rppx1_bls_params bls; + struct rppx1_lin_params lin; struct rppx1_lsc_params lsc; struct rppx1_awbg_params awbg; struct rppx1_ccor_params ccor; diff --git a/drivers/media/platform/dreamchip/rppx1/rpp_params.c b/drivers/media/platform/dreamchip/rppx1/rpp_params.c index 86d445b52504..a75a27a8afd0 100644 --- a/drivers/media/platform/dreamchip/rppx1/rpp_params.c +++ b/drivers/media/platform/dreamchip/rppx1/rpp_params.c @@ -19,6 +19,8 @@ static const struct v4l2_isp_params_block_type_info rppx1_ext_params_blocks_info[] = { RPPX1_PARAMS_BLOCK_INFO(BLS_PRE1, bls), RPPX1_PARAMS_BLOCK_INFO(BLS_PRE2, bls), + RPPX1_PARAMS_BLOCK_INFO(LIN_PRE1, lin), + RPPX1_PARAMS_BLOCK_INFO(LIN_PRE2, lin), RPPX1_PARAMS_BLOCK_INFO(LSC_PRE1, lsc), RPPX1_PARAMS_BLOCK_INFO(LSC_PRE2, lsc), RPPX1_PARAMS_BLOCK_INFO(AWBG_PRE1, awbg), @@ -68,6 +70,9 @@ int rppx1_params(struct rppx1 *rpp, struct vb2_buffer *vb, size_t max_size, case RPPX1_PARAMS_BLOCK_TYPE_BLS_PRE1: module = &rpp->pre1.bls; break; + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1: + module = &rpp->pre1.lin; + break; case RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE1: module = &rpp->pre1.lsc; break; diff --git a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c index 0a0e7ab2d1d6..b389778f9383 100644 --- a/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c +++ b/drivers/media/platform/dreamchip/rppx1/rppx1_lin.c @@ -25,6 +25,11 @@ #define LIN_B_Y_REG_NUM 17 #define LIN_B_Y_REG(n) (0x0098 + (4 * (n))) +#define LIN_PRE1_DEGAMMA_CURVE_MASK GENMASK(23, 0) +#define LIN_PRE1_SAMPLE_POINTS_MASK GENMASK(3, 0) +#define LIN_PRE2_DEGAMMA_CURVE_MASK GENMASK(11, 0) +#define LIN_PRE2_SAMPLE_POINTS_MASK GENMASK(2, 0) + static int rppx1_lin_probe(struct rpp_module *mod) { /* Version check. */ @@ -53,7 +58,55 @@ static int rppx1_lin_start(struct rpp_module *mod, return 0; } +static int rppx1_lin_fill_params(struct rpp_module *mod, + const union rppx1_params_block *block, + rppx1_reg_write write, void *priv) +{ + const struct rppx1_lin_params *cfg = &block->lin; + u8 sample_mask; + u32 dx_lo = 0; + u32 dx_hi = 0; + u32 mask; + + if (cfg->header.flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) { + write(priv, mod->base + LIN_ENABLE_REG, 0); + return 0; + } + + switch (cfg->header.type) { + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1: + mask = LIN_PRE1_DEGAMMA_CURVE_MASK; + sample_mask = LIN_PRE1_SAMPLE_POINTS_MASK; + break; + case RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2: + mask = LIN_PRE2_DEGAMMA_CURVE_MASK; + sample_mask = LIN_PRE2_SAMPLE_POINTS_MASK; + break; + default: + return -EINVAL; + } + + for (unsigned int i = 0; i < 8; ++i) { + dx_lo |= (cfg->dx[i] & sample_mask) << 4 * i; + dx_hi |= (cfg->dx[i + 8] & sample_mask) << 4 * i; + } + + write(priv, mod->base + LIN_DX_LO_REG, dx_lo); + write(priv, mod->base + LIN_DX_HI_REG, dx_hi); + + for (unsigned int i = 0; i < RPPX1_LIN_DEGAMMA_CURVE_NUM; i++) { + write(priv, mod->base + LIN_R_Y_REG(i), cfg->curve_r[i] & mask); + write(priv, mod->base + LIN_G_Y_REG(i), cfg->curve_g[i] & mask); + write(priv, mod->base + LIN_B_Y_REG(i), cfg->curve_b[i] & mask); + } + + write(priv, mod->base + LIN_ENABLE_REG, LIN_ENABLE_GAMMA_IN_EN); + + return 0; +} + const struct rpp_module_ops rppx1_lin_ops = { .probe = rppx1_lin_probe, .start = rppx1_lin_start, + .fill_params = rppx1_lin_fill_params, }; diff --git a/include/uapi/linux/media/dreamchip/rppx1-config.h b/include/uapi/linux/media/dreamchip/rppx1-config.h index aa8a078493af..3507a9ab03df 100644 --- a/include/uapi/linux/media/dreamchip/rppx1-config.h +++ b/include/uapi/linux/media/dreamchip/rppx1-config.h @@ -92,6 +92,8 @@ enum rppx1_meas_chan { * @RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2: PRE2 pipe Lens Shading Correction * @RPPX1_PARAMS_BLOCK_TYPE_GA_HV: Human Vision Pipe Gamma Out Correction * @RPPX1_PARAMS_BLOCK_TYPE_GA_MV: Machine Vision Gamma Out Correction + * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1: PRE1 pipe Linearization (Sensor De-gamma) + * @RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2: PRE2 pipe Linearization (Sensor De-gamma) */ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_WBMEAS_POST, @@ -110,6 +112,8 @@ enum rppx1_params_block_type { RPPX1_PARAMS_BLOCK_TYPE_LSC_PRE2, RPPX1_PARAMS_BLOCK_TYPE_GA_HV, RPPX1_PARAMS_BLOCK_TYPE_GA_MV, + RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1, + RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2, }; /** @@ -561,6 +565,48 @@ struct rppx1_ga_params { __u8 reserved[3]; }; +/* Linearization (Sensor De-gamma) */ +#define RPPX1_LIN_SAMPLE_POINTS_NUM 16 +#define RPPX1_LIN_DEGAMMA_CURVE_NUM 17 + +/** + * struct rppx1_lin_params - Linearization (Sensor De-gamma) configuration + * + * The RPP-X1 linearization module is available on the PRE1 and PRE2 pre-fusion + * pipes. Userspace selects which pipe to operate by setting the @header.type + * field to RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or + * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2. + * + * The LIN module applies the per-color channel de-gamma linearization curves + * @curve_r, @curve_g and @curve_b defined on the input sampling points @dx. + * + * For the PRE1 pipe the de-gamma curves values are 24-bits, for the PRE2 pipe + * the de-gamma curve values are 12-bits. + * + * For the PRE1 pipe de-gamma module sampling points @dx values are in the range + * [0, 15] (4 bits). For the PRE2 pipe de-gamma module sampling points values + * are in the range [0, 7] (3 bits). + * + * Userspace is expected to provide the curve values and sampling points with a + * bit-depth matching the one of pipe in use. + * + * @header: block header (type = RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE1 or + * RPPX1_PARAMS_BLOCK_TYPE_LIN_PRE2) + * @curve_r: de-gamma linearization curve for red channel + * @curve_g: de-gamma linearization curve for green channel + * @curve_b: de-gamma linearization curve for blue channel + * @dx: input sampling points + * @reserved: padding + */ +struct rppx1_lin_params { + struct v4l2_isp_params_block_header header; + __u32 curve_r[RPPX1_LIN_DEGAMMA_CURVE_NUM]; + __u32 curve_g[RPPX1_LIN_DEGAMMA_CURVE_NUM]; + __u32 curve_b[RPPX1_LIN_DEGAMMA_CURVE_NUM]; + __u8 dx[RPPX1_LIN_SAMPLE_POINTS_NUM]; + __u32 reserved; +}; + /** * RPPX1_PARAMS_MAX_SIZE - Maximum size of all RPP-X1 parameter blocks * @@ -583,7 +629,9 @@ struct rppx1_ga_params { sizeof(struct rppx1_lsc_params) + \ sizeof(struct rppx1_lsc_params) + \ sizeof(struct rppx1_ga_params) + \ - sizeof(struct rppx1_ga_params)) + sizeof(struct rppx1_ga_params) + \ + sizeof(struct rppx1_lin_params) + \ + sizeof(struct rppx1_lin_params)) /* --------------------------------------------------------------------------- * Statistics Structures -- cgit From 19eefe1e43244a83330c4604e02292ce61497f00 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 30 Jun 2026 12:34:56 -0400 Subject: MAINTAINERS: media: nxp: imx8-isi: Add Frank Li as reviewer and i.MX mailing list Add Frank Li as a reviewer and the i.MX mailing list for the i.MX8 ISI driver. This helps ensure patches receive review by the NXP i.MX maintainers. Signed-off-by: Frank Li Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260630163456.3317624-1-Frank.Li@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 07177f98c4d4..3785b8c1de0a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19472,7 +19472,9 @@ F: drivers/iio/adc/vf610_adc.c NXP i.MX 8M ISI DRIVER M: Laurent Pinchart +R: Frank Li L: linux-media@vger.kernel.org +L: imx@lists.linux.dev S: Maintained F: Documentation/devicetree/bindings/media/fsl,imx8*-isi.yaml F: Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml -- cgit From fc312f830d8df6c082bd6f7250aa5c0ff063eea4 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Fri, 24 Apr 2026 14:49:50 +0800 Subject: media: dt-bindings: nxp,imx8-isi: Drop fsl,blk-ctrl requirement for i.MX8ULP The i.MX8ULP variant does not require the fsl,blk-ctrl property. Add fsl,imx8ulp-isi to the exception list alongside fsl,imx91-isi. Fixes: 288517a3c6c9 ("dt-bindings: media: nxp,imx8-isi: Add i.MX8ULP ISI compatible string") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Acked-by: Conor Dooley Link: https://patch.msgid.link/20260424-csi2_imx8ulp-v12-1-da148eabc035@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml index b59c4ce30b8b..279c9cd3034a 100644 --- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml +++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml @@ -117,7 +117,9 @@ allOf: compatible: not: contains: - const: fsl,imx91-isi + enum: + - fsl,imx8ulp-isi + - fsl,imx91-isi then: required: - fsl,blk-ctrl -- cgit From 34fd8a99bd4053897adcd39c30c94e22fa51a9f4 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 20 May 2026 22:27:38 +0200 Subject: media: nxp: imx8-isi: Drop unneeded downscaling factor clamping The total scaling factor including bi-linear downscaling and decimation is clamped to 16. The bilinear factor calculation therefore produceds values guaranteed not to exceed the maximum factor of 2.0. The clamping is unneeded, drop it. Note that the ISI_DOWNSCALE_THRESHOLD value of 0x4000 is incorrect, as that would be a factor of 4.0. This was inconsequential given that the computed factor never exceeded 0x2000. Reviewed-by: Frank Li Reviewed-by: Guoniu Zhou Link: https://patch.msgid.link/20260520202738.86782-1-laurent.pinchart@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index 16b20ea2d1db..e07eebae9e2b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -11,8 +11,6 @@ #include "imx8-isi-core.h" #include "imx8-isi-regs.h" -#define ISI_DOWNSCALE_THRESHOLD 0x4000 - static inline u32 mxc_isi_read(struct mxc_isi_pipe *pipe, u32 reg) { return readl(pipe->regs + reg); @@ -118,8 +116,7 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to, * output (input / scale_factor) rounds up to exactly the desired * output. */ - return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec), - ISI_DOWNSCALE_THRESHOLD); + return DIV_ROUND_UP(from * 0x1000, to * *dec); } static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe, -- cgit From 1541ecb9195ad3bd488cade2b43671a9c7c43736 Mon Sep 17 00:00:00 2001 From: "Guoniu.zhou" Date: Thu, 21 May 2026 17:10:04 +0800 Subject: media: nxp: imx8-isi: crossbar: Add get_frame_desc operation Implement the get_frame_desc pad operation for the crossbar subdev using the v4l2_subdev_get_frame_desc_passthrough() helper. This allows the crossbar to properly propagate frame descriptors from its sink pads to its source pads, which is necessary for proper stream configuration in multiplexed streams scenarios. Signed-off-by: Guoniu.zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260521-isi_vc-v5-1-a38eb4fcd58e@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index c580c831972e..90b54402301f 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -404,6 +404,7 @@ static const struct v4l2_subdev_pad_ops mxc_isi_crossbar_subdev_pad_ops = { .enum_mbus_code = mxc_isi_crossbar_enum_mbus_code, .get_fmt = v4l2_subdev_get_fmt, .set_fmt = mxc_isi_crossbar_set_fmt, + .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough, .set_routing = mxc_isi_crossbar_set_routing, .enable_streams = mxc_isi_crossbar_enable_streams, .disable_streams = mxc_isi_crossbar_disable_streams, -- cgit From 79feb648174047feb52728cd05d1d2f8f6ebf07a Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Thu, 21 May 2026 17:10:05 +0800 Subject: media: nxp: imx8-isi: Add virtual channel support The ISI supports different numbers of virtual channels depending on the platform. i.MX95 supports 8 virtual channels, and i.MX8QXP/QM support 4 virtual channels. They are used in multiple camera use cases, such as surround view. Other platforms (such as i.MX8/MN/MP/ULP/91/93) don't support virtual channels, and the VC_ID bits are marked as read-only. Reviewed-by: Frank Li Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260521-isi_vc-v5-2-a38eb4fcd58e@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-core.c | 3 ++ .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 2 + drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c | 17 +++++++- drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c | 2 +- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 48 +++++++++++++++++++++- .../media/platform/nxp/imx8-isi/imx8-isi-regs.h | 12 ++++-- 6 files changed, 77 insertions(+), 7 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c index e8545761b5ff..0ae5f35e017c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c @@ -318,6 +318,7 @@ static const struct mxc_isi_plat_data mxc_imx95_data = { .model = MXC_ISI_IMX95, .num_ports = 4, .num_channels = 8, + .num_vc = 8, .reg_offset = 0x10000, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, @@ -329,6 +330,7 @@ static const struct mxc_isi_plat_data mxc_imx8qm_data = { .model = MXC_ISI_IMX8QM, .num_ports = 5, .num_channels = 8, + .num_vc = 4, .reg_offset = 0x10000, .ier_reg = &mxc_imx8_isi_ier_qm, .set_thd = &mxc_imx8_isi_thd_v1, @@ -340,6 +342,7 @@ static const struct mxc_isi_plat_data mxc_imx8qxp_data = { .model = MXC_ISI_IMX8QXP, .num_ports = 5, .num_channels = 6, + .num_vc = 4, .reg_offset = 0x10000, .ier_reg = &mxc_imx8_isi_ier_v2, .set_thd = &mxc_imx8_isi_thd_v1, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 7547a6559d4c..9bb4d430d15e 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -170,6 +170,7 @@ struct mxc_isi_plat_data { enum model model; unsigned int num_ports; unsigned int num_channels; + unsigned int num_vc; /* Number of VCs, 0 = no VC support */ unsigned int reg_offset; const struct mxc_isi_ier_reg *ier_reg; const struct mxc_isi_set_thd *set_thd; @@ -378,6 +379,7 @@ void mxc_isi_channel_unchain(struct mxc_isi_pipe *pipe); void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, enum mxc_isi_input_id input, + unsigned int vc, const struct v4l2_area *in_size, const struct v4l2_area *scale, const struct v4l2_rect *crop, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c index e07eebae9e2b..6aa760ce3605 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c @@ -305,6 +305,7 @@ static void mxc_isi_channel_set_panic_threshold(struct mxc_isi_pipe *pipe) static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, enum mxc_isi_input_id input, + unsigned int vc, bool bypass) { u32 val; @@ -316,6 +317,10 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, CHNL_CTRL_SRC_TYPE_MASK | CHNL_CTRL_MIPI_VC_ID_MASK | CHNL_CTRL_SRC_INPUT_MASK); + /* Clear the VC_ID_1 bit on platforms supporting more than 4 VCs. */ + if (pipe->isi->pdata->num_vc > 4) + val &= ~CHNL_CTRL_VC_ID_1_MASK; + /* * If no scaling or color space conversion is needed, bypass the * channel. @@ -342,7 +347,14 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, } else { val |= CHNL_CTRL_SRC_TYPE(CHNL_CTRL_SRC_TYPE_DEVICE); val |= CHNL_CTRL_SRC_INPUT(input); - val |= CHNL_CTRL_MIPI_VC_ID(0); /* FIXME: For CSI-2 only */ + val |= CHNL_CTRL_MIPI_VC_ID(vc); /* FIXME: For CSI-2 only */ + + /* + * On platforms with more than 4 VCs (i.MX95), the VC ID is + * split across VC_ID_0 (bits 7:6) and VC_ID_1 (bit 16). + */ + if (pipe->isi->pdata->num_vc > 4) + val |= CHNL_CTRL_VC_ID_1(vc >> 2); } mxc_isi_write(pipe, CHNL_CTRL, val); @@ -352,6 +364,7 @@ static void mxc_isi_channel_set_control(struct mxc_isi_pipe *pipe, void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, enum mxc_isi_input_id input, + unsigned int vc, const struct v4l2_area *in_size, const struct v4l2_area *scale, const struct v4l2_rect *crop, @@ -378,7 +391,7 @@ void mxc_isi_channel_config(struct mxc_isi_pipe *pipe, mxc_isi_channel_set_panic_threshold(pipe); /* Channel control */ - mxc_isi_channel_set_control(pipe, input, csc_bypass && scaler_bypass); + mxc_isi_channel_set_control(pipe, input, vc, csc_bypass && scaler_bypass); } void mxc_isi_channel_set_input_format(struct mxc_isi_pipe *pipe, diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c index de398b232d74..b729eecad0fe 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c @@ -144,7 +144,7 @@ static void mxc_isi_m2m_device_run(void *priv) .height = ctx->queues.cap.format.height, }; - mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, + mxc_isi_channel_config(m2m->pipe, MXC_ISI_INPUT_MEM, 0, &in_size, &scale, &crop, ctx->queues.out.info->encoding, ctx->queues.cap.info->encoding); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 2d0843c86534..16085f23bc0b 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -232,6 +232,47 @@ static inline struct mxc_isi_pipe *to_isi_pipe(struct v4l2_subdev *sd) return container_of(sd, struct mxc_isi_pipe, sd); } +static int mxc_isi_get_vc(struct mxc_isi_pipe *pipe) +{ + struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; + struct device *dev = pipe->isi->dev; + struct v4l2_mbus_frame_desc fd = { }; + unsigned int source_pad = xbar->num_sinks + pipe->id; + unsigned int num_vcs; + unsigned int i; + int ret; + + ret = v4l2_subdev_call(&xbar->sd, pad, get_frame_desc, + source_pad, &fd); + if (ret < 0) { + dev_err(dev, "Failed to get source frame desc from pad %u\n", + source_pad); + return ret; + } + + /* Find stream 0 in the frame descriptor. */ + for (i = 0; i < fd.num_entries; i++) { + if (fd.entry[i].stream == 0) + break; + } + + if (i == fd.num_entries) { + dev_err(dev, "Failed to find stream from source frame desc\n"); + return -EPIPE; + } + + num_vcs = pipe->isi->pdata->num_vc ? : 1; + + /* Check virtual channel range. */ + if (fd.entry[i].bus.csi2.vc >= num_vcs) { + dev_err(dev, "Virtual channel %u exceeds maximum %u\n", + fd.entry[i].bus.csi2.vc, num_vcs - 1); + return -EPIPE; + } + + return fd.entry[i].bus.csi2.vc; +} + int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) { struct mxc_isi_crossbar *xbar = &pipe->isi->crossbar; @@ -246,6 +287,7 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) struct v4l2_rect crop; u32 input; int ret; + int vc; /* * Find the connected input by inspecting the crossbar switch routing @@ -280,8 +322,12 @@ int mxc_isi_pipe_enable(struct mxc_isi_pipe *pipe) v4l2_subdev_unlock_state(state); + vc = mxc_isi_get_vc(pipe); + if (vc < 0) + return vc; + /* Configure the ISI channel. */ - mxc_isi_channel_config(pipe, input, &in_size, &scale, &crop, + mxc_isi_channel_config(pipe, input, vc, &in_size, &scale, &crop, sink_info->encoding, src_info->encoding); mxc_isi_channel_enable(pipe); diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h index 1b65eccdf0da..e795f4daf3ff 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-regs.h @@ -6,6 +6,7 @@ #ifndef __IMX8_ISI_REGS_H__ #define __IMX8_ISI_REGS_H__ +#include #include /* ISI Registers Define */ @@ -19,9 +20,14 @@ #define CHNL_CTRL_CHAIN_BUF_NO_CHAIN 0 #define CHNL_CTRL_CHAIN_BUF_2_CHAIN 1 #define CHNL_CTRL_SW_RST BIT(24) -#define CHNL_CTRL_BLANK_PXL(n) ((n) << 16) -#define CHNL_CTRL_BLANK_PXL_MASK GENMASK(23, 16) -#define CHNL_CTRL_MIPI_VC_ID(n) ((n) << 6) +/* + * CHNL_CTRL_BLANK_PXL: i.MX8{QM,QXP} only + * CHNL_CTRL_VC_ID_1, CHNL_CTRL_VC_ID_1_MASK: i.MX95 only + */ +#define CHNL_CTRL_BLANK_PXL(n) FIELD_PREP(GENMASK(23, 16), (n)) +#define CHNL_CTRL_VC_ID_1(n) FIELD_PREP(BIT(16), (n)) +#define CHNL_CTRL_VC_ID_1_MASK BIT(16) +#define CHNL_CTRL_MIPI_VC_ID(n) FIELD_PREP(GENMASK(7, 6), (n)) #define CHNL_CTRL_MIPI_VC_ID_MASK GENMASK(7, 6) #define CHNL_CTRL_SRC_TYPE(n) ((n) << 4) #define CHNL_CTRL_SRC_TYPE_MASK BIT(4) -- cgit From 795bd2863591f2fee33fab8f10cf9c383c94342e Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 20 Jul 2026 11:34:03 +0800 Subject: media: nxp: imx8-isi: Fix stream ID validation bypass in crossbar routing The crossbar routing validation has a critical bug where it validates the wrong routing table, allowing userspace to bypass validation entirely. The __mxc_isi_crossbar_set_routing() function is called to validate and apply a new routing table from userspace. However, the validation loop iterates over state->routing (the currently active routing table) instead of the routing parameter (the new table being validated): for_each_active_route(&state->routing, route) { This means userspace can submit any invalid routing configuration and it will pass validation as long as the currently active routing is valid. This is a security issue as it allows userspace to configure routes that violate hardware constraints, potentially causing undefined hardware behavior. Fix by validating the routing table that will actually be applied. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260720-isi-v2-1-45845bc5d4fa@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 90b54402301f..e04e2c2e9906 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -107,7 +107,7 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, return ret; /* The memory input can be routed to the first pipeline only. */ - for_each_active_route(&state->routing, route) { + for_each_active_route(routing, route) { if (route->sink_pad == xbar->num_sinks - 1 && route->source_pad != xbar->num_sinks) { dev_dbg(xbar->isi->dev, -- cgit From 162a2a2de63b8028cc75d1197bc37b6cf951c13c Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 20 Jul 2026 11:34:04 +0800 Subject: media: nxp: imx8-isi: Add stream ID validation for crossbar routing Add validation to enforce hardware constraints that were previously missing in the crossbar routing configuration: - SOURCE stream must be 0 (ISI pipes are hardcoded to stream 0) This check complements the existing memory input validation and ensures that all routing configurations respect hardware limitations. Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260720-isi-v2-2-45845bc5d4fa@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index e04e2c2e9906..9f0231ca47a3 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -106,8 +106,20 @@ static int __mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, if (ret) return ret; - /* The memory input can be routed to the first pipeline only. */ + /* + * Validate routes against hardware constraints: + * - SOURCE stream must be 0 (pipes are hardcoded to stream 0) + * - Memory input can only route to the first pipeline + */ for_each_active_route(routing, route) { + if (route->source_stream != 0) { + dev_dbg(xbar->isi->dev, + "route to pipe %u must use source_stream=0, got %u\n", + route->source_pad - xbar->num_sinks, + route->source_stream); + return -ENXIO; + } + if (route->sink_pad == xbar->num_sinks - 1 && route->source_pad != xbar->num_sinks) { dev_dbg(xbar->isi->dev, -- cgit From 4640ec1cb0121473867e7a6850c7449340dcd428 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Mon, 20 Jul 2026 11:34:07 +0800 Subject: media: nxp: imx8-isi: Correct color map between V4L2 and ISI Fix the ISI input format for the color map V4L2_PIX_FMT_XBGR32 in memory-to-memory mode. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Tested-by: Laurent Pinchart Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260720-isi-v2-5-45845bc5d4fa@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index fe4adfa3a1f0..056603055c81 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = { .fourcc = V4L2_PIX_FMT_XBGR32, .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT | MXC_ISI_VIDEO_M2M_CAP, - .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8, .isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888, .mem_planes = 1, .color_planes = 1, -- cgit From 77e60a2c5d824ad2d493f53dc17137ae065753fe Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 28 Jul 2026 14:58:56 +0800 Subject: media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks Use BIT_ULL() instead of BIT() for u64 stream masks to avoid incorrect results on 32-bit architectures when stream IDs are 32 or greater. Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver") Cc: stable@vger.kernel.org Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260728-isi-v5-1-1d22ab91602a@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 9f0231ca47a3..9db9c6e55c2c 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -157,10 +157,10 @@ mxc_isi_crossbar_xlate_streams(struct mxc_isi_crossbar *xbar, */ for_each_active_route(&state->routing, route) { if (route->source_pad != source_pad || - !(source_streams & BIT(route->source_stream))) + !(source_streams & BIT_ULL(route->source_stream))) continue; - sink_streams |= BIT(route->sink_stream); + sink_streams |= BIT_ULL(route->sink_stream); sink_pad = route->sink_pad; } -- cgit From 2be91091797df756d3b7b6ab5cb64295d2cdadb3 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 28 Jul 2026 14:58:57 +0800 Subject: media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams The ISI crossbar needs to properly enable multiple streams from different virtual channels on the same input pad. Currently only the first stream gets enabled in hardware, subsequent streams are silently ignored. The driver uses a single enable_count per input to track the input state. When enable_count is non-zero, the code assumes the input is already active and skips calling v4l2_subdev_enable_streams() for additional streams: Call 1: enable_streams(stream 0) -> enable_count == 0, enable gasket and stream 0 in hardware -> enable_count = 1 Call 2: enable_streams(stream 1) -> enable_count == 1, skip hardware enable -> enable_count = 2 -> stream 1 never gets enabled Similarly on disable, when enable_count reaches zero, ALL streams are disabled regardless of which streams are actually still active. Implement per-stream state tracking by storing the input index and stream mask in the mxc_isi_pipe structure. On enable, record which input and stream the pipe is receiving. On disable, clear the pipe's record and check if any other pipe is still using the same input stream before actually disabling it. Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Reviewed-by: Frank Li Link: https://patch.msgid.link/20260728-isi-v5-2-1d22ab91602a@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 10 ++- .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 90 ++++++++++++++++------ .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 1 + 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h index 9bb4d430d15e..58b47335afc7 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h @@ -186,7 +186,7 @@ struct mxc_isi_dma_buffer { }; struct mxc_isi_input { - unsigned int enable_count; + u64 enabled_streams; }; struct mxc_isi_crossbar { @@ -259,6 +259,14 @@ struct mxc_isi_pipe { u8 acquired_res; u8 chained_res; bool chained; + + unsigned int input; + /* + * Stream on the connected crossbar input, expressed as a bitmask. Zero + * when the pipeline is disabled, a single bit set when the pipeline is + * enabled (as each pipeline processes a single stream). + */ + u64 input_stream; }; struct mxc_isi_m2m { diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c index 9db9c6e55c2c..7bb1335f2110 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c @@ -330,11 +330,32 @@ static int mxc_isi_crossbar_set_routing(struct v4l2_subdev *sd, return __mxc_isi_crossbar_set_routing(sd, state, routing); } +/* + * Check if a stream on a sink pad is in used by any of the ISI pipelines. The + * sink_streams argument is a bitmask that must have a single bit set (enforced + * by mxc_isi_crossbar_xlate_streams() translating the single stream mask of the + * pipeline to a single stream on the crossbar input side). + */ +static bool mxc_isi_crossbar_stream_in_use(const struct mxc_isi_crossbar *xbar, + unsigned int sink_pad, u64 sink_streams) +{ + for (unsigned int i = 0; i < xbar->isi->pdata->num_channels; ++i) { + const struct mxc_isi_pipe *pipe = &xbar->isi->pipes[i]; + + if (pipe->input == sink_pad && + pipe->input_stream == sink_streams) + return true; + } + + return false; +} + static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, u32 pad, u64 streams_mask) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks]; struct v4l2_subdev *remote_sd; struct mxc_isi_input *input; u64 sink_streams; @@ -351,29 +372,44 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd, input = &xbar->inputs[sink_pad]; /* - * TODO: Track per-stream enable counts to support multiplexed - * streams. + * Check if any other pipe already receives the same input stream. + * If so, just record this pipe's usage and return. */ - if (!input->enable_count) { + if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams)) { + pipe->input = sink_pad; + pipe->input_stream = sink_streams; + return 0; + } + + /* Enable the gasket when the first stream is enabled for this input. */ + if (!input->enabled_streams) { ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd, remote_pad, sink_pad); if (ret) return ret; + } - ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, - sink_streams); - if (ret) { - dev_err(xbar->isi->dev, - "failed to enable streams 0x%llx on '%s':%u: %d\n", - sink_streams, remote_sd->name, remote_pad, ret); - mxc_isi_crossbar_gasket_disable(xbar, sink_pad); - return ret; - } + ret = v4l2_subdev_enable_streams(remote_sd, remote_pad, sink_streams); + if (ret) { + dev_err(xbar->isi->dev, + "failed to enable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); + goto err_gasket_disable; } - input->enable_count++; + input->enabled_streams |= sink_streams; + + /* Record the input and stream for this pipe. */ + pipe->input = sink_pad; + pipe->input_stream = sink_streams; return 0; + +err_gasket_disable: + if (!input->enabled_streams) + mxc_isi_crossbar_gasket_disable(xbar, sink_pad); + + return ret; } static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, @@ -381,6 +417,7 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, u32 pad, u64 streams_mask) { struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd); + struct mxc_isi_pipe *pipe = &xbar->isi->pipes[pad - xbar->num_sinks]; struct v4l2_subdev *remote_sd; struct mxc_isi_input *input; u64 sink_streams; @@ -396,18 +433,27 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd, input = &xbar->inputs[sink_pad]; - input->enable_count--; + /* Clear the input and stream for this pipe. */ + pipe->input = UINT_MAX; + pipe->input_stream = 0; - if (!input->enable_count) { - ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, - sink_streams); - if (ret) - dev_err(xbar->isi->dev, - "failed to disable streams 0x%llx on '%s':%u: %d\n", - sink_streams, remote_sd->name, remote_pad, ret); + /* + * Check if any other pipe receives the same input stream. If so we + * can't disable it yet, so return immediately. + */ + if (mxc_isi_crossbar_stream_in_use(xbar, sink_pad, sink_streams)) + return 0; + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad, sink_streams); + if (ret) + dev_err(xbar->isi->dev, + "failed to disable streams 0x%llx on '%s':%u: %d\n", + sink_streams, remote_sd->name, remote_pad, ret); + + input->enabled_streams &= ~sink_streams; + + if (!input->enabled_streams) mxc_isi_crossbar_gasket_disable(xbar, sink_pad); - } return ret; } diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index 16085f23bc0b..c0ec59856374 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -816,6 +816,7 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id) pipe->acquired_res = 0; pipe->chained_res = 0; pipe->chained = false; + pipe->input = UINT_MAX; sd = &pipe->sd; v4l2_subdev_init(sd, &mxc_isi_pipe_subdev_ops); -- cgit From 383d13eaf17ba349e4466bf46e9fc8c71459088d Mon Sep 17 00:00:00 2001 From: Laurentiu Palcu Date: Tue, 28 Jul 2026 14:58:58 +0800 Subject: media: nxp: imx8-isi: Add 16-bit raw Bayer format support Add support for 16-bit raw Bayer formats (SBGGR16, SGBRG16, SGRBG16, SRGGB16) to both the pipeline subdev and video capture interface. These formats are used by image sensors that output 16-bit raw data, enabling the ISI to capture full dynamic range from such sensors. Signed-off-by: Laurentiu Palcu Reviewed-by: Frank Li Reviewed-by: Laurent Pinchart Signed-off-by: Guoniu Zhou Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260728-isi-v5-3-1d22ab91602a@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-pipe.c | 30 +++++++++++++++ .../media/platform/nxp/imx8-isi/imx8-isi-video.c | 45 ++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c index c0ec59856374..934f7b356258 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c @@ -83,6 +83,12 @@ static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = { .pads = BIT(MXC_ISI_PIPE_PAD_SINK) | BIT(MXC_ISI_PIPE_PAD_SOURCE), .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y16_1X16, + .output = MEDIA_BUS_FMT_Y16_1X16, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .output = MEDIA_BUS_FMT_SBGGR8_1X8, @@ -179,6 +185,30 @@ static const struct mxc_isi_bus_format_info mxc_isi_bus_formats[] = { .pads = BIT(MXC_ISI_PIPE_PAD_SINK) | BIT(MXC_ISI_PIPE_PAD_SOURCE), .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .output = MEDIA_BUS_FMT_SBGGR16_1X16, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .output = MEDIA_BUS_FMT_SGBRG16_1X16, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .output = MEDIA_BUS_FMT_SGRBG16_1X16, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .output = MEDIA_BUS_FMT_SRGGB16_1X16, + .pads = BIT(MXC_ISI_PIPE_PAD_SINK) + | BIT(MXC_ISI_PIPE_PAD_SOURCE), + .encoding = MXC_ISI_ENC_RAW, }, /* JPEG */ { diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index 056603055c81..a6e044056762 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -212,6 +212,15 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = { .color_planes = 1, .depth = { 16 }, .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_Y16_1X16, + .fourcc = V4L2_PIX_FMT_Y16, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, }, { .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8, .fourcc = V4L2_PIX_FMT_SBGGR8, @@ -356,6 +365,42 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = { .color_planes = 1, .depth = { 16 }, .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16, + .fourcc = V4L2_PIX_FMT_SBGGR16, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16, + .fourcc = V4L2_PIX_FMT_SGBRG16, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16, + .fourcc = V4L2_PIX_FMT_SGRBG16, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, + }, { + .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16, + .fourcc = V4L2_PIX_FMT_SRGGB16, + .type = MXC_ISI_VIDEO_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RAW16, + .mem_planes = 1, + .color_planes = 1, + .depth = { 16 }, + .encoding = MXC_ISI_ENC_RAW, }, /* JPEG */ { -- cgit From 06c70039c75969e9065a6c0adcdf5178866c0ac1 Mon Sep 17 00:00:00 2001 From: Guoniu Zhou Date: Tue, 28 Jul 2026 14:58:59 +0800 Subject: media: nxp: imx8-isi: Add additional 32-bit RGB format support Add support for additional 32-bit RGB pixel formats (BGRA32, RGBA32, BGRX32, RGBX32, ARGB2101010). Formats with alpha channel (BGRA32, RGBA32) only support capture as ISI ignores alpha bits when reading from memory. Signed-off-by: Robert Chiras Reviewed-by: Frank Li Tested-by: Laurent Pinchart Signed-off-by: Guoniu Zhou Reviewed-by: Laurent Pinchart Reviewed-by: Loic Poulain Link: https://patch.msgid.link/20260728-isi-v5-4-1d22ab91602a@oss.nxp.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../media/platform/nxp/imx8-isi/imx8-isi-video.c | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c index a6e044056762..f45c2aae59ce 100644 --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c @@ -166,6 +166,57 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = { .color_planes = 1, .depth = { 32 }, .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_BGRA32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBA8888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_RGBA32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_ABGR8888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_BGRX32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGBX8, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBX888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_RGBX32, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_XBGR888, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, + }, { + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24, + .fourcc = V4L2_PIX_FMT_ARGB2101010, + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT + | MXC_ISI_VIDEO_M2M_CAP, + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_A2RGB10, + .isi_out_format = CHNL_IMG_CTRL_FORMAT_A2RGB10, + .mem_planes = 1, + .color_planes = 1, + .depth = { 32 }, + .encoding = MXC_ISI_ENC_RGB, }, /* * RAW formats -- cgit From 506742ca85b544fc22a3f448c9c54290aba5035d Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 30 Apr 2026 11:09:27 +0100 Subject: media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs Document the FCPVD blocks present on the RZ/T2H and RZ/N2H SoCs. The FCPVD implementation on these SoCs is identical to that found on the RZ/G2L family. Update the schema to disallow the "resets" property for these SoCs, reflecting the absence of a reset control for the FCPVD instance. Signed-off-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260430100929.1088281-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/renesas,fcp.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml index 5e11ae0ee456..cbb16a7a5481 100644 --- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml +++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml @@ -34,6 +34,8 @@ properties: - renesas,r9a09g047-fcpvd # RZ/G3E - renesas,r9a09g056-fcpvd # RZ/V2N - renesas,r9a09g057-fcpvd # RZ/V2H(P) + - renesas,r9a09g077-fcpvd # RZ/T2H + - renesas,r9a09g087-fcpvd # RZ/N2H - const: renesas,fcpv # Generic FCP for VSP fallback reg: @@ -66,7 +68,6 @@ required: - reg - clocks - power-domains - - resets additionalProperties: false @@ -83,6 +84,8 @@ allOf: - renesas,r9a09g047-fcpvd - renesas,r9a09g056-fcpvd - renesas,r9a09g057-fcpvd + - renesas,r9a09g077-fcpvd + - renesas,r9a09g087-fcpvd then: properties: clocks: @@ -94,6 +97,19 @@ allOf: clocks: maxItems: 1 clock-names: false + - if: + properties: + compatible: + contains: + enum: + - renesas,r9a09g077-fcpvd + - renesas,r9a09g087-fcpvd + then: + properties: + resets: false + else: + required: + - resets examples: # R8A7795 (R-Car H3) FCP for VSP-D1 -- cgit From e98a1f15787430ed131207e83ba2f2b35dafafe3 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 30 Apr 2026 11:09:28 +0100 Subject: media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs Document the VSP2 blocks present on the RZ/T2H and RZ/N2H SoCs. The VSP2 implementation on these SoCs is identical to that on the RZ/G2L SoC. Update the schema to disallow the "resets" property for these SoCs to reflect the hardware integration. Signed-off-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260430100929.1088281-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- .../devicetree/bindings/media/renesas,vsp1.yaml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml index 803358780f01..a28632165804 100644 --- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml +++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml @@ -29,6 +29,8 @@ properties: - renesas,r9a09g047-vsp2 # RZ/G3E - renesas,r9a09g056-vsp2 # RZ/V2N - renesas,r9a09g057-vsp2 # RZ/V2H(P) + - renesas,r9a09g077-vsp2 # RZ/T2H + - renesas,r9a09g087-vsp2 # RZ/N2H - const: renesas,r9a07g044-vsp2 # RZ/G2L fallback reg: @@ -67,7 +69,6 @@ required: - interrupts - clocks - power-domains - - resets additionalProperties: false @@ -101,6 +102,20 @@ allOf: maxItems: 1 clock-names: false + - if: + properties: + compatible: + contains: + enum: + - renesas,r9a09g077-vsp2 + - renesas,r9a09g087-vsp2 + then: + properties: + resets: false + else: + required: + - resets + examples: # R8A7790 (R-Car H2) VSP1-S - | -- cgit From 284a6530a4e9dd19edaaafdd0398945fc54b0601 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Thu, 30 Apr 2026 11:09:29 +0100 Subject: media: renesas: vsp1: Make reset control optional to support platforms without a reset line Switch the VSP1 driver to use devm_reset_control_get_optional_shared() when requesting its reset control. Some newer Renesas SoCs integrating VSP1 such as RZ/T2H do not provide a reset line for the VSP IP block. Signed-off-by: Lad Prabhakar Reviewed-by: Laurent Pinchart Link: https://patch.msgid.link/20260430100929.1088281-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 627b5046fa80..605fac57bd93 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -947,7 +947,7 @@ static int vsp1_probe(struct platform_device *pdev) if (irq < 0) return irq; - vsp1->rstc = devm_reset_control_get_shared(&pdev->dev, NULL); + vsp1->rstc = devm_reset_control_get_optional_shared(&pdev->dev, NULL); if (IS_ERR(vsp1->rstc)) return dev_err_probe(&pdev->dev, PTR_ERR(vsp1->rstc), "failed to get reset control\n"); -- cgit From 31152f5b0f8719f92063b8c6196cd5e34106c73d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 12 May 2026 02:56:34 +0300 Subject: media: renesas: vsp1: Declare index variables in for loop statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using loop indices outside of the loop is a source of out-of-bounds accesses and other bugs. It is important to carefully review those usages. To make them stand out, declare all loop index variables that are not used outside of the loop inside the loop statement. No functional change intended. Reviewed-by: Niklas Söderlund Link: https://patch.msgid.link/20260511235637.3468558-11-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Laurent Pinchart Signed-off-by: Hans Verkuil --- drivers/media/platform/renesas/vsp1/vsp1_brx.c | 7 ++-- drivers/media/platform/renesas/vsp1/vsp1_clu.c | 3 +- drivers/media/platform/renesas/vsp1/vsp1_dl.c | 12 +++---- drivers/media/platform/renesas/vsp1/vsp1_drm.c | 14 +++----- drivers/media/platform/renesas/vsp1/vsp1_drv.c | 42 +++++++++-------------- drivers/media/platform/renesas/vsp1/vsp1_entity.c | 4 +-- drivers/media/platform/renesas/vsp1/vsp1_hgo.c | 7 ++-- drivers/media/platform/renesas/vsp1/vsp1_hgt.c | 13 +++---- drivers/media/platform/renesas/vsp1/vsp1_lut.c | 3 +- drivers/media/platform/renesas/vsp1/vsp1_pipe.c | 5 ++- drivers/media/platform/renesas/vsp1/vsp1_video.c | 34 +++++++----------- drivers/media/platform/renesas/vsp1/vsp1_wpf.c | 8 ++--- 12 files changed, 56 insertions(+), 96 deletions(-) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c index 325be30836d7..360a42502947 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c @@ -155,9 +155,7 @@ static int brx_set_format(struct v4l2_subdev *subdev, /* Propagate the format code to all pads. */ if (fmt->pad == BRX_PAD_SINK(0)) { - unsigned int i; - - for (i = 0; i <= brx->entity.source_pad; ++i) { + for (unsigned int i = 0; i <= brx->entity.source_pad; ++i) { format = v4l2_subdev_state_get_format(state, i); format->code = fmt->format.code; } @@ -271,7 +269,6 @@ static void brx_configure_stream(struct vsp1_entity *entity, struct vsp1_brx *brx = to_brx(&entity->subdev); struct v4l2_mbus_framefmt *format; unsigned int flags; - unsigned int i; format = v4l2_subdev_state_get_format(state, brx->entity.source_pad); @@ -315,7 +312,7 @@ static void brx_configure_stream(struct vsp1_entity *entity, VI6_BRU_ROP_CROP(VI6_ROP_NOP) | VI6_BRU_ROP_AROP(VI6_ROP_NOP)); - for (i = 0; i < brx->entity.source_pad; ++i) { + for (unsigned int i = 0; i < brx->entity.source_pad; ++i) { bool premultiplied = false; u32 ctrl = 0; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c index a6e4bcab5101..4b7d07d730da 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c @@ -43,14 +43,13 @@ static inline void vsp1_clu_write(struct vsp1_clu *clu, static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl) { struct vsp1_dl_body *dlb; - unsigned int i; dlb = vsp1_dl_body_get(clu->pool); if (!dlb) return -ENOMEM; vsp1_dl_body_write(dlb, VI6_CLU_ADDR, 0); - for (i = 0; i < CLU_SIZE; ++i) + for (unsigned int i = 0; i < CLU_SIZE; ++i) vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]); scoped_guard(spinlock_irq, &clu->lock) { diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c index 3dc74fed91dc..6430f2ec8b32 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c @@ -257,7 +257,6 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, { struct vsp1_dl_body_pool *pool; size_t dlb_size; - unsigned int i; pool = kzalloc_obj(*pool); if (!pool) @@ -291,7 +290,7 @@ vsp1_dl_body_pool_create(struct vsp1_device *vsp1, unsigned int num_bodies, spin_lock_init(&pool->lock); INIT_LIST_HEAD(&pool->free); - for (i = 0; i < num_bodies; ++i) { + for (unsigned int i = 0; i < num_bodies; ++i) { struct vsp1_dl_body *dlb = &pool->bodies[i]; dlb->pool = pool; @@ -426,7 +425,6 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, unsigned int num_cmds) { struct vsp1_dl_cmd_pool *pool; - unsigned int i; size_t cmd_size; pool = kzalloc_obj(*pool); @@ -457,7 +455,7 @@ vsp1_dl_cmd_pool_create(struct vsp1_device *vsp1, enum vsp1_extcmd_type type, return NULL; } - for (i = 0; i < num_cmds; ++i) { + for (unsigned int i = 0; i < num_cmds; ++i) { struct vsp1_dl_ext_cmd *cmd = &pool->cmds[i]; size_t cmd_offset = i * cmd_size; /* data_offset must be 16 byte aligned for DMA. */ @@ -1046,7 +1044,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm) /* Hardware Setup */ void vsp1_dlm_setup(struct vsp1_device *vsp1) { - unsigned int i; u32 ctrl = (256 << VI6_DL_CTRL_AR_WAIT_SHIFT) | VI6_DL_CTRL_DC2 | VI6_DL_CTRL_DC1 | VI6_DL_CTRL_DC0 | VI6_DL_CTRL_DLE; @@ -1054,7 +1051,7 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1) | VI6_DL_EXT_CTRL_DLPRI | VI6_DL_EXT_CTRL_EXT; if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) { - for (i = 0; i < vsp1->info->wpf_count; ++i) + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) vsp1_write(vsp1, VI6_DL_EXT_CTRL(i), ext_dl); } @@ -1092,7 +1089,6 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, { struct vsp1_dl_manager *dlm; size_t header_size; - unsigned int i; dlm = devm_kzalloc(vsp1->dev, sizeof(*dlm), GFP_KERNEL); if (!dlm) @@ -1128,7 +1124,7 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1, if (!dlm->pool) return NULL; - for (i = 0; i < prealloc; ++i) { + for (unsigned int i = 0; i < prealloc; ++i) { struct vsp1_dl_list *dl; dl = vsp1_dl_list_alloc(dlm); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c index f6fbd3475329..9cd5c025d2be 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c @@ -419,13 +419,12 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, struct vsp1_entity *uif; bool use_uif = false; struct vsp1_brx *brx; - unsigned int i; int ret; /* Count the number of enabled inputs and sort them by Z-order. */ pipe->num_inputs = 0; - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rpf = vsp1->rpf[i]; unsigned int j; @@ -457,7 +456,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1, brx = to_brx(&pipe->brx->subdev); /* Setup the RPF input pipeline for every enabled input. */ - for (i = 0; i < pipe->brx->source_pad; ++i) { + for (unsigned int i = 0; i < pipe->brx->source_pad; ++i) { struct vsp1_rwpf *rpf = inputs[i]; if (!rpf) { @@ -732,7 +731,6 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) struct vsp1_device *vsp1 = dev_get_drvdata(dev); struct vsp1_drm_pipeline *drm_pipe; struct vsp1_pipeline *pipe; - unsigned int i; int ret; if (pipe_index >= vsp1->info->lif_count) @@ -748,7 +746,7 @@ int vsp1_du_disable(struct device *dev, unsigned int pipe_index) if (ret == -ETIMEDOUT) dev_err(vsp1->dev, "DRM pipeline stop timeout\n"); - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + for (unsigned int i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { struct vsp1_rwpf *rpf = pipe->inputs[i]; if (!rpf) @@ -964,8 +962,6 @@ EXPORT_SYMBOL_GPL(vsp1_du_unmap_sg); int vsp1_drm_init(struct vsp1_device *vsp1) { - unsigned int i; - vsp1->drm = devm_kzalloc(vsp1->dev, sizeof(*vsp1->drm), GFP_KERNEL); if (!vsp1->drm) return -ENOMEM; @@ -973,7 +969,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1) mutex_init(&vsp1->drm->lock); /* Create one DRM pipeline per LIF. */ - for (i = 0; i < vsp1->info->lif_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) { struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i]; struct vsp1_pipeline *pipe = &drm_pipe->pipe; @@ -1010,7 +1006,7 @@ int vsp1_drm_init(struct vsp1_device *vsp1) } /* Disable all RPFs initially. */ - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *input = vsp1->rpf[i]; INIT_LIST_HEAD(&input->entity.list_pipe); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c index 605fac57bd93..762a9e776a94 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c @@ -51,10 +51,9 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) VI6_WPF_IRQ_STA_UND; struct vsp1_device *vsp1 = data; irqreturn_t ret = IRQ_NONE; - unsigned int i; u32 status; - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; if (wpf == NULL) @@ -103,7 +102,6 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1, { struct media_entity *entity = &sink->subdev.entity; struct vsp1_entity *source; - unsigned int pad; int ret; list_for_each_entry(source, &vsp1->entities, list_dev) { @@ -123,7 +121,7 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1, source->index == sink->index ? MEDIA_LNK_FL_ENABLED : 0; - for (pad = 0; pad < entity->num_pads; ++pad) { + for (unsigned int pad = 0; pad < entity->num_pads; ++pad) { if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK)) continue; @@ -144,7 +142,6 @@ static int vsp1_create_sink_links(struct vsp1_device *vsp1, static int vsp1_uapi_create_links(struct vsp1_device *vsp1) { struct vsp1_entity *entity; - unsigned int i; int ret; list_for_each_entry(entity, &vsp1->entities, list_dev) { @@ -177,7 +174,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) return ret; } - for (i = 0; i < vsp1->info->lif_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) { if (!vsp1->lif[i]) continue; @@ -189,7 +186,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) return ret; } - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rpf = vsp1->rpf[i]; ret = media_create_pad_link(&rpf->video->video.entity, 0, @@ -201,7 +198,7 @@ static int vsp1_uapi_create_links(struct vsp1_device *vsp1) return ret; } - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { /* * Connect the video device to the WPF. All connections are * immutable. @@ -253,7 +250,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) struct media_device *mdev = &vsp1->media_dev; struct v4l2_device *vdev = &vsp1->v4l2_dev; struct vsp1_entity *entity; - unsigned int i; int ret; mdev->dev = vsp1->dev; @@ -365,7 +361,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) * enabled skip the LIFs, even when present. */ if (!vsp1->info->uapi) { - for (i = 0; i < vsp1->info->lif_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) { struct vsp1_lif *lif; lif = vsp1_lif_create(vsp1, i); @@ -389,7 +385,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rpf; rpf = vsp1_rpf_create(vsp1, i); @@ -423,7 +419,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->info->uds_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->uds_count; ++i) { struct vsp1_uds *uds; uds = vsp1_uds_create(vsp1, i); @@ -436,7 +432,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->info->uif_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->uif_count; ++i) { struct vsp1_uif *uif; uif = vsp1_uif_create(vsp1, i); @@ -449,7 +445,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uif->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf; wpf = vsp1_wpf_create(vsp1, i); @@ -543,11 +539,10 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index) static int vsp1_device_init(struct vsp1_device *vsp1) { - unsigned int i; int ret; /* Reset any channel that might be running. */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { ret = vsp1_reset_wpf(vsp1, i); if (ret < 0) return ret; @@ -556,13 +551,13 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); - for (i = 0; i < vsp1->info->rpf_count; ++i) + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < vsp1->info->uds_count; ++i) + for (unsigned int i = 0; i < vsp1->info->uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < vsp1->info->uif_count; ++i) + for (unsigned int i = 0; i < vsp1->info->uif_count; ++i) vsp1_write(vsp1, VI6_DPR_UIF_ROUTE(i), VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -587,11 +582,9 @@ static int vsp1_device_init(struct vsp1_device *vsp1) static void vsp1_mask_all_interrupts(struct vsp1_device *vsp1) { - unsigned int i; - - for (i = 0; i < vsp1->info->lif_count; ++i) + for (unsigned int i = 0; i < vsp1->info->lif_count; ++i) vsp1_write(vsp1, VI6_DISP_IRQ_ENB(i), 0); - for (i = 0; i < vsp1->info->wpf_count; ++i) + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) vsp1_write(vsp1, VI6_WPF_IRQ_ENB(i), 0); } @@ -891,7 +884,6 @@ static const struct vsp1_device_info rzg2l_vsp2_device_info = { static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) { const struct vsp1_device_info *info; - unsigned int i; u32 model; u32 soc; @@ -909,7 +901,7 @@ static const struct vsp1_device_info *vsp1_lookup_info(struct vsp1_device *vsp1) model = vsp1->version & VI6_IP_VERSION_MODEL_MASK; soc = vsp1->version & VI6_IP_VERSION_SOC_MASK; - for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { + for (unsigned int i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) { info = &vsp1_device_infos[i]; if (model == info->version && (!info->soc || soc == info->soc)) diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c index 2ae2a573f0de..26b21559878d 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c @@ -372,10 +372,8 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev, static int vsp1_entity_init_state(struct v4l2_subdev *subdev, struct v4l2_subdev_state *sd_state) { - unsigned int pad; - /* Initialize all pad formats with default values. */ - for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { + for (unsigned int pad = 0; pad < subdev->entity.num_pads - 1; ++pad) { struct v4l2_subdev_format format = { .pad = pad, .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c index 0ef512e3a94b..d3eaa7c2d595 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c @@ -42,7 +42,6 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity) { struct vsp1_hgo *hgo = to_hgo(&entity->subdev); struct vsp1_histogram_buffer *buf; - unsigned int i; size_t size; u32 *data; @@ -56,7 +55,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity) *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN); *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); - for (i = 0; i < 256; ++i) { + for (unsigned int i = 0; i < 256; ++i) { vsp1_write(hgo->histo.entity.vsp1, VI6_HGO_EXT_HIST_ADDR, i); *data++ = vsp1_hgo_read(hgo, VI6_HGO_EXT_HIST_DATA); @@ -67,7 +66,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity) *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_MAXMIN); *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); - for (i = 0; i < 64; ++i) + for (unsigned int i = 0; i < 64; ++i) *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i)); size = (2 + 64) * sizeof(u32); @@ -80,7 +79,7 @@ void vsp1_hgo_frame_end(struct vsp1_entity *entity) *data++ = vsp1_hgo_read(hgo, VI6_HGO_G_SUM); *data++ = vsp1_hgo_read(hgo, VI6_HGO_B_SUM); - for (i = 0; i < 64; ++i) { + for (unsigned int i = 0; i < 64; ++i) { data[i] = vsp1_hgo_read(hgo, VI6_HGO_R_HISTO(i)); data[i+64] = vsp1_hgo_read(hgo, VI6_HGO_G_HISTO(i)); data[i+128] = vsp1_hgo_read(hgo, VI6_HGO_B_HISTO(i)); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c index 78b5a9201c70..8c04bdec8510 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c @@ -42,8 +42,6 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity) { struct vsp1_hgt *hgt = to_hgt(&entity->subdev); struct vsp1_histogram_buffer *buf; - unsigned int m; - unsigned int n; u32 *data; buf = vsp1_histogram_buffer_get(&hgt->histo); @@ -55,9 +53,10 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity) *data++ = vsp1_hgt_read(hgt, VI6_HGT_MAXMIN); *data++ = vsp1_hgt_read(hgt, VI6_HGT_SUM); - for (m = 0; m < 6; ++m) - for (n = 0; n < 32; ++n) + for (unsigned int m = 0; m < 6; ++m) { + for (unsigned int n = 0; n < 32; ++n) *data++ = vsp1_hgt_read(hgt, VI6_HGT_HISTO(m, n)); + } vsp1_histogram_buffer_complete(&hgt->histo, buf, HGT_DATA_SIZE); } @@ -71,7 +70,6 @@ void vsp1_hgt_frame_end(struct vsp1_entity *entity) static int hgt_hue_areas_try_ctrl(struct v4l2_ctrl *ctrl) { const u8 *values = ctrl->p_new.p_u8; - unsigned int i; /* * The hardware has constraints on the hue area boundaries beyond the @@ -83,7 +81,7 @@ static int hgt_hue_areas_try_ctrl(struct v4l2_ctrl *ctrl) * * Start by verifying the common part... */ - for (i = 1; i < (HGT_NUM_HUE_AREAS * 2) - 1; ++i) { + for (unsigned int i = 1; i < (HGT_NUM_HUE_AREAS * 2) - 1; ++i) { if (values[i] > values[i+1]) return -EINVAL; } @@ -138,7 +136,6 @@ static void hgt_configure_stream(struct vsp1_entity *entity, unsigned int vratio; u8 lower; u8 upper; - unsigned int i; crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK); compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK); @@ -153,7 +150,7 @@ static void hgt_configure_stream(struct vsp1_entity *entity, (crop->height << VI6_HGT_SIZE_VSIZE_SHIFT)); scoped_guard(mutex, hgt->ctrls.lock) { - for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) { + for (unsigned int i = 0; i < HGT_NUM_HUE_AREAS; ++i) { lower = hgt->hue_areas[i*2 + 0]; upper = hgt->hue_areas[i*2 + 1]; vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i), diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c index a22c31e17cb7..6433b5515ef9 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c @@ -40,13 +40,12 @@ static inline void vsp1_lut_write(struct vsp1_lut *lut, static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl) { struct vsp1_dl_body *dlb; - unsigned int i; dlb = vsp1_dl_body_get(lut->pool); if (!dlb) return -ENOMEM; - for (i = 0; i < LUT_SIZE; ++i) + for (unsigned int i = 0; i < LUT_SIZE; ++i) vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i, ctrl->p_new.p_u32[i]); diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c index 32bb02ce0366..c6a624e193b3 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c @@ -391,16 +391,15 @@ void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func, void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) { struct vsp1_entity *entity; - unsigned int i; if (pipe->brx) { struct vsp1_brx *brx = to_brx(&pipe->brx->subdev); - for (i = 0; i < ARRAY_SIZE(brx->inputs); ++i) + for (unsigned int i = 0; i < ARRAY_SIZE(brx->inputs); ++i) brx->inputs[i].rpf = NULL; } - for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) + for (unsigned int i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) pipe->inputs[i] = NULL; pipe->output = NULL; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c index 5a1d284213ad..b85351986fae 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_video.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c @@ -104,14 +104,13 @@ static int __vsp1_video_try_format(struct vsp1_video *video, const struct vsp1_format_info *info; unsigned int width = pix->width; unsigned int height = pix->height; - unsigned int i; /* * Backward compatibility: replace deprecated RGB formats by their XRGB * equivalent. This selects the format older userspace applications want * while still exposing the new format. */ - for (i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) { + for (unsigned int i = 0; i < ARRAY_SIZE(xrgb_formats); ++i) { if (xrgb_formats[i][0] == pix->pixelformat) { pix->pixelformat = xrgb_formats[i][1]; break; @@ -161,7 +160,7 @@ static int __vsp1_video_try_format(struct vsp1_video *video, * the datasheet, strides not aligned to a multiple of 128 bytes result * in image corruption. */ - for (i = 0; i < min(info->planes, 2U); ++i) { + for (unsigned int i = 0; i < min(info->planes, 2U); ++i) { unsigned int hsub = i > 0 ? info->hsub : 1; unsigned int vsub = i > 0 ? info->vsub : 1; unsigned int align = 128; @@ -209,7 +208,6 @@ vsp1_video_complete_buffer(struct vsp1_video *video) struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; - unsigned int i; scoped_guard(spinlock_irqsave, &video->irqlock) { if (list_empty(&video->irqqueue)) @@ -227,7 +225,7 @@ vsp1_video_complete_buffer(struct vsp1_video *video) done->buf.sequence = pipe->sequence; done->buf.vb2_buf.timestamp = ktime_get_ns(); - for (i = 0; i < done->buf.vb2_buf.num_planes; ++i) + for (unsigned int i = 0; i < done->buf.vb2_buf.num_planes; ++i) vb2_set_plane_payload(&done->buf.vb2_buf, i, vb2_plane_size(&done->buf.vb2_buf, i)); vb2_buffer_done(&done->buf.vb2_buf, VB2_BUF_STATE_DONE); @@ -267,7 +265,6 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) struct vsp1_entity *entity; struct vsp1_dl_body *dlb; struct vsp1_dl_list *dl; - unsigned int partition; dl = vsp1_dl_list_get(pipe->output->dlm); @@ -289,7 +286,7 @@ static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) vsp1_video_pipeline_run_partition(pipe, dl, 0); /* Process consecutive partitions as necessary. */ - for (partition = 1; partition < pipe->partitions; ++partition) { + for (unsigned int partition = 1; partition < pipe->partitions; ++partition) { struct vsp1_dl_list *dl_next; dl_next = vsp1_dl_list_get(pipe->output->dlm); @@ -321,7 +318,6 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, struct vsp1_device *vsp1 = pipe->output->entity.vsp1; enum vsp1_pipeline_state state; unsigned long flags; - unsigned int i; /* M2M Pipelines should never call here with an incomplete frame. */ WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED)); @@ -329,7 +325,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, spin_lock_irqsave(&pipe->irqlock, flags); /* Complete buffers on all video nodes. */ - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { if (!pipe->inputs[i]) continue; @@ -449,7 +445,6 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, struct media_graph graph; struct media_entity *entity = &video->video.entity; struct media_device *mdev = entity->graph_obj.mdev; - unsigned int i; int ret; /* Walk the graph to locate the entities and video nodes. */ @@ -517,7 +512,7 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, * Follow links downstream for each input and make sure the graph * contains no loop and that all branches end at the output WPF. */ - for (i = 0; i < video->vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < video->vsp1->info->rpf_count; ++i) { if (!pipe->inputs[i]) continue; @@ -606,13 +601,12 @@ vsp1_video_queue_setup(struct vb2_queue *vq, { struct vsp1_video *video = vb2_get_drv_priv(vq); const struct v4l2_pix_format_mplane *format = &video->rwpf->format; - unsigned int i; if (*nplanes) { if (*nplanes != format->num_planes) return -EINVAL; - for (i = 0; i < *nplanes; i++) + for (unsigned int i = 0; i < *nplanes; i++) if (sizes[i] < format->plane_fmt[i].sizeimage) return -EINVAL; return 0; @@ -620,7 +614,7 @@ vsp1_video_queue_setup(struct vb2_queue *vq, *nplanes = format->num_planes; - for (i = 0; i < format->num_planes; ++i) + for (unsigned int i = 0; i < format->num_planes; ++i) sizes[i] = format->plane_fmt[i].sizeimage; return 0; @@ -682,7 +676,6 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) const struct v4l2_mbus_framefmt *format; struct vsp1_entity *entity; unsigned int div_size; - unsigned int i; /* * Partitions are computed on the size before rotation, use the format @@ -716,7 +709,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) if (!pipe->part_table) return -ENOMEM; - for (i = 0; i < pipe->partitions; ++i) + for (unsigned int i = 0; i < pipe->partitions; ++i) vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i], div_size, i); @@ -1116,7 +1109,6 @@ static const struct media_entity_operations vsp1_video_media_ops = { void vsp1_video_suspend(struct vsp1_device *vsp1) { - unsigned int i; int ret; /* @@ -1124,7 +1116,7 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) * pipelines twice, first to set them all to the stopping state, and * then to wait for the stop to complete. */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; @@ -1141,7 +1133,7 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) } } - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; @@ -1162,10 +1154,8 @@ void vsp1_video_suspend(struct vsp1_device *vsp1) void vsp1_video_resume(struct vsp1_device *vsp1) { - unsigned int i; - /* Resume all running pipelines. */ - for (i = 0; i < vsp1->info->wpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c index 0ec707d2913f..821887815eb2 100644 --- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c @@ -232,7 +232,6 @@ static void wpf_configure_stream(struct vsp1_entity *entity, const struct v4l2_mbus_framefmt *source_format; const struct v4l2_mbus_framefmt *sink_format; unsigned int index = wpf->entity.index; - unsigned int i; u32 outfmt = 0; u32 srcrpf = 0; int ret; @@ -314,7 +313,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity, * inputs as sub-layers and select the virtual RPF as the master * layer. For VSPX configure the enabled sources as masters. */ - for (i = 0; i < vsp1->info->rpf_count; ++i) { + for (unsigned int i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; if (!input) @@ -399,7 +398,6 @@ static void wpf_configure_partition(struct vsp1_entity *entity, unsigned int left; unsigned int offset; unsigned int flip; - unsigned int i; /* * Cropping. The partition algorithm can split the image into multiple @@ -448,7 +446,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, else offset = left; - for (i = 0; i < format->num_planes; ++i) { + for (unsigned int i = 0; i < format->num_planes; ++i) { unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; unsigned int vsub = i > 0 ? fmtinfo->vsub : 1; @@ -490,7 +488,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity, * Compute the output coordinate. The partition * horizontal (left) offset becomes a vertical offset. */ - for (i = 0; i < format->num_planes; ++i) { + for (unsigned int i = 0; i < format->num_planes; ++i) { unsigned int hsub = i > 0 ? fmtinfo->hsub : 1; mem.addr[i] += hoffset / hsub -- cgit From 8cd5735b88d76dda80b089031747b6f18ee0bca2 Mon Sep 17 00:00:00 2001 From: Narasimharao Vadlamudi Date: Wed, 5 Aug 2026 19:26:34 +0530 Subject: media: i2c: imx415: Return test pattern write errors imx415_set_testpattern() accumulates failures from cci_write(), but drops the value and always returns success. Return the accumulated error so V4L2 reports failures to userspace. Fixes: d5df1c7f3f83 ("media: i2c: imx415: Convert to new CCI register access helpers") Cc: stable@vger.kernel.org Reviewed-by: Michael Riesch Signed-off-by: Narasimharao Vadlamudi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx415.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index 0b424c17e880..f5cd79da512c 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -686,7 +686,7 @@ static int imx415_set_testpattern(struct imx415 *sensor, int val) cci_write(sensor->regmap, IMX415_DIG_CLP_MODE, 0x01, &ret); cci_write(sensor->regmap, IMX415_WRJ_OPEN, 0x01, &ret); } - return 0; + return ret; } static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) -- cgit From bea3001e0f32527a291444e527e84a7ea9b546d4 Mon Sep 17 00:00:00 2001 From: Narasimharao Vadlamudi Date: Wed, 5 Aug 2026 19:26:35 +0530 Subject: media: i2c: imx415: Release runtime PM reference on VBLANK error The VBLANK path returned immediately when programming VMAX failed after pm_runtime_get_if_in_use() had taken a runtime PM reference. Break out of the switch instead so the common pm_runtime_put() path is used. Fixes: 3bcae55ab96a ("media: i2c: imx415: Add read/write control of VBLANK") Cc: stable@vger.kernel.org Reviewed-by: Michael Riesch Signed-off-by: Narasimharao Vadlamudi Signed-off-by: Sakari Ailus --- drivers/media/i2c/imx415.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c index f5cd79da512c..c3b22b238ea0 100644 --- a/drivers/media/i2c/imx415.c +++ b/drivers/media/i2c/imx415.c @@ -720,7 +720,7 @@ static int imx415_s_ctrl(struct v4l2_ctrl *ctrl) ret = cci_write(sensor->regmap, IMX415_VMAX, format->height + ctrl->val, NULL); if (ret) - return ret; + break; /* * Exposure is set based on VMAX which has just changed, so * program exposure register as well -- cgit From ea76b4e6a331312508792980087e8d2f55c24bb1 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Tue, 4 Aug 2026 18:24:30 +0800 Subject: media: rcar-isp: Release ISPCORE resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v4l2_device_register() takes a reference to the parent device, but the ISPCORE remove path never calls v4l2_device_unregister(). The reference is therefore leaked whenever an ISPCORE is removed. Probe failures after rppx1_create() also return without destroying the RPPX1 object. Unregister the V4L2 device and destroy the RPPX1 object on the corresponding error paths, and unregister the V4L2 device during removal. v4l2_device_unregister() also unregisters all attached subdevices, so it replaces the narrower subdevice-only cleanup. Signed-off-by: Linmao Li Reviewed-by: Jacopo Mondi Reviewed-by: Niklas Söderlund Signed-off-by: Sakari Ailus --- drivers/media/platform/renesas/rcar-isp/core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c index f3dc52c13612..181446ae5377 100644 --- a/drivers/media/platform/renesas/rcar-isp/core.c +++ b/drivers/media/platform/renesas/rcar-isp/core.c @@ -870,17 +870,23 @@ int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev, ret = v4l2_device_register(core->dev, &core->v4l2_dev); if (ret) - return ret; + goto err_destroy_rpp; ret = risp_core_create_subdev(core); if (ret) - return ret; + goto err_unregister_v4l2; mutex_init(&core->io_lock); spin_lock_init(&core->lock); INIT_LIST_HEAD(&core->risp_jobs); return 0; + +err_unregister_v4l2: + v4l2_device_unregister(&core->v4l2_dev); +err_destroy_rpp: + rppx1_destroy(core->rpp); + return ret; } void risp_core_remove(struct rcar_isp_core *core) @@ -894,7 +900,7 @@ void risp_core_remove(struct rcar_isp_core *core) for (unsigned int i = 0; i < RISP_CORE_NUM_PADS; i++) risp_core_io_destroy(&core->io[i]); - v4l2_device_unregister_subdev(&core->subdev); + v4l2_device_unregister(&core->v4l2_dev); mutex_destroy(&core->io_lock); rppx1_destroy(core->rpp); -- cgit From 8a6e017c06fbc1b96af618e82a4d35889d442964 Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Tue, 4 Aug 2026 18:24:31 +0800 Subject: media: rcar-isp: Fix VSPX reference leaks of_parse_phandle() and of_find_device_by_node() both acquire references, but the ISPCORE probe never releases them. The device node reference is leaked immediately, and the VSPX device reference is leaked on probe failures and on driver removal. Drop the node reference once the platform device has been looked up, and release the device reference on the probe error paths and in the remove path. Signed-off-by: Linmao Li Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/platform/renesas/rcar-isp/core.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/renesas/rcar-isp/core.c b/drivers/media/platform/renesas/rcar-isp/core.c index 181446ae5377..b5861d0cd0e8 100644 --- a/drivers/media/platform/renesas/rcar-isp/core.c +++ b/drivers/media/platform/renesas/rcar-isp/core.c @@ -820,6 +820,7 @@ static int risp_core_probe_resources(struct rcar_isp_core *core, return -ENODEV; vspx = of_find_device_by_node(of_vspx); + of_node_put(of_vspx); if (!vspx) return -ENODEV; @@ -828,7 +829,7 @@ static int risp_core_probe_resources(struct rcar_isp_core *core, ret = vsp1_isp_init(&vspx->dev); if (ret < 0) - return ret; + goto err_put_vspx; /* Attach to the RPP library * @@ -839,7 +840,7 @@ static int risp_core_probe_resources(struct rcar_isp_core *core, */ ret = clk_prepare_enable(core->clk); if (ret) - return ret; + goto err_put_vspx; usleep_range(2000, 4000); @@ -847,10 +848,16 @@ static int risp_core_probe_resources(struct rcar_isp_core *core, clk_disable_unprepare(core->clk); - if (!core->rpp) - return -ENODEV; + if (!core->rpp) { + ret = -ENODEV; + goto err_put_vspx; + } return 0; + +err_put_vspx: + put_device(&vspx->dev); + return ret; } int risp_core_probe(struct rcar_isp_core *core, struct platform_device *pdev, @@ -886,6 +893,7 @@ err_unregister_v4l2: v4l2_device_unregister(&core->v4l2_dev); err_destroy_rpp: rppx1_destroy(core->rpp); + put_device(core->vspx.dev); return ret; } @@ -904,4 +912,5 @@ void risp_core_remove(struct rcar_isp_core *core) mutex_destroy(&core->io_lock); rppx1_destroy(core->rpp); + put_device(core->vspx.dev); } -- cgit From fea97ee13c5332f67850733d6cefc1fe99460bde Mon Sep 17 00:00:00 2001 From: Yifei Gao Date: Thu, 30 Jul 2026 14:14:03 +0000 Subject: media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem() isp4if_alloc_fw_gpumem() allocates several GPU memory pools in sequence. If one of them fails, it jumps to error_no_memory and returns -ENOMEM without releasing the pools that were already allocated, leaking them. Release the already-allocated pools before returning. isp4if_gpu_mem_free() is a no-op on pools that were not allocated, so calling isp4if_dealloc_fw_gpumem() here safely frees exactly the pools that succeeded. isp4if_gpu_mem_free() previously logged an error for a NULL entry, which is a normal case during partial-allocation cleanup, so make it silent. Fixes: 4c5feef6a62c ("media: platform: amd: Add isp4 fw and hw interface") Signed-off-by: Yifei Gao Reviewed-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/isp4_interface.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c index 00a817909292..4801617f9559 100644 --- a/drivers/media/platform/amd/isp4/isp4_interface.c +++ b/drivers/media/platform/amd/isp4/isp4_interface.c @@ -148,12 +148,9 @@ static void isp4if_gpu_mem_free(struct isp4_interface *ispif, struct isp4if_gpu_mem_info **mem_info_ptr) { struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr; - struct device *dev = ispif->dev; - if (!mem_info) { - dev_err(dev, "invalid mem_info\n"); + if (!mem_info) return; - } *mem_info_ptr = NULL; isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr, @@ -201,6 +198,7 @@ static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif) error_no_memory: dev_err(dev, "failed to allocate gpu memory\n"); + isp4if_dealloc_fw_gpumem(ispif); return -ENOMEM; } -- cgit From ef46d80a7015269a98c9505b5912a83798799199 Mon Sep 17 00:00:00 2001 From: Yifei Gao Date: Thu, 30 Jul 2026 14:14:04 +0000 Subject: media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path isp4sd_pwron_and_init() holds ops_mutex via guard(mutex) and, on any init failure, jumps to err_deinit and calls isp4sd_pwroff_and_deinit(). That helper takes the same ops_mutex, re-acquiring a non-recursive mutex already held by the current thread, so any init failure deadlocks. Unwind the error path in stages instead, releasing only what each failure point acquired. This also avoids the issues that an unconditional teardown would hit at the earlier failures, such as a runtime-PM underflow from pm_runtime_resume_and_get() and MMIO access while the device is unpowered. Fixes: 4e5e7a7ddb4a ("media: platform: amd: isp4 subdev and firmware loading handling added") Assisted-by: Claude:claude-opus-4-8 smatch Signed-off-by: Yifei Gao Reviewed-by: Bin Du Signed-off-by: Sakari Ailus --- drivers/media/platform/amd/isp4/isp4_subdev.c | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c index 2a8bc1207843..6716ab9c128a 100644 --- a/drivers/media/platform/amd/isp4/isp4_subdev.c +++ b/drivers/media/platform/amd/isp4/isp4_subdev.c @@ -687,7 +687,7 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd) if (ret) { dev_err(dev, "fail to power on isp_subdev ret %d\n", ret); - goto err_deinit; + goto err_module_disable; } /* ISPPG ISP Power Status */ @@ -697,7 +697,7 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd) dev_err(dev, "fail to set performance state %u, ret %d\n", perf_state, ret); - goto err_deinit; + goto err_power_off; } ispif->status = ISP4IF_STATUS_PWR_ON; @@ -709,12 +709,12 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd) ret = isp4if_start(ispif); if (ret) { dev_err(dev, "fail to start isp_subdev interface\n"); - goto err_deinit; + goto err_perf_restore; } if (isp4sd_start_resp_proc_threads(isp_subdev)) { dev_err(dev, "isp_start_resp_proc_threads fail\n"); - goto err_deinit; + goto err_stop_interface; } dev_dbg(dev, "create resp threads ok\n"); @@ -724,8 +724,24 @@ int isp4sd_pwron_and_init(struct v4l2_subdev *sd) isp_subdev->irq_enabled = true; return 0; -err_deinit: - isp4sd_pwroff_and_deinit(sd); + +err_stop_interface: + isp4if_stop(ispif); +err_perf_restore: + ret = dev_pm_genpd_set_performance_state(dev, ISP4SD_PERFORMANCE_STATE_LOW); + if (ret) + dev_err(dev, "fail to set performance state %u, ret %d\n", + ISP4SD_PERFORMANCE_STATE_LOW, ret); +err_power_off: + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0); + isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0); + ret = pm_runtime_put_sync(dev); + if (ret) + dev_err(dev, "power off isp_subdev fail %d\n", ret); + ispif->status = ISP4IF_STATUS_PWR_OFF; +err_module_disable: + isp4sd_module_enable(isp_subdev, false); + msleep(20); return -EINVAL; } -- cgit From d7f48aa7d60c65d3e6d5312c27f17d5525a245fb Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Thu, 30 Jul 2026 17:37:54 +0700 Subject: media: staging/ipu7: fix async notifier UAF on probe error path isys_register_devices() registers the V4L2 async notifier via isys_notifier_init(). If a subsequent probe step such as isys_fw_log_init() fails, isys_probe() jumps to the out_cleanup label which only calls isys_unregister_devices(). That helper tears down the video devices, subdevices, V4L2 device and media device, but never unregisters or cleans up the async notifier. As a result the notifier stays chained in the global notifier_list while the enclosing struct ipu7_isys is freed by devres, leading to list corruption and a use-after-free the next time the list is walked. The remove path already does the right thing by calling isys_notifier_cleanup() before isys_unregister_devices(). Mirror that on the probe error path so the notifier is unregistered and cleaned up before the device is torn down. Fixes: a516d36bdc3d ("media: staging/ipu7: add IPU7 input system device driver") Cc: stable@vger.kernel.org Signed-off-by: Cong Nguyen Signed-off-by: Sakari Ailus --- drivers/staging/media/ipu7/ipu7-isys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c index c626de051898..601e5a79ef8e 100644 --- a/drivers/staging/media/ipu7/ipu7-isys.c +++ b/drivers/staging/media/ipu7/ipu7-isys.c @@ -774,6 +774,7 @@ static int isys_probe(struct auxiliary_device *auxdev, return 0; out_cleanup: + isys_notifier_cleanup(isys); isys_unregister_devices(isys); out_cleanup_fw: ipu7_fw_isys_release(isys); -- cgit From abb1f808ceab5a3275f8a6b4e37cff17f9f781c1 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Date: Sat, 8 Aug 2026 16:10:02 +0700 Subject: media: intel/ipu6: fix async notifier cleanup leak on parse error isys_notifier_init() calls v4l2_async_nf_init() and then adds fwnode remote subdevs in a loop with v4l2_async_nf_add_fwnode_remote(). If an endpoint parse or add fails partway through the loop, it jumps to err_parse and returns without calling v4l2_async_nf_cleanup(), leaking every v4l2_async_connection already added to the notifier's waiting list. The register-failure path just below already cleans up correctly, and the caller only tears the notifier down (isys_notifier_cleanup()) once isys_notifier_init() has returned success. Clean up the notifier on the parse error path too. Fixes: f50c4ca0a820 ("media: intel/ipu6: add the main input system driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4 Signed-off-by: Cong Nguyen Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu6/ipu6-isys.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c index c9cdeb7054d7..24db2763de54 100644 --- a/drivers/media/pci/intel/ipu6/ipu6-isys.c +++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c @@ -761,6 +761,7 @@ static int isys_notifier_init(struct ipu6_isys *isys) err_parse: fwnode_handle_put(ep); + v4l2_async_nf_cleanup(&isys->notifier); return ret; } -- cgit From 56c29fa3ee666197516a231e75aed789ae9c530d Mon Sep 17 00:00:00 2001 From: Tommaso Merciai Date: Wed, 29 Jul 2026 12:55:46 +0200 Subject: media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement The RZ/G3E CRU programs the line stride via the AMnIS register, whose IS field encodes the value in units of 128 bytes. If bytesperline is not a multiple of 128, the division truncates and the hardware uses a wrong stride, causing horizontal banding. Commit ace92ccef0c9 ("media: platform: rzg2l-cru: Use v4l2_fill_pixfmt()") replaced the open-coded aligned calculation with v4l2_fill_pixfmt(), which sets no alignment, reintroducing the issue. Round bytesperline up to RZG2L_CRU_STRIDE_ALIGN and recompute sizeimage when info->has_stride is set. RZ/G2L has no AMnIS register and keeps the values from v4l2_fill_pixfmt() unchanged. Fixes: ace92ccef0c9 ("media: platform: rzg2l-cru: Use v4l2_fill_pixfmt()") Cc: stable@vger.kernel.org Signed-off-by: Tommaso Merciai Reviewed-by: Jacopo Mondi Signed-off-by: Sakari Ailus --- drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c index 5185a547461d..91eda5034248 100644 --- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c +++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c @@ -851,6 +851,11 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru, v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height); + if (info->has_stride) { + pix->bytesperline = ALIGN(pix->bytesperline, RZG2L_CRU_STRIDE_ALIGN); + pix->sizeimage = pix->bytesperline * pix->height; + } + dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n", pix->width, pix->height, pix->bytesperline, pix->sizeimage); } -- cgit From 47d82b605351c0e04f6365e42c8ffe2fcfdba615 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Mon, 10 Aug 2026 17:50:12 +0800 Subject: media: v4l2-async: avoid deleting unlinked ASC entry on link error v4l2_async_match_notify() creates ancillary media links before adding asc->asc_subdev_entry to sd->asc_list. If ancillary link creation fails, the function jumps to err_call_unbind while asc_subdev_entry has not been linked yet. Async connections are zero-allocated, so the list entry still has NULL next and prev pointers on this path. Calling list_del() on it can therefore dereference NULL instead of returning the original link creation error. Do not delete asc_subdev_entry from err_call_unbind. There is no list insertion to undo on this path; the bound callback and sub-device registration are the operations that need to be rolled back. Fixes: 28a1295795d8 ("media: v4l: async: Allow multiple connections between entities") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Signed-off-by: Sakari Ailus --- drivers/media/v4l2-core/v4l2-async.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index f36d60e6ff41..460bf3dbbb88 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -392,7 +392,6 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, err_call_unbind: v4l2_async_nf_call_unbind(notifier, sd, asc); - list_del(&asc->asc_subdev_entry); err_unregister_subdev: if (registered) -- cgit From 4900cad020c0580dfb1be27776ff10a4ef110cfa Mon Sep 17 00:00:00 2001 From: José María Martín Date: Mon, 10 Aug 2026 16:30:26 +0200 Subject: media: ipu-bridge: check all DMI entries when overriding sensor rotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A machine can have more than one sensor whose rotation needs to be overridden, which takes one upside_down_sensor_dmi_ids[] entry per sensor, all sharing the same DMI match but with different ACPI HIDs in driver_data. ipu_bridge_parse_rotation() uses dmi_first_match(), which always stops at the first entry matching the running machine, so any further entry for the same machine is unreachable and only one sensor per machine can ever be corrected. Walk the whole table and match every entry for the running machine against the sensor's ACPI HID instead. Fixes: b75710155a82 ("media: ipu-bridge: Add DMI quirk for Dell XPS laptops with upside down sensors") Cc: stable@vger.kernel.org Signed-off-by: José María Martín Signed-off-by: Sakari Ailus --- drivers/media/pci/intel/ipu-bridge.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index f9efa501e8cf..1bb3a3e98d6b 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -357,9 +357,11 @@ static u32 ipu_bridge_parse_rotation(struct acpi_device *adev, { const struct dmi_system_id *dmi_id; - dmi_id = dmi_first_match(upside_down_sensor_dmi_ids); - if (dmi_id && acpi_dev_hid_match(adev, dmi_id->driver_data)) - return 180; + /* A machine may have one entry per sensor, so check all matches. */ + for (dmi_id = dmi_first_match(upside_down_sensor_dmi_ids); dmi_id; + dmi_id = dmi_first_match(dmi_id + 1)) + if (acpi_dev_hid_match(adev, dmi_id->driver_data)) + return 180; switch (ssdb->degree) { case IPU_SENSOR_ROTATION_NORMAL: -- cgit