1 | // SPDX-License-Identifier: (GPL-2.0-only OR MIT) |
2 | /* |
3 | * Copyright (C) 2024 Amlogic, Inc. All rights reserved |
4 | */ |
5 | |
6 | #include <linux/cleanup.h> |
7 | #include <linux/media/amlogic/c3-isp-config.h> |
8 | #include <linux/pm_runtime.h> |
9 | |
10 | #include <media/v4l2-ioctl.h> |
11 | #include <media/v4l2-mc.h> |
12 | #include <media/videobuf2-dma-contig.h> |
13 | |
14 | #include "c3-isp-common.h" |
15 | #include "c3-isp-regs.h" |
16 | |
17 | /* Hardware configuration */ |
18 | |
19 | static void c3_isp_stats_cfg_dmawr_addr(struct c3_isp_stats *stats) |
20 | { |
21 | u32 awb_dma_size = sizeof(struct c3_isp_awb_stats); |
22 | u32 ae_dma_size = sizeof(struct c3_isp_ae_stats); |
23 | u32 awb_dma_addr = stats->buff->dma_addr; |
24 | u32 af_dma_addr; |
25 | u32 ae_dma_addr; |
26 | |
27 | ae_dma_addr = awb_dma_addr + awb_dma_size; |
28 | af_dma_addr = ae_dma_addr + ae_dma_size; |
29 | |
30 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_BADDR0, |
31 | VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK, |
32 | VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(af_dma_addr)); |
33 | |
34 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_BADDR1, |
35 | VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK, |
36 | VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(awb_dma_addr)); |
37 | |
38 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_BADDR2, |
39 | VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK, |
40 | VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(ae_dma_addr)); |
41 | } |
42 | |
43 | static void c3_isp_stats_cfg_buff(struct c3_isp_stats *stats) |
44 | { |
45 | stats->buff = |
46 | list_first_entry_or_null(&stats->pending, |
47 | struct c3_isp_stats_buffer, list); |
48 | if (stats->buff) { |
49 | c3_isp_stats_cfg_dmawr_addr(stats); |
50 | list_del(entry: &stats->buff->list); |
51 | } |
52 | } |
53 | |
54 | void c3_isp_stats_pre_cfg(struct c3_isp_device *isp) |
55 | { |
56 | struct c3_isp_stats *stats = &isp->stats; |
57 | u32 dma_size; |
58 | |
59 | c3_isp_update_bits(isp: stats->isp, ISP_AF_EN_CTRL, |
60 | ISP_AF_EN_CTRL_STAT_SEL_MASK, |
61 | ISP_AF_EN_CTRL_STAT_SEL_NEW); |
62 | c3_isp_update_bits(isp: stats->isp, ISP_AE_CTRL, |
63 | ISP_AE_CTRL_LUMA_MODE_MASK, |
64 | ISP_AE_CTRL_LUMA_MODE_FILTER); |
65 | |
66 | /* The unit of dma_size is 16 bytes */ |
67 | dma_size = sizeof(struct c3_isp_af_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES; |
68 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_SIZE0, |
69 | VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK, |
70 | VIU_DMAWR_SIZE0_AF_STATS_SIZE(dma_size)); |
71 | |
72 | dma_size = sizeof(struct c3_isp_awb_stats) / |
73 | C3_ISP_DMA_SIZE_ALIGN_BYTES; |
74 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_SIZE0, |
75 | VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK, |
76 | VIU_DMAWR_SIZE0_AWB_STATS_SIZE(dma_size)); |
77 | |
78 | dma_size = sizeof(struct c3_isp_ae_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES; |
79 | c3_isp_update_bits(isp: stats->isp, VIU_DMAWR_SIZE1, |
80 | VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK, |
81 | VIU_DMAWR_SIZE1_AE_STATS_SIZE(dma_size)); |
82 | |
83 | guard(spinlock_irqsave)(l: &stats->buff_lock); |
84 | |
85 | c3_isp_stats_cfg_buff(stats); |
86 | } |
87 | |
88 | static int c3_isp_stats_querycap(struct file *file, void *fh, |
89 | struct v4l2_capability *cap) |
90 | { |
91 | strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver)); |
92 | strscpy(cap->card, "AML C3 ISP" , sizeof(cap->card)); |
93 | |
94 | return 0; |
95 | } |
96 | |
97 | static int c3_isp_stats_enum_fmt(struct file *file, void *fh, |
98 | struct v4l2_fmtdesc *f) |
99 | { |
100 | struct c3_isp_stats *stats = video_drvdata(file); |
101 | |
102 | if (f->index > 0 || f->type != stats->vb2_q.type) |
103 | return -EINVAL; |
104 | |
105 | f->pixelformat = V4L2_META_FMT_C3ISP_STATS; |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | static int c3_isp_stats_g_fmt(struct file *file, void *fh, |
111 | struct v4l2_format *f) |
112 | { |
113 | struct c3_isp_stats *stats = video_drvdata(file); |
114 | |
115 | f->fmt.meta = stats->vfmt.fmt.meta; |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static const struct v4l2_ioctl_ops isp_stats_v4l2_ioctl_ops = { |
121 | .vidioc_querycap = c3_isp_stats_querycap, |
122 | .vidioc_enum_fmt_meta_cap = c3_isp_stats_enum_fmt, |
123 | .vidioc_g_fmt_meta_cap = c3_isp_stats_g_fmt, |
124 | .vidioc_s_fmt_meta_cap = c3_isp_stats_g_fmt, |
125 | .vidioc_try_fmt_meta_cap = c3_isp_stats_g_fmt, |
126 | .vidioc_reqbufs = vb2_ioctl_reqbufs, |
127 | .vidioc_querybuf = vb2_ioctl_querybuf, |
128 | .vidioc_qbuf = vb2_ioctl_qbuf, |
129 | .vidioc_expbuf = vb2_ioctl_expbuf, |
130 | .vidioc_dqbuf = vb2_ioctl_dqbuf, |
131 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, |
132 | .vidioc_create_bufs = vb2_ioctl_create_bufs, |
133 | .vidioc_streamon = vb2_ioctl_streamon, |
134 | .vidioc_streamoff = vb2_ioctl_streamoff, |
135 | }; |
136 | |
137 | static const struct v4l2_file_operations isp_stats_v4l2_fops = { |
138 | .open = v4l2_fh_open, |
139 | .release = vb2_fop_release, |
140 | .poll = vb2_fop_poll, |
141 | .unlocked_ioctl = video_ioctl2, |
142 | .mmap = vb2_fop_mmap, |
143 | }; |
144 | |
145 | static int c3_isp_stats_vb2_queue_setup(struct vb2_queue *q, |
146 | unsigned int *num_buffers, |
147 | unsigned int *num_planes, |
148 | unsigned int sizes[], |
149 | struct device *alloc_devs[]) |
150 | { |
151 | if (*num_planes) { |
152 | if (*num_planes != 1) |
153 | return -EINVAL; |
154 | |
155 | if (sizes[0] < sizeof(struct c3_isp_stats_info)) |
156 | return -EINVAL; |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | *num_planes = 1; |
162 | sizes[0] = sizeof(struct c3_isp_stats_info); |
163 | |
164 | return 0; |
165 | } |
166 | |
167 | static void c3_isp_stats_vb2_buf_queue(struct vb2_buffer *vb) |
168 | { |
169 | struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); |
170 | struct c3_isp_stats_buffer *buf = |
171 | container_of(v4l2_buf, struct c3_isp_stats_buffer, vb); |
172 | struct c3_isp_stats *stats = vb2_get_drv_priv(q: vb->vb2_queue); |
173 | |
174 | guard(spinlock_irqsave)(l: &stats->buff_lock); |
175 | |
176 | list_add_tail(new: &buf->list, head: &stats->pending); |
177 | } |
178 | |
179 | static int c3_isp_stats_vb2_buf_prepare(struct vb2_buffer *vb) |
180 | { |
181 | struct c3_isp_stats *stats = vb2_get_drv_priv(q: vb->vb2_queue); |
182 | unsigned int size = stats->vfmt.fmt.meta.buffersize; |
183 | |
184 | if (vb2_plane_size(vb, plane_no: 0) < size) { |
185 | dev_err(stats->isp->dev, |
186 | "User buffer too small (%ld < %u)\n" , |
187 | vb2_plane_size(vb, 0), size); |
188 | return -EINVAL; |
189 | } |
190 | |
191 | vb2_set_plane_payload(vb, plane_no: 0, size); |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static int c3_isp_stats_vb2_buf_init(struct vb2_buffer *vb) |
197 | { |
198 | struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb); |
199 | struct c3_isp_stats_buffer *buf = |
200 | container_of(v4l2_buf, struct c3_isp_stats_buffer, vb); |
201 | |
202 | buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, plane_no: 0); |
203 | |
204 | return 0; |
205 | } |
206 | |
207 | static void c3_isp_stats_vb2_stop_streaming(struct vb2_queue *q) |
208 | { |
209 | struct c3_isp_stats *stats = vb2_get_drv_priv(q); |
210 | |
211 | guard(spinlock_irqsave)(l: &stats->buff_lock); |
212 | |
213 | if (stats->buff) { |
214 | vb2_buffer_done(vb: &stats->buff->vb.vb2_buf, state: VB2_BUF_STATE_ERROR); |
215 | stats->buff = NULL; |
216 | } |
217 | |
218 | while (!list_empty(head: &stats->pending)) { |
219 | struct c3_isp_stats_buffer *buff; |
220 | |
221 | buff = list_first_entry(&stats->pending, |
222 | struct c3_isp_stats_buffer, list); |
223 | list_del(entry: &buff->list); |
224 | vb2_buffer_done(vb: &buff->vb.vb2_buf, state: VB2_BUF_STATE_ERROR); |
225 | } |
226 | } |
227 | |
228 | static const struct vb2_ops isp_stats_vb2_ops = { |
229 | .queue_setup = c3_isp_stats_vb2_queue_setup, |
230 | .buf_queue = c3_isp_stats_vb2_buf_queue, |
231 | .buf_prepare = c3_isp_stats_vb2_buf_prepare, |
232 | .buf_init = c3_isp_stats_vb2_buf_init, |
233 | .stop_streaming = c3_isp_stats_vb2_stop_streaming, |
234 | }; |
235 | |
236 | int c3_isp_stats_register(struct c3_isp_device *isp) |
237 | { |
238 | struct c3_isp_stats *stats = &isp->stats; |
239 | struct video_device *vdev = &stats->vdev; |
240 | struct vb2_queue *vb2_q = &stats->vb2_q; |
241 | int ret; |
242 | |
243 | memset(stats, 0, sizeof(*stats)); |
244 | stats->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_STATS; |
245 | stats->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_stats_info); |
246 | stats->isp = isp; |
247 | INIT_LIST_HEAD(list: &stats->pending); |
248 | spin_lock_init(&stats->buff_lock); |
249 | |
250 | mutex_init(&stats->lock); |
251 | |
252 | snprintf(buf: vdev->name, size: sizeof(vdev->name), fmt: "c3-isp-stats" ); |
253 | vdev->fops = &isp_stats_v4l2_fops; |
254 | vdev->ioctl_ops = &isp_stats_v4l2_ioctl_ops; |
255 | vdev->v4l2_dev = &isp->v4l2_dev; |
256 | vdev->lock = &stats->lock; |
257 | vdev->minor = -1; |
258 | vdev->queue = vb2_q; |
259 | vdev->release = video_device_release_empty; |
260 | vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; |
261 | vdev->vfl_dir = VFL_DIR_RX; |
262 | video_set_drvdata(vdev, data: stats); |
263 | |
264 | vb2_q->drv_priv = stats; |
265 | vb2_q->mem_ops = &vb2_dma_contig_memops; |
266 | vb2_q->ops = &isp_stats_vb2_ops; |
267 | vb2_q->type = V4L2_BUF_TYPE_META_CAPTURE; |
268 | vb2_q->io_modes = VB2_DMABUF | VB2_MMAP; |
269 | vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; |
270 | vb2_q->buf_struct_size = sizeof(struct c3_isp_stats_buffer); |
271 | vb2_q->dev = isp->dev; |
272 | vb2_q->lock = &stats->lock; |
273 | vb2_q->min_queued_buffers = 2; |
274 | |
275 | ret = vb2_queue_init(q: vb2_q); |
276 | if (ret) |
277 | goto err_destroy; |
278 | |
279 | stats->pad.flags = MEDIA_PAD_FL_SINK; |
280 | ret = media_entity_pads_init(entity: &vdev->entity, num_pads: 1, pads: &stats->pad); |
281 | if (ret) |
282 | goto err_queue_release; |
283 | |
284 | ret = video_register_device(vdev, type: VFL_TYPE_VIDEO, nr: -1); |
285 | if (ret) { |
286 | dev_err(isp->dev, |
287 | "Failed to register %s: %d\n" , vdev->name, ret); |
288 | goto err_entity_cleanup; |
289 | } |
290 | |
291 | return 0; |
292 | |
293 | err_entity_cleanup: |
294 | media_entity_cleanup(entity: &vdev->entity); |
295 | err_queue_release: |
296 | vb2_queue_release(q: vb2_q); |
297 | err_destroy: |
298 | mutex_destroy(lock: &stats->lock); |
299 | return ret; |
300 | } |
301 | |
302 | void c3_isp_stats_unregister(struct c3_isp_device *isp) |
303 | { |
304 | struct c3_isp_stats *stats = &isp->stats; |
305 | |
306 | vb2_queue_release(q: &stats->vb2_q); |
307 | media_entity_cleanup(entity: &stats->vdev.entity); |
308 | video_unregister_device(vdev: &stats->vdev); |
309 | mutex_destroy(lock: &stats->lock); |
310 | } |
311 | |
312 | void c3_isp_stats_isr(struct c3_isp_device *isp) |
313 | { |
314 | struct c3_isp_stats *stats = &isp->stats; |
315 | |
316 | guard(spinlock_irqsave)(l: &stats->buff_lock); |
317 | |
318 | if (stats->buff) { |
319 | stats->buff->vb.sequence = stats->isp->frm_sequence; |
320 | stats->buff->vb.vb2_buf.timestamp = ktime_get(); |
321 | stats->buff->vb.field = V4L2_FIELD_NONE; |
322 | vb2_buffer_done(vb: &stats->buff->vb.vb2_buf, state: VB2_BUF_STATE_DONE); |
323 | } |
324 | |
325 | c3_isp_stats_cfg_buff(stats); |
326 | } |
327 | |