2011-02-12 18:05:06 -03:00
/*
* ispccp2 . c
*
* TI OMAP3 ISP - CCP2 module
*
* Copyright ( C ) 2010 Nokia Corporation
* Copyright ( C ) 2010 Texas Instruments , Inc .
*
* Contacts : Laurent Pinchart < laurent . pinchart @ ideasonboard . com >
* Sakari Ailus < sakari . ailus @ iki . fi >
*
* 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 <linux/delay.h>
# include <linux/device.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/uaccess.h>
2011-05-03 07:41:22 -03:00
# include <linux/regulator/consumer.h>
2011-02-12 18:05:06 -03:00
# include "isp.h"
# include "ispreg.h"
# include "ispccp2.h"
/* Number of LCX channels */
# define CCP2_LCx_CHANS_NUM 3
/* Max/Min size for CCP2 video port */
# define ISPCCP2_DAT_START_MIN 0
# define ISPCCP2_DAT_START_MAX 4095
# define ISPCCP2_DAT_SIZE_MIN 0
# define ISPCCP2_DAT_SIZE_MAX 4095
# define ISPCCP2_VPCLK_FRACDIV 65536
# define ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP 0x12
# define ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP 0x16
/* Max/Min size for CCP2 memory channel */
# define ISPCCP2_LCM_HSIZE_COUNT_MIN 16
# define ISPCCP2_LCM_HSIZE_COUNT_MAX 8191
# define ISPCCP2_LCM_HSIZE_SKIP_MIN 0
# define ISPCCP2_LCM_HSIZE_SKIP_MAX 8191
# define ISPCCP2_LCM_VSIZE_MIN 1
# define ISPCCP2_LCM_VSIZE_MAX 8191
# define ISPCCP2_LCM_HWORDS_MIN 1
# define ISPCCP2_LCM_HWORDS_MAX 4095
# define ISPCCP2_LCM_CTRL_BURST_SIZE_32X 5
# define ISPCCP2_LCM_CTRL_READ_THROTTLE_FULL 0
# define ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 2
# define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 2
# define ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 3
# define ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 3
# define ISPCCP2_LCM_CTRL_DST_PORT_VP 0
# define ISPCCP2_LCM_CTRL_DST_PORT_MEM 1
/* Set only the required bits */
# define BIT_SET(var, shift, mask, val) \
do { \
var = ( ( var ) & ~ ( ( mask ) < < ( shift ) ) ) \
| ( ( val ) < < ( shift ) ) ; \
} while ( 0 )
/*
* ccp2_print_status - Print current CCP2 module register values .
*/
# define CCP2_PRINT_REGISTER(isp, name)\
dev_dbg ( isp - > dev , " ###CCP2 " # name " =0x%08x \n " , \
isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_ # # name ) )
static void ccp2_print_status ( struct isp_ccp2_device * ccp2 )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
dev_dbg ( isp - > dev , " -------------CCP2 Register dump------------- \n " ) ;
CCP2_PRINT_REGISTER ( isp , SYSCONFIG ) ;
CCP2_PRINT_REGISTER ( isp , SYSSTATUS ) ;
CCP2_PRINT_REGISTER ( isp , LC01_IRQENABLE ) ;
CCP2_PRINT_REGISTER ( isp , LC01_IRQSTATUS ) ;
CCP2_PRINT_REGISTER ( isp , LC23_IRQENABLE ) ;
CCP2_PRINT_REGISTER ( isp , LC23_IRQSTATUS ) ;
CCP2_PRINT_REGISTER ( isp , LCM_IRQENABLE ) ;
CCP2_PRINT_REGISTER ( isp , LCM_IRQSTATUS ) ;
CCP2_PRINT_REGISTER ( isp , CTRL ) ;
CCP2_PRINT_REGISTER ( isp , LCx_CTRL ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_CODE ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_STAT_START ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_STAT_SIZE ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_SOF_ADDR ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_EOF_ADDR ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_DAT_START ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_DAT_SIZE ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_DAT_PING_ADDR ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_DAT_PONG_ADDR ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCx_DAT_OFST ( 0 ) ) ;
CCP2_PRINT_REGISTER ( isp , LCM_CTRL ) ;
CCP2_PRINT_REGISTER ( isp , LCM_VSIZE ) ;
CCP2_PRINT_REGISTER ( isp , LCM_HSIZE ) ;
CCP2_PRINT_REGISTER ( isp , LCM_PREFETCH ) ;
CCP2_PRINT_REGISTER ( isp , LCM_SRC_ADDR ) ;
CCP2_PRINT_REGISTER ( isp , LCM_SRC_OFST ) ;
CCP2_PRINT_REGISTER ( isp , LCM_DST_ADDR ) ;
CCP2_PRINT_REGISTER ( isp , LCM_DST_OFST ) ;
dev_dbg ( isp - > dev , " -------------------------------------------- \n " ) ;
}
/*
* ccp2_reset - Reset the CCP2
* @ ccp2 : pointer to ISP CCP2 device
*/
static void ccp2_reset ( struct isp_ccp2_device * ccp2 )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
int i = 0 ;
/* Reset the CSI1/CCP2B and wait for reset to complete */
isp_reg_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_SYSCONFIG ,
ISPCCP2_SYSCONFIG_SOFT_RESET ) ;
while ( ! ( isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_SYSSTATUS ) &
ISPCCP2_SYSSTATUS_RESET_DONE ) ) {
udelay ( 10 ) ;
if ( i + + > 10 ) { /* try read 10 times */
dev_warn ( isp - > dev ,
" omap3_isp: timeout waiting for ccp2 reset \n " ) ;
break ;
}
}
}
/*
* ccp2_pwr_cfg - Configure the power mode settings
* @ ccp2 : pointer to ISP CCP2 device
*/
static void ccp2_pwr_cfg ( struct isp_ccp2_device * ccp2 )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
isp_reg_writel ( isp , ISPCCP2_SYSCONFIG_MSTANDBY_MODE_SMART |
( ( isp - > revision = = ISP_REVISION_15_0 & & isp - > autoidle ) ?
ISPCCP2_SYSCONFIG_AUTO_IDLE : 0 ) ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_SYSCONFIG ) ;
}
/*
* ccp2_if_enable - Enable CCP2 interface .
* @ ccp2 : pointer to ISP CCP2 device
* @ enable : enable / disable flag
*/
2013-05-29 00:47:41 -03:00
static int ccp2_if_enable ( struct isp_ccp2_device * ccp2 , u8 enable )
2011-02-12 18:05:06 -03:00
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
2013-05-29 00:47:41 -03:00
int ret ;
2011-02-12 18:05:06 -03:00
int i ;
2013-05-29 00:47:41 -03:00
if ( enable & & ccp2 - > vdds_csib ) {
ret = regulator_enable ( ccp2 - > vdds_csib ) ;
if ( ret < 0 )
return ret ;
}
2011-05-03 07:41:22 -03:00
2011-02-12 18:05:06 -03:00
/* Enable/Disable all the LCx channels */
for ( i = 0 ; i < CCP2_LCx_CHANS_NUM ; i + + )
isp_reg_clr_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCx_CTRL ( i ) ,
ISPCCP2_LCx_CTRL_CHAN_EN ,
enable ? ISPCCP2_LCx_CTRL_CHAN_EN : 0 ) ;
/* Enable/Disable ccp2 interface in ccp2 mode */
isp_reg_clr_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ,
ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN ,
enable ? ( ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN ) : 0 ) ;
2011-05-03 07:41:22 -03:00
if ( ! enable & & ccp2 - > vdds_csib )
regulator_disable ( ccp2 - > vdds_csib ) ;
2013-05-29 00:47:41 -03:00
return 0 ;
2011-02-12 18:05:06 -03:00
}
/*
* ccp2_mem_enable - Enable CCP2 memory interface .
* @ ccp2 : pointer to ISP CCP2 device
* @ enable : enable / disable flag
*/
static void ccp2_mem_enable ( struct isp_ccp2_device * ccp2 , u8 enable )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
if ( enable )
ccp2_if_enable ( ccp2 , 0 ) ;
/* Enable/Disable ccp2 interface in ccp2 mode */
isp_reg_clr_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ,
ISPCCP2_CTRL_MODE , enable ? ISPCCP2_CTRL_MODE : 0 ) ;
isp_reg_clr_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_CTRL ,
ISPCCP2_LCM_CTRL_CHAN_EN ,
enable ? ISPCCP2_LCM_CTRL_CHAN_EN : 0 ) ;
}
/*
* ccp2_phyif_config - Initialize CCP2 phy interface config
* @ ccp2 : Pointer to ISP CCP2 device
* @ config : CCP2 platform data
*
* Configure the CCP2 physical interface module from platform data .
*
* Returns - EIO if strobe is chosen in CSI1 mode , or 0 on success .
*/
static int ccp2_phyif_config ( struct isp_ccp2_device * ccp2 ,
const struct isp_ccp2_platform_data * pdata )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
u32 val ;
/* CCP2B mode */
val = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ) |
ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE ;
/* Data/strobe physical layer */
BIT_SET ( val , ISPCCP2_CTRL_PHY_SEL_SHIFT , ISPCCP2_CTRL_PHY_SEL_MASK ,
pdata - > phy_layer ) ;
BIT_SET ( val , ISPCCP2_CTRL_INV_SHIFT , ISPCCP2_CTRL_INV_MASK ,
pdata - > strobe_clk_pol ) ;
isp_reg_writel ( isp , val , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ) ;
val = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ) ;
if ( ! ( val & ISPCCP2_CTRL_MODE ) ) {
2011-08-12 19:09:34 +02:00
if ( pdata - > ccp2_mode = = ISP_CCP2_MODE_CCP2 )
2011-02-12 18:05:06 -03:00
dev_warn ( isp - > dev , " OMAP3 CCP2 bus not available \n " ) ;
2011-08-12 19:09:34 +02:00
if ( pdata - > phy_layer = = ISP_CCP2_PHY_DATA_STROBE )
2011-02-12 18:05:06 -03:00
/* Strobe mode requires CCP2 */
return - EIO ;
}
return 0 ;
}
/*
* ccp2_vp_config - Initialize CCP2 video port interface .
* @ ccp2 : Pointer to ISP CCP2 device
* @ vpclk_div : Video port divisor
*
* Configure the CCP2 video port with the given clock divisor . The valid divisor
* values depend on the ISP revision :
*
* - revision 1.0 and 2.0 1 to 4
* - revision 15.0 1 to 65536
*
* The exact divisor value used might differ from the requested value , as ISP
* revision 15.0 represent the divisor by 65536 divided by an integer .
*/
static void ccp2_vp_config ( struct isp_ccp2_device * ccp2 ,
unsigned int vpclk_div )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
u32 val ;
/* ISPCCP2_CTRL Video port */
val = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ) ;
val | = ISPCCP2_CTRL_VP_ONLY_EN ; /* Disable the memory write port */
if ( isp - > revision = = ISP_REVISION_15_0 ) {
vpclk_div = clamp_t ( unsigned int , vpclk_div , 1 , 65536 ) ;
vpclk_div = min ( ISPCCP2_VPCLK_FRACDIV / vpclk_div , 65535U ) ;
BIT_SET ( val , ISPCCP2_CTRL_VPCLK_DIV_SHIFT ,
ISPCCP2_CTRL_VPCLK_DIV_MASK , vpclk_div ) ;
} else {
vpclk_div = clamp_t ( unsigned int , vpclk_div , 1 , 4 ) ;
BIT_SET ( val , ISPCCP2_CTRL_VP_OUT_CTRL_SHIFT ,
ISPCCP2_CTRL_VP_OUT_CTRL_MASK , vpclk_div - 1 ) ;
}
isp_reg_writel ( isp , val , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ) ;
}
/*
* ccp2_lcx_config - Initialize CCP2 logical channel interface .
* @ ccp2 : Pointer to ISP CCP2 device
* @ config : Pointer to ISP LCx config structure .
*
* This will analyze the parameters passed by the interface config
* and configure CSI1 / CCP2 logical channel
*
*/
static void ccp2_lcx_config ( struct isp_ccp2_device * ccp2 ,
struct isp_interface_lcx_config * config )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
u32 val , format ;
switch ( config - > format ) {
case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 :
format = ISPCCP2_LCx_CTRL_FORMAT_RAW8_DPCM10_VP ;
break ;
case V4L2_MBUS_FMT_SGRBG10_1X10 :
default :
format = ISPCCP2_LCx_CTRL_FORMAT_RAW10_VP ; /* RAW10+VP */
break ;
}
/* ISPCCP2_LCx_CTRL logical channel #0 */
val = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCx_CTRL ( 0 ) )
| ( ISPCCP2_LCx_CTRL_REGION_EN ) ; /* Region */
if ( isp - > revision = = ISP_REVISION_15_0 ) {
/* CRC */
BIT_SET ( val , ISPCCP2_LCx_CTRL_CRC_SHIFT_15_0 ,
ISPCCP2_LCx_CTRL_CRC_MASK ,
config - > crc ) ;
/* Format = RAW10+VP or RAW8+DPCM10+VP*/
BIT_SET ( val , ISPCCP2_LCx_CTRL_FORMAT_SHIFT_15_0 ,
ISPCCP2_LCx_CTRL_FORMAT_MASK_15_0 , format ) ;
} else {
BIT_SET ( val , ISPCCP2_LCx_CTRL_CRC_SHIFT ,
ISPCCP2_LCx_CTRL_CRC_MASK ,
config - > crc ) ;
BIT_SET ( val , ISPCCP2_LCx_CTRL_FORMAT_SHIFT ,
ISPCCP2_LCx_CTRL_FORMAT_MASK , format ) ;
}
isp_reg_writel ( isp , val , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCx_CTRL ( 0 ) ) ;
/* ISPCCP2_DAT_START for logical channel #0 */
isp_reg_writel ( isp , config - > data_start < < ISPCCP2_LCx_DAT_SHIFT ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCx_DAT_START ( 0 ) ) ;
/* ISPCCP2_DAT_SIZE for logical channel #0 */
isp_reg_writel ( isp , config - > data_size < < ISPCCP2_LCx_DAT_SHIFT ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCx_DAT_SIZE ( 0 ) ) ;
/* Enable error IRQs for logical channel #0 */
val = ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ ;
isp_reg_writel ( isp , val , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LC01_IRQSTATUS ) ;
isp_reg_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LC01_IRQENABLE , val ) ;
}
/*
* ccp2_if_configure - Configure ccp2 with data from sensor
* @ ccp2 : Pointer to ISP CCP2 device
*
* Return 0 on success or a negative error code
*/
static int ccp2_if_configure ( struct isp_ccp2_device * ccp2 )
{
const struct isp_v4l2_subdevs_group * pdata ;
struct v4l2_mbus_framefmt * format ;
struct media_pad * pad ;
struct v4l2_subdev * sensor ;
u32 lines = 0 ;
int ret ;
ccp2_pwr_cfg ( ccp2 ) ;
2013-06-03 05:16:13 -03:00
pad = media_entity_remote_pad ( & ccp2 - > pads [ CCP2_PAD_SINK ] ) ;
2011-02-12 18:05:06 -03:00
sensor = media_entity_to_v4l2_subdev ( pad - > entity ) ;
pdata = sensor - > host_priv ;
ret = ccp2_phyif_config ( ccp2 , & pdata - > bus . ccp2 ) ;
if ( ret < 0 )
return ret ;
ccp2_vp_config ( ccp2 , pdata - > bus . ccp2 . vpclk_div + 1 ) ;
v4l2_subdev_call ( sensor , sensor , g_skip_top_lines , & lines ) ;
format = & ccp2 - > formats [ CCP2_PAD_SINK ] ;
ccp2 - > if_cfg . data_start = lines ;
ccp2 - > if_cfg . crc = pdata - > bus . ccp2 . crc ;
ccp2 - > if_cfg . format = format - > code ;
ccp2 - > if_cfg . data_size = format - > height ;
ccp2_lcx_config ( ccp2 , & ccp2 - > if_cfg ) ;
return 0 ;
}
static int ccp2_adjust_bandwidth ( struct isp_ccp2_device * ccp2 )
{
struct isp_pipeline * pipe = to_isp_pipeline ( & ccp2 - > subdev . entity ) ;
struct isp_device * isp = to_isp_device ( ccp2 ) ;
const struct v4l2_mbus_framefmt * ofmt = & ccp2 - > formats [ CCP2_PAD_SOURCE ] ;
unsigned long l3_ick = pipe - > l3_ick ;
struct v4l2_fract * timeperframe ;
unsigned int vpclk_div = 2 ;
unsigned int value ;
u64 bound ;
u64 area ;
/* Compute the minimum clock divisor, based on the pipeline maximum
* data rate . This is an absolute lower bound if we don ' t want SBL
* overflows , so round the value up .
*/
vpclk_div = max_t ( unsigned int , DIV_ROUND_UP ( l3_ick , pipe - > max_rate ) ,
vpclk_div ) ;
/* Compute the maximum clock divisor, based on the requested frame rate.
* This is a soft lower bound to achieve a frame rate equal or higher
* than the requested value , so round the value down .
*/
timeperframe = & pipe - > max_timeperframe ;
if ( timeperframe - > numerator ) {
area = ofmt - > width * ofmt - > height ;
bound = div_u64 ( area * timeperframe - > denominator ,
timeperframe - > numerator ) ;
value = min_t ( u64 , bound , l3_ick ) ;
vpclk_div = max_t ( unsigned int , l3_ick / value , vpclk_div ) ;
}
dev_dbg ( isp - > dev , " %s: minimum clock divisor = %u \n " , __func__ ,
vpclk_div ) ;
return vpclk_div ;
}
/*
* ccp2_mem_configure - Initialize CCP2 memory input / output interface
* @ ccp2 : Pointer to ISP CCP2 device
* @ config : Pointer to ISP mem interface config structure
*
* This will analyze the parameters passed by the interface config
* structure , and configure the respective registers for proper
* CSI1 / CCP2 memory input .
*/
static void ccp2_mem_configure ( struct isp_ccp2_device * ccp2 ,
struct isp_interface_mem_config * config )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
u32 sink_pixcode = ccp2 - > formats [ CCP2_PAD_SINK ] . code ;
u32 source_pixcode = ccp2 - > formats [ CCP2_PAD_SOURCE ] . code ;
unsigned int dpcm_decompress = 0 ;
u32 val , hwords ;
if ( sink_pixcode ! = source_pixcode & &
sink_pixcode = = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 )
dpcm_decompress = 1 ;
ccp2_pwr_cfg ( ccp2 ) ;
/* Hsize, Skip */
isp_reg_writel ( isp , ISPCCP2_LCM_HSIZE_SKIP_MIN |
( config - > hsize_count < < ISPCCP2_LCM_HSIZE_SHIFT ) ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_HSIZE ) ;
/* Vsize, no. of lines */
isp_reg_writel ( isp , config - > vsize_count < < ISPCCP2_LCM_VSIZE_SHIFT ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_VSIZE ) ;
if ( ccp2 - > video_in . bpl_padding = = 0 )
config - > src_ofst = 0 ;
else
config - > src_ofst = ccp2 - > video_in . bpl_value ;
isp_reg_writel ( isp , config - > src_ofst , OMAP3_ISP_IOMEM_CCP2 ,
ISPCCP2_LCM_SRC_OFST ) ;
/* Source and Destination formats */
val = ISPCCP2_LCM_CTRL_DST_FORMAT_RAW10 < <
ISPCCP2_LCM_CTRL_DST_FORMAT_SHIFT ;
if ( dpcm_decompress ) {
/* source format is RAW8 */
val | = ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW8 < <
ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT ;
/* RAW8 + DPCM10 - simple predictor */
val | = ISPCCP2_LCM_CTRL_SRC_DPCM_PRED ;
/* enable source DPCM decompression */
val | = ISPCCP2_LCM_CTRL_SRC_DECOMPR_DPCM10 < <
ISPCCP2_LCM_CTRL_SRC_DECOMPR_SHIFT ;
} else {
/* source format is RAW10 */
val | = ISPCCP2_LCM_CTRL_SRC_FORMAT_RAW10 < <
ISPCCP2_LCM_CTRL_SRC_FORMAT_SHIFT ;
}
/* Burst size to 32x64 */
val | = ISPCCP2_LCM_CTRL_BURST_SIZE_32X < <
ISPCCP2_LCM_CTRL_BURST_SIZE_SHIFT ;
isp_reg_writel ( isp , val , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_CTRL ) ;
/* Prefetch setup */
if ( dpcm_decompress )
hwords = ( ISPCCP2_LCM_HSIZE_SKIP_MIN +
config - > hsize_count ) > > 3 ;
else
hwords = ( ISPCCP2_LCM_HSIZE_SKIP_MIN +
config - > hsize_count ) > > 2 ;
isp_reg_writel ( isp , hwords < < ISPCCP2_LCM_PREFETCH_SHIFT ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_PREFETCH ) ;
/* Video port */
isp_reg_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_CTRL ,
ISPCCP2_CTRL_IO_OUT_SEL | ISPCCP2_CTRL_MODE ) ;
ccp2_vp_config ( ccp2 , ccp2_adjust_bandwidth ( ccp2 ) ) ;
/* Clear LCM interrupts */
isp_reg_writel ( isp , ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ |
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ ,
OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_IRQSTATUS ) ;
/* Enable LCM interupts */
isp_reg_set ( isp , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_IRQENABLE ,
ISPCCP2_LCM_IRQSTATUS_EOF_IRQ |
ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ ) ;
}
/*
* ccp2_set_inaddr - Sets memory address of input frame .
* @ ccp2 : Pointer to ISP CCP2 device
* @ addr : 32 bit memory address aligned on 32 byte boundary .
*
* Configures the memory address from which the input frame is to be read .
*/
static void ccp2_set_inaddr ( struct isp_ccp2_device * ccp2 , u32 addr )
{
struct isp_device * isp = to_isp_device ( ccp2 ) ;
isp_reg_writel ( isp , addr , OMAP3_ISP_IOMEM_CCP2 , ISPCCP2_LCM_SRC_ADDR ) ;
}
/* -----------------------------------------------------------------------------
* Interrupt handling
*/
static void ccp2_isr_buffer ( struct isp_ccp2_device * ccp2 )
{
struct isp_pipeline * pipe = to_isp_pipeline ( & ccp2 - > subdev . entity ) ;
struct isp_buffer * buffer ;
2011-12-07 08:34:50 -03:00
buffer = omap3isp_video_buffer_next ( & ccp2 - > video_in ) ;
2011-02-12 18:05:06 -03:00
if ( buffer ! = NULL )
ccp2_set_inaddr ( ccp2 , buffer - > isp_addr ) ;
pipe - > state | = ISP_PIPELINE_IDLE_INPUT ;
if ( ccp2 - > state = = ISP_PIPELINE_STREAM_SINGLESHOT ) {
if ( isp_pipeline_ready ( pipe ) )
omap3isp_pipeline_set_stream ( pipe ,
ISP_PIPELINE_STREAM_SINGLESHOT ) ;
}
}
/*
* omap3isp_ccp2_isr - Handle ISP CCP2 interrupts
* @ ccp2 : Pointer to ISP CCP2 device
*
* This will handle the CCP2 interrupts
*/
2011-12-07 08:34:50 -03:00
void omap3isp_ccp2_isr ( struct isp_ccp2_device * ccp2 )
2011-02-12 18:05:06 -03:00
{
2011-12-07 08:34:50 -03:00
struct isp_pipeline * pipe = to_isp_pipeline ( & ccp2 - > subdev . entity ) ;
2011-02-12 18:05:06 -03:00
struct isp_device * isp = to_isp_device ( ccp2 ) ;
static const u32 ISPCCP2_LC01_ERROR =
ISPCCP2_LC01_IRQSTATUS_LC0_FIFO_OVF_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ ;
u32 lcx_irqstatus , lcm_irqstatus ;
/* First clear the interrupts */
lcx_irqstatus = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 ,
ISPCCP2_LC01_IRQSTATUS ) ;
isp_reg_writel ( isp , lcx_irqstatus , OMAP3_ISP_IOMEM_CCP2 ,
ISPCCP2_LC01_IRQSTATUS ) ;
lcm_irqstatus = isp_reg_readl ( isp , OMAP3_ISP_IOMEM_CCP2 ,
ISPCCP2_LCM_IRQSTATUS ) ;
isp_reg_writel ( isp , lcm_irqstatus , OMAP3_ISP_IOMEM_CCP2 ,
ISPCCP2_LCM_IRQSTATUS ) ;
/* Errors */
if ( lcx_irqstatus & ISPCCP2_LC01_ERROR ) {
2011-12-07 08:34:50 -03:00
pipe - > error = true ;
2011-02-12 18:05:06 -03:00
dev_dbg ( isp - > dev , " CCP2 err:%x \n " , lcx_irqstatus ) ;
2011-12-07 08:34:50 -03:00
return ;
2011-02-12 18:05:06 -03:00
}
if ( lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_OCPERROR_IRQ ) {
2011-12-07 08:34:50 -03:00
pipe - > error = true ;
2011-02-12 18:05:06 -03:00
dev_dbg ( isp - > dev , " CCP2 OCP err:%x \n " , lcm_irqstatus ) ;
}
if ( omap3isp_module_sync_is_stopping ( & ccp2 - > wait , & ccp2 - > stopping ) )
2011-12-07 08:34:50 -03:00
return ;
2011-02-12 18:05:06 -03:00
/* Handle queued buffers on frame end interrupts */
if ( lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ )
ccp2_isr_buffer ( ccp2 ) ;
}
/* -----------------------------------------------------------------------------
* V4L2 subdev operations
*/
static const unsigned int ccp2_fmts [ ] = {
V4L2_MBUS_FMT_SGRBG10_1X10 ,
V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 ,
} ;
/*
* __ccp2_get_format - helper function for getting ccp2 format
* @ ccp2 : Pointer to ISP CCP2 device
* @ fh : V4L2 subdev file handle
* @ pad : pad number
* @ which : wanted subdev format
* return format structure or NULL on error
*/
static struct v4l2_mbus_framefmt *
__ccp2_get_format ( struct isp_ccp2_device * ccp2 , struct v4l2_subdev_fh * fh ,
unsigned int pad , enum v4l2_subdev_format_whence which )
{
if ( which = = V4L2_SUBDEV_FORMAT_TRY )
return v4l2_subdev_get_try_format ( fh , pad ) ;
else
return & ccp2 - > formats [ pad ] ;
}
/*
* ccp2_try_format - Handle try format by pad subdev method
* @ ccp2 : Pointer to ISP CCP2 device
* @ fh : V4L2 subdev file handle
* @ pad : pad num
* @ fmt : pointer to v4l2 mbus format structure
* @ which : wanted subdev format
*/
static void ccp2_try_format ( struct isp_ccp2_device * ccp2 ,
struct v4l2_subdev_fh * fh , unsigned int pad ,
struct v4l2_mbus_framefmt * fmt ,
enum v4l2_subdev_format_whence which )
{
struct v4l2_mbus_framefmt * format ;
switch ( pad ) {
case CCP2_PAD_SINK :
if ( fmt - > code ! = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 )
fmt - > code = V4L2_MBUS_FMT_SGRBG10_1X10 ;
if ( ccp2 - > input = = CCP2_INPUT_SENSOR ) {
fmt - > width = clamp_t ( u32 , fmt - > width ,
ISPCCP2_DAT_START_MIN ,
ISPCCP2_DAT_START_MAX ) ;
fmt - > height = clamp_t ( u32 , fmt - > height ,
ISPCCP2_DAT_SIZE_MIN ,
ISPCCP2_DAT_SIZE_MAX ) ;
} else if ( ccp2 - > input = = CCP2_INPUT_MEMORY ) {
fmt - > width = clamp_t ( u32 , fmt - > width ,
ISPCCP2_LCM_HSIZE_COUNT_MIN ,
ISPCCP2_LCM_HSIZE_COUNT_MAX ) ;
fmt - > height = clamp_t ( u32 , fmt - > height ,
ISPCCP2_LCM_VSIZE_MIN ,
ISPCCP2_LCM_VSIZE_MAX ) ;
}
break ;
case CCP2_PAD_SOURCE :
/* Source format - copy sink format and change pixel code
* to SGRBG10_1X10 as we don ' t support CCP2 write to memory .
* When CCP2 write to memory feature will be added this
* should be changed properly .
*/
format = __ccp2_get_format ( ccp2 , fh , CCP2_PAD_SINK , which ) ;
memcpy ( fmt , format , sizeof ( * fmt ) ) ;
fmt - > code = V4L2_MBUS_FMT_SGRBG10_1X10 ;
break ;
}
fmt - > field = V4L2_FIELD_NONE ;
fmt - > colorspace = V4L2_COLORSPACE_SRGB ;
}
/*
* ccp2_enum_mbus_code - Handle pixel format enumeration
* @ sd : pointer to v4l2 subdev structure
* @ fh : V4L2 subdev file handle
* @ code : pointer to v4l2_subdev_mbus_code_enum structure
* return - EINVAL or zero on success
*/
static int ccp2_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_mbus_code_enum * code )
{
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
struct v4l2_mbus_framefmt * format ;
if ( code - > pad = = CCP2_PAD_SINK ) {
if ( code - > index > = ARRAY_SIZE ( ccp2_fmts ) )
return - EINVAL ;
code - > code = ccp2_fmts [ code - > index ] ;
} else {
if ( code - > index ! = 0 )
return - EINVAL ;
format = __ccp2_get_format ( ccp2 , fh , CCP2_PAD_SINK ,
V4L2_SUBDEV_FORMAT_TRY ) ;
code - > code = format - > code ;
}
return 0 ;
}
static int ccp2_enum_frame_size ( struct v4l2_subdev * sd ,
struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_frame_size_enum * fse )
{
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
struct v4l2_mbus_framefmt format ;
if ( fse - > index ! = 0 )
return - EINVAL ;
format . code = fse - > code ;
format . width = 1 ;
format . height = 1 ;
ccp2_try_format ( ccp2 , fh , fse - > pad , & format , V4L2_SUBDEV_FORMAT_TRY ) ;
fse - > min_width = format . width ;
fse - > min_height = format . height ;
if ( format . code ! = fse - > code )
return - EINVAL ;
format . code = fse - > code ;
format . width = - 1 ;
format . height = - 1 ;
ccp2_try_format ( ccp2 , fh , fse - > pad , & format , V4L2_SUBDEV_FORMAT_TRY ) ;
fse - > max_width = format . width ;
fse - > max_height = format . height ;
return 0 ;
}
/*
* ccp2_get_format - Handle get format by pads subdev method
* @ sd : pointer to v4l2 subdev structure
* @ fh : V4L2 subdev file handle
* @ fmt : pointer to v4l2 subdev format structure
2011-03-30 22:57:33 -03:00
* return - EINVAL or zero on success
2011-02-12 18:05:06 -03:00
*/
static int ccp2_get_format ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_format * fmt )
{
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
struct v4l2_mbus_framefmt * format ;
format = __ccp2_get_format ( ccp2 , fh , fmt - > pad , fmt - > which ) ;
if ( format = = NULL )
return - EINVAL ;
fmt - > format = * format ;
return 0 ;
}
/*
* ccp2_set_format - Handle set format by pads subdev method
* @ sd : pointer to v4l2 subdev structure
* @ fh : V4L2 subdev file handle
* @ fmt : pointer to v4l2 subdev format structure
* returns zero
*/
static int ccp2_set_format ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_format * fmt )
{
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
struct v4l2_mbus_framefmt * format ;
format = __ccp2_get_format ( ccp2 , fh , fmt - > pad , fmt - > which ) ;
if ( format = = NULL )
return - EINVAL ;
ccp2_try_format ( ccp2 , fh , fmt - > pad , & fmt - > format , fmt - > which ) ;
* format = fmt - > format ;
/* Propagate the format from sink to source */
if ( fmt - > pad = = CCP2_PAD_SINK ) {
format = __ccp2_get_format ( ccp2 , fh , CCP2_PAD_SOURCE ,
fmt - > which ) ;
* format = fmt - > format ;
ccp2_try_format ( ccp2 , fh , CCP2_PAD_SOURCE , format , fmt - > which ) ;
}
return 0 ;
}
/*
* ccp2_init_formats - Initialize formats on all pads
* @ sd : ISP CCP2 V4L2 subdevice
* @ fh : V4L2 subdev file handle
*
* Initialize all pad formats with default values . If fh is not NULL , try
* formats are initialized on the file handle . Otherwise active formats are
* initialized on the device .
*/
static int ccp2_init_formats ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
struct v4l2_subdev_format format ;
memset ( & format , 0 , sizeof ( format ) ) ;
format . pad = CCP2_PAD_SINK ;
format . which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE ;
format . format . code = V4L2_MBUS_FMT_SGRBG10_1X10 ;
format . format . width = 4096 ;
format . format . height = 4096 ;
ccp2_set_format ( sd , fh , & format ) ;
return 0 ;
}
/*
* ccp2_s_stream - Enable / Disable streaming on ccp2 subdev
* @ sd : pointer to v4l2 subdev structure
* @ enable : 1 = = Enable , 0 = = Disable
* return zero
*/
static int ccp2_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
struct isp_device * isp = to_isp_device ( ccp2 ) ;
struct device * dev = to_device ( ccp2 ) ;
int ret ;
if ( ccp2 - > state = = ISP_PIPELINE_STREAM_STOPPED ) {
if ( enable = = ISP_PIPELINE_STREAM_STOPPED )
return 0 ;
atomic_set ( & ccp2 - > stopping , 0 ) ;
}
switch ( enable ) {
case ISP_PIPELINE_STREAM_CONTINUOUS :
if ( ccp2 - > phy ) {
ret = omap3isp_csiphy_acquire ( ccp2 - > phy ) ;
if ( ret < 0 )
return ret ;
}
ccp2_if_configure ( ccp2 ) ;
ccp2_print_status ( ccp2 ) ;
/* Enable CSI1/CCP2 interface */
2013-05-29 00:47:41 -03:00
ret = ccp2_if_enable ( ccp2 , 1 ) ;
if ( ret < 0 ) {
if ( ccp2 - > phy )
omap3isp_csiphy_release ( ccp2 - > phy ) ;
return ret ;
}
2011-02-12 18:05:06 -03:00
break ;
case ISP_PIPELINE_STREAM_SINGLESHOT :
if ( ccp2 - > state ! = ISP_PIPELINE_STREAM_SINGLESHOT ) {
struct v4l2_mbus_framefmt * format ;
format = & ccp2 - > formats [ CCP2_PAD_SINK ] ;
ccp2 - > mem_cfg . hsize_count = format - > width ;
ccp2 - > mem_cfg . vsize_count = format - > height ;
ccp2 - > mem_cfg . src_ofst = 0 ;
ccp2_mem_configure ( ccp2 , & ccp2 - > mem_cfg ) ;
omap3isp_sbl_enable ( isp , OMAP3_ISP_SBL_CSI1_READ ) ;
ccp2_print_status ( ccp2 ) ;
}
ccp2_mem_enable ( ccp2 , 1 ) ;
break ;
case ISP_PIPELINE_STREAM_STOPPED :
if ( omap3isp_module_sync_idle ( & sd - > entity , & ccp2 - > wait ,
& ccp2 - > stopping ) )
dev_dbg ( dev , " %s: module stop timeout. \n " , sd - > name ) ;
if ( ccp2 - > input = = CCP2_INPUT_MEMORY ) {
ccp2_mem_enable ( ccp2 , 0 ) ;
omap3isp_sbl_disable ( isp , OMAP3_ISP_SBL_CSI1_READ ) ;
} else if ( ccp2 - > input = = CCP2_INPUT_SENSOR ) {
/* Disable CSI1/CCP2 interface */
ccp2_if_enable ( ccp2 , 0 ) ;
if ( ccp2 - > phy )
omap3isp_csiphy_release ( ccp2 - > phy ) ;
}
break ;
}
ccp2 - > state = enable ;
return 0 ;
}
/* subdev video operations */
static const struct v4l2_subdev_video_ops ccp2_sd_video_ops = {
. s_stream = ccp2_s_stream ,
} ;
/* subdev pad operations */
static const struct v4l2_subdev_pad_ops ccp2_sd_pad_ops = {
. enum_mbus_code = ccp2_enum_mbus_code ,
. enum_frame_size = ccp2_enum_frame_size ,
. get_fmt = ccp2_get_format ,
. set_fmt = ccp2_set_format ,
} ;
/* subdev operations */
static const struct v4l2_subdev_ops ccp2_sd_ops = {
. video = & ccp2_sd_video_ops ,
. pad = & ccp2_sd_pad_ops ,
} ;
/* subdev internal operations */
static const struct v4l2_subdev_internal_ops ccp2_sd_internal_ops = {
. open = ccp2_init_formats ,
} ;
/* --------------------------------------------------------------------------
* ISP ccp2 video device node
*/
/*
* ccp2_video_queue - Queue video buffer .
* @ video : Pointer to isp video structure
* @ buffer : Pointer to isp_buffer structure
* return - EIO or zero on success
*/
static int ccp2_video_queue ( struct isp_video * video , struct isp_buffer * buffer )
{
struct isp_ccp2_device * ccp2 = & video - > isp - > isp_ccp2 ;
ccp2_set_inaddr ( ccp2 , buffer - > isp_addr ) ;
return 0 ;
}
static const struct isp_video_operations ccp2_video_ops = {
. queue = ccp2_video_queue ,
} ;
/* -----------------------------------------------------------------------------
* Media entity operations
*/
/*
* ccp2_link_setup - Setup ccp2 connections .
* @ entity : Pointer to media entity structure
* @ local : Pointer to local pad array
* @ remote : Pointer to remote pad array
* @ flags : Link flags
* return - EINVAL on error or zero on success
*/
static int ccp2_link_setup ( struct media_entity * entity ,
const struct media_pad * local ,
const struct media_pad * remote , u32 flags )
{
struct v4l2_subdev * sd = media_entity_to_v4l2_subdev ( entity ) ;
struct isp_ccp2_device * ccp2 = v4l2_get_subdevdata ( sd ) ;
switch ( local - > index | media_entity_type ( remote - > entity ) ) {
case CCP2_PAD_SINK | MEDIA_ENT_T_DEVNODE :
/* read from memory */
if ( flags & MEDIA_LNK_FL_ENABLED ) {
if ( ccp2 - > input = = CCP2_INPUT_SENSOR )
return - EBUSY ;
ccp2 - > input = CCP2_INPUT_MEMORY ;
} else {
if ( ccp2 - > input = = CCP2_INPUT_MEMORY )
ccp2 - > input = CCP2_INPUT_NONE ;
}
break ;
case CCP2_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV :
/* read from sensor/phy */
if ( flags & MEDIA_LNK_FL_ENABLED ) {
if ( ccp2 - > input = = CCP2_INPUT_MEMORY )
return - EBUSY ;
ccp2 - > input = CCP2_INPUT_SENSOR ;
} else {
if ( ccp2 - > input = = CCP2_INPUT_SENSOR )
ccp2 - > input = CCP2_INPUT_NONE ;
} break ;
case CCP2_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV :
/* write to video port/ccdc */
if ( flags & MEDIA_LNK_FL_ENABLED )
ccp2 - > output = CCP2_OUTPUT_CCDC ;
else
ccp2 - > output = CCP2_OUTPUT_NONE ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/* media operations */
static const struct media_entity_operations ccp2_media_ops = {
. link_setup = ccp2_link_setup ,
2012-01-11 13:27:02 -03:00
. link_validate = v4l2_subdev_link_validate ,
2011-02-12 18:05:06 -03:00
} ;
2011-09-22 16:59:26 -03:00
/*
* omap3isp_ccp2_unregister_entities - Unregister media entities : subdev
* @ ccp2 : Pointer to ISP CCP2 device
*/
void omap3isp_ccp2_unregister_entities ( struct isp_ccp2_device * ccp2 )
{
v4l2_device_unregister_subdev ( & ccp2 - > subdev ) ;
omap3isp_video_unregister ( & ccp2 - > video_in ) ;
}
/*
* omap3isp_ccp2_register_entities - Register the subdev media entity
* @ ccp2 : Pointer to ISP CCP2 device
* @ vdev : Pointer to v4l device
* return negative error code or zero on success
*/
int omap3isp_ccp2_register_entities ( struct isp_ccp2_device * ccp2 ,
struct v4l2_device * vdev )
{
int ret ;
/* Register the subdev and video nodes. */
ret = v4l2_device_register_subdev ( vdev , & ccp2 - > subdev ) ;
if ( ret < 0 )
goto error ;
ret = omap3isp_video_register ( & ccp2 - > video_in , vdev ) ;
if ( ret < 0 )
goto error ;
return 0 ;
error :
omap3isp_ccp2_unregister_entities ( ccp2 ) ;
return ret ;
}
/* -----------------------------------------------------------------------------
* ISP ccp2 initialisation and cleanup
*/
2011-02-12 18:05:06 -03:00
/*
* ccp2_init_entities - Initialize ccp2 subdev and media entity .
* @ ccp2 : Pointer to ISP CCP2 device
* return negative error code or zero on success
*/
static int ccp2_init_entities ( struct isp_ccp2_device * ccp2 )
{
struct v4l2_subdev * sd = & ccp2 - > subdev ;
struct media_pad * pads = ccp2 - > pads ;
struct media_entity * me = & sd - > entity ;
int ret ;
ccp2 - > input = CCP2_INPUT_NONE ;
ccp2 - > output = CCP2_OUTPUT_NONE ;
v4l2_subdev_init ( sd , & ccp2_sd_ops ) ;
sd - > internal_ops = & ccp2_sd_internal_ops ;
strlcpy ( sd - > name , " OMAP3 ISP CCP2 " , sizeof ( sd - > name ) ) ;
sd - > grp_id = 1 < < 16 ; /* group ID for isp subdevs */
v4l2_set_subdevdata ( sd , ccp2 ) ;
sd - > flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
pads [ CCP2_PAD_SINK ] . flags = MEDIA_PAD_FL_SINK ;
pads [ CCP2_PAD_SOURCE ] . flags = MEDIA_PAD_FL_SOURCE ;
me - > ops = & ccp2_media_ops ;
ret = media_entity_init ( me , CCP2_PADS_NUM , pads , 0 ) ;
if ( ret < 0 )
return ret ;
ccp2_init_formats ( sd , NULL ) ;
/*
* The CCP2 has weird line alignment requirements , possibly caused by
* DPCM8 decompression . Line length for data read from memory must be a
* multiple of 128 bits ( 16 bytes ) in continuous mode ( when no padding
* is present at end of lines ) . Additionally , if padding is used , the
* padded line length must be a multiple of 32 bytes . To simplify the
* implementation we use a fixed 32 bytes alignment regardless of the
* input format and width . If strict 128 bits alignment support is
* required ispvideo will need to be made aware of this special dual
* alignement requirements .
*/
ccp2 - > video_in . type = V4L2_BUF_TYPE_VIDEO_OUTPUT ;
ccp2 - > video_in . bpl_alignment = 32 ;
ccp2 - > video_in . bpl_max = 0xffffffe0 ;
ccp2 - > video_in . isp = to_isp_device ( ccp2 ) ;
ccp2 - > video_in . ops = & ccp2_video_ops ;
ccp2 - > video_in . capture_mem = PAGE_ALIGN ( 4096 * 4096 ) * 3 ;
ret = omap3isp_video_init ( & ccp2 - > video_in , " CCP2 " ) ;
if ( ret < 0 )
2011-09-22 17:10:30 -03:00
goto error_video ;
2011-02-12 18:05:06 -03:00
/* Connect the video node to the ccp2 subdev. */
ret = media_entity_create_link ( & ccp2 - > video_in . video . entity , 0 ,
& ccp2 - > subdev . entity , CCP2_PAD_SINK , 0 ) ;
if ( ret < 0 )
2011-09-22 17:10:30 -03:00
goto error_link ;
2011-02-12 18:05:06 -03:00
return 0 ;
2011-09-22 17:10:30 -03:00
error_link :
omap3isp_video_cleanup ( & ccp2 - > video_in ) ;
error_video :
media_entity_cleanup ( & ccp2 - > subdev . entity ) ;
return ret ;
2011-02-12 18:05:06 -03:00
}
/*
* omap3isp_ccp2_init - CCP2 initialization .
* @ isp : Pointer to ISP device
* return negative error code or zero on success
*/
int omap3isp_ccp2_init ( struct isp_device * isp )
{
struct isp_ccp2_device * ccp2 = & isp - > isp_ccp2 ;
int ret ;
init_waitqueue_head ( & ccp2 - > wait ) ;
2011-05-03 07:41:22 -03:00
/*
* On the OMAP34xx the CSI1 receiver is operated in the CSIb IO
* complex , which is powered by vdds_csib power rail . Hence the
* request for the regulator .
*
* On the OMAP36xx , the CCP2 uses the CSI PHY1 or PHY2 , shared with
2011-02-12 18:05:06 -03:00
* the CSI2c or CSI2a receivers . The PHY then needs to be explicitly
* configured .
*
* TODO : Don ' t hardcode the usage of PHY1 ( shared with CSI2c ) .
*/
2011-05-03 07:41:22 -03:00
if ( isp - > revision = = ISP_REVISION_2_0 ) {
2012-12-25 17:27:31 -03:00
ccp2 - > vdds_csib = devm_regulator_get ( isp - > dev , " vdds_csib " ) ;
2011-05-03 07:41:22 -03:00
if ( IS_ERR ( ccp2 - > vdds_csib ) ) {
dev_dbg ( isp - > dev ,
" Could not get regulator vdds_csib \n " ) ;
ccp2 - > vdds_csib = NULL ;
}
} else if ( isp - > revision = = ISP_REVISION_15_0 ) {
2011-02-12 18:05:06 -03:00
ccp2 - > phy = & isp - > isp_csiphy1 ;
2011-05-03 07:41:22 -03:00
}
2011-02-12 18:05:06 -03:00
ret = ccp2_init_entities ( ccp2 ) ;
2012-12-25 17:27:31 -03:00
if ( ret < 0 )
2011-09-22 17:10:30 -03:00
return ret ;
2011-02-12 18:05:06 -03:00
ccp2_reset ( ccp2 ) ;
2011-09-22 17:10:30 -03:00
return 0 ;
2011-02-12 18:05:06 -03:00
}
2011-09-22 16:59:26 -03:00
/*
* omap3isp_ccp2_cleanup - CCP2 un - initialization
* @ isp : Pointer to ISP device
*/
void omap3isp_ccp2_cleanup ( struct isp_device * isp )
{
struct isp_ccp2_device * ccp2 = & isp - > isp_ccp2 ;
omap3isp_video_cleanup ( & ccp2 - > video_in ) ;
media_entity_cleanup ( & ccp2 - > subdev . entity ) ;
}