mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
OMAP: DSS2: OMAPFB: Implement auto-update mode
Implement auto-update mode for manual-update displays. omapfb driver uses a delayed work to update the display with a constant rate. The update mode can be changed via OMAPFB_SET_UPDATE_MODE ioctl, which previously called omapdss but is now handled inside omapfb, and a new sysfs file, "update_mode". The update interval is by default 20 times per second, but can be changed via "auto_update_freq" module parameter. There is also a new module parameter "auto_update", which will make omapfb start manual update displays in auto-update mode. This auto-update mode can be used for testing if the userspace does not support manual update displays properly. However, it is a very inefficient solution, and should be considered more as a hack for testing than something that could be used as a long term solution. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
This commit is contained in:
parent
065a40bd46
commit
27cc213ea7
4 changed files with 214 additions and 68 deletions
|
@ -316,68 +316,68 @@ int omapfb_update_window(struct fb_info *fbi,
|
|||
}
|
||||
EXPORT_SYMBOL(omapfb_update_window);
|
||||
|
||||
static int omapfb_set_update_mode(struct fb_info *fbi,
|
||||
int omapfb_set_update_mode(struct fb_info *fbi,
|
||||
enum omapfb_update_mode mode)
|
||||
{
|
||||
struct omap_dss_device *display = fb2display(fbi);
|
||||
enum omap_dss_update_mode um;
|
||||
struct omapfb_info *ofbi = FB2OFB(fbi);
|
||||
struct omapfb2_device *fbdev = ofbi->fbdev;
|
||||
struct omapfb_display_data *d;
|
||||
int r;
|
||||
|
||||
if (!display || !display->driver->set_update_mode)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mode) {
|
||||
case OMAPFB_UPDATE_DISABLED:
|
||||
um = OMAP_DSS_UPDATE_DISABLED;
|
||||
break;
|
||||
|
||||
case OMAPFB_AUTO_UPDATE:
|
||||
um = OMAP_DSS_UPDATE_AUTO;
|
||||
break;
|
||||
|
||||
case OMAPFB_MANUAL_UPDATE:
|
||||
um = OMAP_DSS_UPDATE_MANUAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = display->driver->set_update_mode(display, um);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int omapfb_get_update_mode(struct fb_info *fbi,
|
||||
enum omapfb_update_mode *mode)
|
||||
{
|
||||
struct omap_dss_device *display = fb2display(fbi);
|
||||
enum omap_dss_update_mode m;
|
||||
|
||||
if (!display)
|
||||
return -EINVAL;
|
||||
|
||||
if (!display->driver->get_update_mode) {
|
||||
*mode = OMAPFB_AUTO_UPDATE;
|
||||
if (mode != OMAPFB_AUTO_UPDATE && mode != OMAPFB_MANUAL_UPDATE)
|
||||
return -EINVAL;
|
||||
|
||||
omapfb_lock(fbdev);
|
||||
|
||||
d = get_display_data(fbdev, display);
|
||||
|
||||
if (d->update_mode == mode) {
|
||||
omapfb_unlock(fbdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
m = display->driver->get_update_mode(display);
|
||||
r = 0;
|
||||
|
||||
switch (m) {
|
||||
case OMAP_DSS_UPDATE_DISABLED:
|
||||
*mode = OMAPFB_UPDATE_DISABLED;
|
||||
break;
|
||||
case OMAP_DSS_UPDATE_AUTO:
|
||||
*mode = OMAPFB_AUTO_UPDATE;
|
||||
break;
|
||||
case OMAP_DSS_UPDATE_MANUAL:
|
||||
*mode = OMAPFB_MANUAL_UPDATE;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
|
||||
if (mode == OMAPFB_AUTO_UPDATE)
|
||||
omapfb_start_auto_update(fbdev, display);
|
||||
else /* MANUAL_UPDATE */
|
||||
omapfb_stop_auto_update(fbdev, display);
|
||||
|
||||
d->update_mode = mode;
|
||||
} else { /* AUTO_UPDATE */
|
||||
if (mode == OMAPFB_MANUAL_UPDATE)
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
omapfb_unlock(fbdev);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int omapfb_get_update_mode(struct fb_info *fbi,
|
||||
enum omapfb_update_mode *mode)
|
||||
{
|
||||
struct omap_dss_device *display = fb2display(fbi);
|
||||
struct omapfb_info *ofbi = FB2OFB(fbi);
|
||||
struct omapfb2_device *fbdev = ofbi->fbdev;
|
||||
struct omapfb_display_data *d;
|
||||
|
||||
if (!display)
|
||||
return -EINVAL;
|
||||
|
||||
omapfb_lock(fbdev);
|
||||
|
||||
d = get_display_data(fbdev, display);
|
||||
|
||||
*mode = d->update_mode;
|
||||
|
||||
omapfb_unlock(fbdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ static char *def_vram;
|
|||
static int def_vrfb;
|
||||
static int def_rotate;
|
||||
static int def_mirror;
|
||||
static bool auto_update;
|
||||
static unsigned int auto_update_freq;
|
||||
module_param(auto_update, bool, 0);
|
||||
module_param(auto_update_freq, uint, 0644);
|
||||
|
||||
#ifdef DEBUG
|
||||
unsigned int omapfb_debug;
|
||||
|
@ -1242,6 +1246,7 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
|
|||
struct omapfb_info *ofbi = FB2OFB(fbi);
|
||||
struct omapfb2_device *fbdev = ofbi->fbdev;
|
||||
struct omap_dss_device *display = fb2display(fbi);
|
||||
struct omapfb_display_data *d;
|
||||
int r = 0;
|
||||
|
||||
if (!display)
|
||||
|
@ -1249,6 +1254,8 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
|
|||
|
||||
omapfb_lock(fbdev);
|
||||
|
||||
d = get_display_data(fbdev, display);
|
||||
|
||||
switch (blank) {
|
||||
case FB_BLANK_UNBLANK:
|
||||
if (display->state != OMAP_DSS_DISPLAY_SUSPENDED)
|
||||
|
@ -1257,6 +1264,11 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
|
|||
if (display->driver->resume)
|
||||
r = display->driver->resume(display);
|
||||
|
||||
if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) &&
|
||||
d->update_mode == OMAPFB_AUTO_UPDATE &&
|
||||
!d->auto_update_work_enabled)
|
||||
omapfb_start_auto_update(fbdev, display);
|
||||
|
||||
break;
|
||||
|
||||
case FB_BLANK_NORMAL:
|
||||
|
@ -1268,6 +1280,9 @@ static int omapfb_blank(int blank, struct fb_info *fbi)
|
|||
if (display->state != OMAP_DSS_DISPLAY_ACTIVE)
|
||||
goto exit;
|
||||
|
||||
if (d->auto_update_work_enabled)
|
||||
omapfb_stop_auto_update(fbdev, display);
|
||||
|
||||
if (display->driver->suspend)
|
||||
r = display->driver->suspend(display);
|
||||
|
||||
|
@ -1724,6 +1739,78 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
|
|||
return r;
|
||||
}
|
||||
|
||||
static void omapfb_auto_update_work(struct work_struct *work)
|
||||
{
|
||||
struct omap_dss_device *dssdev;
|
||||
struct omap_dss_driver *dssdrv;
|
||||
struct omapfb_display_data *d;
|
||||
u16 w, h;
|
||||
unsigned int freq;
|
||||
struct omapfb2_device *fbdev;
|
||||
|
||||
d = container_of(work, struct omapfb_display_data,
|
||||
auto_update_work.work);
|
||||
|
||||
dssdev = d->dssdev;
|
||||
dssdrv = dssdev->driver;
|
||||
fbdev = d->fbdev;
|
||||
|
||||
if (!dssdrv || !dssdrv->update)
|
||||
return;
|
||||
|
||||
if (dssdrv->sync)
|
||||
dssdrv->sync(dssdev);
|
||||
|
||||
dssdrv->get_resolution(dssdev, &w, &h);
|
||||
dssdrv->update(dssdev, 0, 0, w, h);
|
||||
|
||||
freq = auto_update_freq;
|
||||
if (freq == 0)
|
||||
freq = 20;
|
||||
queue_delayed_work(fbdev->auto_update_wq,
|
||||
&d->auto_update_work, HZ / freq);
|
||||
}
|
||||
|
||||
void omapfb_start_auto_update(struct omapfb2_device *fbdev,
|
||||
struct omap_dss_device *display)
|
||||
{
|
||||
struct omapfb_display_data *d;
|
||||
|
||||
if (fbdev->auto_update_wq == NULL) {
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
wq = create_singlethread_workqueue("omapfb_auto_update");
|
||||
|
||||
if (wq == NULL) {
|
||||
dev_err(fbdev->dev, "Failed to create workqueue for "
|
||||
"auto-update\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fbdev->auto_update_wq = wq;
|
||||
}
|
||||
|
||||
d = get_display_data(fbdev, display);
|
||||
|
||||
INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work);
|
||||
|
||||
d->auto_update_work_enabled = true;
|
||||
|
||||
omapfb_auto_update_work(&d->auto_update_work.work);
|
||||
}
|
||||
|
||||
void omapfb_stop_auto_update(struct omapfb2_device *fbdev,
|
||||
struct omap_dss_device *display)
|
||||
{
|
||||
struct omapfb_display_data *d;
|
||||
|
||||
d = get_display_data(fbdev, display);
|
||||
|
||||
cancel_delayed_work_sync(&d->auto_update_work);
|
||||
|
||||
d->auto_update_work_enabled = false;
|
||||
}
|
||||
|
||||
/* initialize fb_info, var, fix to something sane based on the display */
|
||||
static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
|
||||
{
|
||||
|
@ -1859,12 +1946,22 @@ static void omapfb_free_resources(struct omapfb2_device *fbdev)
|
|||
|
||||
for (i = 0; i < fbdev->num_displays; i++) {
|
||||
struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;
|
||||
|
||||
if (fbdev->displays[i].auto_update_work_enabled)
|
||||
omapfb_stop_auto_update(fbdev, dssdev);
|
||||
|
||||
if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
|
||||
dssdev->driver->disable(dssdev);
|
||||
|
||||
omap_dss_put_device(dssdev);
|
||||
}
|
||||
|
||||
if (fbdev->auto_update_wq != NULL) {
|
||||
flush_workqueue(fbdev->auto_update_wq);
|
||||
destroy_workqueue(fbdev->auto_update_wq);
|
||||
fbdev->auto_update_wq = NULL;
|
||||
}
|
||||
|
||||
dev_set_drvdata(fbdev->dev, NULL);
|
||||
kfree(fbdev);
|
||||
}
|
||||
|
@ -2183,6 +2280,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
|
|||
struct omap_dss_device *dssdev)
|
||||
{
|
||||
struct omap_dss_driver *dssdrv = dssdev->driver;
|
||||
struct omapfb_display_data *d;
|
||||
int r;
|
||||
|
||||
r = dssdrv->enable(dssdev);
|
||||
|
@ -2192,8 +2290,20 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
|
|||
return r;
|
||||
}
|
||||
|
||||
d = get_display_data(fbdev, dssdev);
|
||||
|
||||
d->fbdev = fbdev;
|
||||
|
||||
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
|
||||
u16 w, h;
|
||||
|
||||
if (auto_update) {
|
||||
omapfb_start_auto_update(fbdev, dssdev);
|
||||
d->update_mode = OMAPFB_AUTO_UPDATE;
|
||||
} else {
|
||||
d->update_mode = OMAPFB_MANUAL_UPDATE;
|
||||
}
|
||||
|
||||
if (dssdrv->enable_te) {
|
||||
r = dssdrv->enable_te(dssdev, 1);
|
||||
if (r) {
|
||||
|
@ -2202,16 +2312,6 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
|
|||
}
|
||||
}
|
||||
|
||||
if (dssdrv->set_update_mode) {
|
||||
r = dssdrv->set_update_mode(dssdev,
|
||||
OMAP_DSS_UPDATE_MANUAL);
|
||||
if (r) {
|
||||
dev_err(fbdev->dev,
|
||||
"Failed to set update mode\n");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
dssdrv->get_resolution(dssdev, &w, &h);
|
||||
r = dssdrv->update(dssdev, 0, 0, w, h);
|
||||
if (r) {
|
||||
|
@ -2220,15 +2320,7 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
|
|||
return r;
|
||||
}
|
||||
} else {
|
||||
if (dssdrv->set_update_mode) {
|
||||
r = dssdrv->set_update_mode(dssdev,
|
||||
OMAP_DSS_UPDATE_AUTO);
|
||||
if (r) {
|
||||
dev_err(fbdev->dev,
|
||||
"Failed to set update mode\n");
|
||||
return r;
|
||||
}
|
||||
}
|
||||
d->update_mode = OMAPFB_AUTO_UPDATE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2276,6 +2368,8 @@ static int omapfb_probe(struct platform_device *pdev)
|
|||
fbdev->num_displays = 0;
|
||||
dssdev = NULL;
|
||||
for_each_dss_dev(dssdev) {
|
||||
struct omapfb_display_data *d;
|
||||
|
||||
omap_dss_get_device(dssdev);
|
||||
|
||||
if (!dssdev->driver) {
|
||||
|
@ -2283,7 +2377,12 @@ static int omapfb_probe(struct platform_device *pdev)
|
|||
r = -ENODEV;
|
||||
}
|
||||
|
||||
fbdev->displays[fbdev->num_displays++].dssdev = dssdev;
|
||||
d = &fbdev->displays[fbdev->num_displays++];
|
||||
d->dssdev = dssdev;
|
||||
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)
|
||||
d->update_mode = OMAPFB_MANUAL_UPDATE;
|
||||
else
|
||||
d->update_mode = OMAPFB_AUTO_UPDATE;
|
||||
}
|
||||
|
||||
if (r)
|
||||
|
|
|
@ -518,6 +518,39 @@ static ssize_t show_virt(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region->vaddr);
|
||||
}
|
||||
|
||||
static ssize_t show_upd_mode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct fb_info *fbi = dev_get_drvdata(dev);
|
||||
enum omapfb_update_mode mode;
|
||||
int r;
|
||||
|
||||
r = omapfb_get_update_mode(fbi, &mode);
|
||||
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", (unsigned)mode);
|
||||
}
|
||||
|
||||
static ssize_t store_upd_mode(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fb_info *fbi = dev_get_drvdata(dev);
|
||||
unsigned mode;
|
||||
int r;
|
||||
|
||||
r = kstrtouint(buf, 0, &mode);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = omapfb_set_update_mode(fbi, mode);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute omapfb_attrs[] = {
|
||||
__ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type,
|
||||
store_rotate_type),
|
||||
|
@ -528,6 +561,7 @@ static struct device_attribute omapfb_attrs[] = {
|
|||
store_overlays_rotate),
|
||||
__ATTR(phys_addr, S_IRUGO, show_phys, NULL),
|
||||
__ATTR(virt_addr, S_IRUGO, show_virt, NULL),
|
||||
__ATTR(update_mode, S_IRUGO | S_IWUSR, show_upd_mode, store_upd_mode),
|
||||
};
|
||||
|
||||
int omapfb_create_sysfs(struct omapfb2_device *fbdev)
|
||||
|
|
|
@ -74,8 +74,12 @@ struct omapfb_info {
|
|||
};
|
||||
|
||||
struct omapfb_display_data {
|
||||
struct omapfb2_device *fbdev;
|
||||
struct omap_dss_device *dssdev;
|
||||
u8 bpp_override;
|
||||
enum omapfb_update_mode update_mode;
|
||||
bool auto_update_work_enabled;
|
||||
struct delayed_work auto_update_work;
|
||||
};
|
||||
|
||||
struct omapfb2_device {
|
||||
|
@ -96,6 +100,8 @@ struct omapfb2_device {
|
|||
struct omap_overlay *overlays[10];
|
||||
unsigned num_managers;
|
||||
struct omap_overlay_manager *managers[10];
|
||||
|
||||
struct workqueue_struct *auto_update_wq;
|
||||
};
|
||||
|
||||
struct omapfb_colormode {
|
||||
|
@ -127,6 +133,13 @@ int dss_mode_to_fb_mode(enum omap_color_mode dssmode,
|
|||
int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,
|
||||
u16 posx, u16 posy, u16 outw, u16 outh);
|
||||
|
||||
void omapfb_start_auto_update(struct omapfb2_device *fbdev,
|
||||
struct omap_dss_device *display);
|
||||
void omapfb_stop_auto_update(struct omapfb2_device *fbdev,
|
||||
struct omap_dss_device *display);
|
||||
int omapfb_get_update_mode(struct fb_info *fbi, enum omapfb_update_mode *mode);
|
||||
int omapfb_set_update_mode(struct fb_info *fbi, enum omapfb_update_mode mode);
|
||||
|
||||
/* find the display connected to this fb, if any */
|
||||
static inline struct omap_dss_device *fb2display(struct fb_info *fbi)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue