2011-04-04 09:44:19 -03:00
/*
2013-05-31 10:38:23 -03:00
* Samsung S5P / EXYNOS SoC series MIPI - CSI receiver driver
2011-04-04 09:44:19 -03:00
*
2013-05-31 10:38:23 -03:00
* Copyright ( C ) 2011 - 2013 Samsung Electronics Co . , Ltd .
* Author : Sylwester Nawrocki < s . nawrocki @ samsung . com >
2011-04-04 09:44:19 -03:00
*
* 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/delay.h>
# include <linux/device.h>
# include <linux/errno.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/memory.h>
# include <linux/module.h>
2013-03-26 08:20:30 -03:00
# include <linux/of.h>
2014-02-10 22:01:48 +01:00
# include <linux/of_graph.h>
2013-10-16 21:58:11 +05:30
# include <linux/phy/phy.h>
2011-04-04 09:44:19 -03:00
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
2014-08-20 13:50:03 -06:00
# include <linux/sizes.h>
2011-04-04 09:44:19 -03:00
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/videodev2.h>
2014-04-15 08:35:25 -03:00
# include <media/exynos-fimc.h>
2013-03-26 08:20:30 -03:00
# include <media/v4l2-of.h>
2011-04-04 09:44:19 -03:00
# include <media/v4l2-subdev.h>
2013-03-26 08:20:30 -03:00
2011-04-04 09:44:19 -03:00
# include "mipi-csis.h"
static int debug ;
module_param ( debug , int , 0644 ) ;
2012-09-05 10:10:37 -03:00
MODULE_PARM_DESC ( debug , " Debug level (0-2) " ) ;
2011-04-04 09:44:19 -03:00
/* Register map definition */
/* CSIS global control */
# define S5PCSIS_CTRL 0x00
# define S5PCSIS_CTRL_DPDN_DEFAULT (0 << 31)
# define S5PCSIS_CTRL_DPDN_SWAP (1 << 31)
# define S5PCSIS_CTRL_ALIGN_32BIT (1 << 20)
# define S5PCSIS_CTRL_UPDATE_SHADOW (1 << 16)
# define S5PCSIS_CTRL_WCLK_EXTCLK (1 << 8)
# define S5PCSIS_CTRL_RESET (1 << 4)
# define S5PCSIS_CTRL_ENABLE (1 << 0)
/* D-PHY control */
# define S5PCSIS_DPHYCTRL 0x04
# define S5PCSIS_DPHYCTRL_HSS_MASK (0x1f << 27)
# define S5PCSIS_DPHYCTRL_ENABLE (0x1f << 0)
# define S5PCSIS_CONFIG 0x08
# define S5PCSIS_CFG_FMT_YCBCR422_8BIT (0x1e << 2)
# define S5PCSIS_CFG_FMT_RAW8 (0x2a << 2)
# define S5PCSIS_CFG_FMT_RAW10 (0x2b << 2)
# define S5PCSIS_CFG_FMT_RAW12 (0x2c << 2)
/* User defined formats, x = 1...4 */
# define S5PCSIS_CFG_FMT_USER(x) ((0x30 + x - 1) << 2)
# define S5PCSIS_CFG_FMT_MASK (0x3f << 2)
# define S5PCSIS_CFG_NR_LANE_MASK 3
2012-09-05 10:10:37 -03:00
/* Interrupt mask */
2011-04-04 09:44:19 -03:00
# define S5PCSIS_INTMSK 0x10
2012-09-05 10:10:37 -03:00
# define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31)
# define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30)
# define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29)
# define S5PCSIS_INTMSK_ODD_AFTER (1 << 28)
2013-05-31 10:38:23 -03:00
# define S5PCSIS_INTMSK_FRAME_START (1 << 27)
# define S5PCSIS_INTMSK_FRAME_END (1 << 26)
2012-09-05 10:10:37 -03:00
# define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12)
# define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5)
# define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4)
# define S5PCSIS_INTMSK_ERR_OVER (1 << 3)
# define S5PCSIS_INTMSK_ERR_ECC (1 << 2)
# define S5PCSIS_INTMSK_ERR_CRC (1 << 1)
# define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0)
2013-05-31 10:38:23 -03:00
# define S5PCSIS_INTMSK_EXYNOS4_EN_ALL 0xf000103f
# define S5PCSIS_INTMSK_EXYNOS5_EN_ALL 0xfc00103f
2012-09-05 10:10:37 -03:00
/* Interrupt source */
2011-04-04 09:44:19 -03:00
# define S5PCSIS_INTSRC 0x14
2012-09-05 10:10:37 -03:00
# define S5PCSIS_INTSRC_EVEN_BEFORE (1 << 31)
# define S5PCSIS_INTSRC_EVEN_AFTER (1 << 30)
# define S5PCSIS_INTSRC_EVEN (0x3 << 30)
# define S5PCSIS_INTSRC_ODD_BEFORE (1 << 29)
# define S5PCSIS_INTSRC_ODD_AFTER (1 << 28)
# define S5PCSIS_INTSRC_ODD (0x3 << 28)
2013-11-08 06:52:24 -03:00
# define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
2013-05-31 10:38:23 -03:00
# define S5PCSIS_INTSRC_FRAME_START (1 << 27)
# define S5PCSIS_INTSRC_FRAME_END (1 << 26)
2012-09-05 10:10:37 -03:00
# define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12)
# define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5)
# define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4)
# define S5PCSIS_INTSRC_ERR_OVER (1 << 3)
# define S5PCSIS_INTSRC_ERR_ECC (1 << 2)
# define S5PCSIS_INTSRC_ERR_CRC (1 << 1)
# define S5PCSIS_INTSRC_ERR_UNKNOWN (1 << 0)
# define S5PCSIS_INTSRC_ERRORS 0xf03f
2011-04-04 09:44:19 -03:00
/* Pixel resolution */
# define S5PCSIS_RESOL 0x2c
# define CSIS_MAX_PIX_WIDTH 0xffff
# define CSIS_MAX_PIX_HEIGHT 0xffff
2012-09-21 15:18:41 -03:00
/* Non-image packet data buffers */
# define S5PCSIS_PKTDATA_ODD 0x2000
# define S5PCSIS_PKTDATA_EVEN 0x3000
# define S5PCSIS_PKTDATA_SIZE SZ_4K
2011-04-04 09:44:19 -03:00
enum {
CSIS_CLK_MUX ,
CSIS_CLK_GATE ,
} ;
static char * csi_clock_name [ ] = {
[ CSIS_CLK_MUX ] = " sclk_csis " ,
[ CSIS_CLK_GATE ] = " csis " ,
} ;
# define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
2013-03-26 08:20:30 -03:00
# define DEFAULT_SCLK_CSIS_FREQ 166000000UL
2011-04-04 09:44:19 -03:00
2011-06-29 13:08:49 -03:00
static const char * const csis_supply_name [ ] = {
2012-09-17 06:03:50 -03:00
" vddcore " , /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */
" vddio " , /* CSIS I/O and PLL (1.8V) supply */
2011-06-29 13:08:49 -03:00
} ;
# define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name)
2011-04-04 09:44:19 -03:00
enum {
ST_POWERED = 1 ,
ST_STREAMING = 2 ,
ST_SUSPENDED = 4 ,
} ;
2012-09-05 10:10:37 -03:00
struct s5pcsis_event {
u32 mask ;
const char * const name ;
unsigned int counter ;
} ;
static const struct s5pcsis_event s5pcsis_events [ ] = {
/* Errors */
{ S5PCSIS_INTSRC_ERR_SOT_HS , " SOT Error " } ,
{ S5PCSIS_INTSRC_ERR_LOST_FS , " Lost Frame Start Error " } ,
{ S5PCSIS_INTSRC_ERR_LOST_FE , " Lost Frame End Error " } ,
{ S5PCSIS_INTSRC_ERR_OVER , " FIFO Overflow Error " } ,
{ S5PCSIS_INTSRC_ERR_ECC , " ECC Error " } ,
{ S5PCSIS_INTSRC_ERR_CRC , " CRC Error " } ,
{ S5PCSIS_INTSRC_ERR_UNKNOWN , " Unknown Error " } ,
/* Non-image data receive events */
{ S5PCSIS_INTSRC_EVEN_BEFORE , " Non-image data before even frame " } ,
{ S5PCSIS_INTSRC_EVEN_AFTER , " Non-image data after even frame " } ,
{ S5PCSIS_INTSRC_ODD_BEFORE , " Non-image data before odd frame " } ,
{ S5PCSIS_INTSRC_ODD_AFTER , " Non-image data after odd frame " } ,
2013-05-31 10:38:23 -03:00
/* Frame start/end */
{ S5PCSIS_INTSRC_FRAME_START , " Frame Start " } ,
{ S5PCSIS_INTSRC_FRAME_END , " Frame End " } ,
2012-09-05 10:10:37 -03:00
} ;
# define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events)
2012-09-21 15:18:41 -03:00
struct csis_pktbuf {
u32 * data ;
unsigned int len ;
} ;
2013-05-31 10:38:23 -03:00
struct csis_drvdata {
/* Mask of all used interrupts in S5PCSIS_INTMSK register */
u32 interrupt_mask ;
} ;
2011-04-04 09:44:19 -03:00
/**
* struct csis_state - the driver ' s internal state data structure
* @ lock : mutex serializing the subdev and power management operations ,
* protecting @ format and @ flags members
* @ pads : CSIS pads array
* @ sd : v4l2_subdev associated with CSIS device instance
2012-09-17 06:03:10 -03:00
* @ index : the hardware instance index
2011-04-04 09:44:19 -03:00
* @ pdev : CSIS platform device
2013-10-16 21:58:11 +05:30
* @ phy : pointer to the CSIS generic PHY
2011-04-04 09:44:19 -03:00
* @ regs : mmaped I / O registers memory
2012-09-05 10:10:37 -03:00
* @ supplies : CSIS regulator supplies
2011-04-04 09:44:19 -03:00
* @ clock : CSIS clocks
* @ irq : requested s5p - mipi - csis irq number
2013-05-31 10:38:23 -03:00
* @ interrupt_mask : interrupt mask of the all used interrupts
2011-04-04 09:44:19 -03:00
* @ flags : the state variable for power and streaming control
2013-03-26 08:20:30 -03:00
* @ clock_frequency : device bus clock frequency
* @ hs_settle : HS - RX settle time
* @ num_lanes : number of MIPI - CSI data lanes used
* @ max_num_lanes : maximum number of MIPI - CSI data lanes supported
* @ wclk_ext : CSI wrapper clock : 0 - bus clock , 1 - external SCLK_CAM
2011-04-04 09:44:19 -03:00
* @ csis_fmt : current CSIS pixel format
* @ format : common media bus format for the source and sink pad
2012-09-05 10:10:37 -03:00
* @ slock : spinlock protecting structure members below
2012-09-21 15:18:41 -03:00
* @ pkt_buf : the frame embedded ( non - image ) data buffer
2012-09-05 10:10:37 -03:00
* @ events : MIPI - CSIS event ( error ) counters
2011-04-04 09:44:19 -03:00
*/
struct csis_state {
struct mutex lock ;
struct media_pad pads [ CSIS_PADS_NUM ] ;
struct v4l2_subdev sd ;
2012-09-17 06:03:10 -03:00
u8 index ;
2011-04-04 09:44:19 -03:00
struct platform_device * pdev ;
2013-10-16 21:58:11 +05:30
struct phy * phy ;
2011-04-04 09:44:19 -03:00
void __iomem * regs ;
2011-06-29 13:08:49 -03:00
struct regulator_bulk_data supplies [ CSIS_NUM_SUPPLIES ] ;
2011-04-04 09:44:19 -03:00
struct clk * clock [ NUM_CSIS_CLOCKS ] ;
int irq ;
2013-05-31 10:38:23 -03:00
u32 interrupt_mask ;
2011-04-04 09:44:19 -03:00
u32 flags ;
2013-03-26 08:20:30 -03:00
u32 clk_frequency ;
u32 hs_settle ;
u32 num_lanes ;
u32 max_num_lanes ;
u8 wclk_ext ;
2011-04-04 09:44:19 -03:00
const struct csis_pix_format * csis_fmt ;
struct v4l2_mbus_framefmt format ;
2012-09-05 10:10:37 -03:00
2012-11-29 16:45:07 -03:00
spinlock_t slock ;
2012-09-21 15:18:41 -03:00
struct csis_pktbuf pkt_buf ;
2012-09-05 10:10:37 -03:00
struct s5pcsis_event events [ S5PCSIS_NUM_EVENTS ] ;
2011-04-04 09:44:19 -03:00
} ;
/**
* struct csis_pix_format - CSIS pixel format description
* @ pix_width_alignment : horizontal pixel alignment , width will be
* multiple of 2 ^ pix_width_alignment
* @ code : corresponding media bus code
* @ fmt_reg : S5PCSIS_CONFIG register value
2012-03-21 06:21:30 -03:00
* @ data_alignment : MIPI - CSI data alignment in bits
2011-04-04 09:44:19 -03:00
*/
struct csis_pix_format {
unsigned int pix_width_alignment ;
2014-11-10 14:28:31 -03:00
u32 code ;
2011-04-04 09:44:19 -03:00
u32 fmt_reg ;
2012-03-21 06:21:30 -03:00
u8 data_alignment ;
2011-04-04 09:44:19 -03:00
} ;
static const struct csis_pix_format s5pcsis_formats [ ] = {
{
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_VYUY8_2X8 ,
2011-04-04 09:44:19 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT ,
2012-03-21 06:21:30 -03:00
. data_alignment = 32 ,
2011-04-04 09:44:19 -03:00
} , {
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_JPEG_1X8 ,
2011-04-04 09:44:19 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_USER ( 1 ) ,
2012-03-21 06:21:30 -03:00
. data_alignment = 32 ,
2012-09-24 11:08:45 -03:00
} , {
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_S5C_UYVY_JPEG_1X8 ,
2012-09-24 11:08:45 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_USER ( 1 ) ,
. data_alignment = 32 ,
2012-08-29 14:35:21 -03:00
} , {
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_SGRBG8_1X8 ,
2012-08-29 14:35:21 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_RAW8 ,
. data_alignment = 24 ,
} , {
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_SGRBG10_1X10 ,
2012-08-29 14:35:21 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_RAW10 ,
. data_alignment = 24 ,
} , {
2014-11-10 14:28:31 -03:00
. code = MEDIA_BUS_FMT_SGRBG12_1X12 ,
2012-08-29 14:35:21 -03:00
. fmt_reg = S5PCSIS_CFG_FMT_RAW12 ,
. data_alignment = 24 ,
2012-09-24 11:08:45 -03:00
}
2011-04-04 09:44:19 -03:00
} ;
# define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
# define s5pcsis_read(__csis, __r) readl(__csis->regs + __r)
static struct csis_state * sd_to_csis_state ( struct v4l2_subdev * sdev )
{
return container_of ( sdev , struct csis_state , sd ) ;
}
static const struct csis_pix_format * find_csis_format (
struct v4l2_mbus_framefmt * mf )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( s5pcsis_formats ) ; i + + )
if ( mf - > code = = s5pcsis_formats [ i ] . code )
return & s5pcsis_formats [ i ] ;
return NULL ;
}
static void s5pcsis_enable_interrupts ( struct csis_state * state , bool on )
{
u32 val = s5pcsis_read ( state , S5PCSIS_INTMSK ) ;
2013-05-31 10:38:23 -03:00
if ( on )
val | = state - > interrupt_mask ;
else
val & = ~ state - > interrupt_mask ;
2011-04-04 09:44:19 -03:00
s5pcsis_write ( state , S5PCSIS_INTMSK , val ) ;
}
static void s5pcsis_reset ( struct csis_state * state )
{
u32 val = s5pcsis_read ( state , S5PCSIS_CTRL ) ;
s5pcsis_write ( state , S5PCSIS_CTRL , val | S5PCSIS_CTRL_RESET ) ;
udelay ( 10 ) ;
}
static void s5pcsis_system_enable ( struct csis_state * state , int on )
{
2012-11-02 14:20:27 -03:00
u32 val , mask ;
2011-04-04 09:44:19 -03:00
val = s5pcsis_read ( state , S5PCSIS_CTRL ) ;
if ( on )
val | = S5PCSIS_CTRL_ENABLE ;
else
val & = ~ S5PCSIS_CTRL_ENABLE ;
s5pcsis_write ( state , S5PCSIS_CTRL , val ) ;
val = s5pcsis_read ( state , S5PCSIS_DPHYCTRL ) ;
2012-11-02 14:20:27 -03:00
val & = ~ S5PCSIS_DPHYCTRL_ENABLE ;
if ( on ) {
2013-03-26 08:20:30 -03:00
mask = ( 1 < < ( state - > num_lanes + 1 ) ) - 1 ;
2012-11-02 14:20:27 -03:00
val | = ( mask & S5PCSIS_DPHYCTRL_ENABLE ) ;
}
2011-04-04 09:44:19 -03:00
s5pcsis_write ( state , S5PCSIS_DPHYCTRL , val ) ;
}
/* Called with the state.lock mutex held */
static void __s5pcsis_set_format ( struct csis_state * state )
{
struct v4l2_mbus_framefmt * mf = & state - > format ;
u32 val ;
2012-09-24 11:08:45 -03:00
v4l2_dbg ( 1 , debug , & state - > sd , " fmt: %#x, %d x %d \n " ,
2011-04-04 09:44:19 -03:00
mf - > code , mf - > width , mf - > height ) ;
/* Color format */
val = s5pcsis_read ( state , S5PCSIS_CONFIG ) ;
val = ( val & ~ S5PCSIS_CFG_FMT_MASK ) | state - > csis_fmt - > fmt_reg ;
s5pcsis_write ( state , S5PCSIS_CONFIG , val ) ;
/* Pixel resolution */
val = ( mf - > width < < 16 ) | mf - > height ;
s5pcsis_write ( state , S5PCSIS_RESOL , val ) ;
}
static void s5pcsis_set_hsync_settle ( struct csis_state * state , int settle )
{
u32 val = s5pcsis_read ( state , S5PCSIS_DPHYCTRL ) ;
val = ( val & ~ S5PCSIS_DPHYCTRL_HSS_MASK ) | ( settle < < 27 ) ;
s5pcsis_write ( state , S5PCSIS_DPHYCTRL , val ) ;
}
static void s5pcsis_set_params ( struct csis_state * state )
{
u32 val ;
val = s5pcsis_read ( state , S5PCSIS_CONFIG ) ;
2013-03-26 08:20:30 -03:00
val = ( val & ~ S5PCSIS_CFG_NR_LANE_MASK ) | ( state - > num_lanes - 1 ) ;
2011-04-04 09:44:19 -03:00
s5pcsis_write ( state , S5PCSIS_CONFIG , val ) ;
__s5pcsis_set_format ( state ) ;
2013-03-26 08:20:30 -03:00
s5pcsis_set_hsync_settle ( state , state - > hs_settle ) ;
2011-04-04 09:44:19 -03:00
val = s5pcsis_read ( state , S5PCSIS_CTRL ) ;
2012-03-21 06:21:30 -03:00
if ( state - > csis_fmt - > data_alignment = = 32 )
2011-04-04 09:44:19 -03:00
val | = S5PCSIS_CTRL_ALIGN_32BIT ;
else /* 24-bits */
val & = ~ S5PCSIS_CTRL_ALIGN_32BIT ;
2012-09-17 06:03:38 -03:00
2011-04-04 09:44:19 -03:00
val & = ~ S5PCSIS_CTRL_WCLK_EXTCLK ;
2013-03-26 08:20:30 -03:00
if ( state - > wclk_ext )
2012-09-17 06:03:38 -03:00
val | = S5PCSIS_CTRL_WCLK_EXTCLK ;
2011-04-04 09:44:19 -03:00
s5pcsis_write ( state , S5PCSIS_CTRL , val ) ;
/* Update the shadow register. */
val = s5pcsis_read ( state , S5PCSIS_CTRL ) ;
s5pcsis_write ( state , S5PCSIS_CTRL , val | S5PCSIS_CTRL_UPDATE_SHADOW ) ;
}
static void s5pcsis_clk_put ( struct csis_state * state )
{
int i ;
2012-01-30 11:39:30 -03:00
for ( i = 0 ; i < NUM_CSIS_CLOCKS ; i + + ) {
2013-01-29 06:52:29 -03:00
if ( IS_ERR ( state - > clock [ i ] ) )
2012-01-30 11:39:30 -03:00
continue ;
clk_unprepare ( state - > clock [ i ] ) ;
clk_put ( state - > clock [ i ] ) ;
2013-01-29 06:52:29 -03:00
state - > clock [ i ] = ERR_PTR ( - EINVAL ) ;
2012-01-30 11:39:30 -03:00
}
2011-04-04 09:44:19 -03:00
}
static int s5pcsis_clk_get ( struct csis_state * state )
{
struct device * dev = & state - > pdev - > dev ;
2012-01-30 11:39:30 -03:00
int i , ret ;
2011-04-04 09:44:19 -03:00
2013-01-29 06:52:29 -03:00
for ( i = 0 ; i < NUM_CSIS_CLOCKS ; i + + )
state - > clock [ i ] = ERR_PTR ( - EINVAL ) ;
2011-04-04 09:44:19 -03:00
for ( i = 0 ; i < NUM_CSIS_CLOCKS ; i + + ) {
state - > clock [ i ] = clk_get ( dev , csi_clock_name [ i ] ) ;
2013-01-29 06:52:29 -03:00
if ( IS_ERR ( state - > clock [ i ] ) ) {
ret = PTR_ERR ( state - > clock [ i ] ) ;
2012-01-30 11:39:30 -03:00
goto err ;
2013-01-29 06:52:29 -03:00
}
2012-01-30 11:39:30 -03:00
ret = clk_prepare ( state - > clock [ i ] ) ;
if ( ret < 0 ) {
clk_put ( state - > clock [ i ] ) ;
2013-01-29 06:52:29 -03:00
state - > clock [ i ] = ERR_PTR ( - EINVAL ) ;
2012-01-30 11:39:30 -03:00
goto err ;
2011-04-04 09:44:19 -03:00
}
}
return 0 ;
2012-01-30 11:39:30 -03:00
err :
s5pcsis_clk_put ( state ) ;
dev_err ( dev , " failed to get clock: %s \n " , csi_clock_name [ i ] ) ;
2013-01-29 06:52:29 -03:00
return ret ;
2011-04-04 09:44:19 -03:00
}
2012-09-28 11:05:53 -03:00
static void dump_regs ( struct csis_state * state , const char * label )
{
struct {
u32 offset ;
const char * const name ;
} registers [ ] = {
{ 0x00 , " CTRL " } ,
{ 0x04 , " DPHYCTRL " } ,
{ 0x08 , " CONFIG " } ,
{ 0x0c , " DPHYSTS " } ,
{ 0x10 , " INTMSK " } ,
{ 0x2c , " RESOL " } ,
{ 0x38 , " SDW_CONFIG " } ,
} ;
u32 i ;
v4l2_info ( & state - > sd , " --- %s --- \n " , label ) ;
for ( i = 0 ; i < ARRAY_SIZE ( registers ) ; i + + ) {
u32 cfg = s5pcsis_read ( state , registers [ i ] . offset ) ;
v4l2_info ( & state - > sd , " %10s: 0x%08x \n " , registers [ i ] . name , cfg ) ;
}
}
2011-04-04 09:44:19 -03:00
static void s5pcsis_start_stream ( struct csis_state * state )
{
s5pcsis_reset ( state ) ;
s5pcsis_set_params ( state ) ;
s5pcsis_system_enable ( state , true ) ;
s5pcsis_enable_interrupts ( state , true ) ;
}
static void s5pcsis_stop_stream ( struct csis_state * state )
{
s5pcsis_enable_interrupts ( state , false ) ;
s5pcsis_system_enable ( state , false ) ;
}
2012-09-05 10:10:37 -03:00
static void s5pcsis_clear_counters ( struct csis_state * state )
{
unsigned long flags ;
int i ;
spin_lock_irqsave ( & state - > slock , flags ) ;
for ( i = 0 ; i < S5PCSIS_NUM_EVENTS ; i + + )
state - > events [ i ] . counter = 0 ;
spin_unlock_irqrestore ( & state - > slock , flags ) ;
}
static void s5pcsis_log_counters ( struct csis_state * state , bool non_errors )
{
int i = non_errors ? S5PCSIS_NUM_EVENTS : S5PCSIS_NUM_EVENTS - 4 ;
unsigned long flags ;
spin_lock_irqsave ( & state - > slock , flags ) ;
2012-11-23 15:17:40 -03:00
for ( i - - ; i > = 0 ; i - - ) {
if ( state - > events [ i ] . counter > 0 | | debug )
2012-09-05 10:10:37 -03:00
v4l2_info ( & state - > sd , " %s events: %d \n " ,
state - > events [ i ] . name ,
state - > events [ i ] . counter ) ;
2012-11-23 15:17:40 -03:00
}
2012-09-05 10:10:37 -03:00
spin_unlock_irqrestore ( & state - > slock , flags ) ;
}
/*
* V4L2 subdev operations
*/
static int s5pcsis_s_power ( struct v4l2_subdev * sd , int on )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
struct device * dev = & state - > pdev - > dev ;
if ( on )
return pm_runtime_get_sync ( dev ) ;
return pm_runtime_put_sync ( dev ) ;
}
2011-04-04 09:44:19 -03:00
static int s5pcsis_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
int ret = 0 ;
v4l2_dbg ( 1 , debug , sd , " %s: %d, state: 0x%x \n " ,
__func__ , enable , state - > flags ) ;
if ( enable ) {
2012-09-05 10:10:37 -03:00
s5pcsis_clear_counters ( state ) ;
2011-04-04 09:44:19 -03:00
ret = pm_runtime_get_sync ( & state - > pdev - > dev ) ;
if ( ret & & ret ! = 1 )
return ret ;
}
2012-09-05 10:10:37 -03:00
2011-04-04 09:44:19 -03:00
mutex_lock ( & state - > lock ) ;
if ( enable ) {
if ( state - > flags & ST_SUSPENDED ) {
ret = - EBUSY ;
goto unlock ;
}
s5pcsis_start_stream ( state ) ;
state - > flags | = ST_STREAMING ;
} else {
s5pcsis_stop_stream ( state ) ;
state - > flags & = ~ ST_STREAMING ;
2012-09-05 10:10:37 -03:00
if ( debug > 0 )
s5pcsis_log_counters ( state , true ) ;
2011-04-04 09:44:19 -03:00
}
unlock :
mutex_unlock ( & state - > lock ) ;
if ( ! enable )
pm_runtime_put ( & state - > pdev - > dev ) ;
return ret = = 1 ? 0 : ret ;
}
static int s5pcsis_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_mbus_code_enum * code )
{
if ( code - > index > = ARRAY_SIZE ( s5pcsis_formats ) )
return - EINVAL ;
code - > code = s5pcsis_formats [ code - > index ] . code ;
return 0 ;
}
static struct csis_pix_format const * s5pcsis_try_format (
struct v4l2_mbus_framefmt * mf )
{
struct csis_pix_format const * csis_fmt ;
csis_fmt = find_csis_format ( mf ) ;
if ( csis_fmt = = NULL )
csis_fmt = & s5pcsis_formats [ 0 ] ;
mf - > code = csis_fmt - > code ;
v4l_bound_align_image ( & mf - > width , 1 , CSIS_MAX_PIX_WIDTH ,
csis_fmt - > pix_width_alignment ,
& mf - > height , 1 , CSIS_MAX_PIX_HEIGHT , 1 ,
0 ) ;
return csis_fmt ;
}
static struct v4l2_mbus_framefmt * __s5pcsis_get_format (
struct csis_state * state , struct v4l2_subdev_fh * fh ,
2013-04-23 12:40:24 -03:00
enum v4l2_subdev_format_whence which )
2011-04-04 09:44:19 -03:00
{
if ( which = = V4L2_SUBDEV_FORMAT_TRY )
2013-04-23 12:40:24 -03:00
return fh ? v4l2_subdev_get_try_format ( fh , 0 ) : NULL ;
2011-04-04 09:44:19 -03:00
return & state - > format ;
}
static int s5pcsis_set_fmt ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_format * fmt )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
struct csis_pix_format const * csis_fmt ;
struct v4l2_mbus_framefmt * mf ;
2013-04-23 12:40:24 -03:00
mf = __s5pcsis_get_format ( state , fh , fmt - > which ) ;
2011-04-04 09:44:19 -03:00
if ( fmt - > pad = = CSIS_PAD_SOURCE ) {
if ( mf ) {
mutex_lock ( & state - > lock ) ;
fmt - > format = * mf ;
mutex_unlock ( & state - > lock ) ;
}
return 0 ;
}
csis_fmt = s5pcsis_try_format ( & fmt - > format ) ;
if ( mf ) {
mutex_lock ( & state - > lock ) ;
* mf = fmt - > format ;
if ( fmt - > which = = V4L2_SUBDEV_FORMAT_ACTIVE )
state - > csis_fmt = csis_fmt ;
mutex_unlock ( & state - > lock ) ;
}
return 0 ;
}
static int s5pcsis_get_fmt ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh ,
struct v4l2_subdev_format * fmt )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
struct v4l2_mbus_framefmt * mf ;
2013-04-23 12:40:24 -03:00
mf = __s5pcsis_get_format ( state , fh , fmt - > which ) ;
2011-04-04 09:44:19 -03:00
if ( ! mf )
return - EINVAL ;
mutex_lock ( & state - > lock ) ;
fmt - > format = * mf ;
mutex_unlock ( & state - > lock ) ;
return 0 ;
}
2012-09-21 15:18:41 -03:00
static int s5pcsis_s_rx_buffer ( struct v4l2_subdev * sd , void * buf ,
unsigned int * size )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
unsigned long flags ;
* size = min_t ( unsigned int , * size , S5PCSIS_PKTDATA_SIZE ) ;
spin_lock_irqsave ( & state - > slock , flags ) ;
state - > pkt_buf . data = buf ;
state - > pkt_buf . len = * size ;
spin_unlock_irqrestore ( & state - > slock , flags ) ;
return 0 ;
}
2012-09-05 10:10:37 -03:00
static int s5pcsis_log_status ( struct v4l2_subdev * sd )
{
struct csis_state * state = sd_to_csis_state ( sd ) ;
2012-09-28 11:05:53 -03:00
mutex_lock ( & state - > lock ) ;
2012-09-05 10:10:37 -03:00
s5pcsis_log_counters ( state , true ) ;
2012-09-28 11:05:53 -03:00
if ( debug & & ( state - > flags & ST_POWERED ) )
dump_regs ( state , __func__ ) ;
mutex_unlock ( & state - > lock ) ;
2012-09-05 10:10:37 -03:00
return 0 ;
}
2011-11-15 15:34:06 -03:00
static int s5pcsis_open ( struct v4l2_subdev * sd , struct v4l2_subdev_fh * fh )
{
struct v4l2_mbus_framefmt * format = v4l2_subdev_get_try_format ( fh , 0 ) ;
format - > colorspace = V4L2_COLORSPACE_JPEG ;
format - > code = s5pcsis_formats [ 0 ] . code ;
format - > width = S5PCSIS_DEF_PIX_WIDTH ;
format - > height = S5PCSIS_DEF_PIX_HEIGHT ;
format - > field = V4L2_FIELD_NONE ;
return 0 ;
}
static const struct v4l2_subdev_internal_ops s5pcsis_sd_internal_ops = {
. open = s5pcsis_open ,
} ;
2011-04-04 09:44:19 -03:00
static struct v4l2_subdev_core_ops s5pcsis_core_ops = {
. s_power = s5pcsis_s_power ,
2012-09-05 10:10:37 -03:00
. log_status = s5pcsis_log_status ,
2011-04-04 09:44:19 -03:00
} ;
static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = {
. enum_mbus_code = s5pcsis_enum_mbus_code ,
. get_fmt = s5pcsis_get_fmt ,
. set_fmt = s5pcsis_set_fmt ,
} ;
static struct v4l2_subdev_video_ops s5pcsis_video_ops = {
2012-09-21 15:18:41 -03:00
. s_rx_buffer = s5pcsis_s_rx_buffer ,
2011-04-04 09:44:19 -03:00
. s_stream = s5pcsis_s_stream ,
} ;
static struct v4l2_subdev_ops s5pcsis_subdev_ops = {
. core = & s5pcsis_core_ops ,
. pad = & s5pcsis_pad_ops ,
. video = & s5pcsis_video_ops ,
} ;
static irqreturn_t s5pcsis_irq_handler ( int irq , void * dev_id )
{
struct csis_state * state = dev_id ;
2012-09-21 15:18:41 -03:00
struct csis_pktbuf * pktbuf = & state - > pkt_buf ;
2012-09-05 10:10:37 -03:00
unsigned long flags ;
u32 status ;
status = s5pcsis_read ( state , S5PCSIS_INTSRC ) ;
spin_lock_irqsave ( & state - > slock , flags ) ;
2012-09-21 15:18:41 -03:00
if ( ( status & S5PCSIS_INTSRC_NON_IMAGE_DATA ) & & pktbuf - > data ) {
u32 offset ;
if ( status & S5PCSIS_INTSRC_EVEN )
offset = S5PCSIS_PKTDATA_EVEN ;
else
offset = S5PCSIS_PKTDATA_ODD ;
memcpy ( pktbuf - > data , state - > regs + offset , pktbuf - > len ) ;
pktbuf - > data = NULL ;
rmb ( ) ;
}
2012-09-05 10:10:37 -03:00
/* Update the event/error counters */
if ( ( status & S5PCSIS_INTSRC_ERRORS ) | | debug ) {
int i ;
for ( i = 0 ; i < S5PCSIS_NUM_EVENTS ; i + + ) {
if ( ! ( status & state - > events [ i ] . mask ) )
continue ;
state - > events [ i ] . counter + + ;
v4l2_dbg ( 2 , debug , & state - > sd , " %s: %d \n " ,
state - > events [ i ] . name ,
state - > events [ i ] . counter ) ;
}
v4l2_dbg ( 2 , debug , & state - > sd , " status: %08x \n " , status ) ;
}
spin_unlock_irqrestore ( & state - > slock , flags ) ;
2011-04-04 09:44:19 -03:00
2012-09-05 10:10:37 -03:00
s5pcsis_write ( state , S5PCSIS_INTSRC , status ) ;
2011-04-04 09:44:19 -03:00
return IRQ_HANDLED ;
}
2013-03-26 08:20:30 -03:00
static int s5pcsis_parse_dt ( struct platform_device * pdev ,
struct csis_state * state )
{
struct device_node * node = pdev - > dev . of_node ;
struct v4l2_of_endpoint endpoint ;
if ( of_property_read_u32 ( node , " clock-frequency " ,
& state - > clk_frequency ) )
state - > clk_frequency = DEFAULT_SCLK_CSIS_FREQ ;
if ( of_property_read_u32 ( node , " bus-width " ,
& state - > max_num_lanes ) )
return - EINVAL ;
2014-02-10 22:01:48 +01:00
node = of_graph_get_next_endpoint ( node , NULL ) ;
2013-03-26 08:20:30 -03:00
if ( ! node ) {
dev_err ( & pdev - > dev , " No port node at %s \n " ,
2013-04-26 04:52:57 -03:00
pdev - > dev . of_node - > full_name ) ;
2013-03-26 08:20:30 -03:00
return - EINVAL ;
}
/* Get port node and validate MIPI-CSI channel id. */
v4l2_of_parse_endpoint ( node , & endpoint ) ;
2014-02-14 11:53:56 +01:00
state - > index = endpoint . base . port - FIMC_INPUT_MIPI_CSI2_0 ;
2014-08-26 11:17:16 -03:00
if ( state - > index > = CSIS_MAX_ENTITIES )
2013-03-26 08:20:30 -03:00
return - ENXIO ;
/* Get MIPI CSI-2 bus configration from the endpoint node. */
of_property_read_u32 ( node , " samsung,csis-hs-settle " ,
& state - > hs_settle ) ;
state - > wclk_ext = of_property_read_bool ( node ,
" samsung,csis-wclk " ) ;
state - > num_lanes = endpoint . bus . mipi_csi2 . num_data_lanes ;
of_node_put ( node ) ;
2013-10-16 21:58:11 +05:30
2013-03-26 08:20:30 -03:00
return 0 ;
}
2013-10-19 18:13:12 -03:00
static int s5pcsis_pm_resume ( struct device * dev , bool runtime ) ;
2013-05-31 10:38:23 -03:00
static const struct of_device_id s5pcsis_of_match [ ] ;
2012-12-21 13:17:53 -08:00
static int s5pcsis_probe ( struct platform_device * pdev )
2011-04-04 09:44:19 -03:00
{
2013-05-31 10:38:23 -03:00
const struct of_device_id * of_id ;
const struct csis_drvdata * drv_data ;
2013-03-26 08:20:30 -03:00
struct device * dev = & pdev - > dev ;
2011-04-04 09:44:19 -03:00
struct resource * mem_res ;
struct csis_state * state ;
int ret = - ENOMEM ;
2011-06-29 13:08:49 -03:00
int i ;
2011-04-04 09:44:19 -03:00
2013-03-26 08:20:30 -03:00
state = devm_kzalloc ( dev , sizeof ( * state ) , GFP_KERNEL ) ;
2011-04-04 09:44:19 -03:00
if ( ! state )
return - ENOMEM ;
mutex_init ( & state - > lock ) ;
2012-09-05 10:10:37 -03:00
spin_lock_init ( & state - > slock ) ;
2011-04-04 09:44:19 -03:00
state - > pdev = pdev ;
2014-04-15 08:35:25 -03:00
of_id = of_match_node ( s5pcsis_of_match , dev - > of_node ) ;
if ( WARN_ON ( of_id = = NULL ) )
return - EINVAL ;
2013-05-31 10:38:23 -03:00
2014-04-15 08:35:25 -03:00
drv_data = of_id - > data ;
state - > interrupt_mask = drv_data - > interrupt_mask ;
2013-05-31 10:38:23 -03:00
2014-04-15 08:35:25 -03:00
ret = s5pcsis_parse_dt ( pdev , state ) ;
2013-03-26 08:20:30 -03:00
if ( ret < 0 )
return ret ;
2011-04-04 09:44:19 -03:00
2013-03-26 08:20:30 -03:00
if ( state - > num_lanes = = 0 | | state - > num_lanes > state - > max_num_lanes ) {
dev_err ( dev , " Unsupported number of data lanes: %d (max. %d) \n " ,
state - > num_lanes , state - > max_num_lanes ) ;
2012-02-14 13:23:46 -03:00
return - EINVAL ;
2011-04-04 09:44:19 -03:00
}
2013-10-16 21:58:11 +05:30
state - > phy = devm_phy_get ( dev , " csis " ) ;
if ( IS_ERR ( state - > phy ) )
return PTR_ERR ( state - > phy ) ;
2011-04-04 09:44:19 -03:00
mem_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-03-26 08:20:30 -03:00
state - > regs = devm_ioremap_resource ( dev , mem_res ) ;
2013-01-21 06:09:07 -03:00
if ( IS_ERR ( state - > regs ) )
return PTR_ERR ( state - > regs ) ;
2011-04-04 09:44:19 -03:00
state - > irq = platform_get_irq ( pdev , 0 ) ;
if ( state - > irq < 0 ) {
2013-03-26 08:20:30 -03:00
dev_err ( dev , " Failed to get irq \n " ) ;
2012-02-14 13:23:46 -03:00
return state - > irq ;
2011-04-04 09:44:19 -03:00
}
2011-06-29 13:08:49 -03:00
for ( i = 0 ; i < CSIS_NUM_SUPPLIES ; i + + )
state - > supplies [ i ] . supply = csis_supply_name [ i ] ;
2013-03-26 08:20:30 -03:00
ret = devm_regulator_bulk_get ( dev , CSIS_NUM_SUPPLIES ,
2011-06-29 13:08:49 -03:00
state - > supplies ) ;
2012-02-14 13:23:46 -03:00
if ( ret )
return ret ;
ret = s5pcsis_clk_get ( state ) ;
2013-01-29 06:52:29 -03:00
if ( ret < 0 )
return ret ;
2011-04-04 09:44:19 -03:00
2013-03-26 08:20:30 -03:00
if ( state - > clk_frequency )
2013-01-29 06:52:29 -03:00
ret = clk_set_rate ( state - > clock [ CSIS_CLK_MUX ] ,
2013-03-26 08:20:30 -03:00
state - > clk_frequency ) ;
2012-02-14 13:23:46 -03:00
else
2013-03-26 08:20:30 -03:00
dev_WARN ( dev , " No clock frequency specified! \n " ) ;
2013-01-29 06:52:29 -03:00
if ( ret < 0 )
goto e_clkput ;
ret = clk_enable ( state - > clock [ CSIS_CLK_MUX ] ) ;
if ( ret < 0 )
goto e_clkput ;
2012-02-14 13:23:46 -03:00
2013-03-26 08:20:30 -03:00
ret = devm_request_irq ( dev , state - > irq , s5pcsis_irq_handler ,
0 , dev_name ( dev ) , state ) ;
2011-04-04 09:44:19 -03:00
if ( ret ) {
2013-03-26 08:20:30 -03:00
dev_err ( dev , " Interrupt request failed \n " ) ;
2013-01-18 13:52:38 -03:00
goto e_clkdis ;
2011-04-04 09:44:19 -03:00
}
v4l2_subdev_init ( & state - > sd , & s5pcsis_subdev_ops ) ;
state - > sd . owner = THIS_MODULE ;
2013-03-26 08:20:30 -03:00
snprintf ( state - > sd . name , sizeof ( state - > sd . name ) , " %s.%d " ,
CSIS_SUBDEV_NAME , state - > index ) ;
2011-11-15 15:34:06 -03:00
state - > sd . flags | = V4L2_SUBDEV_FL_HAS_DEVNODE ;
2011-04-04 09:44:19 -03:00
state - > csis_fmt = & s5pcsis_formats [ 0 ] ;
2011-11-15 15:34:06 -03:00
state - > format . code = s5pcsis_formats [ 0 ] . code ;
state - > format . width = S5PCSIS_DEF_PIX_WIDTH ;
state - > format . height = S5PCSIS_DEF_PIX_HEIGHT ;
2011-04-04 09:44:19 -03:00
state - > pads [ CSIS_PAD_SINK ] . flags = MEDIA_PAD_FL_SINK ;
state - > pads [ CSIS_PAD_SOURCE ] . flags = MEDIA_PAD_FL_SOURCE ;
ret = media_entity_init ( & state - > sd . entity ,
CSIS_PADS_NUM , state - > pads , 0 ) ;
if ( ret < 0 )
2013-01-18 13:52:38 -03:00
goto e_clkdis ;
2011-04-04 09:44:19 -03:00
/* This allows to retrieve the platform device id by the host driver */
v4l2_set_subdevdata ( & state - > sd , pdev ) ;
/* .. and a pointer to the subdev. */
platform_set_drvdata ( pdev , & state - > sd ) ;
2012-09-05 10:10:37 -03:00
memcpy ( state - > events , s5pcsis_events , sizeof ( state - > events ) ) ;
2013-10-19 18:13:12 -03:00
2013-03-26 08:20:30 -03:00
pm_runtime_enable ( dev ) ;
2013-10-19 18:13:12 -03:00
if ( ! pm_runtime_enabled ( dev ) ) {
ret = s5pcsis_pm_resume ( dev , true ) ;
if ( ret < 0 )
goto e_m_ent ;
}
2012-09-05 10:10:37 -03:00
2013-03-26 08:20:30 -03:00
dev_info ( & pdev - > dev , " lanes: %d, hs_settle: %d, wclk: %d, freq: %u \n " ,
state - > num_lanes , state - > hs_settle , state - > wclk_ext ,
state - > clk_frequency ) ;
2011-04-04 09:44:19 -03:00
return 0 ;
2013-10-19 18:13:12 -03:00
e_m_ent :
media_entity_cleanup ( & state - > sd . entity ) ;
2013-01-18 13:52:38 -03:00
e_clkdis :
2011-04-04 09:44:19 -03:00
clk_disable ( state - > clock [ CSIS_CLK_MUX ] ) ;
2013-01-18 13:52:38 -03:00
e_clkput :
2011-04-04 09:44:19 -03:00
s5pcsis_clk_put ( state ) ;
return ret ;
}
2011-07-07 12:13:25 -03:00
static int s5pcsis_pm_suspend ( struct device * dev , bool runtime )
2011-04-04 09:44:19 -03:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct v4l2_subdev * sd = platform_get_drvdata ( pdev ) ;
struct csis_state * state = sd_to_csis_state ( sd ) ;
2011-05-18 12:06:40 -03:00
int ret = 0 ;
2011-04-04 09:44:19 -03:00
v4l2_dbg ( 1 , debug , sd , " %s: flags: 0x%x \n " ,
__func__ , state - > flags ) ;
mutex_lock ( & state - > lock ) ;
if ( state - > flags & ST_POWERED ) {
s5pcsis_stop_stream ( state ) ;
2013-10-16 21:58:11 +05:30
ret = phy_power_off ( state - > phy ) ;
2011-04-04 09:44:19 -03:00
if ( ret )
goto unlock ;
2011-06-29 13:08:49 -03:00
ret = regulator_bulk_disable ( CSIS_NUM_SUPPLIES ,
state - > supplies ) ;
if ( ret )
goto unlock ;
2011-04-04 09:44:19 -03:00
clk_disable ( state - > clock [ CSIS_CLK_GATE ] ) ;
state - > flags & = ~ ST_POWERED ;
2011-07-07 12:13:25 -03:00
if ( ! runtime )
state - > flags | = ST_SUSPENDED ;
2011-04-04 09:44:19 -03:00
}
unlock :
mutex_unlock ( & state - > lock ) ;
return ret ? - EAGAIN : 0 ;
}
2011-07-07 12:13:25 -03:00
static int s5pcsis_pm_resume ( struct device * dev , bool runtime )
2011-04-04 09:44:19 -03:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct v4l2_subdev * sd = platform_get_drvdata ( pdev ) ;
struct csis_state * state = sd_to_csis_state ( sd ) ;
int ret = 0 ;
v4l2_dbg ( 1 , debug , sd , " %s: flags: 0x%x \n " ,
__func__ , state - > flags ) ;
mutex_lock ( & state - > lock ) ;
2011-07-07 12:13:25 -03:00
if ( ! runtime & & ! ( state - > flags & ST_SUSPENDED ) )
2011-04-04 09:44:19 -03:00
goto unlock ;
if ( ! ( state - > flags & ST_POWERED ) ) {
2011-06-29 13:08:49 -03:00
ret = regulator_bulk_enable ( CSIS_NUM_SUPPLIES ,
state - > supplies ) ;
2011-04-04 09:44:19 -03:00
if ( ret )
goto unlock ;
2013-10-16 21:58:11 +05:30
ret = phy_power_on ( state - > phy ) ;
2011-04-04 09:44:19 -03:00
if ( ! ret ) {
state - > flags | = ST_POWERED ;
2011-06-29 13:08:49 -03:00
} else {
regulator_bulk_disable ( CSIS_NUM_SUPPLIES ,
state - > supplies ) ;
2011-04-04 09:44:19 -03:00
goto unlock ;
}
clk_enable ( state - > clock [ CSIS_CLK_GATE ] ) ;
}
if ( state - > flags & ST_STREAMING )
s5pcsis_start_stream ( state ) ;
state - > flags & = ~ ST_SUSPENDED ;
unlock :
mutex_unlock ( & state - > lock ) ;
return ret ? - EAGAIN : 0 ;
}
# ifdef CONFIG_PM_SLEEP
2011-07-07 12:13:25 -03:00
static int s5pcsis_suspend ( struct device * dev )
2011-04-04 09:44:19 -03:00
{
2011-07-07 12:13:25 -03:00
return s5pcsis_pm_suspend ( dev , false ) ;
2011-04-04 09:44:19 -03:00
}
2011-07-07 12:13:25 -03:00
static int s5pcsis_resume ( struct device * dev )
2011-04-04 09:44:19 -03:00
{
2011-07-07 12:13:25 -03:00
return s5pcsis_pm_resume ( dev , false ) ;
}
# endif
2011-04-04 09:44:19 -03:00
2014-12-04 01:10:10 +01:00
# ifdef CONFIG_PM
2011-07-07 12:13:25 -03:00
static int s5pcsis_runtime_suspend ( struct device * dev )
{
return s5pcsis_pm_suspend ( dev , true ) ;
}
2011-04-04 09:44:19 -03:00
2011-07-07 12:13:25 -03:00
static int s5pcsis_runtime_resume ( struct device * dev )
{
return s5pcsis_pm_resume ( dev , true ) ;
2011-04-04 09:44:19 -03:00
}
# endif
2012-12-21 13:17:53 -08:00
static int s5pcsis_remove ( struct platform_device * pdev )
2011-04-04 09:44:19 -03:00
{
struct v4l2_subdev * sd = platform_get_drvdata ( pdev ) ;
struct csis_state * state = sd_to_csis_state ( sd ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2013-10-19 18:13:12 -03:00
s5pcsis_pm_suspend ( & pdev - > dev , true ) ;
2011-04-04 09:44:19 -03:00
clk_disable ( state - > clock [ CSIS_CLK_MUX ] ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
s5pcsis_clk_put ( state ) ;
media_entity_cleanup ( & state - > sd . entity ) ;
return 0 ;
}
static const struct dev_pm_ops s5pcsis_pm_ops = {
2011-07-07 12:13:25 -03:00
SET_RUNTIME_PM_OPS ( s5pcsis_runtime_suspend , s5pcsis_runtime_resume ,
NULL )
SET_SYSTEM_SLEEP_PM_OPS ( s5pcsis_suspend , s5pcsis_resume )
2011-04-04 09:44:19 -03:00
} ;
2013-05-31 10:38:23 -03:00
static const struct csis_drvdata exynos4_csis_drvdata = {
. interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL ,
} ;
static const struct csis_drvdata exynos5_csis_drvdata = {
. interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL ,
} ;
2013-03-26 08:20:30 -03:00
static const struct of_device_id s5pcsis_of_match [ ] = {
2013-05-31 10:38:23 -03:00
{
. compatible = " samsung,s5pv210-csis " ,
. data = & exynos4_csis_drvdata ,
} , {
. compatible = " samsung,exynos4210-csis " ,
. data = & exynos4_csis_drvdata ,
} , {
. compatible = " samsung,exynos5250-csis " ,
. data = & exynos5_csis_drvdata ,
} ,
2013-03-26 08:20:30 -03:00
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , s5pcsis_of_match ) ;
2011-04-04 09:44:19 -03:00
static struct platform_driver s5pcsis_driver = {
. probe = s5pcsis_probe ,
2012-12-21 13:17:53 -08:00
. remove = s5pcsis_remove ,
2011-04-04 09:44:19 -03:00
. driver = {
2013-03-26 08:20:30 -03:00
. of_match_table = s5pcsis_of_match ,
. name = CSIS_DRIVER_NAME ,
. pm = & s5pcsis_pm_ops ,
2011-04-04 09:44:19 -03:00
} ,
} ;
2012-03-21 09:58:09 -03:00
module_platform_driver ( s5pcsis_driver ) ;
2011-04-04 09:44:19 -03:00
MODULE_AUTHOR ( " Sylwester Nawrocki <s.nawrocki@samsung.com> " ) ;
2012-03-21 09:58:09 -03:00
MODULE_DESCRIPTION ( " Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver " ) ;
2011-04-04 09:44:19 -03:00
MODULE_LICENSE ( " GPL " ) ;