forked from Minki/linux
e99e88a9d2
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>
276 lines
6.7 KiB
C
276 lines
6.7 KiB
C
/*
|
|
* Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
|
|
* Author: Yakir Yang <ykk@rock-chips.com>
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_crtc_helper.h>
|
|
|
|
#include "rockchip_drm_drv.h"
|
|
#include "rockchip_drm_psr.h"
|
|
|
|
#define PSR_FLUSH_TIMEOUT msecs_to_jiffies(100)
|
|
|
|
enum psr_state {
|
|
PSR_FLUSH,
|
|
PSR_ENABLE,
|
|
PSR_DISABLE,
|
|
};
|
|
|
|
struct psr_drv {
|
|
struct list_head list;
|
|
struct drm_encoder *encoder;
|
|
|
|
spinlock_t lock;
|
|
bool active;
|
|
enum psr_state state;
|
|
|
|
struct timer_list flush_timer;
|
|
|
|
void (*set)(struct drm_encoder *encoder, bool enable);
|
|
};
|
|
|
|
static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
|
|
{
|
|
struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
|
|
struct psr_drv *psr;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
|
|
list_for_each_entry(psr, &drm_drv->psr_list, list) {
|
|
if (psr->encoder->crtc == crtc)
|
|
goto out;
|
|
}
|
|
psr = ERR_PTR(-ENODEV);
|
|
|
|
out:
|
|
spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
|
|
return psr;
|
|
}
|
|
|
|
static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
|
|
{
|
|
/*
|
|
* Allowed finite state machine:
|
|
*
|
|
* PSR_ENABLE < = = = = = > PSR_FLUSH
|
|
* | ^ |
|
|
* | | |
|
|
* v | |
|
|
* PSR_DISABLE < - - - - - - - - -
|
|
*/
|
|
if (state == psr->state || !psr->active)
|
|
return;
|
|
|
|
/* Already disabled in flush, change the state, but not the hardware */
|
|
if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
|
|
psr->state = state;
|
|
return;
|
|
}
|
|
|
|
psr->state = state;
|
|
|
|
/* Actually commit the state change to hardware */
|
|
switch (psr->state) {
|
|
case PSR_ENABLE:
|
|
psr->set(psr->encoder, true);
|
|
break;
|
|
|
|
case PSR_DISABLE:
|
|
case PSR_FLUSH:
|
|
psr->set(psr->encoder, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void psr_set_state(struct psr_drv *psr, enum psr_state state)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&psr->lock, flags);
|
|
psr_set_state_locked(psr, state);
|
|
spin_unlock_irqrestore(&psr->lock, flags);
|
|
}
|
|
|
|
static void psr_flush_handler(struct timer_list *t)
|
|
{
|
|
struct psr_drv *psr = from_timer(psr, t, flush_timer);
|
|
unsigned long flags;
|
|
|
|
/* If the state has changed since we initiated the flush, do nothing */
|
|
spin_lock_irqsave(&psr->lock, flags);
|
|
if (psr->state == PSR_FLUSH)
|
|
psr_set_state_locked(psr, PSR_ENABLE);
|
|
spin_unlock_irqrestore(&psr->lock, flags);
|
|
}
|
|
|
|
/**
|
|
* rockchip_drm_psr_activate - activate PSR on the given pipe
|
|
* @crtc: CRTC to obtain the PSR encoder
|
|
*
|
|
* Returns:
|
|
* Zero on success, negative errno on failure.
|
|
*/
|
|
int rockchip_drm_psr_activate(struct drm_crtc *crtc)
|
|
{
|
|
struct psr_drv *psr = find_psr_by_crtc(crtc);
|
|
unsigned long flags;
|
|
|
|
if (IS_ERR(psr))
|
|
return PTR_ERR(psr);
|
|
|
|
spin_lock_irqsave(&psr->lock, flags);
|
|
psr->active = true;
|
|
spin_unlock_irqrestore(&psr->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_activate);
|
|
|
|
/**
|
|
* rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
|
|
* @crtc: CRTC to obtain the PSR encoder
|
|
*
|
|
* Returns:
|
|
* Zero on success, negative errno on failure.
|
|
*/
|
|
int rockchip_drm_psr_deactivate(struct drm_crtc *crtc)
|
|
{
|
|
struct psr_drv *psr = find_psr_by_crtc(crtc);
|
|
unsigned long flags;
|
|
|
|
if (IS_ERR(psr))
|
|
return PTR_ERR(psr);
|
|
|
|
spin_lock_irqsave(&psr->lock, flags);
|
|
psr->active = false;
|
|
spin_unlock_irqrestore(&psr->lock, flags);
|
|
del_timer_sync(&psr->flush_timer);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
|
|
|
|
static void rockchip_drm_do_flush(struct psr_drv *psr)
|
|
{
|
|
mod_timer(&psr->flush_timer,
|
|
round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
|
|
psr_set_state(psr, PSR_FLUSH);
|
|
}
|
|
|
|
/**
|
|
* rockchip_drm_psr_flush - flush a single pipe
|
|
* @crtc: CRTC of the pipe to flush
|
|
*
|
|
* Returns:
|
|
* 0 on success, -errno on fail
|
|
*/
|
|
int rockchip_drm_psr_flush(struct drm_crtc *crtc)
|
|
{
|
|
struct psr_drv *psr = find_psr_by_crtc(crtc);
|
|
if (IS_ERR(psr))
|
|
return PTR_ERR(psr);
|
|
|
|
rockchip_drm_do_flush(psr);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_flush);
|
|
|
|
/**
|
|
* rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
|
|
* @dev: drm device
|
|
*
|
|
* Disable the PSR function for all registered encoders, and then enable the
|
|
* PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
|
|
* changed during flush time, then keep the state no change after flush
|
|
* timeout.
|
|
*
|
|
* Returns:
|
|
* Zero on success, negative errno on failure.
|
|
*/
|
|
void rockchip_drm_psr_flush_all(struct drm_device *dev)
|
|
{
|
|
struct rockchip_drm_private *drm_drv = dev->dev_private;
|
|
struct psr_drv *psr;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
|
|
list_for_each_entry(psr, &drm_drv->psr_list, list)
|
|
rockchip_drm_do_flush(psr);
|
|
spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
|
|
|
|
/**
|
|
* rockchip_drm_psr_register - register encoder to psr driver
|
|
* @encoder: encoder that obtain the PSR function
|
|
* @psr_set: call back to set PSR state
|
|
*
|
|
* Returns:
|
|
* Zero on success, negative errno on failure.
|
|
*/
|
|
int rockchip_drm_psr_register(struct drm_encoder *encoder,
|
|
void (*psr_set)(struct drm_encoder *, bool enable))
|
|
{
|
|
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
|
|
struct psr_drv *psr;
|
|
unsigned long flags;
|
|
|
|
if (!encoder || !psr_set)
|
|
return -EINVAL;
|
|
|
|
psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
|
|
if (!psr)
|
|
return -ENOMEM;
|
|
|
|
timer_setup(&psr->flush_timer, psr_flush_handler, 0);
|
|
spin_lock_init(&psr->lock);
|
|
|
|
psr->active = true;
|
|
psr->state = PSR_DISABLE;
|
|
psr->encoder = encoder;
|
|
psr->set = psr_set;
|
|
|
|
spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
|
|
list_add_tail(&psr->list, &drm_drv->psr_list);
|
|
spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_register);
|
|
|
|
/**
|
|
* rockchip_drm_psr_unregister - unregister encoder to psr driver
|
|
* @encoder: encoder that obtain the PSR function
|
|
* @psr_set: call back to set PSR state
|
|
*
|
|
* Returns:
|
|
* Zero on success, negative errno on failure.
|
|
*/
|
|
void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
|
|
{
|
|
struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
|
|
struct psr_drv *psr, *n;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
|
|
list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
|
|
if (psr->encoder == encoder) {
|
|
del_timer(&psr->flush_timer);
|
|
list_del(&psr->list);
|
|
kfree(psr);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
|
|
}
|
|
EXPORT_SYMBOL(rockchip_drm_psr_unregister);
|