提交 9198891b 编写于 作者: T Tomi Valkeinen

drm/omap: remove dss compat code

We have removed all the uses of compat code from omapdrm and the
non-compat parts of omapdss, so now we can remove all the compat code
itself.
Signed-off-by: NTomi Valkeinen <tomi.valkeinen@ti.com>
上级 751d2e18
......@@ -3,9 +3,6 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o
# Core DSS files
omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
output.o dss-of.o pll.o video-pll.o
# DSS compat layer files
omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
dispc-compat.o display-sysfs.o
omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o
omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o
omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
......
/*
* Copyright (C) 2011 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* 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 "APPLY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
#include "dispc-compat.h"
/*
* We have 4 levels of cache for the dispc settings. First two are in SW and
* the latter two in HW.
*
* set_info()
* v
* +--------------------+
* | user_info |
* +--------------------+
* v
* apply()
* v
* +--------------------+
* | info |
* +--------------------+
* v
* write_regs()
* v
* +--------------------+
* | shadow registers |
* +--------------------+
* v
* VFP or lcd/digit_enable
* v
* +--------------------+
* | registers |
* +--------------------+
*/
struct ovl_priv_data {
bool user_info_dirty;
struct omap_overlay_info user_info;
bool info_dirty;
struct omap_overlay_info info;
bool shadow_info_dirty;
bool extra_info_dirty;
bool shadow_extra_info_dirty;
bool enabled;
u32 fifo_low, fifo_high;
/*
* True if overlay is to be enabled. Used to check and calculate configs
* for the overlay before it is enabled in the HW.
*/
bool enabling;
};
struct mgr_priv_data {
bool user_info_dirty;
struct omap_overlay_manager_info user_info;
bool info_dirty;
struct omap_overlay_manager_info info;
bool shadow_info_dirty;
/* If true, GO bit is up and shadow registers cannot be written.
* Never true for manual update displays */
bool busy;
/* If true, dispc output is enabled */
bool updating;
/* If true, a display is enabled using this manager */
bool enabled;
bool extra_info_dirty;
bool shadow_extra_info_dirty;
struct omap_video_timings timings;
struct dss_lcd_mgr_config lcd_config;
void (*framedone_handler)(void *);
void *framedone_handler_data;
};
static struct {
struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
bool irq_enabled;
} dss_data;
/* protects dss_data */
static spinlock_t data_lock;
/* lock for blocking functions */
static DEFINE_MUTEX(apply_lock);
static DECLARE_COMPLETION(extra_updated_completion);
static void dss_register_vsync_isr(void);
static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl)
{
return &dss_data.ovl_priv_data_array[ovl->id];
}
static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr)
{
return &dss_data.mgr_priv_data_array[mgr->id];
}
static void apply_init_priv(void)
{
const int num_ovls = dss_feat_get_num_ovls();
struct mgr_priv_data *mp;
int i;
spin_lock_init(&data_lock);
for (i = 0; i < num_ovls; ++i) {
struct ovl_priv_data *op;
op = &dss_data.ovl_priv_data_array[i];
op->info.color_mode = OMAP_DSS_COLOR_RGB16;
op->info.rotation_type = OMAP_DSS_ROT_DMA;
op->info.global_alpha = 255;
switch (i) {
case 0:
op->info.zorder = 0;
break;
case 1:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0;
break;
case 2:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0;
break;
case 3:
op->info.zorder =
dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0;
break;
}
op->user_info = op->info;
}
/*
* Initialize some of the lcd_config fields for TV manager, this lets
* us prevent checking if the manager is LCD or TV at some places
*/
mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT];
mp->lcd_config.video_port_width = 24;
mp->lcd_config.clock_info.lck_div = 1;
mp->lcd_config.clock_info.pck_div = 1;
}
/*
* A LCD manager's stallmode decides whether it is in manual or auto update. TV
* manager is always auto update, stallmode field for TV manager is false by
* default
*/
static bool ovl_manual_update(struct omap_overlay *ovl)
{
struct mgr_priv_data *mp = get_mgr_priv(ovl->manager);
return mp->lcd_config.stallmode;
}
static bool mgr_manual_update(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
return mp->lcd_config.stallmode;
}
static int dss_check_settings_low(struct omap_overlay_manager *mgr,
bool applying)
{
struct omap_overlay_info *oi;
struct omap_overlay_manager_info *mi;
struct omap_overlay *ovl;
struct omap_overlay_info *ois[MAX_DSS_OVERLAYS];
struct ovl_priv_data *op;
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->enabled)
return 0;
if (applying && mp->user_info_dirty)
mi = &mp->user_info;
else
mi = &mp->info;
/* collect the infos to be tested into the array */
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
if (!op->enabled && !op->enabling)
oi = NULL;
else if (applying && op->user_info_dirty)
oi = &op->user_info;
else
oi = &op->info;
ois[ovl->id] = oi;
}
return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois);
}
/*
* check manager and overlay settings using overlay_info from data->info
*/
static int dss_check_settings(struct omap_overlay_manager *mgr)
{
return dss_check_settings_low(mgr, false);
}
/*
* check manager and overlay settings using overlay_info from ovl->info if
* dirty and from data->info otherwise
*/
static int dss_check_settings_apply(struct omap_overlay_manager *mgr)
{
return dss_check_settings_low(mgr, true);
}
static bool need_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
struct omap_overlay *ovl;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
if (mgr_manual_update(mgr)) {
/* to catch FRAMEDONE */
if (mp->updating)
return true;
} else {
/* to catch GO bit going down */
if (mp->busy)
return true;
/* to write new values to registers */
if (mp->info_dirty)
return true;
/* to set GO bit */
if (mp->shadow_info_dirty)
return true;
/*
* NOTE: we don't check extra_info flags for disabled
* managers, once the manager is enabled, the extra_info
* related manager changes will be taken in by HW.
*/
/* to write new values to registers */
if (mp->extra_info_dirty)
return true;
/* to set GO bit */
if (mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
/*
* NOTE: we check extra_info flags even for
* disabled overlays, as extra_infos need to be
* always written.
*/
/* to write new values to registers */
if (op->extra_info_dirty)
return true;
/* to set GO bit */
if (op->shadow_extra_info_dirty)
return true;
if (!op->enabled)
continue;
/* to write new values to registers */
if (op->info_dirty)
return true;
/* to set GO bit */
if (op->shadow_info_dirty)
return true;
}
}
}
return false;
}
static bool need_go(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
struct ovl_priv_data *op;
mp = get_mgr_priv(mgr);
if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
if (op->shadow_info_dirty || op->shadow_extra_info_dirty)
return true;
}
return false;
}
/* returns true if an extra_info field is currently being updated */
static bool extra_info_update_ongoing(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
if (!mp->updating)
continue;
if (mp->extra_info_dirty || mp->shadow_extra_info_dirty)
return true;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct ovl_priv_data *op = get_ovl_priv(ovl);
if (op->extra_info_dirty || op->shadow_extra_info_dirty)
return true;
}
}
return false;
}
/* wait until no extra_info updates are pending */
static void wait_pending_extra_info_updates(void)
{
bool updating;
unsigned long flags;
unsigned long t;
int r;
spin_lock_irqsave(&data_lock, flags);
updating = extra_info_update_ongoing();
if (!updating) {
spin_unlock_irqrestore(&data_lock, flags);
return;
}
init_completion(&extra_updated_completion);
spin_unlock_irqrestore(&data_lock, flags);
t = msecs_to_jiffies(500);
r = wait_for_completion_timeout(&extra_updated_completion, t);
if (r == 0)
DSSWARN("timeout in wait_pending_extra_info_updates\n");
}
static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr)
{
struct omap_dss_device *dssdev;
dssdev = mgr->output;
if (dssdev == NULL)
return NULL;
while (dssdev->dst)
dssdev = dssdev->dst;
if (dssdev->driver)
return dssdev;
else
return NULL;
}
static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl)
{
return ovl->manager ? dss_mgr_get_device(ovl->manager) : NULL;
}
static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
int r;
if (mgr->output == NULL)
return -ENODEV;
r = dispc_runtime_get();
if (r)
return r;
switch (mgr->output->id) {
case OMAP_DSS_OUTPUT_VENC:
irq = DISPC_IRQ_EVSYNC_ODD;
break;
case OMAP_DSS_OUTPUT_HDMI:
irq = DISPC_IRQ_EVSYNC_EVEN;
break;
default:
irq = dispc_mgr_get_vsync_irq(mgr->id);
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
dispc_runtime_put();
return r;
}
static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
u32 irq;
unsigned long flags;
int r;
int i;
spin_lock_irqsave(&data_lock, flags);
if (mgr_manual_update(mgr)) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
if (!mp->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
spin_unlock_irqrestore(&data_lock, flags);
r = dispc_runtime_get();
if (r)
return r;
irq = dispc_mgr_get_vsync_irq(mgr->id);
i = 0;
while (1) {
bool shadow_dirty, dirty;
spin_lock_irqsave(&data_lock, flags);
dirty = mp->info_dirty;
shadow_dirty = mp->shadow_info_dirty;
spin_unlock_irqrestore(&data_lock, flags);
if (!dirty && !shadow_dirty) {
r = 0;
break;
}
/* 4 iterations is the worst case:
* 1 - initial iteration, dirty = true (between VFP and VSYNC)
* 2 - first VSYNC, dirty = true
* 3 - dirty = false, shadow_dirty = true
* 4 - shadow_dirty = false */
if (i++ == 3) {
DSSERR("mgr(%d)->wait_for_go() not finishing\n",
mgr->id);
r = 0;
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
if (r == -ERESTARTSYS)
break;
if (r) {
DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id);
break;
}
}
dispc_runtime_put();
return r;
}
static int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
{
unsigned long timeout = msecs_to_jiffies(500);
struct ovl_priv_data *op;
struct mgr_priv_data *mp;
u32 irq;
unsigned long flags;
int r;
int i;
if (!ovl->manager)
return 0;
mp = get_mgr_priv(ovl->manager);
spin_lock_irqsave(&data_lock, flags);
if (ovl_manual_update(ovl)) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
if (!mp->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
spin_unlock_irqrestore(&data_lock, flags);
r = dispc_runtime_get();
if (r)
return r;
irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
op = get_ovl_priv(ovl);
i = 0;
while (1) {
bool shadow_dirty, dirty;
spin_lock_irqsave(&data_lock, flags);
dirty = op->info_dirty;
shadow_dirty = op->shadow_info_dirty;
spin_unlock_irqrestore(&data_lock, flags);
if (!dirty && !shadow_dirty) {
r = 0;
break;
}
/* 4 iterations is the worst case:
* 1 - initial iteration, dirty = true (between VFP and VSYNC)
* 2 - first VSYNC, dirty = true
* 3 - dirty = false, shadow_dirty = true
* 4 - shadow_dirty = false */
if (i++ == 3) {
DSSERR("ovl(%d)->wait_for_go() not finishing\n",
ovl->id);
r = 0;
break;
}
r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
if (r == -ERESTARTSYS)
break;
if (r) {
DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id);
break;
}
}
dispc_runtime_put();
return r;
}
static void dss_ovl_write_regs(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct omap_overlay_info *oi;
bool replication;
struct mgr_priv_data *mp;
int r;
DSSDBG("writing ovl %d regs\n", ovl->id);
if (!op->enabled || !op->info_dirty)
return;
oi = &op->info;
mp = get_mgr_priv(ovl->manager);
replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode);
r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings, false);
if (r) {
/*
* We can't do much here, as this function can be called from
* vsync interrupt.
*/
DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id);
/* This will leave fifo configurations in a nonoptimal state */
op->enabled = false;
dispc_ovl_enable(ovl->id, false);
return;
}
op->info_dirty = false;
if (mp->updating)
op->shadow_info_dirty = true;
}
static void dss_ovl_write_regs_extra(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct mgr_priv_data *mp;
DSSDBG("writing ovl %d regs extra\n", ovl->id);
if (!op->extra_info_dirty)
return;
/* note: write also when op->enabled == false, so that the ovl gets
* disabled */
dispc_ovl_enable(ovl->id, op->enabled);
dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high);
mp = get_mgr_priv(ovl->manager);
op->extra_info_dirty = false;
if (mp->updating)
op->shadow_extra_info_dirty = true;
}
static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
struct omap_overlay *ovl;
DSSDBG("writing mgr %d regs\n", mgr->id);
if (!mp->enabled)
return;
WARN_ON(mp->busy);
/* Commit overlay settings */
list_for_each_entry(ovl, &mgr->overlays, list) {
dss_ovl_write_regs(ovl);
dss_ovl_write_regs_extra(ovl);
}
if (mp->info_dirty) {
dispc_mgr_setup(mgr->id, &mp->info);
mp->info_dirty = false;
if (mp->updating)
mp->shadow_info_dirty = true;
}
}
static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
DSSDBG("writing mgr %d regs extra\n", mgr->id);
if (!mp->extra_info_dirty)
return;
dispc_mgr_set_timings(mgr->id, &mp->timings);
/* lcd_config parameters */
if (dss_mgr_is_lcd(mgr->id))
dispc_mgr_set_lcd_config(mgr->id, &mp->lcd_config);
mp->extra_info_dirty = false;
if (mp->updating)
mp->shadow_extra_info_dirty = true;
}
static void dss_write_regs(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
int r;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
continue;
r = dss_check_settings(mgr);
if (r) {
DSSERR("cannot write registers for manager %s: "
"illegal configuration\n", mgr->name);
continue;
}
dss_mgr_write_regs(mgr);
dss_mgr_write_regs_extra(mgr);
}
}
static void dss_set_go_bits(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
int i;
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled || mgr_manual_update(mgr) || mp->busy)
continue;
if (!need_go(mgr))
continue;
mp->busy = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
dispc_mgr_go(mgr->id);
}
}
static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
struct ovl_priv_data *op;
mp = get_mgr_priv(mgr);
mp->shadow_info_dirty = false;
mp->shadow_extra_info_dirty = false;
list_for_each_entry(ovl, &mgr->overlays, list) {
op = get_ovl_priv(ovl);
op->shadow_info_dirty = false;
op->shadow_extra_info_dirty = false;
}
}
static int dss_mgr_connect_compat(enum omap_channel channel,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
return mgr->set_output(mgr, dst);
}
static void dss_mgr_disconnect_compat(enum omap_channel channel,
struct omap_dss_device *dst)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
mgr->unset_output(mgr);
}
static void dss_mgr_start_update_compat(enum omap_channel channel)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
spin_lock_irqsave(&data_lock, flags);
WARN_ON(mp->updating);
r = dss_check_settings(mgr);
if (r) {
DSSERR("cannot start manual update: illegal configuration\n");
spin_unlock_irqrestore(&data_lock, flags);
return;
}
dss_mgr_write_regs(mgr);
dss_mgr_write_regs_extra(mgr);
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
dispc_mgr_enable_sync(mgr->id);
spin_unlock_irqrestore(&data_lock, flags);
}
static void dss_apply_irq_handler(void *data, u32 mask);
static void dss_register_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
u32 mask;
int r, i;
mask = 0;
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_vsync_irq(i);
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
dss_data.irq_enabled = true;
}
static void dss_unregister_vsync_isr(void)
{
const int num_mgrs = dss_feat_get_num_mgrs();
u32 mask;
int r, i;
mask = 0;
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_vsync_irq(i);
for (i = 0; i < num_mgrs; ++i)
mask |= dispc_mgr_get_framedone_irq(i);
r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask);
WARN_ON(r);
dss_data.irq_enabled = false;
}
static void dss_apply_irq_handler(void *data, u32 mask)
{
const int num_mgrs = dss_feat_get_num_mgrs();
int i;
bool extra_updating;
spin_lock(&data_lock);
/* clear busy, updating flags, shadow_dirty flags */
for (i = 0; i < num_mgrs; i++) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mp->enabled)
continue;
mp->updating = dispc_mgr_is_enabled(i);
if (!mgr_manual_update(mgr)) {
bool was_busy = mp->busy;
mp->busy = dispc_mgr_go_busy(i);
if (was_busy && !mp->busy)
mgr_clear_shadow_dirty(mgr);
}
}
dss_write_regs();
dss_set_go_bits();
extra_updating = extra_info_update_ongoing();
if (!extra_updating)
complete_all(&extra_updated_completion);
/* call framedone handlers for manual update displays */
for (i = 0; i < num_mgrs; i++) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
mgr = omap_dss_get_overlay_manager(i);
mp = get_mgr_priv(mgr);
if (!mgr_manual_update(mgr) || !mp->framedone_handler)
continue;
if (mask & dispc_mgr_get_framedone_irq(i))
mp->framedone_handler(mp->framedone_handler_data);
}
if (!need_isr())
dss_unregister_vsync_isr();
spin_unlock(&data_lock);
}
static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl)
{
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
if (!op->user_info_dirty)
return;
op->user_info_dirty = false;
op->info_dirty = true;
op->info = op->user_info;
}
static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->user_info_dirty)
return;
mp->user_info_dirty = false;
mp->info_dirty = true;
mp->info = mp->user_info;
}
static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr)
{
unsigned long flags;
struct omap_overlay *ovl;
int r;
DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name);
spin_lock_irqsave(&data_lock, flags);
r = dss_check_settings_apply(mgr);
if (r) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("failed to apply settings: illegal configuration.\n");
return r;
}
/* Configure overlays */
list_for_each_entry(ovl, &mgr->overlays, list)
omap_dss_mgr_apply_ovl(ovl);
/* Configure manager */
omap_dss_mgr_apply_mgr(mgr);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_apply_ovl_enable(struct omap_overlay *ovl, bool enable)
{
struct ovl_priv_data *op;
op = get_ovl_priv(ovl);
if (op->enabled == enable)
return;
op->enabled = enable;
op->extra_info_dirty = true;
}
static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl,
u32 fifo_low, u32 fifo_high)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
if (op->fifo_low == fifo_low && op->fifo_high == fifo_high)
return;
op->fifo_low = fifo_low;
op->fifo_high = fifo_high;
op->extra_info_dirty = true;
}
static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
u32 fifo_low, fifo_high;
bool use_fifo_merge = false;
if (!op->enabled && !op->enabling)
return;
dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high,
use_fifo_merge, ovl_manual_update(ovl));
dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high);
}
static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
mp = get_mgr_priv(mgr);
if (!mp->enabled)
return;
list_for_each_entry(ovl, &mgr->overlays, list)
dss_ovl_setup_fifo(ovl);
}
static void dss_setup_fifos(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
struct omap_overlay_manager *mgr;
int i;
for (i = 0; i < num_mgrs; ++i) {
mgr = omap_dss_get_overlay_manager(i);
dss_mgr_setup_fifos(mgr);
}
}
static int dss_mgr_enable_compat(enum omap_channel channel)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (mp->enabled)
goto out;
spin_lock_irqsave(&data_lock, flags);
mp->enabled = true;
r = dss_check_settings(mgr);
if (r) {
DSSERR("failed to enable manager %d: check_settings failed\n",
mgr->id);
goto err;
}
dss_setup_fifos();
dss_write_regs();
dss_set_go_bits();
if (!mgr_manual_update(mgr))
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
dss_register_vsync_isr();
spin_unlock_irqrestore(&data_lock, flags);
if (!mgr_manual_update(mgr))
dispc_mgr_enable_sync(mgr->id);
out:
mutex_unlock(&apply_lock);
return 0;
err:
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return r;
}
static void dss_mgr_disable_compat(enum omap_channel channel)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
mutex_lock(&apply_lock);
if (!mp->enabled)
goto out;
wait_pending_extra_info_updates();
if (!mgr_manual_update(mgr))
dispc_mgr_disable_sync(mgr->id);
spin_lock_irqsave(&data_lock, flags);
mp->updating = false;
mp->enabled = false;
spin_unlock_irqrestore(&data_lock, flags);
out:
mutex_unlock(&apply_lock);
}
static int dss_mgr_set_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
r = dss_mgr_simple_check(mgr, info);
if (r)
return r;
spin_lock_irqsave(&data_lock, flags);
mp->user_info = *info;
mp->user_info_dirty = true;
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_mgr_get_info(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
spin_lock_irqsave(&data_lock, flags);
*info = mp->user_info;
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_mgr_set_output(struct omap_overlay_manager *mgr,
struct omap_dss_device *output)
{
int r;
mutex_lock(&apply_lock);
if (mgr->output) {
DSSERR("manager %s is already connected to an output\n",
mgr->name);
r = -EINVAL;
goto err;
}
if ((mgr->supported_outputs & output->id) == 0) {
DSSERR("output does not support manager %s\n",
mgr->name);
r = -EINVAL;
goto err;
}
output->manager = mgr;
mgr->output = output;
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_mgr_unset_output(struct omap_overlay_manager *mgr)
{
int r;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
mutex_lock(&apply_lock);
if (!mgr->output) {
DSSERR("failed to unset output, output not set\n");
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
if (mp->enabled) {
DSSERR("output can't be unset when manager is enabled\n");
r = -EINVAL;
goto err1;
}
spin_unlock_irqrestore(&data_lock, flags);
mgr->output->manager = NULL;
mgr->output = NULL;
mutex_unlock(&apply_lock);
return 0;
err1:
spin_unlock_irqrestore(&data_lock, flags);
err:
mutex_unlock(&apply_lock);
return r;
}
static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
mp->timings = *timings;
mp->extra_info_dirty = true;
}
static void dss_mgr_set_timings_compat(enum omap_channel channel,
const struct omap_video_timings *timings)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
unsigned long flags;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
spin_lock_irqsave(&data_lock, flags);
if (mp->updating) {
DSSERR("cannot set timings for %s: manager needs to be disabled\n",
mgr->name);
goto out;
}
dss_apply_mgr_timings(mgr, timings);
out:
spin_unlock_irqrestore(&data_lock, flags);
}
static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
mp->lcd_config = *config;
mp->extra_info_dirty = true;
}
static void dss_mgr_set_lcd_config_compat(enum omap_channel channel,
const struct dss_lcd_mgr_config *config)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
unsigned long flags;
struct mgr_priv_data *mp = get_mgr_priv(mgr);
spin_lock_irqsave(&data_lock, flags);
if (mp->enabled) {
DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n",
mgr->name);
goto out;
}
dss_apply_mgr_lcd_config(mgr, config);
out:
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_ovl_set_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
r = dss_ovl_simple_check(ovl, info);
if (r)
return r;
spin_lock_irqsave(&data_lock, flags);
op->user_info = *info;
op->user_info_dirty = true;
spin_unlock_irqrestore(&data_lock, flags);
return 0;
}
static void dss_ovl_get_info(struct omap_overlay *ovl,
struct omap_overlay_info *info)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
spin_lock_irqsave(&data_lock, flags);
*info = op->user_info;
spin_unlock_irqrestore(&data_lock, flags);
}
static int dss_ovl_set_manager(struct omap_overlay *ovl,
struct omap_overlay_manager *mgr)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
if (!mgr)
return -EINVAL;
mutex_lock(&apply_lock);
if (ovl->manager) {
DSSERR("overlay '%s' already has a manager '%s'\n",
ovl->name, ovl->manager->name);
r = -EINVAL;
goto err;
}
r = dispc_runtime_get();
if (r)
goto err;
spin_lock_irqsave(&data_lock, flags);
if (op->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("overlay has to be disabled to change the manager\n");
r = -EINVAL;
goto err1;
}
dispc_ovl_set_channel_out(ovl->id, mgr->id);
ovl->manager = mgr;
list_add_tail(&ovl->list, &mgr->overlays);
spin_unlock_irqrestore(&data_lock, flags);
dispc_runtime_put();
mutex_unlock(&apply_lock);
return 0;
err1:
dispc_runtime_put();
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_ovl_unset_manager(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (!ovl->manager) {
DSSERR("failed to detach overlay: manager not set\n");
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
if (op->enabled) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("overlay has to be disabled to unset the manager\n");
r = -EINVAL;
goto err;
}
spin_unlock_irqrestore(&data_lock, flags);
/* wait for pending extra_info updates to ensure the ovl is disabled */
wait_pending_extra_info_updates();
/*
* For a manual update display, there is no guarantee that the overlay
* is really disabled in HW, we may need an extra update from this
* manager before the configurations can go in. Return an error if the
* overlay needed an update from the manager.
*
* TODO: Instead of returning an error, try to do a dummy manager update
* here to disable the overlay in hardware. Use the *GATED fields in
* the DISPC_CONFIG registers to do a dummy update.
*/
spin_lock_irqsave(&data_lock, flags);
if (ovl_manual_update(ovl) && op->extra_info_dirty) {
spin_unlock_irqrestore(&data_lock, flags);
DSSERR("need an update to change the manager\n");
r = -EINVAL;
goto err;
}
ovl->manager = NULL;
list_del(&ovl->list);
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static bool dss_ovl_is_enabled(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
bool e;
spin_lock_irqsave(&data_lock, flags);
e = op->enabled;
spin_unlock_irqrestore(&data_lock, flags);
return e;
}
static int dss_ovl_enable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (op->enabled) {
r = 0;
goto err1;
}
if (ovl->manager == NULL || ovl->manager->output == NULL) {
r = -EINVAL;
goto err1;
}
spin_lock_irqsave(&data_lock, flags);
op->enabling = true;
r = dss_check_settings(ovl->manager);
if (r) {
DSSERR("failed to enable overlay %d: check_settings failed\n",
ovl->id);
goto err2;
}
dss_setup_fifos();
op->enabling = false;
dss_apply_ovl_enable(ovl, true);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err2:
op->enabling = false;
spin_unlock_irqrestore(&data_lock, flags);
err1:
mutex_unlock(&apply_lock);
return r;
}
static int dss_ovl_disable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
int r;
mutex_lock(&apply_lock);
if (!op->enabled) {
r = 0;
goto err;
}
if (ovl->manager == NULL || ovl->manager->output == NULL) {
r = -EINVAL;
goto err;
}
spin_lock_irqsave(&data_lock, flags);
dss_apply_ovl_enable(ovl, false);
dss_write_regs();
dss_set_go_bits();
spin_unlock_irqrestore(&data_lock, flags);
mutex_unlock(&apply_lock);
return 0;
err:
mutex_unlock(&apply_lock);
return r;
}
static int dss_mgr_register_framedone_handler_compat(enum omap_channel channel,
void (*handler)(void *), void *data)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
if (mp->framedone_handler)
return -EBUSY;
mp->framedone_handler = handler;
mp->framedone_handler_data = data;
return 0;
}
static void dss_mgr_unregister_framedone_handler_compat(enum omap_channel channel,
void (*handler)(void *), void *data)
{
struct omap_overlay_manager *mgr = omap_dss_get_overlay_manager(channel);
struct mgr_priv_data *mp = get_mgr_priv(mgr);
WARN_ON(mp->framedone_handler != handler ||
mp->framedone_handler_data != data);
mp->framedone_handler = NULL;
mp->framedone_handler_data = NULL;
}
static const struct dss_mgr_ops apply_mgr_ops = {
.connect = dss_mgr_connect_compat,
.disconnect = dss_mgr_disconnect_compat,
.start_update = dss_mgr_start_update_compat,
.enable = dss_mgr_enable_compat,
.disable = dss_mgr_disable_compat,
.set_timings = dss_mgr_set_timings_compat,
.set_lcd_config = dss_mgr_set_lcd_config_compat,
.register_framedone_handler = dss_mgr_register_framedone_handler_compat,
.unregister_framedone_handler = dss_mgr_unregister_framedone_handler_compat,
};
static int compat_refcnt;
static DEFINE_MUTEX(compat_init_lock);
int omapdss_compat_init(void)
{
struct platform_device *pdev = dss_get_core_pdev();
int i, r;
mutex_lock(&compat_init_lock);
if (compat_refcnt++ > 0)
goto out;
apply_init_priv();
dss_init_overlay_managers_sysfs(pdev);
dss_init_overlays(pdev);
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
mgr->set_output = &dss_mgr_set_output;
mgr->unset_output = &dss_mgr_unset_output;
mgr->apply = &omap_dss_mgr_apply;
mgr->set_manager_info = &dss_mgr_set_info;
mgr->get_manager_info = &dss_mgr_get_info;
mgr->wait_for_go = &dss_mgr_wait_for_go;
mgr->wait_for_vsync = &dss_mgr_wait_for_vsync;
mgr->get_device = &dss_mgr_get_device;
}
for (i = 0; i < omap_dss_get_num_overlays(); i++) {
struct omap_overlay *ovl = omap_dss_get_overlay(i);
ovl->is_enabled = &dss_ovl_is_enabled;
ovl->enable = &dss_ovl_enable;
ovl->disable = &dss_ovl_disable;
ovl->set_manager = &dss_ovl_set_manager;
ovl->unset_manager = &dss_ovl_unset_manager;
ovl->set_overlay_info = &dss_ovl_set_info;
ovl->get_overlay_info = &dss_ovl_get_info;
ovl->wait_for_go = &dss_mgr_wait_for_go_ovl;
ovl->get_device = &dss_ovl_get_device;
}
r = dss_install_mgr_ops(&apply_mgr_ops);
if (r)
goto err_mgr_ops;
r = display_init_sysfs(pdev);
if (r)
goto err_disp_sysfs;
dispc_runtime_get();
r = dss_dispc_initialize_irq();
if (r)
goto err_init_irq;
dispc_runtime_put();
out:
mutex_unlock(&compat_init_lock);
return 0;
err_init_irq:
dispc_runtime_put();
display_uninit_sysfs(pdev);
err_disp_sysfs:
dss_uninstall_mgr_ops();
err_mgr_ops:
dss_uninit_overlay_managers_sysfs(pdev);
dss_uninit_overlays(pdev);
compat_refcnt--;
mutex_unlock(&compat_init_lock);
return r;
}
EXPORT_SYMBOL(omapdss_compat_init);
void omapdss_compat_uninit(void)
{
struct platform_device *pdev = dss_get_core_pdev();
mutex_lock(&compat_init_lock);
if (--compat_refcnt > 0)
goto out;
dss_dispc_uninitialize_irq();
display_uninit_sysfs(pdev);
dss_uninstall_mgr_ops();
dss_uninit_overlay_managers_sysfs(pdev);
dss_uninit_overlays(pdev);
out:
mutex_unlock(&compat_init_lock);
}
EXPORT_SYMBOL(omapdss_compat_uninit);
/*
* Copyright (C) 2012 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* 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 "APPLY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/seq_file.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
#include "dispc-compat.h"
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
DISPC_IRQ_OCP_ERR | \
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
DISPC_IRQ_SYNC_LOST | \
DISPC_IRQ_SYNC_LOST_DIGIT)
#define DISPC_MAX_NR_ISRS 8
struct omap_dispc_isr_data {
omap_dispc_isr_t isr;
void *arg;
u32 mask;
};
struct dispc_irq_stats {
unsigned long last_reset;
unsigned irq_count;
unsigned irqs[32];
};
static struct {
spinlock_t irq_lock;
u32 irq_error_mask;
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
u32 error_irqs;
struct work_struct error_work;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spinlock_t irq_stats_lock;
struct dispc_irq_stats irq_stats;
#endif
} dispc_compat;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
static void dispc_dump_irqs(struct seq_file *s)
{
unsigned long flags;
struct dispc_irq_stats stats;
spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
stats = dispc_compat.irq_stats;
memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
dispc_compat.irq_stats.last_reset = jiffies;
spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
seq_printf(s, "period %u ms\n",
jiffies_to_msecs(jiffies - stats.last_reset));
seq_printf(s, "irqs %d\n", stats.irq_count);
#define PIS(x) \
seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]);
PIS(FRAMEDONE);
PIS(VSYNC);
PIS(EVSYNC_EVEN);
PIS(EVSYNC_ODD);
PIS(ACBIAS_COUNT_STAT);
PIS(PROG_LINE_NUM);
PIS(GFX_FIFO_UNDERFLOW);
PIS(GFX_END_WIN);
PIS(PAL_GAMMA_MASK);
PIS(OCP_ERR);
PIS(VID1_FIFO_UNDERFLOW);
PIS(VID1_END_WIN);
PIS(VID2_FIFO_UNDERFLOW);
PIS(VID2_END_WIN);
if (dss_feat_get_num_ovls() > 3) {
PIS(VID3_FIFO_UNDERFLOW);
PIS(VID3_END_WIN);
}
PIS(SYNC_LOST);
PIS(SYNC_LOST_DIGIT);
PIS(WAKEUP);
if (dss_has_feature(FEAT_MGR_LCD2)) {
PIS(FRAMEDONE2);
PIS(VSYNC2);
PIS(ACBIAS_COUNT_STAT2);
PIS(SYNC_LOST2);
}
if (dss_has_feature(FEAT_MGR_LCD3)) {
PIS(FRAMEDONE3);
PIS(VSYNC3);
PIS(ACBIAS_COUNT_STAT3);
PIS(SYNC_LOST3);
}
#undef PIS
}
#endif
/* dispc.irq_lock has to be locked by the caller */
static void _omap_dispc_set_irqs(void)
{
u32 mask;
int i;
struct omap_dispc_isr_data *isr_data;
mask = dispc_compat.irq_error_mask;
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr == NULL)
continue;
mask |= isr_data->mask;
}
dispc_write_irqenable(mask);
}
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
{
int i;
int ret;
unsigned long flags;
struct omap_dispc_isr_data *isr_data;
if (isr == NULL)
return -EINVAL;
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
/* check for duplicate entry */
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr == isr && isr_data->arg == arg &&
isr_data->mask == mask) {
ret = -EINVAL;
goto err;
}
}
isr_data = NULL;
ret = -EBUSY;
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr != NULL)
continue;
isr_data->isr = isr;
isr_data->arg = arg;
isr_data->mask = mask;
ret = 0;
break;
}
if (ret)
goto err;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return 0;
err:
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return ret;
}
EXPORT_SYMBOL(omap_dispc_register_isr);
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
{
int i;
unsigned long flags;
int ret = -EINVAL;
struct omap_dispc_isr_data *isr_data;
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &dispc_compat.registered_isr[i];
if (isr_data->isr != isr || isr_data->arg != arg ||
isr_data->mask != mask)
continue;
/* found the correct isr */
isr_data->isr = NULL;
isr_data->arg = NULL;
isr_data->mask = 0;
ret = 0;
break;
}
if (ret == 0)
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
return ret;
}
EXPORT_SYMBOL(omap_dispc_unregister_isr);
static void print_irq_status(u32 status)
{
if ((status & dispc_compat.irq_error_mask) == 0)
return;
#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
status,
PIS(OCP_ERR),
PIS(GFX_FIFO_UNDERFLOW),
PIS(VID1_FIFO_UNDERFLOW),
PIS(VID2_FIFO_UNDERFLOW),
dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
PIS(SYNC_LOST),
PIS(SYNC_LOST_DIGIT),
dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
#undef PIS
}
/* Called from dss.c. Note that we don't touch clocks here,
* but we presume they are on because we got an IRQ. However,
* an irq handler may turn the clocks off, so we may not have
* clock later in the function. */
static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
{
int i;
u32 irqstatus, irqenable;
u32 handledirqs = 0;
u32 unhandled_errors;
struct omap_dispc_isr_data *isr_data;
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
spin_lock(&dispc_compat.irq_lock);
irqstatus = dispc_read_irqstatus();
irqenable = dispc_read_irqenable();
/* IRQ is not for us */
if (!(irqstatus & irqenable)) {
spin_unlock(&dispc_compat.irq_lock);
return IRQ_NONE;
}
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spin_lock(&dispc_compat.irq_stats_lock);
dispc_compat.irq_stats.irq_count++;
dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
spin_unlock(&dispc_compat.irq_stats_lock);
#endif
print_irq_status(irqstatus);
/* Ack the interrupt. Do it here before clocks are possibly turned
* off */
dispc_clear_irqstatus(irqstatus);
/* flush posted write */
dispc_read_irqstatus();
/* make a copy and unlock, so that isrs can unregister
* themselves */
memcpy(registered_isr, dispc_compat.registered_isr,
sizeof(registered_isr));
spin_unlock(&dispc_compat.irq_lock);
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
isr_data = &registered_isr[i];
if (!isr_data->isr)
continue;
if (isr_data->mask & irqstatus) {
isr_data->isr(isr_data->arg, irqstatus);
handledirqs |= isr_data->mask;
}
}
spin_lock(&dispc_compat.irq_lock);
unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
if (unhandled_errors) {
dispc_compat.error_irqs |= unhandled_errors;
dispc_compat.irq_error_mask &= ~unhandled_errors;
_omap_dispc_set_irqs();
schedule_work(&dispc_compat.error_work);
}
spin_unlock(&dispc_compat.irq_lock);
return IRQ_HANDLED;
}
static void dispc_error_worker(struct work_struct *work)
{
int i;
u32 errors;
unsigned long flags;
static const unsigned fifo_underflow_bits[] = {
DISPC_IRQ_GFX_FIFO_UNDERFLOW,
DISPC_IRQ_VID1_FIFO_UNDERFLOW,
DISPC_IRQ_VID2_FIFO_UNDERFLOW,
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
};
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
errors = dispc_compat.error_irqs;
dispc_compat.error_irqs = 0;
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
dispc_runtime_get();
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
struct omap_overlay *ovl;
unsigned bit;
ovl = omap_dss_get_overlay(i);
bit = fifo_underflow_bits[i];
if (bit & errors) {
DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
ovl->name);
ovl->disable(ovl);
msleep(50);
}
}
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
unsigned bit;
mgr = omap_dss_get_overlay_manager(i);
bit = dispc_mgr_get_sync_lost_irq(i);
if (bit & errors) {
int j;
DSSERR("SYNC_LOST on channel %s, restarting the output "
"with video overlays disabled\n",
mgr->name);
dss_mgr_disable(mgr->id);
for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
struct omap_overlay *ovl;
ovl = omap_dss_get_overlay(j);
if (ovl->id != OMAP_DSS_GFX &&
ovl->manager == mgr)
ovl->disable(ovl);
}
dss_mgr_enable(mgr->id);
}
}
if (errors & DISPC_IRQ_OCP_ERR) {
DSSERR("OCP_ERR\n");
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
struct omap_overlay_manager *mgr;
mgr = omap_dss_get_overlay_manager(i);
dss_mgr_disable(mgr->id);
}
}
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
dispc_compat.irq_error_mask |= errors;
_omap_dispc_set_irqs();
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
dispc_runtime_put();
}
int dss_dispc_initialize_irq(void)
{
int r;
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
spin_lock_init(&dispc_compat.irq_stats_lock);
dispc_compat.irq_stats.last_reset = jiffies;
dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
#endif
spin_lock_init(&dispc_compat.irq_lock);
memset(dispc_compat.registered_isr, 0,
sizeof(dispc_compat.registered_isr));
dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
if (dss_has_feature(FEAT_MGR_LCD2))
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
if (dss_has_feature(FEAT_MGR_LCD3))
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
if (dss_feat_get_num_ovls() > 3)
dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
/*
* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
* so clear it
*/
dispc_clear_irqstatus(dispc_read_irqstatus());
INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
_omap_dispc_set_irqs();
r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
if (r) {
DSSERR("dispc_request_irq failed\n");
return r;
}
return 0;
}
void dss_dispc_uninitialize_irq(void)
{
dispc_free_irq(&dispc_compat);
}
static void dispc_mgr_disable_isr(void *data, u32 mask)
{
struct completion *compl = data;
complete(compl);
}
static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
{
dispc_mgr_enable(channel, true);
}
static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
{
DECLARE_COMPLETION_ONSTACK(framedone_compl);
int r;
u32 irq;
if (!dispc_mgr_is_enabled(channel))
return;
/*
* When we disable LCD output, we need to wait for FRAMEDONE to know
* that DISPC has finished with the LCD output.
*/
irq = dispc_mgr_get_framedone_irq(channel);
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
irq);
if (r)
DSSERR("failed to register FRAMEDONE isr\n");
dispc_mgr_enable(channel, false);
/* if we couldn't register for framedone, just sleep and exit */
if (r) {
msleep(100);
return;
}
if (!wait_for_completion_timeout(&framedone_compl,
msecs_to_jiffies(100)))
DSSERR("timeout waiting for FRAME DONE\n");
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
irq);
if (r)
DSSERR("failed to unregister FRAMEDONE isr\n");
}
static void dispc_digit_out_enable_isr(void *data, u32 mask)
{
struct completion *compl = data;
/* ignore any sync lost interrupts */
if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
complete(compl);
}
static void dispc_mgr_enable_digit_out(void)
{
DECLARE_COMPLETION_ONSTACK(vsync_compl);
int r;
u32 irq_mask;
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
return;
/*
* Digit output produces some sync lost interrupts during the first
* frame when enabling. Those need to be ignored, so we register for the
* sync lost irq to prevent the error handler from triggering.
*/
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
irq_mask);
if (r) {
DSSERR("failed to register %x isr\n", irq_mask);
return;
}
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
/* wait for the first evsync */
if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
DSSERR("timeout waiting for digit out to start\n");
r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
irq_mask);
if (r)
DSSERR("failed to unregister %x isr\n", irq_mask);
}
static void dispc_mgr_disable_digit_out(void)
{
DECLARE_COMPLETION_ONSTACK(framedone_compl);
int r, i;
u32 irq_mask;
int num_irqs;
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
return;
/*
* When we disable the digit output, we need to wait for FRAMEDONE to
* know that DISPC has finished with the output.
*/
irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
num_irqs = 1;
if (!irq_mask) {
/*
* omap 2/3 don't have framedone irq for TV, so we need to use
* vsyncs for this.
*/
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
/*
* We need to wait for both even and odd vsyncs. Note that this
* is not totally reliable, as we could get a vsync interrupt
* before we disable the output, which leads to timeout in the
* wait_for_completion.
*/
num_irqs = 2;
}
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
irq_mask);
if (r)
DSSERR("failed to register %x isr\n", irq_mask);
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
/* if we couldn't register the irq, just sleep and exit */
if (r) {
msleep(100);
return;
}
for (i = 0; i < num_irqs; ++i) {
if (!wait_for_completion_timeout(&framedone_compl,
msecs_to_jiffies(100)))
DSSERR("timeout waiting for digit out to stop\n");
}
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
irq_mask);
if (r)
DSSERR("failed to unregister %x isr\n", irq_mask);
}
void dispc_mgr_enable_sync(enum omap_channel channel)
{
if (dss_mgr_is_lcd(channel))
dispc_mgr_enable_lcd_out(channel);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_enable_digit_out();
else
WARN_ON(1);
}
void dispc_mgr_disable_sync(enum omap_channel channel)
{
if (dss_mgr_is_lcd(channel))
dispc_mgr_disable_lcd_out(channel);
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
dispc_mgr_disable_digit_out();
else
WARN_ON(1);
}
static inline void dispc_irq_wait_handler(void *data, u32 mask)
{
complete((struct completion *)data);
}
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
unsigned long timeout)
{
int r;
DECLARE_COMPLETION_ONSTACK(completion);
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
irqmask);
if (r)
return r;
timeout = wait_for_completion_interruptible_timeout(&completion,
timeout);
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
if (timeout == 0)
return -ETIMEDOUT;
if (timeout == -ERESTARTSYS)
return -ERESTARTSYS;
return 0;
}
/*
* Copyright (C) 2012 Texas Instruments
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*
* 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/>.
*/
#ifndef __OMAP2_DSS_DISPC_COMPAT_H
#define __OMAP2_DSS_DISPC_COMPAT_H
void dispc_mgr_enable_sync(enum omap_channel channel);
void dispc_mgr_disable_sync(enum omap_channel channel);
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
unsigned long timeout);
int dss_dispc_initialize_irq(void);
void dss_dispc_uninitialize_irq(void);
#endif
......@@ -4155,8 +4155,6 @@ static int dispc_bind(struct device *dev, struct device *master, void *data)
dispc_runtime_put();
dss_init_overlay_managers();
dss_debugfs_create_file("dispc", dispc_dump_regs);
return 0;
......@@ -4170,8 +4168,6 @@ static void dispc_unbind(struct device *dev, struct device *master,
void *data)
{
pm_runtime_disable(dev);
dss_uninit_overlay_managers();
}
static const struct component_ops dispc_component_ops = {
......
/*
* 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 "DISPLAY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <video/omapdss.h>
#include "dss.h"
static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n",
dssdev->name ?
dssdev->name : "");
}
static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n",
omapdss_device_is_enabled(dssdev));
}
static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool enable;
r = strtobool(buf, &enable);
if (r)
return r;
if (enable == omapdss_device_is_enabled(dssdev))
return size;
if (omapdss_device_is_connected(dssdev) == false)
return -ENODEV;
if (enable) {
r = dssdev->driver->enable(dssdev);
if (r)
return r;
} else {
dssdev->driver->disable(dssdev);
}
return size;
}
static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n",
dssdev->driver->get_te ?
dssdev->driver->get_te(dssdev) : 0);
}
static ssize_t display_tear_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool te;
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
return -ENOENT;
r = strtobool(buf, &te);
if (r)
return r;
r = dssdev->driver->enable_te(dssdev, te);
if (r)
return r;
return size;
}
static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf)
{
struct omap_video_timings t;
if (!dssdev->driver->get_timings)
return -ENOENT;
dssdev->driver->get_timings(dssdev, &t);
return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
t.pixelclock,
t.x_res, t.hfp, t.hbp, t.hsw,
t.y_res, t.vfp, t.vbp, t.vsw);
}
static ssize_t display_timings_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
struct omap_video_timings t = dssdev->panel.timings;
int r, found;
if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
return -ENOENT;
found = 0;
#ifdef CONFIG_OMAP2_DSS_VENC
if (strncmp("pal", buf, 3) == 0) {
t = omap_dss_pal_timings;
found = 1;
} else if (strncmp("ntsc", buf, 4) == 0) {
t = omap_dss_ntsc_timings;
found = 1;
}
#endif
if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
&t.pixelclock,
&t.x_res, &t.hfp, &t.hbp, &t.hsw,
&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
return -EINVAL;
r = dssdev->driver->check_timings(dssdev, &t);
if (r)
return r;
dssdev->driver->disable(dssdev);
dssdev->driver->set_timings(dssdev, &t);
r = dssdev->driver->enable(dssdev);
if (r)
return r;
return size;
}
static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf)
{
int rotate;
if (!dssdev->driver->get_rotate)
return -ENOENT;
rotate = dssdev->driver->get_rotate(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
}
static ssize_t display_rotate_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int rot, r;
if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
return -ENOENT;
r = kstrtoint(buf, 0, &rot);
if (r)
return r;
r = dssdev->driver->set_rotate(dssdev, rot);
if (r)
return r;
return size;
}
static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf)
{
int mirror;
if (!dssdev->driver->get_mirror)
return -ENOENT;
mirror = dssdev->driver->get_mirror(dssdev);
return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
}
static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
int r;
bool mirror;
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -ENOENT;
r = strtobool(buf, &mirror);
if (r)
return r;
r = dssdev->driver->set_mirror(dssdev, mirror);
if (r)
return r;
return size;
}
static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf)
{
unsigned int wss;
if (!dssdev->driver->get_wss)
return -ENOENT;
wss = dssdev->driver->get_wss(dssdev);
return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
}
static ssize_t display_wss_store(struct omap_dss_device *dssdev,
const char *buf, size_t size)
{
u32 wss;
int r;
if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
return -ENOENT;
r = kstrtou32(buf, 0, &wss);
if (r)
return r;
if (wss > 0xfffff)
return -EINVAL;
r = dssdev->driver->set_wss(dssdev, wss);
if (r)
return r;
return size;
}
struct display_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_dss_device *, char *);
ssize_t (*store)(struct omap_dss_device *, const char *, size_t);
};
#define DISPLAY_ATTR(_name, _mode, _show, _store) \
struct display_attribute display_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL);
static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL);
static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
display_enabled_show, display_enabled_store);
static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR,
display_tear_show, display_tear_store);
static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR,
display_timings_show, display_timings_store);
static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR,
display_rotate_show, display_rotate_store);
static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR,
display_mirror_show, display_mirror_store);
static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR,
display_wss_show, display_wss_store);
static struct attribute *display_sysfs_attrs[] = {
&display_attr_name.attr,
&display_attr_display_name.attr,
&display_attr_enabled.attr,
&display_attr_tear_elim.attr,
&display_attr_timings.attr,
&display_attr_rotate.attr,
&display_attr_mirror.attr,
&display_attr_wss.attr,
NULL
};
static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_dss_device *dssdev;
struct display_attribute *display_attr;
dssdev = container_of(kobj, struct omap_dss_device, kobj);
display_attr = container_of(attr, struct display_attribute, attr);
if (!display_attr->show)
return -ENOENT;
return display_attr->show(dssdev, buf);
}
static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_dss_device *dssdev;
struct display_attribute *display_attr;
dssdev = container_of(kobj, struct omap_dss_device, kobj);
display_attr = container_of(attr, struct display_attribute, attr);
if (!display_attr->store)
return -ENOENT;
return display_attr->store(dssdev, buf, size);
}
static const struct sysfs_ops display_sysfs_ops = {
.show = display_attr_show,
.store = display_attr_store,
};
static struct kobj_type display_ktype = {
.sysfs_ops = &display_sysfs_ops,
.default_attrs = display_sysfs_attrs,
};
int display_init_sysfs(struct platform_device *pdev)
{
struct omap_dss_device *dssdev = NULL;
int r;
for_each_dss_dev(dssdev) {
r = kobject_init_and_add(&dssdev->kobj, &display_ktype,
&pdev->dev.kobj, "%s", dssdev->alias);
if (r) {
DSSERR("failed to create sysfs files\n");
omap_dss_put_device(dssdev);
goto err;
}
}
return 0;
err:
display_uninit_sysfs(pdev);
return r;
}
void display_uninit_sysfs(struct platform_device *pdev)
{
struct omap_dss_device *dssdev = NULL;
for_each_dss_dev(dssdev) {
if (kobject_name(&dssdev->kobj) == NULL)
continue;
kobject_del(&dssdev->kobj);
kobject_put(&dssdev->kobj);
memset(&dssdev->kobj, 0, sizeof(dssdev->kobj));
}
}
......@@ -207,25 +207,6 @@ void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask);
int dss_set_min_bus_tput(struct device *dev, unsigned long tput);
int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *));
/* display */
int display_init_sysfs(struct platform_device *pdev);
void display_uninit_sysfs(struct platform_device *pdev);
/* manager */
int dss_init_overlay_managers(void);
void dss_uninit_overlay_managers(void);
int dss_init_overlay_managers_sysfs(struct platform_device *pdev);
void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev);
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
const struct omap_overlay_manager_info *info);
int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings);
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
const struct dss_lcd_mgr_config *config,
struct omap_overlay_info **overlay_infos);
static inline bool dss_mgr_is_lcd(enum omap_channel id)
{
if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 ||
......@@ -235,24 +216,6 @@ static inline bool dss_mgr_is_lcd(enum omap_channel id)
return false;
}
int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
struct platform_device *pdev);
void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr);
/* overlay */
void dss_init_overlays(struct platform_device *pdev);
void dss_uninit_overlays(struct platform_device *pdev);
void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr);
int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info);
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings);
bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
enum omap_color_mode mode);
int dss_overlay_kobj_init(struct omap_overlay *ovl,
struct platform_device *pdev);
void dss_overlay_kobj_uninit(struct omap_overlay *ovl);
/* DSS */
int dss_init_platform_driver(void) __init;
void dss_uninit_platform_driver(void);
......
/*
* 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 "MANAGER"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name);
}
static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf)
{
struct omap_dss_device *dssdev = mgr->get_device(mgr);
return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ?
dssdev->name : "<none>");
}
static int manager_display_match(struct omap_dss_device *dssdev, void *data)
{
const char *str = data;
return sysfs_streq(dssdev->name, str);
}
static ssize_t manager_display_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
int r = 0;
size_t len = size;
struct omap_dss_device *dssdev = NULL;
struct omap_dss_device *old_dssdev;
if (buf[size-1] == '\n')
--len;
if (len > 0)
dssdev = omap_dss_find_device((void *)buf,
manager_display_match);
if (len > 0 && dssdev == NULL)
return -EINVAL;
if (dssdev) {
DSSDBG("display %s found\n", dssdev->name);
if (omapdss_device_is_connected(dssdev)) {
DSSERR("new display is already connected\n");
r = -EINVAL;
goto put_device;
}
if (omapdss_device_is_enabled(dssdev)) {
DSSERR("new display is not disabled\n");
r = -EINVAL;
goto put_device;
}
}
old_dssdev = mgr->get_device(mgr);
if (old_dssdev) {
if (omapdss_device_is_enabled(old_dssdev)) {
DSSERR("old display is not disabled\n");
r = -EINVAL;
goto put_device;
}
old_dssdev->driver->disconnect(old_dssdev);
}
if (dssdev) {
r = dssdev->driver->connect(dssdev);
if (r) {
DSSERR("failed to connect new device\n");
goto put_device;
}
old_dssdev = mgr->get_device(mgr);
if (old_dssdev != dssdev) {
DSSERR("failed to connect device to this manager\n");
dssdev->driver->disconnect(dssdev);
goto put_device;
}
r = mgr->apply(mgr);
if (r) {
DSSERR("failed to apply dispc config\n");
goto put_device;
}
}
put_device:
if (dssdev)
omap_dss_put_device(dssdev);
return r ? r : size;
}
static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color);
}
static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
u32 color;
int r;
r = kstrtouint(buf, 0, &color);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.default_color = color;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static const char *trans_key_type_str[] = {
"gfx-destination",
"video-source",
};
static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr,
char *buf)
{
enum omap_dss_trans_key_type key_type;
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
key_type = info.trans_key_type;
BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str));
return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]);
}
static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
enum omap_dss_trans_key_type key_type;
struct omap_overlay_manager_info info;
int r;
for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
key_type < ARRAY_SIZE(trans_key_type_str); key_type++) {
if (sysfs_streq(buf, trans_key_type_str[key_type]))
break;
}
if (key_type == ARRAY_SIZE(trans_key_type_str))
return -EINVAL;
mgr->get_manager_info(mgr, &info);
info.trans_key_type = key_type;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key);
}
static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
u32 key_value;
int r;
r = kstrtouint(buf, 0, &key_value);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.trans_key = key_value;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled);
}
static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
bool enable;
int r;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.trans_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_alpha_blending_enabled_show(
struct omap_overlay_manager *mgr, char *buf)
{
struct omap_overlay_manager_info info;
if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return -ENODEV;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.partial_alpha_enabled);
}
static ssize_t manager_alpha_blending_enabled_store(
struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
bool enable;
int r;
if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return -ENODEV;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
info.partial_alpha_enabled = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable);
}
static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
int r;
bool enable;
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
r = strtobool(buf, &enable);
if (r)
return r;
mgr->get_manager_info(mgr, &info);
if (info.cpr_enable == enable)
return size;
info.cpr_enable = enable;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr,
char *buf)
{
struct omap_overlay_manager_info info;
mgr->get_manager_info(mgr, &info);
return snprintf(buf, PAGE_SIZE,
"%d %d %d %d %d %d %d %d %d\n",
info.cpr_coefs.rr,
info.cpr_coefs.rg,
info.cpr_coefs.rb,
info.cpr_coefs.gr,
info.cpr_coefs.gg,
info.cpr_coefs.gb,
info.cpr_coefs.br,
info.cpr_coefs.bg,
info.cpr_coefs.bb);
}
static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr,
const char *buf, size_t size)
{
struct omap_overlay_manager_info info;
struct omap_dss_cpr_coefs coefs;
int r, i;
s16 *arr;
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd",
&coefs.rr, &coefs.rg, &coefs.rb,
&coefs.gr, &coefs.gg, &coefs.gb,
&coefs.br, &coefs.bg, &coefs.bb) != 9)
return -EINVAL;
arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb,
coefs.gr, coefs.gg, coefs.gb,
coefs.br, coefs.bg, coefs.bb };
for (i = 0; i < 9; ++i) {
if (arr[i] < -512 || arr[i] > 511)
return -EINVAL;
}
mgr->get_manager_info(mgr, &info);
info.cpr_coefs = coefs;
r = mgr->set_manager_info(mgr, &info);
if (r)
return r;
r = mgr->apply(mgr);
if (r)
return r;
return size;
}
struct manager_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay_manager *, char *);
ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t);
};
#define MANAGER_ATTR(_name, _mode, _show, _store) \
struct manager_attribute manager_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL);
static MANAGER_ATTR(display, S_IRUGO|S_IWUSR,
manager_display_show, manager_display_store);
static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR,
manager_default_color_show, manager_default_color_store);
static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR,
manager_trans_key_type_show, manager_trans_key_type_store);
static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR,
manager_trans_key_value_show, manager_trans_key_value_store);
static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR,
manager_trans_key_enabled_show,
manager_trans_key_enabled_store);
static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR,
manager_alpha_blending_enabled_show,
manager_alpha_blending_enabled_store);
static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR,
manager_cpr_enable_show,
manager_cpr_enable_store);
static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR,
manager_cpr_coef_show,
manager_cpr_coef_store);
static struct attribute *manager_sysfs_attrs[] = {
&manager_attr_name.attr,
&manager_attr_display.attr,
&manager_attr_default_color.attr,
&manager_attr_trans_key_type.attr,
&manager_attr_trans_key_value.attr,
&manager_attr_trans_key_enabled.attr,
&manager_attr_alpha_blending_enabled.attr,
&manager_attr_cpr_enable.attr,
&manager_attr_cpr_coef.attr,
NULL
};
static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_overlay_manager *manager;
struct manager_attribute *manager_attr;
manager = container_of(kobj, struct omap_overlay_manager, kobj);
manager_attr = container_of(attr, struct manager_attribute, attr);
if (!manager_attr->show)
return -ENOENT;
return manager_attr->show(manager, buf);
}
static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_overlay_manager *manager;
struct manager_attribute *manager_attr;
manager = container_of(kobj, struct omap_overlay_manager, kobj);
manager_attr = container_of(attr, struct manager_attribute, attr);
if (!manager_attr->store)
return -ENOENT;
return manager_attr->store(manager, buf, size);
}
static const struct sysfs_ops manager_sysfs_ops = {
.show = manager_attr_show,
.store = manager_attr_store,
};
static struct kobj_type manager_ktype = {
.sysfs_ops = &manager_sysfs_ops,
.default_attrs = manager_sysfs_attrs,
};
int dss_manager_kobj_init(struct omap_overlay_manager *mgr,
struct platform_device *pdev)
{
return kobject_init_and_add(&mgr->kobj, &manager_ktype,
&pdev->dev.kobj, "manager%d", mgr->id);
}
void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr)
{
kobject_del(&mgr->kobj);
kobject_put(&mgr->kobj);
memset(&mgr->kobj, 0, sizeof(mgr->kobj));
}
/*
* linux/drivers/video/omap2/dss/manager.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 "MANAGER"
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static int num_managers;
static struct omap_overlay_manager *managers;
int dss_init_overlay_managers(void)
{
int i;
num_managers = dss_feat_get_num_mgrs();
managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers,
GFP_KERNEL);
BUG_ON(managers == NULL);
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
switch (i) {
case 0:
mgr->name = "lcd";
mgr->id = OMAP_DSS_CHANNEL_LCD;
break;
case 1:
mgr->name = "tv";
mgr->id = OMAP_DSS_CHANNEL_DIGIT;
break;
case 2:
mgr->name = "lcd2";
mgr->id = OMAP_DSS_CHANNEL_LCD2;
break;
case 3:
mgr->name = "lcd3";
mgr->id = OMAP_DSS_CHANNEL_LCD3;
break;
}
mgr->caps = 0;
mgr->supported_displays =
dss_feat_get_supported_displays(mgr->id);
mgr->supported_outputs =
dss_feat_get_supported_outputs(mgr->id);
INIT_LIST_HEAD(&mgr->overlays);
}
return 0;
}
int dss_init_overlay_managers_sysfs(struct platform_device *pdev)
{
int i, r;
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
r = dss_manager_kobj_init(mgr, pdev);
if (r)
DSSERR("failed to create sysfs file\n");
}
return 0;
}
void dss_uninit_overlay_managers(void)
{
kfree(managers);
managers = NULL;
num_managers = 0;
}
void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev)
{
int i;
for (i = 0; i < num_managers; ++i) {
struct omap_overlay_manager *mgr = &managers[i];
dss_manager_kobj_uninit(mgr);
}
}
int omap_dss_get_num_overlay_managers(void)
{
return num_managers;
}
EXPORT_SYMBOL(omap_dss_get_num_overlay_managers);
struct omap_overlay_manager *omap_dss_get_overlay_manager(int num)
{
if (num >= num_managers)
return NULL;
return &managers[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay_manager);
int dss_mgr_simple_check(struct omap_overlay_manager *mgr,
const struct omap_overlay_manager_info *info)
{
if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) {
/*
* OMAP3 supports only graphics source transparency color key
* and alpha blending simultaneously. See TRM 15.4.2.4.2.2
* Alpha Mode.
*/
if (info->partial_alpha_enabled && info->trans_enabled
&& info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) {
DSSERR("check_manager: illegal transparency key\n");
return -EINVAL;
}
}
return 0;
}
static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl1, *ovl2;
struct omap_overlay_info *info1, *info2;
list_for_each_entry(ovl1, &mgr->overlays, list) {
info1 = overlay_infos[ovl1->id];
if (info1 == NULL)
continue;
list_for_each_entry(ovl2, &mgr->overlays, list) {
if (ovl1 == ovl2)
continue;
info2 = overlay_infos[ovl2->id];
if (info2 == NULL)
continue;
if (info1->zorder == info2->zorder) {
DSSERR("overlays %d and %d have the same "
"zorder %d\n",
ovl1->id, ovl2->id, info1->zorder);
return -EINVAL;
}
}
}
return 0;
}
int dss_mgr_check_timings(struct omap_overlay_manager *mgr,
const struct omap_video_timings *timings)
{
if (!dispc_mgr_timings_ok(mgr->id, timings)) {
DSSERR("check_manager: invalid timings\n");
return -EINVAL;
}
return 0;
}
static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr,
const struct dss_lcd_mgr_config *config)
{
struct dispc_clock_info cinfo = config->clock_info;
int dl = config->video_port_width;
bool stallmode = config->stallmode;
bool fifohandcheck = config->fifohandcheck;
if (cinfo.lck_div < 1 || cinfo.lck_div > 255)
return -EINVAL;
if (cinfo.pck_div < 1 || cinfo.pck_div > 255)
return -EINVAL;
if (dl != 12 && dl != 16 && dl != 18 && dl != 24)
return -EINVAL;
/* fifohandcheck should be used only with stallmode */
if (!stallmode && fifohandcheck)
return -EINVAL;
/*
* io pad mode can be only checked by using dssdev connected to the
* manager. Ignore checking these for now, add checks when manager
* is capable of holding information related to the connected interface
*/
return 0;
}
int dss_mgr_check(struct omap_overlay_manager *mgr,
struct omap_overlay_manager_info *info,
const struct omap_video_timings *mgr_timings,
const struct dss_lcd_mgr_config *lcd_config,
struct omap_overlay_info **overlay_infos)
{
struct omap_overlay *ovl;
int r;
if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) {
r = dss_mgr_check_zorder(mgr, overlay_infos);
if (r)
return r;
}
r = dss_mgr_check_timings(mgr, mgr_timings);
if (r)
return r;
r = dss_mgr_check_lcd_config(mgr, lcd_config);
if (r)
return r;
list_for_each_entry(ovl, &mgr->overlays, list) {
struct omap_overlay_info *oi;
int r;
oi = overlay_infos[ovl->id];
if (oi == NULL)
continue;
r = dss_ovl_check(ovl, oi, mgr_timings);
if (r)
return r;
}
return 0;
}
/*
* 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 "OVERLAY"
#include <linux/module.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name);
}
static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n",
ovl->manager ? ovl->manager->name : "<none>");
}
static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf,
size_t size)
{
int i, r;
struct omap_overlay_manager *mgr = NULL;
struct omap_overlay_manager *old_mgr;
int len = size;
if (buf[size-1] == '\n')
--len;
if (len > 0) {
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
mgr = omap_dss_get_overlay_manager(i);
if (sysfs_streq(buf, mgr->name))
break;
mgr = NULL;
}
}
if (len > 0 && mgr == NULL)
return -EINVAL;
if (mgr)
DSSDBG("manager %s found\n", mgr->name);
if (mgr == ovl->manager)
return size;
old_mgr = ovl->manager;
r = dispc_runtime_get();
if (r)
return r;
/* detach old manager */
if (old_mgr) {
r = ovl->unset_manager(ovl);
if (r) {
DSSERR("detach failed\n");
goto err;
}
r = old_mgr->apply(old_mgr);
if (r)
goto err;
}
if (mgr) {
r = ovl->set_manager(ovl, mgr);
if (r) {
DSSERR("Failed to attach overlay\n");
goto err;
}
r = mgr->apply(mgr);
if (r)
goto err;
}
dispc_runtime_put();
return size;
err:
dispc_runtime_put();
return r;
}
static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.width, info.height);
}
static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.screen_width);
}
static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.pos_x, info.pos_y);
}
static ssize_t overlay_position_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
char *last;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.pos_x = simple_strtoul(buf, &last, 10);
++last;
if (last - buf >= size)
return -EINVAL;
info.pos_y = simple_strtoul(last, &last, 10);
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d,%d\n",
info.out_width, info.out_height);
}
static ssize_t overlay_output_size_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
char *last;
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.out_width = simple_strtoul(buf, &last, 10);
++last;
if (last - buf >= size)
return -EINVAL;
info.out_height = simple_strtoul(last, &last, 10);
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl));
}
static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
size_t size)
{
int r;
bool enable;
r = strtobool(buf, &enable);
if (r)
return r;
if (enable)
r = ovl->enable(ovl);
else
r = ovl->disable(ovl);
if (r)
return r;
return size;
}
static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.global_alpha);
}
static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 alpha;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.global_alpha = alpha;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl,
char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n",
info.pre_mult_alpha);
}
static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 alpha;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &alpha);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.pre_mult_alpha = alpha;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf)
{
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
return snprintf(buf, PAGE_SIZE, "%d\n", info.zorder);
}
static ssize_t overlay_zorder_store(struct omap_overlay *ovl,
const char *buf, size_t size)
{
int r;
u8 zorder;
struct omap_overlay_info info;
if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
return -ENODEV;
r = kstrtou8(buf, 0, &zorder);
if (r)
return r;
ovl->get_overlay_info(ovl, &info);
info.zorder = zorder;
r = ovl->set_overlay_info(ovl, &info);
if (r)
return r;
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
return r;
}
return size;
}
struct overlay_attribute {
struct attribute attr;
ssize_t (*show)(struct omap_overlay *, char *);
ssize_t (*store)(struct omap_overlay *, const char *, size_t);
};
#define OVERLAY_ATTR(_name, _mode, _show, _store) \
struct overlay_attribute overlay_attr_##_name = \
__ATTR(_name, _mode, _show, _store)
static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL);
static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR,
overlay_manager_show, overlay_manager_store);
static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL);
static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL);
static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR,
overlay_position_show, overlay_position_store);
static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR,
overlay_output_size_show, overlay_output_size_store);
static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR,
overlay_enabled_show, overlay_enabled_store);
static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR,
overlay_global_alpha_show, overlay_global_alpha_store);
static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR,
overlay_pre_mult_alpha_show,
overlay_pre_mult_alpha_store);
static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR,
overlay_zorder_show, overlay_zorder_store);
static struct attribute *overlay_sysfs_attrs[] = {
&overlay_attr_name.attr,
&overlay_attr_manager.attr,
&overlay_attr_input_size.attr,
&overlay_attr_screen_width.attr,
&overlay_attr_position.attr,
&overlay_attr_output_size.attr,
&overlay_attr_enabled.attr,
&overlay_attr_global_alpha.attr,
&overlay_attr_pre_mult_alpha.attr,
&overlay_attr_zorder.attr,
NULL
};
static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct omap_overlay *overlay;
struct overlay_attribute *overlay_attr;
overlay = container_of(kobj, struct omap_overlay, kobj);
overlay_attr = container_of(attr, struct overlay_attribute, attr);
if (!overlay_attr->show)
return -ENOENT;
return overlay_attr->show(overlay, buf);
}
static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t size)
{
struct omap_overlay *overlay;
struct overlay_attribute *overlay_attr;
overlay = container_of(kobj, struct omap_overlay, kobj);
overlay_attr = container_of(attr, struct overlay_attribute, attr);
if (!overlay_attr->store)
return -ENOENT;
return overlay_attr->store(overlay, buf, size);
}
static const struct sysfs_ops overlay_sysfs_ops = {
.show = overlay_attr_show,
.store = overlay_attr_store,
};
static struct kobj_type overlay_ktype = {
.sysfs_ops = &overlay_sysfs_ops,
.default_attrs = overlay_sysfs_attrs,
};
int dss_overlay_kobj_init(struct omap_overlay *ovl,
struct platform_device *pdev)
{
return kobject_init_and_add(&ovl->kobj, &overlay_ktype,
&pdev->dev.kobj, "overlay%d", ovl->id);
}
void dss_overlay_kobj_uninit(struct omap_overlay *ovl)
{
kobject_del(&ovl->kobj);
kobject_put(&ovl->kobj);
}
/*
* linux/drivers/video/omap2/dss/overlay.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 "OVERLAY"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <video/omapdss.h>
#include "dss.h"
#include "dss_features.h"
static int num_overlays;
static struct omap_overlay *overlays;
int omap_dss_get_num_overlays(void)
{
return num_overlays;
}
EXPORT_SYMBOL(omap_dss_get_num_overlays);
struct omap_overlay *omap_dss_get_overlay(int num)
{
if (num >= num_overlays)
return NULL;
return &overlays[num];
}
EXPORT_SYMBOL(omap_dss_get_overlay);
void dss_init_overlays(struct platform_device *pdev)
{
int i, r;
num_overlays = dss_feat_get_num_ovls();
overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays,
GFP_KERNEL);
BUG_ON(overlays == NULL);
for (i = 0; i < num_overlays; ++i) {
struct omap_overlay *ovl = &overlays[i];
switch (i) {
case 0:
ovl->name = "gfx";
ovl->id = OMAP_DSS_GFX;
break;
case 1:
ovl->name = "vid1";
ovl->id = OMAP_DSS_VIDEO1;
break;
case 2:
ovl->name = "vid2";
ovl->id = OMAP_DSS_VIDEO2;
break;
case 3:
ovl->name = "vid3";
ovl->id = OMAP_DSS_VIDEO3;
break;
}
ovl->caps = dss_feat_get_overlay_caps(ovl->id);
ovl->supported_modes =
dss_feat_get_supported_color_modes(ovl->id);
r = dss_overlay_kobj_init(ovl, pdev);
if (r)
DSSERR("failed to create sysfs file\n");
}
}
void dss_uninit_overlays(struct platform_device *pdev)
{
int i;
for (i = 0; i < num_overlays; ++i) {
struct omap_overlay *ovl = &overlays[i];
dss_overlay_kobj_uninit(ovl);
}
kfree(overlays);
overlays = NULL;
num_overlays = 0;
}
int dss_ovl_simple_check(struct omap_overlay *ovl,
const struct omap_overlay_info *info)
{
if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
if (info->out_width != 0 && info->width != info->out_width) {
DSSERR("check_overlay: overlay %d doesn't support "
"scaling\n", ovl->id);
return -EINVAL;
}
if (info->out_height != 0 && info->height != info->out_height) {
DSSERR("check_overlay: overlay %d doesn't support "
"scaling\n", ovl->id);
return -EINVAL;
}
}
if ((ovl->supported_modes & info->color_mode) == 0) {
DSSERR("check_overlay: overlay %d doesn't support mode %d\n",
ovl->id, info->color_mode);
return -EINVAL;
}
if (info->zorder >= omap_dss_get_num_overlays()) {
DSSERR("check_overlay: zorder %d too high\n", info->zorder);
return -EINVAL;
}
if (dss_feat_rotation_type_supported(info->rotation_type) == 0) {
DSSERR("check_overlay: rotation type %d not supported\n",
info->rotation_type);
return -EINVAL;
}
return 0;
}
int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info,
const struct omap_video_timings *mgr_timings)
{
u16 outw, outh;
u16 dw, dh;
dw = mgr_timings->x_res;
dh = mgr_timings->y_res;
if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {
outw = info->width;
outh = info->height;
} else {
if (info->out_width == 0)
outw = info->width;
else
outw = info->out_width;
if (info->out_height == 0)
outh = info->height;
else
outh = info->out_height;
}
if (dw < info->pos_x + outw) {
DSSERR("overlay %d horizontally not inside the display area "
"(%d + %d >= %d)\n",
ovl->id, info->pos_x, outw, dw);
return -EINVAL;
}
if (dh < info->pos_y + outh) {
DSSERR("overlay %d vertically not inside the display area "
"(%d + %d >= %d)\n",
ovl->id, info->pos_y, outh, dh);
return -EINVAL;
}
return 0;
}
/*
* Checks if replication logic should be used. Only use when overlay is in
* RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp
*/
bool dss_ovl_use_replication(struct dss_lcd_mgr_config config,
enum omap_color_mode mode)
{
if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
return false;
return config.video_port_width > 16;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册