diff options
author | Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | 2018-10-30 21:17:03 +0530 |
---|---|---|
committer | Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | 2018-11-20 14:07:00 +0530 |
commit | 5743172d2084e4b49073ca3b9a61858708787250 (patch) | |
tree | b944ab00652a9d9507c8fec16bc1d8fb10523f79 | |
parent | 0f3537d2ba163e24ce368021b6f72a9ae6214091 (diff) | |
download | 96b-common-5743172d2084e4b49073ca3b9a61858708787250.tar.gz |
media: hisi: Add ISP driver
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
-rw-r--r-- | drivers/media/platform/Kconfig | 7 | ||||
-rw-r--r-- | drivers/media/platform/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/Makefile | 10 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-csiphy.c | 472 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-csiphy.h | 149 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-cvdr.c | 455 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-cvdr.h | 951 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-sr.c | 267 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp-sr.h | 114 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp.c | 699 | ||||
-rw-r--r-- | drivers/media/platform/hisi/isp/isp.h | 159 |
11 files changed, 3285 insertions, 0 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index fd0c99859d6f..fbfc09ca0f22 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -117,6 +117,13 @@ config VIDEO_QCOM_CAMSS select VIDEOBUF2_DMA_SG select V4L2_FWNODE +config VIDEO_HISI_ISP + tristate "HiSilicon ISP driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on ARCH_HISI || COMPILE_TEST + select VIDEOBUF2_DMA_SG + select V4L2_FWNODE + config VIDEO_S3C_CAMIF tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver" depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 003b0bb2cddf..4c42f1e97979 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -96,4 +96,6 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ +obj-$(CONFIG_VIDEO_HISI_ISP) += hisi/isp/ + obj-y += meson/ diff --git a/drivers/media/platform/hisi/isp/Makefile b/drivers/media/platform/hisi/isp/Makefile new file mode 100644 index 000000000000..9fa39e3a4867 --- /dev/null +++ b/drivers/media/platform/hisi/isp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# Makefile for HISI ISP driver + +hisi-isp-objs += \ + isp.o \ + isp-csiphy.o \ + isp-sr.o \ + isp-cvdr.o \ + +obj-$(CONFIG_VIDEO_HISI_ISP) += hisi-isp.o diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.c b/drivers/media/platform/hisi/isp/isp-csiphy.c new file mode 100644 index 000000000000..f6c050fc0814 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-csiphy.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * isp-csiphy.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 TSTCODE_SETREG8(reg_base, addr, value) \ + do { \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL1_REG,\ + (1 << 16) | addr); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 2); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 0); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL1_REG, value); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 2); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 0); \ + } while (0) + +#define TSTCODE_GETREG8(reg_base, addr, value)\ + do { \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL1_REG,\ + (1 << 16) | addr); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 2); \ + isp_writel(reg_base, CSI2IF_PHY_TEST_CTRL0_REG, 0); \ + (value) = \ + ((isp_readl(reg_base, CSI2IF_PHY_TEST_CTRL1_REG) >> 8) &\ + (0x000000ff)); \ + } while (0) + +#define ISP_CSIPHY_NAME "isp_csiphy" + +struct csiphy_format { + u32 code; + u8 bpp; +}; + +static const struct csiphy_format csiphy_formats_8x16[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, +}; + +static const struct csiphy_format csiphy_formats_8x96[] = { + { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, + { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, + { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, + { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, + { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, + { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, + { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, + { MEDIA_BUS_FMT_Y10_1X10, 10 }, +}; + +static int csi2if_dphy_init(char __iomem *base, unsigned char settle_time) +{ + unsigned char value = 0; + + if (settle_time > 0) { /* configure settle time mannually */ + TSTCODE_SETREG8(base, LANE0_SETTLE, settle_time); + TSTCODE_SETREG8(base, LANE1_SETTLE, settle_time); + TSTCODE_SETREG8(base, LANE2_SETTLE, settle_time); + TSTCODE_SETREG8(base, LANE3_SETTLE, settle_time); + TSTCODE_SETREG8(base, CFG_CLK_DETECT, 0x01); + } else { /* enable clock detect */ + TSTCODE_SETREG8(base, LANE0_SETTLE, 0x01); + TSTCODE_SETREG8(base, LANE0_ADDITION, 0x05); + TSTCODE_SETREG8(base, LANE1_SETTLE, 0x01); + TSTCODE_SETREG8(base, LANE1_ADDITION, 0x05); + TSTCODE_SETREG8(base, LANE2_SETTLE, 0x01); + TSTCODE_SETREG8(base, LANE2_ADDITION, 0x05); + TSTCODE_SETREG8(base, LANE3_SETTLE, 0x01); + TSTCODE_SETREG8(base, LANE3_ADDITION, 0x05); + } + + /* setup time and hold time for dphy v1.2 */ + TSTCODE_SETREG8(base, LANE0_DESKEW_1, 0x0D); + TSTCODE_SETREG8(base, LANE1_DESKEW_1, 0x0D); + TSTCODE_SETREG8(base, LANE2_DESKEW_1, 0x0D); + TSTCODE_SETREG8(base, LANE3_DESKEW_1, 0x0D); + TSTCODE_SETREG8(base, LANE0_DESKEW_3, 0x03); + TSTCODE_SETREG8(base, LANE1_DESKEW_3, 0x03); + TSTCODE_SETREG8(base, LANE2_DESKEW_3, 0x03); + TSTCODE_SETREG8(base, LANE3_DESKEW_3, 0x03); + + TSTCODE_SETREG8(base, CFG_CLK_ATTR, 0x50); + + TSTCODE_GETREG8(base, LANE0_SETTLE, value); + pr_info("### LANE0_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE1_SETTLE, value); + pr_info("### LANE1_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE2_SETTLE, value); + pr_info("### LANE2_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE3_SETTLE, value); + pr_info("### LANE3_SETTLE = %d", value); + + TSTCODE_GETREG8(base, CFG_CLK_DETECT, value); + pr_info("### CFG_CLK_DETECT = 0x%x", value); + + TSTCODE_GETREG8(base, CFG_CLK_ATTR, value); + pr_info("### CFG_CLK_ATTR = 0x%x", value); + + return 0; +} + +int csi2if_enable(struct csiphy_device *csiphy, unsigned char num_lanes, + unsigned char settle_time) +{ + unsigned int phy_rx; + unsigned int phy_state; + + if (num_lanes > 4 || 0 == num_lanes) { + dev_err(csiphy->isp->dev, "number of lanes %d out of range!!\n", + num_lanes); + return -EINVAL; + } + + /* de-assert the shutdown signal*/ + isp_writel(csiphy->base, CSI2IF_PHY_SHUTDOWNZ_REG, 0); + isp_writel(csiphy->base, CSI2IF_DPHY_RSTZ_REG, 0); + isp_writel(csiphy->base, CSI2IF_CSI2_RESETN_REG, 0); + + isp_writel(csiphy->base, CSI2IF_PHY_TEST_CTRL0_REG, 1); + isp_writel(csiphy->base, CSI2IF_PHY_TEST_CTRL0_REG, 0); + mdelay(1); + + isp_writel(csiphy->base, CSI2IF_PHY_SHUTDOWNZ_REG, 1); + isp_writel(csiphy->base, CSI2IF_N_LANES_REG, (num_lanes-1)); + + isp_writel(csiphy->base, CSI2IF_DPHY_RSTZ_REG, 1); + isp_writel(csiphy->base, CSI2IF_CSI2_RESETN_REG, 1); + + /* Configure HUAWEI D-PHY */ + if (csi2if_dphy_init(csiphy->base, settle_time) < 0) + return -1; + + /* confirm the D-PHY is in right state */ + phy_rx = isp_readl(csiphy->base, CSI2IF_PHY_RX_REG); + phy_state = isp_readl(csiphy->base, CSI2IF_PHY_STOPSTATE_REG); + + if ((isp_readl_field(phy_state, CSI2IF_PHY_STOPSTATEDATA_0) == 0) && + (isp_readl_field(phy_state, CSI2IF_PHY_STOPSTATECLK) == 0)) { + + pr_info("### not all data and clock lanes in stop state!\n"); + pr_info("phy_rx = 0x%x, phy_state = %x\n", phy_rx, phy_state); + } + + if (isp_readl_field(phy_state, CSI2IF_PHY_RXCLKACTIVEHS) == 0) + pr_info("### D-PHY is not receiving a clock!\n"); + + mdelay(1); + + phy_rx = isp_readl(csiphy->base, CSI2IF_PHY_RX_REG); + phy_state = isp_readl(csiphy->base, CSI2IF_PHY_STOPSTATE_REG); + pr_info("### D-PHY state: phy_rx = 0x%x, phy_state = 0x%x\n", + phy_rx, phy_state); + + return 0; +} + +int csi2if_disable(struct csiphy_device *csiphy) +{ + isp_writel(csiphy->base, CSI2IF_CSI2_RESETN_REG, 0); + isp_writel(csiphy->base, CSI2IF_PHY_SHUTDOWNZ_REG, 0); + + return 0; +} + +int isp_ispss_reset_all(struct isp *isp) +{ + isp_writel(isp->ispss_ctrl, 0x060, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x064, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x068, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x06C, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x070, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x074, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x078, 0xFFFFFFFF); + isp_writel(isp->ispss_ctrl, 0x374, 0x00000003); + + mdelay(1); + + isp_writel(isp->ispss_ctrl, 0x060, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x064, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x068, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x06C, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x070, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x074, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x078, 0x00000000); + isp_writel(isp->ispss_ctrl, 0x374, 0x00000000); + + mdelay(1); + + return 0; + +} + +void isp_ispss_clk_enable(struct isp *isp) +{ + /* enable all clock of isp sub-modules */ + isp_writel(isp->ispss_ctrl, 0x010, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x014, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x018, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x01C, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x020, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x024, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x028, 0xffffffff); + isp_writel(isp->ispss_ctrl, 0x364, 0x00000003); +} + +/* + * + * csiphy_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 csiphy_set_power(struct v4l2_subdev *sd, int on) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct isp *isp = csiphy->isp; + int ret = 0; + + ret = isp_enable_clocks(isp->clks, isp->dev); + if (ret < 0) + return ret; + + isp_ispss_clk_enable(isp); + mdelay(100); + + usleep_range(5000, 15000); + + return ret; +} + +/* + * csiphy_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 csiphy_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct csiphy_device *csiphy = v4l2_get_subdevdata(sd); + struct isp *isp = csiphy->isp; + int ret; + + isp_ispss_reset_all(isp); + mdelay(100); + + ret = csi2if_enable(csiphy, 2, 0); + if (ret < 0) + return ret; + + isp_ispss_clear_irq_state(isp); + + isp_ispss_enable_irq(isp); + + pr_info("%s: %d\n", __func__, __LINE__); + return ret; +} + +/* + * csiphy_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 csiphy_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = ISP_CSIPHY_PAD_SINK, + .which = fh ? V4L2_SUBDEV_FORMAT_TRY : + V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 1920, + .height = 1080 + } + }; + + return 0; +} + +/* + * isp_csiphy_subdev_init - Initialize CSIPHY device structure and resources + * @csiphy: CSIPHY device + * @res: CSIPHY module resources table + * @id: CSIPHY module id + * + * Return 0 on success or a negative error code otherwise + */ +int isp_csiphy_subdev_init(struct isp *isp, + struct csiphy_device *csiphy, + const struct resources *res, u8 id) +{ + struct device *dev = isp->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + + csiphy->isp = isp; + csiphy->id = id; + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg); + csiphy->base = devm_ioremap_resource(dev, r); + if (IS_ERR(csiphy->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(csiphy->base); + } + + return 0; +} + +/* + * csiphy_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 csiphy_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 csiphy_device *csiphy; + struct sr_device *sr; + + if (media_entity_remote_pad(local)) + return -EBUSY; + + sd = media_entity_to_v4l2_subdev(entity); + csiphy = v4l2_get_subdevdata(sd); + + sd = media_entity_to_v4l2_subdev(remote->entity); + sr = v4l2_get_subdevdata(sd); + } + + return 0; +} + +static const struct v4l2_subdev_core_ops csiphy_core_ops = { + .s_power = csiphy_set_power, +}; + +static const struct v4l2_subdev_video_ops csiphy_video_ops = { + .s_stream = csiphy_set_stream, +}; + +static const struct v4l2_subdev_ops csiphy_v4l2_ops = { + .core = &csiphy_core_ops, + .video = &csiphy_video_ops, +}; + +static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops = { + .open = csiphy_init_formats, +}; + +static const struct media_entity_operations csiphy_media_ops = { + .link_setup = csiphy_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * isp_csiphy_register_entity - Register subdev node for CSIPHY module + * @csiphy: CSIPHY device + * @v4l2_dev: V4L2 device + * + * Return 0 on success or a negative error code otherwise + */ +int isp_csiphy_register_entity(struct csiphy_device *csiphy, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &csiphy->subdev; + struct media_pad *pads = csiphy->pads; + struct device *dev = csiphy->isp->dev; + int ret; + + v4l2_subdev_init(sd, &csiphy_v4l2_ops); + sd->internal_ops = &csiphy_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", + ISP_CSIPHY_NAME, csiphy->id); + v4l2_set_subdevdata(sd, csiphy); + + ret = csiphy_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 = &csiphy_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_csiphy_unregister_entity - Unregister CSIPHY module subdev node + * @csiphy: CSIPHY device + */ +void isp_csiphy_unregister_entity(struct csiphy_device *csiphy) +{ + v4l2_device_unregister_subdev(&csiphy->subdev); + media_entity_cleanup(&csiphy->subdev.entity); +} diff --git a/drivers/media/platform/hisi/isp/isp-csiphy.h b/drivers/media/platform/hisi/isp/isp-csiphy.h new file mode 100644 index 000000000000..2382ca2de585 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-csiphy.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Linaro Ltd. + */ + +#ifndef HISI_ISP_CSIPHY_H +#define HISI_ISP_CSIPHY_H + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define ISP_CSIPHY_PAD_SINK 0 +#define ISP_CSIPHY_PAD_SRC 1 +#define ISP_CSIPHY_PADS_NUM 2 + +/* CSI2IF Registers */ +#define CSI2IF_VERSION_REG 0x0 +#define CSI2IF_N_LANES_REG 0x4 +#define CSI2IF_CSI2_RESETN_REG 0x8 +#define CSI2IF_INT_ST_MAIN_REG 0xC +#define CSI2IF_DATA_IDS_1_REG 0x10 +#define CSI2IF_PHY_SHUTDOWNZ_REG 0x40 +#define CSI2IF_DPHY_RSTZ_REG 0x44 +#define CSI2IF_PHY_RX_REG 0x48 +#define CSI2IF_PHY_STOPSTATE_REG 0x4C +#define CSI2IF_PHY_TEST_CTRL0_REG 0x50 +#define CSI2IF_PHY_TEST_CTRL1_REG 0x54 +#define CSI2IF_PHY_CAL_REG 0xCC +#define CSI2IF_INT_ST_PHY_FATAL_REG 0xE0 +#define CSI2IF_INT_MSK_PHY_FATAL_REG 0xE4 +#define CSI2IF_INT_FORCE_PHY_FATAL_REG 0xE8 +#define CSI2IF_INT_ST_PKT_FATAL_REG 0xF0 +#define CSI2IF_INT_MSK_PKT_FATAL_REG 0xF4 +#define CSI2IF_INT_FORCE_PKT_FATAL_REG 0xF8 +#define CSI2IF_INT_ST_FRAME_FATAL_REG 0x100 +#define CSI2IF_INT_MSK_FRAME_FATAL_REG 0x104 +#define CSI2IF_INT_FORCE_FRAME_FATAL_REG 0x108 +#define CSI2IF_INT_ST_PHY_REG 0x110 +#define CSI2IF_INT_MSK_PHY_REG 0x114 +#define CSI2IF_INT_FORCE_PHY_REG 0x118 +#define CSI2IF_INT_ST_PKT_REG 0x120 +#define CSI2IF_INT_MSK_PKT_REG 0x124 +#define CSI2IF_INT_FORCE_PKT_REG 0x128 +#define CSI2IF_INT_ST_LINE_REG 0x130 +#define CSI2IF_INT_MSK_LINE_REG 0x134 +#define CSI2IF_INT_FORCE_LINE_REG 0x138 + +/* CSI2IF_PHY_STOPSTATE_REG Register bits */ +#define CSI2IF_PHY_STOPSTATEDATA_0_OFFSET 0 +#define CSI2IF_PHY_STOPSTATEDATA_0_LEN 1 +#define CSI2IF_PHY_STOPSTATECLK_OFFSET 16 +#define CSI2IF_PHY_STOPSTATECLK_LEN 1 +#define CSI2IF_PHY_RXCLKACTIVEHS_OFFSET 17 +#define CSI2IF_PHY_RXCLKACTIVEHS_LEN 1 + +#define CSI_INDEX_CNT (3) + +#define CFG_CLK_ATTR (0x01) +#define CFG_CLK_DETECT (0x0A) +#define CFG_DESKEW_1 (0x0B) +#define CFG_DESKEW_2 (0x0F) +#define LANE0_SETTLE (0x30) +#define LANE0_MISC (0x31) +#define LANE0_ADDITION (0x32) +#define LANE0_DESKEW_1 (0x3A) +#define LANE0_DESKEW_2 (0x3B) +#define LANE0_DESKEW_3 (0x3C) +#define LANE1_SETTLE (0x40) +#define LANE1_MISC (0x41) +#define LANE1_ADDITION (0x42) +#define LANE1_DESKEW_1 (0x4A) +#define LANE1_DESKEW_2 (0x4B) +#define LANE1_DESKEW_3 (0x4C) +#define LANE2_SETTLE (0x50) +#define LANE2_MISC (0x51) +#define LANE2_ADDITION (0x52) +#define LANE2_DESKEW_1 (0x5A) +#define LANE2_DESKEW_2 (0x5B) +#define LANE2_DESKEW_3 (0x5C) +#define LANE3_SETTLE (0x60) +#define LANE3_MISC (0x61) +#define LANE3_ADDITION (0x62) +#define LANE3_DESKEW_1 (0x6A) +#define LANE3_DESKEW_2 (0x6B) +#define LANE3_DESKEW_3 (0x6C) + +struct csiphy_lane { + u8 pos; + u8 pol; +}; + +struct csiphy_lanes_cfg { + int num_data; + struct csiphy_lane *data; + struct csiphy_lane clk; +}; + +struct csiphy_csi2_cfg { + struct csiphy_lanes_cfg lane_cfg; +}; + +struct csiphy_config { + u8 combo_mode; + u8 csid_id; + struct csiphy_csi2_cfg *csi2; +}; + +struct csiphy_device; + +struct csiphy_hw_ops { + void (*csi2_enable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg); + void (*csi2_disable)(struct csiphy_device *csiphy, + struct csiphy_config *cfg); +}; + +struct csiphy_device { + struct isp *isp; + u8 id; + struct v4l2_subdev subdev; + struct media_pad pads[ISP_CSIPHY_PADS_NUM]; + void __iomem *base; + u32 irq; + char irq_name[30]; + struct isp_clock *clock; + int nclocks; + struct csiphy_config cfg; + struct v4l2_mbus_framefmt fmt[ISP_CSIPHY_PADS_NUM]; + const struct csiphy_hw_ops *ops; + const struct csiphy_format *formats; + unsigned int nformats; +}; + +struct resources; + +int isp_csiphy_subdev_init(struct isp *isp, + struct csiphy_device *csiphy, + const struct resources *res, u8 id); + +int isp_csiphy_register_entity(struct csiphy_device *csiphy, + struct v4l2_device *v4l2_dev); + +void isp_csiphy_unregister_entity(struct csiphy_device *csiphy); + +#endif /* HISI_ISP_CSIPHY_H */ 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); +} diff --git a/drivers/media/platform/hisi/isp/isp-cvdr.h b/drivers/media/platform/hisi/isp/isp-cvdr.h new file mode 100644 index 000000000000..8824a7c39ba1 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-cvdr.h @@ -0,0 +1,951 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#ifndef HISI_ISP_CVDR_H +#define HISI_ISP_CVDR_H + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define ISP_CVDR_PAD_SINK 0 +#define ISP_CVDR_PAD_SRC 1 +#define ISP_CVDR_PADS_NUM 1 + +#define CVDR_SRT_CVDR_CFG_REG 0x0 +#define CVDR_RT_CVDR_CFG_REG 0x0 +#define CVDR_SRT_VP_WR_IF_CFG_0_REG 0x28 +#define CVDR_SRT_VP_RD_IF_CFG_0_REG 0x514 + +#define CVDR_CVDR_CVDR_CFG_REG 0x0 +#define CVDR_CVDR_CVDR_DEBUG_EN_REG 0x4 +#define CVDR_CVDR_CVDR_DEBUG_REG 0x8 +#define CVDR_CVDR_CVDR_WR_QOS_CFG_REG 0xC +#define CVDR_CVDR_CVDR_RD_QOS_CFG_REG 0x10 +#define CVDR_CVDR_OTHER_RO_REG 0x14 +#define CVDR_CVDR_OTHER_RW_REG 0x18 +#define CVDR_CVDR_VP_WR_CFG_0_REG 0x1C +#define CVDR_CVDR_VP_WR_AXI_FS_0_REG 0x20 +#define CVDR_CVDR_VP_WR_AXI_LINE_0_REG 0x24 +#define CVDR_CVDR_VP_WR_IF_CFG_0_REG 0x28 +#define CVDR_CVDR_VP_WR_CFG_1_REG 0x2C +#define CVDR_CVDR_VP_WR_AXI_FS_1_REG 0x30 +#define CVDR_CVDR_VP_WR_AXI_LINE_1_REG 0x34 +#define CVDR_CVDR_VP_WR_IF_CFG_1_REG 0x38 +#define CVDR_CVDR_VP_WR_CFG_2_REG 0x3C +#define CVDR_CVDR_VP_WR_AXI_FS_2_REG 0x40 +#define CVDR_CVDR_VP_WR_AXI_LINE_2_REG 0x44 +#define CVDR_CVDR_VP_WR_IF_CFG_2_REG 0x48 +#define CVDR_CVDR_VP_WR_CFG_3_REG 0x4C +#define CVDR_CVDR_VP_WR_AXI_FS_3_REG 0x50 +#define CVDR_CVDR_VP_WR_AXI_LINE_3_REG 0x54 +#define CVDR_CVDR_VP_WR_IF_CFG_3_REG 0x58 +#define CVDR_CVDR_VP_WR_CFG_4_REG 0x5C +#define CVDR_CVDR_VP_WR_AXI_FS_4_REG 0x60 +#define CVDR_CVDR_VP_WR_AXI_LINE_4_REG 0x64 +#define CVDR_CVDR_VP_WR_IF_CFG_4_REG 0x68 +#define CVDR_CVDR_VP_WR_CFG_5_REG 0x6C +#define CVDR_CVDR_VP_WR_AXI_FS_5_REG 0x70 +#define CVDR_CVDR_VP_WR_AXI_LINE_5_REG 0x74 +#define CVDR_CVDR_VP_WR_IF_CFG_5_REG 0x78 +#define CVDR_CVDR_VP_WR_CFG_8_REG 0x9C +#define CVDR_CVDR_VP_WR_AXI_FS_8_REG 0xA0 +#define CVDR_CVDR_VP_WR_AXI_LINE_8_REG 0xA4 +#define CVDR_CVDR_VP_WR_IF_CFG_8_REG 0xA8 +#define CVDR_CVDR_VP_WR_CFG_9_REG 0xAC +#define CVDR_CVDR_VP_WR_AXI_FS_9_REG 0xB0 +#define CVDR_CVDR_VP_WR_AXI_LINE_9_REG 0xB4 +#define CVDR_CVDR_VP_WR_IF_CFG_9_REG 0xB8 +#define CVDR_CVDR_VP_WR_CFG_10_REG 0xBC +#define CVDR_CVDR_VP_WR_AXI_FS_10_REG 0xC0 +#define CVDR_CVDR_VP_WR_AXI_LINE_10_REG 0xC4 +#define CVDR_CVDR_VP_WR_IF_CFG_10_REG 0xC8 +#define CVDR_CVDR_VP_WR_CFG_11_REG 0xCC +#define CVDR_CVDR_VP_WR_AXI_FS_11_REG 0xD0 +#define CVDR_CVDR_VP_WR_AXI_LINE_11_REG 0xD4 +#define CVDR_CVDR_VP_WR_IF_CFG_11_REG 0xD8 +#define CVDR_CVDR_VP_WR_CFG_12_REG 0xDC +#define CVDR_CVDR_VP_WR_AXI_FS_12_REG 0xE0 +#define CVDR_CVDR_VP_WR_AXI_LINE_12_REG 0xE4 +#define CVDR_CVDR_VP_WR_IF_CFG_12_REG 0xE8 +#define CVDR_CVDR_VP_WR_CFG_14_REG 0xFC +#define CVDR_CVDR_VP_WR_AXI_FS_14_REG 0x100 +#define CVDR_CVDR_VP_WR_AXI_LINE_14_REG 0x104 +#define CVDR_CVDR_VP_WR_IF_CFG_14_REG 0x108 +#define CVDR_CVDR_VP_WR_CFG_15_REG 0x10C +#define CVDR_CVDR_VP_WR_AXI_FS_15_REG 0x110 +#define CVDR_CVDR_VP_WR_AXI_LINE_15_REG 0x114 +#define CVDR_CVDR_VP_WR_IF_CFG_15_REG 0x118 +#define CVDR_CVDR_VP_WR_CFG_16_REG 0x11C +#define CVDR_CVDR_VP_WR_AXI_FS_16_REG 0x120 +#define CVDR_CVDR_VP_WR_AXI_LINE_16_REG 0x124 +#define CVDR_CVDR_VP_WR_IF_CFG_16_REG 0x128 +#define CVDR_CVDR_VP_WR_CFG_17_REG 0x12C +#define CVDR_CVDR_VP_WR_AXI_FS_17_REG 0x130 +#define CVDR_CVDR_VP_WR_AXI_LINE_17_REG 0x134 +#define CVDR_CVDR_VP_WR_IF_CFG_17_REG 0x138 +#define CVDR_CVDR_VP_WR_CFG_18_REG 0x13C +#define CVDR_CVDR_VP_WR_AXI_FS_18_REG 0x140 +#define CVDR_CVDR_VP_WR_AXI_LINE_18_REG 0x144 +#define CVDR_CVDR_VP_WR_IF_CFG_18_REG 0x148 +#define CVDR_CVDR_VP_WR_CFG_19_REG 0x14C +#define CVDR_CVDR_VP_WR_AXI_FS_19_REG 0x150 +#define CVDR_CVDR_VP_WR_AXI_LINE_19_REG 0x154 +#define CVDR_CVDR_VP_WR_IF_CFG_19_REG 0x158 +#define CVDR_CVDR_VP_WR_CFG_20_REG 0x15C +#define CVDR_CVDR_VP_WR_AXI_FS_20_REG 0x160 +#define CVDR_CVDR_VP_WR_AXI_LINE_20_REG 0x164 +#define CVDR_CVDR_VP_WR_IF_CFG_20_REG 0x168 +#define CVDR_CVDR_VP_WR_CFG_21_REG 0x16C +#define CVDR_CVDR_VP_WR_AXI_FS_21_REG 0x170 +#define CVDR_CVDR_VP_WR_AXI_LINE_21_REG 0x174 +#define CVDR_CVDR_VP_WR_IF_CFG_21_REG 0x178 +#define CVDR_CVDR_VP_WR_CFG_22_REG 0x17C +#define CVDR_CVDR_VP_WR_AXI_FS_22_REG 0x180 +#define CVDR_CVDR_VP_WR_AXI_LINE_22_REG 0x184 +#define CVDR_CVDR_VP_WR_IF_CFG_22_REG 0x188 +#define CVDR_CVDR_VP_WR_CFG_23_REG 0x18C +#define CVDR_CVDR_VP_WR_AXI_FS_23_REG 0x190 +#define CVDR_CVDR_VP_WR_AXI_LINE_23_REG 0x194 +#define CVDR_CVDR_VP_WR_IF_CFG_23_REG 0x198 +#define CVDR_CVDR_VP_WR_CFG_24_REG 0x19C +#define CVDR_CVDR_VP_WR_AXI_FS_24_REG 0x1A0 +#define CVDR_CVDR_VP_WR_AXI_LINE_24_REG 0x1A4 +#define CVDR_CVDR_VP_WR_IF_CFG_24_REG 0x1A8 +#define CVDR_CVDR_VP_WR_CFG_25_REG 0x1AC +#define CVDR_CVDR_VP_WR_AXI_FS_25_REG 0x1B0 +#define CVDR_CVDR_VP_WR_AXI_LINE_25_REG 0x1B4 +#define CVDR_CVDR_VP_WR_IF_CFG_25_REG 0x1B8 +#define CVDR_CVDR_VP_WR_CFG_26_REG 0x1BC +#define CVDR_CVDR_VP_WR_AXI_FS_26_REG 0x1C0 +#define CVDR_CVDR_VP_WR_AXI_LINE_26_REG 0x1C4 +#define CVDR_CVDR_VP_WR_IF_CFG_26_REG 0x1C8 +#define CVDR_CVDR_VP_WR_CFG_27_REG 0x1CC +#define CVDR_CVDR_VP_WR_AXI_FS_27_REG 0x1D0 +#define CVDR_CVDR_VP_WR_AXI_LINE_27_REG 0x1D4 +#define CVDR_CVDR_VP_WR_IF_CFG_27_REG 0x1D8 +#define CVDR_CVDR_VP_WR_CFG_28_REG 0x1DC +#define CVDR_CVDR_VP_WR_AXI_FS_28_REG 0x1E0 +#define CVDR_CVDR_VP_WR_AXI_LINE_28_REG 0x1E4 +#define CVDR_CVDR_VP_WR_IF_CFG_28_REG 0x1E8 +#define CVDR_CVDR_VP_WR_CFG_29_REG 0x1EC +#define CVDR_CVDR_VP_WR_AXI_FS_29_REG 0x1F0 +#define CVDR_CVDR_VP_WR_AXI_LINE_29_REG 0x1F4 +#define CVDR_CVDR_VP_WR_IF_CFG_29_REG 0x1F8 +#define CVDR_CVDR_VP_WR_CFG_30_REG 0x1FC +#define CVDR_CVDR_VP_WR_AXI_FS_30_REG 0x200 +#define CVDR_CVDR_VP_WR_AXI_LINE_30_REG 0x204 +#define CVDR_CVDR_VP_WR_IF_CFG_30_REG 0x208 +#define CVDR_CVDR_VP_WR_CFG_31_REG 0x20C +#define CVDR_CVDR_VP_WR_AXI_FS_31_REG 0x210 +#define CVDR_CVDR_VP_WR_AXI_LINE_31_REG 0x214 +#define CVDR_CVDR_VP_WR_IF_CFG_31_REG 0x218 +#define CVDR_CVDR_VP_WR_CFG_32_REG 0x21C +#define CVDR_CVDR_VP_WR_AXI_FS_32_REG 0x220 +#define CVDR_CVDR_VP_WR_AXI_LINE_32_REG 0x224 +#define CVDR_CVDR_VP_WR_IF_CFG_32_REG 0x228 +#define CVDR_CVDR_VP_WR_CFG_33_REG 0x22C +#define CVDR_CVDR_VP_WR_AXI_FS_33_REG 0x230 +#define CVDR_CVDR_VP_WR_AXI_LINE_33_REG 0x234 +#define CVDR_CVDR_VP_WR_IF_CFG_33_REG 0x238 +#define CVDR_CVDR_VP_WR_CFG_34_REG 0x23C +#define CVDR_CVDR_VP_WR_AXI_FS_34_REG 0x240 +#define CVDR_CVDR_VP_WR_AXI_LINE_34_REG 0x244 +#define CVDR_CVDR_VP_WR_IF_CFG_34_REG 0x248 +#define CVDR_CVDR_VP_WR_CFG_35_REG 0x24C +#define CVDR_CVDR_VP_WR_AXI_FS_35_REG 0x250 +#define CVDR_CVDR_VP_WR_AXI_LINE_35_REG 0x254 +#define CVDR_CVDR_VP_WR_IF_CFG_35_REG 0x258 +#define CVDR_CVDR_VP_WR_CFG_36_REG 0x25C +#define CVDR_CVDR_VP_WR_AXI_FS_36_REG 0x260 +#define CVDR_CVDR_VP_WR_AXI_LINE_36_REG 0x264 +#define CVDR_CVDR_VP_WR_IF_CFG_36_REG 0x268 +#define CVDR_CVDR_VP_WR_CFG_37_REG 0x26C +#define CVDR_CVDR_VP_WR_AXI_FS_37_REG 0x270 +#define CVDR_CVDR_VP_WR_AXI_LINE_37_REG 0x274 +#define CVDR_CVDR_VP_WR_IF_CFG_37_REG 0x278 +#define CVDR_CVDR_LIMITER_VP_WR_0_REG 0x400 + +#define CVDR_VP_RD_NBR (22) +#define CVDR_VP_WR_NBR (38) +#define CVDR_NR_WR_NBR (4) +#define CVDR_NR_RD_NBR (8) + +#define ONE_REG_OFFSET (0x4) + +#define VP_WR_REG_OFFSET (0x10) + +#define ISP_CLK (480) +#define DERATE (1.2) + +#define CVDR_ALIGN_BYTES (16) + +enum cvdr_pix_fmt_e { + DF_1PF8 = 0x0, + DF_1PF10 = 0x1, + DF_1PF12 = 0x2, + DF_1PF14 = 0x3, + DF_2PF8 = 0x4, + DF_2PF10 = 0x5, + DF_2PF12 = 0x6, + DF_2PF14 = 0x7, + DF_3PF8 = 0x8, + DF_3PF10 = 0x9, + DF_3PF12 = 0xA, + DF_3PF14 = 0xB, + DF_D32 = 0xC, + DF_D48 = 0xD, + DF_D64 = 0xE, + DF_FMT_INVALID, +}; + +enum vp_wr_id_e { + W12_1 = 0, + W12_2 = 1, + W6_1 = 2, + W6_2 = 3, + W5_1_1 = 4, + W5_1_2 = 5, + W4_1_1 = 8, + W4_1_2 = 9, + W4_2_1 = 10, + W4_2_2 = 11, + W7_1 = 12, + W13_1 = 14, + W13_2 = 15, + W1_1 = 16, + W1_2 = 17, + W14_1 = 18, + W14_2 = 19, + W11_1 = 20, + W11_2 = 21, + W2_3 = 22, + W2_4 = 23, + W2_5 = 24, + W8_1 = 25, + W3_1 = 26, + W2_2 = 27, + W2_1 = 28, + W15_1 = 29, + W16_1 = 30, + W16_2 = 31, + W17_1 = 32, + W17_2 = 33, + W19_1 = 34, + W19_2 = 35, + W20_1 = 36, + W20_2 = 37, +}; + +struct cvdr_wr_fmt_desc_t { + unsigned int fs_addr; + unsigned int last_page; + + enum cvdr_pix_fmt_e pix_fmt; + unsigned char pix_expan; + unsigned short line_stride; + unsigned short line_wrap; +}; + +struct cvdr_bw_cfg_t { + unsigned char bw_limiter0; + unsigned char bw_limiter1; + unsigned char bw_limiter2; + unsigned char bw_limiter3; + unsigned char bw_limiter_reload; +}; + +struct cvdr_smmu_cfg_t { + unsigned char to_use; + + unsigned int num; + unsigned int smr_nscfg; +}; + +struct cvdr_vp_wr_cfg_t { + unsigned char to_use; + unsigned char id; + struct cvdr_wr_fmt_desc_t fmt; + struct cvdr_bw_cfg_t bw; +}; + +struct cvdr_rd_fmt_desc_t { + unsigned int fs_addr; + unsigned int last_page; + enum cvdr_pix_fmt_e pix_fmt; + unsigned char pix_expan; + unsigned short allocated_du; + unsigned short line_size; + unsigned short hblank; + unsigned short frame_size; + unsigned short vblank; + unsigned short line_stride; + unsigned short line_wrap; +}; + +struct cvdr_vp_rd_cfg_t { + unsigned char to_use; + unsigned char id; + struct cvdr_rd_fmt_desc_t fmt; + struct cvdr_bw_cfg_t bw; +}; + +struct cvdr_nr_wr_cfg_t { + unsigned char to_use; + unsigned char nr_wr_stop_en_du_thr; + unsigned char nr_wr_stop_en_flux_ctrl; + unsigned char nr_wr_stop_en_pressure; + unsigned char nr_wr_stop_ok; + unsigned char nr_wr_stop; + unsigned char en; + struct cvdr_bw_cfg_t bw; +}; + +struct cvdr_nr_rd_cfg_t { + unsigned char to_use; + unsigned short allocated_du; + unsigned char nr_rd_stop_en_du_thr; + unsigned char nr_rd_stop_en_flux_ctrl; + unsigned char nr_rd_stop_en_pressure; + unsigned char nr_rd_stop_ok; + unsigned char nr_rd_stop; + unsigned char en; + struct cvdr_bw_cfg_t bw; +}; + +struct cfg_tab_cvdr_t { + struct cvdr_smmu_cfg_t smmu_nr_rd_cfg[CVDR_NR_RD_NBR]; + struct cvdr_smmu_cfg_t smmu_vp_wr_cfg[CVDR_VP_WR_NBR]; + struct cvdr_smmu_cfg_t smmu_vp_rd_cfg[CVDR_VP_RD_NBR]; + struct cvdr_vp_wr_cfg_t vp_wr_cfg[CVDR_VP_WR_NBR]; + struct cvdr_vp_rd_cfg_t vp_rd_cfg[CVDR_VP_RD_NBR]; + struct cvdr_nr_wr_cfg_t nr_wr_cfg[CVDR_NR_WR_NBR]; + struct cvdr_nr_rd_cfg_t nr_rd_cfg[CVDR_NR_RD_NBR]; +}; + +struct cvdr_opt_bw_t { + unsigned int srt; + unsigned int pclk; + unsigned int throughput; +}; + +enum cvdr_dev_e { + CVDR_RT = 0, + CVDR_SRT = 1, +}; + +enum vp_rd_id_e { + R3_1 = 0, + R11_1 = 4, + R12_1 = 5, + R11_2 = 6, + R12_2 = 7, + R10_1 = 8, + R10_2 = 9, + R2_1 = 10, + R8_1_1 = 11, + R8_2_1 = 12, + R8_1_2 = 13, + R8_2_2 = 14, + R13_1 = 15, + R13_2 = 16, + R5_1_4 = 19, + R5_1_5 = 20, + R5_1_6 = 21, +}; + +/* Define the union U_CVDR_CFG */ +union U_CVDR_CFG { + /* Define the struct bits */ + struct { + unsigned int axiwrite_du_threshold : 6; /* [5..0] */ + unsigned int reserved_0 : 2; /* [7..6] */ + unsigned int du_threshold_reached : 8; /* [15..8] */ + unsigned int max_axiread_id : 5; /* [20..16] */ + unsigned int reserved_1 : 3; /* [23..21] */ + unsigned int max_axiwrite_id : 5; /* [28..24] */ + unsigned int reserved_2 : 1; /* [29] */ + unsigned int force_rd_clk_on : 1; /* [30] */ + unsigned int force_wr_clk_on : 1; /* [31] */ + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_CVDR_DEBUG_EN */ +union U_CVDR_DEBUG_EN { + /* Define the struct bits */ + struct { + unsigned int wr_peak_en : 1; + unsigned int reserved_0 : 7; + unsigned int rd_peak_en : 1; + unsigned int reserved_1 : 23; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_CVDR_DEBUG */ +union U_CVDR_DEBUG { + /* Define the struct bits */ + struct { + unsigned int wr_peak : 8; + unsigned int rd_peak : 8; + unsigned int reserved_0 : 16; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_CVDR_WR_QOS_CFG */ +union U_CVDR_WR_QOS_CFG { + /* Define the struct bits */ + struct { + unsigned int wr_qos_threshold_01_stop : 4; + unsigned int wr_qos_threshold_01_start : 4; + unsigned int wr_qos_threshold_10_stop : 4; + unsigned int wr_qos_threshold_10_start : 4; + unsigned int wr_qos_threshold_11_stop : 4; + unsigned int wr_qos_threshold_11_start : 4; + unsigned int reserved_0 : 2; + unsigned int wr_qos_min : 2; + unsigned int wr_qos_max : 2; + unsigned int wr_qos_sr : 2; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_CVDR_RD_QOS_CFG */ +union U_CVDR_RD_QOS_CFG { + /* Define the struct bits */ + struct { + unsigned int rd_qos_threshold_01_stop : 4; + unsigned int rd_qos_threshold_01_start : 4; + unsigned int rd_qos_threshold_10_stop : 4; + unsigned int rd_qos_threshold_10_start : 4; + unsigned int rd_qos_threshold_11_stop : 4; + unsigned int rd_qos_threshold_11_start : 4; + unsigned int reserved_0 : 2; + unsigned int rd_qos_min : 2; + unsigned int rd_qos_max : 2; + unsigned int rd_qos_sr : 2; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_OTHER_RO */ +union U_CVDR_OTHER_RO { + /* Define the struct bits */ + struct { + unsigned int other_ro : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_OTHER_RW */ +union U_CVDR_OTHER_RW { + /* Define the struct bits */ + struct { + unsigned int other_rw : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + + +/* Define the union U_VP_WR_CFG */ +union U_VP_WR_CFG { + /* Define the struct bits */ + struct { + unsigned int vpwr_pixel_format : 4; + unsigned int vpwr_pixel_expansion : 1; + unsigned int reserved_0 : 10; + unsigned int vpwr_last_page : 17; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_WR_AXI_FS */ +union U_VP_WR_AXI_FS { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 4; + unsigned int vpwr_address_frame_start : 28; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_WR_AXI_LINE */ +union U_VP_WR_AXI_LINE { + /* Define the struct bits */ + struct { + unsigned int vpwr_line_stride : 10; + unsigned int reserved_0 : 5; + unsigned int vpwr_line_wrap : 14; + unsigned int reserved_1 : 3; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_CVDR_RT_VP_WR_IF_CFG */ +union U_VP_WR_IF_CFG { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 16; + unsigned int vp_wr_stop_enable_du_threshold_reached : 1; + unsigned int vp_wr_stop_enable_flux_ctrl : 1; + unsigned int vp_wr_stop_enable_pressure : 1; + unsigned int reserved_1 : 5; + unsigned int vp_wr_stop_ok : 1; + unsigned int vp_wr_stop : 1; + unsigned int reserved_2 : 5; + unsigned int vpwr_prefetch_bypass : 1; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + + +/* Define the union U_LIMITER_VP_WR */ +union U_LIMITER_VP_WR { + /* Define the struct bits */ + struct { + unsigned int vpwr_access_limiter_0 : 4; + unsigned int vpwr_access_limiter_1 : 4; + unsigned int vpwr_access_limiter_2 : 4; + unsigned int vpwr_access_limiter_3 : 4; + unsigned int reserved_0 : 8; + unsigned int vpwr_access_limiter_reload : 4; + unsigned int reserved_1 : 4; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_CFG */ +union U_VP_RD_CFG { + /* Define the struct bits */ + struct { + unsigned int vprd_pixel_format : 4; + unsigned int vprd_pixel_expansion : 1; + unsigned int vprd_allocated_du : 5; + unsigned int reserved_0 : 5; + unsigned int vprd_last_page : 17; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_VP_RD_LWG */ +union U_VP_RD_LWG { + /* Define the struct bits */ + struct { + unsigned int vprd_line_size : 13; + unsigned int reserved_0 : 3; + unsigned int vprd_horizontal_blanking : 8; + unsigned int reserved_1 : 8; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_FHG */ +union U_VP_RD_FHG { + /* Define the struct bits */ + struct { + unsigned int vprd_frame_size : 13; + unsigned int reserved_0 : 3; + unsigned int vprd_vertical_blanking : 8; + unsigned int reserved_1 : 8; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_AXI_FS */ +union U_VP_RD_AXI_FS { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 4; + unsigned int vprd_axi_frame_start : 28; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_AXI_LINE */ +union U_VP_RD_AXI_LINE { + /* Define the struct bits */ + struct { + unsigned int vprd_line_stride : 10; + unsigned int reserved_0 : 6; + unsigned int vprd_line_wrap : 13; + unsigned int reserved_1 : 3; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_IF_CFG */ +union U_VP_RD_IF_CFG { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 16; + unsigned int vp_rd_stop_enable_du_threshold_reached : 1; + unsigned int vp_rd_stop_enable_flux_ctrl : 1; + unsigned int vp_rd_stop_enable_pressure : 1; + unsigned int reserved_1 : 5; + unsigned int vp_rd_stop_ok : 1; + unsigned int vp_rd_stop : 1; + unsigned int reserved_2 : 5; + unsigned int vprd_prefetch_bypass : 1; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_VP_RD_DEBUG */ +union U_VP_RD_DEBUG { + /* Define the struct bits */ + struct { + unsigned int vp_rd_debug : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_LIMITER_VP_RD */ +union U_LIMITER_VP_RD { + /* Define the struct bits */ + struct { + unsigned int vprd_access_limiter_0 : 4; + unsigned int vprd_access_limiter_1 : 4; + unsigned int vprd_access_limiter_2 : 4; + unsigned int vprd_access_limiter_3 : 4; + unsigned int reserved_0 : 8; + unsigned int vprd_access_limiter_reload : 4; + unsigned int reserved_1 : 4; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_NR_WR_CFG */ +union U_NR_WR_CFG { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 16; + unsigned int nr_wr_stop_enable_du_threshold_reached : 1; + unsigned int nr_wr_stop_enable_flux_ctrl : 1; + unsigned int nr_wr_stop_enable_pressure : 1; + unsigned int reserved_1 : 5; + unsigned int nr_wr_stop_ok : 1; + unsigned int nr_wr_stop : 1; + unsigned int reserved_2 : 5; + unsigned int nrwr_enable : 1; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_NR_WR_DEBUG */ +union U_NR_WR_DEBUG { + /* Define the struct bits */ + struct { + unsigned int nr_wr_debug : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_LIMITER_NR_WR */ +union U_LIMITER_NR_WR { + /* Define the struct bits */ + struct { + unsigned int nrwr_access_limiter_0 : 4; + unsigned int nrwr_access_limiter_1 : 4; + unsigned int nrwr_access_limiter_2 : 4; + unsigned int nrwr_access_limiter_3 : 4; + unsigned int reserved_0 : 8; + unsigned int nrwr_access_limiter_reload : 4; + unsigned int reserved_1 : 4; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_NR_RD_CFG */ +union U_NR_RD_CFG { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 5; + unsigned int nrrd_allocated_du : 5; + unsigned int reserved_1 : 6; + unsigned int nr_rd_stop_enable_du_threshold_reached : 1; + unsigned int nr_rd_stop_enable_flux_ctrl : 1; + unsigned int nr_rd_stop_enable_pressure : 1; + unsigned int reserved_2 : 5; + unsigned int nr_rd_stop_ok : 1; + unsigned int nr_rd_stop : 1; + unsigned int reserved_3 : 5; + unsigned int nrrd_enable : 1; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_NR_RD_DEBUG */ +union U_NR_RD_DEBUG { + /* Define the struct bits */ + struct { + unsigned int nr_rd_debug : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_LIMITER_NR_RD */ +union U_LIMITER_NR_RD { + /* Define the struct bits */ + struct { + unsigned int nrrd_access_limiter_0 : 4; + unsigned int nrrd_access_limiter_1 : 4; + unsigned int nrrd_access_limiter_2 : 4; + unsigned int nrrd_access_limiter_3 : 4; + unsigned int reserved_0 : 8; + unsigned int nrrd_access_limiter_reload : 4; + unsigned int reserved_1 : 4; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_DEBUG */ +union U_DEBUG { + /* Define the struct bits */ + struct { + unsigned int debug : 32 ; /* [31..0] */ + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_AXI_CFG_NR_WR */ +union U_AXI_CFG_NR_WR { + /* Define the struct bits */ + struct { + unsigned int nr_wr_mid : 6; + unsigned int reserved_0 : 26; + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +/* Define the union U_AXI_CFG_NR_RD */ +union U_AXI_CFG_NR_RD { + /* Define the struct bits */ + struct { + unsigned int nr_rd_mid : 6; + unsigned int reserved_0 : 26; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_AXI_CFG_VP_WR */ +union U_AXI_CFG_VP_WR { + /* Define the struct bits */ + struct { + unsigned int vp_wr_mid : 6; + unsigned int reserved_0 : 26; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_AXI_CFG_VP_RD */ +union U_AXI_CFG_VP_RD { + /* Define the struct bits */ + struct { + unsigned int vp_rd_mid : 6; + unsigned int reserved_0 : 26; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_SPARE */ +union U_SPARE { + /* Define the struct bits */ + struct { + unsigned int spare : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_VP_WR_SMOOTHING */ +union U_VP_WR_SMOOTHING { + /* Define the struct bits */ + struct { + unsigned int vpwr_smoothing_access_limiter_0 : 4; + unsigned int vpwr_smoothing_access_limiter_1 : 4; + unsigned int vpwr_smoothing_access_limiter_2 : 4; + unsigned int vpwr_smoothing_access_limiter_3 : 4; + unsigned int vpwr_smoothing_threshold : 7; + unsigned int reserved_0 : 9; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_VP_WR_DEBUG */ +union U_VP_WR_DEBUG { + /* Define the struct bits */ + struct { + unsigned int vp_wr_debug : 32; + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +struct S_VP_WR { + union U_VP_WR_CFG VP_WR_CFG; + union U_VP_WR_AXI_FS VP_WR_AXI_FS; + union U_VP_WR_AXI_LINE VP_WR_AXI_LINE; + union U_VP_WR_IF_CFG VP_WR_IF_CFG; +}; + +struct S_VP_RD { + union U_VP_RD_CFG VP_RD_CFG; + union U_VP_RD_LWG VP_RD_LWG; + union U_VP_RD_FHG VP_RD_FHG; + union U_VP_RD_AXI_FS VP_RD_AXI_FS; + union U_VP_RD_AXI_LINE VP_RD_AXI_LINE; + union U_VP_RD_IF_CFG VP_RD_IF_CFG; + unsigned int VP_RD_DEBUG; +}; + +struct S_NR_WR { + union U_NR_WR_CFG NR_WR_CFG; + union U_NR_WR_DEBUG NR_WR_DEBUG; + union U_LIMITER_NR_WR LIMITER_NR_WR; + unsigned int reserved_0; +}; + +struct S_NR_RD { + union U_NR_RD_CFG NR_RD_CFG; + union U_NR_RD_DEBUG NR_RD_DEBUG; + union U_LIMITER_NR_RD LIMITER_NR_RD; + unsigned int reserved_0; +}; + +/* Define the global struct */ +struct S_CVDR_REGS_TYPE { + union U_CVDR_CFG CVDR_CFG ; /* 0x0 */ + union U_CVDR_DEBUG_EN CVDR_DEBUG_EN ; /* 0x4 */ + union U_CVDR_DEBUG CVDR_DEBUG ; /* 0x8 */ + union U_CVDR_WR_QOS_CFG CVDR_WR_QOS_CFG ; /* 0xc */ + union U_CVDR_RD_QOS_CFG CVDR_RD_QOS_CFG ; /* 0x10 */ + union U_CVDR_OTHER_RO CVDR_OTHER_RO ; /* 0x14 */ + union U_CVDR_OTHER_RW CVDR_OTHER_RW ; /* 0x18 */ + struct S_VP_WR VP_WR[38] ; /* 0x1c~0x278 */ + unsigned int reserved_0[73] ; /* 0x27c~0x39c */ + union U_LIMITER_VP_WR LIMITER_VP_WR[38] ; /* 0x400~0x494 */ + unsigned int reserved_1[2] ; /* 0x498~0x49c */ + struct S_VP_RD VP_RD[22] ; /* 0x500~0x7bc */ + unsigned int reserved_2[48] ; /* 0x7c0~0x87c */ + union U_LIMITER_VP_RD LIMITER_VP_RD[22] ; /* 0x880~0x8d4 */ + unsigned int reserved_3[10] ; /* 0x8d8~0x8fc */ + struct S_NR_WR NR_WR[4] ; /* 0x900~0x93c */ + unsigned int reserved_4[48] ; /* 0x940~0x9fc */ + struct S_NR_RD NR_RD[8] ; /* 0xa00~0xa7c */ + unsigned int reserved_5[32] ; /* 0xa80~0xafc */ + union U_DEBUG DEBUG[16] ; /* 0xb00~0xb3c */ + unsigned int reserved_6[48] ; /* 0xb40~0xbfc */ + union U_AXI_CFG_NR_WR AXI_CFG_NR_WR[4] ; /* 0xc00~0xc0c */ + unsigned int reserved_7[12] ; /* 0xc10~0xc3c */ + union U_AXI_CFG_NR_RD AXI_CFG_NR_RD[8] ; /* 0xc40~0xc5c */ + unsigned int reserved_8[8] ; /* 0xc60~0xc7c */ + union U_AXI_CFG_VP_WR AXI_CFG_VP_WR[38] ; /* 0xc80~0xd14 */ + unsigned int reserved_9[26] ; /* 0xd18~0xd7c */ + union U_AXI_CFG_VP_RD AXI_CFG_VP_RD[22] ; /* 0xd80~0xdd4 */ + unsigned int reserved_10[10] ; /* 0xdd8~0xdfc */ + union U_SPARE SPARE[4] ; /* 0xe00~0xe0c */ + union U_VP_WR_SMOOTHING VP_WR_SMOOTHING[20] ; /* 0xe10~0xe5c */ + unsigned int reserved_11[40] ; /* 0xe60~0xefc */ + union U_VP_WR_DEBUG VP_WR_DEBUG[38] ; /* 0xf00~0xf94 */ +}; + +struct cvdr_device { + struct isp *isp; + struct v4l2_subdev subdev; + struct media_pad pads[ISP_CVDR_PADS_NUM]; + void __iomem *cvdr_rt; + void __iomem *cvdr_srt; + void __iomem *sub_ctrl; + unsigned int irq_vic1; +}; + +int isp_cvdr_subdev_init(struct isp *isp, + struct cvdr_device *cvdr, + const struct resources *res); + +int isp_cvdr_register_entity(struct cvdr_device *cvdr, + struct v4l2_device *v4l2_dev); + +void isp_cvdr_unregister_entity(struct cvdr_device *cvdr); + +#endif /* HISI_ISP_CVDR_H */ diff --git a/drivers/media/platform/hisi/isp/isp-sr.c b/drivers/media/platform/hisi/isp/isp-sr.c new file mode 100644 index 000000000000..14f1ac4d4cef --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-sr.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * isp-sr.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_SR_NAME "isp_sr" + +void isp_sr_config(struct sr_device *sr) +{ + int data_type = 1; + union U_ID_ROUTER_1 reg1; + union U_REFORMAT reg; + + isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_A_REG, + (0 << 6) | (YUV_DT_422_8BITS << 0)); + + reg1.u32 = isp_readl(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG); + reg1.bits.idr_enable_6 = 1; + reg1.bits.idr_input_stream_6 = (0 << 2) | (0 << 0); + isp_writel(sr->base, STREAM_ROUTER_ID_ROUTER_1_REG, reg1.u32); + + reg.u32 = 0; + reg.bits.reformat_enable = 1; + reg.bits.reformat_num_pixels = 1920 * 2 - 1; + reg.bits.reformat_num_lines = 1080 - 1; + reg.bits.reformat_pixel_reorder = data_type; + + isp_writel(sr->base, STREAM_ROUTER_REFORMAT_REG(6), reg.u32); + isp_writel(sr->base, STREAM_ROUTER_REFORMAT_MINSPACE_REG(6), (0x1F << 0)); +} + +void isp_sr_go(struct sr_device *sr, unsigned int go_bit) +{ + isp_writel(sr->base, STREAM_ROUTER_CSIFILTER_GO_REG, go_bit); +} + +/* + * sr_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 sr_set_power(struct v4l2_subdev *sd, int on) +{ + + pr_info("%s: %d\n", __func__, __LINE__); + return 0; +} + +/* + * sr_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 sr_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct sr_device *sr = v4l2_get_subdevdata(sd); + int ret = 0; + + enable_irq(sr->irq_frproc0); + isp_config_smmu_bypass(sr->isp); + isp_sr_config(sr); + isp_sr_go(sr, 0); + + pr_info("%s: %d\n", __func__, __LINE__); + + return ret; +} + +/* + * sr_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 sr_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + return 0; +} + +irqreturn_t isp_frproc0_handler(int irq, void *dev) +{ + struct sr_device *sr = dev; + struct isp *isp = sr->isp; + unsigned int val = isp_clear_irq(sr->isp, IRQ_MERGER_FRPROC_0); + + if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_EOF_VPWR_23_OFFSET)) { + // FIXME: Use CSI index + isp_sr_go(sr, (1 << 0)); + isp->frame_num++; + } + + return IRQ_HANDLED; +} + +/* + * isp_sr_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_sr_subdev_init(struct isp *isp, + struct sr_device *sr, + const struct resources *res) +{ + struct device *dev = isp->dev; + struct platform_device *pdev = to_platform_device(dev); + struct resource *r; + int ret; + + sr->isp = isp; + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg); + sr->base = devm_ioremap_resource(dev, r); + if (IS_ERR(sr->base)) { + dev_err(dev, "could not map memory\n"); + return PTR_ERR(sr->base); + } + + ret = platform_get_irq_byname(pdev, "isp_frproc0"); + if (ret <= 0) { + dev_err(isp->dev, "No IRQ resource\n"); + return -ENODEV; + } + + sr->irq_frproc0 = ret; + + ret = devm_request_irq(&pdev->dev, sr->irq_frproc0, + isp_frproc0_handler, 0, "isp-frproc0", sr); + if (ret) + return ret; + + disable_irq(sr->irq_frproc0); + + return 0; +} + +/* + * sr_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 sr_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 sr_device *sr; + + if (media_entity_remote_pad(local)) + return -EBUSY; + + sd = media_entity_to_v4l2_subdev(entity); + sr = v4l2_get_subdevdata(sd); + } + + return 0; +} + +static const struct v4l2_subdev_core_ops sr_core_ops = { + .s_power = sr_set_power, +}; + +static const struct v4l2_subdev_video_ops sr_video_ops = { + .s_stream = sr_set_stream, +}; + +static const struct v4l2_subdev_ops sr_v4l2_ops = { + .core = &sr_core_ops, + .video = &sr_video_ops, +}; + +static const struct v4l2_subdev_internal_ops sr_v4l2_internal_ops = { + .open = sr_init_formats, +}; + +static const struct media_entity_operations sr_media_ops = { + .link_setup = sr_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +/* + * isp_sr_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_sr_register_entity(struct sr_device *sr, + struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &sr->subdev; + struct media_pad *pads = sr->pads; + struct device *dev = sr->isp->dev; + int ret; + + v4l2_subdev_init(sd, &sr_v4l2_ops); + sd->internal_ops = &sr_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s", + ISP_SR_NAME); + v4l2_set_subdevdata(sd, sr); + + ret = sr_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 = &sr_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_sr_unregister_entity - Unregister CSIPHY module subdev node + * @sr: CSIPHY device + */ +void isp_sr_unregister_entity(struct sr_device *sr) +{ + v4l2_device_unregister_subdev(&sr->subdev); + media_entity_cleanup(&sr->subdev.entity); +} diff --git a/drivers/media/platform/hisi/isp/isp-sr.h b/drivers/media/platform/hisi/isp/isp-sr.h new file mode 100644 index 000000000000..aa5e23a259a8 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp-sr.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2016-2018 Linaro Ltd. + */ + +#ifndef HISI_ISP_SR_H +#define HISI_ISP_SR_H + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <media/media-entity.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mediabus.h> +#include <media/v4l2-subdev.h> + +#define ISP_SR_PAD_SINK 0 +#define ISP_SR_PAD_SRC 1 +#define ISP_SR_PADS_NUM 1 + +#define STREAM_ROUTER_CSIFILTER_A_REG 0x0 +#define STREAM_ROUTER_CSIFILTER_B_REG 0x4 +#define STREAM_ROUTER_CSIFILTER_C_REG 0x8 +#define STREAM_ROUTER_CSIFILTER_GO_REG 0xC +#define STREAM_ROUTER_PRESSURE_START_REG 0x10 +#define STREAM_ROUTER_PRESSURE_STOP_REG 0x14 +#define STREAM_ROUTER_ID_ROUTER_0_REG 0x18 +#define STREAM_ROUTER_ID_ROUTER_1_REG 0x1C +#define STREAM_ROUTER_REFORMAT_0_REG 0x20 +#define STREAM_ROUTER_REFORMAT_1_REG 0x28 +#define STREAM_ROUTER_REFORMAT_2_REG 0x30 +#define STREAM_ROUTER_REFORMAT_3_REG 0x38 +#define STREAM_ROUTER_REFORMAT_4_REG 0x40 +#define STREAM_ROUTER_REFORMAT_5_REG 0x48 +#define STREAM_ROUTER_REFORMAT_6_REG 0x50 +#define STREAM_ROUTER_REFORMAT_7_REG 0x58 +#define STREAM_ROUTER_REFORMAT_MINSPACE_0_REG 0x24 +#define STREAM_ROUTER_REFORMAT_MINSPACE_1_REG 0x2C +#define STREAM_ROUTER_REFORMAT_MINSPACE_2_REG 0x34 +#define STREAM_ROUTER_REFORMAT_MINSPACE_3_REG 0x3C +#define STREAM_ROUTER_REFORMAT_MINSPACE_4_REG 0x44 +#define STREAM_ROUTER_REFORMAT_MINSPACE_5_REG 0x4C +#define STREAM_ROUTER_REFORMAT_MINSPACE_6_REG 0x54 +#define STREAM_ROUTER_REFORMAT_MINSPACE_7_REG 0x5C +#define STREAM_ROUTER_EOL_CNT_REG 0x60 +#define STREAM_ROUTER_DPCM_0_REG 0x78 +#define STREAM_ROUTER_DPCM_1_REG 0x7C +#define STREAM_ROUTER_DPCM_2_REG 0x80 +#define STREAM_ROUTER_VP_ROUTER_0_REG 0x90 +#define STREAM_ROUTER_VP_ROUTER_1_REG 0x94 +#define STREAM_ROUTER_VP_ROUTER_2_REG 0x98 +#define STREAM_ROUTER_VP_ROUTER_3_REG 0x9C +#define STREAM_ROUTER_PIXFRAG_CONVERT_REG 0xA0 + +/* Define the union U_ID_ROUTER_1 */ +union U_ID_ROUTER_1 { + /* Define the struct bits */ + struct { + unsigned int reserved_0 : 16 ; /* [15..0] */ + unsigned int idr_input_stream_6 : 4 ; /* [19..16] */ + unsigned int idr_enable_6 : 1 ; /* [20] */ + unsigned int reserved_1 : 3 ; /* [23..21] */ + unsigned int idr_input_stream_7 : 4 ; /* [27..24] */ + unsigned int idr_enable_7 : 1 ; /* [28] */ + unsigned int reserved_2 : 3 ; /* [31..29] */ + } bits; + + /* Define an unsigned member */ + unsigned int u32; + +}; + +/* Define the union U_REFORMAT */ +union U_REFORMAT { + /* Define the struct bits */ + struct { + unsigned int reformat_num_lines : 13 ; /* [12..0] */ + unsigned int reformat_pixel_reorder : 3 ; /* [15..13] */ + unsigned int reformat_num_pixels : 13 ; /* [28..16] */ + unsigned int reserved_0 : 2 ; /* [30..29] */ + unsigned int reformat_enable : 1 ; /* [31] */ + } bits; + + /* Define an unsigned member */ + unsigned int u32; +}; + +#define STREAM_ROUTER_REFORMAT_MINSPACE_REG(x) \ + (STREAM_ROUTER_REFORMAT_MINSPACE_0_REG +\ + (x)*(STREAM_ROUTER_REFORMAT_MINSPACE_1_REG -\ + STREAM_ROUTER_REFORMAT_MINSPACE_0_REG)) + +#define STREAM_ROUTER_REFORMAT_REG(x) \ + (STREAM_ROUTER_REFORMAT_0_REG +\ + (x)*(STREAM_ROUTER_REFORMAT_1_REG -\ + STREAM_ROUTER_REFORMAT_0_REG)) + +struct sr_device { + struct isp *isp; + struct v4l2_subdev subdev; + struct media_pad pads[ISP_SR_PADS_NUM]; + void __iomem *base; + unsigned int irq_frproc0; +}; + +int isp_sr_subdev_init(struct isp *isp, + struct sr_device *sr, + const struct resources *res); + +int isp_sr_register_entity(struct sr_device *sr, + struct v4l2_device *v4l2_dev); + +void isp_sr_unregister_entity(struct sr_device *sr); + +#endif /* HISI_ISP_SR_H */ diff --git a/drivers/media/platform/hisi/isp/isp.c b/drivers/media/platform/hisi/isp/isp.c new file mode 100644 index 000000000000..f9650d178cd9 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * isp.c + * + * Copyright (C) 2018 Linaro Ltd. + */ + +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/media-bus-format.h> +#include <linux/media.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/pm_runtime.h> +#include <linux/pm_domain.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/media-device.h> +#include <media/v4l2-async.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-mc.h> +#include <media/v4l2-fwnode.h> + +#include "isp.h" + +#define DOVDD_VOLTAGE 1800000 +#define ISP_CLK_FREQ 24000000 + +const struct resources isp_res[] = { + { + .reg = "ispss_ctrl", + }, + { + .reg = "smmu_ctrl", + }, + { + .reg = "irq_merger2", + }, +}; + +const struct resources csiphy_res[] = { + { + .reg = "csi0", + }, + { + .reg = "csi1", + }, +}; + +const struct resources sr_res[] = { + { + .reg = "sr", + }, +}; + +const struct resources cvdr_res[] = { + { + .reg = "cvdr_rt", + }, + { + .reg = "cvdr_srt", + }, + { + .reg = "sub_ctrl", + }, +}; + +void isp_writel(void __iomem *base , u32 reg, u32 value) +{ + writel(value, base + reg); +} + +u32 isp_readl(void __iomem *base , u32 reg) +{ + return readl(base + reg); +} + +u32 isp_clear_irq(struct isp *isp, enum IRQ_MERGER_TYPE irq) +{ + u32 val; + + val = isp_readl(isp->irq_merger2, irq + IRQ_MERGER_FUNC_RIS); + isp_writel(isp->irq_merger2, irq+IRQ_MERGER_FUNC_ICR, val); + + return val; +} + +int frame_num2Offset(struct isp *isp, int frame_num) +{ + return (isp->frame_size * (frame_num % isp->frame_count)); +} + +void isp_config_smmu_bypass(struct isp *isp) +{ + isp_writel(isp->smmu_ctrl, 0x0, 0x1); +} + +void isp_ispss_enable_irq(struct isp *isp) +{ + isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_DEBUG1, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_IMSC_FRPROC0, 0xFFFFFFFF); +} + +void isp_ispss_clear_irq_state(struct isp *isp) +{ + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG0, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG1, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG2, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_DEBUG3, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_ERROR0, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_ERROR1, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_FRPROC0, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_FRPROC1, 0xFFFFFFFF); + isp_writel(isp->irq_merger2, IRQ_MERGER_ICR_FRPROC2, 0xFFFFFFFF); +} + +/* + * isp_enable_clocks - Enable multiple clocks + * @nclocks: Number of clocks in clock array + * @clock: Clock array + * @dev: Device + * + * Return 0 on success or a negative error code otherwise + */ +int isp_enable_clocks(struct isp_clock *clks, struct device *dev) +{ + int ret; + int i; + + for (i = 0; i < ISP_NUM_CLKS; i++) { + ret = clk_prepare_enable(clks[i].clk); + if (ret) { + dev_err(dev, "clock enable failed: %d\n", ret); + goto error; + } + } + + return 0; + +error: + for (i--; i >= 0; i--) + clk_disable_unprepare(clks[i].clk); + + return ret; +} + +/* + * isp_disable_clocks - Disable multiple clocks + * @nclocks: Number of clocks in clock array + * @clock: Clock array + */ +void isp_disable_clocks(struct isp_clock *clks) +{ + int i; + + for (i = ISP_NUM_CLKS - 1; i >= 0; i--) + clk_disable_unprepare(clks[i].clk); +} + +/* + * isp_find_sensor - Find a linked media entity which represents a sensor + * @entity: Media entity to start searching from + * + * Return a pointer to sensor media entity or NULL if not found + */ +static struct media_entity *isp_find_sensor(struct media_entity *entity) +{ + struct media_pad *pad; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + pad = media_entity_remote_pad(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return NULL; + + entity = pad->entity; + + if (entity->function == MEDIA_ENT_F_CAM_SENSOR) + return entity; + } +} + +/* + * isp_get_pixel_clock - Get pixel clock rate from sensor + * @entity: Media entity in the current pipeline + * @pixel_clock: Received pixel clock value + * + * Return 0 on success or a negative error code otherwise + */ +int isp_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) +{ + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_ctrl *ctrl; + + sensor = isp_find_sensor(entity); + if (!sensor) + return -ENODEV; + + subdev = media_entity_to_v4l2_subdev(sensor); + + ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE); + + if (!ctrl) + return -EINVAL; + + *pixel_clock = v4l2_ctrl_g_ctrl_int64(ctrl); + + return 0; +} + +/* + * isp_of_parse_endpoint_node - Parse port endpoint node + * @dev: Device + * @node: Device node to be parsed + * @csd: Parsed data from port endpoint node + * + * Return 0 on success or a negative error code on failure + */ +static int isp_of_parse_endpoint_node(struct device *dev, + struct device_node *node, + struct isp_async_subdev *csd) +{ + struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2; + struct v4l2_fwnode_endpoint vep = { { 0 } }; + unsigned int i; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + + csd->interface.csiphy_id = vep.base.port; + + mipi_csi2 = &vep.bus.mipi_csi2; + lncfg->clk.pos = mipi_csi2->clock_lane; + lncfg->clk.pol = mipi_csi2->lane_polarities[0]; + lncfg->num_data = mipi_csi2->num_data_lanes; + + lncfg->data = devm_kcalloc(dev, + lncfg->num_data, sizeof(*lncfg->data), + GFP_KERNEL); + if (!lncfg->data) + return -ENOMEM; + + for (i = 0; i < lncfg->num_data; i++) { + lncfg->data[i].pos = mipi_csi2->data_lanes[i]; + lncfg->data[i].pol = mipi_csi2->lane_polarities[i + 1]; + } + + return 0; +} + +/* + * isp_of_parse_ports - Parse ports node + * @dev: Device + * @notifier: v4l2_device notifier data + * + * Return number of "port" nodes found in "ports" node + */ +static int isp_of_parse_ports(struct isp *isp) +{ + struct device *dev = isp->dev; + struct device_node *node = NULL; + struct device_node *remote = NULL; + int ret, num_subdevs = 0; + + for_each_endpoint_of_node(dev->of_node, node) { + struct isp_async_subdev *csd; + struct v4l2_async_subdev *asd; + + if (!of_device_is_available(node)) + continue; + + remote = of_graph_get_remote_port_parent(node); + if (!remote) { + dev_err(dev, "Cannot get remote parent\n"); + ret = -EINVAL; + goto err_cleanup; + } + + asd = v4l2_async_notifier_add_fwnode_subdev( + &isp->notifier, of_fwnode_handle(remote), + sizeof(*csd)); + if (IS_ERR(asd)) { + ret = PTR_ERR(asd); + of_node_put(remote); + goto err_cleanup; + } + + csd = container_of(asd, struct isp_async_subdev, asd); + + ret = isp_of_parse_endpoint_node(dev, node, csd); + if (ret < 0) + goto err_cleanup; + + num_subdevs++; + } + + return num_subdevs; + +err_cleanup: + v4l2_async_notifier_cleanup(&isp->notifier); + of_node_put(node); + return ret; +} + +/* + * isp_init_subdevices - Initialize subdev structures and resources + * + * Return 0 on success or a negative error code on failure + */ +static int isp_init_subdevices(struct isp *isp) +{ + unsigned int i; + int ret; + + for (i = 0; i < isp->csiphy_num; i++) { + ret = isp_csiphy_subdev_init(isp, &isp->csiphy[i], + &csiphy_res[i], i); + if (ret < 0) { + dev_err(isp->dev, + "Failed to init csiphy%d sub-device: %d\n", + i, ret); + return ret; + } + } + + ret = isp_sr_subdev_init(isp, isp->sr, sr_res); + if (ret < 0) { + dev_err(isp->dev, "Failed to init SR sub-device: %d\n", + ret); + return ret; + } + + ret = isp_cvdr_subdev_init(isp, isp->cvdr, cvdr_res); + if (ret < 0) { + dev_err(isp->dev, "Failed to init CVDR sub-device: %d\n", + ret); + return ret; + } + + return 0; +} + +/* + * isp_register_entities - Register subdev nodes and create links + * + * Return 0 on success or a negative error code on failure + */ +static int isp_register_entities(struct isp *isp) +{ + int i; + int ret; + + for (i = 0; i < isp->csiphy_num; i++) { + ret = isp_csiphy_register_entity(&isp->csiphy[i], + &isp->v4l2_dev); + if (ret < 0) { + dev_err(isp->dev, + "Failed to register csiphy%d entity: %d\n", + i, ret); + goto err_reg_csiphy; + } + } + + ret = isp_sr_register_entity(isp->sr, &isp->v4l2_dev); + if (ret < 0) { + dev_err(isp->dev, "Failed to register sr entity: %d\n", + ret); + goto err_reg_sr; + } + + ret = isp_cvdr_register_entity(isp->cvdr, &isp->v4l2_dev); + if (ret < 0) { + dev_err(isp->dev, "Failed to register cvdr entity: %d\n", + ret); + goto err_reg_sr; + } + + for (i = 0; i < isp->csiphy_num; i++) { + ret = media_create_pad_link( + &isp->csiphy[i].subdev.entity, + ISP_CSIPHY_PAD_SRC, + &isp->sr->subdev.entity, + ISP_SR_PAD_SINK, + 0); + if (ret < 0) { + dev_err(isp->dev, + "Failed to link %s->%s entities: %d\n", + isp->csiphy[i].subdev.entity.name, + isp->sr->subdev.entity.name, + ret); + goto err_reg_sr; + } + } + + ret = media_create_pad_link(&isp->sr->subdev.entity, + ISP_SR_PAD_SRC, + &isp->cvdr->subdev.entity, + ISP_CVDR_PAD_SINK, + 0); + if (ret < 0) { + dev_err(isp->dev, "Failed to link %s->%s entities: %d\n", + isp->sr->subdev.entity.name, + isp->cvdr->subdev.entity.name, + ret); + goto err_reg_sr; + } + + return 0; + +err_reg_sr: + isp_sr_unregister_entity(isp->sr); + + i = isp->csiphy_num; +err_reg_csiphy: + for (i--; i >= 0; i--) + isp_csiphy_unregister_entity(&isp->csiphy[i]); + + return ret; +} + +static void isp_unregister_entities(struct isp *isp) +{ + unsigned int i; + + for (i = 0; i < isp->csiphy_num; i++) + isp_csiphy_unregister_entity(&isp->csiphy[i]); + isp_sr_unregister_entity(isp->sr); + isp_cvdr_unregister_entity(isp->cvdr); +} + +static int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct isp *isp = container_of(async, struct isp, notifier); + struct isp_async_subdev *csd = + container_of(asd, struct isp_async_subdev, asd); + u8 id = csd->interface.csiphy_id; + struct csiphy_device *csiphy = &isp->csiphy[id]; + + csiphy->cfg.csi2 = &csd->interface.csi2; + subdev->host_priv = csiphy; + + return 0; +} + +static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) +{ + struct isp *isp = container_of(async, struct isp, notifier); + struct v4l2_device *v4l2_dev = &isp->v4l2_dev; + struct v4l2_subdev *sd; + int ret; + + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (sd->host_priv) { + struct media_entity *sensor = &sd->entity; + struct csiphy_device *csiphy = + (struct csiphy_device *) sd->host_priv; + struct media_entity *input = &csiphy->subdev.entity; + unsigned int i; + + for (i = 0; i < sensor->num_pads; i++) { + if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE) + break; + } + if (i == sensor->num_pads) { + dev_err(isp->dev, + "No source pad in external entity\n"); + return -EINVAL; + } + + ret = media_create_pad_link(sensor, i, + input, ISP_CSIPHY_PAD_SINK, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + dev_err(isp->dev, + "Failed to link %s->%s entities: %d\n", + sensor->name, input->name, ret); + return ret; + } + } + } + + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + if (ret < 0) + return ret; + + return media_device_register(&isp->media_dev); +} + +static const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { + .bound = isp_subdev_notifier_bound, + .complete = isp_subdev_notifier_complete, +}; + +static const struct media_device_ops isp_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* + * isp_probe - Probe ISP platform device + * @pdev: Pointer to ISP platform device + * + * Return 0 on success or a negative error code on failure + */ +static int isp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct isp *isp; + struct resource *r; + int num_subdevs, ret, i; + + isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL); + if (!isp) + return -ENOMEM; + + isp->dev = dev; + platform_set_drvdata(pdev, isp); + + isp->csiphy_num = 2; + isp->csiphy = devm_kcalloc(dev, isp->csiphy_num, + sizeof(*isp->csiphy), GFP_KERNEL); + if (!isp->csiphy) + return -ENOMEM; + + isp->sr = devm_kzalloc(dev, sizeof(*isp->sr), GFP_KERNEL); + if (!isp->sr) + return -ENOMEM; + + isp->cvdr = devm_kzalloc(dev, sizeof(*isp->cvdr), GFP_KERNEL); + if (!isp->cvdr) + return -ENOMEM; + + v4l2_async_notifier_init(&isp->notifier); + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, isp_res[0].reg); + isp->ispss_ctrl = devm_ioremap_resource(dev, r); + if (IS_ERR(isp->ispss_ctrl)) { + dev_err(dev, "could not map ispss_ctrl memory\n"); + ret = PTR_ERR(isp->ispss_ctrl); + goto err_cleanup; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, isp_res[1].reg); + isp->smmu_ctrl = devm_ioremap_resource(dev, r); + if (IS_ERR(isp->smmu_ctrl)) { + dev_err(dev, "could not map smmu_ctrl memory\n"); + ret = PTR_ERR(isp->smmu_ctrl); + goto err_cleanup; + } + + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, isp_res[2].reg); + isp->irq_merger2 = devm_ioremap_resource(dev, r); + if (IS_ERR(isp->irq_merger2)) { + dev_err(dev, "could not map irq_merger2 memory\n"); + ret = PTR_ERR(isp->irq_merger2); + goto err_cleanup; + } + + isp->clks[0].name = "isp_snclk0"; + isp->clks[0].freq = ISP_CLK_FREQ; + isp->clks[1].name = "isp_snclk1"; + isp->clks[1].freq = ISP_CLK_FREQ; + + for (i = 0; i < ISP_NUM_CLKS; i++) { + isp->clks[i].clk = devm_clk_get(&pdev->dev, isp->clks[i].name); + if (IS_ERR(isp->clks[i].clk)) { + dev_err(isp->dev, "could not get clock: %s\n", + isp->clks[i].name); + ret = -EINVAL; + goto err_cleanup; + } + + clk_set_rate(isp->clks[i].clk, isp->clks[i].freq); + } + + ret = of_property_read_u32(dev->of_node, "pool-size", &isp->pool_size); + if (ret < 0) + goto err_cleanup; + + isp->virt_addr = dma_alloc_coherent(dev, isp->pool_size, + &(isp->hw_addr), GFP_KERNEL); + if (!isp->virt_addr) { + pr_err("dma_alloc_coherent (ISP) alloc err!\n"); + ret = -ENOMEM; + goto err_cleanup; + } + + memset(isp->virt_addr, 0, isp->pool_size); + + num_subdevs = isp_of_parse_ports(isp); + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cleanup; + } + + ret = isp_init_subdevices(isp); + if (ret < 0) + goto err_cleanup; + + isp->media_dev.dev = isp->dev; + strscpy(isp->media_dev.model, "HiSilicon ISP", + sizeof(isp->media_dev.model)); + isp->media_dev.ops = &isp_media_ops; + media_device_init(&isp->media_dev); + + /* FIXME: Get these from userspace */ + isp->frame_size = 0x3F4800; + isp->frame_count = 4; + isp->frame_num = 0; + isp->v4l2_dev.mdev = &isp->media_dev; + ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); + if (ret < 0) { + dev_err(dev, "Failed to register V4L2 device: %d\n", ret); + goto err_cleanup; + } + + ret = isp_register_entities(isp); + if (ret < 0) + goto err_register_entities; + + if (num_subdevs) { + isp->notifier.ops = &isp_subdev_notifier_ops; + + ret = v4l2_async_notifier_register(&isp->v4l2_dev, + &isp->notifier); + if (ret) { + dev_err(dev, + "Failed to register async subdev nodes: %d\n", + ret); + goto err_register_subdevs; + } + } else { + ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); + if (ret < 0) { + dev_err(dev, "Failed to register subdev nodes: %d\n", + ret); + goto err_register_subdevs; + } + + ret = media_device_register(&isp->media_dev); + if (ret < 0) { + dev_err(dev, "Failed to register media device: %d\n", + ret); + goto err_register_subdevs; + } + } + + return 0; + +err_register_subdevs: + isp_unregister_entities(isp); +err_register_entities: + v4l2_device_unregister(&isp->v4l2_dev); +err_cleanup: + v4l2_async_notifier_cleanup(&isp->notifier); + + return ret; +} + +void isp_delete(struct isp *isp) +{ + v4l2_device_unregister(&isp->v4l2_dev); + media_device_unregister(&isp->media_dev); + media_device_cleanup(&isp->media_dev); +} + +static int isp_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id isp_dt_match[] = { + { .compatible = "hisilicon,hisi-isp" }, + { } +}; + +MODULE_DEVICE_TABLE(of, isp_dt_match); + +static struct platform_driver hisi_isp_driver = { + .probe = isp_probe, + .remove = isp_remove, + .driver = { + .name = "hisi-isp", + .of_match_table = isp_dt_match, + }, +}; + +module_platform_driver(hisi_isp_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/hisi/isp/isp.h b/drivers/media/platform/hisi/isp/isp.h new file mode 100644 index 000000000000..dc9aa590d615 --- /dev/null +++ b/drivers/media/platform/hisi/isp/isp.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Linaro Ltd. + */ + +#ifndef HISI_ISP_H +#define HISI_ISP_H + +#include <linux/regulator/consumer.h> + +#include "isp-csiphy.h" +#include "isp-sr.h" +#include "isp-cvdr.h" + +#define MASK0(name) ((1<<(name##_LEN))-1) +#define MASK1(name) (((1<<(name##_LEN))-1) << (name##_OFFSET)) + +/* operation on the field of a variable */ +#define isp_readl_field(reg, name) \ + (((reg) >> (name##_OFFSET)) & MASK0(name)) + +#define isp_writel_field(reg, name, val) \ + (reg = ((reg) & ~MASK1(name)) | \ + (((val) & MASK0(name)) << (name##_OFFSET))) + +#define ISP_NUM_CLKS 2 + +enum IRQ_MERGER_TYPE { + IRQ_MERGER_DEBUG_0 = 0x0, + IRQ_MERGER_DEBUG_1 = 0x20, + IRQ_MERGER_DEBUG_2 = 0x40, + IRQ_MERGER_DEBUG_3 = 0x60, + IRQ_MERGER_DEBUG_4 = 0x80, + IRQ_MERGER_ERROR_0 = 0xA0, + IRQ_MERGER_ERROR_1 = 0xC0, + IRQ_MERGER_FRPROC_0 = 0xE0, + IRQ_MERGER_FRPROC_1 = 0x100, + IRQ_MERGER_FRPROC_2 = 0x120, + IRQ_MERGER_COMBINED = 0x140, + IRQ_MERGER_IRQ_MAX = 0xFF, +}; + +enum IRQ_MERGER_FUNC_TYPE { + IRQ_MERGER_FUNC_IMS = 0x0, + IRQ_MERGER_FUNC_RIS = 0x4, + IRQ_MERGER_FUNC_MIS = 0x8, + IRQ_MERGER_FUNC_ICR = 0xC, + IRQ_MERGER_FUNC_ISR = 0x10, + IRQ_MERGER_FUNC_NO = 0x1F, +}; + +#define IRQ_MERGER_IMSC_DEBUG0 \ + (IRQ_MERGER_DEBUG_0) +#define IRQ_MERGER_IMSC_DEBUG1 \ + (IRQ_MERGER_DEBUG_1) +#define IRQ_MERGER_IMSC_DEBUG2 \ + (IRQ_MERGER_DEBUG_2) +#define IRQ_MERGER_IMSC_DEBUG3 \ + (IRQ_MERGER_DEBUG_3) +#define IRQ_MERGER_IMSC_ERROR0 \ + (IRQ_MERGER_ERROR_0) +#define IRQ_MERGER_IMSC_ERROR1 \ + (IRQ_MERGER_ERROR_1) +#define IRQ_MERGER_IMSC_FRPROC0 \ + (IRQ_MERGER_FRPROC_0) +#define IRQ_MERGER_IMSC_FRPROC1 \ + (IRQ_MERGER_FRPROC_1) +#define IRQ_MERGER_IMSC_FRPROC2 \ + (IRQ_MERGER_FRPROC_2) + +#define IRQ_MERGER_ICR_DEBUG0 \ + (IRQ_MERGER_DEBUG_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG1 \ + (IRQ_MERGER_DEBUG_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG2 \ + (IRQ_MERGER_DEBUG_2 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG3 \ + (IRQ_MERGER_DEBUG_3 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_ERROR0 \ + (IRQ_MERGER_ERROR_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_ERROR1 \ + (IRQ_MERGER_ERROR_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC0 \ + (IRQ_MERGER_FRPROC_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC1 \ + (IRQ_MERGER_FRPROC_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC2 \ + (IRQ_MERGER_FRPROC_2 + IRQ_MERGER_FUNC_ICR) + +#define IRQ_MERGER_SR_4_CVDR_RT_SOF_VPWR_23_OFFSET 17 +#define IRQ_MERGER_SR_4_CVDR_RT_EOF_VPWR_23_OFFSET 21 + +#define YUV_DT_420_8BITS 0x18 +#define YUV_DT_420_10BITS 0x19 +#define YUV_DT_LEGACY_420_8BITS 0x1A +#define YUV_DT_420_8BITS_C 0x1C +#define YUV_DT_420_10BITS_C 0x1D +#define YUV_DT_422_8BITS 0x1E +#define YUV_DT_422_10BITS 0x1F + +#define RAW_DT_RAW6 0x28 +#define RAW_DT_RAW7 0x29 +#define RAW_DT_RAW8 0x2A +#define RAW_DT_RAW10 0x2B +#define RAW_DT_RAW12 0x2C +#define RAW_DT_RAW14 0x2D + +struct resources { + char *reg; +}; + +struct isp_clock { + struct clk *clk; + const char *name; + u32 freq; +}; + +struct isp { + void __iomem *ispss_ctrl; + void __iomem *smmu_ctrl; + void __iomem *irq_merger2; + struct v4l2_device v4l2_dev; + struct v4l2_async_notifier notifier; + struct media_device media_dev; + struct device *dev; + struct csiphy_device *csiphy; + struct sr_device *sr; + struct cvdr_device *cvdr; + struct isp_clock clks[ISP_NUM_CLKS]; + void *virt_addr; + dma_addr_t hw_addr; + int csiphy_num; + unsigned int pool_size; + unsigned int frame_size; + unsigned int frame_count; + unsigned int frame_num; +}; + +struct isp_camera_interface { + u8 csiphy_id; + struct csiphy_csi2_cfg csi2; +}; + +struct isp_async_subdev { + struct v4l2_async_subdev asd; /* must be first */ + struct isp_camera_interface interface; +}; + +void isp_writel(void __iomem *base , u32 reg, u32 value); +u32 isp_readl(void __iomem *base , u32 reg); +int isp_enable_clocks(struct isp_clock *clks, struct device *dev); +void isp_disable_clocks(struct isp_clock *clks); +void isp_ispss_clear_irq_state(struct isp *isp); +void isp_ispss_enable_irq(struct isp *isp); +u32 isp_clear_irq(struct isp *isp, enum IRQ_MERGER_TYPE irq); +int frame_num2Offset(struct isp *isp, int frame_num); +void isp_config_smmu_bypass(struct isp *isp); + +#endif /* HISI_ISP_H */ |