/*
* isp.c
*
* TI OMAP3 ISP - Core
*
* Copyright (C) 2006-2010 Nokia Corporation
* Copyright (C) 2007-2009 Texas Instruments, Inc.
*
* Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Sakari Ailus <sakari.ailus@iki.fi>
*
* Contributors:
* Laurent Pinchart <laurent.pinchart@ideasonboard.com>
* Sakari Ailus <sakari.ailus@iki.fi>
* David Cohen <dacohen@gmail.com>
* Stanimir Varbanov <svarbanov@mm-sol.com>
* Vimarsh Zutshi <vimarsh.zutshi@gmail.com>
* Tuukka Toivonen <tuukkat76@gmail.com>
* Sergio Aguirre <saaguirre@ti.com>
* Antti Koskipaa <akoskipa@gmail.com>
* Ivan T. Ivanov <iivanov@mm-sol.com>
* RaniSuneela <r-m@ti.com>
* Atanas Filipov <afilipov@mm-sol.com>
* Gjorgji Rosikopulos <grosikopulos@mm-sol.com>
* Hiroshi DOYU <hiroshi.doyu@nokia.com>
* Nayden Kanchev <nkanchev@mm-sol.com>
* Phil Carmody <ext-phil.2.carmody@nokia.com>
* Artem Bityutskiy <artem.bityutskiy@nokia.com>
* Dominic Curran <dcurran@ti.com>
* Ilkka Myllyperkio <ilkka.myllyperkio@sofica.fi>
* Pallavi Kulkarni <p-kulkarni@ti.com>
* Vaibhav Hiremath <hvaibhav@ti.com>
* Mohit Jalori <mjalori@ti.com>
* Sameer Venkatraman <sameerv@ti.com>
* Senthilvadivu Guruswamy <svadivu@ti.com>
* Thara Gopinath <thara@ti.com>
* Toni Leinonen <toni.leinonen@nokia.com>
* Troy Laramy <t-laramy@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <asm/cacheflush.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include "isp.h"
#include "ispreg.h"
#include "ispccdc.h"
#include "isppreview.h"
#include "ispresizer.h"
#include "ispcsi2.h"
#include "ispccp2.h"
#include "isph3a.h"
#include "isphist.h"
static unsigned int autoidle;
module_param(autoidle, int, 0444);
MODULE_PARM_DESC(autoidle, "Enable OMAP3ISP AUTOIDLE support");
static void isp_save_ctx(struct isp_device *isp);
static void isp_restore_ctx(struct isp_device *isp);
static const struct isp_res_mapping isp_res_maps[] = {
{
.isp_rev = ISP_REVISION_2_0,
.map = 1 << OMAP3_ISP_IOMEM_MAIN |
1 << OMAP3_ISP_IOMEM_CCP2 |
1 << OMAP3_ISP_IOMEM_CCDC |
1 << OMAP3_ISP_IOMEM_HIST |
1 << OMAP3_ISP_IOMEM_H3A |
1 << OMAP3_ISP_IOMEM_PREV |
1 << OMAP3_ISP_IOMEM_RESZ |
1 << OMAP3_ISP_IOMEM_SBL |
1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
1 << OMAP3_ISP_IOMEM_CSIPHY2 |
1 << OMAP3_ISP_IOMEM_343X_CONTROL_CSIRXFE,
},
{
.isp_rev = ISP_REVISION_15_0,
.map = 1 << OMAP3_ISP_IOMEM_MAIN |
1 << OMAP3_ISP_IOMEM_CCP2 |
1 << OMAP3_ISP_IOMEM_CCDC |
1 << OMAP3_ISP_IOMEM_HIST |
1 << OMAP3_ISP_IOMEM_H3A |
1 << OMAP3_ISP_IOMEM_PREV |
1 << OMAP3_ISP_IOMEM_RESZ |
1 << OMAP3_ISP_IOMEM_SBL |
1 << OMAP3_ISP_IOMEM_CSI2A_REGS1 |
1 << OMAP3_ISP_IOMEM_CSIPHY2 |
1 << OMAP3_ISP_IOMEM_CSI2A_REGS2 |
1 << OMAP3_ISP_IOMEM_CSI2C_REGS1 |
1 << OMAP3_ISP_IOMEM_CSIPHY1 |
1 << OMAP3_ISP_IOMEM_CSI2C_REGS2 |
1 << OMAP3_ISP_IOMEM_3630_CONTROL_CAMERA_PHY_CTRL,
},
};
/* Structure for saving/restoring ISP module registers */
static struct isp_reg isp_reg_list[] = {
{OMAP3_ISP_IOMEM_MAIN, ISP_SYSCONFIG, 0},
{OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, 0},
{OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL, 0},
{0, ISP_TOK_TERM, 0}
};
/*
* omap3isp_flush - Post pending L3 bus writes by doing a register readback
* @isp: OMAP3 ISP device
*
* In order to force posting of pending writes, we need to write and
* readback the same register, in this case the revision register.
*
* See this link for reference:
* http://www.mail-archive.com/linux-omap@vger.kernel.org/msg08149.html
*/
void omap3isp_flush(struct isp_device *isp)
{
isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_REVISION);
}
/* -----------------------------------------------------------------------------
* XCLK
*/
#define to_isp_xclk(_hw) container_of(_hw, struct isp_xclk, hw)
static void isp_xclk_update(struct isp_xclk *xclk, u32 divider)
{
switch (xclk->id) {
case ISP_XCLK_A:
isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVA_MASK,
divider << ISPTCTRL_CTRL_DIVA_SHIFT);
break;
case ISP_XCLK_B:
isp_reg_clr_set(xclk->isp, OMAP3_ISP_IOMEM_MAIN, ISP_TCTRL_CTRL,
ISPTCTRL_CTRL_DIVB_MASK,
divider << ISPTCTRL_CTRL_DIVB_SHIFT);
break;
}
}
static int isp_xclk_prepare(struct clk_hw *hw)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
omap3isp_get(xclk->isp);
return 0;
}
static void isp_xclk_unprepare(struct clk_hw *hw)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
omap3isp_put(xclk->isp);
}
static int isp_xclk_enable(struct clk_hw *hw)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
unsigned long flags;
spin_lock_irqsave(&xclk->lock, flags);
isp_xclk_update(xclk, xclk->divider);
xclk->enabled = true;
spin_unlock_irqrestore(&xclk->lock, flags);
return 0;
}
static void isp_xclk_disable(struct clk_hw *hw)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
unsigned long flags;
spin_lock_irqsave(&xclk->lock, flags);
isp_xclk_update(xclk, 0);
xclk->enabled = false;
spin_unlock_irqrestore(&xclk->lock, flags);
}
static unsigned long isp_xclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
return parent_rate / xclk->divider;
}
static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
{
u32 divider;
if (*rate >= parent_rate) {
*rate = parent_rate;
return ISPTCTRL_CTRL_DIV_BYPASS;
}
divider = DIV_ROUND_CLOSEST(parent_rate, *rate);
if (divider >= ISPTCTRL_CTRL_DIV_BYPASS)
divider = ISPTCTRL_CTRL_DIV_BYPASS - 1;
*rate = parent_rate / divider;
return divider;
}
static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
isp_xclk_calc_divider(&rate, *parent_rate);
return rate;
}
static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct isp_xclk *xclk = to_isp_xclk(hw);
unsigned long flags;
u32 divider;
divider = isp_xclk_calc_divider(&rate, parent_rate);
spin_lock_irqsave(&xclk->lock, flags);
xclk->divider = divider;
if (xclk->enabled)
isp_xclk_update(xclk, divider);
spin_unlock_irqrestore(&xclk->lock, flags);
dev_dbg(xclk->isp->dev, "%s: cam_xclk%c set to %lu Hz (div %u)\n",
__func__, xclk->id == ISP_XCLK_A ? 'a' : 'b', rate, divider);
return 0;
}
static const struct clk_ops isp_xclk_ops = {
.prepare = isp_xclk_prepare,
.unprepare = isp_xclk_unprepare,
.enable = isp_xclk_enable,
.disable = isp_xclk_disable,
.recalc_rate = isp_xclk_recalc_rate,
.round_rate = isp_xclk_round_rate,
.set_rate = isp_xclk_set_rate,
};
static const char *isp_xclk_parent_name = "cam_mclk";
static const struct clk_init_data isp_xclk_init_data = {
.name = "cam_xclk",
.ops = &isp_xclk_ops,
.parent_names = &isp_xclk_parent_name,
.num_parents = 1,
};
static int isp_xclk_i
评论0