2017-04-07 14:55:19 -03:00
/*
* vimc - sensor . c Virtual Media Controller Driver
*
* Copyright ( C ) 2015 - 2017 Helen Koike < helen . fornazier @ gmail . com >
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
2017-06-19 14:00:18 -03:00
# include <linux/component.h>
2017-04-07 14:55:19 -03:00
# include <linux/freezer.h>
# include <linux/kthread.h>
2017-06-19 14:00:18 -03:00
# include <linux/module.h>
2018-06-19 22:47:28 -07:00
# include <linux/mod_devicetable.h>
2017-06-19 14:00:18 -03:00
# include <linux/platform_device.h>
2017-04-07 14:55:19 -03:00
# include <linux/v4l2-mediabus.h>
# include <linux/vmalloc.h>
2017-11-06 05:20:01 -05:00
# include <media/v4l2-ctrls.h>
2018-02-02 08:00:32 -05:00
# include <media/v4l2-event.h>
2017-04-07 14:55:19 -03:00
# include <media/v4l2-subdev.h>
2017-10-09 06:02:25 -04:00
# include <media/tpg/v4l2-tpg.h>
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:18 -03:00
# include "vimc-common.h"
# define VIMC_SEN_DRV_NAME "vimc-sensor"
2017-04-07 14:55:19 -03:00
struct vimc_sen_device {
struct vimc_ent_device ved ;
struct v4l2_subdev sd ;
2017-06-19 14:00:18 -03:00
struct device * dev ;
2017-06-19 14:00:10 -03:00
struct tpg_data tpg ;
2017-04-07 14:55:19 -03:00
struct task_struct * kthread_sen ;
u8 * frame ;
/* The active format */
struct v4l2_mbus_framefmt mbus_format ;
2017-11-06 05:20:01 -05:00
struct v4l2_ctrl_handler hdl ;
2017-04-07 14:55:19 -03:00
} ;
2017-06-19 14:00:16 -03:00
static const struct v4l2_mbus_framefmt fmt_default = {
. width = 640 ,
. height = 480 ,
. code = MEDIA_BUS_FMT_RGB888_1X24 ,
. field = V4L2_FIELD_NONE ,
. colorspace = V4L2_COLORSPACE_DEFAULT ,
} ;
static int vimc_sen_init_cfg ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg )
{
unsigned int i ;
for ( i = 0 ; i < sd - > entity . num_pads ; i + + ) {
struct v4l2_mbus_framefmt * mf ;
mf = v4l2_subdev_get_try_format ( sd , cfg , i ) ;
* mf = fmt_default ;
}
return 0 ;
}
2017-04-07 14:55:19 -03:00
static int vimc_sen_enum_mbus_code ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_mbus_code_enum * code )
{
2017-06-19 14:00:16 -03:00
const struct vimc_pix_map * vpix = vimc_pix_map_by_index ( code - > index ) ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:16 -03:00
if ( ! vpix )
2017-04-07 14:55:19 -03:00
return - EINVAL ;
2017-06-19 14:00:16 -03:00
code - > code = vpix - > code ;
2017-04-07 14:55:19 -03:00
return 0 ;
}
static int vimc_sen_enum_frame_size ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_frame_size_enum * fse )
{
2017-06-19 14:00:16 -03:00
const struct vimc_pix_map * vpix ;
2017-04-07 14:55:19 -03:00
if ( fse - > index )
return - EINVAL ;
2017-06-19 14:00:16 -03:00
/* Only accept code in the pix map table */
vpix = vimc_pix_map_by_code ( fse - > code ) ;
if ( ! vpix )
2017-04-07 14:55:19 -03:00
return - EINVAL ;
2017-06-19 14:00:16 -03:00
fse - > min_width = VIMC_FRAME_MIN_WIDTH ;
fse - > max_width = VIMC_FRAME_MAX_WIDTH ;
fse - > min_height = VIMC_FRAME_MIN_HEIGHT ;
fse - > max_height = VIMC_FRAME_MAX_HEIGHT ;
2017-04-07 14:55:19 -03:00
return 0 ;
}
static int vimc_sen_get_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
2017-06-19 14:00:16 -03:00
struct v4l2_subdev_format * fmt )
2017-04-07 14:55:19 -03:00
{
struct vimc_sen_device * vsen =
container_of ( sd , struct vimc_sen_device , sd ) ;
2017-06-19 14:00:16 -03:00
fmt - > format = fmt - > which = = V4L2_SUBDEV_FORMAT_TRY ?
* v4l2_subdev_get_try_format ( sd , cfg , fmt - > pad ) :
vsen - > mbus_format ;
2017-04-07 14:55:19 -03:00
return 0 ;
}
2017-06-19 14:00:10 -03:00
static void vimc_sen_tpg_s_format ( struct vimc_sen_device * vsen )
{
const struct vimc_pix_map * vpix =
vimc_pix_map_by_code ( vsen - > mbus_format . code ) ;
tpg_reset_source ( & vsen - > tpg , vsen - > mbus_format . width ,
vsen - > mbus_format . height , vsen - > mbus_format . field ) ;
tpg_s_bytesperline ( & vsen - > tpg , 0 , vsen - > mbus_format . width * vpix - > bpp ) ;
tpg_s_buf_height ( & vsen - > tpg , vsen - > mbus_format . height ) ;
tpg_s_fourcc ( & vsen - > tpg , vpix - > pixelformat ) ;
/* TODO: add support for V4L2_FIELD_ALTERNATE */
tpg_s_field ( & vsen - > tpg , vsen - > mbus_format . field , false ) ;
tpg_s_colorspace ( & vsen - > tpg , vsen - > mbus_format . colorspace ) ;
tpg_s_ycbcr_enc ( & vsen - > tpg , vsen - > mbus_format . ycbcr_enc ) ;
tpg_s_quantization ( & vsen - > tpg , vsen - > mbus_format . quantization ) ;
tpg_s_xfer_func ( & vsen - > tpg , vsen - > mbus_format . xfer_func ) ;
}
2017-06-19 14:00:16 -03:00
static void vimc_sen_adjust_fmt ( struct v4l2_mbus_framefmt * fmt )
{
const struct vimc_pix_map * vpix ;
/* Only accept code in the pix map table */
vpix = vimc_pix_map_by_code ( fmt - > code ) ;
if ( ! vpix )
fmt - > code = fmt_default . code ;
fmt - > width = clamp_t ( u32 , fmt - > width , VIMC_FRAME_MIN_WIDTH ,
VIMC_FRAME_MAX_WIDTH ) & ~ 1 ;
fmt - > height = clamp_t ( u32 , fmt - > height , VIMC_FRAME_MIN_HEIGHT ,
VIMC_FRAME_MAX_HEIGHT ) & ~ 1 ;
/* TODO: add support for V4L2_FIELD_ALTERNATE */
if ( fmt - > field = = V4L2_FIELD_ANY | | fmt - > field = = V4L2_FIELD_ALTERNATE )
fmt - > field = fmt_default . field ;
vimc_colorimetry_clamp ( fmt ) ;
}
static int vimc_sen_set_fmt ( struct v4l2_subdev * sd ,
struct v4l2_subdev_pad_config * cfg ,
struct v4l2_subdev_format * fmt )
{
struct vimc_sen_device * vsen = v4l2_get_subdevdata ( sd ) ;
struct v4l2_mbus_framefmt * mf ;
if ( fmt - > which = = V4L2_SUBDEV_FORMAT_ACTIVE ) {
/* Do not change the format while stream is on */
if ( vsen - > frame )
return - EBUSY ;
mf = & vsen - > mbus_format ;
} else {
mf = v4l2_subdev_get_try_format ( sd , cfg , fmt - > pad ) ;
}
/* Set the new format */
vimc_sen_adjust_fmt ( & fmt - > format ) ;
2017-06-19 14:00:18 -03:00
dev_dbg ( vsen - > dev , " %s: format update: "
2017-06-19 14:00:16 -03:00
" old:%dx%d (0x%x, %d, %d, %d, %d) "
" new:%dx%d (0x%x, %d, %d, %d, %d) \n " , vsen - > sd . name ,
/* old */
mf - > width , mf - > height , mf - > code ,
mf - > colorspace , mf - > quantization ,
mf - > xfer_func , mf - > ycbcr_enc ,
/* new */
fmt - > format . width , fmt - > format . height , fmt - > format . code ,
fmt - > format . colorspace , fmt - > format . quantization ,
fmt - > format . xfer_func , fmt - > format . ycbcr_enc ) ;
* mf = fmt - > format ;
return 0 ;
}
2017-04-07 14:55:19 -03:00
static const struct v4l2_subdev_pad_ops vimc_sen_pad_ops = {
2017-06-19 14:00:16 -03:00
. init_cfg = vimc_sen_init_cfg ,
2017-04-07 14:55:19 -03:00
. enum_mbus_code = vimc_sen_enum_mbus_code ,
. enum_frame_size = vimc_sen_enum_frame_size ,
. get_fmt = vimc_sen_get_fmt ,
2017-06-19 14:00:16 -03:00
. set_fmt = vimc_sen_set_fmt ,
2017-04-07 14:55:19 -03:00
} ;
2017-06-19 14:00:10 -03:00
static int vimc_sen_tpg_thread ( void * data )
2017-04-07 14:55:19 -03:00
{
struct vimc_sen_device * vsen = data ;
unsigned int i ;
set_freezable ( ) ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
for ( ; ; ) {
try_to_freeze ( ) ;
if ( kthread_should_stop ( ) )
break ;
2017-06-19 14:00:10 -03:00
tpg_fill_plane_buffer ( & vsen - > tpg , 0 , 0 , vsen - > frame ) ;
2017-04-07 14:55:19 -03:00
/* Send the frame to all source pads */
for ( i = 0 ; i < vsen - > sd . entity . num_pads ; i + + )
vimc_propagate_frame ( & vsen - > sd . entity . pads [ i ] ,
vsen - > frame ) ;
/* 60 frames per second */
schedule_timeout ( HZ / 60 ) ;
}
return 0 ;
}
static int vimc_sen_s_stream ( struct v4l2_subdev * sd , int enable )
{
struct vimc_sen_device * vsen =
container_of ( sd , struct vimc_sen_device , sd ) ;
int ret ;
if ( enable ) {
const struct vimc_pix_map * vpix ;
2017-06-19 14:00:10 -03:00
unsigned int frame_size ;
2017-04-07 14:55:19 -03:00
if ( vsen - > kthread_sen )
2017-06-19 14:00:10 -03:00
/* tpg is already executing */
return 0 ;
2017-04-07 14:55:19 -03:00
/* Calculate the frame size */
vpix = vimc_pix_map_by_code ( vsen - > mbus_format . code ) ;
2017-06-19 14:00:10 -03:00
frame_size = vsen - > mbus_format . width * vpix - > bpp *
vsen - > mbus_format . height ;
2017-04-07 14:55:19 -03:00
/*
* Allocate the frame buffer . Use vmalloc to be able to
* allocate a large amount of memory
*/
2017-06-19 14:00:10 -03:00
vsen - > frame = vmalloc ( frame_size ) ;
2017-04-07 14:55:19 -03:00
if ( ! vsen - > frame )
return - ENOMEM ;
2017-06-19 14:00:10 -03:00
/* configure the test pattern generator */
vimc_sen_tpg_s_format ( vsen ) ;
2017-04-07 14:55:19 -03:00
/* Initialize the image generator thread */
2017-06-19 14:00:10 -03:00
vsen - > kthread_sen = kthread_run ( vimc_sen_tpg_thread , vsen ,
" %s-sen " , vsen - > sd . v4l2_dev - > name ) ;
2017-04-07 14:55:19 -03:00
if ( IS_ERR ( vsen - > kthread_sen ) ) {
2017-06-19 14:00:18 -03:00
dev_err ( vsen - > dev , " %s: kernel_thread() failed \n " ,
vsen - > sd . name ) ;
2017-04-07 14:55:19 -03:00
vfree ( vsen - > frame ) ;
vsen - > frame = NULL ;
return PTR_ERR ( vsen - > kthread_sen ) ;
}
} else {
if ( ! vsen - > kthread_sen )
2017-06-19 14:00:10 -03:00
return 0 ;
2017-04-07 14:55:19 -03:00
/* Stop image generator */
ret = kthread_stop ( vsen - > kthread_sen ) ;
2017-06-19 14:00:10 -03:00
if ( ret )
return ret ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:10 -03:00
vsen - > kthread_sen = NULL ;
2017-04-07 14:55:19 -03:00
vfree ( vsen - > frame ) ;
vsen - > frame = NULL ;
2017-06-19 14:00:10 -03:00
return 0 ;
2017-04-07 14:55:19 -03:00
}
return 0 ;
}
2018-02-02 08:00:32 -05:00
static struct v4l2_subdev_core_ops vimc_sen_core_ops = {
. log_status = v4l2_ctrl_subdev_log_status ,
. subscribe_event = v4l2_ctrl_subdev_subscribe_event ,
. unsubscribe_event = v4l2_event_subdev_unsubscribe ,
} ;
2017-08-08 06:58:28 -04:00
static const struct v4l2_subdev_video_ops vimc_sen_video_ops = {
2017-04-07 14:55:19 -03:00
. s_stream = vimc_sen_s_stream ,
} ;
static const struct v4l2_subdev_ops vimc_sen_ops = {
2018-02-02 08:00:32 -05:00
. core = & vimc_sen_core_ops ,
2017-04-07 14:55:19 -03:00
. pad = & vimc_sen_pad_ops ,
. video = & vimc_sen_video_ops ,
} ;
2017-11-06 05:20:01 -05:00
static int vimc_sen_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct vimc_sen_device * vsen =
container_of ( ctrl - > handler , struct vimc_sen_device , hdl ) ;
switch ( ctrl - > id ) {
case VIMC_CID_TEST_PATTERN :
tpg_s_pattern ( & vsen - > tpg , ctrl - > val ) ;
break ;
case V4L2_CID_HFLIP :
tpg_s_hflip ( & vsen - > tpg , ctrl - > val ) ;
break ;
case V4L2_CID_VFLIP :
tpg_s_vflip ( & vsen - > tpg , ctrl - > val ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static const struct v4l2_ctrl_ops vimc_sen_ctrl_ops = {
. s_ctrl = vimc_sen_s_ctrl ,
} ;
2017-06-19 14:00:18 -03:00
static void vimc_sen_comp_unbind ( struct device * comp , struct device * master ,
void * master_data )
2017-04-07 14:55:19 -03:00
{
2017-06-19 14:00:18 -03:00
struct vimc_ent_device * ved = dev_get_drvdata ( comp ) ;
2017-04-07 14:55:19 -03:00
struct vimc_sen_device * vsen =
container_of ( ved , struct vimc_sen_device , ved ) ;
2017-06-19 14:00:12 -03:00
vimc_ent_sd_unregister ( ved , & vsen - > sd ) ;
2017-11-06 05:20:01 -05:00
v4l2_ctrl_handler_free ( & vsen - > hdl ) ;
2017-06-19 14:00:10 -03:00
tpg_free ( & vsen - > tpg ) ;
2017-04-07 14:55:19 -03:00
kfree ( vsen ) ;
}
2017-11-06 05:20:01 -05:00
/* Image Processing Controls */
static const struct v4l2_ctrl_config vimc_sen_ctrl_class = {
. flags = V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY ,
. id = VIMC_CID_VIMC_CLASS ,
. name = " VIMC Controls " ,
. type = V4L2_CTRL_TYPE_CTRL_CLASS ,
} ;
static const struct v4l2_ctrl_config vimc_sen_ctrl_test_pattern = {
. ops = & vimc_sen_ctrl_ops ,
. id = VIMC_CID_TEST_PATTERN ,
. name = " Test Pattern " ,
. type = V4L2_CTRL_TYPE_MENU ,
. max = TPG_PAT_NOISE ,
. qmenu = tpg_pattern_strings ,
} ;
2017-06-19 14:00:18 -03:00
static int vimc_sen_comp_bind ( struct device * comp , struct device * master ,
void * master_data )
2017-04-07 14:55:19 -03:00
{
2017-06-19 14:00:18 -03:00
struct v4l2_device * v4l2_dev = master_data ;
struct vimc_platform_data * pdata = comp - > platform_data ;
2017-04-07 14:55:19 -03:00
struct vimc_sen_device * vsen ;
int ret ;
/* Allocate the vsen struct */
vsen = kzalloc ( sizeof ( * vsen ) , GFP_KERNEL ) ;
if ( ! vsen )
2017-06-19 14:00:18 -03:00
return - ENOMEM ;
2017-04-07 14:55:19 -03:00
2017-11-06 05:20:01 -05:00
v4l2_ctrl_handler_init ( & vsen - > hdl , 4 ) ;
v4l2_ctrl_new_custom ( & vsen - > hdl , & vimc_sen_ctrl_class , NULL ) ;
v4l2_ctrl_new_custom ( & vsen - > hdl , & vimc_sen_ctrl_test_pattern , NULL ) ;
v4l2_ctrl_new_std ( & vsen - > hdl , & vimc_sen_ctrl_ops ,
V4L2_CID_VFLIP , 0 , 1 , 1 , 0 ) ;
v4l2_ctrl_new_std ( & vsen - > hdl , & vimc_sen_ctrl_ops ,
V4L2_CID_HFLIP , 0 , 1 , 1 , 0 ) ;
vsen - > sd . ctrl_handler = & vsen - > hdl ;
if ( vsen - > hdl . error ) {
ret = vsen - > hdl . error ;
goto err_free_vsen ;
}
2017-06-19 14:00:12 -03:00
/* Initialize ved and sd */
2017-06-19 14:00:18 -03:00
ret = vimc_ent_sd_register ( & vsen - > ved , & vsen - > sd , v4l2_dev ,
pdata - > entity_name ,
2018-02-07 12:06:30 -05:00
MEDIA_ENT_F_CAM_SENSOR , 1 ,
2017-06-19 14:00:18 -03:00
( const unsigned long [ 1 ] ) { MEDIA_PAD_FL_SOURCE } ,
& vimc_sen_ops ) ;
2017-04-07 14:55:19 -03:00
if ( ret )
2017-11-06 05:20:01 -05:00
goto err_free_hdl ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:18 -03:00
dev_set_drvdata ( comp , & vsen - > ved ) ;
vsen - > dev = comp ;
2017-06-19 14:00:16 -03:00
/* Initialize the frame format */
vsen - > mbus_format = fmt_default ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:10 -03:00
/* Initialize the test pattern generator */
tpg_init ( & vsen - > tpg , vsen - > mbus_format . width ,
vsen - > mbus_format . height ) ;
2017-06-19 14:00:16 -03:00
ret = tpg_alloc ( & vsen - > tpg , VIMC_FRAME_MAX_WIDTH ) ;
2017-06-19 14:00:10 -03:00
if ( ret )
2017-06-19 14:00:12 -03:00
goto err_unregister_ent_sd ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:18 -03:00
return 0 ;
2017-04-07 14:55:19 -03:00
2017-06-19 14:00:12 -03:00
err_unregister_ent_sd :
vimc_ent_sd_unregister ( & vsen - > ved , & vsen - > sd ) ;
2017-11-06 05:20:01 -05:00
err_free_hdl :
v4l2_ctrl_handler_free ( & vsen - > hdl ) ;
2017-04-07 14:55:19 -03:00
err_free_vsen :
kfree ( vsen ) ;
2017-06-19 14:00:18 -03:00
return ret ;
2017-04-07 14:55:19 -03:00
}
2017-06-19 14:00:18 -03:00
static const struct component_ops vimc_sen_comp_ops = {
. bind = vimc_sen_comp_bind ,
. unbind = vimc_sen_comp_unbind ,
} ;
static int vimc_sen_probe ( struct platform_device * pdev )
{
return component_add ( & pdev - > dev , & vimc_sen_comp_ops ) ;
}
static int vimc_sen_remove ( struct platform_device * pdev )
{
component_del ( & pdev - > dev , & vimc_sen_comp_ops ) ;
return 0 ;
}
2017-07-14 05:58:39 -03:00
static const struct platform_device_id vimc_sen_driver_ids [ ] = {
{
. name = VIMC_SEN_DRV_NAME ,
} ,
{ }
} ;
2017-06-19 14:00:18 -03:00
static struct platform_driver vimc_sen_pdrv = {
. probe = vimc_sen_probe ,
. remove = vimc_sen_remove ,
2017-07-14 05:58:39 -03:00
. id_table = vimc_sen_driver_ids ,
2017-06-19 14:00:18 -03:00
. driver = {
. name = VIMC_SEN_DRV_NAME ,
} ,
} ;
module_platform_driver ( vimc_sen_pdrv ) ;
MODULE_DEVICE_TABLE ( platform , vimc_sen_driver_ids ) ;
MODULE_DESCRIPTION ( " Virtual Media Controller Driver (VIMC) Sensor " ) ;
MODULE_AUTHOR ( " Helen Mae Koike Fornazier <helen.fornazier@gmail.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;