2019-05-27 08:55:19 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2011-06-11 14:46:49 -03:00
/*
* Support for the camera device found on Marvell MMP processors ; known
* to work with the Armada 610 as used in the OLPC 1.75 system .
*
* Copyright 2011 Jonathan Corbet < corbet @ lwn . net >
2019-05-28 05:07:30 -04:00
* Copyright 2018 Lubomir Rintel < lkundrak @ v3 . sk >
2011-06-11 14:46:49 -03:00
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/spinlock.h>
# include <linux/slab.h>
# include <linux/videodev2.h>
# include <media/v4l2-device.h>
2015-11-16 08:35:53 -02:00
# include <linux/platform_data/media/mmp-camera.h>
2011-06-11 14:46:49 -03:00
# include <linux/device.h>
2019-05-28 05:07:29 -04:00
# include <linux/of.h>
# include <linux/of_platform.h>
2011-06-11 14:46:49 -03:00
# include <linux/platform_device.h>
2020-08-18 00:08:50 +02:00
# include <linux/pm_runtime.h>
2011-06-11 14:46:49 -03:00
# include <linux/io.h>
# include <linux/list.h>
2011-12-30 14:13:41 -03:00
# include <linux/pm.h>
2013-07-03 01:55:58 -03:00
# include <linux/clk.h>
2011-06-11 14:46:49 -03:00
# include "mcam-core.h"
2011-09-07 05:36:46 -03:00
MODULE_ALIAS ( " platform:mmp-camera " ) ;
2011-06-11 14:46:49 -03:00
MODULE_AUTHOR ( " Jonathan Corbet <corbet@lwn.net> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2019-05-28 05:07:28 -04:00
static char * mcam_clks [ ] = { " axi " , " func " , " phy " } ;
2013-07-03 01:55:59 -03:00
2011-06-11 14:46:49 -03:00
struct mmp_camera {
struct platform_device * pdev ;
struct mcam_camera mcam ;
struct list_head devlist ;
2013-07-03 01:55:58 -03:00
struct clk * mipi_clk ;
2011-06-11 14:46:49 -03:00
int irq ;
} ;
static inline struct mmp_camera * mcam_to_cam ( struct mcam_camera * mcam )
{
return container_of ( mcam , struct mmp_camera , mcam ) ;
}
2013-07-03 01:55:58 -03:00
/*
* calc the dphy register values
* There are three dphy registers being used .
* dphy [ 0 ] - CSI2_DPHY3
* dphy [ 1 ] - CSI2_DPHY5
* dphy [ 2 ] - CSI2_DPHY6
* CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
* or be calculated dynamically
*/
2018-04-05 12:14:59 -04:00
static void mmpcam_calc_dphy ( struct mcam_camera * mcam )
2013-07-03 01:55:58 -03:00
{
struct mmp_camera * cam = mcam_to_cam ( mcam ) ;
struct mmp_camera_platform_data * pdata = cam - > pdev - > dev . platform_data ;
struct device * dev = & cam - > pdev - > dev ;
unsigned long tx_clk_esc ;
/*
* If CSI2_DPHY3 is calculated dynamically ,
* pdata - > lane_clk should be already set
* either in the board driver statically
* or in the sensor driver dynamically .
*/
/*
* dphy [ 0 ] - CSI2_DPHY3 :
* bit 0 ~ bit 7 : HS Term Enable .
* defines the time that the DPHY
* wait before enabling the data
* lane termination after detecting
* that the sensor has driven the data
* lanes to the LP00 bridge state .
* The value is calculated by :
* ( Max T ( D_TERM_EN ) / Period ( DDR ) ) - 1
* bit 8 ~ bit 15 : HS_SETTLE
* Time interval during which the HS
* receiver shall ignore any Data Lane
2019-02-18 14:29:00 -05:00
* HS transitions .
* The value has been calibrated on
2013-07-03 01:55:58 -03:00
* different boards . It seems to work well .
*
* More detail please refer
* MIPI Alliance Spectification for D - PHY
* document for explanation of HS - SETTLE
* and D - TERM - EN .
*/
switch ( pdata - > dphy3_algo ) {
case DPHY3_ALGO_PXA910 :
/*
* Calculate CSI2_DPHY3 algo for PXA910
*/
pdata - > dphy [ 0 ] =
( ( ( 1 + ( pdata - > lane_clk * 80 ) / 1000 ) & 0xff ) < < 8 )
| ( 1 + pdata - > lane_clk * 35 / 1000 ) ;
break ;
case DPHY3_ALGO_PXA2128 :
/*
* Calculate CSI2_DPHY3 algo for PXA2128
*/
pdata - > dphy [ 0 ] =
( ( ( 2 + ( pdata - > lane_clk * 110 ) / 1000 ) & 0xff ) < < 8 )
| ( 1 + pdata - > lane_clk * 35 / 1000 ) ;
break ;
default :
/*
* Use default CSI2_DPHY3 value for PXA688 / PXA988
*/
dev_dbg ( dev , " camera: use the default CSI2_DPHY3 value \n " ) ;
}
/*
* mipi_clk will never be changed , it is a fixed value on MMP
*/
if ( IS_ERR ( cam - > mipi_clk ) )
return ;
/* get the escape clk, this is hard coded */
2013-11-05 05:29:07 -03:00
clk_prepare_enable ( cam - > mipi_clk ) ;
2013-07-03 01:55:58 -03:00
tx_clk_esc = ( clk_get_rate ( cam - > mipi_clk ) / 1000000 ) / 12 ;
2013-11-05 05:29:07 -03:00
clk_disable_unprepare ( cam - > mipi_clk ) ;
2013-07-03 01:55:58 -03:00
/*
* dphy [ 2 ] - CSI2_DPHY6 :
* bit 0 ~ bit 7 : CK Term Enable
* Time for the Clock Lane receiver to enable the HS line
* termination . The value is calculated similarly with
* HS Term Enable
* bit 8 ~ bit 15 : CK Settle
* Time interval during which the HS receiver shall ignore
* any Clock Lane HS transitions .
* The value is calibrated on the boards .
*/
pdata - > dphy [ 2 ] =
( ( ( ( 534 * tx_clk_esc ) / 2000 - 1 ) & 0xff ) < < 8 )
| ( ( ( 38 * tx_clk_esc ) / 1000 - 1 ) & 0xff ) ;
dev_dbg ( dev , " camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x \n " ,
pdata - > dphy [ 0 ] , pdata - > dphy [ 1 ] , pdata - > dphy [ 2 ] ) ;
}
2011-06-11 14:46:49 -03:00
static irqreturn_t mmpcam_irq ( int irq , void * data )
{
struct mcam_camera * mcam = data ;
unsigned int irqs , handled ;
spin_lock ( & mcam - > dev_lock ) ;
irqs = mcam_reg_read ( mcam , REG_IRQSTAT ) ;
handled = mccic_irq ( mcam , irqs ) ;
spin_unlock ( & mcam - > dev_lock ) ;
return IRQ_RETVAL ( handled ) ;
}
2013-07-03 01:55:59 -03:00
static void mcam_init_clk ( struct mcam_camera * mcam )
{
unsigned int i ;
for ( i = 0 ; i < NR_MCAM_CLK ; i + + ) {
if ( mcam_clks [ i ] ! = NULL ) {
/* Some clks are not necessary on some boards
* We still try to run even it fails getting clk
*/
mcam - > clk [ i ] = devm_clk_get ( mcam - > dev , mcam_clks [ i ] ) ;
if ( IS_ERR ( mcam - > clk [ i ] ) )
dev_warn ( mcam - > dev , " Could not get clk: %s \n " ,
mcam_clks [ i ] ) ;
}
}
}
2011-06-11 14:46:49 -03:00
static int mmpcam_probe ( struct platform_device * pdev )
{
struct mmp_camera * cam ;
struct mcam_camera * mcam ;
struct resource * res ;
2019-05-28 05:07:30 -04:00
struct fwnode_handle * ep ;
2011-06-11 14:46:49 -03:00
struct mmp_camera_platform_data * pdata ;
2021-01-18 02:52:51 +01:00
struct v4l2_async_subdev * asd ;
2011-06-11 14:46:49 -03:00
int ret ;
2013-07-03 01:56:04 -03:00
cam = devm_kzalloc ( & pdev - > dev , sizeof ( * cam ) , GFP_KERNEL ) ;
2011-06-11 14:46:49 -03:00
if ( cam = = NULL )
return - ENOMEM ;
2020-08-18 00:08:48 +02:00
platform_set_drvdata ( pdev , cam ) ;
2011-06-11 14:46:49 -03:00
cam - > pdev = pdev ;
INIT_LIST_HEAD ( & cam - > devlist ) ;
mcam = & cam - > mcam ;
2013-07-03 01:55:58 -03:00
mcam - > calc_dphy = mmpcam_calc_dphy ;
2011-06-11 14:46:49 -03:00
mcam - > dev = & pdev - > dev ;
2019-05-28 05:07:29 -04:00
pdata = pdev - > dev . platform_data ;
if ( pdata ) {
mcam - > mclk_src = pdata - > mclk_src ;
mcam - > mclk_div = pdata - > mclk_div ;
mcam - > bus_type = pdata - > bus_type ;
mcam - > dphy = pdata - > dphy ;
mcam - > lane = pdata - > lane ;
} else {
/*
* These are values that used to be hardcoded in mcam - core and
* work well on a OLPC XO 1.75 with a parallel bus sensor .
* If it turns out other setups make sense , the values should
* be obtained from the device tree .
*/
mcam - > mclk_src = 3 ;
mcam - > mclk_div = 2 ;
}
2018-07-03 17:19:27 -04:00
if ( mcam - > bus_type = = V4L2_MBUS_CSI2_DPHY ) {
2013-11-05 05:29:07 -03:00
cam - > mipi_clk = devm_clk_get ( mcam - > dev , " mipi " ) ;
if ( ( IS_ERR ( cam - > mipi_clk ) & & mcam - > dphy [ 2 ] = = 0 ) )
return PTR_ERR ( cam - > mipi_clk ) ;
}
2013-07-03 01:55:58 -03:00
mcam - > mipi_enabled = false ;
2013-05-29 06:59:44 -03:00
mcam - > chip_id = MCAM_ARMADA610 ;
2011-06-30 17:05:28 -03:00
mcam - > buffer_mode = B_DMA_sg ;
2018-09-10 08:19:14 -04:00
strscpy ( mcam - > bus_info , " platform:mmp-camera " , sizeof ( mcam - > bus_info ) ) ;
2011-06-11 14:46:49 -03:00
spin_lock_init ( & mcam - > dev_lock ) ;
/*
* Get our I / O memory .
*/
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-07-03 01:56:04 -03:00
mcam - > regs = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( mcam - > regs ) )
return PTR_ERR ( mcam - > regs ) ;
2013-05-29 07:00:02 -03:00
mcam - > regs_size = resource_size ( res ) ;
2013-07-03 01:55:59 -03:00
mcam_init_clk ( mcam ) ;
2019-05-28 05:07:30 -04:00
/*
* Create a match of the sensor against its OF node .
*/
ep = fwnode_graph_get_next_endpoint ( of_fwnode_handle ( pdev - > dev . of_node ) ,
NULL ) ;
if ( ! ep )
return - ENODEV ;
2021-01-18 02:52:51 +01:00
v4l2_async_notifier_init ( & mcam - > notifier ) ;
2019-05-28 05:07:30 -04:00
2021-01-18 02:52:58 +01:00
asd = v4l2_async_notifier_add_fwnode_remote_subdev ( & mcam - > notifier , ep ,
struct v4l2_async_subdev ) ;
2019-05-28 05:07:30 -04:00
fwnode_handle_put ( ep ) ;
2021-01-18 02:52:51 +01:00
if ( IS_ERR ( asd ) ) {
ret = PTR_ERR ( asd ) ;
goto out ;
}
2019-05-28 05:07:30 -04:00
2011-06-11 14:46:49 -03:00
/*
2019-05-28 05:07:31 -04:00
* Register the device with the core .
2011-06-11 14:46:49 -03:00
*/
2013-07-03 01:55:58 -03:00
ret = mccic_register ( mcam ) ;
if ( ret )
2019-05-28 05:07:31 -04:00
return ret ;
/*
* Add OF clock provider .
*/
ret = of_clk_add_provider ( pdev - > dev . of_node , of_clk_src_simple_get ,
mcam - > mclk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " can't add DT clock provider \n " ) ;
goto out ;
}
2019-05-28 05:07:30 -04:00
2011-06-11 14:46:49 -03:00
/*
* Finally , set up our IRQ now that the core is ready to
* deal with it .
*/
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( res = = NULL ) {
ret = - ENODEV ;
2019-05-28 05:07:31 -04:00
goto out ;
2011-06-11 14:46:49 -03:00
}
cam - > irq = res - > start ;
2013-07-03 01:56:04 -03:00
ret = devm_request_irq ( & pdev - > dev , cam - > irq , mmpcam_irq , IRQF_SHARED ,
" mmp-camera " , mcam ) ;
2020-08-18 00:08:48 +02:00
if ( ret )
goto out ;
2011-06-11 14:46:49 -03:00
2020-08-18 00:08:50 +02:00
pm_runtime_enable ( & pdev - > dev ) ;
2020-08-18 00:08:48 +02:00
return 0 ;
2019-05-28 05:07:31 -04:00
out :
2011-06-11 14:46:49 -03:00
mccic_shutdown ( mcam ) ;
2019-05-28 05:07:31 -04:00
2011-06-11 14:46:49 -03:00
return ret ;
}
static int mmpcam_remove ( struct mmp_camera * cam )
{
struct mcam_camera * mcam = & cam - > mcam ;
mccic_shutdown ( mcam ) ;
2020-08-18 00:08:50 +02:00
pm_runtime_force_suspend ( mcam - > dev ) ;
2011-06-11 14:46:49 -03:00
return 0 ;
}
static int mmpcam_platform_remove ( struct platform_device * pdev )
{
2020-08-18 00:08:48 +02:00
struct mmp_camera * cam = platform_get_drvdata ( pdev ) ;
2011-06-11 14:46:49 -03:00
if ( cam = = NULL )
return - ENODEV ;
return mmpcam_remove ( cam ) ;
}
2011-12-30 14:13:41 -03:00
/*
* Suspend / resume support .
*/
2020-09-11 13:27:07 +02:00
static int __maybe_unused mmpcam_runtime_resume ( struct device * dev )
2020-08-18 00:08:50 +02:00
{
struct mmp_camera * cam = dev_get_drvdata ( dev ) ;
struct mcam_camera * mcam = & cam - > mcam ;
unsigned int i ;
for ( i = 0 ; i < NR_MCAM_CLK ; i + + ) {
if ( ! IS_ERR ( mcam - > clk [ i ] ) )
clk_prepare_enable ( mcam - > clk [ i ] ) ;
}
return 0 ;
}
2020-09-11 13:27:07 +02:00
static int __maybe_unused mmpcam_runtime_suspend ( struct device * dev )
2020-08-18 00:08:50 +02:00
{
struct mmp_camera * cam = dev_get_drvdata ( dev ) ;
struct mcam_camera * mcam = & cam - > mcam ;
int i ;
for ( i = NR_MCAM_CLK - 1 ; i > = 0 ; i - - ) {
if ( ! IS_ERR ( mcam - > clk [ i ] ) )
clk_disable_unprepare ( mcam - > clk [ i ] ) ;
}
return 0 ;
}
2020-09-09 13:29:21 +02:00
static int __maybe_unused mmpcam_suspend ( struct device * dev )
2011-12-30 14:13:41 -03:00
{
2020-08-18 00:08:49 +02:00
struct mmp_camera * cam = dev_get_drvdata ( dev ) ;
2011-12-30 14:13:41 -03:00
2020-08-18 00:08:50 +02:00
if ( ! pm_runtime_suspended ( dev ) )
mccic_suspend ( & cam - > mcam ) ;
2011-12-30 14:13:41 -03:00
return 0 ;
}
2020-09-09 13:29:21 +02:00
static int __maybe_unused mmpcam_resume ( struct device * dev )
2011-12-30 14:13:41 -03:00
{
2020-08-18 00:08:49 +02:00
struct mmp_camera * cam = dev_get_drvdata ( dev ) ;
2011-12-30 14:13:41 -03:00
2020-08-18 00:08:50 +02:00
if ( ! pm_runtime_suspended ( dev ) )
return mccic_resume ( & cam - > mcam ) ;
return 0 ;
2011-12-30 14:13:41 -03:00
}
2020-08-18 00:08:50 +02:00
static const struct dev_pm_ops mmpcam_pm_ops = {
SET_RUNTIME_PM_OPS ( mmpcam_runtime_suspend , mmpcam_runtime_resume , NULL )
SET_SYSTEM_SLEEP_PM_OPS ( mmpcam_suspend , mmpcam_resume )
} ;
2011-12-30 14:13:41 -03:00
2019-05-28 05:07:29 -04:00
static const struct of_device_id mmpcam_of_match [ ] = {
{ . compatible = " marvell,mmp2-ccic " , } ,
{ } ,
} ;
2019-07-22 07:33:10 -04:00
MODULE_DEVICE_TABLE ( of , mmpcam_of_match ) ;
2011-06-11 14:46:49 -03:00
static struct platform_driver mmpcam_driver = {
. probe = mmpcam_probe ,
. remove = mmpcam_platform_remove ,
. driver = {
. name = " mmp-camera " ,
2019-05-28 05:07:29 -04:00
. of_match_table = of_match_ptr ( mmpcam_of_match ) ,
2020-08-18 00:08:49 +02:00
. pm = & mmpcam_pm_ops ,
2011-06-11 14:46:49 -03:00
}
} ;
2020-08-18 00:08:48 +02:00
module_platform_driver ( mmpcam_driver ) ;