Skip to content

Commit 96d6388

Browse files
Bin Du1Naim
authored andcommitted
media: platform: amd: isp4 subdev and firmware loading handling added
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: Sultan Alsawaf <sultan@kerneltoast.com> Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com> 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> Reviewed-by: Sultan Alsawaf <sultan@kerneltoast.com> Tested-by: Alexey Zagorodnikov <xglooom@gmail.com> Tested-by: Kate Hsuan <hpa@redhat.com>
1 parent f35454f commit 96d6388

6 files changed

Lines changed: 1259 additions & 6 deletions

File tree

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,8 @@ F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
11781178
F: drivers/media/platform/amd/isp4/isp4_hw_reg.h
11791179
F: drivers/media/platform/amd/isp4/isp4_interface.c
11801180
F: drivers/media/platform/amd/isp4/isp4_interface.h
1181+
F: drivers/media/platform/amd/isp4/isp4_subdev.c
1182+
F: drivers/media/platform/amd/isp4/isp4_subdev.h
11811183

11821184
AMD KFD
11831185
M: Felix Kuehling <Felix.Kuehling@amd.com>

drivers/media/platform/amd/isp4/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44

55
obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o
66
amd_isp4_capture-objs := isp4.o \
7-
isp4_interface.o
7+
isp4_interface.o \
8+
isp4_subdev.o

drivers/media/platform/amd/isp4/isp4.c

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,122 @@
33
* Copyright (C) 2025 Advanced Micro Devices, Inc.
44
*/
55

6+
#include <linux/irq.h>
67
#include <linux/pm_runtime.h>
78
#include <linux/vmalloc.h>
89
#include <media/v4l2-ioctl.h>
910

1011
#include "isp4.h"
12+
#include "isp4_hw_reg.h"
1113

1214
#define ISP4_DRV_NAME "amd_isp_capture"
15+
#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
16+
(ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \
17+
ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
1318

1419
static const struct {
1520
const char *name;
1621
u32 status_mask;
1722
u32 en_mask;
1823
u32 ack_mask;
1924
u32 rb_int_num;
20-
} isp4_irq[] = {
25+
} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
2126
/* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */
2227
{
2328
.name = "isp_irq_global",
29+
.status_mask =
30+
ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK,
31+
.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK,
32+
.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK,
2433
.rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */
2534
},
2635
{
2736
.name = "isp_irq_stream1",
37+
.status_mask =
38+
ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK,
39+
.en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK,
40+
.ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK,
2841
.rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */
2942
},
3043
};
3144

45+
void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable)
46+
{
47+
u32 intr_en;
48+
49+
/* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */
50+
spin_lock_irq(&isp_subdev->irq_lock);
51+
intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN);
52+
if (enable)
53+
intr_en |= isp4_irq[index].en_mask;
54+
else
55+
intr_en &= ~isp4_irq[index].en_mask;
56+
57+
isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
58+
spin_unlock_irq(&isp_subdev->irq_lock);
59+
}
60+
61+
static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index)
62+
{
63+
struct isp4sd_thread_handler *thread_ctx =
64+
&isp_subdev->fw_resp_thread[index];
65+
66+
thread_ctx->resp_ready = true;
67+
wake_up_interruptible(&thread_ctx->waitq);
68+
}
69+
3270
static irqreturn_t isp4_irq_handler(int irq, void *arg)
3371
{
72+
struct isp4_subdev *isp_subdev = arg;
73+
u32 intr_ack = 0, intr_en = 0, intr_status;
74+
int seen = 0;
75+
76+
/* Get the ISP_SYS interrupt status */
77+
intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS);
78+
intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
79+
80+
/* Find which ISP_SYS interrupts fired */
81+
for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
82+
if (intr_status & isp4_irq[i].status_mask) {
83+
intr_ack |= isp4_irq[i].ack_mask;
84+
intr_en |= isp4_irq[i].en_mask;
85+
seen |= BIT(i);
86+
}
87+
}
88+
89+
/*
90+
* Disable the ISP_SYS interrupts that fired. Must be done before waking
91+
* the response threads, since they re-enable interrupts when finished.
92+
* The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt().
93+
*/
94+
spin_lock(&isp_subdev->irq_lock);
95+
intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en;
96+
isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
97+
spin_unlock(&isp_subdev->irq_lock);
98+
99+
/*
100+
* Clear the ISP_SYS interrupts. This must be done after the interrupts
101+
* are disabled, so that ISP FW won't flag any new interrupts on these
102+
* streams, and thus we don't need to clear interrupts again before
103+
* re-enabling them in the response thread.
104+
*/
105+
isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack);
106+
107+
/*
108+
* The operation `(seen >> i) << i` is logically equivalent to
109+
* `seen &= ~BIT(i)`, with fewer instructions after compilation.
110+
*/
111+
for (int i; (i = ffs(seen)); seen = (seen >> i) << i)
112+
isp4_wake_up_resp_thread(isp_subdev, i - 1);
113+
34114
return IRQ_HANDLED;
35115
}
36116

