diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 4403aa93039d..16722c0515d1 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -139,6 +139,7 @@ source "drivers/media/platform/rcar-vin/Kconfig" source "drivers/media/platform/atmel/Kconfig" source "drivers/media/platform/rockchip/cif/Kconfig" source "drivers/media/platform/rockchip/isp/Kconfig" +source "drivers/media/platform/rockchip/ispp/Kconfig" config VIDEO_TI_CAL tristate "TI CAL (Camera Adaptation Layer) driver" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 79b8858caf8f..7da84e5bf1bb 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/ obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip/cif/ obj-$(CONFIG_VIDEO_ROCKCHIP_ISP) += rockchip/isp/ +obj-$(CONFIG_VIDEO_ROCKCHIP_ISPP) += rockchip/ispp/ obj-y += omap/ diff --git a/drivers/media/platform/rockchip/ispp/Kconfig b/drivers/media/platform/rockchip/ispp/Kconfig new file mode 100644 index 000000000000..ff7bf6a6fe02 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +config VIDEO_ROCKCHIP_ISPP + tristate "Rockchip Image Signal Post-Processing Unit driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on ARCH_ROCKCHIP || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC + default n + help + Support for ISPP on the rockchip SoC. diff --git a/drivers/media/platform/rockchip/ispp/Makefile b/drivers/media/platform/rockchip/ispp/Makefile new file mode 100644 index 000000000000..8b324bb7dd3c --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_VIDEO_ROCKCHIP_ISPP) += video_rkispp.o + +video_rkispp-objs += dev.o \ + common.o \ + ispp.o \ + stream.o \ + params.o diff --git a/drivers/media/platform/rockchip/ispp/common.c b/drivers/media/platform/rockchip/ispp/common.c new file mode 100644 index 000000000000..5c22ff1e4023 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/common.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019 Rockchip Electronics Co., Ltd */ + +#include +#include +#include "dev.h" +#include "regs.h" + +int rkispp_fh_open(struct file *filp) +{ + struct rkispp_stream *stream = video_drvdata(filp); + struct rkispp_device *isppdev = stream->isppdev; + int ret; + + ret = v4l2_fh_open(filp); + if (!ret) { + ret = v4l2_pipeline_pm_use(&stream->vnode.vdev.entity, 1); + if (ret < 0) { + v4l2_err(&isppdev->v4l2_dev, + "pipeline power on failed %d\n", ret); + vb2_fop_release(filp); + } + } + return ret; +} + +int rkispp_fh_release(struct file *filp) +{ + struct rkispp_stream *stream = video_drvdata(filp); + struct rkispp_device *isppdev = stream->isppdev; + int ret; + + ret = vb2_fop_release(filp); + if (!ret) { + ret = v4l2_pipeline_pm_use(&stream->vnode.vdev.entity, 0); + if (ret < 0) + v4l2_err(&isppdev->v4l2_dev, + "pipeline power off failed %d\n", ret); + } + return ret; +} + +int rkispp_allow_buffer(struct rkispp_device *dev, + struct rkispp_dummy_buffer *buf) +{ + buf->vaddr = dma_alloc_coherent(dev->dev, buf->size, + &buf->dma_addr, GFP_KERNEL); + if (!buf->vaddr) + return -ENOMEM; + return 0; +} + +void rkispp_free_buffer(struct rkispp_device *dev, + struct rkispp_dummy_buffer *buf) +{ + if (buf && buf->vaddr && buf->size) { + dma_free_coherent(dev->dev, buf->size, + buf->vaddr, buf->dma_addr); + buf->size = 0; + buf->vaddr = NULL; + } +} diff --git a/drivers/media/platform/rockchip/ispp/common.h b/drivers/media/platform/rockchip/ispp/common.h new file mode 100644 index 000000000000..f7b24afa8a5e --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/common.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_COMMON_H +#define _RKISPP_COMMON_H + +#include +#include +#include +#include +#include +#include + +#define RKISPP_PLANE_Y 0 +#define RKISPP_PLANE_UV 1 + +struct rkispp_device; + +/* One structure per video node */ +struct rkispp_vdev_node { + struct vb2_queue buf_queue; + struct video_device vdev; + struct media_pad pad; +}; + +struct rkispp_buffer { + struct vb2_v4l2_buffer vb; + struct list_head queue; + union { + u32 buff_addr[VIDEO_MAX_PLANES]; + void *vaddr[VIDEO_MAX_PLANES]; + }; +}; + +struct rkispp_dummy_buffer { + struct list_head queue; + dma_addr_t dma_addr; + void *vaddr; + u32 size; +}; + +extern int rkispp_tnr_3to1; +extern int rkispp_debug; + +static inline struct rkispp_vdev_node *vdev_to_node(struct video_device *vdev) +{ + return container_of(vdev, struct rkispp_vdev_node, vdev); +} + +static inline struct rkispp_vdev_node *queue_to_node(struct vb2_queue *q) +{ + return container_of(q, struct rkispp_vdev_node, buf_queue); +} + +static inline struct rkispp_buffer *to_rkispp_buffer(struct vb2_v4l2_buffer *vb) +{ + return container_of(vb, struct rkispp_buffer, vb); +} + +static inline struct vb2_queue *to_vb2_queue(struct file *file) +{ + struct rkispp_vdev_node *vnode = video_drvdata(file); + + return &vnode->buf_queue; +} + +static inline void rkispp_write(void __iomem *addr, u32 val) +{ + writel(val, addr); +} + +static inline u32 rkispp_read(void __iomem *addr) +{ + return readl(addr); +} + +static inline void rkispp_set_bits(void __iomem *addr, u32 bit_mask, u32 val) +{ + u32 tmp = rkispp_read(addr) & ~bit_mask; + + rkispp_write(addr, val | tmp); +} + +static inline void rkispp_clear_bits(void __iomem *addr, u32 bit_mask) +{ + u32 val = rkispp_read(addr); + + rkispp_write(addr, val & ~bit_mask); +} + +int rkispp_fh_open(struct file *filp); +int rkispp_fh_release(struct file *filp); +int rkispp_allow_buffer(struct rkispp_device *dev, + struct rkispp_dummy_buffer *buf); +void rkispp_free_buffer(struct rkispp_device *dev, + struct rkispp_dummy_buffer *buf); + +#endif diff --git a/drivers/media/platform/rockchip/ispp/dev.c b/drivers/media/platform/rockchip/ispp/dev.c new file mode 100644 index 000000000000..ab1487634cc7 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/dev.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019 Rockchip Electronics Co., Ltd */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev.h" +#include "regs.h" +#include "version.h" + +#define RKISPP_VERNO_LEN 10 + +struct ispp_irqs_data { + const char *name; + irqreturn_t (*irq_hdl)(int irq, void *ctx); +}; + +struct ispp_match_data { + int clks_num; + const char * const *clks; + enum rkispp_ver ispp_ver; + struct ispp_irqs_data *irqs; + int num_irqs; +}; + +int rkispp_tnr_3to1; +module_param_named(tnr_3to1, rkispp_tnr_3to1, int, 0644); +MODULE_PARM_DESC(tnr_3to1, "tnr 3to1 mode"); + +int rkispp_debug; +module_param_named(debug, rkispp_debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-3)"); + +static char rkispp_version[RKISPP_VERNO_LEN]; +module_param_string(version, rkispp_version, RKISPP_VERNO_LEN, 0444); +MODULE_PARM_DESC(version, "version number"); + +static inline bool is_iommu_enable(struct device *dev) +{ + struct device_node *iommu; + + iommu = of_parse_phandle(dev->of_node, "iommus", 0); + if (!iommu) { + dev_info(dev, "no iommu attached, using non-iommu buffers\n"); + return false; + } else if (!of_device_is_available(iommu)) { + dev_info(dev, "iommu is disabled, using non-iommu buffers\n"); + of_node_put(iommu); + return false; + } + of_node_put(iommu); + + return true; +} + +#if defined(CONFIG_VIDEO_ROCKCHIP_ISP) +extern void +rkisp_get_mpfbc_sd(struct platform_device *dev, struct v4l2_subdev **sd); +#else +static void +rkisp_get_mpfbc_sd(struct platform_device *dev, struct v4l2_subdev **sd) +{ + *sd = NULL; +} +#endif +static void get_remote_node_dev(struct rkispp_device *ispp_dev) +{ + struct device *dev = ispp_dev->dev; + struct device_node *parent = dev->of_node; + struct platform_device *remote_dev = NULL; + struct device_node *remote = NULL; + struct v4l2_subdev *sd = NULL; + int i; + + for (i = 0; i < 2; i++) { + remote = of_graph_get_remote_node(parent, 0, i); + if (!remote) + continue; + remote_dev = of_find_device_by_node(remote); + of_node_put(remote); + if (!remote_dev) { + dev_err(dev, "Failed to get remote device(%s)\n", + of_node_full_name(remote)); + continue; + } else { + rkisp_get_mpfbc_sd(remote_dev, &sd); + if (!sd) { + dev_err(dev, "Failed to get remote mpfbc sd\n"); + } else { + ispp_dev->ispp_sdev.remote_sd = sd; + dev_info(dev, "get remote mpfbc sd:%s\n", + sd->name); + break; + } + } + } +} + +static int rkispp_create_links(struct rkispp_device *ispp_dev) +{ + struct rkispp_stream_vdev *stream_vdev; + struct media_entity *source, *sink; + struct rkispp_stream *stream; + unsigned int flags = 0; + int ret; + + stream_vdev = &ispp_dev->stream_vdev; + stream = &stream_vdev->stream[STREAM_II]; + /* input stream links */ + sink = &ispp_dev->ispp_sdev.sd.entity; + get_remote_node_dev(ispp_dev); + if (ispp_dev->ispp_sdev.remote_sd) { + ispp_dev->inp = INP_ISP; + } else { + flags = MEDIA_LNK_FL_ENABLED; + ispp_dev->inp = INP_DDR; + stream->linked = true; + } + source = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, 0, sink, + RKISPP_PAD_SINK, flags); + if (ret < 0) + return ret; + + /* params links */ + flags = MEDIA_LNK_FL_ENABLED; + source = &ispp_dev->params_vdev.vnode.vdev.entity; + ret = media_create_pad_link(source, 0, sink, + RKISPP_PAD_SINK_PARAMS, flags); + if (ret < 0) + return ret; + + /* output stream links */ + stream = &stream_vdev->stream[STREAM_MB]; + stream->linked = flags; + source = &ispp_dev->ispp_sdev.sd.entity; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKISPP_PAD_SOURCE, + sink, 0, flags); + if (ret < 0) + return ret; + + flags = 0; + stream = &stream_vdev->stream[STREAM_S0]; + stream->linked = flags; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKISPP_PAD_SOURCE, + sink, 0, flags); + if (ret < 0) + return ret; + + stream = &stream_vdev->stream[STREAM_S1]; + stream->linked = flags; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKISPP_PAD_SOURCE, + sink, 0, flags); + if (ret < 0) + return ret; + + stream = &stream_vdev->stream[STREAM_S2]; + sink = &stream->vnode.vdev.entity; + ret = media_create_pad_link(source, RKISPP_PAD_SOURCE, + sink, 0, flags); + if (ret < 0) + return ret; + + //TODO + ispp_dev->module_en[ISPP_3DNR] = true; + ispp_dev->module_en[ISPP_2DNR] = true; + ispp_dev->module_en[ISPP_SHP] = true; + ispp_dev->module_en[ISPP_FEC] = false; + ispp_dev->module_en[ISPP_SCL] = true; + return 0; +} + +static int rkispp_register_platform_subdevs(struct rkispp_device *ispp_dev) +{ + int ret; + + ret = rkispp_register_stream_vdevs(ispp_dev); + if (ret < 0) + return ret; + + ret = rkispp_register_params_vdev(ispp_dev); + if (ret < 0) + goto err_unreg_stream_vdevs; + + ret = rkispp_register_subdev(ispp_dev, &ispp_dev->v4l2_dev); + if (ret < 0) + goto err_unreg_params_vdev; + + ret = rkispp_create_links(ispp_dev); + if (ret < 0) + goto err_unreg_ispp_subdev; + return ret; +err_unreg_ispp_subdev: + rkispp_unregister_subdev(ispp_dev); +err_unreg_params_vdev: + rkispp_unregister_params_vdev(ispp_dev); +err_unreg_stream_vdevs: + rkispp_unregister_stream_vdevs(ispp_dev); + return ret; +} + +static void rkispp_disable_sys_clk(struct rkispp_device *ispp_dev) +{ + int i; + + for (i = 0; i < ispp_dev->clks_num; i++) + clk_disable_unprepare(ispp_dev->clks[i]); +} + +static int rkispp_enable_sys_clk(struct rkispp_device *ispp_dev) +{ + int i, ret = -EINVAL; + + for (i = 0; i < ispp_dev->clks_num; i++) { + ret = clk_prepare_enable(ispp_dev->clks[i]); + if (ret < 0) + goto err; + } + + return 0; +err: + for (--i; i >= 0; --i) + clk_disable_unprepare(ispp_dev->clks[i]); + return ret; +} + +static const struct of_device_id rkispp_plat_of_match[] = { + {}, +}; + +static int rkispp_plat_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct ispp_match_data *match_data; + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct v4l2_device *v4l2_dev; + struct rkispp_device *ispp_dev; + struct resource *res; + int i, ret, irq; + + sprintf(rkispp_version, "v%02x.%02x.%02x", + RKISPP_DRIVER_VERSION >> 16, + (RKISPP_DRIVER_VERSION & 0xff00) >> 8, + RKISPP_DRIVER_VERSION & 0x00ff); + + dev_info(dev, "rkispp driver version: %s\n", rkispp_version); + + match = of_match_node(rkispp_plat_of_match, node); + if (IS_ERR(match)) + return PTR_ERR(match); + + ispp_dev = devm_kzalloc(dev, sizeof(*ispp_dev), GFP_KERNEL); + if (!ispp_dev) + return -ENOMEM; + + dev_set_drvdata(dev, ispp_dev); + ispp_dev->dev = dev; + match_data = match->data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ispp_dev->base_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(ispp_dev->base_addr)) { + dev_err(dev, "get resource failed\n"); + return PTR_ERR(ispp_dev->base_addr); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + match_data->irqs[0].name); + if (res) { + /* there are irq names in dts */ + for (i = 0; i < match_data->num_irqs; i++) { + irq = platform_get_irq_byname(pdev, + match_data->irqs[i].name); + if (irq < 0) { + dev_err(dev, "no irq %s in dts\n", + match_data->irqs[i].name); + return irq; + } + ret = devm_request_irq(dev, irq, + match_data->irqs[i].irq_hdl, + IRQF_SHARED, + dev_driver_string(dev), + dev); + if (ret < 0) { + dev_err(dev, "request %s failed: %d\n", + match_data->irqs[i].name, ret); + return ret; + } + } + } + + for (i = 0; i < match_data->clks_num; i++) { + struct clk *clk = devm_clk_get(dev, match_data->clks[i]); + + if (IS_ERR(clk)) + dev_warn(dev, "failed to get %s\n", + match_data->clks[i]); + ispp_dev->clks[i] = clk; + } + ispp_dev->clks_num = match_data->clks_num; + ispp_dev->ispp_ver = match_data->ispp_ver; + + mutex_init(&ispp_dev->apilock); + + strlcpy(ispp_dev->media_dev.model, "rkispp", + sizeof(ispp_dev->media_dev.model)); + ispp_dev->media_dev.dev = &pdev->dev; + v4l2_dev = &ispp_dev->v4l2_dev; + v4l2_dev->mdev = &ispp_dev->media_dev; + strlcpy(v4l2_dev->name, "rkispp", sizeof(v4l2_dev->name)); + v4l2_ctrl_handler_init(&ispp_dev->ctrl_handler, 5); + v4l2_dev->ctrl_handler = &ispp_dev->ctrl_handler; + + ret = v4l2_device_register(ispp_dev->dev, v4l2_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "register v4l2 device failed:%d\n", ret); + return ret; + } + media_device_init(&ispp_dev->media_dev); + ret = media_device_register(&ispp_dev->media_dev); + if (ret < 0) { + v4l2_err(v4l2_dev, "register media device failed:%d\n", ret); + goto err_unreg_v4l2_dev; + } + + ret = rkispp_register_platform_subdevs(ispp_dev); + if (ret < 0) + goto err_unreg_media_dev; + + if (!is_iommu_enable(dev)) { + ret = of_reserved_mem_device_init(dev); + if (ret) + v4l2_warn(v4l2_dev, + "No reserved memory region assign to ispp\n"); + } + + pm_runtime_enable(&pdev->dev); + + return 0; + +err_unreg_media_dev: + media_device_unregister(&ispp_dev->media_dev); +err_unreg_v4l2_dev: + v4l2_device_unregister(&ispp_dev->v4l2_dev); + return ret; +} + +static int rkispp_plat_remove(struct platform_device *pdev) +{ + struct rkispp_device *ispp_dev = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + rkispp_unregister_subdev(ispp_dev); + rkispp_unregister_params_vdev(ispp_dev); + rkispp_unregister_stream_vdevs(ispp_dev); + + media_device_unregister(&ispp_dev->media_dev); + v4l2_device_unregister(&ispp_dev->v4l2_dev); + mutex_destroy(&ispp_dev->apilock); + return 0; +} + +static int __maybe_unused rkispp_runtime_suspend(struct device *dev) +{ + struct rkispp_device *ispp_dev = dev_get_drvdata(dev); + + rkispp_disable_sys_clk(ispp_dev); + return 0; +} + +static int __maybe_unused rkispp_runtime_resume(struct device *dev) +{ + struct rkispp_device *ispp_dev = dev_get_drvdata(dev); + + rkispp_enable_sys_clk(ispp_dev); + return 0; +} + +static const struct dev_pm_ops rkispp_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(rkispp_runtime_suspend, + rkispp_runtime_resume, NULL) +}; + +static struct platform_driver rkispp_plat_drv = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(rkispp_plat_of_match), + .pm = &rkispp_plat_pm_ops, + }, + .probe = rkispp_plat_probe, + .remove = rkispp_plat_remove, +}; + +module_platform_driver(rkispp_plat_drv); +MODULE_AUTHOR("Rockchip Camera/ISP team"); +MODULE_DESCRIPTION("Rockchip ISPP platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/rockchip/ispp/dev.h b/drivers/media/platform/rockchip/ispp/dev.h new file mode 100644 index 000000000000..c3a9824d2e5a --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/dev.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_DEV_H +#define _RKISPP_DEV_H + +#include "ispp.h" +#include "params.h" +#include "stream.h" + +#define DRIVER_NAME "rkispp" +#define II_VDEV_NAME DRIVER_NAME "_input_image" +#define MB_VDEV_NAME DRIVER_NAME "_m_bypass" +#define S0_VDEV_NAME DRIVER_NAME "_scale0" +#define S1_VDEV_NAME DRIVER_NAME "_scale1" +#define S2_VDEV_NAME DRIVER_NAME "_scale2" + +#define ISPP_MAX_BUS_CLK 4 + +enum rkispp_ver { + ISPP_V10 = 0x00, +}; + +enum rkispp_module_id { + ISPP_3DNR, + ISPP_2DNR, + ISPP_SHP, + ISPP_FEC, + ISPP_SCL, + ISPP_MD_MAX, +}; + +enum rkispp_input { + INP_INVAL = 0, + INP_ISP, + INP_DDR, +}; + +struct rkispp_device { + struct device *dev; + int irq; + int clks_num; + struct clk *clks[ISPP_MAX_BUS_CLK]; + void __iomem *base_addr; + struct iommu_domain *domain; + struct vb2_alloc_ctx *alloc_ctx; + + struct media_device media_dev; + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + + struct rkispp_subdev ispp_sdev; + struct rkispp_stream_vdev stream_vdev; + struct rkispp_params_vdev params_vdev; + + enum rkispp_ver ispp_ver; + /* mutex to serialize the calls from user */ + struct mutex apilock; + + u8 module_en[ISPP_MD_MAX]; + enum rkispp_input inp; +}; +#endif diff --git a/drivers/media/platform/rockchip/ispp/ispp.c b/drivers/media/platform/rockchip/ispp/ispp.c new file mode 100644 index 000000000000..5aceda9eb380 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/ispp.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include + +#include "dev.h" +#include "regs.h" + +u32 cal_fec_mesh(u32 width, u32 height, u32 mode) +{ + u32 mesh_size, mesh_left_height; + u32 w = ALIGN(width, 32); + u32 h = ALIGN(height, 32); + u32 spb_num = (h + 127) >> 7; + u32 left_height = h & 127; + u32 mesh_width = mode ? (w / 32 + 1) : (w / 16 + 1); + u32 mesh_height = mode ? 9 : 17; + + if (!left_height) + left_height = 128; + mesh_left_height = mode ? (left_height / 16 + 1) : + (left_height / 8 + 1); + mesh_size = (spb_num - 1) * mesh_width * mesh_height + + mesh_width * mesh_left_height; + + return mesh_size; +} + +static const struct isppsd_fmt rkispp_formats[] = { + { + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, + .fourcc = V4L2_PIX_FMT_NV16, + .wr_fmt = FMT_YUV422, + }, +}; + +static const struct isppsd_fmt *find_fmt(u32 mbus_code) +{ + const struct isppsd_fmt *fmt; + int i, array_size = ARRAY_SIZE(rkispp_formats); + + for (i = 0; i < array_size; i++) { + fmt = &rkispp_formats[i]; + if (fmt->mbus_code == mbus_code) + return fmt; + } + + return NULL; +} + +static int rkispp_subdev_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, + u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct rkispp_subdev *ispp_sdev; + struct rkispp_device *dev; + struct rkispp_stream_vdev *vdev; + struct rkispp_stream *stream = NULL; + + if (!sd) + return -ENODEV; + ispp_sdev = v4l2_get_subdevdata(sd); + dev = ispp_sdev->dev; + vdev = &dev->stream_vdev; + + if (!strcmp(remote->entity->name, II_VDEV_NAME)) { + stream = &vdev->stream[STREAM_II]; + if (flags & MEDIA_LNK_FL_ENABLED) + dev->inp = INP_DDR; + else if (ispp_sdev->remote_sd) + dev->inp = INP_ISP; + else + dev->inp = INP_INVAL; + } else if (!strcmp(remote->entity->name, MB_VDEV_NAME)) { + stream = &vdev->stream[STREAM_MB]; + } else if (!strcmp(remote->entity->name, S0_VDEV_NAME)) { + stream = &vdev->stream[STREAM_S0]; + } else if (!strcmp(remote->entity->name, S1_VDEV_NAME)) { + stream = &vdev->stream[STREAM_S1]; + } else if (!strcmp(remote->entity->name, S2_VDEV_NAME)) { + stream = &vdev->stream[STREAM_S2]; + } + if (stream) + stream->linked = flags & MEDIA_LNK_FL_ENABLED; + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "input:%d\n", dev->inp); + return 0; +} + +static int rkispp_sd_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd); + struct rkispp_device *dev = ispp_sdev->dev; + const struct isppsd_fmt *ispp_fmt; + int ret = 0; + + if (dev->inp != INP_ISP) + return 0; + + ret = v4l2_subdev_call(ispp_sdev->remote_sd, + pad, get_fmt, cfg, fmt); + if (ret == 0) { + ispp_fmt = find_fmt(fmt->format.code); + if (!ispp_fmt) + return -EINVAL; + ispp_sdev->in_fmt = *fmt; + ispp_sdev->out_fmt = *ispp_fmt; + ispp_sdev->out_fmt.width = fmt->format.width; + ispp_sdev->out_fmt.height = fmt->format.height; + } + + return ret; +} + +static int rkispp_sd_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd); + struct rkispp_device *dev = ispp_sdev->dev; + const struct isppsd_fmt *ispp_fmt; + + if (dev->inp != INP_ISP) + return 0; + + ispp_fmt = find_fmt(fmt->format.code); + if (!ispp_fmt) + return -EINVAL; + + ispp_sdev->out_fmt = *ispp_fmt; + ispp_sdev->in_fmt = *fmt; + return v4l2_subdev_call(ispp_sdev->remote_sd, pad, + set_fmt, NULL, fmt); +} + +static int rkispp_sd_s_stream(struct v4l2_subdev *sd, int on) +{ + struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd); + struct rkispp_device *dev = ispp_sdev->dev; + struct rkispp_stream_vdev *vdev; + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "s_stream on:%d\n", on); + + vdev = &dev->stream_vdev; + if (on) { + void *buf, *size; + + if (dev->module_en[ISPP_3DNR]) { + buf = &vdev->tnr_buf.pic_cur.dma_addr; + size = &vdev->tnr_buf.pic_cur.size; + v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_rx_buffer, buf, size); + buf = &vdev->tnr_buf.gain_cur.dma_addr; + size = &vdev->tnr_buf.gain_cur.size; + if (rkispp_tnr_3to1) { + v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_rx_buffer, buf, size); + buf = &vdev->tnr_buf.pic_next.dma_addr; + size = &vdev->tnr_buf.pic_next.size; + v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_rx_buffer, buf, size); + buf = &vdev->tnr_buf.gain_next.dma_addr; + size = &vdev->tnr_buf.gain_next.size; + } + } else { + buf = &vdev->nr_buf.pic_cur.dma_addr; + size = &vdev->nr_buf.pic_cur.size; + v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_rx_buffer, buf, size); + buf = &vdev->nr_buf.gain_cur.dma_addr; + size = &vdev->nr_buf.gain_cur.size; + } + v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_rx_buffer, buf, size); + } + return v4l2_subdev_call(ispp_sdev->remote_sd, + video, s_stream, on); +} + +static int rkispp_sd_s_power(struct v4l2_subdev *sd, int on) +{ + struct rkispp_subdev *ispp_sdev = v4l2_get_subdevdata(sd); + struct rkispp_device *ispp_dev = ispp_sdev->dev; + int ret; + + v4l2_dbg(1, rkispp_debug, &ispp_dev->v4l2_dev, + "s_power on:%d\n", on); + + if (on) { + ret = pm_runtime_get_sync(ispp_dev->dev); + if (ret < 0) { + v4l2_err(&ispp_dev->v4l2_dev, + "%s runtime get failed:%d\n", + __func__, ret); + return ret; + } + atomic_set(&ispp_sdev->frm_sync_seq, 0); + rkispp_soft_reset(ispp_dev->base_addr); + writel(0xfffffff, ispp_dev->base_addr + RKISPP_CTRL_INT_MSK); + if (ispp_dev->inp == INP_ISP) { + struct v4l2_subdev_format *fmt = &ispp_sdev->in_fmt; + + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, fmt); + if (ret < 0) { + v4l2_err(&ispp_dev->v4l2_dev, + "%s get format fail:%d\n", + __func__, ret); + goto err; + } + ret = v4l2_subdev_call(ispp_sdev->remote_sd, + core, s_power, 1); + if (ret < 0) { + v4l2_err(&ispp_dev->v4l2_dev, + "%s set isp power on fail:%d\n", + __func__, ret); + goto err; + } + } + } else { + writel(0, ispp_dev->base_addr + RKISPP_CTRL_INT_MSK); + if (ispp_dev->inp == INP_ISP) + v4l2_subdev_call(ispp_sdev->remote_sd, core, s_power, 0); + ret = pm_runtime_put(ispp_dev->dev); + if (ret < 0) + v4l2_err(&ispp_dev->v4l2_dev, + "%s runtime put failed:%d\n", + __func__, ret); + } + + return ret; +err: + pm_runtime_put(ispp_dev->dev); + return ret; +} + +static const struct media_entity_operations rkispp_sd_media_ops = { + .link_setup = rkispp_subdev_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_pad_ops rkispp_sd_pad_ops = { + .get_fmt = rkispp_sd_get_fmt, + .set_fmt = rkispp_sd_set_fmt, +}; + +static const struct v4l2_subdev_video_ops rkispp_sd_video_ops = { + .s_stream = rkispp_sd_s_stream, +}; + +static const struct v4l2_subdev_core_ops rkispp_sd_core_ops = { + .s_power = rkispp_sd_s_power, +}; + +static struct v4l2_subdev_ops rkispp_sd_ops = { + .core = &rkispp_sd_core_ops, + .video = &rkispp_sd_video_ops, + .pad = &rkispp_sd_pad_ops, +}; + +int rkispp_register_subdev(struct rkispp_device *dev, + struct v4l2_device *v4l2_dev) +{ + struct rkispp_subdev *ispp_sdev = &dev->ispp_sdev; + struct v4l2_subdev *sd; + int ret; + + memset(ispp_sdev, 0, sizeof(*ispp_sdev)); + ispp_sdev->dev = dev; + sd = &ispp_sdev->sd; + + v4l2_subdev_init(sd, &rkispp_sd_ops); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->entity.ops = &rkispp_sd_media_ops; + snprintf(sd->name, sizeof(sd->name), "rkispp-subdev"); + + ispp_sdev->pads[RKISPP_PAD_SINK].flags = + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT; + ispp_sdev->pads[RKISPP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK; + ispp_sdev->pads[RKISPP_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + + ret = media_entity_pads_init(&sd->entity, RKISPP_PAD_MAX, + ispp_sdev->pads); + if (ret < 0) + return ret; + sd->owner = THIS_MODULE; + v4l2_set_subdevdata(sd, ispp_sdev); + sd->grp_id = GRP_ID_ISPP; + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) + goto free_media; + + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret < 0) + goto free_subdev; + return ret; +free_subdev: + v4l2_device_unregister_subdev(sd); +free_media: + media_entity_cleanup(&sd->entity); + v4l2_err(sd, "Failed to register subdev, ret:%d\n", ret); + return ret; +} + +void rkispp_unregister_subdev(struct rkispp_device *dev) +{ + struct v4l2_subdev *sd = &dev->ispp_sdev.sd; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); +} diff --git a/drivers/media/platform/rockchip/ispp/ispp.h b/drivers/media/platform/rockchip/ispp/ispp.h new file mode 100644 index 000000000000..fe6b1129ec7e --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/ispp.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_ISPP_H +#define _RKISPP_ISPP_H + +#include "common.h" + +#define GRP_ID_ISPP BIT(0) + +enum rkispp_pad { + RKISPP_PAD_SINK, + RKISPP_PAD_SINK_PARAMS, + RKISPP_PAD_SOURCE, + RKISPP_PAD_MAX +}; + +struct isppsd_fmt { + u32 mbus_code; + u32 fourcc; + u32 width; + u32 height; + u8 wr_fmt; +}; + +struct rkispp_subdev { + struct rkispp_device *dev; + struct v4l2_subdev sd; + struct v4l2_subdev *remote_sd; + struct media_pad pads[RKISPP_PAD_MAX]; + struct v4l2_subdev_format in_fmt; + struct isppsd_fmt out_fmt; + atomic_t frm_sync_seq; +}; + +u32 cal_fec_mesh(u32 width, u32 height, u32 mode); + +int rkispp_register_subdev(struct rkispp_device *dev, + struct v4l2_device *v4l2_dev); +void rkispp_unregister_subdev(struct rkispp_device *dev); +#endif diff --git a/drivers/media/platform/rockchip/ispp/params.c b/drivers/media/platform/rockchip/ispp/params.c new file mode 100644 index 000000000000..8975bd299206 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/params.c @@ -0,0 +1,794 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include "dev.h" +#include "regs.h" + +#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2 +#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8 + +#define ISPP_PACK_4BIT(a, b, c, d, e, f, g, h) \ + (((a) & 0xf) << 0 | ((b) & 0xf) << 4 | \ + ((c) & 0xf) << 8 | ((d) & 0xf) << 12 | \ + ((e) & 0xf) << 16 | ((f) & 0xf) << 20 | \ + ((g) & 0xf) << 24 | ((h) & 0xf) << 28) + +#define ISPP_PACK_4BYTE(a, b, c, d) \ + (((a) & 0xFF) << 0 | ((b) & 0xFF) << 8 | \ + ((c) & 0xFF) << 16 | ((d) & 0xFF) << 24) + +#define ISPP_PACK_2SHORT(a, b) \ + (((a) & 0xFFFF) << 0 | ((b) & 0xFFFF) << 16) + +static void tnr_config(struct rkispp_params_vdev *params_vdev, + struct rkispp_tnr_config *arg) +{ + void __iomem *base = params_vdev->dev->base_addr; + u32 i, val; + + val = arg->opty_en << 2 | arg->optc_en << 3 | + arg->gain_en << 4; + rkispp_set_bits(base + RKISPP_TNR_CORE_CTRL, + SW_TNR_OPTY_EN | SW_TNR_OPTC_EN | + SW_TNR_GLB_GAIN_EN, val); + + val = ISPP_PACK_4BYTE(arg->pk0_y, arg->pk1_y, + arg->pk0_c, arg->pk1_c); + rkispp_write(base + RKISPP_TNR_CORE_PK0, val); + + val = ISPP_PACK_2SHORT(arg->glb_gain_cur, arg->glb_gain_nxt); + rkispp_write(base + RKISPP_TNR_CORE_GLB_GAIN, val); + val = ISPP_PACK_2SHORT(arg->glb_gain_cur_div, arg->glb_gain_cur_sqrt); + rkispp_write(base + RKISPP_TNR_CORE_GLB_GAIN_DIV, val); + + for (i = 0; i < TNR_SIGMA_CURVE_SIZE - 1; i += 2) + rkispp_write(base + RKISPP_TNR_CORE_SIG_Y01 + i * 2, + ISPP_PACK_2SHORT(arg->sigma_curve[i].y, + arg->sigma_curve[i + 1].y)); + rkispp_write(base + RKISPP_TNR_CORE_SIG_Y10, arg->sigma_curve[16].y); + rkispp_write(base + RKISPP_TNR_CORE_SIG_X18, + ISPP_PACK_4BIT(arg->sigma_curve[0].x, arg->sigma_curve[1].x, + arg->sigma_curve[2].x, arg->sigma_curve[3].x, + arg->sigma_curve[4].x, arg->sigma_curve[5].x, + arg->sigma_curve[6].x, arg->sigma_curve[7].x)); + rkispp_write(base + RKISPP_TNR_CORE_SIG_X910, + ISPP_PACK_4BIT(arg->sigma_curve[8].x, arg->sigma_curve[9].x, + arg->sigma_curve[10].x, arg->sigma_curve[11].x, + arg->sigma_curve[12].x, arg->sigma_curve[13].x, + arg->sigma_curve[14].x, arg->sigma_curve[15].x)); + + for (i = 0; i < TNR_LUMA_CURVE_SIZE; i += 2) { + val = ISPP_PACK_2SHORT(arg->luma_curve[i], arg->luma_curve[i + 1]); + rkispp_write(base + RKISPP_TNR_CORE_LUMACURVE_Y01 + i * 2, val); + } + + val = ISPP_PACK_2SHORT(arg->txt_th0_y, arg->txt_th1_y); + rkispp_write(base + RKISPP_TNR_CORE_TH_Y, val); + val = ISPP_PACK_2SHORT(arg->txt_th0_c, arg->txt_th1_c); + rkispp_write(base + RKISPP_TNR_CORE_TH_C, val); + val = ISPP_PACK_2SHORT(arg->txt_thy_dlt, arg->txt_thc_dlt); + rkispp_write(base + RKISPP_TNR_CORE_TH_DLT, val); + + val = ISPP_PACK_4BYTE(arg->gfcoef_y0[0], arg->gfcoef_y0[1], + arg->gfcoef_y0[2], arg->gfcoef_y0[3]); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_Y0_0, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_y0[4], arg->gfcoef_y0[5], 0, 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_Y0_1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_y1[0], arg->gfcoef_y1[1], + arg->gfcoef_y1[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_Y1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_y2[0], arg->gfcoef_y2[1], + arg->gfcoef_y2[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_Y2, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_y3[0], arg->gfcoef_y3[1], + arg->gfcoef_y3[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_Y3, val); + + val = ISPP_PACK_4BYTE(arg->gfcoef_yg0[0], arg->gfcoef_yg0[1], + arg->gfcoef_yg0[2], arg->gfcoef_yg0[3]); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YG0_0, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yg0[4], arg->gfcoef_yg0[5], 0, 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YG0_1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yg1[0], arg->gfcoef_yg1[1], + arg->gfcoef_yg1[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YG1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yg2[0], arg->gfcoef_yg2[1], + arg->gfcoef_yg2[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YG2, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yg3[0], arg->gfcoef_yg3[1], + arg->gfcoef_yg3[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YG3, val); + + val = ISPP_PACK_4BYTE(arg->gfcoef_yl0[0], arg->gfcoef_yl0[1], + arg->gfcoef_yl0[2], arg->gfcoef_yl0[3]); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YL0_0, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yl0[4], arg->gfcoef_yl0[5], 0, 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YL0_1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yl1[0], arg->gfcoef_yl1[1], + arg->gfcoef_yl1[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YL1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_yl2[0], arg->gfcoef_yl2[1], + arg->gfcoef_yl2[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_YL2, val); + + val = ISPP_PACK_4BYTE(arg->gfcoef_cg0[0], arg->gfcoef_cg0[1], + arg->gfcoef_cg0[2], arg->gfcoef_cg0[3]); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CG0_0, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_cg0[4], arg->gfcoef_cg0[5], 0, 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CG0_1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_cg1[0], arg->gfcoef_cg1[1], + arg->gfcoef_cg1[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CG1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_cg2[0], arg->gfcoef_cg2[1], + arg->gfcoef_cg2[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CG2, val); + + val = ISPP_PACK_4BYTE(arg->gfcoef_cl0[0], arg->gfcoef_cl0[1], + arg->gfcoef_cl0[2], arg->gfcoef_cl0[3]); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CL0_0, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_cl0[4], arg->gfcoef_cl0[5], 0, 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CL0_1, val); + val = ISPP_PACK_4BYTE(arg->gfcoef_cl1[0], arg->gfcoef_cl1[1], + arg->gfcoef_cl1[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_GFCOEF_CL1, val); + + val = ISPP_PACK_2SHORT(arg->scale_yg[0], arg->scale_yg[1]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_YG01, val); + val = ISPP_PACK_2SHORT(arg->scale_yg[2], arg->scale_yg[3]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_YG23, val); + val = ISPP_PACK_2SHORT(arg->scale_yl[0], arg->scale_yl[1]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_YL01, val); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_YL2, arg->scale_yl[2]); + val = ISPP_PACK_2SHORT(arg->scale_cg[0], arg->scale_y2cg[0]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CG0, val); + val = ISPP_PACK_2SHORT(arg->scale_cg[1], arg->scale_y2cg[1]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CG1, val); + val = ISPP_PACK_2SHORT(arg->scale_cg[2], arg->scale_y2cg[2]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CG2, val); + val = ISPP_PACK_2SHORT(arg->scale_cl[0], arg->scale_y2cl[0]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CL0, val); + val = ISPP_PACK_2SHORT(arg->scale_cl[1], arg->scale_y2cl[1]); + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CL1, val); + val = arg->scale_y2cl[2] << 16; + rkispp_write(base + RKISPP_TNR_CORE_SCALE_CL2, val); + val = ISPP_PACK_4BYTE(arg->weight_y[0], arg->weight_y[1], + arg->weight_y[2], 0); + rkispp_write(base + RKISPP_TNR_CORE_WEIGHT, val); +} + +static void tnr_enable(struct rkispp_params_vdev *params_vdev, bool en) +{ + void __iomem *base = params_vdev->dev->base_addr; + + rkispp_set_bits(base + RKISPP_TNR_CORE_CTRL, SW_TNR_EN, en); +} + +static void nr_config(struct rkispp_params_vdev *params_vdev, + struct rkispp_nr_config *arg) +{ + void __iomem *base = params_vdev->dev->base_addr; + u32 i, val; + + val = arg->uvnr_step1_en << 1 | arg->uvnr_step2_en << 2 | + arg->nr_gain_en << 3 | arg->uvnr_nobig_en << 5 | + arg->uvnr_big_en << 6; + rkispp_set_bits(base + RKISPP_NR_UVNR_CTRL_PARA, + SW_UVNR_STEP1_ON | SW_UVNR_STEP2_ON | + SW_NR_GAIN_BYPASS | SW_UVNR_NOBIG_EN | + SW_UVNR_BIG_EN, val); + + rkispp_write(base + RKISPP_NR_UVNR_GAIN_1SIGMA, + arg->uvnr_gain_1sigma); + rkispp_write(base + RKISPP_NR_UVNR_GAIN_OFFSET, + arg->uvnr_gain_offset); + val = ISPP_PACK_4BYTE(arg->uvnr_gain_uvgain[0], + arg->uvnr_gain_uvgain[1], arg->uvnr_gain_t2gen, + arg->uvnr_gain_iso); + rkispp_write(base + RKISPP_NR_UVNR_GAIN_GBLGAIN, val); + rkispp_write(base + RKISPP_NR_UVNR_T1GEN_M3ALPHA, + arg->uvnr_t1gen_m3alpha); + rkispp_write(base + RKISPP_NR_UVNR_T1FLT_MODE, + arg->uvnr_t1flt_mode); + rkispp_write(base + RKISPP_NR_UVNR_T1FLT_MSIGMA, + arg->uvnr_t1flt_msigma); + rkispp_write(base + RKISPP_NR_UVNR_T1FLT_WTP, + arg->uvnr_t1flt_wtp); + for (i = 0; i < NR_UVNR_T1FLT_WTQ_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->uvnr_t1flt_wtq[i], + arg->uvnr_t1flt_wtq[i + 1], arg->uvnr_t1flt_wtq[i + 2], + arg->uvnr_t1flt_wtq[i + 3]); + rkispp_write(base + RKISPP_NR_UVNR_T1FLT_WTQ0 + i, val); + } + rkispp_write(base + RKISPP_NR_UVNR_T2GEN_M3ALPHA, + arg->uvnr_t2gen_m3alpha); + rkispp_write(base + RKISPP_NR_UVNR_T2GEN_MSIGMA, + arg->uvnr_t2gen_msigma); + rkispp_write(base + RKISPP_NR_UVNR_T2GEN_WTP, + arg->uvnr_t2gen_wtp); + val = ISPP_PACK_4BYTE(arg->uvnr_t2gen_wtq[0], + arg->uvnr_t2gen_wtq[1], arg->uvnr_t2gen_wtq[2], + arg->uvnr_t2gen_wtq[3]); + rkispp_write(base + RKISPP_NR_UVNR_T2GEN_WTQ, val); + rkispp_write(base + RKISPP_NR_UVNR_T2FLT_MSIGMA, + arg->uvnr_t2flt_msigma); + val = ISPP_PACK_4BYTE(arg->uvnr_t2flt_wtp, + arg->uvnr_t2flt_wt[0], arg->uvnr_t2flt_wt[1], + arg->uvnr_t2flt_wt[2]); + rkispp_write(base + RKISPP_NR_UVNR_T2FLT_WT, val); + + val = ISPP_PACK_4BIT(arg->ynr_sgm_dx[0], arg->ynr_sgm_dx[1], + arg->ynr_sgm_dx[2], arg->ynr_sgm_dx[3], + arg->ynr_sgm_dx[4], arg->ynr_sgm_dx[5], + arg->ynr_sgm_dx[6], arg->ynr_sgm_dx[7]); + rkispp_write(base + RKISPP_NR_YNR_SGM_DX_1_8, val); + val = ISPP_PACK_4BIT(arg->ynr_sgm_dx[8], arg->ynr_sgm_dx[9], + arg->ynr_sgm_dx[10], arg->ynr_sgm_dx[11], + arg->ynr_sgm_dx[12], arg->ynr_sgm_dx[13], + arg->ynr_sgm_dx[14], arg->ynr_sgm_dx[15]); + rkispp_write(base + RKISPP_NR_YNR_SGM_DX_9_16, val); + + for (i = 0; i < NR_YNR_SGM_Y_SIZE - 1; i += 2) { + rkispp_write(base + RKISPP_NR_YNR_LSGM_Y_0_1 + i * 2, + ISPP_PACK_2SHORT(arg->ynr_lsgm_y[i], arg->ynr_lsgm_y[i + 1])); + + rkispp_write(base + RKISPP_NR_YNR_HSGM_Y_0_1 + i * 2, + ISPP_PACK_2SHORT(arg->ynr_hsgm_y[i], arg->ynr_hsgm_y[i + 1])); + } + rkispp_write(base + RKISPP_NR_YNR_LSGM_Y_16, arg->ynr_lsgm_y[16]); + rkispp_write(base + RKISPP_NR_YNR_HSGM_Y_16, arg->ynr_hsgm_y[16]); + + val = ISPP_PACK_4BYTE(arg->ynr_lci[0], arg->ynr_lci[1], + arg->ynr_lci[2], arg->ynr_lci[3]); + rkispp_write(base + RKISPP_NR_YNR_LCI, val); + val = ISPP_PACK_4BYTE(arg->ynr_lgain_min[0], arg->ynr_lgain_min[1], + arg->ynr_lgain_min[2], arg->ynr_lgain_min[3]); + rkispp_write(base + RKISPP_NR_YNR_LGAIN_DIRE_MIN, val); + rkispp_write(base + RKISPP_NR_YNR_IGAIN_DIRE_MAX, arg->ynr_lgain_max); + val = ISPP_PACK_4BYTE(arg->ynr_lmerge_bound, arg->ynr_lmerge_ratio, 0, 0); + rkispp_write(base + RKISPP_NR_YNR_LMERGE, val); + val = ISPP_PACK_4BYTE(arg->ynr_lweit_flt[0], arg->ynr_lweit_flt[1], + arg->ynr_lweit_flt[2], arg->ynr_lweit_flt[3]); + rkispp_write(base + RKISPP_NR_YNR_LWEIT_FLT, val); + val = ISPP_PACK_4BYTE(arg->ynr_hlci[0], arg->ynr_hlci[1], + arg->ynr_hlci[2], arg->ynr_hlci[3]); + rkispp_write(base + RKISPP_NR_YNR_HLCI, val); + val = ISPP_PACK_4BYTE(arg->ynr_lhci[0], arg->ynr_lhci[1], + arg->ynr_lhci[2], arg->ynr_lhci[3]); + rkispp_write(base + RKISPP_NR_YNR_LHCI, val); + val = ISPP_PACK_4BYTE(arg->ynr_hhci[0], arg->ynr_hhci[1], + arg->ynr_hhci[2], arg->ynr_hhci[3]); + rkispp_write(base + RKISPP_NR_YNR_HHCI, val); + val = ISPP_PACK_4BYTE(arg->ynr_hgain_sgm[0], arg->ynr_hgain_sgm[1], + arg->ynr_hgain_sgm[2], arg->ynr_hgain_sgm[3]); + rkispp_write(base + RKISPP_NR_YNR_HGAIN_SGM, val); + + for (i = 0; i < NR_YNR_HWEIT_D_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->ynr_hweit_d[i], arg->ynr_hweit_d[i + 1], + arg->ynr_hweit_d[i + 2], arg->ynr_hweit_d[i + 3]); + rkispp_write(base + RKISPP_NR_YNR_HWEIT_D0 + i, val); + } + + for (i = 0; i < NR_YNR_HGRAD_Y_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->ynr_hgrad_y[0], arg->ynr_hgrad_y[1], + arg->ynr_hgrad_y[2], arg->ynr_hgrad_y[3]); + rkispp_write(base + RKISPP_NR_YNR_HGRAD_Y0 + i, val); + } + + val = ISPP_PACK_2SHORT(arg->ynr_hweit[0], arg->ynr_hweit[1]); + rkispp_write(base + RKISPP_NR_YNR_HWEIT_1_2, val); + val = ISPP_PACK_2SHORT(arg->ynr_hweit[2], arg->ynr_hweit[3]); + rkispp_write(base + RKISPP_NR_YNR_HWEIT_3_4, val); + + rkispp_write(base + RKISPP_NR_YNR_HADJUST_EXGAIN, arg->ynr_hmax_adjust); + rkispp_write(base + RKISPP_NR_YNR_HSTRENGTH, arg->ynr_hstrength); + + val = ISPP_PACK_4BYTE(arg->ynr_lweit_cmp[0], arg->ynr_lweit_cmp[1], 0, 0); + rkispp_write(base + RKISPP_NR_YNR_LWEIT_CMP, val); + rkispp_write(base + RKISPP_NR_YNR_LMAXGAIN_LV4, arg->ynr_lmaxgain_lv4); + + for (i = 0; i < NR_YNR_HSTV_Y_SIZE - 1; i += 2) { + val = ISPP_PACK_2SHORT(arg->ynr_hstv_y[i], arg->ynr_hstv_y[i + 1]); + rkispp_write(base + RKISPP_NR_YNR_HSTV_Y_0_1 + i * 2, val); + } + rkispp_write(base + RKISPP_NR_YNR_HSTV_Y_16, arg->ynr_hstv_y[16]); + + val = ISPP_PACK_2SHORT(arg->ynr_st_scale[0], arg->ynr_st_scale[1]); + rkispp_write(base + RKISPP_NR_YNR_ST_SCALE_LV1_LV2, val); + rkispp_write(base + RKISPP_NR_YNR_ST_SCALE_LV3, arg->ynr_st_scale[2]); +} + +static void nr_enable(struct rkispp_params_vdev *params_vdev, bool en) +{ + void __iomem *base = params_vdev->dev->base_addr; + + rkispp_set_bits(base + RKISPP_NR_UVNR_CTRL_PARA, SW_NR_EN, en); +} + +static void shp_config(struct rkispp_params_vdev *params_vdev, + struct rkispp_sharp_config *arg) +{ + void __iomem *base = params_vdev->dev->base_addr; + u32 i, val; + + val = arg->alpha_adp_en << 1 | arg->yin_flt_en << 3 | + arg->edge_avg_en << 4; + rkispp_set_bits(base + RKISPP_SHARP_CORE_CTRL, + SW_SHP_ALPHA_ADP_EN | SW_SHP_YIN_FLT_EN | + SW_SHP_EDGE_AVG_EN, val); + + rkispp_write(base + RKISPP_SHARP_HBF_FACTOR, arg->hbf_ratio | + arg->ehf_th << 16 | arg->pbf_ratio << 24); + rkispp_write(base + RKISPP_SHARP_EDGE_TH, arg->edge_thed | + arg->dir_min << 8 | arg->smoth_th4 << 16); + val = ISPP_PACK_2SHORT(arg->l_alpha, arg->g_alpha); + rkispp_write(base + RKISPP_SHARP_EDGE_ALPHA, val); + val = ISPP_PACK_4BYTE(arg->pbf_k[0], arg->pbf_k[1], arg->pbf_k[2], 0); + rkispp_write(base + RKISPP_SHARP_PBF_KERNEL, val); + val = ISPP_PACK_4BYTE(arg->mrf_k[0], arg->mrf_k[1], arg->mrf_k[2], arg->mrf_k[3]); + rkispp_write(base + RKISPP_SHARP_MRF_KERNEL0, val); + val = ISPP_PACK_4BYTE(arg->mrf_k[4], arg->mrf_k[5], 0, 0); + rkispp_write(base + RKISPP_SHARP_MRF_KERNEL1, val); + + for (i = 0; i < SHP_MBF_KERNEL_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->mbf_k[i], arg->mbf_k[i + 1], + arg->mbf_k[i + 2], arg->mbf_k[i + 3]); + rkispp_write(base + RKISPP_SHARP_MBF_KERNEL0 + i, val); + } + + val = ISPP_PACK_4BYTE(arg->hrf_k[0], arg->hrf_k[1], arg->hrf_k[2], arg->hrf_k[3]); + rkispp_write(base + RKISPP_SHARP_HRF_KERNEL0, val); + val = ISPP_PACK_4BYTE(arg->hrf_k[4], arg->hrf_k[5], 0, 0); + rkispp_write(base + RKISPP_SHARP_HRF_KERNEL1, val); + val = ISPP_PACK_4BYTE(arg->hbf_k[0], arg->hbf_k[1], arg->hbf_k[2], 0); + rkispp_write(base + RKISPP_SHARP_HBF_KERNEL, val); + + val = ISPP_PACK_4BYTE(arg->eg_coef[0], arg->eg_coef[1], arg->eg_coef[2], 0); + rkispp_write(base + RKISPP_SHARP_EDGE_COEF, val); + val = ISPP_PACK_4BYTE(arg->eg_smoth[0], arg->eg_smoth[1], arg->eg_smoth[2], 0); + rkispp_write(base + RKISPP_SHARP_EDGE_SMOTH, val); + val = ISPP_PACK_4BYTE(arg->eg_gaus[0], arg->eg_gaus[1], arg->eg_gaus[2], arg->eg_gaus[3]); + rkispp_write(base + RKISPP_SHARP_EDGE_GAUS0, val); + val = ISPP_PACK_4BYTE(arg->eg_gaus[4], arg->eg_gaus[5], 0, 0); + rkispp_write(base + RKISPP_SHARP_EDGE_GAUS1, val); + + val = ISPP_PACK_4BYTE(arg->dog_k[0], arg->dog_k[1], arg->dog_k[2], arg->dog_k[3]); + rkispp_write(base + RKISPP_SHARP_DOG_KERNEL0, val); + val = ISPP_PACK_4BYTE(arg->dog_k[4], arg->dog_k[5], 0, 0); + rkispp_write(base + RKISPP_SHARP_DOG_KERNEL1, val); + val = ISPP_PACK_4BYTE(arg->lum_point[0], arg->lum_point[1], + arg->lum_point[2], arg->lum_point[3]); + rkispp_write(base + RKISPP_SHARP_LUM_POINT0, val); + val = ISPP_PACK_4BYTE(arg->lum_point[4], arg->lum_point[5], 0, 0); + rkispp_write(base + RKISPP_SHARP_LUM_POINT1, val); + + val = ISPP_PACK_4BYTE(arg->pbf_shf_bits, arg->mbf_shf_bits, arg->hbf_shf_bits, 0); + rkispp_write(base + RKISPP_SHARP_SHF_BITS, val); + + for (i = 0; i < SHP_SIGMA_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->pbf_sigma[i], arg->pbf_sigma[i + 1], + arg->pbf_sigma[i + 2], arg->pbf_sigma[i + 3]); + rkispp_write(base + RKISPP_SHARP_PBF_SIGMA_INV0 + i, val); + val = ISPP_PACK_4BYTE(arg->mbf_sigma[i], arg->mbf_sigma[i + 1], + arg->mbf_sigma[i + 2], arg->mbf_sigma[i + 3]); + rkispp_write(base + RKISPP_SHARP_MBF_SIGMA_INV0 + i, val); + val = ISPP_PACK_4BYTE(arg->hbf_sigma[i], arg->hbf_sigma[i + 1], + arg->hbf_sigma[i + 2], arg->hbf_sigma[i + 3]); + rkispp_write(base + RKISPP_SHARP_HBF_SIGMA_INV0 + i, val); + } + + for (i = 0; i < SHP_LUM_CLP_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->lum_clp_m[i], arg->lum_clp_m[i + 1], + arg->lum_clp_m[i + 2], arg->lum_clp_m[i + 3]); + rkispp_write(base + RKISPP_SHARP_LUM_CLP_M0 + i, val); + val = ISPP_PACK_4BYTE(arg->lum_clp_h[i], arg->lum_clp_h[i + 1], + arg->lum_clp_h[i + 2], arg->lum_clp_h[i + 3]); + rkispp_write(base + RKISPP_SHARP_LUM_CLP_H0 + i, val); + } + + for (i = 0; i < SHP_LUM_MIN_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->lum_min_m[i], arg->lum_min_m[i + 1], + arg->lum_min_m[i + 2], arg->lum_min_m[i + 3]); + rkispp_write(base + RKISPP_SHARP_LUM_MIN_M0 + i, val); + } + + for (i = 0; i < SHP_EDGE_LUM_THED_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->edge_lum_thed[i], arg->edge_lum_thed[i + 1], + arg->edge_lum_thed[i + 2], arg->edge_lum_thed[i + 3]); + rkispp_write(base + RKISPP_SHARP_EDGE_LUM_THED0 + i, val); + } + + for (i = 0; i < SHP_CLAMP_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->clamp_pos[i], arg->clamp_pos[i + 1], + arg->clamp_pos[i + 2], arg->clamp_pos[i + 3]); + rkispp_write(base + RKISPP_SHARP_CLAMP_POS_DOG0 + i, val); + val = ISPP_PACK_4BYTE(arg->clamp_neg[i], arg->clamp_neg[i + 1], + arg->clamp_neg[i + 2], arg->clamp_neg[i + 3]); + rkispp_write(base + RKISPP_SHARP_CLAMP_NEG_DOG0 + i, val); + } + + for (i = 0; i < SHP_DETAIL_ALPHA_SIZE; i += 4) { + val = ISPP_PACK_4BYTE(arg->detail_alpha[i], arg->detail_alpha[i + 1], + arg->detail_alpha[i + 2], arg->detail_alpha[i + 3]); + rkispp_write(base + RKISPP_SHARP_DETAIL_ALPHA_DOG0 + i, val); + } + + val = ISPP_PACK_2SHORT(arg->rfl_ratio, arg->rfh_ratio); + rkispp_write(base + RKISPP_SHARP_RF_RATIO, val); + + val = ISPP_PACK_4BYTE(arg->m_ratio, arg->h_ratio, 0, 0); + rkispp_write(base + RKISPP_SHARP_GRAD_RATIO, val); +} + +static void shp_enable(struct rkispp_params_vdev *params_vdev, bool en) +{ + void __iomem *base = params_vdev->dev->base_addr; + + rkispp_set_bits(base + RKISPP_SHARP_CORE_CTRL, SW_SHP_EN, en); +} + +static void fec_config(struct rkispp_params_vdev *params_vdev, + struct rkispp_fec_config *arg) +{ +} + +static void fec_enable(struct rkispp_params_vdev *params_vdev, bool en) +{ +} + +static int rkispp_params_enum_fmt_meta_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct video_device *video = video_devdata(file); + struct rkispp_params_vdev *params_vdev = video_get_drvdata(video); + + if (f->index > 0 || f->type != video->queue->type) + return -EINVAL; + + f->pixelformat = params_vdev->vdev_fmt.fmt.meta.dataformat; + + return 0; +} + +static int rkispp_params_g_fmt_meta_out(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct video_device *video = video_devdata(file); + struct rkispp_params_vdev *params_vdev = video_get_drvdata(video); + struct v4l2_meta_format *meta = &f->fmt.meta; + + if (f->type != video->queue->type) + return -EINVAL; + + memset(meta, 0, sizeof(*meta)); + meta->dataformat = params_vdev->vdev_fmt.fmt.meta.dataformat; + meta->buffersize = params_vdev->vdev_fmt.fmt.meta.buffersize; + + return 0; +} + +static int rkispp_params_querycap(struct file *file, + void *priv, struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + struct rkispp_params_vdev *params_vdev = video_get_drvdata(vdev); + + snprintf(cap->driver, sizeof(cap->driver), + "%s_v%d", DRIVER_NAME, + params_vdev->dev->ispp_ver >> 4); + strlcpy(cap->card, vdev->name, sizeof(cap->card)); + strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info)); + + return 0; +} + +static const struct v4l2_ioctl_ops rkispp_params_ioctl = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_fmt_meta_out = rkispp_params_enum_fmt_meta_out, + .vidioc_g_fmt_meta_out = rkispp_params_g_fmt_meta_out, + .vidioc_s_fmt_meta_out = rkispp_params_g_fmt_meta_out, + .vidioc_try_fmt_meta_out = rkispp_params_g_fmt_meta_out, + .vidioc_querycap = rkispp_params_querycap +}; + +static int rkispp_params_vb2_queue_setup(struct vb2_queue *vq, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkispp_params_vdev *params_vdev = vq->drv_priv; + + *num_buffers = clamp_t(u32, *num_buffers, + RKISP1_ISP_PARAMS_REQ_BUFS_MIN, + RKISP1_ISP_PARAMS_REQ_BUFS_MAX); + + *num_planes = 1; + sizes[0] = sizeof(struct rkispp_params_cfg); + INIT_LIST_HEAD(¶ms_vdev->params); + params_vdev->first_params = true; + + return 0; +} + +static void rkispp_params_vb2_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkispp_buffer *params_buf = to_rkispp_buffer(vbuf); + struct vb2_queue *vq = vb->vb2_queue; + struct rkispp_params_vdev *params_vdev = vq->drv_priv; + struct rkispp_params_cfg *new_params; + unsigned long flags; + + if (params_vdev->first_params) { + new_params = (struct rkispp_params_cfg *) + (vb2_plane_vaddr(vb, 0)); + vb2_buffer_done(¶ms_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + params_vdev->first_params = false; + params_vdev->cur_params = *new_params; + return; + } + + params_buf->vaddr[0] = vb2_plane_vaddr(vb, 0); + spin_lock_irqsave(¶ms_vdev->config_lock, flags); + list_add_tail(¶ms_buf->queue, ¶ms_vdev->params); + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags); +} + +static void rkispp_params_vb2_stop_streaming(struct vb2_queue *vq) +{ + struct rkispp_params_vdev *params_vdev = vq->drv_priv; + struct rkispp_buffer *buf; + unsigned long flags; + int i; + + /* stop params input firstly */ + spin_lock_irqsave(¶ms_vdev->config_lock, flags); + params_vdev->streamon = false; + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags); + + for (i = 0; i < RKISP1_ISP_PARAMS_REQ_BUFS_MAX; i++) { + spin_lock_irqsave(¶ms_vdev->config_lock, flags); + if (!list_empty(¶ms_vdev->params)) { + buf = list_first_entry(¶ms_vdev->params, + struct rkispp_buffer, queue); + list_del(&buf->queue); + spin_unlock_irqrestore(¶ms_vdev->config_lock, + flags); + } else { + spin_unlock_irqrestore(¶ms_vdev->config_lock, + flags); + break; + } + + if (buf) + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + buf = NULL; + } + + /* clean module params */ + params_vdev->cur_params.module_cfg_update = 0; + params_vdev->cur_params.module_en_update = 0; +} + +static int +rkispp_params_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) +{ + struct rkispp_params_vdev *params_vdev = queue->drv_priv; + unsigned long flags; + + spin_lock_irqsave(¶ms_vdev->config_lock, flags); + params_vdev->streamon = true; + spin_unlock_irqrestore(¶ms_vdev->config_lock, flags); + + return 0; +} + +static struct vb2_ops rkispp_params_vb2_ops = { + .queue_setup = rkispp_params_vb2_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_queue = rkispp_params_vb2_buf_queue, + .start_streaming = rkispp_params_vb2_start_streaming, + .stop_streaming = rkispp_params_vb2_stop_streaming, + +}; + +struct v4l2_file_operations rkispp_params_fops = { + .mmap = vb2_fop_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .open = v4l2_fh_open, + .release = vb2_fop_release +}; + +static int +rkispp_params_init_vb2_queue(struct vb2_queue *q, + struct rkispp_params_vdev *params_vdev) +{ + q->type = V4L2_BUF_TYPE_META_OUTPUT; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->drv_priv = params_vdev; + q->ops = &rkispp_params_vb2_ops; + q->mem_ops = &vb2_vmalloc_memops; + q->buf_struct_size = sizeof(struct rkispp_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = ¶ms_vdev->dev->apilock; + q->dev = params_vdev->dev->dev; + + return vb2_queue_init(q); +} + +void rkispp_params_isr(struct rkispp_params_vdev *params_vdev, u32 mis) +{ + struct rkispp_params_cfg *new_params = NULL; + u32 module_en_update, module_cfg_update, module_ens; + + spin_lock(¶ms_vdev->config_lock); + if (!params_vdev->streamon) { + spin_unlock(¶ms_vdev->config_lock); + return; + } + + if (!params_vdev->cur_buf && + !list_empty(¶ms_vdev->params)) { + params_vdev->cur_buf = list_first_entry(¶ms_vdev->params, + struct rkispp_buffer, queue); + list_del(¶ms_vdev->cur_buf->queue); + } + + if (!params_vdev->cur_buf) { + spin_unlock(¶ms_vdev->config_lock); + return; + } + new_params = (struct rkispp_params_cfg *)(params_vdev->cur_buf->vaddr[0]); + + module_en_update = new_params->module_en_update; + module_cfg_update = new_params->module_cfg_update; + module_ens = new_params->module_ens; + + if (module_cfg_update & ISPP_MODULE_TNR && + mis & TNR_INT) + tnr_config(params_vdev, + &new_params->tnr_cfg); + if (module_en_update & ISPP_MODULE_TNR && + mis & TNR_INT) + tnr_enable(params_vdev, + !!(module_ens & ISPP_MODULE_TNR)); + + if (module_cfg_update & ISPP_MODULE_NR && + mis & NR_INT) + nr_config(params_vdev, + &new_params->nr_cfg); + if (module_en_update & ISPP_MODULE_NR && + mis & NR_INT) + nr_enable(params_vdev, + !!(module_ens & ISPP_MODULE_NR)); + + if (module_cfg_update & ISPP_MODULE_SHP && + mis & SHP_INT) + shp_config(params_vdev, + &new_params->shp_cfg); + if (module_en_update & ISPP_MODULE_SHP && + mis & SHP_INT) + shp_enable(params_vdev, + !!(module_ens & ISPP_MODULE_SHP)); + + if (module_cfg_update & ISPP_MODULE_FEC) + fec_config(params_vdev, + &new_params->fec_cfg); + if (module_en_update & ISPP_MODULE_FEC) + fec_enable(params_vdev, + !!(module_ens & ISPP_MODULE_FEC)); + //TODO diff mode to release buf + if (mis & SHP_INT) { + vb2_buffer_done(¶ms_vdev->cur_buf->vb.vb2_buf, + VB2_BUF_STATE_DONE); + params_vdev->cur_buf = NULL; + } + spin_unlock(¶ms_vdev->config_lock); +} + +void rkispp_params_configure(struct rkispp_params_vdev *params_vdev) +{ + u32 module_en_update = params_vdev->cur_params.module_en_update; + u32 module_cfg_update = params_vdev->cur_params.module_cfg_update; + u32 module_ens = params_vdev->cur_params.module_ens; + + if (module_cfg_update & ISPP_MODULE_TNR) + tnr_config(params_vdev, + ¶ms_vdev->cur_params.tnr_cfg); + if (module_en_update & ISPP_MODULE_TNR) + tnr_enable(params_vdev, + !!(module_ens & ISPP_MODULE_TNR)); + + if (module_cfg_update & ISPP_MODULE_NR) + nr_config(params_vdev, + ¶ms_vdev->cur_params.nr_cfg); + if (module_en_update & ISPP_MODULE_NR) + nr_enable(params_vdev, + !!(module_ens & ISPP_MODULE_NR)); + + if (module_cfg_update & ISPP_MODULE_SHP) + shp_config(params_vdev, + ¶ms_vdev->cur_params.shp_cfg); + if (module_en_update & ISPP_MODULE_SHP) + shp_enable(params_vdev, + !!(module_ens & ISPP_MODULE_SHP)); + + if (module_cfg_update & ISPP_MODULE_FEC) + fec_config(params_vdev, + ¶ms_vdev->cur_params.fec_cfg); + if (module_en_update & ISPP_MODULE_FEC) + fec_enable(params_vdev, + !!(module_ens & ISPP_MODULE_FEC)); +} + +int rkispp_register_params_vdev(struct rkispp_device *dev) +{ + struct rkispp_params_vdev *params_vdev = &dev->params_vdev; + struct rkispp_vdev_node *node = ¶ms_vdev->vnode; + struct video_device *vdev = &node->vdev; + int ret; + + params_vdev->dev = dev; + spin_lock_init(¶ms_vdev->config_lock); + + strlcpy(vdev->name, "rkispp_input_params", sizeof(vdev->name)); + + video_set_drvdata(vdev, params_vdev); + vdev->ioctl_ops = &rkispp_params_ioctl; + vdev->fops = &rkispp_params_fops; + vdev->release = video_device_release_empty; + /* + * Provide a mutex to v4l2 core. It will be used + * to protect all fops and v4l2 ioctls. + */ + vdev->lock = &dev->apilock; + vdev->v4l2_dev = &dev->v4l2_dev; + vdev->queue = &node->buf_queue; + vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT; + vdev->vfl_dir = VFL_DIR_TX; + rkispp_params_init_vb2_queue(vdev->queue, params_vdev); + params_vdev->vdev_fmt.fmt.meta.dataformat = + V4L2_META_FMT_RK_ISPP_PARAMS; + + node->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto err_release_queue; + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + dev_err(&vdev->dev, + "could not register Video for Linux device\n"); + goto err_cleanup_media_entity; + } + return 0; +err_cleanup_media_entity: + media_entity_cleanup(&vdev->entity); +err_release_queue: + vb2_queue_release(vdev->queue); + return ret; +} + +void rkispp_unregister_params_vdev(struct rkispp_device *dev) +{ + struct rkispp_params_vdev *params_vdev = &dev->params_vdev; + struct rkispp_vdev_node *node = ¶ms_vdev->vnode; + struct video_device *vdev = &node->vdev; + + video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + vb2_queue_release(vdev->queue); +} diff --git a/drivers/media/platform/rockchip/ispp/params.h b/drivers/media/platform/rockchip/ispp/params.h new file mode 100644 index 000000000000..254339ddcade --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/params.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_PARAMS_H +#define _RKISPP_PARAMS_H + +#include +#include "common.h" + +/* rkispp parameters device + * config_lock: lock to protect config + * params: queued buffer list + * cur_params: ispp params config + * cur_params: current buf of parameters + * first_params: the first params should take effect immediately + */ +struct rkispp_params_vdev { + struct rkispp_vdev_node vnode; + struct rkispp_device *dev; + + spinlock_t config_lock; + struct list_head params; + struct rkispp_params_cfg cur_params; + struct rkispp_buffer *cur_buf; + struct v4l2_format vdev_fmt; + bool streamon; + bool first_params; +}; + +int rkispp_register_params_vdev(struct rkispp_device *dev); +void rkispp_unregister_params_vdev(struct rkispp_device *dev); +void rkispp_params_configure(struct rkispp_params_vdev *params_vdev); +void rkispp_params_isr(struct rkispp_params_vdev *params_vdev, u32 mis); +#endif diff --git a/drivers/media/platform/rockchip/ispp/regs.h b/drivers/media/platform/rockchip/ispp/regs.h new file mode 100644 index 000000000000..c8bce32d2c7a --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/regs.h @@ -0,0 +1,501 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_REGS_H +#define _RKISPP_REGS_H + +/* registers */ +#define RKISPP_CTRL 0x0000 +#define RKISPP_CTRL_STRT (RKISPP_CTRL + 0x0000) +#define RKISPP_CTRL_UPDATE (RKISPP_CTRL + 0x0004) +#define RKISPP_CTRL_QUICK (RKISPP_CTRL + 0x0008) +#define RKISPP_CTRL_RESET (RKISPP_CTRL + 0x000C) +#define RKISPP_CTRL_CLKGATE (RKISPP_CTRL + 0x0010) +#define RKISPP_CTRL_SIZE (RKISPP_CTRL + 0x0014) +#define RKISPP_CTRL_TNR_SIZE (RKISPP_CTRL + 0x0018) +#define RKISPP_CTRL_IP_VERSION (RKISPP_CTRL + 0x001C) +#define RKISPP_CTRL_INT_MSK (RKISPP_CTRL + 0x0020) +#define RKISPP_CTRL_INT_RAW_STA (RKISPP_CTRL + 0x0024) +#define RKISPP_CTRL_INT_STA (RKISPP_CTRL + 0x0028) +#define RKISPP_CTRL_INT_SET (RKISPP_CTRL + 0x002C) +#define RKISPP_CTRL_INT_CLR (RKISPP_CTRL + 0x0030) +#define RKISPP_CTRL_SYS_STATUS (RKISPP_CTRL + 0x003C) +#define RKISPP_CTRL_QUICK_FRM_NUM (RKISPP_CTRL + 0x0040) +#define RKISPP_CTRL_SYS_CTL_STA0 (RKISPP_CTRL + 0x0054) +#define RKISPP_CTRL_SYS_CTL_STA1 (RKISPP_CTRL + 0x0058) +#define RKISPP_CTRL_SYS_CTL_STA2 (RKISPP_CTRL + 0x005C) +#define RKISPP_CTRL_SYS_FRM_CNT0 (RKISPP_CTRL + 0x0060) +#define RKISPP_CTRL_SYS_FRM_CNT1 (RKISPP_CTRL + 0x0064) + +#define RKISPP_TNR 0x0200 +#define RKISPP_TNR_CTRL (RKISPP_TNR + 0x0000) +#define RKISPP_TNR_CUR_VIR_STRIDE (RKISPP_TNR + 0x0004) +#define RKISPP_TNR_CUR_Y_BASE (RKISPP_TNR + 0x0008) +#define RKISPP_TNR_CUR_UV_BASE (RKISPP_TNR + 0x000C) +#define RKISPP_TNR_IIR_VIR_STRIDE (RKISPP_TNR + 0x0010) +#define RKISPP_TNR_IIR_Y_BASE (RKISPP_TNR + 0x0014) +#define RKISPP_TNR_IIR_UV_BASE (RKISPP_TNR + 0x0018) +#define RKISPP_TNR_NXT_VIR_STRIDE (RKISPP_TNR + 0x001C) +#define RKISPP_TNR_NXT_Y_BASE (RKISPP_TNR + 0x0020) +#define RKISPP_TNR_NXT_UV_BASE (RKISPP_TNR + 0x0024) +#define RKISPP_TNR_GAIN_CUR_VIR_STRIDE (RKISPP_TNR + 0x0028) +#define RKISPP_TNR_GAIN_CUR_Y_BASE (RKISPP_TNR + 0x002C) +#define RKISPP_TNR_GAIN_NXT_VIR_STRIDE (RKISPP_TNR + 0x0030) +#define RKISPP_TNR_GAIN_NXT_Y_BASE (RKISPP_TNR + 0x0034) +#define RKISPP_TNR_GAIN_KG_VIR_STRIDE (RKISPP_TNR + 0x0038) +#define RKISPP_TNR_GAIN_KG_Y_BASE (RKISPP_TNR + 0x003C) +#define RKISPP_TNR_WR_VIR_STRIDE (RKISPP_TNR + 0x0040) +#define RKISPP_TNR_WR_Y_BASE (RKISPP_TNR + 0x0044) +#define RKISPP_TNR_WR_UV_BASE (RKISPP_TNR + 0x0048) +#define RKISPP_TNR_GAIN_WR_VIR_STRIDE (RKISPP_TNR + 0x004C) +#define RKISPP_TNR_GAIN_WR_Y_BASE (RKISPP_TNR + 0x0050) +#define RKISPP_TNR_CUR_Y_BASE_SHD (RKISPP_TNR + 0x0054) +#define RKISPP_TNR_CUR_UV_BASE_SHD (RKISPP_TNR + 0x0058) +#define RKISPP_TNR_IIR_Y_BASE_SHD (RKISPP_TNR + 0x005C) +#define RKISPP_TNR_IIR_UV_BASE_SHD (RKISPP_TNR + 0x0060) +#define RKISPP_TNR_NXT_Y_BASE_SHD (RKISPP_TNR + 0x0064) +#define RKISPP_TNR_NXT_UV_BASE_SHD (RKISPP_TNR + 0x0068) +#define RKISPP_TNR_GAIN_CUR_Y_BASE_SHD (RKISPP_TNR + 0x006C) +#define RKISPP_TNR_GAIN_NXT_Y_BASE_SHD (RKISPP_TNR + 0x0070) +#define RKISPP_TNR_WR_Y_BASE_SHD (RKISPP_TNR + 0x0074) +#define RKISPP_TNR_WR_UV_BASE_SHD (RKISPP_TNR + 0x0078) +#define RKISPP_TNR_GAIN_WR_Y_BASE_SHD (RKISPP_TNR + 0x007C) +#define RKISPP_TNR_CORE_CTRL (RKISPP_TNR + 0x0080) +#define RKISPP_TNR_CORE_PK0 (RKISPP_TNR + 0x0088) +#define RKISPP_TNR_CORE_GLB_GAIN (RKISPP_TNR + 0x008C) +#define RKISPP_TNR_CORE_GLB_GAIN_DIV (RKISPP_TNR + 0x0090) +#define RKISPP_TNR_CORE_SIG_X18 (RKISPP_TNR + 0x0094) +#define RKISPP_TNR_CORE_SIG_X910 (RKISPP_TNR + 0x0098) +#define RKISPP_TNR_CORE_SIG_Y01 (RKISPP_TNR + 0x009C) +#define RKISPP_TNR_CORE_SIG_Y23 (RKISPP_TNR + 0x00A0) +#define RKISPP_TNR_CORE_SIG_Y45 (RKISPP_TNR + 0x00A4) +#define RKISPP_TNR_CORE_SIG_Y67 (RKISPP_TNR + 0x00A8) +#define RKISPP_TNR_CORE_SIG_Y89 (RKISPP_TNR + 0x00AC) +#define RKISPP_TNR_CORE_SIG_YAB (RKISPP_TNR + 0x00B0) +#define RKISPP_TNR_CORE_SIG_YCD (RKISPP_TNR + 0x00B4) +#define RKISPP_TNR_CORE_SIG_YEF (RKISPP_TNR + 0x00B8) +#define RKISPP_TNR_CORE_SIG_Y10 (RKISPP_TNR + 0x00BC) +#define RKISPP_TNR_CORE_LUMACURVE_Y01 (RKISPP_TNR + 0x00C4) +#define RKISPP_TNR_CORE_LUMACURVE_Y23 (RKISPP_TNR + 0x00C8) +#define RKISPP_TNR_CORE_LUMACURVE_Y45 (RKISPP_TNR + 0x00CC) +#define RKISPP_TNR_CORE_TH_Y (RKISPP_TNR + 0x00D0) +#define RKISPP_TNR_CORE_TH_C (RKISPP_TNR + 0x00D4) +#define RKISPP_TNR_CORE_TH_DLT (RKISPP_TNR + 0x00D8) +#define RKISPP_TNR_CORE_GFCOEF_Y0_0 (RKISPP_TNR + 0x00DC) +#define RKISPP_TNR_CORE_GFCOEF_Y0_1 (RKISPP_TNR + 0x00E0) +#define RKISPP_TNR_CORE_GFCOEF_Y1 (RKISPP_TNR + 0x00E4) +#define RKISPP_TNR_CORE_GFCOEF_Y2 (RKISPP_TNR + 0x00E8) +#define RKISPP_TNR_CORE_GFCOEF_Y3 (RKISPP_TNR + 0x00EC) +#define RKISPP_TNR_CORE_GFCOEF_YG0_0 (RKISPP_TNR + 0x00F0) +#define RKISPP_TNR_CORE_GFCOEF_YG0_1 (RKISPP_TNR + 0x00F4) +#define RKISPP_TNR_CORE_GFCOEF_YG1 (RKISPP_TNR + 0x00F8) +#define RKISPP_TNR_CORE_GFCOEF_YG2 (RKISPP_TNR + 0x00FC) +#define RKISPP_TNR_CORE_GFCOEF_YG3 (RKISPP_TNR + 0x0100) +#define RKISPP_TNR_CORE_GFCOEF_YL0_0 (RKISPP_TNR + 0x0104) +#define RKISPP_TNR_CORE_GFCOEF_YL0_1 (RKISPP_TNR + 0x0108) +#define RKISPP_TNR_CORE_GFCOEF_YL1 (RKISPP_TNR + 0x010C) +#define RKISPP_TNR_CORE_GFCOEF_YL2 (RKISPP_TNR + 0x0110) +#define RKISPP_TNR_CORE_GFCOEF_CG0_0 (RKISPP_TNR + 0x0114) +#define RKISPP_TNR_CORE_GFCOEF_CG0_1 (RKISPP_TNR + 0x0118) +#define RKISPP_TNR_CORE_GFCOEF_CG1 (RKISPP_TNR + 0x011C) +#define RKISPP_TNR_CORE_GFCOEF_CG2 (RKISPP_TNR + 0x0120) +#define RKISPP_TNR_CORE_GFCOEF_CL0_0 (RKISPP_TNR + 0x0124) +#define RKISPP_TNR_CORE_GFCOEF_CL0_1 (RKISPP_TNR + 0x0128) +#define RKISPP_TNR_CORE_GFCOEF_CL1 (RKISPP_TNR + 0x012C) +#define RKISPP_TNR_CORE_SCALE_YG01 (RKISPP_TNR + 0x0130) +#define RKISPP_TNR_CORE_SCALE_YG23 (RKISPP_TNR + 0x0134) +#define RKISPP_TNR_CORE_SCALE_YL01 (RKISPP_TNR + 0x0138) +#define RKISPP_TNR_CORE_SCALE_YL2 (RKISPP_TNR + 0x013c) +#define RKISPP_TNR_CORE_SCALE_CG0 (RKISPP_TNR + 0x0140) +#define RKISPP_TNR_CORE_SCALE_CG1 (RKISPP_TNR + 0x0144) +#define RKISPP_TNR_CORE_SCALE_CG2 (RKISPP_TNR + 0x0148) +#define RKISPP_TNR_CORE_SCALE_CL0 (RKISPP_TNR + 0x014C) +#define RKISPP_TNR_CORE_SCALE_CL1 (RKISPP_TNR + 0x0150) +#define RKISPP_TNR_CORE_SCALE_CL2 (RKISPP_TNR + 0x0154) +#define RKISPP_TNR_CORE_WEIGHT (RKISPP_TNR + 0x0158) + +#define RKISPP_NR 0x0400 +#define RKISPP_NR_CTRL (RKISPP_NR + 0x0000) +#define RKISPP_NR_VIR_STRIDE (RKISPP_NR + 0x0004) +#define RKISPP_NR_ADDR_BASE_Y (RKISPP_NR + 0x0008) +#define RKISPP_NR_ADDR_BASE_UV (RKISPP_NR + 0x000C) +#define RKISPP_NR_VIR_STRIDE_GAIN (RKISPP_NR + 0x001C) +#define RKISPP_NR_ADDR_BASE_GAIN (RKISPP_NR + 0x0020) +#define RKISPP_NR_ADDR_BASE_Y_SHD (RKISPP_NR + 0x0024) +#define RKISPP_NR_ADDR_BASE_UV_SHD (RKISPP_NR + 0x0028) +#define RKISPP_FBC_VIR_HEIGHT (RKISPP_NR + 0x002C) +#define RKISPP_NR_UVNR_CTRL_PARA (RKISPP_NR + 0x0080) +#define RKISPP_NR_UVNR_GAIN_1SIGMA (RKISPP_NR + 0x0084) +#define RKISPP_NR_UVNR_GAIN_OFFSET (RKISPP_NR + 0x0088) +#define RKISPP_NR_UVNR_GAIN_GBLGAIN (RKISPP_NR + 0x008C) +#define RKISPP_NR_UVNR_T1GEN_M3ALPHA (RKISPP_NR + 0x0090) +#define RKISPP_NR_UVNR_T1FLT_MODE (RKISPP_NR + 0x0094) +#define RKISPP_NR_UVNR_T1FLT_MSIGMA (RKISPP_NR + 0x0098) +#define RKISPP_NR_UVNR_T1FLT_WTP (RKISPP_NR + 0x009C) +#define RKISPP_NR_UVNR_T1FLT_WTQ0 (RKISPP_NR + 0x00A0) +#define RKISPP_NR_UVNR_T1FLT_WTQ1 (RKISPP_NR + 0x00A4) +#define RKISPP_NR_UVNR_T2GEN_M3ALPHA (RKISPP_NR + 0x00A8) +#define RKISPP_NR_UVNR_T2GEN_MSIGMA (RKISPP_NR + 0x00AC) +#define RKISPP_NR_UVNR_T2GEN_WTP (RKISPP_NR + 0x00B0) +#define RKISPP_NR_UVNR_T2GEN_WTQ (RKISPP_NR + 0x00B4) +#define RKISPP_NR_UVNR_T2FLT_MSIGMA (RKISPP_NR + 0x00B8) +#define RKISPP_NR_UVNR_T2FLT_WT (RKISPP_NR + 0x00BC) +#define RKISPP_NR_YNR_SGM_DX_1_8 (RKISPP_NR + 0x0104) +#define RKISPP_NR_YNR_SGM_DX_9_16 (RKISPP_NR + 0x0108) +#define RKISPP_NR_YNR_LSGM_Y_0_1 (RKISPP_NR + 0x010C) +#define RKISPP_NR_YNR_LSGM_Y_2_3 (RKISPP_NR + 0x0110) +#define RKISPP_NR_YNR_LSGM_Y_4_5 (RKISPP_NR + 0x0114) +#define RKISPP_NR_YNR_LSGM_Y_6_7 (RKISPP_NR + 0x0118) +#define RKISPP_NR_YNR_LSGM_Y_8_9 (RKISPP_NR + 0x011C) +#define RKISPP_NR_YNR_LSGM_Y_10_11 (RKISPP_NR + 0x0120) +#define RKISPP_NR_YNR_LSGM_Y_12_13 (RKISPP_NR + 0x0124) +#define RKISPP_NR_YNR_LSGM_Y_14_15 (RKISPP_NR + 0x0128) +#define RKISPP_NR_YNR_LSGM_Y_16 (RKISPP_NR + 0x012C) +#define RKISPP_NR_YNR_LCI (RKISPP_NR + 0x0130) +#define RKISPP_NR_YNR_LGAIN_DIRE_MIN (RKISPP_NR + 0x0134) +#define RKISPP_NR_YNR_IGAIN_DIRE_MAX (RKISPP_NR + 0x0138) +#define RKISPP_NR_YNR_LMERGE (RKISPP_NR + 0x013C) +#define RKISPP_NR_YNR_LWEIT_FLT (RKISPP_NR + 0x0140) +#define RKISPP_NR_YNR_HSGM_Y_0_1 (RKISPP_NR + 0x0144) +#define RKISPP_NR_YNR_HSGM_Y_2_3 (RKISPP_NR + 0x0148) +#define RKISPP_NR_YNR_HSGM_Y_4_5 (RKISPP_NR + 0x014C) +#define RKISPP_NR_YNR_HSGM_Y_6_7 (RKISPP_NR + 0x0150) +#define RKISPP_NR_YNR_HSGM_Y_8_9 (RKISPP_NR + 0x0154) +#define RKISPP_NR_YNR_HSGM_Y_10_11 (RKISPP_NR + 0x0158) +#define RKISPP_NR_YNR_HSGM_Y_12_13 (RKISPP_NR + 0x015C) +#define RKISPP_NR_YNR_HSGM_Y_14_15 (RKISPP_NR + 0x0160) +#define RKISPP_NR_YNR_HSGM_Y_16 (RKISPP_NR + 0x0164) +#define RKISPP_NR_YNR_HLCI (RKISPP_NR + 0x0168) +#define RKISPP_NR_YNR_LHCI (RKISPP_NR + 0x016C) +#define RKISPP_NR_YNR_HHCI (RKISPP_NR + 0x0170) +#define RKISPP_NR_YNR_HGAIN_SGM (RKISPP_NR + 0x0174) +#define RKISPP_NR_YNR_HWEIT_D0 (RKISPP_NR + 0x0178) +#define RKISPP_NR_YNR_HWEIT_D1 (RKISPP_NR + 0x017C) +#define RKISPP_NR_YNR_HWEIT_D2 (RKISPP_NR + 0x0180) +#define RKISPP_NR_YNR_HWEIT_D3 (RKISPP_NR + 0x0184) +#define RKISPP_NR_YNR_HWEIT_D4 (RKISPP_NR + 0x0188) +#define RKISPP_NR_YNR_HGRAD_Y0 (RKISPP_NR + 0x018C) +#define RKISPP_NR_YNR_HGRAD_Y1 (RKISPP_NR + 0x0190) +#define RKISPP_NR_YNR_HGRAD_Y2 (RKISPP_NR + 0x0194) +#define RKISPP_NR_YNR_HGRAD_Y3 (RKISPP_NR + 0x0198) +#define RKISPP_NR_YNR_HGRAD_Y4 (RKISPP_NR + 0x019C) +#define RKISPP_NR_YNR_HGRAD_Y5 (RKISPP_NR + 0x01A0) +#define RKISPP_NR_YNR_HWEIT_1_2 (RKISPP_NR + 0x01A4) +#define RKISPP_NR_YNR_HWEIT_3_4 (RKISPP_NR + 0x01A8) +#define RKISPP_NR_YNR_HADJUST_EXGAIN (RKISPP_NR + 0x01AC) +#define RKISPP_NR_YNR_HMAX_ADJUST (RKISPP_NR + 0x01B0) +#define RKISPP_NR_YNR_HSTRENGTH (RKISPP_NR + 0x01B4) +#define RKISPP_NR_YNR_LWEIT_CMP (RKISPP_NR + 0x01B8) +#define RKISPP_NR_YNR_LMAXGAIN_LV4 (RKISPP_NR + 0x01BC) +#define RKISPP_NR_YNR_HSTV_Y_0_1 (RKISPP_NR + 0x01C0) +#define RKISPP_NR_YNR_HSTV_Y_2_3 (RKISPP_NR + 0x01C4) +#define RKISPP_NR_YNR_HSTV_Y_4_5 (RKISPP_NR + 0x01C8) +#define RKISPP_NR_YNR_HSTV_Y_6_7 (RKISPP_NR + 0x01CC) +#define RKISPP_NR_YNR_HSTV_Y_8_9 (RKISPP_NR + 0x01D0) +#define RKISPP_NR_YNR_HSTV_Y_10_11 (RKISPP_NR + 0x01D4) +#define RKISPP_NR_YNR_HSTV_Y_12_13 (RKISPP_NR + 0x01D8) +#define RKISPP_NR_YNR_HSTV_Y_14_15 (RKISPP_NR + 0x01DC) +#define RKISPP_NR_YNR_HSTV_Y_16 (RKISPP_NR + 0x01E0) +#define RKISPP_NR_YNR_ST_SCALE_LV1_LV2 (RKISPP_NR + 0x01E4) +#define RKISPP_NR_YNR_ST_SCALE_LV3 (RKISPP_NR + 0x01E8) + +#define RKISPP_SHARP 0x0600 +#define RKISPP_SHARP_CTRL (RKISPP_SHARP + 0x0000) +#define RKISPP_SHARP_WR_VIR_STRIDE (RKISPP_SHARP + 0x0004) +#define RKISPP_SHARP_WR_Y_BASE (RKISPP_SHARP + 0x0008) +#define RKISPP_SHARP_WR_UV_BASE (RKISPP_SHARP + 0x000C) +#define RKISPP_SHARP_SC_DOWN (RKISPP_SHARP + 0x0010) +#define RKISPP_SHARP_TMP_YUV_BASE (RKISPP_SHARP + 0x0014) +#define RKISPP_SHARP_WR_Y_BASE_SHD (RKISPP_SHARP + 0x0024) +#define RKISPP_SHARP_WR_UV_BASE_SHD (RKISPP_SHARP + 0x0028) +#define RKISPP_SHARP_CORE_CTRL (RKISPP_SHARP + 0x0080) +#define RKISPP_SHARP_HBF_FACTOR (RKISPP_SHARP + 0x0084) +#define RKISPP_SHARP_EDGE_TH (RKISPP_SHARP + 0x0088) +#define RKISPP_SHARP_EDGE_ALPHA (RKISPP_SHARP + 0x008C) +#define RKISPP_SHARP_PBF_KERNEL (RKISPP_SHARP + 0x0090) +#define RKISPP_SHARP_MRF_KERNEL0 (RKISPP_SHARP + 0x0094) +#define RKISPP_SHARP_MRF_KERNEL1 (RKISPP_SHARP + 0x0098) +#define RKISPP_SHARP_MBF_KERNEL0 (RKISPP_SHARP + 0x009C) +#define RKISPP_SHARP_MBF_KERNEL1 (RKISPP_SHARP + 0x00A0) +#define RKISPP_SHARP_MBF_KERNEL2 (RKISPP_SHARP + 0x00A4) +#define RKISPP_SHARP_HRF_KERNEL0 (RKISPP_SHARP + 0x00A8) +#define RKISPP_SHARP_HRF_KERNEL1 (RKISPP_SHARP + 0x00AC) +#define RKISPP_SHARP_HBF_KERNEL (RKISPP_SHARP + 0x00B0) +#define RKISPP_SHARP_EDGE_COEF (RKISPP_SHARP + 0x00B4) +#define RKISPP_SHARP_EDGE_SMOTH (RKISPP_SHARP + 0x00B8) +#define RKISPP_SHARP_EDGE_GAUS0 (RKISPP_SHARP + 0x00BC) +#define RKISPP_SHARP_EDGE_GAUS1 (RKISPP_SHARP + 0x00C0) +#define RKISPP_SHARP_DOG_KERNEL0 (RKISPP_SHARP + 0x00C4) +#define RKISPP_SHARP_DOG_KERNEL1 (RKISPP_SHARP + 0x00C8) +#define RKISPP_SHARP_LUM_POINT0 (RKISPP_SHARP + 0x00CC) +#define RKISPP_SHARP_LUM_POINT1 (RKISPP_SHARP + 0x00D0) +#define RKISPP_SHARP_SHF_BITS (RKISPP_SHARP + 0x00D4) +#define RKISPP_SHARP_PBF_SIGMA_INV0 (RKISPP_SHARP + 0x00D8) +#define RKISPP_SHARP_PBF_SIGMA_INV1 (RKISPP_SHARP + 0x00DC) +#define RKISPP_SHARP_LUM_CLP_M0 (RKISPP_SHARP + 0x00E0) +#define RKISPP_SHARP_LUM_CLP_M1 (RKISPP_SHARP + 0x00E4) +#define RKISPP_SHARP_LUM_MIN_M0 (RKISPP_SHARP + 0x00E8) +#define RKISPP_SHARP_LUM_MIN_M1 (RKISPP_SHARP + 0x00EC) +#define RKISPP_SHARP_MBF_SIGMA_INV0 (RKISPP_SHARP + 0x00F0) +#define RKISPP_SHARP_MBF_SIGMA_INV1 (RKISPP_SHARP + 0x00F4) +#define RKISPP_SHARP_LUM_CLP_H0 (RKISPP_SHARP + 0x00F8) +#define RKISPP_SHARP_LUM_CLP_H1 (RKISPP_SHARP + 0x00FC) +#define RKISPP_SHARP_HBF_SIGMA_INV0 (RKISPP_SHARP + 0x0100) +#define RKISPP_SHARP_HBF_SIGMA_INV1 (RKISPP_SHARP + 0x0104) +#define RKISPP_SHARP_EDGE_LUM_THED0 (RKISPP_SHARP + 0x0108) +#define RKISPP_SHARP_EDGE_LUM_THED1 (RKISPP_SHARP + 0x010C) +#define RKISPP_SHARP_CLAMP_POS_DOG0 (RKISPP_SHARP + 0x0110) +#define RKISPP_SHARP_CLAMP_POS_DOG1 (RKISPP_SHARP + 0x0114) +#define RKISPP_SHARP_CLAMP_NEG_DOG0 (RKISPP_SHARP + 0x0118) +#define RKISPP_SHARP_CLAMP_NEG_DOG1 (RKISPP_SHARP + 0x011C) +#define RKISPP_SHARP_DETAIL_ALPHA_DOG0 (RKISPP_SHARP + 0x0120) +#define RKISPP_SHARP_DETAIL_ALPHA_DOG1 (RKISPP_SHARP + 0x0124) +#define RKISPP_SHARP_RF_RATIO (RKISPP_SHARP + 0x0128) +#define RKISPP_SHARP_GRAD_RATIO (RKISPP_SHARP + 0x012C) + +#define RKISPP_SCL0 0x0800 +#define RKISPP_SCL0_CTRL (RKISPP_SCL0 + 0x0000) +#define RKISPP_SCL0_CUR_VIR_STRIDE (RKISPP_SCL0 + 0x0004) +#define RKISPP_SCL0_CUR_Y_BASE (RKISPP_SCL0 + 0x0008) +#define RKISPP_SCL0_CUR_UV_BASE (RKISPP_SCL0 + 0x000C) +#define RKISPP_SCL0_CUR_Y_BASE_SHD (RKISPP_SCL0 + 0x0010) +#define RKISPP_SCL0_CUR_UV_BASE_SHD (RKISPP_SCL0 + 0x0014) +#define RKISPP_SCL0_FACTOR (RKISPP_SCL0 + 0x0018) + +#define RKISPP_SCL1 0x0900 +#define RKISPP_SCL1_CTRL (RKISPP_SCL1 + 0x0000) +#define RKISPP_SCL1_CUR_VIR_STRIDE (RKISPP_SCL1 + 0x0004) +#define RKISPP_SCL1_CUR_Y_BASE (RKISPP_SCL1 + 0x0008) +#define RKISPP_SCL1_CUR_UV_BASE (RKISPP_SCL1 + 0x000C) +#define RKISPP_SCL1_CUR_Y_BASE_SHD (RKISPP_SCL1 + 0x0010) +#define RKISPP_SCL1_CUR_UV_BASE_SHD (RKISPP_SCL1 + 0x0014) +#define RKISPP_SCL1_FACTOR (RKISPP_SCL1 + 0x0018) + +#define RKISPP_SCL2 0x0A00 +#define RKISPP_SCL2_CTRL (RKISPP_SCL2 + 0x0000) +#define RKISPP_SCL2_CUR_VIR_STRIDE (RKISPP_SCL2 + 0x0004) +#define RKISPP_SCL2_CUR_Y_BASE (RKISPP_SCL2 + 0x0008) +#define RKISPP_SCL2_CUR_UV_BASE (RKISPP_SCL2 + 0x000C) +#define RKISPP_SCL2_CUR_Y_BASE_SHD (RKISPP_SCL2 + 0x0010) +#define RKISPP_SCL2_CUR_UV_BASE_SHD (RKISPP_SCL2 + 0x0014) +#define RKISPP_SCL2_FACTOR (RKISPP_SCL2 + 0x0018) + +#define RKISPP_FEC 0x0C00 +#define RKISPP_FEC_CTRL (RKISPP_FEC + 0x0000) +#define RKISPP_FEC_RD_VIR_STRIDE (RKISPP_FEC + 0x0004) +#define RKISPP_FEC_RD_Y_BASE (RKISPP_FEC + 0x0008) +#define RKISPP_FEC_RD_UV_BASE (RKISPP_FEC + 0x000C) +#define RKISPP_FEC_MESH_XINT_BASE (RKISPP_FEC + 0x0010) +#define RKISPP_FEC_MESH_XFRA_BASE (RKISPP_FEC + 0x0014) +#define RKISPP_FEC_MESH_YINT_BASE (RKISPP_FEC + 0x0018) +#define RKISPP_FEC_MESH_YFRA_BASE (RKISPP_FEC + 0x001C) +#define RKISPP_FEC_WR_VIR_STRIDE (RKISPP_FEC + 0x0020) +#define RKISPP_FEC_WR_Y_BASE (RKISPP_FEC + 0x0024) +#define RKISPP_FEC_WR_UV_BASE (RKISPP_FEC + 0x0028) +#define RKISPP_FEC_RD_Y_BASE_SHD (RKISPP_FEC + 0x002C) +#define RKISPP_FEC_RD_UV_BASE_SHD (RKISPP_FEC + 0x0030) +#define RKISPP_FEC_MESH_XINT_BASE_SHD (RKISPP_FEC + 0x0034) +#define RKISPP_FEC_MESH_XFRA_BASE_SHD (RKISPP_FEC + 0x0038) +#define RKISPP_FEC_MESH_YINT_BASE_SHD (RKISPP_FEC + 0x003C) +#define RKISPP_FEC_MESH_YFRA_BASE_SHD (RKISPP_FEC + 0x0040) +#define RKISPP_FEC_WR_Y_BASE_SHD (RKISPP_FEC + 0x0044) +#define RKISPP_FEC_WR_UV_BASE_SHD (RKISPP_FEC + 0x0048) +#define RKISPP_FEC_CORE_CTRL (RKISPP_FEC + 0x0080) +#define RKISPP_FEC_PIC_SIZE (RKISPP_FEC + 0x0088) +#define RKISPP_FEC_MESH_SIZE (RKISPP_FEC + 0x008C) +#define RKISPP_FEC_DMA_STATUS (RKISPP_FEC + 0x0090) +#define RKISPP_FEC_CROP (RKISPP_FEC + 0x0094) + +#define FMT_WR_MASK GENMASK(7, 5) +#define FMT_RD_MASK GENMASK(3, 1) +#define FMT_YC_SWAP BIT(3) +#define FMT_YUYV BIT(2) +#define FMT_YUV422 BIT(1) +#define FMT_YUV420 0 + +/* ISPP_STRT */ +#define FEC_ST BIT(2) +#define NR_SHP_ST BIT(1) +#define TNR_ST BIT(0) + +/* ISPP_UPDATE */ +#define ALL_FORCE_UPD (FEC_FORCE_UPD | TNR_FORCE_UPD | OTHER_FORCE_UPD) +#define FEC_FORCE_UPD BIT(2) +#define TNR_FORCE_UPD BIT(1) +#define OTHER_FORCE_UPD BIT(0) + +/* ISPP_CTRL_QUICK */ +#define GLB_QUICK_MODE_MASK GENMASK(9, 8) +#define GLB_FEC2SCL_EN BIT(11) +#define GLB_NR_SD32_TNR BIT(10) +#define GLB_QUICK_MODE(a) (((a) & 0x3) << 8) +#define GLB_QUICK_EN BIT(0) + +/* ISPP_CTRL_RESET */ +#define FBCD_ERR_PROTECT_DIS BIT(12) +#define GLB_SOFT_RST_SCL BIT(11) +#define GLB_SOFT_RST_SHP BIT(10) +#define GLB_SOFT_RST_NR BIT(9) +#define GLB_SOFT_RST_TNR BIT(8) +#define RST_PROTECT_DIS BIT(1) +#define GLB_SOFT_RST_ALL BIT(0) + +/* INT_MASK INT_RAW_STA INT_STA INT_SET INT_CLR */ +#define INT_FRAME(stream) \ + ((stream)->config->frame_end_id) +#define NR_LOST_ERR BIT(26) +#define TNR_LOST_ERR BIT(25) +#define UVNR_MONITOR_ERR BIT(24) +#define FBCH_EMPTY_NR BIT(23) +#define FBCH_EMPTY_TNR BIT(22) +#define FBCD_DEC_ERR_NR BIT(21) +#define FBCD_DEC_ERR_TNR BIT(20) +#define BUS_ERR_NR BIT(17) +#define BUS_ERR_TNR BIT(16) +#define CMD_NR_SHP_ST_DONE BIT(12) +#define CMD_TNR_ST_DONE BIT(11) +#define SCL2_INT BIT(10) +#define SCL1_INT BIT(9) +#define SCL0_INT BIT(8) +#define FEC_INT BIT(7) +#define ORB_INT BIT(6) +#define SHP_INT BIT(4) +#define NR_INT BIT(3) +#define TNR_INT BIT(2) +#define FRAME_INT BIT(1) +#define QUICK_INT BIT(0) + +/* TNR CTRL */ +#define SW_TNR_WR_FORMAT_MASK GENMASK(7, 5) +#define SW_TNR_RD_FORMAT_MASK GENMASK(3, 1) +#define SW_TNR_THROUGH_MODE BIT(10) +#define SW_TNR_1ST_FRM BIT(9) +#define SW_TNR_RD_PINGPONG2TO1 BIT(8) +#define SW_TNR_WR_YUYV_YCSWAP BIT(7) +#define SW_TNR_WR_YUYV_FORMAT BIT(6) +#define SW_TNR_WR_YUV_FORMAT BIT(5) +#define SW_TNR_WR_FBCE_MODE BIT(4) +#define SW_TNR_RD_YUYV_YCSWAP BIT(3) +#define SW_TNR_RD_YUYV_FORMAT BIT(2) +#define SW_TNR_RD_YUV_FORMAT BIT(1) +#define SW_TNR_FBCD_MODE BIT(0) + +/* CORE_CTRL */ +#define SW_TNR_GLB_GAIN_EN_SHD BIT(28) +#define SW_TNR_OPTC_EN_SHD BIT(27) +#define SW_TNR_OPTY_EN_SHD BIT(26) +#define SW_TNR_MODE_SHD BIT(25) +#define SW_TNR_EN_SHD BIT(24) +#define SW_TNR_GLB_GAIN_EN BIT(4) +#define SW_TNR_OPTC_EN BIT(3) +#define SW_TNR_OPTY_EN BIT(2) +#define SW_TNR_MODE BIT(1) +#define SW_TNR_EN BIT(0) + +/* NR CTRL */ +#define SW_NR_RD_FBCD_READ_MODE BIT(31) +#define SW_NR_NEW_ALGORITHM BIT(16) +#define SW_UVNR_1ST_FRM_SELF_MODE BIT(10) +#define SW_UVNR_1ST_FRM_SELF BIT(9) +#define SW_NR_RD_YUYV_YCSWAP BIT(3) +#define SW_NR_RD_YUYV_FORMAT BIT(2) +#define SW_NR_RD_YUV_FORMAT BIT(1) +#define SW_NR_RD_FBCD_MODE BIT(0) + +/* NR_CTRL_PARA */ +#define SW_UVNR_BIG_EN BIT(6) +#define SW_UVNR_NOBIG_EN BIT(5) +#define SW_UVNR_SD32_SELF_EN BIT(4) +#define SW_NR_GAIN_BYPASS BIT(3) +#define SW_UVNR_STEP2_ON BIT(2) +#define SW_UVNR_STEP1_ON BIT(1) +#define SW_NR_EN BIT(0) + +/* SHP CTRL*/ +#define SW_SHP_WR_FORMAT_MASK GENMASK(3, 1) +#define SW_SHP_WR_ROT_MODE(a) (((a) & 0x3) << 5) +#define SW_SHP_WR_YUV_LIMIT BIT(4) +#define SW_SHP_WR_YUYV_YCSWAP BIT(3) +#define SW_SHP_WR_YUYV_FORMAT BIT(2) +#define SW_SHP_WR_YUY_FORMAT BIT(1) +#define SW_SHP_WR_FBCE_MODE BIT(0) + +/* CORE_CTRL */ +#define SW_SHP_EDGE_AVG_EN BIT(4) +#define SW_SHP_YIN_FLT_EN BIT(3) +#define SW_SHP_DMA_DIS BIT(2) +#define SW_SHP_ALPHA_ADP_EN BIT(1) +#define SW_SHP_EN BIT(0) + +/* SCL_CTRL */ +#define SW_SCL_BYPASS_SHD BIT(31) +#define SW_SCL_ENABLE_SHD BIT(30) +#define SW_SCL_RAM_CLK_ON BIT(8) +#define SW_SCL_WR_YUV_LIMIT BIT(7) +#define SW_SCL_WR_YUYV_YCSWAP BIT(6) +#define SW_SCL_WR_YUYV_FORMAT BIT(5) +#define SW_SCL_WR_YUV_FORMAT BIT(4) +#define SW_SCL_WR_UV_DIS BIT(3) +#define SW_SCL_FIRST_MODE BIT(2) +#define SW_SCL_BYPASS BIT(1) +#define SW_SCL_ENABLE BIT(0) + +/* FEC_CTRL*/ +#define SW_FEC_WR_YUV_LIMIT BIT(8) +#define SW_FEC_WR_YUYV_YC_SWAP BIT(7) +#define SW_FEC_WR_YUYV_FORMAT BIT(6) +#define SW_FEC_WR_YUV_FORMAT BIT(5) +#define SW_FEC_WR_FBCE_MODE BIT(4) +#define SW_FEC_RD_YUYV_YCSWAP BIT(3) +#define SW_FEC_RD_YUYV_FORMAT BIT(2) +#define SW_FEC_RD_YUV_FORMAT BIT(1) + +/* FEC_CORE_CTRL */ +#define SW_FEC_EN_SHD BIT(31) +#define SW_MINBUF_NON_UPDATE_SHD BIT(30) +#define SW_MINBUF_NON_UPDATE BIT(6) +#define SW_MESH_DDENSITY BIT(5) +#define SW_FEC2DDR_DIS BIT(1) +#define SW_FEC_EN BIT(0) + +static inline void rkispp_soft_reset(void __iomem *base) +{ + writel(GLB_SOFT_RST_ALL, base + RKISPP_CTRL_RESET); +} + +static inline void set_y_addr(struct rkispp_stream *stream, u32 val) +{ + void __iomem *base = stream->isppdev->base_addr; + + writel(val, base + stream->config->reg.cur_y_base); +} + +static inline void set_uv_addr(struct rkispp_stream *stream, u32 val) +{ + void __iomem *base = stream->isppdev->base_addr; + + writel(val, base + stream->config->reg.cur_uv_base); +} + +static inline void set_vir_stride(struct rkispp_stream *stream, u32 val) +{ + void __iomem *base = stream->isppdev->base_addr; + + writel(val, base + stream->config->reg.cur_vir_stride); +} + +static inline void set_scl_factor(struct rkispp_stream *stream, u32 val) +{ + void __iomem *base = stream->isppdev->base_addr; + + writel(val, base + stream->config->reg.factor); +} + +static inline void set_ctrl(struct rkispp_stream *stream, u32 val) +{ + void __iomem *base = stream->isppdev->base_addr; + + writel(val, base + stream->config->reg.ctrl); +} + +#endif /* _RKISPP_REGS_H */ diff --git a/drivers/media/platform/rockchip/ispp/stream.c b/drivers/media/platform/rockchip/ispp/stream.c new file mode 100644 index 000000000000..a4a7b73a13ca --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/stream.c @@ -0,0 +1,1465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "dev.h" +#include "regs.h" + +#define STREAM_IN_REQ_BUFS_MIN 1 +#define STREAM_OUT_REQ_BUFS_MIN 0 + +/* + * DDR->| |->MB------->DDR + * |->3DNR->DDR->2DNR->SHARP->DDR->FEC->|->SCL0----->DDR + * ISP->| |->SCL1----->DDR + * |->SCL2----->DDR + */ + +static const struct capture_fmt pixel_fmts[] = { + { + .fourcc = V4L2_PIX_FMT_YUYV, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .wr_fmt = FMT_YC_SWAP | FMT_YUYV | FMT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .bpp = { 16 }, + .cplanes = 1, + .mplanes = 1, + .wr_fmt = FMT_YUYV | FMT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV16, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .wr_fmt = FMT_YUV422, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .bpp = { 8, 16 }, + .cplanes = 2, + .mplanes = 1, + .wr_fmt = FMT_YUV420, + } +}; + +static struct stream_config input_config = { + .fmts = pixel_fmts, + .fmt_size = ARRAY_SIZE(pixel_fmts), +}; + +static struct stream_config mb_config = { + .fmts = pixel_fmts, + .fmt_size = ARRAY_SIZE(pixel_fmts), +}; + +static struct stream_config scl0_config = { + .fmts = pixel_fmts, + .fmt_size = ARRAY_SIZE(pixel_fmts), + .frame_end_id = SCL0_INT, + .reg = { + .ctrl = RKISPP_SCL0_CTRL, + .factor = RKISPP_SCL0_FACTOR, + .cur_y_base = RKISPP_SCL0_CUR_Y_BASE, + .cur_uv_base = RKISPP_SCL0_CUR_UV_BASE, + .cur_vir_stride = RKISPP_SCL0_CUR_VIR_STRIDE, + .cur_y_base_shd = RKISPP_SCL0_CUR_Y_BASE_SHD, + .cur_uv_base_shd = RKISPP_SCL0_CUR_UV_BASE_SHD, + }, +}; + +static struct stream_config scl1_config = { + .fmts = pixel_fmts, + .fmt_size = ARRAY_SIZE(pixel_fmts), + .frame_end_id = SCL1_INT, + .reg = { + .ctrl = RKISPP_SCL1_CTRL, + .factor = RKISPP_SCL1_FACTOR, + .cur_y_base = RKISPP_SCL1_CUR_Y_BASE, + .cur_uv_base = RKISPP_SCL1_CUR_UV_BASE, + .cur_vir_stride = RKISPP_SCL1_CUR_VIR_STRIDE, + .cur_y_base_shd = RKISPP_SCL1_CUR_Y_BASE_SHD, + .cur_uv_base_shd = RKISPP_SCL1_CUR_UV_BASE_SHD, + }, +}; + +static struct stream_config scl2_config = { + .fmts = pixel_fmts, + .fmt_size = ARRAY_SIZE(pixel_fmts), + .frame_end_id = SCL2_INT, + .reg = { + .ctrl = RKISPP_SCL2_CTRL, + .factor = RKISPP_SCL2_FACTOR, + .cur_y_base = RKISPP_SCL2_CUR_Y_BASE, + .cur_uv_base = RKISPP_SCL2_CUR_UV_BASE, + .cur_vir_stride = RKISPP_SCL2_CUR_VIR_STRIDE, + .cur_y_base_shd = RKISPP_SCL2_CUR_Y_BASE_SHD, + .cur_uv_base_shd = RKISPP_SCL2_CUR_UV_BASE_SHD, + }, +}; + +static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs) +{ + switch (fcc) { + case V4L2_PIX_FMT_GREY: + *xsubs = 1; + *ysubs = 1; + break; + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + *xsubs = 2; + *ysubs = 1; + break; + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + *xsubs = 2; + *ysubs = 2; + break; + default: + return -EINVAL; + } + return 0; +} + +static const +struct capture_fmt *find_fmt(struct rkispp_stream *stream, + const u32 pixelfmt) +{ + const struct capture_fmt *fmt; + unsigned int i; + + for (i = 0; i < stream->config->fmt_size; i++) { + fmt = &stream->config->fmts[i]; + if (fmt->fourcc == pixelfmt) + return fmt; + } + return NULL; +} + +static void update_mi(struct rkispp_stream *stream) +{ + void __iomem *base = stream->isppdev->base_addr; + struct rkispp_dummy_buffer *dummy_buf; + u8 en = 0; + + if (stream->next_buf) { + set_y_addr(stream, + stream->next_buf->buff_addr[RKISPP_PLANE_Y]); + set_uv_addr(stream, + stream->next_buf->buff_addr[RKISPP_PLANE_UV]); + if (stream->type == STREAM_INPUT && stream->curr_buf) { + if (stream->isppdev->module_en[ISPP_FEC]) + en = FEC_ST; + if (stream->isppdev->module_en[ISPP_2DNR]) + en = NR_SHP_ST; + if (stream->isppdev->module_en[ISPP_3DNR]) + en = TNR_ST; + writel(en, base + RKISPP_CTRL_STRT); + } + } + + if (stream->type == STREAM_OUTPUT && + !stream->next_buf) { + dummy_buf = &stream->dummy_buf; + if (!dummy_buf->vaddr) + return; + set_y_addr(stream, dummy_buf->dma_addr); + set_uv_addr(stream, dummy_buf->dma_addr); + } + + if (stream->id == STREAM_MB && + stream->isppdev->inp == INP_DDR && + stream->last_module == ISPP_3DNR) { + writel(readl(base + RKISPP_TNR_WR_Y_BASE_SHD), + base + RKISPP_TNR_IIR_Y_BASE); + writel(readl(base + RKISPP_TNR_WR_UV_BASE_SHD), + base + RKISPP_TNR_IIR_UV_BASE); + } + v4l2_dbg(2, rkispp_debug, &stream->isppdev->v4l2_dev, + "%s stream:%d CUR(Y:0x%x UV:0x%x) SHD(Y:0x%x UV:0x%x)\n", + __func__, stream->id, + readl(base + stream->config->reg.cur_y_base), + readl(base + stream->config->reg.cur_uv_base), + readl(base + stream->config->reg.cur_y_base_shd), + readl(base + stream->config->reg.cur_uv_base_shd)); +} + +static int rkispp_frame_end(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + struct capture_fmt *fmt = &stream->out_cap_fmt; + unsigned long lock_flags = 0; + int i = 0; + + if (stream->curr_buf) { + u64 ns = ktime_get_ns(); + + for (i = 0; i < fmt->mplanes; i++) { + u32 payload_size = + stream->out_fmt.plane_fmt[i].sizeimage; + vb2_set_plane_payload(&stream->curr_buf->vb.vb2_buf, i, + payload_size); + } + stream->curr_buf->vb.sequence = + atomic_read(&dev->ispp_sdev.frm_sync_seq) - 1; + stream->curr_buf->vb.vb2_buf.timestamp = ns; + vb2_buffer_done(&stream->curr_buf->vb.vb2_buf, + VB2_BUF_STATE_DONE); + stream->curr_buf = NULL; + } + + stream->curr_buf = stream->next_buf; + stream->next_buf = NULL; + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (!list_empty(&stream->buf_queue)) { + stream->next_buf = list_first_entry(&stream->buf_queue, + struct rkispp_buffer, queue); + list_del(&stream->next_buf->queue); + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + update_mi(stream); + return 0; +} + +static int config_3dnr(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *vdev; + struct rkispp_stream *stream = NULL; + void __iomem *base = dev->base_addr; + bool fbc_mode = false; + int ret, mult = 1; + u32 i, width, height, fmt; + u32 pic_size, gain_size, kg_size; + u32 addr_offs; + + vdev = &dev->stream_vdev; + if (!dev->module_en[ISPP_3DNR]) + return 0; + + if (dev->inp == INP_DDR) { + stream = &vdev->stream[STREAM_II]; + width = stream->out_fmt.width; + height = stream->out_fmt.height; + pic_size = stream->out_fmt.plane_fmt[0].sizeimage; + fmt = stream->out_cap_fmt.wr_fmt; + addr_offs = width * height; + } else { + u32 w, h; + + fbc_mode = true; + width = dev->ispp_sdev.out_fmt.width; + height = dev->ispp_sdev.out_fmt.height; + fmt = dev->ispp_sdev.out_fmt.wr_fmt; + w = ALIGN(width, 16); + h = ALIGN(height, 16); + addr_offs = w * h >> 4; + /* header and payload */ + pic_size = fmt ? w * h * 2 : w * h * 3 >> 1; + pic_size += w * h >> 4; + } + gain_size = ALIGN(width, 64) * ALIGN(height, 128) >> 5; + kg_size = gain_size * 4; + if (fmt & FMT_YUYV) + mult = 2; + + if (dev->module_en[ISPP_2DNR] || dev->module_en[ISPP_FEC]) { + struct rkispp_dummy_buffer *buf; + + if (dev->inp == INP_ISP) { + rkispp_set_bits(base + RKISPP_CTRL_QUICK, + GLB_QUICK_MODE_MASK, + GLB_QUICK_MODE(0)); + + buf = &vdev->tnr_buf.pic_cur; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_CUR_Y_BASE); + writel(buf->dma_addr + addr_offs, + base + RKISPP_TNR_CUR_UV_BASE); + + buf = &vdev->tnr_buf.gain_cur; + buf->size = gain_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_GAIN_CUR_Y_BASE); + + buf = &vdev->tnr_buf.iir; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + + if (rkispp_tnr_3to1) { + buf = &vdev->tnr_buf.pic_next; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_NXT_Y_BASE); + writel(buf->dma_addr + addr_offs, + base + RKISPP_TNR_NXT_UV_BASE); + buf = &vdev->tnr_buf.gain_next; + buf->size = gain_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_GAIN_NXT_Y_BASE); + } + } + buf = &vdev->tnr_buf.gain_kg; + buf->size = kg_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_GAIN_KG_Y_BASE); + + buf = &vdev->tnr_buf.pic_wr; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_WR_Y_BASE); + writel(buf->dma_addr + addr_offs, + base + RKISPP_TNR_WR_UV_BASE); + if (vdev->tnr_buf.iir.vaddr) + buf = &vdev->tnr_buf.iir; + writel(buf->dma_addr, + base + RKISPP_TNR_IIR_Y_BASE); + writel(buf->dma_addr + addr_offs, + base + RKISPP_TNR_IIR_UV_BASE); + + buf = &vdev->tnr_buf.gain_wr; + buf->size = gain_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, + base + RKISPP_TNR_GAIN_WR_Y_BASE); + + writel(ALIGN(width * mult, 16) >> 2, + base + RKISPP_TNR_WR_VIR_STRIDE); + writel(fmt << 4 | SW_TNR_1ST_FRM, + base + RKISPP_TNR_CTRL); + } + + if (stream) { + stream->config->frame_end_id = TNR_INT; + stream->config->reg.cur_y_base = RKISPP_TNR_CUR_Y_BASE; + stream->config->reg.cur_uv_base = RKISPP_TNR_CUR_UV_BASE; + stream->config->reg.cur_y_base_shd = RKISPP_TNR_CUR_Y_BASE_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_TNR_CUR_UV_BASE_SHD; + } + + rkispp_set_bits(base + RKISPP_TNR_CTRL, FMT_RD_MASK, fmt); + if (fbc_mode) { + rkispp_set_bits(base + RKISPP_TNR_CTRL, 0, + SW_TNR_FBCD_MODE | SW_TNR_WR_FBCE_MODE); + writel(0, base + RKISPP_TNR_CUR_VIR_STRIDE); + writel(0, base + RKISPP_TNR_IIR_VIR_STRIDE); + writel(0, base + RKISPP_TNR_NXT_VIR_STRIDE); + } else { + rkispp_clear_bits(base + RKISPP_TNR_CTRL, + SW_TNR_FBCD_MODE | SW_TNR_WR_FBCE_MODE); + writel(ALIGN(width * mult, 16) >> 2, + base + RKISPP_TNR_CUR_VIR_STRIDE); + writel(ALIGN(width * mult, 16) >> 2, + base + RKISPP_TNR_IIR_VIR_STRIDE); + writel(ALIGN(width * mult, 16) >> 2, + base + RKISPP_TNR_NXT_VIR_STRIDE); + } + //writel(0xf, base + RKISPP_TNR_CORE_CTRL);//3to1 + //writel(0xd, base + RKISPP_TNR_CORE_CTRL);//2to1 + writel(rkispp_tnr_3to1 << 1, base + RKISPP_TNR_CORE_CTRL);//bypass tnr + writel(ALIGN(width, 64) >> 4, + base + RKISPP_TNR_GAIN_CUR_VIR_STRIDE); + writel(ALIGN(width, 64) >> 4, + base + RKISPP_TNR_GAIN_NXT_VIR_STRIDE); + writel(ALIGN(width, 16) * 6, + base + RKISPP_TNR_GAIN_KG_VIR_STRIDE); + writel(ALIGN(width, 64) >> 4, + base + RKISPP_TNR_GAIN_WR_VIR_STRIDE); + + writel(height << 16 | width, + base + RKISPP_CTRL_TNR_SIZE); + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s size:%dx%d ctrl:0x%x core_ctrl:0x%x\n", + __func__, width, height, + readl(base + RKISPP_TNR_CTRL), + readl(base + RKISPP_TNR_CORE_CTRL)); + return 0; +err: + for (i = 0; i < sizeof(vdev->tnr_buf) / + sizeof(struct rkispp_dummy_buffer); i++) + rkispp_free_buffer(dev, &vdev->tnr_buf.pic_cur + i); + v4l2_err(&dev->v4l2_dev, + "%s Failed to allocate buffer\n", __func__); + return ret; +} + +static int config_2dnr_shp(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *vdev; + struct rkispp_stream *stream = NULL; + void __iomem *base = dev->base_addr; + struct rkispp_dummy_buffer *buf; + u32 width, height, fmt; + u32 pic_size, gain_size; + u32 i, addr_offs; + int ret, mult = 1; + bool fbcd_mode = false; + + vdev = &dev->stream_vdev; + if (!dev->module_en[ISPP_2DNR]) + return 0; + + if (dev->inp == INP_DDR) { + stream = &vdev->stream[STREAM_II]; + width = stream->out_fmt.width; + height = stream->out_fmt.height; + pic_size = stream->out_fmt.plane_fmt[0].sizeimage; + fmt = stream->out_cap_fmt.wr_fmt; + addr_offs = width * height; + } else { + u32 w, h; + + fbcd_mode = true; + width = dev->ispp_sdev.out_fmt.width; + height = dev->ispp_sdev.out_fmt.height; + fmt = dev->ispp_sdev.out_fmt.wr_fmt; + w = ALIGN(width, 16); + h = ALIGN(height, 16); + addr_offs = w * h >> 4; + /* header and payload */ + pic_size = fmt ? w * h * 2 : w * h * 3 >> 1; + pic_size += w * h >> 4; + } + gain_size = ALIGN(width, 64) * ALIGN(height, 128) >> 5; + if (fmt & FMT_YUYV) + mult = 2; + + if (dev->module_en[ISPP_3DNR]) { + writel(readl(base + RKISPP_TNR_WR_Y_BASE), + base + RKISPP_NR_ADDR_BASE_Y); + writel(readl(base + RKISPP_TNR_WR_UV_BASE), + base + RKISPP_NR_ADDR_BASE_UV); + writel(readl(base + RKISPP_TNR_GAIN_WR_Y_BASE), + base + RKISPP_NR_ADDR_BASE_GAIN); + rkispp_set_bits(base + RKISPP_CTRL_QUICK, 0, GLB_NR_SD32_TNR); + } else { + if (dev->inp == INP_ISP) { + /* tnr need to set same format with nr in the fbc mode */ + rkispp_set_bits(base + RKISPP_TNR_CTRL, + FMT_RD_MASK, fmt); + rkispp_set_bits(base + RKISPP_CTRL_QUICK, + GLB_QUICK_MODE_MASK, + GLB_QUICK_MODE(2)); + + buf = &vdev->nr_buf.pic_cur; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, base + RKISPP_NR_ADDR_BASE_Y); + writel(buf->dma_addr + addr_offs, + base + RKISPP_NR_ADDR_BASE_UV); + buf = &vdev->nr_buf.gain_cur; + buf->size = gain_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, base + RKISPP_NR_ADDR_BASE_GAIN); + rkispp_clear_bits(base + RKISPP_CTRL_QUICK, GLB_NR_SD32_TNR); + } else if (stream) { + stream->config->frame_end_id = NR_INT; + stream->config->reg.cur_y_base = RKISPP_NR_ADDR_BASE_Y; + stream->config->reg.cur_uv_base = RKISPP_NR_ADDR_BASE_UV; + stream->config->reg.cur_y_base_shd = RKISPP_NR_ADDR_BASE_Y_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_NR_ADDR_BASE_UV_SHD; + } + } + + rkispp_clear_bits(base + RKISPP_CTRL_QUICK, GLB_FEC2SCL_EN); + if (dev->module_en[ISPP_FEC]) { + buf = &vdev->nr_buf.pic_wr; + buf->size = pic_size; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, base + RKISPP_SHARP_WR_Y_BASE); + writel(buf->dma_addr + addr_offs, + base + RKISPP_SHARP_WR_UV_BASE); + + writel(ALIGN(width * mult, 16) >> 2, + base + RKISPP_SHARP_WR_VIR_STRIDE); + rkispp_set_bits(base + RKISPP_SHARP_CTRL, + SW_SHP_WR_FORMAT_MASK, fmt); + } + + buf = &vdev->nr_buf.tmp_yuv; + buf->size = width * 42; + ret = rkispp_allow_buffer(dev, buf); + if (ret < 0) + goto err; + writel(buf->dma_addr, base + RKISPP_SHARP_TMP_YUV_BASE); + + rkispp_set_bits(base + RKISPP_NR_CTRL, FMT_RD_MASK, fmt); + if (fbcd_mode) { + rkispp_set_bits(base + RKISPP_NR_CTRL, 0, SW_NR_RD_FBCD_MODE); + writel(0, base + RKISPP_NR_VIR_STRIDE); + writel(ALIGN(height, 16), base + RKISPP_FBC_VIR_HEIGHT); + } else { + rkispp_clear_bits(base + RKISPP_NR_CTRL, SW_NR_RD_FBCD_MODE); + writel(ALIGN(width * mult, 16) >> 2, base + RKISPP_NR_VIR_STRIDE); + } + writel(ALIGN(width, 64) >> 4, base + RKISPP_NR_VIR_STRIDE_GAIN); + writel(0, base + 0x480);//ynr&uvnr bypass + writel(0, base + 0x680);//sharp_enable + writel(height << 16 | width, base + RKISPP_CTRL_SIZE); + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s size:%dx%d\n" + "nr ctrl:0x%x ctrl_para:0x%x\n" + "shp ctrl:0x%x core_ctrl:0x%x\n", + __func__, width, height, + readl(base + RKISPP_NR_CTRL), + readl(base + RKISPP_NR_UVNR_CTRL_PARA), + readl(base + RKISPP_SHARP_CTRL), + readl(base + RKISPP_SHARP_CORE_CTRL)); + return 0; +err: + for (i = 0; i < sizeof(vdev->nr_buf) / + sizeof(struct rkispp_dummy_buffer); i++) + rkispp_free_buffer(dev, &vdev->nr_buf.pic_cur + i); + v4l2_err(&dev->v4l2_dev, + "%s Failed to allocate buffer\n", __func__); + return ret; +} + +static int config_fec(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *vdev; + struct rkispp_stream *stream = NULL; + void __iomem *base = dev->base_addr; + u32 width, height, fmt, mult = 1; + + vdev = &dev->stream_vdev; + if (!dev->module_en[ISPP_FEC]) + return 0; + + if (dev->inp == INP_DDR) { + stream = &vdev->stream[STREAM_II]; + width = stream->out_fmt.width; + height = stream->out_fmt.height; + fmt = stream->out_cap_fmt.wr_fmt; + } else { + width = dev->ispp_sdev.out_fmt.width; + height = dev->ispp_sdev.out_fmt.height; + fmt = dev->ispp_sdev.out_fmt.wr_fmt; + } + if (fmt & FMT_YUYV) + mult = 2; + + if (dev->module_en[ISPP_2DNR]) { + writel(readl(base + RKISPP_SHARP_WR_Y_BASE), + base + RKISPP_FEC_RD_Y_BASE); + writel(readl(base + RKISPP_SHARP_WR_UV_BASE), + base + RKISPP_FEC_RD_UV_BASE); + } else if (stream) { + stream->config->frame_end_id = FEC_INT; + stream->config->reg.cur_y_base = RKISPP_FEC_RD_Y_BASE; + stream->config->reg.cur_uv_base = RKISPP_FEC_RD_UV_BASE; + stream->config->reg.cur_y_base_shd = RKISPP_FEC_RD_Y_BASE_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_FEC_RD_UV_BASE_SHD; + } + + rkispp_set_bits(base + RKISPP_FEC_CTRL, FMT_RD_MASK, fmt); + writel(ALIGN(width * mult, 16) >> 2, base + RKISPP_FEC_RD_VIR_STRIDE); + writel(height << 16 | width, base + RKISPP_FEC_PIC_SIZE); + rkispp_set_bits(base + RKISPP_CTRL_QUICK, 0, GLB_FEC2SCL_EN); + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s size:%dx%d ctrl:0x%x core_ctrl:0x%x\n", + __func__, width, height, + readl(base + RKISPP_FEC_CTRL), + readl(base + RKISPP_FEC_CORE_CTRL)); + return 0; +} + +static int config_modules(struct rkispp_device *dev) +{ + int ret; + + ret = config_3dnr(dev); + if (ret < 0) + return ret; + + ret = config_2dnr_shp(dev); + if (ret < 0) + return ret; + + return config_fec(dev); +} + +static int start_ii(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + struct rkispp_stream_vdev *vdev = &dev->stream_vdev; + void __iomem *base = dev->base_addr; + struct rkispp_stream *st; + int i; + + rkispp_clear_bits(base + RKISPP_CTRL_QUICK, GLB_QUICK_EN); + writel(ALL_FORCE_UPD, base + RKISPP_CTRL_UPDATE); + for (i = STREAM_S2; i >= STREAM_II; i--) { + st = &vdev->stream[i]; + if (i == STREAM_II) + stream->streaming = true; + rkispp_frame_end(st); + } + return 0; +} + +static int config_ii(struct rkispp_stream *stream) +{ + return config_modules(stream->isppdev); +} + +static int config_mb(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + void __iomem *base = dev->base_addr; + int i, mult = 1; + + for (i = ISPP_FEC; i >= ISPP_3DNR; i--) { + if (dev->module_en[i]) + break; + } + if (i < 0) + return -EINVAL; + + stream->last_module = i; + switch (i) { + case ISPP_3DNR: + stream->config->frame_end_id = TNR_INT; + stream->config->reg.cur_y_base = RKISPP_TNR_WR_Y_BASE; + stream->config->reg.cur_uv_base = RKISPP_TNR_WR_UV_BASE; + stream->config->reg.cur_vir_stride = RKISPP_TNR_WR_VIR_STRIDE; + stream->config->reg.cur_y_base_shd = RKISPP_TNR_WR_Y_BASE_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_TNR_WR_UV_BASE_SHD; + rkispp_set_bits(base + RKISPP_TNR_CTRL, FMT_WR_MASK, + SW_TNR_1ST_FRM | stream->out_cap_fmt.wr_fmt << 4); + break; + case ISPP_2DNR: + case ISPP_SHP: + stream->config->frame_end_id = SHP_INT; + stream->config->reg.cur_y_base = RKISPP_SHARP_WR_Y_BASE; + stream->config->reg.cur_uv_base = RKISPP_SHARP_WR_UV_BASE; + stream->config->reg.cur_vir_stride = RKISPP_SHARP_WR_VIR_STRIDE; + stream->config->reg.cur_y_base_shd = RKISPP_SHARP_WR_Y_BASE_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_SHARP_WR_UV_BASE_SHD; + rkispp_set_bits(base + RKISPP_SHARP_CTRL, SW_SHP_WR_FORMAT_MASK, + stream->out_cap_fmt.wr_fmt); + rkispp_clear_bits(base + RKISPP_SHARP_CORE_CTRL, SW_SHP_DMA_DIS); + break; + default: + stream->config->frame_end_id = FEC_INT; + stream->config->reg.cur_y_base = RKISPP_FEC_WR_Y_BASE; + stream->config->reg.cur_uv_base = RKISPP_FEC_WR_UV_BASE; + stream->config->reg.cur_vir_stride = RKISPP_FEC_WR_VIR_STRIDE; + stream->config->reg.cur_y_base_shd = RKISPP_FEC_WR_Y_BASE_SHD; + stream->config->reg.cur_uv_base_shd = RKISPP_FEC_WR_UV_BASE_SHD; + rkispp_set_bits(base + RKISPP_FEC_CTRL, FMT_WR_MASK, + stream->out_cap_fmt.wr_fmt << 4); + writel((stream->out_fmt.height << 16) | stream->out_fmt.width, + base + RKISPP_FEC_PIC_SIZE); + } + if (stream->out_cap_fmt.wr_fmt & FMT_YUYV) + mult = 2; + set_vir_stride(stream, ALIGN(stream->out_fmt.width * mult, 16) >> 2); + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s last module:%d\n", __func__, i); + return 0; +} + +static void stop_mb(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + void __iomem *base = dev->base_addr; + + switch (stream->last_module) { + case ISPP_3DNR: + rkispp_clear_bits(base + RKISPP_TNR_CORE_CTRL, SW_TNR_EN); + break; + case ISPP_2DNR: + case ISPP_SHP: + rkispp_clear_bits(base + RKISPP_NR_UVNR_CTRL_PARA, SW_NR_EN); + rkispp_clear_bits(base + RKISPP_SHARP_CORE_CTRL, SW_SHP_EN); + break; + default: + rkispp_clear_bits(base + RKISPP_FEC_CORE_CTRL, SW_FEC_EN); + } +} + +static int is_stopped_mb(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + void __iomem *base = dev->base_addr; + u32 val, en; + + switch (stream->last_module) { + case ISPP_3DNR: + en = SW_TNR_EN_SHD; + val = readl(base + RKISPP_TNR_CORE_CTRL); + break; + case ISPP_2DNR: + case ISPP_SHP: + en = SW_SHP_EN; + val = readl(base + RKISPP_SHARP_CORE_CTRL); + rkispp_set_bits(base + RKISPP_SHARP_CORE_CTRL, + 0, SW_SHP_DMA_DIS); + break; + default: + en = SW_FEC_EN_SHD; + val = readl(base + RKISPP_FEC_CORE_CTRL); + rkispp_set_bits(base + RKISPP_FEC_CORE_CTRL, + 0, SW_FEC2DDR_DIS); + } + + return !(val & en); +} + +static int config_scl(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + void __iomem *base = dev->base_addr; + const struct capture_fmt *fmt = &stream->out_cap_fmt; + u32 in_width = dev->ispp_sdev.out_fmt.width; + u32 in_height = dev->ispp_sdev.out_fmt.height; + u32 hy_fac = (stream->out_fmt.width - 1) * 8192 / + (in_width - 1) + 1; + u32 vy_fac = (stream->out_fmt.height - 1) * 8192 / + (in_height - 1) + 1; + u8 bypass = 0, mult = 1; + + if (hy_fac == 8193 && vy_fac == 8193) + bypass = SW_SCL_BYPASS; + if (fmt->wr_fmt & FMT_YUYV) + mult = 2; + set_vir_stride(stream, ALIGN(stream->out_fmt.width * mult, 16) >> 2); + set_scl_factor(stream, vy_fac << 16 | hy_fac); + set_ctrl(stream, fmt->wr_fmt << 3 | bypass | SW_SCL_ENABLE); + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "scl%d ctrl:0x%x stride:0x%x factor:0x%x\n", + stream->id - STREAM_S0, + readl(base + stream->config->reg.ctrl), + readl(base + stream->config->reg.cur_vir_stride), + readl(base + stream->config->reg.factor)); + return 0; +} + +static void stop_scl(struct rkispp_stream *stream) +{ + void __iomem *base = stream->isppdev->base_addr; + + rkispp_clear_bits(base + stream->config->reg.ctrl, SW_SCL_ENABLE); +} + +static int is_stopped_scl(struct rkispp_stream *stream) +{ + void __iomem *base = stream->isppdev->base_addr; + + return !(readl(base + stream->config->reg.ctrl) & SW_SCL_ENABLE_SHD); +} + +static struct streams_ops input_stream_ops = { + .config = config_ii, + .start = start_ii, +}; + +static struct streams_ops mb_stream_ops = { + .config = config_mb, + .stop = stop_mb, + .is_stopped = is_stopped_mb, +}; + +static struct streams_ops scal_stream_ops = { + .config = config_scl, + .stop = stop_scl, + .is_stopped = is_stopped_scl, +}; + +/***************************** vb2 operations*******************************/ + +static int rkispp_queue_setup(struct vb2_queue *queue, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_ctxs[]) +{ + struct rkispp_stream *stream = queue->drv_priv; + struct rkispp_device *dev = stream->isppdev; + const struct v4l2_pix_format_mplane *pixm = NULL; + const struct capture_fmt *cap_fmt = NULL; + u32 i; + + pixm = &stream->out_fmt; + cap_fmt = &stream->out_cap_fmt; + *num_planes = cap_fmt->mplanes; + + for (i = 0; i < cap_fmt->mplanes; i++) { + const struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &pixm->plane_fmt[i]; + sizes[i] = plane_fmt->sizeimage; + } + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s count %d, size %d\n", + v4l2_type_names[queue->type], *num_buffers, sizes[0]); + + return 0; +} + +static void rkispp_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct rkispp_buffer *isppbuf = to_rkispp_buffer(vbuf); + struct vb2_queue *queue = vb->vb2_queue; + struct rkispp_stream *stream = queue->drv_priv; + unsigned long lock_flags = 0; + struct v4l2_pix_format_mplane *pixm = &stream->out_fmt; + struct capture_fmt *cap_fmt = &stream->out_cap_fmt; + int i; + + memset(isppbuf->buff_addr, 0, sizeof(isppbuf->buff_addr)); + for (i = 0; i < cap_fmt->mplanes; i++) + isppbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); + + /* + * NOTE: plane_fmt[0].sizeimage is total size of all planes for single + * memory plane formats, so calculate the size explicitly. + */ + if (cap_fmt->mplanes == 1) { + for (i = 0; i < cap_fmt->cplanes - 1; i++) { + isppbuf->buff_addr[i + 1] = (i == 0) ? + isppbuf->buff_addr[i] + + pixm->plane_fmt[i].bytesperline * + pixm->height : + isppbuf->buff_addr[i] + + pixm->plane_fmt[i].sizeimage; + } + } + + v4l2_dbg(2, rkispp_debug, &stream->isppdev->v4l2_dev, + "stream:%d queue buf:0x%x\n", + stream->id, isppbuf->buff_addr[0]); + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->type == STREAM_INPUT && + stream->streaming && + !stream->next_buf) { + stream->next_buf = isppbuf; + update_mi(stream); + } else { + list_add_tail(&isppbuf->queue, &stream->buf_queue); + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); +} + +static int rkispp_create_dummy_buf(struct rkispp_stream *stream) +{ + struct rkispp_dummy_buffer *buf = &stream->dummy_buf; + struct rkispp_device *dev = stream->isppdev; + + if (stream->type != STREAM_OUTPUT) + return 0; + + buf->size = max3(stream->out_fmt.plane_fmt[0].bytesperline * + stream->out_fmt.height, + stream->out_fmt.plane_fmt[1].sizeimage, + stream->out_fmt.plane_fmt[2].sizeimage); + return rkispp_allow_buffer(dev, buf); +} + +static void rkispp_destroy_dummy_buf(struct rkispp_stream *stream) +{ + struct rkispp_dummy_buffer *buf = &stream->dummy_buf; + struct rkispp_device *dev = stream->isppdev; + struct rkispp_stream_vdev *vdev; + u32 i; + + vdev = &dev->stream_vdev; + rkispp_free_buffer(dev, buf); + + if (atomic_read(&vdev->refcnt) == 1) { + for (i = 0; i < sizeof(vdev->tnr_buf) / + sizeof(struct rkispp_dummy_buffer); i++) + rkispp_free_buffer(dev, &vdev->tnr_buf.pic_cur + i); + for (i = 0; i < sizeof(vdev->nr_buf) / + sizeof(struct rkispp_dummy_buffer); i++) + rkispp_free_buffer(dev, &vdev->nr_buf.pic_cur + i); + } +} + +static void rkispp_stream_stop(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + bool wait = false; + int ret = 0; + + stream->stopping = true; + if (stream->ops->stop) { + stream->ops->stop(stream); + if (dev->inp == INP_DDR && + !dev->stream_vdev.stream[STREAM_II].streaming) + wait = false; + else + wait = true; + } + if (dev->inp == INP_ISP && + atomic_read(&dev->stream_vdev.refcnt) == 1) + v4l2_subdev_call(&dev->ispp_sdev.sd, + video, s_stream, false); + if (wait) { + ret = wait_event_timeout(stream->done, + !stream->streaming, + msecs_to_jiffies(100)); + if (!ret) + v4l2_warn(&dev->v4l2_dev, + "waiting on event ret:%d\n", ret); + } + stream->streaming = false; + stream->stopping = false; +} + +static void destroy_buf_queue(struct rkispp_stream *stream, + enum vb2_buffer_state state) +{ + unsigned long lock_flags = 0; + struct rkispp_buffer *buf; + + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->curr_buf) { + list_add_tail(&stream->curr_buf->queue, &stream->buf_queue); + stream->curr_buf = NULL; + } + if (stream->next_buf) { + list_add_tail(&stream->next_buf->queue, &stream->buf_queue); + stream->next_buf = NULL; + } + while (!list_empty(&stream->buf_queue)) { + buf = list_first_entry(&stream->buf_queue, + struct rkispp_buffer, queue); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb.vb2_buf, state); + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); +} + +static void rkispp_stop_streaming(struct vb2_queue *queue) +{ + struct rkispp_stream *stream = queue->drv_priv; + struct rkispp_device *dev = stream->isppdev; + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, stream->id); + + rkispp_stream_stop(stream); + destroy_buf_queue(stream, VB2_BUF_STATE_ERROR); + rkispp_destroy_dummy_buf(stream); + atomic_dec(&dev->stream_vdev.refcnt); +} + +static int trigger_isp(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *vdev = &dev->stream_vdev; + struct rkispp_stream *stream; + int i, ret; + + if (dev->inp != INP_ISP) + return 0; + + /* output stream enable then start isp*/ + for (i = STREAM_MB; i < STREAM_S2 + 1; i++) { + stream = &vdev->stream[i]; + if (stream->linked && !stream->streaming) + return 0; + } + ret = config_modules(dev); + if (ret) + return ret; + rkispp_set_bits(dev->base_addr + RKISPP_CTRL_QUICK, + 0, GLB_QUICK_EN); + writel(ALL_FORCE_UPD, dev->base_addr + RKISPP_CTRL_UPDATE); + for (i = STREAM_MB; i < STREAM_MAX; i++) { + stream = &vdev->stream[i]; + rkispp_frame_end(stream); + } + return v4l2_subdev_call(&dev->ispp_sdev.sd, + video, s_stream, true); +} + +static int rkispp_start_streaming(struct vb2_queue *queue, + unsigned int count) +{ + struct rkispp_stream *stream = queue->drv_priv; + struct rkispp_device *dev = stream->isppdev; + int ret = -1; + + v4l2_dbg(1, rkispp_debug, &dev->v4l2_dev, + "%s id:%d\n", __func__, stream->id); + + if (stream->streaming) + return -EBUSY; + + atomic_inc(&dev->stream_vdev.refcnt); + if (!dev->inp || !stream->linked) { + v4l2_err(&dev->v4l2_dev, + "invalid input source\n"); + goto free_buf_queue; + } + + ret = rkispp_create_dummy_buf(stream); + if (ret < 0) + goto free_buf_queue; + + if (stream->ops->config) { + ret = stream->ops->config(stream); + if (ret < 0) + goto free_dummy_buf; + } + + /* config first buf */ + rkispp_frame_end(stream); + + /* start from ddr */ + if (stream->ops->start) + stream->ops->start(stream); + + stream->streaming = true; + + /* start from isp */ + ret = trigger_isp(dev); + if (ret) + goto free_dummy_buf; + return 0; +free_dummy_buf: + rkispp_destroy_dummy_buf(stream); +free_buf_queue: + destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED); + atomic_dec(&dev->stream_vdev.refcnt); + return ret; +} + +static struct vb2_ops stream_vb2_ops = { + .queue_setup = rkispp_queue_setup, + .buf_queue = rkispp_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .stop_streaming = rkispp_stop_streaming, + .start_streaming = rkispp_start_streaming, +}; + +static int rkispp_init_vb2_queue(struct vb2_queue *q, + struct rkispp_stream *stream, + enum v4l2_buf_type buf_type) +{ + q->type = buf_type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_USERPTR; + q->drv_priv = stream; + q->ops = &stream_vb2_ops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct rkispp_buffer); + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + q->min_buffers_needed = STREAM_IN_REQ_BUFS_MIN; + else + q->min_buffers_needed = STREAM_OUT_REQ_BUFS_MIN; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &stream->isppdev->apilock; + q->dev = stream->isppdev->dev; + + return vb2_queue_init(q); +} + +static int rkispp_set_fmt(struct rkispp_stream *stream, + struct v4l2_pix_format_mplane *pixm, + bool try) +{ + const struct capture_fmt *fmt; + unsigned int imagsize = 0; + unsigned int planes; + u32 xsubs = 1, ysubs = 1; + unsigned int i; + + fmt = find_fmt(stream, pixm->pixelformat); + if (!fmt) { + v4l2_err(&stream->isppdev->v4l2_dev, + "nonsupport pixelformat:%c%c%c%c\n", + pixm->pixelformat, + pixm->pixelformat >> 8, + pixm->pixelformat >> 16, + pixm->pixelformat >> 24); + return -EINVAL; + } + + pixm->num_planes = fmt->mplanes; + pixm->field = V4L2_FIELD_NONE; + if (!pixm->quantization) + pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE; + + /* calculate size */ + fcc_xysubs(fmt->fourcc, &xsubs, &ysubs); + planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes; + for (i = 0; i < planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + unsigned int width, height, bytesperline; + + plane_fmt = pixm->plane_fmt + i; + + if (i == 0) { + width = pixm->width; + height = pixm->height; + } else { + width = pixm->width / xsubs; + height = pixm->height / ysubs; + } + + bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8); + if (i != 0 || + plane_fmt->bytesperline < bytesperline) + plane_fmt->bytesperline = bytesperline; + + plane_fmt->sizeimage = plane_fmt->bytesperline * height; + + imagsize += plane_fmt->sizeimage; + } + + if (fmt->mplanes == 1) + pixm->plane_fmt[0].sizeimage = imagsize; + + if (!try) { + stream->out_cap_fmt = *fmt; + stream->out_fmt = *pixm; + + if (stream->id == STREAM_II) { + struct rkispp_subdev *sdev = &stream->isppdev->ispp_sdev; + + sdev->out_fmt.width = pixm->width; + sdev->out_fmt.height = pixm->height; + } + v4l2_dbg(1, rkispp_debug, &stream->isppdev->v4l2_dev, + "%s: stream: %d req(%d, %d) out(%d, %d)\n", __func__, + stream->id, pixm->width, pixm->height, + stream->out_fmt.width, stream->out_fmt.height); + } + return 0; +} + +/************************* v4l2_file_operations***************************/ + +static const struct v4l2_file_operations rkispp_fops = { + .open = rkispp_fh_open, + .release = rkispp_fh_release, + .unlocked_ioctl = video_ioctl2, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, +}; + +static int rkispp_try_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkispp_stream *stream = video_drvdata(file); + + return rkispp_set_fmt(stream, &f->fmt.pix_mp, true); +} + +static int rkispp_enum_fmt_vid_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct rkispp_stream *stream = video_drvdata(file); + const struct capture_fmt *fmt = NULL; + + if (f->index >= stream->config->fmt_size) + return -EINVAL; + + fmt = &stream->config->fmts[f->index]; + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int rkispp_s_fmt_vid_mplane(struct file *file, + void *priv, struct v4l2_format *f) +{ + struct rkispp_stream *stream = video_drvdata(file); + struct video_device *vdev = &stream->vnode.vdev; + struct rkispp_vdev_node *node = vdev_to_node(vdev); + struct rkispp_device *dev = stream->isppdev; + + if (vb2_is_busy(&node->buf_queue)) { + v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__); + return -EBUSY; + } + + return rkispp_set_fmt(stream, &f->fmt.pix_mp, false); +} + +static int rkispp_g_fmt_vid_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct rkispp_stream *stream = video_drvdata(file); + + f->fmt.pix_mp = stream->out_fmt; + + return 0; +} + +static int rkispp_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct rkispp_stream *stream = video_drvdata(file); + struct device *dev = stream->isppdev->dev; + struct video_device *vdev = video_devdata(file); + + strlcpy(cap->card, vdev->name, sizeof(cap->card)); + snprintf(cap->driver, sizeof(cap->driver), + "%s_v%d", dev->driver->name, + stream->isppdev->ispp_ver >> 4); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + + return 0; +} + +static const struct v4l2_ioctl_ops rkispp_v4l2_ioctl_ops = { + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_try_fmt_vid_cap_mplane = rkispp_try_fmt_vid_mplane, + .vidioc_enum_fmt_vid_cap_mplane = rkispp_enum_fmt_vid_mplane, + .vidioc_s_fmt_vid_cap_mplane = rkispp_s_fmt_vid_mplane, + .vidioc_g_fmt_vid_cap_mplane = rkispp_g_fmt_vid_mplane, + .vidioc_try_fmt_vid_out_mplane = rkispp_try_fmt_vid_mplane, + .vidioc_enum_fmt_vid_out_mplane = rkispp_enum_fmt_vid_mplane, + .vidioc_s_fmt_vid_out_mplane = rkispp_s_fmt_vid_mplane, + .vidioc_g_fmt_vid_out_mplane = rkispp_g_fmt_vid_mplane, + .vidioc_querycap = rkispp_querycap, +}; + +static void rkispp_unregister_stream_video(struct rkispp_stream *stream) +{ + media_entity_cleanup(&stream->vnode.vdev.entity); + video_unregister_device(&stream->vnode.vdev); +} + +static int rkispp_register_stream_video(struct rkispp_stream *stream) +{ + struct rkispp_device *dev = stream->isppdev; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct video_device *vdev = &stream->vnode.vdev; + struct rkispp_vdev_node *node; + enum v4l2_buf_type buf_type; + int ret = 0; + + node = vdev_to_node(vdev); + vdev->release = video_device_release_empty; + vdev->fops = &rkispp_fops; + vdev->minor = -1; + vdev->v4l2_dev = v4l2_dev; + vdev->lock = &dev->apilock; + video_set_drvdata(vdev, stream); + + vdev->ioctl_ops = &rkispp_v4l2_ioctl_ops; + if (stream->type == STREAM_INPUT) { + vdev->device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_OUTPUT_MPLANE; + vdev->vfl_dir = VFL_DIR_TX; + node->pad.flags = MEDIA_PAD_FL_SOURCE; + buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + } else { + vdev->device_caps = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE; + vdev->vfl_dir = VFL_DIR_RX; + node->pad.flags = MEDIA_PAD_FL_SINK; + buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + } + + rkispp_init_vb2_queue(&node->buf_queue, stream, buf_type); + vdev->queue = &node->buf_queue; + + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + if (ret < 0) { + v4l2_err(v4l2_dev, + "video register failed with error %d\n", ret); + return ret; + } + + ret = media_entity_pads_init(&vdev->entity, 1, &node->pad); + if (ret < 0) + goto unreg; + return 0; +unreg: + video_unregister_device(vdev); + return ret; +} + +void rkispp_isr(u32 mis_val, struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *vdev; + struct rkispp_stream *stream; + void __iomem *base = dev->base_addr; + u32 i, err_mask = NR_LOST_ERR | TNR_LOST_ERR | + UVNR_MONITOR_ERR | FBCH_EMPTY_NR | + FBCH_EMPTY_TNR | FBCD_DEC_ERR_NR | + BUS_ERR_NR | BUS_ERR_TNR; + + v4l2_dbg(3, rkispp_debug, &dev->v4l2_dev, + "isr:0x%x\n", mis_val); + + vdev = &dev->stream_vdev; + writel(mis_val, base + RKISPP_CTRL_INT_CLR); + if (mis_val & err_mask) + v4l2_err(&dev->v4l2_dev, + "ispp err:0x%x\n", mis_val); + + if (mis_val & (CMD_TNR_ST_DONE | CMD_NR_SHP_ST_DONE)) + atomic_inc(&dev->ispp_sdev.frm_sync_seq); + + if (mis_val & TNR_INT) { + if (readl(base + RKISPP_TNR_CTRL) & SW_TNR_1ST_FRM) + rkispp_clear_bits(base + RKISPP_TNR_CTRL, + SW_TNR_1ST_FRM); + if (dev->inp == INP_DDR && + dev->module_en[ISPP_2DNR]) + writel(NR_SHP_ST, base + RKISPP_CTRL_STRT); + } + + if (mis_val & SHP_INT && + dev->inp == INP_DDR && + dev->module_en[ISPP_FEC]) + writel(FEC_ST, base + RKISPP_CTRL_STRT); + + for (i = 0; i < STREAM_MAX; i++) { + stream = &vdev->stream[i]; + + if (!stream->streaming || + !(mis_val & INT_FRAME(stream))) + continue; + if (stream->stopping) { + if (stream->ops->is_stopped && + stream->ops->is_stopped(stream)) { + stream->stopping = false; + stream->streaming = false; + wake_up(&stream->done); + } + } else { + rkispp_frame_end(stream); + } + } +} + +int rkispp_register_stream_vdevs(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *stream_vdev; + struct rkispp_stream *stream; + struct video_device *vdev; + char *vdev_name; + int i, j, ret = 0; + + stream_vdev = &dev->stream_vdev; + memset(stream_vdev, 0, sizeof(*stream_vdev)); + atomic_set(&stream_vdev->refcnt, 0); + + for (i = 0; i < STREAM_MAX; i++) { + stream = &stream_vdev->stream[i]; + stream->id = i; + stream->isppdev = dev; + INIT_LIST_HEAD(&stream->buf_queue); + init_waitqueue_head(&stream->done); + spin_lock_init(&stream->vbq_lock); + vdev = &stream->vnode.vdev; + switch (i) { + case STREAM_II: + vdev_name = II_VDEV_NAME; + stream->type = STREAM_INPUT; + stream->ops = &input_stream_ops; + stream->config = &input_config; + break; + case STREAM_MB: + vdev_name = MB_VDEV_NAME; + stream->type = STREAM_OUTPUT; + stream->ops = &mb_stream_ops; + stream->config = &mb_config; + break; + case STREAM_S0: + vdev_name = S0_VDEV_NAME; + stream->type = STREAM_OUTPUT; + stream->ops = &scal_stream_ops; + stream->config = &scl0_config; + break; + case STREAM_S1: + vdev_name = S1_VDEV_NAME; + stream->type = STREAM_OUTPUT; + stream->ops = &scal_stream_ops; + stream->config = &scl1_config; + break; + case STREAM_S2: + vdev_name = S2_VDEV_NAME; + stream->type = STREAM_OUTPUT; + stream->ops = &scal_stream_ops; + stream->config = &scl2_config; + break; + default: + v4l2_err(&dev->v4l2_dev, "Invalid stream:%d\n", i); + return -EINVAL; + } + strlcpy(vdev->name, vdev_name, sizeof(vdev->name)); + ret = rkispp_register_stream_video(stream); + if (ret < 0) + goto err; + } + return 0; +err: + for (j = 0; j < i; j++) { + stream = &stream_vdev->stream[j]; + rkispp_unregister_stream_video(stream); + } + return ret; +} + +void rkispp_unregister_stream_vdevs(struct rkispp_device *dev) +{ + struct rkispp_stream_vdev *stream_vdev; + struct rkispp_stream *stream; + int i; + + stream_vdev = &dev->stream_vdev; + for (i = 0; i < STREAM_MAX; i++) { + stream = &stream_vdev->stream[i]; + rkispp_unregister_stream_video(stream); + } +} diff --git a/drivers/media/platform/rockchip/ispp/stream.h b/drivers/media/platform/rockchip/ispp/stream.h new file mode 100644 index 000000000000..575b3e8347f5 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/stream.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_STREAM_H +#define _RKISPP_STREAM_H + +#include "common.h" + +struct rkispp_stream; + +/* + * STREAM_II: input image data + * STREAM_MB: module bypass output, no scale + * STREAM_S0: scale0 output + * STREAM_S1: scale1 output + * STREAM_S2: scale2 output + */ +enum rkispp_stream_id { + STREAM_II = 0, + STREAM_MB, + STREAM_S0, + STREAM_S1, + STREAM_S2, + STREAM_MAX +}; + +/* + * fourcc: pixel format + * cplanes: number of colour planes + * mplanes: number of stored memory planes + * wr_fmt: defines format for reg + * bpp: bits per pixel + */ +struct capture_fmt { + u32 fourcc; + u8 cplanes; + u8 mplanes; + u8 wr_fmt; + u8 bpp[VIDEO_MAX_PLANES]; +}; + +/* Different config for stream */ +struct stream_config { + const struct capture_fmt *fmts; + unsigned int fmt_size; + u32 frame_end_id; + /* registers */ + struct { + u32 ctrl; + u32 factor; + u32 cur_y_base; + u32 cur_uv_base; + u32 cur_vir_stride; + u32 cur_y_base_shd; + u32 cur_uv_base_shd; + } reg; +}; + +/* Different reg ops for stream */ +struct streams_ops { + int (*config)(struct rkispp_stream *stream); + void (*update)(struct rkispp_stream *stream); + void (*stop)(struct rkispp_stream *stream); + int (*start)(struct rkispp_stream *stream); + int (*is_stopped)(struct rkispp_stream *stream); +}; + +/* stream input/out flag */ +enum stream_type { + STREAM_INPUT, + STREAM_OUTPUT, +}; + +/* tnr internal using buf */ +struct in_tnr_buf { + struct rkispp_dummy_buffer pic_cur; + struct rkispp_dummy_buffer pic_next; + struct rkispp_dummy_buffer gain_cur; + struct rkispp_dummy_buffer gain_next; + struct rkispp_dummy_buffer gain_kg; + struct rkispp_dummy_buffer iir; + struct rkispp_dummy_buffer pic_wr; + struct rkispp_dummy_buffer gain_wr; +}; + +/* nr internal using buf */ +struct in_nr_buf { + struct rkispp_dummy_buffer pic_cur; + struct rkispp_dummy_buffer gain_cur; + struct rkispp_dummy_buffer pic_wr; + struct rkispp_dummy_buffer tmp_yuv; +}; + +/* struct rkispp_stream - ISPP stream video device + * id: stream video identify + * buf_queue: queued buffer list + * curr_buf: the buffer used for current frame + * next_buf: the buffer used for next frame + * dummy_buf: dummy space to store dropped data + * done: wait frame end event queue + * vbq_lock: lock to protect buf_queue + * out_cap_fmt: the output of ispp + * out_fmt: the output of v4l2 pix format + * last_module: last function module + * streaming: stream start flag + * stopping: stream stop flag + * linked: link enable flag + */ +struct rkispp_stream { + enum rkispp_stream_id id; + struct rkispp_device *isppdev; + struct rkispp_vdev_node vnode; + + struct list_head buf_queue; + struct rkispp_buffer *curr_buf; + struct rkispp_buffer *next_buf; + struct rkispp_dummy_buffer dummy_buf; + wait_queue_head_t done; + spinlock_t vbq_lock; + + enum stream_type type; + struct streams_ops *ops; + struct stream_config *config; + struct capture_fmt out_cap_fmt; + struct v4l2_pix_format_mplane out_fmt; + u8 last_module; + u8 streaming; + u8 stopping; + u8 linked; +}; + +/* rkispp stream device */ +struct rkispp_stream_vdev { + struct rkispp_stream stream[STREAM_MAX]; + struct in_tnr_buf tnr_buf; + struct in_nr_buf nr_buf; + atomic_t refcnt; +}; + +void rkispp_isr(u32 mis_val, struct rkispp_device *dev); +void rkispp_unregister_stream_vdevs(struct rkispp_device *dev); +int rkispp_register_stream_vdevs(struct rkispp_device *dev); +#endif diff --git a/drivers/media/platform/rockchip/ispp/version.h b/drivers/media/platform/rockchip/ispp/version.h new file mode 100644 index 000000000000..a0fb70ed64b9 --- /dev/null +++ b/drivers/media/platform/rockchip/ispp/version.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd. */ + +#ifndef _RKISPP_VERSION_H +#define _RKISPP_VERSION_H +#include + +/* + * RKISPP DRIVER VERSION NOTE + * + * v0.1.0: + * 1. First version; + */ + +#define RKISPP_DRIVER_VERSION KERNEL_VERSION(0, 1, 0x0) + +#endif diff --git a/include/uapi/linux/rkispp-config.h b/include/uapi/linux/rkispp-config.h new file mode 100644 index 000000000000..7f03acd424ef --- /dev/null +++ b/include/uapi/linux/rkispp-config.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) + * + * Copyright (C) 2019 Rockchip Electronics Co., Ltd. + */ + +#ifndef _UAPI_RKISPP_CONFIG_H +#define _UAPI_RKISPP_CONFIG_H + +#include +#include + +#define ISPP_MODULE_TNR BIT(0) +#define ISPP_MODULE_NR BIT(1) +#define ISPP_MODULE_SHP BIT(2) +#define ISPP_MODULE_FEC BIT(3) + +#define TNR_SIGMA_CURVE_SIZE 17 +#define TNR_LUMA_CURVE_SIZE 6 +#define TNR_GFCOEF5_SIZE 6 +#define TNR_GFCOEF3_SIZE 3 +#define TNR_SCALE_YG_SIZE 4 +#define TNR_SCALE_YL_SIZE 3 +#define TNR_SCALE_CG_SIZE 3 +#define TNR_SCALE_Y2CG_SIZE 3 +#define TNR_SCALE_CL_SIZE 2 +#define TNR_SCALE_Y2CL_SIZE 3 +#define TNR_WEIGHT_Y_SIZE 3 + +#define NR_UVNR_UVGAIN_SIZE 2 +#define NR_UVNR_T1FLT_WTQ_SIZE 8 +#define NR_UVNR_T2GEN_WTQ_SIZE 4 +#define NR_UVNR_T2FLT_WT_SIZE 3 +#define NR_YNR_SGM_DX_SIZE 16 +#define NR_YNR_SGM_Y_SIZE 17 +#define NR_YNR_HWEIT_D_SIZE 20 +#define NR_YNR_HGRAD_Y_SIZE 24 +#define NR_YNR_HSTV_Y_SIZE 17 +#define NR_YNR_CI_SIZE 4 +#define NR_YNR_LGAIN_MIN_SIZE 4 +#define NR_YNR_LWEIT_FLT_SIZE 4 +#define NR_YNR_HGAIN_SGM_SIZE 4 +#define NR_YNR_HWEIT_SIZE 4 +#define NR_YNR_LWEIT_CMP_SIZE 2 +#define NR_YNR_ST_SCALE_SIZE 3 + +#define SHP_PBF_KERNEL_SIZE 3 +#define SHP_MRF_KERNEL_SIZE 6 +#define SHP_MBF_KERNEL_SIZE 12 +#define SHP_HRF_KERNEL_SIZE 6 +#define SHP_HBF_KERNEL_SIZE 3 +#define SHP_EDGE_COEF_SIZE 3 +#define SHP_EDGE_SMOTH_SIZE 3 +#define SHP_EDGE_GAUS_SIZE 6 +#define SHP_DOG_KERNEL_SIZE 6 +#define SHP_LUM_POINT_SIZE 6 +#define SHP_SIGMA_SIZE 8 +#define SHP_LUM_CLP_SIZE 8 +#define SHP_LUM_MIN_SIZE 8 +#define SHP_EDGE_LUM_THED_SIZE 8 +#define SHP_CLAMP_SIZE 8 +#define SHP_DETAIL_ALPHA_SIZE 8 + +struct tnr_sigma_curve { + u8 x; + u16 y; +} __attribute__ ((packed)); + +struct rkispp_tnr_config { + u8 en; + u8 opty_en; + u8 optc_en; + u8 gain_en; + + u8 pk0_y; + u8 pk1_y; + u8 pk0_c; + u8 pk1_c; + + u16 glb_gain_cur; + u16 glb_gain_nxt; + u16 glb_gain_cur_div; + u8 glb_gain_cur_sqrt; + + struct tnr_sigma_curve sigma_curve[TNR_SIGMA_CURVE_SIZE]; + + u16 luma_curve[TNR_LUMA_CURVE_SIZE]; + + u16 txt_th0_y; + u16 txt_th1_y; + u16 txt_th0_c; + u16 txt_th1_c; + u16 txt_thy_dlt; + u16 txt_thc_dlt; + + u8 gfcoef_y0[TNR_GFCOEF5_SIZE]; + u8 gfcoef_y1[TNR_GFCOEF3_SIZE]; + u8 gfcoef_y2[TNR_GFCOEF5_SIZE]; + u8 gfcoef_y3[TNR_GFCOEF3_SIZE]; + + u8 gfcoef_yg0[TNR_GFCOEF5_SIZE]; + u8 gfcoef_yg1[TNR_GFCOEF3_SIZE]; + u8 gfcoef_yg2[TNR_GFCOEF3_SIZE]; + u8 gfcoef_yg3[TNR_GFCOEF3_SIZE]; + + u8 gfcoef_yl0[TNR_GFCOEF5_SIZE]; + u8 gfcoef_yl1[TNR_GFCOEF3_SIZE]; + u8 gfcoef_yl2[TNR_GFCOEF3_SIZE]; + + u8 gfcoef_cg0[TNR_GFCOEF5_SIZE]; + u8 gfcoef_cg1[TNR_GFCOEF3_SIZE]; + u8 gfcoef_cg2[TNR_GFCOEF3_SIZE]; + + u8 gfcoef_cl0[TNR_GFCOEF5_SIZE]; + u8 gfcoef_cl1[TNR_GFCOEF3_SIZE]; + + u16 scale_yg[TNR_SCALE_YG_SIZE]; + u16 scale_yl[TNR_SCALE_YL_SIZE]; + u16 scale_cg[TNR_SCALE_CG_SIZE]; + u16 scale_y2cg[TNR_SCALE_Y2CG_SIZE]; + u16 scale_cl[TNR_SCALE_CL_SIZE]; + u16 scale_y2cl[TNR_SCALE_Y2CL_SIZE]; + + u8 weight_y[TNR_WEIGHT_Y_SIZE]; +} __attribute__ ((packed)); + +struct rkispp_nr_config { + u8 nr_en; + u8 uvnr_step1_en; + u8 uvnr_step2_en; + u8 nr_gain_en; + u8 uvnr_nobig_en; + u8 uvnr_big_en; + + u8 uvnr_gain_1sigma; + u8 uvnr_gain_offset; + u8 uvnr_gain_uvgain[NR_UVNR_UVGAIN_SIZE]; + u8 uvnr_gain_t2gen; + u8 uvnr_gain_iso; + u8 uvnr_t1gen_m3alpha; + u8 uvnr_t1flt_mode; + u16 uvnr_t1flt_msigma; + u8 uvnr_t1flt_wtp; + u8 uvnr_t1flt_wtq[NR_UVNR_T1FLT_WTQ_SIZE]; + u8 uvnr_t2gen_m3alpha; + u16 uvnr_t2gen_msigma; + u8 uvnr_t2gen_wtp; + u8 uvnr_t2gen_wtq[NR_UVNR_T2GEN_WTQ_SIZE]; + u16 uvnr_t2flt_msigma; + + u8 uvnr_t2flt_wtp; + u8 uvnr_t2flt_wt[NR_UVNR_T2FLT_WT_SIZE]; + + u8 ynr_sgm_dx[NR_YNR_SGM_DX_SIZE]; + u16 ynr_lsgm_y[NR_YNR_SGM_Y_SIZE]; + u8 ynr_lci[NR_YNR_CI_SIZE]; + u8 ynr_lgain_min[NR_YNR_LGAIN_MIN_SIZE]; + u8 ynr_lgain_max; + + u8 ynr_lmerge_bound; + u8 ynr_lmerge_ratio; + + u8 ynr_lweit_flt[NR_YNR_LWEIT_FLT_SIZE]; + u16 ynr_hsgm_y[NR_YNR_SGM_Y_SIZE]; + u8 ynr_hlci[NR_YNR_CI_SIZE]; + u8 ynr_lhci[NR_YNR_CI_SIZE]; + u8 ynr_hhci[NR_YNR_CI_SIZE]; + u8 ynr_hgain_sgm[NR_YNR_HGAIN_SGM_SIZE]; + u8 ynr_hweit_d[NR_YNR_HWEIT_D_SIZE]; + u8 ynr_hgrad_y[NR_YNR_HGRAD_Y_SIZE]; + u16 ynr_hweit[NR_YNR_HWEIT_SIZE]; + u8 ynr_hmax_adjust; + u8 ynr_hstrength; + u8 ynr_lweit_cmp[NR_YNR_LWEIT_CMP_SIZE]; + u8 ynr_lmaxgain_lv4; + u16 ynr_hstv_y[NR_YNR_HSTV_Y_SIZE]; + u16 ynr_st_scale[NR_YNR_ST_SCALE_SIZE]; +} __attribute__ ((packed)); + +struct rkispp_sharp_config { + u8 en; + u8 alpha_adp_en; + u8 yin_flt_en; + u8 edge_avg_en; + + u16 hbf_ratio; + u8 ehf_th; + u8 pbf_ratio; + + u8 edge_thed; + u8 dir_min; + u8 smoth_th4; + + u16 l_alpha; + u16 g_alpha; + + u8 pbf_k[SHP_PBF_KERNEL_SIZE]; + u8 mrf_k[SHP_MRF_KERNEL_SIZE]; + u8 mbf_k[SHP_MBF_KERNEL_SIZE]; + u8 hrf_k[SHP_HRF_KERNEL_SIZE]; + u8 hbf_k[SHP_HBF_KERNEL_SIZE]; + + u8 eg_coef[SHP_EDGE_COEF_SIZE]; + u8 eg_smoth[SHP_EDGE_SMOTH_SIZE]; + u8 eg_gaus[SHP_EDGE_GAUS_SIZE]; + u8 dog_k[SHP_DOG_KERNEL_SIZE]; + u8 lum_point[SHP_LUM_POINT_SIZE]; + + u8 pbf_shf_bits; + u8 mbf_shf_bits; + u8 hbf_shf_bits; + + u8 pbf_sigma[SHP_SIGMA_SIZE]; + u8 lum_clp_m[SHP_LUM_CLP_SIZE]; + u8 lum_min_m[SHP_LUM_MIN_SIZE]; + u8 mbf_sigma[SHP_SIGMA_SIZE]; + u8 lum_clp_h[SHP_LUM_CLP_SIZE]; + u8 hbf_sigma[SHP_SIGMA_SIZE]; + u8 edge_lum_thed[SHP_EDGE_LUM_THED_SIZE]; + u8 clamp_pos[SHP_CLAMP_SIZE]; + u8 clamp_neg[SHP_CLAMP_SIZE]; + u8 detail_alpha[SHP_DETAIL_ALPHA_SIZE]; + + u16 rfl_ratio; + u16 rfh_ratio; + + u8 m_ratio; + u8 h_ratio; +} __attribute__ ((packed)); + +struct rkispp_fec_config { + u8 en; + u8 mesh_density; +} __attribute__ ((packed)); + +/** + * struct rkispp_params_cfg - Rockchip ISPP Input Parameters Meta Data + * + * @module_en_update: mask the enable bits of which module should be updated + * @module_ens: mask the enable value of each module, only update the module + * which correspond bit was set in module_en_update + * @module_cfg_update: mask the config bits of which module should be updated + */ +struct rkispp_params_cfg { + unsigned int module_en_update; + unsigned int module_ens; + unsigned int module_cfg_update; + + struct rkispp_tnr_config tnr_cfg; + struct rkispp_nr_config nr_cfg; + struct rkispp_sharp_config shp_cfg; + struct rkispp_fec_config fec_cfg; +} __attribute__ ((packed)); + +#endif diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 4c0b9dde166c..f8e51d2d03d4 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -722,6 +722,8 @@ struct v4l2_pix_format { #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 params */ #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A statistics */ +#define V4L2_META_FMT_RK_ISPP_PARAMS v4l2_fourcc('R', 'K', 'P', 'P') /* Rockchip ISPP params */ + /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe