1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
|
// SPDX-License-Identifier: GPL-2.0
/*
* camss-vfe-680.c
*
* Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v680
*
* Copyright (C) 2025 Linaro Ltd.
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include "camss.h"
#include "camss-vfe.h"
#define VFE_TOP_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x1c : 0x44) + (n) * 4)
#define VFE_TOP_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x24 : 0x34) + (n) * 4)
#define VFE_TOP_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x2c : 0x3c) + (n) * 4)
#define VFE_IRQ1_SOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(2) : BIT(8)) << ((n) * 2))
#define VFE_IRQ1_EOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(3) : BIT(9)) << ((n) * 2))
#define VFE_TOP_IRQ_CMD(vfe) (vfe_is_lite(vfe) ? 0x38 : 0x30)
#define VFE_TOP_IRQ_CMD_GLOBAL_CLEAR BIT(0)
#define VFE_TOP_DIAG_CONFIG (vfe_is_lite(vfe) ? 0x40 : 0x50)
#define VFE_TOP_DEBUG_11(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xcc)
#define VFE_TOP_DEBUG_12(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd0)
#define VFE_TOP_DEBUG_13(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd4)
#define VFE_BUS_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x218 : 0xc18) + (n) * 4)
#define VFE_BUS_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x220 : 0xc20) + (n) * 4)
#define VFE_BUS_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x228 : 0xc28) + (n) * 4)
#define VFE_BUS_IRQ_GLOBAL_CLEAR(vfe) (vfe_is_lite(vfe) ? 0x230 : 0xc30)
#define VFE_BUS_WR_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x264 : 0xc64)
#define VFE_BUS_WR_OVERFLOW_STATUS(vfe) (vfe_is_lite(vfe) ? 0x268 : 0xc68)
#define VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x270 : 0xc70)
#define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100)
#define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0)
#define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100)
#define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100)
#define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100)
#define VFE_BUS_IMAGE_CFG0_DATA(h, s) (((h) << 16) | ((s) >> 4))
#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
#define VFE_BUS_IMAGE_CFG1(vfe, c) ((vfe_is_lite(vfe) ? 0x410 : 0xe10) + (c) * 0x100)
#define VFE_BUS_IMAGE_CFG2(vfe, c) ((vfe_is_lite(vfe) ? 0x414 : 0xe14) + (c) * 0x100)
#define VFE_BUS_PACKER_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x418 : 0xe18) + (c) * 0x100)
#define VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x430 : 0xe30) + (c) * 0x100)
#define VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x434 : 0xe34) + (c) * 0x100)
#define VFE_BUS_FRAMEDROP_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x438 : 0xe38) + (c) * 0x100)
#define VFE_BUS_FRAMEDROP_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x43c : 0xe3c) + (c) * 0x100)
#define VFE_BUS_MMU_PREFETCH_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x460 : 0xe60) + (c) * 0x100)
#define VFE_BUS_MMU_PREFETCH_CFG_EN BIT(0)
#define VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, c) ((vfe_is_lite(vfe) ? 0x464 : 0xe64) + (c) * 0x100)
#define VFE_BUS_ADDR_STATUS0(vfe, c) ((vfe_is_lite(vfe) ? 0x470 : 0xe70) + (c) * 0x100)
/*
* TODO: differentiate the port id based on requested type of RDI, BHIST etc
*
* IFE write master IDs
*
* VIDEO_FULL_Y 0
* VIDEO_FULL_C 1
* VIDEO_DS_4:1 2
* VIDEO_DS_16:1 3
* DISPLAY_FULL_Y 4
* DISPLAY_FULL_C 5
* DISPLAY_DS_4:1 6
* DISPLAY_DS_16:1 7
* FD_Y 8
* FD_C 9
* PIXEL_RAW 10
* STATS_BE0 11
* STATS_BHIST0 12
* STATS_TINTLESS_BG 13
* STATS_AWB_BG 14
* STATS_AWB_BFW 15
* STATS_BAF 16
* STATS_BHIST 17
* STATS_RS 18
* STATS_IHIST 19
* SPARSE_PD 20
* PDAF_V2.0_PD_DATA 21
* PDAF_V2.0_SAD 22
* LCR 23
* RDI0 24
* RDI1 25
* RDI2 26
* LTM_STATS 27
*
* IFE Lite write master IDs
*
* RDI0 0
* RDI1 1
* RDI2 2
* RDI3 3
* GAMMA 4
* BE 5
*/
/* TODO: assign an ENUM in resources and use the provided master
* id directly for RDI, STATS, AWB_BG, BHIST.
* This macro only works because RDI is all we support right now.
*/
#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 24) + (n))
static void vfe_global_reset(struct vfe_device *vfe)
{
/* VFE680 has no global reset, simply report a completion */
complete(&vfe->reset_complete);
}
/*
* vfe_isr - VFE module interrupt handler
* @irq: Interrupt line
* @dev: VFE device
*
* Return IRQ_HANDLED on success
*/
static irqreturn_t vfe_isr(int irq, void *dev)
{
return IRQ_HANDLED;
}
/*
* vfe_halt - Trigger halt on VFE module and wait to complete
* @vfe: VFE device
*
* Return 0 on success or a negative error code otherwise
*/
static int vfe_halt(struct vfe_device *vfe)
{
/* rely on vfe_disable_output() to stop the VFE */
return 0;
}
static void vfe_disable_irq(struct vfe_device *vfe)
{
writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 0));
writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 1));
writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 0));
writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 1));
}
static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr,
struct vfe_line *line)
{
u8 wm = RDI_WM(rdi);
writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm));
}
static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line)
{
struct v4l2_pix_format_mplane *pix =
&line->video_out.active_fmt.fmt.pix_mp;
u32 stride = pix->plane_fmt[0].bytesperline;
u32 cfg;
u8 wm;
cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride);
wm = RDI_WM(rdi);
writel(cfg, vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm));
writel(0, vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm));
writel(stride, vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm));
writel(0, vfe->base + VFE_BUS_PACKER_CFG(vfe, wm));
/* Set total frame increment value */
writel(pix->plane_fmt[0].bytesperline * pix->height,
vfe->base + VFE_BUS_FRAME_INCR(vfe, wm));
/* MMU */
writel(VFE_BUS_MMU_PREFETCH_CFG_EN, vfe->base + VFE_BUS_MMU_PREFETCH_CFG(vfe, wm));
writel(~0u, vfe->base + VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, wm));
/* no dropped frames, one irq per frame */
writel(1, vfe->base + VFE_BUS_FRAMEDROP_PATTERN(vfe, wm));
writel(0, vfe->base + VFE_BUS_FRAMEDROP_PERIOD(vfe, wm));
writel(1, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, wm));
writel(0, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, wm));
/* We don't process IRQs for VFE in RDI mode at the moment */
vfe_disable_irq(vfe);
/* Enable WM */
writel(VFE_BUS_WRITE_CLIENT_CFG_EN,
vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n",
rdi, wm, pix->width, pix->height, stride);
}
static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi)
{
u8 wm = RDI_WM(rdi);
writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
}
static const struct camss_video_ops vfe_video_ops_680 = {
.queue_buffer = vfe_queue_buffer_v2,
.flush_buffers = vfe_flush_buffers,
};
static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
{
vfe->video_ops = vfe_video_ops_680;
}
static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
{
int port_id = line_id;
camss_reg_update(vfe->camss, vfe->id, port_id, false);
}
static inline void vfe_reg_update_clear(struct vfe_device *vfe,
enum vfe_line_id line_id)
{
int port_id = line_id;
camss_reg_update(vfe->camss, vfe->id, port_id, true);
}
const struct vfe_hw_ops vfe_ops_680 = {
.global_reset = vfe_global_reset,
.hw_version = vfe_hw_version,
.isr = vfe_isr,
.pm_domain_off = vfe_pm_domain_off,
.pm_domain_on = vfe_pm_domain_on,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_disable,
.vfe_enable = vfe_enable_v2,
.vfe_halt = vfe_halt,
.vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
.vfe_buf_done = vfe_buf_done,
.vfe_wm_update = vfe_wm_update,
.reg_update = vfe_reg_update,
.reg_update_clear = vfe_reg_update_clear,
};
|