Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Stephen Warren @ 2012-10-19 16:00 UTC (permalink / raw)
  To: Tony Prisk
  Cc: Alexandre Courbot, linux-fbdev, Mark Brown, Stephen Warren,
	linux-pm, Leela Krishna Amudala, linux-doc, linux-kernel,
	Rob Herring, Anton Vorontsov, linux-tegra, David Woodhouse,
	devicetree-discuss
In-Reply-To: <1350638436.3339.2.camel@gitbox>

On 10/19/2012 03:20 AM, Tony Prisk wrote:
> On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
>> Make use of the power sequences specified in the device tree or platform
>> data to control how the backlight is powered on and off.

Tony, please do cut down the amount of the patch that you quote. If you
don't, it's impossible to find your comments. Thanks.

^ permalink raw reply

* Re: [PATCH 4/5] xen-fbfront: handle backend CLOSED without CLOSING
From: Konrad Rzeszutek Wilk @ 2012-10-19 13:00 UTC (permalink / raw)
  To: David Vrabel, FlorianSchandinat; +Cc: xen-devel, linux-kernel, linux-fbdev
In-Reply-To: <1350554618-14582-4-git-send-email-david.vrabel@citrix.com>

On Thu, Oct 18, 2012 at 11:03:37AM +0100, David Vrabel wrote:
> From: David Vrabel <david.vrabel@citrix.com>
> 
> Backend drivers shouldn't transistion to CLOSED unless the frontend is
> CLOSED.  If a backend does transition to CLOSED too soon then the
> frontend may not see the CLOSING state and will not properly shutdown.
> 
> So, treat an unexpected backend CLOSED state the same as CLOSING.
> 
> Signed-off-by: David Vrabel <david.vrabel@citrix.com>
> Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
> Cc: linux-fbdev@vger.kernel.org
> Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>

Hey Florian,

Should I prep a git pull for you with this or would it be OK
if I just have your Ack to put this in my git pull for Linus?

Thanks!
> ---
>  drivers/video/xen-fbfront.c |    5 ++++-
>  1 files changed, 4 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
> index b7f5173..917bb56 100644
> --- a/drivers/video/xen-fbfront.c
> +++ b/drivers/video/xen-fbfront.c
> @@ -641,7 +641,6 @@ static void xenfb_backend_changed(struct xenbus_device *dev,
>  	case XenbusStateReconfiguring:
>  	case XenbusStateReconfigured:
>  	case XenbusStateUnknown:
> -	case XenbusStateClosed:
>  		break;
>  
>  	case XenbusStateInitWait:
> @@ -670,6 +669,10 @@ InitWait:
>  		info->feature_resize = val;
>  		break;
>  
> +	case XenbusStateClosed:
> +		if (dev->state = XenbusStateClosed)
> +			break;
> +		/* Missed the backend's CLOSING state -- fallthrough */
>  	case XenbusStateClosing:
>  		xenbus_frontend_closed(dev);
>  		break;
> -- 
> 1.7.2.5

^ permalink raw reply

* Re: [Qemu-devel] [PATCH] add bochs dispi interface framebuffer driver
From: Vasilis Liaskovitis @ 2012-10-19 10:35 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: linux-fbdev, qemu-devel
In-Reply-To: <1331201626-23402-1-git-send-email-kraxel@redhat.com>

Hi,

On Thu, Mar 08, 2012 at 11:13:46AM +0100, Gerd Hoffmann wrote:
> This patchs adds a frame buffer driver for (virtual/emulated) vga cards
> implementing the bochs dispi interface.  Supported hardware are the
> bochs vga card with vbe extension and the qemu standard vga.
> 
> The driver uses a fixed depth of 32bpp.  Otherwise it supports the full
> (but small) feature set of the bochs dispi interface:  Resolution
> switching and display panning.  It is tweaked to maximize fbcon speed,
> so you'll get the comfort of the framebuffer console in kvm guests
> without performance penalty.

I am testing this driver with qemu-kvm-1.2 or qemu-kvm master (commit)
and "-std vga". The driver works fine in general.

When I test a guest that runs X (ubuntu-12.04 desktop amd64), sometimes parts of
the screen and keyboard input is mixed between the X terminal and fbconsole
terminals. This happens only on the initial X11 login (right after boot or
reboot) and only sometimes.

During this time, there is a second keyboard cursor at top of the screen on the
X11 login. When switching to an fbconsole (ctrl+alt+f1), screen output of the X11
login screen gets mixed with fbconsole screen. And vice-versa when I go back to the
X11 terminal(I can send you 2 screendumps if needed, I haven't attached them here
due to size)
If I try to login (pressing enter), the X11-login is redrawn and from then on vt
switching works with no problems (I have to retype login, I am not sure where the
original keyboard input goes to)

Xorg driver used is fbdev (i can send xorg log), not sure if another driver
should be used/implemented for the bochsfb.

According to "xrandr -q" same resolution as bochsfb is used:
Screen 0: minimum 1024 x 768, current 1024 x 768, maximum 1024 x 768
    1024x768    116.0*

"fbset -i" output is as expected:

mode "1024x768-116"
    # D: 100.000 MHz, H: 93.985 kHz, V: 116.318 Hz
    geometry 1024 768 1024 4096 32
    timings 10000 16 16 16 16 8 8
    rgba 8/16,8/8,8/0,8/24
endmode

Frame buffer device information:
    Name        : bochsfb
    Address     : 0xfd000000
    Size        : 16777216
    Type        : PACKED PIXELS
    Visual      : TRUECOLOR
    XPanStep    : 1
    YPanStep    : 1
    YWrapStep   : 0
    LineLength  : 4096
    Accelerator : No

Some framebuffer-relevant guest kernel options used:

CONFIG_FB_BOOT_VESA_SUPPORT=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
CONFIG_FB_DEFERRED_IO=y
#
# Frame buffer hardware drivers
#
CONFIG_FB_BOCHS=m
CONFIG_FB_VESA=y
# CONFIG_FB_EFI is not set

Should FB_VESA be turned to "not set" for this test? (it's not tristate in Kconfig)

Btw (slightly off-topic) are other framebuffer drivers suitable for the
standard qemu vga-pci device? Would vesafb or uvesafb work? 

I haven't been able to load uvesafb in a guest, because the userspace helper
program v86d segfaults (maybe it tries to access vga ioports that are not
implemented in qemu?)

> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> ---
>  drivers/video/Kconfig   |   18 +++
>  drivers/video/Makefile  |    1 +
>  drivers/video/bochsfb.c |  385 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 404 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/video/bochsfb.c
> 
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 6ca0c40..4d21f90 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -286,6 +286,24 @@ config FB_CIRRUS
>  	  Say N unless you have such a graphics board or plan to get one
>  	  before you next recompile the kernel.
>  
> +config FB_BOCHS
> +	tristate "Bochs dispi interface support"
> +	depends on FB && PCI
> +	select FB_CFB_FILLRECT
> +	select FB_CFB_COPYAREA
> +	select FB_CFB_IMAGEBLIT
> +	---help---
> +	  This is the frame buffer driver for (virtual/emulated) vga
> +          cards implementing the bochs dispi interface.  Supported
> +          hardware are the bochs vga card with vbe extension and the
> +          qemu standard vga.
> +
> +          The driver handles the PCI variants only.  It uses a fixed
> +          depth of 32bpp, anything else doesn't make sense these days.
> +
> +          Say Y here if you plan to run the kernel in a virtual machine
> +          emulated by bochs or qemu.
> +
>  config FB_PM2
>  	tristate "Permedia2 support"
>  	depends on FB && ((AMIGA && BROKEN) || PCI)
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 1426068..a065ad3 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -99,6 +99,7 @@ obj-$(CONFIG_FB_ARMCLCD)	  += amba-clcd.o
>  obj-$(CONFIG_FB_68328)            += 68328fb.o
>  obj-$(CONFIG_FB_GBE)              += gbefb.o
>  obj-$(CONFIG_FB_CIRRUS)		  += cirrusfb.o
> +obj-$(CONFIG_FB_BOCHS)		  += bochsfb.o
>  obj-$(CONFIG_FB_ASILIANT)	  += asiliantfb.o
>  obj-$(CONFIG_FB_PXA)		  += pxafb.o
>  obj-$(CONFIG_FB_PXA168)		  += pxa168fb.o
> diff --git a/drivers/video/bochsfb.c b/drivers/video/bochsfb.c
> new file mode 100644
> index 0000000..18a94dc
> --- /dev/null
> +++ b/drivers/video/bochsfb.c
> @@ -0,0 +1,385 @@
> +/*
> + *  This file is subject to the terms and conditions of the GNU General Public
> + *  License. See the file COPYING in the main directory of this archive for
> + *  more details.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/mm.h>
> +#include <linux/vmalloc.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/fb.h>
> +#include <linux/pm.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/console.h>
> +#include <asm/io.h>
> +
> +#define VBE_DISPI_IOPORT_INDEX           0x01CE
> +#define VBE_DISPI_IOPORT_DATA            0x01CF
> +
> +#define VBE_DISPI_INDEX_ID               0x0
> +#define VBE_DISPI_INDEX_XRES             0x1
> +#define VBE_DISPI_INDEX_YRES             0x2
> +#define VBE_DISPI_INDEX_BPP              0x3
> +#define VBE_DISPI_INDEX_ENABLE           0x4
> +#define VBE_DISPI_INDEX_BANK             0x5
> +#define VBE_DISPI_INDEX_VIRT_WIDTH       0x6
> +#define VBE_DISPI_INDEX_VIRT_HEIGHT      0x7
> +#define VBE_DISPI_INDEX_X_OFFSET         0x8
> +#define VBE_DISPI_INDEX_Y_OFFSET         0x9
> +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
> +
> +#define VBE_DISPI_ID0                    0xB0C0
> +#define VBE_DISPI_ID1                    0xB0C1
> +#define VBE_DISPI_ID2                    0xB0C2
> +#define VBE_DISPI_ID3                    0xB0C3
> +#define VBE_DISPI_ID4                    0xB0C4
> +#define VBE_DISPI_ID5                    0xB0C5
> +
> +#define VBE_DISPI_DISABLED               0x00
> +#define VBE_DISPI_ENABLED                0x01
> +#define VBE_DISPI_GETCAPS                0x02
> +#define VBE_DISPI_8BIT_DAC               0x20
> +#define VBE_DISPI_LFB_ENABLED            0x40
> +#define VBE_DISPI_NOCLEARMEM             0x80
> +
> +enum bochs_types {
> +	BOCHS_QEMU_STDVGA,
> +	BOCHS_UNKNOWN,
> +};
> +
> +static const char *bochs_names[] = {
> +	[ BOCHS_QEMU_STDVGA ] = "QEMU standard vga",
> +	[ BOCHS_UNKNOWN ]     = "unknown",
> +};
> +
> +static struct fb_fix_screeninfo bochsfb_fix __devinitdata = {
> +	.id          = "bochsfb",
> +	.type        = FB_TYPE_PACKED_PIXELS,
> +	.visual      = FB_VISUAL_TRUECOLOR,
> +	.accel       = FB_ACCEL_NONE,
> +	.xpanstep    = 1,
> +	.ypanstep    = 1,
> +};
> +
> +static struct fb_var_screeninfo bochsfb_var __devinitdata = {
> +	.xres           = 1024,
> +	.yres           = 768,
> +	.bits_per_pixel = 32,
> +#ifdef __BIG_ENDIAN
> +	.transp         = { .length = 8, .offset =  0 },
> +	.red            = { .length = 8, .offset =  8 },
> +	.green          = { .length = 8, .offset = 16 },
> +	.blue           = { .length = 8, .offset = 24 },
> +#else
> +	.transp         = { .length = 8, .offset = 24 },
> +	.red            = { .length = 8, .offset = 16 },
> +	.green          = { .length = 8, .offset =  8 },
> +	.blue           = { .length = 8, .offset =  0 },
> +#endif
> +	.height         = -1,
> +	.width          = -1,
> +	.vmode          = FB_VMODE_NONINTERLACED,
> +	.pixclock       = 10000,
> +	.left_margin    = 16,
> +	.right_margin   = 16,
> +	.upper_margin   = 16,
> +	.lower_margin   = 16,
> +	.hsync_len      = 8,
> +	.vsync_len      = 8,
> +};
> +
> +static char *mode __devinitdata;
> +module_param(mode, charp, 0);
> +MODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480'");
> +
> +static u16 bochs_read(u16 reg)
> +{
> +	outw(reg, VBE_DISPI_IOPORT_INDEX);
> +	return inw(VBE_DISPI_IOPORT_DATA);
> +}
> +
> +static void bochs_write(u16 reg, u16 val)
> +{
> +	outw(reg, VBE_DISPI_IOPORT_INDEX);
> +	outw(val, VBE_DISPI_IOPORT_DATA);
> +}
> +
> +static int bochsfb_check_var(struct fb_var_screeninfo *var,
> +			     struct fb_info *info)
> +{
> +	uint32_t x,y, xv,yv, pixels;
> +
> +	if (var->bits_per_pixel != 32 ||
> +	    var->xres > 65535 ||
> +	    var->xres_virtual > 65535 ||
> +	    (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
> +		return -EINVAL;
> +
> +	x  = var->xres & ~0x0f;
> +	y  = var->yres & ~0x03;
> +	xv = var->xres_virtual & ~0x0f;
> +	yv = var->yres_virtual & ~0x03;
> +	if (xv < x)
> +		xv = x;
> +	pixels = info->fix.smem_len * 8 / info->var.bits_per_pixel;
> +	yv = pixels / xv;
> +	if (y > yv)
> +		return -EINVAL;
> +
> +	var->xres = x;
> +	var->yres = y;
> +	var->xres_virtual = xv;
> +	var->yres_virtual = yv;
> +	var->xoffset = 0;
> +	var->yoffset = 0;
> +
> +	return 0;
> +}
> +
> +static int bochsfb_set_par(struct fb_info *info)
> +{
> +	dev_dbg(info->dev, "set mode: real: %dx%d, virtual: %dx%d\n",
> +		info->var.xres, info->var.yres,
> +		info->var.xres_virtual, info->var.yres_virtual);
> +
> +	info->fix.line_length = info->var.xres * info->var.bits_per_pixel / 8;
> +
> +	bochs_write(VBE_DISPI_INDEX_BPP,         info->var.bits_per_pixel);
> +	bochs_write(VBE_DISPI_INDEX_XRES,        info->var.xres);
> +	bochs_write(VBE_DISPI_INDEX_YRES,        info->var.yres);
> +	bochs_write(VBE_DISPI_INDEX_BANK,        0);
> +	bochs_write(VBE_DISPI_INDEX_VIRT_WIDTH,  info->var.xres_virtual);
> +	bochs_write(VBE_DISPI_INDEX_VIRT_HEIGHT, info->var.yres_virtual);
> +	bochs_write(VBE_DISPI_INDEX_X_OFFSET,    info->var.xoffset);
> +	bochs_write(VBE_DISPI_INDEX_Y_OFFSET,    info->var.yoffset);
> +
> +	bochs_write(VBE_DISPI_INDEX_ENABLE,
> +		    VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
> +	return 0;
> +}
> +
> +static int bochsfb_setcolreg(unsigned regno, unsigned red, unsigned green,
> +			     unsigned blue, unsigned transp,
> +			     struct fb_info *info)
> +{
> +	if (regno < 16 && info->var.bits_per_pixel = 32) {
> +		red   >>= 8;
> +		green >>= 8;
> +		blue  >>= 8;
> +		((u32 *)(info->pseudo_palette))[regno] > +			(red   << info->var.red.offset)   |
> +			(green << info->var.green.offset) |
> +			(blue  << info->var.blue.offset);
> +	}
> +	return 0;
> +}
> +
> +static int bochsfb_pan_display(struct fb_var_screeninfo *var,
> +			       struct fb_info *info)
> +{
> +	bochs_write(VBE_DISPI_INDEX_X_OFFSET, var->xoffset);
> +	bochs_write(VBE_DISPI_INDEX_Y_OFFSET, var->yoffset);
> +	return 0;
> +}
> +
> +static struct fb_ops bochsfb_ops = {
> +	.owner	        = THIS_MODULE,
> +	.fb_check_var   = bochsfb_check_var,
> +	.fb_set_par     = bochsfb_set_par,
> +	.fb_setcolreg   = bochsfb_setcolreg,
> +	.fb_pan_display = bochsfb_pan_display,
> +	.fb_fillrect    = cfb_fillrect,
> +	.fb_copyarea    = cfb_copyarea,
> +	.fb_imageblit   = cfb_imageblit,
> +};
> +
> +static int __devinit
> +bochsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent)
> +{
> +	struct fb_info *p;
> +	unsigned long addr, size, mem;
> +	u16 id;
> +	int rc = -ENODEV;
> +
> +	id = bochs_read(VBE_DISPI_INDEX_ID);;
> +	mem = bochs_read(VBE_DISPI_INDEX_VIDEO_MEMORY_64K) * 64 * 1024;
> +	dev_info(&dp->dev,"Found bochs VGA, ID 0x%x, mem %ldk, type \"%s\".\n",
> +		 id, mem / 1024, bochs_names[ent->driver_data]);
> +	if ((id & 0xfff0) != VBE_DISPI_ID0) {
> +		dev_err(&dp->dev, "ID mismatch\n");
> +		goto err_out;
> +	}
> +
> +	if (pci_enable_device(dp) < 0) {
> +		dev_err(&dp->dev, "Cannot enable PCI device\n");
> +		goto err_out;
> +	}
> +
> +	if ((dp->resource[0].flags & IORESOURCE_MEM) = 0)
> +		goto err_disable;
> +	addr = pci_resource_start(dp, 0);
> +	size = pci_resource_len(dp, 0);
> +	if (addr = 0)
> +		goto err_disable;
> +	if (size != mem) {
> +		dev_err(&dp->dev, "Size mismatch: pci=%ld, bochs=%ld\n", size, mem);
> +		size = min(size, mem);
> +	}
> +
> +	p = framebuffer_alloc(0, &dp->dev);
> +	if (p = NULL) {
> +		dev_err(&dp->dev, "Cannot allocate framebuffer structure\n");
> +		rc = -ENOMEM;
> +		goto err_disable;
> +	}
> +
> +	if (pci_request_region(dp, 0, "bochsfb") != 0) {
> +		dev_err(&dp->dev, "Cannot request framebuffer\n");
> +		rc = -EBUSY;
> +		goto err_release_fb;
> +	}
> +
> +	if (!request_region(VBE_DISPI_IOPORT_INDEX, 2, "bochsfb")) {
> +		dev_err(&dp->dev, "Cannot request ioports\n");
> +		rc = -EBUSY;
> +		goto err_release_pci;
> +	}
> +
> +	p->screen_base = ioremap(addr, size);
> +	if (p->screen_base = NULL) {
> +		dev_err(&dp->dev, "Cannot map framebuffer\n");
> +		rc = -ENOMEM;
> +		goto err_release_ports;
> +	}
> +	memset(p->screen_base, 0, size);
> +
> +	pci_set_drvdata(dp, p);
> +	p->fbops = &bochsfb_ops;
> +	p->flags = FBINFO_FLAG_DEFAULT
> +		| FBINFO_READS_FAST
> +		| FBINFO_HWACCEL_XPAN
> +		| FBINFO_HWACCEL_YPAN;
> +	p->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
> +	p->fix = bochsfb_fix;
> +	p->fix.smem_start = addr;
> +	p->fix.smem_len = size;
> +
> +	p->var = bochsfb_var;
> +	bochsfb_check_var(&p->var, p);
> +	if (mode) {
> +		fb_find_mode(&p->var, p, mode, NULL, 0, NULL, 32);
> +	}
> +
> +	if (register_framebuffer(p) < 0) {
> +		dev_err(&dp->dev,"Framebuffer failed to register\n");
> +		goto err_unmap;
> +	}
> +
> +	dev_info(&dp->dev,"fb%d: bochs VGA frame buffer initialized.\n",
> +		 p->node);
> +
> +	return 0;
> +
> + err_unmap:
> +	iounmap(p->screen_base);
> + err_release_ports:
> +	release_region(VBE_DISPI_IOPORT_INDEX, 2);
> + err_release_pci:
> +	pci_release_region(dp, 0);
> + err_release_fb:
> +	framebuffer_release(p);
> + err_disable:
> + err_out:
> +	return rc;
> +}
> +
> +static void __devexit bochsfb_remove(struct pci_dev *dp)
> +{
> +	struct fb_info *p = pci_get_drvdata(dp);
> +
> +	if (p->screen_base = NULL)
> +		return;
> +	unregister_framebuffer(p);
> +	iounmap(p->screen_base);
> +	p->screen_base = NULL;
> +	release_region(VBE_DISPI_IOPORT_INDEX, 2);
> +	pci_release_region(dp, 0);
> +	kfree(p->pseudo_palette);
> +	framebuffer_release(p);
> +}
> +
> +static struct pci_device_id bochsfb_pci_tbl[] = {
> +	{
> +		.vendor      = 0x1234,
> +		.device      = 0x1111,
> +		.subvendor   = 0x1af4,
> +		.subdevice   = 0x1100,
> +		.driver_data = BOCHS_QEMU_STDVGA,
> +	},
> +	{
> +		.vendor      = 0x1234,
> +		.device      = 0x1111,
> +		.subvendor   = PCI_ANY_ID,
> +		.subdevice   = PCI_ANY_ID,
> +		.driver_data = BOCHS_UNKNOWN,
> +	},
> +	{ /* end of list */ }
> +};
> +
> +MODULE_DEVICE_TABLE(pci, bochsfb_pci_tbl);
> +
> +static struct pci_driver bochsfb_driver = {
> +	.name =		"bochsfb",
> +	.id_table =	bochsfb_pci_tbl,
> +	.probe =	bochsfb_pci_init,
> +	.remove =	__devexit_p(bochsfb_remove),
> +};
> +
> +#ifndef MODULE
> +static int __init bochsfb_setup(char *options)
> +{
> +	char *this_opt;
> +
> +	if (!options || !*options)
> +		return 0;
> +
> +	while ((this_opt = strsep(&options, ",")) != NULL) {
> +		if (!*this_opt)
> +			continue;
> +		if (!strncmp(this_opt, "mode:", 5))
> +			mode = this_opt + 5;
> +		else
> +			mode = this_opt;
> +	}
> +	return 0;
> +}
> +#endif
> +
> +int __init bochs_init(void)
> +{
> +#ifndef MODULE
> +	char *option = NULL;
> +
> +	if (fb_get_options("bochsfb", &option))
> +		return -ENODEV;
> +	bochsfb_setup(option);
> +#endif
> +	return pci_register_driver(&bochsfb_driver);
> +}
> +
> +module_init(bochs_init);
> +
> +static void __exit bochsfb_exit(void)
> +{
> +	pci_unregister_driver(&bochsfb_driver);
> +}
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
> +MODULE_DESCRIPTION("bochs dispi interface framebuffer driver");
> -- 
> 1.7.1
> 
> 

