OpenCloudOS-Kernel/drivers/media/platform/s5p-mfc/s5p_mfc.c

1674 lines
46 KiB
C
Raw Normal View History

/*
* Samsung S5P Multi Format Codec v 5.1
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Kamil Debski, <k.debski@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#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>
#include <media/v4l2-event.h>
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_reserved_mem.h>
#include <media/videobuf2-v4l2.h>
#include "s5p_mfc_common.h"
#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"
#include "s5p_mfc_iommu.h"
#include "s5p_mfc_opr.h"
#include "s5p_mfc_cmd.h"
#include "s5p_mfc_pm.h"
#define S5P_MFC_DEC_NAME "s5p-mfc-dec"
#define S5P_MFC_ENC_NAME "s5p-mfc-enc"
int mfc_debug_level;
module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages");
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08: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");
/* Helper functions for interrupt processing */
/* Remove from hw execution round robin */
void clear_work_bit(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
spin_lock(&dev->condlock);
__clear_bit(ctx->num, &dev->ctx_work_bits);
spin_unlock(&dev->condlock);
}
/* 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);
}
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;
}
/* 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);
}
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-17 05:43:17 +08:00
static void s5p_mfc_watchdog(struct timer_list *t)
{
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-17 05:43:17 +08:00
struct s5p_mfc_dev *dev = from_timer(dev, t, watchdog_timer);
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");
schedule_work(&dev->watchdog_work);
}
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;
s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
clear_work_bit(ctx);
wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0);
}
clear_bit(0, &dev->hw_lock);
spin_unlock_irqrestore(&dev->irqlock, flags);
/* De-init MFC */
s5p_mfc_deinit_hw(dev);
/* 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) {
ret = s5p_mfc_load_firmware(dev);
if (ret) {
mfc_err("Failed to reload FW\n");
goto unlock;
}
s5p_mfc_clock_on();
ret = s5p_mfc_init_hw(dev);
s5p_mfc_clock_off();
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;
struct s5p_mfc_dev *dev = ctx->dev;
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",
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);
list_del(&dst_buf->list);
dst_buf->flags |= MFC_BUF_FLAG_EOS;
ctx->dst_queue_cnt--;
dst_buf->b->sequence = (ctx->sequence++);
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))
dst_buf->b->field = V4L2_FIELD_NONE;
else
dst_buf->b->field = V4L2_FIELD_INTERLACED;
dst_buf->b->flags |= V4L2_BUF_FLAG_LAST;
ctx->dec_dst_flag &= ~(1 << dst_buf->b->vb2_buf.index);
vb2_buffer_done(&dst_buf->b->vb2_buf, VB2_BUF_STATE_DONE);
}
}
static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *dst_buf, *src_buf;
size_t dec_y_addr;
unsigned int frame_type;
/* Make sure we actually have a new frame before continuing. */
frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev);
if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED)
return;
dec_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev);
/* Copy timestamp / timecode from decoded src to dst and set
appropriate flags. */
src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list);
list_for_each_entry(dst_buf, &ctx->dst_queue, list) {
if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0)
== dec_y_addr) {
dst_buf->b->timecode =
src_buf->b->timecode;
dst_buf->b->vb2_buf.timestamp =
src_buf->b->vb2_buf.timestamp;
dst_buf->b->flags &=
~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
dst_buf->b->flags |=
src_buf->b->flags
& V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
switch (frame_type) {
case S5P_FIMV_DECODE_FRAME_I_FRAME:
dst_buf->b->flags |=
V4L2_BUF_FLAG_KEYFRAME;
break;
case S5P_FIMV_DECODE_FRAME_P_FRAME:
dst_buf->b->flags |=
V4L2_BUF_FLAG_PFRAME;
break;
case S5P_FIMV_DECODE_FRAME_B_FRAME:
dst_buf->b->flags |=
V4L2_BUF_FLAG_BFRAME;
break;
default:
/* Don't know how to handle
S5P_FIMV_DECODE_FRAME_OTHER_FRAME. */
mfc_debug(2, "Unexpected frame type: %d\n",
frame_type);
}
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;
size_t dspl_y_addr;
unsigned int frame_type;
dspl_y_addr = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev);
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);
/* 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) {
/* Check if this is the buffer we're looking for */
if (vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0)
== dspl_y_addr) {
list_del(&dst_buf->list);
ctx->dst_queue_cnt--;
dst_buf->b->sequence = ctx->sequence;
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))
dst_buf->b->field = V4L2_FIELD_NONE;
else
dst_buf->b->field =
V4L2_FIELD_INTERLACED;
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,
&ctx->dec_dst_flag);
vb2_buffer_done(&dst_buf->b->vb2_buf, err ?
VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
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 20:32:57 +08:00
unsigned int dec_frame_status;
struct s5p_mfc_buf *src_buf;
unsigned int res_change;
dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev)
& 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 20:32:57 +08:00
dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev)
& S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK;
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;
mfc_debug(2, "Frame Status: %x\n", dst_frame_status);
if (ctx->state == MFCINST_RES_CHANGE_INIT)
ctx->state = MFCINST_RES_CHANGE_FLUSH;
if (res_change == S5P_FIMV_RES_INCREASE ||
res_change == S5P_FIMV_RES_DECREASE) {
ctx->state = MFCINST_RES_CHANGE_INIT;
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
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) {
static const struct v4l2_event ev_src_ch = {
.type = V4L2_EVENT_SOURCE_CHANGE,
.u.src_change.changes =
V4L2_EVENT_SRC_CH_RESOLUTION,
};
s5p_mfc_handle_frame_all_extracted(ctx);
ctx->state = MFCINST_RES_CHANGE_END;
v4l2_event_queue_fh(&ctx->fh, &ev_src_ch);
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 20:32:57 +08:00
if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY)
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);
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 20:33:02 +08:00
ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC &&
ctx->consumed_stream + STUFF_BYTE <
src_buf->b->vb2_buf.planes[0].bytesused) {
/* 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;
if (src_buf->flags & MFC_BUF_FLAG_EOS)
ctx->state = MFCINST_FINISHING;
list_del(&src_buf->list);
ctx->src_queue_cnt--;
if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0)
vb2_buffer_done(&src_buf->b->vb2_buf,
VB2_BUF_STATE_ERROR);
else
vb2_buffer_done(&src_buf->b->vb2_buf,
VB2_BUF_STATE_DONE);
}
}
leave_handle_frame:
if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING)
|| ctx->dst_queue_cnt < ctx->pb_count)
clear_work_bit(ctx);
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
wake_up_ctx(ctx, reason, err);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
/* 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
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
/* Error handling for interrupt */
static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev,
struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err)
{
mfc_err("Interrupt Error: %08x\n", err);
if (ctx) {
/* 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:
/* It is highly probable that an error occurred
* while decoding a frame */
clear_work_bit(ctx);
ctx->state = MFCINST_ERROR;
/* Mark all dst buffers as having an error */
s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
/* Mark all src buffers as having an error */
s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
wake_up_ctx(ctx, reason, err);
break;
default:
clear_work_bit(ctx);
ctx->state = MFCINST_ERROR;
wake_up_ctx(ctx, reason, err);
break;
}
}
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_clock_off();
wake_up_dev(dev, reason, err);
}
/* 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;
if (!ctx)
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 {
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);
s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx);
ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count,
dev);
ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count,
dev);
ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops,
get_min_scratch_buf_size, dev);
if (ctx->img_width == 0 || ctx->img_height == 0)
ctx->state = MFCINST_ERROR;
else
ctx->state = MFCINST_HEAD_PARSED;
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) <
src_buf->b->vb2_buf.planes[0].bytesused)
ctx->head_processed = 0;
else
ctx->head_processed = 1;
} else {
ctx->head_processed = 1;
}
}
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
clear_work_bit(ctx);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
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;
if (!ctx)
return;
dev = ctx->dev;
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
ctx->int_cond = 1;
clear_work_bit(ctx);
if (err == 0) {
ctx->state = MFCINST_RUNNING;
if (!ctx->dpb_flush_flag && ctx->head_processed) {
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--;
vb2_buffer_done(&src_buf->b->vb2_buf,
VB2_BUF_STATE_DONE);
}
} else {
ctx->dpb_flush_flag = 0;
}
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
wake_up(&ctx->queue);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
wake_up(&ctx->queue);
}
}
static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
struct s5p_mfc_buf *mb_entry;
mfc_debug(2, "Stream completed\n");
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--;
vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0);
vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE);
}
clear_work_bit(ctx);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
wake_up(&ctx->queue);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
}
/* 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);
spin_lock(&dev->irqlock);
ctx = dev->ctx[dev->curr_ctx];
/* Get the reason of interrupt and the error code */
reason = s5p_mfc_hw_call(dev->mfc_ops, get_int_reason, dev);
err = s5p_mfc_hw_call(dev->mfc_ops, get_int_err, dev);
mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err);
switch (reason) {
case S5P_MFC_R2H_CMD_ERR_RET:
/* An error has occurred */
if (ctx->state == MFCINST_RUNNING &&
(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))
s5p_mfc_handle_frame(ctx, reason, err);
else
s5p_mfc_handle_error(dev, ctx, reason, err);
clear_bit(0, &dev->enter_suspend);
break;
case S5P_MFC_R2H_CMD_SLICE_DONE_RET:
case S5P_MFC_R2H_CMD_FIELD_DONE_RET:
case S5P_MFC_R2H_CMD_FRAME_DONE_RET:
if (ctx->c_ops->post_frame_start) {
if (ctx->c_ops->post_frame_start(ctx))
mfc_err("post_frame_start() failed\n");
if (ctx->state == MFCINST_FINISHING &&
list_empty(&ctx->ref_queue)) {
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
s5p_mfc_handle_stream_complete(ctx);
break;
}
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
s5p_mfc_clock_off();
[media] s5p-mfc: Fix race between interrupt routine and device functions Interrupt routine must wake process waiting for given interrupt AFTER updating driver's internal structures and contexts. Doing it in-between is a serious bug. This patch moves all calls to the wake() function to the end of the interrupt processing block to avoid potential and real races, especially on multi-core platforms. This also fixes following issue reported from clock core (clocks were disabled in interrupt after being unprepared from the other place in the driver, the stack trace however points to the different place than s5p_mfc driver because of the race): WARNING: CPU: 1 PID: 18 at drivers/clk/clk.c:544 clk_core_unprepare+0xc8/0x108 Modules linked in: CPU: 1 PID: 18 Comm: kworker/1:0 Not tainted 4.10.0-next-20170223-00070-g04e18bc99ab9-dirty #2154 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) Workqueue: pm pm_runtime_work [<c010d8b0>] (unwind_backtrace) from [<c010a534>] (show_stack+0x10/0x14) [<c010a534>] (show_stack) from [<c033292c>] (dump_stack+0x74/0x94) [<c033292c>] (dump_stack) from [<c011cef4>] (__warn+0xd4/0x100) [<c011cef4>] (__warn) from [<c011cf40>] (warn_slowpath_null+0x20/0x28) [<c011cf40>] (warn_slowpath_null) from [<c0387a84>] (clk_core_unprepare+0xc8/0x108) [<c0387a84>] (clk_core_unprepare) from [<c0389d84>] (clk_unprepare+0x24/0x2c) [<c0389d84>] (clk_unprepare) from [<c03d4660>] (exynos_sysmmu_suspend+0x48/0x60) [<c03d4660>] (exynos_sysmmu_suspend) from [<c042b9b0>] (pm_generic_runtime_suspend+0x2c/0x38) [<c042b9b0>] (pm_generic_runtime_suspend) from [<c0437580>] (genpd_runtime_suspend+0x94/0x220) [<c0437580>] (genpd_runtime_suspend) from [<c042e240>] (__rpm_callback+0x134/0x208) [<c042e240>] (__rpm_callback) from [<c042e334>] (rpm_callback+0x20/0x80) [<c042e334>] (rpm_callback) from [<c042d3b8>] (rpm_suspend+0xdc/0x458) [<c042d3b8>] (rpm_suspend) from [<c042ea24>] (pm_runtime_work+0x80/0x90) [<c042ea24>] (pm_runtime_work) from [<c01322c4>] (process_one_work+0x120/0x318) [<c01322c4>] (process_one_work) from [<c0132520>] (worker_thread+0x2c/0x4ac) [<c0132520>] (worker_thread) from [<c0137ab0>] (kthread+0xfc/0x134) [<c0137ab0>] (kthread) from [<c0107978>] (ret_from_fork+0x14/0x3c) ---[ end trace 1ead49a7bb83f0d8 ]--- Fixes: af93574678108 ("[media] MFC: Add MFC 5.1 V4L2 driver") Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> CC: stable@vger.kernel.org # v4.5+ Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-23 19:43:27 +08:00
wake_up_ctx(ctx, reason, err);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
} else {
s5p_mfc_handle_frame(ctx, reason, err);
}
break;
case S5P_MFC_R2H_CMD_SEQ_DONE_RET:
s5p_mfc_handle_seq_done(ctx, reason, err);
break;
case S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET:
ctx->inst_no = s5p_mfc_hw_call(dev->mfc_ops, get_inst_no, dev);
ctx->state = MFCINST_GOT_INST;
goto irq_cleanup_hw;
case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET:
ctx->inst_no = MFC_NO_INSTANCE_SET;
ctx->state = MFCINST_FREE;
goto irq_cleanup_hw;
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:
if (ctx)
clear_work_bit(ctx);
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
clear_bit(0, &dev->hw_lock);
clear_bit(0, &dev->enter_suspend);
[media] s5p-mfc: Fix race between interrupt routine and device functions Interrupt routine must wake process waiting for given interrupt AFTER updating driver's internal structures and contexts. Doing it in-between is a serious bug. This patch moves all calls to the wake() function to the end of the interrupt processing block to avoid potential and real races, especially on multi-core platforms. This also fixes following issue reported from clock core (clocks were disabled in interrupt after being unprepared from the other place in the driver, the stack trace however points to the different place than s5p_mfc driver because of the race): WARNING: CPU: 1 PID: 18 at drivers/clk/clk.c:544 clk_core_unprepare+0xc8/0x108 Modules linked in: CPU: 1 PID: 18 Comm: kworker/1:0 Not tainted 4.10.0-next-20170223-00070-g04e18bc99ab9-dirty #2154 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) Workqueue: pm pm_runtime_work [<c010d8b0>] (unwind_backtrace) from [<c010a534>] (show_stack+0x10/0x14) [<c010a534>] (show_stack) from [<c033292c>] (dump_stack+0x74/0x94) [<c033292c>] (dump_stack) from [<c011cef4>] (__warn+0xd4/0x100) [<c011cef4>] (__warn) from [<c011cf40>] (warn_slowpath_null+0x20/0x28) [<c011cf40>] (warn_slowpath_null) from [<c0387a84>] (clk_core_unprepare+0xc8/0x108) [<c0387a84>] (clk_core_unprepare) from [<c0389d84>] (clk_unprepare+0x24/0x2c) [<c0389d84>] (clk_unprepare) from [<c03d4660>] (exynos_sysmmu_suspend+0x48/0x60) [<c03d4660>] (exynos_sysmmu_suspend) from [<c042b9b0>] (pm_generic_runtime_suspend+0x2c/0x38) [<c042b9b0>] (pm_generic_runtime_suspend) from [<c0437580>] (genpd_runtime_suspend+0x94/0x220) [<c0437580>] (genpd_runtime_suspend) from [<c042e240>] (__rpm_callback+0x134/0x208) [<c042e240>] (__rpm_callback) from [<c042e334>] (rpm_callback+0x20/0x80) [<c042e334>] (rpm_callback) from [<c042d3b8>] (rpm_suspend+0xdc/0x458) [<c042d3b8>] (rpm_suspend) from [<c042ea24>] (pm_runtime_work+0x80/0x90) [<c042ea24>] (pm_runtime_work) from [<c01322c4>] (process_one_work+0x120/0x318) [<c01322c4>] (process_one_work) from [<c0132520>] (worker_thread+0x2c/0x4ac) [<c0132520>] (worker_thread) from [<c0137ab0>] (kthread+0xfc/0x134) [<c0137ab0>] (kthread) from [<c0107978>] (ret_from_fork+0x14/0x3c) ---[ end trace 1ead49a7bb83f0d8 ]--- Fixes: af93574678108 ("[media] MFC: Add MFC 5.1 V4L2 driver") Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> CC: stable@vger.kernel.org # v4.5+ Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-23 19:43:27 +08:00
wake_up_dev(dev, reason, err);
break;
case S5P_MFC_R2H_CMD_INIT_BUFFERS_RET:
s5p_mfc_handle_init_buffers(ctx, reason, err);
break;
case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET:
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
ctx->int_type = reason;
ctx->int_err = err;
s5p_mfc_handle_stream_complete(ctx);
break;
case S5P_MFC_R2H_CMD_DPB_FLUSH_RET:
ctx->state = MFCINST_RUNNING;
goto irq_cleanup_hw;
default:
mfc_debug(2, "Unknown int reason\n");
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
}
spin_unlock(&dev->irqlock);
mfc_debug_leave();
return IRQ_HANDLED;
irq_cleanup_hw:
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
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();
[media] s5p-mfc: Fix race between interrupt routine and device functions Interrupt routine must wake process waiting for given interrupt AFTER updating driver's internal structures and contexts. Doing it in-between is a serious bug. This patch moves all calls to the wake() function to the end of the interrupt processing block to avoid potential and real races, especially on multi-core platforms. This also fixes following issue reported from clock core (clocks were disabled in interrupt after being unprepared from the other place in the driver, the stack trace however points to the different place than s5p_mfc driver because of the race): WARNING: CPU: 1 PID: 18 at drivers/clk/clk.c:544 clk_core_unprepare+0xc8/0x108 Modules linked in: CPU: 1 PID: 18 Comm: kworker/1:0 Not tainted 4.10.0-next-20170223-00070-g04e18bc99ab9-dirty #2154 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) Workqueue: pm pm_runtime_work [<c010d8b0>] (unwind_backtrace) from [<c010a534>] (show_stack+0x10/0x14) [<c010a534>] (show_stack) from [<c033292c>] (dump_stack+0x74/0x94) [<c033292c>] (dump_stack) from [<c011cef4>] (__warn+0xd4/0x100) [<c011cef4>] (__warn) from [<c011cf40>] (warn_slowpath_null+0x20/0x28) [<c011cf40>] (warn_slowpath_null) from [<c0387a84>] (clk_core_unprepare+0xc8/0x108) [<c0387a84>] (clk_core_unprepare) from [<c0389d84>] (clk_unprepare+0x24/0x2c) [<c0389d84>] (clk_unprepare) from [<c03d4660>] (exynos_sysmmu_suspend+0x48/0x60) [<c03d4660>] (exynos_sysmmu_suspend) from [<c042b9b0>] (pm_generic_runtime_suspend+0x2c/0x38) [<c042b9b0>] (pm_generic_runtime_suspend) from [<c0437580>] (genpd_runtime_suspend+0x94/0x220) [<c0437580>] (genpd_runtime_suspend) from [<c042e240>] (__rpm_callback+0x134/0x208) [<c042e240>] (__rpm_callback) from [<c042e334>] (rpm_callback+0x20/0x80) [<c042e334>] (rpm_callback) from [<c042d3b8>] (rpm_suspend+0xdc/0x458) [<c042d3b8>] (rpm_suspend) from [<c042ea24>] (pm_runtime_work+0x80/0x90) [<c042ea24>] (pm_runtime_work) from [<c01322c4>] (process_one_work+0x120/0x318) [<c01322c4>] (process_one_work) from [<c0132520>] (worker_thread+0x2c/0x4ac) [<c0132520>] (worker_thread) from [<c0137ab0>] (kthread+0xfc/0x134) [<c0137ab0>] (kthread) from [<c0107978>] (ret_from_fork+0x14/0x3c) ---[ end trace 1ead49a7bb83f0d8 ]--- Fixes: af93574678108 ("[media] MFC: Add MFC 5.1 V4L2 driver") Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> CC: stable@vger.kernel.org # v4.5+ Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-23 19:43:27 +08:00
clear_work_bit(ctx);
wake_up(&ctx->queue);
s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
spin_unlock(&dev->irqlock);
mfc_debug(2, "Exit via irq_cleanup_hw\n");
return IRQ_HANDLED;
}
/* Open an MFC node */
static int s5p_mfc_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct s5p_mfc_dev *dev = video_drvdata(file);
struct s5p_mfc_ctx *ctx = NULL;
struct vb2_queue *q;
int ret = 0;
mfc_debug_enter();
if (mutex_lock_interruptible(&dev->mfc_mutex))
return -ERESTARTSYS;
dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
/* Allocate memory for context */
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
ret = -ENOMEM;
goto err_alloc;
}
init_waitqueue_head(&ctx->queue);
v4l2_fh_init(&ctx->fh, vdev);
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) {
mfc_debug(2, "Too many open contexts\n");
ret = -EBUSY;
goto err_no_ctx;
}
}
/* Mark context as idle */
clear_work_bit_irqsave(ctx);
dev->ctx[ctx->num] = ctx;
if (vdev == dev->vfd_dec) {
ctx->type = MFCINST_DECODER;
ctx->c_ops = get_dec_codec_ops();
s5p_mfc_dec_init(ctx);
/* Setup ctrl handler */
ret = s5p_mfc_dec_ctrls_setup(ctx);
if (ret) {
mfc_err("Failed to setup mfc controls\n");
goto err_ctrls_setup;
}
} else if (vdev == dev->vfd_enc) {
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;
s5p_mfc_enc_init(ctx);
/* 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;
ctx->inst_no = MFC_NO_INSTANCE_SET;
/* 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();
ret = s5p_mfc_load_firmware(dev);
if (ret) {
s5p_mfc_clock_off();
goto err_load_fw;
}
/* Init the FW */
ret = s5p_mfc_init_hw(dev);
s5p_mfc_clock_off();
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;
q->lock = &dev->mfc_mutex;
if (vdev == dev->vfd_dec) {
q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
} else if (vdev == dev->vfd_enc) {
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
ret = -ENOENT;
goto err_queue_init;
}
/*
* We'll do mostly sequential access, so sacrifice TLB efficiency for
* faster allocation.
*/
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
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;
q->lock = &dev->mfc_mutex;
if (vdev == dev->vfd_dec) {
q->io_modes = VB2_MMAP;
q->ops = get_dec_queue_ops();
} else if (vdev == dev->vfd_enc) {
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = get_enc_queue_ops();
} else {
ret = -ENOENT;
goto err_queue_init;
}
/* 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;
/*
* We'll do mostly sequential access, so sacrifice TLB efficiency for
* faster allocation.
*/
q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(q);
if (ret) {
mfc_err("Failed to initialize videobuf2 queue(output)\n");
goto err_queue_init;
}
mutex_unlock(&dev->mfc_mutex);
mfc_debug_leave();
return ret;
/* Deinit when failure occurred */
err_queue_init:
if (dev->num_inst == 1)
s5p_mfc_deinit_hw(dev);
err_init_hw:
err_load_fw:
err_pwr_enable:
if (dev->num_inst == 1) {
if (s5p_mfc_power_off() < 0)
mfc_err("power off failed\n");
del_timer_sync(&dev->watchdog_timer);
}
err_ctrls_setup:
s5p_mfc_dec_ctrls_delete(ctx);
err_bad_node:
dev->ctx[ctx->num] = NULL;
err_no_ctx:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
err_alloc:
dev->num_inst--;
mutex_unlock(&dev->mfc_mutex);
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;
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
/* if dev is null, do cleanup that doesn't need dev */
mfc_debug_enter();
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
if (dev)
mutex_lock(&dev->mfc_mutex);
vb2_queue_release(&ctx->vq_src);
vb2_queue_release(&ctx->vq_dst);
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
if (dev) {
s5p_mfc_clock_on();
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08: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);
s5p_mfc_clock_off();
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
if (s5p_mfc_power_off() < 0)
mfc_err("Power off failed\n");
} else {
mfc_debug(2, "Shutting down clock\n");
s5p_mfc_clock_off();
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
}
}
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
if (dev)
dev->ctx[ctx->num] = NULL;
s5p_mfc_dec_ctrls_delete(ctx);
v4l2_fh_del(&ctx->fh);
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
/* vdev is gone if dev is null */
if (dev)
v4l2_fh_exit(&ctx->fh);
kfree(ctx);
mfc_debug_leave();
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
if (dev)
mutex_unlock(&dev->mfc_mutex);
return 0;
}
/* Poll */
static __poll_t s5p_mfc_poll(struct file *file,
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;
__poll_t rc = 0;
unsigned long flags;
mutex_lock(&dev->mfc_mutex);
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))) {
rc = EPOLLERR;
goto end;
}
mutex_unlock(&dev->mfc_mutex);
poll_wait(file, &ctx->fh.wait, wait);
poll_wait(file, &src_q->done_wq, wait);
poll_wait(file, &dst_q->done_wq, wait);
mutex_lock(&dev->mfc_mutex);
if (v4l2_event_pending(&ctx->fh))
rc |= EPOLLPRI;
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))
rc |= EPOLLOUT | EPOLLWRNORM;
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))
rc |= EPOLLIN | EPOLLRDNORM;
spin_unlock_irqrestore(&dst_q->done_lock, flags);
end:
mutex_unlock(&dev->mfc_mutex);
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;
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,
};
/* DMA memory related helper functions */
static void s5p_mfc_memdev_release(struct device *dev)
{
of_reserved_mem_device_release(dev);
}
static struct device *s5p_mfc_alloc_memdev(struct device *dev,
const char *name, unsigned int idx)
{
struct device *child;
int ret;
child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL);
if (!child)
return NULL;
device_initialize(child);
dev_set_name(child, "%s:%s", dev_name(dev), name);
child->parent = dev;
child->bus = dev->bus;
child->coherent_dma_mask = dev->coherent_dma_mask;
child->dma_mask = dev->dma_mask;
child->release = s5p_mfc_memdev_release;
if (device_add(child) == 0) {
ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
idx);
if (ret == 0)
return child;
[media] s5p-mfc: fix failure path of s5p_mfc_alloc_memdev() s5p_mfc_alloc_memdev() function lacks proper releasing of allocated device in case of reserved memory initialization failure. This results in NULL pointer dereference: [ 2.828457] Unable to handle kernel NULL pointer dereference at virtual address 00000001 [ 2.835089] pgd = c0004000 [ 2.837752] [00000001] *pgd=00000000 [ 2.844696] Internal error: Oops: 5 [#1] PREEMPT SMP ARM [ 2.848680] Modules linked in: [ 2.851722] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 4.8.0-rc6-00002-gafa1b97 #878 [ 2.859357] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 2.865433] task: ef080000 task.stack: ef06c000 [ 2.869952] PC is at strcmp+0x0/0x30 [ 2.873508] LR is at platform_match+0x84/0xac [ 2.877847] pc : [<c032621c>] lr : [<c03f65e8>] psr: 20000013 [ 2.877847] sp : ef06dea0 ip : 00000000 fp : 00000000 [ 2.889303] r10: 00000000 r9 : c0b34848 r8 : c0b1e968 [ 2.894511] r7 : 00000000 r6 : 00000001 r5 : c086e7fc r4 : eeb8e010 [ 2.901021] r3 : 0000006d r2 : 00000000 r1 : c086e7fc r0 : 00000001 [ 2.907533] Flags: nzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 2.914649] Control: 10c5387d Table: 4000404a DAC: 00000051 [ 2.920378] Process swapper/0 (pid: 1, stack limit = 0xef06c210) [ 2.926367] Stack: (0xef06dea0 to 0xef06e000) [ 2.930711] dea0: eeb8e010 c0c2d91c c03f4a6c c03f4a8c 00000000 c0c2d91c c03f4a6c c03f2fc8 [ 2.938870] dec0: ef003274 ef10c4c0 c0c2d91c ef10cc80 c0c21270 c03f3fa4 c09c1be8 c0c2d91c [ 2.947028] dee0: 00000006 c0c2d91c 00000006 c0b3483c c0c47000 c03f5314 c0c2d908 c0b5fed8 [ 2.955188] df00: 00000006 c010178c 60000013 c0a4ef14 00000000 c06feaa0 ef080000 60000013 [ 2.963347] df20: 00000000 c0c095c8 efffca76 c0816b8c 000000d5 c0134098 c0b34848 c09d6cdc [ 2.971506] df40: c0a4de70 00000000 00000006 00000006 c0c09568 efffca40 c0b5fed8 00000006 [ 2.979665] df60: c0b3483c c0c47000 000000d5 c0b34848 c0b005a4 c0b00d84 00000006 00000006 [ 2.987824] df80: 00000000 c0b005a4 00000000 c06fb4d8 00000000 00000000 00000000 00000000 [ 2.995983] dfa0: 00000000 c06fb4e0 00000000 c01079b8 00000000 00000000 00000000 00000000 [ 3.004142] dfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 3.012302] dfe0: 00000000 00000000 00000000 00000000 00000013 00000000 ffffffff ffffffff [ 3.020469] [<c032621c>] (strcmp) from [<c03f65e8>] (platform_match+0x84/0xac) [ 3.027672] [<c03f65e8>] (platform_match) from [<c03f4a8c>] (__driver_attach+0x20/0xb0) [ 3.035654] [<c03f4a8c>] (__driver_attach) from [<c03f2fc8>] (bus_for_each_dev+0x54/0x88) [ 3.043812] [<c03f2fc8>] (bus_for_each_dev) from [<c03f3fa4>] (bus_add_driver+0xe8/0x1f4) [ 3.051971] [<c03f3fa4>] (bus_add_driver) from [<c03f5314>] (driver_register+0x78/0xf4) [ 3.059958] [<c03f5314>] (driver_register) from [<c010178c>] (do_one_initcall+0x3c/0x16c) [ 3.068123] [<c010178c>] (do_one_initcall) from [<c0b00d84>] (kernel_init_freeable+0x120/0x1ec) [ 3.076802] [<c0b00d84>] (kernel_init_freeable) from [<c06fb4e0>] (kernel_init+0x8/0x118) [ 3.084958] [<c06fb4e0>] (kernel_init) from [<c01079b8>] (ret_from_fork+0x14/0x3c) [ 3.092506] Code: 1afffffb e12fff1e e1a03000 eafffff7 (e4d03001) [ 3.098618] ---[ end trace 511bf9d750810709 ]--- [ 3.103207] Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b This patch fixes this issue. Fixes: c79667dd93b084fe412bcfe7fbf0ba43f7dec520 ("media: s5p-mfc: replace custom reserved memory handling code with generic one") CC: stable@vger.kernel.org # v4.7+ Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-09-16 14:14:33 +08:00
device_del(child);
}
put_device(child);
return NULL;
}
static int s5p_mfc_configure_2port_memory(struct s5p_mfc_dev *mfc_dev)
{
struct device *dev = &mfc_dev->plat_dev->dev;
void *bank2_virt;
dma_addr_t bank2_dma_addr;
unsigned long align_size = 1 << MFC_BASE_ALIGN_ORDER;
int ret;
/*
* Create and initialize virtual devices for accessing
* reserved memory regions.
*/
mfc_dev->mem_dev[BANK_L_CTX] = s5p_mfc_alloc_memdev(dev, "left",
BANK_L_CTX);
if (!mfc_dev->mem_dev[BANK_L_CTX])
return -ENODEV;
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]);
return -ENODEV;
}
/* Allocate memory for firmware and initialize both banks addresses */
ret = s5p_mfc_alloc_firmware(mfc_dev);
if (ret) {
device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
return ret;
}
mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->fw_buf.dma;
bank2_virt = dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX],
align_size, &bank2_dma_addr, GFP_KERNEL);
if (!bank2_virt) {
mfc_err("Allocating bank2 base failed\n");
s5p_mfc_release_firmware(mfc_dev);
device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
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.
*/
mfc_dev->dma_base[BANK_R_CTX] = bank2_dma_addr - align_size;
dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt,
bank2_dma_addr);
vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX],
DMA_BIT_MASK(32));
vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX],
DMA_BIT_MASK(32));
return 0;
}
static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev)
{
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]);
}
static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
{
struct device *dev = &mfc_dev->plat_dev->dev;
[media] s5p-mfc: Use preallocated block allocator always for MFC v6+ It turned out that all versions of MFC v6+ hardware doesn't have a strict requirement for ALL buffers to be allocated on higher addresses than the firmware base like it was documented for MFC v5. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Basing on this fact, the special DMA configuration based on two reserved memory regions is not really needed for MFC v6+ devices, because the memory requirements for the firmware, device and per-context buffers can be fulfilled by the simple probe-time pre-allocated block allocator introduced in previous patch. This patch enables support for such pre-allocated block based allocator always for MFC v6+ devices. Due to the limitations of the memory management subsystem the largest supported size of the pre-allocated buffer when no CMA (Contiguous Memory Allocator) is enabled is 4 MiB. This patch also removes the requirement to provide two reserved memory regions for MFC v6+ devices in device tree. Now the driver is fully functional without them. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:58:14 +08:00
unsigned long mem_size = SZ_4M;
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08:00
unsigned int bitmap_size;
[media] s5p-mfc: Use preallocated block allocator always for MFC v6+ It turned out that all versions of MFC v6+ hardware doesn't have a strict requirement for ALL buffers to be allocated on higher addresses than the firmware base like it was documented for MFC v5. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Basing on this fact, the special DMA configuration based on two reserved memory regions is not really needed for MFC v6+ devices, because the memory requirements for the firmware, device and per-context buffers can be fulfilled by the simple probe-time pre-allocated block allocator introduced in previous patch. This patch enables support for such pre-allocated block based allocator always for MFC v6+ devices. Due to the limitations of the memory management subsystem the largest supported size of the pre-allocated buffer when no CMA (Contiguous Memory Allocator) is enabled is 4 MiB. This patch also removes the requirement to provide two reserved memory regions for MFC v6+ devices in device tree. Now the driver is fully functional without them. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:58:14 +08:00
if (IS_ENABLED(CONFIG_DMA_CMA) || exynos_is_iommu_available(dev))
mem_size = SZ_8M;
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08: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);
if (!mfc_dev->mem_bitmap)
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08:00
return -ENOMEM;
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08: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;
mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->mem_base;
mfc_dev->dma_base[BANK_R_CTX] = mfc_dev->mem_base;
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08: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);
mfc_dev->dma_base[BANK_L_CTX] += offset;
mfc_dev->dma_base[BANK_R_CTX] += offset;
}
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08:00
/* Firmware allocation cannot fail in this case */
s5p_mfc_alloc_firmware(mfc_dev);
mfc_dev->mem_dev[BANK_L_CTX] = mfc_dev->mem_dev[BANK_R_CTX] = dev;
vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32));
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08:00
dev_info(dev, "preallocated %ld MiB buffer for the firmware and context buffers\n",
(mem_size / SZ_1M));
return 0;
}
static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev)
{
struct device *dev = &mfc_dev->plat_dev->dev;
[media] s5p-mfc: Add support for probe-time preallocated block based allocator Current MFC driver depends on the fact that when IOMMU is available, the DMA-mapping framework and its IOMMU glue will use first-fit allocator. This was true for ARM architecture, but its not for ARM64 arch. However, in case of MFC v6+ hardware and latest firmware, it turned out that there is no strict requirement for ALL buffers to be allocated on higher addresses than the firmware base. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Such relaxed requirements for the memory buffers can be easily fulfilled by allocating firmware, device and per-context buffers from the probe-time preallocated larger buffer. This patch adds support for it. This way the driver finally works fine on ARM64 architecture. The size of the preallocated buffer is 8 MiB, what is enough for three instances H264 decoders or encoders (other codecs have smaller memory requirements). If one needs more for particular use case, one can use "mem" module parameter to force larger (or smaller) buffer (for example by adding "s5p_mfc.mem=16M" to kernel command line). [mchehab@s-opensource.com: fix two checkpatch warnings: don't initialize static to NULL; don't use S_foo permisions] Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:57:11 +08:00
dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt,
mfc_dev->mem_base);
kfree(mfc_dev->mem_bitmap);
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;
[media] s5p-mfc: Use preallocated block allocator always for MFC v6+ It turned out that all versions of MFC v6+ hardware doesn't have a strict requirement for ALL buffers to be allocated on higher addresses than the firmware base like it was documented for MFC v5. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Basing on this fact, the special DMA configuration based on two reserved memory regions is not really needed for MFC v6+ devices, because the memory requirements for the firmware, device and per-context buffers can be fulfilled by the simple probe-time pre-allocated block allocator introduced in previous patch. This patch enables support for such pre-allocated block based allocator always for MFC v6+ devices. Due to the limitations of the memory management subsystem the largest supported size of the pre-allocated buffer when no CMA (Contiguous Memory Allocator) is enabled is 4 MiB. This patch also removes the requirement to provide two reserved memory regions for MFC v6+ devices in device tree. Now the driver is fully functional without them. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:58:14 +08:00
if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
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);
[media] s5p-mfc: Use preallocated block allocator always for MFC v6+ It turned out that all versions of MFC v6+ hardware doesn't have a strict requirement for ALL buffers to be allocated on higher addresses than the firmware base like it was documented for MFC v5. This requirement is true only for the device and per-context buffers. All video data buffers can be allocated anywhere for all MFC v6+ versions. Basing on this fact, the special DMA configuration based on two reserved memory regions is not really needed for MFC v6+ devices, because the memory requirements for the firmware, device and per-context buffers can be fulfilled by the simple probe-time pre-allocated block allocator introduced in previous patch. This patch enables support for such pre-allocated block based allocator always for MFC v6+ devices. Due to the limitations of the memory management subsystem the largest supported size of the pre-allocated buffer when no CMA (Contiguous Memory Allocator) is enabled is 4 MiB. This patch also removes the requirement to provide two reserved memory regions for MFC v6+ devices in device tree. Now the driver is fully functional without them. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Javier Martinez Canillas <javier@osg.samsung.com> Acked-by: Andrzej Hajda <a.hajda@samsung.com> Tested-by: Smitha T Murthy <smitha.t@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-02-13 18:58:14 +08:00
if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev))
s5p_mfc_unconfigure_common_memory(mfc_dev);
else
s5p_mfc_unconfigure_2port_memory(mfc_dev);
}
/* MFC probe function */
static int s5p_mfc_probe(struct platform_device *pdev)
{
struct s5p_mfc_dev *dev;
struct video_device *vfd;
struct resource *res;
int ret;
pr_debug("%s++\n", __func__);
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
spin_lock_init(&dev->irqlock);
spin_lock_init(&dev->condlock);
dev->plat_dev = pdev;
if (!dev->plat_dev) {
dev_err(&pdev->dev, "No platform data specified\n");
return -ENODEV;
}
dev->variant = of_device_get_match_data(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dev->regs_base))
return PTR_ERR(dev->regs_base);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get irq resource\n");
return -ENOENT;
}
dev->irq = res->start;
ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
0, pdev->name, dev);
if (ret) {
dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
return ret;
}
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");
goto err_dma;
}
media: s5p-mfc: Fix lock contention - request_firmware() once Driver calls request_firmware() whenever the device is opened for the first time. As the device gets opened and closed, dev->num_inst == 1 is true several times. This is not necessary since the firmware is saved in the fw_buf. s5p_mfc_load_firmware() copies the buffer returned by the request_firmware() to dev->fw_buf. fw_buf sticks around until it gets released from s5p_mfc_remove(), hence there is no need to keep requesting firmware and copying it to fw_buf. This might have been overlooked when changes are made to free fw_buf from the device release interface s5p_mfc_release(). Fix s5p_mfc_load_firmware() to call request_firmware() once and keep state. Change _probe() to load firmware once fw_buf has been allocated. s5p_mfc_open() and it continues to call s5p_mfc_load_firmware() and init hardware which is the step where firmware is written to the device. This addresses the mfc_mutex contention due to repeated request_firmware() calls from open() in the following circular locking warning: [ 552.194115] qtdemux0:sink/2710 is trying to acquire lock: [ 552.199488] (&dev->mfc_mutex){+.+.}, at: [<bf145544>] s5p_mfc_mmap+0x28/0xd4 [s5p_mfc] [ 552.207459] but task is already holding lock: [ 552.213264] (&mm->mmap_sem){++++}, at: [<c01df2e4>] vm_mmap_pgoff+0x44/0xb8 [ 552.220284] which lock already depends on the new lock. [ 552.228429] the existing dependency chain (in reverse order) is: [ 552.235881] -> #2 (&mm->mmap_sem){++++}: [ 552.241259] __might_fault+0x80/0xb0 [ 552.245331] filldir64+0xc0/0x2f8 [ 552.249144] call_filldir+0xb0/0x14c [ 552.253214] ext4_readdir+0x768/0x90c [ 552.257374] iterate_dir+0x74/0x168 [ 552.261360] SyS_getdents64+0x7c/0x1a0 [ 552.265608] ret_fast_syscall+0x0/0x28 [ 552.269850] -> #1 (&type->i_mutex_dir_key#2){++++}: [ 552.276180] down_read+0x48/0x90 [ 552.279904] lookup_slow+0x74/0x178 [ 552.283889] walk_component+0x1a4/0x2e4 [ 552.288222] link_path_walk+0x174/0x4a0 [ 552.292555] path_openat+0x68/0x944 [ 552.296541] do_filp_open+0x60/0xc4 [ 552.300528] file_open_name+0xe4/0x114 [ 552.304772] filp_open+0x28/0x48 [ 552.308499] kernel_read_file_from_path+0x30/0x78 [ 552.313700] _request_firmware+0x3ec/0x78c [ 552.318291] request_firmware+0x3c/0x54 [ 552.322642] s5p_mfc_load_firmware+0x54/0x150 [s5p_mfc] [ 552.328358] s5p_mfc_open+0x4e4/0x550 [s5p_mfc] [ 552.333394] v4l2_open+0xa0/0x104 [videodev] [ 552.338137] chrdev_open+0xa4/0x18c [ 552.342121] do_dentry_open+0x208/0x310 [ 552.346454] path_openat+0x28c/0x944 [ 552.350526] do_filp_open+0x60/0xc4 [ 552.354512] do_sys_open+0x118/0x1c8 [ 552.358586] ret_fast_syscall+0x0/0x28 [ 552.362830] -> #0 (&dev->mfc_mutex){+.+.}: -> #0 (&dev->mfc_mutex){+.+.}: [ 552.368379] lock_acquire+0x6c/0x88 [ 552.372364] __mutex_lock+0x68/0xa34 [ 552.376437] mutex_lock_interruptible_nested+0x1c/0x24 [ 552.382086] s5p_mfc_mmap+0x28/0xd4 [s5p_mfc] [ 552.386939] v4l2_mmap+0x54/0x88 [videodev] [ 552.391601] mmap_region+0x3a8/0x638 [ 552.395673] do_mmap+0x330/0x3a4 [ 552.399400] vm_mmap_pgoff+0x90/0xb8 [ 552.403472] SyS_mmap_pgoff+0x90/0xc0 [ 552.407632] ret_fast_syscall+0x0/0x28 [ 552.411876] other info that might help us debug this: [ 552.419848] Chain exists of: &dev->mfc_mutex --> &type->i_mutex_dir_key#2 --> &mm->mmap_sem [ 552.431200] Possible unsafe locking scenario: [ 552.437092] CPU0 CPU1 [ 552.441598] ---- ---- [ 552.446104] lock(&mm->mmap_sem); [ 552.449484] lock(&type->i_mutex_dir_key#2); [ 552.456329] lock(&mm->mmap_sem); [ 552.462222] lock(&dev->mfc_mutex); [ 552.465775] *** DEADLOCK *** Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-11-04 10:01:58 +08: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);
mutex_init(&dev->mfc_mutex);
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-17 05:43:17 +08:00
timer_setup(&dev->watchdog_timer, s5p_mfc_watchdog, 0);
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;
}
vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
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;
}
vfd->fops = &s5p_mfc_fops;
vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
vfd->release = video_device_release;
vfd->lock = &dev->mfc_mutex;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->vfl_dir = VFL_DIR_M2M;
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);
/* Initialize HW ops and commands based on MFC version */
s5p_mfc_init_hw_ops(dev);
s5p_mfc_init_hw_cmds(dev);
s5p_mfc_init_regs(dev);
s5p-mfc: Fix race between s5p_mfc_probe() and s5p_mfc_open() The s5p_mfc_probe() function registers the video devices before all the resources needed by s5p_mfc_open() are correctly initalized. So if s5p_mfc_open() function is called before s5p_mfc_probe() finishes (since the video dev is already registered), a NULL pointer dereference will happen due s5p_mfc_open() accessing uninitialized vars such as the struct s5p_mfc_dev .watchdog_timer and .mfc_ops fields. An example is following BUG caused by add_timer() getting a NULL pointer: [ 45.765374] kernel BUG at kernel/time/timer.c:790! [ 45.765381] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM ... [ 45.766149] [<c016fdf4>] (mod_timer) from [<bf181d18>] (s5p_mfc_open+0x274/0x4d4 [s5p_mfc]) [ 45.766416] [<bf181d18>] (s5p_mfc_open [s5p_mfc]) from [<bf0214a0>] (v4l2_open+0x9c/0x100 [videodev]) [ 45.766547] [<bf0214a0>] (v4l2_open [videodev]) from [<c01e355c>] (chrdev_open+0x9c/0x178) [ 45.766575] [<c01e355c>] (chrdev_open) from [<c01dceb4>] (do_dentry_open+0x1e0/0x300) [ 45.766595] [<c01dceb4>] (do_dentry_open) from [<c01ec2f0>] (path_openat+0x800/0x10d4) [ 45.766610] [<c01ec2f0>] (path_openat) from [<c01ed8b8>] (do_filp_open+0x5c/0xc0) [ 45.766624] [<c01ed8b8>] (do_filp_open) from [<c01de218>] (do_sys_open+0x10c/0x1bc) [ 45.766642] [<c01de218>] (do_sys_open) from [<c01078c0>] (ret_fast_syscall+0x0/0x3c) [ 45.766655] Code: eaffffe3 e3a00001 e28dd008 e8bd81f0 (e7f001f2) Fix it by registering the video devs as the last step in s5p_mfc_probe(). Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
2016-05-04 04:27:18 +08:00
/* Register decoder and encoder */
ret = video_register_device(dev->vfd_dec, VFL_TYPE_GRABBER, 0);
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);
ret = video_register_device(dev->vfd_enc, VFL_TYPE_GRABBER, 0);
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);
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:
s5p-mfc: Fix race between s5p_mfc_probe() and s5p_mfc_open() The s5p_mfc_probe() function registers the video devices before all the resources needed by s5p_mfc_open() are correctly initalized. So if s5p_mfc_open() function is called before s5p_mfc_probe() finishes (since the video dev is already registered), a NULL pointer dereference will happen due s5p_mfc_open() accessing uninitialized vars such as the struct s5p_mfc_dev .watchdog_timer and .mfc_ops fields. An example is following BUG caused by add_timer() getting a NULL pointer: [ 45.765374] kernel BUG at kernel/time/timer.c:790! [ 45.765381] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM ... [ 45.766149] [<c016fdf4>] (mod_timer) from [<bf181d18>] (s5p_mfc_open+0x274/0x4d4 [s5p_mfc]) [ 45.766416] [<bf181d18>] (s5p_mfc_open [s5p_mfc]) from [<bf0214a0>] (v4l2_open+0x9c/0x100 [videodev]) [ 45.766547] [<bf0214a0>] (v4l2_open [videodev]) from [<c01e355c>] (chrdev_open+0x9c/0x178) [ 45.766575] [<c01e355c>] (chrdev_open) from [<c01dceb4>] (do_dentry_open+0x1e0/0x300) [ 45.766595] [<c01dceb4>] (do_dentry_open) from [<c01ec2f0>] (path_openat+0x800/0x10d4) [ 45.766610] [<c01ec2f0>] (path_openat) from [<c01ed8b8>] (do_filp_open+0x5c/0xc0) [ 45.766624] [<c01ed8b8>] (do_filp_open) from [<c01de218>] (do_sys_open+0x10c/0x1bc) [ 45.766642] [<c01de218>] (do_sys_open) from [<c01078c0>] (ret_fast_syscall+0x0/0x3c) [ 45.766655] Code: eaffffe3 e3a00001 e28dd008 e8bd81f0 (e7f001f2) Fix it by registering the video devs as the last step in s5p_mfc_probe(). Signed-off-by: Javier Martinez Canillas <javier@osg.samsung.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
2016-05-04 04:27:18 +08:00
video_device_release(dev->vfd_enc);
err_enc_alloc:
video_device_release(dev->vfd_dec);
err_dec_alloc:
v4l2_device_unregister(&dev->v4l2_dev);
err_v4l2_dev_reg:
s5p_mfc_final_pm(dev);
err_dma:
s5p_mfc_unconfigure_dma_memory(dev);
pr_debug("%s-- with error\n", __func__);
return ret;
}
/* Remove the driver */
static int s5p_mfc_remove(struct platform_device *pdev)
{
struct s5p_mfc_dev *dev = platform_get_drvdata(pdev);
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08:00
struct s5p_mfc_ctx *ctx;
int i;
v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name);
[media] media: s5p-mfc fix invalid memory access from s5p_mfc_release() If s5p_mfc_release() runs after s5p_mfc_remove(), the former will access invalid s5p_mfc_dev pointer saved in the s5p_mfc_ctx and runs into kernel paging request errors. Clear ctx dev pointer in s5p_mfc_remove() and change s5p_mfc_release() to avoid work that requires ctx->dev. odroid kernel: Unable to handle kernel paging request at virtual address f17c1104 odroid kernel: pgd = ebca4000 odroid kernel: [f17c1104] *pgd=6e23d811, *pte=00000000, *ppte=00000000 odroid kernel: Internal error: Oops: 807 [#1] PREEMPT SMP ARM odroid kernel: Modules linked in: cpufreq_userspace cpufreq_powersave cpufreq_conservative s5p_mfc s5p_jpeg v4l2_mem2mem videobuf2_dma_contig videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev media odroid kernel: Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) odroid kernel: task: c2241400 ti: e7018000 task.ti: e7018000 odroid kernel: PC is at s5p_mfc_reset+0x40/0x28c [s5p_mfc] odroid kernel: LR is at s5p_mfc_reset+0x34/0x28c [s5p_mfc] odroid kernel: pc : [<bf15bfbc>] lr : [<bf15bfb0>] psr: 60010013 odroid kernel: [<bf15bfbc>] (s5p_mfc_reset [s5p_mfc]) from [<bf15c62c>] (s5p_mfc_deinit_hw+0x14/0x3c [s5p_mfc]) odroid kernel: [<bf15c62c>] (s5p_mfc_deinit_hw [s5p_mfc]) from [<bf155958>] (s5p_mfc_release+0xac/0x1c4 [s5p_mfc]) odroid kernel: [<bf155958>] (s5p_mfc_release [s5p_mfc]) from [<bf021344>] (v4l2_release+0x38/0x74 [videodev]) odroid kernel: [<bf021344>] (v4l2_release [videodev]) from [<c01e4274>] (__fput+0x80/0x1c8) odroid kernel: [<c01e4274>] (__fput) from [<c0135c58>] (task_work_run+0x94/0xc8) odroid kernel: [<c0135c58>] (task_work_run) from [<c010a9d4>] (do_work_pending+0x7c/0xa4) odroid kernel: [<c010a9d4>] (do_work_pending) from [<c0107794>] (slow_work_pending+0xc/0x20) odroid kernel: Code: eb3edacc e5953078 e3a06000 e2833c11 (e5836004) Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> Tested-by: Luis de Bethencourt <luisbg@osg.samsung.com> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-09 06:29:25 +08: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);
del_timer_sync(&dev->watchdog_timer);
flush_work(&dev->watchdog_work);
video_unregister_device(dev->vfd_enc);
video_unregister_device(dev->vfd_dec);
video_device_release(dev->vfd_enc);
video_device_release(dev->vfd_dec);
v4l2_device_unregister(&dev->v4l2_dev);
s5p_mfc_unconfigure_dma_memory(dev);
s5p_mfc_final_pm(dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int s5p_mfc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
int ret;
if (m_dev->num_inst == 0)
return 0;
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,
m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT));
if (ret == 0) {
mfc_err("Waiting for hardware to finish timed out\n");
clear_bit(0, &m_dev->enter_suspend);
return -EIO;
}
}
ret = s5p_mfc_sleep(m_dev);
if (ret) {
clear_bit(0, &m_dev->enter_suspend);
clear_bit(0, &m_dev->hw_lock);
}
return ret;
}
static int s5p_mfc_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct s5p_mfc_dev *m_dev = platform_get_drvdata(pdev);
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)
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = {
.h264_ctx = MFC_H264_CTX_BUF_SIZE,
.non_h264_ctx = MFC_CTX_BUF_SIZE,
.dsc = DESC_BUF_SIZE,
.shm = SHARED_BUF_SIZE,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size buf_size_v5 = {
.fw = MAX_FW_SIZE,
.cpb = MAX_CPB_SIZE,
.priv = &mfc_buf_size_v5,
};
static struct s5p_mfc_variant mfc_drvdata_v5 = {
.version = MFC_VERSION,
.version_bit = MFC_V5_BIT,
.port_num = MFC_NUM_PORTS,
.buf_size = &buf_size_v5,
.fw_name[0] = "s5p-mfc.fw",
.clk_names = {"mfc", "sclk_mfc"},
.num_clocks = 2,
.use_clock_gating = true,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = {
.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,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size buf_size_v6 = {
.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,
.version_bit = MFC_V6_BIT,
.port_num = MFC_NUM_PORTS_V6,
.buf_size = &buf_size_v6,
.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",
.clk_names = {"mfc"},
.num_clocks = 1,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
.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,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size buf_size_v7 = {
.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,
.version_bit = MFC_V7_BIT,
.port_num = MFC_NUM_PORTS_V7,
.buf_size = &buf_size_v7,
.fw_name[0] = "s5p-mfc-v7.fw",
.clk_names = {"mfc", "sclk_mfc"},
.num_clocks = 2,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = {
.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,
.h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V8,
.other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8,
};
[media] s5p_mfc: use static for some structs drivers/media/platform/s5p-mfc/s5p_mfc.c:1334:28: warning: symbol 'mfc_buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1341:25: warning: symbol 'buf_size_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1347:26: warning: symbol 'mfc_buf_align_v5' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1360:28: warning: symbol 'mfc_buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1368:25: warning: symbol 'buf_size_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1374:26: warning: symbol 'mfc_buf_align_v6' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1392:28: warning: symbol 'mfc_buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1400:25: warning: symbol 'buf_size_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1406:26: warning: symbol 'mfc_buf_align_v7' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1419:28: warning: symbol 'mfc_buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1427:25: warning: symbol 'buf_size_v8' was not declared. Should it be static? drivers/media/platform/s5p-mfc/s5p_mfc.c:1433:26: warning: symbol 'mfc_buf_align_v8' was not declared. Should it be static? Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
2014-09-24 20:08:10 +08:00
static struct s5p_mfc_buf_size buf_size_v8 = {
.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,
.fw_name[0] = "s5p-mfc-v8.fw",
.clk_names = {"mfc"},
.num_clocks = 1,
};
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,
};
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,
.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",
};
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,
}, {
.compatible = "samsung,mfc-v7",
.data = &mfc_drvdata_v7,
}, {
.compatible = "samsung,mfc-v8",
.data = &mfc_drvdata_v8,
}, {
.compatible = "samsung,exynos5433-mfc",
.data = &mfc_drvdata_v8_5433,
}, {
.compatible = "samsung,mfc-v10",
.data = &mfc_drvdata_v10,
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_mfc_match);
static struct platform_driver s5p_mfc_driver = {
.probe = s5p_mfc_probe,
.remove = s5p_mfc_remove,
.driver = {
.name = S5P_MFC_NAME,
.pm = &s5p_mfc_pm_ops,
.of_match_table = exynos_mfc_match,
},
};
module_platform_driver(s5p_mfc_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver");