aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/platform/hisi/isp/isp-cvdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/hisi/isp/isp-cvdr.c')
-rw-r--r--drivers/media/platform/hisi/isp/isp-cvdr.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.c b/drivers/media/platform/hisi/isp/isp-cvdr.c
new file mode 100644
index 000000000000..666dd016028f
--- /dev/null
+++ b/drivers/media/platform/hisi/isp/isp-cvdr.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * isp-cvdr.c
+ *
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "isp.h"
+
+#define ISP_CVDR_NAME "isp_cvdr"
+
+struct cvdr_opt_bw_t cvdr_vp_wr_bw[CVDR_VP_WR_NBR] = {
+ [W12_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [W12_2] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [W6_1] = {CVDR_SRT, 480, (float)DERATE*480*2},
+ [W6_2] = {CVDR_SRT, 480, (float)DERATE*480*2},
+ [W5_1_1] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W5_1_2] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W4_1_1] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W4_1_2] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W4_2_1] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W4_2_2] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W7_1] = {CVDR_SRT, 480, (float)DERATE*480},
+ [W13_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [W13_2] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [W1_1] = {CVDR_RT, 720, 720*2},
+ [W1_2] = {CVDR_RT, 720, 720*2},
+ [W14_1] = {CVDR_RT, 720, 720*2},
+ [W14_2] = {CVDR_RT, 720, 720*2},
+ [W11_1] = {CVDR_RT, 585, ISP_CLK},
+ [W11_2] = {CVDR_RT, 585, ISP_CLK},
+ [W2_3] = {CVDR_RT, 720, 720*2},
+ [W2_4] = {CVDR_RT, 720, 720*2},
+ [W2_5] = {CVDR_RT, 720, 720*2},
+ [W8_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [W3_1] = {CVDR_RT, 480/4, 4*480/29},
+ [W2_2] = {CVDR_RT, 720, 720*2},
+ [W2_1] = {CVDR_RT, 720, 720*2},
+ [W15_1] = {CVDR_RT, 585, 374},
+ [W16_1] = {CVDR_RT, 585, 187},
+ [W16_2] = {CVDR_RT, 585, 187},
+ [W17_1] = {CVDR_RT, 585, ISP_CLK},
+ [W17_2] = {CVDR_RT, 585, ISP_CLK},
+ [W19_1] = {CVDR_RT, ISP_CLK, (float)DERATE*ISP_CLK*16/8},
+ [W19_2] = {CVDR_RT, ISP_CLK, (float)DERATE*ISP_CLK*16/8},
+ [W20_1] = {CVDR_RT, ISP_CLK, ISP_CLK},
+ [W20_2] = {CVDR_RT, ISP_CLK, ISP_CLK},
+};
+
+struct cvdr_opt_bw_t cvdr_vp_rd_bw[CVDR_VP_RD_NBR] = {
+ [R3_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [R11_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [R12_1] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [R11_2] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [R12_2] = {CVDR_SRT, ISP_CLK, ISP_CLK},
+ [R10_1] = {CVDR_RT, ISP_CLK, ISP_CLK},
+ [R10_2] = {CVDR_RT, ISP_CLK, ISP_CLK},
+ [R2_1] = {CVDR_RT, ISP_CLK, ISP_CLK*2},
+ [R8_1_1] = {CVDR_SRT, ISP_CLK, ISP_CLK*2*DERATE},
+ [R8_2_1] = {CVDR_SRT, ISP_CLK, ISP_CLK*2*DERATE},
+ [R8_1_2] = {CVDR_SRT, ISP_CLK, ISP_CLK*2*DERATE},
+ [R8_2_2] = {CVDR_SRT, ISP_CLK, ISP_CLK*2*DERATE},
+ [R13_1] = {CVDR_RT, ISP_CLK, ISP_CLK},
+ [R13_2] = {CVDR_RT, ISP_CLK, ISP_CLK},
+ [R5_1_4] = {CVDR_SRT, 240, (float)DERATE*2*240},
+ [R5_1_5] = {CVDR_SRT, 240, (float)DERATE*1.5*240},
+ [R5_1_6] = {CVDR_SRT, 240, (float)DERATE*1.5*240},
+};
+
+int cvdr_set_vp_wr_ready(char __iomem *base,
+ unsigned char port,
+ struct cvdr_wr_fmt_desc_t *desc,
+ struct cvdr_bw_cfg_t *bw)
+{
+ union U_VP_WR_CFG tmp_cfg;
+ union U_VP_WR_AXI_FS tmp_fs;
+ union U_VP_WR_AXI_LINE tmp_line;
+ union U_LIMITER_VP_WR tmp_limiter;
+ u32 reg_addr;
+
+ if (desc->fs_addr & 0xF) {
+ pr_err("failed\n");
+ return -1;
+ }
+
+ tmp_cfg.u32 = 0;
+ tmp_fs.u32 = 0;
+ tmp_line.u32 = 0;
+ tmp_limiter.u32 = 0;
+
+ if (!bw) {
+ pr_err("vdr_bw_cfg_t* bw NULL!\n");
+ return -1;
+ }
+
+ tmp_cfg.bits.vpwr_pixel_format = desc->pix_fmt;
+ tmp_cfg.bits.vpwr_pixel_expansion = desc->pix_expan;
+ tmp_cfg.bits.vpwr_last_page = desc->last_page;
+
+ tmp_fs.bits.vpwr_address_frame_start = desc->fs_addr >> 4;
+
+ tmp_line.bits.vpwr_line_stride = desc->line_stride;
+ tmp_line.bits.vpwr_line_wrap = desc->line_wrap;
+
+ tmp_limiter.bits.vpwr_access_limiter_0 = bw->bw_limiter0;
+ tmp_limiter.bits.vpwr_access_limiter_1 = bw->bw_limiter1;
+ tmp_limiter.bits.vpwr_access_limiter_2 = bw->bw_limiter2;
+ tmp_limiter.bits.vpwr_access_limiter_3 = bw->bw_limiter3;
+ tmp_limiter.bits.vpwr_access_limiter_reload = bw->bw_limiter_reload;
+
+ switch (port) {
+ /*VP_WR @CVDR_SRT&CMDLST TO CONFIG */
+ case W4_1_1:
+ case W4_1_2:
+ case W4_2_1:
+ case W4_2_2:
+ case W5_1_1:
+ case W5_1_2:
+ case W19_1:
+ case W19_2:
+ case W6_1:
+ case W6_2:
+ case W7_1:
+ case W3_1:
+ pr_err("not support yet\n");
+ break;
+
+ default:
+ reg_addr = CVDR_CVDR_LIMITER_VP_WR_0_REG +
+ ONE_REG_OFFSET * port;
+ isp_writel(base, reg_addr, tmp_limiter.u32);
+ reg_addr = CVDR_CVDR_VP_WR_CFG_0_REG +
+ VP_WR_REG_OFFSET * port;
+ isp_writel(base, reg_addr, tmp_cfg.u32);
+ reg_addr = CVDR_CVDR_VP_WR_AXI_LINE_0_REG +
+ VP_WR_REG_OFFSET * port;
+ isp_writel(base, reg_addr, tmp_line.u32);
+ reg_addr = CVDR_CVDR_VP_WR_AXI_FS_0_REG +
+ VP_WR_REG_OFFSET * port;
+ isp_writel(base, reg_addr, tmp_fs.u32);
+ break;
+ }
+
+ return 0;
+}
+
+void isp_cvdr_config(struct cvdr_device *cvdr, dma_addr_t hw_addr)
+{
+ struct cvdr_wr_fmt_desc_t cvdr_wr_fmt = {
+ .fs_addr = hw_addr,
+ .pix_fmt = DF_D32,
+ .pix_expan = 0,
+ .last_page = (1920 * 1080 * 2 + hw_addr) >> 15,
+ .line_stride = 1920*2/CVDR_ALIGN_BYTES - 1,
+ .line_wrap = 8000,
+ };
+ struct cvdr_bw_cfg_t cvdr_bw = {0xF, 0xF, 0xF, 0xF, 0xF};
+
+ cvdr_set_vp_wr_ready(cvdr->cvdr_rt, W2_4, &cvdr_wr_fmt, &cvdr_bw);
+}
+
+void isp_cvdr_init(struct cvdr_device *cvdr)
+{
+ unsigned int i;
+ unsigned int prefetch_bypass;
+ char __iomem *base;
+ unsigned int reg_val;
+
+ prefetch_bypass = 1;
+
+ for (i = 0; i < CVDR_VP_WR_NBR; ++i) {
+ base = ((cvdr_vp_wr_bw[i].srt == CVDR_SRT) ?
+ cvdr->cvdr_srt : cvdr->cvdr_rt);
+
+ reg_val = isp_readl(base, CVDR_SRT_VP_WR_IF_CFG_0_REG + 0x10 * i);
+ reg_val = (reg_val & 0x7FFFFFFF) | (prefetch_bypass << 31);
+ isp_writel(base, CVDR_SRT_VP_WR_IF_CFG_0_REG + 0x10 * i, reg_val);
+ }
+
+ for (i = 0; i < CVDR_VP_RD_NBR; ++i) {
+ base = ((cvdr_vp_rd_bw[i].srt == CVDR_SRT) ?
+ cvdr->cvdr_srt : cvdr->cvdr_rt);
+ reg_val = isp_readl(base, CVDR_SRT_VP_RD_IF_CFG_0_REG + 0x20 * i);
+ reg_val = (reg_val & 0x7FFFFFFF) | (prefetch_bypass << 31);
+ isp_writel(base, CVDR_SRT_VP_RD_IF_CFG_0_REG + 0x20 * i, reg_val);
+ }
+
+ /* CVDR RT*/
+ isp_writel(cvdr->cvdr_rt, CVDR_RT_CVDR_CFG_REG,
+ (1 << 0)
+ | (64 << 8)
+ | (11 << 16)
+ | (11 << 24)
+ );
+
+ /* CVDR SRT*/
+ isp_writel(cvdr->cvdr_srt, CVDR_SRT_CVDR_CFG_REG,
+ (1 << 0)
+ | (34 << 8)
+ | (19 << 16)
+ | (11 << 24)
+ );
+
+ isp_writel(cvdr->cvdr_rt, 0x00C, 0xf8765432);
+ isp_writel(cvdr->cvdr_rt, 0x010, 0xf8122334);
+
+ isp_writel(cvdr->cvdr_srt, 0x00C, 0xd0765432);
+ isp_writel(cvdr->cvdr_srt, 0x010, 0xd0122334);
+
+ isp_writel(cvdr->sub_ctrl, 0x190, 0x00026e10);
+ isp_writel(cvdr->sub_ctrl, 0x194, 0x0000021f);
+
+ isp_writel(cvdr->sub_ctrl, 0x198, 0x00027210);
+ isp_writel(cvdr->sub_ctrl, 0x19C, 0x0000024e);
+}
+
+/*
+ * cvdr_set_power - Power on/off CSIPHY module
+ * @sd: CSIPHY V4L2 subdevice
+ * @on: Requested power state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int cvdr_set_power(struct v4l2_subdev *sd, int on)
+{
+
+ pr_info("%s: %d\n", __func__, __LINE__);
+ return 0;
+}
+
+/*
+ * cvdr_set_stream - Enable/disable streaming on CSIPHY module
+ * @sd: CSIPHY V4L2 subdevice
+ * @enable: Requested streaming state
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int cvdr_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct cvdr_device *cvdr = v4l2_get_subdevdata(sd);
+ struct isp *isp = cvdr->isp;
+ int ret = 0;
+
+ enable_irq(cvdr->irq_vic1);
+ isp_cvdr_init(cvdr);
+ isp_cvdr_config(cvdr, isp->hw_addr);
+ pr_info("%s: %d\n", __func__, __LINE__);
+
+ return ret;
+}
+
+/*
+ * cvdr_init_formats - Initialize formats on all pads
+ * @sd: CSIPHY V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int cvdr_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ return 0;
+}
+
+irqreturn_t isp_vic1_handler(int irq, void *dev)
+{
+ struct cvdr_device *cvdr = dev;
+ struct isp *isp = cvdr->isp;
+ unsigned int val = isp_clear_irq(isp, IRQ_MERGER_DEBUG_1);
+
+ if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_SOF_VPWR_23_OFFSET))
+ isp_cvdr_config(cvdr,
+ isp->hw_addr + frame_num2Offset(isp, isp->frame_num + 1));
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * isp_cvdr_subdev_init - Initialize CSIPHY device structure and resources
+ * @sr: CSIPHY device
+ * @res: CSIPHY module resources table
+ * @id: CSIPHY module id
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int isp_cvdr_subdev_init(struct isp *isp,
+ struct cvdr_device *cvdr,
+ const struct resources *res)
+{
+ struct device *dev = isp->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *r;
+ int ret;
+
+ cvdr->isp = isp;
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res[0].reg);
+ cvdr->cvdr_rt = devm_ioremap_resource(dev, r);
+ if (IS_ERR(cvdr->cvdr_rt)) {
+ dev_err(dev, "could not map memory\n");
+ return PTR_ERR(cvdr->cvdr_rt);
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res[1].reg);
+ cvdr->cvdr_srt = devm_ioremap_resource(dev, r);
+ if (IS_ERR(cvdr->cvdr_srt)) {
+ dev_err(dev, "could not map memory\n");
+ return PTR_ERR(cvdr->cvdr_srt);
+ }
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res[2].reg);
+ cvdr->sub_ctrl = devm_ioremap_resource(dev, r);
+ if (IS_ERR(cvdr->sub_ctrl)) {
+ dev_err(dev, "could not map memory\n");
+ return PTR_ERR(cvdr->sub_ctrl);
+ }
+
+ ret = platform_get_irq_byname(pdev, "isp_vic1");
+ if (ret <= 0) {
+ dev_err(isp->dev, "No IRQ resource\n");
+ return -ENODEV;
+ }
+
+ cvdr->irq_vic1 = ret;
+
+ ret = devm_request_irq(&pdev->dev, cvdr->irq_vic1,
+ isp_vic1_handler, 0, "isp-vic1", cvdr);
+ if (ret)
+ return ret;
+
+ disable_irq(cvdr->irq_vic1);
+
+ return 0;
+}
+
+/*
+ * cvdr_link_setup - Setup CSIPHY connections
+ * @entity: Pointer to media entity structure
+ * @local: Pointer to local pad
+ * @remote: Pointer to remote pad
+ * @flags: Link flags
+ *
+ * Rreturn 0 on success
+ */
+static int cvdr_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
+ (flags & MEDIA_LNK_FL_ENABLED)) {
+ struct v4l2_subdev *sd;
+ struct cvdr_device *cvdr;
+
+ if (media_entity_remote_pad(local))
+ return -EBUSY;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+ cvdr = v4l2_get_subdevdata(sd);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops cvdr_core_ops = {
+ .s_power = cvdr_set_power,
+};
+
+static const struct v4l2_subdev_video_ops cvdr_video_ops = {
+ .s_stream = cvdr_set_stream,
+};
+
+static const struct v4l2_subdev_ops cvdr_v4l2_ops = {
+ .core = &cvdr_core_ops,
+ .video = &cvdr_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops cvdr_v4l2_internal_ops = {
+ .open = cvdr_init_formats,
+};
+
+static const struct media_entity_operations cvdr_media_ops = {
+ .link_setup = cvdr_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * isp_cvdr_register_entity - Register subdev node for CSIPHY module
+ * @sr: CSIPHY device
+ * @v4l2_dev: V4L2 device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int isp_cvdr_register_entity(struct cvdr_device *cvdr,
+ struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd = &cvdr->subdev;
+ struct media_pad *pads = cvdr->pads;
+ struct device *dev = cvdr->isp->dev;
+ int ret;
+
+ v4l2_subdev_init(sd, &cvdr_v4l2_ops);
+ sd->internal_ops = &cvdr_v4l2_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s",
+ ISP_CVDR_NAME);
+ v4l2_set_subdevdata(sd, cvdr);
+
+ ret = cvdr_init_formats(sd, NULL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init format: %d\n", ret);
+ return ret;
+ }
+
+ pads[ISP_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[ISP_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+
+ sd->entity.function = MEDIA_ENT_F_IO_V4L;
+ sd->entity.ops = &cvdr_media_ops;
+ ret = media_entity_pads_init(&sd->entity, ISP_CSIPHY_PADS_NUM, pads);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init media entity: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register subdev: %d\n", ret);
+ media_entity_cleanup(&sd->entity);
+ }
+
+ return ret;
+}
+
+/*
+ * isp_cvdr_unregister_entity - Unregister CSIPHY module subdev node
+ * @sr: CSIPHY device
+ */
+void isp_cvdr_unregister_entity(struct cvdr_device *cvdr)
+{
+ v4l2_device_unregister_subdev(&cvdr->subdev);
+ media_entity_cleanup(&cvdr->subdev.entity);
+}