media: Import xiaomi camera ispv3 driver

Change-Id: If359c12219100477c49d0bac563158921754c6ee
Signed-off-by: Jens Reidel <adrian@travitia.xyz>
This commit is contained in:
Jens Reidel 2024-04-21 21:42:29 +02:00
parent 7af371ea3a
commit 48fa558e2a
No known key found for this signature in database
GPG Key ID: 23C1E5F512C12303
13 changed files with 1982 additions and 0 deletions

View File

@ -586,3 +586,4 @@ config VIDEO_RCAR_DRIF
endif # SDR_PLATFORM_DRIVERS
source "drivers/media/platform/msm/Kconfig"
source "drivers/media/platform/xiaomi/Kconfig"

View File

@ -82,3 +82,4 @@ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
obj-y += sunxi/
obj-y += msm/
obj-y += xiaomi/

View File

@ -0,0 +1 @@
source "drivers/media/platform/xiaomi/ispv3/Kconfig"

View File

@ -0,0 +1,2 @@
obj-$(CONFIG_ISPV3) += ispv3/
#obj-m += ispv3/

View 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

View 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

View 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");

View 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

View 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");

View 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");

View 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

View 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__

View 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