blob: 77c0d50babeab85563bf28eba6d7524721f312ec [file] [log] [blame]
/*
* Copyright (C) 2011-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*!
* @file mxc_dispdrv.c
* @brief mxc display driver framework.
*
* A display device driver could call mxc_dispdrv_register(drv) in its dev_probe() function.
* Move all dev_probe() things into mxc_dispdrv_driver->init(), init() function should init
* and feedback setting;
* Move all dev_remove() things into mxc_dispdrv_driver->deinit();
* Move all dev_suspend() things into fb_notifier for SUSPEND, if there is;
* Move all dev_resume() things into fb_notifier for RESUME, if there is;
*
* mxc fb driver could call mxc_dispdrv_gethandle(name, setting) before a fb
* need be added, with fbi param passing by setting, after
* mxc_dispdrv_gethandle() return, FB driver should get the basic setting
* about fbi info and crtc.
*
* @ingroup Framebuffer
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/string.h>
#include "mxc_dispdrv.h"
static LIST_HEAD(dispdrv_list);
static DEFINE_MUTEX(dispdrv_lock);
struct mxc_dispdrv_entry {
/* Note: drv always the first element */
struct mxc_dispdrv_driver *drv;
bool active;
void *priv;
struct list_head list;
};
struct mxc_dispdrv_handle *mxc_dispdrv_register(struct mxc_dispdrv_driver *drv)
{
struct mxc_dispdrv_entry *new;
mutex_lock(&dispdrv_lock);
new = kzalloc(sizeof(struct mxc_dispdrv_entry), GFP_KERNEL);
if (!new) {
mutex_unlock(&dispdrv_lock);
return ERR_PTR(-ENOMEM);
}
new->drv = drv;
list_add_tail(&new->list, &dispdrv_list);
mutex_unlock(&dispdrv_lock);
return (struct mxc_dispdrv_handle *)new;
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_register);
int mxc_dispdrv_unregister(struct mxc_dispdrv_handle *handle)
{
struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
if (entry) {
mutex_lock(&dispdrv_lock);
list_del(&entry->list);
mutex_unlock(&dispdrv_lock);
kfree(entry);
return 0;
} else
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_unregister);
struct mxc_dispdrv_handle *mxc_dispdrv_gethandle(char *name,
struct mxc_dispdrv_setting *setting)
{
int ret = -ENODEV, found = 0;
struct mxc_dispdrv_entry *entry;
mutex_lock(&dispdrv_lock);
list_for_each_entry(entry, &dispdrv_list, list) {
if (!strcmp(entry->drv->name, name) && (entry->drv->init)) {
ret = entry->drv->init((struct mxc_dispdrv_handle *)
entry, setting);
if (ret >= 0) {
entry->active = true;
found = 1;
break;
}
}
}
mutex_unlock(&dispdrv_lock);
return found ? (struct mxc_dispdrv_handle *)entry:ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_gethandle);
void mxc_dispdrv_puthandle(struct mxc_dispdrv_handle *handle)
{
struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
mutex_lock(&dispdrv_lock);
if (entry && entry->active && entry->drv->deinit) {
entry->drv->deinit(handle);
entry->active = false;
}
mutex_unlock(&dispdrv_lock);
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_puthandle);
int mxc_dispdrv_setdata(struct mxc_dispdrv_handle *handle, void *data)
{
struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
if (entry) {
entry->priv = data;
return 0;
} else
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_setdata);
void *mxc_dispdrv_getdata(struct mxc_dispdrv_handle *handle)
{
struct mxc_dispdrv_entry *entry = (struct mxc_dispdrv_entry *)handle;
if (entry) {
return entry->priv;
} else
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL_GPL(mxc_dispdrv_getdata);