1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */
3
4 #include <linux/clk.h>
5 #include <linux/delay.h>
6 #include <media/v4l2-device.h>
7 #include <media/v4l2-fh.h>
8 #include <media/v4l2-ioctl.h>
9 #include <media/videobuf2-dma-contig.h>
10 #include <media/videobuf2-v4l2.h>
11 #include <linux/pm_runtime.h>
12 #include <linux/rk-ispp-config.h>
13 #include <uapi/linux/rk-video-format.h>
14
15 #include "hw.h"
16 #include "ispp.h"
17 #include "regs.h"
18 #include "stream.h"
19 #include "common.h"
20
21 struct rkispp_fec_buf {
22 struct list_head list;
23 struct file *file;
24 int fd;
25 struct dma_buf *dbuf;
26 void *mem;
27 };
28
29 static const struct vb2_mem_ops *g_ops = &vb2_dma_contig_memops;
30
fec_buf_add(struct file * file,int fd,int size)31 static void *fec_buf_add(struct file *file, int fd, int size)
32 {
33 struct rkispp_fec_dev *fec = video_drvdata(file);
34 struct rkispp_fec_buf *buf = NULL;
35 struct dma_buf *dbuf;
36 void *mem = NULL;
37 bool is_add = true;
38
39 dbuf = dma_buf_get(fd);
40 v4l2_dbg(4, rkispp_debug, &fec->v4l2_dev,
41 "%s file:%p fd:%d dbuf:%p\n", __func__, file, fd, dbuf);
42 if (IS_ERR_OR_NULL(dbuf)) {
43 v4l2_err(&fec->v4l2_dev, "invalid dmabuf fd:%d for in picture", fd);
44 return mem;
45 }
46 if (size && dbuf->size < size) {
47 v4l2_err(&fec->v4l2_dev,
48 "input fd:%d size error:%zu < %u\n", fd, dbuf->size, size);
49 dma_buf_put(dbuf);
50 return mem;
51 }
52
53 mutex_lock(&fec->hw->dev_lock);
54 list_for_each_entry(buf, &fec->list, list) {
55 if (buf->file == file && buf->fd == fd && buf->dbuf == dbuf) {
56 is_add = false;
57 break;
58 }
59 }
60
61 if (is_add) {
62 mem = g_ops->attach_dmabuf(fec->hw->dev, dbuf, dbuf->size, DMA_BIDIRECTIONAL);
63 if (IS_ERR(mem)) {
64 v4l2_err(&fec->v4l2_dev, "failed to attach dmabuf, fd:%d\n", fd);
65 dma_buf_put(dbuf);
66 goto end;
67 }
68 if (g_ops->map_dmabuf(mem)) {
69 v4l2_err(&fec->v4l2_dev, "failed to map, fd:%d\n", fd);
70 g_ops->detach_dmabuf(mem);
71 dma_buf_put(dbuf);
72 mem = NULL;
73 goto end;
74 }
75 buf = kzalloc(sizeof(struct rkispp_fec_buf), GFP_KERNEL);
76 if (!buf) {
77 g_ops->unmap_dmabuf(mem);
78 g_ops->detach_dmabuf(mem);
79 dma_buf_put(dbuf);
80 mem = NULL;
81 goto end;
82 }
83 buf->fd = fd;
84 buf->file = file;
85 buf->dbuf = dbuf;
86 buf->mem = mem;
87 list_add_tail(&buf->list, &fec->list);
88 } else {
89 dma_buf_put(dbuf);
90 mem = buf->mem;
91 }
92 end:
93 mutex_unlock(&fec->hw->dev_lock);
94 return mem;
95 }
96
fec_buf_del(struct file * file,int fd,bool is_all)97 static void fec_buf_del(struct file *file, int fd, bool is_all)
98 {
99 struct rkispp_fec_dev *fec = video_drvdata(file);
100 struct rkispp_fec_buf *buf, *next;
101
102 mutex_lock(&fec->hw->dev_lock);
103 list_for_each_entry_safe(buf, next, &fec->list, list) {
104 if (buf->file == file && (is_all || buf->fd == fd)) {
105 v4l2_dbg(4, rkispp_debug, &fec->v4l2_dev,
106 "%s file:%p fd:%d dbuf:%p\n",
107 __func__, file, buf->fd, buf->dbuf);
108 g_ops->unmap_dmabuf(buf->mem);
109 g_ops->detach_dmabuf(buf->mem);
110 dma_buf_put(buf->dbuf);
111 buf->file = NULL;
112 buf->mem = NULL;
113 buf->dbuf = NULL;
114 buf->fd = -1;
115 list_del(&buf->list);
116 kfree(buf);
117 }
118 }
119 mutex_unlock(&fec->hw->dev_lock);
120 }
121
fec_running(struct file * file,struct rkispp_fec_in_out * buf)122 static int fec_running(struct file *file, struct rkispp_fec_in_out *buf)
123 {
124 struct rkispp_fec_dev *fec = video_drvdata(file);
125 u32 in_fmt, out_fmt, in_mult = 1, out_mult = 1;
126 u32 in_size, in_offs, out_size, out_offs, val;
127 u32 in_w = buf->in_width, in_h = buf->in_height;
128 u32 out_w = buf->out_width, out_h = buf->out_height;
129 u32 density, mesh_size;
130 void __iomem *base = fec->hw->base_addr;
131 void *mem;
132 int ret = -EINVAL;
133 ktime_t t = 0;
134 s64 us = 0;
135
136 if (rkispp_debug)
137 t = ktime_get();
138 v4l2_dbg(3, rkispp_debug, &fec->v4l2_dev,
139 "%s enter %dx%d->%dx%d format(in:%c%c%c%c out:%c%c%c%c)\n",
140 __func__, in_w, in_h, out_w, out_h,
141 buf->in_fourcc, buf->in_fourcc >> 8,
142 buf->in_fourcc >> 16, buf->in_fourcc >> 24,
143 buf->out_fourcc, buf->out_fourcc >> 8,
144 buf->out_fourcc >> 16, buf->out_fourcc >> 24);
145
146 if (clk_get_rate(fec->hw->clks[0]) <= fec->hw->core_clk_min)
147 rkispp_set_clk_rate(fec->hw->clks[0], fec->hw->core_clk_max);
148
149 init_completion(&fec->cmpl);
150 density = out_w > 1920 ? SW_MESH_DENSITY : 0;
151 mesh_size = cal_fec_mesh(out_w, out_h, !!density);
152
153 switch (buf->in_fourcc) {
154 case V4L2_PIX_FMT_YUYV:
155 in_fmt = FMT_YC_SWAP | FMT_YUYV | FMT_YUV422;
156 in_mult = 2;
157 break;
158 case V4L2_PIX_FMT_UYVY:
159 in_fmt = FMT_YUYV | FMT_YUV422;
160 in_mult = 2;
161 break;
162 case V4L2_PIX_FMT_NV16:
163 in_fmt = FMT_YUV422;
164 break;
165 case V4L2_PIX_FMT_NV12:
166 in_fmt = FMT_YUV420;
167 break;
168 default:
169 v4l2_err(&fec->v4l2_dev,
170 "no support in format:%c%c%c%c\n",
171 buf->in_fourcc, buf->in_fourcc >> 8,
172 buf->in_fourcc >> 16, buf->in_fourcc >> 24);
173 return -EINVAL;
174 }
175 in_offs = in_w * in_h;
176 in_size = (in_fmt & FMT_YUV422) ?
177 in_w * in_h * 2 : in_w * in_h * 3 / 2;
178
179 switch (buf->out_fourcc) {
180 case V4L2_PIX_FMT_YUYV:
181 out_fmt = FMT_YC_SWAP | FMT_YUYV | FMT_YUV422;
182 out_mult = 2;
183 break;
184 case V4L2_PIX_FMT_UYVY:
185 out_fmt = FMT_YUYV | FMT_YUV422;
186 out_mult = 2;
187 break;
188 case V4L2_PIX_FMT_NV16:
189 out_fmt = FMT_YUV422;
190 break;
191 case V4L2_PIX_FMT_NV12:
192 out_fmt = FMT_YUV420;
193 break;
194 case V4L2_PIX_FMT_FBC2:
195 out_fmt = FMT_YUV422 | FMT_FBC;
196 break;
197 case V4L2_PIX_FMT_FBC0:
198 out_fmt = FMT_YUV420 | FMT_FBC;
199 break;
200 default:
201 v4l2_err(&fec->v4l2_dev, "no support out format:%c%c%c%c\n",
202 buf->out_fourcc, buf->out_fourcc >> 8,
203 buf->out_fourcc >> 16, buf->out_fourcc >> 24);
204 return -EINVAL;
205 }
206 out_size = 0;
207 out_offs = out_w * out_h;
208 if (out_fmt & FMT_FBC) {
209 out_w = ALIGN(out_w, 16);
210 out_h = ALIGN(out_h, 16);
211 out_offs = out_w * out_h >> 4;
212 out_size = out_offs;
213 }
214 out_size += (out_fmt & FMT_YUV422) ?
215 out_w * out_h * 2 : out_w * out_h * 3 / 2;
216
217 /* input picture buf */
218 mem = fec_buf_add(file, buf->in_pic_fd, in_size);
219 if (!mem)
220 goto free_buf;
221 val = *((dma_addr_t *)g_ops->cookie(mem));
222 writel(val, base + RKISPP_FEC_RD_Y_BASE);
223 val += in_offs;
224 writel(val, base + RKISPP_FEC_RD_UV_BASE);
225
226 /* output picture buf */
227 mem = fec_buf_add(file, buf->out_pic_fd, out_size);
228 if (!mem)
229 goto free_buf;
230 val = *((dma_addr_t *)g_ops->cookie(mem));
231 writel(val, base + RKISPP_FEC_WR_Y_BASE);
232 val += out_offs;
233 writel(val, base + RKISPP_FEC_WR_UV_BASE);
234
235 /* mesh xint buf */
236 mem = fec_buf_add(file, buf->mesh_xint_fd, mesh_size * 2);
237 if (!mem)
238 goto free_buf;
239 val = *((dma_addr_t *)g_ops->cookie(mem));
240 writel(val, base + RKISPP_FEC_MESH_XINT_BASE);
241
242 /* mesh xfra buf */
243 mem = fec_buf_add(file, buf->mesh_xfra_fd, mesh_size);
244 if (!mem)
245 goto free_buf;
246 val = *((dma_addr_t *)g_ops->cookie(mem));
247 writel(val, base + RKISPP_FEC_MESH_XFRA_BASE);
248
249 /* mesh yint buf */
250 mem = fec_buf_add(file, buf->mesh_yint_fd, mesh_size * 2);
251 if (!mem)
252 goto free_buf;
253 val = *((dma_addr_t *)g_ops->cookie(mem));
254 writel(val, base + RKISPP_FEC_MESH_YINT_BASE);
255
256 /* mesh yfra buf */
257 mem = fec_buf_add(file, buf->mesh_yfra_fd, mesh_size);
258 if (!mem)
259 goto free_buf;
260 val = *((dma_addr_t *)g_ops->cookie(mem));
261 writel(val, base + RKISPP_FEC_MESH_YFRA_BASE);
262
263 val = out_fmt << 4 | in_fmt;
264 writel(val, base + RKISPP_FEC_CTRL);
265 val = ALIGN(in_w * in_mult, 16) >> 2;
266 writel(val, base + RKISPP_FEC_RD_VIR_STRIDE);
267 val = ALIGN(out_w * out_mult, 16) >> 2;
268 writel(val, base + RKISPP_FEC_WR_VIR_STRIDE);
269 val = out_h << 16 | out_w;
270 writel(val, base + RKISPP_FEC_DST_SIZE);
271 val = in_h << 16 | in_w;
272 writel(val, base + RKISPP_FEC_SRC_SIZE);
273 writel(mesh_size, base + RKISPP_FEC_MESH_SIZE);
274 val = SW_FEC_EN | density;
275 writel(val, base + RKISPP_FEC_CORE_CTRL);
276
277 writel(FEC_FORCE_UPD, base + RKISPP_CTRL_UPDATE);
278 v4l2_dbg(3, rkispp_debug, &fec->v4l2_dev,
279 "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n"
280 "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n"
281 "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n",
282 RKISPP_CTRL_SYS_STATUS, readl(base + RKISPP_CTRL_SYS_STATUS),
283 RKISPP_FEC_CTRL, readl(base + RKISPP_FEC_CTRL),
284 RKISPP_FEC_RD_VIR_STRIDE, readl(base + RKISPP_FEC_RD_VIR_STRIDE),
285 RKISPP_FEC_WR_VIR_STRIDE, readl(base + RKISPP_FEC_WR_VIR_STRIDE),
286 RKISPP_FEC_RD_Y_BASE_SHD, readl(base + RKISPP_FEC_RD_Y_BASE_SHD),
287 RKISPP_FEC_RD_UV_BASE_SHD, readl(base + RKISPP_FEC_RD_UV_BASE_SHD),
288 RKISPP_FEC_MESH_XINT_BASE_SHD, readl(base + RKISPP_FEC_MESH_XINT_BASE_SHD),
289 RKISPP_FEC_MESH_XFRA_BASE_SHD, readl(base + RKISPP_FEC_MESH_XFRA_BASE_SHD),
290 RKISPP_FEC_MESH_YINT_BASE_SHD, readl(base + RKISPP_FEC_MESH_YINT_BASE_SHD),
291 RKISPP_FEC_MESH_YFRA_BASE_SHD, readl(base + RKISPP_FEC_MESH_YFRA_BASE_SHD),
292 RKISPP_FEC_WR_Y_BASE_SHD, readl(base + RKISPP_FEC_WR_Y_BASE_SHD),
293 RKISPP_FEC_WR_UV_BASE_SHD, readl(base + RKISPP_FEC_WR_UV_BASE_SHD),
294 RKISPP_FEC_CORE_CTRL, readl(base + RKISPP_FEC_CORE_CTRL),
295 RKISPP_FEC_DST_SIZE, readl(base + RKISPP_FEC_DST_SIZE),
296 RKISPP_FEC_SRC_SIZE, readl(base + RKISPP_FEC_SRC_SIZE),
297 RKISPP_FEC_MESH_SIZE, readl(base + RKISPP_FEC_MESH_SIZE));
298 if (!fec->hw->is_shutdown)
299 writel(FEC_ST, base + RKISPP_CTRL_STRT);
300
301 ret = wait_for_completion_timeout(&fec->cmpl, msecs_to_jiffies(300));
302 if (!ret) {
303 v4l2_err(&fec->v4l2_dev, "fec working timeout\n");
304 ret = -EAGAIN;
305 } else {
306 ret = 0;
307 }
308 writel(SW_FEC2DDR_DIS, base + RKISPP_FEC_CORE_CTRL);
309
310 if (rkispp_debug)
311 us = ktime_us_delta(ktime_get(), t);
312 v4l2_dbg(3, rkispp_debug, &fec->v4l2_dev,
313 "%s exit ret:%d, time:%lldus\n", __func__, ret, us);
314 return ret;
315 free_buf:
316 fec_buf_del(file, 0, true);
317 return ret;
318 }
319
fec_ioctl_default(struct file * file,void * fh,bool valid_prio,unsigned int cmd,void * arg)320 static long fec_ioctl_default(struct file *file, void *fh,
321 bool valid_prio, unsigned int cmd, void *arg)
322 {
323 long ret = 0;
324
325 if (!arg)
326 return -EINVAL;
327
328 switch (cmd) {
329 case RKISPP_CMD_FEC_IN_OUT:
330 ret = fec_running(file, arg);
331 break;
332 case RKISPP_CMD_FEC_BUF_ADD:
333 if (!fec_buf_add(file, *(int *)arg, 0))
334 ret = -ENOMEM;
335 break;
336 case RKISPP_CMD_FEC_BUF_DEL:
337 fec_buf_del(file, *(int *)arg, false);
338 break;
339 default:
340 ret = -EFAULT;
341 }
342
343 return ret;
344 }
345
346 static const struct v4l2_ioctl_ops fec_ioctl_ops = {
347 .vidioc_default = fec_ioctl_default,
348 };
349
fec_open(struct file * file)350 static int fec_open(struct file *file)
351 {
352 struct rkispp_fec_dev *fec = video_drvdata(file);
353 int ret;
354
355 ret = v4l2_fh_open(file);
356 if (ret)
357 goto end;
358
359 mutex_lock(&fec->hw->dev_lock);
360 ret = pm_runtime_get_sync(fec->hw->dev);
361 mutex_unlock(&fec->hw->dev_lock);
362 if (ret < 0)
363 v4l2_fh_release(file);
364 end:
365 v4l2_dbg(1, rkispp_debug, &fec->v4l2_dev,
366 "%s ret:%d\n", __func__, ret);
367 return (ret > 0) ? 0 : ret;
368 }
369
fec_release(struct file * file)370 static int fec_release(struct file *file)
371 {
372 struct rkispp_fec_dev *fec = video_drvdata(file);
373
374 v4l2_dbg(1, rkispp_debug, &fec->v4l2_dev, "%s\n", __func__);
375
376 v4l2_fh_release(file);
377 fec_buf_del(file, 0, true);
378 mutex_lock(&fec->hw->dev_lock);
379 pm_runtime_put_sync(fec->hw->dev);
380 mutex_unlock(&fec->hw->dev_lock);
381 return 0;
382 }
383
384 static const struct v4l2_file_operations fec_fops = {
385 .owner = THIS_MODULE,
386 .open = fec_open,
387 .release = fec_release,
388 .unlocked_ioctl = video_ioctl2,
389 #ifdef CONFIG_COMPAT
390 .compat_ioctl32 = video_ioctl2,
391 #endif
392 };
393
394 static const struct video_device fec_videodev = {
395 .name = "rkispp_fec",
396 .vfl_dir = VFL_DIR_RX,
397 .fops = &fec_fops,
398 .ioctl_ops = &fec_ioctl_ops,
399 .minor = -1,
400 .release = video_device_release_empty,
401 };
402
rkispp_fec_irq(struct rkispp_hw_dev * hw)403 void rkispp_fec_irq(struct rkispp_hw_dev *hw)
404 {
405 v4l2_dbg(3, rkispp_debug, &hw->fec_dev.v4l2_dev,
406 "%s\n", __func__);
407
408 if (!completion_done(&hw->fec_dev.cmpl))
409 complete(&hw->fec_dev.cmpl);
410 }
411
rkispp_register_fec(struct rkispp_hw_dev * hw)412 int rkispp_register_fec(struct rkispp_hw_dev *hw)
413 {
414 struct rkispp_fec_dev *fec = &hw->fec_dev;
415 struct v4l2_device *v4l2_dev;
416 struct video_device *vfd;
417 int ret;
418
419 if (!IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISPP_FEC))
420 return 0;
421
422 fec->hw = hw;
423 hw->is_fec_ext = true;
424 v4l2_dev = &fec->v4l2_dev;
425 strlcpy(v4l2_dev->name, fec_videodev.name, sizeof(v4l2_dev->name));
426 ret = v4l2_device_register(hw->dev, v4l2_dev);
427 if (ret)
428 return ret;
429
430 mutex_init(&fec->apilock);
431 fec->vfd = fec_videodev;
432 vfd = &fec->vfd;
433 vfd->device_caps = V4L2_CAP_STREAMING;
434 vfd->lock = &fec->apilock;
435 vfd->v4l2_dev = v4l2_dev;
436 ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
437 if (ret) {
438 v4l2_err(v4l2_dev, "Failed to register video device\n");
439 goto unreg_v4l2;
440 }
441 video_set_drvdata(vfd, fec);
442 INIT_LIST_HEAD(&fec->list);
443 return 0;
444 unreg_v4l2:
445 mutex_destroy(&fec->apilock);
446 v4l2_device_unregister(v4l2_dev);
447 return ret;
448 }
449
rkispp_unregister_fec(struct rkispp_hw_dev * hw)450 void rkispp_unregister_fec(struct rkispp_hw_dev *hw)
451 {
452 if (!IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISPP_FEC))
453 return;
454
455 mutex_destroy(&hw->fec_dev.apilock);
456 video_unregister_device(&hw->fec_dev.vfd);
457 v4l2_device_unregister(&hw->fec_dev.v4l2_dev);
458 }
459