From 71de48f58470356d6031f6340aa1f39efb3e343e Mon Sep 17 00:00:00 2001 From: zyc Date: Fri, 21 Mar 2014 16:53:22 +0800 Subject: [PATCH] add isp driver for rk3288 --- arch/arm/boot/dts/rk3288-pinctrl.dtsi | 62 + arch/arm/boot/dts/rk3288.dtsi | 16 + drivers/media/Kconfig | 1 + drivers/media/Makefile | 1 + drivers/media/video/rk_camsys/Kconfig | 18 + drivers/media/video/rk_camsys/Makefile | 7 + drivers/media/video/rk_camsys/camsys_cif.c | 340 ++++ drivers/media/video/rk_camsys/camsys_cif.h | 58 + drivers/media/video/rk_camsys/camsys_drv.c | 1422 +++++++++++++++++ drivers/media/video/rk_camsys/camsys_gpio.h | 90 ++ .../media/video/rk_camsys/camsys_internal.h | 313 ++++ drivers/media/video/rk_camsys/camsys_marvin.c | 391 +++++ drivers/media/video/rk_camsys/camsys_marvin.h | 44 + .../video/rk_camsys/camsys_mipicsi_phy.c | 126 ++ .../video/rk_camsys/camsys_mipicsi_phy.h | 28 + include/media/camsys_head.h | 218 +++ 16 files changed, 3135 insertions(+) create mode 100755 drivers/media/video/rk_camsys/Kconfig create mode 100755 drivers/media/video/rk_camsys/Makefile create mode 100755 drivers/media/video/rk_camsys/camsys_cif.c create mode 100755 drivers/media/video/rk_camsys/camsys_cif.h create mode 100755 drivers/media/video/rk_camsys/camsys_drv.c create mode 100755 drivers/media/video/rk_camsys/camsys_gpio.h create mode 100755 drivers/media/video/rk_camsys/camsys_internal.h create mode 100755 drivers/media/video/rk_camsys/camsys_marvin.c create mode 100755 drivers/media/video/rk_camsys/camsys_marvin.h create mode 100755 drivers/media/video/rk_camsys/camsys_mipicsi_phy.c create mode 100755 drivers/media/video/rk_camsys/camsys_mipicsi_phy.h create mode 100755 include/media/camsys_head.h diff --git a/arch/arm/boot/dts/rk3288-pinctrl.dtsi b/arch/arm/boot/dts/rk3288-pinctrl.dtsi index ade638143e98..e542b012afcb 100755 --- a/arch/arm/boot/dts/rk3288-pinctrl.dtsi +++ b/arch/arm/boot/dts/rk3288-pinctrl.dtsi @@ -1054,6 +1054,68 @@ }; + isp_pin { + isp_mipi:isp_mipi{ + rockchip,pins = ; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + isp_dvp_sync_d2d9:isp_dvp_force { + rockchip,pins = ,, + ,, + ,, + ,, + ,, + ,; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + + }; + + isp_dvp_d0d1:isp_d0d1 { + rockchip,pins = ,; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + + isp_dvpd10d11:ispd10d11 { + rockchip,pins = ,; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + + isp_shutter:isp_shutter { + rockchip,pins = ,; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + + isp_flash_trigger:isp_trigger { + rockchip,pins = ; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + + isp_prelight:isp_prelight { + rockchip,pins = ; + rockchip,pull = ; + //rockchip,voltage = ; + rockchip,drive = ; + //rockchip,tristate = ; + }; + }; }; }; diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi index 27cc2ec22ba2..44a64d75ab15 100755 --- a/arch/arm/boot/dts/rk3288.dtsi +++ b/arch/arm/boot/dts/rk3288.dtsi @@ -1002,4 +1002,20 @@ >; }; + isp:isp@0xFF910000{ + compatible = "rockchip,isp"; + reg = <0xFF910000 0x10000>; + interrupts = ; + clocks = <&clk_gates16 2>, <&clk_gates16 1>, <&clk_isp>, <&clk_isp_jpe>, <&dummy>, <&clk_cif_out>; + clock_names = "aclk_isp", "hclk_isp", "clk_isp", "clk_isp_jpe", "pclkin_isp", "clk_vipout"; + pinctrl-names = "default", "isp_dvp8bit","isp_dvp10bit","isp_dvp12bit"; + pinctrl-0 = <&isp_mipi>; + pinctrl-1 = <&isp_mipi &isp_dvp_sync_d2d9>; + pinctrl-2 = <&isp_mipi &isp_dvp_sync_d2d9 &isp_dvp_d0d1>; + pinctrl-3 = <&isp_mipi &isp_dvp_sync_d2d9 &isp_dvp_d0d1 &isp_dvpd10d11>; + + status = "disabled"; + }; + + }; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 8270388e2a0d..8c14041057d8 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -198,5 +198,6 @@ config MEDIA_ATTACH source "drivers/media/i2c/Kconfig" source "drivers/media/tuners/Kconfig" source "drivers/media/dvb-frontends/Kconfig" +source "drivers/media/video/rk_camsys/Kconfig" endif # MEDIA_SUPPORT diff --git a/drivers/media/Makefile b/drivers/media/Makefile index 620f275a45c9..fe039e96f9c8 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -31,3 +31,4 @@ obj-y += rc/ obj-y += common/ platform/ pci/ usb/ mmc/ firewire/ parport/ obj-$(CONFIG_VIDEO_DEV) += radio/ +obj-y += video/rk_camsys/ diff --git a/drivers/media/video/rk_camsys/Kconfig b/drivers/media/video/rk_camsys/Kconfig new file mode 100755 index 000000000000..c4aba692c677 --- /dev/null +++ b/drivers/media/video/rk_camsys/Kconfig @@ -0,0 +1,18 @@ +config CAMSYS_DRV + tristate "camsys driver " + default n + +menu "RockChip camera system driver" + depends on CAMSYS_DRV + +config CAMSYS_MRV + tristate "camsys driver for marvin isp " + default n + ---help--- + +config CAMSYS_CIF + tristate "camsys driver for cif " + default n + ---help--- + +endmenu diff --git a/drivers/media/video/rk_camsys/Makefile b/drivers/media/video/rk_camsys/Makefile new file mode 100755 index 000000000000..4c0cc6a0bf9a --- /dev/null +++ b/drivers/media/video/rk_camsys/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for rockchip camsys driver +# +obj-$(CONFIG_CAMSYS_DRV) += camsys_drv.o +obj-$(CONFIG_CAMSYS_MRV) += camsys_marvin.o camsys_mipicsi_phy.o +obj-$(CONFIG_CAMSYS_CIF) += camsys_cif.o + diff --git a/drivers/media/video/rk_camsys/camsys_cif.c b/drivers/media/video/rk_camsys/camsys_cif.c new file mode 100755 index 000000000000..96e596482230 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_cif.c @@ -0,0 +1,340 @@ +#include "camsys_cif.h" + +static const char miscdev_cif0_name[] = CAMSYS_CIF0_DEVNAME; +static const char miscdev_cif1_name[] = CAMSYS_CIF1_DEVNAME; + +static int camsys_cif_iomux_cb(camsys_extdev_t *extdev,void *ptr) +{ + unsigned int cif_index; + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + + if (strcmp(dev_name(camsys_dev->miscdev.this_device), CAMSYS_CIF1_DEVNAME)==0) { + cif_index = 1; + } else { + cif_index = 0; + } + +#if defined(CONFIG_ARCH_RK3066B) || defined(CONFIG_ARCH_RK3188) + switch(cif_index){ + case 0: + { + iomux_set(CIF0_CLKOUT); + write_grf_reg(GRF_IO_CON3, (CIF_DRIVER_STRENGTH_MASK|CIF_DRIVER_STRENGTH_8MA)); + write_grf_reg(GRF_IO_CON4, (CIF_CLKOUT_AMP_MASK|CIF_CLKOUT_AMP_1V8)); + break; + } + default: + camsys_err("Cif index(%d) is invalidate!!!\n",cif_index); + break; + } +#elif defined(CONFIG_ARCH_RK30) + switch(cif_index){ + case 0: + { + rk30_mux_api_set(GPIO1B3_CIF0CLKOUT_NAME, GPIO1B_CIF0_CLKOUT); + break; + } + case 1: + { + rk30_mux_api_set(GPIO1C0_CIF1DATA2_RMIICLKOUT_RMIICLKIN_NAME,GPIO1C_CIF1_DATA2); + rk30_mux_api_set(GPIO1C1_CIFDATA3_RMIITXEN_NAME,GPIO1C_CIF_DATA3); + rk30_mux_api_set(GPIO1C2_CIF1DATA4_RMIITXD1_NAME,GPIO1C_CIF1_DATA4); + rk30_mux_api_set(GPIO1C3_CIFDATA5_RMIITXD0_NAME,GPIO1C_CIF_DATA5); + rk30_mux_api_set(GPIO1C4_CIFDATA6_RMIIRXERR_NAME,GPIO1C_CIF_DATA6); + rk30_mux_api_set(GPIO1C5_CIFDATA7_RMIICRSDVALID_NAME,GPIO1C_CIF_DATA7); + rk30_mux_api_set(GPIO1C6_CIFDATA8_RMIIRXD1_NAME,GPIO1C_CIF_DATA8); + rk30_mux_api_set(GPIO1C7_CIFDATA9_RMIIRXD0_NAME,GPIO1C_CIF_DATA9); + + rk30_mux_api_set(GPIO1D0_CIF1VSYNC_MIIMD_NAME,GPIO1D_CIF1_VSYNC); + rk30_mux_api_set(GPIO1D1_CIF1HREF_MIIMDCLK_NAME,GPIO1D_CIF1_HREF); + rk30_mux_api_set(GPIO1D2_CIF1CLKIN_NAME,GPIO1D_CIF1_CLKIN); + rk30_mux_api_set(GPIO1D3_CIF1DATA0_NAME,GPIO1D_CIF1_DATA0); + rk30_mux_api_set(GPIO1D4_CIF1DATA1_NAME,GPIO1D_CIF1_DATA1); + rk30_mux_api_set(GPIO1D5_CIF1DATA10_NAME,GPIO1D_CIF1_DATA10); + rk30_mux_api_set(GPIO1D6_CIF1DATA11_NAME,GPIO1D_CIF1_DATA11); + rk30_mux_api_set(GPIO1D7_CIF1CLKOUT_NAME,GPIO1D_CIF1_CLKOUT); + break; + } + default: + camsys_err("Cif index(%d) is invalidate!!!\n", cif_index); + break; + } +#elif defined(CONFIG_ARCH_RK319X) + switch(cif_index){ + case 0: + { + unsigned int cif_vol_sel; + //set cif vol domain + cif_vol_sel = __raw_readl(RK30_GRF_BASE+0x018c); + __raw_writel( (cif_vol_sel |0x20002),RK30_GRF_BASE+0x018c); + //set driver strength + __raw_writel(0xffffffff, RK30_GRF_BASE+0x01dc); + + iomux_set(CIF0_CLKOUT); + iomux_set(CIF0_CLKIN); + iomux_set(CIF0_HREF); + iomux_set(CIF0_VSYNC); + iomux_set(CIF0_D0); + iomux_set(CIF0_D1); + iomux_set(CIF0_D2); + iomux_set(CIF0_D3); + iomux_set(CIF0_D4); + iomux_set(CIF0_D5); + iomux_set(CIF0_D6); + iomux_set(CIF0_D7); + iomux_set(CIF0_D8); + iomux_set(CIF0_D9); + camsys_trace(1, "%s cif iomux success\n",dev_name(camsys_dev->miscdev.this_device)); + break; + } + case 1: + default: + camsys_err("Cif index(%d) is invalidate!!!\n", cif_index); + break; + } +#endif + + return 0; +} +static int camsys_cif_clkin_cb(void *ptr, unsigned int on) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + camsys_cif_clk_t *clk = (camsys_cif_clk_t*)camsys_dev->clk; + + spin_lock(&clk->lock); + if (on && !clk->in_on) { + clk_enable(clk->pd_cif); + clk_enable(clk->aclk_cif); + clk_enable(clk->hclk_cif); + clk_enable(clk->cif_clk_in); + + clk->in_on = true; + camsys_trace(1, "%s clock in turn on",dev_name(camsys_dev->miscdev.this_device)); + } else if (!on && clk->in_on) { + clk_disable(clk->aclk_cif); + clk_disable(clk->hclk_cif); + clk_disable(clk->cif_clk_in); + clk_disable(clk->pd_cif); + clk->in_on = false; + camsys_trace(1, "%s clock in turn off",dev_name(camsys_dev->miscdev.this_device)); + } + spin_unlock(&clk->lock); + return 0; +} + +static int camsys_cif_clkout_cb(void *ptr, unsigned int on) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + camsys_cif_clk_t *clk = (camsys_cif_clk_t*)camsys_dev->clk; + struct clk *cif_clk_out_div; + + + spin_lock(&clk->lock); + if (on && (clk->out_on != on)) { + clk_enable(clk->cif_clk_out); + clk_set_rate(clk->cif_clk_out,on); + + clk->out_on = on; + camsys_trace(1, "%s clock out(rate: %dHz) turn on",dev_name(camsys_dev->miscdev.this_device), + clk->out_on); + } else if (!on && clk->out_on) { + if (strcmp(dev_name(camsys_dev->miscdev.this_device),miscdev_cif1_name)==0) { + cif_clk_out_div = clk_get(NULL, "cif1_out_div"); + } else{ + cif_clk_out_div = clk_get(NULL, "cif0_out_div"); + if(IS_ERR_OR_NULL(cif_clk_out_div)) { + cif_clk_out_div = clk_get(NULL, "cif_out_div"); + } + } + + if(!IS_ERR_OR_NULL(cif_clk_out_div)) { + clk_set_parent(clk->cif_clk_out, cif_clk_out_div); + clk_put(cif_clk_out_div); + } else { + camsys_warn("%s clock out may be not off!", dev_name(camsys_dev->miscdev.this_device)); + } + clk_disable(clk->cif_clk_out); + clk->out_on = 0; + + camsys_trace(1, "%s clock out turn off",dev_name(camsys_dev->miscdev.this_device)); + } + spin_unlock(&clk->lock); + + { + // __raw_writel(0x00, CRU_PCLK_REG30+RK30_CRU_BASE); + } + + return 0; +} + +static irqreturn_t camsys_cif_irq(int irq, void *data) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)data; + camsys_irqstas_t *irqsta; + camsys_irqpool_t *irqpool; + unsigned int intsta,frmsta; + + intsta = __raw_readl(camsys_dev->devmems.registermem->vir_base + CIF_INITSTA); + frmsta = __raw_readl(camsys_dev->devmems.registermem->vir_base + CIF_FRAME_STATUS); + printk("get oneframe,intsta = 0x%x \n",intsta); + + if (intsta & 0x200) { + __raw_writel(0x200,camsys_dev->devmems.registermem->vir_base + CIF_INITSTA); + __raw_writel(0xf000,camsys_dev->devmems.registermem->vir_base + CIF_CTRL); + } + + if (intsta &0x01) { + __raw_writel(0x01,camsys_dev->devmems.registermem->vir_base + CIF_INITSTA); + __raw_writel(0x02,camsys_dev->devmems.registermem->vir_base + CIF_FRAME_STATUS); + __raw_writel(0xf001,camsys_dev->devmems.registermem->vir_base + CIF_CTRL); + } + + spin_lock(&camsys_dev->irq.lock); + list_for_each_entry(irqpool, &camsys_dev->irq.irq_pool, list) { + spin_lock(&irqpool->lock); + if (!list_empty(&irqpool->deactive)) { + irqsta = list_first_entry(&irqpool->deactive, camsys_irqstas_t, list); + irqsta->sta.mis = intsta; + irqsta->sta.ris = intsta; + list_del_init(&irqsta->list); + list_add_tail(&irqsta->list,&irqpool->active); + irqsta = list_first_entry(&irqpool->active, camsys_irqstas_t, list); + //wake_up_all(&camsys_dev->irq.irq_done); + wake_up(&irqpool->done); + } + spin_unlock(&irqpool->lock); + } + spin_unlock(&camsys_dev->irq.lock); + + return IRQ_HANDLED; +} + +static int camsys_cif_remove(struct platform_device *pdev) +{ + camsys_dev_t *camsys_dev = platform_get_drvdata(pdev); + camsys_cif_clk_t *cif_clk; + + if (camsys_dev->clk != NULL) { + cif_clk = (camsys_cif_clk_t*)camsys_dev->clk; + if (cif_clk->out_on) + camsys_cif_clkout_cb(camsys_dev->clk, 0); + if (cif_clk->in_on) + camsys_cif_clkin_cb(camsys_dev->clk, 0); + + if (cif_clk->pd_cif) + clk_put(cif_clk->pd_cif); + if (cif_clk->aclk_cif) + clk_put(cif_clk->aclk_cif); + if (cif_clk->hclk_cif) + clk_put(cif_clk->hclk_cif); + if (cif_clk->cif_clk_in) + clk_put(cif_clk->cif_clk_in); + if (cif_clk->cif_clk_out) + clk_put(cif_clk->cif_clk_out); + + kfree(cif_clk); + cif_clk = NULL; + } + + return 0; +} + +int camsys_cif_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev) +{ + int err = 0; + camsys_cif_clk_t *cif_clk; + + //Irq init + err = request_irq(camsys_dev->irq.irq_id, camsys_cif_irq, 0, CAMSYS_CIF_IRQNAME,camsys_dev); + if (err) { + camsys_err("request irq for %s failed",CAMSYS_CIF_IRQNAME); + goto end; + } + + //Clk and Iomux init + cif_clk = kzalloc(sizeof(camsys_cif_clk_t),GFP_KERNEL); + if (cif_clk == NULL) { + camsys_err("Allocate camsys_cif_clk_t failed!"); + err = -EINVAL; + goto end; + } + + if (strcmp(dev_name(&pdev->dev),CAMSYS_PLATFORM_CIF1_NAME) == 0) { + cif_clk->pd_cif = clk_get(NULL, "pd_cif1"); + cif_clk->aclk_cif = clk_get(NULL, "aclk_cif1"); + cif_clk->hclk_cif = clk_get(NULL, "hclk_cif1"); + cif_clk->cif_clk_in = clk_get(NULL, "cif1_in"); + cif_clk->cif_clk_out = clk_get(NULL, "cif1_out"); + spin_lock_init(&cif_clk->lock); + cif_clk->in_on = false; + cif_clk->out_on = false; + } else { + cif_clk->pd_cif = clk_get(NULL, "pd_cif0"); + cif_clk->aclk_cif = clk_get(NULL, "aclk_cif0"); + cif_clk->hclk_cif = clk_get(NULL, "hclk_cif0"); + cif_clk->cif_clk_in = clk_get(NULL, "pclkin_cif0"); + cif_clk->cif_clk_out = clk_get(NULL, "cif0_out"); + spin_lock_init(&cif_clk->lock); + cif_clk->in_on = false; + cif_clk->out_on = false; + } + camsys_dev->clk = (void*)cif_clk; + camsys_dev->clkin_cb = camsys_cif_clkin_cb; + camsys_dev->clkout_cb = camsys_cif_clkout_cb; + camsys_dev->iomux = camsys_cif_iomux_cb; + + //Misc device init + camsys_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + if (strcmp(dev_name(&pdev->dev),CAMSYS_PLATFORM_CIF1_NAME) == 0) { + camsys_dev->miscdev.name = miscdev_cif1_name; + camsys_dev->miscdev.nodename = miscdev_cif1_name; + } else { + camsys_dev->miscdev.name = miscdev_cif0_name; + camsys_dev->miscdev.nodename = miscdev_cif0_name; + } + camsys_dev->miscdev.fops = &camsys_fops; + err = misc_register(&camsys_dev->miscdev); + if (err < 0) { + camsys_trace(1,"Register /dev/%s misc device failed",camsys_dev->miscdev.name); + goto misc_register_failed; + } else { + camsys_trace(1,"Register /dev/%s misc device success",camsys_dev->miscdev.name); + } + + //Variable init + if (strcmp(dev_name(&pdev->dev),CAMSYS_PLATFORM_CIF1_NAME) == 0) { + camsys_dev->dev_id = CAMSYS_DEVID_CIF_1; + } else { + camsys_dev->dev_id = CAMSYS_DEVID_CIF_0; + } + camsys_dev->platform_remove = camsys_cif_remove; + + return 0; + +misc_register_failed: + if (!IS_ERR(camsys_dev->miscdev.this_device)) { + misc_deregister(&camsys_dev->miscdev); + } + + if (cif_clk) { + + if (cif_clk->pd_cif) + clk_put(cif_clk->pd_cif); + if (cif_clk->aclk_cif) + clk_put(cif_clk->aclk_cif); + if (cif_clk->hclk_cif) + clk_put(cif_clk->hclk_cif); + if (cif_clk->cif_clk_in) + clk_put(cif_clk->cif_clk_in); + if (cif_clk->cif_clk_out) + clk_put(cif_clk->cif_clk_out); + + kfree(cif_clk); + cif_clk = NULL; + } + +end: + return err; +} +EXPORT_SYMBOL_GPL(camsys_cif_probe_cb); + diff --git a/drivers/media/video/rk_camsys/camsys_cif.h b/drivers/media/video/rk_camsys/camsys_cif.h new file mode 100755 index 000000000000..61f662656550 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_cif.h @@ -0,0 +1,58 @@ +#ifndef __CAMSYS_CIF_H__ +#define __CAMSYS_CIF_H__ + +#include "camsys_internal.h" + +#define CAMSYS_CIF_IRQNAME "CifIrq" + + +#define CIF_BASE 0x00 +#define CIF_CTRL (CIF_BASE) +#define CIF_INITSTA (CIF_BASE+0x08) +#define CIF_FRAME_STATUS (CIF_BASE+0x60) +#define CIF_LAST_LINE (CIF_BASE+0x68) +#define CIF_LAST_PIX (CIF_BASE+0x6c) +#define CRU_PCLK_REG30 0xbc + + +#if defined(CONFIG_ARCH_RK3066B) || defined(CONFIG_ARCH_RK3188) || defined(CONFIG_ARCH_ROCKCHIP) +//GRF_IO_CON3 0x100 +#define CIF_DRIVER_STRENGTH_2MA (0x00 << 12) +#define CIF_DRIVER_STRENGTH_4MA (0x01 << 12) +#define CIF_DRIVER_STRENGTH_8MA (0x02 << 12) +#define CIF_DRIVER_STRENGTH_12MA (0x03 << 12) +#define CIF_DRIVER_STRENGTH_MASK (0x03 << 28) + +//GRF_IO_CON4 0x104 +#define CIF_CLKOUT_AMP_3V3 (0x00 << 10) +#define CIF_CLKOUT_AMP_1V8 (0x01 << 10) +#define CIF_CLKOUT_AMP_MASK (0x01 << 26) + +#define write_grf_reg(addr, val) __raw_writel(val, addr+RK_GRF_VIRT) +#define read_grf_reg(addr) __raw_readl(addr+RK_GRF_VIRT) +#define mask_grf_reg(addr, msk, val) write_grf_reg(addr,(val)|((~(msk))&read_grf_reg(addr))) +#else +#define write_grf_reg(addr, val) +#define read_grf_reg(addr) 0 +#define mask_grf_reg(addr, msk, val) +#endif + + +typedef struct camsys_cif_clk_s { + struct clk *pd_cif; + struct clk *aclk_cif; + struct clk *hclk_cif; + struct clk *cif_clk_in; + bool in_on; + + struct clk *cif_clk_out; + unsigned int out_on; + + spinlock_t lock; +} camsys_cif_clk_t; + + +int camsys_cif_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev); + +#endif + diff --git a/drivers/media/video/rk_camsys/camsys_drv.c b/drivers/media/video/rk_camsys/camsys_drv.c new file mode 100755 index 000000000000..fb2a2a0cedd9 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_drv.c @@ -0,0 +1,1422 @@ +#include + +#include "camsys_cif.h" +#include "camsys_marvin.h" +#include "camsys_mipicsi_phy.h" +#include "camsys_gpio.h" + +unsigned int camsys_debug=1; +module_param(camsys_debug, int, S_IRUGO|S_IWUSR); + +static int drv_version = CAMSYS_DRIVER_VERSION; +module_param(drv_version, int, S_IRUGO); +static int head_version = CAMSYS_HEAD_VERSION; +module_param(head_version, int, S_IRUGO); + + +typedef struct camsys_devs_s { + spinlock_t lock; + struct list_head devs; +} camsys_devs_t; + +static camsys_devs_t camsys_devs; + +static int camsys_i2c_write(camsys_i2c_info_t *i2cinfo, camsys_dev_t *camsys_dev) +{ + int err = 0,i,j; + unsigned char buf[8],*bufp; + unsigned short msg_times,totallen,onelen; + struct i2c_msg msg[1]; + struct i2c_adapter *adapter; + camsys_extdev_t *extdev; + + adapter = i2c_get_adapter(i2cinfo->bus_num); + if (adapter == NULL) { + camsys_err("Get %d i2c adapter is failed!",i2cinfo->bus_num); + err = -EINVAL; + goto end; + } + + if (i2cinfo->i2cbuf_directly) { + if (camsys_dev->devmems.i2cmem == NULL) { + camsys_err("%s has not i2c mem, it isn't support i2c buf write!",dev_name(camsys_dev->miscdev.this_device)); + err = -EINVAL; + goto end; + } + totallen = (i2cinfo->i2cbuf_bytes&0xffff); + onelen = (i2cinfo->i2cbuf_bytes&0xffff0000)>>16; + msg_times = totallen/onelen; + if (totallen > camsys_dev->devmems.i2cmem->size) { + camsys_err("Want to write 0x%x bytes, i2c memory(size: 0x%x) is overlap",totallen,camsys_dev->devmems.i2cmem->size); + err = -EINVAL; + goto end; + } + bufp = (unsigned char*)camsys_dev->devmems.i2cmem->vir_base; + } else { + for (i=0; ireg_size; i++) { + buf[i] = (i2cinfo->reg_addr>>((i2cinfo->reg_size-1-i)*8))&0xff; + } + for (j=0; jval_size; j++) { + buf[i+j] = (i2cinfo->val>>(i2cinfo->val_size-1-j))&0xff; + } + bufp = buf; + onelen = i2cinfo->val_size + i2cinfo->reg_size; + msg_times = 1; + } + + err = -EAGAIN; + msg->addr = (i2cinfo->slave_addr>>1); + msg->flags = 0; + msg->scl_rate = i2cinfo->speed; + // msg->read_type = 0; + msg->len = onelen; + for (i=0; ibuf = bufp+i*onelen; + err = i2c_transfer(adapter, msg, 1); + if (err < 0) { + camsys_err("i2c write dev(addr:0x%x) failed!",i2cinfo->slave_addr); + udelay(10); + } + } + +end: + #if ((defined CONFIG_ARCH_RK319X) || (CONFIG_ARCH_ROCKCHIP)) + if (!list_empty(&camsys_dev->extdevs.active)) { + list_for_each_entry(extdev, &camsys_dev->extdevs.active, active) { + if (extdev->phy.type == CamSys_Phy_Cif) { + if (extdev->phy.info.cif.fmt >= CamSys_Fmt_Raw_10b) { + //iomux_set(CIF0_D0); // ZYC FOR 32 + //iomux_set(CIF0_D1); // ZYC FOR 32 + } + } + } + } + #endif + return err; +} + +static int camsys_i2c_read(camsys_i2c_info_t *i2cinfo, camsys_dev_t *camsys_dev) +{ + int err = 0,i,retry=2,tmp; + unsigned char buf[8]; + struct i2c_msg msg[2]; + struct i2c_adapter *adapter; + camsys_extdev_t *extdev; + + adapter = i2c_get_adapter(i2cinfo->bus_num); + if (adapter == NULL) { + camsys_err("Get %d i2c adapter is failed!",i2cinfo->bus_num); + err = -EINVAL; + goto end; + } + + for (i=0; ireg_size; i++) { + buf[i] = (i2cinfo->reg_addr>>((i2cinfo->reg_size-1-i)*8))&0xff; + } + + msg[0].addr = (i2cinfo->slave_addr>>1); + msg[0].flags = 0; + msg[0].scl_rate = i2cinfo->speed; + //msg[0].read_type = 0; + msg[0].buf = buf; + msg[0].len = i2cinfo->reg_size; + + msg[1].addr = (i2cinfo->slave_addr>>1); + msg[1].flags = I2C_M_RD; + msg[1].scl_rate = i2cinfo->speed; +// msg[1].read_type = 0; + msg[1].buf = buf; + msg[1].len = (unsigned short)i2cinfo->val_size; + err = -EAGAIN; + + while ((retry-- > 0) && (err < 0)) { /* ddl@rock-chips.com : Transfer again if transent is failed */ + err = i2c_transfer(adapter, msg, 2); + + if (err >= 0) { + err = 0; + } else { + camsys_err("i2c read dev(addr:0x%x) failed,try again-%d!",i2cinfo->slave_addr,retry); + udelay(10); + } + } + + if (err==0) { + i2cinfo->val = 0x00; + for(i=0; ival_size; i++) { + tmp = buf[i]; + i2cinfo->val |= (tmp<<((i2cinfo->val_size-1-i)*8)); + } + } + +end: + #if ((defined CONFIG_ARCH_RK319X) || (CONFIG_ARCH_ROCKCHIP)) + if (!list_empty(&camsys_dev->extdevs.active)) { + list_for_each_entry(extdev, &camsys_dev->extdevs.active, active) { + if (extdev->phy.type == CamSys_Phy_Cif) { + if (extdev->phy.info.cif.fmt >= CamSys_Fmt_Raw_10b) { + //iomux_set(CIF0_D0);//ZYC FOR 32 + //iomux_set(CIF0_D1);//ZYC FOR 32 + } + } + } + } + #endif + return err; +} + + +static int camsys_extdev_register(camsys_devio_name_t *devio, camsys_dev_t *camsys_dev) +{ + int err = 0,i; + camsys_extdev_t *extdev; + camsys_regulator_info_t *regulator_info; + camsys_regulator_t *regulator; + camsys_gpio_info_t *gpio_info; + camsys_gpio_t *gpio; + + + if ((devio->dev_id & CAMSYS_DEVID_EXTERNAL) == 0) { + err = -EINVAL; + camsys_err("dev_id: 0x%x is not support for camsys!",devio->dev_id); + goto end; + } + + if (devio->phy.type == CamSys_Phy_Mipi) { + if (camsys_find_devmem(CAMSYS_REGISTER_MIPIPHY_RES_NAME, camsys_dev) == NULL) { + camsys_err("dev_id: 0x%x is connect to MIPI CSI, but %s isn't support",devio->dev_id, + dev_name(camsys_dev->miscdev.this_device)); + + err = -EINVAL; + goto end; + } + } + + + extdev = camsys_find_extdev(devio->dev_id, camsys_dev); + if (extdev != NULL) { + err = 0; + camsys_warn("Extdev(dev_id: 0x%x) has been registered in %s!", + devio->dev_id, dev_name(camsys_dev->miscdev.this_device)); + goto end; + } + + extdev = kzalloc(sizeof(camsys_extdev_t),GFP_KERNEL); + if (extdev == NULL) { + camsys_err("alloc camsys_extdev_t failed!"); + err = -ENOMEM; + goto end; + } + + regulator_info = &devio->avdd; + regulator = &extdev->avdd; + for (i=0; i<4; i++) { + if (strcmp(regulator_info->name,"NC")) { + regulator->ldo = regulator_get(NULL,regulator_info->name); + if (IS_ERR(regulator->ldo)) { + camsys_err("Get %s regulator for dev_id 0x%x failed!",regulator_info->name,devio->dev_id); + err = -EINVAL; + goto fail; + } + + regulator->min_uv = regulator_info->min_uv; + regulator->max_uv = regulator_info->max_uv; + camsys_trace(1,"Get %s regulator(min: %duv max: %duv) for dev_id 0x%x success", + regulator_info->name,regulator->min_uv,regulator->max_uv, + devio->dev_id); + } else { + regulator->ldo = NULL; + regulator->min_uv = 0; + regulator->max_uv = 0; + } + + regulator++; + regulator_info++; + } + + gpio_info = &devio->pwrdn; + gpio = &extdev->pwrdn; + for (i=0; i<4; i++) { + if (strcmp(gpio_info->name,"NC")) { + gpio->io = camsys_gpio_get(gpio_info->name); + if (gpio->io < 0) { + camsys_err("Get %s gpio for dev_id 0x%x failed!",gpio_info->name,devio->dev_id); + err = -EINVAL; + goto fail; + } + if (gpio_request(gpio->io,"camsys_gpio")<0) { + camsys_err("Request %s(%d) failed",gpio_info->name,gpio->io); + } + gpio->active = gpio_info->active; + camsys_trace(1,"Get %s(%d) gpio(active: %d) for dev_id 0x%x success!", + gpio_info->name,gpio->io,gpio->active,devio->dev_id); + } else { + gpio->io = 0xffffffff; + gpio->active = 0xffffffff; + } + + gpio++; + gpio_info++; + } + + extdev->pdev = camsys_dev->pdev; + extdev->phy = devio->phy; + extdev->clk = devio->clk; + extdev->dev_id = devio->dev_id; + //spin_lock(&camsys_dev->lock); + mutex_lock(&camsys_dev->extdevs.mut); + list_add_tail(&extdev->list, &camsys_dev->extdevs.list); + //spin_unlock(&camsys_dev->lock); + mutex_unlock(&camsys_dev->extdevs.mut); + + camsys_dev->iomux(extdev, (void*)camsys_dev); + + camsys_trace(1,"Extdev(dev_id: 0x%x) register success",extdev->dev_id); + + return 0; +fail: + if (extdev) { + kfree(extdev); + extdev = NULL; + } +end: + + return err; +} + +static int camsys_extdev_deregister(unsigned int dev_id, camsys_dev_t *camsys_dev, bool all) +{ + int err = 0,i; + camsys_extdev_t *extdev; + camsys_regulator_t *regulator; + camsys_gpio_t *gpio; + + if (all == false) { + if ((dev_id & CAMSYS_DEVID_EXTERNAL) == 0) { + err = -EINVAL; + camsys_err("dev_id: 0x%x is not support for %s!",dev_id, dev_name(camsys_dev->miscdev.this_device)); + goto end; + } + + extdev = camsys_find_extdev(dev_id, camsys_dev); + if (extdev != NULL) { + err = -EINVAL; + camsys_warn("Extdev(dev_id: 0x%x) isn't registered in %s!", + dev_id, dev_name(camsys_dev->miscdev.this_device)); + goto end; + } + + regulator = &extdev->avdd; + for (i=0; i<4; i++) { + if (!IS_ERR_OR_NULL(regulator->ldo)) { + while(regulator_is_enabled(regulator->ldo)>0) + regulator_disable(regulator->ldo); + regulator_put(regulator->ldo); + } + regulator++; + } + + gpio = &extdev->pwrdn; + for (i=0; i<4; i++) { + if (gpio->io!=0xffffffff) { + gpio_free(gpio->io); + } + gpio++; + } + + //spin_lock(&camsys_dev->lock); + mutex_lock(&camsys_dev->extdevs.mut); + list_del_init(&extdev->list); + list_del_init(&extdev->active); + //spin_unlock(&camsys_dev->lock); + mutex_unlock(&camsys_dev->extdevs.mut); + kfree(extdev); + extdev = NULL; + camsys_trace(1,"Extdev(dev_id: 0x%x) is deregister success", extdev->dev_id); + } else { + //spin_lock(&camsys_dev->lock); + mutex_lock(&camsys_dev->extdevs.mut); + while (!list_empty(&camsys_dev->extdevs.list)) { + + extdev = list_first_entry(&camsys_dev->extdevs.list, camsys_extdev_t, list); + if (extdev) { + regulator = &extdev->avdd; + for (i=0; i<4; i++) { + if (!IS_ERR(regulator->ldo)) { + while(regulator_is_enabled(regulator->ldo)>0) + regulator_disable(regulator->ldo); + regulator_put(regulator->ldo); + } + regulator++; + } + + gpio = &extdev->pwrdn; + for (i=0; i<4; i++) { + if (gpio->io!=0xffffffff) { + gpio_free(gpio->io); + } + gpio++; + } + camsys_trace(1,"Extdev(dev_id: 0x%x) is deregister success", extdev->dev_id); + list_del_init(&extdev->list); + list_del_init(&extdev->active); + kfree(extdev); + extdev=NULL; + } + } + //spin_unlock(&camsys_dev->lock); + mutex_unlock(&camsys_dev->extdevs.mut); + camsys_trace(1, "All extdev is deregister success!"); + } + + +end: + return err; + +} + +static int camsys_sysctl(camsys_sysctrl_t *devctl, camsys_dev_t *camsys_dev) +{ + int i; + int err = 0; + camsys_extdev_t *extdev,*extdev2; + + //spin_lock(&camsys_dev->lock); + mutex_lock(&camsys_dev->extdevs.mut); + if(devctl->ops == 0xaa){ + dump_stack(); + return 0; + } + //Internal + if (camsys_dev->dev_id & devctl->dev_mask) { + switch (devctl->ops) + { + case CamSys_ClkIn: + { + camsys_dev->clkin_cb(camsys_dev,devctl->on); + break; + } + + case CamSys_Rst: + { + camsys_dev->reset_cb(camsys_dev); + break; + } + default: + break; + + } + } + + //External + for (i=0; i<8; i++) { + if (devctl->dev_mask & (1<<(i+24))) { + extdev = camsys_find_extdev((1<<(i+24)), camsys_dev); + if (extdev) { + camsys_sysctl_extdev(extdev, devctl, camsys_dev); + + if (devctl->ops == CamSys_ClkIn) { + if (devctl->on) { + list_add_tail(&extdev->active,&camsys_dev->extdevs.active); + } else { + if (!list_empty(&camsys_dev->extdevs.active)) { /* ddla@rock-chips.com: v0.0.7 */ + list_for_each_entry(extdev2, &camsys_dev->extdevs.active, active) { + if (extdev2 == extdev) { + list_del_init(&extdev->active); + break; + } + } + } + } + } + + } else { + camsys_err("Can not find dev_id 0x%x device in %s!", (1<<(i+24)), dev_name(camsys_dev->miscdev.this_device)); + } + } + } + + //spin_unlock(&camsys_dev->lock); + mutex_unlock(&camsys_dev->extdevs.mut); + return err; +} +static int camsys_phy_ops (camsys_extdev_phy_t *phy, void* ptr, unsigned int on) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + int err = 0; + + if (phy->type == CamSys_Phy_Mipi) { + if (camsys_dev->mipiphy.ops && camsys_dev->mipiphy.clkin_cb) { + err = camsys_dev->mipiphy.clkin_cb(camsys_dev,on); + err = camsys_dev->mipiphy.ops(&phy->info.mipi,&camsys_dev->mipiphy, on); + } else { + camsys_err("%s isn't support mipi phy",dev_name(camsys_dev->miscdev.this_device)); + err = -EINVAL; + } + } else if (phy->type == CamSys_Phy_Cif) { + if (camsys_dev->cifphy.ops && camsys_dev->cifphy.clkin_cb) { + err = camsys_dev->cifphy.clkin_cb(camsys_dev,on); + err = camsys_dev->cifphy.ops(&phy->info.cif,&camsys_dev->cifphy, on); + } else { + //camsys_err("%s isn't support cif phy",dev_name(camsys_dev->miscdev.this_device)); + //err = -EINVAL; + } + } + + return err; +} +static int camsys_irq_connect(camsys_irqcnnt_t *irqcnnt, camsys_dev_t *camsys_dev) +{ + int err = 0,i; + camsys_irqpool_t *irqpool; + unsigned long int flags; + + if ((irqcnnt->mis != MRV_ISP_MIS) && + (irqcnnt->mis != MRV_MIPI_MIS) && + (irqcnnt->mis != MRV_MI_MIS)) { + + camsys_err("this thread(pid: %d) irqcnnt->mis(0x%x) is invalidate, irq connect failed!", + irqcnnt->pid, irqcnnt->mis); + + err = -EINVAL; + goto end; + } + + spin_lock_irqsave(&camsys_dev->irq.lock,flags); + if (!list_empty(&camsys_dev->irq.irq_pool)) { + list_for_each_entry(irqpool, &camsys_dev->irq.irq_pool, list) { + if (irqpool->pid == irqcnnt->pid) { + camsys_warn("this thread(pid: %d) had been connect irq!",current->pid); + spin_unlock(&camsys_dev->irq.lock); + goto end; + } + } + } + spin_unlock_irqrestore(&camsys_dev->irq.lock,flags); + + irqpool = kzalloc(sizeof(camsys_irqpool_t),GFP_KERNEL); + if (irqpool) { + spin_lock_init(&irqpool->lock); + irqpool->pid = irqcnnt->pid; + irqpool->timeout = irqcnnt->timeout; + irqpool->mis = irqcnnt->mis; + irqpool->icr = irqcnnt->icr; + INIT_LIST_HEAD(&irqpool->active); + INIT_LIST_HEAD(&irqpool->deactive); + init_waitqueue_head(&irqpool->done); + for (i=0; ipool[i].list, &irqpool->deactive); + } + } + + spin_lock_irqsave(&camsys_dev->irq.lock,flags); + //camsys_dev->irq.timeout = irqcnnt->timeout; + list_add_tail(&irqpool->list, &camsys_dev->irq.irq_pool); + spin_unlock_irqrestore(&camsys_dev->irq.lock,flags); + camsys_trace(1, "Thread(pid: %d) connect %s irq success! mis: 0x%x icr: 0x%x ", irqpool->pid, dev_name(camsys_dev->miscdev.this_device), + irqpool->mis,irqpool->icr); + +end: + return err; +} +static int active_list_isnot_empty(camsys_irqpool_t *irqpool) +{ + int err; + unsigned long int flags; + + spin_lock_irqsave(&irqpool->lock,flags); + err = list_empty(&irqpool->active); + spin_unlock_irqrestore(&irqpool->lock,flags); + + return !err; + +} +static int camsys_irq_wait(camsys_irqsta_t *irqsta, camsys_dev_t *camsys_dev) +{ + int err = 0; + bool find_pool = false; + camsys_irqstas_t *irqstas; + camsys_irqpool_t *irqpool; + unsigned long int flags; + + spin_lock_irqsave(&camsys_dev->irq.lock,flags); + if (!list_empty(&camsys_dev->irq.irq_pool)) { + list_for_each_entry(irqpool, &camsys_dev->irq.irq_pool, list) { + if (irqpool->pid == current->pid) { + find_pool = true; + break; + } + } + } + spin_unlock_irqrestore(&camsys_dev->irq.lock,flags); + + if (find_pool == false) { + camsys_err("this thread(pid: %d) hasn't been connect irq, so wait irq failed!",current->pid); + err = -EINVAL; + goto end; + } + + + spin_lock_irqsave(&irqpool->lock,flags); + if (!list_empty(&irqpool->active)) { + irqstas = list_first_entry(&irqpool->active, camsys_irqstas_t, list); + *irqsta = irqstas->sta; + list_del_init(&irqstas->list); + list_add_tail(&irqstas->list,&irqpool->deactive); + spin_unlock_irqrestore(&irqpool->lock,flags); + } else { + spin_unlock_irqrestore(&irqpool->lock,flags); + + wait_event_interruptible_timeout(irqpool->done, + active_list_isnot_empty(irqpool), + usecs_to_jiffies(irqpool->timeout)); + + if (irqpool->pid == current->pid) { + if (active_list_isnot_empty(irqpool)) { + spin_lock_irqsave(&irqpool->lock,flags); + irqstas = list_first_entry(&irqpool->active, camsys_irqstas_t, list); + *irqsta = irqstas->sta; + list_del_init(&irqstas->list); + list_add_tail(&irqstas->list,&irqpool->deactive); + spin_unlock_irqrestore(&irqpool->lock,flags); + } else { + camsys_warn("Thread(pid: %d) wait irq timeout!!",current->pid); + err = -EAGAIN; + } + } else { + camsys_warn("Thread(pid: %d) has been disconnect!",current->pid); + err = -EAGAIN; + } + } + + if (err == 0) { + camsys_trace(3,"Thread(pid: %d) has been wake up for irq(mis: 0x%x ris:0x%x)!", + current->pid, irqsta->mis, irqsta->ris); + } + +end: + return err; +} + +static int camsys_irq_disconnect(camsys_irqcnnt_t *irqcnnt, camsys_dev_t *camsys_dev, bool all) +{ + int err = 0; + bool find_pool = false; + camsys_irqpool_t *irqpool; + unsigned long int flags; + + if (all == false) { + spin_lock_irqsave(&camsys_dev->irq.lock,flags); + printk("%s++++++++++++++++\n",__func__); + if (!list_empty(&camsys_dev->irq.irq_pool)) { + list_for_each_entry(irqpool, &camsys_dev->irq.irq_pool, list) { + if (irqpool->pid == irqcnnt->pid) { + find_pool = true; + irqpool->pid = 0; + break; + } + } + } + printk("%s -------------\n",__func__); + spin_unlock_irqrestore(&camsys_dev->irq.lock,flags); + + if (find_pool == false) { + camsys_err("this thread(pid: %d) have not been connect irq!, disconnect failed",current->pid); + } else { + wake_up_all(&irqpool->done); + } + + camsys_trace(1, "Thread(pid: %d) disconnect %s irq success!", irqcnnt->pid, dev_name(camsys_dev->miscdev.this_device)); + } else { + spin_lock_irqsave(&camsys_dev->irq.lock,flags); + while (!list_empty(&camsys_dev->irq.irq_pool)) { + irqpool = list_first_entry(&camsys_dev->irq.irq_pool, camsys_irqpool_t, list); + list_del_init(&irqpool->list); + irqpool->pid = 0; + wake_up_all(&irqpool->done); + kfree(irqpool); + irqpool = NULL; + } + spin_unlock_irqrestore(&camsys_dev->irq.lock,flags); + + camsys_trace(1, "All thread disconnect %s irq success!", dev_name(camsys_dev->miscdev.this_device)); + } + + + return err; +} + +static int camsys_querymem (camsys_dev_t *camsys_dev, camsys_querymem_t *qmem) +{ + int err = 0; + + if (qmem->mem_type == CamSys_Mmap_RegisterMem) { + if (camsys_dev->devmems.registermem == NULL) { + camsys_err("%s register memory isn't been register!", dev_name(camsys_dev->miscdev.this_device)); + err = -EINVAL; + goto end; + } + + qmem->mem_size = camsys_dev->devmems.registermem->size; + qmem->mem_offset = CamSys_Mmap_RegisterMem*PAGE_SIZE; + } else if (qmem->mem_type == CamSys_Mmap_I2cMem) { + if (camsys_dev->devmems.i2cmem== NULL) { + camsys_err("%s i2c memory isn't been register!", dev_name(camsys_dev->miscdev.this_device)); + err = -EINVAL; + goto end; + } + + qmem->mem_size = camsys_dev->devmems.i2cmem->size; + qmem->mem_offset = CamSys_Mmap_I2cMem*PAGE_SIZE; + } else { + camsys_err("%d memory type have not in %s memory list",qmem->mem_type,dev_name(camsys_dev->miscdev.this_device)); + err = -EINVAL; + goto end; + } + + + return 0; +end: + return err; +} +static int camsys_open(struct inode *inode, struct file *file) +{ + int err = 0; + int minor = iminor(inode); + camsys_dev_t *camsys_dev; + + spin_lock(&camsys_devs.lock); + list_for_each_entry(camsys_dev, &camsys_devs.devs, list) { + if (camsys_dev->miscdev.minor == minor) { + file->private_data = (void*)(camsys_dev); + break; + } + } + spin_unlock(&camsys_devs.lock); + + if (file->private_data == NULL) { + camsys_err("Cann't find camsys_dev!"); + err = -ENODEV; + goto end; + } else { + camsys_trace(1,"%s(%p) is opened!",dev_name(camsys_dev->miscdev.this_device),camsys_dev); + } + +end: + return err; +} + +static int camsys_release(struct inode *inode, struct file *file) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)file->private_data; + + camsys_irq_disconnect(NULL,camsys_dev, true); + + camsys_trace(1,"%s(%p) is closed",dev_name(camsys_dev->miscdev.this_device),camsys_dev); + + return 0; +} + +/* +* The ioctl() implementation +*/ + +static long camsys_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) +{ + long err = 0; + camsys_dev_t *camsys_dev = (camsys_dev_t*)filp->private_data; + + if (_IOC_TYPE(cmd) != CAMSYS_IOC_MAGIC) { + camsys_err("ioctl type(%c!=%c) is invalidate\n",_IOC_TYPE(cmd),CAMSYS_IOC_MAGIC); + err = -ENOTTY; + goto end; + } + if (_IOC_NR(cmd) > CAMSYS_IOC_MAXNR) { + camsys_err("ioctl index(%d>%d) is invalidate\n",_IOC_NR(cmd),CAMSYS_IOC_MAXNR); + err = -ENOTTY; + goto end; + } + + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + + if (err) { + camsys_err("ioctl(0x%x) operation not permitted for %s",cmd,dev_name(camsys_dev->miscdev.this_device)); + err = -EFAULT; + goto end; + } + + switch (cmd) { + + case CAMSYS_VERCHK: + { + camsys_version_t camsys_ver; + + camsys_ver.drv_ver = CAMSYS_DRIVER_VERSION; + camsys_ver.head_ver = CAMSYS_HEAD_VERSION; + if (copy_to_user((void __user *)arg,(void*)&camsys_ver, sizeof(camsys_version_t))) + return -EFAULT; + break; + } + + case CAMSYS_I2CRD: + { + camsys_i2c_info_t i2cinfo; + + if (copy_from_user((void*)&i2cinfo,(void __user *)arg, sizeof(camsys_i2c_info_t))) + return -EFAULT; + + err = camsys_i2c_read(&i2cinfo,camsys_dev); + if (err==0) { + if (copy_to_user((void __user *)arg,(void*)&i2cinfo, sizeof(camsys_i2c_info_t))) + return -EFAULT; + } + break; + } + + case CAMSYS_I2CWR: + { + camsys_i2c_info_t i2cinfo; + + if (copy_from_user((void*)&i2cinfo,(void __user *)arg, sizeof(camsys_i2c_info_t))) + return -EFAULT; + + err = camsys_i2c_write(&i2cinfo,camsys_dev); + break; + } + + case CAMSYS_SYSCTRL: + { + camsys_sysctrl_t devctl; + + if (copy_from_user((void*)&devctl,(void __user *)arg, sizeof(camsys_sysctrl_t))) + return -EFAULT; + + err = camsys_sysctl(&devctl, camsys_dev); + break; + } + + case CAMSYS_REGRD: + { + + break; + } + + case CAMSYS_REGWR: + { + + break; + } + + case CAMSYS_REGISTER_DEVIO: + { + camsys_devio_name_t devio; + + if (copy_from_user((void*)&devio,(void __user *)arg, sizeof(camsys_devio_name_t))) + return -EFAULT; + + err = camsys_extdev_register(&devio,camsys_dev); + break; + } + + case CAMSYS_DEREGISTER_DEVIO: + { + unsigned int dev_id; + + if (copy_from_user((void*)&dev_id,(void __user *)arg, sizeof(unsigned int))) + return -EFAULT; + + err = camsys_extdev_deregister(dev_id, camsys_dev, false); + break; + } + + case CAMSYS_IRQCONNECT: + { + camsys_irqcnnt_t irqcnnt; + + if (copy_from_user((void*)&irqcnnt,(void __user *)arg, sizeof(camsys_irqcnnt_t))) + return -EFAULT; + + err = camsys_irq_connect(&irqcnnt, camsys_dev); + + break; + } + + case CAMSYS_IRQWAIT: + { + camsys_irqsta_t irqsta; + + err = camsys_irq_wait(&irqsta, camsys_dev); + if (err==0) { + if (copy_to_user((void __user *)arg,(void*)&irqsta, sizeof(camsys_irqsta_t))) + return -EFAULT; + } + break; + } + + case CAMSYS_IRQDISCONNECT: + { + camsys_irqcnnt_t irqcnnt; + + if (copy_from_user((void*)&irqcnnt,(void __user *)arg, sizeof(camsys_irqcnnt_t))) + return -EFAULT; + printk("disconnect ++++++++++++++++\n"); + err = camsys_irq_disconnect(&irqcnnt,camsys_dev,false); + printk("disconnect ----------------\n"); + break; + } + + + case CAMSYS_QUREYMEM: + { + camsys_querymem_t qmem; + + if (copy_from_user((void*)&qmem,(void __user *)arg, sizeof(camsys_querymem_t))) + return -EFAULT; + + err = camsys_querymem(camsys_dev,&qmem); + if (err == 0) { + if (copy_to_user((void __user *)arg,(void*)&qmem, sizeof(camsys_querymem_t))) + return -EFAULT; + } + break; + } + + default : + break; + } + +end: + return err; + +} +/* + * VMA operations. + */ +static void camsys_vm_open(struct vm_area_struct *vma) +{ + camsys_meminfo_t *meminfo = (camsys_meminfo_t*)vma->vm_private_data; + + meminfo->vmas++; + return; +} + +static void camsys_vm_close(struct vm_area_struct *vma) +{ + camsys_meminfo_t *meminfo = (camsys_meminfo_t*)vma->vm_private_data; + + meminfo->vmas--; + return; +} + +static const struct vm_operations_struct camsys_vm_ops = { + .open = camsys_vm_open, + .close = camsys_vm_close, +}; + +int camsys_mmap(struct file *flip, struct vm_area_struct *vma) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)flip->private_data; + unsigned long addr, start, size; + camsys_mmap_type_t mem_type; + camsys_meminfo_t *meminfo; + int ret = 0; + + mem_type = vma->vm_pgoff; + + if (mem_type == CamSys_Mmap_RegisterMem) { + if (camsys_dev->devmems.registermem != NULL) { + meminfo = camsys_dev->devmems.registermem; + } else { + camsys_err("this camsys device has not register mem!"); + ret = -EINVAL; + goto done; + } + } else if (mem_type == CamSys_Mmap_I2cMem) { + if (camsys_dev->devmems.i2cmem != NULL) { + meminfo = camsys_dev->devmems.i2cmem; + } else { + camsys_err("this camsys device has not i2c mem!"); + ret = -EINVAL; + goto done; + } + } else { + camsys_err("mmap buffer type %d is invalidate!",mem_type); + ret = -EINVAL; + goto done; + } + + size = vma->vm_end - vma->vm_start; + if (size > meminfo->size) { + ret = -ENOMEM; + camsys_err("mmap size(0x%lx) > memory size(0x%x), so failed!",size,meminfo->size); + goto done; + } + + start = vma->vm_start; + addr = __phys_to_pfn(meminfo->phy_base); + + if (remap_pfn_range(vma, start, addr,size,pgprot_noncached(vma->vm_page_prot))) { + + ret = -EAGAIN; + goto done; + } + + vma->vm_ops = &camsys_vm_ops; + vma->vm_flags |= VM_IO; + vma->vm_flags |=VM_ACCOUNT;//same as VM_RESERVED; + vma->vm_private_data = (void*)meminfo; + camsys_vm_open(vma); + +done: + return ret; +} + +struct file_operations camsys_fops = { + .owner = THIS_MODULE, + .open = camsys_open, + .release = camsys_release, + .unlocked_ioctl = camsys_ioctl, + .mmap = camsys_mmap, +}; + +static int camsys_platform_probe_new(struct platform_device *pdev){ + int err = 0; + camsys_dev_t *camsys_dev; + struct resource register_res ; + struct device *dev = &pdev->dev; + unsigned long i2cmem; + camsys_meminfo_t *meminfo; + unsigned int irq_id; + + err = of_address_to_resource(dev->of_node, 0, ®ister_res); + if (err < 0){ + camsys_err("Get register resource from %s platform device failed!",pdev->name); + err = -ENODEV; + goto fail_end; + } + + + //map irqs + irq_id = irq_of_parse_and_map(dev->of_node, 0); + if (irq_id < 0) { + camsys_err("Get irq resource from %s platform device failed!",pdev->name); + err = -ENODEV; + goto fail_end; + } + + camsys_dev = (camsys_dev_t*)devm_kzalloc(&pdev->dev,sizeof(camsys_dev_t), GFP_KERNEL); + if (camsys_dev == NULL) { + camsys_err("Allocate camsys_dev for %s platform device failed",pdev->name); + err = -ENOMEM; + goto fail_end; + } + + //spin_lock_init(&camsys_dev->lock); + mutex_init(&camsys_dev->extdevs.mut); + INIT_LIST_HEAD(&camsys_dev->extdevs.list); + INIT_LIST_HEAD(&camsys_dev->extdevs.active); + INIT_LIST_HEAD(&camsys_dev->list); + + + //IRQ init + camsys_dev->irq.irq_id = irq_id; + spin_lock_init(&camsys_dev->irq.lock); + INIT_LIST_HEAD(&camsys_dev->irq.irq_pool); + //init_waitqueue_head(&camsys_dev->irq.irq_done); + + INIT_LIST_HEAD(&camsys_dev->devmems.memslist); + + + //Register mem init + meminfo = kzalloc(sizeof(camsys_meminfo_t),GFP_KERNEL); + if (meminfo == NULL) { + err = -ENOMEM; + goto request_mem_fail; + } + + meminfo->vir_base = (unsigned int)devm_ioremap_resource(dev, ®ister_res); + if (!meminfo->vir_base){ + camsys_err("%s ioremap %s failed",dev_name(&pdev->dev), CAMSYS_REGISTER_MEM_NAME); + err = -ENXIO; + goto request_mem_fail; + } + + strlcpy(meminfo->name, CAMSYS_REGISTER_MEM_NAME,sizeof(meminfo->name)); + meminfo->phy_base = register_res.start; + meminfo->size = register_res.end - register_res.start + 1; + list_add_tail(&meminfo->list, &camsys_dev->devmems.memslist); + + + //I2c mem init + i2cmem = __get_free_page(GFP_KERNEL); + if (i2cmem == 0) { + camsys_err("Allocate i2cmem failed!"); + err = -ENOMEM; + goto request_mem_fail; + } + SetPageReserved(virt_to_page(i2cmem)); + + meminfo = kzalloc(sizeof(camsys_meminfo_t),GFP_KERNEL); + if (meminfo == NULL) { + err = -ENOMEM; + goto request_mem_fail; + } + strlcpy(meminfo->name,CAMSYS_I2C_MEM_NAME,sizeof(meminfo->name)); + meminfo->vir_base = i2cmem; + meminfo->phy_base = virt_to_phys((void*)i2cmem); + meminfo->size = PAGE_SIZE; + list_add_tail(&meminfo->list, &camsys_dev->devmems.memslist); + + { + unsigned int *tmpp; + + tmpp = (unsigned int*)meminfo->vir_base; + *tmpp = 0xfa561243; + } + + //Special init + + { + if (camsys_mipiphy_probe_cb(pdev, camsys_dev) <0) { + camsys_err("Mipi phy probe failed!"); + } + } + + if (strcmp(dev_name(&pdev->dev),CAMSYS_PLATFORM_MARVIN_NAME) == 0) { + #if (defined(CONFIG_CAMSYS_MRV)) + camsys_mrv_probe_cb(pdev, camsys_dev); + #else + camsys_err("Marvin controller camsys driver haven't been complie!!!"); + #endif + } else { + #if (defined(CONFIG_CAMSYS_CIF)) + camsys_cif_probe_cb(pdev,camsys_dev); + #else + camsys_err("CIF controller camsys driver haven't been complie!!!"); + #endif + } + + camsys_trace(1, "%s memory:",dev_name(&pdev->dev)); + list_for_each_entry(meminfo, &camsys_dev->devmems.memslist, list) { + if (strcmp(meminfo->name,CAMSYS_I2C_MEM_NAME) == 0) { + camsys_dev->devmems.i2cmem = meminfo; + camsys_trace(1," I2c memory (phy: 0x%x vir: 0x%x size: 0x%x)", + meminfo->phy_base,meminfo->vir_base,meminfo->size); + } + if (strcmp(meminfo->name,CAMSYS_REGISTER_MEM_NAME) == 0) { + camsys_dev->devmems.registermem = meminfo; + camsys_trace(1," Register memory (phy: 0x%x vir: 0x%x size: 0x%x)", + meminfo->phy_base,meminfo->vir_base,meminfo->size); + } + } + + camsys_dev->phy_cb = camsys_phy_ops; + platform_set_drvdata(pdev,(void*)camsys_dev); + //Camsys_devs list add + spin_lock(&camsys_devs.lock); + list_add_tail(&camsys_dev->list, &camsys_devs.devs); + spin_unlock(&camsys_devs.lock); + + + camsys_trace(1, "Probe %s device success ", dev_name(&pdev->dev)); + return 0; +request_mem_fail: + if (camsys_dev != NULL) { + + while(!list_empty(&camsys_dev->devmems.memslist)) { + meminfo = list_first_entry(&camsys_dev->devmems.memslist, camsys_meminfo_t, list); + if (meminfo) { + list_del_init(&meminfo->list); + if (strcmp(meminfo->name,CAMSYS_REGISTER_MEM_NAME)==0) { + iounmap((void __iomem *)meminfo->vir_base); + release_mem_region(meminfo->phy_base,meminfo->size); + } else if (strcmp(meminfo->name,CAMSYS_I2C_MEM_NAME)==0) { + kfree((void*)meminfo->vir_base); + } + kfree(meminfo); + meminfo = NULL; + } + } + + kfree(camsys_dev); + camsys_dev = NULL; + } +fail_end: + return -1; + + +} +#if 0 +static int camsys_platform_probe(struct platform_device *pdev) +{ + int err = 0; + unsigned int irq_id; + camsys_dev_t *camsys_dev; + unsigned long i2cmem; + camsys_meminfo_t *meminfo; + struct resource *register_res, *mipiphy_register_res; + + camsys_trace(1, "Probe %s device now", dev_name(&pdev->dev)); + register_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, CAMSYS_REGISTER_RES_NAME); + if (register_res == NULL) { + camsys_err("Get register resource from %s platform device failed!",pdev->name); + err = -ENODEV; + goto fail_end; + } + + mipiphy_register_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, CAMSYS_REGISTER_MIPIPHY_RES_NAME); + + irq_id = platform_get_irq_byname(pdev, CAMSYS_IRQ_RES_NAME); + if (irq_id < 0) { + camsys_err("Get irq resource from %s platform device failed!",pdev->name); + err = -ENODEV; + goto fail_end; + } + + camsys_dev = (camsys_dev_t*)kzalloc(sizeof(camsys_dev_t), GFP_KERNEL); + if (camsys_dev == NULL) { + camsys_err("Allocate camsys_dev for %s platform device failed",pdev->name); + err = -ENOMEM; + goto fail_end; + } + + //spin_lock_init(&camsys_dev->lock); + mutex_init(&camsys_dev->extdevs.mut); + INIT_LIST_HEAD(&camsys_dev->extdevs.list); + INIT_LIST_HEAD(&camsys_dev->extdevs.active); + INIT_LIST_HEAD(&camsys_dev->list); + + + //IRQ init + camsys_dev->irq.irq_id = irq_id; + spin_lock_init(&camsys_dev->irq.lock); + INIT_LIST_HEAD(&camsys_dev->irq.irq_pool); + //init_waitqueue_head(&camsys_dev->irq.irq_done); + + INIT_LIST_HEAD(&camsys_dev->devmems.memslist); + if (!request_mem_region(register_res->start, register_res->end - register_res->start + 1, + dev_name(&pdev->dev))) { + err = -ENOMEM; + goto request_mem_fail; + } + + //Register mem init + meminfo = kzalloc(sizeof(camsys_meminfo_t),GFP_KERNEL); + if (meminfo == NULL) { + err = -ENOMEM; + goto request_mem_fail; + } + + meminfo->vir_base = (unsigned int)ioremap(register_res->start, register_res->end - register_res->start + 1); + if (meminfo->vir_base == 0) { + camsys_err("%s ioremap %s failed",dev_name(&pdev->dev), CAMSYS_REGISTER_MEM_NAME); + err = -ENXIO; + goto request_mem_fail; + } + + strlcpy(meminfo->name, CAMSYS_REGISTER_MEM_NAME,sizeof(meminfo->name)); + meminfo->phy_base = register_res->start; + meminfo->size = register_res->end - register_res->start + 1; + list_add_tail(&meminfo->list, &camsys_dev->devmems.memslist); + + //I2c mem init + i2cmem = __get_free_page(GFP_KERNEL); + if (i2cmem == 0) { + camsys_err("Allocate i2cmem failed!"); + err = -ENOMEM; + goto request_mem_fail; + } + SetPageReserved(virt_to_page(i2cmem)); + + meminfo = kzalloc(sizeof(camsys_meminfo_t),GFP_KERNEL); + if (meminfo == NULL) { + err = -ENOMEM; + goto request_mem_fail; + } + strlcpy(meminfo->name,CAMSYS_I2C_MEM_NAME,sizeof(meminfo->name)); + meminfo->vir_base = i2cmem; + meminfo->phy_base = virt_to_phys((void*)i2cmem); + meminfo->size = PAGE_SIZE; + list_add_tail(&meminfo->list, &camsys_dev->devmems.memslist); + + { + unsigned int *tmpp; + + tmpp = (unsigned int*)meminfo->vir_base; + *tmpp = 0xfa561243; + } + + //Special init + + if (mipiphy_register_res) { + if (camsys_mipiphy_probe_cb(pdev, camsys_dev) <0) { + camsys_err("Mipi phy probe failed!"); + } + } + + if (strcmp(dev_name(&pdev->dev),CAMSYS_PLATFORM_MARVIN_NAME) == 0) { + #if (defined(CONFIG_CAMSYS_MRV)) + camsys_mrv_probe_cb(pdev, camsys_dev); + #else + camsys_err("Marvin controller camsys driver haven't been complie!!!"); + #endif + } else { + #if (defined(CONFIG_CAMSYS_CIF)) + camsys_cif_probe_cb(pdev,camsys_dev); + #else + camsys_err("CIF controller camsys driver haven't been complie!!!"); + #endif + } + + camsys_trace(1, "%s memory:",dev_name(&pdev->dev)); + list_for_each_entry(meminfo, &camsys_dev->devmems.memslist, list) { + if (strcmp(meminfo->name,CAMSYS_I2C_MEM_NAME) == 0) { + camsys_dev->devmems.i2cmem = meminfo; + camsys_trace(1," I2c memory (phy: 0x%x vir: 0x%x size: 0x%x)", + meminfo->phy_base,meminfo->vir_base,meminfo->size); + } + if (strcmp(meminfo->name,CAMSYS_REGISTER_MEM_NAME) == 0) { + camsys_dev->devmems.registermem = meminfo; + camsys_trace(1," Register memory (phy: 0x%x vir: 0x%x size: 0x%x)", + meminfo->phy_base,meminfo->vir_base,meminfo->size); + } + } + + camsys_dev->phy_cb = camsys_phy_ops; + camsys_dev->pdev = pdev; + platform_set_drvdata(pdev,(void*)camsys_dev); + + //Camsys_devs list add + spin_lock(&camsys_devs.lock); + list_add_tail(&camsys_dev->list, &camsys_devs.devs); + spin_unlock(&camsys_devs.lock); + + + camsys_trace(1, "Probe %s device success ", dev_name(&pdev->dev)); + return 0; +request_mem_fail: + if (camsys_dev != NULL) { + + while(!list_empty(&camsys_dev->devmems.memslist)) { + meminfo = list_first_entry(&camsys_dev->devmems.memslist, camsys_meminfo_t, list); + if (meminfo) { + list_del_init(&meminfo->list); + if (strcmp(meminfo->name,CAMSYS_REGISTER_MEM_NAME)==0) { + iounmap((void __iomem *)meminfo->vir_base); + release_mem_region(meminfo->phy_base,meminfo->size); + } else if (strcmp(meminfo->name,CAMSYS_I2C_MEM_NAME)==0) { + kfree((void*)meminfo->vir_base); + } + kfree(meminfo); + meminfo = NULL; + } + } + + kfree(camsys_dev); + camsys_dev = NULL; + } +fail_end: + return -1; +} +#endif +static int camsys_platform_remove(struct platform_device *pdev) +{ + camsys_dev_t *camsys_dev = platform_get_drvdata(pdev); + camsys_meminfo_t *meminfo; + + if (camsys_dev) { + + //Mem deinit + while(!list_empty(&camsys_dev->devmems.memslist)) { + meminfo = list_first_entry(&camsys_dev->devmems.memslist, camsys_meminfo_t, list); + if (meminfo) { + list_del_init(&meminfo->list); + if (strcmp(meminfo->name,CAMSYS_REGISTER_MEM_NAME)==0) { + iounmap((void __iomem *)meminfo->vir_base); + release_mem_region(meminfo->phy_base,meminfo->size); + } else if (strcmp(meminfo->name,CAMSYS_I2C_MEM_NAME)==0) { + kfree((void*)meminfo->vir_base); + } + kfree(meminfo); + meminfo = NULL; + } + } + + //Irq deinit + if (camsys_dev->irq.irq_id) { + free_irq(camsys_dev->irq.irq_id, camsys_dev); + camsys_irq_disconnect(NULL,camsys_dev,true); + } + + //Extdev deinit + if (!list_empty(&camsys_dev->extdevs.list)) { + camsys_extdev_deregister(0,camsys_dev,true); + } + + if (camsys_dev->mipiphy.remove) + camsys_dev->mipiphy.remove(pdev); + if (camsys_dev->cifphy.remove) + camsys_dev->cifphy.remove(pdev); + camsys_dev->platform_remove(pdev); + + misc_deregister(&camsys_dev->miscdev); + + spin_lock(&camsys_devs.lock); + list_del_init(&camsys_dev->list); + spin_unlock(&camsys_devs.lock); + + kfree(camsys_dev); + camsys_dev=NULL; + } else { + camsys_err("This platform device havn't obtain camsys_dev!"); + } + + return 0; +} + + +static const struct of_device_id cif_of_match[] = { + { .compatible = "rodkchip,isp" }, +}; +MODULE_DEVICE_TABLE(of, cif_of_match); + +static struct platform_driver camsys_platform_driver = +{ + .driver = { + .name = CAMSYS_PLATFORM_DRV_NAME, + .of_match_table = cif_of_match, + }, + .probe = camsys_platform_probe_new, + .remove = (camsys_platform_remove), +}; + + +static int __init camsys_platform_init(void) +{ + printk("CamSys driver version: v%d.%d.%d, CamSys head file version: v%d.%d.%d\n", + (CAMSYS_DRIVER_VERSION&0xff0000)>>16, (CAMSYS_DRIVER_VERSION&0xff00)>>8, + CAMSYS_DRIVER_VERSION&0xff, + (CAMSYS_HEAD_VERSION&0xff0000)>>16, (CAMSYS_HEAD_VERSION&0xff00)>>8, + CAMSYS_HEAD_VERSION&0xff); + spin_lock_init(&camsys_devs.lock); + INIT_LIST_HEAD(&camsys_devs.devs); + platform_driver_register(&camsys_platform_driver); + + + return 0; +} + +static void __exit camsys_platform_exit(void) +{ + platform_driver_unregister(&camsys_platform_driver); +} + +module_init(camsys_platform_init); +module_exit(camsys_platform_exit); + +MODULE_DESCRIPTION("RockChip Camera System"); +MODULE_AUTHOR(""); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/video/rk_camsys/camsys_gpio.h b/drivers/media/video/rk_camsys/camsys_gpio.h new file mode 100755 index 000000000000..6d6e81e1fe4e --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_gpio.h @@ -0,0 +1,90 @@ +#ifndef __RKCAMSYS_GPIO_H__ +#define __RKCAMSYS_GPIO_H__ + +//#include +#include +#if defined(CONFIG_ARCH_ROCKCHIP) +#define RK30_PIN0_PA0 (0) +#define NUM_GROUP (32) +#define GPIO_BANKS (9) +#endif + +static inline unsigned int camsys_gpio_group_pin(unsigned char *io_name) +{ + unsigned char *pin_char; + unsigned char pin; + + if (strstr(io_name, "PA")) { + pin_char = strstr(io_name, "PA"); + pin_char += 2; + pin = *pin_char - 0x30; + } else if (strstr(io_name, "PB")) { + pin_char = strstr(io_name, "PB"); + pin_char += 2; + pin = *pin_char - 0x30; + pin += 8; + } else if (strstr(io_name, "PC")) { + pin_char = strstr(io_name, "PC"); + pin_char += 2; + pin = *pin_char - 0x30; + pin += 16; + } else if (strstr(io_name, "PD")) { + pin_char = strstr(io_name, "PD"); + pin_char += 2; + pin = *pin_char - 0x30; + pin += 24; + } + + return pin; +} + +static inline unsigned int camsys_gpio_group(unsigned char *io_name) +{ + unsigned int group; + + if (strstr(io_name,"PIN0")) { + group = 0; + } else if (strstr(io_name,"PIN1")) { + group = 1; + } else if (strstr(io_name,"PIN2")) { + group = 2; + } else if (strstr(io_name,"PIN3")) { + group = 3; + } else if (strstr(io_name,"PIN4")) { + group = 4; + } else if (strstr(io_name,"PIN5")) { + group = 5; + } else if (strstr(io_name,"PIN6")) { + group = 6; + } + + return group; +} + +static inline unsigned int camsys_gpio_get(unsigned char *io_name) +{ + unsigned int gpio; + unsigned int group; + unsigned int group_pin; + +#if (defined(CONFIG_ARCH_RK3066B) || defined(CONFIG_ARCH_RK3188) || defined(CONFIG_ARCH_RK319X) ||defined(CONFIG_ARCH_ROCKCHIP)) + if (strstr(io_name, "RK30_")) { + gpio = RK30_PIN0_PA0; + group = camsys_gpio_group(io_name); + group_pin = camsys_gpio_group_pin(io_name); + + if (group >= GPIO_BANKS) { + gpio = 0xffffffff; + } else { + gpio += group*NUM_GROUP + group_pin; + } + } + +#endif + + + return gpio; +} + +#endif + diff --git a/drivers/media/video/rk_camsys/camsys_internal.h b/drivers/media/video/rk_camsys/camsys_internal.h new file mode 100755 index 000000000000..e027dca69efb --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_internal.h @@ -0,0 +1,313 @@ +#ifndef __RKCAMSYS_INTERNAL_H__ +#define __RKCAMSYS_INTERNAL_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +//#include +//#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +/* +* C A M S Y S D R I V E R V E R S I O N +* +*v0.0.1: +* 1) test version; +*v0.0.2: +* 1) add mipi csi phy; +*v0.0.3: +* 1) add support cif phy for marvin; +*v0.0.4: +* 1) add clock information in struct camsys_devio_name_s; +*v0.0.5: +* 1) set isp clock at 32MHz; +*v0.0.6: +* 1) iomux d0 d1 for cif phy raw10 in rk319x after i2c operated; +* 2) check mis value in camsys_irq_connect; +* 3) add soft rest callback; +*v0.0.7: +* 1) check extdev is activate or not before delete from camsys_dev active list; +*/ +#define CAMSYS_DRIVER_VERSION KERNEL_VERSION(0,0,6) + + +#define CAMSYS_PLATFORM_DRV_NAME "RockChip-CamSys" +#define CAMSYS_PLATFORM_MARVIN_NAME "Platform_MarvinDev" +#define CAMSYS_PLATFORM_CIF0_NAME "Platform_Cif0Dev" +#define CAMSYS_PLATFORM_CIF1_NAME "Platform_Cif1Dev" + +#define CAMSYS_REGISTER_RES_NAME "CamSys_RegMem" +#define CAMSYS_REGISTER_MIPIPHY_RES_NAME "CamSys_RegMem_MipiPhy" +#define CAMSYS_IRQ_RES_NAME "CamSys_Irq" + +#define CAMSYS_REGISTER_MEM_NAME CAMSYS_REGISTER_RES_NAME +#define CAMSYS_I2C_MEM_NAME "CamSys_I2cMem" + +#define CAMSYS_NAMELEN_MIN(a) ((strlen(a)>(CAMSYS_NAME_LEN-1))?(CAMSYS_NAME_LEN-1):strlen(a)) +#define CAMSYS_IRQPOOL_NUM 128 + +extern unsigned int camsys_debug; + +#define camsys_trace(level, msg,...) \ + do { \ + if (1/*camsys_debug >= level*/) \ + printk("D%d:%s(%d): " msg "\n",level, __FUNCTION__,__LINE__, ## __VA_ARGS__); \ + } while (0) + +#define camsys_warn(msg,...) printk(KERN_ERR "W:%s(%d): " msg "\n", __FUNCTION__,__LINE__, ## __VA_ARGS__) +#define camsys_err(msg,...) printk(KERN_ERR "E:%s(%d): " msg "\n", __FUNCTION__,__LINE__, ## __VA_ARGS__) + + +typedef struct camsys_irqstas_s { + camsys_irqsta_t sta; + struct list_head list; +} camsys_irqstas_t; + +typedef struct camsys_irqpool_s { + pid_t pid; + unsigned int timeout; //us + unsigned int mis; + unsigned int icr; + + + spinlock_t lock; // lock for list + camsys_irqstas_t pool[CAMSYS_IRQPOOL_NUM]; + struct list_head active; + struct list_head deactive; + + struct list_head list; + + wait_queue_head_t done; +} camsys_irqpool_t; + +typedef struct camsys_irq_s { + unsigned int irq_id; + + spinlock_t lock; //lock for timeout and irq_connect in ioctl + //unsigned int timeout; + + //wait_queue_head_t irq_done; + + struct list_head irq_pool; +} camsys_irq_t; + +typedef struct camsys_meminfo_s { + unsigned char name[32]; + unsigned int phy_base; + unsigned int vir_base; + unsigned int size; + unsigned int vmas; + + struct list_head list; + +} camsys_meminfo_t; + +typedef struct camsys_devmems_s { + camsys_meminfo_t *registermem; + camsys_meminfo_t *i2cmem; + struct list_head memslist; +} camsys_devmems_t; + +typedef struct camsys_regulator_s { + struct regulator *ldo; + int min_uv; + int max_uv; +} camsys_regulator_t; + +typedef struct camsys_gpio_s { + unsigned int io; + unsigned int active; +} camsys_gpio_t; +typedef struct camsys_flash_s { + camsys_gpio_t fl; +} camsys_flash_t; +typedef struct camsys_extdev_s { + unsigned int dev_id; + camsys_regulator_t avdd; + camsys_regulator_t dovdd; + camsys_regulator_t dvdd; + camsys_regulator_t afvdd; + + camsys_gpio_t pwrdn; + camsys_gpio_t rst; + camsys_gpio_t afpwr; + camsys_gpio_t afpwrdn; + + camsys_flash_t fl; + + camsys_extdev_phy_t phy; + camsys_extdev_clk_t clk; + + unsigned int dev_cfg; + + struct platform_device *pdev; + + struct list_head list; + struct list_head active; +} camsys_extdev_t; + +typedef struct camsys_phyinfo_s { + void *clk; + camsys_meminfo_t *reg; + + int (*clkin_cb)(void *ptr, unsigned int on); + int (*ops) (void *phy, void *phyinfo, unsigned int on); + int (*remove)(struct platform_device *pdev); +} camsys_phyinfo_t; + +typedef struct camsys_exdevs_s { + struct mutex mut; + struct list_head list; + struct list_head active; +} camsys_exdevs_t; + +typedef struct camsys_dev_s { + unsigned int dev_id; + + camsys_irq_t irq; + camsys_devmems_t devmems; + struct miscdevice miscdev; + void *clk; + + camsys_phyinfo_t mipiphy; + camsys_phyinfo_t cifphy; + + camsys_exdevs_t extdevs; + struct list_head list; + struct platform_device *pdev; + + int (*clkin_cb)(void *ptr, unsigned int on); + int (*clkout_cb)(void *ptr,unsigned int on); + int (*reset_cb)(void *ptr); + int (*phy_cb) (camsys_extdev_phy_t *phy,void* ptr, unsigned int on); + int (*iomux)(camsys_extdev_t *extdev,void *ptr); + int (*platform_remove)(struct platform_device *pdev); +} camsys_dev_t; + + +static inline camsys_extdev_t* camsys_find_extdev(unsigned int dev_id, camsys_dev_t *camsys_dev) +{ + camsys_extdev_t *extdev; + + if (!list_empty(&camsys_dev->extdevs.list)) { + list_for_each_entry(extdev, &camsys_dev->extdevs.list, list) { + if (extdev->dev_id == dev_id) { + return extdev; + } + } + } + return NULL; +} + +static inline camsys_meminfo_t* camsys_find_devmem(char *name, camsys_dev_t *camsys_dev) +{ + camsys_meminfo_t *devmem; + + if (!list_empty(&camsys_dev->devmems.memslist)) { + list_for_each_entry(devmem, &camsys_dev->devmems.memslist, list) { + if (strcmp(devmem->name, name) == 0) { + return devmem; + } + } + } + camsys_err("%s memory have not been find in %s!",name,dev_name(camsys_dev->miscdev.this_device)); + return NULL; +} + + +static inline int camsys_sysctl_extdev(camsys_extdev_t *extdev, camsys_sysctrl_t *devctl, camsys_dev_t *camsys_dev) +{ + int err = 0; + camsys_regulator_t *regulator; + camsys_gpio_t *gpio; + + if (devctl->ops < CamSys_Vdd_Tag) { + regulator = &extdev->avdd; + regulator += devctl->ops; + + //printk("regulator: %p regulator->ldo: %p\n",regulator,regulator->ldo); + if (!IS_ERR_OR_NULL(regulator->ldo)) { + if (devctl->on) { + regulator_set_voltage(regulator->ldo,regulator->min_uv,regulator->max_uv); + regulator_enable(regulator->ldo); + camsys_trace(1,"Sysctl %d success, regulator set (%d,%d) uv!",devctl->ops, regulator->min_uv,regulator->max_uv); + } else { + while(regulator_is_enabled(regulator->ldo)>0) + regulator_disable(regulator->ldo); + camsys_trace(1,"Sysctl %d success, regulator off!",devctl->ops); + } + } else { + camsys_err("Sysctl %d failed, because regulator ldo is NULL!",devctl->ops); + err = -EINVAL; + goto end; + } + } else if (devctl->ops < CamSys_Gpio_Tag) { + gpio = &extdev->pwrdn; + gpio += devctl->ops - CamSys_Vdd_Tag -1; + + if (gpio->io != 0xffffffff) { + if (devctl->on) { + gpio_direction_output(gpio->io, gpio->active); + gpio_set_value(gpio->io, gpio->active); + camsys_trace(1,"Sysctl %d success, gpio(%d) set %d",devctl->ops, gpio->io, gpio->active); + } else { + gpio_direction_output(gpio->io, !gpio->active); + gpio_set_value(gpio->io, !gpio->active); + camsys_trace(1,"Sysctl %d success, gpio(%d) set %d",devctl->ops, gpio->io, !gpio->active); + } + } else { + camsys_err("Sysctl %d failed, because gpio is NULL!",devctl->ops); + err = -EINVAL; + goto end; + } + } else if (devctl->ops == CamSys_ClkIn) { + if (camsys_dev->clkout_cb) + camsys_dev->clkout_cb(camsys_dev, extdev->clk.in_rate); + if (camsys_dev->phy_cb) + camsys_dev->phy_cb(&extdev->phy, camsys_dev, devctl->on); + } + +end: + return err; +} + +extern struct file_operations camsys_fops; +#endif diff --git a/drivers/media/video/rk_camsys/camsys_marvin.c b/drivers/media/video/rk_camsys/camsys_marvin.c new file mode 100755 index 000000000000..295355793416 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_marvin.c @@ -0,0 +1,391 @@ +#include "camsys_marvin.h" + +static const char miscdev_name[] = CAMSYS_MARVIN_DEVNAME; + + +static int camsys_mrv_iomux_cb(camsys_extdev_t *extdev,void *ptr) +{ + unsigned int cif_vol_sel; +#if 0 + if (extdev->dev_cfg & CAMSYS_DEVCFG_FLASHLIGHT) { + iomux_set(ISP_FLASH_TRIG); + if (extdev->fl.fl.io != 0xffffffff) { + iomux_set(ISP_FL_TRIG); + } + } + + if (extdev->dev_cfg & CAMSYS_DEVCFG_PREFLASHLIGHT) { + iomux_set(ISP_PRELIGHT_TRIG); + } + + if (extdev->dev_cfg & CAMSYS_DEVCFG_SHUTTER) { + iomux_set(ISP_SHUTTER_OPEN); + iomux_set(ISP_SHUTTER_TRIG); + } + + iomux_set(CIF0_CLKOUT); +#endif + + struct pinctrl *pinctrl; + struct pinctrl_state *state; + int retval = 0; + char state_str[20] = {0}; + + struct device *dev = &(extdev->pdev->dev); + + if (extdev->phy.type == CamSys_Phy_Cif) { + if ((extdev->phy.info.cif.fmt >= CamSys_Fmt_Raw_8b)&& (extdev->phy.info.cif.fmt <= CamSys_Fmt_Raw_12b)) { + + strcpy(state_str,"isp_dvp8bit"); + + } + + if ((extdev->phy.info.cif.fmt >= CamSys_Fmt_Raw_10b)&& (extdev->phy.info.cif.fmt <= CamSys_Fmt_Raw_12b)) { + strcpy(state_str,"isp_dvp10bit"); + } + + if (extdev->phy.info.cif.fmt == CamSys_Fmt_Raw_12b) { + strcpy(state_str,"isp_dvp12bit"); + + } + }else{ + strcpy(state_str,"default"); + } + + //mux CIF0_CLKOUT + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(pinctrl)) { + camsys_err("%s:Get pinctrl failed!\n",__func__); + return -1; + } + state = pinctrl_lookup_state(pinctrl, + state_str); + if (IS_ERR(state)){ + dev_err(dev, "%s:could not get %s pinstate\n",__func__,state_str); + return -1; + } + + if (!IS_ERR(state)) { + retval = pinctrl_select_state(pinctrl, state); + if (retval){ + dev_err(dev, + "%s:could not set %s pins\n",__func__,state_str); + return -1; + + } + } + + //set cif vol domain + if (extdev->phy.type == CamSys_Phy_Cif) { + + #if 0 + if (!IS_ERR_OR_NULL(extdev->dovdd.ldo)) { + if (extdev->dovdd.max_uv >= 25000000) { + __raw_writel(((1<<1)|(1<<(1+16))),RK30_GRF_BASE+0x018c); + } else { + __raw_writel((1<<(1+16)),RK30_GRF_BASE+0x018c); + } + } else { + __raw_writel(((1<<1)|(1<<(1+16))),RK30_GRF_BASE+0x018c); + } + #else + __raw_writel(((1<<1)|(1<<(1+16))),RK_GRF_VIRT+0x018c); + #endif + + //set driver strength + __raw_writel(0xffffffff, RK_GRF_VIRT+0x01dc); + } + + return 0; +} + +static int camsys_mrv_reset_cb(void *ptr) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + #if 0 //do nothing ,zyc + cru_set_soft_reset(SOFT_RST_ISP,true); + udelay(100); + cru_set_soft_reset(SOFT_RST_ISP,false); + #endif + camsys_trace(1, "%s soft reset\n",dev_name(camsys_dev->miscdev.this_device)); + return 0; +} + +static int camsys_mrv_clkin_cb(void *ptr, unsigned int on) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + camsys_mrv_clk_t *clk = (camsys_mrv_clk_t*)camsys_dev->clk; + + spin_lock(&clk->lock); + if (on && !clk->in_on) { + clk_enable(clk->pd_isp); + clk_enable(clk->aclk_isp); + clk_enable(clk->hclk_isp); + clk_enable(clk->isp); + clk_enable(clk->isp_jpe); + clk_enable(clk->pclkin_isp); + + clk->in_on = true; + + camsys_trace(1, "%s clock in turn on",dev_name(camsys_dev->miscdev.this_device)); + camsys_mrv_reset_cb(ptr); + + } else if (!on && clk->in_on) { + clk_disable(clk->pd_isp); + clk_disable(clk->aclk_isp); + clk_disable(clk->hclk_isp); + clk_disable(clk->isp); + clk_disable(clk->isp_jpe); + clk_disable(clk->pclkin_isp); + clk->in_on = false; + camsys_trace(1, "%s clock in turn off",dev_name(camsys_dev->miscdev.this_device)); + } + spin_unlock(&clk->lock); + return 0; +} + +static int camsys_mrv_clkout_cb(void *ptr, unsigned int on) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)ptr; + camsys_mrv_clk_t *clk = (camsys_mrv_clk_t*)camsys_dev->clk; + struct clk *cif_clk_out_div; + + spin_lock(&clk->lock); + if (on && (clk->out_on != on)) { + clk_enable(clk->cif_clk_out); + clk_set_rate(clk->cif_clk_out,on); + + clk->out_on = on; + camsys_trace(1, "%s clock out(rate: %dHz) turn on",dev_name(camsys_dev->miscdev.this_device), + clk->out_on); + } else if (!on && clk->out_on) { + cif_clk_out_div = clk_get(NULL, "cif0_out_div"); + if(IS_ERR_OR_NULL(cif_clk_out_div)) { + cif_clk_out_div = clk_get(NULL, "cif_out_div"); + } + + if(!IS_ERR_OR_NULL(cif_clk_out_div)) { + clk_set_parent(clk->cif_clk_out, cif_clk_out_div); + clk_put(cif_clk_out_div); + } else { + camsys_warn("%s clock out may be not off!", dev_name(camsys_dev->miscdev.this_device)); + } + clk_disable(clk->cif_clk_out); + clk->out_on = 0; + + camsys_trace(1, "%s clock out turn off",dev_name(camsys_dev->miscdev.this_device)); + } + spin_unlock(&clk->lock); + + return 0; +} +static irqreturn_t camsys_mrv_irq(int irq, void *data) +{ + camsys_dev_t *camsys_dev = (camsys_dev_t*)data; + camsys_irqstas_t *irqsta; + camsys_irqpool_t *irqpool; + unsigned int isp_mis,mipi_mis,mi_mis,*mis; + + isp_mis = __raw_readl((void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_ISP_MIS)); + mipi_mis = __raw_readl((void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_MIPI_MIS)); + mi_mis = __raw_readl((void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_MI_MIS)); + + __raw_writel(isp_mis, (void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_ISP_ICR)); + __raw_writel(mipi_mis, (void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_MIPI_ICR)); + __raw_writel(mi_mis, (void volatile *)(camsys_dev->devmems.registermem->vir_base + MRV_MI_ICR)); + + spin_lock(&camsys_dev->irq.lock); + if (!list_empty(&camsys_dev->irq.irq_pool)) { + list_for_each_entry(irqpool, &camsys_dev->irq.irq_pool, list) { + if (irqpool->pid != 0) { + switch(irqpool->mis) + { + case MRV_ISP_MIS: + { + mis = &isp_mis; + break; + } + + case MRV_MIPI_MIS: + { + mis = &mipi_mis; + break; + } + case MRV_MI_MIS: + { + mis = &mi_mis; + break; + } + + default: + { + camsys_trace(2,"Thread(pid:%d) irqpool mis(%d) is invalidate",irqpool->pid,irqpool->mis); + goto end; + } + } + + if (*mis != 0) { + spin_lock(&irqpool->lock); + if (!list_empty(&irqpool->deactive)) { + irqsta = list_first_entry(&irqpool->deactive, camsys_irqstas_t, list); + irqsta->sta.mis = *mis; + list_del_init(&irqsta->list); + list_add_tail(&irqsta->list,&irqpool->active); + wake_up(&irqpool->done); + } + spin_unlock(&irqpool->lock); + } + } + } + } +end: + spin_unlock(&camsys_dev->irq.lock); + + return IRQ_HANDLED; +} +static int camsys_mrv_remove_cb(struct platform_device *pdev) +{ + camsys_dev_t *camsys_dev = platform_get_drvdata(pdev); + camsys_mrv_clk_t *mrv_clk=NULL; + + if (camsys_dev->clk != NULL) { + + mrv_clk = (camsys_mrv_clk_t*)camsys_dev->clk; + if (mrv_clk->out_on) + camsys_mrv_clkout_cb(mrv_clk,0); + if (mrv_clk->in_on) + camsys_mrv_clkin_cb(mrv_clk,0); + + if (!IS_ERR_OR_NULL(mrv_clk->pd_isp)) { + clk_put(mrv_clk->pd_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->aclk_isp)) { + clk_put(mrv_clk->aclk_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->hclk_isp)) { + clk_put(mrv_clk->hclk_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->isp)) { + clk_put(mrv_clk->isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->isp_jpe)) { + clk_put(mrv_clk->isp_jpe); + } + if (!IS_ERR_OR_NULL(mrv_clk->pclkin_isp)) { + clk_put(mrv_clk->pclkin_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->cif_clk_out)) { + clk_put(mrv_clk->cif_clk_out); + } + + kfree(mrv_clk); + mrv_clk = NULL; + } + + return 0; +} +int camsys_mrv_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev) +{ + int err = 0; + camsys_mrv_clk_t *mrv_clk=NULL; + //struct clk *clk_parent; + + err = request_irq(camsys_dev->irq.irq_id, camsys_mrv_irq, 0, CAMSYS_MARVIN_IRQNAME,camsys_dev); + if (err) { + camsys_err("request irq for %s failed",CAMSYS_MARVIN_IRQNAME); + goto end; + } + + //Clk and Iomux init + mrv_clk = kzalloc(sizeof(camsys_mrv_clk_t),GFP_KERNEL); + if (mrv_clk == NULL) { + camsys_err("Allocate camsys_mrv_clk_t failed!"); + err = -EINVAL; + goto clk_failed; + } + + // mrv_clk->pd_isp = devm_clk_get(&pdev->dev, "pd_isp"); + mrv_clk->pd_isp = NULL; + mrv_clk->aclk_isp = devm_clk_get(&pdev->dev, "aclk_isp"); + mrv_clk->hclk_isp = devm_clk_get(&pdev->dev, "hclk_isp"); + mrv_clk->isp = devm_clk_get(&pdev->dev, "clk_isp"); + mrv_clk->isp_jpe = devm_clk_get(&pdev->dev, "clk_isp_jpe"); + mrv_clk->pclkin_isp = devm_clk_get(&pdev->dev, "pclkin_isp"); + mrv_clk->cif_clk_out = devm_clk_get(&pdev->dev, "clk_vipout"); + if (/*IS_ERR_OR_NULL(mrv_clk->pd_isp) ||*/ IS_ERR_OR_NULL(mrv_clk->aclk_isp) || IS_ERR_OR_NULL(mrv_clk->hclk_isp) || + IS_ERR_OR_NULL(mrv_clk->isp) || IS_ERR_OR_NULL(mrv_clk->isp_jpe) || IS_ERR_OR_NULL(mrv_clk->pclkin_isp) || + IS_ERR_OR_NULL(mrv_clk->cif_clk_out)) { + camsys_err("Get %s clock resouce failed!\n",miscdev_name); + err = -EINVAL; + goto clk_failed; + } + + clk_set_rate(mrv_clk->isp,320000000); + clk_set_rate(mrv_clk->isp_jpe,320000000); + + spin_lock_init(&mrv_clk->lock); + + mrv_clk->in_on = false; + mrv_clk->out_on = 0; + + camsys_dev->clk = (void*)mrv_clk; + camsys_dev->clkin_cb = camsys_mrv_clkin_cb; + camsys_dev->clkout_cb = camsys_mrv_clkout_cb; + camsys_dev->reset_cb = camsys_mrv_reset_cb; + camsys_dev->iomux = camsys_mrv_iomux_cb; + + camsys_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + camsys_dev->miscdev.name = miscdev_name; + camsys_dev->miscdev.nodename = miscdev_name; + camsys_dev->miscdev.fops = &camsys_fops; + + err = misc_register(&camsys_dev->miscdev); + if (err < 0) { + camsys_err("misc register %s failed!",miscdev_name); + goto misc_register_failed; + } + + //Variable init + camsys_dev->dev_id = CAMSYS_DEVID_MARVIN; + camsys_dev->platform_remove = camsys_mrv_remove_cb; + + return 0; +misc_register_failed: + if (!IS_ERR_OR_NULL(camsys_dev->miscdev.this_device)) { + misc_deregister(&camsys_dev->miscdev); + } + +clk_failed: + if (mrv_clk != NULL) { + if (!IS_ERR_OR_NULL(mrv_clk->pd_isp)) { + clk_put(mrv_clk->pd_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->aclk_isp)) { + clk_put(mrv_clk->aclk_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->hclk_isp)) { + clk_put(mrv_clk->hclk_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->isp)) { + clk_put(mrv_clk->isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->isp_jpe)) { + clk_put(mrv_clk->isp_jpe); + } + if (!IS_ERR_OR_NULL(mrv_clk->pclkin_isp)) { + clk_put(mrv_clk->pclkin_isp); + } + if (!IS_ERR_OR_NULL(mrv_clk->cif_clk_out)) { + clk_put(mrv_clk->cif_clk_out); + } + + kfree(mrv_clk); + mrv_clk = NULL; + } + +end: + return err; +} +EXPORT_SYMBOL_GPL(camsys_mrv_probe_cb); + diff --git a/drivers/media/video/rk_camsys/camsys_marvin.h b/drivers/media/video/rk_camsys/camsys_marvin.h new file mode 100755 index 000000000000..7b92eddfb11c --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_marvin.h @@ -0,0 +1,44 @@ +#ifndef __CAMSYS_MARVIN_H__ +#define __CAMSYS_MARVIN_H__ + +#include "camsys_internal.h" + +#define CAMSYS_MARVIN_IRQNAME "MarvinIrq" + + + +#define MRV_ISP_BASE 0x400 +#define MRV_ISP_RIS (MRV_ISP_BASE+0x1c0) +#define MRV_ISP_MIS (MRV_ISP_BASE+0x1c4) +#define MRV_ISP_ICR (MRV_ISP_BASE+0x1c8) + +#define MRV_MIPI_BASE 0x1C00 +#define MRV_MIPI_MIS (MRV_MIPI_BASE+0x10) +#define MRV_MIPI_ICR (MRV_MIPI_BASE+0x14) + +#define MRV_MI_BASE (0x1400) +#define MRV_MI_MIS (MRV_MI_BASE+0x100) +#define MRV_MI_ICR (MRV_MI_BASE+0x104) + + + +typedef struct camsys_mrv_clk_s { + struct clk *pd_isp; + struct clk *hclk_isp; + struct clk *aclk_isp; + struct clk *isp; + struct clk *isp_jpe; + struct clk *pclkin_isp; + bool in_on; + + struct clk *cif_clk_out; + unsigned int out_on; + + spinlock_t lock; +} camsys_mrv_clk_t; + +int camsys_mrv_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev); + +#endif + + diff --git a/drivers/media/video/rk_camsys/camsys_mipicsi_phy.c b/drivers/media/video/rk_camsys/camsys_mipicsi_phy.c new file mode 100755 index 000000000000..62335ad55ec7 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_mipicsi_phy.c @@ -0,0 +1,126 @@ +#include "camsys_mipicsi_phy.h" + +#if defined(CONFIG_ARCH_ROCKCHIP) +//GRF_SOC_CON14 +//bit 0 dphy_rx0_testclr +//bit 1 dphy_rx0_testclk +//bit 2 dphy_rx0_testen +//bit 3:10 dphy_rx0_testdin +#define GRF_SOC_CON14_OFFSET (0x027c) +#define DPHY_RX0_TESTCLR_MASK (0x1<<16) +#define DPHY_RX0_TESTCLK_MASK (0x1<<17) +#define DPHY_RX0_TESTEN_MASK (0x1<<18) +#define DPHY_RX0_TESTDIN_MASK (0xff<<19) + +#define DPHY_RX0_TESTCLR (1<<0) +#define DPHY_RX0_TESTCLK (1<<1) +#define DPHY_RX0_TESTEN (1<<2) +#define DPHY_RX0_TESTDIN_OFFSET (3) + + +//GRF_SOC_CON6 +//bit 0 grf_con_disable_isp +//bit 1 grf_con_isp_dphy_sel 1'b0 mipi phy rx0 +#define GRF_SOC_CON6_OFFSET (0x025c) +#define MIPI_PHY_RX0_MASK (0x1<<16) +#define MIPI_PHY_RX0 (0x1<<0) + +#endif + +static void phy0_WriteReg(uint8_t addr, uint8_t data) +{ + + //TESTEN =1,TESTDIN=addr + write_grf_reg(GRF_SOC_CON14_OFFSET,(( addr << DPHY_RX0_TESTDIN_OFFSET) |DPHY_RX0_TESTDIN_MASK | DPHY_RX0_TESTEN| DPHY_RX0_TESTEN_MASK)); + //TESTCLK=0 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK); + //TESTEN =0,TESTDIN=data + write_grf_reg(GRF_SOC_CON14_OFFSET, (( data << DPHY_RX0_TESTDIN_OFFSET)|DPHY_RX0_TESTDIN_MASK |DPHY_RX0_TESTEN)); + //TESTCLK=1 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK |DPHY_RX0_TESTCLK); + +} + +static uint8_t phy0_ReadReg(uint8_t addr) //read 0xff968034 bit8~15 +{ + uint8_t data = 0; + //TESTEN =1,TESTDIN=addr + write_grf_reg(GRF_SOC_CON14_OFFSET,(( addr << DPHY_RX0_TESTDIN_OFFSET) |DPHY_RX0_TESTDIN_MASK | DPHY_RX0_TESTEN| DPHY_RX0_TESTEN_MASK)); + //TESTCLK=0 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK); + + data = ((read_grf_reg(GRF_SOC_CON14_OFFSET) >> DPHY_RX0_TESTDIN_OFFSET) & (0xff)); + + camsys_err("%s phy addr = 0x%x,value = 0x%x\n",__func__,addr,data); + return data ; + + +} + + +static void PHY0_Init(int numLane,int clkfreq) +{ + +// select phy rx0 + write_grf_reg(GRF_SOC_CON6_OFFSET, MIPI_PHY_RX0_MASK | MIPI_PHY_RX0); + +//TESTCLK=1 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK |DPHY_RX0_TESTCLK); +//TESTCLR=1 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK |DPHY_RX0_TESTCLK | DPHY_RX0_TESTCLR_MASK |DPHY_RX0_TESTCLR); +//TESTCLR=0 zyc + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK |DPHY_RX0_TESTCLK); + +//**********************************************************************// + +//set clock lane + phy0_WriteReg(0x34,0x14); + +//set lane 0 + /******************** + 500-550M 0x0E + 600-650M 0x10 + 720M 0x12 + 360M 0x2A + *******************/ + phy0_WriteReg(0x44,0x10); + + //**********************************************************************// + +//Normal operation + phy0_ReadReg(0x00); + //TESTCLK=1 + write_grf_reg(GRF_SOC_CON14_OFFSET, DPHY_RX0_TESTCLK_MASK |DPHY_RX0_TESTCLK); + + //TESTEN =0 + write_grf_reg(GRF_SOC_CON14_OFFSET, (DPHY_RX0_TESTEN)); + + } + + +static int camsys_mipiphy_ops (void *phy, void *phyinfo, unsigned int on) +{ + PHY0_Init(1,0); + return 0; +} + +static int camsys_mipiphy_clkin_cb(void *ptr, unsigned int on) +{ + return 0; +} + +static int camsys_mipiphy_remove_cb(struct platform_device *pdev) +{ + return 0; +} +int camsys_mipiphy_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev) +{ + + camsys_dev->mipiphy.clkin_cb = camsys_mipiphy_clkin_cb; + camsys_dev->mipiphy.ops = camsys_mipiphy_ops; + camsys_dev->mipiphy.remove = camsys_mipiphy_remove_cb; + + return 0; + +} + diff --git a/drivers/media/video/rk_camsys/camsys_mipicsi_phy.h b/drivers/media/video/rk_camsys/camsys_mipicsi_phy.h new file mode 100755 index 000000000000..4c0d64cb1bd7 --- /dev/null +++ b/drivers/media/video/rk_camsys/camsys_mipicsi_phy.h @@ -0,0 +1,28 @@ +#ifndef __CAMSYS_MIPICSI_PHY_H__ +#define __CAMSYS_MIPICSI_PHY_H__ + +#include "camsys_internal.h" + +#if defined(CONFIG_ARCH_ROCKCHIP) + +#define write_grf_reg(addr, val) __raw_writel(val, addr+RK_GRF_VIRT) +#define read_grf_reg(addr) __raw_readl(addr+RK_GRF_VIRT) +#define mask_grf_reg(addr, msk, val) write_grf_reg(addr,(val)|((~(msk))&read_grf_reg(addr))) +#else +#define write_grf_reg(addr, val) +#define read_grf_reg(addr) 0 +#define mask_grf_reg(addr, msk, val) +#endif + + +typedef struct camsys_mipiphy_clk_s { + struct clk *pd_mipi_csi; + struct clk *pclk_mipiphy_csi; + bool in_on; + spinlock_t lock; +} camsys_mipiphy_clk_t; + + +int camsys_mipiphy_probe_cb(struct platform_device *pdev, camsys_dev_t *camsys_dev); + +#endif diff --git a/include/media/camsys_head.h b/include/media/camsys_head.h new file mode 100755 index 000000000000..3adc33a13c73 --- /dev/null +++ b/include/media/camsys_head.h @@ -0,0 +1,218 @@ +#ifndef __RKCAMSYS_HEAR_H__ +#define __RKCAMSYS_HEAR_H__ + +#include + +/* +* C A M S Y S H E A D F I L E V E R S I O N +* +*v0.0.1: +* 1) test version; +*v0.0.2: +* 1) modify camsys_irqcnnt_t; +*v0.0.3: +* 1) add support cif phy for marvin; +*v0.0.4: +* 1) add clock information in struct camsys_devio_name_s; +*/ +#define CAMSYS_HEAD_VERSION KERNEL_VERSION(0,0,4) + +#define CAMSYS_MARVIN_DEVNAME "camsys_marvin" +#define CAMSYS_CIF0_DEVNAME "camsys_cif0" +#define CAMSYS_CIF1_DEVNAME "camsys_cif1" + +#define CAMSYS_NAME_LEN 32 + +#define CAMSYS_DEVID_MARVIN 0x00000001 +#define CAMSYS_DEVID_CIF_0 0x00000002 +#define CAMSYS_DEVID_CIF_1 0x00000004 +#define CAMSYS_DEVID_INTERNAL 0x000000FF + +#define CAMSYS_DEVID_SENSOR_1A 0x01000000 +#define CAMSYS_DEVID_SENSOR_1B 0x02000000 +#define CAMSYS_DEVID_SENSOR_2 0x04000000 +#define CAMSYS_DEVID_EXTERNAL 0xFF000000 +#define CAMSYS_DEVID_EXTERNAL_NUM 8 + +#define CAMSYS_DEVCFG_FLASHLIGHT 0x00000001 +#define CAMSYS_DEVCFG_PREFLASHLIGHT 0x00000002 +#define CAMSYS_DEVCFG_SHUTTER 0x00000004 + +typedef struct camsys_irqsta_s { + unsigned int ris; //Raw interrupt status + unsigned int mis; //Masked interrupt status +} camsys_irqsta_t; + +typedef struct camsys_irqcnnt_s { + int pid; + unsigned int timeout; //us + + unsigned int mis; + unsigned int icr; +} camsys_irqcnnt_t; + +typedef enum camsys_mmap_type_e { //this type can be filled in mmap offset argument + CamSys_Mmap_RegisterMem, + CamSys_Mmap_I2cMem, + + CamSys_Mmap_End +} camsys_mmap_type_t; + +typedef struct camsys_querymem_s { + camsys_mmap_type_t mem_type; + unsigned long mem_offset; + + unsigned int mem_size; +} camsys_querymem_t; + +typedef struct camsys_i2c_info_s { + unsigned char bus_num; + unsigned short slave_addr; + unsigned int reg_addr; //i2c device register address + unsigned int reg_size; //register address size + unsigned int val; + unsigned int val_size; //register value size + unsigned int i2cbuf_directly; + unsigned int i2cbuf_bytes; + unsigned int speed; //100000 == 100KHz +} camsys_i2c_info_t; + +typedef struct camsys_reginfo_s { + unsigned int dev_mask; + unsigned int reg_offset; + unsigned int val; +} camsys_reginfo_t; + +typedef enum camsys_sysctrl_ops_e { + CamSys_Avdd =0, + CamSys_Dovdd, + CamSys_Dvdd, + CamSys_Afvdd, + + CamSys_Vdd_Tag = 10, + + CamSys_PwrDn, + CamSys_Rst, + CamSys_AfPwr, + CamSys_AfPwrDn, + + CamSys_Gpio_Tag = 50, + + CamSys_ClkIn +} camsys_sysctrl_ops_t; + +typedef struct camsys_regulator_info_s { + unsigned char name[CAMSYS_NAME_LEN]; + int min_uv; + int max_uv; +} camsys_regulator_info_t; + +typedef struct camsys_gpio_info_s { + unsigned char name[CAMSYS_NAME_LEN]; + unsigned int active; +} camsys_gpio_info_t; + +typedef struct camsys_sysctrl_s { + unsigned int dev_mask; + camsys_sysctrl_ops_t ops; + unsigned int on; +} camsys_sysctrl_t; + +typedef struct camsys_flash_info_s { + camsys_gpio_info_t fl; +} camsys_flash_info_t; + +typedef struct camsys_mipiphy_s { + unsigned int data_en_bit; //data lane enable bit; +} camsys_mipiphy_t; + +typedef enum camsys_fmt_e { + CamSys_Fmt_Yuv420_8b = 0x18, + CamSys_Fmt_Yuv420_10b = 0x19, + CamSys_Fmt_LegacyYuv420_8b = 0x19, + + CamSys_Fmt_Yuv422_8b = 0x1e, + CamSys_Fmt_Yuv422_10b = 0x1f, + + CamSys_Fmt_Raw_6b = 0x28, + CamSys_Fmt_Raw_7b = 0x29, + CamSys_Fmt_Raw_8b = 0x2a, + CamSys_Fmt_Raw_10b = 0x2b, + CamSys_Fmt_Raw_12b = 0x2c, + CamSys_Fmt_Raw_14b = 0x2d, +} camsys_fmt_t; + +typedef struct camsys_cifphy_s { + unsigned int cif_num; + camsys_fmt_t fmt; +} camsys_cifphy_t; + +typedef enum camsys_phy_type_e { + CamSys_Phy_Mipi, + CamSys_Phy_Cif, + + CamSys_Phy_end +} camsys_phy_type_t; + +typedef struct camsys_extdev_phy_s { + camsys_phy_type_t type; + union { + camsys_mipiphy_t mipi; + camsys_cifphy_t cif; + } info; + +} camsys_extdev_phy_t; + +typedef struct camsys_extdev_clk_s { + unsigned int in_rate; +} camsys_extdev_clk_t; + +typedef struct camsys_devio_name_s { + unsigned int dev_id; + + camsys_regulator_info_t avdd; // sensor avdd power regulator name + camsys_regulator_info_t dovdd; // sensor dovdd power regulator name + camsys_regulator_info_t dvdd; // sensor dvdd power regulator name "NC" describe no regulator + camsys_regulator_info_t afvdd; + + camsys_gpio_info_t pwrdn; // standby gpio name + camsys_gpio_info_t rst; // hard reset gpio name + camsys_gpio_info_t afpwr; // auto focus vcm driver ic power gpio name + camsys_gpio_info_t afpwrdn; // auto focus vcm driver ic standby gpio + + camsys_flash_info_t fl; + + camsys_extdev_phy_t phy; + camsys_extdev_clk_t clk; + + unsigned int dev_cfg; // function bit mask configuration +} camsys_devio_name_t; + +typedef struct camsys_version_s { + unsigned int drv_ver; + unsigned int head_ver; +} camsys_version_t; + +/* + * I O C T L C O D E S F O R R O C K C H I P S C A M S Y S D E V I C E S + * + */ +#define CAMSYS_IOC_MAGIC 'M' +#define CAMSYS_IOC_MAXNR 12 + +#define CAMSYS_VERCHK _IOR(CAMSYS_IOC_MAGIC, 0, camsys_version_t) + +#define CAMSYS_I2CRD _IOWR(CAMSYS_IOC_MAGIC, 1, camsys_i2c_info_t) +#define CAMSYS_I2CWR _IOW(CAMSYS_IOC_MAGIC, 2, camsys_i2c_info_t) + +#define CAMSYS_SYSCTRL _IOW(CAMSYS_IOC_MAGIC, 3, camsys_sysctrl_t) +#define CAMSYS_REGRD _IOWR(CAMSYS_IOC_MAGIC, 4, camsys_reginfo_t) +#define CAMSYS_REGWR _IOW(CAMSYS_IOC_MAGIC, 5, camsys_reginfo_t) +#define CAMSYS_REGISTER_DEVIO _IOW(CAMSYS_IOC_MAGIC, 6, camsys_devio_name_t) +#define CAMSYS_DEREGISTER_DEVIO _IOW(CAMSYS_IOC_MAGIC, 7, unsigned int) +#define CAMSYS_IRQCONNECT _IOW(CAMSYS_IOC_MAGIC, 8, camsys_irqcnnt_t) +#define CAMSYS_IRQWAIT _IOR(CAMSYS_IOC_MAGIC, 9, camsys_irqsta_t) +#define CAMSYS_IRQDISCONNECT _IOW(CAMSYS_IOC_MAGIC, 10, camsys_irqcnnt_t) + +#define CAMSYS_QUREYMEM _IOR(CAMSYS_IOC_MAGIC, 11, camsys_querymem_t) +#endif -- 2.34.1