oleavr-rgl-a500-mini-linux-.../drivers/media/platform/sunxi-vin/vin-cci/sunxi_cci.c
Ole André Vadla Ravnås 169c65d57e Initial commit
2022-05-07 01:01:45 +02:00

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