162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * isp.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TI OMAP3 ISP - Core 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2006-2010 Nokia Corporation 862306a36Sopenharmony_ci * Copyright (C) 2007-2009 Texas Instruments, Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1162306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Contributors: 1462306a36Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1562306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 1662306a36Sopenharmony_ci * David Cohen <dacohen@gmail.com> 1762306a36Sopenharmony_ci * Stanimir Varbanov <svarbanov@mm-sol.com> 1862306a36Sopenharmony_ci * Vimarsh Zutshi <vimarsh.zutshi@gmail.com> 1962306a36Sopenharmony_ci * Tuukka Toivonen <tuukkat76@gmail.com> 2062306a36Sopenharmony_ci * Sergio Aguirre <saaguirre@ti.com> 2162306a36Sopenharmony_ci * Antti Koskipaa <akoskipa@gmail.com> 2262306a36Sopenharmony_ci * Ivan T. Ivanov <iivanov@mm-sol.com> 2362306a36Sopenharmony_ci * RaniSuneela <r-m@ti.com> 2462306a36Sopenharmony_ci * Atanas Filipov <afilipov@mm-sol.com> 2562306a36Sopenharmony_ci * Gjorgji Rosikopulos <grosikopulos@mm-sol.com> 2662306a36Sopenharmony_ci * Hiroshi DOYU <hiroshi.doyu@nokia.com> 2762306a36Sopenharmony_ci * Nayden Kanchev <nkanchev@mm-sol.com> 2862306a36Sopenharmony_ci * Phil Carmody <ext-phil.2.carmody@nokia.com> 2962306a36Sopenharmony_ci * Artem Bityutskiy <artem.bityutskiy@nokia.com> 3062306a36Sopenharmony_ci * Dominic Curran <dcurran@ti.com> 3162306a36Sopenharmony_ci * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi> 3262306a36Sopenharmony_ci * Pallavi Kulkarni <p-kulkarni@ti.com> 3362306a36Sopenharmony_ci * Vaibhav Hiremath <hvaibhav@ti.com> 3462306a36Sopenharmony_ci * Mohit Jalori <mjalori@ti.com> 3562306a36Sopenharmony_ci * Sameer Venkatraman <sameerv@ti.com> 3662306a36Sopenharmony_ci * Senthilvadivu Guruswamy <svadivu@ti.com> 3762306a36Sopenharmony_ci * Thara Gopinath <thara@ti.com> 3862306a36Sopenharmony_ci * Toni Leinonen <toni.leinonen@nokia.com> 3962306a36Sopenharmony_ci * Troy Laramy <t-laramy@ti.com> 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <linux/clk.h> 4362306a36Sopenharmony_ci#include <linux/clkdev.h> 4462306a36Sopenharmony_ci#include <linux/delay.h> 4562306a36Sopenharmony_ci#include <linux/device.h> 4662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4762306a36Sopenharmony_ci#include <linux/i2c.h> 4862306a36Sopenharmony_ci#include <linux/interrupt.h> 4962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 5062306a36Sopenharmony_ci#include <linux/module.h> 5162306a36Sopenharmony_ci#include <linux/omap-iommu.h> 5262306a36Sopenharmony_ci#include <linux/platform_device.h> 5362306a36Sopenharmony_ci#include <linux/property.h> 5462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 5562306a36Sopenharmony_ci#include <linux/slab.h> 5662306a36Sopenharmony_ci#include <linux/sched.h> 5762306a36Sopenharmony_ci#include <linux/vmalloc.h> 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 6062306a36Sopenharmony_ci#include <asm/dma-iommu.h> 6162306a36Sopenharmony_ci#endif 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#include <media/v4l2-common.h> 6462306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 6562306a36Sopenharmony_ci#include <media/v4l2-device.h> 6662306a36Sopenharmony_ci#include <media/v4l2-mc.h> 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#include "isp.h" 6962306a36Sopenharmony_ci#include "ispreg.h" 7062306a36Sopenharmony_ci#include "ispccdc.h" 7162306a36Sopenharmony_ci#include "isppreview.h" 7262306a36Sopenharmony_ci#include "ispresizer.h" 7362306a36Sopenharmony_ci#include "ispcsi2.h" 7462306a36Sopenharmony_ci#include "ispccp2.h" 7562306a36Sopenharmony_ci#include "isph3a.h" 7662306a36Sopenharmony_ci#include "isphist.h" 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic unsigned int autoidle; 7962306a36Sopenharmony_cimodule_param(autoidle, int, 0444); 8062306a36Sopenharmony_ciMODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void isp_save_ctx(struct isp_device *isp); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void isp_restore_ctx(struct isp_device *isp); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic const struct isp_res_mapping isp_res_maps[] = { 8762306a36Sopenharmony_ci { 8862306a36Sopenharmony_ci .isp_rev = ISP_REVISION_2_0, 8962306a36Sopenharmony_ci .offset = { 9062306a36Sopenharmony_ci /* first MMIO area */ 9162306a36Sopenharmony_ci 0x0000, /* base, len 0x0070 */ 9262306a36Sopenharmony_ci 0x0400, /* ccp2, len 0x01f0 */ 9362306a36Sopenharmony_ci 0x0600, /* ccdc, len 0x00a8 */ 9462306a36Sopenharmony_ci 0x0a00, /* hist, len 0x0048 */ 9562306a36Sopenharmony_ci 0x0c00, /* h3a, len 0x0060 */ 9662306a36Sopenharmony_ci 0x0e00, /* preview, len 0x00a0 */ 9762306a36Sopenharmony_ci 0x1000, /* resizer, len 0x00ac */ 9862306a36Sopenharmony_ci 0x1200, /* sbl, len 0x00fc */ 9962306a36Sopenharmony_ci /* second MMIO area */ 10062306a36Sopenharmony_ci 0x0000, /* csi2a, len 0x0170 */ 10162306a36Sopenharmony_ci 0x0170, /* csiphy2, len 0x000c */ 10262306a36Sopenharmony_ci }, 10362306a36Sopenharmony_ci .phy_type = ISP_PHY_TYPE_3430, 10462306a36Sopenharmony_ci }, 10562306a36Sopenharmony_ci { 10662306a36Sopenharmony_ci .isp_rev = ISP_REVISION_15_0, 10762306a36Sopenharmony_ci .offset = { 10862306a36Sopenharmony_ci /* first MMIO area */ 10962306a36Sopenharmony_ci 0x0000, /* base, len 0x0070 */ 11062306a36Sopenharmony_ci 0x0400, /* ccp2, len 0x01f0 */ 11162306a36Sopenharmony_ci 0x0600, /* ccdc, len 0x00a8 */ 11262306a36Sopenharmony_ci 0x0a00, /* hist, len 0x0048 */ 11362306a36Sopenharmony_ci 0x0c00, /* h3a, len 0x0060 */ 11462306a36Sopenharmony_ci 0x0e00, /* preview, len 0x00a0 */ 11562306a36Sopenharmony_ci 0x1000, /* resizer, len 0x00ac */ 11662306a36Sopenharmony_ci 0x1200, /* sbl, len 0x00fc */ 11762306a36Sopenharmony_ci /* second MMIO area */ 11862306a36Sopenharmony_ci 0x0000, /* csi2a, len 0x0170 (1st area) */ 11962306a36Sopenharmony_ci 0x0170, /* csiphy2, len 0x000c */ 12062306a36Sopenharmony_ci 0x01c0, /* csi2a, len 0x0040 (2nd area) */ 12162306a36Sopenharmony_ci 0x0400, /* csi2c, len 0x0170 (1st area) */ 12262306a36Sopenharmony_ci 0x0570, /* csiphy1, len 0x000c */ 12362306a36Sopenharmony_ci 0x05c0, /* csi2c, len 0x0040 (2nd area) */ 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci .phy_type = ISP_PHY_TYPE_3630, 12662306a36Sopenharmony_ci }, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* Structure for saving/restoring ISP module registers */ 13062306a36Sopenharmony_cistatic struct isp_reg isp_reg_list[] = { 13162306a36Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, 13262306a36Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, 13362306a36Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, 13462306a36Sopenharmony_ci {0, ISP_TOK_TERM, 0} 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * omap3isp_flush - Post pending L3 bus writes by doing a register readback 13962306a36Sopenharmony_ci * @isp: OMAP3 ISP device 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * In order to force posting of pending writes, we need to write and 14262306a36Sopenharmony_ci * readback the same register, in this case the revision register. 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * See this link for reference: 14562306a36Sopenharmony_ci * https://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_civoid omap3isp_flush(struct isp_device *isp) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 15062306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 15462306a36Sopenharmony_ci * XCLK 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw) 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void isp_xclk_update(struct isp_xclk *xclk, u32 divider) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci switch (xclk->id) { 16262306a36Sopenharmony_ci case ISP_XCLK_A: 16362306a36Sopenharmony_ci isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 16462306a36Sopenharmony_ci ISPTCTRL_CTRL_DIVA_MASK, 16562306a36Sopenharmony_ci divider << ISPTCTRL_CTRL_DIVA_SHIFT); 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case ISP_XCLK_B: 16862306a36Sopenharmony_ci isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 16962306a36Sopenharmony_ci ISPTCTRL_CTRL_DIVB_MASK, 17062306a36Sopenharmony_ci divider << ISPTCTRL_CTRL_DIVB_SHIFT); 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int isp_xclk_prepare(struct clk_hw *hw) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci omap3isp_get(xclk->isp); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void isp_xclk_unprepare(struct clk_hw *hw) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci omap3isp_put(xclk->isp); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int isp_xclk_enable(struct clk_hw *hw) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 19462306a36Sopenharmony_ci unsigned long flags; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 19762306a36Sopenharmony_ci isp_xclk_update(xclk, xclk->divider); 19862306a36Sopenharmony_ci xclk->enabled = true; 19962306a36Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return 0; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void isp_xclk_disable(struct clk_hw *hw) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 20762306a36Sopenharmony_ci unsigned long flags; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 21062306a36Sopenharmony_ci isp_xclk_update(xclk, 0); 21162306a36Sopenharmony_ci xclk->enabled = false; 21262306a36Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic unsigned long isp_xclk_recalc_rate(struct clk_hw *hw, 21662306a36Sopenharmony_ci unsigned long parent_rate) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return parent_rate / xclk->divider; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci u32 divider; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (*rate >= parent_rate) { 22862306a36Sopenharmony_ci *rate = parent_rate; 22962306a36Sopenharmony_ci return ISPTCTRL_CTRL_DIV_BYPASS; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (*rate == 0) 23362306a36Sopenharmony_ci *rate = 1; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci divider = DIV_ROUND_CLOSEST(parent_rate, *rate); 23662306a36Sopenharmony_ci if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) 23762306a36Sopenharmony_ci divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci *rate = parent_rate / divider; 24062306a36Sopenharmony_ci return divider; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate, 24462306a36Sopenharmony_ci unsigned long *parent_rate) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci isp_xclk_calc_divider(&rate, *parent_rate); 24762306a36Sopenharmony_ci return rate; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate, 25162306a36Sopenharmony_ci unsigned long parent_rate) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 25462306a36Sopenharmony_ci unsigned long flags; 25562306a36Sopenharmony_ci u32 divider; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci divider = isp_xclk_calc_divider(&rate, parent_rate); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci xclk->divider = divider; 26262306a36Sopenharmony_ci if (xclk->enabled) 26362306a36Sopenharmony_ci isp_xclk_update(xclk, divider); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n", 26862306a36Sopenharmony_ci __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider); 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic const struct clk_ops isp_xclk_ops = { 27362306a36Sopenharmony_ci .prepare = isp_xclk_prepare, 27462306a36Sopenharmony_ci .unprepare = isp_xclk_unprepare, 27562306a36Sopenharmony_ci .enable = isp_xclk_enable, 27662306a36Sopenharmony_ci .disable = isp_xclk_disable, 27762306a36Sopenharmony_ci .recalc_rate = isp_xclk_recalc_rate, 27862306a36Sopenharmony_ci .round_rate = isp_xclk_round_rate, 27962306a36Sopenharmony_ci .set_rate = isp_xclk_set_rate, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic const char *isp_xclk_parent_name = "cam_mclk"; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci unsigned int idx = clkspec->args[0]; 28762306a36Sopenharmony_ci struct isp_device *isp = data; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (idx >= ARRAY_SIZE(isp->xclks)) 29062306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return isp->xclks[idx].clk; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int isp_xclk_init(struct isp_device *isp) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci struct device_node *np = isp->dev->of_node; 29862306a36Sopenharmony_ci struct clk_init_data init = {}; 29962306a36Sopenharmony_ci unsigned int i; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) 30262306a36Sopenharmony_ci isp->xclks[i].clk = ERR_PTR(-EINVAL); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 30562306a36Sopenharmony_ci struct isp_xclk *xclk = &isp->xclks[i]; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci xclk->isp = isp; 30862306a36Sopenharmony_ci xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; 30962306a36Sopenharmony_ci xclk->divider = 1; 31062306a36Sopenharmony_ci spin_lock_init(&xclk->lock); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci init.name = i == 0 ? "cam_xclka" : "cam_xclkb"; 31362306a36Sopenharmony_ci init.ops = &isp_xclk_ops; 31462306a36Sopenharmony_ci init.parent_names = &isp_xclk_parent_name; 31562306a36Sopenharmony_ci init.num_parents = 1; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci xclk->hw.init = &init; 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * The first argument is NULL in order to avoid circular 32062306a36Sopenharmony_ci * reference, as this driver takes reference on the 32162306a36Sopenharmony_ci * sensor subdevice modules and the sensors would take 32262306a36Sopenharmony_ci * reference on this module through clk_get(). 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_ci xclk->clk = clk_register(NULL, &xclk->hw); 32562306a36Sopenharmony_ci if (IS_ERR(xclk->clk)) 32662306a36Sopenharmony_ci return PTR_ERR(xclk->clk); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (np) 33062306a36Sopenharmony_ci of_clk_add_provider(np, isp_xclk_src_get, isp); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void isp_xclk_cleanup(struct isp_device *isp) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct device_node *np = isp->dev->of_node; 33862306a36Sopenharmony_ci unsigned int i; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (np) 34162306a36Sopenharmony_ci of_clk_del_provider(np); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 34462306a36Sopenharmony_ci struct isp_xclk *xclk = &isp->xclks[i]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!IS_ERR(xclk->clk)) 34762306a36Sopenharmony_ci clk_unregister(xclk->clk); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 35262306a36Sopenharmony_ci * Interrupts 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* 35662306a36Sopenharmony_ci * isp_enable_interrupts - Enable ISP interrupts. 35762306a36Sopenharmony_ci * @isp: OMAP3 ISP device 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic void isp_enable_interrupts(struct isp_device *isp) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci static const u32 irq = IRQ0ENABLE_CSIA_IRQ 36262306a36Sopenharmony_ci | IRQ0ENABLE_CSIB_IRQ 36362306a36Sopenharmony_ci | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ 36462306a36Sopenharmony_ci | IRQ0ENABLE_CCDC_LSC_DONE_IRQ 36562306a36Sopenharmony_ci | IRQ0ENABLE_CCDC_VD0_IRQ 36662306a36Sopenharmony_ci | IRQ0ENABLE_CCDC_VD1_IRQ 36762306a36Sopenharmony_ci | IRQ0ENABLE_HS_VS_IRQ 36862306a36Sopenharmony_ci | IRQ0ENABLE_HIST_DONE_IRQ 36962306a36Sopenharmony_ci | IRQ0ENABLE_H3A_AWB_DONE_IRQ 37062306a36Sopenharmony_ci | IRQ0ENABLE_H3A_AF_DONE_IRQ 37162306a36Sopenharmony_ci | IRQ0ENABLE_PRV_DONE_IRQ 37262306a36Sopenharmony_ci | IRQ0ENABLE_RSZ_DONE_IRQ; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 37562306a36Sopenharmony_ci isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * isp_disable_interrupts - Disable ISP interrupts. 38062306a36Sopenharmony_ci * @isp: OMAP3 ISP device 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_cistatic void isp_disable_interrupts(struct isp_device *isp) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * isp_core_init - ISP core settings 38962306a36Sopenharmony_ci * @isp: OMAP3 ISP device 39062306a36Sopenharmony_ci * @idle: Consider idle state. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * Set the power settings for the ISP and SBL bus and configure the HS/VS 39362306a36Sopenharmony_ci * interrupt source. 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * We need to configure the HS/VS interrupt source before interrupts get 39662306a36Sopenharmony_ci * enabled, as the sensor might be free-running and the ISP default setting 39762306a36Sopenharmony_ci * (HS edge) would put an unnecessary burden on the CPU. 39862306a36Sopenharmony_ci */ 39962306a36Sopenharmony_cistatic void isp_core_init(struct isp_device *isp, int idle) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci isp_reg_writel(isp, 40262306a36Sopenharmony_ci ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : 40362306a36Sopenharmony_ci ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << 40462306a36Sopenharmony_ci ISP_SYSCONFIG_MIDLEMODE_SHIFT) | 40562306a36Sopenharmony_ci ((isp->revision == ISP_REVISION_15_0) ? 40662306a36Sopenharmony_ci ISP_SYSCONFIG_AUTOIDLE : 0), 40762306a36Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci isp_reg_writel(isp, 41062306a36Sopenharmony_ci (isp->autoidle ? ISPCTRL_SBL_AUTOIDLE : 0) | 41162306a36Sopenharmony_ci ISPCTRL_SYNC_DETECT_VSRISE, 41262306a36Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci/* 41662306a36Sopenharmony_ci * Configure the bridge and lane shifter. Valid inputs are 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * CCDC_INPUT_PARALLEL: Parallel interface 41962306a36Sopenharmony_ci * CCDC_INPUT_CSI2A: CSI2a receiver 42062306a36Sopenharmony_ci * CCDC_INPUT_CCP2B: CCP2b receiver 42162306a36Sopenharmony_ci * CCDC_INPUT_CSI2C: CSI2c receiver 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * The bridge and lane shifter are configured according to the selected input 42462306a36Sopenharmony_ci * and the ISP platform data. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_civoid omap3isp_configure_bridge(struct isp_device *isp, 42762306a36Sopenharmony_ci enum ccdc_input_entity input, 42862306a36Sopenharmony_ci const struct isp_parallel_cfg *parcfg, 42962306a36Sopenharmony_ci unsigned int shift, unsigned int bridge) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci u32 ispctrl_val; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 43462306a36Sopenharmony_ci ispctrl_val &= ~ISPCTRL_SHIFT_MASK; 43562306a36Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; 43662306a36Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; 43762306a36Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; 43862306a36Sopenharmony_ci ispctrl_val |= bridge; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (input) { 44162306a36Sopenharmony_ci case CCDC_INPUT_PARALLEL: 44262306a36Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; 44362306a36Sopenharmony_ci ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; 44462306a36Sopenharmony_ci shift += parcfg->data_lane_shift; 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci case CCDC_INPUT_CSI2A: 44862306a36Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci case CCDC_INPUT_CCP2B: 45262306a36Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; 45362306a36Sopenharmony_ci break; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci case CCDC_INPUT_CSI2C: 45662306a36Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci default: 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 46662306a36Sopenharmony_ci} 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_civoid omap3isp_hist_dma_done(struct isp_device *isp) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci if (omap3isp_ccdc_busy(&isp->isp_ccdc) || 47162306a36Sopenharmony_ci omap3isp_stat_pcr_busy(&isp->isp_hist)) { 47262306a36Sopenharmony_ci /* Histogram cannot be enabled in this frame anymore */ 47362306a36Sopenharmony_ci atomic_set(&isp->isp_hist.buf_err, 1); 47462306a36Sopenharmony_ci dev_dbg(isp->dev, 47562306a36Sopenharmony_ci "hist: Out of synchronization with CCDC. Ignoring next buffer.\n"); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic inline void __maybe_unused isp_isr_dbg(struct isp_device *isp, 48062306a36Sopenharmony_ci u32 irqstatus) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci static const char *name[] = { 48362306a36Sopenharmony_ci "CSIA_IRQ", 48462306a36Sopenharmony_ci "res1", 48562306a36Sopenharmony_ci "res2", 48662306a36Sopenharmony_ci "CSIB_LCM_IRQ", 48762306a36Sopenharmony_ci "CSIB_IRQ", 48862306a36Sopenharmony_ci "res5", 48962306a36Sopenharmony_ci "res6", 49062306a36Sopenharmony_ci "res7", 49162306a36Sopenharmony_ci "CCDC_VD0_IRQ", 49262306a36Sopenharmony_ci "CCDC_VD1_IRQ", 49362306a36Sopenharmony_ci "CCDC_VD2_IRQ", 49462306a36Sopenharmony_ci "CCDC_ERR_IRQ", 49562306a36Sopenharmony_ci "H3A_AF_DONE_IRQ", 49662306a36Sopenharmony_ci "H3A_AWB_DONE_IRQ", 49762306a36Sopenharmony_ci "res14", 49862306a36Sopenharmony_ci "res15", 49962306a36Sopenharmony_ci "HIST_DONE_IRQ", 50062306a36Sopenharmony_ci "CCDC_LSC_DONE", 50162306a36Sopenharmony_ci "CCDC_LSC_PREFETCH_COMPLETED", 50262306a36Sopenharmony_ci "CCDC_LSC_PREFETCH_ERROR", 50362306a36Sopenharmony_ci "PRV_DONE_IRQ", 50462306a36Sopenharmony_ci "CBUFF_IRQ", 50562306a36Sopenharmony_ci "res22", 50662306a36Sopenharmony_ci "res23", 50762306a36Sopenharmony_ci "RSZ_DONE_IRQ", 50862306a36Sopenharmony_ci "OVF_IRQ", 50962306a36Sopenharmony_ci "res26", 51062306a36Sopenharmony_ci "res27", 51162306a36Sopenharmony_ci "MMU_ERR_IRQ", 51262306a36Sopenharmony_ci "OCP_ERR_IRQ", 51362306a36Sopenharmony_ci "SEC_ERR_IRQ", 51462306a36Sopenharmony_ci "HS_VS_IRQ", 51562306a36Sopenharmony_ci }; 51662306a36Sopenharmony_ci int i; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci dev_dbg(isp->dev, "ISP IRQ: "); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(name); i++) { 52162306a36Sopenharmony_ci if ((1 << i) & irqstatus) 52262306a36Sopenharmony_ci printk(KERN_CONT "%s ", name[i]); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci printk(KERN_CONT "\n"); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void isp_isr_sbl(struct isp_device *isp) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct device *dev = isp->dev; 53062306a36Sopenharmony_ci struct isp_pipeline *pipe; 53162306a36Sopenharmony_ci u32 sbl_pcr; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* 53462306a36Sopenharmony_ci * Handle shared buffer logic overflows for video buffers. 53562306a36Sopenharmony_ci * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); 53862306a36Sopenharmony_ci isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); 53962306a36Sopenharmony_ci sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (sbl_pcr) 54262306a36Sopenharmony_ci dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) { 54562306a36Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity); 54662306a36Sopenharmony_ci if (pipe != NULL) 54762306a36Sopenharmony_ci pipe->error = true; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) { 55162306a36Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity); 55262306a36Sopenharmony_ci if (pipe != NULL) 55362306a36Sopenharmony_ci pipe->error = true; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) { 55762306a36Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity); 55862306a36Sopenharmony_ci if (pipe != NULL) 55962306a36Sopenharmony_ci pipe->error = true; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { 56362306a36Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity); 56462306a36Sopenharmony_ci if (pipe != NULL) 56562306a36Sopenharmony_ci pipe->error = true; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF 56962306a36Sopenharmony_ci | ISPSBL_PCR_RSZ2_WBL_OVF 57062306a36Sopenharmony_ci | ISPSBL_PCR_RSZ3_WBL_OVF 57162306a36Sopenharmony_ci | ISPSBL_PCR_RSZ4_WBL_OVF)) { 57262306a36Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_res.subdev.entity); 57362306a36Sopenharmony_ci if (pipe != NULL) 57462306a36Sopenharmony_ci pipe->error = true; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) 57862306a36Sopenharmony_ci omap3isp_stat_sbl_overflow(&isp->isp_af); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) 58162306a36Sopenharmony_ci omap3isp_stat_sbl_overflow(&isp->isp_aewb); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/* 58562306a36Sopenharmony_ci * isp_isr - Interrupt Service Routine for Camera ISP module. 58662306a36Sopenharmony_ci * @irq: Not used currently. 58762306a36Sopenharmony_ci * @_isp: Pointer to the OMAP3 ISP device 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * Handles the corresponding callback if plugged in. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic irqreturn_t isp_isr(int irq, void *_isp) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | 59462306a36Sopenharmony_ci IRQ0STATUS_CCDC_LSC_DONE_IRQ | 59562306a36Sopenharmony_ci IRQ0STATUS_CCDC_VD0_IRQ | 59662306a36Sopenharmony_ci IRQ0STATUS_CCDC_VD1_IRQ | 59762306a36Sopenharmony_ci IRQ0STATUS_HS_VS_IRQ; 59862306a36Sopenharmony_ci struct isp_device *isp = _isp; 59962306a36Sopenharmony_ci u32 irqstatus; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 60262306a36Sopenharmony_ci isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci isp_isr_sbl(isp); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_CSIA_IRQ) 60762306a36Sopenharmony_ci omap3isp_csi2_isr(&isp->isp_csi2a); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_CSIB_IRQ) 61062306a36Sopenharmony_ci omap3isp_ccp2_isr(&isp->isp_ccp2); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { 61362306a36Sopenharmony_ci if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) 61462306a36Sopenharmony_ci omap3isp_preview_isr_frame_sync(&isp->isp_prev); 61562306a36Sopenharmony_ci if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) 61662306a36Sopenharmony_ci omap3isp_resizer_isr_frame_sync(&isp->isp_res); 61762306a36Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_aewb); 61862306a36Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_af); 61962306a36Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_hist); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (irqstatus & ccdc_events) 62362306a36Sopenharmony_ci omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { 62662306a36Sopenharmony_ci if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) 62762306a36Sopenharmony_ci omap3isp_resizer_isr_frame_sync(&isp->isp_res); 62862306a36Sopenharmony_ci omap3isp_preview_isr(&isp->isp_prev); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) 63262306a36Sopenharmony_ci omap3isp_resizer_isr(&isp->isp_res); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) 63562306a36Sopenharmony_ci omap3isp_stat_isr(&isp->isp_aewb); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) 63862306a36Sopenharmony_ci omap3isp_stat_isr(&isp->isp_af); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) 64162306a36Sopenharmony_ci omap3isp_stat_isr(&isp->isp_hist); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci omap3isp_flush(isp); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci#if defined(DEBUG) && defined(ISP_ISR_DEBUG) 64662306a36Sopenharmony_ci isp_isr_dbg(isp, irqstatus); 64762306a36Sopenharmony_ci#endif 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci return IRQ_HANDLED; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic const struct media_device_ops isp_media_ops = { 65362306a36Sopenharmony_ci .link_notify = v4l2_pipeline_link_notify, 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 65762306a36Sopenharmony_ci * Pipeline stream management 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/* 66162306a36Sopenharmony_ci * isp_pipeline_enable - Enable streaming on a pipeline 66262306a36Sopenharmony_ci * @pipe: ISP pipeline 66362306a36Sopenharmony_ci * @mode: Stream mode (single shot or continuous) 66462306a36Sopenharmony_ci * 66562306a36Sopenharmony_ci * Walk the entities chain starting at the pipeline output video node and start 66662306a36Sopenharmony_ci * all modules in the chain in the given mode. 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * Return 0 if successful, or the return value of the failed video::s_stream 66962306a36Sopenharmony_ci * operation otherwise. 67062306a36Sopenharmony_ci */ 67162306a36Sopenharmony_cistatic int isp_pipeline_enable(struct isp_pipeline *pipe, 67262306a36Sopenharmony_ci enum isp_pipeline_stream_state mode) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct isp_device *isp = pipe->output->isp; 67562306a36Sopenharmony_ci struct media_entity *entity; 67662306a36Sopenharmony_ci struct media_pad *pad; 67762306a36Sopenharmony_ci struct v4l2_subdev *subdev; 67862306a36Sopenharmony_ci unsigned long flags; 67962306a36Sopenharmony_ci int ret; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Refuse to start streaming if an entity included in the pipeline has 68262306a36Sopenharmony_ci * crashed. This check must be performed before the loop below to avoid 68362306a36Sopenharmony_ci * starting entities if the pipeline won't start anyway (those entities 68462306a36Sopenharmony_ci * would then likely fail to stop, making the problem worse). 68562306a36Sopenharmony_ci */ 68662306a36Sopenharmony_ci if (media_entity_enum_intersects(&pipe->ent_enum, &isp->crashed)) 68762306a36Sopenharmony_ci return -EIO; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 69062306a36Sopenharmony_ci pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); 69162306a36Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci pipe->do_propagation = false; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci mutex_lock(&isp->media_dev.graph_mutex); 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci entity = &pipe->output->video.entity; 69862306a36Sopenharmony_ci while (1) { 69962306a36Sopenharmony_ci pad = &entity->pads[0]; 70062306a36Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 70162306a36Sopenharmony_ci break; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci pad = media_pad_remote_pad_first(pad); 70462306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci entity = pad->entity; 70862306a36Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, mode); 71162306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 71262306a36Sopenharmony_ci mutex_unlock(&isp->media_dev.graph_mutex); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (subdev == &isp->isp_ccdc.subdev) { 71762306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_aewb.subdev, video, 71862306a36Sopenharmony_ci s_stream, mode); 71962306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_af.subdev, video, 72062306a36Sopenharmony_ci s_stream, mode); 72162306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_hist.subdev, video, 72262306a36Sopenharmony_ci s_stream, mode); 72362306a36Sopenharmony_ci pipe->do_propagation = true; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci /* Stop at the first external sub-device. */ 72762306a36Sopenharmony_ci if (subdev->dev != isp->dev) 72862306a36Sopenharmony_ci break; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci mutex_unlock(&isp->media_dev.graph_mutex); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int isp_pipeline_wait_resizer(struct isp_device *isp) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci return omap3isp_resizer_busy(&isp->isp_res); 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int isp_pipeline_wait_preview(struct isp_device *isp) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci return omap3isp_preview_busy(&isp->isp_prev); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic int isp_pipeline_wait_ccdc(struct isp_device *isp) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci return omap3isp_stat_busy(&isp->isp_af) 74962306a36Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_aewb) 75062306a36Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_hist) 75162306a36Sopenharmony_ci || omap3isp_ccdc_busy(&isp->isp_ccdc); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic int isp_pipeline_wait(struct isp_device *isp, 75762306a36Sopenharmony_ci int(*busy)(struct isp_device *isp)) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci while (!time_after(jiffies, timeout)) { 76262306a36Sopenharmony_ci if (!busy(isp)) 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return 1; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * isp_pipeline_disable - Disable streaming on a pipeline 77162306a36Sopenharmony_ci * @pipe: ISP pipeline 77262306a36Sopenharmony_ci * 77362306a36Sopenharmony_ci * Walk the entities chain starting at the pipeline output video node and stop 77462306a36Sopenharmony_ci * all modules in the chain. Wait synchronously for the modules to be stopped if 77562306a36Sopenharmony_ci * necessary. 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module 77862306a36Sopenharmony_ci * can't be stopped (in which case a software reset of the ISP is probably 77962306a36Sopenharmony_ci * necessary). 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_cistatic int isp_pipeline_disable(struct isp_pipeline *pipe) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct isp_device *isp = pipe->output->isp; 78462306a36Sopenharmony_ci struct media_entity *entity; 78562306a36Sopenharmony_ci struct media_pad *pad; 78662306a36Sopenharmony_ci struct v4l2_subdev *subdev; 78762306a36Sopenharmony_ci int failure = 0; 78862306a36Sopenharmony_ci int ret; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* 79162306a36Sopenharmony_ci * We need to stop all the modules after CCDC first or they'll 79262306a36Sopenharmony_ci * never stop since they may not get a full frame from CCDC. 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci entity = &pipe->output->video.entity; 79562306a36Sopenharmony_ci while (1) { 79662306a36Sopenharmony_ci pad = &entity->pads[0]; 79762306a36Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 79862306a36Sopenharmony_ci break; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci pad = media_pad_remote_pad_first(pad); 80162306a36Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci entity = pad->entity; 80562306a36Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (subdev == &isp->isp_ccdc.subdev) { 80862306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_aewb.subdev, 80962306a36Sopenharmony_ci video, s_stream, 0); 81062306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_af.subdev, 81162306a36Sopenharmony_ci video, s_stream, 0); 81262306a36Sopenharmony_ci v4l2_subdev_call(&isp->isp_hist.subdev, 81362306a36Sopenharmony_ci video, s_stream, 0); 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, 0); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Stop at the first external sub-device. */ 81962306a36Sopenharmony_ci if (subdev->dev != isp->dev) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (subdev == &isp->isp_res.subdev) 82362306a36Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer); 82462306a36Sopenharmony_ci else if (subdev == &isp->isp_prev.subdev) 82562306a36Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_preview); 82662306a36Sopenharmony_ci else if (subdev == &isp->isp_ccdc.subdev) 82762306a36Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* Handle stop failures. An entity that fails to stop can 83062306a36Sopenharmony_ci * usually just be restarted. Flag the stop failure nonetheless 83162306a36Sopenharmony_ci * to trigger an ISP reset the next time the device is released, 83262306a36Sopenharmony_ci * just in case. 83362306a36Sopenharmony_ci * 83462306a36Sopenharmony_ci * The preview engine is a special case. A failure to stop can 83562306a36Sopenharmony_ci * mean a hardware crash. When that happens the preview engine 83662306a36Sopenharmony_ci * won't respond to read/write operations on the L4 bus anymore, 83762306a36Sopenharmony_ci * resulting in a bus fault and a kernel oops next time it gets 83862306a36Sopenharmony_ci * accessed. Mark it as crashed to prevent pipelines including 83962306a36Sopenharmony_ci * it from being started. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci if (ret) { 84262306a36Sopenharmony_ci dev_info(isp->dev, "Unable to stop %s\n", subdev->name); 84362306a36Sopenharmony_ci isp->stop_failure = true; 84462306a36Sopenharmony_ci if (subdev == &isp->isp_prev.subdev) 84562306a36Sopenharmony_ci media_entity_enum_set(&isp->crashed, 84662306a36Sopenharmony_ci &subdev->entity); 84762306a36Sopenharmony_ci failure = -ETIMEDOUT; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return failure; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* 85562306a36Sopenharmony_ci * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline 85662306a36Sopenharmony_ci * @pipe: ISP pipeline 85762306a36Sopenharmony_ci * @state: Stream state (stopped, single shot or continuous) 85862306a36Sopenharmony_ci * 85962306a36Sopenharmony_ci * Set the pipeline to the given stream state. Pipelines can be started in 86062306a36Sopenharmony_ci * single-shot or continuous mode. 86162306a36Sopenharmony_ci * 86262306a36Sopenharmony_ci * Return 0 if successful, or the return value of the failed video::s_stream 86362306a36Sopenharmony_ci * operation otherwise. The pipeline state is not updated when the operation 86462306a36Sopenharmony_ci * fails, except when stopping the pipeline. 86562306a36Sopenharmony_ci */ 86662306a36Sopenharmony_ciint omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, 86762306a36Sopenharmony_ci enum isp_pipeline_stream_state state) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int ret; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (state == ISP_PIPELINE_STREAM_STOPPED) 87262306a36Sopenharmony_ci ret = isp_pipeline_disable(pipe); 87362306a36Sopenharmony_ci else 87462306a36Sopenharmony_ci ret = isp_pipeline_enable(pipe, state); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED) 87762306a36Sopenharmony_ci pipe->stream_state = state; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return ret; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/* 88362306a36Sopenharmony_ci * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline 88462306a36Sopenharmony_ci * @pipe: ISP pipeline 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * Cancelling a stream mark all buffers on all video nodes in the pipeline as 88762306a36Sopenharmony_ci * erroneous and makes sure no new buffer can be queued. This function is called 88862306a36Sopenharmony_ci * when a fatal error that prevents any further operation on the pipeline 88962306a36Sopenharmony_ci * occurs. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_civoid omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci if (pipe->input) 89462306a36Sopenharmony_ci omap3isp_video_cancel_stream(pipe->input); 89562306a36Sopenharmony_ci if (pipe->output) 89662306a36Sopenharmony_ci omap3isp_video_cancel_stream(pipe->output); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/* 90062306a36Sopenharmony_ci * isp_pipeline_resume - Resume streaming on a pipeline 90162306a36Sopenharmony_ci * @pipe: ISP pipeline 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * Resume video output and input and re-enable pipeline. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_cistatic void isp_pipeline_resume(struct isp_pipeline *pipe) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci omap3isp_video_resume(pipe->output, !singleshot); 91062306a36Sopenharmony_ci if (singleshot) 91162306a36Sopenharmony_ci omap3isp_video_resume(pipe->input, 0); 91262306a36Sopenharmony_ci isp_pipeline_enable(pipe, pipe->stream_state); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/* 91662306a36Sopenharmony_ci * isp_pipeline_suspend - Suspend streaming on a pipeline 91762306a36Sopenharmony_ci * @pipe: ISP pipeline 91862306a36Sopenharmony_ci * 91962306a36Sopenharmony_ci * Suspend pipeline. 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_cistatic void isp_pipeline_suspend(struct isp_pipeline *pipe) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci isp_pipeline_disable(pipe); 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci/* 92762306a36Sopenharmony_ci * isp_pipeline_is_last - Verify if entity has an enabled link to the output 92862306a36Sopenharmony_ci * video node 92962306a36Sopenharmony_ci * @me: ISP module's media entity 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * Returns 1 if the entity has an enabled link to the output video node or 0 93262306a36Sopenharmony_ci * otherwise. It's true only while pipeline can have no more than one output 93362306a36Sopenharmony_ci * node. 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_cistatic int isp_pipeline_is_last(struct media_entity *me) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct isp_pipeline *pipe; 93862306a36Sopenharmony_ci struct media_pad *pad; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci pipe = to_isp_pipeline(me); 94162306a36Sopenharmony_ci if (!pipe || pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) 94262306a36Sopenharmony_ci return 0; 94362306a36Sopenharmony_ci pad = media_pad_remote_pad_first(&pipe->output->pad); 94462306a36Sopenharmony_ci return pad->entity == me; 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci/* 94862306a36Sopenharmony_ci * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module 94962306a36Sopenharmony_ci * @me: ISP module's media entity 95062306a36Sopenharmony_ci * 95162306a36Sopenharmony_ci * Suspend the whole pipeline if module's entity has an enabled link to the 95262306a36Sopenharmony_ci * output video node. It works only while pipeline can have no more than one 95362306a36Sopenharmony_ci * output node. 95462306a36Sopenharmony_ci */ 95562306a36Sopenharmony_cistatic void isp_suspend_module_pipeline(struct media_entity *me) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci if (isp_pipeline_is_last(me)) 95862306a36Sopenharmony_ci isp_pipeline_suspend(to_isp_pipeline(me)); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* 96262306a36Sopenharmony_ci * isp_resume_module_pipeline - Resume pipeline to which belongs the module 96362306a36Sopenharmony_ci * @me: ISP module's media entity 96462306a36Sopenharmony_ci * 96562306a36Sopenharmony_ci * Resume the whole pipeline if module's entity has an enabled link to the 96662306a36Sopenharmony_ci * output video node. It works only while pipeline can have no more than one 96762306a36Sopenharmony_ci * output node. 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_cistatic void isp_resume_module_pipeline(struct media_entity *me) 97062306a36Sopenharmony_ci{ 97162306a36Sopenharmony_ci if (isp_pipeline_is_last(me)) 97262306a36Sopenharmony_ci isp_pipeline_resume(to_isp_pipeline(me)); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci/* 97662306a36Sopenharmony_ci * isp_suspend_modules - Suspend ISP submodules. 97762306a36Sopenharmony_ci * @isp: OMAP3 ISP device 97862306a36Sopenharmony_ci * 97962306a36Sopenharmony_ci * Returns 0 if suspend left in idle state all the submodules properly, 98062306a36Sopenharmony_ci * or returns 1 if a general Reset is required to suspend the submodules. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_cistatic int __maybe_unused isp_suspend_modules(struct isp_device *isp) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci unsigned long timeout; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_aewb); 98762306a36Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_af); 98862306a36Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_hist); 98962306a36Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); 99062306a36Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); 99162306a36Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); 99262306a36Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); 99362306a36Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci timeout = jiffies + ISP_STOP_TIMEOUT; 99662306a36Sopenharmony_ci while (omap3isp_stat_busy(&isp->isp_af) 99762306a36Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_aewb) 99862306a36Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_hist) 99962306a36Sopenharmony_ci || omap3isp_preview_busy(&isp->isp_prev) 100062306a36Sopenharmony_ci || omap3isp_resizer_busy(&isp->isp_res) 100162306a36Sopenharmony_ci || omap3isp_ccdc_busy(&isp->isp_ccdc)) { 100262306a36Sopenharmony_ci if (time_after(jiffies, timeout)) { 100362306a36Sopenharmony_ci dev_info(isp->dev, "can't stop modules.\n"); 100462306a36Sopenharmony_ci return 1; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci msleep(1); 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci return 0; 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/* 101362306a36Sopenharmony_ci * isp_resume_modules - Resume ISP submodules. 101462306a36Sopenharmony_ci * @isp: OMAP3 ISP device 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_cistatic void __maybe_unused isp_resume_modules(struct isp_device *isp) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci omap3isp_stat_resume(&isp->isp_aewb); 101962306a36Sopenharmony_ci omap3isp_stat_resume(&isp->isp_af); 102062306a36Sopenharmony_ci omap3isp_stat_resume(&isp->isp_hist); 102162306a36Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_res.subdev.entity); 102262306a36Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); 102362306a36Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); 102462306a36Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); 102562306a36Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci/* 102962306a36Sopenharmony_ci * isp_reset - Reset ISP with a timeout wait for idle. 103062306a36Sopenharmony_ci * @isp: OMAP3 ISP device 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_cistatic int isp_reset(struct isp_device *isp) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci unsigned long timeout = 0; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci isp_reg_writel(isp, 103762306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) 103862306a36Sopenharmony_ci | ISP_SYSCONFIG_SOFTRESET, 103962306a36Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); 104062306a36Sopenharmony_ci while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, 104162306a36Sopenharmony_ci ISP_SYSSTATUS) & 0x1)) { 104262306a36Sopenharmony_ci if (timeout++ > 10000) { 104362306a36Sopenharmony_ci dev_alert(isp->dev, "cannot reset ISP\n"); 104462306a36Sopenharmony_ci return -ETIMEDOUT; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci udelay(1); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci isp->stop_failure = false; 105062306a36Sopenharmony_ci media_entity_enum_zero(&isp->crashed); 105162306a36Sopenharmony_ci return 0; 105262306a36Sopenharmony_ci} 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci/* 105562306a36Sopenharmony_ci * isp_save_context - Saves the values of the ISP module registers. 105662306a36Sopenharmony_ci * @isp: OMAP3 ISP device 105762306a36Sopenharmony_ci * @reg_list: Structure containing pairs of register address and value to 105862306a36Sopenharmony_ci * modify on OMAP. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_cistatic void 106162306a36Sopenharmony_ciisp_save_context(struct isp_device *isp, struct isp_reg *reg_list) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci struct isp_reg *next = reg_list; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci for (; next->reg != ISP_TOK_TERM; next++) 106662306a36Sopenharmony_ci next->val = isp_reg_readl(isp, next->mmio_range, next->reg); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci/* 107062306a36Sopenharmony_ci * isp_restore_context - Restores the values of the ISP module registers. 107162306a36Sopenharmony_ci * @isp: OMAP3 ISP device 107262306a36Sopenharmony_ci * @reg_list: Structure containing pairs of register address and value to 107362306a36Sopenharmony_ci * modify on OMAP. 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_cistatic void 107662306a36Sopenharmony_ciisp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci struct isp_reg *next = reg_list; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci for (; next->reg != ISP_TOK_TERM; next++) 108162306a36Sopenharmony_ci isp_reg_writel(isp, next->val, next->mmio_range, next->reg); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/* 108562306a36Sopenharmony_ci * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. 108662306a36Sopenharmony_ci * @isp: OMAP3 ISP device 108762306a36Sopenharmony_ci * 108862306a36Sopenharmony_ci * Routine for saving the context of each module in the ISP. 108962306a36Sopenharmony_ci * CCDC, HIST, H3A, PREV, RESZ and MMU. 109062306a36Sopenharmony_ci */ 109162306a36Sopenharmony_cistatic void isp_save_ctx(struct isp_device *isp) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci isp_save_context(isp, isp_reg_list); 109462306a36Sopenharmony_ci omap_iommu_save_ctx(isp->dev); 109562306a36Sopenharmony_ci} 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci/* 109862306a36Sopenharmony_ci * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. 109962306a36Sopenharmony_ci * @isp: OMAP3 ISP device 110062306a36Sopenharmony_ci * 110162306a36Sopenharmony_ci * Routine for restoring the context of each module in the ISP. 110262306a36Sopenharmony_ci * CCDC, HIST, H3A, PREV, RESZ and MMU. 110362306a36Sopenharmony_ci */ 110462306a36Sopenharmony_cistatic void isp_restore_ctx(struct isp_device *isp) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci isp_restore_context(isp, isp_reg_list); 110762306a36Sopenharmony_ci omap_iommu_restore_ctx(isp->dev); 110862306a36Sopenharmony_ci omap3isp_ccdc_restore_context(isp); 110962306a36Sopenharmony_ci omap3isp_preview_restore_context(isp); 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 111362306a36Sopenharmony_ci * SBL resources management 111462306a36Sopenharmony_ci */ 111562306a36Sopenharmony_ci#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ 111662306a36Sopenharmony_ci OMAP3_ISP_SBL_CCDC_LSC_READ | \ 111762306a36Sopenharmony_ci OMAP3_ISP_SBL_PREVIEW_READ | \ 111862306a36Sopenharmony_ci OMAP3_ISP_SBL_RESIZER_READ) 111962306a36Sopenharmony_ci#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ 112062306a36Sopenharmony_ci OMAP3_ISP_SBL_CSI2A_WRITE | \ 112162306a36Sopenharmony_ci OMAP3_ISP_SBL_CSI2C_WRITE | \ 112262306a36Sopenharmony_ci OMAP3_ISP_SBL_CCDC_WRITE | \ 112362306a36Sopenharmony_ci OMAP3_ISP_SBL_PREVIEW_WRITE) 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_civoid omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci u32 sbl = 0; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci isp->sbl_resources |= res; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) 113262306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTA; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) 113562306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTB; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) 113862306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_WPORTC; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) 114162306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_WR0_RAM_EN; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) 114462306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_WR1_RAM_EN; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_READ) 114762306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_RD_RAM_EN; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_civoid omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci u32 sbl = 0; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci isp->sbl_resources &= ~res; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) 115962306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTA; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) 116262306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTB; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) 116562306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_WPORTC; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) 116862306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_WR0_RAM_EN; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) 117162306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_WR1_RAM_EN; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) 117462306a36Sopenharmony_ci sbl |= ISPCTRL_SBL_RD_RAM_EN; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/* 118062306a36Sopenharmony_ci * isp_module_sync_idle - Helper to sync module with its idle state 118162306a36Sopenharmony_ci * @me: ISP submodule's media entity 118262306a36Sopenharmony_ci * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization 118362306a36Sopenharmony_ci * @stopping: flag which tells module wants to stop 118462306a36Sopenharmony_ci * 118562306a36Sopenharmony_ci * This function checks if ISP submodule needs to wait for next interrupt. If 118662306a36Sopenharmony_ci * yes, makes the caller to sleep while waiting for such event. 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_ciint omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, 118962306a36Sopenharmony_ci atomic_t *stopping) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(me); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || 119462306a36Sopenharmony_ci (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && 119562306a36Sopenharmony_ci !isp_pipeline_ready(pipe))) 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* 119962306a36Sopenharmony_ci * atomic_set() doesn't include memory barrier on ARM platform for SMP 120062306a36Sopenharmony_ci * scenario. We'll call it here to avoid race conditions. 120162306a36Sopenharmony_ci */ 120262306a36Sopenharmony_ci atomic_set(stopping, 1); 120362306a36Sopenharmony_ci smp_mb(); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * If module is the last one, it's writing to memory. In this case, 120762306a36Sopenharmony_ci * it's necessary to check if the module is already paused due to 120862306a36Sopenharmony_ci * DMA queue underrun or if it has to wait for next interrupt to be 120962306a36Sopenharmony_ci * idle. 121062306a36Sopenharmony_ci * If it isn't the last one, the function won't sleep but *stopping 121162306a36Sopenharmony_ci * will still be set to warn next submodule caller's interrupt the 121262306a36Sopenharmony_ci * module wants to be idle. 121362306a36Sopenharmony_ci */ 121462306a36Sopenharmony_ci if (isp_pipeline_is_last(me)) { 121562306a36Sopenharmony_ci struct isp_video *video = pipe->output; 121662306a36Sopenharmony_ci unsigned long flags; 121762306a36Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 121862306a36Sopenharmony_ci if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { 121962306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 122062306a36Sopenharmony_ci atomic_set(stopping, 0); 122162306a36Sopenharmony_ci smp_mb(); 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 122562306a36Sopenharmony_ci if (!wait_event_timeout(*wait, !atomic_read(stopping), 122662306a36Sopenharmony_ci msecs_to_jiffies(1000))) { 122762306a36Sopenharmony_ci atomic_set(stopping, 0); 122862306a36Sopenharmony_ci smp_mb(); 122962306a36Sopenharmony_ci return -ETIMEDOUT; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci } 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return 0; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* 123762306a36Sopenharmony_ci * omap3isp_module_sync_is_stopping - Helper to verify if module was stopping 123862306a36Sopenharmony_ci * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization 123962306a36Sopenharmony_ci * @stopping: flag which tells module wants to stop 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * This function checks if ISP submodule was stopping. In case of yes, it 124262306a36Sopenharmony_ci * notices the caller by setting stopping to 0 and waking up the wait queue. 124362306a36Sopenharmony_ci * Returns 1 if it was stopping or 0 otherwise. 124462306a36Sopenharmony_ci */ 124562306a36Sopenharmony_ciint omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, 124662306a36Sopenharmony_ci atomic_t *stopping) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci if (atomic_cmpxchg(stopping, 1, 0)) { 124962306a36Sopenharmony_ci wake_up(wait); 125062306a36Sopenharmony_ci return 1; 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci return 0; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 125762306a36Sopenharmony_ci * Clock management 125862306a36Sopenharmony_ci */ 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ 126162306a36Sopenharmony_ci ISPCTRL_HIST_CLK_EN | \ 126262306a36Sopenharmony_ci ISPCTRL_RSZ_CLK_EN | \ 126362306a36Sopenharmony_ci (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ 126462306a36Sopenharmony_ci (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic void __isp_subclk_update(struct isp_device *isp) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci u32 clk = 0; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* AEWB and AF share the same clock. */ 127162306a36Sopenharmony_ci if (isp->subclk_resources & 127262306a36Sopenharmony_ci (OMAP3_ISP_SUBCLK_AEWB | OMAP3_ISP_SUBCLK_AF)) 127362306a36Sopenharmony_ci clk |= ISPCTRL_H3A_CLK_EN; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) 127662306a36Sopenharmony_ci clk |= ISPCTRL_HIST_CLK_EN; 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) 127962306a36Sopenharmony_ci clk |= ISPCTRL_RSZ_CLK_EN; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* NOTE: For CCDC & Preview submodules, we need to affect internal 128262306a36Sopenharmony_ci * RAM as well. 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) 128562306a36Sopenharmony_ci clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) 128862306a36Sopenharmony_ci clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 129162306a36Sopenharmony_ci ISPCTRL_CLKS_MASK, clk); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_civoid omap3isp_subclk_enable(struct isp_device *isp, 129562306a36Sopenharmony_ci enum isp_subclk_resource res) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci isp->subclk_resources |= res; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci __isp_subclk_update(isp); 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_civoid omap3isp_subclk_disable(struct isp_device *isp, 130362306a36Sopenharmony_ci enum isp_subclk_resource res) 130462306a36Sopenharmony_ci{ 130562306a36Sopenharmony_ci isp->subclk_resources &= ~res; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci __isp_subclk_update(isp); 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci/* 131162306a36Sopenharmony_ci * isp_enable_clocks - Enable ISP clocks 131262306a36Sopenharmony_ci * @isp: OMAP3 ISP device 131362306a36Sopenharmony_ci * 131462306a36Sopenharmony_ci * Return 0 if successful, or clk_prepare_enable return value if any of them 131562306a36Sopenharmony_ci * fails. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_cistatic int isp_enable_clocks(struct isp_device *isp) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci int r; 132062306a36Sopenharmony_ci unsigned long rate; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); 132362306a36Sopenharmony_ci if (r) { 132462306a36Sopenharmony_ci dev_err(isp->dev, "failed to enable cam_ick clock\n"); 132562306a36Sopenharmony_ci goto out_clk_enable_ick; 132662306a36Sopenharmony_ci } 132762306a36Sopenharmony_ci r = clk_set_rate(isp->clock[ISP_CLK_CAM_MCLK], CM_CAM_MCLK_HZ); 132862306a36Sopenharmony_ci if (r) { 132962306a36Sopenharmony_ci dev_err(isp->dev, "clk_set_rate for cam_mclk failed\n"); 133062306a36Sopenharmony_ci goto out_clk_enable_mclk; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]); 133362306a36Sopenharmony_ci if (r) { 133462306a36Sopenharmony_ci dev_err(isp->dev, "failed to enable cam_mclk clock\n"); 133562306a36Sopenharmony_ci goto out_clk_enable_mclk; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); 133862306a36Sopenharmony_ci if (rate != CM_CAM_MCLK_HZ) 133962306a36Sopenharmony_ci dev_warn(isp->dev, "unexpected cam_mclk rate:\n" 134062306a36Sopenharmony_ci " expected : %d\n" 134162306a36Sopenharmony_ci " actual : %ld\n", CM_CAM_MCLK_HZ, rate); 134262306a36Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CSI2_FCK]); 134362306a36Sopenharmony_ci if (r) { 134462306a36Sopenharmony_ci dev_err(isp->dev, "failed to enable csi2_fck clock\n"); 134562306a36Sopenharmony_ci goto out_clk_enable_csi2_fclk; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci return 0; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ciout_clk_enable_csi2_fclk: 135062306a36Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); 135162306a36Sopenharmony_ciout_clk_enable_mclk: 135262306a36Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); 135362306a36Sopenharmony_ciout_clk_enable_ick: 135462306a36Sopenharmony_ci return r; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci/* 135862306a36Sopenharmony_ci * isp_disable_clocks - Disable ISP clocks 135962306a36Sopenharmony_ci * @isp: OMAP3 ISP device 136062306a36Sopenharmony_ci */ 136162306a36Sopenharmony_cistatic void isp_disable_clocks(struct isp_device *isp) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); 136462306a36Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); 136562306a36Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CSI2_FCK]); 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic const char *isp_clocks[] = { 136962306a36Sopenharmony_ci "cam_ick", 137062306a36Sopenharmony_ci "cam_mclk", 137162306a36Sopenharmony_ci "csi2_96m_fck", 137262306a36Sopenharmony_ci "l3_ick", 137362306a36Sopenharmony_ci}; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic int isp_get_clocks(struct isp_device *isp) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci struct clk *clk; 137862306a36Sopenharmony_ci unsigned int i; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { 138162306a36Sopenharmony_ci clk = devm_clk_get(isp->dev, isp_clocks[i]); 138262306a36Sopenharmony_ci if (IS_ERR(clk)) { 138362306a36Sopenharmony_ci dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); 138462306a36Sopenharmony_ci return PTR_ERR(clk); 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci isp->clock[i] = clk; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci return 0; 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci/* 139462306a36Sopenharmony_ci * omap3isp_get - Acquire the ISP resource. 139562306a36Sopenharmony_ci * 139662306a36Sopenharmony_ci * Initializes the clocks for the first acquire. 139762306a36Sopenharmony_ci * 139862306a36Sopenharmony_ci * Increment the reference count on the ISP. If the first reference is taken, 139962306a36Sopenharmony_ci * enable clocks and power-up all submodules. 140062306a36Sopenharmony_ci * 140162306a36Sopenharmony_ci * Return a pointer to the ISP device structure, or NULL if an error occurred. 140262306a36Sopenharmony_ci */ 140362306a36Sopenharmony_cistatic struct isp_device *__omap3isp_get(struct isp_device *isp, bool irq) 140462306a36Sopenharmony_ci{ 140562306a36Sopenharmony_ci struct isp_device *__isp = isp; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (isp == NULL) 140862306a36Sopenharmony_ci return NULL; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci mutex_lock(&isp->isp_mutex); 141162306a36Sopenharmony_ci if (isp->ref_count > 0) 141262306a36Sopenharmony_ci goto out; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci if (isp_enable_clocks(isp) < 0) { 141562306a36Sopenharmony_ci __isp = NULL; 141662306a36Sopenharmony_ci goto out; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci /* We don't want to restore context before saving it! */ 142062306a36Sopenharmony_ci if (isp->has_context) 142162306a36Sopenharmony_ci isp_restore_ctx(isp); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (irq) 142462306a36Sopenharmony_ci isp_enable_interrupts(isp); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ciout: 142762306a36Sopenharmony_ci if (__isp != NULL) 142862306a36Sopenharmony_ci isp->ref_count++; 142962306a36Sopenharmony_ci mutex_unlock(&isp->isp_mutex); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci return __isp; 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistruct isp_device *omap3isp_get(struct isp_device *isp) 143562306a36Sopenharmony_ci{ 143662306a36Sopenharmony_ci return __omap3isp_get(isp, true); 143762306a36Sopenharmony_ci} 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci/* 144062306a36Sopenharmony_ci * omap3isp_put - Release the ISP 144162306a36Sopenharmony_ci * 144262306a36Sopenharmony_ci * Decrement the reference count on the ISP. If the last reference is released, 144362306a36Sopenharmony_ci * power-down all submodules, disable clocks and free temporary buffers. 144462306a36Sopenharmony_ci */ 144562306a36Sopenharmony_cistatic void __omap3isp_put(struct isp_device *isp, bool save_ctx) 144662306a36Sopenharmony_ci{ 144762306a36Sopenharmony_ci if (isp == NULL) 144862306a36Sopenharmony_ci return; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci mutex_lock(&isp->isp_mutex); 145162306a36Sopenharmony_ci BUG_ON(isp->ref_count == 0); 145262306a36Sopenharmony_ci if (--isp->ref_count == 0) { 145362306a36Sopenharmony_ci isp_disable_interrupts(isp); 145462306a36Sopenharmony_ci if (save_ctx) { 145562306a36Sopenharmony_ci isp_save_ctx(isp); 145662306a36Sopenharmony_ci isp->has_context = 1; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci /* Reset the ISP if an entity has failed to stop. This is the 145962306a36Sopenharmony_ci * only way to recover from such conditions. 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_ci if (!media_entity_enum_empty(&isp->crashed) || 146262306a36Sopenharmony_ci isp->stop_failure) 146362306a36Sopenharmony_ci isp_reset(isp); 146462306a36Sopenharmony_ci isp_disable_clocks(isp); 146562306a36Sopenharmony_ci } 146662306a36Sopenharmony_ci mutex_unlock(&isp->isp_mutex); 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_civoid omap3isp_put(struct isp_device *isp) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci __omap3isp_put(isp, true); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci/* -------------------------------------------------------------------------- 147562306a36Sopenharmony_ci * Platform device driver 147662306a36Sopenharmony_ci */ 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci/* 147962306a36Sopenharmony_ci * omap3isp_print_status - Prints the values of the ISP Control Module registers 148062306a36Sopenharmony_ci * @isp: OMAP3 ISP device 148162306a36Sopenharmony_ci */ 148262306a36Sopenharmony_ci#define ISP_PRINT_REGISTER(isp, name)\ 148362306a36Sopenharmony_ci dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ 148462306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) 148562306a36Sopenharmony_ci#define SBL_PRINT_REGISTER(isp, name)\ 148662306a36Sopenharmony_ci dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ 148762306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_civoid omap3isp_print_status(struct isp_device *isp) 149062306a36Sopenharmony_ci{ 149162306a36Sopenharmony_ci dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, SYSCONFIG); 149462306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, SYSSTATUS); 149562306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, IRQ0ENABLE); 149662306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, IRQ0STATUS); 149762306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); 149862306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); 149962306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, CTRL); 150062306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_CTRL); 150162306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_FRAME); 150262306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); 150362306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); 150462306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); 150562306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); 150662306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); 150762306a36Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci SBL_PRINT_REGISTER(isp, PCR); 151062306a36Sopenharmony_ci SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci dev_dbg(isp->dev, "--------------------------------------------\n"); 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci#ifdef CONFIG_PM 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci/* 151862306a36Sopenharmony_ci * Power management support. 151962306a36Sopenharmony_ci * 152062306a36Sopenharmony_ci * As the ISP can't properly handle an input video stream interruption on a non 152162306a36Sopenharmony_ci * frame boundary, the ISP pipelines need to be stopped before sensors get 152262306a36Sopenharmony_ci * suspended. However, as suspending the sensors can require a running clock, 152362306a36Sopenharmony_ci * which can be provided by the ISP, the ISP can't be completely suspended 152462306a36Sopenharmony_ci * before the sensor. 152562306a36Sopenharmony_ci * 152662306a36Sopenharmony_ci * To solve this problem power management support is split into prepare/complete 152762306a36Sopenharmony_ci * and suspend/resume operations. The pipelines are stopped in prepare() and the 152862306a36Sopenharmony_ci * ISP clocks get disabled in suspend(). Similarly, the clocks are re-enabled in 152962306a36Sopenharmony_ci * resume(), and the pipelines are restarted in complete(). 153062306a36Sopenharmony_ci * 153162306a36Sopenharmony_ci * TODO: PM dependencies between the ISP and sensors are not modelled explicitly 153262306a36Sopenharmony_ci * yet. 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_cistatic int isp_pm_prepare(struct device *dev) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 153762306a36Sopenharmony_ci int reset; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci WARN_ON(mutex_is_locked(&isp->isp_mutex)); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci if (isp->ref_count == 0) 154262306a36Sopenharmony_ci return 0; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci reset = isp_suspend_modules(isp); 154562306a36Sopenharmony_ci isp_disable_interrupts(isp); 154662306a36Sopenharmony_ci isp_save_ctx(isp); 154762306a36Sopenharmony_ci if (reset) 154862306a36Sopenharmony_ci isp_reset(isp); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci return 0; 155162306a36Sopenharmony_ci} 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_cistatic int isp_pm_suspend(struct device *dev) 155462306a36Sopenharmony_ci{ 155562306a36Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci WARN_ON(mutex_is_locked(&isp->isp_mutex)); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (isp->ref_count) 156062306a36Sopenharmony_ci isp_disable_clocks(isp); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return 0; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic int isp_pm_resume(struct device *dev) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (isp->ref_count == 0) 157062306a36Sopenharmony_ci return 0; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return isp_enable_clocks(isp); 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic void isp_pm_complete(struct device *dev) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (isp->ref_count == 0) 158062306a36Sopenharmony_ci return; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci isp_restore_ctx(isp); 158362306a36Sopenharmony_ci isp_enable_interrupts(isp); 158462306a36Sopenharmony_ci isp_resume_modules(isp); 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci#else 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci#define isp_pm_prepare NULL 159062306a36Sopenharmony_ci#define isp_pm_suspend NULL 159162306a36Sopenharmony_ci#define isp_pm_resume NULL 159262306a36Sopenharmony_ci#define isp_pm_complete NULL 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci#endif /* CONFIG_PM */ 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic void isp_unregister_entities(struct isp_device *isp) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci media_device_unregister(&isp->media_dev); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci omap3isp_csi2_unregister_entities(&isp->isp_csi2a); 160162306a36Sopenharmony_ci omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); 160262306a36Sopenharmony_ci omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); 160362306a36Sopenharmony_ci omap3isp_preview_unregister_entities(&isp->isp_prev); 160462306a36Sopenharmony_ci omap3isp_resizer_unregister_entities(&isp->isp_res); 160562306a36Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_aewb); 160662306a36Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_af); 160762306a36Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_hist); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci v4l2_device_unregister(&isp->v4l2_dev); 161062306a36Sopenharmony_ci media_device_cleanup(&isp->media_dev); 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic int isp_link_entity( 161462306a36Sopenharmony_ci struct isp_device *isp, struct media_entity *entity, 161562306a36Sopenharmony_ci enum isp_interface_type interface) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct media_entity *input; 161862306a36Sopenharmony_ci unsigned int flags; 161962306a36Sopenharmony_ci unsigned int pad; 162062306a36Sopenharmony_ci unsigned int i; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* Connect the sensor to the correct interface module. 162362306a36Sopenharmony_ci * Parallel sensors are connected directly to the CCDC, while 162462306a36Sopenharmony_ci * serial sensors are connected to the CSI2a, CCP2b or CSI2c 162562306a36Sopenharmony_ci * receiver through CSIPHY1 or CSIPHY2. 162662306a36Sopenharmony_ci */ 162762306a36Sopenharmony_ci switch (interface) { 162862306a36Sopenharmony_ci case ISP_INTERFACE_PARALLEL: 162962306a36Sopenharmony_ci input = &isp->isp_ccdc.subdev.entity; 163062306a36Sopenharmony_ci pad = CCDC_PAD_SINK; 163162306a36Sopenharmony_ci flags = 0; 163262306a36Sopenharmony_ci break; 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci case ISP_INTERFACE_CSI2A_PHY2: 163562306a36Sopenharmony_ci input = &isp->isp_csi2a.subdev.entity; 163662306a36Sopenharmony_ci pad = CSI2_PAD_SINK; 163762306a36Sopenharmony_ci flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; 163862306a36Sopenharmony_ci break; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY1: 164162306a36Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY2: 164262306a36Sopenharmony_ci input = &isp->isp_ccp2.subdev.entity; 164362306a36Sopenharmony_ci pad = CCP2_PAD_SINK; 164462306a36Sopenharmony_ci flags = 0; 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci case ISP_INTERFACE_CSI2C_PHY1: 164862306a36Sopenharmony_ci input = &isp->isp_csi2c.subdev.entity; 164962306a36Sopenharmony_ci pad = CSI2_PAD_SINK; 165062306a36Sopenharmony_ci flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; 165162306a36Sopenharmony_ci break; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci default: 165462306a36Sopenharmony_ci dev_err(isp->dev, "%s: invalid interface type %u\n", __func__, 165562306a36Sopenharmony_ci interface); 165662306a36Sopenharmony_ci return -EINVAL; 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci /* 166062306a36Sopenharmony_ci * Not all interfaces are available on all revisions of the 166162306a36Sopenharmony_ci * ISP. The sub-devices of those interfaces aren't initialised 166262306a36Sopenharmony_ci * in such a case. Check this by ensuring the num_pads is 166362306a36Sopenharmony_ci * non-zero. 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_ci if (!input->num_pads) { 166662306a36Sopenharmony_ci dev_err(isp->dev, "%s: invalid input %u\n", entity->name, 166762306a36Sopenharmony_ci interface); 166862306a36Sopenharmony_ci return -EINVAL; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 167262306a36Sopenharmony_ci if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE) 167362306a36Sopenharmony_ci break; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci if (i == entity->num_pads) { 167662306a36Sopenharmony_ci dev_err(isp->dev, "%s: no source pad in external entity %s\n", 167762306a36Sopenharmony_ci __func__, entity->name); 167862306a36Sopenharmony_ci return -EINVAL; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci return media_create_pad_link(entity, i, input, pad, flags); 168262306a36Sopenharmony_ci} 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_cistatic int isp_register_entities(struct isp_device *isp) 168562306a36Sopenharmony_ci{ 168662306a36Sopenharmony_ci int ret; 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci isp->media_dev.dev = isp->dev; 168962306a36Sopenharmony_ci strscpy(isp->media_dev.model, "TI OMAP3 ISP", 169062306a36Sopenharmony_ci sizeof(isp->media_dev.model)); 169162306a36Sopenharmony_ci isp->media_dev.hw_revision = isp->revision; 169262306a36Sopenharmony_ci isp->media_dev.ops = &isp_media_ops; 169362306a36Sopenharmony_ci media_device_init(&isp->media_dev); 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci isp->v4l2_dev.mdev = &isp->media_dev; 169662306a36Sopenharmony_ci ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); 169762306a36Sopenharmony_ci if (ret < 0) { 169862306a36Sopenharmony_ci dev_err(isp->dev, "%s: V4L2 device registration failed (%d)\n", 169962306a36Sopenharmony_ci __func__, ret); 170062306a36Sopenharmony_ci goto done; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci 170362306a36Sopenharmony_ci /* Register internal entities */ 170462306a36Sopenharmony_ci ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); 170562306a36Sopenharmony_ci if (ret < 0) 170662306a36Sopenharmony_ci goto done; 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); 170962306a36Sopenharmony_ci if (ret < 0) 171062306a36Sopenharmony_ci goto done; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); 171362306a36Sopenharmony_ci if (ret < 0) 171462306a36Sopenharmony_ci goto done; 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci ret = omap3isp_preview_register_entities(&isp->isp_prev, 171762306a36Sopenharmony_ci &isp->v4l2_dev); 171862306a36Sopenharmony_ci if (ret < 0) 171962306a36Sopenharmony_ci goto done; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); 172262306a36Sopenharmony_ci if (ret < 0) 172362306a36Sopenharmony_ci goto done; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); 172662306a36Sopenharmony_ci if (ret < 0) 172762306a36Sopenharmony_ci goto done; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); 173062306a36Sopenharmony_ci if (ret < 0) 173162306a36Sopenharmony_ci goto done; 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); 173462306a36Sopenharmony_ci if (ret < 0) 173562306a36Sopenharmony_ci goto done; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cidone: 173862306a36Sopenharmony_ci if (ret < 0) 173962306a36Sopenharmony_ci isp_unregister_entities(isp); 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci return ret; 174262306a36Sopenharmony_ci} 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci/* 174562306a36Sopenharmony_ci * isp_create_links() - Create links for internal and external ISP entities 174662306a36Sopenharmony_ci * @isp : Pointer to ISP device 174762306a36Sopenharmony_ci * 174862306a36Sopenharmony_ci * This function creates all links between ISP internal and external entities. 174962306a36Sopenharmony_ci * 175062306a36Sopenharmony_ci * Return: A negative error code on failure or zero on success. Possible error 175162306a36Sopenharmony_ci * codes are those returned by media_create_pad_link(). 175262306a36Sopenharmony_ci */ 175362306a36Sopenharmony_cistatic int isp_create_links(struct isp_device *isp) 175462306a36Sopenharmony_ci{ 175562306a36Sopenharmony_ci int ret; 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* Create links between entities and video nodes. */ 175862306a36Sopenharmony_ci ret = media_create_pad_link( 175962306a36Sopenharmony_ci &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, 176062306a36Sopenharmony_ci &isp->isp_csi2a.video_out.video.entity, 0, 0); 176162306a36Sopenharmony_ci if (ret < 0) 176262306a36Sopenharmony_ci return ret; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci ret = media_create_pad_link( 176562306a36Sopenharmony_ci &isp->isp_ccp2.video_in.video.entity, 0, 176662306a36Sopenharmony_ci &isp->isp_ccp2.subdev.entity, CCP2_PAD_SINK, 0); 176762306a36Sopenharmony_ci if (ret < 0) 176862306a36Sopenharmony_ci return ret; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci ret = media_create_pad_link( 177162306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, 177262306a36Sopenharmony_ci &isp->isp_ccdc.video_out.video.entity, 0, 0); 177362306a36Sopenharmony_ci if (ret < 0) 177462306a36Sopenharmony_ci return ret; 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci ret = media_create_pad_link( 177762306a36Sopenharmony_ci &isp->isp_prev.video_in.video.entity, 0, 177862306a36Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); 177962306a36Sopenharmony_ci if (ret < 0) 178062306a36Sopenharmony_ci return ret; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci ret = media_create_pad_link( 178362306a36Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, 178462306a36Sopenharmony_ci &isp->isp_prev.video_out.video.entity, 0, 0); 178562306a36Sopenharmony_ci if (ret < 0) 178662306a36Sopenharmony_ci return ret; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci ret = media_create_pad_link( 178962306a36Sopenharmony_ci &isp->isp_res.video_in.video.entity, 0, 179062306a36Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 179162306a36Sopenharmony_ci if (ret < 0) 179262306a36Sopenharmony_ci return ret; 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci ret = media_create_pad_link( 179562306a36Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SOURCE, 179662306a36Sopenharmony_ci &isp->isp_res.video_out.video.entity, 0, 0); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if (ret < 0) 179962306a36Sopenharmony_ci return ret; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci /* Create links between entities. */ 180262306a36Sopenharmony_ci ret = media_create_pad_link( 180362306a36Sopenharmony_ci &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, 180462306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); 180562306a36Sopenharmony_ci if (ret < 0) 180662306a36Sopenharmony_ci return ret; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci ret = media_create_pad_link( 180962306a36Sopenharmony_ci &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, 181062306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); 181162306a36Sopenharmony_ci if (ret < 0) 181262306a36Sopenharmony_ci return ret; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci ret = media_create_pad_link( 181562306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 181662306a36Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); 181762306a36Sopenharmony_ci if (ret < 0) 181862306a36Sopenharmony_ci return ret; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci ret = media_create_pad_link( 182162306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, 182262306a36Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 182362306a36Sopenharmony_ci if (ret < 0) 182462306a36Sopenharmony_ci return ret; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci ret = media_create_pad_link( 182762306a36Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, 182862306a36Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 182962306a36Sopenharmony_ci if (ret < 0) 183062306a36Sopenharmony_ci return ret; 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci ret = media_create_pad_link( 183362306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 183462306a36Sopenharmony_ci &isp->isp_aewb.subdev.entity, 0, 183562306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 183662306a36Sopenharmony_ci if (ret < 0) 183762306a36Sopenharmony_ci return ret; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci ret = media_create_pad_link( 184062306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 184162306a36Sopenharmony_ci &isp->isp_af.subdev.entity, 0, 184262306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 184362306a36Sopenharmony_ci if (ret < 0) 184462306a36Sopenharmony_ci return ret; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci ret = media_create_pad_link( 184762306a36Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 184862306a36Sopenharmony_ci &isp->isp_hist.subdev.entity, 0, 184962306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 185062306a36Sopenharmony_ci if (ret < 0) 185162306a36Sopenharmony_ci return ret; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci return 0; 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cistatic void isp_cleanup_modules(struct isp_device *isp) 185762306a36Sopenharmony_ci{ 185862306a36Sopenharmony_ci omap3isp_h3a_aewb_cleanup(isp); 185962306a36Sopenharmony_ci omap3isp_h3a_af_cleanup(isp); 186062306a36Sopenharmony_ci omap3isp_hist_cleanup(isp); 186162306a36Sopenharmony_ci omap3isp_resizer_cleanup(isp); 186262306a36Sopenharmony_ci omap3isp_preview_cleanup(isp); 186362306a36Sopenharmony_ci omap3isp_ccdc_cleanup(isp); 186462306a36Sopenharmony_ci omap3isp_ccp2_cleanup(isp); 186562306a36Sopenharmony_ci omap3isp_csi2_cleanup(isp); 186662306a36Sopenharmony_ci omap3isp_csiphy_cleanup(isp); 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_cistatic int isp_initialize_modules(struct isp_device *isp) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci int ret; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci ret = omap3isp_csiphy_init(isp); 187462306a36Sopenharmony_ci if (ret < 0) { 187562306a36Sopenharmony_ci dev_err(isp->dev, "CSI PHY initialization failed\n"); 187662306a36Sopenharmony_ci return ret; 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci ret = omap3isp_csi2_init(isp); 188062306a36Sopenharmony_ci if (ret < 0) { 188162306a36Sopenharmony_ci dev_err(isp->dev, "CSI2 initialization failed\n"); 188262306a36Sopenharmony_ci goto error_csi2; 188362306a36Sopenharmony_ci } 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci ret = omap3isp_ccp2_init(isp); 188662306a36Sopenharmony_ci if (ret < 0) { 188762306a36Sopenharmony_ci dev_err_probe(isp->dev, ret, "CCP2 initialization failed\n"); 188862306a36Sopenharmony_ci goto error_ccp2; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci ret = omap3isp_ccdc_init(isp); 189262306a36Sopenharmony_ci if (ret < 0) { 189362306a36Sopenharmony_ci dev_err(isp->dev, "CCDC initialization failed\n"); 189462306a36Sopenharmony_ci goto error_ccdc; 189562306a36Sopenharmony_ci } 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci ret = omap3isp_preview_init(isp); 189862306a36Sopenharmony_ci if (ret < 0) { 189962306a36Sopenharmony_ci dev_err(isp->dev, "Preview initialization failed\n"); 190062306a36Sopenharmony_ci goto error_preview; 190162306a36Sopenharmony_ci } 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci ret = omap3isp_resizer_init(isp); 190462306a36Sopenharmony_ci if (ret < 0) { 190562306a36Sopenharmony_ci dev_err(isp->dev, "Resizer initialization failed\n"); 190662306a36Sopenharmony_ci goto error_resizer; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci ret = omap3isp_hist_init(isp); 191062306a36Sopenharmony_ci if (ret < 0) { 191162306a36Sopenharmony_ci dev_err(isp->dev, "Histogram initialization failed\n"); 191262306a36Sopenharmony_ci goto error_hist; 191362306a36Sopenharmony_ci } 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci ret = omap3isp_h3a_aewb_init(isp); 191662306a36Sopenharmony_ci if (ret < 0) { 191762306a36Sopenharmony_ci dev_err(isp->dev, "H3A AEWB initialization failed\n"); 191862306a36Sopenharmony_ci goto error_h3a_aewb; 191962306a36Sopenharmony_ci } 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci ret = omap3isp_h3a_af_init(isp); 192262306a36Sopenharmony_ci if (ret < 0) { 192362306a36Sopenharmony_ci dev_err(isp->dev, "H3A AF initialization failed\n"); 192462306a36Sopenharmony_ci goto error_h3a_af; 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci return 0; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_cierror_h3a_af: 193062306a36Sopenharmony_ci omap3isp_h3a_aewb_cleanup(isp); 193162306a36Sopenharmony_cierror_h3a_aewb: 193262306a36Sopenharmony_ci omap3isp_hist_cleanup(isp); 193362306a36Sopenharmony_cierror_hist: 193462306a36Sopenharmony_ci omap3isp_resizer_cleanup(isp); 193562306a36Sopenharmony_cierror_resizer: 193662306a36Sopenharmony_ci omap3isp_preview_cleanup(isp); 193762306a36Sopenharmony_cierror_preview: 193862306a36Sopenharmony_ci omap3isp_ccdc_cleanup(isp); 193962306a36Sopenharmony_cierror_ccdc: 194062306a36Sopenharmony_ci omap3isp_ccp2_cleanup(isp); 194162306a36Sopenharmony_cierror_ccp2: 194262306a36Sopenharmony_ci omap3isp_csi2_cleanup(isp); 194362306a36Sopenharmony_cierror_csi2: 194462306a36Sopenharmony_ci omap3isp_csiphy_cleanup(isp); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci return ret; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic void isp_detach_iommu(struct isp_device *isp) 195062306a36Sopenharmony_ci{ 195162306a36Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 195262306a36Sopenharmony_ci arm_iommu_detach_device(isp->dev); 195362306a36Sopenharmony_ci arm_iommu_release_mapping(isp->mapping); 195462306a36Sopenharmony_ci isp->mapping = NULL; 195562306a36Sopenharmony_ci#endif 195662306a36Sopenharmony_ci} 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_cistatic int isp_attach_iommu(struct isp_device *isp) 195962306a36Sopenharmony_ci{ 196062306a36Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 196162306a36Sopenharmony_ci struct dma_iommu_mapping *mapping; 196262306a36Sopenharmony_ci int ret; 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci /* 196562306a36Sopenharmony_ci * Create the ARM mapping, used by the ARM DMA mapping core to allocate 196662306a36Sopenharmony_ci * VAs. This will allocate a corresponding IOMMU domain. 196762306a36Sopenharmony_ci */ 196862306a36Sopenharmony_ci mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G); 196962306a36Sopenharmony_ci if (IS_ERR(mapping)) { 197062306a36Sopenharmony_ci dev_err(isp->dev, "failed to create ARM IOMMU mapping\n"); 197162306a36Sopenharmony_ci return PTR_ERR(mapping); 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci isp->mapping = mapping; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci /* Attach the ARM VA mapping to the device. */ 197762306a36Sopenharmony_ci ret = arm_iommu_attach_device(isp->dev, mapping); 197862306a36Sopenharmony_ci if (ret < 0) { 197962306a36Sopenharmony_ci dev_err(isp->dev, "failed to attach device to VA mapping\n"); 198062306a36Sopenharmony_ci goto error; 198162306a36Sopenharmony_ci } 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci return 0; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_cierror: 198662306a36Sopenharmony_ci arm_iommu_release_mapping(isp->mapping); 198762306a36Sopenharmony_ci isp->mapping = NULL; 198862306a36Sopenharmony_ci return ret; 198962306a36Sopenharmony_ci#else 199062306a36Sopenharmony_ci return -ENODEV; 199162306a36Sopenharmony_ci#endif 199262306a36Sopenharmony_ci} 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci/* 199562306a36Sopenharmony_ci * isp_remove - Remove ISP platform device 199662306a36Sopenharmony_ci * @pdev: Pointer to ISP platform device 199762306a36Sopenharmony_ci * 199862306a36Sopenharmony_ci * Always returns 0. 199962306a36Sopenharmony_ci */ 200062306a36Sopenharmony_cistatic void isp_remove(struct platform_device *pdev) 200162306a36Sopenharmony_ci{ 200262306a36Sopenharmony_ci struct isp_device *isp = platform_get_drvdata(pdev); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci v4l2_async_nf_unregister(&isp->notifier); 200562306a36Sopenharmony_ci v4l2_async_nf_cleanup(&isp->notifier); 200662306a36Sopenharmony_ci isp_unregister_entities(isp); 200762306a36Sopenharmony_ci isp_cleanup_modules(isp); 200862306a36Sopenharmony_ci isp_xclk_cleanup(isp); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci __omap3isp_get(isp, false); 201162306a36Sopenharmony_ci isp_detach_iommu(isp); 201262306a36Sopenharmony_ci __omap3isp_put(isp, false); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci media_entity_enum_cleanup(&isp->crashed); 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci kfree(isp); 201762306a36Sopenharmony_ci} 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_cienum isp_of_phy { 202062306a36Sopenharmony_ci ISP_OF_PHY_PARALLEL = 0, 202162306a36Sopenharmony_ci ISP_OF_PHY_CSIPHY1, 202262306a36Sopenharmony_ci ISP_OF_PHY_CSIPHY2, 202362306a36Sopenharmony_ci}; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cistatic int isp_subdev_notifier_bound(struct v4l2_async_notifier *async, 202662306a36Sopenharmony_ci struct v4l2_subdev *sd, 202762306a36Sopenharmony_ci struct v4l2_async_connection *asc) 202862306a36Sopenharmony_ci{ 202962306a36Sopenharmony_ci struct isp_device *isp = container_of(async, struct isp_device, 203062306a36Sopenharmony_ci notifier); 203162306a36Sopenharmony_ci struct isp_bus_cfg *bus_cfg = 203262306a36Sopenharmony_ci &container_of(asc, struct isp_async_subdev, asd)->bus; 203362306a36Sopenharmony_ci int ret; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci mutex_lock(&isp->media_dev.graph_mutex); 203662306a36Sopenharmony_ci ret = isp_link_entity(isp, &sd->entity, bus_cfg->interface); 203762306a36Sopenharmony_ci mutex_unlock(&isp->media_dev.graph_mutex); 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci return ret; 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) 204362306a36Sopenharmony_ci{ 204462306a36Sopenharmony_ci struct isp_device *isp = container_of(async, struct isp_device, 204562306a36Sopenharmony_ci notifier); 204662306a36Sopenharmony_ci int ret; 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci mutex_lock(&isp->media_dev.graph_mutex); 204962306a36Sopenharmony_ci ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); 205062306a36Sopenharmony_ci mutex_unlock(&isp->media_dev.graph_mutex); 205162306a36Sopenharmony_ci if (ret) 205262306a36Sopenharmony_ci return ret; 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_ci ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); 205562306a36Sopenharmony_ci if (ret < 0) 205662306a36Sopenharmony_ci return ret; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci return media_device_register(&isp->media_dev); 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cistatic void isp_parse_of_parallel_endpoint(struct device *dev, 206262306a36Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 206362306a36Sopenharmony_ci struct isp_bus_cfg *buscfg) 206462306a36Sopenharmony_ci{ 206562306a36Sopenharmony_ci buscfg->interface = ISP_INTERFACE_PARALLEL; 206662306a36Sopenharmony_ci buscfg->bus.parallel.data_lane_shift = vep->bus.parallel.data_shift; 206762306a36Sopenharmony_ci buscfg->bus.parallel.clk_pol = 206862306a36Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING); 206962306a36Sopenharmony_ci buscfg->bus.parallel.hs_pol = 207062306a36Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW); 207162306a36Sopenharmony_ci buscfg->bus.parallel.vs_pol = 207262306a36Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW); 207362306a36Sopenharmony_ci buscfg->bus.parallel.fld_pol = 207462306a36Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW); 207562306a36Sopenharmony_ci buscfg->bus.parallel.data_pol = 207662306a36Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW); 207762306a36Sopenharmony_ci buscfg->bus.parallel.bt656 = vep->bus_type == V4L2_MBUS_BT656; 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_cistatic void isp_parse_of_csi2_endpoint(struct device *dev, 208162306a36Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 208262306a36Sopenharmony_ci struct isp_bus_cfg *buscfg) 208362306a36Sopenharmony_ci{ 208462306a36Sopenharmony_ci unsigned int i; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pos = vep->bus.mipi_csi2.clock_lane; 208762306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pol = 208862306a36Sopenharmony_ci vep->bus.mipi_csi2.lane_polarities[0]; 208962306a36Sopenharmony_ci dev_dbg(dev, "clock lane polarity %u, pos %u\n", 209062306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pol, 209162306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pos); 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci buscfg->bus.csi2.num_data_lanes = vep->bus.mipi_csi2.num_data_lanes; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci for (i = 0; i < buscfg->bus.csi2.num_data_lanes; i++) { 209662306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pos = 209762306a36Sopenharmony_ci vep->bus.mipi_csi2.data_lanes[i]; 209862306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pol = 209962306a36Sopenharmony_ci vep->bus.mipi_csi2.lane_polarities[i + 1]; 210062306a36Sopenharmony_ci dev_dbg(dev, 210162306a36Sopenharmony_ci "data lane %u polarity %u, pos %u\n", i, 210262306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pol, 210362306a36Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pos); 210462306a36Sopenharmony_ci } 210562306a36Sopenharmony_ci /* 210662306a36Sopenharmony_ci * FIXME: now we assume the CRC is always there. Implement a way to 210762306a36Sopenharmony_ci * obtain this information from the sensor. Frame descriptors, perhaps? 210862306a36Sopenharmony_ci */ 210962306a36Sopenharmony_ci buscfg->bus.csi2.crc = 1; 211062306a36Sopenharmony_ci} 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_cistatic void isp_parse_of_csi1_endpoint(struct device *dev, 211362306a36Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 211462306a36Sopenharmony_ci struct isp_bus_cfg *buscfg) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pos = vep->bus.mipi_csi1.clock_lane; 211762306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pol = vep->bus.mipi_csi1.lane_polarity[0]; 211862306a36Sopenharmony_ci dev_dbg(dev, "clock lane polarity %u, pos %u\n", 211962306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pol, 212062306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pos); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pos = vep->bus.mipi_csi1.data_lane; 212362306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pol = 212462306a36Sopenharmony_ci vep->bus.mipi_csi1.lane_polarity[1]; 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci dev_dbg(dev, "data lane polarity %u, pos %u\n", 212762306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pol, 212862306a36Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pos); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci buscfg->bus.ccp2.strobe_clk_pol = vep->bus.mipi_csi1.clock_inv; 213162306a36Sopenharmony_ci buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; 213262306a36Sopenharmony_ci buscfg->bus.ccp2.ccp2_mode = vep->bus_type == V4L2_MBUS_CCP2; 213362306a36Sopenharmony_ci buscfg->bus.ccp2.vp_clk_pol = 1; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci buscfg->bus.ccp2.crc = 1; 213662306a36Sopenharmony_ci} 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_cistatic struct { 213962306a36Sopenharmony_ci u32 phy; 214062306a36Sopenharmony_ci u32 csi2_if; 214162306a36Sopenharmony_ci u32 csi1_if; 214262306a36Sopenharmony_ci} isp_bus_interfaces[2] = { 214362306a36Sopenharmony_ci { ISP_OF_PHY_CSIPHY1, 214462306a36Sopenharmony_ci ISP_INTERFACE_CSI2C_PHY1, ISP_INTERFACE_CCP2B_PHY1 }, 214562306a36Sopenharmony_ci { ISP_OF_PHY_CSIPHY2, 214662306a36Sopenharmony_ci ISP_INTERFACE_CSI2A_PHY2, ISP_INTERFACE_CCP2B_PHY2 }, 214762306a36Sopenharmony_ci}; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_cistatic int isp_parse_of_endpoints(struct isp_device *isp) 215062306a36Sopenharmony_ci{ 215162306a36Sopenharmony_ci struct fwnode_handle *ep; 215262306a36Sopenharmony_ci struct isp_async_subdev *isd = NULL; 215362306a36Sopenharmony_ci unsigned int i; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id( 215662306a36Sopenharmony_ci dev_fwnode(isp->dev), ISP_OF_PHY_PARALLEL, 0, 215762306a36Sopenharmony_ci FWNODE_GRAPH_ENDPOINT_NEXT); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci if (ep) { 216062306a36Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { 216162306a36Sopenharmony_ci .bus_type = V4L2_MBUS_PARALLEL 216262306a36Sopenharmony_ci }; 216362306a36Sopenharmony_ci int ret; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci dev_dbg(isp->dev, "parsing parallel interface\n"); 216662306a36Sopenharmony_ci 216762306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci if (!ret) { 217062306a36Sopenharmony_ci isd = v4l2_async_nf_add_fwnode_remote(&isp->notifier, 217162306a36Sopenharmony_ci ep, struct 217262306a36Sopenharmony_ci isp_async_subdev); 217362306a36Sopenharmony_ci if (!IS_ERR(isd)) 217462306a36Sopenharmony_ci isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus); 217562306a36Sopenharmony_ci } 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci fwnode_handle_put(ep); 217862306a36Sopenharmony_ci } 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) { 218162306a36Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { 218262306a36Sopenharmony_ci .bus_type = V4L2_MBUS_CSI2_DPHY 218362306a36Sopenharmony_ci }; 218462306a36Sopenharmony_ci int ret; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id( 218762306a36Sopenharmony_ci dev_fwnode(isp->dev), isp_bus_interfaces[i].phy, 0, 218862306a36Sopenharmony_ci FWNODE_GRAPH_ENDPOINT_NEXT); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci if (!ep) 219162306a36Sopenharmony_ci continue; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i, 219462306a36Sopenharmony_ci to_of_node(ep)); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 219762306a36Sopenharmony_ci if (ret == -ENXIO) { 219862306a36Sopenharmony_ci vep = (struct v4l2_fwnode_endpoint) 219962306a36Sopenharmony_ci { .bus_type = V4L2_MBUS_CSI1 }; 220062306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci if (ret == -ENXIO) { 220362306a36Sopenharmony_ci vep = (struct v4l2_fwnode_endpoint) 220462306a36Sopenharmony_ci { .bus_type = V4L2_MBUS_CCP2 }; 220562306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (!ret) { 221062306a36Sopenharmony_ci isd = v4l2_async_nf_add_fwnode_remote(&isp->notifier, 221162306a36Sopenharmony_ci ep, 221262306a36Sopenharmony_ci struct 221362306a36Sopenharmony_ci isp_async_subdev); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci if (!IS_ERR(isd)) { 221662306a36Sopenharmony_ci switch (vep.bus_type) { 221762306a36Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 221862306a36Sopenharmony_ci isd->bus.interface = 221962306a36Sopenharmony_ci isp_bus_interfaces[i].csi2_if; 222062306a36Sopenharmony_ci isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus); 222162306a36Sopenharmony_ci break; 222262306a36Sopenharmony_ci case V4L2_MBUS_CSI1: 222362306a36Sopenharmony_ci case V4L2_MBUS_CCP2: 222462306a36Sopenharmony_ci isd->bus.interface = 222562306a36Sopenharmony_ci isp_bus_interfaces[i].csi1_if; 222662306a36Sopenharmony_ci isp_parse_of_csi1_endpoint(isp->dev, &vep, 222762306a36Sopenharmony_ci &isd->bus); 222862306a36Sopenharmony_ci break; 222962306a36Sopenharmony_ci default: 223062306a36Sopenharmony_ci break; 223162306a36Sopenharmony_ci } 223262306a36Sopenharmony_ci } 223362306a36Sopenharmony_ci } 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci fwnode_handle_put(ep); 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci return 0; 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { 224262306a36Sopenharmony_ci .bound = isp_subdev_notifier_bound, 224362306a36Sopenharmony_ci .complete = isp_subdev_notifier_complete, 224462306a36Sopenharmony_ci}; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci/* 224762306a36Sopenharmony_ci * isp_probe - Probe ISP platform device 224862306a36Sopenharmony_ci * @pdev: Pointer to ISP platform device 224962306a36Sopenharmony_ci * 225062306a36Sopenharmony_ci * Returns 0 if successful, 225162306a36Sopenharmony_ci * -ENOMEM if no memory available, 225262306a36Sopenharmony_ci * -ENODEV if no platform device resources found 225362306a36Sopenharmony_ci * or no space for remapping registers, 225462306a36Sopenharmony_ci * -EINVAL if couldn't install ISR, 225562306a36Sopenharmony_ci * or clk_get return error value. 225662306a36Sopenharmony_ci */ 225762306a36Sopenharmony_cistatic int isp_probe(struct platform_device *pdev) 225862306a36Sopenharmony_ci{ 225962306a36Sopenharmony_ci struct isp_device *isp; 226062306a36Sopenharmony_ci struct resource *mem; 226162306a36Sopenharmony_ci int ret; 226262306a36Sopenharmony_ci int i, m; 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_ci isp = kzalloc(sizeof(*isp), GFP_KERNEL); 226562306a36Sopenharmony_ci if (!isp) { 226662306a36Sopenharmony_ci dev_err(&pdev->dev, "could not allocate memory\n"); 226762306a36Sopenharmony_ci return -ENOMEM; 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node), 227162306a36Sopenharmony_ci "ti,phy-type", &isp->phy_type); 227262306a36Sopenharmony_ci if (ret) 227362306a36Sopenharmony_ci goto error_release_isp; 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 227662306a36Sopenharmony_ci "syscon"); 227762306a36Sopenharmony_ci if (IS_ERR(isp->syscon)) { 227862306a36Sopenharmony_ci ret = PTR_ERR(isp->syscon); 227962306a36Sopenharmony_ci goto error_release_isp; 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci ret = of_property_read_u32_index(pdev->dev.of_node, 228362306a36Sopenharmony_ci "syscon", 1, &isp->syscon_offset); 228462306a36Sopenharmony_ci if (ret) 228562306a36Sopenharmony_ci goto error_release_isp; 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci isp->autoidle = autoidle; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci mutex_init(&isp->isp_mutex); 229062306a36Sopenharmony_ci spin_lock_init(&isp->stat_lock); 229162306a36Sopenharmony_ci isp->dev = &pdev->dev; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci isp->ref_count = 0; 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); 229662306a36Sopenharmony_ci if (ret) 229762306a36Sopenharmony_ci goto error; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci platform_set_drvdata(pdev, isp); 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* Regulators */ 230262306a36Sopenharmony_ci isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1"); 230362306a36Sopenharmony_ci if (IS_ERR(isp->isp_csiphy1.vdd)) { 230462306a36Sopenharmony_ci ret = PTR_ERR(isp->isp_csiphy1.vdd); 230562306a36Sopenharmony_ci goto error; 230662306a36Sopenharmony_ci } 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2"); 230962306a36Sopenharmony_ci if (IS_ERR(isp->isp_csiphy2.vdd)) { 231062306a36Sopenharmony_ci ret = PTR_ERR(isp->isp_csiphy2.vdd); 231162306a36Sopenharmony_ci goto error; 231262306a36Sopenharmony_ci } 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci /* Clocks 231562306a36Sopenharmony_ci * 231662306a36Sopenharmony_ci * The ISP clock tree is revision-dependent. We thus need to enable ICLK 231762306a36Sopenharmony_ci * manually to read the revision before calling __omap3isp_get(). 231862306a36Sopenharmony_ci * 231962306a36Sopenharmony_ci * Start by mapping the ISP MMIO area, which is in two pieces. 232062306a36Sopenharmony_ci * The ISP IOMMU is in between. Map both now, and fill in the 232162306a36Sopenharmony_ci * ISP revision specific portions a little later in the 232262306a36Sopenharmony_ci * function. 232362306a36Sopenharmony_ci */ 232462306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 232562306a36Sopenharmony_ci unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci isp->mmio_base[map_idx] = 232862306a36Sopenharmony_ci devm_platform_get_and_ioremap_resource(pdev, i, &mem); 232962306a36Sopenharmony_ci if (IS_ERR(isp->mmio_base[map_idx])) { 233062306a36Sopenharmony_ci ret = PTR_ERR(isp->mmio_base[map_idx]); 233162306a36Sopenharmony_ci goto error; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci ret = isp_get_clocks(isp); 233662306a36Sopenharmony_ci if (ret < 0) 233762306a36Sopenharmony_ci goto error; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci ret = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); 234062306a36Sopenharmony_ci if (ret < 0) 234162306a36Sopenharmony_ci goto error; 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 234462306a36Sopenharmony_ci dev_info(isp->dev, "Revision %d.%d found\n", 234562306a36Sopenharmony_ci (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci clk_disable(isp->clock[ISP_CLK_CAM_ICK]); 234862306a36Sopenharmony_ci 234962306a36Sopenharmony_ci if (__omap3isp_get(isp, false) == NULL) { 235062306a36Sopenharmony_ci ret = -ENODEV; 235162306a36Sopenharmony_ci goto error; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci ret = isp_reset(isp); 235562306a36Sopenharmony_ci if (ret < 0) 235662306a36Sopenharmony_ci goto error_isp; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci ret = isp_xclk_init(isp); 235962306a36Sopenharmony_ci if (ret < 0) 236062306a36Sopenharmony_ci goto error_isp; 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci /* Memory resources */ 236362306a36Sopenharmony_ci for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) 236462306a36Sopenharmony_ci if (isp->revision == isp_res_maps[m].isp_rev) 236562306a36Sopenharmony_ci break; 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci if (m == ARRAY_SIZE(isp_res_maps)) { 236862306a36Sopenharmony_ci dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", 236962306a36Sopenharmony_ci (isp->revision & 0xf0) >> 4, isp->revision & 0xf); 237062306a36Sopenharmony_ci ret = -ENODEV; 237162306a36Sopenharmony_ci goto error_isp; 237262306a36Sopenharmony_ci } 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) 237562306a36Sopenharmony_ci isp->mmio_base[i] = 237662306a36Sopenharmony_ci isp->mmio_base[0] + isp_res_maps[m].offset[i]; 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci for (i = OMAP3_ISP_IOMEM_CSIPHY2; i < OMAP3_ISP_IOMEM_LAST; i++) 237962306a36Sopenharmony_ci isp->mmio_base[i] = 238062306a36Sopenharmony_ci isp->mmio_base[OMAP3_ISP_IOMEM_CSI2A_REGS1] 238162306a36Sopenharmony_ci + isp_res_maps[m].offset[i]; 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci isp->mmio_hist_base_phys = 238462306a36Sopenharmony_ci mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST]; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci /* IOMMU */ 238762306a36Sopenharmony_ci ret = isp_attach_iommu(isp); 238862306a36Sopenharmony_ci if (ret < 0) { 238962306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to attach to IOMMU\n"); 239062306a36Sopenharmony_ci goto error_isp; 239162306a36Sopenharmony_ci } 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci /* Interrupt */ 239462306a36Sopenharmony_ci ret = platform_get_irq(pdev, 0); 239562306a36Sopenharmony_ci if (ret < 0) 239662306a36Sopenharmony_ci goto error_iommu; 239762306a36Sopenharmony_ci isp->irq_num = ret; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED, 240062306a36Sopenharmony_ci "OMAP3 ISP", isp)) { 240162306a36Sopenharmony_ci dev_err(isp->dev, "Unable to request IRQ\n"); 240262306a36Sopenharmony_ci ret = -EINVAL; 240362306a36Sopenharmony_ci goto error_iommu; 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci /* Entities */ 240762306a36Sopenharmony_ci ret = isp_initialize_modules(isp); 240862306a36Sopenharmony_ci if (ret < 0) 240962306a36Sopenharmony_ci goto error_iommu; 241062306a36Sopenharmony_ci 241162306a36Sopenharmony_ci ret = isp_register_entities(isp); 241262306a36Sopenharmony_ci if (ret < 0) 241362306a36Sopenharmony_ci goto error_modules; 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci ret = isp_create_links(isp); 241662306a36Sopenharmony_ci if (ret < 0) 241762306a36Sopenharmony_ci goto error_register_entities; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci isp->notifier.ops = &isp_subdev_notifier_ops; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci ret = isp_parse_of_endpoints(isp); 242462306a36Sopenharmony_ci if (ret < 0) 242562306a36Sopenharmony_ci goto error_register_entities; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci ret = v4l2_async_nf_register(&isp->notifier); 242862306a36Sopenharmony_ci if (ret) 242962306a36Sopenharmony_ci goto error_register_entities; 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci isp_core_init(isp, 1); 243262306a36Sopenharmony_ci omap3isp_put(isp); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci return 0; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_cierror_register_entities: 243762306a36Sopenharmony_ci v4l2_async_nf_cleanup(&isp->notifier); 243862306a36Sopenharmony_ci isp_unregister_entities(isp); 243962306a36Sopenharmony_cierror_modules: 244062306a36Sopenharmony_ci isp_cleanup_modules(isp); 244162306a36Sopenharmony_cierror_iommu: 244262306a36Sopenharmony_ci isp_detach_iommu(isp); 244362306a36Sopenharmony_cierror_isp: 244462306a36Sopenharmony_ci isp_xclk_cleanup(isp); 244562306a36Sopenharmony_ci __omap3isp_put(isp, false); 244662306a36Sopenharmony_cierror: 244762306a36Sopenharmony_ci mutex_destroy(&isp->isp_mutex); 244862306a36Sopenharmony_cierror_release_isp: 244962306a36Sopenharmony_ci kfree(isp); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci return ret; 245262306a36Sopenharmony_ci} 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_cistatic const struct dev_pm_ops omap3isp_pm_ops = { 245562306a36Sopenharmony_ci .prepare = isp_pm_prepare, 245662306a36Sopenharmony_ci .suspend = isp_pm_suspend, 245762306a36Sopenharmony_ci .resume = isp_pm_resume, 245862306a36Sopenharmony_ci .complete = isp_pm_complete, 245962306a36Sopenharmony_ci}; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_cistatic const struct platform_device_id omap3isp_id_table[] = { 246262306a36Sopenharmony_ci { "omap3isp", 0 }, 246362306a36Sopenharmony_ci { }, 246462306a36Sopenharmony_ci}; 246562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, omap3isp_id_table); 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_cistatic const struct of_device_id omap3isp_of_table[] = { 246862306a36Sopenharmony_ci { .compatible = "ti,omap3-isp" }, 246962306a36Sopenharmony_ci { }, 247062306a36Sopenharmony_ci}; 247162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap3isp_of_table); 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_cistatic struct platform_driver omap3isp_driver = { 247462306a36Sopenharmony_ci .probe = isp_probe, 247562306a36Sopenharmony_ci .remove_new = isp_remove, 247662306a36Sopenharmony_ci .id_table = omap3isp_id_table, 247762306a36Sopenharmony_ci .driver = { 247862306a36Sopenharmony_ci .name = "omap3isp", 247962306a36Sopenharmony_ci .pm = &omap3isp_pm_ops, 248062306a36Sopenharmony_ci .of_match_table = omap3isp_of_table, 248162306a36Sopenharmony_ci }, 248262306a36Sopenharmony_ci}; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_cimodule_platform_driver(omap3isp_driver); 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation"); 248762306a36Sopenharmony_ciMODULE_DESCRIPTION("TI OMAP3 ISP driver"); 248862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 248962306a36Sopenharmony_ciMODULE_VERSION(ISP_VIDEO_DRIVER_VERSION); 2490