// SPDX-License-Identifier: GPL-2.0 /* * isp-cvdr.c * * Copyright (C) 2018 Linaro Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #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); }