2010-07-04 08:55:10 +04:00
/*
2012-10-30 18:28:59 +04:00
* V4L2 Driver for i . MX27 camera host
2010-07-04 08:55:10 +04:00
*
* Copyright ( C ) 2008 , Sascha Hauer , Pengutronix
* Copyright ( C ) 2010 , Baruch Siach , Orex Computed Radiography
2012-01-30 16:14:10 +04:00
* Copyright ( C ) 2012 , Javier Martin , Vista Silicon S . L .
2010-07-04 08:55:10 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/init.h>
# include <linux/module.h>
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/dma-mapping.h>
# include <linux/errno.h>
# include <linux/fs.h>
2012-02-28 19:26:43 +04:00
# include <linux/gcd.h>
2010-07-04 08:55:10 +04:00
# include <linux/interrupt.h>
# include <linux/kernel.h>
2012-05-08 18:46:44 +04:00
# include <linux/math64.h>
2010-07-04 08:55:10 +04:00
# include <linux/mm.h>
# include <linux/moduleparam.h>
# include <linux/time.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <media/v4l2-common.h>
# include <media/v4l2-dev.h>
2012-01-30 16:14:10 +04:00
# include <media/videobuf2-core.h>
# include <media/videobuf2-dma-contig.h>
2010-07-04 08:55:10 +04:00
# include <media/soc_camera.h>
# include <media/soc_mediabus.h>
# include <linux/videodev2.h>
2012-08-24 17:14:29 +04:00
# include <linux/platform_data/camera-mx2.h>
2010-07-04 08:55:10 +04:00
# include <asm/dma.h>
# define MX2_CAM_DRV_NAME "mx2-camera"
2011-06-25 18:28:37 +04:00
# define MX2_CAM_VERSION "0.0.6"
2010-07-04 08:55:10 +04:00
# define MX2_CAM_DRIVER_DESCRIPTION "i.MX2x_Camera"
/* reset values */
# define CSICR1_RESET_VAL 0x40000800
# define CSICR2_RESET_VAL 0x0
# define CSICR3_RESET_VAL 0x0
/* csi control reg 1 */
# define CSICR1_SWAP16_EN (1 << 31)
# define CSICR1_EXT_VSYNC (1 << 30)
# define CSICR1_EOF_INTEN (1 << 29)
# define CSICR1_PRP_IF_EN (1 << 28)
# define CSICR1_CCIR_MODE (1 << 27)
# define CSICR1_COF_INTEN (1 << 26)
# define CSICR1_SF_OR_INTEN (1 << 25)
# define CSICR1_RF_OR_INTEN (1 << 24)
# define CSICR1_STATFF_LEVEL (3 << 22)
# define CSICR1_STATFF_INTEN (1 << 21)
2012-10-30 18:28:59 +04:00
# define CSICR1_RXFF_LEVEL(l) (((l) & 3) << 19)
2010-07-04 08:55:10 +04:00
# define CSICR1_RXFF_INTEN (1 << 18)
# define CSICR1_SOF_POL (1 << 17)
# define CSICR1_SOF_INTEN (1 << 16)
# define CSICR1_MCLKDIV(d) (((d) & 0xF) << 12)
# define CSICR1_HSYNC_POL (1 << 11)
# define CSICR1_CCIR_EN (1 << 10)
# define CSICR1_MCLKEN (1 << 9)
# define CSICR1_FCC (1 << 8)
# define CSICR1_PACK_DIR (1 << 7)
# define CSICR1_CLR_STATFIFO (1 << 6)
# define CSICR1_CLR_RXFIFO (1 << 5)
# define CSICR1_GCLK_MODE (1 << 4)
# define CSICR1_INV_DATA (1 << 3)
# define CSICR1_INV_PCLK (1 << 2)
# define CSICR1_REDGE (1 << 1)
2012-07-11 19:34:54 +04:00
# define CSICR1_FMT_MASK (CSICR1_PACK_DIR | CSICR1_SWAP16_EN)
2010-07-04 08:55:10 +04:00
# define SHIFT_STATFF_LEVEL 22
# define SHIFT_RXFF_LEVEL 19
# define SHIFT_MCLKDIV 12
# define SHIFT_FRMCNT 16
# define CSICR1 0x00
# define CSICR2 0x04
2012-10-30 18:28:59 +04:00
# define CSISR 0x08
2010-07-04 08:55:10 +04:00
# define CSISTATFIFO 0x0c
# define CSIRFIFO 0x10
# define CSIRXCNT 0x14
2012-10-30 18:28:59 +04:00
# define CSICR3 0x1c
2010-07-04 08:55:10 +04:00
# define CSIDMASA_STATFIFO 0x20
# define CSIDMATA_STATFIFO 0x24
# define CSIDMASA_FB1 0x28
# define CSIDMASA_FB2 0x2c
# define CSIFBUF_PARA 0x30
# define CSIIMAG_PARA 0x34
/* EMMA PrP */
# define PRP_CNTL 0x00
# define PRP_INTR_CNTL 0x04
# define PRP_INTRSTATUS 0x08
# define PRP_SOURCE_Y_PTR 0x0c
# define PRP_SOURCE_CB_PTR 0x10
# define PRP_SOURCE_CR_PTR 0x14
# define PRP_DEST_RGB1_PTR 0x18
# define PRP_DEST_RGB2_PTR 0x1c
# define PRP_DEST_Y_PTR 0x20
# define PRP_DEST_CB_PTR 0x24
# define PRP_DEST_CR_PTR 0x28
# define PRP_SRC_FRAME_SIZE 0x2c
# define PRP_DEST_CH1_LINE_STRIDE 0x30
# define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
# define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
# define PRP_CH1_OUT_IMAGE_SIZE 0x3c
# define PRP_CH2_OUT_IMAGE_SIZE 0x40
# define PRP_SRC_LINE_STRIDE 0x44
# define PRP_CSC_COEF_012 0x48
# define PRP_CSC_COEF_345 0x4c
# define PRP_CSC_COEF_678 0x50
# define PRP_CH1_RZ_HORI_COEF1 0x54
# define PRP_CH1_RZ_HORI_COEF2 0x58
# define PRP_CH1_RZ_HORI_VALID 0x5c
# define PRP_CH1_RZ_VERT_COEF1 0x60
# define PRP_CH1_RZ_VERT_COEF2 0x64
# define PRP_CH1_RZ_VERT_VALID 0x68
# define PRP_CH2_RZ_HORI_COEF1 0x6c
# define PRP_CH2_RZ_HORI_COEF2 0x70
# define PRP_CH2_RZ_HORI_VALID 0x74
# define PRP_CH2_RZ_VERT_COEF1 0x78
# define PRP_CH2_RZ_VERT_COEF2 0x7c
# define PRP_CH2_RZ_VERT_VALID 0x80
# define PRP_CNTL_CH1EN (1 << 0)
# define PRP_CNTL_CH2EN (1 << 1)
# define PRP_CNTL_CSIEN (1 << 2)
# define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
# define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
# define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
# define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
# define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
# define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
# define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
# define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
# define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
# define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
# define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
# define PRP_CNTL_CH1_LEN (1 << 9)
# define PRP_CNTL_CH2_LEN (1 << 10)
# define PRP_CNTL_SKIP_FRAME (1 << 11)
# define PRP_CNTL_SWRST (1 << 12)
# define PRP_CNTL_CLKEN (1 << 13)
# define PRP_CNTL_WEN (1 << 14)
# define PRP_CNTL_CH1BYP (1 << 15)
# define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
# define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
# define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
# define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
# define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
# define PRP_CNTL_CH2B1EN (1 << 29)
# define PRP_CNTL_CH2B2EN (1 << 30)
# define PRP_CNTL_CH2FEN (1 << 31)
/* IRQ Enable and status register */
# define PRP_INTR_RDERR (1 << 0)
# define PRP_INTR_CH1WERR (1 << 1)
# define PRP_INTR_CH2WERR (1 << 2)
# define PRP_INTR_CH1FC (1 << 3)
# define PRP_INTR_CH2FC (1 << 5)
# define PRP_INTR_LBOVF (1 << 7)
# define PRP_INTR_CH2OVF (1 << 8)
2012-02-28 19:26:43 +04:00
/* Resizing registers */
# define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
# define PRP_RZ_VALID_BILINEAR (1 << 31)
2010-07-04 08:55:10 +04:00
# define MAX_VIDEO_MEM 16
2012-02-28 19:26:43 +04:00
# define RESIZE_NUM_MIN 1
# define RESIZE_NUM_MAX 20
# define BC_COEF 3
# define SZ_COEF (1 << BC_COEF)
# define RESIZE_DIR_H 0
# define RESIZE_DIR_V 1
# define RESIZE_ALGO_BILINEAR 0
# define RESIZE_ALGO_AVERAGING 1
2011-12-14 20:30:14 +04:00
struct mx2_prp_cfg {
int channel ;
u32 in_fmt ;
u32 out_fmt ;
u32 src_pixel ;
u32 ch1_pixel ;
u32 irq_flags ;
2012-07-11 19:34:54 +04:00
u32 csicr1 ;
2011-12-14 20:30:14 +04:00
} ;
2012-02-28 19:26:43 +04:00
/* prp resizing parameters */
struct emma_prp_resize {
int algo ; /* type of algorithm used */
int len ; /* number of coefficients */
unsigned char s [ RESIZE_NUM_MAX ] ; /* table of coefficients */
} ;
2011-12-14 20:30:14 +04:00
/* prp configuration for a client-host fmt pair */
struct mx2_fmt_cfg {
2014-11-10 20:28:31 +03:00
u32 in_fmt ;
2011-12-14 20:30:14 +04:00
u32 out_fmt ;
struct mx2_prp_cfg cfg ;
} ;
2012-02-22 14:59:34 +04:00
struct mx2_buf_internal {
struct list_head queue ;
int bufnum ;
bool discard ;
} ;
2012-01-30 16:14:10 +04:00
/* buffer for one video frame */
struct mx2_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb ;
2012-02-22 14:59:34 +04:00
struct mx2_buf_internal internal ;
2012-01-30 16:14:10 +04:00
} ;
2012-09-16 12:26:20 +04:00
enum mx2_camera_type {
IMX27_CAMERA ,
} ;
2010-07-04 08:55:10 +04:00
struct mx2_camera_dev {
struct device * dev ;
struct soc_camera_host soc_host ;
2012-10-30 16:03:26 +04:00
struct clk * clk_emma_ahb , * clk_emma_ipg ;
struct clk * clk_csi_ahb , * clk_csi_per ;
2010-07-04 08:55:10 +04:00
void __iomem * base_csi , * base_emma ;
struct mx2_camera_platform_data * pdata ;
unsigned long platform_flags ;
struct list_head capture ;
struct list_head active_bufs ;
2012-02-07 14:14:42 +04:00
struct list_head discard ;
2010-07-04 08:55:10 +04:00
spinlock_t lock ;
int dma ;
struct mx2_buffer * active ;
struct mx2_buffer * fb1_active ;
struct mx2_buffer * fb2_active ;
u32 csicr1 ;
2012-09-16 12:26:20 +04:00
enum mx2_camera_type devtype ;
2010-07-04 08:55:10 +04:00
2012-02-22 14:59:34 +04:00
struct mx2_buf_internal buf_discard [ 2 ] ;
2010-07-27 09:03:30 +04:00
void * discard_buffer ;
2010-07-04 08:55:10 +04:00
dma_addr_t discard_buffer_dma ;
size_t discard_size ;
2011-12-14 20:30:14 +04:00
struct mx2_fmt_cfg * emma_prp ;
2012-02-28 19:26:43 +04:00
struct emma_prp_resize resizing [ 2 ] ;
unsigned int s_width , s_height ;
2011-12-22 19:12:00 +04:00
u32 frame_count ;
2012-01-30 16:14:10 +04:00
struct vb2_alloc_ctx * alloc_ctx ;
2010-07-04 08:55:10 +04:00
} ;
2012-09-16 12:26:20 +04:00
static struct platform_device_id mx2_camera_devtype [ ] = {
{
. name = " imx27-camera " ,
. driver_data = IMX27_CAMERA ,
} , {
/* sentinel */
}
} ;
MODULE_DEVICE_TABLE ( platform , mx2_camera_devtype ) ;
2012-02-22 14:59:34 +04:00
static struct mx2_buffer * mx2_ibuf_to_buf ( struct mx2_buf_internal * int_buf )
{
return container_of ( int_buf , struct mx2_buffer , internal ) ;
}
2011-12-14 20:30:14 +04:00
static struct mx2_fmt_cfg mx27_emma_prp_table [ ] = {
/*
* This is a generic configuration which is valid for most
* prp input - output format combinations .
2012-12-27 20:33:02 +04:00
* We set the incoming and outgoing pixelformat to a
2011-12-14 20:30:14 +04:00
* 16 Bit wide format and adjust the bytesperline
* accordingly . With this configuration the inputdata
* will not be changed by the emma and could be any type
* of 16 Bit Pixelformat .
*/
{
. in_fmt = 0 ,
. out_fmt = 0 ,
. cfg = {
. channel = 1 ,
. in_fmt = PRP_CNTL_DATA_IN_RGB16 ,
. out_fmt = PRP_CNTL_CH1_OUT_RGB16 ,
. src_pixel = 0x2ca00565 , /* RGB565 */
. ch1_pixel = 0x2ca00565 , /* RGB565 */
. irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
PRP_INTR_CH1FC | PRP_INTR_LBOVF ,
2012-07-11 19:34:54 +04:00
. csicr1 = 0 ,
2011-12-14 20:30:14 +04:00
}
} ,
2012-07-12 12:56:13 +04:00
{
2014-11-10 20:28:31 +03:00
. in_fmt = MEDIA_BUS_FMT_UYVY8_2X8 ,
2012-07-12 12:56:13 +04:00
. out_fmt = V4L2_PIX_FMT_YUYV ,
. cfg = {
. channel = 1 ,
. in_fmt = PRP_CNTL_DATA_IN_YUV422 ,
. out_fmt = PRP_CNTL_CH1_OUT_YUV422 ,
. src_pixel = 0x22000888 , /* YUV422 (YUYV) */
. ch1_pixel = 0x62000888 , /* YUV422 (YUYV) */
. irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
PRP_INTR_CH1FC | PRP_INTR_LBOVF ,
. csicr1 = CSICR1_SWAP16_EN ,
}
} ,
{
2014-11-10 20:28:31 +03:00
. in_fmt = MEDIA_BUS_FMT_YUYV8_2X8 ,
2012-07-12 12:56:13 +04:00
. out_fmt = V4L2_PIX_FMT_YUYV ,
. cfg = {
. channel = 1 ,
. in_fmt = PRP_CNTL_DATA_IN_YUV422 ,
. out_fmt = PRP_CNTL_CH1_OUT_YUV422 ,
. src_pixel = 0x22000888 , /* YUV422 (YUYV) */
. ch1_pixel = 0x62000888 , /* YUV422 (YUYV) */
. irq_flags = PRP_INTR_RDERR | PRP_INTR_CH1WERR |
PRP_INTR_CH1FC | PRP_INTR_LBOVF ,
. csicr1 = CSICR1_PACK_DIR ,
}
} ,
2011-12-14 20:30:14 +04:00
{
2014-11-10 20:28:31 +03:00
. in_fmt = MEDIA_BUS_FMT_YUYV8_2X8 ,
2011-12-14 20:30:14 +04:00
. out_fmt = V4L2_PIX_FMT_YUV420 ,
. cfg = {
. channel = 2 ,
. in_fmt = PRP_CNTL_DATA_IN_YUV422 ,
. out_fmt = PRP_CNTL_CH2_OUT_YUV420 ,
. src_pixel = 0x22000888 , /* YUV422 (YUYV) */
. irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
PRP_INTR_CH2FC | PRP_INTR_LBOVF |
PRP_INTR_CH2OVF ,
2012-07-11 19:34:54 +04:00
. csicr1 = CSICR1_PACK_DIR ,
}
} ,
{
2014-11-10 20:28:31 +03:00
. in_fmt = MEDIA_BUS_FMT_UYVY8_2X8 ,
2012-07-11 19:34:54 +04:00
. out_fmt = V4L2_PIX_FMT_YUV420 ,
. cfg = {
. channel = 2 ,
. in_fmt = PRP_CNTL_DATA_IN_YUV422 ,
. out_fmt = PRP_CNTL_CH2_OUT_YUV420 ,
. src_pixel = 0x22000888 , /* YUV422 (YUYV) */
. irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
PRP_INTR_CH2FC | PRP_INTR_LBOVF |
PRP_INTR_CH2OVF ,
. csicr1 = CSICR1_SWAP16_EN ,
2011-12-14 20:30:14 +04:00
}
} ,
} ;
2014-11-10 20:28:31 +03:00
static struct mx2_fmt_cfg * mx27_emma_prp_get_format ( u32 in_fmt , u32 out_fmt )
2011-12-14 20:30:14 +04:00
{
int i ;
for ( i = 1 ; i < ARRAY_SIZE ( mx27_emma_prp_table ) ; i + + )
if ( ( mx27_emma_prp_table [ i ] . in_fmt = = in_fmt ) & &
( mx27_emma_prp_table [ i ] . out_fmt = = out_fmt ) ) {
return & mx27_emma_prp_table [ i ] ;
}
/* If no match return the most generic configuration */
return & mx27_emma_prp_table [ 0 ] ;
} ;
2012-02-07 14:14:42 +04:00
static void mx27_update_emma_buf ( struct mx2_camera_dev * pcdev ,
unsigned long phys , int bufnum )
{
struct mx2_fmt_cfg * prp = pcdev - > emma_prp ;
if ( prp - > cfg . channel = = 1 ) {
writel ( phys , pcdev - > base_emma +
PRP_DEST_RGB1_PTR + 4 * bufnum ) ;
} else {
writel ( phys , pcdev - > base_emma +
PRP_DEST_Y_PTR - 0x14 * bufnum ) ;
if ( prp - > out_fmt = = V4L2_PIX_FMT_YUV420 ) {
2013-04-04 15:21:12 +04:00
u32 imgsize = pcdev - > soc_host . icd - > user_height *
pcdev - > soc_host . icd - > user_width ;
2012-02-07 14:14:42 +04:00
writel ( phys + imgsize , pcdev - > base_emma +
PRP_DEST_CB_PTR - 0x14 * bufnum ) ;
writel ( phys + ( ( 5 * imgsize ) / 4 ) , pcdev - > base_emma +
PRP_DEST_CR_PTR - 0x14 * bufnum ) ;
}
}
}
2010-07-04 08:55:10 +04:00
static void mx2_camera_deactivate ( struct mx2_camera_dev * pcdev )
{
2012-10-30 16:03:26 +04:00
clk_disable_unprepare ( pcdev - > clk_csi_ahb ) ;
clk_disable_unprepare ( pcdev - > clk_csi_per ) ;
2010-07-04 08:55:10 +04:00
writel ( 0 , pcdev - > base_csi + CSICR1 ) ;
2012-10-30 18:28:59 +04:00
writel ( 0 , pcdev - > base_emma + PRP_CNTL ) ;
2010-07-04 08:55:10 +04:00
}
2013-04-04 18:49:13 +04:00
static int mx2_camera_add_device ( struct soc_camera_device * icd )
{
dev_info ( icd - > parent , " Camera driver attached to camera %d \n " ,
icd - > devnum ) ;
return 0 ;
}
static void mx2_camera_remove_device ( struct soc_camera_device * icd )
{
dev_info ( icd - > parent , " Camera driver detached from camera %d \n " ,
icd - > devnum ) ;
}
2010-07-04 08:55:10 +04:00
/*
* The following two functions absolutely depend on the fact , that
* there can be only one camera on mx2 camera sensor interface
*/
2013-04-04 18:49:13 +04:00
static int mx2_camera_clock_start ( struct soc_camera_host * ici )
2010-07-04 08:55:10 +04:00
{
struct mx2_camera_dev * pcdev = ici - > priv ;
int ret ;
u32 csicr1 ;
2012-10-30 16:03:26 +04:00
ret = clk_prepare_enable ( pcdev - > clk_csi_ahb ) ;
2010-07-04 08:55:10 +04:00
if ( ret < 0 )
return ret ;
2012-10-30 16:03:26 +04:00
ret = clk_prepare_enable ( pcdev - > clk_csi_per ) ;
if ( ret < 0 )
goto exit_csi_ahb ;
2012-10-30 18:28:59 +04:00
csicr1 = CSICR1_MCLKEN | CSICR1_PRP_IF_EN | CSICR1_FCC |
CSICR1_RXFF_LEVEL ( 0 ) ;
2010-07-04 08:55:10 +04:00
pcdev - > csicr1 = csicr1 ;
writel ( pcdev - > csicr1 , pcdev - > base_csi + CSICR1 ) ;
2012-02-07 14:14:42 +04:00
pcdev - > frame_count = 0 ;
2010-07-04 08:55:10 +04:00
return 0 ;
2012-10-30 16:03:26 +04:00
exit_csi_ahb :
clk_disable_unprepare ( pcdev - > clk_csi_ahb ) ;
return ret ;
2010-07-04 08:55:10 +04:00
}
2013-04-04 18:49:13 +04:00
static void mx2_camera_clock_stop ( struct soc_camera_host * ici )
2010-07-04 08:55:10 +04:00
{
struct mx2_camera_dev * pcdev = ici - > priv ;
mx2_camera_deactivate ( pcdev ) ;
}
/*
* Videobuf operations
*/
2012-01-30 16:14:10 +04:00
static int mx2_videobuf_setup ( struct vb2_queue * vq ,
const struct v4l2_format * fmt ,
unsigned int * count , unsigned int * num_planes ,
unsigned int sizes [ ] , void * alloc_ctxs [ ] )
2010-07-04 08:55:10 +04:00
{
2012-01-30 16:14:10 +04:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vq ) ;
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
2010-07-04 08:55:10 +04:00
2012-01-30 16:14:10 +04:00
dev_dbg ( icd - > parent , " count=%d, size=%d \n " , * count , sizes [ 0 ] ) ;
/* TODO: support for VIDIOC_CREATE_BUFS not ready */
if ( fmt ! = NULL )
return - ENOTTY ;
2010-07-04 08:55:10 +04:00
2012-01-30 16:14:10 +04:00
alloc_ctxs [ 0 ] = pcdev - > alloc_ctx ;
2012-03-21 15:03:21 +04:00
sizes [ 0 ] = icd - > sizeimage ;
2010-07-04 08:55:10 +04:00
if ( 0 = = * count )
* count = 32 ;
2012-01-30 16:14:10 +04:00
if ( ! * num_planes & &
sizes [ 0 ] * * count > MAX_VIDEO_MEM * 1024 * 1024 )
* count = ( MAX_VIDEO_MEM * 1024 * 1024 ) / sizes [ 0 ] ;
2010-07-04 08:55:10 +04:00
2012-01-30 16:14:10 +04:00
* num_planes = 1 ;
2010-07-04 08:55:10 +04:00
2012-01-30 16:14:10 +04:00
return 0 ;
2010-07-04 08:55:10 +04:00
}
2012-01-30 16:14:10 +04:00
static int mx2_videobuf_prepare ( struct vb2_buffer * vb )
2010-07-04 08:55:10 +04:00
{
2012-01-30 16:14:10 +04:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2010-07-04 08:55:10 +04:00
int ret = 0 ;
2012-01-30 16:14:10 +04:00
dev_dbg ( icd - > parent , " %s (vb=0x%p) 0x%p %lu \n " , __func__ ,
vb , vb2_plane_vaddr ( vb , 0 ) , vb2_get_plane_payload ( vb , 0 ) ) ;
2010-07-04 08:55:10 +04:00
# ifdef DEBUG
/*
* This can be useful if you want to see if we actually fill
* the buffer with something
*/
2012-01-30 16:14:10 +04:00
memset ( ( void * ) vb2_plane_vaddr ( vb , 0 ) ,
0xaa , vb2_get_plane_payload ( vb , 0 ) ) ;
2010-07-04 08:55:10 +04:00
# endif
2012-03-21 15:03:21 +04:00
vb2_set_plane_payload ( vb , 0 , icd - > sizeimage ) ;
2012-01-30 16:14:10 +04:00
if ( vb2_plane_vaddr ( vb , 0 ) & &
vb2_get_plane_payload ( vb , 0 ) > vb2_plane_size ( vb , 0 ) ) {
2010-07-04 08:55:10 +04:00
ret = - EINVAL ;
goto out ;
}
return 0 ;
out :
return ret ;
}
2012-01-30 16:14:10 +04:00
static void mx2_videobuf_queue ( struct vb2_buffer * vb )
2010-07-04 08:55:10 +04:00
{
2012-01-30 16:14:10 +04:00
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2010-07-04 08:55:10 +04:00
struct soc_camera_host * ici =
2011-07-16 03:03:38 +04:00
to_soc_camera_host ( icd - > parent ) ;
2010-07-04 08:55:10 +04:00
struct mx2_camera_dev * pcdev = ici - > priv ;
struct mx2_buffer * buf = container_of ( vb , struct mx2_buffer , vb ) ;
unsigned long flags ;
2012-01-30 16:14:10 +04:00
dev_dbg ( icd - > parent , " %s (vb=0x%p) 0x%p %lu \n " , __func__ ,
vb , vb2_plane_vaddr ( vb , 0 ) , vb2_get_plane_payload ( vb , 0 ) ) ;
2010-07-04 08:55:10 +04:00
spin_lock_irqsave ( & pcdev - > lock , flags ) ;
2012-02-22 14:59:34 +04:00
list_add_tail ( & buf - > internal . queue , & pcdev - > capture ) ;
2010-07-04 08:55:10 +04:00
spin_unlock_irqrestore ( & pcdev - > lock , flags ) ;
}
2012-02-07 14:14:42 +04:00
static void mx27_camera_emma_buf_init ( struct soc_camera_device * icd ,
int bytesperline )
{
struct soc_camera_host * ici =
to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
struct mx2_fmt_cfg * prp = pcdev - > emma_prp ;
2012-02-28 19:26:43 +04:00
writel ( ( pcdev - > s_width < < 16 ) | pcdev - > s_height ,
2012-02-07 14:14:42 +04:00
pcdev - > base_emma + PRP_SRC_FRAME_SIZE ) ;
writel ( prp - > cfg . src_pixel ,
pcdev - > base_emma + PRP_SRC_PIXEL_FORMAT_CNTL ) ;
if ( prp - > cfg . channel = = 1 ) {
writel ( ( icd - > user_width < < 16 ) | icd - > user_height ,
pcdev - > base_emma + PRP_CH1_OUT_IMAGE_SIZE ) ;
writel ( bytesperline ,
pcdev - > base_emma + PRP_DEST_CH1_LINE_STRIDE ) ;
writel ( prp - > cfg . ch1_pixel ,
pcdev - > base_emma + PRP_CH1_PIXEL_FORMAT_CNTL ) ;
} else { /* channel 2 */
writel ( ( icd - > user_width < < 16 ) | icd - > user_height ,
pcdev - > base_emma + PRP_CH2_OUT_IMAGE_SIZE ) ;
}
/* Enable interrupts */
writel ( prp - > cfg . irq_flags , pcdev - > base_emma + PRP_INTR_CNTL ) ;
}
2012-02-28 19:26:43 +04:00
static void mx2_prp_resize_commit ( struct mx2_camera_dev * pcdev )
{
int dir ;
for ( dir = RESIZE_DIR_H ; dir < = RESIZE_DIR_V ; dir + + ) {
unsigned char * s = pcdev - > resizing [ dir ] . s ;
int len = pcdev - > resizing [ dir ] . len ;
unsigned int coeff [ 2 ] = { 0 , 0 } ;
unsigned int valid = 0 ;
int i ;
if ( len = = 0 )
continue ;
for ( i = RESIZE_NUM_MAX - 1 ; i > = 0 ; i - - ) {
int j ;
j = i > 9 ? 1 : 0 ;
coeff [ j ] = ( coeff [ j ] < < BC_COEF ) |
( s [ i ] & ( SZ_COEF - 1 ) ) ;
if ( i = = 5 | | i = = 15 )
coeff [ j ] < < = 1 ;
valid = ( valid < < 1 ) | ( s [ i ] > > BC_COEF ) ;
}
valid | = PRP_RZ_VALID_TBL_LEN ( len ) ;
if ( pcdev - > resizing [ dir ] . algo = = RESIZE_ALGO_BILINEAR )
valid | = PRP_RZ_VALID_BILINEAR ;
if ( pcdev - > emma_prp - > cfg . channel = = 1 ) {
if ( dir = = RESIZE_DIR_H ) {
writel ( coeff [ 0 ] , pcdev - > base_emma +
PRP_CH1_RZ_HORI_COEF1 ) ;
writel ( coeff [ 1 ] , pcdev - > base_emma +
PRP_CH1_RZ_HORI_COEF2 ) ;
writel ( valid , pcdev - > base_emma +
PRP_CH1_RZ_HORI_VALID ) ;
} else {
writel ( coeff [ 0 ] , pcdev - > base_emma +
PRP_CH1_RZ_VERT_COEF1 ) ;
writel ( coeff [ 1 ] , pcdev - > base_emma +
PRP_CH1_RZ_VERT_COEF2 ) ;
writel ( valid , pcdev - > base_emma +
PRP_CH1_RZ_VERT_VALID ) ;
}
} else {
if ( dir = = RESIZE_DIR_H ) {
writel ( coeff [ 0 ] , pcdev - > base_emma +
PRP_CH2_RZ_HORI_COEF1 ) ;
writel ( coeff [ 1 ] , pcdev - > base_emma +
PRP_CH2_RZ_HORI_COEF2 ) ;
writel ( valid , pcdev - > base_emma +
PRP_CH2_RZ_HORI_VALID ) ;
} else {
writel ( coeff [ 0 ] , pcdev - > base_emma +
PRP_CH2_RZ_VERT_COEF1 ) ;
writel ( coeff [ 1 ] , pcdev - > base_emma +
PRP_CH2_RZ_VERT_COEF2 ) ;
writel ( valid , pcdev - > base_emma +
PRP_CH2_RZ_VERT_VALID ) ;
}
}
}
}
2012-01-30 16:14:11 +04:00
static int mx2_start_streaming ( struct vb2_queue * q , unsigned int count )
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( q ) ;
struct soc_camera_host * ici =
to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
struct mx2_fmt_cfg * prp = pcdev - > emma_prp ;
2012-02-07 14:14:42 +04:00
struct vb2_buffer * vb ;
struct mx2_buffer * buf ;
unsigned long phys ;
int bytesperline ;
2012-10-30 18:28:59 +04:00
unsigned long flags ;
2012-01-30 16:14:11 +04:00
2012-10-30 18:28:59 +04:00
if ( count < 2 )
2013-12-13 20:13:44 +04:00
return - ENOBUFS ;
2012-01-30 16:14:11 +04:00
2012-10-30 18:28:59 +04:00
spin_lock_irqsave ( & pcdev - > lock , flags ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
buf = list_first_entry ( & pcdev - > capture , struct mx2_buffer ,
internal . queue ) ;
buf - > internal . bufnum = 0 ;
vb = & buf - > vb ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
phys = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
mx27_update_emma_buf ( pcdev , phys , buf - > internal . bufnum ) ;
list_move_tail ( pcdev - > capture . next , & pcdev - > active_bufs ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
buf = list_first_entry ( & pcdev - > capture , struct mx2_buffer ,
internal . queue ) ;
buf - > internal . bufnum = 1 ;
vb = & buf - > vb ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
phys = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
mx27_update_emma_buf ( pcdev , phys , buf - > internal . bufnum ) ;
list_move_tail ( pcdev - > capture . next , & pcdev - > active_bufs ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
bytesperline = soc_mbus_bytes_per_line ( icd - > user_width ,
icd - > current_fmt - > host_fmt ) ;
if ( bytesperline < 0 ) {
spin_unlock_irqrestore ( & pcdev - > lock , flags ) ;
return bytesperline ;
}
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
/*
* I didn ' t manage to properly enable / disable the prp
* on a per frame basis during running transfers ,
* thus we allocate a buffer here and use it to
* discard frames when no buffer is available .
* Feel free to work on this ; )
*/
pcdev - > discard_size = icd - > user_height * bytesperline ;
pcdev - > discard_buffer = dma_alloc_coherent ( ici - > v4l2_dev . dev ,
pcdev - > discard_size ,
& pcdev - > discard_buffer_dma , GFP_ATOMIC ) ;
if ( ! pcdev - > discard_buffer ) {
spin_unlock_irqrestore ( & pcdev - > lock , flags ) ;
return - ENOMEM ;
}
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
pcdev - > buf_discard [ 0 ] . discard = true ;
list_add_tail ( & pcdev - > buf_discard [ 0 ] . queue ,
& pcdev - > discard ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
pcdev - > buf_discard [ 1 ] . discard = true ;
list_add_tail ( & pcdev - > buf_discard [ 1 ] . queue ,
& pcdev - > discard ) ;
2012-02-28 19:26:43 +04:00
2012-10-30 18:28:59 +04:00
mx2_prp_resize_commit ( pcdev ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
mx27_camera_emma_buf_init ( icd , bytesperline ) ;
if ( prp - > cfg . channel = = 1 ) {
writel ( PRP_CNTL_CH1EN |
PRP_CNTL_CSIEN |
prp - > cfg . in_fmt |
prp - > cfg . out_fmt |
PRP_CNTL_CH1_LEN |
PRP_CNTL_CH1BYP |
PRP_CNTL_CH1_TSKIP ( 0 ) |
PRP_CNTL_IN_TSKIP ( 0 ) ,
pcdev - > base_emma + PRP_CNTL ) ;
} else {
writel ( PRP_CNTL_CH2EN |
PRP_CNTL_CSIEN |
prp - > cfg . in_fmt |
prp - > cfg . out_fmt |
PRP_CNTL_CH2_LEN |
PRP_CNTL_CH2_TSKIP ( 0 ) |
PRP_CNTL_IN_TSKIP ( 0 ) ,
pcdev - > base_emma + PRP_CNTL ) ;
2012-01-30 16:14:11 +04:00
}
2012-10-30 18:28:59 +04:00
spin_unlock_irqrestore ( & pcdev - > lock , flags ) ;
2012-01-30 16:14:11 +04:00
return 0 ;
}
2014-04-17 09:47:21 +04:00
static void mx2_stop_streaming ( struct vb2_queue * q )
2012-01-30 16:14:11 +04:00
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( q ) ;
struct soc_camera_host * ici =
to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
struct mx2_fmt_cfg * prp = pcdev - > emma_prp ;
unsigned long flags ;
2012-02-07 14:14:42 +04:00
void * b ;
2012-01-30 16:14:11 +04:00
u32 cntl ;
2012-10-30 18:28:59 +04:00
spin_lock_irqsave ( & pcdev - > lock , flags ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
cntl = readl ( pcdev - > base_emma + PRP_CNTL ) ;
if ( prp - > cfg . channel = = 1 ) {
writel ( cntl & ~ PRP_CNTL_CH1EN ,
pcdev - > base_emma + PRP_CNTL ) ;
} else {
writel ( cntl & ~ PRP_CNTL_CH2EN ,
pcdev - > base_emma + PRP_CNTL ) ;
}
INIT_LIST_HEAD ( & pcdev - > capture ) ;
INIT_LIST_HEAD ( & pcdev - > active_bufs ) ;
INIT_LIST_HEAD ( & pcdev - > discard ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
b = pcdev - > discard_buffer ;
pcdev - > discard_buffer = NULL ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
spin_unlock_irqrestore ( & pcdev - > lock , flags ) ;
2012-02-07 14:14:42 +04:00
2012-10-30 18:28:59 +04:00
dma_free_coherent ( ici - > v4l2_dev . dev ,
pcdev - > discard_size , b , pcdev - > discard_buffer_dma ) ;
2012-01-30 16:14:11 +04:00
}
2012-01-30 16:14:10 +04:00
static struct vb2_ops mx2_videobuf_ops = {
2012-01-30 16:14:11 +04:00
. queue_setup = mx2_videobuf_setup ,
. buf_prepare = mx2_videobuf_prepare ,
. buf_queue = mx2_videobuf_queue ,
. start_streaming = mx2_start_streaming ,
. stop_streaming = mx2_stop_streaming ,
2010-07-04 08:55:10 +04:00
} ;
2012-01-30 16:14:10 +04:00
static int mx2_camera_init_videobuf ( struct vb2_queue * q ,
2010-07-04 08:55:10 +04:00
struct soc_camera_device * icd )
{
2012-01-30 16:14:10 +04:00
q - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
q - > io_modes = VB2_MMAP | VB2_USERPTR ;
q - > drv_priv = icd ;
q - > ops = & mx2_videobuf_ops ;
q - > mem_ops = & vb2_dma_contig_memops ;
q - > buf_struct_size = sizeof ( struct mx2_buffer ) ;
2014-02-26 02:12:19 +04:00
q - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
2012-01-30 16:14:10 +04:00
return vb2_queue_init ( q ) ;
2010-07-04 08:55:10 +04:00
}
2011-07-27 19:38:11 +04:00
# define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
V4L2_MBUS_VSYNC_ACTIVE_LOW | \
V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
V4L2_MBUS_PCLK_SAMPLE_RISING | \
V4L2_MBUS_PCLK_SAMPLE_FALLING | \
V4L2_MBUS_DATA_ACTIVE_HIGH | \
V4L2_MBUS_DATA_ACTIVE_LOW )
2010-07-04 08:55:10 +04:00
static int mx27_camera_emma_prp_reset ( struct mx2_camera_dev * pcdev )
{
int count = 0 ;
2014-08-26 18:20:42 +04:00
readl ( pcdev - > base_emma + PRP_CNTL ) ;
2010-07-04 08:55:10 +04:00
writel ( PRP_CNTL_SWRST , pcdev - > base_emma + PRP_CNTL ) ;
while ( count + + < 100 ) {
if ( ! ( readl ( pcdev - > base_emma + PRP_CNTL ) & PRP_CNTL_SWRST ) )
return 0 ;
barrier ( ) ;
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
2011-09-22 00:52:51 +04:00
static int mx2_camera_set_bus_param ( struct soc_camera_device * icd )
2010-07-04 08:55:10 +04:00
{
2011-07-27 19:38:11 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2010-07-04 08:55:10 +04:00
struct mx2_camera_dev * pcdev = ici - > priv ;
2011-07-27 19:38:11 +04:00
struct v4l2_mbus_config cfg = { . type = V4L2_MBUS_PARALLEL , } ;
unsigned long common_flags ;
int ret ;
2010-07-04 08:55:10 +04:00
int bytesperline ;
u32 csicr1 = pcdev - > csicr1 ;
2011-07-27 19:38:11 +04:00
ret = v4l2_subdev_call ( sd , video , g_mbus_config , & cfg ) ;
if ( ! ret ) {
common_flags = soc_mbus_config_compatible ( & cfg , MX2_BUS_FLAGS ) ;
if ( ! common_flags ) {
dev_warn ( icd - > parent ,
" Flags incompatible: camera 0x%x, host 0x%x \n " ,
cfg . flags , MX2_BUS_FLAGS ) ;
return - EINVAL ;
}
} else if ( ret ! = - ENOIOCTLCMD ) {
return ret ;
} else {
common_flags = MX2_BUS_FLAGS ;
}
2010-07-04 08:55:10 +04:00
2011-07-27 19:38:11 +04:00
if ( ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ) ) {
2010-07-04 08:55:10 +04:00
if ( pcdev - > platform_flags & MX2_CAMERA_HSYNC_HIGH )
2011-07-27 19:38:11 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_LOW ;
2010-07-04 08:55:10 +04:00
else
2011-07-27 19:38:11 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_HIGH ;
2010-07-04 08:55:10 +04:00
}
2011-07-27 19:38:11 +04:00
if ( ( common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING ) & &
( common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING ) ) {
2010-07-04 08:55:10 +04:00
if ( pcdev - > platform_flags & MX2_CAMERA_PCLK_SAMPLE_RISING )
2011-07-27 19:38:11 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_FALLING ;
2010-07-04 08:55:10 +04:00
else
2011-07-27 19:38:11 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_RISING ;
2010-07-04 08:55:10 +04:00
}
2011-07-27 19:38:11 +04:00
cfg . flags = common_flags ;
ret = v4l2_subdev_call ( sd , video , s_mbus_config , & cfg ) ;
if ( ret < 0 & & ret ! = - ENOIOCTLCMD ) {
dev_dbg ( icd - > parent , " camera s_mbus_config(0x%lx) returned %d \n " ,
common_flags , ret ) ;
2010-07-04 08:55:10 +04:00
return ret ;
2011-07-27 19:38:11 +04:00
}
2010-07-04 08:55:10 +04:00
2012-07-11 19:34:54 +04:00
csicr1 = ( csicr1 & ~ CSICR1_FMT_MASK ) | pcdev - > emma_prp - > cfg . csicr1 ;
2011-07-27 19:38:11 +04:00
if ( common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING )
2010-08-03 13:37:55 +04:00
csicr1 | = CSICR1_REDGE ;
2011-07-27 19:38:11 +04:00
if ( common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH )
2010-07-04 08:55:10 +04:00
csicr1 | = CSICR1_SOF_POL ;
2011-07-27 19:38:11 +04:00
if ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH )
2010-07-04 08:55:10 +04:00
csicr1 | = CSICR1_HSYNC_POL ;
if ( pcdev - > platform_flags & MX2_CAMERA_EXT_VSYNC )
csicr1 | = CSICR1_EXT_VSYNC ;
if ( pcdev - > platform_flags & MX2_CAMERA_CCIR )
csicr1 | = CSICR1_CCIR_EN ;
if ( pcdev - > platform_flags & MX2_CAMERA_CCIR_INTERLACE )
csicr1 | = CSICR1_CCIR_MODE ;
if ( pcdev - > platform_flags & MX2_CAMERA_GATED_CLOCK )
csicr1 | = CSICR1_GCLK_MODE ;
if ( pcdev - > platform_flags & MX2_CAMERA_INV_DATA )
csicr1 | = CSICR1_INV_DATA ;
pcdev - > csicr1 = csicr1 ;
bytesperline = soc_mbus_bytes_per_line ( icd - > user_width ,
icd - > current_fmt - > host_fmt ) ;
if ( bytesperline < 0 )
return bytesperline ;
2012-10-30 18:28:59 +04:00
ret = mx27_camera_emma_prp_reset ( pcdev ) ;
if ( ret )
return ret ;
2010-07-04 08:55:10 +04:00
writel ( pcdev - > csicr1 , pcdev - > base_csi + CSICR1 ) ;
return 0 ;
}
static int mx2_camera_set_crop ( struct soc_camera_device * icd ,
2012-09-28 14:46:52 +04:00
const struct v4l2_crop * a )
2010-07-04 08:55:10 +04:00
{
2012-09-28 14:46:52 +04:00
struct v4l2_crop a_writable = * a ;
struct v4l2_rect * rect = & a_writable . c ;
2010-07-04 08:55:10 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
struct v4l2_mbus_framefmt mf ;
int ret ;
soc_camera_limit_side ( & rect - > left , & rect - > width , 0 , 2 , 4096 ) ;
soc_camera_limit_side ( & rect - > top , & rect - > height , 0 , 2 , 4096 ) ;
ret = v4l2_subdev_call ( sd , video , s_crop , a ) ;
if ( ret < 0 )
return ret ;
/* The capture device might have changed its output */
ret = v4l2_subdev_call ( sd , video , g_mbus_fmt , & mf ) ;
if ( ret < 0 )
return ret ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Sensor cropped %dx%d \n " ,
2010-07-04 08:55:10 +04:00
mf . width , mf . height ) ;
icd - > user_width = mf . width ;
icd - > user_height = mf . height ;
return ret ;
}
2011-12-14 20:30:14 +04:00
static int mx2_camera_get_formats ( struct soc_camera_device * icd ,
unsigned int idx ,
struct soc_camera_format_xlate * xlate )
{
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
const struct soc_mbus_pixelfmt * fmt ;
struct device * dev = icd - > parent ;
2014-11-10 20:28:31 +03:00
u32 code ;
2011-12-14 20:30:14 +04:00
int ret , formats = 0 ;
ret = v4l2_subdev_call ( sd , video , enum_mbus_fmt , idx , & code ) ;
if ( ret < 0 )
/* no more formats */
return 0 ;
fmt = soc_mbus_get_fmtdesc ( code ) ;
if ( ! fmt ) {
dev_err ( dev , " Invalid format code #%u: %d \n " , idx , code ) ;
return 0 ;
}
2014-11-10 20:28:31 +03:00
if ( code = = MEDIA_BUS_FMT_YUYV8_2X8 | |
code = = MEDIA_BUS_FMT_UYVY8_2X8 ) {
2011-12-14 20:30:14 +04:00
formats + + ;
if ( xlate ) {
/*
* CH2 can output YUV420 which is a standard format in
* soc_mediabus . c
*/
xlate - > host_fmt =
2014-11-10 20:28:31 +03:00
soc_mbus_get_fmtdesc ( MEDIA_BUS_FMT_YUYV8_1_5X8 ) ;
2011-12-14 20:30:14 +04:00
xlate - > code = code ;
dev_dbg ( dev , " Providing host format %s for sensor code %d \n " ,
xlate - > host_fmt - > name , code ) ;
xlate + + ;
}
}
2014-11-10 20:28:31 +03:00
if ( code = = MEDIA_BUS_FMT_UYVY8_2X8 ) {
2012-07-12 12:56:13 +04:00
formats + + ;
if ( xlate ) {
xlate - > host_fmt =
2014-11-10 20:28:31 +03:00
soc_mbus_get_fmtdesc ( MEDIA_BUS_FMT_YUYV8_2X8 ) ;
2012-07-12 12:56:13 +04:00
xlate - > code = code ;
dev_dbg ( dev , " Providing host format %s for sensor code %d \n " ,
xlate - > host_fmt - > name , code ) ;
xlate + + ;
}
}
2011-12-14 20:30:14 +04:00
/* Generic pass-trough */
formats + + ;
if ( xlate ) {
xlate - > host_fmt = fmt ;
xlate - > code = code ;
xlate + + ;
}
return formats ;
}
2012-02-28 19:26:43 +04:00
static int mx2_emmaprp_resize ( struct mx2_camera_dev * pcdev ,
struct v4l2_mbus_framefmt * mf_in ,
struct v4l2_pix_format * pix_out , bool apply )
{
2014-09-18 16:23:36 +04:00
unsigned int num , den ;
2012-02-28 19:26:43 +04:00
unsigned long m ;
int i , dir ;
for ( dir = RESIZE_DIR_H ; dir < = RESIZE_DIR_V ; dir + + ) {
struct emma_prp_resize tmprsz ;
unsigned char * s = tmprsz . s ;
int len = 0 ;
int in , out ;
if ( dir = = RESIZE_DIR_H ) {
in = mf_in - > width ;
out = pix_out - > width ;
} else {
in = mf_in - > height ;
out = pix_out - > height ;
}
if ( in < out )
return - EINVAL ;
else if ( in = = out )
continue ;
/* Calculate ratio */
m = gcd ( in , out ) ;
num = in / m ;
den = out / m ;
if ( num > RESIZE_NUM_MAX )
return - EINVAL ;
if ( ( num > = 2 * den ) & & ( den = = 1 ) & &
( num < 9 ) & & ( ! ( num & 0x01 ) ) ) {
int sum = 0 ;
int j ;
/* Average scaling for >= 2:1 ratios */
/* Support can be added for num >=9 and odd values */
tmprsz . algo = RESIZE_ALGO_AVERAGING ;
len = num ;
for ( i = 0 ; i < ( len / 2 ) ; i + + )
s [ i ] = 8 ;
do {
for ( i = 0 ; i < ( len / 2 ) ; i + + ) {
s [ i ] = s [ i ] > > 1 ;
sum = 0 ;
for ( j = 0 ; j < ( len / 2 ) ; j + + )
sum + = s [ j ] ;
if ( sum = = 4 )
break ;
}
} while ( sum ! = 4 ) ;
for ( i = ( len / 2 ) ; i < len ; i + + )
s [ i ] = s [ len - i - 1 ] ;
s [ len - 1 ] | = SZ_COEF ;
} else {
/* bilinear scaling for < 2:1 ratios */
int v ; /* overflow counter */
int coeff , nxt ; /* table output */
int in_pos_inc = 2 * den ;
int out_pos = num ;
int out_pos_inc = 2 * num ;
int init_carry = num - den ;
int carry = init_carry ;
tmprsz . algo = RESIZE_ALGO_BILINEAR ;
v = den + in_pos_inc ;
do {
coeff = v - out_pos ;
out_pos + = out_pos_inc ;
carry + = out_pos_inc ;
for ( nxt = 0 ; v < out_pos ; nxt + + ) {
v + = in_pos_inc ;
carry - = in_pos_inc ;
}
if ( len > RESIZE_NUM_MAX )
return - EINVAL ;
coeff = ( ( coeff < < BC_COEF ) +
( in_pos_inc > > 1 ) ) / in_pos_inc ;
if ( coeff > = ( SZ_COEF - 1 ) )
coeff - - ;
coeff | = SZ_COEF ;
s [ len ] = ( unsigned char ) coeff ;
len + + ;
for ( i = 1 ; i < nxt ; i + + ) {
if ( len > = RESIZE_NUM_MAX )
return - EINVAL ;
s [ len ] = 0 ;
len + + ;
}
} while ( carry ! = init_carry ) ;
}
tmprsz . len = len ;
if ( dir = = RESIZE_DIR_H )
mf_in - > width = pix_out - > width ;
else
mf_in - > height = pix_out - > height ;
if ( apply )
memcpy ( & pcdev - > resizing [ dir ] , & tmprsz , sizeof ( tmprsz ) ) ;
}
return 0 ;
}
2010-07-04 08:55:10 +04:00
static int mx2_camera_set_fmt ( struct soc_camera_device * icd ,
struct v4l2_format * f )
{
2011-12-14 20:30:14 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
2010-07-04 08:55:10 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
const struct soc_camera_format_xlate * xlate ;
struct v4l2_pix_format * pix = & f - > fmt . pix ;
struct v4l2_mbus_framefmt mf ;
int ret ;
2012-02-28 19:26:43 +04:00
dev_dbg ( icd - > parent , " %s: requested params: width = %d, height = %d \n " ,
__func__ , pix - > width , pix - > height ) ;
2010-07-04 08:55:10 +04:00
xlate = soc_camera_xlate_by_fourcc ( icd , pix - > pixelformat ) ;
if ( ! xlate ) {
2011-07-16 03:03:38 +04:00
dev_warn ( icd - > parent , " Format %x not found \n " ,
2010-07-04 08:55:10 +04:00
pix - > pixelformat ) ;
return - EINVAL ;
}
mf . width = pix - > width ;
mf . height = pix - > height ;
mf . field = pix - > field ;
mf . colorspace = pix - > colorspace ;
mf . code = xlate - > code ;
ret = v4l2_subdev_call ( sd , video , s_mbus_fmt , & mf ) ;
if ( ret < 0 & & ret ! = - ENOIOCTLCMD )
return ret ;
2012-02-28 19:26:43 +04:00
/* Store width and height returned by the sensor for resizing */
pcdev - > s_width = mf . width ;
pcdev - > s_height = mf . height ;
dev_dbg ( icd - > parent , " %s: sensor params: width = %d, height = %d \n " ,
__func__ , pcdev - > s_width , pcdev - > s_height ) ;
pcdev - > emma_prp = mx27_emma_prp_get_format ( xlate - > code ,
xlate - > host_fmt - > fourcc ) ;
memset ( pcdev - > resizing , 0 , sizeof ( pcdev - > resizing ) ) ;
if ( ( mf . width ! = pix - > width | | mf . height ! = pix - > height ) & &
pcdev - > emma_prp - > cfg . in_fmt = = PRP_CNTL_DATA_IN_YUV422 ) {
if ( mx2_emmaprp_resize ( pcdev , & mf , pix , true ) < 0 )
dev_dbg ( icd - > parent , " %s: can't resize \n " , __func__ ) ;
}
2010-07-04 08:55:10 +04:00
if ( mf . code ! = xlate - > code )
return - EINVAL ;
pix - > width = mf . width ;
pix - > height = mf . height ;
pix - > field = mf . field ;
pix - > colorspace = mf . colorspace ;
icd - > current_fmt = xlate ;
2012-02-28 19:26:43 +04:00
dev_dbg ( icd - > parent , " %s: returned params: width = %d, height = %d \n " ,
__func__ , pix - > width , pix - > height ) ;
2011-12-14 20:30:14 +04:00
2010-07-04 08:55:10 +04:00
return 0 ;
}
static int mx2_camera_try_fmt ( struct soc_camera_device * icd ,
struct v4l2_format * f )
{
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
const struct soc_camera_format_xlate * xlate ;
struct v4l2_pix_format * pix = & f - > fmt . pix ;
struct v4l2_mbus_framefmt mf ;
__u32 pixfmt = pix - > pixelformat ;
2012-02-28 19:26:43 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
struct mx2_camera_dev * pcdev = ici - > priv ;
2012-08-20 13:49:24 +04:00
struct mx2_fmt_cfg * emma_prp ;
2010-07-04 08:55:10 +04:00
int ret ;
2012-02-28 19:26:43 +04:00
dev_dbg ( icd - > parent , " %s: requested params: width = %d, height = %d \n " ,
__func__ , pix - > width , pix - > height ) ;
2010-07-04 08:55:10 +04:00
xlate = soc_camera_xlate_by_fourcc ( icd , pixfmt ) ;
if ( pixfmt & & ! xlate ) {
2011-07-16 03:03:38 +04:00
dev_warn ( icd - > parent , " Format %x not found \n " , pixfmt ) ;
2010-07-04 08:55:10 +04:00
return - EINVAL ;
}
2012-10-30 18:28:59 +04:00
/*
* limit to MX27 hardware capabilities : width must be a multiple of 8 as
* requested by the CSI . ( Table 39 - 2 in the i . MX27 Reference Manual ) .
*/
pix - > width & = ~ 0x7 ;
2010-07-04 08:55:10 +04:00
/* limit to sensor capabilities */
mf . width = pix - > width ;
mf . height = pix - > height ;
mf . field = pix - > field ;
mf . colorspace = pix - > colorspace ;
mf . code = xlate - > code ;
ret = v4l2_subdev_call ( sd , video , try_mbus_fmt , & mf ) ;
if ( ret < 0 )
return ret ;
2012-02-28 19:26:43 +04:00
dev_dbg ( icd - > parent , " %s: sensor params: width = %d, height = %d \n " ,
__func__ , pcdev - > s_width , pcdev - > s_height ) ;
/* If the sensor does not support image size try PrP resizing */
2012-08-20 13:49:24 +04:00
emma_prp = mx27_emma_prp_get_format ( xlate - > code ,
2012-10-30 18:28:59 +04:00
xlate - > host_fmt - > fourcc ) ;
2012-02-28 19:26:43 +04:00
if ( ( mf . width ! = pix - > width | | mf . height ! = pix - > height ) & &
2012-08-20 13:49:24 +04:00
emma_prp - > cfg . in_fmt = = PRP_CNTL_DATA_IN_YUV422 ) {
2012-02-28 19:26:43 +04:00
if ( mx2_emmaprp_resize ( pcdev , & mf , pix , false ) < 0 )
dev_dbg ( icd - > parent , " %s: can't resize \n " , __func__ ) ;
}
2010-07-04 08:55:10 +04:00
if ( mf . field = = V4L2_FIELD_ANY )
mf . field = V4L2_FIELD_NONE ;
2011-12-14 20:30:14 +04:00
/*
* Driver supports interlaced images provided they have
* both fields so that they can be processed as if they
* were progressive .
*/
if ( mf . field ! = V4L2_FIELD_NONE & & ! V4L2_FIELD_HAS_BOTH ( mf . field ) ) {
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " Field type %d unsupported. \n " ,
2010-07-04 08:55:10 +04:00
mf . field ) ;
return - EINVAL ;
}
pix - > width = mf . width ;
pix - > height = mf . height ;
pix - > field = mf . field ;
pix - > colorspace = mf . colorspace ;
2012-02-28 19:26:43 +04:00
dev_dbg ( icd - > parent , " %s: returned params: width = %d, height = %d \n " ,
__func__ , pix - > width , pix - > height ) ;
2010-07-04 08:55:10 +04:00
return 0 ;
}
static int mx2_camera_querycap ( struct soc_camera_host * ici ,
struct v4l2_capability * cap )
{
/* cap->name is set by the friendly caller:-> */
strlcpy ( cap - > card , MX2_CAM_DRIVER_DESCRIPTION , sizeof ( cap - > card ) ) ;
2015-01-18 22:30:11 +03:00
cap - > device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING ;
cap - > capabilities = cap - > device_caps | V4L2_CAP_DEVICE_CAPS ;
2010-07-04 08:55:10 +04:00
return 0 ;
}
static unsigned int mx2_camera_poll ( struct file * file , poll_table * pt )
{
2010-11-08 23:52:45 +03:00
struct soc_camera_device * icd = file - > private_data ;
2010-07-04 08:55:10 +04:00
2012-01-30 16:14:10 +04:00
return vb2_poll ( & icd - > vb2_vidq , file , pt ) ;
2010-07-04 08:55:10 +04:00
}
static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
. owner = THIS_MODULE ,
. add = mx2_camera_add_device ,
. remove = mx2_camera_remove_device ,
2013-04-04 18:49:13 +04:00
. clock_start = mx2_camera_clock_start ,
. clock_stop = mx2_camera_clock_stop ,
2010-07-04 08:55:10 +04:00
. set_fmt = mx2_camera_set_fmt ,
. set_crop = mx2_camera_set_crop ,
2011-12-14 20:30:14 +04:00
. get_formats = mx2_camera_get_formats ,
2010-07-04 08:55:10 +04:00
. try_fmt = mx2_camera_try_fmt ,
2012-01-30 16:14:10 +04:00
. init_videobuf2 = mx2_camera_init_videobuf ,
2010-07-04 08:55:10 +04:00
. poll = mx2_camera_poll ,
. querycap = mx2_camera_querycap ,
. set_bus_param = mx2_camera_set_bus_param ,
} ;
static void mx27_camera_frame_done_emma ( struct mx2_camera_dev * pcdev ,
2012-02-22 14:34:36 +04:00
int bufnum , bool err )
2010-07-04 08:55:10 +04:00
{
2012-02-13 18:40:01 +04:00
# ifdef DEBUG
2011-12-14 20:30:14 +04:00
struct mx2_fmt_cfg * prp = pcdev - > emma_prp ;
2012-02-13 18:40:01 +04:00
# endif
2012-02-22 14:59:34 +04:00
struct mx2_buf_internal * ibuf ;
2010-07-04 08:55:10 +04:00
struct mx2_buffer * buf ;
2012-01-30 16:14:10 +04:00
struct vb2_buffer * vb ;
2010-07-04 08:55:10 +04:00
unsigned long phys ;
2012-02-22 14:59:34 +04:00
ibuf = list_first_entry ( & pcdev - > active_bufs , struct mx2_buf_internal ,
queue ) ;
2010-07-04 08:55:10 +04:00
2012-02-22 14:59:34 +04:00
BUG_ON ( ibuf - > bufnum ! = bufnum ) ;
2010-07-04 08:55:10 +04:00
2012-02-22 14:59:34 +04:00
if ( ibuf - > discard ) {
2012-02-07 14:14:42 +04:00
/*
* Discard buffer must not be returned to user space .
* Just return it to the discard queue .
*/
list_move_tail ( pcdev - > active_bufs . next , & pcdev - > discard ) ;
} else {
2012-02-22 14:59:34 +04:00
buf = mx2_ibuf_to_buf ( ibuf ) ;
2010-07-04 08:55:10 +04:00
vb = & buf - > vb ;
# ifdef DEBUG
2012-01-30 16:14:10 +04:00
phys = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
2011-12-14 20:30:14 +04:00
if ( prp - > cfg . channel = = 1 ) {
if ( readl ( pcdev - > base_emma + PRP_DEST_RGB1_PTR +
4 * bufnum ) ! = phys ) {
2012-02-13 18:40:01 +04:00
dev_err ( pcdev - > dev , " %lx != %x \n " , phys ,
readl ( pcdev - > base_emma +
PRP_DEST_RGB1_PTR + 4 * bufnum ) ) ;
2011-12-14 20:30:14 +04:00
}
} else {
if ( readl ( pcdev - > base_emma + PRP_DEST_Y_PTR -
0x14 * bufnum ) ! = phys ) {
2012-02-13 18:40:01 +04:00
dev_err ( pcdev - > dev , " %lx != %x \n " , phys ,
readl ( pcdev - > base_emma +
PRP_DEST_Y_PTR - 0x14 * bufnum ) ) ;
2011-12-14 20:30:14 +04:00
}
2010-07-04 08:55:10 +04:00
}
# endif
2012-01-30 16:14:10 +04:00
dev_dbg ( pcdev - > dev , " %s (vb=0x%p) 0x%p %lu \n " , __func__ , vb ,
vb2_plane_vaddr ( vb , 0 ) ,
vb2_get_plane_payload ( vb , 0 ) ) ;
2010-07-04 08:55:10 +04:00
2012-02-22 14:59:34 +04:00
list_del_init ( & buf - > internal . queue ) ;
2012-09-15 22:14:42 +04:00
v4l2_get_timestamp ( & vb - > v4l2_buf . timestamp ) ;
2012-01-30 16:14:10 +04:00
vb - > v4l2_buf . sequence = pcdev - > frame_count ;
2012-02-22 14:34:36 +04:00
if ( err )
vb2_buffer_done ( vb , VB2_BUF_STATE_ERROR ) ;
else
vb2_buffer_done ( vb , VB2_BUF_STATE_DONE ) ;
2010-07-04 08:55:10 +04:00
}
2012-01-30 16:14:11 +04:00
pcdev - > frame_count + + ;
2010-07-04 08:55:10 +04:00
if ( list_empty ( & pcdev - > capture ) ) {
2012-02-07 14:14:42 +04:00
if ( list_empty ( & pcdev - > discard ) ) {
dev_warn ( pcdev - > dev , " %s: trying to access empty discard list \n " ,
__func__ ) ;
return ;
2011-12-14 20:30:14 +04:00
}
2012-02-07 14:14:42 +04:00
2012-02-22 14:59:34 +04:00
ibuf = list_first_entry ( & pcdev - > discard ,
struct mx2_buf_internal , queue ) ;
ibuf - > bufnum = bufnum ;
2012-02-07 14:14:42 +04:00
list_move_tail ( pcdev - > discard . next , & pcdev - > active_bufs ) ;
mx27_update_emma_buf ( pcdev , pcdev - > discard_buffer_dma , bufnum ) ;
2010-07-04 08:55:10 +04:00
return ;
}
2012-02-22 14:59:34 +04:00
buf = list_first_entry ( & pcdev - > capture , struct mx2_buffer ,
internal . queue ) ;
2010-07-04 08:55:10 +04:00
2012-02-22 14:59:34 +04:00
buf - > internal . bufnum = bufnum ;
2010-07-04 08:55:10 +04:00
list_move_tail ( pcdev - > capture . next , & pcdev - > active_bufs ) ;
vb = & buf - > vb ;
2012-01-30 16:14:10 +04:00
phys = vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
2012-02-07 14:14:42 +04:00
mx27_update_emma_buf ( pcdev , phys , bufnum ) ;
2010-07-04 08:55:10 +04:00
}
static irqreturn_t mx27_camera_emma_irq ( int irq_emma , void * data )
{
struct mx2_camera_dev * pcdev = data ;
unsigned int status = readl ( pcdev - > base_emma + PRP_INTRSTATUS ) ;
2012-02-22 14:59:34 +04:00
struct mx2_buf_internal * ibuf ;
2012-02-07 14:14:42 +04:00
2012-02-13 17:51:52 +04:00
spin_lock ( & pcdev - > lock ) ;
2012-02-07 14:14:42 +04:00
if ( list_empty ( & pcdev - > active_bufs ) ) {
dev_warn ( pcdev - > dev , " %s: called while active list is empty \n " ,
__func__ ) ;
2012-02-13 17:51:53 +04:00
if ( ! status ) {
spin_unlock ( & pcdev - > lock ) ;
return IRQ_NONE ;
}
2012-02-07 14:14:42 +04:00
}
2010-07-04 08:55:10 +04:00
if ( status & ( 1 < < 7 ) ) { /* overflow */
2012-02-22 14:34:36 +04:00
u32 cntl = readl ( pcdev - > base_emma + PRP_CNTL ) ;
2011-12-14 20:30:14 +04:00
writel ( cntl & ~ ( PRP_CNTL_CH1EN | PRP_CNTL_CH2EN ) ,
pcdev - > base_emma + PRP_CNTL ) ;
2010-07-04 08:55:10 +04:00
writel ( cntl , pcdev - > base_emma + PRP_CNTL ) ;
2012-02-22 14:34:36 +04:00
2012-02-22 14:59:34 +04:00
ibuf = list_first_entry ( & pcdev - > active_bufs ,
struct mx2_buf_internal , queue ) ;
2012-02-22 14:34:36 +04:00
mx27_camera_frame_done_emma ( pcdev ,
2012-02-22 14:59:34 +04:00
ibuf - > bufnum , true ) ;
2012-02-22 14:34:36 +04:00
status & = ~ ( 1 < < 7 ) ;
} else if ( ( ( status & ( 3 < < 5 ) ) = = ( 3 < < 5 ) ) | |
2012-02-07 14:14:42 +04:00
( ( status & ( 3 < < 3 ) ) = = ( 3 < < 3 ) ) ) {
2010-07-04 08:55:10 +04:00
/*
* Both buffers have triggered , process the one we ' re expecting
* to first
*/
2012-02-22 14:59:34 +04:00
ibuf = list_first_entry ( & pcdev - > active_bufs ,
struct mx2_buf_internal , queue ) ;
mx27_camera_frame_done_emma ( pcdev , ibuf - > bufnum , false ) ;
status & = ~ ( 1 < < ( 6 - ibuf - > bufnum ) ) ; /* mark processed */
2012-02-22 14:34:36 +04:00
} else if ( ( status & ( 1 < < 6 ) ) | | ( status & ( 1 < < 4 ) ) ) {
mx27_camera_frame_done_emma ( pcdev , 0 , false ) ;
} else if ( ( status & ( 1 < < 5 ) ) | | ( status & ( 1 < < 3 ) ) ) {
mx27_camera_frame_done_emma ( pcdev , 1 , false ) ;
2010-07-04 08:55:10 +04:00
}
2012-02-13 17:51:52 +04:00
spin_unlock ( & pcdev - > lock ) ;
2010-07-04 08:55:10 +04:00
writel ( status , pcdev - > base_emma + PRP_INTRSTATUS ) ;
return IRQ_HANDLED ;
}
2012-12-22 01:17:53 +04:00
static int mx27_camera_emma_init ( struct platform_device * pdev )
2010-07-04 08:55:10 +04:00
{
2012-09-20 09:45:39 +04:00
struct mx2_camera_dev * pcdev = platform_get_drvdata ( pdev ) ;
struct resource * res_emma ;
int irq_emma ;
2010-07-04 08:55:10 +04:00
int err = 0 ;
2012-09-20 09:45:39 +04:00
res_emma = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
irq_emma = platform_get_irq ( pdev , 1 ) ;
if ( ! res_emma | | ! irq_emma ) {
dev_err ( pcdev - > dev , " no EMMA resources \n " ) ;
2012-09-24 13:11:03 +04:00
err = - ENODEV ;
2010-07-04 08:55:10 +04:00
goto out ;
}
2013-01-21 14:09:07 +04:00
pcdev - > base_emma = devm_ioremap_resource ( pcdev - > dev , res_emma ) ;
if ( IS_ERR ( pcdev - > base_emma ) ) {
err = PTR_ERR ( pcdev - > base_emma ) ;
2012-09-20 09:45:39 +04:00
goto out ;
2010-07-04 08:55:10 +04:00
}
2012-09-20 09:45:39 +04:00
err = devm_request_irq ( pcdev - > dev , irq_emma , mx27_camera_emma_irq , 0 ,
MX2_CAM_DRV_NAME , pcdev ) ;
2010-07-04 08:55:10 +04:00
if ( err ) {
2013-04-03 09:00:36 +04:00
dev_err ( pcdev - > dev , " Camera EMMA interrupt register failed \n " ) ;
2012-09-20 09:45:39 +04:00
goto out ;
2010-07-04 08:55:10 +04:00
}
2012-09-20 09:45:39 +04:00
pcdev - > clk_emma_ipg = devm_clk_get ( pcdev - > dev , " emma-ipg " ) ;
2012-08-01 13:16:44 +04:00
if ( IS_ERR ( pcdev - > clk_emma_ipg ) ) {
err = PTR_ERR ( pcdev - > clk_emma_ipg ) ;
2012-09-20 09:45:39 +04:00
goto out ;
2010-07-04 08:55:10 +04:00
}
2012-08-01 13:16:44 +04:00
clk_prepare_enable ( pcdev - > clk_emma_ipg ) ;
2012-09-20 09:45:39 +04:00
pcdev - > clk_emma_ahb = devm_clk_get ( pcdev - > dev , " emma-ahb " ) ;
2012-08-01 13:16:44 +04:00
if ( IS_ERR ( pcdev - > clk_emma_ahb ) ) {
err = PTR_ERR ( pcdev - > clk_emma_ahb ) ;
2012-09-20 09:45:39 +04:00
goto exit_clk_emma_ipg ;
2012-08-01 13:16:44 +04:00
}
clk_prepare_enable ( pcdev - > clk_emma_ahb ) ;
2010-07-04 08:55:10 +04:00
err = mx27_camera_emma_prp_reset ( pcdev ) ;
if ( err )
2012-09-20 09:45:39 +04:00
goto exit_clk_emma_ahb ;
2010-07-04 08:55:10 +04:00
return err ;
2012-09-20 09:45:39 +04:00
exit_clk_emma_ahb :
2012-08-01 13:16:44 +04:00
clk_disable_unprepare ( pcdev - > clk_emma_ahb ) ;
2012-09-20 09:45:39 +04:00
exit_clk_emma_ipg :
2012-08-01 13:16:44 +04:00
clk_disable_unprepare ( pcdev - > clk_emma_ipg ) ;
2010-07-04 08:55:10 +04:00
out :
return err ;
}
2012-12-22 01:17:53 +04:00
static int mx2_camera_probe ( struct platform_device * pdev )
2010-07-04 08:55:10 +04:00
{
struct mx2_camera_dev * pcdev ;
2012-09-20 09:45:39 +04:00
struct resource * res_csi ;
int irq_csi ;
2010-07-04 08:55:10 +04:00
int err = 0 ;
dev_dbg ( & pdev - > dev , " initialising \n " ) ;
res_csi = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq_csi = platform_get_irq ( pdev , 0 ) ;
if ( res_csi = = NULL | | irq_csi < 0 ) {
dev_err ( & pdev - > dev , " Missing platform resources data \n " ) ;
err = - ENODEV ;
goto exit ;
}
2012-09-20 09:45:39 +04:00
pcdev = devm_kzalloc ( & pdev - > dev , sizeof ( * pcdev ) , GFP_KERNEL ) ;
2010-07-04 08:55:10 +04:00
if ( ! pcdev ) {
dev_err ( & pdev - > dev , " Could not allocate pcdev \n " ) ;
err = - ENOMEM ;
goto exit ;
}
2012-10-30 16:03:26 +04:00
pcdev - > clk_csi_ahb = devm_clk_get ( & pdev - > dev , " ahb " ) ;
if ( IS_ERR ( pcdev - > clk_csi_ahb ) ) {
dev_err ( & pdev - > dev , " Could not get csi ahb clock \n " ) ;
err = PTR_ERR ( pcdev - > clk_csi_ahb ) ;
goto exit ;
}
pcdev - > clk_csi_per = devm_clk_get ( & pdev - > dev , " per " ) ;
if ( IS_ERR ( pcdev - > clk_csi_per ) ) {
dev_err ( & pdev - > dev , " Could not get csi per clock \n " ) ;
err = PTR_ERR ( pcdev - > clk_csi_per ) ;
2012-09-20 09:45:39 +04:00
goto exit ;
2010-07-04 08:55:10 +04:00
}
pcdev - > pdata = pdev - > dev . platform_data ;
if ( pcdev - > pdata ) {
long rate ;
pcdev - > platform_flags = pcdev - > pdata - > flags ;
2012-10-30 16:03:26 +04:00
rate = clk_round_rate ( pcdev - > clk_csi_per ,
pcdev - > pdata - > clk * 2 ) ;
2010-07-04 08:55:10 +04:00
if ( rate < = 0 ) {
err = - ENODEV ;
2012-09-20 09:45:39 +04:00
goto exit ;
2010-07-04 08:55:10 +04:00
}
2012-10-30 16:03:26 +04:00
err = clk_set_rate ( pcdev - > clk_csi_per , rate ) ;
2010-07-04 08:55:10 +04:00
if ( err < 0 )
2012-09-20 09:45:39 +04:00
goto exit ;
2010-07-04 08:55:10 +04:00
}
INIT_LIST_HEAD ( & pcdev - > capture ) ;
INIT_LIST_HEAD ( & pcdev - > active_bufs ) ;
2012-02-07 14:14:42 +04:00
INIT_LIST_HEAD ( & pcdev - > discard ) ;
2010-07-04 08:55:10 +04:00
spin_lock_init ( & pcdev - > lock ) ;
2013-01-21 14:09:07 +04:00
pcdev - > base_csi = devm_ioremap_resource ( & pdev - > dev , res_csi ) ;
if ( IS_ERR ( pcdev - > base_csi ) ) {
err = PTR_ERR ( pcdev - > base_csi ) ;
2012-09-20 09:45:39 +04:00
goto exit ;
2010-07-04 08:55:10 +04:00
}
pcdev - > dev = & pdev - > dev ;
2012-09-20 09:45:39 +04:00
platform_set_drvdata ( pdev , pcdev ) ;
2010-07-04 08:55:10 +04:00
2012-10-30 18:28:59 +04:00
err = mx27_camera_emma_init ( pdev ) ;
if ( err )
goto exit ;
2010-07-04 08:55:10 +04:00
2012-09-20 09:45:39 +04:00
/*
* We ' re done with drvdata here . Clear the pointer so that
* v4l2 core can start using drvdata on its purpose .
*/
platform_set_drvdata ( pdev , NULL ) ;
2010-07-04 08:55:10 +04:00
pcdev - > soc_host . drv_name = MX2_CAM_DRV_NAME ,
pcdev - > soc_host . ops = & mx2_soc_camera_host_ops ,
pcdev - > soc_host . priv = pcdev ;
pcdev - > soc_host . v4l2_dev . dev = & pdev - > dev ;
pcdev - > soc_host . nr = pdev - > id ;
2012-01-30 16:14:10 +04:00
pcdev - > alloc_ctx = vb2_dma_contig_init_ctx ( & pdev - > dev ) ;
if ( IS_ERR ( pcdev - > alloc_ctx ) ) {
err = PTR_ERR ( pcdev - > alloc_ctx ) ;
goto eallocctx ;
}
2010-07-04 08:55:10 +04:00
err = soc_camera_host_register ( & pcdev - > soc_host ) ;
if ( err )
goto exit_free_emma ;
2010-08-27 16:39:05 +04:00
dev_info ( & pdev - > dev , " MX2 Camera (CSI) driver probed, clock frequency: %ld \n " ,
2012-10-30 16:03:26 +04:00
clk_get_rate ( pcdev - > clk_csi_per ) ) ;
2010-08-27 16:39:05 +04:00
2010-07-04 08:55:10 +04:00
return 0 ;
exit_free_emma :
2012-01-30 16:14:10 +04:00
vb2_dma_contig_cleanup_ctx ( pcdev - > alloc_ctx ) ;
eallocctx :
2012-10-30 18:28:59 +04:00
clk_disable_unprepare ( pcdev - > clk_emma_ipg ) ;
clk_disable_unprepare ( pcdev - > clk_emma_ahb ) ;
2010-07-04 08:55:10 +04:00
exit :
return err ;
}
2012-12-22 01:17:53 +04:00
static int mx2_camera_remove ( struct platform_device * pdev )
2010-07-04 08:55:10 +04:00
{
struct soc_camera_host * soc_host = to_soc_camera_host ( & pdev - > dev ) ;
struct mx2_camera_dev * pcdev = container_of ( soc_host ,
struct mx2_camera_dev , soc_host ) ;
soc_camera_host_unregister ( & pcdev - > soc_host ) ;
2012-01-30 16:14:10 +04:00
vb2_dma_contig_cleanup_ctx ( pcdev - > alloc_ctx ) ;
2012-10-30 18:28:59 +04:00
clk_disable_unprepare ( pcdev - > clk_emma_ipg ) ;
clk_disable_unprepare ( pcdev - > clk_emma_ahb ) ;
2010-07-04 08:55:10 +04:00
dev_info ( & pdev - > dev , " MX2 Camera driver unloaded \n " ) ;
return 0 ;
}
static struct platform_driver mx2_camera_driver = {
2013-04-03 09:00:36 +04:00
. driver = {
2010-07-04 08:55:10 +04:00
. name = MX2_CAM_DRV_NAME ,
} ,
2012-09-16 12:26:20 +04:00
. id_table = mx2_camera_devtype ,
2012-12-22 01:17:53 +04:00
. remove = mx2_camera_remove ,
2010-07-04 08:55:10 +04:00
} ;
2013-03-18 13:43:56 +04:00
module_platform_driver_probe ( mx2_camera_driver , mx2_camera_probe ) ;
2010-07-04 08:55:10 +04:00
2012-10-30 18:28:59 +04:00
MODULE_DESCRIPTION ( " i.MX27 SoC Camera Host driver " ) ;
2010-07-04 08:55:10 +04:00
MODULE_AUTHOR ( " Sascha Hauer <sha@pengutronix.de> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-06-25 18:28:37 +04:00
MODULE_VERSION ( MX2_CAM_VERSION ) ;