2019-05-27 06:55:01 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2011-06-21 13:51:26 +00:00
|
|
|
/*
|
|
|
|
* Samsung S5P Multi Format Codec v 5.1
|
|
|
|
*
|
|
|
|
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
|
|
|
* Kamil Debski, <k.debski@samsung.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/videodev2.h>
|
2012-08-21 11:05:32 +00:00
|
|
|
#include <media/v4l2-event.h>
|
2011-06-21 13:51:26 +00:00
|
|
|
#include <linux/workqueue.h>
|
2012-10-25 08:24:14 +00:00
|
|
|
#include <linux/of.h>
|
2017-02-20 13:38:51 +00:00
|
|
|
#include <linux/of_device.h>
|
2016-05-24 13:31:26 +00:00
|
|
|
#include <linux/of_reserved_mem.h>
|
2015-09-22 13:30:29 +00:00
|
|
|
#include <media/videobuf2-v4l2.h>
|
2012-10-04 01:19:08 +00:00
|
|
|
#include "s5p_mfc_common.h"
|
2011-06-21 13:51:26 +00:00
|
|
|
#include "s5p_mfc_ctrl.h"
|
|
|
|
#include "s5p_mfc_debug.h"
|
|
|
|
#include "s5p_mfc_dec.h"
|
|
|
|
#include "s5p_mfc_enc.h"
|
|
|
|
#include "s5p_mfc_intr.h"
|
2016-05-24 13:31:27 +00:00
|
|
|
#include "s5p_mfc_iommu.h"
|
2012-10-04 01:19:08 +00:00
|
|
|
#include "s5p_mfc_opr.h"
|
|
|
|
#include "s5p_mfc_cmd.h"
|
2011-06-21 13:51:26 +00:00
|
|
|
#include "s5p_mfc_pm.h"
|
|
|
|
|
|
|
|
#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
|
|
|
|
#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
|
|
|
|
|
2014-08-22 11:07:57 +00:00
|
|
|
int mfc_debug_level;
|
|
|
|
module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR);
|
2011-06-21 13:51:26 +00:00
|
|
|
MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
|
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
static char *mfc_mem_size;
|
|
|
|
module_param_named(mem, mfc_mem_size, charp, 0644);
|
|
|
|
MODULE_PARM_DESC(mem, "Preallocated memory size for the firmware and context buffers");
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Helper functions for interrupt processing */
|
2012-08-14 09:13:40 +00:00
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Remove from hw execution round robin */
|
2012-08-14 09:13:40 +00:00
|
|
|
void clear_work_bit(struct s5p_mfc_ctx *ctx)
|
2011-06-21 13:51:26 +00:00
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
|
|
|
|
spin_lock(&dev->condlock);
|
2012-08-14 09:13:40 +00:00
|
|
|
__clear_bit(ctx->num, &dev->ctx_work_bits);
|
2011-06-21 13:51:26 +00:00
|
|
|
spin_unlock(&dev->condlock);
|
|
|
|
}
|
|
|
|
|
2012-08-14 09:13:40 +00:00
|
|
|
/* Add to hw execution round robin */
|
|
|
|
void set_work_bit(struct s5p_mfc_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
|
|
|
|
spin_lock(&dev->condlock);
|
|
|
|
__set_bit(ctx->num, &dev->ctx_work_bits);
|
|
|
|
spin_unlock(&dev->condlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove from hw execution round robin */
|
|
|
|
void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->condlock, flags);
|
|
|
|
__clear_bit(ctx->num, &dev->ctx_work_bits);
|
|
|
|
spin_unlock_irqrestore(&dev->condlock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add to hw execution round robin */
|
|
|
|
void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->condlock, flags);
|
|
|
|
__set_bit(ctx->num, &dev->ctx_work_bits);
|
|
|
|
spin_unlock_irqrestore(&dev->condlock, flags);
|
|
|
|
}
|
|
|
|
|
2015-12-02 08:22:28 +00:00
|
|
|
int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int ctx;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dev->condlock, flags);
|
|
|
|
ctx = dev->curr_ctx;
|
|
|
|
do {
|
|
|
|
ctx = (ctx + 1) % MFC_NUM_CONTEXTS;
|
|
|
|
if (ctx == dev->curr_ctx) {
|
|
|
|
if (!test_bit(ctx, &dev->ctx_work_bits))
|
|
|
|
ctx = -EAGAIN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (!test_bit(ctx, &dev->ctx_work_bits));
|
|
|
|
spin_unlock_irqrestore(&dev->condlock, flags);
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Wake up context wait_queue */
|
|
|
|
static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason,
|
|
|
|
unsigned int err)
|
|
|
|
{
|
|
|
|
ctx->int_cond = 1;
|
|
|
|
ctx->int_type = reason;
|
|
|
|
ctx->int_err = err;
|
|
|
|
wake_up(&ctx->queue);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Wake up device wait_queue */
|
|
|
|
static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
|
|
|
|
unsigned int err)
|
|
|
|
{
|
|
|
|
dev->int_cond = 1;
|
|
|
|
dev->int_type = reason;
|
|
|
|
dev->int_err = err;
|
|
|
|
wake_up(&dev->queue);
|
|
|
|
}
|
|
|
|
|
2015-12-02 08:22:29 +00:00
|
|
|
void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_buf *b;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
while (!list_empty(lh)) {
|
|
|
|
b = list_entry(lh->next, struct s5p_mfc_buf, list);
|
|
|
|
for (i = 0; i < b->b->vb2_buf.num_planes; i++)
|
|
|
|
vb2_set_plane_payload(&b->b->vb2_buf, i, 0);
|
|
|
|
vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR);
|
|
|
|
list_del(&b->list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-16 21:43:17 +00:00
|
|
|
static void s5p_mfc_watchdog(struct timer_list *t)
|
2011-06-21 13:51:26 +00:00
|
|
|
{
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-16 21:43:17 +00:00
|
|
|
struct s5p_mfc_dev *dev = from_timer(dev, t, watchdog_timer);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
if (test_bit(0, &dev->hw_lock))
|
|
|
|
atomic_inc(&dev->watchdog_cnt);
|
|
|
|
if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) {
|
|
|
|
/* This means that hw is busy and no interrupts were
|
|
|
|
* generated by hw for the Nth time of running this
|
|
|
|
* watchdog timer. This usually means a serious hw
|
|
|
|
* error. Now it is time to kill all instances and
|
|
|
|
* reset the MFC. */
|
|
|
|
mfc_err("Time out during waiting for HW\n");
|
2016-07-16 08:30:25 +00:00
|
|
|
schedule_work(&dev->watchdog_work);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
dev->watchdog_timer.expires = jiffies +
|
|
|
|
msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
|
|
|
|
add_timer(&dev->watchdog_timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_watchdog_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev;
|
|
|
|
struct s5p_mfc_ctx *ctx;
|
|
|
|
unsigned long flags;
|
|
|
|
int mutex_locked;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
dev = container_of(work, struct s5p_mfc_dev, watchdog_work);
|
|
|
|
|
|
|
|
mfc_err("Driver timeout error handling\n");
|
|
|
|
/* Lock the mutex that protects open and release.
|
|
|
|
* This is necessary as they may load and unload firmware. */
|
|
|
|
mutex_locked = mutex_trylock(&dev->mfc_mutex);
|
|
|
|
if (!mutex_locked)
|
|
|
|
mfc_err("Error: some instance may be closing/opening\n");
|
|
|
|
spin_lock_irqsave(&dev->irqlock, flags);
|
|
|
|
|
|
|
|
s5p_mfc_clock_off();
|
|
|
|
|
|
|
|
for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
|
|
|
|
ctx = dev->ctx[i];
|
|
|
|
if (!ctx)
|
|
|
|
continue;
|
|
|
|
ctx->state = MFCINST_ERROR;
|
2015-12-02 08:22:29 +00:00
|
|
|
s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
|
|
|
|
s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
|
2011-06-21 13:51:26 +00:00
|
|
|
clear_work_bit(ctx);
|
2012-10-04 01:19:08 +00:00
|
|
|
wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
clear_bit(0, &dev->hw_lock);
|
|
|
|
spin_unlock_irqrestore(&dev->irqlock, flags);
|
2014-10-21 11:07:03 +00:00
|
|
|
|
|
|
|
/* De-init MFC */
|
|
|
|
s5p_mfc_deinit_hw(dev);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Double check if there is at least one instance running.
|
|
|
|
* If no instance is in memory than no firmware should be present */
|
|
|
|
if (dev->num_inst > 0) {
|
2014-05-21 09:29:29 +00:00
|
|
|
ret = s5p_mfc_load_firmware(dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ret) {
|
|
|
|
mfc_err("Failed to reload FW\n");
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
s5p_mfc_clock_on();
|
|
|
|
ret = s5p_mfc_init_hw(dev);
|
2017-03-22 07:53:57 +00:00
|
|
|
s5p_mfc_clock_off();
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ret)
|
|
|
|
mfc_err("Failed to reinit FW\n");
|
|
|
|
}
|
|
|
|
unlock:
|
|
|
|
if (mutex_locked)
|
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_buf *dst_buf;
|
2012-10-04 01:19:08 +00:00
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
ctx->state = MFCINST_FINISHED;
|
|
|
|
ctx->sequence++;
|
|
|
|
while (!list_empty(&ctx->dst_queue)) {
|
|
|
|
dst_buf = list_entry(ctx->dst_queue.next,
|
|
|
|
struct s5p_mfc_buf, list);
|
|
|
|
mfc_debug(2, "Cleaning up buffer: %d\n",
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->vb2_buf.index);
|
|
|
|
vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, 0);
|
|
|
|
vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
list_del(&dst_buf->list);
|
2015-10-07 10:13:45 +00:00
|
|
|
dst_buf->flags |= MFC_BUF_FLAG_EOS;
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->dst_queue_cnt--;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->sequence = (ctx->sequence++);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) ==
|
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx))
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->field = V4L2_FIELD_NONE;
|
2011-06-21 13:51:26 +00:00
|
|
|
else
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->field = V4L2_FIELD_INTERLACED;
|
|
|
|
dst_buf->b->flags |= V4L2_BUF_FLAG_LAST;
|
2011-06-21 13:51:26 +00:00
|
|
|
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
ctx->dec_dst_flag &= ~(1 << dst_buf->b->vb2_buf.index);
|
|
|
|
vb2_buffer_done(&dst_buf->b->vb2_buf, VB2_BUF_STATE_DONE);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
2018-06-05 13:33:59 +00:00
|
|
|
struct s5p_mfc_buf *dst_buf, *src_buf;
|
|
|
|
u32 dec_y_addr;
|
2012-10-04 01:19:08 +00:00
|
|
|
unsigned int frame_type;
|
|
|
|
|
2014-10-21 11:06:58 +00:00
|
|
|
/* Make sure we actually have a new frame before continuing. */
|
2012-10-04 01:19:08 +00:00
|
|
|
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
|
2014-10-21 11:06:58 +00:00
|
|
|
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED)
|
|
|
|
return;
|
2018-06-05 13:33:59 +00:00
|
|
|
dec_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
/* Copy timestamp / timecode from decoded src to dst and set
|
2014-10-21 11:06:58 +00:00
|
|
|
appropriate flags. */
|
2011-06-21 13:51:26 +00:00
|
|
|
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
|
|
|
|
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
|
2018-06-05 13:33:59 +00:00
|
|
|
u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0);
|
|
|
|
|
|
|
|
if (addr == dec_y_addr) {
|
|
|
|
dst_buf->b->timecode = src_buf->b->timecode;
|
2015-11-03 10:16:37 +00:00
|
|
|
dst_buf->b->vb2_buf.timestamp =
|
|
|
|
src_buf->b->vb2_buf.timestamp;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->flags &=
|
2014-02-08 17:21:35 +00:00
|
|
|
~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->flags |=
|
|
|
|
src_buf->b->flags
|
2014-02-08 17:21:35 +00:00
|
|
|
& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
|
2011-06-21 13:51:26 +00:00
|
|
|
switch (frame_type) {
|
|
|
|
case S5P_FIMV_DECODE_FRAME_I_FRAME:
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->flags |=
|
2011-06-21 13:51:26 +00:00
|
|
|
V4L2_BUF_FLAG_KEYFRAME;
|
|
|
|
break;
|
|
|
|
case S5P_FIMV_DECODE_FRAME_P_FRAME:
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->flags |=
|
2011-06-21 13:51:26 +00:00
|
|
|
V4L2_BUF_FLAG_PFRAME;
|
|
|
|
break;
|
|
|
|
case S5P_FIMV_DECODE_FRAME_B_FRAME:
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->flags |=
|
2011-06-21 13:51:26 +00:00
|
|
|
V4L2_BUF_FLAG_BFRAME;
|
|
|
|
break;
|
2014-10-21 11:06:58 +00:00
|
|
|
default:
|
|
|
|
/* Don't know how to handle
|
|
|
|
S5P_FIMV_DECODE_FRAME_OTHER_FRAME. */
|
|
|
|
mfc_debug(2, "Unexpected frame type: %d\n",
|
|
|
|
frame_type);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
struct s5p_mfc_buf *dst_buf;
|
2018-06-05 13:33:59 +00:00
|
|
|
u32 dspl_y_addr;
|
2012-10-04 01:19:08 +00:00
|
|
|
unsigned int frame_type;
|
2011-06-21 13:51:26 +00:00
|
|
|
|
2018-06-05 13:33:59 +00:00
|
|
|
dspl_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
|
2014-09-22 12:52:02 +00:00
|
|
|
if (IS_MFCV6_PLUS(dev))
|
|
|
|
frame_type = s5p_mfc_hw_call(dev->mfc_ops,
|
|
|
|
get_disp_frame_type, ctx);
|
|
|
|
else
|
|
|
|
frame_type = s5p_mfc_hw_call(dev->mfc_ops,
|
|
|
|
get_dec_frame_type, dev);
|
2012-10-04 01:19:08 +00:00
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* If frame is same as previous then skip and do not dequeue */
|
|
|
|
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) {
|
|
|
|
if (!ctx->after_packed_pb)
|
|
|
|
ctx->sequence++;
|
|
|
|
ctx->after_packed_pb = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ctx->sequence++;
|
|
|
|
/* The MFC returns address of the buffer, now we have to
|
|
|
|
* check which videobuf does it correspond to */
|
|
|
|
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
|
2018-06-05 13:33:59 +00:00
|
|
|
u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Check if this is the buffer we're looking for */
|
2018-06-05 13:33:59 +00:00
|
|
|
if (addr == dspl_y_addr) {
|
2011-06-21 13:51:26 +00:00
|
|
|
list_del(&dst_buf->list);
|
|
|
|
ctx->dst_queue_cnt--;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->sequence = ctx->sequence;
|
2012-10-04 01:19:08 +00:00
|
|
|
if (s5p_mfc_hw_call(dev->mfc_ops,
|
|
|
|
get_pic_type_top, ctx) ==
|
|
|
|
s5p_mfc_hw_call(dev->mfc_ops,
|
|
|
|
get_pic_type_bot, ctx))
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->field = V4L2_FIELD_NONE;
|
2011-06-21 13:51:26 +00:00
|
|
|
else
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
dst_buf->b->field =
|
2011-06-21 13:51:26 +00:00
|
|
|
V4L2_FIELD_INTERLACED;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0,
|
|
|
|
ctx->luma_size);
|
|
|
|
vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1,
|
|
|
|
ctx->chroma_size);
|
|
|
|
clear_bit(dst_buf->b->vb2_buf.index,
|
2011-06-21 13:51:26 +00:00
|
|
|
&ctx->dec_dst_flag);
|
|
|
|
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_buffer_done(&dst_buf->b->vb2_buf, err ?
|
|
|
|
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle frame decoding interrupt */
|
|
|
|
static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx,
|
|
|
|
unsigned int reason, unsigned int err)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
unsigned int dst_frame_status;
|
[media] s5p-mfc: Copy timestamps only when a frame is produced
Timestamps for destination buffers are assigned by copying them from
corresponding source buffers when the decode operation results in a frame
being outputted to a destination buffer. But the decision when to do this, i.e.
whether the decode operation on current source buffer produced a destination
frame, is wrongly based on "display status". Display status reflects the status
of the destination buffer, not source.
This used to work for firmwares version <= 6, because in addition to the above,
we'd check the decoded frame type register, which was set to "skipped" if
a destination frame was not produced, exiting early from
s5p_mfc_handle_frame_new().
Firmware >=7 does not set the frame type register for frames that were not
decoded anymore though, which results in us wrongly overwriting timestamps of
previously decoded buffers (firmware reports the same destination buffer address
as previously decoded one if a frame wasn't decoded during current operation).
To do it properly, we should be basing our decision to copy the timestamp on the
status of the source buffer, i.e. "decode status". The decode status register
values are confusing, because in its case "display" means "a frame has been
outputted to a destination buffer". We should copy if "decode and display"
is returned in it. This also works on <= v6 firmware, which behaves in the same
way with regards to decode status register.
Signed-off-by: Pawel Osciak <posciak@chromium.org>
Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
2014-05-19 12:32:57 +00:00
|
|
|
unsigned int dec_frame_status;
|
2011-06-21 13:51:26 +00:00
|
|
|
struct s5p_mfc_buf *src_buf;
|
|
|
|
unsigned int res_change;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
|
2011-06-21 13:51:26 +00:00
|
|
|
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
|
[media] s5p-mfc: Copy timestamps only when a frame is produced
Timestamps for destination buffers are assigned by copying them from
corresponding source buffers when the decode operation results in a frame
being outputted to a destination buffer. But the decision when to do this, i.e.
whether the decode operation on current source buffer produced a destination
frame, is wrongly based on "display status". Display status reflects the status
of the destination buffer, not source.
This used to work for firmwares version <= 6, because in addition to the above,
we'd check the decoded frame type register, which was set to "skipped" if
a destination frame was not produced, exiting early from
s5p_mfc_handle_frame_new().
Firmware >=7 does not set the frame type register for frames that were not
decoded anymore though, which results in us wrongly overwriting timestamps of
previously decoded buffers (firmware reports the same destination buffer address
as previously decoded one if a frame wasn't decoded during current operation).
To do it properly, we should be basing our decision to copy the timestamp on the
status of the source buffer, i.e. "decode status". The decode status register
values are confusing, because in its case "display" means "a frame has been
outputted to a destination buffer". We should copy if "decode and display"
is returned in it. This also works on <= v6 firmware, which behaves in the same
way with regards to decode status register.
Signed-off-by: Pawel Osciak <posciak@chromium.org>
Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
2014-05-19 12:32:57 +00:00
|
|
|
dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
|
|
|
|
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
|
2012-10-04 01:19:11 +00:00
|
|
|
res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
|
|
|
|
& S5P_FIMV_DEC_STATUS_RESOLUTION_MASK)
|
|
|
|
>> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT;
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
|
|
|
|
if (ctx->state == MFCINST_RES_CHANGE_INIT)
|
|
|
|
ctx->state = MFCINST_RES_CHANGE_FLUSH;
|
2012-10-04 01:19:11 +00:00
|
|
|
if (res_change == S5P_FIMV_RES_INCREASE ||
|
|
|
|
res_change == S5P_FIMV_RES_DECREASE) {
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->state = MFCINST_RES_CHANGE_INIT;
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
wake_up_ctx(ctx, reason, err);
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_clock_off();
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ctx->dpb_flush_flag)
|
|
|
|
ctx->dpb_flush_flag = 0;
|
|
|
|
|
|
|
|
/* All frames remaining in the buffer have been extracted */
|
|
|
|
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) {
|
|
|
|
if (ctx->state == MFCINST_RES_CHANGE_FLUSH) {
|
2014-05-14 06:59:43 +00:00
|
|
|
static const struct v4l2_event ev_src_ch = {
|
|
|
|
.type = V4L2_EVENT_SOURCE_CHANGE,
|
|
|
|
.u.src_change.changes =
|
|
|
|
V4L2_EVENT_SRC_CH_RESOLUTION,
|
|
|
|
};
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_handle_frame_all_extracted(ctx);
|
|
|
|
ctx->state = MFCINST_RES_CHANGE_END;
|
2014-05-14 06:59:43 +00:00
|
|
|
v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
goto leave_handle_frame;
|
|
|
|
} else {
|
|
|
|
s5p_mfc_handle_frame_all_extracted(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
[media] s5p-mfc: Copy timestamps only when a frame is produced
Timestamps for destination buffers are assigned by copying them from
corresponding source buffers when the decode operation results in a frame
being outputted to a destination buffer. But the decision when to do this, i.e.
whether the decode operation on current source buffer produced a destination
frame, is wrongly based on "display status". Display status reflects the status
of the destination buffer, not source.
This used to work for firmwares version <= 6, because in addition to the above,
we'd check the decoded frame type register, which was set to "skipped" if
a destination frame was not produced, exiting early from
s5p_mfc_handle_frame_new().
Firmware >=7 does not set the frame type register for frames that were not
decoded anymore though, which results in us wrongly overwriting timestamps of
previously decoded buffers (firmware reports the same destination buffer address
as previously decoded one if a frame wasn't decoded during current operation).
To do it properly, we should be basing our decision to copy the timestamp on the
status of the source buffer, i.e. "decode status". The decode status register
values are confusing, because in its case "display" means "a frame has been
outputted to a destination buffer". We should copy if "decode and display"
is returned in it. This also works on <= v6 firmware, which behaves in the same
way with regards to decode status register.
Signed-off-by: Pawel Osciak <posciak@chromium.org>
Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
2014-05-19 12:32:57 +00:00
|
|
|
if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_handle_frame_copy_time(ctx);
|
|
|
|
|
|
|
|
/* A frame has been decoded and is in the buffer */
|
|
|
|
if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY ||
|
|
|
|
dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) {
|
|
|
|
s5p_mfc_handle_frame_new(ctx, err);
|
|
|
|
} else {
|
|
|
|
mfc_debug(2, "No frame decode\n");
|
|
|
|
}
|
|
|
|
/* Mark source buffer as complete */
|
|
|
|
if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY
|
|
|
|
&& !list_empty(&ctx->src_queue)) {
|
|
|
|
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf,
|
|
|
|
list);
|
2012-10-04 01:19:08 +00:00
|
|
|
ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops,
|
|
|
|
get_consumed_stream, dev);
|
|
|
|
if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC &&
|
[media] s5p-mfc: Don't try to resubmit VP8 bitstream buffer for decode
Currently, for formats that are not H264, MFC driver will check
the consumed stream size returned by the firmware and, based on that,
will try to decide whether the bitstream buffer contained more than
one frame. If the size of the buffer is larger than the consumed
stream, it assumes that there are more frames in the buffer and that the
buffer should be resubmitted for decode. This rarely works though and
actually introduces problems, because:
- v7 firmware will always return consumed stream size equal to whatever
the driver passed to it when running decode (which is the size of the whole
buffer), which means we will never try to resubmit, because the firmware
will always tell us that it consumed all the data we passed to it;
- v6 firmware will return the number of consumed bytes, but will not
include the padding ("stuffing") bytes that are allowed after the frame
in VP8. Since there is no way of figuring out how many of those bytes
follow the frame without getting the frame size from IVF headers (or
somewhere else, but not from the stream itself), the driver tries to guess that
padding size is not larger than 4 bytes, which is not always true;
The only way to make it work is to queue only one frame per buffer from
userspace and the check in the kernel is useless and wrong for VP8.
So adding VP8 also along with H264 to disallow re-submitting of buffer
back to hardware for decode.
Signed-off-by: Pawel Osciak <posciak@chromium.org>
Signed-off-by: Arun Kumar K <arun.kk@samsung.com>
Signed-off-by: Kamil Debski <k.debski@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
2014-05-19 12:33:02 +00:00
|
|
|
ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&
|
2012-11-14 12:26:45 +00:00
|
|
|
ctx->consumed_stream + STUFF_BYTE <
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
src_buf->b->vb2_buf.planes[0].bytesused) {
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Run MFC again on the same buffer */
|
|
|
|
mfc_debug(2, "Running again the same buffer\n");
|
|
|
|
ctx->after_packed_pb = 1;
|
|
|
|
} else {
|
|
|
|
mfc_debug(2, "MFC needs next buffer\n");
|
|
|
|
ctx->consumed_stream = 0;
|
2013-01-11 15:29:33 +00:00
|
|
|
if (src_buf->flags & MFC_BUF_FLAG_EOS)
|
|
|
|
ctx->state = MFCINST_FINISHING;
|
2011-06-21 13:51:26 +00:00
|
|
|
list_del(&src_buf->list);
|
|
|
|
ctx->src_queue_cnt--;
|
2012-10-04 01:19:08 +00:00
|
|
|
if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0)
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_buffer_done(&src_buf->b->vb2_buf,
|
|
|
|
VB2_BUF_STATE_ERROR);
|
2011-06-21 13:51:26 +00:00
|
|
|
else
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_buffer_done(&src_buf->b->vb2_buf,
|
|
|
|
VB2_BUF_STATE_DONE);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
leave_handle_frame:
|
|
|
|
if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
|
2013-04-24 12:41:53 +00:00
|
|
|
|| ctx->dst_queue_cnt < ctx->pb_count)
|
2011-06-21 13:51:26 +00:00
|
|
|
clear_work_bit(ctx);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
wake_up_ctx(ctx, reason, err);
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_clock_off();
|
2013-10-04 04:47:19 +00:00
|
|
|
/* if suspending, wake up device and do not try_run again*/
|
|
|
|
if (test_bit(0, &dev->enter_suspend))
|
|
|
|
wake_up_dev(dev, reason, err);
|
|
|
|
else
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Error handling for interrupt */
|
2012-12-21 08:32:59 +00:00
|
|
|
static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
|
|
|
|
struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
|
2011-06-21 13:51:26 +00:00
|
|
|
{
|
|
|
|
mfc_err("Interrupt Error: %08x\n", err);
|
|
|
|
|
2017-09-08 20:37:00 +00:00
|
|
|
if (ctx) {
|
2012-12-21 08:32:59 +00:00
|
|
|
/* Error recovery is dependent on the state of context */
|
|
|
|
switch (ctx->state) {
|
|
|
|
case MFCINST_RES_CHANGE_INIT:
|
|
|
|
case MFCINST_RES_CHANGE_FLUSH:
|
|
|
|
case MFCINST_RES_CHANGE_END:
|
|
|
|
case MFCINST_FINISHING:
|
|
|
|
case MFCINST_FINISHED:
|
|
|
|
case MFCINST_RUNNING:
|
2013-10-21 00:34:01 +00:00
|
|
|
/* It is highly probable that an error occurred
|
2012-12-21 08:32:59 +00:00
|
|
|
* while decoding a frame */
|
|
|
|
clear_work_bit(ctx);
|
|
|
|
ctx->state = MFCINST_ERROR;
|
|
|
|
/* Mark all dst buffers as having an error */
|
2015-12-02 08:22:29 +00:00
|
|
|
s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
|
2012-12-21 08:32:59 +00:00
|
|
|
/* Mark all src buffers as having an error */
|
2015-12-02 08:22:29 +00:00
|
|
|
s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
|
2012-12-21 08:32:59 +00:00
|
|
|
wake_up_ctx(ctx, reason, err);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
clear_work_bit(ctx);
|
|
|
|
ctx->state = MFCINST_ERROR;
|
|
|
|
wake_up_ctx(ctx, reason, err);
|
|
|
|
break;
|
|
|
|
}
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2012-12-21 08:32:59 +00:00
|
|
|
s5p_mfc_clock_off();
|
|
|
|
wake_up_dev(dev, reason, err);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Header parsing interrupt handling */
|
|
|
|
static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
|
|
|
|
unsigned int reason, unsigned int err)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev;
|
|
|
|
|
2017-09-08 20:37:00 +00:00
|
|
|
if (!ctx)
|
2011-06-21 13:51:26 +00:00
|
|
|
return;
|
|
|
|
dev = ctx->dev;
|
|
|
|
if (ctx->c_ops->post_seq_start) {
|
|
|
|
if (ctx->c_ops->post_seq_start(ctx))
|
|
|
|
mfc_err("post_seq_start() failed\n");
|
|
|
|
} else {
|
2012-10-04 01:19:08 +00:00
|
|
|
ctx->img_width = s5p_mfc_hw_call(dev->mfc_ops, get_img_width,
|
|
|
|
dev);
|
|
|
|
ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height,
|
|
|
|
dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
|
2012-10-04 01:19:09 +00:00
|
|
|
|
2013-04-24 12:41:53 +00:00
|
|
|
ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
|
2012-10-04 01:19:08 +00:00
|
|
|
dev);
|
2012-10-04 01:19:11 +00:00
|
|
|
ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count,
|
|
|
|
dev);
|
2019-06-12 13:57:57 +00:00
|
|
|
if (FW_HAS_E_MIN_SCRATCH_BUF(dev))
|
|
|
|
ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops,
|
2018-02-02 12:25:39 +00:00
|
|
|
get_min_scratch_buf_size, dev);
|
2012-01-12 20:49:30 +00:00
|
|
|
if (ctx->img_width == 0 || ctx->img_height == 0)
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->state = MFCINST_ERROR;
|
|
|
|
else
|
|
|
|
ctx->state = MFCINST_HEAD_PARSED;
|
2012-10-04 01:19:11 +00:00
|
|
|
|
|
|
|
if ((ctx->codec_mode == S5P_MFC_CODEC_H264_DEC ||
|
|
|
|
ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) &&
|
|
|
|
!list_empty(&ctx->src_queue)) {
|
|
|
|
struct s5p_mfc_buf *src_buf;
|
|
|
|
src_buf = list_entry(ctx->src_queue.next,
|
|
|
|
struct s5p_mfc_buf, list);
|
|
|
|
if (s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream,
|
|
|
|
dev) <
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
src_buf->b->vb2_buf.planes[0].bytesused)
|
2012-10-04 01:19:11 +00:00
|
|
|
ctx->head_processed = 0;
|
|
|
|
else
|
|
|
|
ctx->head_processed = 1;
|
|
|
|
} else {
|
|
|
|
ctx->head_processed = 1;
|
|
|
|
}
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
clear_work_bit(ctx);
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_clock_off();
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
wake_up_ctx(ctx, reason, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Header parsing interrupt handling */
|
|
|
|
static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
|
|
|
|
unsigned int reason, unsigned int err)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_buf *src_buf;
|
|
|
|
struct s5p_mfc_dev *dev;
|
|
|
|
|
2017-09-08 20:37:00 +00:00
|
|
|
if (!ctx)
|
2011-06-21 13:51:26 +00:00
|
|
|
return;
|
|
|
|
dev = ctx->dev;
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->int_type = reason;
|
|
|
|
ctx->int_err = err;
|
|
|
|
ctx->int_cond = 1;
|
2012-08-14 09:13:40 +00:00
|
|
|
clear_work_bit(ctx);
|
2011-06-21 13:51:26 +00:00
|
|
|
if (err == 0) {
|
|
|
|
ctx->state = MFCINST_RUNNING;
|
2012-10-04 01:19:11 +00:00
|
|
|
if (!ctx->dpb_flush_flag && ctx->head_processed) {
|
2011-06-21 13:51:26 +00:00
|
|
|
if (!list_empty(&ctx->src_queue)) {
|
|
|
|
src_buf = list_entry(ctx->src_queue.next,
|
|
|
|
struct s5p_mfc_buf, list);
|
|
|
|
list_del(&src_buf->list);
|
|
|
|
ctx->src_queue_cnt--;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_buffer_done(&src_buf->b->vb2_buf,
|
2011-06-21 13:51:26 +00:00
|
|
|
VB2_BUF_STATE_DONE);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ctx->dpb_flush_flag = 0;
|
|
|
|
}
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
s5p_mfc_clock_off();
|
|
|
|
|
|
|
|
wake_up(&ctx->queue);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
} else {
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
s5p_mfc_clock_off();
|
|
|
|
|
|
|
|
wake_up(&ctx->queue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-07 10:15:31 +00:00
|
|
|
static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
|
2012-08-21 11:05:32 +00:00
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
struct s5p_mfc_buf *mb_entry;
|
|
|
|
|
2013-05-28 06:26:16 +00:00
|
|
|
mfc_debug(2, "Stream completed\n");
|
2012-08-21 11:05:32 +00:00
|
|
|
|
|
|
|
ctx->state = MFCINST_FINISHED;
|
|
|
|
|
|
|
|
if (!list_empty(&ctx->dst_queue)) {
|
|
|
|
mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf,
|
|
|
|
list);
|
|
|
|
list_del(&mb_entry->list);
|
|
|
|
ctx->dst_queue_cnt--;
|
[media] media: videobuf2: Restructure vb2_buffer
Remove v4l2 stuff - v4l2_buf, v4l2_plane - from struct vb2_buffer.
Add new member variables - bytesused, length, offset, userptr, fd,
data_offset - to struct vb2_plane in order to cover all information
of v4l2_plane.
struct vb2_plane {
<snip>
unsigned int bytesused;
unsigned int length;
union {
unsigned int offset;
unsigned long userptr;
int fd;
} m;
unsigned int data_offset;
}
Replace v4l2_buf with new member variables - index, type, memory - which
are common fields for buffer management.
struct vb2_buffer {
<snip>
unsigned int index;
unsigned int type;
unsigned int memory;
unsigned int num_planes;
struct vb2_plane planes[VIDEO_MAX_PLANES];
<snip>
};
v4l2 specific fields - flags, field, timestamp, timecode,
sequence - are moved to vb2_v4l2_buffer in videobuf2-v4l2.c
struct vb2_v4l2_buffer {
struct vb2_buffer vb2_buf;
__u32 flags;
__u32 field;
struct timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
};
Signed-off-by: Junghak Sung <jh1009.sung@samsung.com>
Signed-off-by: Geunyoung Kim <nenggun.kim@samsung.com>
Acked-by: Seung-Woo Kim <sw0312.kim@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2015-09-22 13:30:30 +00:00
|
|
|
vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0);
|
|
|
|
vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE);
|
2012-08-21 11:05:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
clear_work_bit(ctx);
|
|
|
|
|
2013-01-22 04:00:06 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2012-08-21 11:05:32 +00:00
|
|
|
|
|
|
|
s5p_mfc_clock_off();
|
|
|
|
wake_up(&ctx->queue);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2012-08-21 11:05:32 +00:00
|
|
|
}
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Interrupt processing */
|
|
|
|
static irqreturn_t s5p_mfc_irq(int irq, void *priv)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = priv;
|
|
|
|
struct s5p_mfc_ctx *ctx;
|
|
|
|
unsigned int reason;
|
|
|
|
unsigned int err;
|
|
|
|
|
|
|
|
mfc_debug_enter();
|
|
|
|
/* Reset the timeout watchdog */
|
|
|
|
atomic_set(&dev->watchdog_cnt, 0);
|
2015-12-02 08:22:31 +00:00
|
|
|
spin_lock(&dev->irqlock);
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx = dev->ctx[dev->curr_ctx];
|
|
|
|
/* Get the reason of interrupt and the error code */
|
2012-10-04 01:19:08 +00:00
|
|
|
reason = s5p_mfc_hw_call(dev->mfc_ops, get_int_reason, dev);
|
|
|
|
err = s5p_mfc_hw_call(dev->mfc_ops, get_int_err, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
|
|
|
|
switch (reason) {
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_ERR_RET:
|
2013-10-21 00:34:01 +00:00
|
|
|
/* An error has occurred */
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ctx->state == MFCINST_RUNNING &&
|
2016-11-10 10:31:22 +00:00
|
|
|
(s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >=
|
|
|
|
dev->warn_start ||
|
|
|
|
err == S5P_FIMV_ERR_NO_VALID_SEQ_HDR ||
|
|
|
|
err == S5P_FIMV_ERR_INCOMPLETE_FRAME ||
|
|
|
|
err == S5P_FIMV_ERR_TIMEOUT))
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_handle_frame(ctx, reason, err);
|
|
|
|
else
|
2012-12-21 08:32:59 +00:00
|
|
|
s5p_mfc_handle_error(dev, ctx, reason, err);
|
2011-06-21 13:51:26 +00:00
|
|
|
clear_bit(0, &dev->enter_suspend);
|
|
|
|
break;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_SLICE_DONE_RET:
|
|
|
|
case S5P_MFC_R2H_CMD_FIELD_DONE_RET:
|
|
|
|
case S5P_MFC_R2H_CMD_FRAME_DONE_RET:
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ctx->c_ops->post_frame_start) {
|
|
|
|
if (ctx->c_ops->post_frame_start(ctx))
|
|
|
|
mfc_err("post_frame_start() failed\n");
|
2015-10-07 10:15:31 +00:00
|
|
|
|
|
|
|
if (ctx->state == MFCINST_FINISHING &&
|
|
|
|
list_empty(&ctx->ref_queue)) {
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2015-10-07 10:15:31 +00:00
|
|
|
s5p_mfc_handle_stream_complete(ctx);
|
|
|
|
break;
|
|
|
|
}
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2014-10-21 11:07:01 +00:00
|
|
|
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_clock_off();
|
2017-02-23 11:43:27 +00:00
|
|
|
wake_up_ctx(ctx, reason, err);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
} else {
|
|
|
|
s5p_mfc_handle_frame(ctx, reason, err);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_SEQ_DONE_RET:
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_handle_seq_done(ctx, reason, err);
|
|
|
|
break;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET:
|
|
|
|
ctx->inst_no = s5p_mfc_hw_call(dev->mfc_ops, get_inst_no, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->state = MFCINST_GOT_INST;
|
|
|
|
goto irq_cleanup_hw;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
|
2014-05-19 12:33:00 +00:00
|
|
|
ctx->inst_no = MFC_NO_INSTANCE_SET;
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->state = MFCINST_FREE;
|
|
|
|
goto irq_cleanup_hw;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_SYS_INIT_RET:
|
|
|
|
case S5P_MFC_R2H_CMD_FW_STATUS_RET:
|
|
|
|
case S5P_MFC_R2H_CMD_SLEEP_RET:
|
|
|
|
case S5P_MFC_R2H_CMD_WAKEUP_RET:
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ctx)
|
|
|
|
clear_work_bit(ctx);
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
clear_bit(0, &dev->hw_lock);
|
|
|
|
clear_bit(0, &dev->enter_suspend);
|
2017-02-23 11:43:27 +00:00
|
|
|
wake_up_dev(dev, reason, err);
|
2011-06-21 13:51:26 +00:00
|
|
|
break;
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_INIT_BUFFERS_RET:
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_handle_init_buffers(ctx, reason, err);
|
|
|
|
break;
|
2012-08-21 11:05:32 +00:00
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET:
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2015-10-07 10:15:31 +00:00
|
|
|
ctx->int_type = reason;
|
|
|
|
ctx->int_err = err;
|
|
|
|
s5p_mfc_handle_stream_complete(ctx);
|
2012-08-21 11:05:32 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-22 09:15:55 +00:00
|
|
|
case S5P_MFC_R2H_CMD_DPB_FLUSH_RET:
|
|
|
|
ctx->state = MFCINST_RUNNING;
|
|
|
|
goto irq_cleanup_hw;
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
default:
|
|
|
|
mfc_debug(2, "Unknown int reason\n");
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
2015-12-02 08:22:31 +00:00
|
|
|
spin_unlock(&dev->irqlock);
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug_leave();
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
irq_cleanup_hw:
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->int_type = reason;
|
|
|
|
ctx->int_err = err;
|
|
|
|
ctx->int_cond = 1;
|
|
|
|
if (test_and_clear_bit(0, &dev->hw_lock) == 0)
|
|
|
|
mfc_err("Failed to unlock hw\n");
|
|
|
|
|
|
|
|
s5p_mfc_clock_off();
|
2017-02-23 11:43:27 +00:00
|
|
|
clear_work_bit(ctx);
|
|
|
|
wake_up(&ctx->queue);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
2015-12-02 08:22:32 +00:00
|
|
|
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
|
2015-12-02 08:22:31 +00:00
|
|
|
spin_unlock(&dev->irqlock);
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug(2, "Exit via irq_cleanup_hw\n");
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Open an MFC node */
|
|
|
|
static int s5p_mfc_open(struct file *file)
|
|
|
|
{
|
2013-12-03 13:12:51 +00:00
|
|
|
struct video_device *vdev = video_devdata(file);
|
2011-06-21 13:51:26 +00:00
|
|
|
struct s5p_mfc_dev *dev = video_drvdata(file);
|
|
|
|
struct s5p_mfc_ctx *ctx = NULL;
|
|
|
|
struct vb2_queue *q;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
mfc_debug_enter();
|
2012-06-24 10:13:33 +00:00
|
|
|
if (mutex_lock_interruptible(&dev->mfc_mutex))
|
|
|
|
return -ERESTARTSYS;
|
2011-06-21 13:51:26 +00:00
|
|
|
dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
|
|
|
|
/* Allocate memory for context */
|
2012-08-17 06:22:55 +00:00
|
|
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
2011-06-21 13:51:26 +00:00
|
|
|
if (!ctx) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_alloc;
|
|
|
|
}
|
2017-02-03 14:05:18 +00:00
|
|
|
init_waitqueue_head(&ctx->queue);
|
2014-07-23 04:06:12 +00:00
|
|
|
v4l2_fh_init(&ctx->fh, vdev);
|
2011-06-21 13:51:26 +00:00
|
|
|
file->private_data = &ctx->fh;
|
|
|
|
v4l2_fh_add(&ctx->fh);
|
|
|
|
ctx->dev = dev;
|
|
|
|
INIT_LIST_HEAD(&ctx->src_queue);
|
|
|
|
INIT_LIST_HEAD(&ctx->dst_queue);
|
|
|
|
ctx->src_queue_cnt = 0;
|
|
|
|
ctx->dst_queue_cnt = 0;
|
|
|
|
/* Get context number */
|
|
|
|
ctx->num = 0;
|
|
|
|
while (dev->ctx[ctx->num]) {
|
|
|
|
ctx->num++;
|
|
|
|
if (ctx->num >= MFC_NUM_CONTEXTS) {
|
2016-07-14 14:40:22 +00:00
|
|
|
mfc_debug(2, "Too many open contexts\n");
|
2011-06-21 13:51:26 +00:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto err_no_ctx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Mark context as idle */
|
2012-08-14 09:13:40 +00:00
|
|
|
clear_work_bit_irqsave(ctx);
|
2011-06-21 13:51:26 +00:00
|
|
|
dev->ctx[ctx->num] = ctx;
|
2013-12-03 13:12:51 +00:00
|
|
|
if (vdev == dev->vfd_dec) {
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->type = MFCINST_DECODER;
|
|
|
|
ctx->c_ops = get_dec_codec_ops();
|
2012-10-04 01:19:08 +00:00
|
|
|
s5p_mfc_dec_init(ctx);
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Setup ctrl handler */
|
|
|
|
ret = s5p_mfc_dec_ctrls_setup(ctx);
|
|
|
|
if (ret) {
|
|
|
|
mfc_err("Failed to setup mfc controls\n");
|
|
|
|
goto err_ctrls_setup;
|
|
|
|
}
|
2013-12-03 13:12:51 +00:00
|
|
|
} else if (vdev == dev->vfd_enc) {
|
2011-06-21 13:51:26 +00:00
|
|
|
ctx->type = MFCINST_ENCODER;
|
|
|
|
ctx->c_ops = get_enc_codec_ops();
|
|
|
|
/* only for encoder */
|
|
|
|
INIT_LIST_HEAD(&ctx->ref_queue);
|
|
|
|
ctx->ref_queue_cnt = 0;
|
2012-10-04 01:19:08 +00:00
|
|
|
s5p_mfc_enc_init(ctx);
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Setup ctrl handler */
|
|
|
|
ret = s5p_mfc_enc_ctrls_setup(ctx);
|
|
|
|
if (ret) {
|
|
|
|
mfc_err("Failed to setup mfc controls\n");
|
|
|
|
goto err_ctrls_setup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err_bad_node;
|
|
|
|
}
|
|
|
|
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
|
2014-05-19 12:33:00 +00:00
|
|
|
ctx->inst_no = MFC_NO_INSTANCE_SET;
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Load firmware if this is the first instance */
|
|
|
|
if (dev->num_inst == 1) {
|
|
|
|
dev->watchdog_timer.expires = jiffies +
|
|
|
|
msecs_to_jiffies(MFC_WATCHDOG_INTERVAL);
|
|
|
|
add_timer(&dev->watchdog_timer);
|
|
|
|
ret = s5p_mfc_power_on();
|
|
|
|
if (ret < 0) {
|
|
|
|
mfc_err("power on failed\n");
|
|
|
|
goto err_pwr_enable;
|
|
|
|
}
|
|
|
|
s5p_mfc_clock_on();
|
2013-01-03 14:02:07 +00:00
|
|
|
ret = s5p_mfc_load_firmware(dev);
|
|
|
|
if (ret) {
|
|
|
|
s5p_mfc_clock_off();
|
|
|
|
goto err_load_fw;
|
|
|
|
}
|
2011-06-21 13:51:26 +00:00
|
|
|
/* Init the FW */
|
|
|
|
ret = s5p_mfc_init_hw(dev);
|
2013-01-03 14:02:07 +00:00
|
|
|
s5p_mfc_clock_off();
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ret)
|
|
|
|
goto err_init_hw;
|
|
|
|
}
|
|
|
|
/* Init videobuf2 queue for CAPTURE */
|
|
|
|
q = &ctx->vq_dst;
|
|
|
|
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
|
|
|
q->drv_priv = &ctx->fh;
|
2014-11-26 22:42:32 +00:00
|
|
|
q->lock = &dev->mfc_mutex;
|
2013-12-03 13:12:51 +00:00
|
|
|
if (vdev == dev->vfd_dec) {
|
2011-06-21 13:51:26 +00:00
|
|
|
q->io_modes = VB2_MMAP;
|
|
|
|
q->ops = get_dec_queue_ops();
|
2013-12-03 13:12:51 +00:00
|
|
|
} else if (vdev == dev->vfd_enc) {
|
2011-06-21 13:51:26 +00:00
|
|
|
q->io_modes = VB2_MMAP | VB2_USERPTR;
|
|
|
|
q->ops = get_enc_queue_ops();
|
|
|
|
} else {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err_queue_init;
|
|
|
|
}
|
2016-11-16 09:04:50 +00:00
|
|
|
/*
|
|
|
|
* We'll do mostly sequential access, so sacrifice TLB efficiency for
|
|
|
|
* faster allocation.
|
|
|
|
*/
|
|
|
|
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
|
2014-10-21 14:55:35 +00:00
|
|
|
q->mem_ops = &vb2_dma_contig_memops;
|
2014-02-25 22:12:19 +00:00
|
|
|
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
2011-06-21 13:51:26 +00:00
|
|
|
ret = vb2_queue_init(q);
|
|
|
|
if (ret) {
|
|
|
|
mfc_err("Failed to initialize videobuf2 queue(capture)\n");
|
|
|
|
goto err_queue_init;
|
|
|
|
}
|
|
|
|
/* Init videobuf2 queue for OUTPUT */
|
|
|
|
q = &ctx->vq_src;
|
|
|
|
q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
|
|
|
q->drv_priv = &ctx->fh;
|
2015-03-03 14:32:58 +00:00
|
|
|
q->lock = &dev->mfc_mutex;
|
2013-12-03 13:12:51 +00:00
|
|
|
if (vdev == dev->vfd_dec) {
|
2011-06-21 13:51:26 +00:00
|
|
|
q->io_modes = VB2_MMAP;
|
|
|
|
q->ops = get_dec_queue_ops();
|
2013-12-03 13:12:51 +00:00
|
|
|
} else if (vdev == dev->vfd_enc) {
|
2011-06-21 13:51:26 +00:00
|
|
|
q->io_modes = VB2_MMAP | VB2_USERPTR;
|
|
|
|
q->ops = get_enc_queue_ops();
|
|
|
|
} else {
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto err_queue_init;
|
|
|
|
}
|
2015-02-23 12:26:19 +00:00
|
|
|
/* One way to indicate end-of-stream for MFC is to set the
|
|
|
|
* bytesused == 0. However by default videobuf2 handles bytesused
|
|
|
|
* equal to 0 as a special case and changes its value to the size
|
|
|
|
* of the buffer. Set the allow_zero_bytesused flag so that videobuf2
|
|
|
|
* will keep the value of bytesused intact.
|
|
|
|
*/
|
|
|
|
q->allow_zero_bytesused = 1;
|
2016-11-16 09:04:50 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We'll do mostly sequential access, so sacrifice TLB efficiency for
|
|
|
|
* faster allocation.
|
|
|
|
*/
|
|
|
|
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
|
2014-10-21 14:55:35 +00:00
|
|
|
q->mem_ops = &vb2_dma_contig_memops;
|
2014-02-25 22:12:19 +00:00
|
|
|
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
2011-06-21 13:51:26 +00:00
|
|
|
ret = vb2_queue_init(q);
|
|
|
|
if (ret) {
|
|
|
|
mfc_err("Failed to initialize videobuf2 queue(output)\n");
|
|
|
|
goto err_queue_init;
|
|
|
|
}
|
2012-06-24 10:13:33 +00:00
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug_leave();
|
|
|
|
return ret;
|
2013-10-21 00:34:01 +00:00
|
|
|
/* Deinit when failure occurred */
|
2011-06-21 13:51:26 +00:00
|
|
|
err_queue_init:
|
2013-01-03 14:02:07 +00:00
|
|
|
if (dev->num_inst == 1)
|
|
|
|
s5p_mfc_deinit_hw(dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
err_init_hw:
|
2013-01-03 14:02:07 +00:00
|
|
|
err_load_fw:
|
2011-06-21 13:51:26 +00:00
|
|
|
err_pwr_enable:
|
|
|
|
if (dev->num_inst == 1) {
|
|
|
|
if (s5p_mfc_power_off() < 0)
|
|
|
|
mfc_err("power off failed\n");
|
2012-11-22 13:00:28 +00:00
|
|
|
del_timer_sync(&dev->watchdog_timer);
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
err_ctrls_setup:
|
|
|
|
s5p_mfc_dec_ctrls_delete(ctx);
|
|
|
|
err_bad_node:
|
2012-11-22 13:00:28 +00:00
|
|
|
dev->ctx[ctx->num] = NULL;
|
2011-06-21 13:51:26 +00:00
|
|
|
err_no_ctx:
|
|
|
|
v4l2_fh_del(&ctx->fh);
|
|
|
|
v4l2_fh_exit(&ctx->fh);
|
|
|
|
kfree(ctx);
|
|
|
|
err_alloc:
|
|
|
|
dev->num_inst--;
|
2012-06-24 10:13:33 +00:00
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug_leave();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Release MFC context */
|
|
|
|
static int s5p_mfc_release(struct file *file)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
|
2016-07-08 22:29:25 +00:00
|
|
|
/* if dev is null, do cleanup that doesn't need dev */
|
2011-06-21 13:51:26 +00:00
|
|
|
mfc_debug_enter();
|
2016-07-08 22:29:25 +00:00
|
|
|
if (dev)
|
|
|
|
mutex_lock(&dev->mfc_mutex);
|
2011-06-21 13:51:26 +00:00
|
|
|
vb2_queue_release(&ctx->vq_src);
|
|
|
|
vb2_queue_release(&ctx->vq_dst);
|
2016-07-08 22:29:25 +00:00
|
|
|
if (dev) {
|
2016-11-14 14:09:26 +00:00
|
|
|
s5p_mfc_clock_on();
|
|
|
|
|
2016-07-08 22:29:25 +00:00
|
|
|
/* Mark context as idle */
|
|
|
|
clear_work_bit_irqsave(ctx);
|
|
|
|
/*
|
|
|
|
* If instance was initialised and not yet freed,
|
|
|
|
* return instance and free resources
|
|
|
|
*/
|
|
|
|
if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) {
|
|
|
|
mfc_debug(2, "Has to free instance\n");
|
|
|
|
s5p_mfc_close_mfc_inst(dev, ctx);
|
|
|
|
}
|
|
|
|
/* hardware locking scheme */
|
|
|
|
if (dev->curr_ctx == ctx->num)
|
|
|
|
clear_bit(0, &dev->hw_lock);
|
|
|
|
dev->num_inst--;
|
|
|
|
if (dev->num_inst == 0) {
|
|
|
|
mfc_debug(2, "Last instance\n");
|
|
|
|
s5p_mfc_deinit_hw(dev);
|
|
|
|
del_timer_sync(&dev->watchdog_timer);
|
2016-11-16 09:04:53 +00:00
|
|
|
s5p_mfc_clock_off();
|
2016-07-08 22:29:25 +00:00
|
|
|
if (s5p_mfc_power_off() < 0)
|
|
|
|
mfc_err("Power off failed\n");
|
2016-11-16 09:04:53 +00:00
|
|
|
} else {
|
|
|
|
mfc_debug(2, "Shutting down clock\n");
|
|
|
|
s5p_mfc_clock_off();
|
2016-07-08 22:29:25 +00:00
|
|
|
}
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
2016-07-08 22:29:25 +00:00
|
|
|
if (dev)
|
|
|
|
dev->ctx[ctx->num] = NULL;
|
2011-06-21 13:51:26 +00:00
|
|
|
s5p_mfc_dec_ctrls_delete(ctx);
|
|
|
|
v4l2_fh_del(&ctx->fh);
|
2016-07-08 22:29:25 +00:00
|
|
|
/* vdev is gone if dev is null */
|
|
|
|
if (dev)
|
|
|
|
v4l2_fh_exit(&ctx->fh);
|
2011-06-21 13:51:26 +00:00
|
|
|
kfree(ctx);
|
|
|
|
mfc_debug_leave();
|
2016-07-08 22:29:25 +00:00
|
|
|
if (dev)
|
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Poll */
|
2017-07-03 07:02:56 +00:00
|
|
|
static __poll_t s5p_mfc_poll(struct file *file,
|
2011-06-21 13:51:26 +00:00
|
|
|
struct poll_table_struct *wait)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
struct s5p_mfc_dev *dev = ctx->dev;
|
|
|
|
struct vb2_queue *src_q, *dst_q;
|
|
|
|
struct vb2_buffer *src_vb = NULL, *dst_vb = NULL;
|
2017-07-03 07:02:56 +00:00
|
|
|
__poll_t rc = 0;
|
2011-06-21 13:51:26 +00:00
|
|
|
unsigned long flags;
|
|
|
|
|
2012-06-24 10:13:33 +00:00
|
|
|
mutex_lock(&dev->mfc_mutex);
|
2011-06-21 13:51:26 +00:00
|
|
|
src_q = &ctx->vq_src;
|
|
|
|
dst_q = &ctx->vq_dst;
|
|
|
|
/*
|
|
|
|
* There has to be at least one buffer queued on each queued_list, which
|
|
|
|
* means either in driver already or waiting for driver to claim it
|
|
|
|
* and start processing.
|
|
|
|
*/
|
|
|
|
if ((!src_q->streaming || list_empty(&src_q->queued_list))
|
|
|
|
&& (!dst_q->streaming || list_empty(&dst_q->queued_list))) {
|
2018-02-11 22:34:03 +00:00
|
|
|
rc = EPOLLERR;
|
2011-06-21 13:51:26 +00:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
2012-08-21 11:05:32 +00:00
|
|
|
poll_wait(file, &ctx->fh.wait, wait);
|
2011-06-21 13:51:26 +00:00
|
|
|
poll_wait(file, &src_q->done_wq, wait);
|
|
|
|
poll_wait(file, &dst_q->done_wq, wait);
|
|
|
|
mutex_lock(&dev->mfc_mutex);
|
2012-08-21 11:05:32 +00:00
|
|
|
if (v4l2_event_pending(&ctx->fh))
|
2018-02-11 22:34:03 +00:00
|
|
|
rc |= EPOLLPRI;
|
2011-06-21 13:51:26 +00:00
|
|
|
spin_lock_irqsave(&src_q->done_lock, flags);
|
|
|
|
if (!list_empty(&src_q->done_list))
|
|
|
|
src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer,
|
|
|
|
done_entry);
|
|
|
|
if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE
|
|
|
|
|| src_vb->state == VB2_BUF_STATE_ERROR))
|
2018-02-11 22:34:03 +00:00
|
|
|
rc |= EPOLLOUT | EPOLLWRNORM;
|
2011-06-21 13:51:26 +00:00
|
|
|
spin_unlock_irqrestore(&src_q->done_lock, flags);
|
|
|
|
spin_lock_irqsave(&dst_q->done_lock, flags);
|
|
|
|
if (!list_empty(&dst_q->done_list))
|
|
|
|
dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer,
|
|
|
|
done_entry);
|
|
|
|
if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE
|
|
|
|
|| dst_vb->state == VB2_BUF_STATE_ERROR))
|
2018-02-11 22:34:03 +00:00
|
|
|
rc |= EPOLLIN | EPOLLRDNORM;
|
2011-06-21 13:51:26 +00:00
|
|
|
spin_unlock_irqrestore(&dst_q->done_lock, flags);
|
|
|
|
end:
|
2012-06-24 10:13:33 +00:00
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
2011-06-21 13:51:26 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mmap */
|
|
|
|
static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma)
|
|
|
|
{
|
|
|
|
struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data);
|
|
|
|
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
|
|
|
int ret;
|
2012-06-24 10:13:33 +00:00
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
if (offset < DST_QUEUE_OFF_BASE) {
|
|
|
|
mfc_debug(2, "mmaping source\n");
|
|
|
|
ret = vb2_mmap(&ctx->vq_src, vma);
|
|
|
|
} else { /* capture */
|
|
|
|
mfc_debug(2, "mmaping destination\n");
|
|
|
|
vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT);
|
|
|
|
ret = vb2_mmap(&ctx->vq_dst, vma);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* v4l2 ops */
|
|
|
|
static const struct v4l2_file_operations s5p_mfc_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.open = s5p_mfc_open,
|
|
|
|
.release = s5p_mfc_release,
|
|
|
|
.poll = s5p_mfc_poll,
|
|
|
|
.unlocked_ioctl = video_ioctl2,
|
|
|
|
.mmap = s5p_mfc_mmap,
|
|
|
|
};
|
|
|
|
|
2016-05-24 13:31:26 +00:00
|
|
|
/* DMA memory related helper functions */
|
2016-05-03 20:27:17 +00:00
|
|
|
static void s5p_mfc_memdev_release(struct device *dev)
|
|
|
|
{
|
2016-05-24 13:31:26 +00:00
|
|
|
of_reserved_mem_device_release(dev);
|
2016-05-03 20:27:17 +00:00
|
|
|
}
|
|
|
|
|
2016-05-24 13:31:26 +00:00
|
|
|
static struct device *s5p_mfc_alloc_memdev(struct device *dev,
|
|
|
|
const char *name, unsigned int idx)
|
2013-01-18 18:42:34 +00:00
|
|
|
{
|
2016-05-24 13:31:26 +00:00
|
|
|
struct device *child;
|
|
|
|
int ret;
|
2013-01-18 18:42:34 +00:00
|
|
|
|
2017-09-08 20:30:09 +00:00
|
|
|
child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL);
|
2016-05-24 13:31:26 +00:00
|
|
|
if (!child)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
device_initialize(child);
|
|
|
|
dev_set_name(child, "%s:%s", dev_name(dev), name);
|
|
|
|
child->parent = dev;
|
|
|
|
child->coherent_dma_mask = dev->coherent_dma_mask;
|
|
|
|
child->dma_mask = dev->dma_mask;
|
|
|
|
child->release = s5p_mfc_memdev_release;
|
2020-05-28 14:03:26 +00:00
|
|
|
child->dma_parms = devm_kzalloc(dev, sizeof(*child->dma_parms),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!child->dma_parms)
|
|
|
|
goto err;
|
2016-05-24 13:31:26 +00:00
|
|
|
|
2019-01-14 15:14:14 +00:00
|
|
|
/*
|
|
|
|
* The memdevs are not proper OF platform devices, so in order for them
|
|
|
|
* to be treated as valid DMA masters we need a bit of a hack to force
|
|
|
|
* them to inherit the MFC node's DMA configuration.
|
|
|
|
*/
|
|
|
|
of_dma_configure(child, dev->of_node, true);
|
|
|
|
|
2016-05-24 13:31:26 +00:00
|
|
|
if (device_add(child) == 0) {
|
|
|
|
ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
|
|
|
|
idx);
|
|
|
|
if (ret == 0)
|
|
|
|
return child;
|
2016-09-16 06:14:33 +00:00
|
|
|
device_del(child);
|
2013-01-18 18:42:34 +00:00
|
|
|
}
|
2020-05-28 14:03:26 +00:00
|
|
|
err:
|
2016-05-24 13:31:26 +00:00
|
|
|
put_device(child);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-01-18 18:42:34 +00:00
|
|
|
|
2017-02-20 13:11:42 +00:00
|
|
|
static int s5p_mfc_configure_2port_memory(struct s5p_mfc_dev *mfc_dev)
|
2016-05-24 13:31:26 +00:00
|
|
|
{
|
|
|
|
struct device *dev = &mfc_dev->plat_dev->dev;
|
2017-02-08 14:18:54 +00:00
|
|
|
void *bank2_virt;
|
|
|
|
dma_addr_t bank2_dma_addr;
|
|
|
|
unsigned long align_size = 1 << MFC_BASE_ALIGN_ORDER;
|
|
|
|
int ret;
|
2016-05-03 20:27:16 +00:00
|
|
|
|
2016-05-24 13:31:26 +00:00
|
|
|
/*
|
|
|
|
* Create and initialize virtual devices for accessing
|
|
|
|
* reserved memory regions.
|
|
|
|
*/
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->mem_dev[BANK_L_CTX] = s5p_mfc_alloc_memdev(dev, "left",
|
|
|
|
BANK_L_CTX);
|
|
|
|
if (!mfc_dev->mem_dev[BANK_L_CTX])
|
2016-05-24 13:31:26 +00:00
|
|
|
return -ENODEV;
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->mem_dev[BANK_R_CTX] = s5p_mfc_alloc_memdev(dev, "right",
|
|
|
|
BANK_R_CTX);
|
|
|
|
if (!mfc_dev->mem_dev[BANK_R_CTX]) {
|
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
|
2016-05-24 13:31:26 +00:00
|
|
|
return -ENODEV;
|
2013-01-18 18:42:34 +00:00
|
|
|
}
|
2016-05-24 13:31:26 +00:00
|
|
|
|
2017-02-08 14:18:54 +00:00
|
|
|
/* Allocate memory for firmware and initialize both banks addresses */
|
|
|
|
ret = s5p_mfc_alloc_firmware(mfc_dev);
|
|
|
|
if (ret) {
|
2017-03-20 10:49:24 +00:00
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
|
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
|
2017-02-08 14:18:54 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->fw_buf.dma;
|
2017-02-08 14:18:54 +00:00
|
|
|
|
2017-03-20 10:49:24 +00:00
|
|
|
bank2_virt = dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX],
|
|
|
|
align_size, &bank2_dma_addr, GFP_KERNEL);
|
2017-02-08 14:18:54 +00:00
|
|
|
if (!bank2_virt) {
|
|
|
|
mfc_err("Allocating bank2 base failed\n");
|
|
|
|
s5p_mfc_release_firmware(mfc_dev);
|
2017-03-20 10:49:24 +00:00
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
|
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
|
2017-02-08 14:18:54 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Valid buffers passed to MFC encoder with LAST_FRAME command
|
|
|
|
* should not have address of bank2 - MFC will treat it as a null frame.
|
|
|
|
* To avoid such situation we set bank2 address below the pool address.
|
|
|
|
*/
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->dma_base[BANK_R_CTX] = bank2_dma_addr - align_size;
|
2017-02-08 14:18:54 +00:00
|
|
|
|
2017-03-20 10:49:24 +00:00
|
|
|
dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt,
|
2017-02-08 14:18:54 +00:00
|
|
|
bank2_dma_addr);
|
|
|
|
|
2017-03-20 10:49:24 +00:00
|
|
|
vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX],
|
2017-02-08 11:29:39 +00:00
|
|
|
DMA_BIT_MASK(32));
|
2017-03-20 10:49:24 +00:00
|
|
|
vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX],
|
2017-02-08 11:29:39 +00:00
|
|
|
DMA_BIT_MASK(32));
|
|
|
|
|
2013-01-18 18:42:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-20 13:11:42 +00:00
|
|
|
static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev)
|
2016-05-24 13:31:26 +00:00
|
|
|
{
|
2017-03-20 10:49:24 +00:00
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
|
|
|
|
device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
|
|
|
|
vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX]);
|
|
|
|
vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX]);
|
2017-02-20 13:11:42 +00:00
|
|
|
}
|
2016-05-24 13:31:27 +00:00
|
|
|
|
2017-02-20 13:11:42 +00:00
|
|
|
static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
|
|
|
|
{
|
|
|
|
struct device *dev = &mfc_dev->plat_dev->dev;
|
2017-02-13 10:58:14 +00:00
|
|
|
unsigned long mem_size = SZ_4M;
|
2017-02-13 10:57:11 +00:00
|
|
|
unsigned int bitmap_size;
|
2017-02-08 14:18:54 +00:00
|
|
|
|
2017-02-13 10:58:14 +00:00
|
|
|
if (IS_ENABLED(CONFIG_DMA_CMA) || exynos_is_iommu_available(dev))
|
|
|
|
mem_size = SZ_8M;
|
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
if (mfc_mem_size)
|
|
|
|
mem_size = memparse(mfc_mem_size, NULL);
|
|
|
|
|
|
|
|
bitmap_size = BITS_TO_LONGS(mem_size >> PAGE_SHIFT) * sizeof(long);
|
|
|
|
|
|
|
|
mfc_dev->mem_bitmap = kzalloc(bitmap_size, GFP_KERNEL);
|
2017-02-13 10:47:51 +00:00
|
|
|
if (!mfc_dev->mem_bitmap)
|
2017-02-13 10:57:11 +00:00
|
|
|
return -ENOMEM;
|
2016-05-24 13:31:27 +00:00
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size,
|
|
|
|
&mfc_dev->mem_base, GFP_KERNEL);
|
|
|
|
if (!mfc_dev->mem_virt) {
|
|
|
|
kfree(mfc_dev->mem_bitmap);
|
|
|
|
dev_err(dev, "failed to preallocate %ld MiB for the firmware and context buffers\n",
|
|
|
|
(mem_size / SZ_1M));
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
mfc_dev->mem_size = mem_size;
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->mem_base;
|
|
|
|
mfc_dev->dma_base[BANK_R_CTX] = mfc_dev->mem_base;
|
2017-02-13 10:57:11 +00:00
|
|
|
|
2017-02-13 10:47:51 +00:00
|
|
|
/*
|
|
|
|
* MFC hardware cannot handle 0 as a base address, so mark first 128K
|
|
|
|
* as used (to keep required base alignment) and adjust base address
|
|
|
|
*/
|
|
|
|
if (mfc_dev->mem_base == (dma_addr_t)0) {
|
|
|
|
unsigned int offset = 1 << MFC_BASE_ALIGN_ORDER;
|
|
|
|
|
|
|
|
bitmap_set(mfc_dev->mem_bitmap, 0, offset >> PAGE_SHIFT);
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->dma_base[BANK_L_CTX] += offset;
|
|
|
|
mfc_dev->dma_base[BANK_R_CTX] += offset;
|
2017-02-13 10:47:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
/* Firmware allocation cannot fail in this case */
|
|
|
|
s5p_mfc_alloc_firmware(mfc_dev);
|
|
|
|
|
2017-03-20 10:49:24 +00:00
|
|
|
mfc_dev->mem_dev[BANK_L_CTX] = mfc_dev->mem_dev[BANK_R_CTX] = dev;
|
2017-02-20 13:11:42 +00:00
|
|
|
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
|
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
dev_info(dev, "preallocated %ld MiB buffer for the firmware and context buffers\n",
|
|
|
|
(mem_size / SZ_1M));
|
|
|
|
|
2017-02-20 13:11:42 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev)
|
|
|
|
{
|
|
|
|
struct device *dev = &mfc_dev->plat_dev->dev;
|
|
|
|
|
2017-02-13 10:57:11 +00:00
|
|
|
dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt,
|
|
|
|
mfc_dev->mem_base);
|
|
|
|
kfree(mfc_dev->mem_bitmap);
|
2017-02-20 13:11:42 +00:00
|
|
|
vb2_dma_contig_clear_max_seg_size(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev)
|
|
|
|
{
|
|
|
|
struct device *dev = &mfc_dev->plat_dev->dev;
|
|
|
|
|
2017-02-13 10:58:14 +00:00
|
|
|
if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
|
2017-02-20 13:11:42 +00:00
|
|
|
return s5p_mfc_configure_common_memory(mfc_dev);
|
|
|
|
else
|
|
|
|
return s5p_mfc_configure_2port_memory(mfc_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev)
|
|
|
|
{
|
|
|
|
struct device *dev = &mfc_dev->plat_dev->dev;
|
|
|
|
|
|
|
|
s5p_mfc_release_firmware(mfc_dev);
|
2017-02-13 10:58:14 +00:00
|
|
|
if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
|
2017-02-20 13:11:42 +00:00
|
|
|
s5p_mfc_unconfigure_common_memory(mfc_dev);
|
|
|
|
else
|
|
|
|
s5p_mfc_unconfigure_2port_memory(mfc_dev);
|
2016-05-24 13:31:26 +00:00
|
|
|
}
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
/* MFC probe function */
|
2011-08-08 16:12:51 +00:00
|
|
|
static int s5p_mfc_probe(struct platform_device *pdev)
|
2011-06-21 13:51:26 +00:00
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev;
|
|
|
|
struct video_device *vfd;
|
|
|
|
struct resource *res;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("%s++\n", __func__);
|
2012-08-17 06:22:55 +00:00
|
|
|
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
|
2017-09-08 20:25:17 +00:00
|
|
|
if (!dev)
|
2011-06-21 13:51:26 +00:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_init(&dev->irqlock);
|
|
|
|
spin_lock_init(&dev->condlock);
|
|
|
|
dev->plat_dev = pdev;
|
|
|
|
if (!dev->plat_dev) {
|
2021-08-05 07:55:35 +00:00
|
|
|
mfc_err("No platform data specified\n");
|
2012-05-14 11:22:27 +00:00
|
|
|
return -ENODEV;
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 13:38:51 +00:00
|
|
|
dev->variant = of_device_get_match_data(&pdev->dev);
|
2021-08-11 13:32:28 +00:00
|
|
|
if (!dev->variant) {
|
|
|
|
dev_err(&pdev->dev, "Failed to get device MFC hardware variant information\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2012-10-04 01:19:09 +00:00
|
|
|
|
2021-09-01 05:56:16 +00:00
|
|
|
dev->regs_base = devm_platform_ioremap_resource(pdev, 0);
|
2013-01-21 09:09:07 +00:00
|
|
|
if (IS_ERR(dev->regs_base))
|
|
|
|
return PTR_ERR(dev->regs_base);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
2017-09-08 20:37:00 +00:00
|
|
|
if (!res) {
|
2011-06-21 13:51:26 +00:00
|
|
|
dev_err(&pdev->dev, "failed to get irq resource\n");
|
2016-06-08 11:33:40 +00:00
|
|
|
return -ENOENT;
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
dev->irq = res->start;
|
2012-05-14 11:22:27 +00:00
|
|
|
ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
|
2013-10-13 05:58:39 +00:00
|
|
|
0, pdev->name, dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ret) {
|
|
|
|
dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
|
2016-06-08 11:33:40 +00:00
|
|
|
return ret;
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
2016-05-24 13:31:26 +00:00
|
|
|
ret = s5p_mfc_configure_dma_memory(dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to configure DMA memory\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = s5p_mfc_init_pm(dev);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed to get mfc clock source\n");
|
2016-06-08 11:33:40 +00:00
|
|
|
goto err_dma;
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
2017-11-04 02:01:58 +00:00
|
|
|
/*
|
|
|
|
* Load fails if fs isn't mounted. Try loading anyway.
|
|
|
|
* _open() will load it, it it fails now. Ignore failure.
|
|
|
|
*/
|
|
|
|
s5p_mfc_load_firmware(dev);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
mutex_init(&dev->mfc_mutex);
|
2017-02-03 14:05:18 +00:00
|
|
|
init_waitqueue_head(&dev->queue);
|
|
|
|
dev->hw_lock = 0;
|
|
|
|
INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker);
|
|
|
|
atomic_set(&dev->watchdog_cnt, 0);
|
treewide: setup_timer() -> timer_setup()
This converts all remaining cases of the old setup_timer() API into using
timer_setup(), where the callback argument is the structure already
holding the struct timer_list. These should have no behavioral changes,
since they just change which pointer is passed into the callback with
the same available pointers after conversion. It handles the following
examples, in addition to some other variations.
Casting from unsigned long:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
setup_timer(&ptr->my_timer, my_callback, ptr);
and forced object casts:
void my_callback(struct something *ptr)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr);
become:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
Direct function assignments:
void my_callback(unsigned long data)
{
struct something *ptr = (struct something *)data;
...
}
...
ptr->my_timer.function = my_callback;
have a temporary cast added, along with converting the args:
void my_callback(struct timer_list *t)
{
struct something *ptr = from_timer(ptr, t, my_timer);
...
}
...
ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback;
And finally, callbacks without a data assignment:
void my_callback(unsigned long data)
{
...
}
...
setup_timer(&ptr->my_timer, my_callback, 0);
have their argument renamed to verify they're unused during conversion:
void my_callback(struct timer_list *unused)
{
...
}
...
timer_setup(&ptr->my_timer, my_callback, 0);
The conversion is done with the following Coccinelle script:
spatch --very-quiet --all-includes --include-headers \
-I ./arch/x86/include -I ./arch/x86/include/generated \
-I ./include -I ./arch/x86/include/uapi \
-I ./arch/x86/include/generated/uapi -I ./include/uapi \
-I ./include/generated/uapi --include ./include/linux/kconfig.h \
--dir . \
--cocci-file ~/src/data/timer_setup.cocci
@fix_address_of@
expression e;
@@
setup_timer(
-&(e)
+&e
, ...)
// Update any raw setup_timer() usages that have a NULL callback, but
// would otherwise match change_timer_function_usage, since the latter
// will update all function assignments done in the face of a NULL
// function initialization in setup_timer().
@change_timer_function_usage_NULL@
expression _E;
identifier _timer;
type _cast_data;
@@
(
-setup_timer(&_E->_timer, NULL, _E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E->_timer, NULL, (_cast_data)_E);
+timer_setup(&_E->_timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, &_E);
+timer_setup(&_E._timer, NULL, 0);
|
-setup_timer(&_E._timer, NULL, (_cast_data)&_E);
+timer_setup(&_E._timer, NULL, 0);
)
@change_timer_function_usage@
expression _E;
identifier _timer;
struct timer_list _stl;
identifier _callback;
type _cast_func, _cast_data;
@@
(
-setup_timer(&_E->_timer, _callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, &_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, _E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, &_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E);
+timer_setup(&_E._timer, _callback, 0);
|
_E->_timer@_stl.function = _callback;
|
_E->_timer@_stl.function = &_callback;
|
_E->_timer@_stl.function = (_cast_func)_callback;
|
_E->_timer@_stl.function = (_cast_func)&_callback;
|
_E._timer@_stl.function = _callback;
|
_E._timer@_stl.function = &_callback;
|
_E._timer@_stl.function = (_cast_func)_callback;
|
_E._timer@_stl.function = (_cast_func)&_callback;
)
// callback(unsigned long arg)
@change_callback_handle_cast
depends on change_timer_function_usage@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
identifier _handle;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
(
... when != _origarg
_handletype *_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(_handletype *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
|
... when != _origarg
_handletype *_handle;
... when != _handle
_handle =
-(void *)_origarg;
+from_timer(_handle, t, _timer);
... when != _origarg
)
}
// callback(unsigned long arg) without existing variable
@change_callback_handle_cast_no_arg
depends on change_timer_function_usage &&
!change_callback_handle_cast@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _origtype;
identifier _origarg;
type _handletype;
@@
void _callback(
-_origtype _origarg
+struct timer_list *t
)
{
+ _handletype *_origarg = from_timer(_origarg, t, _timer);
+
... when != _origarg
- (_handletype *)_origarg
+ _origarg
... when != _origarg
}
// Avoid already converted callbacks.
@match_callback_converted
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier t;
@@
void _callback(struct timer_list *t)
{ ... }
// callback(struct something *handle)
@change_callback_handle_arg
depends on change_timer_function_usage &&
!match_callback_converted &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
@@
void _callback(
-_handletype *_handle
+struct timer_list *t
)
{
+ _handletype *_handle = from_timer(_handle, t, _timer);
...
}
// If change_callback_handle_arg ran on an empty function, remove
// the added handler.
@unchange_callback_handle_arg
depends on change_timer_function_usage &&
change_callback_handle_arg@
identifier change_timer_function_usage._callback;
identifier change_timer_function_usage._timer;
type _handletype;
identifier _handle;
identifier t;
@@
void _callback(struct timer_list *t)
{
- _handletype *_handle = from_timer(_handle, t, _timer);
}
// We only want to refactor the setup_timer() data argument if we've found
// the matching callback. This undoes changes in change_timer_function_usage.
@unchange_timer_function_usage
depends on change_timer_function_usage &&
!change_callback_handle_cast &&
!change_callback_handle_cast_no_arg &&
!change_callback_handle_arg@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type change_timer_function_usage._cast_data;
@@
(
-timer_setup(&_E->_timer, _callback, 0);
+setup_timer(&_E->_timer, _callback, (_cast_data)_E);
|
-timer_setup(&_E._timer, _callback, 0);
+setup_timer(&_E._timer, _callback, (_cast_data)&_E);
)
// If we fixed a callback from a .function assignment, fix the
// assignment cast now.
@change_timer_function_assignment
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression change_timer_function_usage._E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_func;
typedef TIMER_FUNC_TYPE;
@@
(
_E->_timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E->_timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-&_callback;
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)_callback
+(TIMER_FUNC_TYPE)_callback
;
|
_E._timer.function =
-(_cast_func)&_callback
+(TIMER_FUNC_TYPE)_callback
;
)
// Sometimes timer functions are called directly. Replace matched args.
@change_timer_function_calls
depends on change_timer_function_usage &&
(change_callback_handle_cast ||
change_callback_handle_cast_no_arg ||
change_callback_handle_arg)@
expression _E;
identifier change_timer_function_usage._timer;
identifier change_timer_function_usage._callback;
type _cast_data;
@@
_callback(
(
-(_cast_data)_E
+&_E->_timer
|
-(_cast_data)&_E
+&_E._timer
|
-_E
+&_E->_timer
)
)
// If a timer has been configured without a data argument, it can be
// converted without regard to the callback argument, since it is unused.
@match_timer_function_unused_data@
expression _E;
identifier _timer;
identifier _callback;
@@
(
-setup_timer(&_E->_timer, _callback, 0);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0L);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E->_timer, _callback, 0UL);
+timer_setup(&_E->_timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0L);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_E._timer, _callback, 0UL);
+timer_setup(&_E._timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0L);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(&_timer, _callback, 0UL);
+timer_setup(&_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0L);
+timer_setup(_timer, _callback, 0);
|
-setup_timer(_timer, _callback, 0UL);
+timer_setup(_timer, _callback, 0);
)
@change_callback_unused_data
depends on match_timer_function_unused_data@
identifier match_timer_function_unused_data._callback;
type _origtype;
identifier _origarg;
@@
void _callback(
-_origtype _origarg
+struct timer_list *unused
)
{
... when != _origarg
}
Signed-off-by: Kees Cook <keescook@chromium.org>
2017-10-16 21:43:17 +00:00
|
|
|
timer_setup(&dev->watchdog_timer, s5p_mfc_watchdog, 0);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
|
|
|
|
if (ret)
|
|
|
|
goto err_v4l2_dev_reg;
|
|
|
|
|
|
|
|
/* decoder */
|
|
|
|
vfd = video_device_alloc();
|
|
|
|
if (!vfd) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_dec_alloc;
|
|
|
|
}
|
2014-02-21 02:22:12 +00:00
|
|
|
vfd->fops = &s5p_mfc_fops;
|
2011-06-21 13:51:26 +00:00
|
|
|
vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
|
2014-02-21 02:22:12 +00:00
|
|
|
vfd->release = video_device_release;
|
2011-06-21 13:51:26 +00:00
|
|
|
vfd->lock = &dev->mfc_mutex;
|
|
|
|
vfd->v4l2_dev = &dev->v4l2_dev;
|
2012-09-05 09:05:50 +00:00
|
|
|
vfd->vfl_dir = VFL_DIR_M2M;
|
2019-06-04 07:06:24 +00:00
|
|
|
vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
|
2018-10-04 20:02:42 +00:00
|
|
|
set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags);
|
2011-06-21 13:51:26 +00:00
|
|
|
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
|
|
|
|
dev->vfd_dec = vfd;
|
|
|
|
video_set_drvdata(vfd, dev);
|
|
|
|
|
|
|
|
/* encoder */
|
|
|
|
vfd = video_device_alloc();
|
|
|
|
if (!vfd) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto err_enc_alloc;
|
|
|
|
}
|
2014-02-21 02:22:12 +00:00
|
|
|
vfd->fops = &s5p_mfc_fops;
|
2011-06-21 13:51:26 +00:00
|
|
|
vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
|
2014-02-21 02:22:12 +00:00
|
|
|
vfd->release = video_device_release;
|
2011-06-21 13:51:26 +00:00
|
|
|
vfd->lock = &dev->mfc_mutex;
|
|
|
|
vfd->v4l2_dev = &dev->v4l2_dev;
|
2012-10-04 19:14:56 +00:00
|
|
|
vfd->vfl_dir = VFL_DIR_M2M;
|
2019-06-04 07:06:24 +00:00
|
|
|
vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
|
2011-06-21 13:51:26 +00:00
|
|
|
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
|
|
|
|
dev->vfd_enc = vfd;
|
|
|
|
video_set_drvdata(vfd, dev);
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
|
|
|
2012-10-04 01:19:08 +00:00
|
|
|
/* Initialize HW ops and commands based on MFC version */
|
|
|
|
s5p_mfc_init_hw_ops(dev);
|
|
|
|
s5p_mfc_init_hw_cmds(dev);
|
2014-05-19 12:33:05 +00:00
|
|
|
s5p_mfc_init_regs(dev);
|
2012-10-04 01:19:08 +00:00
|
|
|
|
2016-05-03 20:27:18 +00:00
|
|
|
/* Register decoder and encoder */
|
2020-02-03 11:41:18 +00:00
|
|
|
ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0);
|
2016-05-03 20:27:18 +00:00
|
|
|
if (ret) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
|
|
|
|
goto err_dec_reg;
|
|
|
|
}
|
|
|
|
v4l2_info(&dev->v4l2_dev,
|
|
|
|
"decoder registered as /dev/video%d\n", dev->vfd_dec->num);
|
|
|
|
|
2020-02-03 11:41:18 +00:00
|
|
|
ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0);
|
2016-05-03 20:27:18 +00:00
|
|
|
if (ret) {
|
|
|
|
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
|
|
|
|
goto err_enc_reg;
|
|
|
|
}
|
|
|
|
v4l2_info(&dev->v4l2_dev,
|
|
|
|
"encoder registered as /dev/video%d\n", dev->vfd_enc->num);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
pr_debug("%s--\n", __func__);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Deinit MFC if probe had failed */
|
|
|
|
err_enc_reg:
|
|
|
|
video_unregister_device(dev->vfd_dec);
|
|
|
|
err_dec_reg:
|
2016-05-03 20:27:18 +00:00
|
|
|
video_device_release(dev->vfd_enc);
|
|
|
|
err_enc_alloc:
|
2011-06-21 13:51:26 +00:00
|
|
|
video_device_release(dev->vfd_dec);
|
|
|
|
err_dec_alloc:
|
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
|
|
|
err_v4l2_dev_reg:
|
|
|
|
s5p_mfc_final_pm(dev);
|
2016-06-08 11:33:40 +00:00
|
|
|
err_dma:
|
|
|
|
s5p_mfc_unconfigure_dma_memory(dev);
|
2012-05-14 11:22:27 +00:00
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
pr_debug("%s-- with error\n", __func__);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the driver */
|
2012-12-21 21:17:53 +00:00
|
|
|
static int s5p_mfc_remove(struct platform_device *pdev)
|
2011-06-21 13:51:26 +00:00
|
|
|
{
|
|
|
|
struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
|
2016-07-08 22:29:25 +00:00
|
|
|
struct s5p_mfc_ctx *ctx;
|
|
|
|
int i;
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
|
|
|
|
|
2016-07-08 22:29:25 +00:00
|
|
|
/*
|
|
|
|
* Clear ctx dev pointer to avoid races between s5p_mfc_remove()
|
|
|
|
* and s5p_mfc_release() and s5p_mfc_release() accessing ctx->dev
|
|
|
|
* after s5p_mfc_remove() is run during unbind.
|
|
|
|
*/
|
|
|
|
mutex_lock(&dev->mfc_mutex);
|
|
|
|
for (i = 0; i < MFC_NUM_CONTEXTS; i++) {
|
|
|
|
ctx = dev->ctx[i];
|
|
|
|
if (!ctx)
|
|
|
|
continue;
|
|
|
|
/* clear ctx->dev */
|
|
|
|
ctx->dev = NULL;
|
|
|
|
}
|
|
|
|
mutex_unlock(&dev->mfc_mutex);
|
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
del_timer_sync(&dev->watchdog_timer);
|
2016-07-16 08:30:25 +00:00
|
|
|
flush_work(&dev->watchdog_work);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
video_unregister_device(dev->vfd_enc);
|
|
|
|
video_unregister_device(dev->vfd_dec);
|
2016-06-28 19:17:17 +00:00
|
|
|
video_device_release(dev->vfd_enc);
|
|
|
|
video_device_release(dev->vfd_dec);
|
2011-06-21 13:51:26 +00:00
|
|
|
v4l2_device_unregister(&dev->v4l2_dev);
|
2016-05-24 13:31:26 +00:00
|
|
|
s5p_mfc_unconfigure_dma_memory(dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
s5p_mfc_final_pm(dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
|
|
|
|
|
|
static int s5p_mfc_suspend(struct device *dev)
|
|
|
|
{
|
2018-04-19 14:05:57 +00:00
|
|
|
struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (m_dev->num_inst == 0)
|
|
|
|
return 0;
|
2012-09-28 07:01:35 +00:00
|
|
|
|
2011-06-21 13:51:26 +00:00
|
|
|
if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) {
|
|
|
|
mfc_err("Error: going to suspend for a second time\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if we're processing then wait if it necessary. */
|
|
|
|
while (test_and_set_bit(0, &m_dev->hw_lock) != 0) {
|
|
|
|
/* Try and lock the HW */
|
|
|
|
/* Wait on the interrupt waitqueue */
|
|
|
|
ret = wait_event_interruptible_timeout(m_dev->queue,
|
2013-10-04 04:47:19 +00:00
|
|
|
m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
|
2011-06-21 13:51:26 +00:00
|
|
|
if (ret == 0) {
|
|
|
|
mfc_err("Waiting for hardware to finish timed out\n");
|
2014-10-21 11:06:57 +00:00
|
|
|
clear_bit(0, &m_dev->enter_suspend);
|
2011-06-21 13:51:26 +00:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
2012-09-28 07:01:35 +00:00
|
|
|
|
2014-10-21 11:06:57 +00:00
|
|
|
ret = s5p_mfc_sleep(m_dev);
|
|
|
|
if (ret) {
|
|
|
|
clear_bit(0, &m_dev->enter_suspend);
|
|
|
|
clear_bit(0, &m_dev->hw_lock);
|
|
|
|
}
|
|
|
|
return ret;
|
2011-06-21 13:51:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int s5p_mfc_resume(struct device *dev)
|
|
|
|
{
|
2018-04-19 14:05:57 +00:00
|
|
|
struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
if (m_dev->num_inst == 0)
|
|
|
|
return 0;
|
|
|
|
return s5p_mfc_wakeup(m_dev);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Power management */
|
|
|
|
static const struct dev_pm_ops s5p_mfc_pm_ops = {
|
|
|
|
SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume)
|
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
|
2012-10-04 01:19:09 +00:00
|
|
|
.h264_ctx = MFC_H264_CTX_BUF_SIZE,
|
|
|
|
.non_h264_ctx = MFC_CTX_BUF_SIZE,
|
|
|
|
.dsc = DESC_BUF_SIZE,
|
|
|
|
.shm = SHARED_BUF_SIZE,
|
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size buf_size_v5 = {
|
2012-10-04 01:19:09 +00:00
|
|
|
.fw = MAX_FW_SIZE,
|
|
|
|
.cpb = MAX_CPB_SIZE,
|
|
|
|
.priv = &mfc_buf_size_v5,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v5 = {
|
|
|
|
.version = MFC_VERSION,
|
2014-05-20 13:15:13 +00:00
|
|
|
.version_bit = MFC_V5_BIT,
|
2012-10-04 01:19:09 +00:00
|
|
|
.port_num = MFC_NUM_PORTS,
|
|
|
|
.buf_size = &buf_size_v5,
|
2014-05-21 09:29:30 +00:00
|
|
|
.fw_name[0] = "s5p-mfc.fw",
|
2016-11-16 09:04:57 +00:00
|
|
|
.clk_names = {"mfc", "sclk_mfc"},
|
|
|
|
.num_clocks = 2,
|
2016-11-10 10:31:23 +00:00
|
|
|
.use_clock_gating = true,
|
2012-10-04 01:19:11 +00:00
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
|
2012-10-04 01:19:11 +00:00
|
|
|
.dev_ctx = MFC_CTX_BUF_SIZE_V6,
|
|
|
|
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V6,
|
|
|
|
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V6,
|
|
|
|
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V6,
|
|
|
|
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V6,
|
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size buf_size_v6 = {
|
2012-10-04 01:19:11 +00:00
|
|
|
.fw = MAX_FW_SIZE_V6,
|
|
|
|
.cpb = MAX_CPB_SIZE_V6,
|
|
|
|
.priv = &mfc_buf_size_v6,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v6 = {
|
|
|
|
.version = MFC_VERSION_V6,
|
2014-05-20 13:15:13 +00:00
|
|
|
.version_bit = MFC_V6_BIT,
|
2012-10-04 01:19:11 +00:00
|
|
|
.port_num = MFC_NUM_PORTS_V6,
|
|
|
|
.buf_size = &buf_size_v6,
|
2014-05-21 09:29:30 +00:00
|
|
|
.fw_name[0] = "s5p-mfc-v6.fw",
|
|
|
|
/*
|
|
|
|
* v6-v2 firmware contains bug fixes and interface change
|
|
|
|
* for init buffer command
|
|
|
|
*/
|
|
|
|
.fw_name[1] = "s5p-mfc-v6-v2.fw",
|
2016-11-16 09:04:57 +00:00
|
|
|
.clk_names = {"mfc"},
|
|
|
|
.num_clocks = 1,
|
2012-10-04 01:19:09 +00:00
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
|
2013-07-09 04:24:38 +00:00
|
|
|
.dev_ctx = MFC_CTX_BUF_SIZE_V7,
|
|
|
|
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7,
|
|
|
|
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
|
|
|
|
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7,
|
|
|
|
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
|
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size buf_size_v7 = {
|
2013-07-09 04:24:38 +00:00
|
|
|
.fw = MAX_FW_SIZE_V7,
|
|
|
|
.cpb = MAX_CPB_SIZE_V7,
|
|
|
|
.priv = &mfc_buf_size_v7,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v7 = {
|
|
|
|
.version = MFC_VERSION_V7,
|
2014-05-20 13:15:13 +00:00
|
|
|
.version_bit = MFC_V7_BIT,
|
2013-07-09 04:24:38 +00:00
|
|
|
.port_num = MFC_NUM_PORTS_V7,
|
|
|
|
.buf_size = &buf_size_v7,
|
2014-05-21 09:29:30 +00:00
|
|
|
.fw_name[0] = "s5p-mfc-v7.fw",
|
2016-11-16 09:04:57 +00:00
|
|
|
.clk_names = {"mfc", "sclk_mfc"},
|
|
|
|
.num_clocks = 2,
|
2013-07-09 04:24:38 +00:00
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
|
2014-05-19 12:50:01 +00:00
|
|
|
.dev_ctx = MFC_CTX_BUF_SIZE_V8,
|
|
|
|
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8,
|
|
|
|
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8,
|
2014-05-19 12:50:02 +00:00
|
|
|
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V8,
|
|
|
|
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
|
2014-05-19 12:50:01 +00:00
|
|
|
};
|
|
|
|
|
2014-09-24 12:08:10 +00:00
|
|
|
static struct s5p_mfc_buf_size buf_size_v8 = {
|
2014-05-19 12:50:01 +00:00
|
|
|
.fw = MAX_FW_SIZE_V8,
|
|
|
|
.cpb = MAX_CPB_SIZE_V8,
|
|
|
|
.priv = &mfc_buf_size_v8,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v8 = {
|
|
|
|
.version = MFC_VERSION_V8,
|
|
|
|
.version_bit = MFC_V8_BIT,
|
|
|
|
.port_num = MFC_NUM_PORTS_V8,
|
|
|
|
.buf_size = &buf_size_v8,
|
2014-05-21 09:29:30 +00:00
|
|
|
.fw_name[0] = "s5p-mfc-v8.fw",
|
2016-11-16 09:04:57 +00:00
|
|
|
.clk_names = {"mfc"},
|
|
|
|
.num_clocks = 1,
|
2014-05-19 12:50:01 +00:00
|
|
|
};
|
|
|
|
|
2016-11-16 09:04:58 +00:00
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v8_5433 = {
|
|
|
|
.version = MFC_VERSION_V8,
|
|
|
|
.version_bit = MFC_V8_BIT,
|
|
|
|
.port_num = MFC_NUM_PORTS_V8,
|
|
|
|
.buf_size = &buf_size_v8,
|
|
|
|
.fw_name[0] = "s5p-mfc-v8.fw",
|
|
|
|
.clk_names = {"pclk", "aclk", "aclk_xiu"},
|
|
|
|
.num_clocks = 3,
|
|
|
|
};
|
|
|
|
|
2018-02-02 12:25:38 +00:00
|
|
|
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v10 = {
|
|
|
|
.dev_ctx = MFC_CTX_BUF_SIZE_V10,
|
|
|
|
.h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V10,
|
|
|
|
.other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V10,
|
|
|
|
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V10,
|
2018-02-02 12:25:47 +00:00
|
|
|
.hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V10,
|
2018-02-02 12:25:38 +00:00
|
|
|
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V10,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_buf_size buf_size_v10 = {
|
|
|
|
.fw = MAX_FW_SIZE_V10,
|
|
|
|
.cpb = MAX_CPB_SIZE_V10,
|
|
|
|
.priv = &mfc_buf_size_v10,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct s5p_mfc_variant mfc_drvdata_v10 = {
|
|
|
|
.version = MFC_VERSION_V10,
|
|
|
|
.version_bit = MFC_V10_BIT,
|
|
|
|
.port_num = MFC_NUM_PORTS_V10,
|
|
|
|
.buf_size = &buf_size_v10,
|
|
|
|
.fw_name[0] = "s5p-mfc-v10.fw",
|
|
|
|
};
|
|
|
|
|
2012-10-25 08:24:14 +00:00
|
|
|
static const struct of_device_id exynos_mfc_match[] = {
|
|
|
|
{
|
|
|
|
.compatible = "samsung,mfc-v5",
|
|
|
|
.data = &mfc_drvdata_v5,
|
|
|
|
}, {
|
|
|
|
.compatible = "samsung,mfc-v6",
|
|
|
|
.data = &mfc_drvdata_v6,
|
2013-07-09 04:24:38 +00:00
|
|
|
}, {
|
|
|
|
.compatible = "samsung,mfc-v7",
|
|
|
|
.data = &mfc_drvdata_v7,
|
2014-05-19 12:50:01 +00:00
|
|
|
}, {
|
|
|
|
.compatible = "samsung,mfc-v8",
|
|
|
|
.data = &mfc_drvdata_v8,
|
2016-11-16 09:04:58 +00:00
|
|
|
}, {
|
|
|
|
.compatible = "samsung,exynos5433-mfc",
|
|
|
|
.data = &mfc_drvdata_v8_5433,
|
2018-02-02 12:25:38 +00:00
|
|
|
}, {
|
|
|
|
.compatible = "samsung,mfc-v10",
|
|
|
|
.data = &mfc_drvdata_v10,
|
2012-10-25 08:24:14 +00:00
|
|
|
},
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, exynos_mfc_match);
|
|
|
|
|
2011-08-08 16:12:51 +00:00
|
|
|
static struct platform_driver s5p_mfc_driver = {
|
2012-10-04 01:19:09 +00:00
|
|
|
.probe = s5p_mfc_probe,
|
2012-12-21 21:17:53 +00:00
|
|
|
.remove = s5p_mfc_remove,
|
2011-06-21 13:51:26 +00:00
|
|
|
.driver = {
|
|
|
|
.name = S5P_MFC_NAME,
|
2012-10-25 08:24:14 +00:00
|
|
|
.pm = &s5p_mfc_pm_ops,
|
|
|
|
.of_match_table = exynos_mfc_match,
|
2011-06-21 13:51:26 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2012-01-10 06:21:49 +00:00
|
|
|
module_platform_driver(s5p_mfc_driver);
|
2011-06-21 13:51:26 +00:00
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
|
|
|
|
MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");
|
|
|
|
|