2009-11-03 09:23:50 +00:00
|
|
|
/*
|
|
|
|
* linux/drivers/video/omap2/dss/core.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 Nokia Corporation
|
|
|
|
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
|
|
|
|
*
|
|
|
|
* Some code and ideas taken from drivers/video/omap/ driver
|
|
|
|
* by Imre Deak.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DSS_SUBSYS_NAME "CORE"
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/clk.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <linux/debugfs.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/device.h>
|
2010-02-04 15:03:41 +00:00
|
|
|
#include <linux/regulator/consumer.h>
|
OMAPDSS: Use PM notifiers for system suspend
The current way how omapdss handles system suspend and resume is that
omapdss device (a platform device, which is not part of the device
hierarchy of the DSS HW devices, like DISPC and DSI, or panels.) uses
the suspend and resume callbacks from platform_driver to handle system
suspend. It does this by disabling all enabled panels on suspend, and
resuming the previously disabled panels on resume.
This presents a few problems.
One is that as omapdss device is not related to the panel devices or the
DSS HW devices, there's no ordering in the suspend process. This means
that suspend could be first ran for DSS HW devices and panels, and only
then for omapdss device. Currently this is not a problem, as DSS HW
devices and panels do not handle suspend.
Another, more pressing problem, is that when suspending or resuming, the
runtime PM functions return -EACCES as runtime PM is disabled during
system suspend. This causes the driver to print warnings, and operations
to fail as they think that they failed to bring up the HW.
This patch changes the omapdss suspend handling to use PM notifiers,
which are called before suspend and after resume. This way we have a
normally functioning system when we are suspending and resuming the
panels.
This patch, I believe, creates a problem that somebody could enable or
disable a panel between PM_SUSPEND_PREPARE and the system suspend, and
similarly the other way around in resume. I choose to ignore the problem
for now, as it sounds rather unlikely, and if it happens, it's not
fatal.
In the long run the system suspend handling of omapdss and panels should
be thought out properly. The current approach feels rather hacky.
Perhaps the panel drivers should handle system suspend, or the users of
omapdss (omapfb, omapdrm) should handle system suspend.
Note that after this patch we could probably revert
0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 (OMAPDSS: use sync versions of
pm_runtime_put). But as I said, this patch may be temporary, so let's
leave the sync version still in place.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reported-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Joe Woodward <jw@terrafix.co.uk>
Signed-off-by: Archit Taneja <archit@ti.com>
[fts: fixed 2 brace coding style issues]
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
2012-07-04 12:43:48 +00:00
|
|
|
#include <linux/suspend.h>
|
2012-09-10 10:58:29 +00:00
|
|
|
#include <linux/slab.h>
|
2009-11-03 09:23:50 +00:00
|
|
|
|
2011-05-11 11:05:07 +00:00
|
|
|
#include <video/omapdss.h>
|
2009-11-03 09:23:50 +00:00
|
|
|
|
|
|
|
#include "dss.h"
|
2010-09-15 13:50:00 +00:00
|
|
|
#include "dss_features.h"
|
2009-11-03 09:23:50 +00:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
struct platform_device *pdev;
|
2010-02-04 15:03:41 +00:00
|
|
|
|
|
|
|
struct regulator *vdds_dsi_reg;
|
|
|
|
struct regulator *vdds_sdi_reg;
|
2012-02-23 11:00:51 +00:00
|
|
|
|
|
|
|
const char *default_display_name;
|
2009-11-03 09:23:50 +00:00
|
|
|
} core;
|
|
|
|
|
|
|
|
static char *def_disp_name;
|
|
|
|
module_param_named(def_disp, def_disp_name, charp, 0);
|
2011-02-16 10:53:44 +00:00
|
|
|
MODULE_PARM_DESC(def_disp, "default display name");
|
2009-11-03 09:23:50 +00:00
|
|
|
|
2013-05-23 09:07:50 +00:00
|
|
|
static bool dss_initialized;
|
|
|
|
|
2012-10-29 10:40:46 +00:00
|
|
|
const char *omapdss_get_default_display_name(void)
|
2012-09-06 11:26:10 +00:00
|
|
|
{
|
|
|
|
return core.default_display_name;
|
|
|
|
}
|
2012-10-29 10:40:46 +00:00
|
|
|
EXPORT_SYMBOL(omapdss_get_default_display_name);
|
2012-09-06 11:26:10 +00:00
|
|
|
|
2012-10-18 10:46:29 +00:00
|
|
|
enum omapdss_version omapdss_get_version(void)
|
|
|
|
{
|
|
|
|
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
|
|
|
return pdata->version;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omapdss_get_version);
|
|
|
|
|
2013-05-23 09:07:50 +00:00
|
|
|
bool omapdss_is_initialized(void)
|
|
|
|
{
|
|
|
|
return dss_initialized;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omapdss_is_initialized);
|
|
|
|
|
2012-10-10 07:46:06 +00:00
|
|
|
struct platform_device *dss_get_core_pdev(void)
|
|
|
|
{
|
|
|
|
return core.pdev;
|
|
|
|
}
|
|
|
|
|
2010-02-04 15:03:41 +00:00
|
|
|
/* REGULATORS */
|
|
|
|
|
|
|
|
struct regulator *dss_get_vdds_dsi(void)
|
|
|
|
{
|
|
|
|
struct regulator *reg;
|
|
|
|
|
|
|
|
if (core.vdds_dsi_reg != NULL)
|
|
|
|
return core.vdds_dsi_reg;
|
|
|
|
|
|
|
|
reg = regulator_get(&core.pdev->dev, "vdds_dsi");
|
|
|
|
if (!IS_ERR(reg))
|
|
|
|
core.vdds_dsi_reg = reg;
|
|
|
|
|
|
|
|
return reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct regulator *dss_get_vdds_sdi(void)
|
|
|
|
{
|
|
|
|
struct regulator *reg;
|
|
|
|
|
|
|
|
if (core.vdds_sdi_reg != NULL)
|
|
|
|
return core.vdds_sdi_reg;
|
|
|
|
|
|
|
|
reg = regulator_get(&core.pdev->dev, "vdds_sdi");
|
|
|
|
if (!IS_ERR(reg))
|
|
|
|
core.vdds_sdi_reg = reg;
|
|
|
|
|
|
|
|
return reg;
|
|
|
|
}
|
|
|
|
|
2012-02-20 09:50:06 +00:00
|
|
|
int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
|
|
|
|
{
|
|
|
|
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
|
|
|
|
|
|
|
if (!board_data->dsi_enable_pads)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
return board_data->dsi_enable_pads(dsi_id, lane_mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
|
|
|
|
{
|
|
|
|
struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
|
|
|
|
|
2012-10-16 11:51:21 +00:00
|
|
|
if (!board_data->dsi_disable_pads)
|
2012-02-20 09:50:06 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
return board_data->dsi_disable_pads(dsi_id, lane_mask);
|
|
|
|
}
|
|
|
|
|
2012-03-08 10:52:38 +00:00
|
|
|
int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
|
|
|
|
{
|
|
|
|
struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
|
|
|
|
|
|
|
|
if (pdata->set_min_bus_tput)
|
|
|
|
return pdata->set_min_bus_tput(dev, tput);
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-29 05:55:42 +00:00
|
|
|
#if defined(CONFIG_OMAP2_DSS_DEBUGFS)
|
2009-11-03 09:23:50 +00:00
|
|
|
static int dss_debug_show(struct seq_file *s, void *unused)
|
|
|
|
{
|
|
|
|
void (*func)(struct seq_file *) = s->private;
|
|
|
|
func(s);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dss_debug_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
return single_open(file, dss_debug_show, inode->i_private);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations dss_debug_fops = {
|
|
|
|
.open = dss_debug_open,
|
|
|
|
.read = seq_read,
|
|
|
|
.llseek = seq_lseek,
|
|
|
|
.release = single_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct dentry *dss_debugfs_dir;
|
|
|
|
|
|
|
|
static int dss_initialize_debugfs(void)
|
|
|
|
{
|
|
|
|
dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
|
|
|
|
if (IS_ERR(dss_debugfs_dir)) {
|
|
|
|
int err = PTR_ERR(dss_debugfs_dir);
|
|
|
|
dss_debugfs_dir = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
|
|
|
|
&dss_debug_dump_clocks, &dss_debug_fops);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dss_uninitialize_debugfs(void)
|
|
|
|
{
|
|
|
|
if (dss_debugfs_dir)
|
|
|
|
debugfs_remove_recursive(dss_debugfs_dir);
|
|
|
|
}
|
2012-03-02 16:01:07 +00:00
|
|
|
|
|
|
|
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
|
|
|
{
|
|
|
|
struct dentry *d;
|
|
|
|
|
|
|
|
d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
|
|
|
|
write, &dss_debug_fops);
|
|
|
|
|
2013-03-19 08:03:14 +00:00
|
|
|
return PTR_RET(d);
|
2012-03-02 16:01:07 +00:00
|
|
|
}
|
2012-09-29 05:55:42 +00:00
|
|
|
#else /* CONFIG_OMAP2_DSS_DEBUGFS */
|
2010-05-07 09:58:41 +00:00
|
|
|
static inline int dss_initialize_debugfs(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static inline void dss_uninitialize_debugfs(void)
|
|
|
|
{
|
|
|
|
}
|
2012-05-23 13:45:09 +00:00
|
|
|
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
|
2012-03-02 16:01:07 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2012-09-29 05:55:42 +00:00
|
|
|
#endif /* CONFIG_OMAP2_DSS_DEBUGFS */
|
2009-11-03 09:23:50 +00:00
|
|
|
|
|
|
|
/* PLATFORM DEVICE */
|
OMAPDSS: Use PM notifiers for system suspend
The current way how omapdss handles system suspend and resume is that
omapdss device (a platform device, which is not part of the device
hierarchy of the DSS HW devices, like DISPC and DSI, or panels.) uses
the suspend and resume callbacks from platform_driver to handle system
suspend. It does this by disabling all enabled panels on suspend, and
resuming the previously disabled panels on resume.
This presents a few problems.
One is that as omapdss device is not related to the panel devices or the
DSS HW devices, there's no ordering in the suspend process. This means
that suspend could be first ran for DSS HW devices and panels, and only
then for omapdss device. Currently this is not a problem, as DSS HW
devices and panels do not handle suspend.
Another, more pressing problem, is that when suspending or resuming, the
runtime PM functions return -EACCES as runtime PM is disabled during
system suspend. This causes the driver to print warnings, and operations
to fail as they think that they failed to bring up the HW.
This patch changes the omapdss suspend handling to use PM notifiers,
which are called before suspend and after resume. This way we have a
normally functioning system when we are suspending and resuming the
panels.
This patch, I believe, creates a problem that somebody could enable or
disable a panel between PM_SUSPEND_PREPARE and the system suspend, and
similarly the other way around in resume. I choose to ignore the problem
for now, as it sounds rather unlikely, and if it happens, it's not
fatal.
In the long run the system suspend handling of omapdss and panels should
be thought out properly. The current approach feels rather hacky.
Perhaps the panel drivers should handle system suspend, or the users of
omapdss (omapfb, omapdrm) should handle system suspend.
Note that after this patch we could probably revert
0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 (OMAPDSS: use sync versions of
pm_runtime_put). But as I said, this patch may be temporary, so let's
leave the sync version still in place.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reported-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Joe Woodward <jw@terrafix.co.uk>
Signed-off-by: Archit Taneja <archit@ti.com>
[fts: fixed 2 brace coding style issues]
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
2012-07-04 12:43:48 +00:00
|
|
|
static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
|
|
|
|
{
|
|
|
|
DSSDBG("pm notif %lu\n", v);
|
|
|
|
|
|
|
|
switch (v) {
|
|
|
|
case PM_SUSPEND_PREPARE:
|
|
|
|
DSSDBG("suspending displays\n");
|
|
|
|
return dss_suspend_all_devices();
|
|
|
|
|
|
|
|
case PM_POST_SUSPEND:
|
|
|
|
DSSDBG("resuming displays\n");
|
|
|
|
return dss_resume_all_devices();
|
|
|
|
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block omap_dss_pm_notif_block = {
|
|
|
|
.notifier_call = omap_dss_pm_notif,
|
|
|
|
};
|
|
|
|
|
2012-02-17 15:41:13 +00:00
|
|
|
static int __init omap_dss_probe(struct platform_device *pdev)
|
2009-11-03 09:23:50 +00:00
|
|
|
{
|
|
|
|
struct omap_dss_board_info *pdata = pdev->dev.platform_data;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
core.pdev = pdev;
|
|
|
|
|
2012-10-18 10:46:29 +00:00
|
|
|
dss_features_init(omapdss_get_version());
|
2010-09-15 13:50:00 +00:00
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
r = dss_initialize_debugfs();
|
|
|
|
if (r)
|
2010-05-07 09:58:42 +00:00
|
|
|
goto err_debugfs;
|
2009-11-03 09:23:50 +00:00
|
|
|
|
2012-02-23 11:00:51 +00:00
|
|
|
if (def_disp_name)
|
|
|
|
core.default_display_name = def_disp_name;
|
2012-11-16 12:59:56 +00:00
|
|
|
else if (pdata->default_display_name)
|
|
|
|
core.default_display_name = pdata->default_display_name;
|
2012-02-23 11:00:51 +00:00
|
|
|
else if (pdata->default_device)
|
|
|
|
core.default_display_name = pdata->default_device->name;
|
|
|
|
|
OMAPDSS: Use PM notifiers for system suspend
The current way how omapdss handles system suspend and resume is that
omapdss device (a platform device, which is not part of the device
hierarchy of the DSS HW devices, like DISPC and DSI, or panels.) uses
the suspend and resume callbacks from platform_driver to handle system
suspend. It does this by disabling all enabled panels on suspend, and
resuming the previously disabled panels on resume.
This presents a few problems.
One is that as omapdss device is not related to the panel devices or the
DSS HW devices, there's no ordering in the suspend process. This means
that suspend could be first ran for DSS HW devices and panels, and only
then for omapdss device. Currently this is not a problem, as DSS HW
devices and panels do not handle suspend.
Another, more pressing problem, is that when suspending or resuming, the
runtime PM functions return -EACCES as runtime PM is disabled during
system suspend. This causes the driver to print warnings, and operations
to fail as they think that they failed to bring up the HW.
This patch changes the omapdss suspend handling to use PM notifiers,
which are called before suspend and after resume. This way we have a
normally functioning system when we are suspending and resuming the
panels.
This patch, I believe, creates a problem that somebody could enable or
disable a panel between PM_SUSPEND_PREPARE and the system suspend, and
similarly the other way around in resume. I choose to ignore the problem
for now, as it sounds rather unlikely, and if it happens, it's not
fatal.
In the long run the system suspend handling of omapdss and panels should
be thought out properly. The current approach feels rather hacky.
Perhaps the panel drivers should handle system suspend, or the users of
omapdss (omapfb, omapdrm) should handle system suspend.
Note that after this patch we could probably revert
0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 (OMAPDSS: use sync versions of
pm_runtime_put). But as I said, this patch may be temporary, so let's
leave the sync version still in place.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reported-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Joe Woodward <jw@terrafix.co.uk>
Signed-off-by: Archit Taneja <archit@ti.com>
[fts: fixed 2 brace coding style issues]
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
2012-07-04 12:43:48 +00:00
|
|
|
register_pm_notifier(&omap_dss_pm_notif_block);
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
return 0;
|
|
|
|
|
2010-05-07 09:58:42 +00:00
|
|
|
err_debugfs:
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_dss_remove(struct platform_device *pdev)
|
|
|
|
{
|
OMAPDSS: Use PM notifiers for system suspend
The current way how omapdss handles system suspend and resume is that
omapdss device (a platform device, which is not part of the device
hierarchy of the DSS HW devices, like DISPC and DSI, or panels.) uses
the suspend and resume callbacks from platform_driver to handle system
suspend. It does this by disabling all enabled panels on suspend, and
resuming the previously disabled panels on resume.
This presents a few problems.
One is that as omapdss device is not related to the panel devices or the
DSS HW devices, there's no ordering in the suspend process. This means
that suspend could be first ran for DSS HW devices and panels, and only
then for omapdss device. Currently this is not a problem, as DSS HW
devices and panels do not handle suspend.
Another, more pressing problem, is that when suspending or resuming, the
runtime PM functions return -EACCES as runtime PM is disabled during
system suspend. This causes the driver to print warnings, and operations
to fail as they think that they failed to bring up the HW.
This patch changes the omapdss suspend handling to use PM notifiers,
which are called before suspend and after resume. This way we have a
normally functioning system when we are suspending and resuming the
panels.
This patch, I believe, creates a problem that somebody could enable or
disable a panel between PM_SUSPEND_PREPARE and the system suspend, and
similarly the other way around in resume. I choose to ignore the problem
for now, as it sounds rather unlikely, and if it happens, it's not
fatal.
In the long run the system suspend handling of omapdss and panels should
be thought out properly. The current approach feels rather hacky.
Perhaps the panel drivers should handle system suspend, or the users of
omapdss (omapfb, omapdrm) should handle system suspend.
Note that after this patch we could probably revert
0eaf9f52e94f756147dbfe1faf1f77a02378dbf9 (OMAPDSS: use sync versions of
pm_runtime_put). But as I said, this patch may be temporary, so let's
leave the sync version still in place.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Reported-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Jassi Brar <jaswinder.singh@linaro.org>
Tested-by: Joe Woodward <jw@terrafix.co.uk>
Signed-off-by: Archit Taneja <archit@ti.com>
[fts: fixed 2 brace coding style issues]
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
2012-07-04 12:43:48 +00:00
|
|
|
unregister_pm_notifier(&omap_dss_pm_notif_block);
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
dss_uninitialize_debugfs();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_dss_shutdown(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
DSSDBG("shutdown\n");
|
|
|
|
dss_disable_all_devices();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver omap_dss_driver = {
|
|
|
|
.remove = omap_dss_remove,
|
|
|
|
.shutdown = omap_dss_shutdown,
|
|
|
|
.driver = {
|
|
|
|
.name = "omapdss",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/* BUS */
|
|
|
|
static int dss_bus_match(struct device *dev, struct device_driver *driver)
|
|
|
|
{
|
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
|
|
|
|
|
|
|
DSSDBG("bus_match. dev %s/%s, drv %s\n",
|
|
|
|
dev_name(dev), dssdev->driver_name, driver->name);
|
|
|
|
|
|
|
|
return strcmp(dssdev->driver_name, driver->name) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t device_name_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
|
|
|
dssdev->name ?
|
|
|
|
dssdev->name : "");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct device_attribute default_dev_attrs[] = {
|
|
|
|
__ATTR(name, S_IRUGO, device_name_show, NULL),
|
|
|
|
__ATTR_NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static ssize_t driver_name_show(struct device_driver *drv, char *buf)
|
|
|
|
{
|
|
|
|
struct omap_dss_driver *dssdrv = to_dss_driver(drv);
|
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n",
|
|
|
|
dssdrv->driver.name ?
|
|
|
|
dssdrv->driver.name : "");
|
|
|
|
}
|
|
|
|
static struct driver_attribute default_drv_attrs[] = {
|
|
|
|
__ATTR(name, S_IRUGO, driver_name_show, NULL),
|
|
|
|
__ATTR_NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct bus_type dss_bus_type = {
|
|
|
|
.name = "omapdss",
|
|
|
|
.match = dss_bus_match,
|
|
|
|
.dev_attrs = default_dev_attrs,
|
|
|
|
.drv_attrs = default_drv_attrs,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void dss_bus_release(struct device *dev)
|
|
|
|
{
|
|
|
|
DSSDBG("bus_release\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct device dss_bus = {
|
|
|
|
.release = dss_bus_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bus_type *dss_get_bus(void)
|
|
|
|
{
|
|
|
|
return &dss_bus_type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DRIVER */
|
|
|
|
static int dss_driver_probe(struct device *dev)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
|
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
|
|
|
|
|
|
|
DSSDBG("driver_probe: dev %s/%s, drv %s\n",
|
|
|
|
dev_name(dev), dssdev->driver_name,
|
|
|
|
dssdrv->driver.name);
|
|
|
|
|
|
|
|
r = dssdrv->probe(dssdev);
|
|
|
|
|
|
|
|
if (r) {
|
|
|
|
DSSERR("driver probe failed: %d\n", r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
DSSDBG("probe done for device %s\n", dev_name(dev));
|
|
|
|
|
|
|
|
dssdev->driver = dssdrv;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dss_driver_remove(struct device *dev)
|
|
|
|
{
|
|
|
|
struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
|
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
|
|
|
|
|
|
|
DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev),
|
|
|
|
dssdev->driver_name);
|
|
|
|
|
|
|
|
dssdrv->remove(dssdev);
|
|
|
|
|
|
|
|
dssdev->driver = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
|
|
|
|
{
|
|
|
|
dssdriver->driver.bus = &dss_bus_type;
|
|
|
|
dssdriver->driver.probe = dss_driver_probe;
|
|
|
|
dssdriver->driver.remove = dss_driver_remove;
|
2010-01-11 11:54:33 +00:00
|
|
|
|
|
|
|
if (dssdriver->get_resolution == NULL)
|
|
|
|
dssdriver->get_resolution = omapdss_default_get_resolution;
|
2010-01-11 12:33:40 +00:00
|
|
|
if (dssdriver->get_recommended_bpp == NULL)
|
|
|
|
dssdriver->get_recommended_bpp =
|
|
|
|
omapdss_default_get_recommended_bpp;
|
2012-03-15 18:00:23 +00:00
|
|
|
if (dssdriver->get_timings == NULL)
|
|
|
|
dssdriver->get_timings = omapdss_default_get_timings;
|
2010-01-11 11:54:33 +00:00
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
return driver_register(&dssdriver->driver);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dss_register_driver);
|
|
|
|
|
|
|
|
void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver)
|
|
|
|
{
|
|
|
|
driver_unregister(&dssdriver->driver);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dss_unregister_driver);
|
|
|
|
|
|
|
|
/* DEVICE */
|
|
|
|
|
|
|
|
static void omap_dss_dev_release(struct device *dev)
|
|
|
|
{
|
2012-09-10 10:58:29 +00:00
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
|
|
|
kfree(dssdev);
|
2009-11-03 09:23:50 +00:00
|
|
|
}
|
|
|
|
|
2012-09-06 10:57:39 +00:00
|
|
|
static int disp_num_counter;
|
|
|
|
|
2012-09-10 10:58:29 +00:00
|
|
|
struct omap_dss_device *dss_alloc_and_init_device(struct device *parent)
|
2009-11-03 09:23:50 +00:00
|
|
|
{
|
2012-09-10 10:58:29 +00:00
|
|
|
struct omap_dss_device *dssdev;
|
|
|
|
|
|
|
|
dssdev = kzalloc(sizeof(*dssdev), GFP_KERNEL);
|
|
|
|
if (!dssdev)
|
|
|
|
return NULL;
|
2012-09-06 10:57:39 +00:00
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
dssdev->dev.bus = &dss_bus_type;
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 13:45:53 +00:00
|
|
|
dssdev->dev.parent = parent;
|
2009-11-03 09:23:50 +00:00
|
|
|
dssdev->dev.release = omap_dss_dev_release;
|
2012-09-06 10:57:39 +00:00
|
|
|
dev_set_name(&dssdev->dev, "display%d", disp_num_counter++);
|
2012-09-10 10:58:29 +00:00
|
|
|
|
|
|
|
device_initialize(&dssdev->dev);
|
|
|
|
|
|
|
|
return dssdev;
|
2009-11-03 09:23:50 +00:00
|
|
|
}
|
|
|
|
|
2012-09-10 10:58:29 +00:00
|
|
|
int dss_add_device(struct omap_dss_device *dssdev)
|
|
|
|
{
|
|
|
|
return device_add(&dssdev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dss_put_device(struct omap_dss_device *dssdev)
|
|
|
|
{
|
|
|
|
put_device(&dssdev->dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dss_unregister_device(struct omap_dss_device *dssdev)
|
2009-11-03 09:23:50 +00:00
|
|
|
{
|
|
|
|
device_unregister(&dssdev->dev);
|
|
|
|
}
|
|
|
|
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 13:45:53 +00:00
|
|
|
static int dss_unregister_dss_dev(struct device *dev, void *data)
|
|
|
|
{
|
|
|
|
struct omap_dss_device *dssdev = to_dss_device(dev);
|
2012-09-10 10:58:29 +00:00
|
|
|
dss_unregister_device(dssdev);
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 13:45:53 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-10 10:58:29 +00:00
|
|
|
void dss_unregister_child_devices(struct device *parent)
|
OMAPDSS: interface drivers register their panel devices
Currently the higher level omapdss platform driver gets the list of
displays in its platform data, and uses that list to create the
omap_dss_device for each display.
With DT, the logical way to do the above is to list the displays under
each individual output, i.e. we'd have "dpi" node, under which we would
have the display that uses DPI. In other words, each output driver
handles the displays that use that particular output.
To make the current code ready for DT, this patch modifies the output
drivers so that each of them creates the display devices which use that
output. However, instead of changing the platform data to suit this
method, each output driver is passed the full list of displays, and the
drivers pick the displays that are meant for them. This allows us to
keep the old platform data, and thus we avoid the need to change the
board files.
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
2012-03-01 13:45:53 +00:00
|
|
|
{
|
|
|
|
device_for_each_child(parent, NULL, dss_unregister_dss_dev);
|
|
|
|
}
|
|
|
|
|
2012-09-10 10:58:29 +00:00
|
|
|
void dss_copy_device_pdata(struct omap_dss_device *dst,
|
|
|
|
const struct omap_dss_device *src)
|
|
|
|
{
|
|
|
|
u8 *d = (u8 *)dst;
|
|
|
|
u8 *s = (u8 *)src;
|
|
|
|
size_t dsize = sizeof(struct device);
|
|
|
|
|
|
|
|
memcpy(d + dsize, s + dsize, sizeof(struct omap_dss_device) - dsize);
|
|
|
|
}
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
/* BUS */
|
2012-02-17 15:41:13 +00:00
|
|
|
static int __init omap_dss_bus_register(void)
|
2009-11-03 09:23:50 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = bus_register(&dss_bus_type);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("bus register failed\n");
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_set_name(&dss_bus, "omapdss");
|
|
|
|
r = device_register(&dss_bus);
|
|
|
|
if (r) {
|
|
|
|
DSSERR("bus driver register failed\n");
|
|
|
|
bus_unregister(&dss_bus_type);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* INIT */
|
2012-03-02 15:37:53 +00:00
|
|
|
static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
|
2012-10-22 12:57:25 +00:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_DSI
|
|
|
|
dsi_init_platform_driver,
|
|
|
|
#endif
|
2012-03-02 15:37:53 +00:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_DPI
|
|
|
|
dpi_init_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_SDI
|
|
|
|
sdi_init_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_RFBI
|
|
|
|
rfbi_init_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_VENC
|
|
|
|
venc_init_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP4_DSS_HDMI
|
|
|
|
hdmi_init_platform_driver,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
|
2012-10-22 12:57:25 +00:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_DSI
|
|
|
|
dsi_uninit_platform_driver,
|
|
|
|
#endif
|
2012-03-02 15:37:53 +00:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_DPI
|
|
|
|
dpi_uninit_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_SDI
|
|
|
|
sdi_uninit_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_RFBI
|
|
|
|
rfbi_uninit_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP2_DSS_VENC
|
|
|
|
venc_uninit_platform_driver,
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_OMAP4_DSS_HDMI
|
|
|
|
hdmi_uninit_platform_driver,
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
|
2009-11-03 09:23:50 +00:00
|
|
|
|
2012-03-19 13:05:02 +00:00
|
|
|
static int __init omap_dss_register_drivers(void)
|
|
|
|
{
|
|
|
|
int r;
|
2012-03-02 15:37:53 +00:00
|
|
|
int i;
|
2012-03-19 13:05:02 +00:00
|
|
|
|
2012-03-07 10:53:18 +00:00
|
|
|
r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
|
2012-03-19 13:05:02 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = dss_init_platform_driver();
|
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to initialize DSS platform driver\n");
|
|
|
|
goto err_dss;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = dispc_init_platform_driver();
|
|
|
|
if (r) {
|
|
|
|
DSSERR("Failed to initialize dispc platform driver\n");
|
|
|
|
goto err_dispc;
|
|
|
|
}
|
|
|
|
|
2012-03-02 15:37:53 +00:00
|
|
|
/*
|
|
|
|
* It's ok if the output-driver register fails. It happens, for example,
|
|
|
|
* when there is no output-device (e.g. SDI for OMAP4).
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
|
|
|
|
r = dss_output_drv_reg_funcs[i]();
|
|
|
|
if (r == 0)
|
|
|
|
dss_output_drv_loaded[i] = true;
|
2012-03-19 13:05:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_dispc:
|
|
|
|
dss_uninit_platform_driver();
|
|
|
|
err_dss:
|
|
|
|
platform_driver_unregister(&omap_dss_driver);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit omap_dss_unregister_drivers(void)
|
|
|
|
{
|
2012-03-02 15:37:53 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) {
|
|
|
|
if (dss_output_drv_loaded[i])
|
|
|
|
dss_output_drv_unreg_funcs[i]();
|
|
|
|
}
|
|
|
|
|
2012-03-19 13:05:02 +00:00
|
|
|
dispc_uninit_platform_driver();
|
|
|
|
dss_uninit_platform_driver();
|
|
|
|
|
|
|
|
platform_driver_unregister(&omap_dss_driver);
|
|
|
|
}
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
#ifdef CONFIG_OMAP2_DSS_MODULE
|
|
|
|
static void omap_dss_bus_unregister(void)
|
|
|
|
{
|
|
|
|
device_unregister(&dss_bus);
|
|
|
|
|
|
|
|
bus_unregister(&dss_bus_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init omap_dss_init(void)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = omap_dss_bus_register();
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
2012-03-19 13:05:02 +00:00
|
|
|
r = omap_dss_register_drivers();
|
2009-11-03 09:23:50 +00:00
|
|
|
if (r) {
|
|
|
|
omap_dss_bus_unregister();
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-05-23 09:07:50 +00:00
|
|
|
dss_initialized = true;
|
|
|
|
|
2009-11-03 09:23:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit omap_dss_exit(void)
|
|
|
|
{
|
2010-02-04 15:03:41 +00:00
|
|
|
if (core.vdds_dsi_reg != NULL) {
|
|
|
|
regulator_put(core.vdds_dsi_reg);
|
|
|
|
core.vdds_dsi_reg = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (core.vdds_sdi_reg != NULL) {
|
|
|
|
regulator_put(core.vdds_sdi_reg);
|
|
|
|
core.vdds_sdi_reg = NULL;
|
|
|
|
}
|
|
|
|
|
2012-03-19 13:05:02 +00:00
|
|
|
omap_dss_unregister_drivers();
|
2009-11-03 09:23:50 +00:00
|
|
|
|
|
|
|
omap_dss_bus_unregister();
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(omap_dss_init);
|
|
|
|
module_exit(omap_dss_exit);
|
|
|
|
#else
|
|
|
|
static int __init omap_dss_init(void)
|
|
|
|
{
|
|
|
|
return omap_dss_bus_register();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init omap_dss_init2(void)
|
|
|
|
{
|
2013-05-23 09:07:50 +00:00
|
|
|
int r;
|
|
|
|
|
|
|
|
r = omap_dss_register_drivers();
|
|
|
|
if (r)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
dss_initialized = true;
|
|
|
|
|
|
|
|
return 0;
|
2009-11-03 09:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
core_initcall(omap_dss_init);
|
|
|
|
device_initcall(omap_dss_init2);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
|
|
|
|
MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
|
|
|