media: Hisilicon isp: add Hisilicon isp driver on Hikey970 board. support for yuv sensor ov5640 & D3 Camera board.

Signed-off-by: xwx495457 <xwx495457@notesmail.huawei.com>
This commit is contained in:
xwx495457
2018-02-12 11:32:36 +08:00
committed by Guodong Xu
parent bb3fd43c36
commit b18aa48f36
32 changed files with 5898 additions and 4 deletions
+4 -4
View File
@@ -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
+2
View File
@@ -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
+2
View File
@@ -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/
+5
View File
@@ -0,0 +1,5 @@
#
# hisilicon isp device configuration.
# Please keep this list sorted alphabetically
source "drivers/media/platform/hisilicon/isp/Kconfig"
@@ -0,0 +1,5 @@
#
# Makefile for hisilicon isp drivers.
# Please keep this list sorted alphabetically
obj-$(CONFIG_VIDEO_HISI_ISP) += isp/
@@ -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
@@ -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
@@ -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;
}
@@ -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 */
@@ -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 <linux/kthread.h>
#include <asm/cacheflush.h>
#include <linux/dma-contiguous.h>
#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");
@@ -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 <linux/sched.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/file.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/of_irq.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/compat.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <linux/regulator/consumer.h>
#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 */
@@ -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;
}
@@ -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 */
@@ -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
@@ -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;
}
@@ -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 */
@@ -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__ */
@@ -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__ */
@@ -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);
@@ -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 */
@@ -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;
}
@@ -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 */
@@ -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 */
@@ -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);
}
@@ -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 */
@@ -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
@@ -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");
@@ -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 */
@@ -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");
@@ -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 */
@@ -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");
@@ -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 */