diff --git a/arch/arm64/configs/hikey970_defconfig b/arch/arm64/configs/hikey970_defconfig index 3c9cb186c6be..e5485ee26749 100755 --- a/arch/arm64/configs/hikey970_defconfig +++ b/arch/arm64/configs/hikey970_defconfig @@ -244,7 +244,7 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_DMA_CMA=y -# CONFIG_CMA_SIZE_MBYTES is not set +CONFIG_CMA_SIZE_MBYTES=64 CONFIG_MTD=y CONFIG_MTD_M25P80=y CONFIG_MTD_SPI_NOR=y @@ -442,9 +442,9 @@ CONFIG_REGULATOR_QCOM_SMD_RPM=y CONFIG_REGULATOR_QCOM_SPMI=y CONFIG_REGULATOR_S2MPS11=y CONFIG_MEDIA_SUPPORT=y -# CONFIG_MEDIA_CAMERA_SUPPORT is not set -# CONFIG_V4L_PLATFORM_DRIVERS is not set -# CONFIG_VIDEO_HISI_ISP is not set +CONFIG_MEDIA_CAMERA_SUPPORT=y +CONFIG_V4L_PLATFORM_DRIVERS=y +CONFIG_VIDEO_HISI_ISP=y CONFIG_DRM=y CONFIG_DRM_NOUVEAU=m CONFIG_DRM_TEGRA=m diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 5ff803efdc03..02bc006c6a9a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -33,6 +33,8 @@ source "drivers/media/platform/omap/Kconfig" source "drivers/media/platform/blackfin/Kconfig" +source "drivers/media/platform/hisilicon/Kconfig" + config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 40b18d12726e..9b30937bcf04 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -66,3 +66,5 @@ ccflags-y += -I$(srctree)/drivers/media/i2c obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/ obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/ + +obj-$(CONFIG_VIDEO_HISI_ISP) += hisilicon/ diff --git a/drivers/media/platform/hisilicon/Kconfig b/drivers/media/platform/hisilicon/Kconfig new file mode 100644 index 000000000000..8d8b60a6c3a0 --- /dev/null +++ b/drivers/media/platform/hisilicon/Kconfig @@ -0,0 +1,5 @@ +# +# hisilicon isp device configuration. +# Please keep this list sorted alphabetically + +source "drivers/media/platform/hisilicon/isp/Kconfig" \ No newline at end of file diff --git a/drivers/media/platform/hisilicon/Makefile b/drivers/media/platform/hisilicon/Makefile new file mode 100644 index 000000000000..2028ffa213b1 --- /dev/null +++ b/drivers/media/platform/hisilicon/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for hisilicon isp drivers. +# Please keep this list sorted alphabetically + +obj-$(CONFIG_VIDEO_HISI_ISP) += isp/ diff --git a/drivers/media/platform/hisilicon/isp/Kconfig b/drivers/media/platform/hisilicon/isp/Kconfig new file mode 100644 index 000000000000..4043f246beea --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/Kconfig @@ -0,0 +1,6 @@ +config VIDEO_HISI_ISP + tristate "ISP Support for Hisilicon Kirin Platform" + depends on V4L_PLATFORM_DRIVERS + default n + help + ISP Support for Hisilicon Kirin Platform diff --git a/drivers/media/platform/hisilicon/isp/Makefile b/drivers/media/platform/hisilicon/isp/Makefile new file mode 100644 index 000000000000..a900e3cd78c2 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/Makefile @@ -0,0 +1,20 @@ +# Makefile for hisi ISP driver + +hisi_isp-objs := hisi_isp_core.o \ + hisi_isp_common.o \ + hisi_isp_csi.o \ + hisi_isp_sr.o \ + hisi_isp_cvdr.o \ + hisi_isp_isr.o \ + hisi_isp_i2c.o + +hisi_ov5645-objs := ov5645.o + +hisi_ov5640_main-objs := ov5640_main.o + +hisi_ov5640_sub-objs := ov5640_sub.o + +obj-$(CONFIG_VIDEO_HISI_ISP) += hisi_isp.o +obj-$(CONFIG_VEDIO_HISI_ISP) += hisi_ov5645.o +obj-$(CONFIG_VIDEO_HISI_ISP) += hisi_ov5640_main.o +obj-$(CONFIG_VIDEO_HISI_ISP) += hisi_ov5640_sub.o diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_common.c b/drivers/media/platform/hisilicon/isp/hisi_isp_common.c new file mode 100644 index 000000000000..62ec404d0945 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_common.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_common.h" +#include "hisi_isp_cvdr.h" +#include "hisi_isp_sr.h" +#include "hisi_isp_csi.h" +#include "hisi_isp_isr.h" + +int frame_num2Offset(struct isp_device *dev, int frame_num) +{ + return (dev->frame_size * (frame_num % dev->frame_count)); +} + +void isp_config_smmu_bypass(struct isp_device *dev) +{ + REG_SET(dev->base + ISP_BASE_ADDR_SMMU, 0x1); +} + +int isp_ispss_reset_all(struct isp_device *dev) +{ + char __iomem *base = dev->base; + + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x060, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x064, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x068, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x06C, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x070, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x074, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x078, 0xFFFFFFFF); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x374, 0x00000003); + mdelay(1); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x060, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x064, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x068, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x06C, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x070, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x074, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x078, 0x00000000); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x374, 0x00000000); + mdelay(1); + return 0; +} +EXPORT_SYMBOL(isp_ispss_reset_all); + +void isp_ispss_enable_clock(struct isp_device *dev) +{ + char __iomem *base = dev->base; + /* enable all clock of isp sub-modules */ + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x010, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x014, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x018, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x01C, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x020, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x024, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x028, 0xffffffff); + REG_SET(base + ISP_BASE_ADDR_ISPSS_CTRL + 0x364, 0x00000003); + mdelay(1); +} +EXPORT_SYMBOL(isp_ispss_enable_clock); + +int isp_start_streaming(struct isp_device *dev, int index) +{ + int ret; + struct isp_i2c_client_t *i2c_client = NULL; + + dev->addr = dev->sensor[index].addr; + dev->flag = dev->sensor[index].flag; + dev->speed_cfg = dev->sensor[index].speed_cfg; + dev->csi_index = dev->sensor[index].csi_index; + dev->dt = dev->sensor[index].dt; + dev->pf = dev->sensor[index].pf; + dev->csi_lane = dev->sensor[index].csi_lane; + dev->pwdn_gpio = dev->sensor[index].pwdn_gpio; + dev->rst_gpio = dev->sensor[index].rst_gpio; + + isp_ispss_enable_clock(dev); + + mdelay(100); + + isp_ispss_reset_all(dev); + + mdelay(100); + + dev->client = create_isp_i2c_client(dev); + if (!dev->client) + return -ENODEV; + + i2c_client = dev->client; + ret = i2c_client->ops->open(i2c_client); + if (ret) { + destroy_isp_i2c_client(i2c_client); + return -EBUSY; + } + + isp_csi_enable(dev, dev->csi_lane, 0); + + ret = dev->sensor[index].camera_sensor_power(i2c_client, 1); + ret += dev->sensor[index].camera_sensor_set_fmt(i2c_client, + dev->w, dev->h); + ret += dev->sensor[index].camera_stream_on_off(i2c_client, 1); + if (ret) { + dev->sensor[index].camera_sensor_power(i2c_client, 0); + destroy_isp_i2c_client(i2c_client); + return -EBUSY; + } + + isp_clear_irq_state(dev); + + enable_irq(dev->isp_irq_frproc0); + enable_irq(dev->isp_irq_vic1); + + isp_enable_irq(dev); + + isp_cvdr_init(dev); + isp_cvdr_config(dev, dev->hw_addr); + + isp_config_smmu_bypass(dev); + + isp_sr_config(dev); + isp_sr_go(dev, (1 << (4*dev->csi_index))); + + return 0; +} + +int isp_stop_streaming(struct isp_device *dev, int index) +{ + struct isp_i2c_client_t *i2c_client = dev->client; + + if (!i2c_client) + return -ENODEV; + + disable_irq(dev->isp_irq_frproc0); + disable_irq(dev->isp_irq_vic1); + + dev->sensor[index].camera_stream_on_off(i2c_client, 0); + dev->sensor[index].camera_sensor_power(i2c_client, 0); + i2c_client->ops->close(i2c_client); + destroy_isp_i2c_client(i2c_client); + + return 0; +} diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_common.h b/drivers/media/platform/hisilicon/isp/hisi_isp_common.h new file mode 100644 index 000000000000..efa38479c3b8 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_common.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_COMMON_DEF_H +#define HISI_ISP_COMMON_DEF_H + +#include "hisi_isp_core.h" +#include "hisi_isp_i2c.h" + +enum yuv_data_type_e { + YUV_DT_420_8BITS = 0x18, + YUV_DT_420_10BITS = 0x19, + YUV_DT_LEGACY_420_8BITS = 0x1A, + YUV_DT_420_8BITS_C = 0x1C, + YUV_DT_420_10BITS_C = 0x1D, + YUV_DT_422_8BITS = 0x1E, + YUV_DT_422_10BITS = 0x1F, +}; + +enum raw_data_type_e { + RAW_DT_RAW6 = 0x28, + RAW_DT_RAW7 = 0x29, + RAW_DT_RAW8 = 0x2A, + RAW_DT_RAW10 = 0x2B, + RAW_DT_RAW12 = 0x2C, + RAW_DT_RAW14 = 0x2D, +}; + +int frame_num2Offset(struct isp_device *dev, int frame_num); +int isp_ispss_reset_all(struct isp_device *dev); +void isp_ispss_enable_clock(struct isp_device *dev); +int isp_start_streaming(struct isp_device *dev, int index); +int isp_stop_streaming(struct isp_device *dev, int index); + +#endif /* HISI_ISP_COMMON_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_core.c b/drivers/media/platform/hisilicon/isp/hisi_isp_core.c new file mode 100644 index 000000000000..a651387144ca --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_core.c @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_core.h" +#include "hisi_isp_isr.h" +#include "hisi_isp_common.h" +#include +#include +#include + +#define DOVDD_VOLTAGE_DIGITAL_IO 1800000 + +static int isp_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pfn; + struct miscdevice *miscdev = file->private_data; + struct isp_device *dev = container_of(miscdev, struct isp_device, dev); + + ISP_ERR("enter\n"); + if (vma->vm_end - vma->vm_start > dev->pool_size) { + ISP_ERR("over pool size\n"); + return -EINVAL; + } + + pfn = __phys_to_pfn(dev->hw_addr); + + return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int isp_stream_on(struct isp_device *dev, struct stream_info *info) +{ + int total; + + if (info->cam_id >= 2) + return -EINVAL; + + if (dev->sensor[info->cam_id].probe == false) { + ISP_ERR("cam_id:%d is not support", info->cam_id); + return -EINVAL; + } + + dev->w = info->w; + dev->h = info->h; + dev->frame_size = PAGE_ALIGN(info->w * info->h * 2); + total = dev->pool_size / dev->frame_size; + if (total < 3) { + dev->streaming = false; + ISP_ERR("pool is too small or frame size is too large"); + return -EINVAL; + } + + dev->frame_count = total; + dev->isp_frame_num = 0; + dev->hal_frame_num = 0; + dev->streaming = true; + dev->cur_frame_num = 0; + + isp_start_streaming(dev, info->cam_id); + + info->pool_size = dev->pool_size; + + return 0; +} + +static void +isp_flush_frame_range(struct isp_device *dev, struct frame_info *info) +{ + int page_num = 0; + int num = 0; + struct page *p; + + page_num = dev->frame_size / PAGE_SIZE; + if (dev->frame_size % PAGE_SIZE > 0) + page_num++; + + for (num = 0; num < page_num; num++) { + p = phys_to_page(dev->hw_addr + info->offset + PAGE_SIZE * num); + flush_dcache_page(p); + } +} + +static int isp_frame_request(struct isp_device *dev, struct frame_info *info) +{ + + ISP_ERR("frame request enter %d\n", info->frame_num); + + if (info->frame_num != 0 && info->frame_num != dev->hal_frame_num + 1) + return -EINVAL; + + if (info->frame_num >= dev->isp_frame_num) { + ISP_ERR("wait for frame complete+++\n"); + wait_for_completion(&dev->frame_comp); + ISP_ERR("frame complete---\n"); + } + + info->offset = frame_num2Offset(dev, info->frame_num); + + isp_flush_frame_range(dev, info); + + dev->hal_frame_num = info->frame_num; + ISP_ERR("frame request exit %d\n", info->frame_num); + + return 0; +} + +static int isp_stream_off(struct isp_device *dev, struct stream_info *info) +{ + if (info->cam_id >= 2) + return -EINVAL; + + if (dev->sensor[info->cam_id].probe == false) + return -EINVAL; + + isp_stop_streaming(dev, info->cam_id); + + dev->streaming = false; + + return 0; +} + +static long isp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct miscdevice *miscdev = filp->private_data; + struct isp_device *dev = container_of(miscdev, struct isp_device, dev); + int ret; + struct stream_info stream; + struct frame_info frame; + + if (!dev) + return -ENODEV; + + switch (cmd) { + case ISP_IOC_STREAM_ON: + { + if (dev->streaming == true) + return -EINVAL; + + if (copy_from_user(&stream, (void __user *)arg, + sizeof(stream))) { + return -EFAULT; + } + ret = isp_stream_on(dev, &stream); + if (ret) + return -EFAULT; + if (copy_to_user((void __user *)arg, + &stream, _IOC_SIZE(cmd))) { + ISP_ERR("copy to user failed!\n"); + return -EFAULT; + } + break; + } + case ISP_IOC_FRAME_REQUEST: + { + if (dev->streaming == false) + return -EINVAL; + + if (copy_from_user(&frame, (void __user *)arg, + sizeof(frame))) { + return -EFAULT; + } + + ret = isp_frame_request(dev, &frame); + if (ret) + return -EFAULT; + + if (copy_to_user((void __user *)arg, + &frame, _IOC_SIZE(cmd))) { + ISP_ERR("copy to user failed!\n"); + return -EFAULT; + } + + break; + } + case ISP_IOC_STREAM_OFF: + { + if (dev->streaming == false) + return -EINVAL; + + if (copy_from_user(&stream, (void __user *)arg, + sizeof(stream))) { + return -EFAULT; + } + ret = isp_stream_off(dev, &stream); + if (ret) + return -EFAULT; + if (copy_to_user((void __user *)arg, + &stream, _IOC_SIZE(cmd))) { + ISP_ERR("copy to user failed!\n"); + return -EFAULT; + } + break; + } + default: + { + ISP_ERR("unknown cmd!\n"); + return -ENOTTY; + } + } + + return 0; +} + +static int isp_release(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct isp_device *dev = container_of(miscdev, struct isp_device, dev); + + dev->streaming = false; + + return 0; +} + +static int isp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations isp_fops = { + .owner = THIS_MODULE, + .open = isp_open, + .release = isp_release, + .unlocked_ioctl = isp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = NULL, +#endif + .mmap = isp_mmap, +}; + +static int isp_getdts(struct platform_device *pdev, struct isp_device *dev) +{ + struct device *device = &pdev->dev; + struct device_node *np = device->of_node; + + if (!np) + return -ENODEV; + + dev->isp_irq_vic1 = irq_of_parse_and_map(np, 0); + if (!dev->isp_irq_vic1) + return -ENXIO; + + dev->isp_irq_frproc0 = irq_of_parse_and_map(np, 1); + if (!dev->isp_irq_frproc0) + return -ENXIO; + + of_property_read_u32(np, "clock-rates", + &dev->xclk_freq); + + of_property_read_u32(np, "pool-size", + &dev->pool_size); + + ISP_INFO("clock rate %d, pool size 0x%x", + dev->xclk_freq, dev->pool_size); + + return 0; +} + +static int isp_probe(struct platform_device *pdev) +{ + int ret; + struct isp_device *idev; + struct device *device = &pdev->dev; + struct device_node *np = device->of_node; + + ISP_INFO("enter!\n"); + + idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL); + if (!idev) + return -ENOMEM; + + if (isp_getdts(pdev, idev)) { + ISP_ERR("failed to dts info.\n"); + return -EINVAL; + } + + idev->base = of_iomap(np, 0); + if (!idev->base) { + ISP_ERR("failed to get base resource.\n"); + return -ENXIO; + } + + ret = devm_request_irq(&pdev->dev, idev->isp_irq_vic1, + isp_vic1_handler, 0, "isp-vic1", idev); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, idev->isp_irq_frproc0, + isp_frproc0_handler, 0, "isp-frproc0", idev); + if (ret) + return ret; + + disable_irq(idev->isp_irq_frproc0); + disable_irq(idev->isp_irq_vic1); + + idev->io_regulator = devm_regulator_get(&pdev->dev, "DOVDD"); + if (IS_ERR(idev->io_regulator)) { + idev->io_regulator = NULL; + return -EINVAL; + } + regulator_set_voltage(idev->io_regulator, + DOVDD_VOLTAGE_DIGITAL_IO, + DOVDD_VOLTAGE_DIGITAL_IO); + + idev->xclk = devm_clk_get(&pdev->dev, "clk_gate_isp_snclk0"); + if (IS_ERR(idev->xclk)) { + ISP_ERR("could not get xclk\n"); + return -EINVAL; + } + clk_set_rate(idev->xclk, idev->xclk_freq); + + idev->xclk1 = devm_clk_get(&pdev->dev, "clk_gate_isp_snclk1"); + if (IS_ERR(idev->xclk1)) { + ISP_ERR("could not get xclk1\n"); + return -EINVAL; + } + clk_set_rate(idev->xclk1, idev->xclk_freq); + + idev->virt_addr = dma_alloc_coherent(device, + idev->pool_size, &(idev->hw_addr), GFP_KERNEL); + if (!idev->virt_addr) { + ISP_ERR("dma_alloc_coherent (ISP) alloc err!\n"); + return -ENOMEM; + } + + memset(idev->virt_addr, 0, idev->pool_size); + + idev->dev.minor = MISC_DYNAMIC_MINOR; + idev->dev.name = "isp"; + idev->dev.fops = &isp_fops; + idev->dev.parent = NULL; + ret = misc_register(&idev->dev); + if (ret) { + dma_free_coherent(device, + idev->pool_size, idev->virt_addr, idev->hw_addr); + return ret; + } + + init_completion(&idev->frame_comp); + + platform_set_drvdata(pdev, idev); + + ISP_INFO("exit!\n"); + + return 0; +} + +static int isp_remove(struct platform_device *pdev) +{ + struct device *device = &pdev->dev; + struct isp_device *idev = platform_get_drvdata(pdev); + + if (!idev) { + pr_err("%s: get drvdata failed\n", __func__); + return -EINVAL; + } + ISP_INFO("enter!\n"); + + dma_free_coherent(device, + idev->pool_size, idev->virt_addr, idev->hw_addr); + + misc_deregister(&idev->dev); + + ISP_INFO("exit!\n"); + return 0; +} + +static const struct of_device_id hisi_isp_match_table[] = { + { + .compatible = COMP_HISI_ISP_NAME, + .data = NULL, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, hisi_isp_match_table); + +static struct platform_driver isp_driver = { + .probe = isp_probe, + .remove = isp_remove, + .driver = { + .name = DEV_NAME_ISP, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(hisi_isp_match_table), + }, +}; + +static int __init isp_init(void) +{ + int ret = 0; + + ISP_INFO("enter!\n"); + + ret = platform_driver_register(&isp_driver); + if (ret) + return ret; + + ISP_INFO("exit!\n"); + + return ret; +} + +static void __exit isp_exit(void) +{ + ISP_INFO("enter!\n"); + + platform_driver_unregister(&isp_driver); + + ISP_INFO("exit!\n"); +} + +module_init(isp_init); +module_exit(isp_exit); + +MODULE_DESCRIPTION("Hisilicon Isp Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_core.h b/drivers/media/platform/hisilicon/isp/hisi_isp_core.h new file mode 100644 index 000000000000..0b1b2ce1d667 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_core.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_CORE_DEF_H +#define HISI_ISP_CORE_DEF_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_isp_reg_offset.h" +#include "hisi_isp_i2c.h" + +#define COMP_HISI_ISP_NAME "hisilicon,hisi-isp" +#define DEV_NAME_ISP "hisi_isp" + +#define USED 0x01 +#define UNUSED 0x00 + +/* #define HISP_DEBUG */ + +#if defined(HISP_DEBUG) +#define ISP_ERR(msg, ...) \ + pr_err("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_WARNING(msg, ...) \ + pr_warn("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_INFO(msg, ...) \ + pr_info("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_DEBUG(msg, ...) \ + pr_debug("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#else +#define ISP_ERR(msg, ...) \ + pr_err("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_WARNING(msg, ...) \ + pr_warn("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_INFO(msg, ...) \ + pr_info("[ISP] %s: "msg, __func__, ## __VA_ARGS__) +#define ISP_DEBUG(msg, ...) +#endif + +#define REG_SET(addr, val) writel(val, addr) +#define REG_GET(addr) readl(addr) + +#define MASK0(name) ((1<<(name##_LEN))-1) +#define MASK1(name) (((1<<(name##_LEN))-1) << (name##_OFFSET)) + +/* operation on the field of a variable */ +#define REG_GET_FIELD(reg, name) \ + (((reg) >> (name##_OFFSET)) & MASK0(name)) + +#define REG_SET_FIELD(reg, name, val) \ + (reg = ((reg) & ~MASK1(name)) | \ + (((val) & MASK0(name)) << (name##_OFFSET))) + +struct stream_info { + int cam_id; + int w; + int h; + int format; + int pool_size; +}; + +struct frame_info { + int cam_id; + int frame_num; + int offset; +}; + +#define ISP_IOC_MAGIC 'I' + +#define ISP_IOC_STREAM_ON _IOWR(ISP_IOC_MAGIC, 0, struct stream_info) + +#define ISP_IOC_FRAME_REQUEST _IOWR(ISP_IOC_MAGIC, 1, struct frame_info) + +#define ISP_IOC_STREAM_OFF _IOWR(ISP_IOC_MAGIC, 2, struct stream_info) + +struct isp_device { + struct miscdevice dev; + char __iomem *base; + char __iomem *base_gpio; + uint32_t isp_irq_vic1; + uint32_t isp_irq_frproc0; + struct clk *xclk; + struct clk *xclk1; + int xclk_freq; + struct regulator *io_regulator; + + void *virt_addr; + dma_addr_t hw_addr; + unsigned int pool_size; + size_t frame_size; + size_t frame_count; + size_t isp_frame_num; + size_t hal_frame_num; + size_t cur_frame_num; + struct completion frame_comp; + bool streaming; + + unsigned short addr; + unsigned int flag; + unsigned int speed_cfg; + + int csi_index; + int dt; + int pf; + int w; + int h; + + int csi_lane; + int pwdn_gpio; + int rst_gpio; + struct isp_i2c_client_t *client; + struct sensor_info { + unsigned short addr; + unsigned int flag; + unsigned int speed_cfg; + + int csi_index; + int dt; + int pf; + int csi_lane; + + int pwdn_gpio; + int rst_gpio; + + int (*camera_sensor_power)(struct isp_i2c_client_t *, int); + int (*camera_sensor_set_fmt)(struct isp_i2c_client_t *, + unsigned int, unsigned int); + int (*camera_stream_on_off)(struct isp_i2c_client_t *, int); + bool probe; + } sensor[2]; +}; +#endif /* HISI_ISP_CORE_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_csi.c b/drivers/media/platform/hisilicon/isp/hisi_isp_csi.c new file mode 100644 index 000000000000..6415fef67a92 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_csi.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_csi.h" +#include "hisi_isp_csi2if_reg_offset.h" + +#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) + +#define TSTCODE_SETREG8(reg_base, addr, value) \ + do { \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL1_REG),\ + (1 << 16) | addr); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 2); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 0); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL1_REG), value); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 2); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 0); \ + } while (0) + +#define TSTCODE_GETREG8(reg_base, addr, value)\ + do { \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL1_REG),\ + (1 << 16) | addr); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 2); \ + REG_SET((reg_base+CSI2IF_PHY_TEST_CTRL0_REG), 0); \ + (value) = \ + ((REG_GET(reg_base+CSI2IF_PHY_TEST_CTRL1_REG) >> 8) &\ + (0x000000ff)); \ + } while (0) + +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); + ISP_DEBUG("### LANE0_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE1_SETTLE, value); + ISP_DEBUG("### LANE1_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE2_SETTLE, value); + ISP_DEBUG("### LANE2_SETTLE = %d", value); + + TSTCODE_GETREG8(base, LANE3_SETTLE, value); + ISP_DEBUG("### LANE3_SETTLE = %d", value); + + TSTCODE_GETREG8(base, CFG_CLK_DETECT, value); + ISP_DEBUG("### CFG_CLK_DETECT = 0x%x", value); + + TSTCODE_GETREG8(base, CFG_CLK_ATTR, value); + ISP_DEBUG("### CFG_CLK_ATTR = 0x%x", value); + + return 0; +} + +int csi2if_enable(char __iomem *base, + unsigned char num_lanes, + unsigned char settle_time) +{ + unsigned int phy_rx; + unsigned int phy_state; + char __iomem *base_addr = base; + + if (num_lanes > 4 || 0 == num_lanes) { + ISP_ERR("number of lanes %d out of range!!\n", num_lanes); + return -1; + } + + /* de-assert the shutdown signal*/ + REG_SET(base_addr + CSI2IF_PHY_SHUTDOWNZ_REG, 0); + REG_SET(base_addr + CSI2IF_DPHY_RSTZ_REG, 0); + REG_SET(base_addr + CSI2IF_CSI2_RESETN_REG, 0); + + REG_SET(base_addr + CSI2IF_PHY_TEST_CTRL0_REG, 1); + REG_SET(base_addr + CSI2IF_PHY_TEST_CTRL0_REG, 0); + mdelay(1); + + REG_SET(base_addr + CSI2IF_PHY_SHUTDOWNZ_REG, 1); + REG_SET(base_addr + CSI2IF_N_LANES_REG, (num_lanes-1)); + + REG_SET(base_addr + CSI2IF_DPHY_RSTZ_REG, 1); + REG_SET(base_addr + CSI2IF_CSI2_RESETN_REG, 1); + + /* Configure HUAWEI D-PHY */ + if (csi2if_dphy_init(base_addr, settle_time) < 0) + return -1; + + /* confirm the D-PHY is in right state */ + phy_rx = REG_GET(base_addr + CSI2IF_PHY_RX_REG); + phy_state = REG_GET(base_addr + CSI2IF_PHY_STOPSTATE_REG); + + if ((REG_GET_FIELD(phy_state, CSI2IF_PHY_STOPSTATEDATA_0) == 0) && + (REG_GET_FIELD(phy_state, CSI2IF_PHY_STOPSTATECLK) == 0)) { + + ISP_INFO("### not all data and clock lanes in stop state!\n"); + ISP_INFO("phy_rx = 0x%x, phy_state = %x\n", phy_rx, phy_state); + } + + if (REG_GET_FIELD(phy_state, CSI2IF_PHY_RXCLKACTIVEHS) == 0) + ISP_INFO("### D-PHY is not receiving a clock!\n"); + + mdelay(1); + + phy_rx = REG_GET(base_addr + CSI2IF_PHY_RX_REG); + phy_state = REG_GET(base_addr + CSI2IF_PHY_STOPSTATE_REG); + ISP_INFO("### D-PHY state: phy_rx = 0x%x, phy_state = 0x%x\n", + phy_rx, phy_state); + + return 0; +} + +int csi2if_disable(char __iomem *base) +{ + REG_SET(base + CSI2IF_CSI2_RESETN_REG, 0); + REG_SET(base + CSI2IF_PHY_SHUTDOWNZ_REG, 0); + + return 0; +} + +int isp_csi_get_reg_offset(int csi_index) +{ + switch (csi_index) { + case 0: + return ISP_BASE_ADDR_CSI2IF_A; + case 1: + return ISP_BASE_ADDR_CSI2IF_B; + case 2: + return ISP_BASE_ADDR_CSI2IF_C; + default: + ISP_ERR("csi_index=%d error\n", csi_index); + return -1; + } +} + +int isp_csi_enable(struct isp_device *dev, + unsigned char num_lanes, unsigned char settle_time) +{ + csi2if_enable(dev->base + isp_csi_get_reg_offset(dev->csi_index), + num_lanes, settle_time); + return 0; +} + +int isp_csi_disable(struct isp_device *dev) +{ + csi2if_disable(dev->base + isp_csi_get_reg_offset(dev->csi_index)); + return 0; +} diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_csi.h b/drivers/media/platform/hisilicon/isp/hisi_isp_csi.h new file mode 100644 index 000000000000..3f6ea13ed77f --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_csi.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_CSI_DEF_H +#define HISI_ISP_CSI_DEF_H + +#include "hisi_isp_core.h" + +int isp_csi_enable(struct isp_device *dev, + unsigned char num_lanes, unsigned char settle_time); +int isp_csi_disable(struct isp_device *dev); + +#endif /* HISI_ISP_CSI_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_csi2if_reg_offset.h b/drivers/media/platform/hisilicon/isp/hisi_isp_csi2if_reg_offset.h new file mode 100644 index 000000000000..8507ebd42bfd --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_csi2if_reg_offset.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __CSI2IF_REG_OFFSET_H__ +#define __CSI2IF_REG_OFFSET_H__ + +#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 + +#endif diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.c b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.c new file mode 100644 index 000000000000..51539908a398 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_cvdr.h" +#include "hisi_isp_cvdr_reg_offset.h" +#include "hisi_isp_cvdr_priv.h" + +#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) + +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, +}; + +static 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}, +}; + +static 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}, +}; + +#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 + +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; + char __iomem *reg_addr; + + if (desc->fs_addr & 0xF) { + ISP_ERR("failed\n"); + return -1; + } + + tmp_cfg.u32 = 0; + tmp_fs.u32 = 0; + tmp_line.u32 = 0; + tmp_limiter.u32 = 0; + + if (!bw) { + ISP_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: + ISP_ERR("not support yet\n"); + break; + + default: + reg_addr = base + CVDR_CVDR_LIMITER_VP_WR_0_REG + + ONE_REG_OFFSET * port; + REG_SET(reg_addr, tmp_limiter.u32); + reg_addr = base + CVDR_CVDR_VP_WR_CFG_0_REG + + VP_WR_REG_OFFSET * port; + REG_SET(reg_addr, tmp_cfg.u32); + reg_addr = base + CVDR_CVDR_VP_WR_AXI_LINE_0_REG + + VP_WR_REG_OFFSET * port; + REG_SET(reg_addr, tmp_line.u32); + reg_addr = base + CVDR_CVDR_VP_WR_AXI_FS_0_REG + + VP_WR_REG_OFFSET * port; + REG_SET(reg_addr, tmp_fs.u32); + break; + } + + return 0; +} + +int isp_cvdr_config(struct isp_device *dev, dma_addr_t hw_addr) +{ + int ret; + + struct cvdr_wr_fmt_desc_t cvdr_wr_fmt = { + .fs_addr = hw_addr, + .pix_fmt = dev->pf, + .pix_expan = 0, + .last_page = (dev->w * dev->h * 2 + hw_addr) >> 15, + .line_stride = dev->w*2/CVDR_ALIGN_BYTES - 1, + .line_wrap = 8000, + }; + struct cvdr_bw_cfg_t cvdr_bw = {0xF, 0xF, 0xF, 0xF, 0xF}; + + ret = cvdr_set_vp_wr_ready(dev->base + ISP_BASE_ADDR_CVDR_RT, + W2_4, &cvdr_wr_fmt, &cvdr_bw); + return ret; +} + +int isp_cvdr_init(struct isp_device *dev) +{ + unsigned int i; + unsigned int prefetch_bypass; + char __iomem *base = dev->base; + char __iomem *reg_addr; + unsigned int reg_val; + + prefetch_bypass = 1; + + for (i = 0; i < CVDR_VP_WR_NBR; ++i) { + reg_addr = ((cvdr_vp_wr_bw[i].srt == CVDR_SRT) ? + (base + ISP_BASE_ADDR_CVDR_SRT) : + (base + ISP_BASE_ADDR_CVDR_RT)) + + CVDR_SRT_VP_WR_IF_CFG_0_REG + 0x10 * i; + reg_val = REG_GET(reg_addr); + reg_val = (reg_val & 0x7FFFFFFF) | (prefetch_bypass << 31); + REG_SET(reg_addr, reg_val); + } + + for (i = 0; i < CVDR_VP_RD_NBR; ++i) { + reg_addr = ((cvdr_vp_rd_bw[i].srt == CVDR_SRT) ? + (base + ISP_BASE_ADDR_CVDR_SRT) : + (base + ISP_BASE_ADDR_CVDR_RT)) + + CVDR_SRT_VP_RD_IF_CFG_0_REG + 0x20 * i; + reg_val = REG_GET(reg_addr); + reg_val = (reg_val & 0x7FFFFFFF) | (prefetch_bypass << 31); + REG_SET(reg_addr, reg_val); + } + + /* CVDR RT*/ + REG_SET(base + ISP_BASE_ADDR_CVDR_RT + CVDR_RT_CVDR_CFG_REG, + (1 << 0) + | (64 << 8) + | (11 << 16) + | (11 << 24) + ); + + /* CVDR SRT*/ + REG_SET(base + ISP_BASE_ADDR_CVDR_SRT + CVDR_SRT_CVDR_CFG_REG, + (1 << 0) + | (34 << 8) + | (19 << 16) + | (11 << 24) + ); + + REG_SET(base + 0x22000 + 0x00C, 0xf8765432); + REG_SET(base + 0x22000 + 0x010, 0xf8122334); + + REG_SET(base + 0x2E000 + 0x00C, 0xd0765432); + REG_SET(base + 0x2E000 + 0x010, 0xd0122334); + + REG_SET(base + SUB_CTRL_BASE_ADDR + 0x190, 0x00026e10); + REG_SET(base + SUB_CTRL_BASE_ADDR + 0x194, 0x0000021f); + + REG_SET(base + SUB_CTRL_BASE_ADDR + 0x198, 0x00027210); + REG_SET(base + SUB_CTRL_BASE_ADDR + 0x19C, 0x0000024e); + + return 0; +} diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.h b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.h new file mode 100644 index 000000000000..c9039faf4dc6 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_CVDR_DEF_H +#define HISI_ISP_CVDR_DEF_H + +#include "hisi_isp_core.h" + +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; +}; + +int isp_cvdr_config(struct isp_device *dev, dma_addr_t hw_addr); +int isp_cvdr_init(struct isp_device *dev); + +#endif /* HISI_ISP_CVDR_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_priv.h b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_priv.h new file mode 100644 index 000000000000..3d7abbf46dba --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_priv.h @@ -0,0 +1,583 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __CVDR_DRV_PRIV_H__ +#define __CVDR_DRV_PRIV_H__ + +/* 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 */ +}; + +#endif /* __CVDR_DRV_PRIV_H__ */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_reg_offset.h b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_reg_offset.h new file mode 100644 index 000000000000..ed978a55aadb --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_cvdr_reg_offset.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __CVDR_REG_OFFSET_H__ +#define __CVDR_REG_OFFSET_H__ + +#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 + +#endif /* __CVDR_REG_OFFSET_H__ */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.c b/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.c new file mode 100644 index 000000000000..a15cee64e44e --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.c @@ -0,0 +1,762 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_i2c.h" +#include "hisi_isp_common.h" + +/* Registers offset */ +#define I2C_CON 0x0 +#define I2C_TAR 0x4 +#define I2C_DATA_CMD 0x10 +#define I2C_SS_SCL_HCNT 0x14 +#define I2C_SS_SCL_LCNT 0x18 +#define I2C_FS_SCL_HCNT 0x1c +#define I2C_FS_SCL_LCNT 0x20 +#define I2C_HS_SCL_HCNT 0x24 +#define I2C_HS_SCL_LCNT 0x28 +#define I2C_SDA_HOLD 0x7C +#define I2C_FS_SPKLEN 0xA0 +#define I2C_HS_SPKLEN 0xA4 + +#define I2C_INTR_STAT 0x2c +#define I2C_INTR_MASK 0x30 +#define I2C_RAW_INTR_STAT 0x34 +#define I2C_RX_TL 0x38 +#define I2C_TX_TL 0x3c +#define I2C_CLR_INTR 0x40 +#define I2C_CLR_RX_UNDER 0x44 +#define I2C_CLR_RX_OVER 0x48 +#define I2C_CLR_TX_OVER 0x4c +#define I2C_CLR_RD_REQ 0x50 +#define I2C_CLR_TX_ABRT 0x54 +#define I2C_CLR_RX_DONE 0x58 +#define I2C_CLR_ACTIVITY 0x5c +#define I2C_CLR_STOP_DET 0x60 +#define I2C_CLR_START_DET 0x64 +#define I2C_CLR_GEN_CALL 0x68 +#define I2C_ENABLE 0x6c +#define I2C_STATUS 0x70 +#define I2C_TXFLR 0x74 +#define I2C_RXFLR 0x78 +#define I2C_TX_ABRT_SOURCE 0x80 +#define I2C_ENABLE_STATUS 0x9c +#define I2C_COMP_TYPE 0xfc +#define I2C_COMP_TYPE_VALUE 0x44570140 + +#define I2C_INTR_RX_UNDER 0x001 +#define I2C_INTR_RX_OVER 0x002 +#define I2C_INTR_RX_FULL 0x004 +#define I2C_INTR_TX_OVER 0x008 +#define I2C_INTR_TX_EMPTY 0x010 +#define I2C_INTR_RD_REQ 0x020 +#define I2C_INTR_TX_ABRT 0x040 +#define I2C_INTR_RX_DONE 0x080 +#define I2C_INTR_ACTIVITY 0x100 +#define I2C_INTR_STOP_DET 0x200 +#define I2C_INTR_START_DET 0x400 +#define I2C_INTR_GEN_CALL 0x800 + +#define I2C_INTR_DEFAULT_MASK (I2C_INTR_RX_FULL | \ + I2C_INTR_TX_EMPTY | \ + I2C_INTR_TX_ABRT | \ + I2C_INTR_STOP_DET) + +#define I2C_STATUS_ACTIVITY 0x1 +#define I2C_ERR_TX_ABRT 0x1 +#define I2C_IC_EN 0x1 + +#define I2C_M_RD 0x01 + +#define I2C_CON_MASTER 0x1 +#define I2C_CON_SPEED_STD 0x2 +#define I2C_CON_SPEED_FAST 0x4 +#define I2C_CON_SPEED_HIGH 0x6 +#define I2C_CON_10BITADDR_MASTER 0x10 +#define I2C_CON_RESTART_EN 0x20 +#define I2C_CON_SLAVE_DISABLE 0x40 + +#define I2C_COMP_PARAM_1 0xf4 + +/* status codes */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +#define ISPSS_CTRL_MODULE_RESET_TOP_REG 0x60 + +static unsigned int isp_i2c_reg_read(struct isp_i2c_dev_t *dev, int offset) +{ + unsigned int value; + + value = readl(dev->base + offset); + return value; +} + +static void isp_i2c_reg_write(struct isp_i2c_dev_t *dev, + int offset, unsigned int b) +{ + writel(b, dev->base + offset); +} + +static int isp_i2c_wait_bus_not_busy(struct isp_i2c_dev_t *dev) +{ + unsigned int timeout = 20;/* ms*/ + + while (isp_i2c_reg_read(dev, I2C_STATUS) & I2C_STATUS_ACTIVITY) { + if (timeout <= 0) { + ISP_ERR("timeout waiting for bus ready\n"); + return -1; + } + timeout--; + mdelay(1); + } + return 0; +} + +static unsigned int isp_i2c_read_clear_intrbits(struct isp_i2c_dev_t *dev) +{ + unsigned int stat; + + stat = isp_i2c_reg_read(dev, I2C_INTR_STAT); + + if (stat & I2C_INTR_RX_UNDER) + isp_i2c_reg_read(dev, I2C_CLR_RX_UNDER); + if (stat & I2C_INTR_RX_OVER) + isp_i2c_reg_read(dev, I2C_CLR_RX_OVER); + if (stat & I2C_INTR_TX_OVER) + isp_i2c_reg_read(dev, I2C_CLR_TX_OVER); + if (stat & I2C_INTR_RD_REQ) + isp_i2c_reg_read(dev, I2C_CLR_RD_REQ); + if (stat & I2C_INTR_TX_ABRT) { + ISP_ERR("ABRT_SOURCE 0x%x\n", + isp_i2c_reg_read(dev, I2C_TX_ABRT_SOURCE)); + isp_i2c_reg_read(dev, I2C_TX_ABRT_SOURCE); + isp_i2c_reg_read(dev, I2C_CLR_TX_ABRT); + } + if (stat & I2C_INTR_RX_DONE) + isp_i2c_reg_read(dev, I2C_CLR_RX_DONE); + if (stat & I2C_INTR_ACTIVITY) + isp_i2c_reg_read(dev, I2C_CLR_ACTIVITY); + if (stat & I2C_INTR_STOP_DET) + isp_i2c_reg_read(dev, I2C_CLR_STOP_DET); + if (stat & I2C_INTR_START_DET) + isp_i2c_reg_read(dev, I2C_CLR_START_DET); + if (stat & I2C_INTR_GEN_CALL) + isp_i2c_reg_read(dev, I2C_CLR_GEN_CALL); + + return stat; +} + +static void isp_i2c_read(struct isp_i2c_dev_t *dev) +{ + struct isp_i2c_msg_t *msgs = dev->msgs; + int rx_valid; + + for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) { + unsigned int len; + unsigned char *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + rx_valid = isp_i2c_reg_read(dev, I2C_RXFLR); + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) { + *buf++ = isp_i2c_reg_read(dev, I2C_DATA_CMD); + dev->rx_outstanding--; + } + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +static void isp_i2c_xfer_msg_loop(struct isp_i2c_dev_t *dev, + struct isp_i2c_msg_t *msgs, + int tx_limit, int rx_limit) +{ + while (dev->tx_buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + unsigned int cmd = 0; + + if (dev->msg_write_idx == dev->msgs_num - 1 && + dev->tx_buf_len == 1) + cmd |= (0x1 << 9); + + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + /* avoid rx buffer overrun */ + if (rx_limit - dev->rx_outstanding <= 0) + break; + + isp_i2c_reg_write(dev, I2C_DATA_CMD, cmd | 0x100); + rx_limit--; + dev->rx_outstanding++; + } else { + isp_i2c_reg_write(dev, + I2C_DATA_CMD, cmd | *(dev->tx_buf)++); + } + + tx_limit--; + dev->tx_buf_len--; + } +} + +static int isp_i2c_xfer_msg(struct isp_i2c_dev_t *dev) +{ + int tx_limit; + int rx_limit; + unsigned int intr_mask; + + struct isp_i2c_msg_t *msgs = dev->msgs; + unsigned int addr = msgs[dev->msg_write_idx].addr; + + intr_mask = I2C_INTR_DEFAULT_MASK; + + for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { + if (msgs[dev->msg_write_idx].addr != addr) { + ISP_ERR(" invalid target address\n"); + dev->msg_err = -1; + break; + } + + if (msgs[dev->msg_write_idx].len == 0) { + ISP_ERR(" invalid message length\n"); + dev->msg_err = -1; + break; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new isp_i2c_msg_t */ + dev->tx_buf = msgs[dev->msg_write_idx].buf; + dev->tx_buf_len = msgs[dev->msg_write_idx].len; + } + + tx_limit = dev->tx_fifo_depth + - isp_i2c_reg_read(dev, I2C_TXFLR); + rx_limit = dev->rx_fifo_depth + - isp_i2c_reg_read(dev, I2C_RXFLR); + + isp_i2c_xfer_msg_loop(dev, msgs, tx_limit, rx_limit); + + if (dev->tx_buf_len > 0) { + /* more bytes to be written */ + dev->status |= STATUS_WRITE_IN_PROGRESS; + break; + } + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + } + + if (dev->msg_write_idx == dev->msgs_num) + intr_mask &= ~I2C_INTR_TX_EMPTY; + if (dev->msg_err) + intr_mask = 0; + + isp_i2c_reg_write(dev, I2C_INTR_MASK, intr_mask); + + return 0; +} + +int isp_i2c_polling(struct isp_i2c_dev_t *dev) +{ + int stat; + + stat = isp_i2c_read_clear_intrbits(dev); + + if (stat & I2C_INTR_TX_ABRT) { + dev->cmd_err |= I2C_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + isp_i2c_reg_write(dev, I2C_INTR_MASK, 0); + goto i2c_isr_err1; + } + + if (stat & I2C_INTR_RX_FULL) + isp_i2c_read(dev); + + if (stat & I2C_INTR_TX_EMPTY) + isp_i2c_xfer_msg(dev); + +i2c_isr_err1: + if ((stat & (I2C_INTR_TX_ABRT | I2C_INTR_STOP_DET)) || dev->msg_err) + return 0; + + return -1; +} + +static void isp_i2c_enable(struct isp_i2c_dev_t *dev) +{ + int timeout = 100; + + do { + isp_i2c_reg_write(dev, I2C_ENABLE, 1); + if ((isp_i2c_reg_read(dev, I2C_ENABLE_STATUS) & 1) == 1) + return; + + mdelay(1); + } while (timeout--); + + ISP_WARNING("i2c dev enable timeout\n"); +} + +static void isp_i2c_disable(struct isp_i2c_dev_t *dev) +{ + int timeout = 100; + + do { + isp_i2c_reg_write(dev, I2C_ENABLE, 0); + if ((isp_i2c_reg_read(dev, I2C_ENABLE_STATUS) & 1) == 0) + return; + + mdelay(1); + } while (timeout--); + + ISP_WARNING("i2c dev disable timeout\n"); +} + +static void isp_i2c_clear_int(struct isp_i2c_dev_t *dev) +{ + isp_i2c_reg_read(dev, I2C_CLR_INTR); +} + +static unsigned int isp_i2c_scl_hcnt(unsigned int ic_clk, unsigned int tSYMBOL, + unsigned int tf, int cond, int offset) +{ + if (cond) + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static unsigned int isp_i2c_scl_lcnt(unsigned int ic_clk, unsigned int tLOW, + unsigned int tf, int offset) +{ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + +static void isp_i2c_xfer_init(struct isp_i2c_dev_t *dev, + enum isp_i2c_speed_e speed_cfg) +{ + struct isp_i2c_msg_t *msgs = dev->msgs; + unsigned int retry_num = 100; + unsigned int ic_con = 0; + unsigned int hcnt; + unsigned int lcnt; + + isp_i2c_reg_write(dev, I2C_ENABLE, 0); + + while ((--retry_num != 0) && + (isp_i2c_reg_read(dev, I2C_ENABLE_STATUS) & I2C_IC_EN)) + mdelay(1); + + if (retry_num <= 0) + ISP_ERR(" disable i2c controller error.\n"); + + /* set the slave (target) address */ + isp_i2c_reg_write(dev, I2C_TAR, msgs[dev->msg_write_idx].addr); + + /* configure the i2c master speed */ + if (speed_cfg > dev->speed_cfg) + ISP_ERR("i2c drv error: %d is beyond speed_cfg\n", speed_cfg); + + switch (speed_cfg) { + case I2C_SPEED_STD: + ic_con = I2C_CON_SPEED_STD; + break; + case I2C_SPEED_FAST: + ic_con = I2C_CON_SPEED_FAST; + hcnt = isp_i2c_scl_hcnt(dev->ic_clk, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: default, 1: Ideal */ + 0); /* No offset */ + lcnt = isp_i2c_scl_lcnt(dev->ic_clk, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + isp_i2c_reg_write(dev, I2C_FS_SCL_HCNT, hcnt); + isp_i2c_reg_write(dev, I2C_FS_SCL_LCNT, lcnt); + break; + case I2C_SPEED_FAST_PLUS: + ic_con = I2C_CON_SPEED_FAST; + hcnt = isp_i2c_scl_hcnt(dev->ic_clk, + 2, /* tHD;STA = tHIGH = 0.26 us */ + 1, /* tf = 0.12 us */ + 0, /* 0: default, 1: Ideal */ + 0); /* No offset */ + lcnt = isp_i2c_scl_lcnt(dev->ic_clk, + 5, /* tLOW = 0.5 us */ + 1, /* tf = 0.12 us */ + 0); /* No offset */ + isp_i2c_reg_write(dev, I2C_FS_SCL_HCNT, hcnt); + isp_i2c_reg_write(dev, I2C_FS_SCL_LCNT, lcnt); + break; + case I2C_SPEED_HIGH: + ic_con = I2C_CON_SPEED_HIGH; + break; + } + + isp_i2c_reg_write(dev, I2C_CON, dev->master_cfg | ic_con); + /* if the slave address is ten bit address, enable 10BITADDR */ + ic_con = isp_i2c_reg_read(dev, I2C_CON); + if (msgs[dev->msg_write_idx].flags & I2C_ADDR_10BIT) + ic_con |= I2C_CON_10BITADDR_MASTER; + else + ic_con &= ~I2C_CON_10BITADDR_MASTER; + + isp_i2c_reg_write(dev, I2C_CON, ic_con); + + isp_i2c_enable(dev); + isp_i2c_clear_int(dev); + /* Enable interrupts */ + isp_i2c_reg_write(dev, I2C_INTR_MASK, I2C_INTR_DEFAULT_MASK); +} + +int isp_i2c_xfer(struct isp_i2c_dev_t *dev, + struct isp_i2c_msg_t msgs[], + int num, int burst_flag, + enum isp_i2c_speed_e speed_cfg) +{ + int totallen; + int i; + int ret; + unsigned int timeout = 100; + + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + + ret = isp_i2c_wait_bus_not_busy(dev); + if (ret < 0) + return -EBUSY; + /* start the transfers */ + isp_i2c_xfer_init(dev, speed_cfg); + + /* calculate total length */ + totallen = 0; + for (i = 0; i < num; i++) + totallen += msgs[i].len; + + /* wait for tx to complete */ + while (true) { + if (timeout <= 0) { + ISP_ERR("i2c timeout\n"); + return -EIO; + } + + if (isp_i2c_polling(dev) == 0) + break; + + mdelay(1); + timeout--; + } + + if (dev->msg_err) { + ISP_ERR("I2C msg_err\n"); + return -EIO; + } + + if (dev->cmd_err == I2C_ERR_TX_ABRT) { + ISP_ERR("I2C abort\n"); + return -EIO; + } + + isp_i2c_disable(dev); + + return 0; +} + +int isp_i2c_read_reg(struct isp_i2c_client_t *client, + unsigned short reg, + unsigned char *val) +{ + struct isp_i2c_msg_t msg[2]; + int ret; + unsigned char addr[2]; + unsigned char data; + + if ((client == NULL) || (client->i2c_dev == NULL)) { + ISP_ERR("device is null\n"); + return -1; + } + + addr[0] = (reg>>8) & 0xff; + addr[1] = reg & 0xff; + + msg[0].addr = client->addr; + msg[0].len = 2; + msg[0].buf = addr; + msg[0].flags = client->flag & I2C_ADDR_10BIT; + + msg[1].addr = client->addr; + msg[1].len = 1; + msg[1].buf = &data; + msg[1].flags = client->flag & I2C_ADDR_10BIT; + msg[1].flags |= I2C_M_RD; + + ISP_ERR("read[0x%x]\n", reg); + + ret = isp_i2c_xfer(client->i2c_dev, msg, 2, 0, client->speed_cfg); + if (ret != 0) { + ISP_ERR("read reg fail"); + return ret; + } + + ISP_ERR("read[0x%x] = 0x%x\n", reg, data); + + *val = data; + + return ret; +} + +int isp_i2c_write_reg(struct isp_i2c_client_t *client, + unsigned short reg, + unsigned char val) +{ + struct isp_i2c_msg_t msg[1]; + int ret; + unsigned char data[3]; + + if ((client == NULL) || (client->i2c_dev == NULL)) { + ISP_ERR("device is null\n"); + return -1; + } + + data[0] = (reg>>8) & 0xff; + data[1] = reg & 0xff; + data[2] = val; + + msg[0].addr = client->addr; + msg[0].len = 3; + msg[0].buf = data; + msg[0].flags = client->flag & I2C_ADDR_10BIT; + + ret = isp_i2c_xfer(client->i2c_dev, msg, 1, 0, client->speed_cfg); + if (ret != 0) { + ISP_ERR("write reg fail\n"); + return ret; + } + + return 0; +} + +static void isp_i2c_ispss_reset(struct isp_i2c_dev_t *dev) +{ + int offset; + + switch (dev->base - dev->isp_base) { + case ISP_BASE_ADDR_I2C_1: + offset = 11; + break; + case ISP_BASE_ADDR_I2C_2: + offset = 10; + break; + case ISP_BASE_ADDR_I2C_3: + offset = 12; + break; + default: + ISP_ERR("invalid i2c device\n"); + return; + } + + REG_SET(dev->isp_base + ISP_BASE_ADDR_ISPSS_CTRL + + ISPSS_CTRL_MODULE_RESET_TOP_REG, 1 << offset); + mdelay(1); + REG_SET(dev->isp_base + ISP_BASE_ADDR_ISPSS_CTRL + + ISPSS_CTRL_MODULE_RESET_TOP_REG, 0x00000000); + mdelay(1); +} + +static int isp_i2c_init(struct isp_i2c_dev_t *dev) +{ + unsigned int hcnt; + unsigned int lcnt; + unsigned int sda_delay_count; + + /* I2C internal clock */ + dev->ic_clk = 480000; + + /* workaround: reset for a poweron sequence */ + isp_i2c_ispss_reset(dev); + + isp_i2c_disable(dev); + + /* Standard-mode */ + hcnt = isp_i2c_scl_hcnt(dev->ic_clk, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: default, 1: Ideal */ + 0); /* No offset */ + + lcnt = isp_i2c_scl_lcnt(dev->ic_clk, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + + isp_i2c_reg_write(dev, I2C_SS_SCL_HCNT, hcnt); + isp_i2c_reg_write(dev, I2C_SS_SCL_LCNT, lcnt); + + /* Fast-mode */ + hcnt = isp_i2c_scl_hcnt(dev->ic_clk, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: default, 1: Ideal */ + 0); /* No offset */ + + lcnt = isp_i2c_scl_lcnt(dev->ic_clk, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + + isp_i2c_reg_write(dev, I2C_FS_SCL_HCNT, hcnt); + isp_i2c_reg_write(dev, I2C_FS_SCL_LCNT, lcnt); + + /* High-mode */ + hcnt = isp_i2c_scl_hcnt(dev->ic_clk, + 1, /* tHD;STA = tHIGH = 0.1 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: default, 1: Ideal */ + 0); /* No offset */ + + lcnt = isp_i2c_scl_lcnt(dev->ic_clk, + 2, /* tLOW = 0.2 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + + isp_i2c_reg_write(dev, I2C_HS_SCL_HCNT, hcnt); + isp_i2c_reg_write(dev, I2C_HS_SCL_LCNT, lcnt); + + /* Spike Suppression (FS 50ns or HS 10ns) */ + isp_i2c_reg_write(dev, I2C_FS_SPKLEN, (dev->ic_clk * 50) / 1000000); + isp_i2c_reg_write(dev, I2C_HS_SPKLEN, (dev->ic_clk * 10) / 1000000); + + /* SDA HOLD TIME (300ns or 70ns)*/ + sda_delay_count = (dev->ic_clk * 300) / 1000000; + isp_i2c_reg_write(dev, I2C_SDA_HOLD, sda_delay_count); + + /* Configure Tx/Rx FIFO threshold levels */ + isp_i2c_reg_write(dev, I2C_TX_TL, dev->tx_fifo_depth / 2); + isp_i2c_reg_write(dev, I2C_RX_TL, 0); + + /* configure the i2c master */ + isp_i2c_reg_write(dev, I2C_CON, dev->master_cfg); + + return 0; +} + +static void isp_i2c_disable_int(struct isp_i2c_dev_t *dev) +{ + isp_i2c_reg_write(dev, I2C_INTR_MASK, 0); +} + +int isp_i2c_open(struct isp_i2c_client_t *client) +{ + struct isp_i2c_dev_t *i2c_dev = client->i2c_dev; + int param; + int ret; + + i2c_dev->master_cfg = I2C_CON_MASTER | + I2C_CON_SLAVE_DISABLE | + I2C_CON_RESTART_EN; + + param = isp_i2c_reg_read(i2c_dev, I2C_COMP_PARAM_1); + i2c_dev->tx_fifo_depth = ((param >> 16) & 0xff) + 1; + i2c_dev->rx_fifo_depth = ((param >> 8) & 0xff) + 1; + + ret = isp_i2c_init(i2c_dev); + if (ret != 0) { + ISP_ERR(" i2c init failed :%x\n", ret); + return -1; + } + + isp_i2c_disable_int(i2c_dev); + + i2c_dev->speed_cfg = I2C_SPEED_HIGH; + + return 0; +} + +int isp_i2c_close(struct isp_i2c_client_t *client) +{ + return 0; +} + +static struct isp_i2c_dev_ops i2c_dops = { + .open = isp_i2c_open, + .close = isp_i2c_close, + .read = isp_i2c_read_reg, + .write = isp_i2c_write_reg, +}; + +int isp_i2c_get_offset(int csi_index) +{ + switch (csi_index) { + case 0: + return ISP_BASE_ADDR_I2C_1; + case 1: + return ISP_BASE_ADDR_I2C_2; + case 2: + return ISP_BASE_ADDR_I2C_3; + default: + ISP_ERR("csi_index=%d error\n", csi_index); + return -1; + } +} + +struct isp_i2c_client_t *create_isp_i2c_client(struct isp_device *isp_dev) +{ + struct isp_i2c_client_t *client = NULL; + + client = kzalloc(sizeof(struct isp_i2c_client_t), GFP_KERNEL); + + if (client) { + client->i2c_dev = + kzalloc(sizeof(struct isp_i2c_dev_t), GFP_KERNEL); + if (client->i2c_dev) { + client->i2c_dev->base = isp_dev->base + + isp_i2c_get_offset(isp_dev->csi_index); + client->i2c_dev->isp_base = isp_dev->base; + client->csi_index = isp_dev->csi_index; + client->addr = isp_dev->addr >> 1; + client->flag = isp_dev->flag; + client->speed_cfg = isp_dev->speed_cfg; + client->ops = &i2c_dops; + client->isp_dev = isp_dev; + } else { + kfree(client); + client = NULL; + } + } + + return client; +} +EXPORT_SYMBOL(create_isp_i2c_client); + +void destroy_isp_i2c_client(struct isp_i2c_client_t *client) +{ + kfree(client->i2c_dev); + kfree(client); +} +EXPORT_SYMBOL(destroy_isp_i2c_client); diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.h b/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.h new file mode 100644 index 000000000000..8342126e2a98 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_i2c.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_I2C_H +#define HISI_ISP_I2C_H + +#include "hisi_isp_core.h" + +enum isp_i2c_addr_e { + I2C_ADDR_7BIT = 0x0, + I2C_ADDR_10BIT = 0x10, +}; + +enum isp_i2c_speed_e { + I2C_SPEED_STD = 0x1, + I2C_SPEED_FAST = 0x2, + I2C_SPEED_FAST_PLUS = 0x3, + I2C_SPEED_HIGH = 0x4, +}; + +enum state_e { + OFF, + ON, + STATE_END, +}; + +struct isp_i2c_msg_t { + unsigned short addr; + unsigned short flags; + unsigned short len; + unsigned char *buf; +}; + +struct isp_i2c_dev_t { + char __iomem *isp_base; + char __iomem *base; + enum isp_i2c_speed_e speed_cfg; + unsigned int irq; + unsigned int ic_clk; + unsigned int master_cfg; + unsigned int cmd_err; + struct isp_i2c_msg_t *msgs; + int msgs_num; + int msg_write_idx; + int msg_read_idx; + unsigned int tx_buf_len; + unsigned int rx_buf_len; + unsigned char *tx_buf; + unsigned char *rx_buf; + int msg_err; + unsigned int status; + unsigned int abort_source; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; + int rx_outstanding; +}; + +struct isp_i2c_client_t { + struct isp_device *isp_dev; + struct isp_i2c_dev_t *i2c_dev; + struct isp_i2c_dev_ops *ops; + int csi_index; + unsigned short addr; + unsigned int flag; + enum isp_i2c_speed_e speed_cfg; + int mode; +}; + +struct isp_i2c_dev_ops { + int (*open)(struct isp_i2c_client_t *client); + int (*close)(struct isp_i2c_client_t *client); + int (*read)(struct isp_i2c_client_t *client, + unsigned short reg, unsigned char *val); + int (*write)(struct isp_i2c_client_t *client, + unsigned short reg, unsigned char val); +}; + +struct isp_i2c_client_t *create_isp_i2c_client(struct isp_device *isp_dev); +void destroy_isp_i2c_client(struct isp_i2c_client_t *client); + +#endif /* HISI_ISP_I2C_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_isr.c b/drivers/media/platform/hisilicon/isp/hisi_isp_isr.c new file mode 100644 index 000000000000..ec07be9a8352 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_isr.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_isr.h" +#include "hisi_isp_common.h" +#include "hisi_isp_cvdr.h" +#include "hisi_isp_sr.h" + +static unsigned int +isp_clear_irq(struct isp_device *d, enum IRQ_MERGER_TYPE irq) +{ + unsigned int val; + + val = REG_GET(d->base + + ISP_BASE_ADDR_IRQ_MERGER2+irq+IRQ_MERGER_FUNC_RIS); + REG_SET(d->base + + ISP_BASE_ADDR_IRQ_MERGER2+irq+IRQ_MERGER_FUNC_ICR, val); + + return val; +} + +void isp_enable_irq(struct isp_device *dev) +{ + REG_SET(dev->base + IRQ_MERGER_IMSC_DEBUG1, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_IMSC_FRPROC0, 0xFFFFFFFF); +} + +void isp_clear_irq_state(struct isp_device *dev) +{ + REG_SET(dev->base + IRQ_MERGER_ICR_DEBUG0, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_DEBUG1, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_DEBUG2, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_DEBUG3, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_ERROR0, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_ERROR1, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_FRPROC0, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_FRPROC1, 0xFFFFFFFF); + REG_SET(dev->base + IRQ_MERGER_ICR_FRPROC2, 0xFFFFFFFF); +} + +irqreturn_t isp_vic1_handler(int irq, void *dev_id) +{ + struct isp_device *d = (struct isp_device *)dev_id; + + unsigned int val = isp_clear_irq(d, IRQ_MERGER_DEBUG_1); + + if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_SOF_VPWR_23_OFFSET)) { + isp_cvdr_config(d, + d->hw_addr + frame_num2Offset(d, d->isp_frame_num + 1)); + + d->cur_frame_num++; + } + return IRQ_HANDLED; +} + +irqreturn_t isp_frproc0_handler(int irq, void *dev_id) +{ + struct isp_device *d = (struct isp_device *)dev_id; + + unsigned int val = isp_clear_irq(d, IRQ_MERGER_FRPROC_0); + + if (val & (1 << IRQ_MERGER_SR_4_CVDR_RT_EOF_VPWR_23_OFFSET)) { + isp_sr_go(d, (1 << (4*d->csi_index))); + if (d->cur_frame_num == SKIP_FRAME_NUM) + memset(d->virt_addr, 0, d->pool_size); + if (d->cur_frame_num > SKIP_FRAME_NUM) { + d->isp_frame_num++; + complete_all(&d->frame_comp); + } + } + + return IRQ_HANDLED; +} diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_isr.h b/drivers/media/platform/hisilicon/isp/hisi_isp_isr.h new file mode 100644 index 000000000000..b1a0227c5bcf --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_isr.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_ISR_DEF_H +#define HISI_ISP_ISR_DEF_H + +#include "hisi_isp_core.h" + +#define SKIP_FRAME_NUM (4) +#define ISP_BASE_ADDR_IRQ_MERGER (0x00025000) +#define ISP_BASE_ADDR_IRQ_MERGER2 (0x0002C000) + +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 = 0xE0, + IRQ_MERGER_ERROR_1 = 0x100, + IRQ_MERGER_FRPROC_0 = 0x120, + IRQ_MERGER_FRPROC_1 = 0x140, + IRQ_MERGER_FRPROC_2 = 0x160, + IRQ_MERGER_COMBINED = 0x1A0, + 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 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_0) +#define IRQ_MERGER_IMSC_DEBUG1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_1) +#define IRQ_MERGER_IMSC_DEBUG2 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_2) +#define IRQ_MERGER_IMSC_DEBUG3 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_3) +#define IRQ_MERGER_IMSC_ERROR0 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_ERROR_0) +#define IRQ_MERGER_IMSC_ERROR1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_ERROR_1) +#define IRQ_MERGER_IMSC_FRPROC0 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_FRPROC_0) +#define IRQ_MERGER_IMSC_FRPROC1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_FRPROC_1) +#define IRQ_MERGER_IMSC_FRPROC2 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_FRPROC_2) + +#define IRQ_MERGER_ICR_DEBUG0 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG2 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_2 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_DEBUG3 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_DEBUG_3 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_ERROR0 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_ERROR_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_ERROR1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_ERROR_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC0 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_FRPROC_0 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC1 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + IRQ_MERGER_FRPROC_1 + IRQ_MERGER_FUNC_ICR) +#define IRQ_MERGER_ICR_FRPROC2 \ + (ISP_BASE_ADDR_IRQ_MERGER2 + 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 + +void isp_enable_irq(struct isp_device *dev); +void isp_clear_irq_state(struct isp_device *dev); + +irqreturn_t isp_vic1_handler(int irq, void *dev_id); +irqreturn_t isp_frproc0_handler(int irq, void *dev_id); + +#endif /* HISI_ISP_ISR_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_reg_offset.h b/drivers/media/platform/hisilicon/isp/hisi_isp_reg_offset.h new file mode 100644 index 000000000000..d775078b6bb4 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_reg_offset.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_REG_OFFSET_DEF_H +#define HISI_ISP_REG_OFFSET_DEF_H + +#define ISP_BASE_ADDR_CSI2IF_A (0x00002000) +#define ISP_BASE_ADDR_CSI2IF_B (0x00003000) +#define ISP_BASE_ADDR_CSI2IF_C (0x00004000) + +#define ISP_BASE_ADDR_I2C_1 (0x00000000) +#define ISP_BASE_ADDR_I2C_2 (0x00001000) +#define ISP_BASE_ADDR_I2C_3 (0x00008000) + +#define ISP_BASE_ADDR_SMMU (0x00006000) + +#define ISP_BASE_ADDR_ISPSS_CTRL (0x00020000) + +#define ISP_BASE_ADDR_STREAM_ROUTER (0x00021000) + +#define ISP_BASE_ADDR_CVDR_RT (0x00022000) +#define ISP_BASE_ADDR_CVDR_SRT (0x0002E000) + +#define SUB_CTRL_BASE_ADDR (0x00183000) + +#endif /* HISI_ISP_REG_OFFSET_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_sr.c b/drivers/media/platform/hisilicon/isp/hisi_isp_sr.c new file mode 100644 index 000000000000..6d72a8377e21 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_sr.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "hisi_isp_sr.h" +#include "hisi_isp_common.h" +#include "hisi_isp_sr_reg_offset.h" + +/* 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)) + +int isp_sr_config(struct isp_device *dev) +{ + int data_type = 1; + union U_ID_ROUTER_1 reg1; + union U_REFORMAT reg; + char __iomem *reg_addr; + + reg_addr = dev->base + ISP_BASE_ADDR_STREAM_ROUTER + + STREAM_ROUTER_CSIFILTER_A_REG + + 0x04 * dev->csi_index; + REG_SET(reg_addr, (0 << 6) | (dev->dt << 0)); + + reg_addr = dev->base + ISP_BASE_ADDR_STREAM_ROUTER + + STREAM_ROUTER_ID_ROUTER_1_REG; + reg1.u32 = REG_GET(reg_addr); + reg1.bits.idr_enable_6 = 1; + reg1.bits.idr_input_stream_6 = (dev->csi_index << 2) | (0 << 0); + REG_SET(reg_addr, reg1.u32); + + reg.u32 = 0; + reg.bits.reformat_enable = 1; + reg.bits.reformat_num_pixels = dev->w * 2 - 1; + reg.bits.reformat_num_lines = dev->h - 1; + reg.bits.reformat_pixel_reorder = data_type; + reg_addr = dev->base + ISP_BASE_ADDR_STREAM_ROUTER + + STREAM_ROUTER_REFORMAT_REG(6); + REG_SET(reg_addr, reg.u32); + + reg_addr = dev->base + ISP_BASE_ADDR_STREAM_ROUTER + + STREAM_ROUTER_REFORMAT_MINSPACE_REG(6); + REG_SET(reg_addr, (0x1F << 0)); + + return 0; +} + +void isp_sr_go(struct isp_device *dev, unsigned int go_bit) +{ + char __iomem *reg_addr; + + reg_addr = dev->base + ISP_BASE_ADDR_STREAM_ROUTER + + STREAM_ROUTER_CSIFILTER_GO_REG; + REG_SET(reg_addr, go_bit); +} diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_sr.h b/drivers/media/platform/hisilicon/isp/hisi_isp_sr.h new file mode 100644 index 000000000000..3c04031c18b9 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_sr.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_ISP_SR_DEF_H +#define HISI_ISP_SR_DEF_H + +#include "hisi_isp_core.h" + +int isp_sr_config(struct isp_device *dev); +void isp_sr_go(struct isp_device *dev, unsigned int go_bit); +#endif /* HISI_ISP_SR_DEF_H */ diff --git a/drivers/media/platform/hisilicon/isp/hisi_isp_sr_reg_offset.h b/drivers/media/platform/hisilicon/isp/hisi_isp_sr_reg_offset.h new file mode 100644 index 000000000000..2a4944f1ace2 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/hisi_isp_sr_reg_offset.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __STREAM_ROUTER_REG_OFFSET_H__ +#define __STREAM_ROUTER_REG_OFFSET_H__ + +#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 + +#endif diff --git a/drivers/media/platform/hisilicon/isp/ov5640_main.c b/drivers/media/platform/hisilicon/isp/ov5640_main.c new file mode 100644 index 000000000000..0b0d55b165b2 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5640_main.c @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "ov5640_main.h" + +#define OV5640_MAIN_VOLTAGE_ANALOG 2800000 +#define OV5640_MAIN_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5640_MAIN_VOLTAGE_DIGITAL_IO 1800000 + +#define OV5640_MAIN_SYSTEM_CTRL0 0x3008 +#define OV5640_MAIN_SYSTEM_CTRL0_START 0x02 +#define OV5640_MAIN_SYSTEM_CTRL0_STOP 0x42 +#define OV5640_MAIN_CHIP_ID_HIGH_REG 0x300a +#define OV5640_MAIN_CHIP_ID_HIGH 0x56 +#define OV5640_MAIN_CHIP_ID_LOW_REG 0x300b +#define OV5640_MAIN_CHIP_ID_LOW 0x40 +#define OV5640_MAIN_AWB_MANUAL_CONTROL 0x3406 +#define OV5640_MAIN_AWB_MANUAL_ENABLE BIT(0) +#define OV5640_MAIN_AEC_PK_MANUAL 0x3503 +#define OV5640_MAIN_AEC_MANUAL_ENABLE BIT(0) +#define OV5640_MAIN_AGC_MANUAL_ENABLE BIT(1) +#define OV5640_MAIN_TIMING_TC_REG20 0x3820 +#define OV5640_MAIN_SENSOR_VFLIP BIT(1) +#define OV5640_MAIN_ISP_VFLIP BIT(2) +#define OV5640_MAIN_TIMING_TC_REG21 0x3821 +#define OV5640_MAIN_ISP_MIRROR BIT(2) +#define OV5640_MAIN_SENSOR_MIRROR BIT(1) +#define OV5640_MAIN_PRE_ISP_TEST_SETTING_1 0x503d +#define OV5640_MAIN_SDE_SAT_U 0x5583 +#define OV5640_MAIN_SDE_SAT_V 0x5584 + +enum ov5640_main_mode { + ov5640_main_mode_min = 0, + ov5640_main_mode_1080p_1920_1080 = 0, + ov5640_main_mode_full_2592_1944 = 1, + ov5640_main_mode_max = 1, +}; + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov5640_main_mode_info { + enum ov5640_main_mode mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +static struct reg_value ov5640_main_global_init_setting[] = { + { 0x3103, 0x11 }, + { 0x3008, 0x82 }, + { 0x3008, 0x42 }, + { 0x3103, 0x03 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x3034, 0x18 }, + { 0x3035, 0x11 }, + { 0x3036, 0x54 }, + { 0x3037, 0x13 }, + { 0x3108, 0x01 }, + { 0x3630, 0x36 }, + { 0x3631, 0x0e }, + { 0x3632, 0xe2 }, + { 0x3633, 0x12 }, + { 0x3621, 0xe0 }, + { 0x3704, 0xa0 }, + { 0x3703, 0x5a }, + { 0x3715, 0x78 }, + { 0x3717, 0x01 }, + { 0x370b, 0x60 }, + { 0x3705, 0x1a }, + { 0x3905, 0x02 }, + { 0x3906, 0x10 }, + { 0x3901, 0x0a }, + { 0x3731, 0x12 }, + { 0x3600, 0x08 }, + { 0x3601, 0x33 }, + { 0x302d, 0x60 }, + { 0x3620, 0x52 }, + { 0x371b, 0x20 }, + { 0x471c, 0x50 }, + { 0x3a13, 0x43 }, + { 0x3a18, 0x00 }, + { 0x3a19, 0xf8 }, + { 0x3635, 0x13 }, + { 0x3636, 0x03 }, + { 0x3634, 0x40 }, + { 0x3622, 0x01 }, + { 0x3c01, 0x34 }, + { 0x3c04, 0x28 }, + { 0x3c05, 0x98 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x07 }, + { 0x3c08, 0x00 }, + { 0x3c09, 0x1c }, + { 0x3c0a, 0x9c }, + { 0x3c0b, 0x40 }, + { 0x3820, 0x40 }, + { 0x3821, 0x06 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3810, 0x00 }, + { 0x3811, 0x10 }, + { 0x3812, 0x00 }, + { 0x3813, 0x04 }, + { 0x3618, 0x04 }, + { 0x3612, 0x2b }, + { 0x3708, 0x63 }, + { 0x3709, 0x12 }, + { 0x370c, 0x00 }, + { 0x4001, 0x02 }, + { 0x4004, 0x06 }, + { 0x4050, 0x6e }, + { 0x4051, 0x8f }, + { 0x3000, 0x00 }, + { 0x3002, 0x1c }, + { 0x3004, 0xff }, + { 0x3006, 0xc3 }, + { 0x300e, 0x45 }, + { 0x302e, 0x08 }, + { 0x4300, 0x32 }, + { 0x4837, 0x0a }, + { 0x501f, 0x00 }, + { 0x4713, 0x02 }, + { 0x4407, 0x04 }, + { 0x440e, 0x00 }, + { 0x460b, 0x37 }, + { 0x460c, 0x20 }, + { 0x5000, 0xa7 }, + { 0x5001, 0x83 }, + { 0x5180, 0xff }, + { 0x5181, 0xf2 }, + { 0x5182, 0x00 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x09 }, + { 0x5187, 0x09 }, + { 0x5188, 0x09 }, + { 0x5189, 0x75 }, + { 0x518a, 0x54 }, + { 0x518b, 0xe0 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x42 }, + { 0x518e, 0x3d }, + { 0x518f, 0x56 }, + { 0x5190, 0x46 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x04 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x03 }, + { 0x5197, 0x01 }, + { 0x5198, 0x04 }, + { 0x5199, 0x12 }, + { 0x519a, 0x04 }, + { 0x519b, 0x00 }, + { 0x519c, 0x06 }, + { 0x519d, 0x82 }, + { 0x519e, 0x38 }, + { 0x5381, 0x1e }, + { 0x5382, 0x5b }, + { 0x5383, 0x08 }, + { 0x5384, 0x0a }, + { 0x5385, 0x7e }, + { 0x5386, 0x88 }, + { 0x5387, 0x7c }, + { 0x5388, 0x6c }, + { 0x5389, 0x10 }, + { 0x538a, 0x01 }, + { 0x538b, 0x98 }, + { 0x5300, 0x08 }, + { 0x5301, 0x30 }, + { 0x5302, 0x10 }, + { 0x5303, 0x00 }, + { 0x5304, 0x08 }, + { 0x5305, 0x30 }, + { 0x5306, 0x08 }, + { 0x5307, 0x16 }, + { 0x5309, 0x08 }, + { 0x530a, 0x30 }, + { 0x530b, 0x04 }, + { 0x530c, 0x06 }, + { 0x5480, 0x01 }, + { 0x5481, 0x08 }, + { 0x5482, 0x14 }, + { 0x5483, 0x28 }, + { 0x5484, 0x51 }, + { 0x5485, 0x65 }, + { 0x5486, 0x71 }, + { 0x5487, 0x7d }, + { 0x5488, 0x87 }, + { 0x5489, 0x91 }, + { 0x548a, 0x9a }, + { 0x548b, 0xaa }, + { 0x548c, 0xb8 }, + { 0x548d, 0xcd }, + { 0x548e, 0xdd }, + { 0x548f, 0xea }, + { 0x5490, 0x1d }, + { 0x5580, 0x02 }, + { 0x5583, 0x40 }, + { 0x5584, 0x10 }, + { 0x5589, 0x10 }, + { 0x558a, 0x00 }, + { 0x558b, 0xf8 }, + { 0x5800, 0x23 }, + { 0x5801, 0x14 }, + { 0x5802, 0x0f }, + { 0x5803, 0x0f }, + { 0x5804, 0x12 }, + { 0x5805, 0x26 }, + { 0x5806, 0x0c }, + { 0x5807, 0x08 }, + { 0x5808, 0x05 }, + { 0x5809, 0x05 }, + { 0x580a, 0x08 }, + { 0x580b, 0x0d }, + { 0x580c, 0x08 }, + { 0x580d, 0x03 }, + { 0x580e, 0x00 }, + { 0x580f, 0x00 }, + { 0x5810, 0x03 }, + { 0x5811, 0x09 }, + { 0x5812, 0x07 }, + { 0x5813, 0x03 }, + { 0x5814, 0x00 }, + { 0x5815, 0x01 }, + { 0x5816, 0x03 }, + { 0x5817, 0x08 }, + { 0x5818, 0x0d }, + { 0x5819, 0x08 }, + { 0x581a, 0x05 }, + { 0x581b, 0x06 }, + { 0x581c, 0x08 }, + { 0x581d, 0x0e }, + { 0x581e, 0x29 }, + { 0x581f, 0x17 }, + { 0x5820, 0x11 }, + { 0x5821, 0x11 }, + { 0x5822, 0x15 }, + { 0x5823, 0x28 }, + { 0x5824, 0x46 }, + { 0x5825, 0x26 }, + { 0x5826, 0x08 }, + { 0x5827, 0x26 }, + { 0x5828, 0x64 }, + { 0x5829, 0x26 }, + { 0x582a, 0x24 }, + { 0x582b, 0x22 }, + { 0x582c, 0x24 }, + { 0x582d, 0x24 }, + { 0x582e, 0x06 }, + { 0x582f, 0x22 }, + { 0x5830, 0x40 }, + { 0x5831, 0x42 }, + { 0x5832, 0x24 }, + { 0x5833, 0x26 }, + { 0x5834, 0x24 }, + { 0x5835, 0x22 }, + { 0x5836, 0x22 }, + { 0x5837, 0x26 }, + { 0x5838, 0x44 }, + { 0x5839, 0x24 }, + { 0x583a, 0x26 }, + { 0x583b, 0x28 }, + { 0x583c, 0x42 }, + { 0x583d, 0xce }, + { 0x5025, 0x00 }, + { 0x3a0f, 0x30 }, + { 0x3a10, 0x28 }, + { 0x3a1b, 0x30 }, + { 0x3a1e, 0x26 }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x14 }, +}; + +static struct reg_value ov5640_main_setting_1080P[] = { + { 0x3800, 0x01 }, + { 0x3801, 0x50 }, + { 0x3802, 0x01 }, + { 0x3803, 0xb2 }, + { 0x3804, 0x08 }, + { 0x3805, 0xef }, + { 0x3806, 0x05 }, + { 0x3807, 0xf1 }, + { 0x3808, 0x07 }, + { 0x3809, 0x80 }, + { 0x380a, 0x04 }, + { 0x380b, 0x38 }, + { 0x380c, 0x09 }, + { 0x380d, 0xc4 }, + { 0x380e, 0x04 }, + { 0x380f, 0x60 }, + { 0x3a02, 0x04 }, + { 0x3a03, 0x60 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x50 }, + { 0x3a0a, 0x01 }, + { 0x3a0b, 0x18 }, + { 0x3a0e, 0x03 }, + { 0x3a0d, 0x04 }, + { 0x3a14, 0x04 }, + { 0x3a15, 0x60 }, + { 0x5684, 0x07 }, + { 0x5685, 0xa0 }, + { 0x5686, 0x04 }, + { 0x5687, 0x40 }, + { 0x3824, 0x04 }, +}; + +static struct reg_value ov5640_main_setting_full[] = { + { 0x3800, 0x00 }, + { 0x3801, 0x00 }, + { 0x3802, 0x00 }, + { 0x3803, 0x00 }, + { 0x3804, 0x0a }, + { 0x3805, 0x3f }, + { 0x3806, 0x07 }, + { 0x3807, 0x96 }, + { 0x3808, 0x0a }, + { 0x3809, 0x20 }, + { 0x380a, 0x07 }, + { 0x380b, 0x98 }, + { 0x380c, 0x0b }, + { 0x380d, 0x1c }, + { 0x380e, 0x07 }, + { 0x380f, 0xb0 }, + { 0x3a02, 0x07 }, + { 0x3a03, 0xb0 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x27 }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0xf6 }, + { 0x3a0e, 0x06 }, + { 0x3a0d, 0x08 }, + { 0x3a14, 0x07 }, + { 0x3a15, 0xb0 }, + { 0x5684, 0x0a }, + { 0x5685, 0x20 }, + { 0x5686, 0x07 }, + { 0x5687, 0x98 }, + { 0x3824, 0x01 }, +}; + +static struct ov5640_main_mode_info +ov5640_main_mode_info_data[ov5640_main_mode_max + 1] = { + {ov5640_main_mode_1080p_1920_1080, 1920, 1080, + ov5640_main_setting_1080P, + ARRAY_SIZE(ov5640_main_setting_1080P)}, + {ov5640_main_mode_full_2592_1944, 2592, 1944, + ov5640_main_setting_full, + ARRAY_SIZE(ov5640_main_setting_full)}, +}; + +static int ov5640_main_regulators_enable(struct isp_device *dev) +{ + int ret = 0; + + if (dev->io_regulator) { + ret = regulator_enable(dev->io_regulator); + if (ret < 0) { + ISP_ERR("set io voltage failed\n"); + return ret; + } + } + + return ret; +} + +static void ov5640_main_regulators_disable(struct isp_device *dev) +{ + if (dev->io_regulator) + regulator_disable(dev->io_regulator); +} + +void __ov5640_main_set_power(struct isp_device *dev, bool on) +{ + if (on) { + ov5640_main_regulators_enable(dev); + clk_prepare_enable(dev->xclk); + usleep_range(5000, 15000); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 0); + usleep_range(1000, 2000); + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 1); + msleep(20); + } else { + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 0); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 1); + ov5640_main_regulators_disable(dev); + clk_disable_unprepare(dev->xclk); + } +} + +static enum ov5640_main_mode +ov5640_main_find_nearest_mode(int width, int height) +{ + int i; + + for (i = ov5640_main_mode_max; i >= 0; i--) { + if (ov5640_main_mode_info_data[i].width <= width && + ov5640_main_mode_info_data[i].height <= height) + break; + } + + if (i < 0) + i = 0; + + return (enum ov5640_main_mode)i; +} + +static int ov5640_main_set_register_array(struct isp_i2c_client_t *client, + struct reg_value *settings, + s32 num_settings) +{ + register u16 reg = 0; + register u8 val = 0; + int i, ret = 0; + + for (i = 0; i < num_settings; ++i, ++settings) { + reg = settings->reg; + val = settings->val; + + ret = client->ops->write(client, reg, val); + if (ret < 0) + goto err; + } +err: + return ret; +} + +static int ov5640_main_init(struct isp_i2c_client_t *client) +{ + struct reg_value *settings = NULL; + int num_settings = 0; + int ret; + + settings = ov5640_main_global_init_setting; + num_settings = ARRAY_SIZE(ov5640_main_global_init_setting); + ret = ov5640_main_set_register_array(client, settings, num_settings); + if (ret < 0) + return ret; + + return 0; +} + +static int ov5640_main_change_mode(struct isp_i2c_client_t *client, + enum ov5640_main_mode mode) +{ + struct reg_value *settings = NULL; + s32 num_settings = 0; + int ret = 0; + + settings = ov5640_main_mode_info_data[mode].init_data_ptr; + num_settings = ov5640_main_mode_info_data[mode].init_data_size; + ret = ov5640_main_set_register_array(client, settings, num_settings); + + return ret; +} + +static int ov5640_main_set_aec_mode(struct isp_i2c_client_t *client, + int auto_mode) +{ + u8 val; + + client->ops->read(client, OV5640_MAIN_AEC_PK_MANUAL, &val); + if (auto_mode) { + val |= OV5640_MAIN_AEC_MANUAL_ENABLE; + } else { /* V4L2_EXPOSURE_MANUAL */ + val &= ~OV5640_MAIN_AEC_MANUAL_ENABLE; + } + + return client->ops->write(client, OV5640_MAIN_AEC_PK_MANUAL, val); +} + +static int ov5640_main_set_awb(struct isp_i2c_client_t *client, s32 enable_auto) +{ + u8 val; + + client->ops->read(client, OV5640_MAIN_AWB_MANUAL_CONTROL, &val); + if (enable_auto) + val &= ~OV5640_MAIN_AWB_MANUAL_ENABLE; + else + val |= OV5640_MAIN_AWB_MANUAL_ENABLE; + + return client->ops->write(client, OV5640_MAIN_AWB_MANUAL_CONTROL, val); +} + +int ov5640_main_set_fmt(struct isp_i2c_client_t *client, + unsigned int w, unsigned int h) +{ + enum ov5640_main_mode new_mode; + + new_mode = ov5640_main_find_nearest_mode(w, h); + + client->mode = new_mode; + + return 0; +} + +int ov5640_main_get_chip_id(struct isp_i2c_client_t *client, + unsigned int *chip_id) +{ + int ret; + unsigned char chip_id_high, chip_id_low; + + ret = client->ops->read(client, + OV5640_MAIN_CHIP_ID_HIGH_REG, &chip_id_high); + if (ret || chip_id_high != OV5640_MAIN_CHIP_ID_HIGH) { + ISP_ERR("get chip id high[%d] fail\n", chip_id_high); + return -ENODEV; + } + + ret = client->ops->read(client, + OV5640_MAIN_CHIP_ID_LOW_REG, &chip_id_low); + if (ret || chip_id_low != OV5640_MAIN_CHIP_ID_LOW) { + ISP_ERR("get chip id low[%d] fail\n", chip_id_low); + return -ENODEV; + } + + *chip_id = (chip_id_high<<8) | chip_id_low; + + return 0; +} + +int ov5640_main_stream_on_off(struct isp_i2c_client_t *client, int enable) +{ + int ret; + + ISP_ERR("enable = %d mode = %d\n", enable, client->mode); + + if (enable) { + ret = ov5640_main_change_mode(client, client->mode); + if (ret < 0) { + ISP_ERR("could not set mode %d\n", + client->mode); + return ret; + } + + ov5640_main_set_aec_mode(client, 0); + ov5640_main_set_awb(client, 1); + client->ops->write(client, OV5640_MAIN_SYSTEM_CTRL0, + OV5640_MAIN_SYSTEM_CTRL0_START); + } else { + client->ops->write(client, OV5640_MAIN_SYSTEM_CTRL0, + OV5640_MAIN_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +int ov5640_main_power(struct isp_i2c_client_t *client, int on) +{ + int ret; + + __ov5640_main_set_power(client->isp_dev, !!on); + + if (on) { + ret = ov5640_main_init(client); + if (ret < 0) { + ISP_ERR("could not set init registers\n"); + return -1; + } + + client->ops->write(client, OV5640_MAIN_SYSTEM_CTRL0, + OV5640_MAIN_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +static int ov5640_main_probe(struct platform_device *pdev) +{ + int ret; + struct isp_device *idev = NULL; + struct device *device = &pdev->dev; + struct device_node *np = device->of_node; + int index = 0; + unsigned int chip_id = 0; + struct isp_i2c_client_t *i2c_client = NULL; + + ISP_INFO("enter!\n"); + if (pdev->dev.parent) + idev = dev_get_drvdata(pdev->dev.parent); + + if (!idev) + return -ENXIO; + + ret = of_property_read_u32(np, "sensor_index", (u32 *)&index); + if (ret < 0) { + ISP_ERR("no sensor index available\n"); + return -ENXIO; + } + + if (idev->sensor[index].probe == true) + return 0; + + idev->sensor[index].rst_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no reset pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + idev->sensor[index].pwdn_gpio = of_get_named_gpio(np, "pwdn-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no powerdown pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].rst_gpio, + GPIOF_OUT_INIT_LOW, "sensor-reset"); + if (ret < 0) + return ret; + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].pwdn_gpio, + GPIOF_OUT_INIT_LOW, "sensor-pwdn"); + if (ret < 0) + return ret; + + idev->sensor[index].addr = OV5640_MAIN_SLAVE_ID; + idev->sensor[index].flag = I2C_ADDR_7BIT; + idev->sensor[index].speed_cfg = I2C_SPEED_STD; + idev->sensor[index].csi_index = 0; + idev->sensor[index].dt = YUV_DT_422_8BITS; + idev->sensor[index].pf = DF_2PF8; + idev->sensor[index].csi_lane = 2; + + idev->addr = idev->sensor[index].addr; + idev->flag = idev->sensor[index].flag; + idev->speed_cfg = idev->sensor[index].speed_cfg; + idev->csi_index = idev->sensor[index].csi_index; + idev->dt = idev->sensor[index].dt; + idev->pf = idev->sensor[index].pf; + idev->csi_lane = idev->sensor[index].csi_lane; + idev->pwdn_gpio = idev->sensor[index].pwdn_gpio; + idev->rst_gpio = idev->sensor[index].rst_gpio; + + isp_ispss_enable_clock(idev); + + mdelay(100); + + isp_ispss_reset_all(idev); + + mdelay(100); + + idev->client = create_isp_i2c_client(idev); + if (!idev->client) + return -ENODEV; + + i2c_client = idev->client; + ret = i2c_client->ops->open(i2c_client); + if (ret) { + destroy_isp_i2c_client(i2c_client); + return -EBUSY; + } + + ov5640_main_power(i2c_client, 1); + ov5640_main_get_chip_id(i2c_client, &chip_id); + ov5640_main_power(i2c_client, 0); + i2c_client->ops->close(i2c_client); + destroy_isp_i2c_client(i2c_client); + ISP_ERR("chip id is 0x%x\n", chip_id); + if (chip_id != 0x5640) { + ISP_ERR("chip id is 0x%x, probe failed\n", chip_id); + return -1; + } + + idev->sensor[index].probe = true; + idev->sensor[index].camera_sensor_power = ov5640_main_power; + idev->sensor[index].camera_sensor_set_fmt = ov5640_main_set_fmt; + idev->sensor[index].camera_stream_on_off = ov5640_main_stream_on_off; + + ISP_INFO("exit!\n"); + + return 0; +} + +static int ov5640_main_remove(struct platform_device *pdev) +{ + ISP_INFO("enter!\n"); + + + ISP_INFO("exit!\n"); + return 0; +} + +static const struct of_device_id ov5640_main_match_table[] = { + { + .compatible = "hisilicon,ov5640_main", + .data = NULL, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov5640_main_match_table); + +static struct platform_driver ov5640_main_driver = { + .probe = ov5640_main_probe, + .remove = ov5640_main_remove, + .driver = { + .name = "hisi-ov5640_main", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ov5640_main_match_table), + }, +}; + +static int __init ov5640_main_module_init(void) +{ + int ret = 0; + + ISP_INFO("enter!\n"); + + ret = platform_driver_register(&ov5640_main_driver); + if (ret) + return ret; + + ISP_INFO("exit!\n"); + + return ret; +} + +static void __exit ov5640_main_module_exit(void) +{ + ISP_INFO("enter!\n"); + + platform_driver_unregister(&ov5640_main_driver); + + ISP_INFO("exit!\n"); +} + +late_initcall(ov5640_main_module_init); +module_exit(ov5640_main_module_exit); + +MODULE_DESCRIPTION("Hisilicon Isp Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/platform/hisilicon/isp/ov5640_main.h b/drivers/media/platform/hisilicon/isp/ov5640_main.h new file mode 100644 index 000000000000..054da4a382df --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5640_main.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_OV5640_MAIN_H +#define HISI_OV5640_MAIN_H + +#include "hisi_isp_core.h" +#include "hisi_isp_i2c.h" +#include "hisi_isp_common.h" +#include "hisi_isp_cvdr.h" + +#define OV5640_MAIN_SLAVE_ID 0x78 + +#endif /* HISI_OV5640_MAIN_H */ diff --git a/drivers/media/platform/hisilicon/isp/ov5640_sub.c b/drivers/media/platform/hisilicon/isp/ov5640_sub.c new file mode 100644 index 000000000000..44f78c2a8b41 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5640_sub.c @@ -0,0 +1,751 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "ov5640_sub.h" + +#define OV5640_SUB_VOLTAGE_ANALOG 2800000 +#define OV5640_SUB_VOLTAGE_DIGITAL_CORE 1500000 +#define OV5640_SUB_VOLTAGE_DIGITAL_IO 1800000 + +#define OV5640_SUB_SYSTEM_CTRL0 0x3008 +#define OV5640_SUB_SYSTEM_CTRL0_START 0x02 +#define OV5640_SUB_SYSTEM_CTRL0_STOP 0x42 +#define OV5640_SUB_CHIP_ID_HIGH_REG 0x300a +#define OV5640_SUB_CHIP_ID_HIGH 0x56 +#define OV5640_SUB_CHIP_ID_LOW_REG 0x300b +#define OV5640_SUB_CHIP_ID_LOW 0x40 +#define OV5640_SUB_AWB_MANUAL_CONTROL 0x3406 +#define OV5640_SUB_AWB_MANUAL_ENABLE BIT(0) +#define OV5640_SUB_AEC_PK_MANUAL 0x3503 +#define OV5640_SUB_AEC_MANUAL_ENABLE BIT(0) +#define OV5640_SUB_AGC_MANUAL_ENABLE BIT(1) +#define OV5640_SUB_TIMING_TC_REG20 0x3820 +#define OV5640_SUB_SENSOR_VFLIP BIT(1) +#define OV5640_SUB_ISP_VFLIP BIT(2) +#define OV5640_SUB_TIMING_TC_REG21 0x3821 +#define OV5640_SUB_ISP_MIRROR BIT(2) +#define OV5640_SUB_SENSOR_MIRROR BIT(1) +#define OV5640_SUB_PRE_ISP_TEST_SETTING_1 0x503d +#define OV5640_SUB_SDE_SAT_U 0x5583 +#define OV5640_SUB_SDE_SAT_V 0x5584 + +enum ov5640_sub_mode { + ov5640_sub_mode_min = 0, + ov5640_sub_mode_1080p_1920_1080 = 0, + ov5640_sub_mode_full_2592_1944 = 1, + ov5640_sub_mode_max = 1, +}; + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov5640_sub_mode_info { + enum ov5640_sub_mode mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +static struct reg_value ov5640_sub_global_init_setting[] = { + { 0x3103, 0x11 }, + { 0x3008, 0x82 }, + { 0x3008, 0x42 }, + { 0x3103, 0x03 }, + { 0x3017, 0x00 }, + { 0x3018, 0x00 }, + { 0x3034, 0x18 }, + { 0x3035, 0x11 }, + { 0x3036, 0x54 }, + { 0x3037, 0x13 }, + { 0x3108, 0x01 }, + { 0x3630, 0x36 }, + { 0x3631, 0x0e }, + { 0x3632, 0xe2 }, + { 0x3633, 0x12 }, + { 0x3621, 0xe0 }, + { 0x3704, 0xa0 }, + { 0x3703, 0x5a }, + { 0x3715, 0x78 }, + { 0x3717, 0x01 }, + { 0x370b, 0x60 }, + { 0x3705, 0x1a }, + { 0x3905, 0x02 }, + { 0x3906, 0x10 }, + { 0x3901, 0x0a }, + { 0x3731, 0x12 }, + { 0x3600, 0x08 }, + { 0x3601, 0x33 }, + { 0x302d, 0x60 }, + { 0x3620, 0x52 }, + { 0x371b, 0x20 }, + { 0x471c, 0x50 }, + { 0x3a13, 0x43 }, + { 0x3a18, 0x00 }, + { 0x3a19, 0xf8 }, + { 0x3635, 0x13 }, + { 0x3636, 0x03 }, + { 0x3634, 0x40 }, + { 0x3622, 0x01 }, + { 0x3c01, 0x34 }, + { 0x3c04, 0x28 }, + { 0x3c05, 0x98 }, + { 0x3c06, 0x00 }, + { 0x3c07, 0x07 }, + { 0x3c08, 0x00 }, + { 0x3c09, 0x1c }, + { 0x3c0a, 0x9c }, + { 0x3c0b, 0x40 }, + { 0x3820, 0x40 }, + { 0x3821, 0x06 }, + { 0x3814, 0x11 }, + { 0x3815, 0x11 }, + { 0x3810, 0x00 }, + { 0x3811, 0x10 }, + { 0x3812, 0x00 }, + { 0x3813, 0x04 }, + { 0x3618, 0x04 }, + { 0x3612, 0x2b }, + { 0x3708, 0x63 }, + { 0x3709, 0x12 }, + { 0x370c, 0x00 }, + { 0x4001, 0x02 }, + { 0x4004, 0x06 }, + { 0x4050, 0x6e }, + { 0x4051, 0x8f }, + { 0x3000, 0x00 }, + { 0x3002, 0x1c }, + { 0x3004, 0xff }, + { 0x3006, 0xc3 }, + { 0x300e, 0x45 }, + { 0x302e, 0x08 }, + { 0x4300, 0x32 }, + { 0x4837, 0x0a }, + { 0x501f, 0x00 }, + { 0x4713, 0x02 }, + { 0x4407, 0x04 }, + { 0x440e, 0x00 }, + { 0x460b, 0x37 }, + { 0x460c, 0x20 }, + { 0x5000, 0xa7 }, + { 0x5001, 0x83 }, + { 0x5180, 0xff }, + { 0x5181, 0xf2 }, + { 0x5182, 0x00 }, + { 0x5183, 0x14 }, + { 0x5184, 0x25 }, + { 0x5185, 0x24 }, + { 0x5186, 0x09 }, + { 0x5187, 0x09 }, + { 0x5188, 0x09 }, + { 0x5189, 0x75 }, + { 0x518a, 0x54 }, + { 0x518b, 0xe0 }, + { 0x518c, 0xb2 }, + { 0x518d, 0x42 }, + { 0x518e, 0x3d }, + { 0x518f, 0x56 }, + { 0x5190, 0x46 }, + { 0x5191, 0xf8 }, + { 0x5192, 0x04 }, + { 0x5193, 0x70 }, + { 0x5194, 0xf0 }, + { 0x5195, 0xf0 }, + { 0x5196, 0x03 }, + { 0x5197, 0x01 }, + { 0x5198, 0x04 }, + { 0x5199, 0x12 }, + { 0x519a, 0x04 }, + { 0x519b, 0x00 }, + { 0x519c, 0x06 }, + { 0x519d, 0x82 }, + { 0x519e, 0x38 }, + { 0x5381, 0x1e }, + { 0x5382, 0x5b }, + { 0x5383, 0x08 }, + { 0x5384, 0x0a }, + { 0x5385, 0x7e }, + { 0x5386, 0x88 }, + { 0x5387, 0x7c }, + { 0x5388, 0x6c }, + { 0x5389, 0x10 }, + { 0x538a, 0x01 }, + { 0x538b, 0x98 }, + { 0x5300, 0x08 }, + { 0x5301, 0x30 }, + { 0x5302, 0x10 }, + { 0x5303, 0x00 }, + { 0x5304, 0x08 }, + { 0x5305, 0x30 }, + { 0x5306, 0x08 }, + { 0x5307, 0x16 }, + { 0x5309, 0x08 }, + { 0x530a, 0x30 }, + { 0x530b, 0x04 }, + { 0x530c, 0x06 }, + { 0x5480, 0x01 }, + { 0x5481, 0x08 }, + { 0x5482, 0x14 }, + { 0x5483, 0x28 }, + { 0x5484, 0x51 }, + { 0x5485, 0x65 }, + { 0x5486, 0x71 }, + { 0x5487, 0x7d }, + { 0x5488, 0x87 }, + { 0x5489, 0x91 }, + { 0x548a, 0x9a }, + { 0x548b, 0xaa }, + { 0x548c, 0xb8 }, + { 0x548d, 0xcd }, + { 0x548e, 0xdd }, + { 0x548f, 0xea }, + { 0x5490, 0x1d }, + { 0x5580, 0x02 }, + { 0x5583, 0x40 }, + { 0x5584, 0x10 }, + { 0x5589, 0x10 }, + { 0x558a, 0x00 }, + { 0x558b, 0xf8 }, + { 0x5800, 0x23 }, + { 0x5801, 0x14 }, + { 0x5802, 0x0f }, + { 0x5803, 0x0f }, + { 0x5804, 0x12 }, + { 0x5805, 0x26 }, + { 0x5806, 0x0c }, + { 0x5807, 0x08 }, + { 0x5808, 0x05 }, + { 0x5809, 0x05 }, + { 0x580a, 0x08 }, + { 0x580b, 0x0d }, + { 0x580c, 0x08 }, + { 0x580d, 0x03 }, + { 0x580e, 0x00 }, + { 0x580f, 0x00 }, + { 0x5810, 0x03 }, + { 0x5811, 0x09 }, + { 0x5812, 0x07 }, + { 0x5813, 0x03 }, + { 0x5814, 0x00 }, + { 0x5815, 0x01 }, + { 0x5816, 0x03 }, + { 0x5817, 0x08 }, + { 0x5818, 0x0d }, + { 0x5819, 0x08 }, + { 0x581a, 0x05 }, + { 0x581b, 0x06 }, + { 0x581c, 0x08 }, + { 0x581d, 0x0e }, + { 0x581e, 0x29 }, + { 0x581f, 0x17 }, + { 0x5820, 0x11 }, + { 0x5821, 0x11 }, + { 0x5822, 0x15 }, + { 0x5823, 0x28 }, + { 0x5824, 0x46 }, + { 0x5825, 0x26 }, + { 0x5826, 0x08 }, + { 0x5827, 0x26 }, + { 0x5828, 0x64 }, + { 0x5829, 0x26 }, + { 0x582a, 0x24 }, + { 0x582b, 0x22 }, + { 0x582c, 0x24 }, + { 0x582d, 0x24 }, + { 0x582e, 0x06 }, + { 0x582f, 0x22 }, + { 0x5830, 0x40 }, + { 0x5831, 0x42 }, + { 0x5832, 0x24 }, + { 0x5833, 0x26 }, + { 0x5834, 0x24 }, + { 0x5835, 0x22 }, + { 0x5836, 0x22 }, + { 0x5837, 0x26 }, + { 0x5838, 0x44 }, + { 0x5839, 0x24 }, + { 0x583a, 0x26 }, + { 0x583b, 0x28 }, + { 0x583c, 0x42 }, + { 0x583d, 0xce }, + { 0x5025, 0x00 }, + { 0x3a0f, 0x30 }, + { 0x3a10, 0x28 }, + { 0x3a1b, 0x30 }, + { 0x3a1e, 0x26 }, + { 0x3a11, 0x60 }, + { 0x3a1f, 0x14 }, +}; + +static struct reg_value ov5640_sub_setting_1080P[] = { + { 0x3800, 0x01 }, + { 0x3801, 0x50 }, + { 0x3802, 0x01 }, + { 0x3803, 0xb2 }, + { 0x3804, 0x08 }, + { 0x3805, 0xef }, + { 0x3806, 0x05 }, + { 0x3807, 0xf1 }, + { 0x3808, 0x07 }, + { 0x3809, 0x80 }, + { 0x380a, 0x04 }, + { 0x380b, 0x38 }, + { 0x380c, 0x09 }, + { 0x380d, 0xc4 }, + { 0x380e, 0x04 }, + { 0x380f, 0x60 }, + { 0x3a02, 0x04 }, + { 0x3a03, 0x60 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x50 }, + { 0x3a0a, 0x01 }, + { 0x3a0b, 0x18 }, + { 0x3a0e, 0x03 }, + { 0x3a0d, 0x04 }, + { 0x3a14, 0x04 }, + { 0x3a15, 0x60 }, + { 0x5684, 0x07 }, + { 0x5685, 0xa0 }, + { 0x5686, 0x04 }, + { 0x5687, 0x40 }, + { 0x3824, 0x04 }, +}; + +static struct reg_value ov5640_sub_setting_full[] = { + { 0x3800, 0x00 }, + { 0x3801, 0x00 }, + { 0x3802, 0x00 }, + { 0x3803, 0x00 }, + { 0x3804, 0x0a }, + { 0x3805, 0x3f }, + { 0x3806, 0x07 }, + { 0x3807, 0x96 }, + { 0x3808, 0x0a }, + { 0x3809, 0x20 }, + { 0x380a, 0x07 }, + { 0x380b, 0x98 }, + { 0x380c, 0x0b }, + { 0x380d, 0x1c }, + { 0x380e, 0x07 }, + { 0x380f, 0xb0 }, + { 0x3a02, 0x07 }, + { 0x3a03, 0xb0 }, + { 0x3a08, 0x01 }, + { 0x3a09, 0x27 }, + { 0x3a0a, 0x00 }, + { 0x3a0b, 0xf6 }, + { 0x3a0e, 0x06 }, + { 0x3a0d, 0x08 }, + { 0x3a14, 0x07 }, + { 0x3a15, 0xb0 }, + { 0x5684, 0x0a }, + { 0x5685, 0x20 }, + { 0x5686, 0x07 }, + { 0x5687, 0x98 }, + { 0x3824, 0x01 }, +}; + +static struct ov5640_sub_mode_info +ov5640_sub_mode_info_data[ov5640_sub_mode_max + 1] = { + {ov5640_sub_mode_1080p_1920_1080, 1920, 1080, + ov5640_sub_setting_1080P, + ARRAY_SIZE(ov5640_sub_setting_1080P)}, + {ov5640_sub_mode_full_2592_1944, 2592, 1944, + ov5640_sub_setting_full, + ARRAY_SIZE(ov5640_sub_setting_full)}, +}; + +static int ov5640_sub_regulators_enable(struct isp_device *dev) +{ + int ret = 0; + + if (dev->io_regulator) { + ret = regulator_enable(dev->io_regulator); + if (ret < 0) { + ISP_ERR("set io voltage failed\n"); + return ret; + } + } + + return ret; +} + +static void ov5640_sub_regulators_disable(struct isp_device *dev) +{ + if (dev->io_regulator) + regulator_disable(dev->io_regulator); +} + +void __ov5640_sub_set_power(struct isp_device *dev, bool on) +{ + if (on) { + + ov5640_sub_regulators_enable(dev); + clk_prepare_enable(dev->xclk1); + usleep_range(5000, 15000); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 0); + usleep_range(1000, 2000); + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 1); + msleep(20); + } else { + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 0); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 1); + ov5640_sub_regulators_disable(dev); + clk_disable_unprepare(dev->xclk1); + } +} + +static enum ov5640_sub_mode ov5640_sub_find_nearest_mode(int width, int height) +{ + int i; + + for (i = ov5640_sub_mode_max; i >= 0; i--) { + if (ov5640_sub_mode_info_data[i].width <= width && + ov5640_sub_mode_info_data[i].height <= height) + break; + } + + if (i < 0) + i = 0; + + return (enum ov5640_sub_mode)i; +} + +static int ov5640_sub_set_register_array(struct isp_i2c_client_t *client, + struct reg_value *settings, + s32 num_settings) +{ + register u16 reg = 0; + register u8 val = 0; + int i, ret = 0; + + for (i = 0; i < num_settings; ++i, ++settings) { + reg = settings->reg; + val = settings->val; + + ret = client->ops->write(client, reg, val); + if (ret < 0) + goto err; + } +err: + return ret; +} + +static int ov5640_sub_init(struct isp_i2c_client_t *client) +{ + struct reg_value *settings = NULL; + int num_settings = 0; + int ret; + + settings = ov5640_sub_global_init_setting; + num_settings = ARRAY_SIZE(ov5640_sub_global_init_setting); + ret = ov5640_sub_set_register_array(client, settings, num_settings); + if (ret < 0) + return ret; + + return 0; +} + +static int +ov5640_sub_change_mode(struct isp_i2c_client_t *client, + enum ov5640_sub_mode mode) +{ + struct reg_value *settings = NULL; + s32 num_settings = 0; + int ret = 0; + + settings = ov5640_sub_mode_info_data[mode].init_data_ptr; + num_settings = ov5640_sub_mode_info_data[mode].init_data_size; + ret = ov5640_sub_set_register_array(client, settings, num_settings); + + return ret; +} + +static int ov5640_sub_set_aec_mode(struct isp_i2c_client_t *client, + int auto_mode) +{ + u8 val; + + client->ops->read(client, OV5640_SUB_AEC_PK_MANUAL, &val); + if (auto_mode) { + val |= OV5640_SUB_AEC_MANUAL_ENABLE; + } else { /* V4L2_EXPOSURE_MANUAL */ + val &= ~OV5640_SUB_AEC_MANUAL_ENABLE; + } + + return client->ops->write(client, OV5640_SUB_AEC_PK_MANUAL, val); +} + +static int ov5640_sub_set_awb(struct isp_i2c_client_t *client, s32 enable_auto) +{ + u8 val; + + client->ops->read(client, OV5640_SUB_AWB_MANUAL_CONTROL, &val); + if (enable_auto) + val &= ~OV5640_SUB_AWB_MANUAL_ENABLE; + else + val |= OV5640_SUB_AWB_MANUAL_ENABLE; + + return client->ops->write(client, OV5640_SUB_AWB_MANUAL_CONTROL, val); +} + +int ov5640_sub_set_fmt(struct isp_i2c_client_t *client, + unsigned int w, unsigned int h) +{ + enum ov5640_sub_mode new_mode; + + new_mode = ov5640_sub_find_nearest_mode(w, h); + + client->mode = new_mode; + + return 0; +} + +int ov5640_sub_get_chip_id(struct isp_i2c_client_t *client, + unsigned int *chip_id) +{ + int ret; + unsigned char chip_id_high, chip_id_low; + + ret = client->ops->read(client, + OV5640_SUB_CHIP_ID_HIGH_REG, &chip_id_high); + if (ret || chip_id_high != OV5640_SUB_CHIP_ID_HIGH) { + ISP_ERR("get chip id high[%d] fail\n", chip_id_high); + return -ENODEV; + } + + ret = client->ops->read(client, + OV5640_SUB_CHIP_ID_LOW_REG, &chip_id_low); + if (ret || chip_id_low != OV5640_SUB_CHIP_ID_LOW) { + ISP_ERR("get chip id low[%d] fail\n", chip_id_low); + return -ENODEV; + } + + *chip_id = (chip_id_high<<8) | chip_id_low; + + return 0; +} + +int ov5640_sub_stream_on_off(struct isp_i2c_client_t *client, int enable) +{ + int ret; + + ISP_ERR("enable = %d mode = %d\n", enable, client->mode); + + if (enable) { + ret = ov5640_sub_change_mode(client, client->mode); + if (ret < 0) { + ISP_ERR("could not set mode %d\n", + client->mode); + return ret; + } + + ov5640_sub_set_aec_mode(client, 0); + ov5640_sub_set_awb(client, 1); + client->ops->write(client, OV5640_SUB_SYSTEM_CTRL0, + OV5640_SUB_SYSTEM_CTRL0_START); + } else { + client->ops->write(client, OV5640_SUB_SYSTEM_CTRL0, + OV5640_SUB_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +int ov5640_sub_power(struct isp_i2c_client_t *client, int on) +{ + int ret; + + __ov5640_sub_set_power(client->isp_dev, !!on); + + if (on) { + ret = ov5640_sub_init(client); + if (ret < 0) { + ISP_ERR("could not set init registers\n"); + return -1; + } + + client->ops->write(client, OV5640_SUB_SYSTEM_CTRL0, + OV5640_SUB_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +static int ov5640_sub_probe(struct platform_device *pdev) +{ + int ret; + struct isp_device *idev = NULL; + struct device *device = &pdev->dev; + struct device_node *np = device->of_node; + int index = 0; + unsigned int chip_id = 0; + struct isp_i2c_client_t *i2c_client = NULL; + + ISP_INFO("enter!\n"); + if (pdev->dev.parent) + idev = dev_get_drvdata(pdev->dev.parent); + + ret = of_property_read_u32(np, "sensor_index", (u32 *)&index); + if (ret < 0) { + ISP_ERR("no sensor index available\n"); + return -ENXIO; + } + + if (idev->sensor[index].probe == true) + return 0; + + idev->sensor[index].rst_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no reset pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + idev->sensor[index].pwdn_gpio = of_get_named_gpio(np, "pwdn-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no powerdown pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].pwdn_gpio, + GPIOF_OUT_INIT_LOW, "sensor-pwdn"); + if (ret < 0) + return ret; + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].rst_gpio, + GPIOF_OUT_INIT_LOW, "sensor-reset"); + if (ret < 0) + return ret; + + idev->sensor[index].addr = OV5640_SUB_SLAVE_ID; + idev->sensor[index].flag = I2C_ADDR_7BIT; + idev->sensor[index].speed_cfg = I2C_SPEED_STD; + idev->sensor[index].csi_index = 1; + idev->sensor[index].dt = YUV_DT_422_8BITS; + idev->sensor[index].pf = DF_2PF8; + idev->sensor[index].csi_lane = 2; + + idev->addr = idev->sensor[index].addr; + idev->flag = idev->sensor[index].flag; + idev->speed_cfg = idev->sensor[index].speed_cfg; + idev->csi_index = idev->sensor[index].csi_index; + idev->dt = idev->sensor[index].dt; + idev->pf = idev->sensor[index].pf; + idev->csi_lane = idev->sensor[index].csi_lane; + idev->pwdn_gpio = idev->sensor[index].pwdn_gpio; + idev->rst_gpio = idev->sensor[index].rst_gpio; + + isp_ispss_enable_clock(idev); + + mdelay(100); + + isp_ispss_reset_all(idev); + + mdelay(100); + + idev->client = create_isp_i2c_client(idev); + if (!idev->client) + return -ENODEV; + + i2c_client = idev->client; + ret = i2c_client->ops->open(i2c_client); + if (ret) { + destroy_isp_i2c_client(i2c_client); + return -EBUSY; + } + + ov5640_sub_power(i2c_client, 1); + ov5640_sub_get_chip_id(i2c_client, &chip_id); + ov5640_sub_power(i2c_client, 0); + i2c_client->ops->close(i2c_client); + destroy_isp_i2c_client(i2c_client); + + if (chip_id != 0x5640) { + ISP_ERR("chip id is 0x%x, probe failed\n", chip_id); + return -1; + } + + idev->sensor[index].probe = true; + idev->sensor[index].camera_sensor_power = ov5640_sub_power; + idev->sensor[index].camera_sensor_set_fmt = ov5640_sub_set_fmt; + idev->sensor[index].camera_stream_on_off = ov5640_sub_stream_on_off; + + ISP_INFO("exit!\n"); + + return 0; +} + +static int ov5640_sub_remove(struct platform_device *pdev) +{ + ISP_INFO("enter!\n"); + + + ISP_INFO("exit!\n"); + return 0; +} + +static const struct of_device_id ov5640_sub_match_table[] = { + { + .compatible = "hisilicon,ov5640_sub", + .data = NULL, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov5640_sub_match_table); + +static struct platform_driver ov5640_sub_driver = { + .probe = ov5640_sub_probe, + .remove = ov5640_sub_remove, + .driver = { + .name = "hisi-ov5640_sub", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ov5640_sub_match_table), + }, +}; + +static int __init ov5640_sub_module_init(void) +{ + int ret = 0; + + ISP_INFO("enter!\n"); + + ret = platform_driver_register(&ov5640_sub_driver); + if (ret) + return ret; + + ISP_INFO("exit!\n"); + + return ret; +} + +static void __exit ov5640_sub_module_exit(void) +{ + ISP_INFO("enter!\n"); + + platform_driver_unregister(&ov5640_sub_driver); + + ISP_INFO("exit!\n"); +} + +late_initcall(ov5640_sub_module_init); +module_exit(ov5640_sub_module_exit); + +MODULE_DESCRIPTION("Hisilicon Isp Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/platform/hisilicon/isp/ov5640_sub.h b/drivers/media/platform/hisilicon/isp/ov5640_sub.h new file mode 100644 index 000000000000..392521ff40a6 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5640_sub.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_OV5640_SUB_H +#define HISI_OV5640_SUB_H + +#include "hisi_isp_core.h" +#include "hisi_isp_i2c.h" +#include "hisi_isp_common.h" +#include "hisi_isp_cvdr.h" + +#define OV5640_SUB_SLAVE_ID 0x78 + +#endif /* HISI_OV5640_H */ diff --git a/drivers/media/platform/hisilicon/isp/ov5645.c b/drivers/media/platform/hisilicon/isp/ov5645.c new file mode 100644 index 000000000000..798645080465 --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5645.c @@ -0,0 +1,839 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include "ov5645.h" + +#define OV5645_SYSTEM_CTRL0 0x3008 +#define OV5645_SYSTEM_CTRL0_START 0x02 +#define OV5645_SYSTEM_CTRL0_STOP 0x42 +#define OV5645_CHIP_ID_HIGH_REG 0x300A +#define OV5645_CHIP_ID_HIGH 0x56 +#define OV5645_CHIP_ID_LOW_REG 0x300B +#define OV5645_CHIP_ID_LOW 0x45 +#define OV5645_AWB_MANUAL_CONTROL 0x3406 +#define OV5645_AWB_MANUAL_ENABLE (1 << 0) +#define OV5645_AEC_PK_MANUAL 0x3503 +#define OV5645_AEC_MANUAL_ENABLE (1 << 0) +#define OV5645_AGC_MANUAL_ENABLE (1 << 1) +#define OV5645_TIMING_TC_REG20 0x3820 +#define OV5645_SENSOR_VFLIP (1 << 1) +#define OV5645_ISP_VFLIP (1 << 2) +#define OV5645_TIMING_TC_REG21 0x3821 +#define OV5645_SENSOR_MIRROR (1 << 1) +#define OV5645_PRE_ISP_TEST_SETTING_1 0x503d + +#define OV5645_SDE_SAT_U 0x5583 +#define OV5645_SDE_SAT_V 0x5584 + +enum ov5645_mode { + ov5645_mode_min = 0, + ov5645_mode_sxga_1280_960 = 0, + ov5645_mode_1080p_1920_1080 = 1, + ov5645_mode_full_2592_1944 = 2, + ov5645_mode_max = 2, +}; + +struct reg_value { + u16 reg; + u8 val; +}; + +struct ov5645_mode_info { + enum ov5645_mode mode; + u32 width; + u32 height; + struct reg_value *init_data_ptr; + u32 init_data_size; +}; + +static struct reg_value ov5645_global_init_setting[] = { + {0x3103, 0x11,}, + {0x3008, 0x82,}, + {0x3008, 0x42,}, + {0x3103, 0x03,}, + {0x3503, 0x07,}, + {0x3002, 0x1c,}, + {0x3006, 0xc3,}, + {0x300e, 0x45,}, + {0x3017, 0x00,}, + {0x3018, 0x00,}, + {0x302e, 0x0b,}, + {0x3037, 0x13,}, + {0x3108, 0x01,}, + {0x3611, 0x06,}, + {0x3500, 0x00,}, + {0x3501, 0x01,}, + {0x3502, 0x00,}, + {0x350a, 0x00,}, + {0x350b, 0x3f,}, + {0x3620, 0x33,}, + {0x3621, 0xe0,}, + {0x3622, 0x01,}, + {0x3630, 0x2e,}, + {0x3631, 0x00,}, + {0x3632, 0x32,}, + {0x3633, 0x52,}, + {0x3634, 0x70,}, + {0x3635, 0x13,}, + {0x3636, 0x03,}, + {0x3703, 0x5a,}, + {0x3704, 0xa0,}, + {0x3705, 0x1a,}, + {0x3709, 0x12,}, + {0x370b, 0x61,}, + {0x370f, 0x10,}, + {0x3715, 0x78,}, + {0x3717, 0x01,}, + {0x371b, 0x20,}, + {0x3731, 0x12,}, + {0x3901, 0x0a,}, + {0x3905, 0x02,}, + {0x3906, 0x10,}, + {0x3719, 0x86,}, + {0x3810, 0x00,}, + {0x3811, 0x10,}, + {0x3812, 0x00,}, + {0x3821, 0x01,}, + {0x3824, 0x01,}, + {0x3826, 0x03,}, + {0x3828, 0x08,}, + {0x3a19, 0xf8,}, + {0x3c01, 0x34,}, + {0x3c04, 0x28,}, + {0x3c05, 0x98,}, + {0x3c07, 0x07,}, + {0x3c09, 0xc2,}, + {0x3c0a, 0x9c,}, + {0x3c0b, 0x40,}, + {0x3c01, 0x34,}, + {0x4001, 0x02,}, + {0x4514, 0x00,}, + {0x4520, 0xb0,}, + {0x460b, 0x37,}, + {0x460c, 0x20,}, + {0x4818, 0x01,}, + {0x481d, 0xf0,}, + {0x481f, 0x50,}, + {0x4823, 0x70,}, + {0x4831, 0x14,}, + {0x5000, 0xa7,}, + {0x5001, 0x83,}, + {0x501d, 0x00,}, + {0x501f, 0x00,}, + {0x503d, 0x00,}, + {0x505c, 0x30,}, + {0x5181, 0x59,}, + {0x5183, 0x00,}, + {0x5191, 0xf0,}, + {0x5192, 0x03,}, + {0x5684, 0x10,}, + {0x5685, 0xa0,}, + {0x5686, 0x0c,}, + {0x5687, 0x78,}, + {0x5a00, 0x08,}, + {0x5a21, 0x00,}, + {0x5a24, 0x00,}, + {0x3008, 0x02,}, + {0x3503, 0x00,}, + {0x5180, 0xff,}, + {0x5181, 0xf2,}, + {0x5182, 0x00,}, + {0x5183, 0x14,}, + {0x5184, 0x25,}, + {0x5185, 0x24,}, + {0x5186, 0x09,}, + {0x5187, 0x09,}, + {0x5188, 0x0a,}, + {0x5189, 0x75,}, + {0x518a, 0x52,}, + {0x518b, 0xea,}, + {0x518c, 0xa8,}, + {0x518d, 0x42,}, + {0x518e, 0x38,}, + {0x518f, 0x56,}, + {0x5190, 0x42,}, + {0x5191, 0xf8,}, + {0x5192, 0x04,}, + {0x5193, 0x70,}, + {0x5194, 0xf0,}, + {0x5195, 0xf0,}, + {0x5196, 0x03,}, + {0x5197, 0x01,}, + {0x5198, 0x04,}, + {0x5199, 0x12,}, + {0x519a, 0x04,}, + {0x519b, 0x00,}, + {0x519c, 0x06,}, + {0x519d, 0x82,}, + {0x519e, 0x38,}, + {0x5381, 0x1e,}, + {0x5382, 0x5b,}, + {0x5383, 0x08,}, + {0x5384, 0x0a,}, + {0x5385, 0x7e,}, + {0x5386, 0x88,}, + {0x5387, 0x7c,}, + {0x5388, 0x6c,}, + {0x5389, 0x10,}, + {0x538a, 0x01,}, + {0x538b, 0x98,}, + {0x5300, 0x08,}, + {0x5301, 0x30,}, + {0x5302, 0x10,}, + {0x5303, 0x00,}, + {0x5304, 0x08,}, + {0x5305, 0x30,}, + {0x5306, 0x08,}, + {0x5307, 0x16,}, + {0x5309, 0x08,}, + {0x530a, 0x30,}, + {0x530b, 0x04,}, + {0x530c, 0x06,}, + {0x5480, 0x01,}, + {0x5481, 0x08,}, + {0x5482, 0x14,}, + {0x5483, 0x28,}, + {0x5484, 0x51,}, + {0x5485, 0x65,}, + {0x5486, 0x71,}, + {0x5487, 0x7d,}, + {0x5488, 0x87,}, + {0x5489, 0x91,}, + {0x548a, 0x9a,}, + {0x548b, 0xaa,}, + {0x548c, 0xb8,}, + {0x548d, 0xcd,}, + {0x548e, 0xdd,}, + {0x548f, 0xea,}, + {0x5490, 0x1d,}, + {0x5580, 0x02,}, + {0x5583, 0x40,}, + {0x5584, 0x10,}, + {0x5589, 0x10,}, + {0x558a, 0x00,}, + {0x558b, 0xf8,}, + {0x5800, 0x3f,}, + {0x5801, 0x16,}, + {0x5802, 0x0e,}, + {0x5803, 0x0d,}, + {0x5804, 0x17,}, + {0x5805, 0x3f,}, + {0x5806, 0x0b,}, + {0x5807, 0x06,}, + {0x5808, 0x04,}, + {0x5809, 0x04,}, + {0x580a, 0x06,}, + {0x580b, 0x0b,}, + {0x580c, 0x09,}, + {0x580d, 0x03,}, + {0x580e, 0x00,}, + {0x580f, 0x00,}, + {0x5810, 0x03,}, + {0x5811, 0x08,}, + {0x5812, 0x0a,}, + {0x5813, 0x03,}, + {0x5814, 0x00,}, + {0x5815, 0x00,}, + {0x5816, 0x04,}, + {0x5817, 0x09,}, + {0x5818, 0x0f,}, + {0x5819, 0x08,}, + {0x581a, 0x06,}, + {0x581b, 0x06,}, + {0x581c, 0x08,}, + {0x581d, 0x0c,}, + {0x581e, 0x3f,}, + {0x581f, 0x1e,}, + {0x5820, 0x12,}, + {0x5821, 0x13,}, + {0x5822, 0x21,}, + {0x5823, 0x3f,}, + {0x5824, 0x68,}, + {0x5825, 0x28,}, + {0x5826, 0x2c,}, + {0x5827, 0x28,}, + {0x5828, 0x08,}, + {0x5829, 0x48,}, + {0x582a, 0x64,}, + {0x582b, 0x62,}, + {0x582c, 0x64,}, + {0x582d, 0x28,}, + {0x582e, 0x46,}, + {0x582f, 0x62,}, + {0x5830, 0x60,}, + {0x5831, 0x62,}, + {0x5832, 0x26,}, + {0x5833, 0x48,}, + {0x5834, 0x66,}, + {0x5835, 0x44,}, + {0x5836, 0x64,}, + {0x5837, 0x28,}, + {0x5838, 0x66,}, + {0x5839, 0x48,}, + {0x583a, 0x2c,}, + {0x583b, 0x28,}, + {0x583c, 0x26,}, + {0x583d, 0xae,}, + {0x5025, 0x00,}, + {0x3a0f, 0x30,}, + {0x3a10, 0x28,}, + {0x3a1b, 0x30,}, + {0x3a1e, 0x26,}, + {0x3a11, 0x60,}, + {0x3a1f, 0x14,}, + {0x0601, 0x02,}, + {0x3008, 0x42,}, + {0x3008, 0x02}, +}; + +static struct reg_value ov5645_setting_sxga[] = { + {0x3612, 0xa9,}, + {0x3614, 0x50,}, + {0x3618, 0x00,}, + {0x3034, 0x18,}, + {0x3035, 0x21,}, + {0x3036, 0x70,}, + {0x3600, 0x09,}, + {0x3601, 0x43,}, + {0x3708, 0x66,}, + {0x370c, 0xc3,}, + {0x3800, 0x00,}, + {0x3801, 0x00,}, + {0x3802, 0x00,}, + {0x3803, 0x06,}, + {0x3804, 0x0a,}, + {0x3805, 0x3f,}, + {0x3806, 0x07,}, + {0x3807, 0x9d,}, + {0x3808, 0x05,}, + {0x3809, 0x00,}, + {0x380a, 0x03,}, + {0x380b, 0xc0,}, + {0x380c, 0x07,}, + {0x380d, 0x68,}, + {0x380e, 0x03,}, + {0x380f, 0xd8,}, + {0x3813, 0x06,}, + {0x3814, 0x31,}, + {0x3815, 0x31,}, + {0x3820, 0x47,}, + {0x3a02, 0x03,}, + {0x3a03, 0xd8,}, + {0x3a08, 0x01,}, + {0x3a09, 0xf8,}, + {0x3a0a, 0x01,}, + {0x3a0b, 0xa4,}, + {0x3a0e, 0x02,}, + {0x3a0d, 0x02,}, + {0x3a14, 0x03,}, + {0x3a15, 0xd8,}, + {0x3a18, 0x00,}, + {0x4004, 0x02,}, + {0x4005, 0x18,}, + {0x4300, 0x32,}, + {0x4202, 0x00,}, +}; + +static struct reg_value ov5645_setting_1080P[] = { + {0x3612, 0xab,}, + {0x3614, 0x50,}, + {0x3618, 0x04,}, + {0x3034, 0x18,}, + {0x3035, 0x11,}, + {0x3036, 0x54,}, + {0x3600, 0x08,}, + {0x3601, 0x33,}, + {0x3708, 0x63,}, + {0x370c, 0xc0,}, + {0x3800, 0x01,}, + {0x3801, 0x50,}, + {0x3802, 0x01,}, + {0x3803, 0xb2,}, + {0x3804, 0x08,}, + {0x3805, 0xef,}, + {0x3806, 0x05,}, + {0x3807, 0xf1,}, + {0x3808, 0x07,}, + {0x3809, 0x80,}, + {0x380a, 0x04,}, + {0x380b, 0x38,}, + {0x380c, 0x09,}, + {0x380d, 0xc4,}, + {0x380e, 0x04,}, + {0x380f, 0x60,}, + {0x3813, 0x04,}, + {0x3814, 0x11,}, + {0x3815, 0x11,}, + {0x3820, 0x47,}, + {0x4514, 0x88,}, + {0x3a02, 0x04,}, + {0x3a03, 0x60,}, + {0x3a08, 0x01,}, + {0x3a09, 0x50,}, + {0x3a0a, 0x01,}, + {0x3a0b, 0x18,}, + {0x3a0e, 0x03,}, + {0x3a0d, 0x04,}, + {0x3a14, 0x04,}, + {0x3a15, 0x60,}, + {0x3a18, 0x00,}, + {0x4004, 0x06,}, + {0x4005, 0x18,}, + {0x4300, 0x32,}, + {0x4202, 0x00,}, + {0x4837, 0x0b,}, +}; + +static struct reg_value ov5645_setting_full[] = { + {0x3612, 0xab,}, + {0x3614, 0x50,}, + {0x3618, 0x04,}, + {0x3034, 0x18,}, + {0x3035, 0x11,}, + {0x3036, 0x54,}, + {0x3600, 0x08,}, + {0x3601, 0x33,}, + {0x3708, 0x63,}, + {0x370c, 0xc0,}, + {0x3800, 0x00,}, + {0x3801, 0x00,}, + {0x3802, 0x00,}, + {0x3803, 0x00,}, + {0x3804, 0x0a,}, + {0x3805, 0x3f,}, + {0x3806, 0x07,}, + {0x3807, 0x9f,}, + {0x3808, 0x0a,}, + {0x3809, 0x20,}, + {0x380a, 0x07,}, + {0x380b, 0x98,}, + {0x380c, 0x0b,}, + {0x380d, 0x1c,}, + {0x380e, 0x07,}, + {0x380f, 0xb0,}, + {0x3813, 0x06,}, + {0x3814, 0x11,}, + {0x3815, 0x11,}, + {0x3820, 0x47,}, + {0x4514, 0x88,}, + {0x3a02, 0x07,}, + {0x3a03, 0xb0,}, + {0x3a08, 0x01,}, + {0x3a09, 0x27,}, + {0x3a0a, 0x00,}, + {0x3a0b, 0xf6,}, + {0x3a0e, 0x06,}, + {0x3a0d, 0x08,}, + {0x3a14, 0x07,}, + {0x3a15, 0xb0,}, + {0x3a18, 0x01,}, + {0x4004, 0x06,}, + {0x4005, 0x18,}, + {0x4300, 0x32,}, + {0x4837, 0x0b,}, + {0x4202, 0x00,}, +}; + +static struct ov5645_mode_info ov5645_mode_info_data[ov5645_mode_max + 1] = { + {ov5645_mode_sxga_1280_960, 1280, 960, + ov5645_setting_sxga, + ARRAY_SIZE(ov5645_setting_sxga)}, + {ov5645_mode_1080p_1920_1080, 1920, 1080, + ov5645_setting_1080P, + ARRAY_SIZE(ov5645_setting_1080P)}, + {ov5645_mode_full_2592_1944, 2592, 1944, + ov5645_setting_full, + ARRAY_SIZE(ov5645_setting_full)}, +}; + +static int ov5645_regulators_enable(struct isp_device *dev) +{ + int ret = 0; + + if (dev->io_regulator) { + ret = regulator_enable(dev->io_regulator); + if (ret < 0) { + ISP_ERR("set io voltage failed\n"); + return ret; + } + } + + return ret; +} + +static void ov5645_regulators_disable(struct isp_device *dev) +{ + if (dev->io_regulator) + regulator_disable(dev->io_regulator); +} + +void __ov5645_set_power(struct isp_device *dev, bool on) +{ + if (on) { + clk_prepare_enable(dev->xclk); + ov5645_regulators_enable(dev); + usleep_range(5000, 15000); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 1); + usleep_range(1000, 2000); + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 1); + msleep(20); + ISP_ERR("enter\n"); + } else { + if (dev->rst_gpio) + gpio_set_value(dev->rst_gpio, 0); + if (dev->pwdn_gpio) + gpio_set_value(dev->pwdn_gpio, 0); + ov5645_regulators_disable(dev); + clk_disable_unprepare(dev->xclk); + } +} + +static enum ov5645_mode ov5645_find_nearest_mode(int width, int height) +{ + int i; + + for (i = ov5645_mode_max; i >= 0; i--) { + if (ov5645_mode_info_data[i].width <= width && + ov5645_mode_info_data[i].height <= height) + break; + } + + if (i < 0) + i = 0; + + return (enum ov5645_mode)i; +} + +static int ov5645_set_register_array(struct isp_i2c_client_t *client, + struct reg_value *settings, + s32 num_settings) +{ + register u16 reg = 0; + register u8 val = 0; + int i, ret = 0; + + for (i = 0; i < num_settings; ++i, ++settings) { + reg = settings->reg; + val = settings->val; + + ret = client->ops->write(client, reg, val); + if (ret < 0) + goto err; + } +err: + return ret; +} + +static int ov5645_init(struct isp_i2c_client_t *client) +{ + struct reg_value *settings = NULL; + int num_settings = 0; + int ret; + + settings = ov5645_global_init_setting; + num_settings = ARRAY_SIZE(ov5645_global_init_setting); + ret = ov5645_set_register_array(client, settings, num_settings); + if (ret < 0) + return ret; + + return 0; +} + +static int +ov5645_change_mode(struct isp_i2c_client_t *client, enum ov5645_mode mode) +{ + struct reg_value *settings = NULL; + s32 num_settings = 0; + int ret = 0; + + settings = ov5645_mode_info_data[mode].init_data_ptr; + num_settings = ov5645_mode_info_data[mode].init_data_size; + ret = ov5645_set_register_array(client, settings, num_settings); + + return ret; +} + +static int ov5645_set_aec_mode(struct isp_i2c_client_t *client, int auto_mode) +{ + u8 val; + + client->ops->read(client, OV5645_AEC_PK_MANUAL, &val); + if (auto_mode) { + val |= OV5645_AEC_MANUAL_ENABLE; + } else { /* V4L2_EXPOSURE_MANUAL */ + val &= ~OV5645_AEC_MANUAL_ENABLE; + } + + return client->ops->write(client, OV5645_AEC_PK_MANUAL, val); +} + +static int ov5645_set_awb(struct isp_i2c_client_t *client, s32 enable_auto) +{ + u8 val; + + client->ops->read(client, OV5645_AWB_MANUAL_CONTROL, &val); + if (enable_auto) + val &= ~OV5645_AWB_MANUAL_ENABLE; + else + val |= OV5645_AWB_MANUAL_ENABLE; + + return client->ops->write(client, OV5645_AWB_MANUAL_CONTROL, val); +} + +int ov5645_set_fmt(struct isp_i2c_client_t *client, + unsigned int w, unsigned int h) +{ + enum ov5645_mode new_mode; + + new_mode = ov5645_find_nearest_mode(w, h); + + client->mode = new_mode; + + return 0; +} + +int ov5645_get_chip_id(struct isp_i2c_client_t *client, unsigned int *chip_id) +{ + int ret; + unsigned char chip_id_high, chip_id_low; + + ret = client->ops->read(client, OV5645_CHIP_ID_HIGH_REG, &chip_id_high); + if (ret || chip_id_high != OV5645_CHIP_ID_HIGH) { + ISP_ERR("get chip id high[%d] fail\n", chip_id_high); + return -ENODEV; + } + + ret = client->ops->read(client, OV5645_CHIP_ID_LOW_REG, &chip_id_low); + if (ret || chip_id_low != OV5645_CHIP_ID_LOW) { + ISP_ERR("get chip id low[%d] fail\n", chip_id_low); + return -ENODEV; + } + + *chip_id = (chip_id_high<<8) | chip_id_low; + + return 0; +} + +int ov5645_stream_on_off(struct isp_i2c_client_t *client, int enable) +{ + int ret; + + ISP_ERR("enable = %d mode = %d\n", enable, client->mode); + + if (enable) { + ret = ov5645_change_mode(client, client->mode); + if (ret < 0) { + ISP_ERR("could not set mode %d\n", + client->mode); + return ret; + } + + ov5645_set_aec_mode(client, 0); + ov5645_set_awb(client, 1); + + client->ops->write(client, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_START); + } else { + client->ops->write(client, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +int ov5645_power(struct isp_i2c_client_t *client, int on) +{ + int ret; + + __ov5645_set_power(client->isp_dev, !!on); + + if (on) { + ret = ov5645_init(client); + if (ret < 0) { + ISP_ERR("could not set init registers\n"); + return -1; + } + + client->ops->write(client, OV5645_SYSTEM_CTRL0, + OV5645_SYSTEM_CTRL0_STOP); + } + + return 0; +} + +static int ov5645_probe(struct platform_device *pdev) +{ + int ret; + struct isp_device *idev = NULL; + struct device *device = &pdev->dev; + struct device_node *np = device->of_node; + int index = 0; + unsigned int chip_id = 0; + struct isp_i2c_client_t *i2c_client = NULL; + + ISP_INFO("enter!\n"); + if (pdev->dev.parent) + idev = dev_get_drvdata(pdev->dev.parent); + + if (!idev) + return -ENXIO; + + ret = of_property_read_u32(np, "sensor_index", (u32 *)&index); + if (ret < 0) { + ISP_ERR("no sensor index available\n"); + return -ENXIO; + } + + if (idev->sensor[index].probe == true) + return 0; + + idev->sensor[index].rst_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no reset pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + idev->sensor[index].pwdn_gpio = of_get_named_gpio(np, "pwdn-gpio", 0); + if (!gpio_is_valid(idev->sensor[index].rst_gpio)) { + ISP_ERR("no powerdown pin available\n"); + idev->sensor[index].rst_gpio = 0; + return -ENXIO; + } + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].rst_gpio, + GPIOF_OUT_INIT_LOW, "sensor-reset"); + if (ret < 0) + return ret; + + ret = devm_gpio_request_one(&pdev->dev, + idev->sensor[index].pwdn_gpio, + GPIOF_OUT_INIT_LOW, "sensor-pwdn"); + if (ret < 0) + return ret; + + idev->sensor[index].addr = OV5645_SLAVE_ID; + idev->sensor[index].flag = I2C_ADDR_7BIT; + idev->sensor[index].speed_cfg = I2C_SPEED_STD; + idev->sensor[index].csi_index = 0; + idev->sensor[index].dt = YUV_DT_422_8BITS; + idev->sensor[index].pf = DF_2PF8; + idev->sensor[index].csi_lane = 2; + + idev->addr = idev->sensor[index].addr; + idev->flag = idev->sensor[index].flag; + idev->speed_cfg = idev->sensor[index].speed_cfg; + idev->csi_index = idev->sensor[index].csi_index; + idev->dt = idev->sensor[index].dt; + idev->pf = idev->sensor[index].pf; + idev->csi_lane = idev->sensor[index].csi_lane; + idev->pwdn_gpio = idev->sensor[index].pwdn_gpio; + idev->rst_gpio = idev->sensor[index].rst_gpio; + + isp_ispss_enable_clock(idev); + + mdelay(100); + + isp_ispss_reset_all(idev); + + mdelay(100); + + idev->client = create_isp_i2c_client(idev); + if (!idev->client) + return -ENODEV; + + i2c_client = idev->client; + ret = i2c_client->ops->open(i2c_client); + if (ret) { + destroy_isp_i2c_client(i2c_client); + return -EBUSY; + } + + ov5645_power(i2c_client, 1); + ov5645_get_chip_id(i2c_client, &chip_id); + ov5645_power(i2c_client, 0); + i2c_client->ops->close(i2c_client); + destroy_isp_i2c_client(i2c_client); + ISP_ERR("chip id is 0x%x\n", chip_id); + if (chip_id != 0x5645) { + ISP_ERR("chip id is 0x%x, probe failed\n", chip_id); + return -1; + } + + idev->sensor[index].probe = true; + idev->sensor[index].camera_sensor_power = ov5645_power; + idev->sensor[index].camera_sensor_set_fmt = ov5645_set_fmt; + idev->sensor[index].camera_stream_on_off = ov5645_stream_on_off; + + ISP_INFO("exit!\n"); + + return 0; +} + +static int ov5645_remove(struct platform_device *pdev) +{ + ISP_INFO("enter!\n"); + + + ISP_INFO("exit!\n"); + return 0; +} + +static const struct of_device_id ov5645_match_table[] = { + { + .compatible = "hisilicon,ov5645", + .data = NULL, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ov5645_match_table); + +static struct platform_driver ov5645_driver = { + .probe = ov5645_probe, + .remove = ov5645_remove, + .driver = { + .name = "hisi-ov5645", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ov5645_match_table), + }, +}; + +static int __init ov5645_module_init(void) +{ + int ret = 0; + + ISP_INFO("enter!\n"); + + ret = platform_driver_register(&ov5645_driver); + if (ret) + return ret; + + ISP_INFO("exit!\n"); + + return ret; +} + +static void __exit ov5645_module_exit(void) +{ + ISP_INFO("enter!\n"); + + platform_driver_unregister(&ov5645_driver); + + ISP_INFO("exit!\n"); +} + +late_initcall(ov5645_module_init); +module_exit(ov5645_module_exit); + +MODULE_DESCRIPTION("Hisilicon Isp Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/platform/hisilicon/isp/ov5645.h b/drivers/media/platform/hisilicon/isp/ov5645.h new file mode 100644 index 000000000000..6e4af2fecaff --- /dev/null +++ b/drivers/media/platform/hisilicon/isp/ov5645.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2017 Linaro Ltd. + * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef HISI_OV5645_H +#define HISI_OV5645_H + +#include "hisi_isp_core.h" +#include "hisi_isp_i2c.h" +#include "hisi_isp_common.h" +#include "hisi_isp_cvdr.h" + +#define OV5645_SLAVE_ID 0x78 + +#endif /* HISI_OV5645_H */