2011-06-08 05:40:19 +04:00
/*
* Copyright ( c ) 2011 Atmel Corporation
* Josh Wu , < josh . wu @ atmel . com >
*
* Based on previous work by Lars Haring , < lars . haring @ atmel . com >
* and Sedji Gaouaou
* Based on the bttv driver for Bt848 with respective copyright holders
*
* 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 .
*/
# include <linux/clk.h>
# include <linux/completion.h>
# include <linux/delay.h>
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2015-05-26 12:54:46 +03:00
# include <linux/pm_runtime.h>
2011-06-08 05:40:19 +04:00
# include <linux/slab.h>
# include <media/atmel-isi.h>
# include <media/soc_camera.h>
# include <media/soc_mediabus.h>
2014-07-28 11:25:17 +04:00
# include <media/v4l2-of.h>
2011-06-08 05:40:19 +04:00
# include <media/videobuf2-dma-contig.h>
# define MAX_BUFFER_NUM 32
# define MAX_SUPPORT_WIDTH 2048
# define MAX_SUPPORT_HEIGHT 2048
# define VID_LIMIT_BYTES (16 * 1024 * 1024)
# define MIN_FRAME_RATE 15
# define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
/* Frame buffer descriptor */
struct fbd {
/* Physical address of the frame buffer */
u32 fb_address ;
/* DMA Control Register(only in HISI2) */
u32 dma_ctrl ;
/* Physical address of the next fbd */
u32 next_fbd_address ;
} ;
static void set_dma_ctrl ( struct fbd * fb_desc , u32 ctrl )
{
fb_desc - > dma_ctrl = ctrl ;
}
struct isi_dma_desc {
struct list_head list ;
struct fbd * p_fbd ;
2014-08-22 14:52:54 +04:00
dma_addr_t fbd_phys ;
2011-06-08 05:40:19 +04:00
} ;
/* Frame buffer data */
struct frame_buffer {
struct vb2_buffer vb ;
struct isi_dma_desc * p_dma_desc ;
struct list_head list ;
} ;
struct atmel_isi {
/* Protects the access of variables shared with the ISR */
spinlock_t lock ;
void __iomem * regs ;
int sequence ;
struct vb2_alloc_ctx * alloc_ctx ;
/* Allocate descriptors for dma buffer use */
struct fbd * p_fb_descriptors ;
2014-08-22 14:52:54 +04:00
dma_addr_t fb_descriptors_phys ;
2011-06-08 05:40:19 +04:00
struct list_head dma_desc_head ;
struct isi_dma_desc dma_desc [ MAX_BUFFER_NUM ] ;
struct completion complete ;
2011-12-08 14:18:49 +04:00
/* ISI peripherial clock */
2011-06-08 05:40:19 +04:00
struct clk * pclk ;
unsigned int irq ;
2014-07-25 14:13:39 +04:00
struct isi_platform_data pdata ;
2011-07-27 19:18:37 +04:00
u16 width_flags ; /* max 12 bits */
2011-06-08 05:40:19 +04:00
struct list_head video_buffer_list ;
struct frame_buffer * active ;
struct soc_camera_host soc_host ;
} ;
static void isi_writel ( struct atmel_isi * isi , u32 reg , u32 val )
{
writel ( val , isi - > regs + reg ) ;
}
static u32 isi_readl ( struct atmel_isi * isi , u32 reg )
{
return readl ( isi - > regs + reg ) ;
}
static int configure_geometry ( struct atmel_isi * isi , u32 width ,
2014-11-10 20:28:31 +03:00
u32 height , u32 code )
2011-06-08 05:40:19 +04:00
{
2015-09-11 10:00:14 +03:00
u32 cfg2 ;
2011-06-08 05:40:19 +04:00
2015-09-11 10:00:14 +03:00
/* According to sensor's output format to set cfg2 */
2011-06-08 05:40:19 +04:00
switch ( code ) {
/* YUV, including grey */
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_Y8_1X8 :
2015-09-11 10:00:14 +03:00
cfg2 = ISI_CFG2_GRAYSCALE | ISI_CFG2_COL_SPACE_YCbCr ;
2011-06-08 05:40:19 +04:00
break ;
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_VYUY8_2X8 :
2015-09-11 10:00:14 +03:00
cfg2 = ISI_CFG2_YCC_SWAP_MODE_3 | ISI_CFG2_COL_SPACE_YCbCr ;
2011-06-08 05:40:19 +04:00
break ;
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_UYVY8_2X8 :
2015-09-11 10:00:14 +03:00
cfg2 = ISI_CFG2_YCC_SWAP_MODE_2 | ISI_CFG2_COL_SPACE_YCbCr ;
2011-06-08 05:40:19 +04:00
break ;
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_YVYU8_2X8 :
2015-09-11 10:00:14 +03:00
cfg2 = ISI_CFG2_YCC_SWAP_MODE_1 | ISI_CFG2_COL_SPACE_YCbCr ;
2011-06-08 05:40:19 +04:00
break ;
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_YUYV8_2X8 :
2015-09-11 10:00:14 +03:00
cfg2 = ISI_CFG2_YCC_SWAP_DEFAULT | ISI_CFG2_COL_SPACE_YCbCr ;
2011-06-08 05:40:19 +04:00
break ;
/* RGB, TODO */
default :
return - EINVAL ;
}
isi_writel ( isi , ISI_CTRL , ISI_CTRL_DIS ) ;
/* Set width */
cfg2 | = ( ( width - 1 ) < < ISI_CFG2_IM_HSIZE_OFFSET ) &
ISI_CFG2_IM_HSIZE_MASK ;
/* Set height */
cfg2 | = ( ( height - 1 ) < < ISI_CFG2_IM_VSIZE_OFFSET )
& ISI_CFG2_IM_VSIZE_MASK ;
isi_writel ( isi , ISI_CFG2 , cfg2 ) ;
return 0 ;
}
static irqreturn_t atmel_isi_handle_streaming ( struct atmel_isi * isi )
{
if ( isi - > active ) {
struct vb2_buffer * vb = & isi - > active - > vb ;
struct frame_buffer * buf = isi - > active ;
list_del_init ( & buf - > list ) ;
2012-09-15 22:14:42 +04:00
v4l2_get_timestamp ( & vb - > v4l2_buf . timestamp ) ;
2011-06-08 05:40:19 +04:00
vb - > v4l2_buf . sequence = isi - > sequence + + ;
vb2_buffer_done ( vb , VB2_BUF_STATE_DONE ) ;
}
if ( list_empty ( & isi - > video_buffer_list ) ) {
isi - > active = NULL ;
} else {
/* start next dma frame. */
isi - > active = list_entry ( isi - > video_buffer_list . next ,
struct frame_buffer , list ) ;
isi_writel ( isi , ISI_DMA_C_DSCR ,
2014-08-22 14:52:54 +04:00
( u32 ) isi - > active - > p_dma_desc - > fbd_phys ) ;
2011-06-08 05:40:19 +04:00
isi_writel ( isi , ISI_DMA_C_CTRL ,
ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE ) ;
isi_writel ( isi , ISI_DMA_CHER , ISI_DMA_CHSR_C_CH ) ;
}
return IRQ_HANDLED ;
}
/* ISI interrupt service routine */
static irqreturn_t isi_interrupt ( int irq , void * dev_id )
{
struct atmel_isi * isi = dev_id ;
u32 status , mask , pending ;
irqreturn_t ret = IRQ_NONE ;
spin_lock ( & isi - > lock ) ;
status = isi_readl ( isi , ISI_STATUS ) ;
mask = isi_readl ( isi , ISI_INTMASK ) ;
pending = status & mask ;
if ( pending & ISI_CTRL_SRST ) {
complete ( & isi - > complete ) ;
isi_writel ( isi , ISI_INTDIS , ISI_CTRL_SRST ) ;
ret = IRQ_HANDLED ;
} else if ( pending & ISI_CTRL_DIS ) {
complete ( & isi - > complete ) ;
isi_writel ( isi , ISI_INTDIS , ISI_CTRL_DIS ) ;
ret = IRQ_HANDLED ;
} else {
if ( likely ( pending & ISI_SR_CXFR_DONE ) )
ret = atmel_isi_handle_streaming ( isi ) ;
}
spin_unlock ( & isi - > lock ) ;
return ret ;
}
# define WAIT_ISI_RESET 1
# define WAIT_ISI_DISABLE 0
static int atmel_isi_wait_status ( struct atmel_isi * isi , int wait_reset )
{
unsigned long timeout ;
/*
* The reset or disable will only succeed if we have a
* pixel clock from the camera .
*/
init_completion ( & isi - > complete ) ;
if ( wait_reset ) {
isi_writel ( isi , ISI_INTEN , ISI_CTRL_SRST ) ;
isi_writel ( isi , ISI_CTRL , ISI_CTRL_SRST ) ;
} else {
isi_writel ( isi , ISI_INTEN , ISI_CTRL_DIS ) ;
isi_writel ( isi , ISI_CTRL , ISI_CTRL_DIS ) ;
}
timeout = wait_for_completion_timeout ( & isi - > complete ,
2015-06-17 13:27:08 +03:00
msecs_to_jiffies ( 500 ) ) ;
2011-06-08 05:40:19 +04:00
if ( timeout = = 0 )
return - ETIMEDOUT ;
return 0 ;
}
/* ------------------------------------------------------------------
Videobuf operations
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2011-08-24 17:30:21 +04:00
static int queue_setup ( struct vb2_queue * vq , const struct v4l2_format * fmt ,
unsigned int * nbuffers , unsigned int * nplanes ,
unsigned int sizes [ ] , void * alloc_ctxs [ ] )
2011-06-08 05:40:19 +04:00
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vq ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
unsigned long size ;
2012-03-21 15:03:21 +04:00
size = icd - > sizeimage ;
2011-06-08 05:40:19 +04:00
if ( ! * nbuffers | | * nbuffers > MAX_BUFFER_NUM )
* nbuffers = MAX_BUFFER_NUM ;
if ( size * * nbuffers > VID_LIMIT_BYTES )
* nbuffers = VID_LIMIT_BYTES / size ;
* nplanes = 1 ;
sizes [ 0 ] = size ;
alloc_ctxs [ 0 ] = isi - > alloc_ctx ;
isi - > sequence = 0 ;
isi - > active = NULL ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " %s, count=%d, size=%ld \n " , __func__ ,
2011-06-08 05:40:19 +04:00
* nbuffers , size ) ;
return 0 ;
}
static int buffer_init ( struct vb2_buffer * vb )
{
struct frame_buffer * buf = container_of ( vb , struct frame_buffer , vb ) ;
buf - > p_dma_desc = NULL ;
INIT_LIST_HEAD ( & buf - > list ) ;
return 0 ;
}
static int buffer_prepare ( struct vb2_buffer * vb )
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
struct frame_buffer * buf = container_of ( vb , struct frame_buffer , vb ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
unsigned long size ;
struct isi_dma_desc * desc ;
2012-03-21 15:03:21 +04:00
size = icd - > sizeimage ;
2011-06-08 05:40:19 +04:00
if ( vb2_plane_size ( vb , 0 ) < size ) {
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " %s data will not fit into plane (%lu < %lu) \n " ,
2011-06-08 05:40:19 +04:00
__func__ , vb2_plane_size ( vb , 0 ) , size ) ;
return - EINVAL ;
}
vb2_set_plane_payload ( & buf - > vb , 0 , size ) ;
if ( ! buf - > p_dma_desc ) {
if ( list_empty ( & isi - > dma_desc_head ) ) {
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " Not enough dma descriptors. \n " ) ;
2011-06-08 05:40:19 +04:00
return - EINVAL ;
} else {
/* Get an available descriptor */
desc = list_entry ( isi - > dma_desc_head . next ,
struct isi_dma_desc , list ) ;
/* Delete the descriptor since now it is used */
list_del_init ( & desc - > list ) ;
/* Initialize the dma descriptor */
desc - > p_fbd - > fb_address =
2011-08-29 10:20:56 +04:00
vb2_dma_contig_plane_dma_addr ( vb , 0 ) ;
2011-06-08 05:40:19 +04:00
desc - > p_fbd - > next_fbd_address = 0 ;
set_dma_ctrl ( desc - > p_fbd , ISI_DMA_CTRL_WB ) ;
buf - > p_dma_desc = desc ;
}
}
return 0 ;
}
static void buffer_cleanup ( struct vb2_buffer * vb )
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
struct frame_buffer * buf = container_of ( vb , struct frame_buffer , vb ) ;
/* This descriptor is available now and we add to head list */
if ( buf - > p_dma_desc )
list_add ( & buf - > p_dma_desc - > list , & isi - > dma_desc_head ) ;
}
static void start_dma ( struct atmel_isi * isi , struct frame_buffer * buffer )
{
u32 ctrl , cfg1 ;
cfg1 = isi_readl ( isi , ISI_CFG1 ) ;
/* Enable irq: cxfr for the codec path, pxfr for the preview path */
isi_writel ( isi , ISI_INTEN ,
ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE ) ;
/* Check if already in a frame */
if ( isi_readl ( isi , ISI_STATUS ) & ISI_CTRL_CDC ) {
2013-04-04 15:21:12 +04:00
dev_err ( isi - > soc_host . icd - > parent , " Already in frame handling. \n " ) ;
2011-06-08 05:40:19 +04:00
return ;
}
2014-08-22 14:52:54 +04:00
isi_writel ( isi , ISI_DMA_C_DSCR , ( u32 ) buffer - > p_dma_desc - > fbd_phys ) ;
2011-06-08 05:40:19 +04:00
isi_writel ( isi , ISI_DMA_C_CTRL , ISI_DMA_CTRL_FETCH | ISI_DMA_CTRL_DONE ) ;
isi_writel ( isi , ISI_DMA_CHER , ISI_DMA_CHSR_C_CH ) ;
2013-12-10 16:25:47 +04:00
cfg1 & = ~ ISI_CFG1_FRATE_DIV_MASK ;
2011-06-08 05:40:19 +04:00
/* Enable linked list */
2014-07-25 14:13:39 +04:00
cfg1 | = isi - > pdata . frate | ISI_CFG1_DISCR ;
2011-06-08 05:40:19 +04:00
/* Enable codec path and ISI */
ctrl = ISI_CTRL_CDC | ISI_CTRL_EN ;
isi_writel ( isi , ISI_CTRL , ctrl ) ;
isi_writel ( isi , ISI_CFG1 , cfg1 ) ;
}
static void buffer_queue ( struct vb2_buffer * vb )
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vb - > vb2_queue ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
struct frame_buffer * buf = container_of ( vb , struct frame_buffer , vb ) ;
unsigned long flags = 0 ;
spin_lock_irqsave ( & isi - > lock , flags ) ;
list_add_tail ( & buf - > list , & isi - > video_buffer_list ) ;
if ( isi - > active = = NULL ) {
isi - > active = buf ;
2011-08-29 15:51:49 +04:00
if ( vb2_is_streaming ( vb - > vb2_queue ) )
start_dma ( isi , buf ) ;
2011-06-08 05:40:19 +04:00
}
spin_unlock_irqrestore ( & isi - > lock , flags ) ;
}
2011-08-29 15:51:49 +04:00
static int start_streaming ( struct vb2_queue * vq , unsigned int count )
2011-06-08 05:40:19 +04:00
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vq ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
2013-11-03 04:25:06 +04:00
int ret ;
2015-05-26 12:54:46 +03:00
pm_runtime_get_sync ( ici - > v4l2_dev . dev ) ;
2013-11-03 04:25:06 +04:00
/* Reset ISI */
ret = atmel_isi_wait_status ( isi , WAIT_ISI_RESET ) ;
if ( ret < 0 ) {
dev_err ( icd - > parent , " Reset ISI timed out \n " ) ;
2015-05-26 12:54:46 +03:00
pm_runtime_put ( ici - > v4l2_dev . dev ) ;
2013-11-03 04:25:06 +04:00
return ret ;
}
/* Disable all interrupts */
2014-08-22 14:53:27 +04:00
isi_writel ( isi , ISI_INTDIS , ( u32 ) ~ 0UL ) ;
2011-06-08 05:40:19 +04:00
spin_lock_irq ( & isi - > lock ) ;
2013-10-24 11:27:11 +04:00
/* Clear any pending interrupt */
2014-08-26 18:21:43 +04:00
isi_readl ( isi , ISI_STATUS ) ;
2011-06-08 05:40:19 +04:00
2011-08-29 15:51:49 +04:00
if ( count )
start_dma ( isi , isi - > active ) ;
2011-06-08 05:40:19 +04:00
spin_unlock_irq ( & isi - > lock ) ;
return 0 ;
}
/* abort streaming and wait for last buffer */
2014-04-17 09:47:21 +04:00
static void stop_streaming ( struct vb2_queue * vq )
2011-06-08 05:40:19 +04:00
{
struct soc_camera_device * icd = soc_camera_from_vb2q ( vq ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
struct frame_buffer * buf , * node ;
int ret = 0 ;
unsigned long timeout ;
spin_lock_irq ( & isi - > lock ) ;
isi - > active = NULL ;
/* Release all active buffers */
list_for_each_entry_safe ( buf , node , & isi - > video_buffer_list , list ) {
list_del_init ( & buf - > list ) ;
vb2_buffer_done ( & buf - > vb , VB2_BUF_STATE_ERROR ) ;
}
spin_unlock_irq ( & isi - > lock ) ;
timeout = jiffies + FRAME_INTERVAL_MILLI_SEC * HZ ;
/* Wait until the end of the current frame. */
while ( ( isi_readl ( isi , ISI_STATUS ) & ISI_CTRL_CDC ) & &
time_before ( jiffies , timeout ) )
msleep ( 1 ) ;
2015-05-26 12:54:45 +03:00
if ( time_after ( jiffies , timeout ) )
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent ,
2011-06-08 05:40:19 +04:00
" Timeout waiting for finishing codec request \n " ) ;
/* Disable interrupts */
isi_writel ( isi , ISI_INTDIS ,
ISI_SR_CXFR_DONE | ISI_SR_PXFR_DONE ) ;
/* Disable ISI and wait for it is done */
ret = atmel_isi_wait_status ( isi , WAIT_ISI_DISABLE ) ;
if ( ret < 0 )
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " Disable ISI timed out \n " ) ;
2015-05-26 12:54:46 +03:00
pm_runtime_put ( ici - > v4l2_dev . dev ) ;
2011-06-08 05:40:19 +04:00
}
static struct vb2_ops isi_video_qops = {
. queue_setup = queue_setup ,
. buf_init = buffer_init ,
. buf_prepare = buffer_prepare ,
. buf_cleanup = buffer_cleanup ,
. buf_queue = buffer_queue ,
. start_streaming = start_streaming ,
. stop_streaming = stop_streaming ,
2014-11-27 01:42:27 +03:00
. wait_prepare = vb2_ops_wait_prepare ,
. wait_finish = vb2_ops_wait_finish ,
2011-06-08 05:40:19 +04:00
} ;
/* ------------------------------------------------------------------
SOC camera operations for the device
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int isi_camera_init_videobuf ( struct vb2_queue * q ,
struct soc_camera_device * icd )
{
2014-11-27 01:42:27 +03:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
q - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
q - > io_modes = VB2_MMAP ;
q - > drv_priv = icd ;
q - > buf_struct_size = sizeof ( struct frame_buffer ) ;
q - > ops = & isi_video_qops ;
q - > mem_ops = & vb2_dma_contig_memops ;
2014-02-26 02:12:19 +04:00
q - > timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC ;
2014-11-27 01:42:27 +03:00
q - > lock = & ici - > host_lock ;
2011-06-08 05:40:19 +04:00
return vb2_queue_init ( q ) ;
}
static int isi_camera_set_fmt ( struct soc_camera_device * icd ,
struct v4l2_format * f )
{
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
const struct soc_camera_format_xlate * xlate ;
struct v4l2_pix_format * pix = & f - > fmt . pix ;
2015-04-09 10:05:59 +03:00
struct v4l2_subdev_format format = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
} ;
struct v4l2_mbus_framefmt * mf = & format . format ;
2011-06-08 05:40:19 +04:00
int ret ;
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 " ,
2011-06-08 05:40:19 +04:00
pix - > pixelformat ) ;
return - EINVAL ;
}
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Plan to set format %dx%d \n " ,
2011-06-08 05:40:19 +04:00
pix - > width , pix - > height ) ;
2015-04-09 10:05:59 +03:00
mf - > width = pix - > width ;
mf - > height = pix - > height ;
mf - > field = pix - > field ;
mf - > colorspace = pix - > colorspace ;
mf - > code = xlate - > code ;
2011-06-08 05:40:19 +04:00
2015-04-09 10:05:59 +03:00
ret = v4l2_subdev_call ( sd , pad , set_fmt , NULL , & format ) ;
2011-06-08 05:40:19 +04:00
if ( ret < 0 )
return ret ;
2015-04-09 10:05:59 +03:00
if ( mf - > code ! = xlate - > code )
2011-06-08 05:40:19 +04:00
return - EINVAL ;
2015-05-26 12:54:46 +03:00
/* Enable PM and peripheral clock before operate isi registers */
pm_runtime_get_sync ( ici - > v4l2_dev . dev ) ;
2011-06-08 05:40:19 +04:00
ret = configure_geometry ( isi , pix - > width , pix - > height , xlate - > code ) ;
2015-05-26 12:54:46 +03:00
pm_runtime_put ( ici - > v4l2_dev . dev ) ;
2011-06-08 05:40:19 +04:00
if ( ret < 0 )
return ret ;
2015-04-09 10:05:59 +03:00
pix - > width = mf - > width ;
pix - > height = mf - > height ;
pix - > field = mf - > field ;
pix - > colorspace = mf - > colorspace ;
2011-06-08 05:40:19 +04:00
icd - > current_fmt = xlate ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Finally set format %dx%d \n " ,
2011-06-08 05:40:19 +04:00
pix - > width , pix - > height ) ;
return ret ;
}
static int isi_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 ;
2015-04-09 10:05:35 +03:00
struct v4l2_subdev_pad_config pad_cfg ;
struct v4l2_subdev_format format = {
. which = V4L2_SUBDEV_FORMAT_TRY ,
} ;
struct v4l2_mbus_framefmt * mf = & format . format ;
2011-06-08 05:40:19 +04:00
u32 pixfmt = pix - > pixelformat ;
int ret ;
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 ) ;
2011-06-08 05:40:19 +04:00
return - EINVAL ;
}
/* limit to Atmel ISI hardware capabilities */
if ( pix - > height > MAX_SUPPORT_HEIGHT )
pix - > height = MAX_SUPPORT_HEIGHT ;
if ( pix - > width > MAX_SUPPORT_WIDTH )
pix - > width = MAX_SUPPORT_WIDTH ;
/* limit to sensor capabilities */
2015-04-09 10:05:35 +03:00
mf - > width = pix - > width ;
mf - > height = pix - > height ;
mf - > field = pix - > field ;
mf - > colorspace = pix - > colorspace ;
mf - > code = xlate - > code ;
2011-06-08 05:40:19 +04:00
2015-04-09 10:05:35 +03:00
ret = v4l2_subdev_call ( sd , pad , set_fmt , & pad_cfg , & format ) ;
2011-06-08 05:40:19 +04:00
if ( ret < 0 )
return ret ;
2015-04-09 10:05:35 +03:00
pix - > width = mf - > width ;
pix - > height = mf - > height ;
pix - > colorspace = mf - > colorspace ;
2011-06-08 05:40:19 +04:00
2015-04-09 10:05:35 +03:00
switch ( mf - > field ) {
2011-06-08 05:40:19 +04:00
case V4L2_FIELD_ANY :
pix - > field = V4L2_FIELD_NONE ;
break ;
case V4L2_FIELD_NONE :
break ;
default :
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent , " Field type %d unsupported. \n " ,
2015-04-09 10:05:35 +03:00
mf - > field ) ;
2011-06-08 05:40:19 +04:00
ret = - EINVAL ;
}
return ret ;
}
static const struct soc_mbus_pixelfmt isi_camera_formats [ ] = {
{
. fourcc = V4L2_PIX_FMT_YUYV ,
. name = " Packed YUV422 16 bit " ,
. bits_per_sample = 8 ,
. packing = SOC_MBUS_PACKING_2X8_PADHI ,
. order = SOC_MBUS_ORDER_LE ,
2012-03-21 15:03:23 +04:00
. layout = SOC_MBUS_LAYOUT_PACKED ,
2011-06-08 05:40:19 +04:00
} ,
} ;
/* This will be corrected as we get more formats */
static bool isi_camera_packing_supported ( const struct soc_mbus_pixelfmt * fmt )
{
return fmt - > packing = = SOC_MBUS_PACKING_NONE | |
( fmt - > bits_per_sample = = 8 & &
fmt - > packing = = SOC_MBUS_PACKING_2X8_PADHI ) | |
( fmt - > bits_per_sample > 8 & &
fmt - > packing = = SOC_MBUS_PACKING_EXTEND16 ) ;
}
2011-07-27 19:18:37 +04:00
# define ISI_BUS_PARAM (V4L2_MBUS_MASTER | \
V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
V4L2_MBUS_HSYNC_ACTIVE_LOW | \
V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
V4L2_MBUS_VSYNC_ACTIVE_LOW | \
V4L2_MBUS_PCLK_SAMPLE_RISING | \
V4L2_MBUS_PCLK_SAMPLE_FALLING | \
V4L2_MBUS_DATA_ACTIVE_HIGH )
2011-06-08 05:40:19 +04:00
static int isi_camera_try_bus_param ( struct soc_camera_device * icd ,
unsigned char buswidth )
{
2011-07-27 19:18:37 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
2011-07-27 19:18:37 +04:00
struct v4l2_mbus_config cfg = { . type = V4L2_MBUS_PARALLEL , } ;
unsigned long common_flags ;
2011-06-08 05:40:19 +04:00
int ret ;
2011-07-27 19:18:37 +04:00
ret = v4l2_subdev_call ( sd , video , g_mbus_config , & cfg ) ;
if ( ! ret ) {
common_flags = soc_mbus_config_compatible ( & cfg ,
ISI_BUS_PARAM ) ;
if ( ! common_flags ) {
dev_warn ( icd - > parent ,
" Flags incompatible: camera 0x%x, host 0x%x \n " ,
cfg . flags , ISI_BUS_PARAM ) ;
return - EINVAL ;
}
} else if ( ret ! = - ENOIOCTLCMD ) {
return ret ;
}
if ( ( 1 < < ( buswidth - 1 ) ) & isi - > width_flags )
return 0 ;
return - EINVAL ;
2011-06-08 05:40:19 +04:00
}
static int isi_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 ) ;
int formats = 0 , ret ;
/* sensor format */
2015-04-09 10:01:33 +03:00
struct v4l2_subdev_mbus_code_enum code = {
. which = V4L2_SUBDEV_FORMAT_ACTIVE ,
. index = idx ,
} ;
2011-06-08 05:40:19 +04:00
/* soc camera host format */
const struct soc_mbus_pixelfmt * fmt ;
2015-04-09 10:01:33 +03:00
ret = v4l2_subdev_call ( sd , pad , enum_mbus_code , NULL , & code ) ;
2011-06-08 05:40:19 +04:00
if ( ret < 0 )
/* No more formats */
return 0 ;
2015-04-09 10:01:33 +03:00
fmt = soc_mbus_get_fmtdesc ( code . code ) ;
2011-06-08 05:40:19 +04:00
if ( ! fmt ) {
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent ,
2015-04-09 10:01:33 +03:00
" Invalid format code #%u: %d \n " , idx , code . code ) ;
2011-06-08 05:40:19 +04:00
return 0 ;
}
/* This also checks support for the requested bits-per-sample */
ret = isi_camera_try_bus_param ( icd , fmt - > bits_per_sample ) ;
if ( ret < 0 ) {
2011-07-16 03:03:38 +04:00
dev_err ( icd - > parent ,
2011-06-08 05:40:19 +04:00
" Fail to try the bus parameters. \n " ) ;
return 0 ;
}
2015-04-09 10:01:33 +03:00
switch ( code . code ) {
2014-11-10 20:28:31 +03:00
case MEDIA_BUS_FMT_UYVY8_2X8 :
case MEDIA_BUS_FMT_VYUY8_2X8 :
case MEDIA_BUS_FMT_YUYV8_2X8 :
case MEDIA_BUS_FMT_YVYU8_2X8 :
2011-06-08 05:40:19 +04:00
formats + + ;
if ( xlate ) {
xlate - > host_fmt = & isi_camera_formats [ 0 ] ;
2015-04-09 10:01:33 +03:00
xlate - > code = code . code ;
2011-06-08 05:40:19 +04:00
xlate + + ;
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent , " Providing format %s using code %d \n " ,
2015-04-09 10:01:33 +03:00
isi_camera_formats [ 0 ] . name , code . code ) ;
2011-06-08 05:40:19 +04:00
}
break ;
default :
if ( ! isi_camera_packing_supported ( fmt ) )
return 0 ;
if ( xlate )
2011-07-16 03:03:38 +04:00
dev_dbg ( icd - > parent ,
2011-06-08 05:40:19 +04:00
" Providing format %s in pass-through mode \n " ,
fmt - > name ) ;
}
/* Generic pass-through */
formats + + ;
if ( xlate ) {
xlate - > host_fmt = fmt ;
2015-04-09 10:01:33 +03:00
xlate - > code = code . code ;
2011-06-08 05:40:19 +04:00
xlate + + ;
}
return formats ;
}
static int isi_camera_add_device ( struct soc_camera_device * icd )
{
2013-04-04 16:56:09 +04:00
dev_dbg ( icd - > parent , " Atmel ISI Camera driver attached to camera %d \n " ,
icd - > devnum ) ;
return 0 ;
}
static void isi_camera_remove_device ( struct soc_camera_device * icd )
{
dev_dbg ( icd - > parent , " Atmel ISI Camera driver detached from camera %d \n " ,
icd - > devnum ) ;
}
2011-06-08 05:40:19 +04:00
static unsigned int isi_camera_poll ( struct file * file , poll_table * pt )
{
struct soc_camera_device * icd = file - > private_data ;
return vb2_poll ( & icd - > vb2_vidq , file , pt ) ;
}
static int isi_camera_querycap ( struct soc_camera_host * ici ,
struct v4l2_capability * cap )
{
strcpy ( cap - > driver , " atmel-isi " ) ;
strcpy ( cap - > card , " Atmel Image Sensor Interface " ) ;
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 ;
2011-06-08 05:40:19 +04:00
return 0 ;
}
2011-09-22 00:52:51 +04:00
static int isi_camera_set_bus_param ( struct soc_camera_device * icd )
2011-06-08 05:40:19 +04:00
{
2011-07-27 19:18:37 +04:00
struct v4l2_subdev * sd = soc_camera_to_subdev ( icd ) ;
2011-07-16 03:03:38 +04:00
struct soc_camera_host * ici = to_soc_camera_host ( icd - > parent ) ;
2011-06-08 05:40:19 +04:00
struct atmel_isi * isi = ici - > priv ;
2011-07-27 19:18:37 +04:00
struct v4l2_mbus_config cfg = { . type = V4L2_MBUS_PARALLEL , } ;
unsigned long common_flags ;
2011-06-08 05:40:19 +04:00
int ret ;
u32 cfg1 = 0 ;
2011-07-27 19:18:37 +04:00
ret = v4l2_subdev_call ( sd , video , g_mbus_config , & cfg ) ;
if ( ! ret ) {
common_flags = soc_mbus_config_compatible ( & cfg ,
ISI_BUS_PARAM ) ;
if ( ! common_flags ) {
dev_warn ( icd - > parent ,
" Flags incompatible: camera 0x%x, host 0x%x \n " ,
cfg . flags , ISI_BUS_PARAM ) ;
return - EINVAL ;
}
} else if ( ret ! = - ENOIOCTLCMD ) {
return ret ;
} else {
common_flags = ISI_BUS_PARAM ;
}
dev_dbg ( icd - > parent , " Flags cam: 0x%x host: 0x%x common: 0x%lx \n " ,
cfg . flags , ISI_BUS_PARAM , common_flags ) ;
2011-06-08 05:40:19 +04:00
/* Make choises, based on platform preferences */
2011-07-27 19:18:37 +04:00
if ( ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ) ) {
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . hsync_act_low )
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_HIGH ;
2011-06-08 05:40:19 +04:00
else
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_HSYNC_ACTIVE_LOW ;
2011-06-08 05:40:19 +04:00
}
2011-07-27 19:18:37 +04:00
if ( ( common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH ) & &
( common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ) ) {
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . vsync_act_low )
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_VSYNC_ACTIVE_HIGH ;
2011-06-08 05:40:19 +04:00
else
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_VSYNC_ACTIVE_LOW ;
2011-06-08 05:40:19 +04:00
}
2011-07-27 19:18:37 +04:00
if ( ( common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING ) & &
( common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING ) ) {
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . pclk_act_falling )
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_RISING ;
2011-06-08 05:40:19 +04:00
else
2011-07-27 19:18:37 +04:00
common_flags & = ~ V4L2_MBUS_PCLK_SAMPLE_FALLING ;
2011-06-08 05:40:19 +04:00
}
2011-07-27 19:18:37 +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 " ,
2011-06-08 05:40:19 +04:00
common_flags , ret ) ;
return ret ;
}
/* set bus param for ISI */
2011-07-27 19:18:37 +04:00
if ( common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW )
2011-06-08 05:40:19 +04:00
cfg1 | = ISI_CFG1_HSYNC_POL_ACTIVE_LOW ;
2011-07-27 19:18:37 +04:00
if ( common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW )
2011-06-08 05:40:19 +04:00
cfg1 | = ISI_CFG1_VSYNC_POL_ACTIVE_LOW ;
2011-07-27 19:18:37 +04:00
if ( common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING )
2011-06-08 05:40:19 +04:00
cfg1 | = ISI_CFG1_PIXCLK_POL_ACTIVE_FALLING ;
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . has_emb_sync )
2011-06-08 05:40:19 +04:00
cfg1 | = ISI_CFG1_EMB_SYNC ;
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . full_mode )
2011-06-08 05:40:19 +04:00
cfg1 | = ISI_CFG1_FULL_MODE ;
2014-11-25 12:30:25 +03:00
cfg1 | = ISI_CFG1_THMASK_BEATS_16 ;
2015-05-26 12:54:46 +03:00
/* Enable PM and peripheral clock before operate isi registers */
pm_runtime_get_sync ( ici - > v4l2_dev . dev ) ;
2011-06-08 05:40:19 +04:00
isi_writel ( isi , ISI_CTRL , ISI_CTRL_DIS ) ;
isi_writel ( isi , ISI_CFG1 , cfg1 ) ;
2015-05-26 12:54:46 +03:00
pm_runtime_put ( ici - > v4l2_dev . dev ) ;
2011-06-08 05:40:19 +04:00
return 0 ;
}
static struct soc_camera_host_ops isi_soc_camera_host_ops = {
. owner = THIS_MODULE ,
. add = isi_camera_add_device ,
. remove = isi_camera_remove_device ,
. set_fmt = isi_camera_set_fmt ,
. try_fmt = isi_camera_try_fmt ,
. get_formats = isi_camera_get_formats ,
. init_videobuf2 = isi_camera_init_videobuf ,
. poll = isi_camera_poll ,
. querycap = isi_camera_querycap ,
. set_bus_param = isi_camera_set_bus_param ,
} ;
/* -----------------------------------------------------------------------*/
2012-12-22 01:17:53 +04:00
static int atmel_isi_remove ( struct platform_device * pdev )
2011-06-08 05:40:19 +04:00
{
struct soc_camera_host * soc_host = to_soc_camera_host ( & pdev - > dev ) ;
struct atmel_isi * isi = container_of ( soc_host ,
struct atmel_isi , soc_host ) ;
soc_camera_host_unregister ( soc_host ) ;
vb2_dma_contig_cleanup_ctx ( isi - > alloc_ctx ) ;
dma_free_coherent ( & pdev - > dev ,
sizeof ( struct fbd ) * MAX_BUFFER_NUM ,
isi - > p_fb_descriptors ,
isi - > fb_descriptors_phys ) ;
2015-05-26 12:54:46 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2011-06-08 05:40:19 +04:00
return 0 ;
}
2014-07-28 11:25:17 +04:00
static int atmel_isi_probe_dt ( struct atmel_isi * isi ,
struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct v4l2_of_endpoint ep ;
int err ;
/* Default settings for ISI */
isi - > pdata . full_mode = 1 ;
isi - > pdata . frate = ISI_CFG1_FRATE_CAPTURE_ALL ;
np = of_graph_get_next_endpoint ( np , NULL ) ;
if ( ! np ) {
dev_err ( & pdev - > dev , " Could not find the endpoint \n " ) ;
return - EINVAL ;
}
err = v4l2_of_parse_endpoint ( np , & ep ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Could not parse the endpoint \n " ) ;
goto err_probe_dt ;
}
switch ( ep . bus . parallel . bus_width ) {
case 8 :
isi - > pdata . data_width_flags = ISI_DATAWIDTH_8 ;
break ;
case 10 :
isi - > pdata . data_width_flags =
ISI_DATAWIDTH_8 | ISI_DATAWIDTH_10 ;
break ;
default :
dev_err ( & pdev - > dev , " Unsupported bus width: %d \n " ,
ep . bus . parallel . bus_width ) ;
err = - EINVAL ;
goto err_probe_dt ;
}
err_probe_dt :
of_node_put ( np ) ;
return err ;
}
2012-12-22 01:17:53 +04:00
static int atmel_isi_probe ( struct platform_device * pdev )
2011-06-08 05:40:19 +04:00
{
unsigned int irq ;
struct atmel_isi * isi ;
struct resource * regs ;
int ret , i ;
struct device * dev = & pdev - > dev ;
struct soc_camera_host * soc_host ;
struct isi_platform_data * pdata ;
pdata = dev - > platform_data ;
2014-07-28 11:25:17 +04:00
if ( ( ! pdata | | ! pdata - > data_width_flags ) & & ! pdev - > dev . of_node ) {
2011-06-08 05:40:19 +04:00
dev_err ( & pdev - > dev ,
" No config available for Atmel ISI \n " ) ;
return - EINVAL ;
}
2013-11-25 19:13:50 +04:00
isi = devm_kzalloc ( & pdev - > dev , sizeof ( struct atmel_isi ) , GFP_KERNEL ) ;
2011-06-08 05:40:19 +04:00
if ( ! isi ) {
dev_err ( & pdev - > dev , " Can't allocate interface! \n " ) ;
2013-11-25 19:13:50 +04:00
return - ENOMEM ;
2011-06-08 05:40:19 +04:00
}
2013-11-25 19:13:50 +04:00
isi - > pclk = devm_clk_get ( & pdev - > dev , " isi_clk " ) ;
if ( IS_ERR ( isi - > pclk ) )
return PTR_ERR ( isi - > pclk ) ;
2014-07-28 11:25:17 +04:00
if ( pdata ) {
memcpy ( & isi - > pdata , pdata , sizeof ( isi - > pdata ) ) ;
} else {
ret = atmel_isi_probe_dt ( isi , pdev ) ;
if ( ret )
return ret ;
}
2011-06-08 05:40:19 +04:00
isi - > active = NULL ;
spin_lock_init ( & isi - > lock ) ;
INIT_LIST_HEAD ( & isi - > video_buffer_list ) ;
INIT_LIST_HEAD ( & isi - > dma_desc_head ) ;
isi - > p_fb_descriptors = dma_alloc_coherent ( & pdev - > dev ,
sizeof ( struct fbd ) * MAX_BUFFER_NUM ,
& isi - > fb_descriptors_phys ,
GFP_KERNEL ) ;
if ( ! isi - > p_fb_descriptors ) {
dev_err ( & pdev - > dev , " Can't allocate descriptors! \n " ) ;
2013-11-25 19:21:33 +04:00
return - ENOMEM ;
2011-06-08 05:40:19 +04:00
}
for ( i = 0 ; i < MAX_BUFFER_NUM ; i + + ) {
isi - > dma_desc [ i ] . p_fbd = isi - > p_fb_descriptors + i ;
isi - > dma_desc [ i ] . fbd_phys = isi - > fb_descriptors_phys +
i * sizeof ( struct fbd ) ;
list_add ( & isi - > dma_desc [ i ] . list , & isi - > dma_desc_head ) ;
}
isi - > alloc_ctx = vb2_dma_contig_init_ctx ( & pdev - > dev ) ;
if ( IS_ERR ( isi - > alloc_ctx ) ) {
ret = PTR_ERR ( isi - > alloc_ctx ) ;
goto err_alloc_ctx ;
}
2013-11-25 19:13:50 +04:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
isi - > regs = devm_ioremap_resource ( & pdev - > dev , regs ) ;
if ( IS_ERR ( isi - > regs ) ) {
ret = PTR_ERR ( isi - > regs ) ;
2011-06-08 05:40:19 +04:00
goto err_ioremap ;
}
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . data_width_flags & ISI_DATAWIDTH_8 )
2011-07-27 19:18:37 +04:00
isi - > width_flags = 1 < < 7 ;
2014-07-25 14:13:39 +04:00
if ( isi - > pdata . data_width_flags & ISI_DATAWIDTH_10 )
2011-07-27 19:18:37 +04:00
isi - > width_flags | = 1 < < 9 ;
2011-06-08 05:40:19 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
2012-11-16 10:50:37 +04:00
if ( IS_ERR_VALUE ( irq ) ) {
2011-06-08 05:40:19 +04:00
ret = irq ;
goto err_req_irq ;
}
2013-11-25 19:13:50 +04:00
ret = devm_request_irq ( & pdev - > dev , irq , isi_interrupt , 0 , " isi " , isi ) ;
2011-06-08 05:40:19 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " Unable to request irq %d \n " , irq ) ;
goto err_req_irq ;
}
isi - > irq = irq ;
soc_host = & isi - > soc_host ;
soc_host - > drv_name = " isi-camera " ;
soc_host - > ops = & isi_soc_camera_host_ops ;
soc_host - > priv = isi ;
soc_host - > v4l2_dev . dev = & pdev - > dev ;
soc_host - > nr = pdev - > id ;
2015-05-26 12:54:46 +03:00
pm_suspend_ignore_children ( & pdev - > dev , true ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2014-07-25 14:13:38 +04:00
if ( isi - > pdata . asd_sizes ) {
soc_host - > asd = isi - > pdata . asd ;
soc_host - > asd_sizes = isi - > pdata . asd_sizes ;
}
2011-06-08 05:40:19 +04:00
ret = soc_camera_host_register ( soc_host ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Unable to register soc camera host \n " ) ;
goto err_register_soc_camera_host ;
}
return 0 ;
err_register_soc_camera_host :
2015-05-26 12:54:46 +03:00
pm_runtime_disable ( & pdev - > dev ) ;
2011-06-08 05:40:19 +04:00
err_req_irq :
err_ioremap :
vb2_dma_contig_cleanup_ctx ( isi - > alloc_ctx ) ;
err_alloc_ctx :
dma_free_coherent ( & pdev - > dev ,
sizeof ( struct fbd ) * MAX_BUFFER_NUM ,
isi - > p_fb_descriptors ,
isi - > fb_descriptors_phys ) ;
return ret ;
}
2015-09-06 13:08:49 +03:00
# ifdef CONFIG_PM
2015-05-26 12:54:46 +03:00
static int atmel_isi_runtime_suspend ( struct device * dev )
{
struct soc_camera_host * soc_host = to_soc_camera_host ( dev ) ;
struct atmel_isi * isi = container_of ( soc_host ,
struct atmel_isi , soc_host ) ;
clk_disable_unprepare ( isi - > pclk ) ;
return 0 ;
}
static int atmel_isi_runtime_resume ( struct device * dev )
{
struct soc_camera_host * soc_host = to_soc_camera_host ( dev ) ;
struct atmel_isi * isi = container_of ( soc_host ,
struct atmel_isi , soc_host ) ;
return clk_prepare_enable ( isi - > pclk ) ;
}
2015-09-06 13:08:49 +03:00
# endif /* CONFIG_PM */
2015-05-26 12:54:46 +03:00
static const struct dev_pm_ops atmel_isi_dev_pm_ops = {
SET_RUNTIME_PM_OPS ( atmel_isi_runtime_suspend ,
atmel_isi_runtime_resume , NULL )
} ;
2014-07-28 11:25:17 +04:00
static const struct of_device_id atmel_isi_of_match [ ] = {
{ . compatible = " atmel,at91sam9g45-isi " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , atmel_isi_of_match ) ;
2011-06-08 05:40:19 +04:00
static struct platform_driver atmel_isi_driver = {
2012-12-22 01:17:53 +04:00
. remove = atmel_isi_remove ,
2011-06-08 05:40:19 +04:00
. driver = {
. name = " atmel_isi " ,
2014-07-28 11:25:17 +04:00
. of_match_table = of_match_ptr ( atmel_isi_of_match ) ,
2015-05-26 12:54:46 +03:00
. pm = & atmel_isi_dev_pm_ops ,
2011-06-08 05:40:19 +04:00
} ,
} ;
2013-03-14 21:09:31 +04:00
module_platform_driver_probe ( atmel_isi_driver , atmel_isi_probe ) ;
2011-06-08 05:40:19 +04:00
MODULE_AUTHOR ( " Josh Wu <josh.wu@atmel.com> " ) ;
MODULE_DESCRIPTION ( " The V4L2 driver for Atmel Linux " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_SUPPORTED_DEVICE ( " video " ) ;