/* * Copyright 2009-2014 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file fsl_csi.c, this file is derived from mx27_csi.c * * @brief mx25 CMOS Sensor interface functions * * @ingroup CSI */ #include #include #include #include #include #include #include #include #include #include #include #include "mxc_v4l2_capture.h" #include "fsl_csi.h" #define CSI_MAX_NUM 2 struct csi_soc csi_array[CSI_MAX_NUM], *csi; static csi_irq_callback_t g_callback; static void *g_callback_data; static struct clk *disp_axi_clk; static struct clk *dcic_clk; static struct clk *csi_clk; void csi_clk_enable(void) { clk_prepare_enable(disp_axi_clk); clk_prepare_enable(dcic_clk); clk_prepare_enable(csi_clk); } EXPORT_SYMBOL(csi_clk_enable); void csi_clk_disable(void) { clk_disable_unprepare(csi_clk); clk_disable_unprepare(dcic_clk); clk_disable_unprepare(disp_axi_clk); } EXPORT_SYMBOL(csi_clk_disable); static irqreturn_t csi_irq_handler(int irq, void *data) { cam_data *cam = (cam_data *) data; struct csi_soc *csi = &csi_array[cam->csi]; unsigned long status = __raw_readl(csi->regbase + CSI_CSISR); __raw_writel(status, csi->regbase + CSI_CSISR); if (status & BIT_HRESP_ERR_INT) pr_warning("Hresponse error is detected.\n"); if (status & BIT_DMA_TSF_DONE_FB1) { if (cam->capture_on) { spin_lock(&cam->queue_int_lock); cam->ping_pong_csi = 1; spin_unlock(&cam->queue_int_lock); cam->enc_callback(0, cam); } else { cam->still_counter++; wake_up_interruptible(&cam->still_queue); } } if (status & BIT_DMA_TSF_DONE_FB2) { if (cam->capture_on) { spin_lock(&cam->queue_int_lock); cam->ping_pong_csi = 2; spin_unlock(&cam->queue_int_lock); cam->enc_callback(0, cam); } else { cam->still_counter++; wake_up_interruptible(&cam->still_queue); } } if (g_callback) g_callback(g_callback_data, status); pr_debug("CSI status = 0x%08lX\n", status); return IRQ_HANDLED; } static void csihw_reset_frame_count(struct csi_soc *csi) { __raw_writel(__raw_readl(csi->regbase + CSI_CSICR3) | BIT_FRMCNT_RST, csi->regbase + CSI_CSICR3); } static void csihw_reset(struct csi_soc *csi) { csihw_reset_frame_count(csi); __raw_writel(CSICR1_RESET_VAL, csi->regbase + CSI_CSICR1); __raw_writel(CSICR2_RESET_VAL, csi->regbase + CSI_CSICR2); __raw_writel(CSICR3_RESET_VAL, csi->regbase + CSI_CSICR3); } /*! * csi_init_interface * Init csi interface */ static void csi_init_interface(struct csi_soc *csi) { unsigned int val = 0; unsigned int imag_para; val |= BIT_SOF_POL; val |= BIT_REDGE; val |= BIT_GCLK_MODE; val |= BIT_HSYNC_POL; val |= BIT_FCC; val |= 1 << SHIFT_MCLKDIV; val |= BIT_MCLKEN; __raw_writel(val, csi->regbase + CSI_CSICR1); imag_para = (640 << 16) | 960; __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); val = 0x1010; val |= BIT_DMA_REFLASH_RFF; __raw_writel(val, csi->regbase + CSI_CSICR3); } void csi_format_swap16(cam_data *cam, bool enable) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned int val; val = __raw_readl(csi->regbase + CSI_CSICR1); if (enable) { val |= BIT_PACK_DIR; val |= BIT_SWAP16_EN; } else { val &= ~BIT_PACK_DIR; val &= ~BIT_SWAP16_EN; } __raw_writel(val, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_format_swap16); /*! * csi_read_mclk_flag * * @return gcsi_mclk_source */ int csi_read_mclk_flag(void) { return 0; } EXPORT_SYMBOL(csi_read_mclk_flag); void csi_start_callback(void *data) { cam_data *cam = (cam_data *) data; if (request_irq(csi_array[cam->csi].irq_nr, csi_irq_handler, 0, "csi", cam) < 0) pr_debug("CSI error: irq request fail\n"); } EXPORT_SYMBOL(csi_start_callback); void csi_stop_callback(void *data) { cam_data *cam = (cam_data *) data; free_irq(csi_array[cam->csi].irq_nr, cam); } EXPORT_SYMBOL(csi_stop_callback); void csi_enable_int(cam_data *cam, int arg) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); cr1 |= BIT_SOF_INTEN; if (arg == 1) { /* still capture needs DMA intterrupt */ cr1 |= BIT_FB1_DMA_DONE_INTEN; cr1 |= BIT_FB2_DMA_DONE_INTEN; } __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_enable_int); void csi_disable_int(cam_data *cam) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); cr1 &= ~BIT_SOF_INTEN; cr1 &= ~BIT_FB1_DMA_DONE_INTEN; cr1 &= ~BIT_FB2_DMA_DONE_INTEN; __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_disable_int); void csi_enable(cam_data *cam, int arg) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr = __raw_readl(csi->regbase + CSI_CSICR18); if (arg == 1) cr |= BIT_CSI_ENABLE; else cr &= ~BIT_CSI_ENABLE; __raw_writel(cr, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_enable); void csi_buf_stride_set(cam_data *cam, u32 stride) { struct csi_soc *csi = &csi_array[cam->csi]; __raw_writel(stride, csi->regbase + CSI_CSIFBUF_PARA); } EXPORT_SYMBOL(csi_buf_stride_set); void csi_deinterlace_enable(cam_data *cam, bool enable) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); if (enable == true) cr18 |= BIT_DEINTERLACE_EN; else cr18 &= ~BIT_DEINTERLACE_EN; __raw_writel(cr18, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_deinterlace_enable); void csi_deinterlace_mode(cam_data *cam, int mode) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); if (mode == V4L2_STD_NTSC) cr18 |= BIT_NTSC_EN; else cr18 &= ~BIT_NTSC_EN; __raw_writel(cr18, csi->regbase + CSI_CSICR18); } EXPORT_SYMBOL(csi_deinterlace_mode); void csi_tvdec_enable(cam_data *cam, bool enable) { struct csi_soc *csi = &csi_array[cam->csi]; unsigned long cr18 = __raw_readl(csi->regbase + CSI_CSICR18); unsigned long cr1 = __raw_readl(csi->regbase + CSI_CSICR1); if (enable == true) { cr18 |= (BIT_TVDECODER_IN_EN | BIT_BASEADDR_SWITCH_EN); cr1 |= BIT_CCIR_MODE | BIT_EXT_VSYNC; cr1 &= ~(BIT_SOF_POL | BIT_REDGE); } else { cr18 &= ~(BIT_TVDECODER_IN_EN | BIT_BASEADDR_SWITCH_EN); cr1 &= ~(BIT_CCIR_MODE | BIT_EXT_VSYNC); cr1 |= BIT_SOF_POL | BIT_REDGE; } __raw_writel(cr18, csi->regbase + CSI_CSICR18); __raw_writel(cr1, csi->regbase + CSI_CSICR1); } EXPORT_SYMBOL(csi_tvdec_enable); void csi_set_32bit_imagpara(cam_data *cam, int width, int height) { struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | height; __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_32bit_imagpara); void csi_set_16bit_imagpara(cam_data *cam, int width, int height) { struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | (height * 2); __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_16bit_imagpara); void csi_set_12bit_imagpara(cam_data *cam, int width, int height) { struct csi_soc *csi = &csi_array[cam->csi]; int imag_para = 0; unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); imag_para = (width << 16) | (height * 3 / 2); __raw_writel(imag_para, csi->regbase + CSI_CSIIMAG_PARA); /* reflash the embeded DMA controller */ __raw_writel(cr3 | BIT_DMA_REFLASH_RFF, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_set_12bit_imagpara); void csi_dmareq_rff_enable(struct csi_soc *csi) { unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); cr3 |= BIT_DMA_REQ_EN_RFF; cr3 |= BIT_HRESP_ERR_EN; __raw_writel(cr3, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_dmareq_rff_enable); void csi_dmareq_rff_disable(struct csi_soc *csi) { unsigned long cr3 = __raw_readl(csi->regbase + CSI_CSICR3); cr3 &= ~BIT_DMA_REQ_EN_RFF; cr3 &= ~BIT_HRESP_ERR_EN; __raw_writel(cr3, csi->regbase + CSI_CSICR3); } EXPORT_SYMBOL(csi_dmareq_rff_disable); struct csi_soc *csi_get_soc(int id) { if (id >= CSI_MAX_NUM) return ERR_PTR(-ENODEV); else if (!csi_array[id].online) return ERR_PTR(-ENODEV); else return &(csi_array[id]); } EXPORT_SYMBOL_GPL(csi_get_soc); static const struct of_device_id fsl_csi_dt_ids[] = { { .compatible = "fsl,imx6sl-csi", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_csi_dt_ids); static int csi_probe(struct platform_device *pdev) { int ret = 0; struct resource *res; int id; id = of_alias_get_id(pdev->dev.of_node, "csi"); if (id < 0) { dev_dbg(&pdev->dev, "can not get alias id\n"); return id; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "No csi irq found.\n"); ret = -ENODEV; goto err; } csi = &csi_array[id]; csi->irq_nr = res->start; csi->online = false; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "No csi base address found.\n"); ret = -ENODEV; goto err; } csi->regbase = devm_ioremap(&pdev->dev, res->start, resource_size(res)); if (!csi->regbase) { dev_err(&pdev->dev, "ioremap failed with csi base\n"); ret = -ENOMEM; goto err; } disp_axi_clk = devm_clk_get(&pdev->dev, "disp-axi"); if (IS_ERR(disp_axi_clk)) { dev_err(&pdev->dev, "get csi clock failed\n"); return PTR_ERR(disp_axi_clk); } csi_clk = devm_clk_get(&pdev->dev, "csi_mclk"); if (IS_ERR(csi_clk)) { dev_err(&pdev->dev, "get csi mclk failed\n"); return PTR_ERR(csi_clk); } dcic_clk = devm_clk_get(&pdev->dev, "dcic"); if (IS_ERR(dcic_clk)) { dev_err(&pdev->dev, "get dcic clk failed\n"); return PTR_ERR(dcic_clk); } platform_set_drvdata(pdev, csi); csi_clk_enable(); csihw_reset(csi); csi_init_interface(csi); csi_dmareq_rff_disable(csi); csi->online = true; err: return ret; } static int csi_remove(struct platform_device *pdev) { struct csi_soc *csi = platform_get_drvdata(pdev); csi->online = false; platform_set_drvdata(pdev, NULL); return 0; } #ifdef CONFIG_PM_SLEEP static int csi_suspend(struct device *dev) { struct csi_soc *csi = dev_get_drvdata(dev); csi->online = false; return 0; } static int csi_resume(struct device *dev) { struct csi_soc *csi = dev_get_drvdata(dev); csi_clk_enable(); csihw_reset(csi); csi_init_interface(csi); csi_dmareq_rff_disable(csi); csi_clk_disable(); csi->online = true; return 0; } #else #define csi_suspend NULL #define csi_resume NULL #endif static const struct dev_pm_ops csi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(csi_suspend, csi_resume) }; static struct platform_driver csi_driver = { .driver = { .name = "fsl_csi", .of_match_table = of_match_ptr(fsl_csi_dt_ids), .pm = &csi_pm_ops, }, .probe = csi_probe, .remove = csi_remove, }; module_platform_driver(csi_driver); MODULE_AUTHOR("Freescale Semiconductor, Inc."); MODULE_DESCRIPTION("fsl CSI driver"); MODULE_LICENSE("GPL");