18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * isp.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * TI OMAP3 ISP - Core 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2006-2010 Nokia Corporation 88c2ecf20Sopenharmony_ci * Copyright (C) 2007-2009 Texas Instruments, Inc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 118c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Contributors: 148c2ecf20Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 158c2ecf20Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 168c2ecf20Sopenharmony_ci * David Cohen <dacohen@gmail.com> 178c2ecf20Sopenharmony_ci * Stanimir Varbanov <svarbanov@mm-sol.com> 188c2ecf20Sopenharmony_ci * Vimarsh Zutshi <vimarsh.zutshi@gmail.com> 198c2ecf20Sopenharmony_ci * Tuukka Toivonen <tuukkat76@gmail.com> 208c2ecf20Sopenharmony_ci * Sergio Aguirre <saaguirre@ti.com> 218c2ecf20Sopenharmony_ci * Antti Koskipaa <akoskipa@gmail.com> 228c2ecf20Sopenharmony_ci * Ivan T. Ivanov <iivanov@mm-sol.com> 238c2ecf20Sopenharmony_ci * RaniSuneela <r-m@ti.com> 248c2ecf20Sopenharmony_ci * Atanas Filipov <afilipov@mm-sol.com> 258c2ecf20Sopenharmony_ci * Gjorgji Rosikopulos <grosikopulos@mm-sol.com> 268c2ecf20Sopenharmony_ci * Hiroshi DOYU <hiroshi.doyu@nokia.com> 278c2ecf20Sopenharmony_ci * Nayden Kanchev <nkanchev@mm-sol.com> 288c2ecf20Sopenharmony_ci * Phil Carmody <ext-phil.2.carmody@nokia.com> 298c2ecf20Sopenharmony_ci * Artem Bityutskiy <artem.bityutskiy@nokia.com> 308c2ecf20Sopenharmony_ci * Dominic Curran <dcurran@ti.com> 318c2ecf20Sopenharmony_ci * Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi> 328c2ecf20Sopenharmony_ci * Pallavi Kulkarni <p-kulkarni@ti.com> 338c2ecf20Sopenharmony_ci * Vaibhav Hiremath <hvaibhav@ti.com> 348c2ecf20Sopenharmony_ci * Mohit Jalori <mjalori@ti.com> 358c2ecf20Sopenharmony_ci * Sameer Venkatraman <sameerv@ti.com> 368c2ecf20Sopenharmony_ci * Senthilvadivu Guruswamy <svadivu@ti.com> 378c2ecf20Sopenharmony_ci * Thara Gopinath <thara@ti.com> 388c2ecf20Sopenharmony_ci * Toni Leinonen <toni.leinonen@nokia.com> 398c2ecf20Sopenharmony_ci * Troy Laramy <t-laramy@ti.com> 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <linux/clk.h> 438c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 448c2ecf20Sopenharmony_ci#include <linux/delay.h> 458c2ecf20Sopenharmony_ci#include <linux/device.h> 468c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 478c2ecf20Sopenharmony_ci#include <linux/i2c.h> 488c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 498c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 508c2ecf20Sopenharmony_ci#include <linux/module.h> 518c2ecf20Sopenharmony_ci#include <linux/omap-iommu.h> 528c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 538c2ecf20Sopenharmony_ci#include <linux/property.h> 548c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 558c2ecf20Sopenharmony_ci#include <linux/slab.h> 568c2ecf20Sopenharmony_ci#include <linux/sched.h> 578c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 608c2ecf20Sopenharmony_ci#include <asm/dma-iommu.h> 618c2ecf20Sopenharmony_ci#endif 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 648c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 658c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 668c2ecf20Sopenharmony_ci#include <media/v4l2-mc.h> 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#include "isp.h" 698c2ecf20Sopenharmony_ci#include "ispreg.h" 708c2ecf20Sopenharmony_ci#include "ispccdc.h" 718c2ecf20Sopenharmony_ci#include "isppreview.h" 728c2ecf20Sopenharmony_ci#include "ispresizer.h" 738c2ecf20Sopenharmony_ci#include "ispcsi2.h" 748c2ecf20Sopenharmony_ci#include "ispccp2.h" 758c2ecf20Sopenharmony_ci#include "isph3a.h" 768c2ecf20Sopenharmony_ci#include "isphist.h" 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic unsigned int autoidle; 798c2ecf20Sopenharmony_cimodule_param(autoidle, int, 0444); 808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void isp_save_ctx(struct isp_device *isp); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic void isp_restore_ctx(struct isp_device *isp); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic const struct isp_res_mapping isp_res_maps[] = { 878c2ecf20Sopenharmony_ci { 888c2ecf20Sopenharmony_ci .isp_rev = ISP_REVISION_2_0, 898c2ecf20Sopenharmony_ci .offset = { 908c2ecf20Sopenharmony_ci /* first MMIO area */ 918c2ecf20Sopenharmony_ci 0x0000, /* base, len 0x0070 */ 928c2ecf20Sopenharmony_ci 0x0400, /* ccp2, len 0x01f0 */ 938c2ecf20Sopenharmony_ci 0x0600, /* ccdc, len 0x00a8 */ 948c2ecf20Sopenharmony_ci 0x0a00, /* hist, len 0x0048 */ 958c2ecf20Sopenharmony_ci 0x0c00, /* h3a, len 0x0060 */ 968c2ecf20Sopenharmony_ci 0x0e00, /* preview, len 0x00a0 */ 978c2ecf20Sopenharmony_ci 0x1000, /* resizer, len 0x00ac */ 988c2ecf20Sopenharmony_ci 0x1200, /* sbl, len 0x00fc */ 998c2ecf20Sopenharmony_ci /* second MMIO area */ 1008c2ecf20Sopenharmony_ci 0x0000, /* csi2a, len 0x0170 */ 1018c2ecf20Sopenharmony_ci 0x0170, /* csiphy2, len 0x000c */ 1028c2ecf20Sopenharmony_ci }, 1038c2ecf20Sopenharmony_ci .phy_type = ISP_PHY_TYPE_3430, 1048c2ecf20Sopenharmony_ci }, 1058c2ecf20Sopenharmony_ci { 1068c2ecf20Sopenharmony_ci .isp_rev = ISP_REVISION_15_0, 1078c2ecf20Sopenharmony_ci .offset = { 1088c2ecf20Sopenharmony_ci /* first MMIO area */ 1098c2ecf20Sopenharmony_ci 0x0000, /* base, len 0x0070 */ 1108c2ecf20Sopenharmony_ci 0x0400, /* ccp2, len 0x01f0 */ 1118c2ecf20Sopenharmony_ci 0x0600, /* ccdc, len 0x00a8 */ 1128c2ecf20Sopenharmony_ci 0x0a00, /* hist, len 0x0048 */ 1138c2ecf20Sopenharmony_ci 0x0c00, /* h3a, len 0x0060 */ 1148c2ecf20Sopenharmony_ci 0x0e00, /* preview, len 0x00a0 */ 1158c2ecf20Sopenharmony_ci 0x1000, /* resizer, len 0x00ac */ 1168c2ecf20Sopenharmony_ci 0x1200, /* sbl, len 0x00fc */ 1178c2ecf20Sopenharmony_ci /* second MMIO area */ 1188c2ecf20Sopenharmony_ci 0x0000, /* csi2a, len 0x0170 (1st area) */ 1198c2ecf20Sopenharmony_ci 0x0170, /* csiphy2, len 0x000c */ 1208c2ecf20Sopenharmony_ci 0x01c0, /* csi2a, len 0x0040 (2nd area) */ 1218c2ecf20Sopenharmony_ci 0x0400, /* csi2c, len 0x0170 (1st area) */ 1228c2ecf20Sopenharmony_ci 0x0570, /* csiphy1, len 0x000c */ 1238c2ecf20Sopenharmony_ci 0x05c0, /* csi2c, len 0x0040 (2nd area) */ 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci .phy_type = ISP_PHY_TYPE_3630, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* Structure for saving/restoring ISP module registers */ 1308c2ecf20Sopenharmony_cistatic struct isp_reg isp_reg_list[] = { 1318c2ecf20Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0}, 1328c2ecf20Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0}, 1338c2ecf20Sopenharmony_ci {OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0}, 1348c2ecf20Sopenharmony_ci {0, ISP_TOK_TERM, 0} 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* 1388c2ecf20Sopenharmony_ci * omap3isp_flush - Post pending L3 bus writes by doing a register readback 1398c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 1408c2ecf20Sopenharmony_ci * 1418c2ecf20Sopenharmony_ci * In order to force posting of pending writes, we need to write and 1428c2ecf20Sopenharmony_ci * readback the same register, in this case the revision register. 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * See this link for reference: 1458c2ecf20Sopenharmony_ci * https://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_civoid omap3isp_flush(struct isp_device *isp) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 1508c2ecf20Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 1548c2ecf20Sopenharmony_ci * XCLK 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw) 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void isp_xclk_update(struct isp_xclk *xclk, u32 divider) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci switch (xclk->id) { 1628c2ecf20Sopenharmony_ci case ISP_XCLK_A: 1638c2ecf20Sopenharmony_ci isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 1648c2ecf20Sopenharmony_ci ISPTCTRL_CTRL_DIVA_MASK, 1658c2ecf20Sopenharmony_ci divider << ISPTCTRL_CTRL_DIVA_SHIFT); 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case ISP_XCLK_B: 1688c2ecf20Sopenharmony_ci isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 1698c2ecf20Sopenharmony_ci ISPTCTRL_CTRL_DIVB_MASK, 1708c2ecf20Sopenharmony_ci divider << ISPTCTRL_CTRL_DIVB_SHIFT); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int isp_xclk_prepare(struct clk_hw *hw) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci omap3isp_get(xclk->isp); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void isp_xclk_unprepare(struct clk_hw *hw) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci omap3isp_put(xclk->isp); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int isp_xclk_enable(struct clk_hw *hw) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 1948c2ecf20Sopenharmony_ci unsigned long flags; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 1978c2ecf20Sopenharmony_ci isp_xclk_update(xclk, xclk->divider); 1988c2ecf20Sopenharmony_ci xclk->enabled = true; 1998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic void isp_xclk_disable(struct clk_hw *hw) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 2078c2ecf20Sopenharmony_ci unsigned long flags; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 2108c2ecf20Sopenharmony_ci isp_xclk_update(xclk, 0); 2118c2ecf20Sopenharmony_ci xclk->enabled = false; 2128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic unsigned long isp_xclk_recalc_rate(struct clk_hw *hw, 2168c2ecf20Sopenharmony_ci unsigned long parent_rate) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return parent_rate / xclk->divider; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci u32 divider; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (*rate >= parent_rate) { 2288c2ecf20Sopenharmony_ci *rate = parent_rate; 2298c2ecf20Sopenharmony_ci return ISPTCTRL_CTRL_DIV_BYPASS; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (*rate == 0) 2338c2ecf20Sopenharmony_ci *rate = 1; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci divider = DIV_ROUND_CLOSEST(parent_rate, *rate); 2368c2ecf20Sopenharmony_ci if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) 2378c2ecf20Sopenharmony_ci divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci *rate = parent_rate / divider; 2408c2ecf20Sopenharmony_ci return divider; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate, 2448c2ecf20Sopenharmony_ci unsigned long *parent_rate) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci isp_xclk_calc_divider(&rate, *parent_rate); 2478c2ecf20Sopenharmony_ci return rate; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate, 2518c2ecf20Sopenharmony_ci unsigned long parent_rate) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct isp_xclk *xclk = to_isp_xclk(hw); 2548c2ecf20Sopenharmony_ci unsigned long flags; 2558c2ecf20Sopenharmony_ci u32 divider; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci divider = isp_xclk_calc_divider(&rate, parent_rate); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&xclk->lock, flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci xclk->divider = divider; 2628c2ecf20Sopenharmony_ci if (xclk->enabled) 2638c2ecf20Sopenharmony_ci isp_xclk_update(xclk, divider); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&xclk->lock, flags); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n", 2688c2ecf20Sopenharmony_ci __func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider); 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const struct clk_ops isp_xclk_ops = { 2738c2ecf20Sopenharmony_ci .prepare = isp_xclk_prepare, 2748c2ecf20Sopenharmony_ci .unprepare = isp_xclk_unprepare, 2758c2ecf20Sopenharmony_ci .enable = isp_xclk_enable, 2768c2ecf20Sopenharmony_ci .disable = isp_xclk_disable, 2778c2ecf20Sopenharmony_ci .recalc_rate = isp_xclk_recalc_rate, 2788c2ecf20Sopenharmony_ci .round_rate = isp_xclk_round_rate, 2798c2ecf20Sopenharmony_ci .set_rate = isp_xclk_set_rate, 2808c2ecf20Sopenharmony_ci}; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic const char *isp_xclk_parent_name = "cam_mclk"; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic struct clk *isp_xclk_src_get(struct of_phandle_args *clkspec, void *data) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci unsigned int idx = clkspec->args[0]; 2878c2ecf20Sopenharmony_ci struct isp_device *isp = data; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (idx >= ARRAY_SIZE(isp->xclks)) 2908c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return isp->xclks[idx].clk; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int isp_xclk_init(struct isp_device *isp) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct device_node *np = isp->dev->of_node; 2988c2ecf20Sopenharmony_ci struct clk_init_data init = {}; 2998c2ecf20Sopenharmony_ci unsigned int i; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) 3028c2ecf20Sopenharmony_ci isp->xclks[i].clk = ERR_PTR(-EINVAL); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 3058c2ecf20Sopenharmony_ci struct isp_xclk *xclk = &isp->xclks[i]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci xclk->isp = isp; 3088c2ecf20Sopenharmony_ci xclk->id = i == 0 ? ISP_XCLK_A : ISP_XCLK_B; 3098c2ecf20Sopenharmony_ci xclk->divider = 1; 3108c2ecf20Sopenharmony_ci spin_lock_init(&xclk->lock); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci init.name = i == 0 ? "cam_xclka" : "cam_xclkb"; 3138c2ecf20Sopenharmony_ci init.ops = &isp_xclk_ops; 3148c2ecf20Sopenharmony_ci init.parent_names = &isp_xclk_parent_name; 3158c2ecf20Sopenharmony_ci init.num_parents = 1; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci xclk->hw.init = &init; 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * The first argument is NULL in order to avoid circular 3208c2ecf20Sopenharmony_ci * reference, as this driver takes reference on the 3218c2ecf20Sopenharmony_ci * sensor subdevice modules and the sensors would take 3228c2ecf20Sopenharmony_ci * reference on this module through clk_get(). 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_ci xclk->clk = clk_register(NULL, &xclk->hw); 3258c2ecf20Sopenharmony_ci if (IS_ERR(xclk->clk)) 3268c2ecf20Sopenharmony_ci return PTR_ERR(xclk->clk); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci if (np) 3308c2ecf20Sopenharmony_ci of_clk_add_provider(np, isp_xclk_src_get, isp); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic void isp_xclk_cleanup(struct isp_device *isp) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct device_node *np = isp->dev->of_node; 3388c2ecf20Sopenharmony_ci unsigned int i; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (np) 3418c2ecf20Sopenharmony_ci of_clk_del_provider(np); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp->xclks); ++i) { 3448c2ecf20Sopenharmony_ci struct isp_xclk *xclk = &isp->xclks[i]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!IS_ERR(xclk->clk)) 3478c2ecf20Sopenharmony_ci clk_unregister(xclk->clk); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 3528c2ecf20Sopenharmony_ci * Interrupts 3538c2ecf20Sopenharmony_ci */ 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * isp_enable_interrupts - Enable ISP interrupts. 3578c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_cistatic void isp_enable_interrupts(struct isp_device *isp) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci static const u32 irq = IRQ0ENABLE_CSIA_IRQ 3628c2ecf20Sopenharmony_ci | IRQ0ENABLE_CSIB_IRQ 3638c2ecf20Sopenharmony_ci | IRQ0ENABLE_CCDC_LSC_PREF_ERR_IRQ 3648c2ecf20Sopenharmony_ci | IRQ0ENABLE_CCDC_LSC_DONE_IRQ 3658c2ecf20Sopenharmony_ci | IRQ0ENABLE_CCDC_VD0_IRQ 3668c2ecf20Sopenharmony_ci | IRQ0ENABLE_CCDC_VD1_IRQ 3678c2ecf20Sopenharmony_ci | IRQ0ENABLE_HS_VS_IRQ 3688c2ecf20Sopenharmony_ci | IRQ0ENABLE_HIST_DONE_IRQ 3698c2ecf20Sopenharmony_ci | IRQ0ENABLE_H3A_AWB_DONE_IRQ 3708c2ecf20Sopenharmony_ci | IRQ0ENABLE_H3A_AF_DONE_IRQ 3718c2ecf20Sopenharmony_ci | IRQ0ENABLE_PRV_DONE_IRQ 3728c2ecf20Sopenharmony_ci | IRQ0ENABLE_RSZ_DONE_IRQ; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 3758c2ecf20Sopenharmony_ci isp_reg_writel(isp, irq, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* 3798c2ecf20Sopenharmony_ci * isp_disable_interrupts - Disable ISP interrupts. 3808c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_cistatic void isp_disable_interrupts(struct isp_device *isp) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* 3888c2ecf20Sopenharmony_ci * isp_core_init - ISP core settings 3898c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 3908c2ecf20Sopenharmony_ci * @idle: Consider idle state. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Set the power settings for the ISP and SBL bus and configure the HS/VS 3938c2ecf20Sopenharmony_ci * interrupt source. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * We need to configure the HS/VS interrupt source before interrupts get 3968c2ecf20Sopenharmony_ci * enabled, as the sensor might be free-running and the ISP default setting 3978c2ecf20Sopenharmony_ci * (HS edge) would put an unnecessary burden on the CPU. 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_cistatic void isp_core_init(struct isp_device *isp, int idle) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci isp_reg_writel(isp, 4028c2ecf20Sopenharmony_ci ((idle ? ISP_SYSCONFIG_MIDLEMODE_SMARTSTANDBY : 4038c2ecf20Sopenharmony_ci ISP_SYSCONFIG_MIDLEMODE_FORCESTANDBY) << 4048c2ecf20Sopenharmony_ci ISP_SYSCONFIG_MIDLEMODE_SHIFT) | 4058c2ecf20Sopenharmony_ci ((isp->revision == ISP_REVISION_15_0) ? 4068c2ecf20Sopenharmony_ci ISP_SYSCONFIG_AUTOIDLE : 0), 4078c2ecf20Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci isp_reg_writel(isp, 4108c2ecf20Sopenharmony_ci (isp->autoidle ? ISPCTRL_SBL_AUTOIDLE : 0) | 4118c2ecf20Sopenharmony_ci ISPCTRL_SYNC_DETECT_VSRISE, 4128c2ecf20Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci/* 4168c2ecf20Sopenharmony_ci * Configure the bridge and lane shifter. Valid inputs are 4178c2ecf20Sopenharmony_ci * 4188c2ecf20Sopenharmony_ci * CCDC_INPUT_PARALLEL: Parallel interface 4198c2ecf20Sopenharmony_ci * CCDC_INPUT_CSI2A: CSI2a receiver 4208c2ecf20Sopenharmony_ci * CCDC_INPUT_CCP2B: CCP2b receiver 4218c2ecf20Sopenharmony_ci * CCDC_INPUT_CSI2C: CSI2c receiver 4228c2ecf20Sopenharmony_ci * 4238c2ecf20Sopenharmony_ci * The bridge and lane shifter are configured according to the selected input 4248c2ecf20Sopenharmony_ci * and the ISP platform data. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_civoid omap3isp_configure_bridge(struct isp_device *isp, 4278c2ecf20Sopenharmony_ci enum ccdc_input_entity input, 4288c2ecf20Sopenharmony_ci const struct isp_parallel_cfg *parcfg, 4298c2ecf20Sopenharmony_ci unsigned int shift, unsigned int bridge) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci u32 ispctrl_val; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ispctrl_val = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 4348c2ecf20Sopenharmony_ci ispctrl_val &= ~ISPCTRL_SHIFT_MASK; 4358c2ecf20Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_CLK_POL_INV; 4368c2ecf20Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_SER_CLK_SEL_MASK; 4378c2ecf20Sopenharmony_ci ispctrl_val &= ~ISPCTRL_PAR_BRIDGE_MASK; 4388c2ecf20Sopenharmony_ci ispctrl_val |= bridge; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci switch (input) { 4418c2ecf20Sopenharmony_ci case CCDC_INPUT_PARALLEL: 4428c2ecf20Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_PARALLEL; 4438c2ecf20Sopenharmony_ci ispctrl_val |= parcfg->clk_pol << ISPCTRL_PAR_CLK_POL_SHIFT; 4448c2ecf20Sopenharmony_ci shift += parcfg->data_lane_shift; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci case CCDC_INPUT_CSI2A: 4488c2ecf20Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIA; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci case CCDC_INPUT_CCP2B: 4528c2ecf20Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIB; 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci case CCDC_INPUT_CSI2C: 4568c2ecf20Sopenharmony_ci ispctrl_val |= ISPCTRL_PAR_SER_CLK_SEL_CSIC; 4578c2ecf20Sopenharmony_ci break; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci default: 4608c2ecf20Sopenharmony_ci return; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ispctrl_val |= ((shift/2) << ISPCTRL_SHIFT_SHIFT) & ISPCTRL_SHIFT_MASK; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_civoid omap3isp_hist_dma_done(struct isp_device *isp) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci if (omap3isp_ccdc_busy(&isp->isp_ccdc) || 4718c2ecf20Sopenharmony_ci omap3isp_stat_pcr_busy(&isp->isp_hist)) { 4728c2ecf20Sopenharmony_ci /* Histogram cannot be enabled in this frame anymore */ 4738c2ecf20Sopenharmony_ci atomic_set(&isp->isp_hist.buf_err, 1); 4748c2ecf20Sopenharmony_ci dev_dbg(isp->dev, 4758c2ecf20Sopenharmony_ci "hist: Out of synchronization with CCDC. Ignoring next buffer.\n"); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic inline void isp_isr_dbg(struct isp_device *isp, u32 irqstatus) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci static const char *name[] = { 4828c2ecf20Sopenharmony_ci "CSIA_IRQ", 4838c2ecf20Sopenharmony_ci "res1", 4848c2ecf20Sopenharmony_ci "res2", 4858c2ecf20Sopenharmony_ci "CSIB_LCM_IRQ", 4868c2ecf20Sopenharmony_ci "CSIB_IRQ", 4878c2ecf20Sopenharmony_ci "res5", 4888c2ecf20Sopenharmony_ci "res6", 4898c2ecf20Sopenharmony_ci "res7", 4908c2ecf20Sopenharmony_ci "CCDC_VD0_IRQ", 4918c2ecf20Sopenharmony_ci "CCDC_VD1_IRQ", 4928c2ecf20Sopenharmony_ci "CCDC_VD2_IRQ", 4938c2ecf20Sopenharmony_ci "CCDC_ERR_IRQ", 4948c2ecf20Sopenharmony_ci "H3A_AF_DONE_IRQ", 4958c2ecf20Sopenharmony_ci "H3A_AWB_DONE_IRQ", 4968c2ecf20Sopenharmony_ci "res14", 4978c2ecf20Sopenharmony_ci "res15", 4988c2ecf20Sopenharmony_ci "HIST_DONE_IRQ", 4998c2ecf20Sopenharmony_ci "CCDC_LSC_DONE", 5008c2ecf20Sopenharmony_ci "CCDC_LSC_PREFETCH_COMPLETED", 5018c2ecf20Sopenharmony_ci "CCDC_LSC_PREFETCH_ERROR", 5028c2ecf20Sopenharmony_ci "PRV_DONE_IRQ", 5038c2ecf20Sopenharmony_ci "CBUFF_IRQ", 5048c2ecf20Sopenharmony_ci "res22", 5058c2ecf20Sopenharmony_ci "res23", 5068c2ecf20Sopenharmony_ci "RSZ_DONE_IRQ", 5078c2ecf20Sopenharmony_ci "OVF_IRQ", 5088c2ecf20Sopenharmony_ci "res26", 5098c2ecf20Sopenharmony_ci "res27", 5108c2ecf20Sopenharmony_ci "MMU_ERR_IRQ", 5118c2ecf20Sopenharmony_ci "OCP_ERR_IRQ", 5128c2ecf20Sopenharmony_ci "SEC_ERR_IRQ", 5138c2ecf20Sopenharmony_ci "HS_VS_IRQ", 5148c2ecf20Sopenharmony_ci }; 5158c2ecf20Sopenharmony_ci int i; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "ISP IRQ: "); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(name); i++) { 5208c2ecf20Sopenharmony_ci if ((1 << i) & irqstatus) 5218c2ecf20Sopenharmony_ci printk(KERN_CONT "%s ", name[i]); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci printk(KERN_CONT "\n"); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void isp_isr_sbl(struct isp_device *isp) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct device *dev = isp->dev; 5298c2ecf20Sopenharmony_ci struct isp_pipeline *pipe; 5308c2ecf20Sopenharmony_ci u32 sbl_pcr; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci /* 5338c2ecf20Sopenharmony_ci * Handle shared buffer logic overflows for video buffers. 5348c2ecf20Sopenharmony_ci * ISPSBL_PCR_CCDCPRV_2_RSZ_OVF can be safely ignored. 5358c2ecf20Sopenharmony_ci */ 5368c2ecf20Sopenharmony_ci sbl_pcr = isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); 5378c2ecf20Sopenharmony_ci isp_reg_writel(isp, sbl_pcr, OMAP3_ISP_IOMEM_SBL, ISPSBL_PCR); 5388c2ecf20Sopenharmony_ci sbl_pcr &= ~ISPSBL_PCR_CCDCPRV_2_RSZ_OVF; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (sbl_pcr) 5418c2ecf20Sopenharmony_ci dev_dbg(dev, "SBL overflow (PCR = 0x%08x)\n", sbl_pcr); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CSIB_WBL_OVF) { 5448c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_ccp2.subdev.entity); 5458c2ecf20Sopenharmony_ci if (pipe != NULL) 5468c2ecf20Sopenharmony_ci pipe->error = true; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CSIA_WBL_OVF) { 5508c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_csi2a.subdev.entity); 5518c2ecf20Sopenharmony_ci if (pipe != NULL) 5528c2ecf20Sopenharmony_ci pipe->error = true; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_CCDC_WBL_OVF) { 5568c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_ccdc.subdev.entity); 5578c2ecf20Sopenharmony_ci if (pipe != NULL) 5588c2ecf20Sopenharmony_ci pipe->error = true; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_PRV_WBL_OVF) { 5628c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_prev.subdev.entity); 5638c2ecf20Sopenharmony_ci if (pipe != NULL) 5648c2ecf20Sopenharmony_ci pipe->error = true; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (sbl_pcr & (ISPSBL_PCR_RSZ1_WBL_OVF 5688c2ecf20Sopenharmony_ci | ISPSBL_PCR_RSZ2_WBL_OVF 5698c2ecf20Sopenharmony_ci | ISPSBL_PCR_RSZ3_WBL_OVF 5708c2ecf20Sopenharmony_ci | ISPSBL_PCR_RSZ4_WBL_OVF)) { 5718c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(&isp->isp_res.subdev.entity); 5728c2ecf20Sopenharmony_ci if (pipe != NULL) 5738c2ecf20Sopenharmony_ci pipe->error = true; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_H3A_AF_WBL_OVF) 5778c2ecf20Sopenharmony_ci omap3isp_stat_sbl_overflow(&isp->isp_af); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (sbl_pcr & ISPSBL_PCR_H3A_AEAWB_WBL_OVF) 5808c2ecf20Sopenharmony_ci omap3isp_stat_sbl_overflow(&isp->isp_aewb); 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci/* 5848c2ecf20Sopenharmony_ci * isp_isr - Interrupt Service Routine for Camera ISP module. 5858c2ecf20Sopenharmony_ci * @irq: Not used currently. 5868c2ecf20Sopenharmony_ci * @_isp: Pointer to the OMAP3 ISP device 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * Handles the corresponding callback if plugged in. 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_cistatic irqreturn_t isp_isr(int irq, void *_isp) 5918c2ecf20Sopenharmony_ci{ 5928c2ecf20Sopenharmony_ci static const u32 ccdc_events = IRQ0STATUS_CCDC_LSC_PREF_ERR_IRQ | 5938c2ecf20Sopenharmony_ci IRQ0STATUS_CCDC_LSC_DONE_IRQ | 5948c2ecf20Sopenharmony_ci IRQ0STATUS_CCDC_VD0_IRQ | 5958c2ecf20Sopenharmony_ci IRQ0STATUS_CCDC_VD1_IRQ | 5968c2ecf20Sopenharmony_ci IRQ0STATUS_HS_VS_IRQ; 5978c2ecf20Sopenharmony_ci struct isp_device *isp = _isp; 5988c2ecf20Sopenharmony_ci u32 irqstatus; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci irqstatus = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 6018c2ecf20Sopenharmony_ci isp_reg_writel(isp, irqstatus, OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci isp_isr_sbl(isp); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_CSIA_IRQ) 6068c2ecf20Sopenharmony_ci omap3isp_csi2_isr(&isp->isp_csi2a); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_CSIB_IRQ) 6098c2ecf20Sopenharmony_ci omap3isp_ccp2_isr(&isp->isp_ccp2); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_CCDC_VD0_IRQ) { 6128c2ecf20Sopenharmony_ci if (isp->isp_ccdc.output & CCDC_OUTPUT_PREVIEW) 6138c2ecf20Sopenharmony_ci omap3isp_preview_isr_frame_sync(&isp->isp_prev); 6148c2ecf20Sopenharmony_ci if (isp->isp_ccdc.output & CCDC_OUTPUT_RESIZER) 6158c2ecf20Sopenharmony_ci omap3isp_resizer_isr_frame_sync(&isp->isp_res); 6168c2ecf20Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_aewb); 6178c2ecf20Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_af); 6188c2ecf20Sopenharmony_ci omap3isp_stat_isr_frame_sync(&isp->isp_hist); 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (irqstatus & ccdc_events) 6228c2ecf20Sopenharmony_ci omap3isp_ccdc_isr(&isp->isp_ccdc, irqstatus & ccdc_events); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_PRV_DONE_IRQ) { 6258c2ecf20Sopenharmony_ci if (isp->isp_prev.output & PREVIEW_OUTPUT_RESIZER) 6268c2ecf20Sopenharmony_ci omap3isp_resizer_isr_frame_sync(&isp->isp_res); 6278c2ecf20Sopenharmony_ci omap3isp_preview_isr(&isp->isp_prev); 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_RSZ_DONE_IRQ) 6318c2ecf20Sopenharmony_ci omap3isp_resizer_isr(&isp->isp_res); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_H3A_AWB_DONE_IRQ) 6348c2ecf20Sopenharmony_ci omap3isp_stat_isr(&isp->isp_aewb); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_H3A_AF_DONE_IRQ) 6378c2ecf20Sopenharmony_ci omap3isp_stat_isr(&isp->isp_af); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (irqstatus & IRQ0STATUS_HIST_DONE_IRQ) 6408c2ecf20Sopenharmony_ci omap3isp_stat_isr(&isp->isp_hist); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci omap3isp_flush(isp); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci#if defined(DEBUG) && defined(ISP_ISR_DEBUG) 6458c2ecf20Sopenharmony_ci isp_isr_dbg(isp, irqstatus); 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic const struct media_device_ops isp_media_ops = { 6528c2ecf20Sopenharmony_ci .link_notify = v4l2_pipeline_link_notify, 6538c2ecf20Sopenharmony_ci}; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 6568c2ecf20Sopenharmony_ci * Pipeline stream management 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci/* 6608c2ecf20Sopenharmony_ci * isp_pipeline_enable - Enable streaming on a pipeline 6618c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 6628c2ecf20Sopenharmony_ci * @mode: Stream mode (single shot or continuous) 6638c2ecf20Sopenharmony_ci * 6648c2ecf20Sopenharmony_ci * Walk the entities chain starting at the pipeline output video node and start 6658c2ecf20Sopenharmony_ci * all modules in the chain in the given mode. 6668c2ecf20Sopenharmony_ci * 6678c2ecf20Sopenharmony_ci * Return 0 if successful, or the return value of the failed video::s_stream 6688c2ecf20Sopenharmony_ci * operation otherwise. 6698c2ecf20Sopenharmony_ci */ 6708c2ecf20Sopenharmony_cistatic int isp_pipeline_enable(struct isp_pipeline *pipe, 6718c2ecf20Sopenharmony_ci enum isp_pipeline_stream_state mode) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct isp_device *isp = pipe->output->isp; 6748c2ecf20Sopenharmony_ci struct media_entity *entity; 6758c2ecf20Sopenharmony_ci struct media_pad *pad; 6768c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 6778c2ecf20Sopenharmony_ci unsigned long flags; 6788c2ecf20Sopenharmony_ci int ret; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Refuse to start streaming if an entity included in the pipeline has 6818c2ecf20Sopenharmony_ci * crashed. This check must be performed before the loop below to avoid 6828c2ecf20Sopenharmony_ci * starting entities if the pipeline won't start anyway (those entities 6838c2ecf20Sopenharmony_ci * would then likely fail to stop, making the problem worse). 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ci if (media_entity_enum_intersects(&pipe->ent_enum, &isp->crashed)) 6868c2ecf20Sopenharmony_ci return -EIO; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci spin_lock_irqsave(&pipe->lock, flags); 6898c2ecf20Sopenharmony_ci pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); 6908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pipe->lock, flags); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci pipe->do_propagation = false; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci entity = &pipe->output->video.entity; 6958c2ecf20Sopenharmony_ci while (1) { 6968c2ecf20Sopenharmony_ci pad = &entity->pads[0]; 6978c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(pad); 7018c2ecf20Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci entity = pad->entity; 7058c2ecf20Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, mode); 7088c2ecf20Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) 7098c2ecf20Sopenharmony_ci return ret; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (subdev == &isp->isp_ccdc.subdev) { 7128c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_aewb.subdev, video, 7138c2ecf20Sopenharmony_ci s_stream, mode); 7148c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_af.subdev, video, 7158c2ecf20Sopenharmony_ci s_stream, mode); 7168c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_hist.subdev, video, 7178c2ecf20Sopenharmony_ci s_stream, mode); 7188c2ecf20Sopenharmony_ci pipe->do_propagation = true; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Stop at the first external sub-device. */ 7228c2ecf20Sopenharmony_ci if (subdev->dev != isp->dev) 7238c2ecf20Sopenharmony_ci break; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return 0; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic int isp_pipeline_wait_resizer(struct isp_device *isp) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci return omap3isp_resizer_busy(&isp->isp_res); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic int isp_pipeline_wait_preview(struct isp_device *isp) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci return omap3isp_preview_busy(&isp->isp_prev); 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int isp_pipeline_wait_ccdc(struct isp_device *isp) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci return omap3isp_stat_busy(&isp->isp_af) 7428c2ecf20Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_aewb) 7438c2ecf20Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_hist) 7448c2ecf20Sopenharmony_ci || omap3isp_ccdc_busy(&isp->isp_ccdc); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci#define ISP_STOP_TIMEOUT msecs_to_jiffies(1000) 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic int isp_pipeline_wait(struct isp_device *isp, 7508c2ecf20Sopenharmony_ci int(*busy)(struct isp_device *isp)) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + ISP_STOP_TIMEOUT; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci while (!time_after(jiffies, timeout)) { 7558c2ecf20Sopenharmony_ci if (!busy(isp)) 7568c2ecf20Sopenharmony_ci return 0; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return 1; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* 7638c2ecf20Sopenharmony_ci * isp_pipeline_disable - Disable streaming on a pipeline 7648c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Walk the entities chain starting at the pipeline output video node and stop 7678c2ecf20Sopenharmony_ci * all modules in the chain. Wait synchronously for the modules to be stopped if 7688c2ecf20Sopenharmony_ci * necessary. 7698c2ecf20Sopenharmony_ci * 7708c2ecf20Sopenharmony_ci * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module 7718c2ecf20Sopenharmony_ci * can't be stopped (in which case a software reset of the ISP is probably 7728c2ecf20Sopenharmony_ci * necessary). 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_cistatic int isp_pipeline_disable(struct isp_pipeline *pipe) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct isp_device *isp = pipe->output->isp; 7778c2ecf20Sopenharmony_ci struct media_entity *entity; 7788c2ecf20Sopenharmony_ci struct media_pad *pad; 7798c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev; 7808c2ecf20Sopenharmony_ci int failure = 0; 7818c2ecf20Sopenharmony_ci int ret; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci /* 7848c2ecf20Sopenharmony_ci * We need to stop all the modules after CCDC first or they'll 7858c2ecf20Sopenharmony_ci * never stop since they may not get a full frame from CCDC. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_ci entity = &pipe->output->video.entity; 7888c2ecf20Sopenharmony_ci while (1) { 7898c2ecf20Sopenharmony_ci pad = &entity->pads[0]; 7908c2ecf20Sopenharmony_ci if (!(pad->flags & MEDIA_PAD_FL_SINK)) 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(pad); 7948c2ecf20Sopenharmony_ci if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci entity = pad->entity; 7988c2ecf20Sopenharmony_ci subdev = media_entity_to_v4l2_subdev(entity); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (subdev == &isp->isp_ccdc.subdev) { 8018c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_aewb.subdev, 8028c2ecf20Sopenharmony_ci video, s_stream, 0); 8038c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_af.subdev, 8048c2ecf20Sopenharmony_ci video, s_stream, 0); 8058c2ecf20Sopenharmony_ci v4l2_subdev_call(&isp->isp_hist.subdev, 8068c2ecf20Sopenharmony_ci video, s_stream, 0); 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, video, s_stream, 0); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* Stop at the first external sub-device. */ 8128c2ecf20Sopenharmony_ci if (subdev->dev != isp->dev) 8138c2ecf20Sopenharmony_ci break; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (subdev == &isp->isp_res.subdev) 8168c2ecf20Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_resizer); 8178c2ecf20Sopenharmony_ci else if (subdev == &isp->isp_prev.subdev) 8188c2ecf20Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_preview); 8198c2ecf20Sopenharmony_ci else if (subdev == &isp->isp_ccdc.subdev) 8208c2ecf20Sopenharmony_ci ret |= isp_pipeline_wait(isp, isp_pipeline_wait_ccdc); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Handle stop failures. An entity that fails to stop can 8238c2ecf20Sopenharmony_ci * usually just be restarted. Flag the stop failure nonetheless 8248c2ecf20Sopenharmony_ci * to trigger an ISP reset the next time the device is released, 8258c2ecf20Sopenharmony_ci * just in case. 8268c2ecf20Sopenharmony_ci * 8278c2ecf20Sopenharmony_ci * The preview engine is a special case. A failure to stop can 8288c2ecf20Sopenharmony_ci * mean a hardware crash. When that happens the preview engine 8298c2ecf20Sopenharmony_ci * won't respond to read/write operations on the L4 bus anymore, 8308c2ecf20Sopenharmony_ci * resulting in a bus fault and a kernel oops next time it gets 8318c2ecf20Sopenharmony_ci * accessed. Mark it as crashed to prevent pipelines including 8328c2ecf20Sopenharmony_ci * it from being started. 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci if (ret) { 8358c2ecf20Sopenharmony_ci dev_info(isp->dev, "Unable to stop %s\n", subdev->name); 8368c2ecf20Sopenharmony_ci isp->stop_failure = true; 8378c2ecf20Sopenharmony_ci if (subdev == &isp->isp_prev.subdev) 8388c2ecf20Sopenharmony_ci media_entity_enum_set(&isp->crashed, 8398c2ecf20Sopenharmony_ci &subdev->entity); 8408c2ecf20Sopenharmony_ci failure = -ETIMEDOUT; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return failure; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci/* 8488c2ecf20Sopenharmony_ci * omap3isp_pipeline_set_stream - Enable/disable streaming on a pipeline 8498c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 8508c2ecf20Sopenharmony_ci * @state: Stream state (stopped, single shot or continuous) 8518c2ecf20Sopenharmony_ci * 8528c2ecf20Sopenharmony_ci * Set the pipeline to the given stream state. Pipelines can be started in 8538c2ecf20Sopenharmony_ci * single-shot or continuous mode. 8548c2ecf20Sopenharmony_ci * 8558c2ecf20Sopenharmony_ci * Return 0 if successful, or the return value of the failed video::s_stream 8568c2ecf20Sopenharmony_ci * operation otherwise. The pipeline state is not updated when the operation 8578c2ecf20Sopenharmony_ci * fails, except when stopping the pipeline. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_ciint omap3isp_pipeline_set_stream(struct isp_pipeline *pipe, 8608c2ecf20Sopenharmony_ci enum isp_pipeline_stream_state state) 8618c2ecf20Sopenharmony_ci{ 8628c2ecf20Sopenharmony_ci int ret; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (state == ISP_PIPELINE_STREAM_STOPPED) 8658c2ecf20Sopenharmony_ci ret = isp_pipeline_disable(pipe); 8668c2ecf20Sopenharmony_ci else 8678c2ecf20Sopenharmony_ci ret = isp_pipeline_enable(pipe, state); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (ret == 0 || state == ISP_PIPELINE_STREAM_STOPPED) 8708c2ecf20Sopenharmony_ci pipe->stream_state = state; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return ret; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci/* 8768c2ecf20Sopenharmony_ci * omap3isp_pipeline_cancel_stream - Cancel stream on a pipeline 8778c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 8788c2ecf20Sopenharmony_ci * 8798c2ecf20Sopenharmony_ci * Cancelling a stream mark all buffers on all video nodes in the pipeline as 8808c2ecf20Sopenharmony_ci * erroneous and makes sure no new buffer can be queued. This function is called 8818c2ecf20Sopenharmony_ci * when a fatal error that prevents any further operation on the pipeline 8828c2ecf20Sopenharmony_ci * occurs. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_civoid omap3isp_pipeline_cancel_stream(struct isp_pipeline *pipe) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci if (pipe->input) 8878c2ecf20Sopenharmony_ci omap3isp_video_cancel_stream(pipe->input); 8888c2ecf20Sopenharmony_ci if (pipe->output) 8898c2ecf20Sopenharmony_ci omap3isp_video_cancel_stream(pipe->output); 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci/* 8938c2ecf20Sopenharmony_ci * isp_pipeline_resume - Resume streaming on a pipeline 8948c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 8958c2ecf20Sopenharmony_ci * 8968c2ecf20Sopenharmony_ci * Resume video output and input and re-enable pipeline. 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_cistatic void isp_pipeline_resume(struct isp_pipeline *pipe) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci int singleshot = pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci omap3isp_video_resume(pipe->output, !singleshot); 9038c2ecf20Sopenharmony_ci if (singleshot) 9048c2ecf20Sopenharmony_ci omap3isp_video_resume(pipe->input, 0); 9058c2ecf20Sopenharmony_ci isp_pipeline_enable(pipe, pipe->stream_state); 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci/* 9098c2ecf20Sopenharmony_ci * isp_pipeline_suspend - Suspend streaming on a pipeline 9108c2ecf20Sopenharmony_ci * @pipe: ISP pipeline 9118c2ecf20Sopenharmony_ci * 9128c2ecf20Sopenharmony_ci * Suspend pipeline. 9138c2ecf20Sopenharmony_ci */ 9148c2ecf20Sopenharmony_cistatic void isp_pipeline_suspend(struct isp_pipeline *pipe) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci isp_pipeline_disable(pipe); 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci/* 9208c2ecf20Sopenharmony_ci * isp_pipeline_is_last - Verify if entity has an enabled link to the output 9218c2ecf20Sopenharmony_ci * video node 9228c2ecf20Sopenharmony_ci * @me: ISP module's media entity 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * Returns 1 if the entity has an enabled link to the output video node or 0 9258c2ecf20Sopenharmony_ci * otherwise. It's true only while pipeline can have no more than one output 9268c2ecf20Sopenharmony_ci * node. 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic int isp_pipeline_is_last(struct media_entity *me) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct isp_pipeline *pipe; 9318c2ecf20Sopenharmony_ci struct media_pad *pad; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (!me->pipe) 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci pipe = to_isp_pipeline(me); 9368c2ecf20Sopenharmony_ci if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci pad = media_entity_remote_pad(&pipe->output->pad); 9398c2ecf20Sopenharmony_ci return pad->entity == me; 9408c2ecf20Sopenharmony_ci} 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci/* 9438c2ecf20Sopenharmony_ci * isp_suspend_module_pipeline - Suspend pipeline to which belongs the module 9448c2ecf20Sopenharmony_ci * @me: ISP module's media entity 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * Suspend the whole pipeline if module's entity has an enabled link to the 9478c2ecf20Sopenharmony_ci * output video node. It works only while pipeline can have no more than one 9488c2ecf20Sopenharmony_ci * output node. 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_cistatic void isp_suspend_module_pipeline(struct media_entity *me) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci if (isp_pipeline_is_last(me)) 9538c2ecf20Sopenharmony_ci isp_pipeline_suspend(to_isp_pipeline(me)); 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci/* 9578c2ecf20Sopenharmony_ci * isp_resume_module_pipeline - Resume pipeline to which belongs the module 9588c2ecf20Sopenharmony_ci * @me: ISP module's media entity 9598c2ecf20Sopenharmony_ci * 9608c2ecf20Sopenharmony_ci * Resume the whole pipeline if module's entity has an enabled link to the 9618c2ecf20Sopenharmony_ci * output video node. It works only while pipeline can have no more than one 9628c2ecf20Sopenharmony_ci * output node. 9638c2ecf20Sopenharmony_ci */ 9648c2ecf20Sopenharmony_cistatic void isp_resume_module_pipeline(struct media_entity *me) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci if (isp_pipeline_is_last(me)) 9678c2ecf20Sopenharmony_ci isp_pipeline_resume(to_isp_pipeline(me)); 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci/* 9718c2ecf20Sopenharmony_ci * isp_suspend_modules - Suspend ISP submodules. 9728c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 9738c2ecf20Sopenharmony_ci * 9748c2ecf20Sopenharmony_ci * Returns 0 if suspend left in idle state all the submodules properly, 9758c2ecf20Sopenharmony_ci * or returns 1 if a general Reset is required to suspend the submodules. 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_cistatic int __maybe_unused isp_suspend_modules(struct isp_device *isp) 9788c2ecf20Sopenharmony_ci{ 9798c2ecf20Sopenharmony_ci unsigned long timeout; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_aewb); 9828c2ecf20Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_af); 9838c2ecf20Sopenharmony_ci omap3isp_stat_suspend(&isp->isp_hist); 9848c2ecf20Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_res.subdev.entity); 9858c2ecf20Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_prev.subdev.entity); 9868c2ecf20Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_ccdc.subdev.entity); 9878c2ecf20Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_csi2a.subdev.entity); 9888c2ecf20Sopenharmony_ci isp_suspend_module_pipeline(&isp->isp_ccp2.subdev.entity); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci timeout = jiffies + ISP_STOP_TIMEOUT; 9918c2ecf20Sopenharmony_ci while (omap3isp_stat_busy(&isp->isp_af) 9928c2ecf20Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_aewb) 9938c2ecf20Sopenharmony_ci || omap3isp_stat_busy(&isp->isp_hist) 9948c2ecf20Sopenharmony_ci || omap3isp_preview_busy(&isp->isp_prev) 9958c2ecf20Sopenharmony_ci || omap3isp_resizer_busy(&isp->isp_res) 9968c2ecf20Sopenharmony_ci || omap3isp_ccdc_busy(&isp->isp_ccdc)) { 9978c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 9988c2ecf20Sopenharmony_ci dev_info(isp->dev, "can't stop modules.\n"); 9998c2ecf20Sopenharmony_ci return 1; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci msleep(1); 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return 0; 10058c2ecf20Sopenharmony_ci} 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci/* 10088c2ecf20Sopenharmony_ci * isp_resume_modules - Resume ISP submodules. 10098c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_cistatic void __maybe_unused isp_resume_modules(struct isp_device *isp) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci omap3isp_stat_resume(&isp->isp_aewb); 10148c2ecf20Sopenharmony_ci omap3isp_stat_resume(&isp->isp_af); 10158c2ecf20Sopenharmony_ci omap3isp_stat_resume(&isp->isp_hist); 10168c2ecf20Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_res.subdev.entity); 10178c2ecf20Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_prev.subdev.entity); 10188c2ecf20Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_ccdc.subdev.entity); 10198c2ecf20Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_csi2a.subdev.entity); 10208c2ecf20Sopenharmony_ci isp_resume_module_pipeline(&isp->isp_ccp2.subdev.entity); 10218c2ecf20Sopenharmony_ci} 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci/* 10248c2ecf20Sopenharmony_ci * isp_reset - Reset ISP with a timeout wait for idle. 10258c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10268c2ecf20Sopenharmony_ci */ 10278c2ecf20Sopenharmony_cistatic int isp_reset(struct isp_device *isp) 10288c2ecf20Sopenharmony_ci{ 10298c2ecf20Sopenharmony_ci unsigned long timeout = 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci isp_reg_writel(isp, 10328c2ecf20Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG) 10338c2ecf20Sopenharmony_ci | ISP_SYSCONFIG_SOFTRESET, 10348c2ecf20Sopenharmony_ci OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG); 10358c2ecf20Sopenharmony_ci while (!(isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, 10368c2ecf20Sopenharmony_ci ISP_SYSSTATUS) & 0x1)) { 10378c2ecf20Sopenharmony_ci if (timeout++ > 10000) { 10388c2ecf20Sopenharmony_ci dev_alert(isp->dev, "cannot reset ISP\n"); 10398c2ecf20Sopenharmony_ci return -ETIMEDOUT; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci udelay(1); 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci isp->stop_failure = false; 10458c2ecf20Sopenharmony_ci media_entity_enum_zero(&isp->crashed); 10468c2ecf20Sopenharmony_ci return 0; 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci/* 10508c2ecf20Sopenharmony_ci * isp_save_context - Saves the values of the ISP module registers. 10518c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10528c2ecf20Sopenharmony_ci * @reg_list: Structure containing pairs of register address and value to 10538c2ecf20Sopenharmony_ci * modify on OMAP. 10548c2ecf20Sopenharmony_ci */ 10558c2ecf20Sopenharmony_cistatic void 10568c2ecf20Sopenharmony_ciisp_save_context(struct isp_device *isp, struct isp_reg *reg_list) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci struct isp_reg *next = reg_list; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci for (; next->reg != ISP_TOK_TERM; next++) 10618c2ecf20Sopenharmony_ci next->val = isp_reg_readl(isp, next->mmio_range, next->reg); 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci/* 10658c2ecf20Sopenharmony_ci * isp_restore_context - Restores the values of the ISP module registers. 10668c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10678c2ecf20Sopenharmony_ci * @reg_list: Structure containing pairs of register address and value to 10688c2ecf20Sopenharmony_ci * modify on OMAP. 10698c2ecf20Sopenharmony_ci */ 10708c2ecf20Sopenharmony_cistatic void 10718c2ecf20Sopenharmony_ciisp_restore_context(struct isp_device *isp, struct isp_reg *reg_list) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci struct isp_reg *next = reg_list; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci for (; next->reg != ISP_TOK_TERM; next++) 10768c2ecf20Sopenharmony_ci isp_reg_writel(isp, next->val, next->mmio_range, next->reg); 10778c2ecf20Sopenharmony_ci} 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci/* 10808c2ecf20Sopenharmony_ci * isp_save_ctx - Saves ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. 10818c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10828c2ecf20Sopenharmony_ci * 10838c2ecf20Sopenharmony_ci * Routine for saving the context of each module in the ISP. 10848c2ecf20Sopenharmony_ci * CCDC, HIST, H3A, PREV, RESZ and MMU. 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_cistatic void isp_save_ctx(struct isp_device *isp) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci isp_save_context(isp, isp_reg_list); 10898c2ecf20Sopenharmony_ci omap_iommu_save_ctx(isp->dev); 10908c2ecf20Sopenharmony_ci} 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci/* 10938c2ecf20Sopenharmony_ci * isp_restore_ctx - Restores ISP, CCDC, HIST, H3A, PREV, RESZ & MMU context. 10948c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * Routine for restoring the context of each module in the ISP. 10978c2ecf20Sopenharmony_ci * CCDC, HIST, H3A, PREV, RESZ and MMU. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_cistatic void isp_restore_ctx(struct isp_device *isp) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci isp_restore_context(isp, isp_reg_list); 11028c2ecf20Sopenharmony_ci omap_iommu_restore_ctx(isp->dev); 11038c2ecf20Sopenharmony_ci omap3isp_ccdc_restore_context(isp); 11048c2ecf20Sopenharmony_ci omap3isp_preview_restore_context(isp); 11058c2ecf20Sopenharmony_ci} 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci/* ----------------------------------------------------------------------------- 11088c2ecf20Sopenharmony_ci * SBL resources management 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_ci#define OMAP3_ISP_SBL_READ (OMAP3_ISP_SBL_CSI1_READ | \ 11118c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_CCDC_LSC_READ | \ 11128c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_PREVIEW_READ | \ 11138c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_RESIZER_READ) 11148c2ecf20Sopenharmony_ci#define OMAP3_ISP_SBL_WRITE (OMAP3_ISP_SBL_CSI1_WRITE | \ 11158c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_CSI2A_WRITE | \ 11168c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_CSI2C_WRITE | \ 11178c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_CCDC_WRITE | \ 11188c2ecf20Sopenharmony_ci OMAP3_ISP_SBL_PREVIEW_WRITE) 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_civoid omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci u32 sbl = 0; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci isp->sbl_resources |= res; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ) 11278c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTA; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ) 11308c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTB; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE) 11338c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_WPORTC; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE) 11368c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_WR0_RAM_EN; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_WRITE) 11398c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_WR1_RAM_EN; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (isp->sbl_resources & OMAP3_ISP_SBL_READ) 11428c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_RD_RAM_EN; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci isp_reg_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_civoid omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci u32 sbl = 0; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci isp->sbl_resources &= ~res; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI1_READ)) 11548c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTA; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CCDC_LSC_READ)) 11578c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_RPORTB; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_CSI2C_WRITE)) 11608c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_SHARED_WPORTC; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_RESIZER_WRITE)) 11638c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_WR0_RAM_EN; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_WRITE)) 11668c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_WR1_RAM_EN; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci if (!(isp->sbl_resources & OMAP3_ISP_SBL_READ)) 11698c2ecf20Sopenharmony_ci sbl |= ISPCTRL_SBL_RD_RAM_EN; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci isp_reg_clr(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, sbl); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/* 11758c2ecf20Sopenharmony_ci * isp_module_sync_idle - Helper to sync module with its idle state 11768c2ecf20Sopenharmony_ci * @me: ISP submodule's media entity 11778c2ecf20Sopenharmony_ci * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization 11788c2ecf20Sopenharmony_ci * @stopping: flag which tells module wants to stop 11798c2ecf20Sopenharmony_ci * 11808c2ecf20Sopenharmony_ci * This function checks if ISP submodule needs to wait for next interrupt. If 11818c2ecf20Sopenharmony_ci * yes, makes the caller to sleep while waiting for such event. 11828c2ecf20Sopenharmony_ci */ 11838c2ecf20Sopenharmony_ciint omap3isp_module_sync_idle(struct media_entity *me, wait_queue_head_t *wait, 11848c2ecf20Sopenharmony_ci atomic_t *stopping) 11858c2ecf20Sopenharmony_ci{ 11868c2ecf20Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(me); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED || 11898c2ecf20Sopenharmony_ci (pipe->stream_state == ISP_PIPELINE_STREAM_SINGLESHOT && 11908c2ecf20Sopenharmony_ci !isp_pipeline_ready(pipe))) 11918c2ecf20Sopenharmony_ci return 0; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * atomic_set() doesn't include memory barrier on ARM platform for SMP 11958c2ecf20Sopenharmony_ci * scenario. We'll call it here to avoid race conditions. 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_ci atomic_set(stopping, 1); 11988c2ecf20Sopenharmony_ci smp_mb(); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci /* 12018c2ecf20Sopenharmony_ci * If module is the last one, it's writing to memory. In this case, 12028c2ecf20Sopenharmony_ci * it's necessary to check if the module is already paused due to 12038c2ecf20Sopenharmony_ci * DMA queue underrun or if it has to wait for next interrupt to be 12048c2ecf20Sopenharmony_ci * idle. 12058c2ecf20Sopenharmony_ci * If it isn't the last one, the function won't sleep but *stopping 12068c2ecf20Sopenharmony_ci * will still be set to warn next submodule caller's interrupt the 12078c2ecf20Sopenharmony_ci * module wants to be idle. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_ci if (isp_pipeline_is_last(me)) { 12108c2ecf20Sopenharmony_ci struct isp_video *video = pipe->output; 12118c2ecf20Sopenharmony_ci unsigned long flags; 12128c2ecf20Sopenharmony_ci spin_lock_irqsave(&video->irqlock, flags); 12138c2ecf20Sopenharmony_ci if (video->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { 12148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 12158c2ecf20Sopenharmony_ci atomic_set(stopping, 0); 12168c2ecf20Sopenharmony_ci smp_mb(); 12178c2ecf20Sopenharmony_ci return 0; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&video->irqlock, flags); 12208c2ecf20Sopenharmony_ci if (!wait_event_timeout(*wait, !atomic_read(stopping), 12218c2ecf20Sopenharmony_ci msecs_to_jiffies(1000))) { 12228c2ecf20Sopenharmony_ci atomic_set(stopping, 0); 12238c2ecf20Sopenharmony_ci smp_mb(); 12248c2ecf20Sopenharmony_ci return -ETIMEDOUT; 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci return 0; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci/* 12328c2ecf20Sopenharmony_ci * omap3isp_module_sync_is_stopping - Helper to verify if module was stopping 12338c2ecf20Sopenharmony_ci * @wait: ISP submodule's wait queue for streamoff/interrupt synchronization 12348c2ecf20Sopenharmony_ci * @stopping: flag which tells module wants to stop 12358c2ecf20Sopenharmony_ci * 12368c2ecf20Sopenharmony_ci * This function checks if ISP submodule was stopping. In case of yes, it 12378c2ecf20Sopenharmony_ci * notices the caller by setting stopping to 0 and waking up the wait queue. 12388c2ecf20Sopenharmony_ci * Returns 1 if it was stopping or 0 otherwise. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ciint omap3isp_module_sync_is_stopping(wait_queue_head_t *wait, 12418c2ecf20Sopenharmony_ci atomic_t *stopping) 12428c2ecf20Sopenharmony_ci{ 12438c2ecf20Sopenharmony_ci if (atomic_cmpxchg(stopping, 1, 0)) { 12448c2ecf20Sopenharmony_ci wake_up(wait); 12458c2ecf20Sopenharmony_ci return 1; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci return 0; 12498c2ecf20Sopenharmony_ci} 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 12528c2ecf20Sopenharmony_ci * Clock management 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci#define ISPCTRL_CLKS_MASK (ISPCTRL_H3A_CLK_EN | \ 12568c2ecf20Sopenharmony_ci ISPCTRL_HIST_CLK_EN | \ 12578c2ecf20Sopenharmony_ci ISPCTRL_RSZ_CLK_EN | \ 12588c2ecf20Sopenharmony_ci (ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN) | \ 12598c2ecf20Sopenharmony_ci (ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN)) 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic void __isp_subclk_update(struct isp_device *isp) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci u32 clk = 0; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* AEWB and AF share the same clock. */ 12668c2ecf20Sopenharmony_ci if (isp->subclk_resources & 12678c2ecf20Sopenharmony_ci (OMAP3_ISP_SUBCLK_AEWB | OMAP3_ISP_SUBCLK_AF)) 12688c2ecf20Sopenharmony_ci clk |= ISPCTRL_H3A_CLK_EN; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_HIST) 12718c2ecf20Sopenharmony_ci clk |= ISPCTRL_HIST_CLK_EN; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_RESIZER) 12748c2ecf20Sopenharmony_ci clk |= ISPCTRL_RSZ_CLK_EN; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /* NOTE: For CCDC & Preview submodules, we need to affect internal 12778c2ecf20Sopenharmony_ci * RAM as well. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_CCDC) 12808c2ecf20Sopenharmony_ci clk |= ISPCTRL_CCDC_CLK_EN | ISPCTRL_CCDC_RAM_EN; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (isp->subclk_resources & OMAP3_ISP_SUBCLK_PREVIEW) 12838c2ecf20Sopenharmony_ci clk |= ISPCTRL_PREV_CLK_EN | ISPCTRL_PREV_RAM_EN; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 12868c2ecf20Sopenharmony_ci ISPCTRL_CLKS_MASK, clk); 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_civoid omap3isp_subclk_enable(struct isp_device *isp, 12908c2ecf20Sopenharmony_ci enum isp_subclk_resource res) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci isp->subclk_resources |= res; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci __isp_subclk_update(isp); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_civoid omap3isp_subclk_disable(struct isp_device *isp, 12988c2ecf20Sopenharmony_ci enum isp_subclk_resource res) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci isp->subclk_resources &= ~res; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci __isp_subclk_update(isp); 13038c2ecf20Sopenharmony_ci} 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci/* 13068c2ecf20Sopenharmony_ci * isp_enable_clocks - Enable ISP clocks 13078c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 13088c2ecf20Sopenharmony_ci * 13098c2ecf20Sopenharmony_ci * Return 0 if successful, or clk_prepare_enable return value if any of them 13108c2ecf20Sopenharmony_ci * fails. 13118c2ecf20Sopenharmony_ci */ 13128c2ecf20Sopenharmony_cistatic int isp_enable_clocks(struct isp_device *isp) 13138c2ecf20Sopenharmony_ci{ 13148c2ecf20Sopenharmony_ci int r; 13158c2ecf20Sopenharmony_ci unsigned long rate; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_ICK]); 13188c2ecf20Sopenharmony_ci if (r) { 13198c2ecf20Sopenharmony_ci dev_err(isp->dev, "failed to enable cam_ick clock\n"); 13208c2ecf20Sopenharmony_ci goto out_clk_enable_ick; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci r = clk_set_rate(isp->clock[ISP_CLK_CAM_MCLK], CM_CAM_MCLK_HZ); 13238c2ecf20Sopenharmony_ci if (r) { 13248c2ecf20Sopenharmony_ci dev_err(isp->dev, "clk_set_rate for cam_mclk failed\n"); 13258c2ecf20Sopenharmony_ci goto out_clk_enable_mclk; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CAM_MCLK]); 13288c2ecf20Sopenharmony_ci if (r) { 13298c2ecf20Sopenharmony_ci dev_err(isp->dev, "failed to enable cam_mclk clock\n"); 13308c2ecf20Sopenharmony_ci goto out_clk_enable_mclk; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci rate = clk_get_rate(isp->clock[ISP_CLK_CAM_MCLK]); 13338c2ecf20Sopenharmony_ci if (rate != CM_CAM_MCLK_HZ) 13348c2ecf20Sopenharmony_ci dev_warn(isp->dev, "unexpected cam_mclk rate:\n" 13358c2ecf20Sopenharmony_ci " expected : %d\n" 13368c2ecf20Sopenharmony_ci " actual : %ld\n", CM_CAM_MCLK_HZ, rate); 13378c2ecf20Sopenharmony_ci r = clk_prepare_enable(isp->clock[ISP_CLK_CSI2_FCK]); 13388c2ecf20Sopenharmony_ci if (r) { 13398c2ecf20Sopenharmony_ci dev_err(isp->dev, "failed to enable csi2_fck clock\n"); 13408c2ecf20Sopenharmony_ci goto out_clk_enable_csi2_fclk; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci return 0; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ciout_clk_enable_csi2_fclk: 13458c2ecf20Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); 13468c2ecf20Sopenharmony_ciout_clk_enable_mclk: 13478c2ecf20Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); 13488c2ecf20Sopenharmony_ciout_clk_enable_ick: 13498c2ecf20Sopenharmony_ci return r; 13508c2ecf20Sopenharmony_ci} 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci/* 13538c2ecf20Sopenharmony_ci * isp_disable_clocks - Disable ISP clocks 13548c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_cistatic void isp_disable_clocks(struct isp_device *isp) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_ICK]); 13598c2ecf20Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CAM_MCLK]); 13608c2ecf20Sopenharmony_ci clk_disable_unprepare(isp->clock[ISP_CLK_CSI2_FCK]); 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic const char *isp_clocks[] = { 13648c2ecf20Sopenharmony_ci "cam_ick", 13658c2ecf20Sopenharmony_ci "cam_mclk", 13668c2ecf20Sopenharmony_ci "csi2_96m_fck", 13678c2ecf20Sopenharmony_ci "l3_ick", 13688c2ecf20Sopenharmony_ci}; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_cistatic int isp_get_clocks(struct isp_device *isp) 13718c2ecf20Sopenharmony_ci{ 13728c2ecf20Sopenharmony_ci struct clk *clk; 13738c2ecf20Sopenharmony_ci unsigned int i; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp_clocks); ++i) { 13768c2ecf20Sopenharmony_ci clk = devm_clk_get(isp->dev, isp_clocks[i]); 13778c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 13788c2ecf20Sopenharmony_ci dev_err(isp->dev, "clk_get %s failed\n", isp_clocks[i]); 13798c2ecf20Sopenharmony_ci return PTR_ERR(clk); 13808c2ecf20Sopenharmony_ci } 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci isp->clock[i] = clk; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci return 0; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci/* 13898c2ecf20Sopenharmony_ci * omap3isp_get - Acquire the ISP resource. 13908c2ecf20Sopenharmony_ci * 13918c2ecf20Sopenharmony_ci * Initializes the clocks for the first acquire. 13928c2ecf20Sopenharmony_ci * 13938c2ecf20Sopenharmony_ci * Increment the reference count on the ISP. If the first reference is taken, 13948c2ecf20Sopenharmony_ci * enable clocks and power-up all submodules. 13958c2ecf20Sopenharmony_ci * 13968c2ecf20Sopenharmony_ci * Return a pointer to the ISP device structure, or NULL if an error occurred. 13978c2ecf20Sopenharmony_ci */ 13988c2ecf20Sopenharmony_cistatic struct isp_device *__omap3isp_get(struct isp_device *isp, bool irq) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct isp_device *__isp = isp; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (isp == NULL) 14038c2ecf20Sopenharmony_ci return NULL; 14048c2ecf20Sopenharmony_ci 14058c2ecf20Sopenharmony_ci mutex_lock(&isp->isp_mutex); 14068c2ecf20Sopenharmony_ci if (isp->ref_count > 0) 14078c2ecf20Sopenharmony_ci goto out; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (isp_enable_clocks(isp) < 0) { 14108c2ecf20Sopenharmony_ci __isp = NULL; 14118c2ecf20Sopenharmony_ci goto out; 14128c2ecf20Sopenharmony_ci } 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_ci /* We don't want to restore context before saving it! */ 14158c2ecf20Sopenharmony_ci if (isp->has_context) 14168c2ecf20Sopenharmony_ci isp_restore_ctx(isp); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (irq) 14198c2ecf20Sopenharmony_ci isp_enable_interrupts(isp); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ciout: 14228c2ecf20Sopenharmony_ci if (__isp != NULL) 14238c2ecf20Sopenharmony_ci isp->ref_count++; 14248c2ecf20Sopenharmony_ci mutex_unlock(&isp->isp_mutex); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci return __isp; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistruct isp_device *omap3isp_get(struct isp_device *isp) 14308c2ecf20Sopenharmony_ci{ 14318c2ecf20Sopenharmony_ci return __omap3isp_get(isp, true); 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci/* 14358c2ecf20Sopenharmony_ci * omap3isp_put - Release the ISP 14368c2ecf20Sopenharmony_ci * 14378c2ecf20Sopenharmony_ci * Decrement the reference count on the ISP. If the last reference is released, 14388c2ecf20Sopenharmony_ci * power-down all submodules, disable clocks and free temporary buffers. 14398c2ecf20Sopenharmony_ci */ 14408c2ecf20Sopenharmony_cistatic void __omap3isp_put(struct isp_device *isp, bool save_ctx) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci if (isp == NULL) 14438c2ecf20Sopenharmony_ci return; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci mutex_lock(&isp->isp_mutex); 14468c2ecf20Sopenharmony_ci BUG_ON(isp->ref_count == 0); 14478c2ecf20Sopenharmony_ci if (--isp->ref_count == 0) { 14488c2ecf20Sopenharmony_ci isp_disable_interrupts(isp); 14498c2ecf20Sopenharmony_ci if (save_ctx) { 14508c2ecf20Sopenharmony_ci isp_save_ctx(isp); 14518c2ecf20Sopenharmony_ci isp->has_context = 1; 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci /* Reset the ISP if an entity has failed to stop. This is the 14548c2ecf20Sopenharmony_ci * only way to recover from such conditions. 14558c2ecf20Sopenharmony_ci */ 14568c2ecf20Sopenharmony_ci if (!media_entity_enum_empty(&isp->crashed) || 14578c2ecf20Sopenharmony_ci isp->stop_failure) 14588c2ecf20Sopenharmony_ci isp_reset(isp); 14598c2ecf20Sopenharmony_ci isp_disable_clocks(isp); 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci mutex_unlock(&isp->isp_mutex); 14628c2ecf20Sopenharmony_ci} 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_civoid omap3isp_put(struct isp_device *isp) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci __omap3isp_put(isp, true); 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/* -------------------------------------------------------------------------- 14708c2ecf20Sopenharmony_ci * Platform device driver 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci/* 14748c2ecf20Sopenharmony_ci * omap3isp_print_status - Prints the values of the ISP Control Module registers 14758c2ecf20Sopenharmony_ci * @isp: OMAP3 ISP device 14768c2ecf20Sopenharmony_ci */ 14778c2ecf20Sopenharmony_ci#define ISP_PRINT_REGISTER(isp, name)\ 14788c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \ 14798c2ecf20Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name)) 14808c2ecf20Sopenharmony_ci#define SBL_PRINT_REGISTER(isp, name)\ 14818c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \ 14828c2ecf20Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name)) 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_civoid omap3isp_print_status(struct isp_device *isp) 14858c2ecf20Sopenharmony_ci{ 14868c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "-------------ISP Register dump--------------\n"); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, SYSCONFIG); 14898c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, SYSSTATUS); 14908c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, IRQ0ENABLE); 14918c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, IRQ0STATUS); 14928c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH); 14938c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY); 14948c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, CTRL); 14958c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_CTRL); 14968c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_FRAME); 14978c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY); 14988c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY); 14998c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY); 15008c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH); 15018c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH); 15028c2ecf20Sopenharmony_ci ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci SBL_PRINT_REGISTER(isp, PCR); 15058c2ecf20Sopenharmony_ci SBL_PRINT_REGISTER(isp, SDR_REQ_EXP); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "--------------------------------------------\n"); 15088c2ecf20Sopenharmony_ci} 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci/* 15138c2ecf20Sopenharmony_ci * Power management support. 15148c2ecf20Sopenharmony_ci * 15158c2ecf20Sopenharmony_ci * As the ISP can't properly handle an input video stream interruption on a non 15168c2ecf20Sopenharmony_ci * frame boundary, the ISP pipelines need to be stopped before sensors get 15178c2ecf20Sopenharmony_ci * suspended. However, as suspending the sensors can require a running clock, 15188c2ecf20Sopenharmony_ci * which can be provided by the ISP, the ISP can't be completely suspended 15198c2ecf20Sopenharmony_ci * before the sensor. 15208c2ecf20Sopenharmony_ci * 15218c2ecf20Sopenharmony_ci * To solve this problem power management support is split into prepare/complete 15228c2ecf20Sopenharmony_ci * and suspend/resume operations. The pipelines are stopped in prepare() and the 15238c2ecf20Sopenharmony_ci * ISP clocks get disabled in suspend(). Similarly, the clocks are re-enabled in 15248c2ecf20Sopenharmony_ci * resume(), and the the pipelines are restarted in complete(). 15258c2ecf20Sopenharmony_ci * 15268c2ecf20Sopenharmony_ci * TODO: PM dependencies between the ISP and sensors are not modelled explicitly 15278c2ecf20Sopenharmony_ci * yet. 15288c2ecf20Sopenharmony_ci */ 15298c2ecf20Sopenharmony_cistatic int isp_pm_prepare(struct device *dev) 15308c2ecf20Sopenharmony_ci{ 15318c2ecf20Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 15328c2ecf20Sopenharmony_ci int reset; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci WARN_ON(mutex_is_locked(&isp->isp_mutex)); 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci if (isp->ref_count == 0) 15378c2ecf20Sopenharmony_ci return 0; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci reset = isp_suspend_modules(isp); 15408c2ecf20Sopenharmony_ci isp_disable_interrupts(isp); 15418c2ecf20Sopenharmony_ci isp_save_ctx(isp); 15428c2ecf20Sopenharmony_ci if (reset) 15438c2ecf20Sopenharmony_ci isp_reset(isp); 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci return 0; 15468c2ecf20Sopenharmony_ci} 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_cistatic int isp_pm_suspend(struct device *dev) 15498c2ecf20Sopenharmony_ci{ 15508c2ecf20Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci WARN_ON(mutex_is_locked(&isp->isp_mutex)); 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci if (isp->ref_count) 15558c2ecf20Sopenharmony_ci isp_disable_clocks(isp); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return 0; 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic int isp_pm_resume(struct device *dev) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (isp->ref_count == 0) 15658c2ecf20Sopenharmony_ci return 0; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci return isp_enable_clocks(isp); 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_cistatic void isp_pm_complete(struct device *dev) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci struct isp_device *isp = dev_get_drvdata(dev); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (isp->ref_count == 0) 15758c2ecf20Sopenharmony_ci return; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci isp_restore_ctx(isp); 15788c2ecf20Sopenharmony_ci isp_enable_interrupts(isp); 15798c2ecf20Sopenharmony_ci isp_resume_modules(isp); 15808c2ecf20Sopenharmony_ci} 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci#else 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci#define isp_pm_prepare NULL 15858c2ecf20Sopenharmony_ci#define isp_pm_suspend NULL 15868c2ecf20Sopenharmony_ci#define isp_pm_resume NULL 15878c2ecf20Sopenharmony_ci#define isp_pm_complete NULL 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic void isp_unregister_entities(struct isp_device *isp) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci media_device_unregister(&isp->media_dev); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci omap3isp_csi2_unregister_entities(&isp->isp_csi2a); 15968c2ecf20Sopenharmony_ci omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); 15978c2ecf20Sopenharmony_ci omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); 15988c2ecf20Sopenharmony_ci omap3isp_preview_unregister_entities(&isp->isp_prev); 15998c2ecf20Sopenharmony_ci omap3isp_resizer_unregister_entities(&isp->isp_res); 16008c2ecf20Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_aewb); 16018c2ecf20Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_af); 16028c2ecf20Sopenharmony_ci omap3isp_stat_unregister_entities(&isp->isp_hist); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci v4l2_device_unregister(&isp->v4l2_dev); 16058c2ecf20Sopenharmony_ci media_device_cleanup(&isp->media_dev); 16068c2ecf20Sopenharmony_ci} 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_cistatic int isp_link_entity( 16098c2ecf20Sopenharmony_ci struct isp_device *isp, struct media_entity *entity, 16108c2ecf20Sopenharmony_ci enum isp_interface_type interface) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci struct media_entity *input; 16138c2ecf20Sopenharmony_ci unsigned int flags; 16148c2ecf20Sopenharmony_ci unsigned int pad; 16158c2ecf20Sopenharmony_ci unsigned int i; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci /* Connect the sensor to the correct interface module. 16188c2ecf20Sopenharmony_ci * Parallel sensors are connected directly to the CCDC, while 16198c2ecf20Sopenharmony_ci * serial sensors are connected to the CSI2a, CCP2b or CSI2c 16208c2ecf20Sopenharmony_ci * receiver through CSIPHY1 or CSIPHY2. 16218c2ecf20Sopenharmony_ci */ 16228c2ecf20Sopenharmony_ci switch (interface) { 16238c2ecf20Sopenharmony_ci case ISP_INTERFACE_PARALLEL: 16248c2ecf20Sopenharmony_ci input = &isp->isp_ccdc.subdev.entity; 16258c2ecf20Sopenharmony_ci pad = CCDC_PAD_SINK; 16268c2ecf20Sopenharmony_ci flags = 0; 16278c2ecf20Sopenharmony_ci break; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci case ISP_INTERFACE_CSI2A_PHY2: 16308c2ecf20Sopenharmony_ci input = &isp->isp_csi2a.subdev.entity; 16318c2ecf20Sopenharmony_ci pad = CSI2_PAD_SINK; 16328c2ecf20Sopenharmony_ci flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; 16338c2ecf20Sopenharmony_ci break; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY1: 16368c2ecf20Sopenharmony_ci case ISP_INTERFACE_CCP2B_PHY2: 16378c2ecf20Sopenharmony_ci input = &isp->isp_ccp2.subdev.entity; 16388c2ecf20Sopenharmony_ci pad = CCP2_PAD_SINK; 16398c2ecf20Sopenharmony_ci flags = 0; 16408c2ecf20Sopenharmony_ci break; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci case ISP_INTERFACE_CSI2C_PHY1: 16438c2ecf20Sopenharmony_ci input = &isp->isp_csi2c.subdev.entity; 16448c2ecf20Sopenharmony_ci pad = CSI2_PAD_SINK; 16458c2ecf20Sopenharmony_ci flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; 16468c2ecf20Sopenharmony_ci break; 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_ci default: 16498c2ecf20Sopenharmony_ci dev_err(isp->dev, "%s: invalid interface type %u\n", __func__, 16508c2ecf20Sopenharmony_ci interface); 16518c2ecf20Sopenharmony_ci return -EINVAL; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci /* 16558c2ecf20Sopenharmony_ci * Not all interfaces are available on all revisions of the 16568c2ecf20Sopenharmony_ci * ISP. The sub-devices of those interfaces aren't initialised 16578c2ecf20Sopenharmony_ci * in such a case. Check this by ensuring the num_pads is 16588c2ecf20Sopenharmony_ci * non-zero. 16598c2ecf20Sopenharmony_ci */ 16608c2ecf20Sopenharmony_ci if (!input->num_pads) { 16618c2ecf20Sopenharmony_ci dev_err(isp->dev, "%s: invalid input %u\n", entity->name, 16628c2ecf20Sopenharmony_ci interface); 16638c2ecf20Sopenharmony_ci return -EINVAL; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci for (i = 0; i < entity->num_pads; i++) { 16678c2ecf20Sopenharmony_ci if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE) 16688c2ecf20Sopenharmony_ci break; 16698c2ecf20Sopenharmony_ci } 16708c2ecf20Sopenharmony_ci if (i == entity->num_pads) { 16718c2ecf20Sopenharmony_ci dev_err(isp->dev, "%s: no source pad in external entity %s\n", 16728c2ecf20Sopenharmony_ci __func__, entity->name); 16738c2ecf20Sopenharmony_ci return -EINVAL; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci return media_create_pad_link(entity, i, input, pad, flags); 16778c2ecf20Sopenharmony_ci} 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic int isp_register_entities(struct isp_device *isp) 16808c2ecf20Sopenharmony_ci{ 16818c2ecf20Sopenharmony_ci int ret; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci isp->media_dev.dev = isp->dev; 16848c2ecf20Sopenharmony_ci strscpy(isp->media_dev.model, "TI OMAP3 ISP", 16858c2ecf20Sopenharmony_ci sizeof(isp->media_dev.model)); 16868c2ecf20Sopenharmony_ci isp->media_dev.hw_revision = isp->revision; 16878c2ecf20Sopenharmony_ci isp->media_dev.ops = &isp_media_ops; 16888c2ecf20Sopenharmony_ci media_device_init(&isp->media_dev); 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci isp->v4l2_dev.mdev = &isp->media_dev; 16918c2ecf20Sopenharmony_ci ret = v4l2_device_register(isp->dev, &isp->v4l2_dev); 16928c2ecf20Sopenharmony_ci if (ret < 0) { 16938c2ecf20Sopenharmony_ci dev_err(isp->dev, "%s: V4L2 device registration failed (%d)\n", 16948c2ecf20Sopenharmony_ci __func__, ret); 16958c2ecf20Sopenharmony_ci goto done; 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci /* Register internal entities */ 16998c2ecf20Sopenharmony_ci ret = omap3isp_ccp2_register_entities(&isp->isp_ccp2, &isp->v4l2_dev); 17008c2ecf20Sopenharmony_ci if (ret < 0) 17018c2ecf20Sopenharmony_ci goto done; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci ret = omap3isp_csi2_register_entities(&isp->isp_csi2a, &isp->v4l2_dev); 17048c2ecf20Sopenharmony_ci if (ret < 0) 17058c2ecf20Sopenharmony_ci goto done; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci ret = omap3isp_ccdc_register_entities(&isp->isp_ccdc, &isp->v4l2_dev); 17088c2ecf20Sopenharmony_ci if (ret < 0) 17098c2ecf20Sopenharmony_ci goto done; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci ret = omap3isp_preview_register_entities(&isp->isp_prev, 17128c2ecf20Sopenharmony_ci &isp->v4l2_dev); 17138c2ecf20Sopenharmony_ci if (ret < 0) 17148c2ecf20Sopenharmony_ci goto done; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci ret = omap3isp_resizer_register_entities(&isp->isp_res, &isp->v4l2_dev); 17178c2ecf20Sopenharmony_ci if (ret < 0) 17188c2ecf20Sopenharmony_ci goto done; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_aewb, &isp->v4l2_dev); 17218c2ecf20Sopenharmony_ci if (ret < 0) 17228c2ecf20Sopenharmony_ci goto done; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_af, &isp->v4l2_dev); 17258c2ecf20Sopenharmony_ci if (ret < 0) 17268c2ecf20Sopenharmony_ci goto done; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci ret = omap3isp_stat_register_entities(&isp->isp_hist, &isp->v4l2_dev); 17298c2ecf20Sopenharmony_ci if (ret < 0) 17308c2ecf20Sopenharmony_ci goto done; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_cidone: 17338c2ecf20Sopenharmony_ci if (ret < 0) 17348c2ecf20Sopenharmony_ci isp_unregister_entities(isp); 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci return ret; 17378c2ecf20Sopenharmony_ci} 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci/* 17408c2ecf20Sopenharmony_ci * isp_create_links() - Create links for internal and external ISP entities 17418c2ecf20Sopenharmony_ci * @isp : Pointer to ISP device 17428c2ecf20Sopenharmony_ci * 17438c2ecf20Sopenharmony_ci * This function creates all links between ISP internal and external entities. 17448c2ecf20Sopenharmony_ci * 17458c2ecf20Sopenharmony_ci * Return: A negative error code on failure or zero on success. Possible error 17468c2ecf20Sopenharmony_ci * codes are those returned by media_create_pad_link(). 17478c2ecf20Sopenharmony_ci */ 17488c2ecf20Sopenharmony_cistatic int isp_create_links(struct isp_device *isp) 17498c2ecf20Sopenharmony_ci{ 17508c2ecf20Sopenharmony_ci int ret; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci /* Create links between entities and video nodes. */ 17538c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17548c2ecf20Sopenharmony_ci &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, 17558c2ecf20Sopenharmony_ci &isp->isp_csi2a.video_out.video.entity, 0, 0); 17568c2ecf20Sopenharmony_ci if (ret < 0) 17578c2ecf20Sopenharmony_ci return ret; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17608c2ecf20Sopenharmony_ci &isp->isp_ccp2.video_in.video.entity, 0, 17618c2ecf20Sopenharmony_ci &isp->isp_ccp2.subdev.entity, CCP2_PAD_SINK, 0); 17628c2ecf20Sopenharmony_ci if (ret < 0) 17638c2ecf20Sopenharmony_ci return ret; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17668c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, 17678c2ecf20Sopenharmony_ci &isp->isp_ccdc.video_out.video.entity, 0, 0); 17688c2ecf20Sopenharmony_ci if (ret < 0) 17698c2ecf20Sopenharmony_ci return ret; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17728c2ecf20Sopenharmony_ci &isp->isp_prev.video_in.video.entity, 0, 17738c2ecf20Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); 17748c2ecf20Sopenharmony_ci if (ret < 0) 17758c2ecf20Sopenharmony_ci return ret; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17788c2ecf20Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, 17798c2ecf20Sopenharmony_ci &isp->isp_prev.video_out.video.entity, 0, 0); 17808c2ecf20Sopenharmony_ci if (ret < 0) 17818c2ecf20Sopenharmony_ci return ret; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17848c2ecf20Sopenharmony_ci &isp->isp_res.video_in.video.entity, 0, 17858c2ecf20Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 17868c2ecf20Sopenharmony_ci if (ret < 0) 17878c2ecf20Sopenharmony_ci return ret; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17908c2ecf20Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SOURCE, 17918c2ecf20Sopenharmony_ci &isp->isp_res.video_out.video.entity, 0, 0); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci if (ret < 0) 17948c2ecf20Sopenharmony_ci return ret; 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci /* Create links between entities. */ 17978c2ecf20Sopenharmony_ci ret = media_create_pad_link( 17988c2ecf20Sopenharmony_ci &isp->isp_csi2a.subdev.entity, CSI2_PAD_SOURCE, 17998c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); 18008c2ecf20Sopenharmony_ci if (ret < 0) 18018c2ecf20Sopenharmony_ci return ret; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18048c2ecf20Sopenharmony_ci &isp->isp_ccp2.subdev.entity, CCP2_PAD_SOURCE, 18058c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SINK, 0); 18068c2ecf20Sopenharmony_ci if (ret < 0) 18078c2ecf20Sopenharmony_ci return ret; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18108c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 18118c2ecf20Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SINK, 0); 18128c2ecf20Sopenharmony_ci if (ret < 0) 18138c2ecf20Sopenharmony_ci return ret; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18168c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_OF, 18178c2ecf20Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 18188c2ecf20Sopenharmony_ci if (ret < 0) 18198c2ecf20Sopenharmony_ci return ret; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18228c2ecf20Sopenharmony_ci &isp->isp_prev.subdev.entity, PREV_PAD_SOURCE, 18238c2ecf20Sopenharmony_ci &isp->isp_res.subdev.entity, RESZ_PAD_SINK, 0); 18248c2ecf20Sopenharmony_ci if (ret < 0) 18258c2ecf20Sopenharmony_ci return ret; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18288c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 18298c2ecf20Sopenharmony_ci &isp->isp_aewb.subdev.entity, 0, 18308c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 18318c2ecf20Sopenharmony_ci if (ret < 0) 18328c2ecf20Sopenharmony_ci return ret; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18358c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 18368c2ecf20Sopenharmony_ci &isp->isp_af.subdev.entity, 0, 18378c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 18388c2ecf20Sopenharmony_ci if (ret < 0) 18398c2ecf20Sopenharmony_ci return ret; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci ret = media_create_pad_link( 18428c2ecf20Sopenharmony_ci &isp->isp_ccdc.subdev.entity, CCDC_PAD_SOURCE_VP, 18438c2ecf20Sopenharmony_ci &isp->isp_hist.subdev.entity, 0, 18448c2ecf20Sopenharmony_ci MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE); 18458c2ecf20Sopenharmony_ci if (ret < 0) 18468c2ecf20Sopenharmony_ci return ret; 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci return 0; 18498c2ecf20Sopenharmony_ci} 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_cistatic void isp_cleanup_modules(struct isp_device *isp) 18528c2ecf20Sopenharmony_ci{ 18538c2ecf20Sopenharmony_ci omap3isp_h3a_aewb_cleanup(isp); 18548c2ecf20Sopenharmony_ci omap3isp_h3a_af_cleanup(isp); 18558c2ecf20Sopenharmony_ci omap3isp_hist_cleanup(isp); 18568c2ecf20Sopenharmony_ci omap3isp_resizer_cleanup(isp); 18578c2ecf20Sopenharmony_ci omap3isp_preview_cleanup(isp); 18588c2ecf20Sopenharmony_ci omap3isp_ccdc_cleanup(isp); 18598c2ecf20Sopenharmony_ci omap3isp_ccp2_cleanup(isp); 18608c2ecf20Sopenharmony_ci omap3isp_csi2_cleanup(isp); 18618c2ecf20Sopenharmony_ci omap3isp_csiphy_cleanup(isp); 18628c2ecf20Sopenharmony_ci} 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_cistatic int isp_initialize_modules(struct isp_device *isp) 18658c2ecf20Sopenharmony_ci{ 18668c2ecf20Sopenharmony_ci int ret; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci ret = omap3isp_csiphy_init(isp); 18698c2ecf20Sopenharmony_ci if (ret < 0) { 18708c2ecf20Sopenharmony_ci dev_err(isp->dev, "CSI PHY initialization failed\n"); 18718c2ecf20Sopenharmony_ci return ret; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci ret = omap3isp_csi2_init(isp); 18758c2ecf20Sopenharmony_ci if (ret < 0) { 18768c2ecf20Sopenharmony_ci dev_err(isp->dev, "CSI2 initialization failed\n"); 18778c2ecf20Sopenharmony_ci goto error_csi2; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci ret = omap3isp_ccp2_init(isp); 18818c2ecf20Sopenharmony_ci if (ret < 0) { 18828c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 18838c2ecf20Sopenharmony_ci dev_err(isp->dev, "CCP2 initialization failed\n"); 18848c2ecf20Sopenharmony_ci goto error_ccp2; 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci ret = omap3isp_ccdc_init(isp); 18888c2ecf20Sopenharmony_ci if (ret < 0) { 18898c2ecf20Sopenharmony_ci dev_err(isp->dev, "CCDC initialization failed\n"); 18908c2ecf20Sopenharmony_ci goto error_ccdc; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci ret = omap3isp_preview_init(isp); 18948c2ecf20Sopenharmony_ci if (ret < 0) { 18958c2ecf20Sopenharmony_ci dev_err(isp->dev, "Preview initialization failed\n"); 18968c2ecf20Sopenharmony_ci goto error_preview; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci ret = omap3isp_resizer_init(isp); 19008c2ecf20Sopenharmony_ci if (ret < 0) { 19018c2ecf20Sopenharmony_ci dev_err(isp->dev, "Resizer initialization failed\n"); 19028c2ecf20Sopenharmony_ci goto error_resizer; 19038c2ecf20Sopenharmony_ci } 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci ret = omap3isp_hist_init(isp); 19068c2ecf20Sopenharmony_ci if (ret < 0) { 19078c2ecf20Sopenharmony_ci dev_err(isp->dev, "Histogram initialization failed\n"); 19088c2ecf20Sopenharmony_ci goto error_hist; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci ret = omap3isp_h3a_aewb_init(isp); 19128c2ecf20Sopenharmony_ci if (ret < 0) { 19138c2ecf20Sopenharmony_ci dev_err(isp->dev, "H3A AEWB initialization failed\n"); 19148c2ecf20Sopenharmony_ci goto error_h3a_aewb; 19158c2ecf20Sopenharmony_ci } 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci ret = omap3isp_h3a_af_init(isp); 19188c2ecf20Sopenharmony_ci if (ret < 0) { 19198c2ecf20Sopenharmony_ci dev_err(isp->dev, "H3A AF initialization failed\n"); 19208c2ecf20Sopenharmony_ci goto error_h3a_af; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci return 0; 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_cierror_h3a_af: 19268c2ecf20Sopenharmony_ci omap3isp_h3a_aewb_cleanup(isp); 19278c2ecf20Sopenharmony_cierror_h3a_aewb: 19288c2ecf20Sopenharmony_ci omap3isp_hist_cleanup(isp); 19298c2ecf20Sopenharmony_cierror_hist: 19308c2ecf20Sopenharmony_ci omap3isp_resizer_cleanup(isp); 19318c2ecf20Sopenharmony_cierror_resizer: 19328c2ecf20Sopenharmony_ci omap3isp_preview_cleanup(isp); 19338c2ecf20Sopenharmony_cierror_preview: 19348c2ecf20Sopenharmony_ci omap3isp_ccdc_cleanup(isp); 19358c2ecf20Sopenharmony_cierror_ccdc: 19368c2ecf20Sopenharmony_ci omap3isp_ccp2_cleanup(isp); 19378c2ecf20Sopenharmony_cierror_ccp2: 19388c2ecf20Sopenharmony_ci omap3isp_csi2_cleanup(isp); 19398c2ecf20Sopenharmony_cierror_csi2: 19408c2ecf20Sopenharmony_ci omap3isp_csiphy_cleanup(isp); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci return ret; 19438c2ecf20Sopenharmony_ci} 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_cistatic void isp_detach_iommu(struct isp_device *isp) 19468c2ecf20Sopenharmony_ci{ 19478c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 19488c2ecf20Sopenharmony_ci arm_iommu_detach_device(isp->dev); 19498c2ecf20Sopenharmony_ci arm_iommu_release_mapping(isp->mapping); 19508c2ecf20Sopenharmony_ci isp->mapping = NULL; 19518c2ecf20Sopenharmony_ci#endif 19528c2ecf20Sopenharmony_ci} 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_cistatic int isp_attach_iommu(struct isp_device *isp) 19558c2ecf20Sopenharmony_ci{ 19568c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM_DMA_USE_IOMMU 19578c2ecf20Sopenharmony_ci struct dma_iommu_mapping *mapping; 19588c2ecf20Sopenharmony_ci int ret; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* 19618c2ecf20Sopenharmony_ci * Create the ARM mapping, used by the ARM DMA mapping core to allocate 19628c2ecf20Sopenharmony_ci * VAs. This will allocate a corresponding IOMMU domain. 19638c2ecf20Sopenharmony_ci */ 19648c2ecf20Sopenharmony_ci mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G); 19658c2ecf20Sopenharmony_ci if (IS_ERR(mapping)) { 19668c2ecf20Sopenharmony_ci dev_err(isp->dev, "failed to create ARM IOMMU mapping\n"); 19678c2ecf20Sopenharmony_ci return PTR_ERR(mapping); 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci isp->mapping = mapping; 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_ci /* Attach the ARM VA mapping to the device. */ 19738c2ecf20Sopenharmony_ci ret = arm_iommu_attach_device(isp->dev, mapping); 19748c2ecf20Sopenharmony_ci if (ret < 0) { 19758c2ecf20Sopenharmony_ci dev_err(isp->dev, "failed to attach device to VA mapping\n"); 19768c2ecf20Sopenharmony_ci goto error; 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci return 0; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_cierror: 19828c2ecf20Sopenharmony_ci arm_iommu_release_mapping(isp->mapping); 19838c2ecf20Sopenharmony_ci isp->mapping = NULL; 19848c2ecf20Sopenharmony_ci return ret; 19858c2ecf20Sopenharmony_ci#else 19868c2ecf20Sopenharmony_ci return -ENODEV; 19878c2ecf20Sopenharmony_ci#endif 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci/* 19918c2ecf20Sopenharmony_ci * isp_remove - Remove ISP platform device 19928c2ecf20Sopenharmony_ci * @pdev: Pointer to ISP platform device 19938c2ecf20Sopenharmony_ci * 19948c2ecf20Sopenharmony_ci * Always returns 0. 19958c2ecf20Sopenharmony_ci */ 19968c2ecf20Sopenharmony_cistatic int isp_remove(struct platform_device *pdev) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci struct isp_device *isp = platform_get_drvdata(pdev); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(&isp->notifier); 20018c2ecf20Sopenharmony_ci isp_unregister_entities(isp); 20028c2ecf20Sopenharmony_ci isp_cleanup_modules(isp); 20038c2ecf20Sopenharmony_ci isp_xclk_cleanup(isp); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci __omap3isp_get(isp, false); 20068c2ecf20Sopenharmony_ci isp_detach_iommu(isp); 20078c2ecf20Sopenharmony_ci __omap3isp_put(isp, false); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci media_entity_enum_cleanup(&isp->crashed); 20108c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&isp->notifier); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci kfree(isp); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci return 0; 20158c2ecf20Sopenharmony_ci} 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cienum isp_of_phy { 20188c2ecf20Sopenharmony_ci ISP_OF_PHY_PARALLEL = 0, 20198c2ecf20Sopenharmony_ci ISP_OF_PHY_CSIPHY1, 20208c2ecf20Sopenharmony_ci ISP_OF_PHY_CSIPHY2, 20218c2ecf20Sopenharmony_ci}; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) 20248c2ecf20Sopenharmony_ci{ 20258c2ecf20Sopenharmony_ci struct isp_device *isp = container_of(async, struct isp_device, 20268c2ecf20Sopenharmony_ci notifier); 20278c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = &isp->v4l2_dev; 20288c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 20298c2ecf20Sopenharmony_ci int ret; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci ret = media_entity_enum_init(&isp->crashed, &isp->media_dev); 20328c2ecf20Sopenharmony_ci if (ret) 20338c2ecf20Sopenharmony_ci return ret; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci list_for_each_entry(sd, &v4l2_dev->subdevs, list) { 20368c2ecf20Sopenharmony_ci if (sd->notifier != &isp->notifier) 20378c2ecf20Sopenharmony_ci continue; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci ret = isp_link_entity(isp, &sd->entity, 20408c2ecf20Sopenharmony_ci v4l2_subdev_to_bus_cfg(sd)->interface); 20418c2ecf20Sopenharmony_ci if (ret < 0) 20428c2ecf20Sopenharmony_ci return ret; 20438c2ecf20Sopenharmony_ci } 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev); 20468c2ecf20Sopenharmony_ci if (ret < 0) 20478c2ecf20Sopenharmony_ci return ret; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci return media_device_register(&isp->media_dev); 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic void isp_parse_of_parallel_endpoint(struct device *dev, 20538c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 20548c2ecf20Sopenharmony_ci struct isp_bus_cfg *buscfg) 20558c2ecf20Sopenharmony_ci{ 20568c2ecf20Sopenharmony_ci buscfg->interface = ISP_INTERFACE_PARALLEL; 20578c2ecf20Sopenharmony_ci buscfg->bus.parallel.data_lane_shift = vep->bus.parallel.data_shift; 20588c2ecf20Sopenharmony_ci buscfg->bus.parallel.clk_pol = 20598c2ecf20Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING); 20608c2ecf20Sopenharmony_ci buscfg->bus.parallel.hs_pol = 20618c2ecf20Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW); 20628c2ecf20Sopenharmony_ci buscfg->bus.parallel.vs_pol = 20638c2ecf20Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW); 20648c2ecf20Sopenharmony_ci buscfg->bus.parallel.fld_pol = 20658c2ecf20Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_FIELD_EVEN_LOW); 20668c2ecf20Sopenharmony_ci buscfg->bus.parallel.data_pol = 20678c2ecf20Sopenharmony_ci !!(vep->bus.parallel.flags & V4L2_MBUS_DATA_ACTIVE_LOW); 20688c2ecf20Sopenharmony_ci buscfg->bus.parallel.bt656 = vep->bus_type == V4L2_MBUS_BT656; 20698c2ecf20Sopenharmony_ci} 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_cistatic void isp_parse_of_csi2_endpoint(struct device *dev, 20728c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 20738c2ecf20Sopenharmony_ci struct isp_bus_cfg *buscfg) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci unsigned int i; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pos = vep->bus.mipi_csi2.clock_lane; 20788c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pol = 20798c2ecf20Sopenharmony_ci vep->bus.mipi_csi2.lane_polarities[0]; 20808c2ecf20Sopenharmony_ci dev_dbg(dev, "clock lane polarity %u, pos %u\n", 20818c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pol, 20828c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.clk.pos); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci buscfg->bus.csi2.num_data_lanes = vep->bus.mipi_csi2.num_data_lanes; 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci for (i = 0; i < buscfg->bus.csi2.num_data_lanes; i++) { 20878c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pos = 20888c2ecf20Sopenharmony_ci vep->bus.mipi_csi2.data_lanes[i]; 20898c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pol = 20908c2ecf20Sopenharmony_ci vep->bus.mipi_csi2.lane_polarities[i + 1]; 20918c2ecf20Sopenharmony_ci dev_dbg(dev, 20928c2ecf20Sopenharmony_ci "data lane %u polarity %u, pos %u\n", i, 20938c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pol, 20948c2ecf20Sopenharmony_ci buscfg->bus.csi2.lanecfg.data[i].pos); 20958c2ecf20Sopenharmony_ci } 20968c2ecf20Sopenharmony_ci /* 20978c2ecf20Sopenharmony_ci * FIXME: now we assume the CRC is always there. Implement a way to 20988c2ecf20Sopenharmony_ci * obtain this information from the sensor. Frame descriptors, perhaps? 20998c2ecf20Sopenharmony_ci */ 21008c2ecf20Sopenharmony_ci buscfg->bus.csi2.crc = 1; 21018c2ecf20Sopenharmony_ci} 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_cistatic void isp_parse_of_csi1_endpoint(struct device *dev, 21048c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint *vep, 21058c2ecf20Sopenharmony_ci struct isp_bus_cfg *buscfg) 21068c2ecf20Sopenharmony_ci{ 21078c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pos = vep->bus.mipi_csi1.clock_lane; 21088c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pol = vep->bus.mipi_csi1.lane_polarity[0]; 21098c2ecf20Sopenharmony_ci dev_dbg(dev, "clock lane polarity %u, pos %u\n", 21108c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pol, 21118c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.clk.pos); 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pos = vep->bus.mipi_csi1.data_lane; 21148c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pol = 21158c2ecf20Sopenharmony_ci vep->bus.mipi_csi1.lane_polarity[1]; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci dev_dbg(dev, "data lane polarity %u, pos %u\n", 21188c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pol, 21198c2ecf20Sopenharmony_ci buscfg->bus.ccp2.lanecfg.data[0].pos); 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci buscfg->bus.ccp2.strobe_clk_pol = vep->bus.mipi_csi1.clock_inv; 21228c2ecf20Sopenharmony_ci buscfg->bus.ccp2.phy_layer = vep->bus.mipi_csi1.strobe; 21238c2ecf20Sopenharmony_ci buscfg->bus.ccp2.ccp2_mode = vep->bus_type == V4L2_MBUS_CCP2; 21248c2ecf20Sopenharmony_ci buscfg->bus.ccp2.vp_clk_pol = 1; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci buscfg->bus.ccp2.crc = 1; 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_cistatic struct { 21308c2ecf20Sopenharmony_ci u32 phy; 21318c2ecf20Sopenharmony_ci u32 csi2_if; 21328c2ecf20Sopenharmony_ci u32 csi1_if; 21338c2ecf20Sopenharmony_ci} isp_bus_interfaces[2] = { 21348c2ecf20Sopenharmony_ci { ISP_OF_PHY_CSIPHY1, 21358c2ecf20Sopenharmony_ci ISP_INTERFACE_CSI2C_PHY1, ISP_INTERFACE_CCP2B_PHY1 }, 21368c2ecf20Sopenharmony_ci { ISP_OF_PHY_CSIPHY2, 21378c2ecf20Sopenharmony_ci ISP_INTERFACE_CSI2A_PHY2, ISP_INTERFACE_CCP2B_PHY2 }, 21388c2ecf20Sopenharmony_ci}; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_cistatic int isp_parse_of_endpoints(struct isp_device *isp) 21418c2ecf20Sopenharmony_ci{ 21428c2ecf20Sopenharmony_ci struct fwnode_handle *ep; 21438c2ecf20Sopenharmony_ci struct isp_async_subdev *isd = NULL; 21448c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd; 21458c2ecf20Sopenharmony_ci unsigned int i; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id( 21488c2ecf20Sopenharmony_ci dev_fwnode(isp->dev), ISP_OF_PHY_PARALLEL, 0, 21498c2ecf20Sopenharmony_ci FWNODE_GRAPH_ENDPOINT_NEXT); 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (ep) { 21528c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { 21538c2ecf20Sopenharmony_ci .bus_type = V4L2_MBUS_PARALLEL 21548c2ecf20Sopenharmony_ci }; 21558c2ecf20Sopenharmony_ci int ret; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "parsing parallel interface\n"); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (!ret) { 21628c2ecf20Sopenharmony_ci asd = v4l2_async_notifier_add_fwnode_remote_subdev( 21638c2ecf20Sopenharmony_ci &isp->notifier, ep, sizeof(*isd)); 21648c2ecf20Sopenharmony_ci if (!IS_ERR(asd)) { 21658c2ecf20Sopenharmony_ci isd = container_of(asd, struct isp_async_subdev, asd); 21668c2ecf20Sopenharmony_ci isp_parse_of_parallel_endpoint(isp->dev, &vep, &isd->bus); 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci } 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci fwnode_handle_put(ep); 21718c2ecf20Sopenharmony_ci } 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(isp_bus_interfaces); i++) { 21748c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint vep = { 21758c2ecf20Sopenharmony_ci .bus_type = V4L2_MBUS_CSI2_DPHY 21768c2ecf20Sopenharmony_ci }; 21778c2ecf20Sopenharmony_ci int ret; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id( 21808c2ecf20Sopenharmony_ci dev_fwnode(isp->dev), isp_bus_interfaces[i].phy, 0, 21818c2ecf20Sopenharmony_ci FWNODE_GRAPH_ENDPOINT_NEXT); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci if (!ep) 21848c2ecf20Sopenharmony_ci continue; 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci dev_dbg(isp->dev, "parsing serial interface %u, node %pOF\n", i, 21878c2ecf20Sopenharmony_ci to_of_node(ep)); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 21908c2ecf20Sopenharmony_ci if (ret == -ENXIO) { 21918c2ecf20Sopenharmony_ci vep = (struct v4l2_fwnode_endpoint) 21928c2ecf20Sopenharmony_ci { .bus_type = V4L2_MBUS_CSI1 }; 21938c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci if (ret == -ENXIO) { 21968c2ecf20Sopenharmony_ci vep = (struct v4l2_fwnode_endpoint) 21978c2ecf20Sopenharmony_ci { .bus_type = V4L2_MBUS_CCP2 }; 21988c2ecf20Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &vep); 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci if (!ret) { 22038c2ecf20Sopenharmony_ci asd = v4l2_async_notifier_add_fwnode_remote_subdev( 22048c2ecf20Sopenharmony_ci &isp->notifier, ep, sizeof(*isd)); 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci if (!IS_ERR(asd)) { 22078c2ecf20Sopenharmony_ci isd = container_of(asd, struct isp_async_subdev, asd); 22088c2ecf20Sopenharmony_ci 22098c2ecf20Sopenharmony_ci switch (vep.bus_type) { 22108c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI2_DPHY: 22118c2ecf20Sopenharmony_ci isd->bus.interface = 22128c2ecf20Sopenharmony_ci isp_bus_interfaces[i].csi2_if; 22138c2ecf20Sopenharmony_ci isp_parse_of_csi2_endpoint(isp->dev, &vep, &isd->bus); 22148c2ecf20Sopenharmony_ci break; 22158c2ecf20Sopenharmony_ci case V4L2_MBUS_CSI1: 22168c2ecf20Sopenharmony_ci case V4L2_MBUS_CCP2: 22178c2ecf20Sopenharmony_ci isd->bus.interface = 22188c2ecf20Sopenharmony_ci isp_bus_interfaces[i].csi1_if; 22198c2ecf20Sopenharmony_ci isp_parse_of_csi1_endpoint(isp->dev, &vep, 22208c2ecf20Sopenharmony_ci &isd->bus); 22218c2ecf20Sopenharmony_ci break; 22228c2ecf20Sopenharmony_ci default: 22238c2ecf20Sopenharmony_ci break; 22248c2ecf20Sopenharmony_ci } 22258c2ecf20Sopenharmony_ci } 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci 22288c2ecf20Sopenharmony_ci fwnode_handle_put(ep); 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci return 0; 22328c2ecf20Sopenharmony_ci} 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { 22358c2ecf20Sopenharmony_ci .complete = isp_subdev_notifier_complete, 22368c2ecf20Sopenharmony_ci}; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci/* 22398c2ecf20Sopenharmony_ci * isp_probe - Probe ISP platform device 22408c2ecf20Sopenharmony_ci * @pdev: Pointer to ISP platform device 22418c2ecf20Sopenharmony_ci * 22428c2ecf20Sopenharmony_ci * Returns 0 if successful, 22438c2ecf20Sopenharmony_ci * -ENOMEM if no memory available, 22448c2ecf20Sopenharmony_ci * -ENODEV if no platform device resources found 22458c2ecf20Sopenharmony_ci * or no space for remapping registers, 22468c2ecf20Sopenharmony_ci * -EINVAL if couldn't install ISR, 22478c2ecf20Sopenharmony_ci * or clk_get return error value. 22488c2ecf20Sopenharmony_ci */ 22498c2ecf20Sopenharmony_cistatic int isp_probe(struct platform_device *pdev) 22508c2ecf20Sopenharmony_ci{ 22518c2ecf20Sopenharmony_ci struct isp_device *isp; 22528c2ecf20Sopenharmony_ci struct resource *mem; 22538c2ecf20Sopenharmony_ci int ret; 22548c2ecf20Sopenharmony_ci int i, m; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci isp = kzalloc(sizeof(*isp), GFP_KERNEL); 22578c2ecf20Sopenharmony_ci if (!isp) { 22588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not allocate memory\n"); 22598c2ecf20Sopenharmony_ci return -ENOMEM; 22608c2ecf20Sopenharmony_ci } 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci ret = fwnode_property_read_u32(of_fwnode_handle(pdev->dev.of_node), 22638c2ecf20Sopenharmony_ci "ti,phy-type", &isp->phy_type); 22648c2ecf20Sopenharmony_ci if (ret) 22658c2ecf20Sopenharmony_ci goto error_release_isp; 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 22688c2ecf20Sopenharmony_ci "syscon"); 22698c2ecf20Sopenharmony_ci if (IS_ERR(isp->syscon)) { 22708c2ecf20Sopenharmony_ci ret = PTR_ERR(isp->syscon); 22718c2ecf20Sopenharmony_ci goto error_release_isp; 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(pdev->dev.of_node, 22758c2ecf20Sopenharmony_ci "syscon", 1, &isp->syscon_offset); 22768c2ecf20Sopenharmony_ci if (ret) 22778c2ecf20Sopenharmony_ci goto error_release_isp; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci isp->autoidle = autoidle; 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci mutex_init(&isp->isp_mutex); 22828c2ecf20Sopenharmony_ci spin_lock_init(&isp->stat_lock); 22838c2ecf20Sopenharmony_ci v4l2_async_notifier_init(&isp->notifier); 22848c2ecf20Sopenharmony_ci isp->dev = &pdev->dev; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci ret = isp_parse_of_endpoints(isp); 22878c2ecf20Sopenharmony_ci if (ret < 0) 22888c2ecf20Sopenharmony_ci goto error; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci isp->ref_count = 0; 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(isp->dev, DMA_BIT_MASK(32)); 22938c2ecf20Sopenharmony_ci if (ret) 22948c2ecf20Sopenharmony_ci goto error; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, isp); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci /* Regulators */ 22998c2ecf20Sopenharmony_ci isp->isp_csiphy1.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy1"); 23008c2ecf20Sopenharmony_ci if (IS_ERR(isp->isp_csiphy1.vdd)) { 23018c2ecf20Sopenharmony_ci ret = PTR_ERR(isp->isp_csiphy1.vdd); 23028c2ecf20Sopenharmony_ci goto error; 23038c2ecf20Sopenharmony_ci } 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci isp->isp_csiphy2.vdd = devm_regulator_get(&pdev->dev, "vdd-csiphy2"); 23068c2ecf20Sopenharmony_ci if (IS_ERR(isp->isp_csiphy2.vdd)) { 23078c2ecf20Sopenharmony_ci ret = PTR_ERR(isp->isp_csiphy2.vdd); 23088c2ecf20Sopenharmony_ci goto error; 23098c2ecf20Sopenharmony_ci } 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci /* Clocks 23128c2ecf20Sopenharmony_ci * 23138c2ecf20Sopenharmony_ci * The ISP clock tree is revision-dependent. We thus need to enable ICLK 23148c2ecf20Sopenharmony_ci * manually to read the revision before calling __omap3isp_get(). 23158c2ecf20Sopenharmony_ci * 23168c2ecf20Sopenharmony_ci * Start by mapping the ISP MMIO area, which is in two pieces. 23178c2ecf20Sopenharmony_ci * The ISP IOMMU is in between. Map both now, and fill in the 23188c2ecf20Sopenharmony_ci * ISP revision specific portions a little later in the 23198c2ecf20Sopenharmony_ci * function. 23208c2ecf20Sopenharmony_ci */ 23218c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 23228c2ecf20Sopenharmony_ci unsigned int map_idx = i ? OMAP3_ISP_IOMEM_CSI2A_REGS1 : 0; 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, i); 23258c2ecf20Sopenharmony_ci isp->mmio_base[map_idx] = 23268c2ecf20Sopenharmony_ci devm_ioremap_resource(isp->dev, mem); 23278c2ecf20Sopenharmony_ci if (IS_ERR(isp->mmio_base[map_idx])) { 23288c2ecf20Sopenharmony_ci ret = PTR_ERR(isp->mmio_base[map_idx]); 23298c2ecf20Sopenharmony_ci goto error; 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci } 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci ret = isp_get_clocks(isp); 23348c2ecf20Sopenharmony_ci if (ret < 0) 23358c2ecf20Sopenharmony_ci goto error; 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci ret = clk_enable(isp->clock[ISP_CLK_CAM_ICK]); 23388c2ecf20Sopenharmony_ci if (ret < 0) 23398c2ecf20Sopenharmony_ci goto error; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci isp->revision = isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION); 23428c2ecf20Sopenharmony_ci dev_info(isp->dev, "Revision %d.%d found\n", 23438c2ecf20Sopenharmony_ci (isp->revision & 0xf0) >> 4, isp->revision & 0x0f); 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci clk_disable(isp->clock[ISP_CLK_CAM_ICK]); 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci if (__omap3isp_get(isp, false) == NULL) { 23488c2ecf20Sopenharmony_ci ret = -ENODEV; 23498c2ecf20Sopenharmony_ci goto error; 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci ret = isp_reset(isp); 23538c2ecf20Sopenharmony_ci if (ret < 0) 23548c2ecf20Sopenharmony_ci goto error_isp; 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci ret = isp_xclk_init(isp); 23578c2ecf20Sopenharmony_ci if (ret < 0) 23588c2ecf20Sopenharmony_ci goto error_isp; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci /* Memory resources */ 23618c2ecf20Sopenharmony_ci for (m = 0; m < ARRAY_SIZE(isp_res_maps); m++) 23628c2ecf20Sopenharmony_ci if (isp->revision == isp_res_maps[m].isp_rev) 23638c2ecf20Sopenharmony_ci break; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_ci if (m == ARRAY_SIZE(isp_res_maps)) { 23668c2ecf20Sopenharmony_ci dev_err(isp->dev, "No resource map found for ISP rev %d.%d\n", 23678c2ecf20Sopenharmony_ci (isp->revision & 0xf0) >> 4, isp->revision & 0xf); 23688c2ecf20Sopenharmony_ci ret = -ENODEV; 23698c2ecf20Sopenharmony_ci goto error_isp; 23708c2ecf20Sopenharmony_ci } 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++) 23738c2ecf20Sopenharmony_ci isp->mmio_base[i] = 23748c2ecf20Sopenharmony_ci isp->mmio_base[0] + isp_res_maps[m].offset[i]; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci for (i = OMAP3_ISP_IOMEM_CSIPHY2; i < OMAP3_ISP_IOMEM_LAST; i++) 23778c2ecf20Sopenharmony_ci isp->mmio_base[i] = 23788c2ecf20Sopenharmony_ci isp->mmio_base[OMAP3_ISP_IOMEM_CSI2A_REGS1] 23798c2ecf20Sopenharmony_ci + isp_res_maps[m].offset[i]; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci isp->mmio_hist_base_phys = 23828c2ecf20Sopenharmony_ci mem->start + isp_res_maps[m].offset[OMAP3_ISP_IOMEM_HIST]; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci /* IOMMU */ 23858c2ecf20Sopenharmony_ci ret = isp_attach_iommu(isp); 23868c2ecf20Sopenharmony_ci if (ret < 0) { 23878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to attach to IOMMU\n"); 23888c2ecf20Sopenharmony_ci goto error_isp; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci /* Interrupt */ 23928c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 23938c2ecf20Sopenharmony_ci if (ret <= 0) { 23948c2ecf20Sopenharmony_ci ret = -ENODEV; 23958c2ecf20Sopenharmony_ci goto error_iommu; 23968c2ecf20Sopenharmony_ci } 23978c2ecf20Sopenharmony_ci isp->irq_num = ret; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci if (devm_request_irq(isp->dev, isp->irq_num, isp_isr, IRQF_SHARED, 24008c2ecf20Sopenharmony_ci "OMAP3 ISP", isp)) { 24018c2ecf20Sopenharmony_ci dev_err(isp->dev, "Unable to request IRQ\n"); 24028c2ecf20Sopenharmony_ci ret = -EINVAL; 24038c2ecf20Sopenharmony_ci goto error_iommu; 24048c2ecf20Sopenharmony_ci } 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci /* Entities */ 24078c2ecf20Sopenharmony_ci ret = isp_initialize_modules(isp); 24088c2ecf20Sopenharmony_ci if (ret < 0) 24098c2ecf20Sopenharmony_ci goto error_iommu; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci ret = isp_register_entities(isp); 24128c2ecf20Sopenharmony_ci if (ret < 0) 24138c2ecf20Sopenharmony_ci goto error_modules; 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci ret = isp_create_links(isp); 24168c2ecf20Sopenharmony_ci if (ret < 0) 24178c2ecf20Sopenharmony_ci goto error_register_entities; 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci isp->notifier.ops = &isp_subdev_notifier_ops; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier); 24228c2ecf20Sopenharmony_ci if (ret) 24238c2ecf20Sopenharmony_ci goto error_register_entities; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci isp_core_init(isp, 1); 24268c2ecf20Sopenharmony_ci omap3isp_put(isp); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci return 0; 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_cierror_register_entities: 24318c2ecf20Sopenharmony_ci isp_unregister_entities(isp); 24328c2ecf20Sopenharmony_cierror_modules: 24338c2ecf20Sopenharmony_ci isp_cleanup_modules(isp); 24348c2ecf20Sopenharmony_cierror_iommu: 24358c2ecf20Sopenharmony_ci isp_detach_iommu(isp); 24368c2ecf20Sopenharmony_cierror_isp: 24378c2ecf20Sopenharmony_ci isp_xclk_cleanup(isp); 24388c2ecf20Sopenharmony_ci __omap3isp_put(isp, false); 24398c2ecf20Sopenharmony_cierror: 24408c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&isp->notifier); 24418c2ecf20Sopenharmony_ci mutex_destroy(&isp->isp_mutex); 24428c2ecf20Sopenharmony_cierror_release_isp: 24438c2ecf20Sopenharmony_ci kfree(isp); 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci return ret; 24468c2ecf20Sopenharmony_ci} 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap3isp_pm_ops = { 24498c2ecf20Sopenharmony_ci .prepare = isp_pm_prepare, 24508c2ecf20Sopenharmony_ci .suspend = isp_pm_suspend, 24518c2ecf20Sopenharmony_ci .resume = isp_pm_resume, 24528c2ecf20Sopenharmony_ci .complete = isp_pm_complete, 24538c2ecf20Sopenharmony_ci}; 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_cistatic const struct platform_device_id omap3isp_id_table[] = { 24568c2ecf20Sopenharmony_ci { "omap3isp", 0 }, 24578c2ecf20Sopenharmony_ci { }, 24588c2ecf20Sopenharmony_ci}; 24598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, omap3isp_id_table); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic const struct of_device_id omap3isp_of_table[] = { 24628c2ecf20Sopenharmony_ci { .compatible = "ti,omap3-isp" }, 24638c2ecf20Sopenharmony_ci { }, 24648c2ecf20Sopenharmony_ci}; 24658c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap3isp_of_table); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_cistatic struct platform_driver omap3isp_driver = { 24688c2ecf20Sopenharmony_ci .probe = isp_probe, 24698c2ecf20Sopenharmony_ci .remove = isp_remove, 24708c2ecf20Sopenharmony_ci .id_table = omap3isp_id_table, 24718c2ecf20Sopenharmony_ci .driver = { 24728c2ecf20Sopenharmony_ci .name = "omap3isp", 24738c2ecf20Sopenharmony_ci .pm = &omap3isp_pm_ops, 24748c2ecf20Sopenharmony_ci .of_match_table = omap3isp_of_table, 24758c2ecf20Sopenharmony_ci }, 24768c2ecf20Sopenharmony_ci}; 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_cimodule_platform_driver(omap3isp_driver); 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation"); 24818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI OMAP3 ISP driver"); 24828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 24838c2ecf20Sopenharmony_ciMODULE_VERSION(ISP_VIDEO_DRIVER_VERSION); 2484