2019-06-04 05:37:33 -04:00
// SPDX-License-Identifier: GPL-2.0
2016-09-02 09:19:54 -03:00
/*
* Copyright ( c ) 2016 MediaTek Inc .
* Author : PC Chen < pc . chen @ mediatek . com >
* Tiffany Lin < tiffany . lin @ mediatek . com >
*/
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/of_device.h>
# include <linux/of.h>
# include <media/v4l2-event.h>
# include <media/v4l2-mem2mem.h>
# include <media/videobuf2-dma-contig.h>
# include "mtk_vcodec_drv.h"
# include "mtk_vcodec_dec.h"
# include "mtk_vcodec_dec_pm.h"
# include "mtk_vcodec_intr.h"
# include "mtk_vcodec_util.h"
2020-08-21 12:35:52 +02:00
# include "mtk_vcodec_fw.h"
2016-09-02 09:19:54 -03:00
# define VDEC_HW_ACTIVE 0x10
# define VDEC_IRQ_CFG 0x11
# define VDEC_IRQ_CLR 0x10
# define VDEC_IRQ_CFG_REG 0xa4
module_param ( mtk_v4l2_dbg_level , int , 0644 ) ;
module_param ( mtk_vcodec_dbg , bool , 0644 ) ;
/* Wake up context wait_queue */
static void wake_up_ctx ( struct mtk_vcodec_ctx * ctx )
{
ctx - > int_cond = 1 ;
wake_up_interruptible ( & ctx - > queue ) ;
}
static irqreturn_t mtk_vcodec_dec_irq_handler ( int irq , void * priv )
{
struct mtk_vcodec_dev * dev = priv ;
struct mtk_vcodec_ctx * ctx ;
u32 cg_status = 0 ;
unsigned int dec_done_status = 0 ;
void __iomem * vdec_misc_addr = dev - > reg_base [ VDEC_MISC ] +
VDEC_IRQ_CFG_REG ;
ctx = mtk_vcodec_get_curr_ctx ( dev ) ;
/* check if HW active or not */
cg_status = readl ( dev - > reg_base [ 0 ] ) ;
if ( ( cg_status & VDEC_HW_ACTIVE ) ! = 0 ) {
mtk_v4l2_err ( " DEC ISR, VDEC active is not 0x0 (0x%08x) " ,
cg_status ) ;
return IRQ_HANDLED ;
}
dec_done_status = readl ( vdec_misc_addr ) ;
ctx - > irq_status = dec_done_status ;
if ( ( dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS ) ! =
MTK_VDEC_IRQ_STATUS_DEC_SUCCESS )
return IRQ_HANDLED ;
/* clear interrupt */
writel ( ( readl ( vdec_misc_addr ) | VDEC_IRQ_CFG ) ,
dev - > reg_base [ VDEC_MISC ] + VDEC_IRQ_CFG_REG ) ;
writel ( ( readl ( vdec_misc_addr ) & ~ VDEC_IRQ_CLR ) ,
dev - > reg_base [ VDEC_MISC ] + VDEC_IRQ_CFG_REG ) ;
wake_up_ctx ( ctx ) ;
mtk_v4l2_debug ( 3 ,
" mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x " ,
ctx - > id , dec_done_status ) ;
return IRQ_HANDLED ;
}
static int fops_vcodec_open ( struct file * file )
{
struct mtk_vcodec_dev * dev = video_drvdata ( file ) ;
struct mtk_vcodec_ctx * ctx = NULL ;
2016-12-02 00:38:34 -02:00
struct mtk_video_dec_buf * mtk_buf = NULL ;
2016-09-02 09:19:54 -03:00
int ret = 0 ;
2016-12-02 00:38:34 -02:00
struct vb2_queue * src_vq ;
2016-09-02 09:19:54 -03:00
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
2016-12-02 00:38:34 -02:00
mtk_buf = kzalloc ( sizeof ( * mtk_buf ) , GFP_KERNEL ) ;
if ( ! mtk_buf ) {
kfree ( ctx ) ;
return - ENOMEM ;
}
2016-09-02 09:19:54 -03:00
mutex_lock ( & dev - > dev_mutex ) ;
2016-12-02 00:38:34 -02:00
ctx - > empty_flush_buf = mtk_buf ;
2016-09-02 09:19:54 -03:00
ctx - > id = dev - > id_counter + + ;
v4l2_fh_init ( & ctx - > fh , video_devdata ( file ) ) ;
file - > private_data = & ctx - > fh ;
v4l2_fh_add ( & ctx - > fh ) ;
INIT_LIST_HEAD ( & ctx - > list ) ;
ctx - > dev = dev ;
init_waitqueue_head ( & ctx - > queue ) ;
mutex_init ( & ctx - > lock ) ;
ctx - > type = MTK_INST_DECODER ;
ret = mtk_vcodec_dec_ctrls_setup ( ctx ) ;
if ( ret ) {
mtk_v4l2_err ( " Failed to setup mt vcodec controls " ) ;
goto err_ctrls_setup ;
}
ctx - > m2m_ctx = v4l2_m2m_ctx_init ( dev - > m2m_dev_dec , ctx ,
& mtk_vcodec_dec_queue_init ) ;
if ( IS_ERR ( ( __force void * ) ctx - > m2m_ctx ) ) {
ret = PTR_ERR ( ( __force void * ) ctx - > m2m_ctx ) ;
mtk_v4l2_err ( " Failed to v4l2_m2m_ctx_init() (%d) " ,
ret ) ;
goto err_m2m_ctx_init ;
}
2016-12-02 00:38:34 -02:00
src_vq = v4l2_m2m_get_vq ( ctx - > m2m_ctx ,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ) ;
2020-01-07 10:44:48 +01:00
ctx - > empty_flush_buf - > m2m_buf . vb . vb2_buf . vb2_queue = src_vq ;
2016-12-02 00:38:34 -02:00
ctx - > empty_flush_buf - > lastframe = true ;
2016-09-02 09:19:54 -03:00
mtk_vcodec_dec_set_default_params ( ctx ) ;
if ( v4l2_fh_is_singular ( & ctx - > fh ) ) {
mtk_vcodec_dec_pw_on ( & dev - > pm ) ;
/*
2020-08-21 12:35:52 +02:00
* Does nothing if firmware was already loaded .
2016-09-02 09:19:54 -03:00
*/
2020-08-21 12:35:52 +02:00
ret = mtk_vcodec_fw_load_firmware ( dev - > fw_handler ) ;
2016-09-02 09:19:54 -03:00
if ( ret < 0 ) {
/*
* Return 0 if downloading firmware successfully ,
* otherwise it is failed
*/
2020-08-21 12:35:52 +02:00
mtk_v4l2_err ( " failed to load firmware! " ) ;
2016-09-02 09:19:54 -03:00
goto err_load_fw ;
}
dev - > dec_capability =
2020-08-21 12:35:52 +02:00
mtk_vcodec_fw_get_vdec_capa ( dev - > fw_handler ) ;
2016-09-02 09:19:54 -03:00
mtk_v4l2_debug ( 0 , " decoder capability %x " , dev - > dec_capability ) ;
}
list_add ( & ctx - > list , & dev - > ctx_list ) ;
mutex_unlock ( & dev - > dev_mutex ) ;
mtk_v4l2_debug ( 0 , " %s decoder [%d] " , dev_name ( & dev - > plat_dev - > dev ) ,
ctx - > id ) ;
return ret ;
/* Deinit when failure occurred */
err_load_fw :
v4l2_m2m_ctx_release ( ctx - > m2m_ctx ) ;
err_m2m_ctx_init :
v4l2_ctrl_handler_free ( & ctx - > ctrl_hdl ) ;
err_ctrls_setup :
v4l2_fh_del ( & ctx - > fh ) ;
v4l2_fh_exit ( & ctx - > fh ) ;
2016-12-02 00:38:34 -02:00
kfree ( ctx - > empty_flush_buf ) ;
2016-09-02 09:19:54 -03:00
kfree ( ctx ) ;
mutex_unlock ( & dev - > dev_mutex ) ;
return ret ;
}
static int fops_vcodec_release ( struct file * file )
{
struct mtk_vcodec_dev * dev = video_drvdata ( file ) ;
struct mtk_vcodec_ctx * ctx = fh_to_ctx ( file - > private_data ) ;
mtk_v4l2_debug ( 0 , " [%d] decoder " , ctx - > id ) ;
mutex_lock ( & dev - > dev_mutex ) ;
/*
* Call v4l2_m2m_ctx_release before mtk_vcodec_dec_release . First , it
* makes sure the worker thread is not running after vdec_if_deinit .
* Second , the decoder will be flushed and all the buffers will be
* returned in stop_streaming .
*/
v4l2_m2m_ctx_release ( ctx - > m2m_ctx ) ;
mtk_vcodec_dec_release ( ctx ) ;
if ( v4l2_fh_is_singular ( & ctx - > fh ) )
mtk_vcodec_dec_pw_off ( & dev - > pm ) ;
v4l2_fh_del ( & ctx - > fh ) ;
v4l2_fh_exit ( & ctx - > fh ) ;
v4l2_ctrl_handler_free ( & ctx - > ctrl_hdl ) ;
list_del_init ( & ctx - > list ) ;
2016-12-02 00:38:34 -02:00
kfree ( ctx - > empty_flush_buf ) ;
2016-09-02 09:19:54 -03:00
kfree ( ctx ) ;
mutex_unlock ( & dev - > dev_mutex ) ;
return 0 ;
}
static const struct v4l2_file_operations mtk_vcodec_fops = {
. owner = THIS_MODULE ,
. open = fops_vcodec_open ,
. release = fops_vcodec_release ,
. poll = v4l2_m2m_fop_poll ,
. unlocked_ioctl = video_ioctl2 ,
. mmap = v4l2_m2m_fop_mmap ,
} ;
static int mtk_vcodec_probe ( struct platform_device * pdev )
{
struct mtk_vcodec_dev * dev ;
struct video_device * vfd_dec ;
struct resource * res ;
2020-08-21 12:35:52 +02:00
phandle rproc_phandle ;
enum mtk_vcodec_fw_type fw_type ;
2016-09-02 09:19:54 -03:00
int i , ret ;
dev = devm_kzalloc ( & pdev - > dev , sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
INIT_LIST_HEAD ( & dev - > ctx_list ) ;
dev - > plat_dev = pdev ;
2020-08-21 12:35:52 +02:00
if ( ! of_property_read_u32 ( pdev - > dev . of_node , " mediatek,vpu " ,
& rproc_phandle ) ) {
fw_type = VPU ;
2020-08-21 12:35:54 +02:00
} else if ( ! of_property_read_u32 ( pdev - > dev . of_node , " mediatek,scp " ,
& rproc_phandle ) ) {
fw_type = SCP ;
2020-08-21 12:35:52 +02:00
} else {
mtk_v4l2_err ( " Could not get vdec IPI device " ) ;
return - ENODEV ;
2016-09-02 09:19:54 -03:00
}
2020-09-03 07:48:33 +02:00
dma_set_max_seg_size ( & pdev - > dev , UINT_MAX ) ;
2016-09-02 09:19:54 -03:00
2020-10-13 14:44:27 +02:00
dev - > fw_handler = mtk_vcodec_fw_select ( dev , fw_type , DECODER ) ;
2020-08-21 12:35:52 +02:00
if ( IS_ERR ( dev - > fw_handler ) )
return PTR_ERR ( dev - > fw_handler ) ;
2016-09-02 09:19:54 -03:00
ret = mtk_vcodec_init_dec_pm ( dev ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Failed to get mt vcodec clock source " ) ;
2020-08-21 12:35:52 +02:00
goto err_dec_pm ;
2016-09-02 09:19:54 -03:00
}
for ( i = 0 ; i < NUM_MAX_VDEC_REG_BASE ; i + + ) {
2019-09-18 06:20:48 -03:00
dev - > reg_base [ i ] = devm_platform_ioremap_resource ( pdev , i ) ;
2016-09-02 09:19:54 -03:00
if ( IS_ERR ( ( __force void * ) dev - > reg_base [ i ] ) ) {
2016-09-06 11:51:15 -03:00
ret = PTR_ERR ( ( __force void * ) dev - > reg_base [ i ] ) ;
2016-09-02 09:19:54 -03:00
goto err_res ;
}
mtk_v4l2_debug ( 2 , " reg[%d] base=%p " , i , dev - > reg_base [ i ] ) ;
}
res = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( res = = NULL ) {
dev_err ( & pdev - > dev , " failed to get irq resource " ) ;
ret = - ENOENT ;
goto err_res ;
}
dev - > dec_irq = platform_get_irq ( pdev , 0 ) ;
2020-08-21 13:19:23 +02:00
irq_set_status_flags ( dev - > dec_irq , IRQ_NOAUTOEN ) ;
2016-09-02 09:19:54 -03:00
ret = devm_request_irq ( & pdev - > dev , dev - > dec_irq ,
mtk_vcodec_dec_irq_handler , 0 , pdev - > name , dev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to install dev->dec_irq %d (%d) " ,
dev - > dec_irq ,
ret ) ;
goto err_res ;
}
mutex_init ( & dev - > dec_mutex ) ;
mutex_init ( & dev - > dev_mutex ) ;
spin_lock_init ( & dev - > irqlock ) ;
snprintf ( dev - > v4l2_dev . name , sizeof ( dev - > v4l2_dev . name ) , " %s " ,
" [/MTK_V4L2_VDEC] " ) ;
ret = v4l2_device_register ( & pdev - > dev , & dev - > v4l2_dev ) ;
if ( ret ) {
mtk_v4l2_err ( " v4l2_device_register err=%d " , ret ) ;
goto err_res ;
}
init_waitqueue_head ( & dev - > queue ) ;
vfd_dec = video_device_alloc ( ) ;
if ( ! vfd_dec ) {
mtk_v4l2_err ( " Failed to allocate video device " ) ;
ret = - ENOMEM ;
goto err_dec_alloc ;
}
vfd_dec - > fops = & mtk_vcodec_fops ;
vfd_dec - > ioctl_ops = & mtk_vdec_ioctl_ops ;
vfd_dec - > release = video_device_release ;
vfd_dec - > lock = & dev - > dev_mutex ;
vfd_dec - > v4l2_dev = & dev - > v4l2_dev ;
vfd_dec - > vfl_dir = VFL_DIR_M2M ;
vfd_dec - > device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_STREAMING ;
snprintf ( vfd_dec - > name , sizeof ( vfd_dec - > name ) , " %s " ,
MTK_VCODEC_DEC_NAME ) ;
video_set_drvdata ( vfd_dec , dev ) ;
dev - > vfd_dec = vfd_dec ;
platform_set_drvdata ( pdev , dev ) ;
dev - > m2m_dev_dec = v4l2_m2m_init ( & mtk_vdec_m2m_ops ) ;
if ( IS_ERR ( ( __force void * ) dev - > m2m_dev_dec ) ) {
mtk_v4l2_err ( " Failed to init mem2mem dec device " ) ;
ret = PTR_ERR ( ( __force void * ) dev - > m2m_dev_dec ) ;
goto err_dec_mem_init ;
}
dev - > decode_workqueue =
alloc_ordered_workqueue ( MTK_VCODEC_DEC_NAME ,
WQ_MEM_RECLAIM | WQ_FREEZABLE ) ;
if ( ! dev - > decode_workqueue ) {
mtk_v4l2_err ( " Failed to create decode workqueue " ) ;
ret = - EINVAL ;
goto err_event_workq ;
}
2020-02-03 12:41:18 +01:00
ret = video_register_device ( vfd_dec , VFL_TYPE_VIDEO , 0 ) ;
2016-09-02 09:19:54 -03:00
if ( ret ) {
mtk_v4l2_err ( " Failed to register video device " ) ;
goto err_dec_reg ;
}
mtk_v4l2_debug ( 0 , " decoder registered as /dev/video%d " ,
vfd_dec - > num ) ;
return 0 ;
err_dec_reg :
destroy_workqueue ( dev - > decode_workqueue ) ;
err_event_workq :
v4l2_m2m_release ( dev - > m2m_dev_dec ) ;
err_dec_mem_init :
video_unregister_device ( vfd_dec ) ;
err_dec_alloc :
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
err_res :
mtk_vcodec_release_dec_pm ( dev ) ;
2020-08-21 12:35:52 +02:00
err_dec_pm :
mtk_vcodec_fw_release ( dev - > fw_handler ) ;
2016-09-02 09:19:54 -03:00
return ret ;
}
static const struct of_device_id mtk_vcodec_match [ ] = {
{ . compatible = " mediatek,mt8173-vcodec-dec " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , mtk_vcodec_match ) ;
static int mtk_vcodec_dec_remove ( struct platform_device * pdev )
{
struct mtk_vcodec_dev * dev = platform_get_drvdata ( pdev ) ;
flush_workqueue ( dev - > decode_workqueue ) ;
destroy_workqueue ( dev - > decode_workqueue ) ;
if ( dev - > m2m_dev_dec )
v4l2_m2m_release ( dev - > m2m_dev_dec ) ;
if ( dev - > vfd_dec )
video_unregister_device ( dev - > vfd_dec ) ;
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
mtk_vcodec_release_dec_pm ( dev ) ;
2020-08-21 12:35:52 +02:00
mtk_vcodec_fw_release ( dev - > fw_handler ) ;
2016-09-02 09:19:54 -03:00
return 0 ;
}
static struct platform_driver mtk_vcodec_dec_driver = {
. probe = mtk_vcodec_probe ,
. remove = mtk_vcodec_dec_remove ,
. driver = {
. name = MTK_VCODEC_DEC_NAME ,
. of_match_table = mtk_vcodec_match ,
} ,
} ;
module_platform_driver ( mtk_vcodec_dec_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " Mediatek video codec V4L2 decoder driver " ) ;