37117
static int isp4_capture_probe(struct platform_device *pdev)
38118
{
119+
int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
39120
struct device *dev = &pdev->dev;
40-
int irq[ARRAY_SIZE(isp4_irq)];
121+
struct isp4_subdev *isp_subdev;
41122
struct isp4_device *isp_dev;
42123
int ret;
43124

@@ -47,6 +128,12 @@ static int isp4_capture_probe(struct platform_device *pdev)
47128

48129
dev->init_name = ISP4_DRV_NAME;
49130

131+
isp_subdev = &isp_dev->isp_subdev;
132+
isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0);
133+
if (IS_ERR(isp_subdev->mmio))
134+
return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio),
135+
"isp ioremap fail\n");
136+
50137
for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
51138
irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num);
52139
if (irq[i] < 0)
@@ -55,7 +142,8 @@ static int isp4_capture_probe(struct platform_device *pdev)
55142
isp4_irq[i].rb_int_num);
56143

57144
ret = devm_request_irq(dev, irq[i], isp4_irq_handler,
58-
IRQF_NO_AUTOEN, isp4_irq[i].name, dev);
145+
IRQF_NO_AUTOEN, isp4_irq[i].name,
146+
isp_subdev);
59147
if (ret)
60148
return dev_err_probe(dev, ret, "fail to req irq %d\n",
61149
irq[i]);
@@ -78,6 +166,13 @@ static int isp4_capture_probe(struct platform_device *pdev)
78166

79167
pm_runtime_set_suspended(dev);
80168
pm_runtime_enable(dev);
169+
spin_lock_init(&isp_subdev->irq_lock);
170+
ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq);
171+
if (ret) {
172+
dev_err_probe(dev, ret, "fail init isp4 sub dev\n");
173+
goto err_pm_disable;
174+
}
175+
81176
ret = media_device_register(&isp_dev->mdev);
82177
if (ret) {
83178
dev_err_probe(dev, ret, "fail to register media device\n");
@@ -89,6 +184,8 @@ static int isp4_capture_probe(struct platform_device *pdev)
89184
return 0;
90185

91186
err_isp4_deinit:
187+
isp4sd_deinit(&isp_dev->isp_subdev);
188+
err_pm_disable:
92189
pm_runtime_disable(dev);
93190
v4l2_device_unregister(&isp_dev->v4l2_dev);
94191
err_clean_media:
@@ -103,6 +200,7 @@ static void isp4_capture_remove(struct platform_device *pdev)
103200
struct device *dev = &pdev->dev;
104201

105202
media_device_unregister(&isp_dev->mdev);
203+
isp4sd_deinit(&isp_dev->isp_subdev);
106204
pm_runtime_disable(dev);
107205
v4l2_device_unregister(&isp_dev->v4l2_dev);
108206
media_device_cleanup(&isp_dev->mdev);

drivers/media/platform/amd/isp4/isp4.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
#ifndef _ISP4_H_
77
#define _ISP4_H_
88

9-
#include <media/v4l2-device.h>
10-
#include <media/videobuf2-memops.h>
9+
#include <drm/amd/isp.h>
10+
#include "isp4_subdev.h"
1111

1212
struct isp4_device {
1313
struct v4l2_device v4l2_dev;
14+
struct isp4_subdev isp_subdev;
1415
struct media_device mdev;
1516
};
1617

18+
void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable);
19+
1720
#endif /* _ISP4_H_ */

0 commit comments

Comments
 (0)