284 lines
5.6 KiB
C
284 lines
5.6 KiB
C
|
|
/*
|
|
******************************************************************************
|
|
*
|
|
* sunxi_cci.c
|
|
*
|
|
* Hawkview ISP - sunxi_cci.c module
|
|
*
|
|
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
|
|
*
|
|
* Version Author Date Description
|
|
*
|
|
* 2.0 Yang Feng 2014/06/23 Second Version
|
|
*
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "bsp_cci.h"
|
|
#include "sunxi_cci.h"
|
|
#include "../platform/platform_cfg.h"
|
|
|
|
#define CCI_MODULE_NAME "vin_cci"
|
|
|
|
static LIST_HEAD(cci_drv_list);
|
|
|
|
#ifdef CCI_IRQ
|
|
static irqreturn_t cci_irq_handler(int this_irq, void *dev)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct cci_dev *cci = (struct cci_dev *)dev;
|
|
spin_lock_irqsave(&cci->slock, flags);
|
|
bsp_cci_irq_process(cci->id);
|
|
spin_unlock_irqrestore(&cci->slock, flags);
|
|
return IRQ_HANDLED;
|
|
}
|
|
#endif
|
|
|
|
static int __cci_clk_get(struct cci_dev *dev)
|
|
{
|
|
#ifndef FPGA_VER
|
|
struct device_node *np = dev->pdev->dev.of_node;
|
|
|
|
dev->clock = of_clk_get(np, 0);
|
|
if (IS_ERR_OR_NULL(dev->clock))
|
|
vin_warn("cci get clk failed!\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int __cci_clk_enable(struct cci_dev *dev, int enable)
|
|
{
|
|
#ifndef FPGA_VER
|
|
if (dev->clock) {
|
|
if (enable) {
|
|
if (clk_prepare_enable(dev->clock)) {
|
|
vin_err("cci clk enable error!\n");
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (dev->clock->enable_count != 0)
|
|
clk_disable_unprepare(dev->clock);
|
|
}
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void __cci_clk_release(struct cci_dev *dev)
|
|
{
|
|
#ifndef FPGA_VER
|
|
if (dev->clock)
|
|
clk_put(dev->clock);
|
|
#endif
|
|
}
|
|
|
|
static int __cci_pin_config(struct cci_dev *dev, int enable)
|
|
{
|
|
#ifndef FPGA_VER
|
|
char pinctrl_names[10] = "";
|
|
|
|
if (!IS_ERR_OR_NULL(dev->pctrl))
|
|
devm_pinctrl_put(dev->pctrl);
|
|
|
|
if (enable)
|
|
strcpy(pinctrl_names, "default");
|
|
else
|
|
strcpy(pinctrl_names, "sleep");
|
|
|
|
dev->pctrl = devm_pinctrl_get_select(&dev->pdev->dev, pinctrl_names);
|
|
if (IS_ERR_OR_NULL(dev->pctrl)) {
|
|
vin_err("cci%d request pinctrl handle failed!\n", dev->id);
|
|
return -EINVAL;
|
|
}
|
|
usleep_range(5000, 6000);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int __cci_pin_release(struct cci_dev *dev)
|
|
{
|
|
#ifndef FPGA_VER
|
|
if (!IS_ERR_OR_NULL(dev->pctrl))
|
|
devm_pinctrl_put(dev->pctrl);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int cci_probe(struct platform_device *pdev)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
struct cci_dev *cci = NULL;
|
|
int ret, irq = 0;
|
|
|
|
if (np == NULL) {
|
|
vin_err("CCI failed to get of node\n");
|
|
return -ENODEV;
|
|
}
|
|
cci = kzalloc(sizeof(struct cci_dev), GFP_KERNEL);
|
|
if (!cci) {
|
|
ret = -ENOMEM;
|
|
goto ekzalloc;
|
|
}
|
|
|
|
of_property_read_u32(np, "device_id", &pdev->id);
|
|
if (pdev->id < 0) {
|
|
vin_err("CCI failed to get device id\n");
|
|
ret = -EINVAL;
|
|
goto freedev;
|
|
}
|
|
cci->id = pdev->id;
|
|
cci->pdev = pdev;
|
|
|
|
irq = irq_of_parse_and_map(np, 0);
|
|
if (irq <= 0) {
|
|
vin_err("[CCI%d] failed to get irq\n", pdev->id);
|
|
ret = -EINVAL;
|
|
goto freedev;
|
|
}
|
|
cci->base = of_iomap(np, 0);
|
|
if (!cci->base) {
|
|
ret = -EIO;
|
|
goto freedev;
|
|
}
|
|
cci->irq = irq;
|
|
spin_lock_init(&cci->slock);
|
|
|
|
list_add_tail(&cci->cci_list, &cci_drv_list);
|
|
init_waitqueue_head(&cci->wait);
|
|
|
|
#ifdef CCI_IRQ
|
|
ret = request_irq(irq, cci_irq_handler,
|
|
IRQF_DISABLED, CCI_MODULE_NAME, cci);
|
|
if (ret) {
|
|
vin_err("[CCI%d] requeset irq failed!\n", cci->id);
|
|
goto unmap;
|
|
}
|
|
#endif
|
|
ret = bsp_csi_cci_set_base_addr(cci->id, (unsigned long)cci->base);
|
|
if (ret < 0)
|
|
goto freeirq;
|
|
|
|
if (__cci_clk_get(cci)) {
|
|
vin_err("cci clock get failed!\n");
|
|
goto freeirq;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, cci);
|
|
vin_print("cci probe end cci_sel = %d!\n", cci->id);
|
|
|
|
return 0;
|
|
|
|
freeirq:
|
|
#ifdef CCI_IRQ
|
|
free_irq(irq, cci);
|
|
unmap:
|
|
#endif
|
|
iounmap(cci->base);
|
|
freedev:
|
|
kfree(cci);
|
|
ekzalloc:
|
|
vin_print("cci probe err!\n");
|
|
return ret;
|
|
}
|
|
|
|
static int cci_remove(struct platform_device *pdev)
|
|
{
|
|
struct cci_dev *cci = platform_get_drvdata(pdev);
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
__cci_pin_release(cci);
|
|
__cci_clk_release(cci);
|
|
#ifdef CCI_IRQ
|
|
free_irq(cci->irq, cci);
|
|
#endif
|
|
if (cci->base)
|
|
iounmap(cci->base);
|
|
list_del(&cci->cci_list);
|
|
kfree(cci);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id sunxi_cci_match[] = {
|
|
{.compatible = "allwinner,sunxi-csi_cci",},
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, sunxi_cci_match);
|
|
|
|
static struct platform_driver cci_platform_driver = {
|
|
.probe = cci_probe,
|
|
.remove = cci_remove,
|
|
.driver = {
|
|
.name = CCI_MODULE_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = sunxi_cci_match,
|
|
}
|
|
};
|
|
|
|
static struct cci_dev *cci_dev_get(int id)
|
|
{
|
|
struct cci_dev *cci;
|
|
|
|
list_for_each_entry(cci, &cci_drv_list, cci_list) {
|
|
if (cci->id == id)
|
|
return cci;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void cci_s_power(unsigned int sel, int on_off)
|
|
{
|
|
struct cci_dev *cci = cci_dev_get(sel);
|
|
|
|
if (cci == NULL) {
|
|
vin_err("cci is NULL!\n");
|
|
return;
|
|
}
|
|
vin_log(VIN_LOG_CCI, "%s, %d!\n", __func__, on_off);
|
|
|
|
if (on_off && (cci->use_cnt)++ > 0)
|
|
return;
|
|
else if (!on_off && (cci->use_cnt == 0 || --(cci->use_cnt) > 0))
|
|
return;
|
|
|
|
__cci_pin_config(cci, on_off);
|
|
|
|
if (on_off) {
|
|
__cci_clk_enable(cci, 1);
|
|
bsp_csi_cci_init_helper(sel);
|
|
} else {
|
|
bsp_csi_cci_exit(sel);
|
|
__cci_clk_enable(cci, 0);
|
|
}
|
|
}
|
|
|
|
static int __init cci_init(void)
|
|
{
|
|
int ret;
|
|
ret = platform_driver_register(&cci_platform_driver);
|
|
if (ret) {
|
|
vin_err("platform driver register failed\n");
|
|
return ret;
|
|
}
|
|
vin_print("cci_init end\n");
|
|
return 0;
|
|
}
|
|
|
|
static void __exit cci_exit(void)
|
|
{
|
|
platform_driver_unregister(&cci_platform_driver);
|
|
vin_print("cci_exit end\n");
|
|
}
|
|
|
|
module_init(cci_init);
|
|
module_exit(cci_exit);
|
|
|
|
MODULE_AUTHOR("yangfeng");
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
|
MODULE_DESCRIPTION("Camera CCI DRIVER for sunxi");
|