^ permalink raw reply

* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Alex Courbot @ 2012-10-19  9:31 UTC (permalink / raw)
  To: Tony Prisk
  Cc: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann, linux-fbdev@vger.kernel.org,
	linux-pm@vger.kernel.org, Leela Krishna Amudala,
	devicetree-discuss@lists.ozlabs.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org
In-Reply-To: <1350638436.3339.2.camel@gitbox>

On Friday 19 October 2012 17:20:36 Tony Prisk wrote:
> On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
> > +static void pwm_backlight_on(struct backlight_device *bl)
> > +{
> > +     struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> > +     int ret;
> > +
> > +     if (pb->enabled)
> > +             return;
> > +
> > +     if (pb->power_on_seq) {
> > +             ret = power_seq_run(pb->power_on_seq);
> > +             if (ret < 0) {
> > +                     dev_err(&bl->dev, "cannot run power on
> > sequence\n");
> > +                     return;
> > +             }
> > +     } else {
> > +             /* legacy framework */
> > +             pwm_config(pb->pwm, 0, pb->period);
> > +             pwm_disable(pb->pwm);
> 
> Is this right? pwm_disable() in the backlight_on function?

Now everybody will notice that I never really tested the legacy interface. >_<

Thanks, this was totally wrong indeed.

Alex.


^ permalink raw reply

* Re: [PATCH v7 2/3] pwm_backlight: use power sequences
From: Tony Prisk @ 2012-10-19  9:20 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA, Mark Brown, Stephen Warren,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Leela Krishna Amudala,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Anton Vorontsov,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, David Woodhouse,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
In-Reply-To: <1350637589-7405-3-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

On Fri, 2012-10-19 at 18:06 +0900, Alexandre Courbot wrote:
> Make use of the power sequences specified in the device tree or platform
> data to control how the backlight is powered on and off.
> 
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
> ---
>  .../bindings/video/backlight/pwm-backlight.txt     |  72 ++++++++-
>  drivers/video/backlight/Kconfig                    |   1 +
>  drivers/video/backlight/pwm_bl.c                   | 161 ++++++++++++++++-----
>  include/linux/pwm_backlight.h                      |  18 ++-
>  4 files changed, 213 insertions(+), 39 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> index 1e4fc72..3ba25ea 100644
> --- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> +++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
> @@ -14,15 +14,83 @@ Required properties:
>  Optional properties:
>    - pwm-names: a list of names for the PWM devices specified in the
>                 "pwms" property (see PWM binding[0])
> +  - low-threshold-brightness: brightness threshold low level. Sets the lowest
> +    brightness value.
> +    On some panels the backlight misbehaves if the duty cycle percentage of the
> +    PWM wave is less than a certain level (say 20%).  In this example the user
> +    can set low-threshold-brightness to a value above 50 (ie, 20% of 255), thus
> +    preventing the PWM duty cycle from going too low.
> +    On setting low-threshold-brightness the range of brightness levels is
> +    calculated in the range low-threshold-brightness to the maximum value in
> +    brightness-levels, described above.
> +  - pwm-names: name for the PWM device specified in the "pwms" property (see PWM
> +    binding[0]). Necessary if power sequences are used
> +  - power-sequences: Power sequences (see Power sequences[1]) used to bring the
> +    backlight on and off. If this property is present, then two power
> +    sequences named "power-on" and "power-off" must be defined to control how
> +    the backlight is to be powered on and off. These sequences must reference
> +    the PWM specified in the pwms property by its name, and can also reference
> +    other resources supported by the power sequences mechanism
>  
>  [0]: Documentation/devicetree/bindings/pwm/pwm.txt
> +[1]: Documentation/devicetree/bindings/power/power_seq.txt
>  
>  Example:
>  
>  	backlight {
>  		compatible = "pwm-backlight";
> -		pwms = <&pwm 0 5000000>;
> -
>  		brightness-levels = <0 4 8 16 32 64 128 255>;
>  		default-brightness-level = <6>;
> +		low-threshold-brightness = <50>;
> +
> +		/* resources used by the power sequences */
> +		pwms = <&pwm 0 5000000>;
> +		pwm-names = "backlight";
> +		power-supply = <&backlight_reg>;
> +
> +		power-sequences {
> +			power-on {
> +				step0 {
> +					type = "regulator";
> +					id = "power";
> +					enable;
> +				};
> +				step1 {
> +					type = "delay";
> +					delay = <10000>;
> +				};
> +				step2 {
> +					type = "pwm";
> +					id = "backlight";
> +					enable;
> +				};
> +				step3 {
> +					type = "gpio";
> +					gpio = <&gpio 28 0>;
> +					value = <1>;
> +				};
> +			};
> +
> +			power-off {
> +				step0 {
> +					type = "gpio";
> +					gpio = <&gpio 28 0>;
> +					value = <0>;
> +				};
> +				step1 {
> +					type = "pwm";
> +					id = "backlight";
> +					disable;
> +				};
> +				step2 {
> +					type = "delay";
> +					delay = <10000>;
> +				};
> +				step3 {
> +					type = "regulator";
> +					id = "power";
> +					disable;
> +				};
> +			};
> +		};
>  	};
> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
> index c101697..c2e5f79 100644
> --- a/drivers/video/backlight/Kconfig
> +++ b/drivers/video/backlight/Kconfig
> @@ -239,6 +239,7 @@ config BACKLIGHT_CARILLO_RANCH
>  config BACKLIGHT_PWM
>  	tristate "Generic PWM based Backlight Driver"
>  	depends on PWM
> +	select POWER_SEQ
>  	help
>  	  If you have a LCD backlight adjustable by PWM, say Y to enable
>  	  this driver.
> diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
> index 069983c..e607c6e 100644
> --- a/drivers/video/backlight/pwm_bl.c
> +++ b/drivers/video/backlight/pwm_bl.c
> @@ -27,6 +27,12 @@ struct pwm_bl_data {
>  	unsigned int		period;
>  	unsigned int		lth_brightness;
>  	unsigned int		*levels;
> +	bool			enabled;
> +	struct power_seq_set	power_seqs;
> +	struct power_seq	*power_on_seq;
> +	struct power_seq	*power_off_seq;
> +
> +	/* Legacy callbacks */
>  	int			(*notify)(struct device *,
>  					  int brightness);
>  	void			(*notify_after)(struct device *,
> @@ -35,6 +41,52 @@ struct pwm_bl_data {
>  	void			(*exit)(struct device *);
>  };
>  
> +static void pwm_backlight_on(struct backlight_device *bl)
> +{
> +	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> +	int ret;
> +
> +	if (pb->enabled)
> +		return;
> +
> +	if (pb->power_on_seq) {
> +		ret = power_seq_run(pb->power_on_seq);
> +		if (ret < 0) {
> +			dev_err(&bl->dev, "cannot run power on sequence\n");
> +			return;
> +		}
> +	} else {
> +		/* legacy framework */
> +		pwm_config(pb->pwm, 0, pb->period);
> +		pwm_disable(pb->pwm);
Is this right? pwm_disable() in the backlight_on function?
> +	}
> +
> +	pb->enabled = true;
> +}
> +
> +static void pwm_backlight_off(struct backlight_device *bl)
> +{
> +	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> +	int ret;
> +
> +	if (!pb->enabled)
> +		return;
> +
> +	if (pb->power_off_seq) {
> +		ret = power_seq_run(pb->power_off_seq);
> +		if (ret < 0) {
> +			dev_err(&bl->dev, "cannot run power off sequence\n");
> +			return;
> +		}
> +	} else {
> +		/* legacy framework */
> +		pwm_enable(pb->pwm);
Same here.. owm_enable() in backlight_off()
> +		return;
> +	}
> +
> +	pb->enabled = false;
> +}
> +
>  static int pwm_backlight_update_status(struct backlight_device *bl)
>  {
>  	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
> @@ -51,8 +103,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>  		brightness = pb->notify(pb->dev, brightness);
>  
>  	if (brightness = 0) {
> -		pwm_config(pb->pwm, 0, pb->period);
> -		pwm_disable(pb->pwm);
> +		pwm_backlight_off(bl);
>  	} else {
>  		int duty_cycle;
>  
> @@ -66,7 +117,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
>  		duty_cycle = pb->lth_brightness +
>  		     (duty_cycle * (pb->period - pb->lth_brightness) / max);
>  		pwm_config(pb->pwm, duty_cycle, pb->period);
> -		pwm_enable(pb->pwm);
> +		pwm_backlight_on(bl);
>  	}
>  
>  	if (pb->notify_after)
> @@ -145,11 +196,10 @@ static int pwm_backlight_parse_dt(struct device *dev,
>  		data->max_brightness--;
>  	}
>  
> -	/*
> -	 * TODO: Most users of this driver use a number of GPIOs to control
> -	 *       backlight power. Support for specifying these needs to be
> -	 *       added.
> -	 */
> +	/* read power sequences */
> +	data->power_seqs = devm_of_parse_power_seq_set(dev);
> +	if (IS_ERR(data->power_seqs))
> +		return PTR_ERR(data->power_seqs);
>  
>  	return 0;
>  }
> @@ -172,6 +222,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
>  {
>  	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
>  	struct platform_pwm_backlight_data defdata;
> +	struct power_seq_resource *res;
>  	struct backlight_properties props;
>  	struct backlight_device *bl;
>  	struct pwm_bl_data *pb;
> @@ -180,7 +231,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
>  
>  	if (!data) {
>  		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
> -		if (ret < 0) {
> +		if (ret = -EPROBE_DEFER) {
> +			return ret;
> +		} else if (ret < 0) {
>  			dev_err(&pdev->dev, "failed to find platform data\n");
>  			return ret;
>  		}
> @@ -201,6 +254,68 @@ static int pwm_backlight_probe(struct platform_device *pdev)
>  		goto err_alloc;
>  	}
>  
> +	if (data->power_seqs) {
> +		/* use power sequences */
> +		struct power_seq_set *seqs = &pb->power_seqs;
> +
> +		power_seq_set_init(seqs, &pdev->dev);
> +		power_seq_set_add_sequences(seqs, data->power_seqs);
> +
> +		/* Check that the required sequences are here */
> +		pb->power_on_seq = power_seq_lookup(seqs, "power-on");
> +		if (!pb->power_on_seq) {
> +			dev_err(&pdev->dev, "missing power-on sequence\n");
> +			return -EINVAL;
> +		}
> +		pb->power_off_seq = power_seq_lookup(seqs, "power-off");
> +		if (!pb->power_off_seq) {
> +			dev_err(&pdev->dev, "missing power-off sequence\n");
> +			return -EINVAL;
> +		}
> +
> +		/* we must have exactly one PWM resource for this driver */
> +		power_seq_for_each_resource(res, seqs) {
> +			if (res->type != POWER_SEQ_PWM)
> +				continue;
> +			if (pb->pwm) {
> +				dev_err(&pdev->dev, "more than one PWM used\n");
> +				return -EINVAL;
> +			}
> +			/* keep the pwm at hand */
> +			pb->pwm = res->pwm.pwm;
> +		}
> +		/* from here we should have a PWM */
> +		if (!pb->pwm) {
> +			dev_err(&pdev->dev, "no PWM defined!\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		/* use legacy interface */
> +		pb->pwm = devm_pwm_get(&pdev->dev, NULL);
> +		if (IS_ERR(pb->pwm)) {
> +			dev_err(&pdev->dev,
> +				"unable to request PWM, trying legacy API\n");
> +
> +			pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
> +			if (IS_ERR(pb->pwm)) {
> +				dev_err(&pdev->dev,
> +					"unable to request legacy PWM\n");
> +				ret = PTR_ERR(pb->pwm);
> +				goto err_alloc;
> +			}
> +		}
> +
> +		dev_dbg(&pdev->dev, "got pwm for backlight\n");
> +
> +		/*
> +		* The DT case will set the pwm_period_ns field to 0 and store
> +		* the period, parsed from the DT, in the PWM device. For the
> +		* non-DT case, set the period from platform data.
> +		*/
> +		if (data->pwm_period_ns > 0)
> +			pwm_set_period(pb->pwm, data->pwm_period_ns);
> +	}
> +
>  	if (data->levels) {
>  		max = data->levels[data->max_brightness];
>  		pb->levels = data->levels;
> @@ -213,28 +328,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
>  	pb->exit = data->exit;
>  	pb->dev = &pdev->dev;
>  
> -	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
> -	if (IS_ERR(pb->pwm)) {
> -		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
> -
> -		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
> -		if (IS_ERR(pb->pwm)) {
> -			dev_err(&pdev->dev, "unable to request legacy PWM\n");
> -			ret = PTR_ERR(pb->pwm);
> -			goto err_alloc;
> -		}
> -	}
> -
> -	dev_dbg(&pdev->dev, "got pwm for backlight\n");
> -
> -	/*
> -	 * The DT case will set the pwm_period_ns field to 0 and store the
> -	 * period, parsed from the DT, in the PWM device. For the non-DT case,
> -	 * set the period from platform data.
> -	 */
> -	if (data->pwm_period_ns > 0)
> -		pwm_set_period(pb->pwm, data->pwm_period_ns);
> -
>  	pb->period = pwm_get_period(pb->pwm);
>  	pb->lth_brightness = data->lth_brightness * (pb->period / max);
>  
> @@ -267,8 +360,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
>  	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
>  
>  	backlight_device_unregister(bl);
> -	pwm_config(pb->pwm, 0, pb->period);
> -	pwm_disable(pb->pwm);
> +	pwm_backlight_off(bl);
>  	if (pb->exit)
>  		pb->exit(&pdev->dev);
>  	return 0;
> @@ -282,8 +374,7 @@ static int pwm_backlight_suspend(struct device *dev)
>  
>  	if (pb->notify)
>  		pb->notify(pb->dev, 0);
> -	pwm_config(pb->pwm, 0, pb->period);
> -	pwm_disable(pb->pwm);
> +	pwm_backlight_off(bl);
>  	if (pb->notify_after)
>  		pb->notify_after(pb->dev, 0);
>  	return 0;
> diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
> index 56f4a86..0dcec1d 100644
> --- a/include/linux/pwm_backlight.h
> +++ b/include/linux/pwm_backlight.h
> @@ -5,14 +5,28 @@
>  #define __LINUX_PWM_BACKLIGHT_H
>  
>  #include <linux/backlight.h>
> +#include <linux/power_seq.h>
>  
>  struct platform_pwm_backlight_data {
> -	int pwm_id;
>  	unsigned int max_brightness;
>  	unsigned int dft_brightness;
>  	unsigned int lth_brightness;
> -	unsigned int pwm_period_ns;
>  	unsigned int *levels;
> +	/*
> +	 * New interface using power sequences. Must include exactly
> +	 * two power sequences named 'power-on' and 'power-off'. If NULL,
> +	 * the legacy interface is used.
> +	 */
> +	struct platform_power_seq_set *power_seqs;
> +
> +	/*
> +	 * Legacy interface - use power sequences instead!
> +	 *
> +	 * pwm_id and pwm_period_ns need only be specified
> +	 * if get_pwm(dev, NULL) would return NULL.
> +	 */
> +	int pwm_id;
> +	unsigned int pwm_period_ns;
>  	int (*init)(struct device *dev);
>  	int (*notify)(struct device *dev, int brightness);
>  	void (*notify_after)(struct device *dev, int brightness);

Regards
Tony P


^ permalink raw reply

* [PATCH v7 3/3] tegra: ventana: add PWM backlight to device tree
From: Alexandre Courbot @ 2012-10-19  9:06 UTC (permalink / raw)
  To: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Leela Krishna Amudala,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1350637589-7405-1-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 arch/arm/boot/dts/tegra20-ventana.dts | 59 ++++++++++++++++++++++++++++++++++-
 1 file changed, 58 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts
index bec8bb2..877a1bb 100644
--- a/arch/arm/boot/dts/tegra20-ventana.dts
+++ b/arch/arm/boot/dts/tegra20-ventana.dts
@@ -454,6 +454,63 @@
 		bus-width = <8>;
 	};
 
+	backlight {
+		compatible = "pwm-backlight";
+		brightness-levels = <0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 255>;
+		default-brightness-level = <12>;
+
+		/* resources used by the power sequences */
+		pwms = <&pwm 2 5000000>;
+		pwm-names = "backlight";
+		power-supply = <&vdd_bl_reg>;
+
+		power-sequences {
+			power-on {
+				step0 {
+					type = "regulator";
+					id = "power";
+					enable;
+				};
+				step1 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step2 {
+					type = "pwm";
+					id = "backlight";
+					enable;
+				};
+				step3 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <1>;
+				};
+			};
+
+			power-off {
+				step0 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <0>;
+				};
+				step1 {
+					type = "pwm";
+					id = "backlight";
+					disable;
+				};
+				step2 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step3 {
+					type = "regulator";
+					id = "power";
+					disable;
+				};
+			};
+		};
+	};
+
 	regulators {
 		compatible = "simple-bus";
 		#address-cells = <1>;
@@ -497,7 +554,7 @@
 			enable-active-high;
 		};
 
-		regulator@4 {
+		vdd_bl_reg: regulator@4 {
 			compatible = "regulator-fixed";
 			reg = <4>;
 			regulator-name = "vdd_bl";
-- 
1.7.12.3


^ permalink raw reply related

* [PATCH v7 2/3] pwm_backlight: use power sequences
From: Alexandre Courbot @ 2012-10-19  9:06 UTC (permalink / raw)
  To: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann
  Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA, Leela Krishna Amudala,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1350637589-7405-1-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Make use of the power sequences specified in the device tree or platform
data to control how the backlight is powered on and off.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 .../bindings/video/backlight/pwm-backlight.txt     |  72 ++++++++-
 drivers/video/backlight/Kconfig                    |   1 +
 drivers/video/backlight/pwm_bl.c                   | 161 ++++++++++++++++-----
 include/linux/pwm_backlight.h                      |  18 ++-
 4 files changed, 213 insertions(+), 39 deletions(-)

diff --git a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
index 1e4fc72..3ba25ea 100644
--- a/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
+++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt
@@ -14,15 +14,83 @@ Required properties:
 Optional properties:
   - pwm-names: a list of names for the PWM devices specified in the
                "pwms" property (see PWM binding[0])
+  - low-threshold-brightness: brightness threshold low level. Sets the lowest
+    brightness value.
+    On some panels the backlight misbehaves if the duty cycle percentage of the
+    PWM wave is less than a certain level (say 20%).  In this example the user
+    can set low-threshold-brightness to a value above 50 (ie, 20% of 255), thus
+    preventing the PWM duty cycle from going too low.
+    On setting low-threshold-brightness the range of brightness levels is
+    calculated in the range low-threshold-brightness to the maximum value in
+    brightness-levels, described above.
+  - pwm-names: name for the PWM device specified in the "pwms" property (see PWM
+    binding[0]). Necessary if power sequences are used
+  - power-sequences: Power sequences (see Power sequences[1]) used to bring the
+    backlight on and off. If this property is present, then two power
+    sequences named "power-on" and "power-off" must be defined to control how
+    the backlight is to be powered on and off. These sequences must reference
+    the PWM specified in the pwms property by its name, and can also reference
+    other resources supported by the power sequences mechanism
 
 [0]: Documentation/devicetree/bindings/pwm/pwm.txt
+[1]: Documentation/devicetree/bindings/power/power_seq.txt
 
 Example:
 
 	backlight {
 		compatible = "pwm-backlight";
-		pwms = <&pwm 0 5000000>;
-
 		brightness-levels = <0 4 8 16 32 64 128 255>;
 		default-brightness-level = <6>;
+		low-threshold-brightness = <50>;
+
+		/* resources used by the power sequences */
+		pwms = <&pwm 0 5000000>;
+		pwm-names = "backlight";
+		power-supply = <&backlight_reg>;
+
+		power-sequences {
+			power-on {
+				step0 {
+					type = "regulator";
+					id = "power";
+					enable;
+				};
+				step1 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step2 {
+					type = "pwm";
+					id = "backlight";
+					enable;
+				};
+				step3 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <1>;
+				};
+			};
+
+			power-off {
+				step0 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <0>;
+				};
+				step1 {
+					type = "pwm";
+					id = "backlight";
+					disable;
+				};
+				step2 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step3 {
+					type = "regulator";
+					id = "power";
+					disable;
+				};
+			};
+		};
 	};
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index c101697..c2e5f79 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -239,6 +239,7 @@ config BACKLIGHT_CARILLO_RANCH
 config BACKLIGHT_PWM
 	tristate "Generic PWM based Backlight Driver"
 	depends on PWM
+	select POWER_SEQ
 	help
 	  If you have a LCD backlight adjustable by PWM, say Y to enable
 	  this driver.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 069983c..e607c6e 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -27,6 +27,12 @@ struct pwm_bl_data {
 	unsigned int		period;
 	unsigned int		lth_brightness;
 	unsigned int		*levels;
+	bool			enabled;
+	struct power_seq_set	power_seqs;
+	struct power_seq	*power_on_seq;
+	struct power_seq	*power_off_seq;
+
+	/* Legacy callbacks */
 	int			(*notify)(struct device *,
 					  int brightness);
 	void			(*notify_after)(struct device *,
@@ -35,6 +41,52 @@ struct pwm_bl_data {
 	void			(*exit)(struct device *);
 };
 
+static void pwm_backlight_on(struct backlight_device *bl)
+{
+	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
+	int ret;
+
+	if (pb->enabled)
+		return;
+
+	if (pb->power_on_seq) {
+		ret = power_seq_run(pb->power_on_seq);
+		if (ret < 0) {
+			dev_err(&bl->dev, "cannot run power on sequence\n");
+			return;
+		}
+	} else {
+		/* legacy framework */
+		pwm_config(pb->pwm, 0, pb->period);
+		pwm_disable(pb->pwm);
+	}
+
+	pb->enabled = true;
+}
+
+static void pwm_backlight_off(struct backlight_device *bl)
+{
+	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
+	int ret;
+
+	if (!pb->enabled)
+		return;
+
+	if (pb->power_off_seq) {
+		ret = power_seq_run(pb->power_off_seq);
+		if (ret < 0) {
+			dev_err(&bl->dev, "cannot run power off sequence\n");
+			return;
+		}
+	} else {
+		/* legacy framework */
+		pwm_enable(pb->pwm);
+		return;
+	}
+
+	pb->enabled = false;
+}
+
 static int pwm_backlight_update_status(struct backlight_device *bl)
 {
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
@@ -51,8 +103,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 		brightness = pb->notify(pb->dev, brightness);
 
 	if (brightness = 0) {
-		pwm_config(pb->pwm, 0, pb->period);
-		pwm_disable(pb->pwm);
+		pwm_backlight_off(bl);
 	} else {
 		int duty_cycle;
 
@@ -66,7 +117,7 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 		duty_cycle = pb->lth_brightness +
 		     (duty_cycle * (pb->period - pb->lth_brightness) / max);
 		pwm_config(pb->pwm, duty_cycle, pb->period);
-		pwm_enable(pb->pwm);
+		pwm_backlight_on(bl);
 	}
 
 	if (pb->notify_after)
@@ -145,11 +196,10 @@ static int pwm_backlight_parse_dt(struct device *dev,
 		data->max_brightness--;
 	}
 
-	/*
-	 * TODO: Most users of this driver use a number of GPIOs to control
-	 *       backlight power. Support for specifying these needs to be
-	 *       added.
-	 */
+	/* read power sequences */
+	data->power_seqs = devm_of_parse_power_seq_set(dev);
+	if (IS_ERR(data->power_seqs))
+		return PTR_ERR(data->power_seqs);
 
 	return 0;
 }
@@ -172,6 +222,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 {
 	struct platform_pwm_backlight_data *data = pdev->dev.platform_data;
 	struct platform_pwm_backlight_data defdata;
+	struct power_seq_resource *res;
 	struct backlight_properties props;
 	struct backlight_device *bl;
 	struct pwm_bl_data *pb;
@@ -180,7 +231,9 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 
 	if (!data) {
 		ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
-		if (ret < 0) {
+		if (ret = -EPROBE_DEFER) {
+			return ret;
+		} else if (ret < 0) {
 			dev_err(&pdev->dev, "failed to find platform data\n");
 			return ret;
 		}
@@ -201,6 +254,68 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 		goto err_alloc;
 	}
 
+	if (data->power_seqs) {
+		/* use power sequences */
+		struct power_seq_set *seqs = &pb->power_seqs;
+
+		power_seq_set_init(seqs, &pdev->dev);
+		power_seq_set_add_sequences(seqs, data->power_seqs);
+
+		/* Check that the required sequences are here */
+		pb->power_on_seq = power_seq_lookup(seqs, "power-on");
+		if (!pb->power_on_seq) {
+			dev_err(&pdev->dev, "missing power-on sequence\n");
+			return -EINVAL;
+		}
+		pb->power_off_seq = power_seq_lookup(seqs, "power-off");
+		if (!pb->power_off_seq) {
+			dev_err(&pdev->dev, "missing power-off sequence\n");
+			return -EINVAL;
+		}
+
+		/* we must have exactly one PWM resource for this driver */
+		power_seq_for_each_resource(res, seqs) {
+			if (res->type != POWER_SEQ_PWM)
+				continue;
+			if (pb->pwm) {
+				dev_err(&pdev->dev, "more than one PWM used\n");
+				return -EINVAL;
+			}
+			/* keep the pwm at hand */
+			pb->pwm = res->pwm.pwm;
+		}
+		/* from here we should have a PWM */
+		if (!pb->pwm) {
+			dev_err(&pdev->dev, "no PWM defined!\n");
+			return -EINVAL;
+		}
+	} else {
+		/* use legacy interface */
+		pb->pwm = devm_pwm_get(&pdev->dev, NULL);
+		if (IS_ERR(pb->pwm)) {
+			dev_err(&pdev->dev,
+				"unable to request PWM, trying legacy API\n");
+
+			pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
+			if (IS_ERR(pb->pwm)) {
+				dev_err(&pdev->dev,
+					"unable to request legacy PWM\n");
+				ret = PTR_ERR(pb->pwm);
+				goto err_alloc;
+			}
+		}
+
+		dev_dbg(&pdev->dev, "got pwm for backlight\n");
+
+		/*
+		* The DT case will set the pwm_period_ns field to 0 and store
+		* the period, parsed from the DT, in the PWM device. For the
+		* non-DT case, set the period from platform data.
+		*/
+		if (data->pwm_period_ns > 0)
+			pwm_set_period(pb->pwm, data->pwm_period_ns);
+	}
+
 	if (data->levels) {
 		max = data->levels[data->max_brightness];
 		pb->levels = data->levels;
@@ -213,28 +328,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	pb->exit = data->exit;
 	pb->dev = &pdev->dev;
 
-	pb->pwm = devm_pwm_get(&pdev->dev, NULL);
-	if (IS_ERR(pb->pwm)) {
-		dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
-
-		pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
-		if (IS_ERR(pb->pwm)) {
-			dev_err(&pdev->dev, "unable to request legacy PWM\n");
-			ret = PTR_ERR(pb->pwm);
-			goto err_alloc;
-		}
-	}
-
-	dev_dbg(&pdev->dev, "got pwm for backlight\n");
-
-	/*
-	 * The DT case will set the pwm_period_ns field to 0 and store the
-	 * period, parsed from the DT, in the PWM device. For the non-DT case,
-	 * set the period from platform data.
-	 */
-	if (data->pwm_period_ns > 0)
-		pwm_set_period(pb->pwm, data->pwm_period_ns);
-
 	pb->period = pwm_get_period(pb->pwm);
 	pb->lth_brightness = data->lth_brightness * (pb->period / max);
 
@@ -267,8 +360,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
 	backlight_device_unregister(bl);
-	pwm_config(pb->pwm, 0, pb->period);
-	pwm_disable(pb->pwm);
+	pwm_backlight_off(bl);
 	if (pb->exit)
 		pb->exit(&pdev->dev);
 	return 0;
@@ -282,8 +374,7 @@ static int pwm_backlight_suspend(struct device *dev)
 
 	if (pb->notify)
 		pb->notify(pb->dev, 0);
-	pwm_config(pb->pwm, 0, pb->period);
-	pwm_disable(pb->pwm);
+	pwm_backlight_off(bl);
 	if (pb->notify_after)
 		pb->notify_after(pb->dev, 0);
 	return 0;
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 56f4a86..0dcec1d 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -5,14 +5,28 @@
 #define __LINUX_PWM_BACKLIGHT_H
 
 #include <linux/backlight.h>
+#include <linux/power_seq.h>
 
 struct platform_pwm_backlight_data {
-	int pwm_id;
 	unsigned int max_brightness;
 	unsigned int dft_brightness;
 	unsigned int lth_brightness;
-	unsigned int pwm_period_ns;
 	unsigned int *levels;
+	/*
+	 * New interface using power sequences. Must include exactly
+	 * two power sequences named 'power-on' and 'power-off'. If NULL,
+	 * the legacy interface is used.
+	 */
+	struct platform_power_seq_set *power_seqs;
+
+	/*
+	 * Legacy interface - use power sequences instead!
+	 *
+	 * pwm_id and pwm_period_ns need only be specified
+	 * if get_pwm(dev, NULL) would return NULL.
+	 */
+	int pwm_id;
+	unsigned int pwm_period_ns;
 	int (*init)(struct device *dev);
 	int (*notify)(struct device *dev, int brightness);
 	void (*notify_after)(struct device *dev, int brightness);
-- 
1.7.12.3


^ permalink raw reply related

* [PATCH v7 1/3] Runtime Interpreted Power Sequences
From: Alexandre Courbot @ 2012-10-19  9:06 UTC (permalink / raw)
  To: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann
  Cc: Leela Krishna Amudala, linux-tegra, linux-kernel, linux-fbdev,
	devicetree-discuss, linux-pm, linux-doc, Alexandre Courbot
In-Reply-To: <1350637589-7405-1-git-send-email-acourbot@nvidia.com>

Some device drivers (e.g. panel backlights ) need to follow precise
sequences for powering on and off, involving GPIOs, regulators, PWMs
with a precise powering order and delays to respect between each steps.
These sequences are device-specific, and do not belong to a particular
driver - therefore they have been performed by board-specific hook
functions to far.

With the advent of the device tree and of ARM kernels that are not
board-tied, we cannot rely on these board-specific hooks anymore but
need a way to implement these sequences in a portable manner. This patch
introduces a simple interpreter that can execute such power sequences
encoded either as platform data or within the device tree.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
 .../devicetree/bindings/power/power_seq.txt        | 121 +++++++
 Documentation/power/power_seq.txt                  | 253 ++++++++++++++
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/power_seq/Kconfig                    |   2 +
 drivers/power/power_seq/Makefile                   |   1 +
 drivers/power/power_seq/power_seq.c                | 376 +++++++++++++++++++++
 drivers/power/power_seq/power_seq_delay.c          |  65 ++++
 drivers/power/power_seq/power_seq_gpio.c           |  94 ++++++
 drivers/power/power_seq/power_seq_pwm.c            |  82 +++++
 drivers/power/power_seq/power_seq_regulator.c      |  83 +++++
 include/linux/power_seq.h                          | 203 +++++++++++
 12 files changed, 1282 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/power_seq.txt
 create mode 100644 Documentation/power/power_seq.txt
 create mode 100644 drivers/power/power_seq/Kconfig
 create mode 100644 drivers/power/power_seq/Makefile
 create mode 100644 drivers/power/power_seq/power_seq.c
 create mode 100644 drivers/power/power_seq/power_seq_delay.c
 create mode 100644 drivers/power/power_seq/power_seq_gpio.c
 create mode 100644 drivers/power/power_seq/power_seq_pwm.c
 create mode 100644 drivers/power/power_seq/power_seq_regulator.c
 create mode 100644 include/linux/power_seq.h

diff --git a/Documentation/devicetree/bindings/power/power_seq.txt b/Documentation/devicetree/bindings/power/power_seq.txt
new file mode 100644
index 0000000..7880a6c
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/power_seq.txt
@@ -0,0 +1,121 @@
+Runtime Interpreted Power Sequences
+=================+
+Power sequences are sequential descriptions of actions to be performed on
+power-related resources. Having these descriptions in a well-defined data format
+allows us to take much of the board- or device- specific power control code out
+of the kernel and place it into the device tree instead, making kernels less
+board-dependant.
+
+A device typically makes use of multiple power sequences, for different purposes
+such as powering on and off. All the power sequences of a given device are
+grouped into a set. In the device tree, this set is a sub-node of the device
+node named "power-sequences".
+
+Power Sequences Structure
+-------------------------
+Every device that makes use of power sequences must have a "power-sequences"
+node into which individual power sequences are declared as sub-nodes. The name
+of the node becomes the name of the sequence within the power sequences
+framework.
+
+Similarly, each power sequence declares its steps as sub-nodes of itself. Steps
+must be named sequentially, with the first step named step0, the second step1,
+etc. Failure to follow this rule will result in a parsing error.
+
+Power Sequences Steps
+---------------------
+Steps of a sequence describe an action to be performed on a resource. They
+always include a "type" property which indicates what kind of resource this
+step works on. Depending on the resource type, additional properties are defined
+to control the action to be performed.
+
+"delay" type required properties:
+  - delay: delay to wait (in microseconds)
+
+"regulator" type required properties:
+  - id: name of the regulator to use.
+  - enable / disable: one of these two empty properties must be present to
+                      enable or disable the resource
+
+"pwm" type required properties:
+  - id: name of the PWM to use.
+  - enable / disable: one of these two empty properties must be present to
+                      enable or disable the resource
+
+"gpio" type required properties:
+  - gpio: phandle of the GPIO to use.
+  - value: value this GPIO should take. Must be 0 or 1.
+
+Example
+-------
+Here are example sequences declared within a backlight device that use all the
+supported resources types:
+
+	backlight {
+		compatible = "pwm-backlight";
+		...
+
+		/* resources used by the power sequences */
+		pwms = <&pwm 2 5000000>;
+		pwm-names = "backlight";
+		power-supply = <&backlight_reg>;
+
+		power-sequences {
+			power-on {
+				step0 {
+					type = "regulator";
+					id = "power";
+					enable;
+				};
+				step1 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step2 {
+					type = "pwm";
+					id = "backlight";
+					enable;
+				};
+				step3 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <1>;
+				};
+			};
+
+			power-off {
+				step0 {
+					type = "gpio";
+					gpio = <&gpio 28 0>;
+					value = <0>;
+				};
+				step1 {
+					type = "pwm";
+					id = "backlight";
+					disable;
+				};
+				step2 {
+					type = "delay";
+					delay = <10000>;
+				};
+				step3 {
+					type = "regulator";
+					id = "power";
+					disable;
+				};
+			};
+		};
+	};
+
+The first part lists the PWM and regulator resources used by the sequences.
+These resources will be requested on behalf of the backlight device when the
+sequences are built and are declared according to their own bindings (for
+instance, regulators and pwms are resolved by name - note though that name
+declaration is done differently by the two frameworks).
+
+After the resources declaration, two sequences follow for powering the backlight
+on and off. Their names are specified by the pwm-backlight device bindings. Once
+the sequences are built by calling devm_of_parse_power_seq_set() on the
+backlight device, they can be added to a set using
+power_seq_set_add_sequences().
diff --git a/Documentation/power/power_seq.txt b/Documentation/power/power_seq.txt
new file mode 100644
index 0000000..8be0570
--- /dev/null
+++ b/Documentation/power/power_seq.txt
@@ -0,0 +1,253 @@
+Runtime Interpreted Power Sequences
+=================+
+Problem
+-------
+Very commonly, boards need the help of out-of-driver code to turn some of their
+devices on and off. For instance, SoC boards might use a GPIO (abstracted to a
+regulator or not) to control the power supply of a backlight. The GPIO that
+should be used, however, as well as the exact power sequence that may also
+involve other resources, is board-dependent and thus unknown to the driver.
+
+This was previously addressed by having hooks in the device's platform data that
+are called whenever the state of the device might need a power status change.
+This approach, however, introduces board-dependant code into the kernel and is
+not compatible with the device tree.
+
+The Runtime Interpreted Power Sequences (or power sequences for short) aim at
+turning this code into platform data or device tree nodes. Power sequences are
+described using a simple format and run by a lightweight interpreter whenever
+needed. This allows device drivers to work without power callbacks and makes the
+kernel less board-dependant.
+
+What are Power Sequences?
+-------------------------
+A power sequence is an array of sequential steps describing an action to be
+performed on a resource. The supported resources and actions operations are:
+- delay (just wait for a given number of microseconds)
+- GPIO (set to 0 or 1)
+- regulator (enable or disable)
+- PWM (enable or disable)
+
+When a power sequence is run, its steps is executed one after the other until
+one step fails or the end of the sequence is reached.
+
+Power sequences are named, and grouped into "sets" which contain all the
+sequences of a device as well as the resources they use.
+
+Power sequences can be declared as platform data or in the device tree.
+
+Platform Data Format
+--------------------
+All relevant data structures for declaring power sequences are located in
+include/linux/power_seq.h.
+
+The platform data for a device may include an instance of platform_power_seq_set
+which references all the power sequences used for a device. The power sequences
+reference resources in their steps, and setup the union member that corresponds
+to the resource's type. Resources, similarly, have a union which relevant member
+depends on their type.
+
+Note that the only "platform data" per se here is platform_power_seq_set. Other
+structures (power_seq and power_seq_resource) will be used at runtime and thus
+*must* survive initialization, so do not declare them with the __initdata
+attribute.
+
+The following example should make it clear how the platform data for power
+sequences is defined. It declares two power sequences named "power-on" and
+"power-off" for a backlight device. The "power-on" sequence enables the "power"
+regulator of the device, waits for 10ms, and then enables PWM "backlight" and
+set GPIO 28 to 1. "power-off" does the opposite.
+
+struct power_seq_resource reg_res = {
+	.type = POWER_SEQ_REGULATOR,
+	.regulator.id = "power",
+};
+
+struct power_seq_resource gpio_res = {
+	.type = POWER_SEQ_GPIO,
+	.gpio.gpio = 28,
+};
+
+struct power_seq_resource pwm_res = {
+	.type = POWER_SEQ_PWM,
+	.pwm.id = "backlight",
+};
+
+struct power_seq_resource delay_res = {
+	.type = POWER_SEQ_DELAY,
+};
+
+struct power_seq power_on_seq = {
+	.id = "power-on",
+	.num_steps = 4,
+	.steps = {
+		{
+			.resource = &reg_res,
+			.regulator.enable = true,
+		}, {
+			.resource = &delay_res,
+			.delay.delay = 10000,
+		}, {
+			.resource = &pwm_res,
+			.pwm.enable = true,
+		}, {
+			.resource = &gpio_res,
+			.gpio.value = 1,
+		},
+	},
+};
+
+struct power_seq power_off_seq = {
+	.id = "power-off",
+	.num_steps = 4,
+	.steps = {
+		{
+			.resource = &gpio_res,
+			.gpio.value = 0,
+		}, {
+			.resource = &pwm_res,
+			.pwm.enable = false,
+		}, {
+			.resource = &delay_res,
+			.delay.delay = 10000,
+		}, {
+			.resource = &reg_res,
+			.regulator.enable = false,
+		},
+	},
+};
+
+struct platform_power_seq_set backlight_power_seqs __initdata = {
+	.num_seqs = 2,
+	.seqs = {
+		&power_on_seq,
+		&power_off_seq,
+	},
+};
+
+"backlight_power_seqs" can then be passed to power_seq_set_add_sequences() in
+order to add the sequences to a set and allocate all the necessary resources.
+More on this later in this document.
+
+Device Tree
+-----------
+Power sequences can also be encoded as device tree nodes. The following
+properties and nodes are equivalent to the platform data defined previously:
+
+pwms = <&pwm 2 5000000>;
+pwm-names = "backlight";
+power-supply = <&vdd_bl_reg>;
+
+power-sequences {
+	power-on {
+		step0 {
+			type = "regulator";
+			id = "power";
+			enable;
+		};
+		step1 {
+			type = "delay";
+			delay = <10000>;
+		};
+		step2 {
+			type = "pwm";
+			id = "backlight";
+			enable;
+		};
+		step3 {
+			type = "gpio";
+			gpio = <&gpio 28 0>;
+			value = <1>;
+		};
+	};
+
+	power-off {
+		step0 {
+			type = "gpio";
+			gpio = <&gpio 28 0>;
+			value = <0>;
+		};
+		step1 {
+			type = "pwm";
+			id = "backlight";
+			disable;
+		};
+		step2 {
+			type = "delay";
+			delay = <10000>;
+		};
+		step3 {
+			type = "regulator";
+			id = "power";
+			disable;
+		};
+	};
+};
+
+See Documentation/devicetree/bindings/power/power_seq.txt for the complete
+syntax of the DT bindings.
+
+Use by Drivers and Resources Management
+---------------------------------------
+Power sequences make use of resources that must be properly allocated and
+managed. The power_seq_set structure manages the sequences and resources for a
+particular device. A driver willing to use power sequences will thus declare one
+instance of power_seq_set per device and initialize it at probe time:
+
+struct my_device_data {
+	struct device *dev;
+	...
+	struct power_set_set power_seqs;
+	...
+};
+
+power_seq_set_init(&my_device->power_seqs, my_device->dev);
+
+The power_seq_set_add_sequence() and power_seq_set_add_sequences() functions are
+then used to add one or several sequences to a set. These functions will also
+allocate all the resources used by the sequence(s) and make sure they are ready
+to be run. All resources are allocated through devm and will thus be freed when
+the set's device is removed.
+
+  int power_seq_set_add_sequence(struct power_seq_set *set,
+			         struct power_seq *seq);
+  int power_seq_set_add_sequences(struct power_seq_set *set,
+				  struct platform_power_seq_set *seqs);
+
+Power sequences added to a set can then be resolved by their name using
+power_seq_lookup():
+
+  struct power_seq *power_seq_lookup(struct power_seq_set *seqs,
+				     const char *id);
+
+power_seq_lookup() returns a ready-to-run pointer to the power sequence which
+name matches the id parameter.
+
+A retrieved power sequence can then be executed by power_seq_run:
+
+  int power_seq_run(struct power_seq *seq);
+
+It returns 0 if the sequence has successfully been run, or an error code if a
+problem occurred.
+
+Sometimes, you may want to browse the list of resources allocated for the
+sequences of a device, for instance to ensure that a resource of a given type is
+present. The power_seq_for_each_resource() macro does this:
+
+  power_seq_for_each_resource(pos, seqs)
+
+Here "pos" will be a pointer to a struct power_seq_resource. This structure
+contains the type of the resource, the information used for identifying it, and
+the resolved resource itself.
+
+Finally, users of the device tree can obtain a platform_power_seq_set structure
+built from the device's node using devm_of_parse_power_seq_set:
+
+  struct platform_power_seq_set *devm_of_parse_power_seq_set(struct device *dev);
+
+The power sequences must be declared under a "power-sequences" node directly
+declared under the device's node. Detailed syntax contained in Documentation/devicetree/bindings/power/power_seq.txt. As the function name
+states, all memory is allocated through devm. The returned
+platform_power_seq_set can be freed after being added to a set, but the
+sequences themselves must be preserved until they are freed by devm.
\ No newline at end of file
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 49a8939..f20d449 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -338,3 +338,4 @@ config AB8500_BATTERY_THERM_ON_BATCTRL
 endif # POWER_SUPPLY
 
 source "drivers/power/avs/Kconfig"
+source "drivers/power/power_seq/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b949cf8..883ad4d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_CHARGER_MAX8997)	+= max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)	+= max8998_charger.o
 obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_CHARGER_SMB347)	+= smb347-charger.o
+obj-$(CONFIG_POWER_SEQ)		+= power_seq/
diff --git a/drivers/power/power_seq/Kconfig b/drivers/power/power_seq/Kconfig
new file mode 100644
index 0000000..3bff26e
--- /dev/null
+++ b/drivers/power/power_seq/Kconfig
@@ -0,0 +1,2 @@
+config POWER_SEQ
+	bool
diff --git a/drivers/power/power_seq/Makefile b/drivers/power/power_seq/Makefile
new file mode 100644
index 0000000..f77a359
--- /dev/null
+++ b/drivers/power/power_seq/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_POWER_SEQ)		+= power_seq.o
diff --git a/drivers/power/power_seq/power_seq.c b/drivers/power/power_seq/power_seq.c
new file mode 100644
index 0000000..255b1a0
--- /dev/null
+++ b/drivers/power/power_seq/power_seq.c
@@ -0,0 +1,376 @@
+/*
+ * power_seq.c - power sequence interpreter for platform devices and device tree
+ *
+ * Author: Alexandre Courbot <acourbot@nvidia.com>
+ *
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/power_seq.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+#include <linux/of.h>
+
+#define power_seq_err(seq, step_nbr, format, ...)			\
+	dev_err(seq->set->dev, "%s[%d]: " format, seq->id, step_nbr,	\
+	##__VA_ARGS__);
+
+/**
+ * struct power_seq_res_ops - operators for power sequences resources
+ * @name:		Name of the resource type. Set to null when a resource
+ *			type support is not compiled in
+ * @of_parse:		Parse a step for this kind of resource from a device
+ *			tree node. The result of parsing must be written into
+ *			step step_nbr of seq
+ * @step_run:		Run a step for this kind of resource
+ * @res_compare:	Return true if the resource used by the resource is the
+ *			same as the one referenced by the step, false otherwise.
+ * @res_alloc:		Resolve and allocate a resource. Return error code if
+ *			the resource cannot be allocated, 0 otherwise
+ */
+struct power_seq_res_ops {
+	const char *name;
+	int (*of_parse)(struct device_node *node, struct power_seq *seq,
+			unsigned int step_nbr, struct power_seq_resource *res);
+	int (*step_run)(struct power_seq_step *step);
+	bool (*res_compare)(struct power_seq_resource *res,
+			    struct power_seq_resource *res2);
+	int (*res_alloc)(struct device *dev,
+			 struct power_seq_resource *res);
+};
+
+static const struct power_seq_res_ops power_seq_ops[POWER_SEQ_NUM_TYPES];
+
+#ifdef CONFIG_OF
+static int of_power_seq_parse_enable_properties(struct device_node *node,
+						struct power_seq *seq,
+						unsigned int step_nbr,
+						bool *enable)
+{
+	if (of_find_property(node, "enable", NULL)) {
+		*enable = true;
+	} else if (of_find_property(node, "disable", NULL)) {
+		*enable = false;
+	} else {
+		power_seq_err(seq, step_nbr,
+			      "missing enable or disable property\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int of_power_seq_parse_step(struct device *dev,
+				   struct device_node *node,
+				   struct power_seq *seq,
+				   unsigned int step_nbr,
+				   struct list_head *resources)
+{
+	struct power_seq_step *step = &seq->steps[step_nbr];
+	struct power_seq_resource res, *res2;
+	const char *type;
+	int i, err;
+
+	err = of_property_read_string(node, "type", &type);
+	if (err < 0) {
+		power_seq_err(seq, step_nbr, "cannot read type property\n");
+		return err;
+	}
+	for (i = 0; i < POWER_SEQ_NUM_TYPES; i++) {
+		if (power_seq_ops[i].name = NULL)
+			continue;
+		if (!strcmp(type, power_seq_ops[i].name))
+			break;
+	}
+	if (i >= POWER_SEQ_NUM_TYPES) {
+		power_seq_err(seq, step_nbr, "unknown type %s\n", type);
+		return -EINVAL;
+	}
+	memset(&res, 0, sizeof(res));
+	res.type = i;
+	err = power_seq_ops[res.type].of_parse(node, seq, step_nbr, &res);
+	if (err < 0)
+		return err;
+
+	/* Use the same instance of the resource if met before */
+	list_for_each_entry(res2, resources, list) {
+		if (res.type = res2->type &&
+		    power_seq_ops[res.type].res_compare(&res, res2))
+			break;
+	}
+	/* Resource never met before, create it */
+	if (&res2->list = resources) {
+		res2 = devm_kzalloc(dev, sizeof(*res2), GFP_KERNEL);
+		if (!res2)
+			return -ENOMEM;
+		memcpy(res2, &res, sizeof(res));
+		list_add_tail(&res2->list, resources);
+	}
+	step->resource = res2;
+
+	return 0;
+}
+
+static struct power_seq *of_parse_power_seq(struct device *dev,
+					    struct device_node *node,
+					    struct list_head *resources)
+{
+	struct device_node *child = NULL;
+	struct power_seq *pseq;
+	int num_steps, sz;
+	int err;
+
+	if (!node)
+		return ERR_PTR(-EINVAL);
+
+	num_steps = of_get_child_count(node);
+	sz = sizeof(*pseq) + sizeof(pseq->steps[0]) * num_steps;
+	pseq = devm_kzalloc(dev, sz, GFP_KERNEL);
+	if (!pseq)
+		return ERR_PTR(-ENOMEM);
+	pseq->id = node->name;
+	pseq->num_steps = num_steps;
+
+	for_each_child_of_node(node, child) {
+		unsigned int pos;
+
+		/* Check that the name's format is correct and within bounds */
+		if (strncmp("step", child->name, 4)) {
+			err = -EINVAL;
+			goto parse_error;
+		}
+
+		err = kstrtouint(child->name + 4, 10, &pos);
+		if (err < 0)
+			goto parse_error;
+
+		/* Invalid step index or step already parsed? */
+		if (pos >= num_steps || pseq->steps[pos].resource != NULL) {
+			err = -EINVAL;
+			goto parse_error;
+		}
+
+		err = of_power_seq_parse_step(dev, child, pseq, pos, resources);
+		if (err)
+			return ERR_PTR(err);
+	}
+
+	return pseq;
+
+parse_error:
+	dev_err(dev, "%s: invalid power step name %s!\n", pseq->id,
+		child->name);
+	return ERR_PTR(err);
+}
+
+/**
+ * devm_of_parse_power_seq_set - build a power_seq_set from the device tree
+ * @dev:	Device to parse the power sequences of
+ *
+ * Sequences must be contained into a subnode named "power-sequences" of the
+ * device root node.
+ *
+ * Memory for the sequence is allocated using devm_kzalloc on dev. The returned
+ * platform_power_seq_set can be freed by devm_kfree after the sequences have
+ * been added, but the sequences themselves must be preserved.
+ *
+ * Returns the built set on success, or an error code in case of failure.
+ */
+struct platform_power_seq_set *devm_of_parse_power_seq_set(struct device *dev)
+{
+	struct platform_power_seq_set *set;
+	struct device_node *root = dev->of_node;
+	struct device_node *seq;
+	struct list_head resources;
+	int n, sz;
+
+	if (!root)
+		return NULL;
+
+	root = of_find_node_by_name(root, "power-sequences");
+	if (!root)
+		return NULL;
+
+	n = of_get_child_count(root);
+	sz = sizeof(*set) + sizeof(struct power_seq *) * n;
+	set = devm_kzalloc(dev, sz, GFP_KERNEL);
+	if (!set)
+		return ERR_PTR(-ENOMEM);
+	set->num_seqs = n;
+
+	n = 0;
+	INIT_LIST_HEAD(&resources);
+	for_each_child_of_node(root, seq) {
+		struct power_seq *pseq;
+
+		pseq = of_parse_power_seq(dev, seq, &resources);
+		if (IS_ERR(pseq))
+			return (void *)pseq;
+
+		set->seqs[n++] = pseq;
+	}
+
+	return set;
+}
+EXPORT_SYMBOL_GPL(devm_of_parse_power_seq_set);
+#endif /* CONFIG_OF */
+
+/**
+ * power_seq_set_init - initialize a power_seq_set
+ * @set:	Set to initialize
+ * @dev:	Device this set is going to belong to
+ */
+void power_seq_set_init(struct power_seq_set *set, struct device *dev)
+{
+	set->dev = dev;
+	INIT_LIST_HEAD(&set->resources);
+	INIT_LIST_HEAD(&set->seqs);
+}
+EXPORT_SYMBOL_GPL(power_seq_set_init);
+
+/**
+ * power_seq_add_sequence - add a power sequence to a set
+ * @set:	Set to add the sequence to
+ * @seq:	Sequence to add
+ *
+ * This step will check that all the resources used by the sequence are
+ * allocated. If they are not, an attempt to allocate them is made. This
+ * operation can fail and and return an error code.
+ *
+ * Returns 0 on success, error code if a resource initialization failed.
+ */
+int power_seq_add_sequence(struct power_seq_set *set, struct power_seq *seq)
+{
+	struct power_seq_resource *res;
+	int i, err;
+
+	for (i = 0; i < seq->num_steps; i++) {
+		struct power_seq_step *step = &seq->steps[i];
+		struct power_seq_resource *step_res = step->resource;
+		list_for_each_entry(res, &set->resources, list) {
+			if (res = step_res)
+				break;
+		}
+		/* resource not allocated yet, allocate and add it */
+		if (&res->list = &set->resources) {
+			err = power_seq_ops[step_res->type].res_alloc(set->dev,
+								      step_res);
+			if (err)
+				return err;
+			list_add_tail(&step->resource->list, &set->resources);
+		}
+	}
+
+	list_add_tail(&seq->list, &set->seqs);
+	seq->set = set;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(power_seq_add_sequence);
+
+/**
+ * power_seq_add_sequences - add power sequences defined as platform data
+ * @set:	Set to add the sequences to
+ * @seqs:	Sequences to add
+ *
+ * See power_seq_add_sequence for more details.
+ *
+ * Returns 0 on success, error code if a resource initialization failed.
+ */
+int power_seq_set_add_sequences(struct power_seq_set *set,
+				struct platform_power_seq_set *seqs)
+{
+	int i, ret;
+
+	for (i = 0; i < seqs->num_seqs; i++) {
+		ret = power_seq_add_sequence(set, seqs->seqs[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(power_seq_set_add_sequences);
+
+/**
+ * power_seq_lookup - Lookup a power sequence by name from a set
+ * @seqs:	The set to look in
+ * @id:		Name to look after
+ *
+ * Returns a matching power sequence if it exists, NULL if it does not.
+ */
+struct power_seq *power_seq_lookup(struct power_seq_set *set, const char *id)
+{
+	struct power_seq *seq;
+
+	list_for_each_entry(seq, &set->seqs, list) {
+		if (!strcmp(seq->id, id))
+			return seq;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(power_seq_lookup);
+
+/**
+ * power_seq_run() - run a power sequence
+ * @seq:	The power sequence to run
+ *
+ * Returns 0 on success, error code in case of failure.
+ */
+int power_seq_run(struct power_seq *seq)
+{
+	unsigned int i;
+	int err;
+
+	if (!seq)
+		return 0;
+
+	if (!seq->set) {
+		pr_err("cannot run a sequence not added to a set");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < seq->num_steps; i++) {
+		unsigned int type = seq->steps[i].resource->type;
+
+		err = power_seq_ops[type].step_run(&seq->steps[i]);
+		if (err) {
+			power_seq_err(seq, i,
+				"error %d while running power sequence step\n",
+				err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(power_seq_run);
+
+#include "power_seq_delay.c"
+#include "power_seq_regulator.c"
+#include "power_seq_pwm.c"
+#include "power_seq_gpio.c"
+
+static const struct power_seq_res_ops power_seq_ops[POWER_SEQ_NUM_TYPES] = {
+	[POWER_SEQ_DELAY] = POWER_SEQ_DELAY_TYPE,
+	[POWER_SEQ_REGULATOR] = POWER_SEQ_REGULATOR_TYPE,
+	[POWER_SEQ_PWM] = POWER_SEQ_PWM_TYPE,
+	[POWER_SEQ_GPIO] = POWER_SEQ_GPIO_TYPE,
+};
+
+MODULE_AUTHOR("Alexandre Courbot <acourbot@nvidia.com>");
+MODULE_DESCRIPTION("Runtime Interpreted Power Sequences");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/power_seq/power_seq_delay.c b/drivers/power/power_seq/power_seq_delay.c
new file mode 100644
index 0000000..5bb0a46
--- /dev/null
+++ b/drivers/power/power_seq/power_seq_delay.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/delay.h>
+
+#ifdef CONFIG_OF
+static int of_power_seq_parse_delay(struct device_node *node,
+				    struct power_seq *seq,
+				    unsigned int step_nbr,
+				    struct power_seq_resource *res)
+{
+	struct power_seq_step *step = &seq->steps[step_nbr];
+	int err;
+
+	err = of_property_read_u32(node, "delay",
+				   &step->delay.delay);
+	if (err < 0)
+		power_seq_err(seq, step_nbr, "error reading delay property\n");
+
+	return err;
+}
+#else
+#define of_power_seq_parse_delay NULL
+#endif
+
+static bool power_seq_res_compare_delay(struct power_seq_resource *res,
+					struct power_seq_resource *res2)
+{
+	/* Delay resources are just here to hold the type of steps, so they are
+	 * all equivalent. */
+	return true;
+}
+
+static int power_seq_res_alloc_delay(struct device *dev,
+				     struct power_seq_resource *res)
+{
+	return 0;
+}
+
+static int power_seq_step_run_delay(struct power_seq_step *step)
+{
+	usleep_range(step->delay.delay,
+		     step->delay.delay + 1000);
+
+	return 0;
+}
+
+#define POWER_SEQ_DELAY_TYPE {				\
+	.name = "delay",				\
+	.of_parse = of_power_seq_parse_delay,		\
+	.step_run = power_seq_step_run_delay,		\
+	.res_compare = power_seq_res_compare_delay,	\
+	.res_alloc = power_seq_res_alloc_delay,		\
+}
diff --git a/drivers/power/power_seq/power_seq_gpio.c b/drivers/power/power_seq/power_seq_gpio.c
new file mode 100644
index 0000000..44b4a3d
--- /dev/null
+++ b/drivers/power/power_seq/power_seq_gpio.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#ifdef CONFIG_OF
+static int power_seq_of_parse_gpio(struct device_node *node,
+				   struct power_seq *seq,
+				   unsigned int step_nbr,
+				   struct power_seq_resource *res)
+{
+	struct power_seq_step *step = &seq->steps[step_nbr];
+	int gpio;
+	int err;
+
+	gpio = of_get_named_gpio(node, "gpio", 0);
+	if (gpio < 0) {
+		power_seq_err(seq, step_nbr, "error reading gpio property\n");
+		return gpio;
+	}
+	res->gpio.gpio = gpio;
+
+	err = of_property_read_u32(node, "value", &step->gpio.value);
+	if (err < 0) {
+		power_seq_err(seq, step_nbr, "error reading value property\n");
+	} else if (step->gpio.value < 0 || step->gpio.value > 1) {
+		power_seq_err(seq, step_nbr,
+			      "value out of range (must be 0 or 1)\n");
+		err = -EINVAL;
+	}
+
+	return err;
+}
+#else
+#define of_power_seq_parse_gpio NULL
+#endif
+
+static bool power_seq_res_compare_gpio(struct power_seq_resource *res,
+				       struct power_seq_resource *res2)
+{
+	return res->gpio.gpio = res2->gpio.gpio;
+}
+
+static int power_seq_res_alloc_gpio(struct device *dev,
+				    struct power_seq_resource *res)
+{
+	int err;
+
+	err = devm_gpio_request(dev, res->gpio.gpio, dev_name(dev));
+	if (err) {
+		dev_err(dev, "cannot get gpio %d\n", res->gpio.gpio);
+		return err;
+	}
+
+	return 0;
+}
+
+static int power_seq_step_run_gpio(struct power_seq_step *step)
+{
+	struct power_seq_resource *res = step->resource;
+
+	/* set the GPIO direction at first use */
+	if (!res->gpio.is_set) {
+		int err = gpio_direction_output(res->gpio.gpio,
+						step->gpio.value);
+		if (err)
+			return err;
+		res->gpio.is_set = true;
+	} else {
+		gpio_set_value_cansleep(res->gpio.gpio, step->gpio.value);
+	}
+
+	return 0;
+}
+
+#define POWER_SEQ_GPIO_TYPE {					\
+	.name = "gpio",					\
+	.of_parse = power_seq_of_parse_gpio,		\
+	.step_run = power_seq_step_run_gpio,		\
+	.res_compare = power_seq_res_compare_gpio,	\
+	.res_alloc = power_seq_res_alloc_gpio,		\
+}
diff --git a/drivers/power/power_seq/power_seq_pwm.c b/drivers/power/power_seq/power_seq_pwm.c
new file mode 100644
index 0000000..c0a1ec3
--- /dev/null
+++ b/drivers/power/power_seq/power_seq_pwm.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+
+#ifdef CONFIG_PWM
+
+#include <linux/pwm.h>
+
+#ifdef CONFIG_OF
+static int power_seq_of_parse_pwm(struct device_node *node,
+				  struct power_seq *seq,
+				  unsigned int step_nbr,
+				  struct power_seq_resource *res)
+{
+	struct power_seq_step *step = &seq->steps[step_nbr];
+	int err;
+
+	err = of_property_read_string(node, "id", &res->pwm.id);
+	if (err) {
+		power_seq_err(seq, step_nbr, "error reading id property\n");
+		return err;
+	}
+
+	err = of_power_seq_parse_enable_properties(node, seq, step_nbr,
+						   &step->pwm.enable);
+	return err;
+}
+#else
+#define of_power_seq_parse_pwm NULL
+#endif
+
+static bool power_seq_res_compare_pwm(struct power_seq_resource *res,
+				      struct power_seq_resource *res2)
+{
+	return !strcmp(res->pwm.id, res2->pwm.id);
+}
+
+static int power_seq_res_alloc_pwm(struct device *dev,
+				   struct power_seq_resource *res)
+{
+	res->pwm.pwm = devm_pwm_get(dev, res->pwm.id);
+	if (IS_ERR(res->pwm.pwm)) {
+		dev_err(dev, "cannot get pwm \"%s\"\n", res->pwm.id);
+		return PTR_ERR(res->pwm.pwm);
+	}
+
+	return 0;
+}
+
+static int power_seq_step_run_pwm(struct power_seq_step *step)
+{
+	if (step->pwm.enable) {
+		return pwm_enable(step->resource->pwm.pwm);
+	} else {
+		pwm_disable(step->resource->pwm.pwm);
+		return 0;
+	}
+}
+
+#define POWER_SEQ_PWM_TYPE {				\
+	.name = "pwm",					\
+	.of_parse = power_seq_of_parse_pwm,		\
+	.step_run = power_seq_step_run_pwm,		\
+	.res_compare = power_seq_res_compare_pwm,	\
+	.res_alloc = power_seq_res_alloc_pwm,		\
+}
+
+#else
+
+#define POWER_SEQ_PWM_TYPE {}
+
+#endif
diff --git a/drivers/power/power_seq/power_seq_regulator.c b/drivers/power/power_seq/power_seq_regulator.c
new file mode 100644
index 0000000..282cc95
--- /dev/null
+++ b/drivers/power/power_seq/power_seq_regulator.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+
+#ifdef CONFIG_REGULATOR
+
+#include <linux/regulator/consumer.h>
+
+#ifdef CONFIG_OF
+static int power_seq_of_parse_regulator(struct device_node *node,
+					struct power_seq *seq,
+					unsigned int step_nbr,
+					struct power_seq_resource *res)
+{
+	struct power_seq_step *step = &seq->steps[step_nbr];
+	int err;
+
+	err = of_property_read_string(node, "id",
+				      &res->regulator.id);
+	if (err) {
+		power_seq_err(seq, step_nbr, "error reading id property\n");
+		return err;
+	}
+
+	err = of_power_seq_parse_enable_properties(node, seq, step_nbr,
+						   &step->regulator.enable);
+	return err;
+}
+#else
+#define of_power_seq_parse_regulator NULL
+#endif
+
+static bool
+power_seq_res_compare_regulator(struct power_seq_resource *res,
+				struct power_seq_resource *res2)
+{
+	return !strcmp(res->regulator.id, res2->regulator.id);
+}
+
+static int power_seq_res_alloc_regulator(struct device *dev,
+					 struct power_seq_resource *res)
+{
+	res->regulator.regulator = devm_regulator_get(dev, res->regulator.id);
+	if (IS_ERR(res->regulator.regulator)) {
+		dev_err(dev, "cannot get regulator \"%s\"\n",
+			res->regulator.id);
+		return PTR_ERR(res->regulator.regulator);
+	}
+
+	return 0;
+}
+
+static int power_seq_step_run_regulator(struct power_seq_step *step)
+{
+	if (step->regulator.enable)
+		return regulator_enable(step->resource->regulator.regulator);
+	else
+		return regulator_disable(step->resource->regulator.regulator);
+}
+
+#define POWER_SEQ_REGULATOR_TYPE {			\
+	.name = "regulator",				\
+	.of_parse = power_seq_of_parse_regulator,	\
+	.step_run = power_seq_step_run_regulator,	\
+	.res_compare = power_seq_res_compare_regulator,	\
+	.res_alloc = power_seq_res_alloc_regulator,	\
+}
+
+#else
+
+#define POWER_SEQ_REGULATOR_TYPE {}
+
+#endif
diff --git a/include/linux/power_seq.h b/include/linux/power_seq.h
new file mode 100644
index 0000000..21b95b6
--- /dev/null
+++ b/include/linux/power_seq.h
@@ -0,0 +1,203 @@
+/*
+ * power_seq.h
+ *
+ * Simple interpreter for power sequences defined as platform data or device
+ * tree properties.
+ *
+ * Power sequences are designed to replace the callbacks typically used in
+ * board-specific files that implement board- or device- specific power
+ * sequences (such as those of backlights). A power sequence is an array of
+ * steps referencing resources (regulators, GPIOs, PWMs, ...) with an action to
+ * perform on them. By having the power sequences interpreted, it becomes
+ * possible to describe them in the device tree and thus to remove
+ * board-specific files from the kernel.
+ *
+ * See Documentation/power/power_seqs.txt for detailed information.
+ *
+ * Author: Alexandre Courbot <acourbot@nvidia.com>
+ *
+ * Copyright (c) 2012 NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __LINUX_POWER_SEQ_H
+#define __LINUX_POWER_SEQ_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct device;
+struct regulator;
+struct pwm_device;
+
+/**
+ * The different kinds of resources that can be controlled by the sequences
+ */
+enum power_seq_res_type {
+	POWER_SEQ_DELAY,
+	POWER_SEQ_REGULATOR,
+	POWER_SEQ_PWM,
+	POWER_SEQ_GPIO,
+	POWER_SEQ_NUM_TYPES,
+};
+
+/**
+ * struct power_seq_regulator_resource
+ * @id:		name of the regulator
+ * @regulator:	resolved regulator. Written during resource resolution.
+ */
+struct power_seq_regulator_resource {
+	const char *id;
+	struct regulator *regulator;
+};
+
+/**
+ * struct power_seq_pwm_resource
+ * @id:		name of the PWM
+ * @regulator:	resolved PWM. Written during resource resolution.
+ */
+struct power_seq_pwm_resource {
+	const char *id;
+	struct pwm_device *pwm;
+};
+
+/**
+ * struct power_seq_gpio_resource
+ * @gpio:	number of the GPIO
+ * @is_set:	track GPIO state to set its direction at first use
+ */
+struct power_seq_gpio_resource {
+	int gpio;
+	bool is_set;
+};
+
+/**
+ * struct power_seq_resource - resource used by power sequences
+ * @type:	type of the resource. This decides which member of the union is
+ *		used for this resource
+ * @list:	link resources together in power_seq_set
+ * @regulator:	used if @type = POWER_SEQ_REGULATOR
+ * @pwm:	used if @type = POWER_SEQ_PWM
+ * @gpio:	used if @type = POWER_SEQ_GPIO
+ */
+struct power_seq_resource {
+	enum power_seq_res_type type;
+	struct list_head list;
+	union {
+		struct power_seq_regulator_resource regulator;
+		struct power_seq_pwm_resource pwm;
+		struct power_seq_gpio_resource gpio;
+	};
+};
+#define power_seq_for_each_resource(pos, set)			\
+	list_for_each_entry(pos, &(set)->resources, list)
+
+/**
+ * struct power_seq_delay_step - action data for delay steps
+ * @delay:	amount of time to wait, in microseconds
+ */
+struct power_seq_delay_step {
+	unsigned int delay;
+};
+
+/**
+ * struct power_seq_regulator_step - platform data for regulator steps
+ * @enable:	whether to enable or disable the regulator during this step
+ */
+struct power_seq_regulator_step {
+	bool enable;
+};
+
+/**
+ * struct power_seq_pwm_step - action data for PWM steps
+ * @enable:	whether to enable or disable the PWM during this step
+ */
+struct power_seq_pwm_step {
+	bool enable;
+};
+
+/**
+ * struct power_seq_gpio_step - action data for GPIO steps
+ * @enable:	whether to enable or disable the GPIO during this step
+ */
+struct power_seq_gpio_step {
+	int value;
+};
+
+/**
+ * struct power_seq_step - data for power sequences steps
+ * @resource:	resource used by this step
+ * @delay:	used if resource->type = POWER_SEQ_DELAY
+ * @regulator:	used if resource->type = POWER_SEQ_REGULATOR
+ * @pwm:	used if resource->type = POWER_SEQ_PWN
+ * @gpio:	used if resource->type = POWER_SEQ_GPIO
+ */
+struct power_seq_step {
+	struct power_seq_resource *resource;
+	union {
+		struct power_seq_delay_step delay;
+		struct power_seq_regulator_step regulator;
+		struct power_seq_pwm_step pwm;
+		struct power_seq_gpio_step gpio;
+	};
+};
+
+struct power_seq_set;
+
+/**
+ * struct power_seq - single power sequence
+ * @id:		name of this sequence
+ * @list:	link sequences together in power_seq_set. Leave as-is
+ * @set:	set this sequence belongs to. Written when added to a set
+ * @num_steps:	number of steps in the sequence
+ * @steps:	array of steps that make the sequence
+ */
+struct power_seq {
+	const char *id;
+	struct list_head list;
+	struct power_seq_set *set;
+	unsigned int num_steps;
+	struct power_seq_step steps[];
+};
+
+/**
+ * struct power_seq_set - power sequences and resources used by a device
+ * @dev:	device this set belongs to
+ * @resources:	list of resources used by power sequences
+ * @seqs:	list of power sequences
+ */
+struct power_seq_set {
+	struct device *dev;
+	struct list_head resources;
+	struct list_head seqs;
+};
+
+/**
+ * struct platform_power_seq_set - define power sequences as platform data
+ * @num_seqs:	number of sequences defined
+ * @seqs:	array of num_seqs power sequences
+ */
+struct platform_power_seq_set {
+	unsigned int num_seqs;
+	struct power_seq *seqs[];
+};
+
+struct platform_power_seq_set *devm_of_parse_power_seq_set(struct device *dev);
+void power_seq_set_init(struct power_seq_set *set, struct device *dev);
+int power_seq_set_add_sequence(struct power_seq_set *set,
+			       struct power_seq *seq);
+int power_seq_set_add_sequences(struct power_seq_set *set,
+				struct platform_power_seq_set *seqs);
+struct power_seq *power_seq_lookup(struct power_seq_set *seqs, const char *id);
+int power_seq_run(struct power_seq *seq);
+
+#endif
-- 
1.7.12.3


^ permalink raw reply related

* [PATCH v7 0/3] Runtime Interpreter Power Sequences
From: Alexandre Courbot @ 2012-10-19  9:06 UTC (permalink / raw)
  To: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann
  Cc: Leela Krishna Amudala, linux-tegra, linux-kernel, linux-fbdev,
	devicetree-discuss, linux-pm, linux-doc, Alexandre Courbot

Quite a big redesign of the feature - both structures and code have been
simplified and should be easier to read and understand, data duplication is
avoided, and much less memory allocations take place at runtime. Usage
interface is also simpler and IMHO more logical.

What happened is that platform_power_seq and power_seq have been merged,
and so power sequences declared as platform data are used "as-is". The
"build" step is not needed anymore - resources are allocated when a
power sequence is added into a set.

There is still one design flaw that I would like to solve: sequences are
still tied to a device, i.e. if you have two devices that use the same
powering scheme, you have to declare two identical power sequences and assign
each of these to one device. It would be much more flexible if sequences could
be shared across devices. The problem is that the resources structure,
references by steps, cannot be shared. Actually we could share it by having
a linked list of devices and resolved resources in the resource structure and
thus share power sequences over devices. That would work for regulator and
PWM resources, which have a get_regulator/pwm(dev, id) function that could
return a different resource depending on the device that is passed.
But there is no similar get_gpio() function for GPIOs which number is directly
specified in the resource, and I don't know if such a function would make
sense otherwise. Opinions on the subject are welcome.

Alexandre Courbot (3):
  Runtime Interpreted Power Sequences
  pwm_backlight: use power sequences
  tegra: ventana: add PWM backlight to device tree

 .../devicetree/bindings/power/power_seq.txt        | 121 +++++++
 .../bindings/video/backlight/pwm-backlight.txt     |  72 +++-
 Documentation/power/power_seq.txt                  | 253 ++++++++++++++
 arch/arm/boot/dts/tegra20-ventana.dts              |  59 +++-
 drivers/power/Kconfig                              |   1 +
 drivers/power/Makefile                             |   1 +
 drivers/power/power_seq/Kconfig                    |   2 +
 drivers/power/power_seq/Makefile                   |   1 +
 drivers/power/power_seq/power_seq.c                | 376 +++++++++++++++++++++
 drivers/power/power_seq/power_seq_delay.c          |  65 ++++
 drivers/power/power_seq/power_seq_gpio.c           |  94 ++++++
 drivers/power/power_seq/power_seq_pwm.c            |  82 +++++
 drivers/power/power_seq/power_seq_regulator.c      |  83 +++++
 drivers/video/backlight/Kconfig                    |   1 +
 drivers/video/backlight/pwm_bl.c                   | 161 +++++++--
 include/linux/power_seq.h                          | 203 +++++++++++
 include/linux/pwm_backlight.h                      |  18 +-
 17 files changed, 1553 insertions(+), 40 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/power_seq.txt
 create mode 100644 Documentation/power/power_seq.txt
 create mode 100644 drivers/power/power_seq/Kconfig
 create mode 100644 drivers/power/power_seq/Makefile
 create mode 100644 drivers/power/power_seq/power_seq.c
 create mode 100644 drivers/power/power_seq/power_seq_delay.c
 create mode 100644 drivers/power/power_seq/power_seq_gpio.c
 create mode 100644 drivers/power/power_seq/power_seq_pwm.c
 create mode 100644 drivers/power/power_seq/power_seq_regulator.c
 create mode 100644 include/linux/power_seq.h

-- 
1.7.12.3


^ permalink raw reply

* Re: [PATCH 1/2] video: Add support for the Solomon SSD1307 OLED Controller
From: Maxime Ripard @ 2012-10-18 15:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50761BB0.3080507@gmx.de>

Hi Florian,

Le 11/10/2012 03:06, Florian Tobias Schandinat a écrit :
> Hi Maxime,
> 
> On 09/25/2012 10:06 AM, Maxime Ripard wrote:
>> Hello Florian,
>>
>> Le 21/09/2012 16:04, Maxime Ripard a écrit :
>>> Le 20/09/2012 18:33, Maxime Ripard a écrit :
>>>> This patch adds support for the Solomon SSD1307 OLED
>>>> controller found on the Crystalfontz CFA10036 board.
>>>>
>>>> This controller can drive a display with a resolution up
>>>> to 128x39 and can operate over I2C or SPI.
>>>>
>>>> The current driver has only been tested on the CFA-10036,
>>>> that is using this controller over I2C to driver a 96x16
>>>> OLED screen.
>>>>
>>>> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
>>>> Cc: Brian Lilly <brian@crystalfontz.com>
>>>
>>> Would you consider merging this patch or review it?
>>> It has been around for quite some time now and has not received any
>>> comments for at least the 2 last versions, so I guess there is not much
>>> left holding it back (unless, of course, if you have some comments to
>>> address), and I really would like to have it in 3.7.
>>
>> I guess we missed the 3.7 merge here. That's no big deal, but I sent the
>> patch series on a regular basis, starting 07/17 (sent new versions on
>> 07/31, 08/23, 09/05 and 09/20), addressed all the issues that were
>> raised, and never got any feedback from you, either positive or
>> negative. The patches affecting the MX28 device trees have already been
>> merged by Shawn Guo into its tree, so only the driver and the bindings
>> remain to be merged. So I'm kind of lost on what should I do here?
>>
>> Is it because I have an obviously broken driver or you don't have enough
>> time, or you don't want to merge drivers for such type of low resolution
>> controllers, or any other reason?
> 
> I don't see any reason to not support low resolutions, might be a nice
> status monitor for embedded devices.
> It's not your fault. It's just that for the past two months writing my
> thesis reduced my free time (and hence the time that I could potentially
> use for reviewing patches) to approximately zero. While I managed to get
> some smaller patches done, there really wasn't any time to process the
> 3-4 completely new things pending and some scarier patches which touch
> the core infrastructure.
> I think that it should be better soon, at least my thesis is now done,
> but I also have to fix up the rest of my life that was neglected while
> writing, including my paid jobs.

Ok, that's fine :)

How do you want to proceed? To you want me to resend the patchset, or
you will review this one when you will have some time?

Thanks,
Maxime

-- 
Maxime Ripard, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com

^ permalink raw reply

* Re: [PATCHv3 0/5] OMAP: VRFB: convert to platform device
From: Tomi Valkeinen @ 2012-10-18 12:45 UTC (permalink / raw)
  To: Tony Lindgren; +Cc: linux-omap, linux-fbdev, archit
In-Reply-To: <20121017182926.GW15569@atomide.com>

[-- Attachment #1: Type: text/plain, Size: 1585 bytes --]

On 2012-10-17 21:29, Tony Lindgren wrote:
> * Tomi Valkeinen <tomi.valkeinen@ti.com> [121017 03:27]:
>> Hi Tony,
>>
>> I have pushed this vrfb series and the omapdss version series to:
>>
>> git://gitorious.org/linux-omap-dss2/linux.git 3.8/dss-version
> 
> I used this branch up to commit 35f70935c64ae229ebd1fc65398ec68d4d803712,
> but left out the x86 compile changes as those seem unrelated to the plat
> issue.

Ok, you're right.

>> git://gitorious.org/linux-omap-dss2/linux.git 3.8/vrfb-conversion
> 
> And then I merged in this branch. I have the merged branch at
> omap-for-v3.8/cleanup-headers-dss.

Ok.

>> If they look ok to you and we don't get any other comments, I think
>> these branches can be considered stable and you can pull them.
> 
> Thanks yes let's consider those frozen and if something needs to
> be fixed it should be done on top of those commits.
>  
>> After these are merged:
>>
>> $ git grep -E "(plat/)|(mach/)" drivers/video/omap2/
>> drivers/video/omap2/omapfb/omapfb-ioctl.c:#include <plat/vram.h>
>> drivers/video/omap2/omapfb/omapfb-main.c:#include <plat/vram.h>
>> drivers/video/omap2/vram.c:#include <plat/vram.h>
>>
>> So the vram stuff should be the only omap dependency left in omapdss and
>> omapfb. I'll start working on that soon.
> 
> OK thanks a lot. Then there's also the two vout files that
> you may already have patches for:
> 
> drivers/media/platform/omap/omap_vout.c
> drivers/media/platform/omap/omap_voutlib.c

No yet. I hope I find time for those at some point soon =).

 Tomi



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]

^ permalink raw reply

* Re: [PATCH 9/9] OMAPDSS: DISPC: cleanup lcd/digit enable/disable
From: Tomi Valkeinen @ 2012-10-18 10:09 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <507EC2EA.4000402@ti.com>

[-- Attachment #1: Type: text/plain, Size: 1010 bytes --]

On 2012-10-17 17:38, Archit Taneja wrote:
> Hi,
> 
> On Wednesday 17 October 2012 04:50 PM, Tomi Valkeinen wrote:

>> -        if (r)
>> -            DSSERR("failed to register FRAMEDONE isr\n");
>> +    /* if we couldn't register for framedone, just sleep and exit */
>> +    if (r) {
>> +        msleep(200);
> 
> We sleep for 200 ms if we fail to register for framedone. But we just
> wait for 100ms for FRAMEDONE to occur. It seems a bit incorrect, both
> should be kept the same, shouldn't they?

They are just arbitrary numbers, but you're right, it makes more sense
to have same numbers for both.

>> -    r = omap_dispc_register_isr(dispc_disable_isr,
>> &frame_done_completion,
>> +    r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
>>               irq_mask);
>>       if (r)
>>           DSSERR("failed to register %x isr\n", irq_mask);
> 
> We should probably sleep here too as we did for LCD above.

Yep, good point. I'll add that.

 Tomi




[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]

^ permalink raw reply

* [PATCH 4/5] xen-fbfront: handle backend CLOSED without CLOSING
From: David Vrabel @ 2012-10-18 10:03 UTC (permalink / raw)
  To: xen-devel
  Cc: David Vrabel, Konrad Rzeszutek Wilk, linux-kernel, linux-fbdev,
	Florian Tobias Schandinat
In-Reply-To: <507FD39F.4060601@citrix.com>

From: David Vrabel <david.vrabel@citrix.com>

Backend drivers shouldn't transistion to CLOSED unless the frontend is
CLOSED.  If a backend does transition to CLOSED too soon then the
frontend may not see the CLOSING state and will not properly shutdown.

So, treat an unexpected backend CLOSED state the same as CLOSING.

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
Cc: linux-fbdev@vger.kernel.org
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
---
 drivers/video/xen-fbfront.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index b7f5173..917bb56 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -641,7 +641,6 @@ static void xenfb_backend_changed(struct xenbus_device *dev,
 	case XenbusStateReconfiguring:
 	case XenbusStateReconfigured:
 	case XenbusStateUnknown:
-	case XenbusStateClosed:
 		break;
 
 	case XenbusStateInitWait:
@@ -670,6 +669,10 @@ InitWait:
 		info->feature_resize = val;
 		break;
 
+	case XenbusStateClosed:
+		if (dev->state = XenbusStateClosed)
+			break;
+		/* Missed the backend's CLOSING state -- fallthrough */
 	case XenbusStateClosing:
 		xenbus_frontend_closed(dev);
 		break;
-- 
1.7.2.5


^ permalink raw reply related

* [PATCH 0/5] drivers: xen frontend devices should handle missed backend CLOSING
From: David Vrabel @ 2012-10-18 10:02 UTC (permalink / raw)
  To: xen-devel@lists.xensource.com, linux-kernel@vger.kernel.org,
	netdev@vger.kernel.org, David S. Miller, Jens Axboe, linux-pci,
	Bjorn Helgaas, linux-fbdev, Florian Tobias Schandinat,
	linux-input, Dmitry Torokhov
  Cc: David Vrabel

Subsystem maintainers, you can either pick up the relevant driver patch
or ack it to go via Konrad's Xen tree.

The series makes all the Xen frontend drivers handle the backend
transitioning to CLOSED without the frontend having previously seen
the backend in the CLOSING state.

Backends shouldn't do this but some do.  e.g., if the host is
XenServer and the toolstack decides to do a forced shutdown of a VBD,
then the blkfront may miss the CLOSING transition and the /dev/xvdX
device will not be destroyed which prevents it being reused.

I have seen systems that ended up in this state but it's not clear if
this was the actual cause.  However, I think in general it's a good
thing to thing to improve the handling of unexpected state
transitions.

David

^ permalink raw reply

* Re: [PATCHv3 0/5] OMAP: VRFB: convert to platform device
From: Tony Lindgren @ 2012-10-17 18:29 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, archit
In-Reply-To: <507E87B7.3070707@ti.com>

* Tomi Valkeinen <tomi.valkeinen@ti.com> [121017 03:27]:
> Hi Tony,
> 
> I have pushed this vrfb series and the omapdss version series to:
> 
> git://gitorious.org/linux-omap-dss2/linux.git 3.8/dss-version

I used this branch up to commit 35f70935c64ae229ebd1fc65398ec68d4d803712,
but left out the x86 compile changes as those seem unrelated to the plat
issue.

> git://gitorious.org/linux-omap-dss2/linux.git 3.8/vrfb-conversion

And then I merged in this branch. I have the merged branch at
omap-for-v3.8/cleanup-headers-dss.
 
> If they look ok to you and we don't get any other comments, I think
> these branches can be considered stable and you can pull them.

Thanks yes let's consider those frozen and if something needs to
be fixed it should be done on top of those commits.
 
> After these are merged:
> 
> $ git grep -E "(plat/)|(mach/)" drivers/video/omap2/
> drivers/video/omap2/omapfb/omapfb-ioctl.c:#include <plat/vram.h>
> drivers/video/omap2/omapfb/omapfb-main.c:#include <plat/vram.h>
> drivers/video/omap2/vram.c:#include <plat/vram.h>
> 
> So the vram stuff should be the only omap dependency left in omapdss and
> omapfb. I'll start working on that soon.

OK thanks a lot. Then there's also the two vout files that
you may already have patches for:

drivers/media/platform/omap/omap_vout.c
drivers/media/platform/omap/omap_voutlib.c

Regards,

Tony

^ permalink raw reply

* Re: [PATCH 9/9] OMAPDSS: DISPC: cleanup lcd/digit enable/disable
From: Archit Taneja @ 2012-10-17 14:50 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1350472835-28727-10-git-send-email-tomi.valkeinen@ti.com>

Hi,

On Wednesday 17 October 2012 04:50 PM, Tomi Valkeinen wrote:
> We currently have a single function to enable and disable the manager
> output for LCD and DIGIT. The functions are a bit complex, as handling
> both enable and disable require some extra steps to ensure that the
> output is enabled or disabled properly without errors before exiting the
> function.
>
> The code can be made simpler to understand by splitting the functions
> into separate enable and disable functions. We'll also clean up the
> comments and some parameter names at the same time.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>   drivers/video/omap2/dss/apply.c |    6 +-
>   drivers/video/omap2/dss/dispc.c |  178 ++++++++++++++++++++++++---------------
>   drivers/video/omap2/dss/dss.h   |    3 +-
>   3 files changed, 116 insertions(+), 71 deletions(-)
>
> diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
> index ae9f70d..4fff8ac 100644
> --- a/drivers/video/omap2/dss/apply.c
> +++ b/drivers/video/omap2/dss/apply.c
> @@ -772,7 +772,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
>   	if (!dss_data.irq_enabled && need_isr())
>   		dss_register_vsync_isr();
>
> -	dispc_mgr_enable(mgr->id, true);
> +	dispc_mgr_enable(mgr->id);
>
>   	mgr_clear_shadow_dirty(mgr);
>
> @@ -1027,7 +1027,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
>   	spin_unlock_irqrestore(&data_lock, flags);
>
>   	if (!mgr_manual_update(mgr))
> -		dispc_mgr_enable(mgr->id, true);
> +		dispc_mgr_enable(mgr->id);
>
>   out:
>   	mutex_unlock(&apply_lock);
> @@ -1052,7 +1052,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
>   		goto out;
>
>   	if (!mgr_manual_update(mgr))
> -		dispc_mgr_enable(mgr->id, false);
> +		dispc_mgr_disable(mgr->id);
>
>   	spin_lock_irqsave(&data_lock, flags);
>
> diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
> index 82339ad..94f393ec 100644
> --- a/drivers/video/omap2/dss/dispc.c
> +++ b/drivers/video/omap2/dss/dispc.c
> @@ -2590,7 +2590,7 @@ int dispc_ovl_enable(enum omap_plane plane, bool enable)
>   	return 0;
>   }
>
> -static void dispc_disable_isr(void *data, u32 mask)
> +static void dispc_mgr_disable_isr(void *data, u32 mask)
>   {
>   	struct completion *compl = data;
>   	complete(compl);
> @@ -2608,122 +2608,166 @@ bool dispc_mgr_is_enabled(enum omap_channel channel)
>   	return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
>   }
>
> -static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
> +static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
>   {
> -	struct completion frame_done_completion;
> -	bool is_on;
> +	_enable_mgr_out(channel, true);
> +}
> +
> +static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
> +{
> +	DECLARE_COMPLETION_ONSTACK(framedone_compl);
>   	int r;
>   	u32 irq;
>
> -	/* When we disable LCD output, we need to wait until frame is done.
> -	 * Otherwise the DSS is still working, and turning off the clocks
> -	 * prevents DSS from going to OFF mode */
> -	is_on = dispc_mgr_is_enabled(channel);
> +	if (dispc_mgr_is_enabled(channel) = false)
> +		return;
> +
> +	/*
> +	 * When we disable LCD output, we need to wait for FRAMEDONE to know
> +	 * that DISPC has finished with the LCD output.
> +	 */
>
> -	irq = mgr_desc[channel].framedone_irq;
> +	irq = dispc_mgr_get_framedone_irq(channel);
>
> -	if (!enable && is_on) {
> -		init_completion(&frame_done_completion);
> +	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
> +			irq);
> +	if (r)
> +		DSSERR("failed to register FRAMEDONE isr\n");
>
> -		r = omap_dispc_register_isr(dispc_disable_isr,
> -				&frame_done_completion, irq);
> +	_enable_mgr_out(channel, false);
>
> -		if (r)
> -			DSSERR("failed to register FRAMEDONE isr\n");
> +	/* if we couldn't register for framedone, just sleep and exit */
> +	if (r) {
> +		msleep(200);

We sleep for 200 ms if we fail to register for framedone. But we just 
wait for 100ms for FRAMEDONE to occur. It seems a bit incorrect, both 
should be kept the same, shouldn't they?

> +		return;
>   	}
>
> -	_enable_mgr_out(channel, enable);
> +	if (!wait_for_completion_timeout(&framedone_compl,
> +				msecs_to_jiffies(100)))
> +		DSSERR("timeout waiting for FRAME DONE\n");
>
> -	if (!enable && is_on) {
> -		if (!wait_for_completion_timeout(&frame_done_completion,
> -					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");
> +}
>
> -		r = omap_dispc_unregister_isr(dispc_disable_isr,
> -				&frame_done_completion, irq);
> +static void dispc_digit_out_enable_isr(void *data, u32 mask)
> +{
> +	struct completion *compl = data;
>
> -		if (r)
> -			DSSERR("failed to unregister FRAMEDONE isr\n");
> +	/* 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) = true)
> +		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;
>   	}
> +
> +	_enable_mgr_out(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_enable_digit_out(bool enable)
> +static void dispc_mgr_disable_digit_out(void)
>   {
> -	struct completion frame_done_completion;
> +	DECLARE_COMPLETION_ONSTACK(framedone_compl);
>   	enum dss_hdmi_venc_clk_source_select src;
>   	int r, i;
>   	u32 irq_mask;
>   	int num_irqs;
>
> -	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) = enable)
> +	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) = false)
>   		return;
>
>   	src = dss_get_hdmi_venc_clk_source();
>
> -	if (enable) {
> -		unsigned long flags;
> -		/* When we enable digit output, we'll get an extra digit
> -		 * sync lost interrupt, that we need to ignore */
> -		spin_lock_irqsave(&dispc.irq_lock, flags);
> -		dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
> -		_omap_dispc_set_irqs();
> -		spin_unlock_irqrestore(&dispc.irq_lock, flags);
> -	}
> -
> -	/* When we disable digit output, we need to wait until fields are done.
> -	 * Otherwise the DSS is still working, and turning off the clocks
> -	 * prevents DSS from going to OFF mode. And when enabling, we need to
> -	 * wait for the extra sync losts */
> -	init_completion(&frame_done_completion);
> +	/*
> +	 * When we disable the digit output, we need to wait for FRAMEDONE to
> +	 * know that DISPC has finished with the output. For analog tv out we'll
> +	 * use vsync, as omap2/3 don't have framedone for TV.
> +	 */
>
> -	if (src = DSS_HDMI_M_PCLK && enable = false) {
> +	if (src = DSS_HDMI_M_PCLK) {
>   		irq_mask = DISPC_IRQ_FRAMEDONETV;
>   		num_irqs = 1;
>   	} else {
> -		irq_mask = DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
> -		/* XXX I understand from TRM that we should only wait for the
> -		 * current field to complete. But it seems we have to wait for
> -		 * both fields */
> +		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_disable_isr, &frame_done_completion,
> +	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
>   			irq_mask);
>   	if (r)
>   		DSSERR("failed to register %x isr\n", irq_mask);

We should probably sleep here too as we did for LCD above.

Archit

^ permalink raw reply

* [PATCH 9/9] OMAPDSS: DISPC: cleanup lcd/digit enable/disable
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

We currently have a single function to enable and disable the manager
output for LCD and DIGIT. The functions are a bit complex, as handling
both enable and disable require some extra steps to ensure that the
output is enabled or disabled properly without errors before exiting the
function.

The code can be made simpler to understand by splitting the functions
into separate enable and disable functions. We'll also clean up the
comments and some parameter names at the same time.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/apply.c |    6 +-
 drivers/video/omap2/dss/dispc.c |  178 ++++++++++++++++++++++++---------------
 drivers/video/omap2/dss/dss.h   |    3 +-
 3 files changed, 116 insertions(+), 71 deletions(-)

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index ae9f70d..4fff8ac 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -772,7 +772,7 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
 	if (!dss_data.irq_enabled && need_isr())
 		dss_register_vsync_isr();
 
-	dispc_mgr_enable(mgr->id, true);
+	dispc_mgr_enable(mgr->id);
 
 	mgr_clear_shadow_dirty(mgr);
 
@@ -1027,7 +1027,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
 	spin_unlock_irqrestore(&data_lock, flags);
 
 	if (!mgr_manual_update(mgr))
-		dispc_mgr_enable(mgr->id, true);
+		dispc_mgr_enable(mgr->id);
 
 out:
 	mutex_unlock(&apply_lock);
@@ -1052,7 +1052,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
 		goto out;
 
 	if (!mgr_manual_update(mgr))
-		dispc_mgr_enable(mgr->id, false);
+		dispc_mgr_disable(mgr->id);
 
 	spin_lock_irqsave(&data_lock, flags);
 
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 82339ad..94f393ec 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -2590,7 +2590,7 @@ int dispc_ovl_enable(enum omap_plane plane, bool enable)
 	return 0;
 }
 
-static void dispc_disable_isr(void *data, u32 mask)
+static void dispc_mgr_disable_isr(void *data, u32 mask)
 {
 	struct completion *compl = data;
 	complete(compl);
@@ -2608,122 +2608,166 @@ bool dispc_mgr_is_enabled(enum omap_channel channel)
 	return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
 }
 
-static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
 {
-	struct completion frame_done_completion;
-	bool is_on;
+	_enable_mgr_out(channel, true);
+}
+
+static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
+{
+	DECLARE_COMPLETION_ONSTACK(framedone_compl);
 	int r;
 	u32 irq;
 
-	/* When we disable LCD output, we need to wait until frame is done.
-	 * Otherwise the DSS is still working, and turning off the clocks
-	 * prevents DSS from going to OFF mode */
-	is_on = dispc_mgr_is_enabled(channel);
+	if (dispc_mgr_is_enabled(channel) = false)
+		return;
+
+	/*
+	 * When we disable LCD output, we need to wait for FRAMEDONE to know
+	 * that DISPC has finished with the LCD output.
+	 */
 
-	irq = mgr_desc[channel].framedone_irq;
+	irq = dispc_mgr_get_framedone_irq(channel);
 
-	if (!enable && is_on) {
-		init_completion(&frame_done_completion);
+	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
+			irq);
+	if (r)
+		DSSERR("failed to register FRAMEDONE isr\n");
 
-		r = omap_dispc_register_isr(dispc_disable_isr,
-				&frame_done_completion, irq);
+	_enable_mgr_out(channel, false);
 
-		if (r)
-			DSSERR("failed to register FRAMEDONE isr\n");
+	/* if we couldn't register for framedone, just sleep and exit */
+	if (r) {
+		msleep(200);
+		return;
 	}
 
-	_enable_mgr_out(channel, enable);
+	if (!wait_for_completion_timeout(&framedone_compl,
+				msecs_to_jiffies(100)))
+		DSSERR("timeout waiting for FRAME DONE\n");
 
-	if (!enable && is_on) {
-		if (!wait_for_completion_timeout(&frame_done_completion,
-					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");
+}
 
-		r = omap_dispc_unregister_isr(dispc_disable_isr,
-				&frame_done_completion, irq);
+static void dispc_digit_out_enable_isr(void *data, u32 mask)
+{
+	struct completion *compl = data;
 
-		if (r)
-			DSSERR("failed to unregister FRAMEDONE isr\n");
+	/* 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) = true)
+		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;
 	}
+
+	_enable_mgr_out(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_enable_digit_out(bool enable)
+static void dispc_mgr_disable_digit_out(void)
 {
-	struct completion frame_done_completion;
+	DECLARE_COMPLETION_ONSTACK(framedone_compl);
 	enum dss_hdmi_venc_clk_source_select src;
 	int r, i;
 	u32 irq_mask;
 	int num_irqs;
 
-	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) = enable)
+	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) = false)
 		return;
 
 	src = dss_get_hdmi_venc_clk_source();
 
-	if (enable) {
-		unsigned long flags;
-		/* When we enable digit output, we'll get an extra digit
-		 * sync lost interrupt, that we need to ignore */
-		spin_lock_irqsave(&dispc.irq_lock, flags);
-		dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
-		_omap_dispc_set_irqs();
-		spin_unlock_irqrestore(&dispc.irq_lock, flags);
-	}
-
-	/* When we disable digit output, we need to wait until fields are done.
-	 * Otherwise the DSS is still working, and turning off the clocks
-	 * prevents DSS from going to OFF mode. And when enabling, we need to
-	 * wait for the extra sync losts */
-	init_completion(&frame_done_completion);
+	/*
+	 * When we disable the digit output, we need to wait for FRAMEDONE to
+	 * know that DISPC has finished with the output. For analog tv out we'll
+	 * use vsync, as omap2/3 don't have framedone for TV.
+	 */
 
-	if (src = DSS_HDMI_M_PCLK && enable = false) {
+	if (src = DSS_HDMI_M_PCLK) {
 		irq_mask = DISPC_IRQ_FRAMEDONETV;
 		num_irqs = 1;
 	} else {
-		irq_mask = DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD;
-		/* XXX I understand from TRM that we should only wait for the
-		 * current field to complete. But it seems we have to wait for
-		 * both fields */
+		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_disable_isr, &frame_done_completion,
+	r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
 			irq_mask);
 	if (r)
 		DSSERR("failed to register %x isr\n", irq_mask);
 
-	_enable_mgr_out(OMAP_DSS_CHANNEL_DIGIT, enable);
+	_enable_mgr_out(OMAP_DSS_CHANNEL_DIGIT, false);
 
 	for (i = 0; i < num_irqs; ++i) {
-		if (!wait_for_completion_timeout(&frame_done_completion,
+		if (!wait_for_completion_timeout(&framedone_compl,
 					msecs_to_jiffies(100)))
-			DSSERR("timeout waiting for digit out to %s\n",
-					enable ? "start" : "stop");
+			DSSERR("timeout waiting for digit out to stop\n");
 	}
 
-	r = omap_dispc_unregister_isr(dispc_disable_isr, &frame_done_completion,
+	r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
 			irq_mask);
 	if (r)
 		DSSERR("failed to unregister %x isr\n", irq_mask);
+}
 
-	if (enable) {
-		unsigned long flags;
-		spin_lock_irqsave(&dispc.irq_lock, flags);
-		dispc.irq_error_mask |= DISPC_IRQ_SYNC_LOST_DIGIT;
-		dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT);
-		_omap_dispc_set_irqs();
-		spin_unlock_irqrestore(&dispc.irq_lock, flags);
-	}
+void dispc_mgr_enable(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_enable(enum omap_channel channel, bool enable)
+void dispc_mgr_disable(enum omap_channel channel)
 {
 	if (dss_mgr_is_lcd(channel))
-		dispc_mgr_enable_lcd_out(channel, enable);
+		dispc_mgr_disable_lcd_out(channel);
 	else if (channel = OMAP_DSS_CHANNEL_DIGIT)
-		dispc_mgr_enable_digit_out(enable);
+		dispc_mgr_disable_digit_out();
 	else
-		BUG();
+		WARN_ON(1);
 }
 
 void dispc_wb_enable(bool enable)
@@ -2740,7 +2784,7 @@ void dispc_wb_enable(bool enable)
 	if (!enable && is_on) {
 		init_completion(&frame_done_completion);
 
-		r = omap_dispc_register_isr(dispc_disable_isr,
+		r = omap_dispc_register_isr(dispc_mgr_disable_isr,
 				&frame_done_completion, irq);
 		if (r)
 			DSSERR("failed to register FRAMEDONEWB isr\n");
@@ -2753,7 +2797,7 @@ void dispc_wb_enable(bool enable)
 					msecs_to_jiffies(100)))
 			DSSERR("timeout waiting for FRAMEDONEWB\n");
 
-		r = omap_dispc_unregister_isr(dispc_disable_isr,
+		r = omap_dispc_unregister_isr(dispc_mgr_disable_isr,
 				&frame_done_completion, irq);
 		if (r)
 			DSSERR("failed to unregister FRAMEDONEWB isr\n");
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index b6e3a06..e501a77 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -464,7 +464,8 @@ u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);
 bool dispc_mgr_go_busy(enum omap_channel channel);
 void dispc_mgr_go(enum omap_channel channel);
 bool dispc_mgr_is_enabled(enum omap_channel channel);
-void dispc_mgr_enable(enum omap_channel channel, bool enable);
+void dispc_mgr_enable(enum omap_channel channel);
+void dispc_mgr_disable(enum omap_channel channel);
 bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
 void dispc_mgr_set_lcd_config(enum omap_channel channel,
 		const struct dss_lcd_mgr_config *config);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 8/9] OMAPDSS: DISPC: add dispc_mgr_get_sync_lost_irq()
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

Add function that returns the sync lost irq mask for the given channel.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dispc.c |    5 +++++
 drivers/video/omap2/dss/dss.h   |    1 +
 2 files changed, 6 insertions(+)

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 492740e..82339ad 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -543,6 +543,11 @@ u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
 	return mgr_desc[channel].framedone_irq;
 }
 
+u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel)
+{
+	return mgr_desc[channel].sync_lost_irq;
+}
+
 u32 dispc_wb_get_framedone_irq(void)
 {
 	return DISPC_IRQ_FRAMEDONEWB;
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 373847c..b6e3a06 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -460,6 +460,7 @@ void dispc_ovl_set_channel_out(enum omap_plane plane,
 
 u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
 u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
+u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel);
 bool dispc_mgr_go_busy(enum omap_channel channel);
 void dispc_mgr_go(enum omap_channel channel);
 bool dispc_mgr_is_enabled(enum omap_channel channel);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 7/9] OMAPDSS: DISPC: cleanup lcd and digit enable
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

dispc.c's functions to enable LCD and DIGIT outputs can be cleaned up a
bit by using common functions to set the enable bit and to check if the
output is enabled.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dispc.c |   27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 9f0ce18..492740e 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -2591,13 +2591,18 @@ static void dispc_disable_isr(void *data, u32 mask)
 	complete(compl);
 }
 
-static void _enable_lcd_out(enum omap_channel channel, bool enable)
+static void _enable_mgr_out(enum omap_channel channel, bool enable)
 {
 	mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
 	/* flush posted write */
 	mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
 }
 
+bool dispc_mgr_is_enabled(enum omap_channel channel)
+{
+	return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
+}
+
 static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
 {
 	struct completion frame_done_completion;
@@ -2608,7 +2613,7 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
 	/* When we disable LCD output, we need to wait until frame is done.
 	 * Otherwise the DSS is still working, and turning off the clocks
 	 * prevents DSS from going to OFF mode */
-	is_on = mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
+	is_on = dispc_mgr_is_enabled(channel);
 
 	irq = mgr_desc[channel].framedone_irq;
 
@@ -2622,7 +2627,7 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
 			DSSERR("failed to register FRAMEDONE isr\n");
 	}
 
-	_enable_lcd_out(channel, enable);
+	_enable_mgr_out(channel, enable);
 
 	if (!enable && is_on) {
 		if (!wait_for_completion_timeout(&frame_done_completion,
@@ -2637,13 +2642,6 @@ static void dispc_mgr_enable_lcd_out(enum omap_channel channel, bool enable)
 	}
 }
 
-static void _enable_digit_out(bool enable)
-{
-	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1);
-	/* flush posted write */
-	dispc_read_reg(DISPC_CONTROL);
-}
-
 static void dispc_mgr_enable_digit_out(bool enable)
 {
 	struct completion frame_done_completion;
@@ -2652,7 +2650,7 @@ static void dispc_mgr_enable_digit_out(bool enable)
 	u32 irq_mask;
 	int num_irqs;
 
-	if (REG_GET(DISPC_CONTROL, 1, 1) = enable)
+	if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT) = enable)
 		return;
 
 	src = dss_get_hdmi_venc_clk_source();
@@ -2689,7 +2687,7 @@ static void dispc_mgr_enable_digit_out(bool enable)
 	if (r)
 		DSSERR("failed to register %x isr\n", irq_mask);
 
-	_enable_digit_out(enable);
+	_enable_mgr_out(OMAP_DSS_CHANNEL_DIGIT, enable);
 
 	for (i = 0; i < num_irqs; ++i) {
 		if (!wait_for_completion_timeout(&frame_done_completion,
@@ -2713,11 +2711,6 @@ static void dispc_mgr_enable_digit_out(bool enable)
 	}
 }
 
-bool dispc_mgr_is_enabled(enum omap_channel channel)
-{
-	return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
-}
-
 void dispc_mgr_enable(enum omap_channel channel, bool enable)
 {
 	if (dss_mgr_is_lcd(channel))
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 6/9] OMAPDSS: DISPC: remove struct omap_overlay use
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

dispc_ovl_setup() uses struct omap_overlay to get the caps for the
overlay. We can change the code to get the caps directly from dss
features, thus removing the dependency to struct omap_overlay.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dispc.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index d3c58eb..9f0ce18 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -2505,7 +2505,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
 		bool mem_to_mem)
 {
 	int r;
-	const struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+	enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
 	enum omap_channel channel;
 
 	channel = dispc_ovl_get_channel_out(plane);
@@ -2516,7 +2516,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
 		oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
 		oi->color_mode, oi->rotation, oi->mirror, channel, replication);
 
-	r = dispc_ovl_setup_common(plane, ovl->caps, oi->paddr, oi->p_uv_addr,
+	r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr,
 		oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
 		oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
 		oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 5/9] OMAPDSS: remove declarations for non-existing funcs
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

dss_mgr_set_device and dss_mgr_unset_device are declared in dss.h, but
the functions do not exist. Remove the declarations.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dss.h |    3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 8447871..373847c 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -217,9 +217,6 @@ int dss_mgr_set_info(struct omap_overlay_manager *mgr,
 		struct omap_overlay_manager_info *info);
 void dss_mgr_get_info(struct omap_overlay_manager *mgr,
 		struct omap_overlay_manager_info *info);
-int dss_mgr_set_device(struct omap_overlay_manager *mgr,
-		struct omap_dss_device *dssdev);
-int dss_mgr_unset_device(struct omap_overlay_manager *mgr);
 int dss_mgr_set_output(struct omap_overlay_manager *mgr,
 		struct omap_dss_output *output);
 int dss_mgr_unset_output(struct omap_overlay_manager *mgr);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 4/9] OMAPDSS: combine LCD related config into one func
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

Dispc has a bunch of functions used to configure output related
parameters:

- dispc_mgr_set_io_pad_mode
- dispc_mgr_enable_stallmode
- dispc_mgr_enable_fifohandcheck
- dispc_mgr_set_clock_div
- dispc_mgr_set_tft_data_lines
- dispc_lcd_enable_signal_polarity
- dispc_mgr_set_lcd_type_tft

These are all called together, and the configuration values are taken
from struct dss_lcd_mgr_config.

Instead of exposing those individual dispc functions, create a new one,
dispc_mgr_set_lcd_config(), which is used to configure the above
parameters from values in struct dss_lcd_mgr_config.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/apply.c |   18 ++----------------
 drivers/video/omap2/dss/dispc.c |   29 +++++++++++++++++++++++------
 drivers/video/omap2/dss/dss.h   |    8 ++------
 3 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 29ce5a8..ae9f70d 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -666,22 +666,8 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
 	dispc_mgr_set_timings(mgr->id, &mp->timings);
 
 	/* lcd_config parameters */
-	if (dss_mgr_is_lcd(mgr->id)) {
-		dispc_mgr_set_io_pad_mode(mp->lcd_config.io_pad_mode);
-
-		dispc_mgr_enable_stallmode(mgr->id, mp->lcd_config.stallmode);
-		dispc_mgr_enable_fifohandcheck(mgr->id,
-			mp->lcd_config.fifohandcheck);
-
-		dispc_mgr_set_clock_div(mgr->id, &mp->lcd_config.clock_info);
-
-		dispc_mgr_set_tft_data_lines(mgr->id,
-			mp->lcd_config.video_port_width);
-
-		dispc_lcd_enable_signal_polarity(mp->lcd_config.lcden_sig_polarity);
-
-		dispc_mgr_set_lcd_type_tft(mgr->id);
-	}
+	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)
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index 4dfc90a..d3c58eb 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -2769,7 +2769,7 @@ bool dispc_wb_is_enabled(void)
 	return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
 }
 
-void dispc_lcd_enable_signal_polarity(bool act_high)
+static void dispc_lcd_enable_signal_polarity(bool act_high)
 {
 	if (!dss_has_feature(FEAT_LCDENABLEPOL))
 		return;
@@ -2793,13 +2793,13 @@ void dispc_pck_free_enable(bool enable)
 	REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
 }
 
-void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
 {
 	mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
 }
 
 
-void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
+static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
 {
 	mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
 }
@@ -2855,7 +2855,7 @@ void dispc_mgr_setup(enum omap_channel channel,
 	}
 }
 
-void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
+static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
 {
 	int code;
 
@@ -2880,7 +2880,7 @@ void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
 	mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
 }
 
-void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
+static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
 {
 	u32 l;
 	int gpout0, gpout1;
@@ -2909,11 +2909,28 @@ void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
 	dispc_write_reg(DISPC_CONTROL, l);
 }
 
-void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
+static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
 {
 	mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
 }
 
+void dispc_mgr_set_lcd_config(enum omap_channel channel,
+		const struct dss_lcd_mgr_config *config)
+{
+	dispc_mgr_set_io_pad_mode(config->io_pad_mode);
+
+	dispc_mgr_enable_stallmode(channel, config->stallmode);
+	dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck);
+
+	dispc_mgr_set_clock_div(channel, &config->clock_info);
+
+	dispc_mgr_set_tft_data_lines(channel, config->video_port_width);
+
+	dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity);
+
+	dispc_mgr_set_lcd_type_tft(channel);
+}
+
 static bool _dispc_mgr_size_ok(u16 width, u16 height)
 {
 	return width <= dss_feat_get_param_max(FEAT_PARAM_MGR_WIDTH) &&
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index eaf0856..8447871 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -435,7 +435,6 @@ void dispc_runtime_put(void);
 void dispc_enable_sidle(void);
 void dispc_disable_sidle(void);
 
-void dispc_lcd_enable_signal_polarity(bool act_high);
 void dispc_lcd_enable_signal(bool enable);
 void dispc_pck_free_enable(bool enable);
 void dispc_enable_fifomerge(bool enable);
@@ -462,7 +461,6 @@ int dispc_ovl_enable(enum omap_plane plane, bool enable);
 void dispc_ovl_set_channel_out(enum omap_plane plane,
 		enum omap_channel channel);
 
-void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable);
 u32 dispc_mgr_get_vsync_irq(enum omap_channel channel);
 u32 dispc_mgr_get_framedone_irq(enum omap_channel channel);
 bool dispc_mgr_go_busy(enum omap_channel channel);
@@ -470,10 +468,8 @@ void dispc_mgr_go(enum omap_channel channel);
 bool dispc_mgr_is_enabled(enum omap_channel channel);
 void dispc_mgr_enable(enum omap_channel channel, bool enable);
 bool dispc_mgr_is_channel_enabled(enum omap_channel channel);
-void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode);
-void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
-void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
-void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
+void dispc_mgr_set_lcd_config(enum omap_channel channel,
+		const struct dss_lcd_mgr_config *config);
 void dispc_mgr_set_timings(enum omap_channel channel,
 		const struct omap_video_timings *timings);
 unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 3/9] OMAPDSS: DISPC: constify function parameters
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

Add consts to dispc function parameters which do not modify the passed
structs.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dispc.c |   10 +++++-----
 drivers/video/omap2/dss/dss.h   |    6 +++---
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index b43477a..4dfc90a 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -1076,7 +1076,7 @@ static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
 }
 
 static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
-		struct omap_dss_cpr_coefs *coefs)
+		const struct omap_dss_cpr_coefs *coefs)
 {
 	u32 coef_r, coef_g, coef_b;
 
@@ -2505,7 +2505,7 @@ int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
 		bool mem_to_mem)
 {
 	int r;
-	struct omap_overlay *ovl = omap_dss_get_overlay(plane);
+	const struct omap_overlay *ovl = omap_dss_get_overlay(plane);
 	enum omap_channel channel;
 
 	channel = dispc_ovl_get_channel_out(plane);
@@ -2842,7 +2842,7 @@ static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
 }
 
 void dispc_mgr_setup(enum omap_channel channel,
-		struct omap_overlay_manager_info *info)
+		const struct omap_overlay_manager_info *info)
 {
 	dispc_mgr_set_default_color(channel, info->default_color);
 	dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key);
@@ -3012,7 +3012,7 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
 
 /* change name to mode? */
 void dispc_mgr_set_timings(enum omap_channel channel,
-		struct omap_video_timings *timings)
+		const struct omap_video_timings *timings)
 {
 	unsigned xtot, ytot;
 	unsigned long ht, vt;
@@ -3533,7 +3533,7 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
 }
 
 void dispc_mgr_set_clock_div(enum omap_channel channel,
-		struct dispc_clock_info *cinfo)
+		const struct dispc_clock_info *cinfo)
 {
 	DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
 	DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 6728892..eaf0856 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -475,16 +475,16 @@ void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable);
 void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines);
 void dispc_mgr_set_lcd_type_tft(enum omap_channel channel);
 void dispc_mgr_set_timings(enum omap_channel channel,
-		struct omap_video_timings *timings);
+		const struct omap_video_timings *timings);
 unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
 unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
 unsigned long dispc_core_clk_rate(void);
 void dispc_mgr_set_clock_div(enum omap_channel channel,
-		struct dispc_clock_info *cinfo);
+		const struct dispc_clock_info *cinfo);
 int dispc_mgr_get_clock_div(enum omap_channel channel,
 		struct dispc_clock_info *cinfo);
 void dispc_mgr_setup(enum omap_channel channel,
-		struct omap_overlay_manager_info *info);
+		const struct omap_overlay_manager_info *info);
 
 u32 dispc_wb_get_framedone_irq(void);
 bool dispc_wb_go_busy(void);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 2/9] OMAPDSS: fix registering the vsync isr in apply
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

When we enable an output we don't check if we need to register the vsync
isr. This causes us to miss vsync interrupts until somebody changes the
configuration of an overlay or an overlay manager.

Add the registration to dss_mgr_enable to fix the problem.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/apply.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 19d66f4..29ce5a8 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -1035,6 +1035,9 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
 	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))
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/9] OMAPDSS: DSI: fix dsi_get_dsidev_from_id()
From: Tomi Valkeinen @ 2012-10-17 11:20 UTC (permalink / raw)
  To: archit, linux-omap, linux-fbdev; +Cc: Tomi Valkeinen
In-Reply-To: <1350472835-28727-1-git-send-email-tomi.valkeinen@ti.com>

If dsi_get_dsidev_from_id() is called with a DSI module id that does not
exist on the board, the function will crash as omap_dss_get_output()
will return NULL.

This happens on omap3 boards when dumping DSI clocks, as the dumping
code will try to get the dsidev for DSI modules 0 and 1, but omap3 only
has DSI module 0.

Also clean up the id -> output mapping, so that if the function is
called with invalid module ID it will return NULL.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Archit Taneja <archit@ti.com>
---
 drivers/video/omap2/dss/dsi.c |   13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index d64ac38..bee9284 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -365,11 +365,20 @@ struct platform_device *dsi_get_dsidev_from_id(int module)
 	struct omap_dss_output *out;
 	enum omap_dss_output_id	id;
 
-	id = module = 0 ? OMAP_DSS_OUTPUT_DSI1 : OMAP_DSS_OUTPUT_DSI2;
+	switch (module) {
+	case 0:
+		id = OMAP_DSS_OUTPUT_DSI1;
+		break;
+	case 1:
+		id = OMAP_DSS_OUTPUT_DSI2;
+		break;
+	default:
+		return NULL;
+	}
 
 	out = omap_dss_get_output(id);
 
-	return out->pdev;
+	return out ? out->pdev : NULL;
 }
 
 static inline void dsi_write_reg(struct platform_device *dsidev,
-- 
1.7.9.5


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox