From: "Du, Bin" <bin.du@amd.com>
To: Sultan Alsawaf <sultan@kerneltoast.com>
Cc: mchehab@kernel.org, hverkuil@xs4all.nl,
laurent.pinchart+renesas@ideasonboard.com,
bryan.odonoghue@linaro.org, sakari.ailus@linux.intel.com,
prabhakar.mahadev-lad.rj@bp.renesas.com,
linux-media@vger.kernel.org, linux-kernel@vger.kernel.org,
pratap.nirujogi@amd.com, benjamin.chan@amd.com, king.li@amd.com,
gjorgji.rosikopulos@amd.com, Phil.Jawich@amd.com,
Dominic.Antony@amd.com, mario.limonciello@amd.com,
richard.gong@amd.com, anson.tsao@amd.com,
Alexey Zagorodnikov <xglooom@gmail.com>
Subject: Re: [PATCH v4 4/7] media: platform: amd: isp4 subdev and firmware loading handling added
Date: Fri, 10 Oct 2025 18:25:48 +0800 [thread overview]
Message-ID: <dbc92a53-a332-4e57-a37a-7a146b067fcd@amd.com> (raw)
In-Reply-To: <aNzXJaH_yGu1UrV2@sultan-box>
Thanks, Sultan. sorry for the delayed response due to the long public
holiday here.
On 10/1/2025 3:24 PM, Sultan Alsawaf wrote:
> On Tue, Sep 30, 2025 at 03:30:49PM +0800, Du, Bin wrote:
>> On 9/23/2025 3:23 PM, Sultan Alsawaf wrote:
>>> On Thu, Sep 11, 2025 at 06:08:44PM +0800, Bin Du wrote:
>>>> Isp4 sub-device is implementing v4l2 sub-device interface. It has one
>>>> capture video node, and supports only preview stream. It manages firmware
>>>> states, stream configuration. Add interrupt handling and notification for
>>>> isp firmware to isp-subdevice.
>>>>
>>>> Co-developed-by: Svetoslav Stoilov <Svetoslav.Stoilov@amd.com>
>>>> Signed-off-by: Svetoslav Stoilov <Svetoslav.Stoilov@amd.com>
>>>> Signed-off-by: Bin Du <Bin.Du@amd.com>
>>>> Tested-by: Alexey Zagorodnikov <xglooom@gmail.com>
>>>
>>> [snip]
>>>
>>>> +++ b/drivers/media/platform/amd/isp4/isp4.c
>>>> @@ -5,13 +5,19 @@
>>>> #include <linux/pm_runtime.h>
>>>> #include <linux/vmalloc.h>
>>>> +
>>>> +#include <media/v4l2-fwnode.h>
>>>> #include <media/v4l2-ioctl.h>
>>>> #include "isp4.h"
>>>> -
>>>> -#define VIDEO_BUF_NUM 5
>>>> +#include "isp4_hw_reg.h"
>>>> #define ISP4_DRV_NAME "amd_isp_capture"
>>>> +#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
>>>> + (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \
>>>> + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK | \
>>>> + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK | \
>>>> + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
>>>> /* interrupt num */
>>>> static const u32 isp4_ringbuf_interrupt_num[] = {
>>>> @@ -21,19 +27,95 @@ static const u32 isp4_ringbuf_interrupt_num[] = {
>>>> 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */
>>>> };
>>>> -#define to_isp4_device(dev) \
>>>> - ((struct isp4_device *)container_of(dev, struct isp4_device, v4l2_dev))
>>>> +#define to_isp4_device(dev) container_of(dev, struct isp4_device, v4l2_dev)
>>>> +
>>>> +static void isp4_wake_up_resp_thread(struct isp4_subdev *isp, u32 index)
>>>> +{
>>>> + if (isp && index < ISP4SD_MAX_FW_RESP_STREAM_NUM) {
>>>> + struct isp4sd_thread_handler *thread_ctx =
>>>> + &isp->fw_resp_thread[index];
>>>> +
>>>> + thread_ctx->wq_cond = 1;
>>>> + wake_up_interruptible(&thread_ctx->waitq);
>>>> + }
>>>> +}
>>>> +
>>>> +static void isp4_resp_interrupt_notify(struct isp4_subdev *isp, u32 intr_status)
>>>> +{
>>>> + bool wake = (isp->ispif.status == ISP4IF_STATUS_FW_RUNNING);
>>>> +
>>>> + u32 intr_ack = 0;
>>>> +
>>>> + /* global response */
>>>> + if (intr_status &
>>>> + ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK) {
>>>> + if (wake)
>>>> + isp4_wake_up_resp_thread(isp, 0);
>>>> +
>>>> + intr_ack |= ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK;
>>>
>>> The INT_MASKs and ACK_MASKs are the same; perhaps the ACK_MASKs can just be
>>> removed so you can just write intr_status to ISP_SYS_INT0_ACK instead?
>>>
>>
>> These macro definitions are automatically generated from the IP design by
>> the hardware team. INT_MASK and ACK_MASK represent specific bits in
>> different registers—the status and acknowledgment registers, respectively.
>> While their values are currently the same, they could differ depending on
>> the IP design. I prefer to keep both definitions to maintain clarity.
>
> Sure, no problem.
>
>>>> +
>>>> + /* clear ISP_SYS interrupts */
>>>> + isp4hw_wreg(ISP4_GET_ISP_REG_BASE(isp), ISP_SYS_INT0_ACK, intr_ack);
>>>> +}
>>>> static irqreturn_t isp4_irq_handler(int irq, void *arg)
>>>> {
>>>> + struct isp4_device *isp_dev = dev_get_drvdata(arg);
>>>
>>> This is technically a data race because setting drvdata and reading drvdata do
>>> not use WRITE_ONCE() and READ_ONCE(), respectively. And enabling the IRQ before
>>> the handler is allowed to do anything is why that `if (!isp_dev)` check exists,
>>> because that is another race.
>>>
>>> Instead, pass the isp_dev pointer through the private pointer of
>>> devm_request_irq() and add IRQ_NOAUTOEN so the IRQ is enabled by default. Then,
>>> when it is safe for the IRQ to run, enable it with enable_irq().
>>>
>>> That way you can delete the `if (!isp_dev)` check and resolve the two races.
>>>
>>
>> Good deep insight, suppose you mean use IRQ_NOAUTOEN to make irq default
>> disabled. Sure, will add support to dynamically enable/disable IRQ during
>> camera open/close and remove unnecessary check.
>
> Sorry for the typo, meant to say default disabled indeed. :)
>
No worries, got your point :)
>>>> + u32 r1;
>>>> +
>>>> + if (!isp_dev)
>>>> + goto error_drv_data;
>>>> +
>>>> + isp = &isp_dev->isp_sdev;
>>>> + /* check ISP_SYS interrupts status */
>>>> + r1 = isp4hw_rreg(ISP4_GET_ISP_REG_BASE(isp), ISP_SYS_INT0_STATUS);
>>>> +
>>>> + isp_sys_irq_status = r1 & ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
>>>
>>> There are four IRQs (one for each stream) but any one of the IRQs can result in
>>> a notification for _all_ streams. Each IRQ should only do the work of its own
>>> stream.
>>>
>>> You can do this by passing devm_request_irq() a private pointer to indicate the
>>> mapping between a stream and its IRQ, so that isp4_irq_handler() can know which
>>> stream it should look at.
>>>
>>
>> Will do optimization to remove unused IRQs and keep only necessary ones
>> (reduce from 4 to 2), actually an IRQ won't result in notification to all
>> streams, please check the implementation of isp4_resp_interrupt_notify, it
>> will only wake up IRQ corresponding stream processing thread.
>
> What I mean is that the IRQ for one stream can wake a different stream if it is
> also ready at the same time according to the interrupt status register.
>
Yes, you are correct, besides its own stream, the IRQ may wake a
different stream if it is ready too in the IRQ status register. But i
believe the shared irq handler can improve the performance without
negative effects. The peseudo code of isp4_irq_handler works like this
(take your below example)
irqreturn_t isp4_irq_handler(...)
{
status = read_irq_status();
if(status & WPT9)
isp4_wake_up_resp_thread(isp, 1);
if(status & WPT10)
isp4_wake_up_resp_thread(isp, 2)
ack_irq_status(status);
return IRQ_HANDLED;
}
Which means the first isp4_irq_handler can process all IRQs at that
time. For the second isp4_irq_handler, because the irq status is cleared
by the first isp4_irq_handler, it just does nothing and quit. So even if
isp4_irq_handler doen't know exactly which IRQ triggers it, there's no
harm as far as I can tell, not sure if I missed something.
> Assume we have ISP_IRQ 0 and 1 for streams 1 (WPT9) and 2 (WPT10), respectively.
> Consider the following sequence of events:
>
> ISP_IRQ0 (WPT9) ISP_IRQ1 (WPT10)
> --------------- ----------------
> <interrupt fires> <interrupt fires>
> isp4_irq_handler() isp4_irq_handler()
> isp_sys_irq_status = WPT9|WPT10 isp_sys_irq_status = WPT9|WPT10
>
> isp4_wake_up_resp_thread(isp, 1) isp4_wake_up_resp_thread(isp, 1)
> // ^ woke up WPT9 from WPT10 IRQ!
>
> isp4_wake_up_resp_thread(isp, 2) isp4_wake_up_resp_thread(isp, 2)
> // ^ woke up WPT10 from WPT9 IRQ!
>
> The problem is that isp4_irq_handler() doesn't know which IRQ triggered the call
> into isp4_irq_handler().
>
>>>> +static int isp4sd_init_meta_buf(struct isp4_subdev *isp_subdev)
>>>> +{
>>>> + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>>> + struct isp4_interface *ispif = &isp_subdev->ispif;
>>>> + struct device *dev = isp_subdev->dev;
>>>> + u32 i;
>>>> +
>>>> + for (i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
>>>> + if (!sensor_info->meta_info_buf[i]) {
>>>> + sensor_info->meta_info_buf[i] = ispif->metainfo_buf_pool[i];
>>>> + if (!sensor_info->meta_info_buf[i]) {
>>>> + dev_err(dev, "invalid %u meta_info_buf fail\n", i);
>>>> + return -ENOMEM;
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>
>>> What is the point of metainfo_buf_pool? Especially since metainfo_buf_pool[i] is
>>> not set to NULL after this "allocation" occurs.
>>>
>>> I think isp4sd_init_meta_buf() and metainfo_buf_pool are unnecessary and can be
>>> factored out.
>>>
>>
>> I suppose you mean meta_info_buf, will remove it together with
>> isp4sd_init_meta_buf() and use metainfo_buf_pool from ispif directly which
>> is vital for ISP FW to carry response info.
>
> I was thinking that metainfo_buf_pool could be renamed to meta_info_buf and then
> the old meta_info_buf could be deleted. Same result either way. :)
>
Sure, will do that in the next version.
>>>> + init_waitqueue_head(&thread_ctx->waitq);
>>>> + timeout = msecs_to_jiffies(ISP4SD_WAIT_RESP_IRQ_TIMEOUT);
>>>> +
>>>> + dev_dbg(dev, "[%u] started\n", para->idx);
>>>> +
>>>> + while (true) {
>>>> + wait_event_interruptible_timeout(thread_ctx->waitq,
>>>> + thread_ctx->wq_cond != 0,
>>>> + timeout);
>>>
>>> Why is there a timeout? What does the timeout even do since the return value of
>>> wait_event_interruptible_timeout() is not checked? Doesn't that mean that once
>>> the timeout is hit, isp4sd_fw_resp_func() will be called for nothing?
>>>
>>> I observe that most of the time spent by these kthreads is due to the constant
>>> wake-ups from the very short 5 ms timeout. This is bad for energy efficiency and
>>> creates needless overhead.
>>>
>>
>> Good catch, previouly before IRQ is really enabled, this is to make sure ISP
>> can work normally even for 120fps sensor, since now IRQ is enabled, we can
>> increase the timeout value to like 200ms to avoid the unwanted timeout
>> caused wake-ups.
>
> What should the kthread do when there is a timeout though? Is the timeout
> necessary to detect when FW is no longer responding? If so, shouldn't there be
> error handling?
>
> If the timeout isn't used to check for error then I think it should be removed.
>
Yes, good suggestion, will remove the timeout in the next version
>>>> + thread_ctx->wq_cond = 0;
>>>> +
>>>> + if (kthread_should_stop()) {
>>>> + dev_dbg(dev, "[%u] quit\n", para->idx);
>>>> + break;
>>>> + }
>>>> +
>>>> + guard(mutex)(&thread_ctx->mutex);
>>>> + isp4sd_fw_resp_func(isp_subdev, stream_id);
>>>> + }
>>>> +
>>>> + mutex_destroy(&thread_ctx->mutex);
>>>> +
>>>> + return 0;
>>>> +}
>
> [snip]
>
>>>> +
>>>> +static int isp4sd_pwroff_and_deinit(struct isp4_subdev *isp_subdev)
>>>> +{
>>>> + struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
>>>> + unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW;
>>>> + struct isp4_interface *ispif = &isp_subdev->ispif;
>>>> +
>>>> + struct device *dev = isp_subdev->dev;
>>>> + u32 cnt;
>>>> + int ret;
>>>> +
>>>> + mutex_lock(&isp_subdev->ops_mutex);
>>>> +
>>>> + if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
>>>> + dev_err(dev, "fail for stream still running\n");
>>>> + mutex_unlock(&isp_subdev->ops_mutex);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + sensor_info->status = ISP4SD_START_STATUS_NOT_START;
>>>> + cnt = isp4sd_get_started_stream_count(isp_subdev);
>>>> + if (cnt > 0) {
>>>> + dev_dbg(dev, "no need power off isp_subdev\n");
>>>> + mutex_unlock(&isp_subdev->ops_mutex);
>>>> + return 0;
>>>> + }
>>>> +
>>>> + isp4if_stop(ispif);
>>>> +
>>>> + ret = dev_pm_genpd_set_performance_state(dev, perf_state);
>>>> + if (ret)
>>>> + dev_err(dev,
>>>> + "fail to set isp_subdev performance state %u,ret %d\n",
>>>> + perf_state, ret);
>>>> + isp4sd_stop_resp_proc_threads(isp_subdev);
>>>> + dev_dbg(dev, "isp_subdev stop resp proc streads suc");
>>>> + /* hold ccpu reset */
>>>> + isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0x0);
>>>> + 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);
>>>> + else
>>>> + dev_dbg(dev, "power off isp_subdev suc\n");
>>>> +
>>>> + ispif->status = ISP4IF_STATUS_PWR_OFF;
>>>> + isp4if_clear_cmdq(ispif);
>>>> + isp4sd_module_enable(isp_subdev, false);
>>>> +
>>>> + msleep(20);
>>>
>>> What is this msleep for?
>>>
>>
>> This is the HW requirement, at least 20ms is needed for the possible quickly
>> open followed.
>
> Add a comment explaining the HW requirement for this msleep.
>
Sure, will add comments to make it clear
> Sultan
--
Regards,
Bin
next prev parent reply other threads:[~2025-10-10 10:26 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-11 10:08 [PATCH v4 0/7] Add AMD ISP4 driver Bin Du
2025-09-11 10:08 ` [PATCH v4 1/7] media: platform: amd: Introduce amd isp4 capture driver Bin Du
2025-09-21 20:23 ` Sultan Alsawaf
2025-09-23 7:56 ` Du, Bin
2025-09-11 10:08 ` [PATCH v4 2/7] media: platform: amd: low level support for isp4 firmware Bin Du
2025-09-21 20:31 ` Sultan Alsawaf
2025-09-23 8:05 ` Du, Bin
2025-09-11 10:08 ` [PATCH v4 3/7] media: platform: amd: Add isp4 fw and hw interface Bin Du
2025-09-21 21:55 ` Sultan Alsawaf
2025-09-23 9:24 ` Du, Bin
2025-09-24 7:09 ` Sultan Alsawaf
2025-09-25 3:56 ` Du, Bin
2025-09-25 7:20 ` Sultan Alsawaf
2025-09-25 9:42 ` Du, Bin
2025-09-11 10:08 ` [PATCH v4 4/7] media: platform: amd: isp4 subdev and firmware loading handling added Bin Du
2025-09-23 7:23 ` Sultan Alsawaf
2025-09-30 7:30 ` Du, Bin
2025-10-01 7:24 ` Sultan Alsawaf
2025-10-10 10:25 ` Du, Bin [this message]
2025-10-11 7:18 ` Sultan Alsawaf
2025-10-11 8:27 ` Du, Bin
2025-09-11 10:08 ` [PATCH v4 5/7] media: platform: amd: isp4 video node and buffers " Bin Du
2025-10-01 6:53 ` Sultan Alsawaf
2025-10-11 9:30 ` Du, Bin
2025-10-12 6:08 ` Sultan Alsawaf
2025-10-13 9:55 ` Du, Bin
2025-10-16 8:13 ` Du, Bin
2025-10-17 8:34 ` Sultan Alsawaf
2025-10-17 9:53 ` Du, Bin
2025-10-19 22:11 ` Sultan Alsawaf
2025-09-11 10:08 ` [PATCH v4 6/7] media: platform: amd: isp4 debug fs logging and more descriptive errors Bin Du
2025-09-11 10:08 ` [PATCH v4 7/7] Documentation: add documentation of AMD isp 4 driver Bin Du
2025-09-19 3:24 ` [PATCH v4 0/7] Add AMD ISP4 driver Du, Bin
2025-09-19 12:23 ` Laurent Pinchart
2025-09-22 2:50 ` Du, Bin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=dbc92a53-a332-4e57-a37a-7a146b067fcd@amd.com \
--to=bin.du@amd.com \
--cc=Dominic.Antony@amd.com \
--cc=Phil.Jawich@amd.com \
--cc=anson.tsao@amd.com \
--cc=benjamin.chan@amd.com \
--cc=bryan.odonoghue@linaro.org \
--cc=gjorgji.rosikopulos@amd.com \
--cc=hverkuil@xs4all.nl \
--cc=king.li@amd.com \
--cc=laurent.pinchart+renesas@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=mchehab@kernel.org \
--cc=prabhakar.mahadev-lad.rj@bp.renesas.com \
--cc=pratap.nirujogi@amd.com \
--cc=richard.gong@amd.com \
--cc=sakari.ailus@linux.intel.com \
--cc=sultan@kerneltoast.com \
--cc=xglooom@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®