media: Import xiaomi camera ispv3 driver
Change-Id: If359c12219100477c49d0bac563158921754c6ee Signed-off-by: Jens Reidel <adrian@travitia.xyz>
This commit is contained in:
parent
7af371ea3a
commit
48fa558e2a
@ -586,3 +586,4 @@ config VIDEO_RCAR_DRIF
|
||||
endif # SDR_PLATFORM_DRIVERS
|
||||
|
||||
source "drivers/media/platform/msm/Kconfig"
|
||||
source "drivers/media/platform/xiaomi/Kconfig"
|
||||
|
@ -82,3 +82,4 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
|
||||
obj-y += sunxi/
|
||||
|
||||
obj-y += msm/
|
||||
obj-y += xiaomi/
|
||||
|
1
drivers/media/platform/xiaomi/Kconfig
Normal file
1
drivers/media/platform/xiaomi/Kconfig
Normal file
@ -0,0 +1 @@
|
||||
source "drivers/media/platform/xiaomi/ispv3/Kconfig"
|
2
drivers/media/platform/xiaomi/Makefile
Normal file
2
drivers/media/platform/xiaomi/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_ISPV3) += ispv3/
|
||||
#obj-m += ispv3/
|
6
drivers/media/platform/xiaomi/ispv3/Kconfig
Normal file
6
drivers/media/platform/xiaomi/ispv3/Kconfig
Normal file
@ -0,0 +1,6 @@
|
||||
config ISPV3
|
||||
tristate "XIAOMI isp for SN/MFNR capture and SN video support"
|
||||
depends on PCI
|
||||
depends on PCI_MSM
|
||||
help
|
||||
Enable support for XIAOMI isp SN/MFNR capture and SN video
|
6
drivers/media/platform/xiaomi/ispv3/Makefile
Normal file
6
drivers/media/platform/xiaomi/ispv3/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
TARGET=ispv3_mfd_dev
|
||||
obj-$(CONFIG_ISPV3)+=$(TARGET).o
|
||||
$(TARGET)-objs:=ispv3_dev.o ispv3_power.o
|
||||
obj-$(CONFIG_ISPV3)+=ispv3_cam_dev.o
|
||||
#obj-$(CONFIG_ISPV3) += ispv3_dev.o ispv3_power.o ispv3_cam_dev.o
|
||||
|
263
drivers/media/platform/xiaomi/ispv3/ispv3_cam_dev.c
Normal file
263
drivers/media/platform/xiaomi/ispv3/ispv3_cam_dev.c
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "ispv3_cam_dev.h"
|
||||
|
||||
static int ispv3_open(struct file *file)
|
||||
{
|
||||
int ret;
|
||||
struct ispv3_v4l2_dev *priv = video_drvdata(file);
|
||||
|
||||
if (!priv)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&priv->isp_lock);
|
||||
ret = v4l2_fh_open(file);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
priv->open_cnt++;
|
||||
mutex_unlock(&priv->isp_lock);
|
||||
|
||||
dev_info(priv->dev, "ispv3 open cnt = %d", priv->open_cnt);
|
||||
return ret;
|
||||
|
||||
end:
|
||||
mutex_unlock(&priv->isp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __poll_t ispv3_poll(struct file *file,
|
||||
struct poll_table_struct *pll_table)
|
||||
{
|
||||
struct ispv3_v4l2_dev *priv = video_drvdata(file);
|
||||
int ret = 0;
|
||||
|
||||
poll_wait(file, &priv->wait, pll_table);
|
||||
|
||||
if (atomic_read(&priv->int_sof)) {
|
||||
atomic_set(&priv->int_sof, 0);
|
||||
ret = POLLOUT | POLLWRNORM;
|
||||
}
|
||||
|
||||
if (atomic_read(&priv->int_eof)) {
|
||||
atomic_set(&priv->int_eof, 0);
|
||||
ret = POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_close(struct file *file)
|
||||
{
|
||||
struct ispv3_v4l2_dev *priv = video_drvdata(file);
|
||||
|
||||
mutex_lock(&priv->isp_lock);
|
||||
|
||||
if (priv->open_cnt <= 0) {
|
||||
mutex_unlock(&priv->isp_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->open_cnt--;
|
||||
v4l2_fh_release(file);
|
||||
mutex_unlock(&priv->isp_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* maps the PCIe BAR into user space for memory-like access using mmap() */
|
||||
static int ispv3_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct ispv3_v4l2_dev *priv = video_drvdata(file);
|
||||
struct ispv3_data *data = priv->pdata;
|
||||
unsigned long vsize;
|
||||
unsigned long paddr;
|
||||
unsigned long psize;
|
||||
unsigned long off;
|
||||
int ret;
|
||||
|
||||
off = vma->vm_pgoff << PAGE_SHIFT;
|
||||
vsize = vma->vm_end - vma->vm_start;
|
||||
|
||||
switch (off) {
|
||||
case ISP_MITOP_REG_MAPOFFSET:
|
||||
paddr = data->bar_res[0].start;
|
||||
psize = MITOP_REG_SIZE;
|
||||
break;
|
||||
case ISP_MIPORT_REG_MAPOFFSET:
|
||||
paddr = data->bar_res[0].start + MIPORT_REG_OFFSET;
|
||||
psize = MIPORT_REG_SIZE;
|
||||
break;
|
||||
case ISP_MITOP_OCRAM_MAPOFFSET:
|
||||
paddr = data->bar_res[1].start;
|
||||
psize = MITOP_OCRAM_SIZE;
|
||||
break;
|
||||
case ISP_MITOP_DDR_MAPOFFSET:
|
||||
paddr = data->bar_res[2].start;
|
||||
psize = MITOP_DDR_SIZE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
if (vsize > psize)
|
||||
return -EINVAL;
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
|
||||
|
||||
ret = io_remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT,
|
||||
vsize, vma->vm_page_prot);
|
||||
|
||||
dev_dbg(priv->dev, "vma=%ps, vma->vm_start=0x%lx, phys=0x%lx, vsize=%lu, psize=%lu, rv=%d\n",
|
||||
vma, vma->vm_start, paddr >> PAGE_SHIFT, vsize, psize, ret);
|
||||
|
||||
if (ret)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_file_operations ispv3_v4l2_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ispv3_open,
|
||||
.poll = ispv3_poll,
|
||||
.release = ispv3_close,
|
||||
.mmap = ispv3_mmap,
|
||||
};
|
||||
|
||||
static int isp_v4l2_device_setup(struct ispv3_v4l2_dev *priv)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* register v4l2 device */
|
||||
priv->v4l2_dev = kzalloc(sizeof(*priv->v4l2_dev),
|
||||
GFP_KERNEL);
|
||||
if (!priv->v4l2_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = v4l2_device_register(priv->dev, priv->v4l2_dev);
|
||||
if (ret)
|
||||
goto v4l2_fail;
|
||||
|
||||
/* register media device */
|
||||
priv->v4l2_dev->mdev = kzalloc(sizeof(*priv->v4l2_dev->mdev), GFP_KERNEL);
|
||||
if (!priv->v4l2_dev->mdev) {
|
||||
ret = -ENOMEM;
|
||||
goto v4l2_fail;
|
||||
}
|
||||
|
||||
media_device_init(priv->v4l2_dev->mdev);
|
||||
priv->v4l2_dev->mdev->dev = priv->dev;
|
||||
strlcpy(priv->v4l2_dev->mdev->model, ISPV3_VNODE_NAME,
|
||||
sizeof(priv->v4l2_dev->mdev->model));
|
||||
|
||||
ret = media_device_register(priv->v4l2_dev->mdev);
|
||||
if (ret)
|
||||
goto media_fail;
|
||||
|
||||
/* register video device */
|
||||
priv->video = video_device_alloc();
|
||||
if (!priv->video) {
|
||||
ret = -ENOMEM;
|
||||
goto media_fail;
|
||||
}
|
||||
|
||||
priv->video->v4l2_dev = priv->v4l2_dev;
|
||||
|
||||
strlcpy(priv->video->name, "ispv3", sizeof(priv->video->name));
|
||||
priv->video->release = video_device_release;
|
||||
priv->video->fops = &ispv3_v4l2_fops;
|
||||
priv->video->minor = -1;
|
||||
priv->video->vfl_type = VFL_TYPE_VIDEO;
|
||||
priv->video->device_caps = V4L2_CAP_VIDEO_CAPTURE;
|
||||
ret = video_register_device(priv->video, VFL_TYPE_VIDEO, -1);
|
||||
if (ret)
|
||||
goto video_fail;
|
||||
|
||||
video_set_drvdata(priv->video, priv);
|
||||
|
||||
ret = media_entity_pads_init(&priv->video->entity, 0, NULL);
|
||||
if (ret)
|
||||
goto entity_fail;
|
||||
|
||||
priv->video->entity.function = ISP_VNODE_DEVICE_TYPE;
|
||||
priv->video->entity.name = video_device_node_name(priv->video);
|
||||
|
||||
return ret;
|
||||
|
||||
entity_fail:
|
||||
video_unregister_device(priv->video);
|
||||
video_fail:
|
||||
video_device_release(priv->video);
|
||||
priv->video = NULL;
|
||||
media_fail:
|
||||
kfree(priv->v4l2_dev->mdev);
|
||||
priv->v4l2_dev->mdev = NULL;
|
||||
v4l2_fail:
|
||||
kfree(priv->v4l2_dev);
|
||||
priv->v4l2_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_v4l2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ispv3_v4l2_dev *priv;
|
||||
struct ispv3_data *data;
|
||||
int ret = -EIO;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct ispv3_v4l2_dev),
|
||||
GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
data = dev_get_drvdata(pdev->dev.parent);
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "The ispv3 data struct is NULL!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_init(&priv->isp_lock);
|
||||
priv->pdata = data;
|
||||
priv->open_cnt = 0;
|
||||
priv->dev = &pdev->dev;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = isp_v4l2_device_setup(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ispv3_v4l2_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ispv3_v4l2_driver = {
|
||||
.probe = ispv3_v4l2_probe,
|
||||
.remove = ispv3_v4l2_remove,
|
||||
.driver = {
|
||||
.name = "ispv3-v4l2",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ispv3_v4l2_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
30
drivers/media/platform/xiaomi/ispv3/ispv3_cam_dev.h
Normal file
30
drivers/media/platform/xiaomi/ispv3/ispv3_cam_dev.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
#ifndef _ISPV3_CAM_DEV_H_
|
||||
#define _ISPV3_CAM_DEV_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/ispv3_defs.h>
|
||||
#include <linux/mfd/ispv3_dev.h>
|
||||
|
||||
#define DRV_NAME "ispv3-v4l2"
|
||||
|
||||
struct ispv3_v4l2_dev {
|
||||
struct v4l2_device *v4l2_dev;
|
||||
struct video_device *video;
|
||||
struct ispv3_data *pdata;
|
||||
wait_queue_head_t wait;
|
||||
struct mutex isp_lock;
|
||||
struct device *dev;
|
||||
atomic_t int_sof;
|
||||
atomic_t int_eof;
|
||||
int open_cnt;
|
||||
};
|
||||
|
||||
#endif
|
902
drivers/media/platform/xiaomi/ispv3/ispv3_dev.c
Normal file
902
drivers/media/platform/xiaomi/ispv3/ispv3_dev.c
Normal file
@ -0,0 +1,902 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/aer.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/mfd/core.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include "ispv3_cam_dev.h"
|
||||
|
||||
#define DRIVER_NAME "xiaomi_ispv3"
|
||||
static struct ispv3_data *ispv3_dev_info;
|
||||
|
||||
static struct resource ispv3_rpmsg_res[RPMSG_RES_NUM];
|
||||
static struct resource ispv3_cam_res[CAM_RES_NUM];
|
||||
|
||||
static struct mfd_cell ispv3_v4l2_cell = {
|
||||
.name = "ispv3-v4l2",
|
||||
.ignore_resource_conflicts = true,
|
||||
};
|
||||
|
||||
static struct mfd_cell ispv3_rpmsg_pci_cell = {
|
||||
.name = "ispv3-rpmsg_pci",
|
||||
.num_resources = ARRAY_SIZE(ispv3_rpmsg_res),
|
||||
.resources = ispv3_rpmsg_res,
|
||||
.ignore_resource_conflicts = true,
|
||||
};
|
||||
|
||||
static struct mfd_cell ispv3_rpmsg_spi_cell = {
|
||||
.name = "ispv3-rpmsg_spi",
|
||||
.num_resources = ARRAY_SIZE(ispv3_rpmsg_res),
|
||||
.resources = ispv3_rpmsg_res,
|
||||
.ignore_resource_conflicts = true,
|
||||
};
|
||||
|
||||
static struct mfd_cell ispv3_cam_cell = {
|
||||
.name = "ispv3-cam",
|
||||
.num_resources = ARRAY_SIZE(ispv3_cam_res),
|
||||
.resources = ispv3_cam_res,
|
||||
.ignore_resource_conflicts = true,
|
||||
};
|
||||
|
||||
static struct ispv3_data *ispv3_get_plat_priv(void);
|
||||
static void ispv3_set_plat_priv(struct ispv3_data *plat_priv);
|
||||
|
||||
static pci_ers_result_t ispv3_io_error_detected(struct pci_dev *pdev,
|
||||
pci_channel_state_t state)
|
||||
{
|
||||
if (state == pci_channel_io_perm_failure)
|
||||
return PCI_ERS_RESULT_DISCONNECT;
|
||||
|
||||
pci_disable_device(pdev);
|
||||
|
||||
/* Request a slot reset. */
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
}
|
||||
|
||||
static pci_ers_result_t ispv3_io_slot_reset(struct pci_dev *pdev)
|
||||
{
|
||||
pci_ers_result_t result;
|
||||
int err;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot re-enable PCI device after reset.\n");
|
||||
result = PCI_ERS_RESULT_DISCONNECT;
|
||||
} else {
|
||||
pdev->state_saved = true;
|
||||
pci_restore_state(pdev);
|
||||
pci_set_master(pdev);
|
||||
|
||||
result = PCI_ERS_RESULT_RECOVERED;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static const struct pci_error_handlers ispv3_err_handler = {
|
||||
.error_detected = ispv3_io_error_detected,
|
||||
.slot_reset = ispv3_io_slot_reset,
|
||||
};
|
||||
|
||||
static int ispv3_init_rpmsg_callback(struct ispv3_data *pdata)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device *pdev = pdata->dev;
|
||||
|
||||
ret = mfd_add_devices(pdev, PLATFORM_DEVID_NONE,
|
||||
&ispv3_rpmsg_spi_cell, 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(pdev, "MFD add rpmsg devices failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_info(pdev, "ispv3 rpmsg device registered.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ispv3_init_v4l2(struct pci_dev *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||
&ispv3_v4l2_cell, 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "MFD add v4l2 devices failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_info(&pdev->dev, "ispv3 v4l2 device registered.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ispv3_init_rpmsg(struct ispv3_data *pdata)
|
||||
{
|
||||
int ret = 0, idx = 0;
|
||||
struct pci_dev *pdev = pdata->pci;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(ispv3_rpmsg_res); idx++) {
|
||||
switch (idx) {
|
||||
case RPMSG_RAM:
|
||||
ispv3_rpmsg_res[idx].flags = IORESOURCE_MEM;
|
||||
ispv3_rpmsg_res[idx].start = pci_resource_start(pdev, BAR_2) +
|
||||
OCRAM_OFFSET;
|
||||
ispv3_rpmsg_res[idx].end = pci_resource_start(pdev, BAR_2) +
|
||||
OCRAM_OFFSET + RPMSG_SIZE - 1;
|
||||
break;
|
||||
case RPMSG_IRQ:
|
||||
ispv3_rpmsg_res[idx].flags = IORESOURCE_IRQ;
|
||||
ispv3_rpmsg_res[idx].start = pdev->irq;
|
||||
ispv3_rpmsg_res[idx].end = pdev->irq;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid parameter!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||
&ispv3_rpmsg_pci_cell, 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "MFD add rpmsg devices failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_info(&pdev->dev, "ispv3 rpmsg device registered.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ispv3_init_camera(struct pci_dev *pdev, struct ispv3_data *priv)
|
||||
{
|
||||
int ret = 0, idx = 0;
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(ispv3_cam_res); idx++) {
|
||||
switch (idx) {
|
||||
case ISP_RAM:
|
||||
ispv3_cam_res[idx].flags = IORESOURCE_MEM;
|
||||
ispv3_cam_res[idx].start = pci_resource_start(pdev, BAR_2) +
|
||||
OCRAM_OFFSET + RPMSG_SIZE;
|
||||
ispv3_cam_res[idx].end = pci_resource_end(pdev, BAR_2);
|
||||
break;
|
||||
case ISP_DDR:
|
||||
ispv3_cam_res[idx].flags = IORESOURCE_MEM;
|
||||
ispv3_cam_res[idx].start = pci_resource_start(pdev, BAR_4);
|
||||
ispv3_cam_res[idx].end = pci_resource_end(pdev, BAR_4);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Invalid parameter!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ispv3_cam_cell.platform_data = (void *)priv;
|
||||
ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
|
||||
&ispv3_cam_cell, 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "MFD add camera device failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev_info(&pdev->dev, "ispv3 cam device registered.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ispv3_init_bar_resource(struct pci_dev *pdev, struct ispv3_data *priv)
|
||||
{
|
||||
int idx = 0;
|
||||
int bar_idx = 0;
|
||||
struct resource *res = priv->bar_res;
|
||||
|
||||
for (idx = 0; idx < ISPV3_BAR_NUM; idx++) {
|
||||
|
||||
res->flags = IORESOURCE_MEM;
|
||||
res->start = pci_resource_start(pdev, bar_idx);
|
||||
res->end = pci_resource_end(pdev, bar_idx);
|
||||
|
||||
bar_idx += 2;
|
||||
res++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ispv3_alloc_irq_vectors(struct pci_dev *pdev, struct ispv3_data *priv)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
bool res = true;
|
||||
int irq = -1;
|
||||
|
||||
switch (priv->pci_irq_type) {
|
||||
case IRQ_TYPE_LEGACY:
|
||||
irq = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "Failed to get Legacy interrupt!\n");
|
||||
break;
|
||||
case IRQ_TYPE_MSI:
|
||||
irq = pci_alloc_irq_vectors(pdev, 1, 3, PCI_IRQ_MSI);
|
||||
if (irq < 0)
|
||||
dev_err(dev, "Failed to get MSI interrupts!\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid IRQ type selected.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (irq < 0) {
|
||||
irq = 0;
|
||||
res = false;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void ispv3_free_irq_vectors(struct pci_dev *pdev)
|
||||
{
|
||||
pci_free_irq_vectors(pdev);
|
||||
}
|
||||
|
||||
static int ispv3_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int idx, ret;
|
||||
struct ispv3_data *priv = ispv3_get_plat_priv();
|
||||
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
for (idx = PCI_STD_RESOURCES; idx < (PCI_STD_RESOURCE_END + 1); idx++) {
|
||||
ret = pci_assign_resource(pdev, idx);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "assign pci resource failed!\n");
|
||||
|
||||
if (pci_resource_flags(pdev, idx) & IORESOURCE_MEM_64)
|
||||
idx = idx + 1;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "enable pci device failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"DMA configuration failed: 0x%x\n", ret);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
/* set up pci connections */
|
||||
ret = pci_request_selected_regions(pdev, ((1 << 1) - 1), DRIVER_NAME);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"pci_request_selected_regions failed %d\n", ret);
|
||||
goto err_disable;
|
||||
}
|
||||
|
||||
if (pci_resource_flags(pdev, BAR_0) & IORESOURCE_MEM) {
|
||||
priv->base = pci_ioremap_bar(pdev, BAR_0);
|
||||
|
||||
if (!priv->base) {
|
||||
dev_err(&pdev->dev, "Failed to map the register of BAR0!\n");
|
||||
goto err_release_region;
|
||||
}
|
||||
}
|
||||
|
||||
pci_enable_pcie_error_reporting(pdev);
|
||||
pci_set_master(pdev);
|
||||
|
||||
priv->pci = pdev;
|
||||
pci_save_state(pdev);
|
||||
priv->default_state = pci_store_saved_state(pdev);
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
atomic_set(&priv->pci_link_state, ISPV3_PCI_LINK_UP);
|
||||
|
||||
priv->pci_irq_type = IRQ_TYPE_MSI;
|
||||
if (!ispv3_alloc_irq_vectors(pdev, priv))
|
||||
goto err_disable;
|
||||
|
||||
ispv3_init_bar_resource(pdev, priv);
|
||||
|
||||
priv->remote_callback = ispv3_init_rpmsg;
|
||||
|
||||
ret = ispv3_init_camera(pdev, priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "init cam mfd failed: %d\n", ret);
|
||||
goto err_disable_irq;
|
||||
}
|
||||
|
||||
ret = ispv3_init_v4l2(pdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "init v4l2 mfd failed: %d\n", ret);
|
||||
goto err_disable_irq;
|
||||
}
|
||||
dev_info(&pdev->dev, "ispv3 all ispv3 mfd devices registered.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_irq:
|
||||
ispv3_free_irq_vectors(pdev);
|
||||
|
||||
err_release_region:
|
||||
pci_release_selected_regions(pdev, ((1 << 1) - 1));
|
||||
|
||||
err_disable:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static void ispv3_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct ispv3_data *priv = ispv3_get_plat_priv();
|
||||
|
||||
if (priv->saved_state)
|
||||
pci_load_and_free_saved_state(pdev, &priv->saved_state);
|
||||
pci_load_and_free_saved_state(pdev, &priv->default_state);
|
||||
mfd_remove_devices(&pdev->dev);
|
||||
pci_disable_pcie_error_reporting(pdev);
|
||||
pci_release_selected_regions(pdev, ((1 << 1) - 1));
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static void ispv3_shutdown(struct pci_dev *pdev)
|
||||
{
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id ispv3_pci_tbl[] = {
|
||||
{ PCI_DEVICE(ISPV3_PCI_VENDOR_ID, ISPV3_PCI_DEVICE_ID) },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, ispv3_pci_tbl);
|
||||
|
||||
static struct pci_driver ispv3_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = ispv3_pci_tbl,
|
||||
.probe = ispv3_pci_probe,
|
||||
.remove = ispv3_pci_remove,
|
||||
.shutdown = ispv3_shutdown,
|
||||
.err_handler = &ispv3_err_handler,
|
||||
|
||||
};
|
||||
|
||||
int ispv3_pci_init(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* enumerate it on PCIE */
|
||||
ret = msm_pcie_enumerate(data->rc_index);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "ispv3 PCIE enumeration failed! Not PCIE mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->interface_type = ISPV3_PCIE;
|
||||
|
||||
ret = pci_register_driver(&ispv3_pci_driver);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to register PCIe driver!\n");
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ispv3_pci_deinit(u32 rc_index)
|
||||
{
|
||||
pci_unregister_driver(&ispv3_pci_driver);
|
||||
}
|
||||
|
||||
static uint32_t RGLTR_COUNT[2] = {
|
||||
ISPV3_SOC_8450_RGLTR_COUNT,
|
||||
ISPV3_SOC_8475_RGLTR_COUNT,
|
||||
};
|
||||
|
||||
static int ispv3_get_dt_regulator_info(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0, count = 0, i = 0;
|
||||
struct device_node *of_node = data->dev->of_node;
|
||||
|
||||
if (!data || !data->dev) {
|
||||
dev_err(data->dev, "Invalid parameters!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->num_rgltr = 0;
|
||||
count = of_property_count_strings(of_node, "regulator-names");
|
||||
if (count != RGLTR_COUNT[data->soc_id]) {
|
||||
dev_err(data->dev, "regulators num error!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data->num_rgltr = count;
|
||||
|
||||
for (i = 0; i < data->num_rgltr; i++) {
|
||||
ret = of_property_read_string_index(of_node,
|
||||
"regulator-names", i, &data->rgltr_name[i]);
|
||||
dev_dbg(data->dev, "rgltr_name[%d] = %s\n",
|
||||
i, data->rgltr_name[i]);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "no regulator resource at cnt=%d\n",
|
||||
i);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(of_node, "rgltr-min-voltage",
|
||||
data->rgltr_min_volt, data->num_rgltr);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "No min volatage value found, ret=%d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(of_node, "rgltr-max-voltage",
|
||||
data->rgltr_max_volt, data->num_rgltr);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "No max volatage value found, ret=%d\n", ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->num_rgltr; i++) {
|
||||
data->rgltr[i] = regulator_get(data->dev, data->rgltr_name[i]);
|
||||
if (IS_ERR_OR_NULL(data->rgltr[i])) {
|
||||
dev_err(data->dev, "Regulator %s get failed!\n",
|
||||
data->rgltr_name[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_get_dt_clk_info(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
int count;
|
||||
int num_clk_rates;
|
||||
struct device_node *of_node = data->dev->of_node;
|
||||
|
||||
if (!data || !data->dev) {
|
||||
dev_err(data->dev, "Invalid parameters!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
count = of_property_count_strings(of_node, "clock-names");
|
||||
if (count != ISPV3_CLK_NUM) {
|
||||
dev_err(data->dev, "invalid count of clocks, count=%d\n",
|
||||
count);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_string(of_node, "clock-names", &data->clk_name);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "reading clock-names failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
num_clk_rates = of_property_count_u32_elems(of_node, "clock-rates");
|
||||
if (num_clk_rates <= 0) {
|
||||
dev_err(data->dev, "reading clock-rates count failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_index(of_node, "clock-rates",
|
||||
0, &data->clk_rate);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Error reading clock-rates, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->clk_rate = (data->clk_rate == 0) ? (int32_t)ISPV3_NO_SET_RATE : data->clk_rate;
|
||||
dev_dbg(data->dev, "mclk_rate = %d", data->clk_rate);
|
||||
|
||||
data->clk = devm_clk_get(data->dev, data->clk_name);
|
||||
if (!data->clk) {
|
||||
dev_err(data->dev, "get clk failed for %s\n", data->clk_name);
|
||||
ret = -ENOENT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_pinctrl_init(struct ispv3_pinctrl_info *sensor_pctrl,
|
||||
struct device *dev)
|
||||
{
|
||||
sensor_pctrl->pinctrl = devm_pinctrl_get(dev);
|
||||
if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) {
|
||||
dev_err(dev, "Getting pinctrl handle failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sensor_pctrl->gpio_state_active =
|
||||
pinctrl_lookup_state(sensor_pctrl->pinctrl,
|
||||
ISPV3_PINCTRL_STATE_DEFAULT);
|
||||
if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_active)) {
|
||||
dev_err(dev, "Failed to get the active state pinctrl handle!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sensor_pctrl->gpio_state_suspend =
|
||||
pinctrl_lookup_state(sensor_pctrl->pinctrl,
|
||||
ISPV3_PINCTRL_STATE_SLEEP);
|
||||
if (IS_ERR_OR_NULL(sensor_pctrl->gpio_state_suspend)) {
|
||||
dev_err(dev, "Failed to get the suspend state pinctrl handle!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ispv3_conf_gpio(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
struct device_node *np = data->dev->of_node;
|
||||
|
||||
data->gpio_sys_reset = of_get_named_gpio(np, "ispv3_gpio_reset", 0);
|
||||
if (data->gpio_sys_reset < 0) {
|
||||
dev_err(data->dev, "get reset gpio failed!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_sys_reset, "reset_gpio0");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "reset gpio request failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(data->gpio_sys_reset, 0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for reset gpio[%d]\n",
|
||||
data->gpio_sys_reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gpio_isolation = of_get_named_gpio(np, "ispv3_gpio_15", 0);
|
||||
if (data->gpio_isolation < 0) {
|
||||
dev_err(data->dev, "get isolation gpio failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_isolation, "isolation_gpio");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "request isolation gpio failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(data->gpio_isolation, 0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for isolation gpio[%d]\n",
|
||||
data->gpio_isolation);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gpio_swcr_reset = of_get_named_gpio(np, "ispv3_gpio_14", 0);
|
||||
if (data->gpio_swcr_reset < 0) {
|
||||
dev_err(data->dev, "get reset1 gpio failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_swcr_reset, "reset_gpio1");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "request reset1 gpio failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(data->gpio_swcr_reset, 0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for reset1 gpio[%d]\n",
|
||||
data->gpio_swcr_reset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gpio_fan_en = of_get_named_gpio(np, "ispv3_gpio_fan_en", 0);
|
||||
if (data->gpio_fan_en < 0) {
|
||||
dev_err(data->dev, "get fan53526 enable gpio failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_fan_en,
|
||||
"fan_en_gpio");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "request fan53526 enable gpio failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_output(data->gpio_fan_en, 0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for fan53526 enable gpio[%d]\n",
|
||||
data->gpio_fan_en);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gpio_int0 = of_get_named_gpio(np, "ispv3_gpio_int0", 0);
|
||||
if (data->gpio_int0 < 0) {
|
||||
dev_err(data->dev, "get interrupt0 gpio failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->gpio_irq_cam = gpio_to_irq(data->gpio_int0);
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_int0, "interrupt_gpio_cam");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "request interrupt0 gpio failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_input(data->gpio_int0);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for interrupt0 gpio[%d]\n",
|
||||
data->gpio_int0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->gpio_int1 = of_get_named_gpio(np, "ispv3_gpio_int1", 0);
|
||||
if (data->gpio_int1 < 0) {
|
||||
dev_err(data->dev, "get interrupt1 gpio failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data->gpio_irq_power = gpio_to_irq(data->gpio_int1);
|
||||
|
||||
ret = devm_gpio_request(data->dev, data->gpio_int1, "interrupt_gpio_power");
|
||||
if (ret) {
|
||||
dev_err(data->dev, "request interrupt1 gpio failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gpio_direction_input(data->gpio_int1);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set direction for interrupt1 gpio[%d]\n",
|
||||
data->gpio_int1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ispv3_free_gpio(struct ispv3_data *data)
|
||||
{
|
||||
if (data->gpio_sys_reset)
|
||||
devm_gpio_free(data->dev, data->gpio_sys_reset);
|
||||
data->gpio_sys_reset = 0;
|
||||
|
||||
if (data->gpio_isolation)
|
||||
devm_gpio_free(data->dev, data->gpio_isolation);
|
||||
data->gpio_isolation = 0;
|
||||
|
||||
if (data->gpio_swcr_reset)
|
||||
devm_gpio_free(data->dev, data->gpio_swcr_reset);
|
||||
data->gpio_swcr_reset = 0;
|
||||
|
||||
if (data->gpio_int0)
|
||||
devm_gpio_free(data->dev, data->gpio_int0);
|
||||
data->gpio_int0 = 0;
|
||||
|
||||
if (data->gpio_int1)
|
||||
devm_gpio_free(data->dev, data->gpio_int1);
|
||||
data->gpio_int1 = 0;
|
||||
|
||||
if (data->gpio_fan_en)
|
||||
devm_gpio_free(data->dev, data->gpio_fan_en);
|
||||
data->gpio_fan_en = 0;
|
||||
}
|
||||
|
||||
static int ispv3_get_dt_data(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 rc_index;
|
||||
struct device_node *np = data->dev->of_node;
|
||||
|
||||
if (of_get_property(np, "sm8475", NULL) != NULL)
|
||||
data->soc_id = ISPV3_SOC_ID_SM8475;
|
||||
else
|
||||
data->soc_id = ISPV3_SOC_ID_SM8450;
|
||||
|
||||
dev_info(data->dev, "soc id is %d", data->soc_id);
|
||||
|
||||
|
||||
ret = of_property_read_u32(np, "qcom,ispv3-rc-index", &rc_index);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to find PCIe RC number!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
data->rc_index = rc_index;
|
||||
|
||||
ret = ispv3_conf_gpio(data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "config pin function failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ispv3_get_dt_regulator_info(data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "get dt regulator failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ispv3_get_dt_clk_info(data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "get dt clk failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
memset(&(data->pinctrl_info), 0x0, sizeof(data->pinctrl_info));
|
||||
ret = ispv3_pinctrl_init(&(data->pinctrl_info), data->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "Initialization of pinctrl failed!\n");
|
||||
data->pinctrl_info.pinctrl_status = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->pinctrl_info.pinctrl_status = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ispv3_set_plat_priv(struct ispv3_data *plat_priv)
|
||||
{
|
||||
ispv3_dev_info = plat_priv;
|
||||
}
|
||||
|
||||
static struct ispv3_data *ispv3_get_plat_priv(void)
|
||||
{
|
||||
return ispv3_dev_info;
|
||||
}
|
||||
|
||||
static int ispv3_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ispv3_data *data = NULL;
|
||||
struct device *dev = &spi->dev;
|
||||
int ret = 0;
|
||||
|
||||
spi->max_speed_hz = ISPV3_SPI_SPEED_HZ;
|
||||
spi->bits_per_word = 8;
|
||||
spi->mode = SPI_MODE_0;
|
||||
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "spi_setup failed (%d)!\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct ispv3_data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, data);
|
||||
ispv3_set_plat_priv(data);
|
||||
data->spi = spi;
|
||||
data->dev = dev;
|
||||
|
||||
ret = ispv3_get_dt_data(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ispv3_get_dt_data failed (%d)!\n", ret);
|
||||
goto free_data;
|
||||
}
|
||||
|
||||
#ifdef BUG_SOF
|
||||
atomic_set(&data->power_state, 1);
|
||||
#else
|
||||
data->power_state = ISPV3_POWER_OFF;
|
||||
#endif
|
||||
data->interface_type = ISPV3_SPI;
|
||||
mutex_init(&data->ispv3_interf_mutex);
|
||||
|
||||
ispv3_gpio_reset_clear(data);
|
||||
|
||||
ret = ispv3_power_on(data);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "ispv3 power on failed (%d)!\n", ret);
|
||||
goto remove_pin;
|
||||
}
|
||||
|
||||
ret = ispv3_pci_init(data);
|
||||
if (ret) {
|
||||
dev_err(dev, "ispv3 pci init failed (%d)! Not PCIE mode\n", ret);
|
||||
data->interface_type = ISPV3_SPI;
|
||||
}
|
||||
|
||||
if (data->interface_type == ISPV3_SPI) {
|
||||
data->remote_callback = ispv3_init_rpmsg_callback;
|
||||
|
||||
ispv3_cam_cell.platform_data = (void *)data;
|
||||
ret = mfd_add_devices(dev, PLATFORM_DEVID_NONE,
|
||||
&ispv3_cam_cell, 1, NULL, 0, NULL);
|
||||
if (ret) {
|
||||
dev_err(dev, "MFD add camera device failed: %d\n", ret);
|
||||
goto power_off;
|
||||
}
|
||||
dev_info(dev, "ispv3 cam device registered.\n");
|
||||
}
|
||||
|
||||
dev_info(dev, "ispv3 all ispv3 mfd devices registered.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
power_off:
|
||||
ispv3_power_off(data);
|
||||
remove_pin:
|
||||
ispv3_free_gpio(data);
|
||||
free_data:
|
||||
devm_kfree(dev, data);
|
||||
ispv3_set_plat_priv(NULL);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ispv3_data *data = spi_get_drvdata(spi);
|
||||
int ret = 0;
|
||||
|
||||
ispv3_free_gpio(data);
|
||||
#ifdef CONFIG_ZISP_OCRAM_AON
|
||||
ret = ispv3_power_off(data);
|
||||
#endif
|
||||
dev_info(&spi->dev, "ispv3 remove success !\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id ispv3_spi_of_match[] = {
|
||||
{.compatible = "xiaomi,ispv3_spi",},
|
||||
{},
|
||||
};
|
||||
|
||||
static const struct spi_device_id ispv3_spi_device_id[] = {
|
||||
{"ispv3_spi", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct spi_driver ispv3_spi_drv = {
|
||||
.driver = {
|
||||
.name = "ispv3_spi",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = ispv3_spi_of_match,
|
||||
},
|
||||
.probe = ispv3_spi_probe,
|
||||
.remove = ispv3_spi_remove,
|
||||
.id_table = ispv3_spi_device_id,
|
||||
};
|
||||
|
||||
static int __init ispv3_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_register_driver(&ispv3_spi_drv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ispv3_module_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ispv3_spi_drv);
|
||||
}
|
||||
|
||||
module_init(ispv3_module_init);
|
||||
module_exit(ispv3_module_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Platform driver for Xiaomi, Inc. ZISP V3");
|
447
drivers/media/platform/xiaomi/ispv3/ispv3_power.c
Normal file
447
drivers/media/platform/xiaomi/ispv3/ispv3_power.c
Normal file
@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/mfd/ispv3_dev.h>
|
||||
|
||||
static int ispv3_regulator_enable(struct ispv3_data *data,
|
||||
enum ispv3_rgltr_type type)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
|
||||
if (!data->rgltr[type]) {
|
||||
dev_err(data->dev, "Invalid NULL parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (regulator_count_voltages(data->rgltr[type]) > 0) {
|
||||
dev_dbg(data->dev, "rgltr_name %s voltage min=%d, max=%d",
|
||||
data->rgltr_name[type], data->rgltr_min_volt[type],
|
||||
data->rgltr_max_volt[type]);
|
||||
|
||||
ret = regulator_set_voltage(data->rgltr[type],
|
||||
data->rgltr_min_volt[type], data->rgltr_max_volt[type]);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "%s set voltage failed\n",
|
||||
data->rgltr_name[type]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->rgltr[type]);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "%s regulator_enable failed\n",
|
||||
data->rgltr_name[type]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_regulator_disable(struct ispv3_data *data,
|
||||
enum ispv3_rgltr_type type)
|
||||
{
|
||||
int32_t ret = 0;
|
||||
|
||||
if (!data->rgltr[type]) {
|
||||
dev_err(data->dev, "Invalid NULL parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regulator_disable(data->rgltr[type]);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "%s regulator disable failed\n",
|
||||
data->rgltr_name[type]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (regulator_count_voltages(data->rgltr[type]) > 0) {
|
||||
//regulator_set_load(data->rgltr[type], 0);
|
||||
regulator_set_voltage(data->rgltr[type], 0,
|
||||
data->rgltr_max_volt[type]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_clk_enable(struct ispv3_data *data)
|
||||
{
|
||||
long clk_rate_round;
|
||||
int ret = 0;
|
||||
|
||||
if (!data->clk || !data->clk_name)
|
||||
return -EINVAL;
|
||||
|
||||
clk_rate_round = clk_round_rate(data->clk, data->clk_rate);
|
||||
if (clk_rate_round < 0) {
|
||||
dev_err(data->dev, "round failed for clock %s ret = %ld",
|
||||
data->clk_name, clk_rate_round);
|
||||
return clk_rate_round;
|
||||
}
|
||||
|
||||
ret = clk_set_rate(data->clk, clk_rate_round);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "set_rate failed on %s", data->clk_name);
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(data->dev, "set %s, rate %d, new_rate %ld", data->clk_name,
|
||||
data->clk_rate, clk_rate_round);
|
||||
|
||||
ret = clk_prepare_enable(data->clk);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "enable failed for %s: ret(%d)", data->clk_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_clk_disable(struct ispv3_data *data)
|
||||
{
|
||||
if (!data->clk || !data->clk_name)
|
||||
return -EINVAL;
|
||||
|
||||
clk_disable_unprepare(data->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ispv3_sys_reset(struct ispv3_data *data, u32 value)
|
||||
{
|
||||
if (data->gpio_sys_reset > 0)
|
||||
gpio_set_value(data->gpio_sys_reset, value);
|
||||
}
|
||||
|
||||
void ispv3_swcr_isolation(struct ispv3_data *data, u32 value)
|
||||
{
|
||||
if (data->gpio_isolation > 0)
|
||||
gpio_set_value(data->gpio_isolation, value);
|
||||
}
|
||||
|
||||
void ispv3_swcr_reset(struct ispv3_data *data, u32 value)
|
||||
{
|
||||
if (data->gpio_swcr_reset > 0)
|
||||
gpio_set_value(data->gpio_swcr_reset, value);
|
||||
}
|
||||
|
||||
void ispv3_fan_enable(struct ispv3_data *data, u32 value)
|
||||
{
|
||||
if (data->gpio_fan_en > 0)
|
||||
gpio_set_value(data->gpio_fan_en, value);
|
||||
}
|
||||
|
||||
static int ispv3_set_pci_config_space(struct ispv3_data *data, bool save)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (save) {
|
||||
ret = pci_save_state(data->pci);
|
||||
data->saved_state = pci_store_saved_state(data->pci);
|
||||
} else {
|
||||
if (data->saved_state)
|
||||
pci_load_and_free_saved_state(data->pci, &data->saved_state);
|
||||
else
|
||||
pci_load_saved_state(data->pci, data->default_state);
|
||||
|
||||
pci_restore_state(data->pci);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ispv3_set_pci_link(struct ispv3_data *data, bool link_up)
|
||||
{
|
||||
enum msm_pcie_pm_opt pm_ops;
|
||||
int retry_time = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (link_up)
|
||||
pm_ops = MSM_PCIE_RESUME;
|
||||
else
|
||||
pm_ops = MSM_PCIE_SUSPEND;
|
||||
|
||||
retry:
|
||||
ret = msm_pcie_pm_control(pm_ops, data->pci->bus->number, data->pci,
|
||||
NULL, PM_OPTIONS_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to %s PCI link with default option, err = %d\n",
|
||||
link_up ? "resume" : "suspend", ret);
|
||||
if (link_up && retry_time++ < LINK_TRAINING_RETRY_MAX_TIMES) {
|
||||
dev_dbg(data->dev, "Retry PCI link training #%d\n",
|
||||
retry_time);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ispv3_resume_pci_link(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!data->pci)
|
||||
return -ENODEV;
|
||||
|
||||
ret = ispv3_set_pci_link(data, ISPV3_PCI_LINK_UP);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to set link up status, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(data->pci);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to enable PCI device, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ispv3_set_pci_config_space(data, RESTORE_PCI_CONFIG_SPACE);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to restore config space, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_master(data->pci);
|
||||
|
||||
atomic_set(&data->pci_link_state, ISPV3_PCI_LINK_UP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ispv3_resume_pci_link);
|
||||
|
||||
int ispv3_suspend_pci_link(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!data->pci)
|
||||
return -ENODEV;
|
||||
|
||||
pci_clear_master(data->pci);
|
||||
|
||||
ret = ispv3_set_pci_config_space(data, SAVE_PCI_CONFIG_SPACE);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to save config space, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_disable_device(data->pci);
|
||||
|
||||
mutex_lock(&data->ispv3_interf_mutex);
|
||||
ret = pci_set_power_state(data->pci, PCI_D3hot);
|
||||
mutex_unlock(&data->ispv3_interf_mutex);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to set D3Hot, err = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ispv3_set_pci_link(data, ISPV3_PCI_LINK_DOWN);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to set link down status, err = %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
atomic_set(&data->pci_link_state, ISPV3_PCI_LINK_DOWN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ispv3_suspend_pci_link);
|
||||
|
||||
void ispv3_gpio_reset_clear(struct ispv3_data *data)
|
||||
{
|
||||
ispv3_swcr_isolation(data, 0);
|
||||
udelay(100);
|
||||
ispv3_swcr_reset(data, 0);
|
||||
udelay(100);
|
||||
ispv3_sys_reset(data, 0);
|
||||
udelay(100);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ispv3_gpio_reset_clear);
|
||||
|
||||
int ispv3_power_on(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* regulator enable and configure reset pin to work state */
|
||||
if (data->soc_id == ISPV3_SOC_ID_SM8475) {
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_S11B);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_S11B regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_S12B);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_S12B regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_VDD1);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_VDD1 regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(100);
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_L12C);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_L12C regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(1);
|
||||
|
||||
ispv3_fan_enable(data, 1);
|
||||
udelay(50);
|
||||
ispv3_swcr_isolation(data, 1);
|
||||
udelay(1);
|
||||
ispv3_swcr_reset(data, 1);
|
||||
udelay(50);
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_VDD2);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_VDD2 regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(100);
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_VDD);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_VDD regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(1);
|
||||
|
||||
ispv3_swcr_isolation(data, 0);
|
||||
udelay(100);
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_VDDR);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_VDDR regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(100);
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_L10C);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_L10C regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
udelay(2500);
|
||||
if (data->pinctrl_info.pinctrl_status) {
|
||||
ret = pinctrl_select_state(
|
||||
data->pinctrl_info.pinctrl,
|
||||
data->pinctrl_info.gpio_state_active);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "cannot set pin to active state\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ispv3_regulator_enable(data, ISPV3_RGLTR_MCLK);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "ISPV3_RGLTR_MCLK regulator enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ispv3_clk_enable(data);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "mclk enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
udelay(1);
|
||||
|
||||
ispv3_sys_reset(data, 1);
|
||||
|
||||
#ifdef BUG_SOF
|
||||
atomic_set(&data->power_state, 0);
|
||||
#else
|
||||
data->power_state = ISPV3_POWER_ON;
|
||||
#endif
|
||||
dev_info(data->dev, "ispv3 power on success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ispv3_power_on);
|
||||
|
||||
int ispv3_power_off(struct ispv3_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* regulator disable and configure reset pin to defaule value */
|
||||
ispv3_sys_reset(data, 0);
|
||||
udelay(1);
|
||||
ispv3_swcr_isolation(data, 1);
|
||||
|
||||
ispv3_clk_disable(data);
|
||||
if (data->pinctrl_info.pinctrl_status) {
|
||||
ret = pinctrl_select_state(
|
||||
data->pinctrl_info.pinctrl,
|
||||
data->pinctrl_info.gpio_state_suspend);
|
||||
if (ret)
|
||||
dev_err(data->dev, "cannot set pin to suspend state\n");
|
||||
}
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_MCLK);
|
||||
udelay(1);
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_L10C);
|
||||
udelay(1);
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_VDDR);
|
||||
udelay(1);
|
||||
|
||||
#ifndef CONFIG_ZISP_OCRAM_AON
|
||||
ispv3_swcr_reset(data, 0);
|
||||
#endif
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_VDD);
|
||||
udelay(1);
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_VDD2);
|
||||
udelay(1);
|
||||
|
||||
ispv3_fan_enable(data, 0);
|
||||
udelay(1);
|
||||
|
||||
#ifndef CONFIG_ZISP_OCRAM_AON
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_L12C);
|
||||
udelay(1);
|
||||
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_VDD1);
|
||||
udelay(1);
|
||||
|
||||
if (data->soc_id == ISPV3_SOC_ID_SM8475) {
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_S11B);
|
||||
ispv3_regulator_disable(data, ISPV3_RGLTR_S12B);
|
||||
}
|
||||
#endif
|
||||
|
||||
ispv3_swcr_isolation(data, 0);
|
||||
#ifdef BUG_SOF
|
||||
atomic_set(&data->power_state, 1);
|
||||
#else
|
||||
data->power_state = ISPV3_POWER_OFF;
|
||||
#endif
|
||||
dev_info(data->dev, "ispv3 power down success");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ispv3_power_off);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
156
include/linux/mfd/ispv3_dev.h
Normal file
156
include/linux/mfd/ispv3_dev.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
#ifndef _ISPV3_DEV_H_
|
||||
#define _ISPV3_DEV_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/msm_pcie.h>
|
||||
|
||||
#define MIPORT_MAX_REG_ADDR 0x1000000
|
||||
|
||||
#define ISPV3_SPI_SPEED_HZ (1 * 1000 * 1000)
|
||||
#define ISPV3_CLK_NUM (1)
|
||||
#define ISPV3_NO_SET_RATE (-1)
|
||||
|
||||
#define ISPV3_PINCTRL_STATE_SLEEP "ispv3_suspend"
|
||||
#define ISPV3_PINCTRL_STATE_DEFAULT "ispv3_default"
|
||||
#define ISPV3_PCI_VENDOR_ID 0x17cd
|
||||
#define ISPV3_PCI_DEVICE_ID 0x0100
|
||||
#define ISPV3_BAR_NUM 3
|
||||
#define ISPV3_PCI_LINK_DOWN 0
|
||||
#define ISPV3_PCI_LINK_UP 1
|
||||
#define LINK_TRAINING_RETRY_MAX_TIMES 3
|
||||
#define PM_OPTIONS_DEFAULT 0
|
||||
#define SAVE_PCI_CONFIG_SPACE 1
|
||||
#define RESTORE_PCI_CONFIG_SPACE 0
|
||||
|
||||
#define IRQ_TYPE_UNDEFINED (-1)
|
||||
#define IRQ_TYPE_LEGACY 0
|
||||
#define IRQ_TYPE_MSI 1
|
||||
#define OCRAM_OFFSET 0x700000
|
||||
#define RPMSG_SIZE (1024 * 300)
|
||||
|
||||
#define BUG_SOF 1
|
||||
|
||||
#define ISPV3_SOC_8450_RGLTR_COUNT 7
|
||||
#define ISPV3_SOC_8475_RGLTR_COUNT 9
|
||||
|
||||
enum ispv3_rgltr_type {
|
||||
ISPV3_RGLTR_VDD1,
|
||||
ISPV3_RGLTR_L12C,
|
||||
ISPV3_RGLTR_VDD2,
|
||||
ISPV3_RGLTR_VDD,
|
||||
ISPV3_RGLTR_VDDR,
|
||||
ISPV3_RGLTR_L10C,
|
||||
ISPV3_RGLTR_MCLK,
|
||||
ISPV3_RGLTR_S11B,
|
||||
ISPV3_RGLTR_S12B,
|
||||
ISPV3_RGLTR_MAX,
|
||||
};
|
||||
|
||||
enum pci_barno {
|
||||
BAR_0,
|
||||
BAR_1,
|
||||
BAR_2,
|
||||
BAR_3,
|
||||
BAR_4,
|
||||
BAR_5,
|
||||
};
|
||||
|
||||
enum rpmsg_res {
|
||||
RPMSG_RAM,
|
||||
RPMSG_IRQ,
|
||||
RPMSG_RES_NUM,
|
||||
};
|
||||
|
||||
enum ispv3_power_state_type {
|
||||
ISPV3_POWER_ON,
|
||||
ISPV3_POWER_OFF,
|
||||
ISPV3_POWER_MAX,
|
||||
};
|
||||
|
||||
enum cam_res {
|
||||
ISP_RAM,
|
||||
ISP_DDR,
|
||||
CAM_RES_NUM,
|
||||
};
|
||||
|
||||
enum ispv3_interface_type {
|
||||
ISPV3_SPI,
|
||||
ISPV3_PCIE,
|
||||
};
|
||||
|
||||
struct ispv3_pinctrl_info {
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *gpio_state_active;
|
||||
struct pinctrl_state *gpio_state_suspend;
|
||||
uint8_t pinctrl_status;
|
||||
};
|
||||
|
||||
enum ispv3_soc_id {
|
||||
ISPV3_SOC_ID_SM8450 = 0,
|
||||
ISPV3_SOC_ID_SM8475,
|
||||
};
|
||||
|
||||
struct ispv3_data {
|
||||
struct ispv3_pinctrl_info pinctrl_info;
|
||||
struct spi_device *spi;
|
||||
struct pci_dev *pci;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
void *rproc;
|
||||
struct resource bar_res[ISPV3_BAR_NUM];
|
||||
#ifdef BUG_SOF
|
||||
atomic_t power_state;
|
||||
#else
|
||||
enum ispv3_power_state_type power_state;
|
||||
#endif
|
||||
struct pci_saved_state *saved_state;
|
||||
struct pci_saved_state *default_state;
|
||||
enum ispv3_interface_type interface_type;
|
||||
enum ispv3_soc_id soc_id;
|
||||
uint32_t num_rgltr;
|
||||
struct regulator *rgltr[ISPV3_RGLTR_MAX];
|
||||
const char *rgltr_name[ISPV3_RGLTR_MAX];
|
||||
uint32_t rgltr_min_volt[ISPV3_RGLTR_MAX];
|
||||
uint32_t rgltr_max_volt[ISPV3_RGLTR_MAX];
|
||||
uint32_t rgltr_op_mode[ISPV3_RGLTR_MAX];
|
||||
const char *clk_name;
|
||||
struct clk *clk;
|
||||
int32_t clk_rate;
|
||||
int32_t gpio_sys_reset;
|
||||
int32_t gpio_isolation;
|
||||
int32_t gpio_swcr_reset;
|
||||
int32_t gpio_int0;
|
||||
int32_t gpio_int1;
|
||||
int32_t gpio_fan_en;
|
||||
int32_t irq_num;
|
||||
int32_t rc_index;
|
||||
int32_t pci_irq_type;
|
||||
uint32_t gpio_irq_cam;
|
||||
uint32_t gpio_irq_power;
|
||||
atomic_t pci_link_state;
|
||||
struct mutex ispv3_interf_mutex;
|
||||
int (*remote_callback)(struct ispv3_data *pdata);
|
||||
};
|
||||
|
||||
void ispv3_gpio_reset_clear(struct ispv3_data *data);
|
||||
int ispv3_power_on(struct ispv3_data *data);
|
||||
int ispv3_power_off(struct ispv3_data *data);
|
||||
void ispv3_write_reset(struct ispv3_data *data, u32 value);
|
||||
void ispv3_write_reset1(struct ispv3_data *data, u32 value);
|
||||
void ispv3_write_isolation(struct ispv3_data *data, u32 value);
|
||||
int ispv3_resume_pci_link(struct ispv3_data *data);
|
||||
int ispv3_suspend_pci_link(struct ispv3_data *data);
|
||||
|
||||
#endif
|
||||
|
42
include/uapi/linux/ispv3_ioparam.h
Normal file
42
include/uapi/linux/ispv3_ioparam.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Xiaomi, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ISPV3_IOPARAM_H__
|
||||
#define __ISPV3_IOPARAM_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/types.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
#endif
|
||||
|
||||
|
||||
/* Command Reserved Types */
|
||||
#define CAM_RESERVED_OPCODE_BASE 0x2000
|
||||
#define CAM_RESERVED_POWERUP_EX (CAM_RESERVED_OPCODE_BASE+0x1)
|
||||
|
||||
/* PowerSetting Config Value properties */
|
||||
#define POWER_CFG_VAL_TYPE_MASK 0xF000
|
||||
#define POWER_CFG_VAL_TYPE_EX 0x2000
|
||||
#define POWER_CFG_VAL_MASK 0x000F
|
||||
typedef enum
|
||||
{
|
||||
ISPV3EventExit = 0,
|
||||
ISPV3EventMipiRXErr,
|
||||
ISPV3EventMax,
|
||||
} ISPV3ICEventType;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __ISPV3_IOPARAM_H__
|
125
include/uapi/media/ispv3_defs.h
Normal file
125
include/uapi/media/ispv3_defs.h
Normal file
@ -0,0 +1,125 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __UAPI_ISP_DEFS_H__
|
||||
#define __UAPI_ISP_DEFS_H__
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define ISPV3_VNODE_NAME "ispv3-devnode"
|
||||
|
||||
#define MFNR_REF_FRAME_NUM 7
|
||||
|
||||
#define ISP_DEVICE_TYPE_BASE (MEDIA_ENT_F_OLD_BASE + 20)
|
||||
#define ISP_VNODE_DEVICE_TYPE (ISP_DEVICE_TYPE_BASE + 1)
|
||||
|
||||
/* MMAP offset definition */
|
||||
#define ISP_MITOP_REG_MAPOFFSET 0
|
||||
#define ISP_MIPORT_REG_MAPOFFSET PAGE_SIZE
|
||||
#define ISP_MITOP_OCRAM_MAPOFFSET (2*PAGE_SIZE)
|
||||
#define ISP_MITOP_DDR_MAPOFFSET (3*PAGE_SIZE)
|
||||
|
||||
/***MMAP Mapping Addr***/
|
||||
#define MIPORT_REG_SIZE 0x200000
|
||||
#define MIPORT_REG_OFFSET 0xe00000
|
||||
#define MIPORT_REG_MASK 0xFFE00000
|
||||
#define MIPORT_REG_MIN_ADDR 0xFFE00000
|
||||
#define MIPORT_REG_MAX_ADDR (MIPORT_REG_MIN_ADDR-1+MIPORT_REG_SIZE)
|
||||
|
||||
#define MITOP_REG_SIZE 0x200000
|
||||
#define MITOP_REG_OFFSET 0x0
|
||||
#define MITOP_REG_MASK 0xFF000000
|
||||
#define MITOP_REG_MIN_ADDR 0xFF000000
|
||||
#define MITOP_REG_MAX_ADDR (MITOP_REG_MIN_ADDR-1+MITOP_REG_SIZE)
|
||||
|
||||
#define MITOP_OCRAM_SIZE 0x750000 /*include 300K OCRAM for test**/
|
||||
#define MITOP_OCRAM_OFFSET 0x0
|
||||
#define MITOP_OCRAM_MASK 0x7F000000
|
||||
#define MITOP_OCRAM_MIN_ADDR 0x7F000000
|
||||
#define MITOP_OCRAM_MAX_ADDR (MITOP_OCRAM_MIN_ADDR-1+MITOP_OCRAM_SIZE)
|
||||
|
||||
#define MITOP_DDR_SIZE 0x10000000
|
||||
#define MITOP_DDR_OFFSET 0x0
|
||||
#define MITOP_DDR_MASK 0x00000000
|
||||
#define MITOP_DDR_MIN_ADDR 0x00000000
|
||||
#define MITOP_DDR_MAX_ADDR (MITOP_DDR_MIN_ADDR-1+MITOP_DDR_SIZE)
|
||||
|
||||
/**
|
||||
* struct ispv3_control - Structure used by ioctl control for ispv3
|
||||
*
|
||||
* @op_code: This is the op code for isp control
|
||||
* @size: Control command size
|
||||
* @handle: Control command payload
|
||||
*/
|
||||
struct ispv3_control {
|
||||
uint32_t op_code;
|
||||
uint32_t size;
|
||||
uint64_t handle;
|
||||
};
|
||||
|
||||
/* ISP IOCTL */
|
||||
#define VIDIOC_ISPV3_CONTROL \
|
||||
_IOWR('V', BASE_VIDIOC_PRIVATE+15, struct ispv3_control)
|
||||
|
||||
|
||||
struct ispv3_mfnr_capture_info {
|
||||
int32_t full_keyframe_fd;
|
||||
int32_t full_refframe_fd[MFNR_REF_FRAME_NUM];
|
||||
int32_t ds_keyframe_fd;
|
||||
int32_t ds_refframe_fd[MFNR_REF_FRAME_NUM];
|
||||
int32_t result_fd;
|
||||
bool is_transfer_ds;
|
||||
};
|
||||
|
||||
//=====================================================
|
||||
|
||||
enum isp_interface_type {
|
||||
ISP_INTERFACE_INVALID = 0,
|
||||
ISP_INTERFACE_IIC,
|
||||
ISP_INTERFACE_SPI,
|
||||
ISP_INTERFACE_PCIE,
|
||||
ISP_INTERFACE_MAX,
|
||||
};
|
||||
|
||||
struct ispv3_reg_array {
|
||||
uint32_t reg_addr;
|
||||
uint32_t reg_data;
|
||||
};
|
||||
|
||||
enum isp_access_mode {
|
||||
ISP_SINGLECPY,
|
||||
ISP_BURSTCPY,
|
||||
ISP_BURSTSET,
|
||||
};
|
||||
|
||||
struct ispv3_reg_burst_setting {
|
||||
uint32_t reg_addr;
|
||||
uint64_t data_buf;
|
||||
};
|
||||
|
||||
struct ispv3_config_setting {
|
||||
enum isp_interface_type bus_type;
|
||||
enum isp_access_mode access_mode;
|
||||
uint32_t data_num;
|
||||
union {
|
||||
//single mode read&write
|
||||
struct ispv3_reg_array *reg_array_buf;
|
||||
//burst mode read&write
|
||||
struct ispv3_reg_burst_setting reg_burst_setting;
|
||||
//burst mode memset
|
||||
struct ispv3_reg_array reg_array;
|
||||
};
|
||||
};
|
||||
struct ispv3_info {
|
||||
int ispv3_fd;
|
||||
int fd_mem;
|
||||
uint32_t *ispv3_miport_reg_pointer;
|
||||
uint32_t *ispv3_mitop_reg_pointer;
|
||||
uint32_t *ispv3_mitop_ocram_pointer;
|
||||
uint32_t *ispv3_ddr_pointer;
|
||||
};
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user