Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH 15/17] OMAPDSS: remove extra_info completion code
From: Tomi Valkeinen @ 2012-09-06 13:42 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <5048A6AA.50009@ti.com>

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

On Thu, 2012-09-06 at 19:05 +0530, Archit Taneja wrote:
> On Thursday 06 September 2012 06:34 PM, Tomi Valkeinen wrote:
> > On Wed, 2012-09-05 at 19:01 +0530, Archit Taneja wrote:
> >> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> >>> Now that fifo merge has been removed, nobody uses the extra_info related
> >>> completion code, which can be removed.
> >>
> >> I think this might come into use when we fix the usage of channel out
> >> field. That is, since channel out is an immediate write field, when we
> >> set a new manager for an overlay, we may need to wait till the overlay
> >> disable kicks in, only then we should change channel out.
> >>
> >> For this, we would need some wait for extra_info, right?
> >
> > Hmm, yes, I think you are right. Previously the ovl_disable waited until
> > the overlay is not used anymore, but now it doesn't.
> >
> > So I think I need to add wait_pending_extra_info_updates() call to the
> > beginning of dss_ovl_set_manager(). Or, should it be in unset_manager...
> > I think unset is better, then a "free" overlay always disabled also in
> > the HW level.
> 
> Yes, I also think it should be in unset_manager. One option could be to 
> leave the wait_pending_extra_info_updates() in ovl_disable itself, as it 
> was before. But that would force us to use mutexes there, and we'd 
> rather have overlay enabling and disabling as a non blocking thing.

Actually, we do have mutexes there. You are thinking about the prototype
API I have. (I also thought we didn't have mutex there =).

So, in fact, we can have the wait at ovl_disable like it was before. The
prototype API, which cannot block, will not have the wait, but there the
caller (i.e. omapdrm) will have to manage the proper wait.

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [RFC PATCH] OMAPDSS: DISPC: Fix IRQ unregister race
From: Tomi Valkeinen @ 2012-09-06 13:36 UTC (permalink / raw)
  To: Dimitar Dimitrov; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346613123-24053-1-git-send-email-dinuxbg@gmail.com>

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

Hi,

On Sun, 2012-09-02 at 22:12 +0300, Dimitar Dimitrov wrote:
> Very rare kernel crashes are reported on a custom OMAP4 board. Kernel
> panics due to corrupted completion structure while executing
> dispc_irq_wait_handler(). Excerpt from kernel log:
> 
>   Internal error: Oops - undefined instruction: 0 [#1] PREEMPT SMP
>   Unable to handle kernel paging request at virtual address 00400130
>   ...
>   PC is at 0xebf205bc
>   LR is at __wake_up_common+0x54/0x94
>   ...
>   (__wake_up_common+0x0/0x94)
>   (complete+0x0/0x60)
>   (dispc_irq_wait_handler.36902+0x0/0x14)
>   (omap_dispc_irq_handler+0x0/0x354)
>   (handle_irq_event_percpu+0x0/0x188)
>   (handle_irq_event+0x0/0x64)
>   (handle_fasteoi_irq+0x0/0x10c)
>   (generic_handle_irq+0x0/0x48)
>   (asm_do_IRQ+0x0/0xc0)
> 
> DISPC IRQ executes callbacks with dispc.irq_lock released. Hence
> unregister_isr() and DISPC IRQ might be running in parallel on different
> CPUs. So there is a chance that a callback is executed even though it
> has been unregistered. As omap_dispc_wait_for_irq_timeout() declares a
> completion on stack, the dispc_irq_wait_handler() callback might try to
> access a completion structure that is invalid. This leads to crashes and
> hangs.
> 
> Solution is to divide unregister calls into two sets:
>   1. Non-strict unregistering of callbacks. Callbacks could safely be
>      executed after unregistering them. This is the case with unregister
>      calls from the IRQ handler itself.
>   2. Strict (synchronized) unregistering. Callbacks are not allowed
>      after unregistering. This is the case with completion waiting.
> 
> The above solution should satisfy one of the original intentions of the
> driver: callbacks should be able to unregister themselves.

I think it'd be better to create a new function for the nosync version,
and keep the old name for the sync version. The reason for this is to
minimize the amount of changes, as I think this one needs to be applied
to stable kernel trees also.

Also, I think we need similar one for dsi.c, as it has the same kind of
irq handling. But with a quick glance only sync version is needed there.

However, I'm not quite sure about this approach. The fix makes sense,
but it makes me think if the irq handling is designed the wrong way.

While debugging and fixing this, did you think some other irq handling
approach would be saner?

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 15/17] OMAPDSS: remove extra_info completion code
From: Tomi Valkeinen @ 2012-09-06 13:04 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <50475444.2080509@ti.com>

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

On Wed, 2012-09-05 at 19:01 +0530, Archit Taneja wrote:
> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> > Now that fifo merge has been removed, nobody uses the extra_info related
> > completion code, which can be removed.
> 
> I think this might come into use when we fix the usage of channel out 
> field. That is, since channel out is an immediate write field, when we 
> set a new manager for an overlay, we may need to wait till the overlay 
> disable kicks in, only then we should change channel out.
> 
> For this, we would need some wait for extra_info, right?

Hmm, yes, I think you are right. Previously the ovl_disable waited until
the overlay is not used anymore, but now it doesn't.

So I think I need to add wait_pending_extra_info_updates() call to the
beginning of dss_ovl_set_manager(). Or, should it be in unset_manager...
I think unset is better, then a "free" overlay always disabled also in
the HW level.

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 13/17] Revert "OMAPDSS: APPLY: add fifo-merge support"
From: Tomi Valkeinen @ 2012-09-06 12:55 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <5047570D.1030601@ti.com>

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

On Wed, 2012-09-05 at 19:13 +0530, Archit Taneja wrote:
> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> > This reverts commit 1d71f42b35ed66d90a9a39bc515bb16cfe2d4a46.
> >
> > Adding fifo merge feature as an omapdss internal configuration was a
> > mistake. We cannot hide from the users of omapdss the complexities of
> > fifo merge.
> >
> > This commit removes the fifo merge support, which luckily is easily done
> > as it was handled totally inside apply.c.
> >
> > The plan is to try fifo merge again later when it is more clear how the
> > hardware acts in various situations, and how the omapdrm wants to use
> > fifo merge.
> 
> Nitpick: This isn't an exact revert right? Maybe we should mention that, 
> so that if someone tries to apply the old patch, it doesn't fail.

Yep, I added a note.

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 4/4] video: mmp: add tpo hvga panel support
From: Jun Nie @ 2012-09-06 12:01 UTC (permalink / raw)
  To: linux-fbdev

2012/8/29 Zhou Zhu <zzhu3@marvell.com>:
> From: Lisa Du <cldu@marvell.com>
>
> Add tpo hvga panel support in marvell display framework.
> This panel driver implements modes query and on/off commands by spi.
> This panel driver also get panel config/ plat power on/off/ connected
> path name from machine-info.
> This panel driver uses mmp_disp supplied register_panel function to
> register panel to path as machine-info defined.
>
> Change-Id: I1d37a9b436f64a01954d7f32407f84f67945286f
> Signed-off-by: Lisa Du <cldu@marvell.com>
> ---
>  drivers/video/mmp/Kconfig                 |    1 +
>  drivers/video/mmp/Makefile                |    2 +-
>  drivers/video/mmp/panel/Kconfig           |    5 +
>  drivers/video/mmp/panel/Makefile          |    1 +
>  drivers/video/mmp/panel/tpo_tj032md01bw.c |  181 +++++++++++++++++++++++++++++
>  5 files changed, 189 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/video/mmp/panel/Kconfig
>  create mode 100644 drivers/video/mmp/panel/Makefile
>  create mode 100644 drivers/video/mmp/panel/tpo_tj032md01bw.c
>
> diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig
> index ed51d15..e9ea39e 100644
> --- a/drivers/video/mmp/Kconfig
> +++ b/drivers/video/mmp/Kconfig
> @@ -6,5 +6,6 @@ menuconfig MMP_DISP
>
>  if MMP_DISP
>  source "drivers/video/mmp/hw/Kconfig"
> +source "drivers/video/mmp/panel/Kconfig"
>  source "drivers/video/mmp/fb/Kconfig"
>  endif
> diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile
> index 6999a09..a014cb3 100644
> --- a/drivers/video/mmp/Makefile
> +++ b/drivers/video/mmp/Makefile
> @@ -1 +1 @@
> -obj-y += core.o hw/ fb/
> +obj-y += core.o hw/ panel/ fb/
> diff --git a/drivers/video/mmp/panel/Kconfig b/drivers/video/mmp/panel/Kconfig
> new file mode 100644
> index 0000000..b6aac76
> --- /dev/null
> +++ b/drivers/video/mmp/panel/Kconfig
> @@ -0,0 +1,5 @@
> +config MMP_PANEL_TPOHVGA
> +       bool "tpohvga panel TJ032MD01BW support"
> +       default n
> +       help
> +               tpohvga panel support
> diff --git a/drivers/video/mmp/panel/Makefile b/drivers/video/mmp/panel/Makefile
> new file mode 100644
> index 0000000..2f91611
> --- /dev/null
> +++ b/drivers/video/mmp/panel/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_MMP_PANEL_TPOHVGA)    += tpo_tj032md01bw.o
> diff --git a/drivers/video/mmp/panel/tpo_tj032md01bw.c b/drivers/video/mmp/panel/tpo_tj032md01bw.c
> new file mode 100644
> index 0000000..8217650
> --- /dev/null
> +++ b/drivers/video/mmp/panel/tpo_tj032md01bw.c
> @@ -0,0 +1,181 @@
> +/*
> + * linux/drivers/video/mmp/panel/tpo_tj032md01bw.c
> + * active panel using spi interface to do init
> + *
> + * Copyright (C) 2012 Marvell Technology Group Ltd.
> + * Authors:  Guoqing Li <ligq@marvell.com>
> + *          Lisa Du <cldu@marvell.com>
> + *          Zhou Zhu <zzhu3@marvell.com>
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/string.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <video/mmp_disp.h>
> +
> +static u16 panel_init[] = {
> +       0x0801,
> +       0x0800,
> +       0x0200,
> +       0x0304,
> +       0x040e,
> +       0x0903,
> +       0x0b18,
> +       0x0c53,
> +       0x0d01,
> +       0x0ee0,
> +       0x0f01,
> +       0x1058,
> +       0x201e,
> +       0x210a,
> +       0x220a,
> +       0x231e,
> +       0x2400,
> +       0x2532,
> +       0x2600,
> +       0x27ac,
> +       0x2904,
> +       0x2aa2,
> +       0x2b45,
> +       0x2c45,
> +       0x2d15,
> +       0x2e5a,
> +       0x2fff,
> +       0x306b,
> +       0x310d,
> +       0x3248,
> +       0x3382,
> +       0x34bd,
> +       0x35e7,
> +       0x3618,
> +       0x3794,
> +       0x3801,
> +       0x395d,
> +       0x3aae,
> +       0x3bff,
> +       0x07c9,
> +};
> +
> +static u16 panel_poweroff[] = {
> +       0x07d9,
> +};
> +
> +static struct mmp_panel_cmds tpohvga_on_cmds[] = {
> +       [0] = {
> +               .cmds = panel_init,
> +               .cmds_num = ARRAY_SIZE(panel_init),
> +               .sleep = 0,
> +       },
> +};
> +
> +static struct mmp_panel_cmds tpohvga_off_cmds[] = {
> +       [0] = {
> +               .cmds = panel_poweroff,
> +               .cmds_num = ARRAY_SIZE(panel_poweroff),
> +               .sleep = 0,
> +       },
> +};
> +
> +static struct mmp_spi_cfg tpohvga_spi_cfg = {
> +       .clk_cnt = 16,
> +       .tx_bits = 16,
> +       .wire_num = 1,
> +};
> +
> +static struct mmp_panel_cmd_sets tpohvga_spi_cmds = {
> +       .type = PANEL_CMDS_SPI,
> +       .on_cmds = tpohvga_on_cmds,
> +       .on_cmds_num = ARRAY_SIZE(tpohvga_on_cmds),
> +       .off_cmds = tpohvga_off_cmds,
> +       .off_cmds_num = ARRAY_SIZE(tpohvga_off_cmds),
> +       .config = &tpohvga_spi_cfg,
> +};
struct mmp_panel_cmds and  struct mmp_panel_cmd_sets are not generic,
can not cover all spi panel neither. Some spi panel initial sequence
include several cmd arrays and delay is needed between arrays. So an
ops is more proper for LCD driver to hook, instead of feeding
commands.
Standard SPI bus implementation is decent than current implementation.
> +
> +static struct mmp_mode mmp_modes_tpohvga[] = {
> +       [0] = {
> +               .pixclock_freq = 10394400,
> +               .refresh = 60,
> +               .xres = 320,
> +               .yres = 480,
> +               .hsync_len = 10,
> +               .left_margin = 15,
> +               .right_margin = 10,
> +               .vsync_len = 2,
> +               .upper_margin = 4,
> +               .lower_margin = 2,
> +               .invert_pixclock = 1,
> +               .pix_fmt_out = PIXFMT_RGB565,
> +       },
> +};
> +
> +static int tpohvga_get_modelist(struct mmp_panel *panel,
> +               struct mmp_mode **modelist)
> +{
> +       *modelist = mmp_modes_tpohvga;
> +       return 1;
> +}
> +
> +static struct mmp_panel panel_tpohvga = {
> +       .name = "tpohvga",
> +       .panel_type = PANELTYPE_Active,
> +       .get_modelist = tpohvga_get_modelist,
> +       .cmd_set = &tpohvga_spi_cmds,
> +};
> +
> +static int __devinit tpohvga_probe(struct platform_device *pdev)
> +{
> +       struct mmp_mach_panel_info *mi;
> +       /* get configs from platform data */
> +       mi = pdev->dev.platform_data;
> +       if (mi = NULL) {
> +               dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       panel_tpohvga.plat_set_onoff = mi->plat_set_onoff;
> +       panel_tpohvga.plat_path_name = mi->plat_path_name;
> +       panel_tpohvga.dev = &pdev->dev;
> +
> +       if (!mmp_register_panel(&panel_tpohvga)) {
> +               dev_err(&pdev->dev, "%s: register failed\n", __func__);
> +               return -EINVAL;
> +       }
> +       return 0;
> +}
> +
> +static struct platform_driver panel_tpohvga_driver = {
> +       .driver         = {
> +               .name   = "tpo-hvga",
> +               .owner  = THIS_MODULE,
> +       },
> +       .probe          = tpohvga_probe,
> +};
> +
> +static int __devinit panel_tpohvga_init(void)
> +{
> +       return platform_driver_register(&panel_tpohvga_driver);
> +}
> +module_init(panel_tpohvga_init);
> +
> +MODULE_AUTHOR("Lisa Du<cldu@marvell.com>");
> +MODULE_DESCRIPTION("Panel driver for tpohvga");
> +MODULE_LICENSE("GPL");
> --
> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] video: bf*: Add missing spinlock init
From: Jean Delvare @ 2012-09-06  7:23 UTC (permalink / raw)
  To: linux-fbdev

It doesn't seem these spinlocks were properly initialized.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
---
I can't even build-test this.

 drivers/video/bf54x-lq043fb.c    |    1 +
 drivers/video/bfin-lq035q1-fb.c  |    1 +
 drivers/video/bfin-t350mcqb-fb.c |    1 +
 3 files changed, 3 insertions(+)

--- linux-3.6-rc4.orig/drivers/video/bf54x-lq043fb.c	2012-07-21 22:58:29.000000000 +0200
+++ linux-3.6-rc4/drivers/video/bf54x-lq043fb.c	2012-09-06 08:49:37.812899185 +0200
@@ -525,6 +525,7 @@ static int __devinit bfin_bf54x_probe(st
 	info = fbinfo->par;
 	info->fb = fbinfo;
 	info->dev = &pdev->dev;
+	spin_lock_init(&info->lock);
 
 	platform_set_drvdata(pdev, fbinfo);
 
--- linux-3.6-rc4.orig/drivers/video/bfin-lq035q1-fb.c	2012-07-21 22:58:29.000000000 +0200
+++ linux-3.6-rc4/drivers/video/bfin-lq035q1-fb.c	2012-09-06 08:48:50.618858480 +0200
@@ -577,6 +577,7 @@ static int __devinit bfin_lq035q1_probe(
 	info = fbinfo->par;
 	info->fb = fbinfo;
 	info->dev = &pdev->dev;
+	spin_lock_init(&info->lock);
 
 	info->disp_info = pdev->dev.platform_data;
 
--- linux-3.6-rc4.orig/drivers/video/bfin-t350mcqb-fb.c	2012-07-21 22:58:29.000000000 +0200
+++ linux-3.6-rc4/drivers/video/bfin-t350mcqb-fb.c	2012-09-06 08:48:12.310825601 +0200
@@ -447,6 +447,7 @@ static int __devinit bfin_t350mcqb_probe
 	info = fbinfo->par;
 	info->fb = fbinfo;
 	info->dev = &pdev->dev;
+	spin_lock_init(&info->lock);
 
 	platform_set_drvdata(pdev, fbinfo);
 


-- 
Jean Delvare

^ permalink raw reply

* Re: [PATCH 2/4] video: mmp fb support
From: Jun Nie @ 2012-09-06  3:01 UTC (permalink / raw)
  To: linux-fbdev

2012/8/29 Zhou Zhu <zzhu3@marvell.com>:
> Added fb support for Marvell mmp display subsystem.
> This driver is configured using "buffer driver mach info".
> With configured name of path, this driver get path using
> using exported interface of mmp display driver.
> Then this driver get ovly using configured id and operates
> on this ovly to show buffers on display devices.
>
> Change-Id: I1a6fb4f89ac933e1364a4511dd3ec1463463d9c8
> Signed-off-by: Zhou Zhu <zzhu3@marvell.com>
> Signed-off-by: Lisa Du <cldu@marvell.com>
> ---
>  drivers/video/mmp/Kconfig     |    4 +
>  drivers/video/mmp/Makefile    |    2 +-
>  drivers/video/mmp/fb/Kconfig  |   13 +
>  drivers/video/mmp/fb/Makefile |    1 +
>  drivers/video/mmp/fb/mmpfb.c  |  663 +++++++++++++++++++++++++++++++++++++++++
>  drivers/video/mmp/fb/mmpfb.h  |   51 ++++
>  6 files changed, 733 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/video/mmp/fb/Kconfig
>  create mode 100644 drivers/video/mmp/fb/Makefile
>  create mode 100644 drivers/video/mmp/fb/mmpfb.c
>  create mode 100644 drivers/video/mmp/fb/mmpfb.h
>
> diff --git a/drivers/video/mmp/Kconfig b/drivers/video/mmp/Kconfig
> index 0554336..6a0b056 100644
> --- a/drivers/video/mmp/Kconfig
> +++ b/drivers/video/mmp/Kconfig
> @@ -3,3 +3,7 @@ menuconfig MMP_DISP
>          depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
>          help
>           Marvell Display Subsystem support.
> +
> +if MMP_DISP
> +source "drivers/video/mmp/fb/Kconfig"
> +endif
> diff --git a/drivers/video/mmp/Makefile b/drivers/video/mmp/Makefile
> index 820eb10..fdcd833 100644
> --- a/drivers/video/mmp/Makefile
> +++ b/drivers/video/mmp/Makefile
> @@ -1 +1 @@
> -obj-y += core.o
> +obj-y += core.o fb/
> diff --git a/drivers/video/mmp/fb/Kconfig b/drivers/video/mmp/fb/Kconfig
> new file mode 100644
> index 0000000..9b0141f
> --- /dev/null
> +++ b/drivers/video/mmp/fb/Kconfig
> @@ -0,0 +1,13 @@
> +if MMP_DISP
> +
> +config MMP_FB
> +       bool "fb driver for Marvell MMP Display Subsystem"
> +       depends on FB
> +       select FB_CFB_FILLRECT
> +       select FB_CFB_COPYAREA
> +       select FB_CFB_IMAGEBLIT
> +       default y
> +       help
> +               fb driver for Marvell MMP Display Subsystem
> +
> +endif
> diff --git a/drivers/video/mmp/fb/Makefile b/drivers/video/mmp/fb/Makefile
> new file mode 100644
> index 0000000..709fd1f
> --- /dev/null
> +++ b/drivers/video/mmp/fb/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_MMP_FB)  += mmpfb.o
> diff --git a/drivers/video/mmp/fb/mmpfb.c b/drivers/video/mmp/fb/mmpfb.c
> new file mode 100644
> index 0000000..83e4a0c
> --- /dev/null
> +++ b/drivers/video/mmp/fb/mmpfb.c
> @@ -0,0 +1,663 @@
> +/*
> + * linux/drivers/video/mmp/fb/mmpfb.c
> + * Framebuffer driver for Marvell Display controller.
> + *
> + * Copyright (C) 2012 Marvell Technology Group Ltd.
> + * Authors: Zhou Zhu <zzhu3@marvell.com>
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/vmalloc.h>
> +#include <asm/cacheflush.h>
> +#include "mmpfb.h"
> +
> +static int var_to_pixfmt(struct fb_var_screeninfo *var)
> +{
> +       /*
> +        * Pseudocolor mode?
> +        */
> +       if (var->bits_per_pixel = 8)
> +               return PIXFMT_PSEUDOCOLOR;
> +
> +       /*
> +        * Check for YUV422PLANAR.
> +        */
> +       if (var->bits_per_pixel = 16 && var->red.length = 8 &&
> +                       var->green.length = 4 && var->blue.length = 4) {
> +               if (var->green.offset >= var->blue.offset)
> +                       return PIXFMT_YUV422P;
> +               else
> +                       return PIXFMT_YVU422P;
> +       }
> +
> +       /*
> +        * Check for YUV420PLANAR.
> +        */
> +       if (var->bits_per_pixel = 12 && var->red.length = 8 &&
> +                       var->green.length = 2 && var->blue.length = 2) {
> +               if (var->green.offset >= var->blue.offset)
> +                       return PIXFMT_YUV420P;
> +               else
> +                       return PIXFMT_YVU420P;
> +       }
> +
> +       /*
> +        * Check for YUV422PACK.
> +        */
> +       if (var->bits_per_pixel = 16 && var->red.length = 16 &&
> +                       var->green.length = 16 && var->blue.length = 16) {
> +               if (var->red.offset = 0)
> +                       return PIXFMT_YUYV;
> +               else if (var->green.offset >= var->blue.offset)
> +                       return PIXFMT_UYVY;
> +               else
> +                       return PIXFMT_VYUY;
> +       }
> +
> +       /*
> +        * Check for 565/1555.
> +        */
> +       if (var->bits_per_pixel = 16 && var->red.length <= 5 &&
> +                       var->green.length <= 6 && var->blue.length <= 5) {
> +               if (var->transp.length = 0) {
> +                       if (var->red.offset >= var->blue.offset)
> +                               return PIXFMT_RGB565;
> +                       else
> +                               return PIXFMT_BGR565;
> +               }
> +       }
> +
> +       /*
> +        * Check for 888/A888.
> +        */
> +       if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
> +                       var->green.length <= 8 && var->blue.length <= 8) {
> +               if (var->bits_per_pixel = 24 && var->transp.length = 0) {
> +                       if (var->red.offset >= var->blue.offset)
> +                               return PIXFMT_RGB888PACK;
> +                       else
> +                               return PIXFMT_BGR888PACK;
> +               }
> +
> +               if (var->bits_per_pixel = 32 && var->transp.offset = 24) {
> +                       if (var->red.offset >= var->blue.offset)
> +                               return PIXFMT_RGBA888;
> +                       else
> +                               return PIXFMT_BGRA888;
> +               } else {
> +                       if (var->red.offset >= var->blue.offset)
> +                               return PIXFMT_RGB888UNPACK;
> +                       else
> +                               return PIXFMT_BGR888UNPACK;
> +               }
> +
> +               /* fall through */
> +       }
> +
> +       return -EINVAL;
> +
> +}
> +
> +static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
> +{
> +       switch (pix_fmt) {
> +       case PIXFMT_RGB565:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 11;   var->red.length = 5;
> +               var->green.offset = 5;   var->green.length = 6;
> +               var->blue.offset = 0;   var->blue.length = 5;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_BGR565:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 0;    var->red.length = 5;
> +               var->green.offset = 5;   var->green.length = 6;
> +               var->blue.offset = 11;  var->blue.length = 5;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_RGB888UNPACK:
> +               var->bits_per_pixel = 32;
> +               var->red.offset = 16;   var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 0;   var->blue.length = 8;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_BGR888UNPACK:
> +               var->bits_per_pixel = 32;
> +               var->red.offset = 0;    var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 16;  var->blue.length = 8;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_RGBA888:
> +               var->bits_per_pixel = 32;
> +               var->red.offset = 16;   var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 0;   var->blue.length = 8;
> +               var->transp.offset = 24; var->transp.length = 8;
> +               break;
> +       case PIXFMT_BGRA888:
> +               var->bits_per_pixel = 32;
> +               var->red.offset = 0;    var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 16;  var->blue.length = 8;
> +               var->transp.offset = 24; var->transp.length = 8;
> +               break;
> +       case PIXFMT_RGB888PACK:
> +               var->bits_per_pixel = 24;
> +               var->red.offset = 16;   var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 0;   var->blue.length = 8;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_BGR888PACK:
> +               var->bits_per_pixel = 24;
> +               var->red.offset = 0;    var->red.length = 8;
> +               var->green.offset = 8;   var->green.length = 8;
> +               var->blue.offset = 16;  var->blue.length = 8;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_YUV420P:
> +               var->bits_per_pixel = 12;
> +               var->red.offset = 4;     var->red.length = 8;
> +               var->green.offset = 2;   var->green.length = 2;
> +               var->blue.offset = 0;   var->blue.length = 2;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_YVU420P:
> +               var->bits_per_pixel = 12;
> +               var->red.offset = 4;     var->red.length = 8;
> +               var->green.offset = 0;   var->green.length = 2;
> +               var->blue.offset = 2;   var->blue.length = 2;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_YUV422P:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 8;     var->red.length = 8;
> +               var->green.offset = 4;   var->green.length = 4;
> +               var->blue.offset = 0;   var->blue.length = 4;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_YVU422P:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 8;     var->red.length = 8;
> +               var->green.offset = 0;   var->green.length = 4;
> +               var->blue.offset = 4;   var->blue.length = 4;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_UYVY:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 8;     var->red.length = 16;
> +               var->green.offset = 4;   var->green.length = 16;
> +               var->blue.offset = 0;   var->blue.length = 16;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_VYUY:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 8;     var->red.length = 16;
> +               var->green.offset = 0;   var->green.length = 16;
> +               var->blue.offset = 4;   var->blue.length = 16;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_YUYV:
> +               var->bits_per_pixel = 16;
> +               var->red.offset = 0;     var->red.length = 16;
> +               var->green.offset = 4;   var->green.length = 16;
> +               var->blue.offset = 8;   var->blue.length = 16;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       case PIXFMT_PSEUDOCOLOR:
> +               var->bits_per_pixel = 8;
> +               var->red.offset = 0;     var->red.length = 8;
> +               var->green.offset = 0;   var->green.length = 8;
> +               var->blue.offset = 0;   var->blue.length = 8;
> +               var->transp.offset = 0;  var->transp.length = 0;
> +               break;
> +       }
> +}
> +
> +/*
> + * fb framework has its limitation:
> + * 1. input color/output color is not seprated
> + * 2. fb_videomode not include output color
> + * so for fb usage, we keep a output format which is not changed
> + *  then it's added for mmpmode
> + */
> +static void fbmode_to_mmpmode(struct mmp_mode *mode,
> +               struct fb_videomode *videomode, int output_fmt)
> +{
> +       u64 div_result = 1000000000000ll;
> +       mode->name = videomode->name;
> +       mode->refresh = videomode->refresh;
> +       mode->xres = videomode->xres;
> +       mode->yres = videomode->yres;
> +
> +       do_div(div_result, videomode->pixclock);
> +       mode->pixclock_freq = (u32)div_result;
> +
> +       mode->left_margin = videomode->left_margin;
> +       mode->right_margin = videomode->right_margin;
> +       mode->upper_margin = videomode->upper_margin;
> +       mode->lower_margin = videomode->lower_margin;
> +       mode->hsync_len = videomode->hsync_len;
> +       mode->vsync_len = videomode->vsync_len;
> +       mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
> +       mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
> +       /* no defined flag in fb, use vmode>>3*/
> +       mode->invert_pixclock = !!(videomode->vmode & 8);
> +       mode->pix_fmt_out = output_fmt;
> +}
> +
> +static void mmpmode_to_fbmode(struct fb_videomode *videomode,
> +               struct mmp_mode *mode)
> +{
> +       u64 div_result = 1000000000000ll;
> +
> +       videomode->name = mode->name;
> +       videomode->refresh = mode->refresh;
> +       videomode->xres = mode->xres;
> +       videomode->yres = mode->yres;
> +
> +       do_div(div_result, mode->pixclock_freq);
> +       videomode->pixclock = (u32)div_result;
> +
> +       videomode->left_margin = mode->left_margin;
> +       videomode->right_margin = mode->right_margin;
> +       videomode->upper_margin = mode->upper_margin;
> +       videomode->lower_margin = mode->lower_margin;
> +       videomode->hsync_len = mode->hsync_len;
> +       videomode->vsync_len = mode->vsync_len;
> +       videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
> +               | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
> +       videomode->vmode = mode->invert_pixclock ? 8 : 0;
> +}
> +
> +
> +static void *alloc_framebuffer(size_t size, dma_addr_t *dma)
> +{
> +       int nr, i = 0;
> +       struct page **pages;
> +       void *start;
> +
> +       nr = size >> PAGE_SHIFT;
> +       start = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO);
> +       if (start = NULL)
> +               return NULL;
> +
> +       *dma = virt_to_phys(start);
> +       pages = vmalloc(sizeof(struct page *) * nr);
> +       if (pages = NULL)
> +               return NULL;
> +
> +       while (i < nr) {
> +               pages[i] = phys_to_page(*dma + (i << PAGE_SHIFT));
> +               i++;
> +       }
> +       start = vmap(pages, nr, 0, pgprot_writecombine(pgprot_kernel));
> +
> +       vfree(pages);
> +       return start;
> +}
> +
> +static int mmpfb_check_var(struct fb_var_screeninfo *var,
> +               struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = info->par;
> +
> +       if (var->bits_per_pixel = 8)
> +               return -EINVAL;
> +       /*
> +        * Basic geometry sanity checks.
> +        */
> +       if (var->xoffset + var->xres > var->xres_virtual)
> +               return -EINVAL;
> +       if (var->yoffset + var->yres > var->yres_virtual)
> +               return -EINVAL;
> +
> +       /*
> +        * Check size of framebuffer.
> +        */
> +       if (var->xres_virtual * var->yres_virtual *
> +                       (var->bits_per_pixel >> 3) > fbi->fb_size)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
> +{
> +       return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
> +}
> +
> +static u32 to_rgb(u16 red, u16 green, u16 blue)
> +{
> +       red >>= 8;
> +       green >>= 8;
> +       blue >>= 8;
> +
> +       return (red << 16) | (green << 8) | blue;
> +}
> +
> +static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
> +               unsigned int green, unsigned int blue,
> +               unsigned int trans, struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = info->par;
> +       u32 val;
> +
> +       if (info->fix.visual = FB_VISUAL_TRUECOLOR && regno < 16) {
> +               val =  chan_to_field(red,   &info->var.red);
> +               val |= chan_to_field(green, &info->var.green);
> +               val |= chan_to_field(blue , &info->var.blue);
> +               fbi->pseudo_palette[regno] = val;
> +       }
> +
> +       if (info->fix.visual = FB_VISUAL_PSEUDOCOLOR && regno < 256) {
> +               val = to_rgb(red, green, blue);
> +               /* TODO */
> +       }
> +
> +       return 0;
> +}
> +
> +static int mmpfb_pan_display(struct fb_var_screeninfo *var,
> +               struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = (struct mmpfb_info *)info->par;
> +       struct mmp_addr addr;
> +
> +       addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
> +               * var->bits_per_pixel / 8 + fbi->fb_start_dma;
> +       mmp_ovly_set_addr(fbi->ovly, &addr);
> +
> +       return 0;
> +}
> +
> +static int var_update(struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = info->par;
> +       struct fb_var_screeninfo *var = &info->var;
> +       struct fb_videomode *m;
> +       int pix_fmt;
> +
> +       /* set pix_fmt */
> +       pix_fmt = var_to_pixfmt(var);
> +       if (pix_fmt < 0)
> +               return -EINVAL;
> +       pixfmt_to_var(var, pix_fmt);
> +       fbi->pix_fmt = pix_fmt;
> +
> +       /* set var according to best video mode*/
> +       m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
> +       if (!m) {
> +               dev_err(fbi->dev, "set par: no match mode, use best mode\n");
> +               m = (struct fb_videomode *)fb_find_best_mode(var,
> +                               &info->modelist);
> +               fb_videomode_to_var(var, m);
> +       }
> +       memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
> +
> +       /* fix to 2* yres */
> +       var->yres_virtual = var->yres * 2;
> +       info->fix.visual = (pix_fmt = PIXFMT_PSEUDOCOLOR) ?
> +               FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
> +       info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
> +       info->fix.ypanstep = var->yres;
> +       return 0;
> +}
> +
> +static int mmpfb_set_par(struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = info->par;
> +       struct fb_var_screeninfo *var = &info->var;
> +       struct mmp_addr addr;
> +       struct mmp_win win;
> +       struct mmp_mode mode;
> +
> +       int ret = var_update(info);
> +       if (ret != 0)
> +               return ret;
> +
> +       /* set window/path according to new videomode */
> +       fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
> +       mmp_path_set_mode(fbi->path, &mode);
> +
> +       win.xsrc = win.xdst = fbi->mode.xres;
> +       win.ysrc = win.ydst = fbi->mode.yres;
> +       win.pix_fmt = fbi->pix_fmt;
> +       mmp_ovly_set_win(fbi->ovly, &win);
> +
> +       /* set address always */
> +       addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
> +               * var->bits_per_pixel / 8 + fbi->fb_start_dma;
> +       mmp_ovly_set_addr(fbi->ovly, &addr);
> +
> +       return 0;
> +}
> +
> +static void mmpfb_gfx_power(struct mmpfb_info *fbi, int power)
> +{
> +       mmp_ovly_set_onoff(fbi->ovly, power);
> +}
> +
> +static int mmpfb_gfx_blank(int blank, struct fb_info *info)
> +{
> +       struct mmpfb_info *fbi = info->par;
> +
> +       mmpfb_gfx_power(fbi, (blank = FB_BLANK_UNBLANK));
> +
> +       return 0;
> +}
> +
> +static struct fb_ops mmpfb_gfx_ops = {
> +       .owner          = THIS_MODULE,
> +       .fb_blank       = mmpfb_gfx_blank,
> +       .fb_check_var   = mmpfb_check_var,
> +       .fb_set_par     = mmpfb_set_par,
> +       .fb_setcolreg   = mmpfb_setcolreg,
> +       .fb_pan_display = mmpfb_pan_display,
> +       .fb_fillrect    = cfb_fillrect,
> +       .fb_copyarea    = cfb_copyarea,
> +       .fb_imageblit   = cfb_imageblit,
> +};
> +
> +static int __devinit mmpfb_probe(struct platform_device *pdev)
> +{
> +       struct mmp_buffer_driver_mach_info *mi;
> +       struct fb_info *info = 0;
> +       struct mmpfb_info *fbi = 0;
> +       int ret, videomode_num, i;
> +       struct fb_videomode *videomodes;
> +       struct mmp_mode *mmp_modes;
> +       struct mmp_win win;
> +       struct mmp_addr addr;
> +
> +       mi = pdev->dev.platform_data;
> +       if (mi = NULL) {
> +               dev_err(&pdev->dev, "no platform data defined\n");
> +               return -EINVAL;
> +       }
> +
> +       /* initialize fb */
> +       info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
> +       if (info = NULL)
> +               return -ENOMEM;
> +       fbi = info->par;
> +       if (!fbi) {
> +               ret = -EINVAL;
> +               goto failed;
> +       }
> +
> +       /* init fb */
> +       fbi->fb_info = info;
> +       platform_set_drvdata(pdev, fbi);
> +       fbi->dev = &pdev->dev;
> +       fbi->pix_fmt = mi->default_pixfmt;
> +       mutex_init(&fbi->access_ok);
> +
> +       /* get display path by name */
> +       fbi->path = mmp_get_path(mi->path_name);
> +       if (!fbi->path) {
> +               dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
> +               ret = -EINVAL;
> +               goto failed_destroy_mutex;
> +       }
> +
> +       dev_info(fbi->dev, "path %s get\n", fbi->path->name);
> +
> +       /* get videomodes from path */
> +       videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
> +       if (!videomode_num) {
> +               dev_err(&pdev->dev, "can't get videomode num\n");
> +               ret = -EINVAL;
> +               goto failed_destroy_mutex;
> +       }
Can you just print warning if mode number is 0, instead of failure?
Plug-able panel is likely not ready when probe and panel driver has to
return no mode info.

> +       /* put videomode list to info structure */
> +       videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
> +                       GFP_KERNEL);
> +       if (!videomodes) {
> +               dev_err(&pdev->dev, "can't malloc video modes\n");
> +               ret = -ENOMEM;
> +               goto failed_destroy_mutex;
> +       }
> +       for (i = 0; i < videomode_num; i++)
> +               mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
> +       fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
> +
> +       /* set videomode[0] as default mode */
> +       memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
> +       fbi->output_fmt = mmp_modes[0].pix_fmt_out;
> +       fb_videomode_to_var(&info->var, &fbi->mode);
> +       mmp_path_set_mode(fbi->path, &mmp_modes[0]);
> +       /* fix to 2* yres */
> +       info->var.yres_virtual = info->var.yres * 2;
> +       pixfmt_to_var(&info->var, fbi->pix_fmt);
> +
> +       /* Allocate framebuffer memory: size = modes xy *4 .*/
> +       fbi->fb_size = PAGE_ALIGN(info->var.xres_virtual *
> +               info->var.yres_virtual * info->var.bits_per_pixel / 8);
> +       fbi->fb_start = alloc_framebuffer(fbi->fb_size + PAGE_SIZE,
> +                               &fbi->fb_start_dma);
> +
> +       if (fbi->fb_start = NULL) {
> +               dev_err(&pdev->dev, "can't alloc framebuffer\n");
> +               ret = -ENOMEM;
> +               goto failed_destroy_mutex;
> +       }
> +       memset(fbi->fb_start, 0, fbi->fb_size);
> +
> +       dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
> +
> +       /* get ovly */
> +       fbi->ovly = mmp_path_get_ovly(fbi->path, mi->ovly_id);
> +       if (!fbi->ovly) {
> +               ret = -EINVAL;
> +               goto failed_destroy_mutex;
> +       }
> +       /* set fetch used */
> +       mmp_ovly_set_fetch(fbi->ovly, mi->dmafetch_id);
> +
> +       /* set win */
> +       memset(&win, 0, sizeof(win));
> +       win.pix_fmt = fbi->pix_fmt;
> +       win.xsrc = win.xdst = fbi->mode.xres;
> +       win.ysrc = win.ydst = fbi->mode.yres;
> +       mmp_ovly_set_win(fbi->ovly, &win);
> +       /* set addr */
> +       memset(&addr, 0, sizeof(addr));
> +       addr.phys[0] = fbi->fb_start_dma;
> +       mmp_ovly_set_addr(fbi->ovly, &addr);
> +       mmp_ovly_set_onoff(fbi->ovly, 1);
> +
> +       /* Initialise static fb parameters.*/
> +       info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
> +               FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
> +       info->node = -1;
> +       strcpy(info->fix.id, mi->name);
> +       info->fix.type = FB_TYPE_PACKED_PIXELS;
> +       info->fix.type_aux = 0;
> +       info->fix.xpanstep = 0;
> +       info->fix.ypanstep = info->var.yres;
> +       info->fix.ywrapstep = 0;
> +       info->fix.accel = FB_ACCEL_NONE;
> +       info->fix.smem_start = fbi->fb_start_dma;
> +       info->fix.smem_len = fbi->fb_size;
> +       info->fix.visual = (fbi->pix_fmt = PIXFMT_PSEUDOCOLOR) ?
> +               FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
> +       info->fix.line_length = info->var.xres_virtual *
> +               info->var.bits_per_pixel / 8;
> +       info->fbops = &mmpfb_gfx_ops;
> +       info->pseudo_palette = fbi->pseudo_palette;
> +       info->screen_base = fbi->fb_start;
> +       info->screen_size = fbi->fb_size;
> +
> +       /* For FB framework: Allocate color map and Register framebuffer*/
> +       if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
> +               ret = -ENOMEM;
> +               goto failed_free_buff;
> +       }
> +       ret = register_framebuffer(info);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
> +               ret = -ENXIO;
> +               goto failed_free_cmap;
> +       }
> +
> +       dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
> +               info->node, info->fix.id);
> +
> +#ifdef CONFIG_ANDROID
> +       if (fbi->fb_start) {
> +               fb_prepare_logo(info, 0);
> +               fb_show_logo(info, 0);
> +       }
> +#endif
> +
> +       return 0;
> +
> +failed_free_cmap:
> +       fb_dealloc_cmap(&info->cmap);
> +failed_free_buff:
> +       vfree(fbi->fb_start);
> +       kfree(videomodes);
> +failed_destroy_mutex:
> +       mutex_destroy(&fbi->access_ok);
> +failed:
> +       dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
> +       platform_set_drvdata(pdev, NULL);
> +
> +       framebuffer_release(info);
> +
> +       return ret;
> +}
> +
> +static struct platform_driver mmpfb_driver = {
> +       .driver         = {
> +               .name   = "mmp-fb",
> +               .owner  = THIS_MODULE,
> +       },
> +       .probe          = mmpfb_probe,
> +};
> +
> +static int __devinit mmpfb_init(void)
> +{
> +       return platform_driver_register(&mmpfb_driver);
> +}
> +module_init(mmpfb_init);
> +
> +MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
> +MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/video/mmp/fb/mmpfb.h b/drivers/video/mmp/fb/mmpfb.h
> new file mode 100644
> index 0000000..7f8a71d
> --- /dev/null
> +++ b/drivers/video/mmp/fb/mmpfb.h
> @@ -0,0 +1,51 @@
> +/*
> + * linux/drivers/video/mmp/fb/mmpfb.h
> + * Framebuffer driver for Marvell Display controller.
> + *
> + * Copyright (C) 2012 Marvell Technology Group Ltd.
> + * Authors: Zhou Zhu <zzhu3@marvell.com>
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef _MMP_FB_H_
> +#define _MMP_FB_H_
> +
> +#include <video/mmp_disp.h>
> +#include <linux/fb.h>
> +
> +/* LCD controller private state. */
> +struct mmpfb_info {
> +       struct device   *dev;
> +       int     id;
> +
> +       struct fb_info  *fb_info;
> +       /* basicaly videomode is for output */
> +       struct fb_videomode     mode;
> +       int     pix_fmt;
> +
> +       void    *fb_start;
> +       int     fb_size;
> +       dma_addr_t      fb_start_dma;
> +
> +       struct mmp_ovly *ovly;
> +       struct mmp_path *path;
> +
> +       struct mutex    access_ok;
> +
> +       unsigned int            pseudo_palette[16];
> +       int output_fmt;
> +};
> +#endif /* _MMP_FB_H_ */
> --
> 1.7.0.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v5 2/4] pwm_backlight: use power sequences
From: Stephen Warren @ 2012-09-05 17:25 UTC (permalink / raw)
  To: Alexandre Courbot
  Cc: Stephen Warren, Thierry Reding, Simon Glass, Grant Likely,
	Rob Herring, Mark Brown, Anton Vorontsov, David Woodhouse,
	Arnd Bergmann, linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	linux-pm-u79uwXL29TY76Z2rM5mHXA,
	devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1346412846-17102-3-git-send-email-acourbot-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

On 08/31/2012 05:34 AM, 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.

> +++ b/Documentation/devicetree/bindings/video/backlight/pwm-backlight.txt

>  Optional properties:
> -  - pwm-names: a list of names for the PWM devices specified in the
> -               "pwms" property (see PWM binding[0])
> +  - pwm-names: name for the PWM device specified in the "pwms" property (see PWM
> +      binding[0]). Necessary if power sequences are used

So this implies that power sequence are completely optional in the pwm
binding...

> diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig

>  config BACKLIGHT_PWM
>  	tristate "Generic PWM based Backlight Driver"
>  	depends on PWM
> +	select POWER_SEQ

... but that implies they're basically mandatory.

Briefly looking at the code, power sequences don't appear to be
optional, at least for the DT case, so perhaps you just need to update
the documentation to make "pwm-names" non-optional?

^ permalink raw reply

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

On 08/31/2012 05:34 AM, Alexandre Courbot wrote:
> Some device drivers (panel backlights especially) 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 board-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.

> +++ b/Documentation/devicetree/bindings/power_seq/power_seq.txt

> +Runtime Interpreted Power Sequences
> +=================> +
> +Power sequences are sequential descriptions of actions to be performed on
> +power-related resources. Having these descriptions in a precise data format
> +allows us to take much of the board-specific power control code out of the
> +kernel and place it into the device tree instead, making kernels less
> +board-dependant.
> +

> +In the device tree, power sequences are grouped into a set. The set is always
> +declared as the "power-sequences" sub-node of the device node. Power sequences
> +may reference resources declared by that device.

I had to read that a few times to realize this was talking about a
device with multiple power sequences, and not talking about the steps in
a sequence. I think if you add the following sentence at the start of
that paragraph, it will be clearer:

A device may support multiple power sequences, for different events such
as powering on and off.

Also, perhaps add "these" at the first comma, so the above would read:

In the device tree, these power sequences are...

> +Power Sequences Structure
> +-------------------------
> +Every device that makes use of power sequences must have a "power-sequences"
> +sub-node. Power sequences are sub-nodes of this set node, and their node name
> +indicates the id of the sequence.
> +
> +Every power sequence in turn contains its steps as sub-nodes of itself. Step

The last word on that line should be "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
> +---------------------
> +Step of a sequence describes an action to be performed on a resource. They

s/Step/Steps/, s/describes/describe/.

> +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_us: delay to wait in microseconds

DT property name should use "-" not "_" to separate words, so "delay-us".

> +"regulator" type required properties:
> +  - id: name of the regulator to use. Regulator is obtained by
> +        regulator_get(dev, id)
> +  - 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. PWM is obtained by pwm_get(dev, id)
> +  - enable / disable: one of these two empty properties must be present to
> +                      enable or disable the resource

For those two, would "name" be a better property name than "id"?

I wonder if we should have "regulator-enable" and "regulator-disable"
step types, rather than a "regulator" step type, with an "enable" or
"disable" property within it. I don't feel strongly though; this is
probably fine.

> +"gpio" type required properties:
> +  - number: phandle of the GPIO to use.

Naming the property "gpio" would seem more consistent with our GPIO
properties.

> +  - enable / disable: one of these two empty properties must be present to
> +                      enable or disable the resource

You can't really enable or disable a GPIO (well, perhaps you can, but
I'd consider that to affect tri-state rather than value); it's more that
you're setting the output value to 0 or 1. I think a "value" or
"set-value" property with value <0> or <1> would be better.

...
> +After the resources declaration, two sequences follow for powering the backlight
> +on and off. Their names are specified by the pwm-backlight driver.

Not the driver, but the binding for the device.

Overall, the general structure of the bindings looks reasonable to me.

> +++ b/Documentation/power/power_seq.txt

...
> +Platform Data Format
> +--------------------
> +All relevant data structures for declaring power sequences are located in
> +include/linux/power_seq.h.
> +
> +The platform data for a given device is an instance of platform_power_seq_set
> +which points to instances of platform_power_seq. Every platform_power_seq is a
> +single power sequence, and is itself composed of a variable length array of
> +steps.

I don't think you can mandate that the entire platform data structure
for a device is a platform_power_seq_set. Instead, I think you should
say that "The platform data for a device may contain an instance of
platform_power_seq_set...".

...
> +A power sequence can 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 occured.

s/occured/occurred/

> +Sometimes, you may want to browse the list of resources allocated by a sequence,
> +to for instance ensure that a resource of a given type is present. The
> +power_seq_set_resources() function returns a list head that can be used with
> +the power_seq_for_each_resource() macro to browse all the resources of a set:
> +
> +  struct list_head *power_seq_set_resources(struct power_seq_set *seqs);
> +  power_seq_for_each_resource(pos, seqs)
> +
> +Here "pos" will be of type struct power_seq_resource. This structure contains a
> +"pdata" pointer that can be used to explore the platform data of this resource,
> +as well as the resolved resource, if applicable.

I'm not sure what "pdata" is supposed to point at; platform data applies
to the original "struct device", not one of the resources used by the
power sequences.

> +Finally, users of the device tree can build the platform data corresponding to
> +the tree node using this function:
> +
> +  struct platform_power_seq_set *devm_of_parse_power_seq_set(struct device *dev);

Hmmm. It'd be nice not to need separate functions for the non-DT and DT
cases. That would require that devm_power_seq_set_build() be able to
find the power sequence definitions somewhere other than platform data
in the non-DT case - that's exactly why the regulator and pinctrl
subsystems represent the device<->data mapping table separately from the
device's platform data.

> +++ b/drivers/power/power_seq/Kconfig

> +config POWER_SEQ
> +	bool

Some kind of help text might be useful?

I didn't review any of the .c files.

> +++ b/include/linux/power_seq.h

> +/**
> + * struct platform_power_seq_step - platform data for power sequences steps
> + * @type:	The type of this step. This decides which member of the union is
> + *		valid for this step.
> + * @delay:	Used if type = POWER_SEQ_DELAY
> + * @regulator:	Used if type = POWER_SEQ_REGULATOR
> + * @pwm:	Used if type = POWER_SEQ_PWN
> + * @gpio:	Used if type = POWER_SEQ_GPIO

In those last 4 line, I think s/type/@type/ since you're referencing
another parameter?

> +struct power_seq_resource {
> +	/* relevant for resolving the resource and knowing its type */
> +	struct platform_power_seq_step *pdata;

Aha. So this isn't really platform data for the resource, but actually a
step definition that referenced it. I think it'd be better to rename
this field "step", and amend the documentation above not to refer to
"pdata" but explicitly talk about a step definition; the step may have
been defined in pdata, but isn't in the DT case.

Alternatively, why not just copy the step type enum here, rather than
referencing the step definition?


^ permalink raw reply

* Re: [PATCH 13/17] Revert "OMAPDSS: APPLY: add fifo-merge support"
From: Archit Taneja @ 2012-09-05 13:55 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346833555-31258-14-git-send-email-tomi.valkeinen@ti.com>

On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> This reverts commit 1d71f42b35ed66d90a9a39bc515bb16cfe2d4a46.
>
> Adding fifo merge feature as an omapdss internal configuration was a
> mistake. We cannot hide from the users of omapdss the complexities of
> fifo merge.
>
> This commit removes the fifo merge support, which luckily is easily done
> as it was handled totally inside apply.c.
>
> The plan is to try fifo merge again later when it is more clear how the
> hardware acts in various situations, and how the omapdrm wants to use
> fifo merge.

Nitpick: This isn't an exact revert right? Maybe we should mention that, 
so that if someone tries to apply the old patch, it doesn't fail.

>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>   drivers/video/omap2/dss/apply.c |  154 ++-------------------------------------
>   1 file changed, 8 insertions(+), 146 deletions(-)
>
> diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
> index 1b49019..02943a5 100644
> --- a/drivers/video/omap2/dss/apply.c
> +++ b/drivers/video/omap2/dss/apply.c
> @@ -993,11 +993,11 @@ static void dss_apply_fifo_merge(bool use_fifo_merge)
>   	dss_data.fifo_merge_dirty = true;
>   }
>
> -static void dss_ovl_setup_fifo(struct omap_overlay *ovl,
> -		bool use_fifo_merge)
> +static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
>   {
>   	struct ovl_priv_data *op = get_ovl_priv(ovl);
>   	u32 fifo_low, fifo_high;
> +	bool use_fifo_merge = false;

This looks like the line which wasn't removed in the original commit.

Archit


^ permalink raw reply

* Re: [PATCH 15/17] OMAPDSS: remove extra_info completion code
From: Archit Taneja @ 2012-09-05 13:43 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346833555-31258-16-git-send-email-tomi.valkeinen@ti.com>

On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> Now that fifo merge has been removed, nobody uses the extra_info related
> completion code, which can be removed.

I think this might come into use when we fix the usage of channel out 
field. That is, since channel out is an immediate write field, when we 
set a new manager for an overlay, we may need to wait till the overlay 
disable kicks in, only then we should change channel out.

For this, we would need some wait for extra_info, right?

Archit

>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>   drivers/video/omap2/dss/apply.c |   69 ---------------------------------------
>   1 file changed, 69 deletions(-)
>
> diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
> index 32b5379..2579d15 100644
> --- a/drivers/video/omap2/dss/apply.c
> +++ b/drivers/video/omap2/dss/apply.c
> @@ -118,7 +118,6 @@ static struct {
>   static spinlock_t data_lock;
>   /* lock for blocking functions */
>   static DEFINE_MUTEX(apply_lock);
> -static DECLARE_COMPLETION(extra_updated_completion);
>
>   static void dss_register_vsync_isr(void);
>
> @@ -355,69 +354,6 @@ static bool need_go(struct omap_overlay_manager *mgr)
>   	return false;
>   }
>
> -/* returns true if an extra_info field is currently being updated */
> -static bool extra_info_update_ongoing(void)
> -{
> -	const int num_mgrs = dss_feat_get_num_mgrs();
> -	int i;
> -
> -	for (i = 0; i < num_mgrs; ++i) {
> -		struct omap_overlay_manager *mgr;
> -		struct omap_overlay *ovl;
> -		struct mgr_priv_data *mp;
> -
> -		mgr = omap_dss_get_overlay_manager(i);
> -		mp = get_mgr_priv(mgr);
> -
> -		if (!mp->enabled)
> -			continue;
> -
> -		if (!mp->updating)
> -			continue;
> -
> -		if (mp->extra_info_dirty || mp->shadow_extra_info_dirty)
> -			return true;
> -
> -		list_for_each_entry(ovl, &mgr->overlays, list) {
> -			struct ovl_priv_data *op = get_ovl_priv(ovl);
> -
> -			if (op->extra_info_dirty || op->shadow_extra_info_dirty)
> -				return true;
> -		}
> -	}
> -
> -	return false;
> -}
> -
> -/* wait until no extra_info updates are pending */
> -static void wait_pending_extra_info_updates(void)
> -{
> -	bool updating;
> -	unsigned long flags;
> -	unsigned long t;
> -	int r;
> -
> -	spin_lock_irqsave(&data_lock, flags);
> -
> -	updating = extra_info_update_ongoing();
> -
> -	if (!updating) {
> -		spin_unlock_irqrestore(&data_lock, flags);
> -		return;
> -	}
> -
> -	init_completion(&extra_updated_completion);
> -
> -	spin_unlock_irqrestore(&data_lock, flags);
> -
> -	t = msecs_to_jiffies(500);
> -	r = wait_for_completion_timeout(&extra_updated_completion, t);
> -	if (r = 0)
> -		DSSWARN("timeout in wait_pending_extra_info_updates\n");
> -	else if (r < 0)
> -		DSSERR("wait_pending_extra_info_updates failed: %d\n", r);
> -}
> -
>   int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
>   {
>   	unsigned long timeout = msecs_to_jiffies(500);
> @@ -823,7 +759,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
>   {
>   	const int num_mgrs = dss_feat_get_num_mgrs();
>   	int i;
> -	bool extra_updating;
>
>   	spin_lock(&data_lock);
>
> @@ -854,10 +789,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
>   	dss_write_regs();
>   	dss_set_go_bits();
>
> -	extra_updating = extra_info_update_ongoing();
> -	if (!extra_updating)
> -		complete_all(&extra_updated_completion);
> -
>   	if (!need_isr())
>   		dss_unregister_vsync_isr();
>
>


^ permalink raw reply

* Re: [PATCH 12/17] OMAPDSS: clean up dss_mgr_set_timings
From: Archit Taneja @ 2012-09-05 11:48 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346845276.32747.11.camel@deskari>

On Wednesday 05 September 2012 05:11 PM, Tomi Valkeinen wrote:
> On Wed, 2012-09-05 at 17:04 +0530, Archit Taneja wrote:
>> On Wednesday 05 September 2012 04:11 PM, Tomi Valkeinen wrote:
>>> On Wed, 2012-09-05 at 14:45 +0530, Archit Taneja wrote:
>>>> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
>>>>> dss_mgr_set_timings() can only be called when the output is not active.
>>>>> This means that most of the code in the function is extra, as there's no
>>>>> need to write the values to registers, etc, because that will be handled
>>>>> when the output will be enabled.
>>>>
>>>> We need to fix dpi_set_timings() before we can make this change. DPI
>>>> still tries to change timings on the fly, i.e, with manager enabled. We
>>>> need to disable and enable the DPI output like we do for other outputs.
>>>
>>> Yep, and for HDMI also (I didn't check the others yet).
>>
>> I don't think HDMI is impacted, we do the full power off and power on
>> for HDMI, so the manager would be disabled when we set the timings.
>
> Ah right.
>
>>> I think the simplest way to handle this is to only write the dpi.timings
>>> in omapdss_dpi_set_timings, and remove the call to dss_mgr_set_timings.
>>> This is not perfect, as a call to omapdss_dpi_set_timings when the
>>> display is enabled would result in changing the dpi.timings, but the
>>> changes wouldn't be actually in use.
>>
>> The simplest way would be to do what other outputs do, disable the
>> output and re-enable the output with the new timings value, if the panel
>> is enabled.
>
> Not quite, as there's the mutex in dpi so we can't call enable/disable
> from set_timings. I could create separate non-locked internal functions
> for enable and disable, but that feels more complex than just removing
> the enable & disable from set_timings.

Okay, right.

>
> In the end we'll anyway only allow changing timings when the output is
> disabled.
>
> The only change I had to do, in addition to removing code from
> set_timings functions, was to add display disable & enable calls to the
> "timings" sysfs write. omapfb already only calls set_timings when the
> output is disabled, and omapdrm does the same.

Okay, that sounds good then.

Archit


^ permalink raw reply

* Re: [PATCH 12/17] OMAPDSS: clean up dss_mgr_set_timings
From: Archit Taneja @ 2012-09-05 11:46 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346841713.32747.6.camel@deskari>

On Wednesday 05 September 2012 04:11 PM, Tomi Valkeinen wrote:
> On Wed, 2012-09-05 at 14:45 +0530, Archit Taneja wrote:
>> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
>>> dss_mgr_set_timings() can only be called when the output is not active.
>>> This means that most of the code in the function is extra, as there's no
>>> need to write the values to registers, etc, because that will be handled
>>> when the output will be enabled.
>>
>> We need to fix dpi_set_timings() before we can make this change. DPI
>> still tries to change timings on the fly, i.e, with manager enabled. We
>> need to disable and enable the DPI output like we do for other outputs.
>
> Yep, and for HDMI also (I didn't check the others yet).

I don't think HDMI is impacted, we do the full power off and power on 
for HDMI, so the manager would be disabled when we set the timings.

>
> I think the simplest way to handle this is to only write the dpi.timings
> in omapdss_dpi_set_timings, and remove the call to dss_mgr_set_timings.
> This is not perfect, as a call to omapdss_dpi_set_timings when the
> display is enabled would result in changing the dpi.timings, but the
> changes wouldn't be actually in use.

The simplest way would be to do what other outputs do, disable the 
output and re-enable the output with the new timings value, if the panel 
is enabled.

Archit


^ permalink raw reply

* Re: [PATCH 12/17] OMAPDSS: clean up dss_mgr_set_timings
From: Tomi Valkeinen @ 2012-09-05 11:41 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <504738C4.7060005@ti.com>

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

On Wed, 2012-09-05 at 17:04 +0530, Archit Taneja wrote:
> On Wednesday 05 September 2012 04:11 PM, Tomi Valkeinen wrote:
> > On Wed, 2012-09-05 at 14:45 +0530, Archit Taneja wrote:
> >> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> >>> dss_mgr_set_timings() can only be called when the output is not active.
> >>> This means that most of the code in the function is extra, as there's no
> >>> need to write the values to registers, etc, because that will be handled
> >>> when the output will be enabled.
> >>
> >> We need to fix dpi_set_timings() before we can make this change. DPI
> >> still tries to change timings on the fly, i.e, with manager enabled. We
> >> need to disable and enable the DPI output like we do for other outputs.
> >
> > Yep, and for HDMI also (I didn't check the others yet).
> 
> I don't think HDMI is impacted, we do the full power off and power on 
> for HDMI, so the manager would be disabled when we set the timings.

Ah right.

> > I think the simplest way to handle this is to only write the dpi.timings
> > in omapdss_dpi_set_timings, and remove the call to dss_mgr_set_timings.
> > This is not perfect, as a call to omapdss_dpi_set_timings when the
> > display is enabled would result in changing the dpi.timings, but the
> > changes wouldn't be actually in use.
> 
> The simplest way would be to do what other outputs do, disable the 
> output and re-enable the output with the new timings value, if the panel 
> is enabled.

Not quite, as there's the mutex in dpi so we can't call enable/disable
from set_timings. I could create separate non-locked internal functions
for enable and disable, but that feels more complex than just removing
the enable & disable from set_timings.

In the end we'll anyway only allow changing timings when the output is
disabled.

The only change I had to do, in addition to removing code from
set_timings functions, was to add display disable & enable calls to the
"timings" sysfs write. omapfb already only calls set_timings when the
output is disabled, and omapdrm does the same.

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 12/17] OMAPDSS: clean up dss_mgr_set_timings
From: Tomi Valkeinen @ 2012-09-05 10:41 UTC (permalink / raw)
  To: Archit Taneja; +Cc: linux-omap, linux-fbdev
In-Reply-To: <5047182F.3030507@ti.com>

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

On Wed, 2012-09-05 at 14:45 +0530, Archit Taneja wrote:
> On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> > dss_mgr_set_timings() can only be called when the output is not active.
> > This means that most of the code in the function is extra, as there's no
> > need to write the values to registers, etc, because that will be handled
> > when the output will be enabled.
> 
> We need to fix dpi_set_timings() before we can make this change. DPI 
> still tries to change timings on the fly, i.e, with manager enabled. We 
> need to disable and enable the DPI output like we do for other outputs.

Yep, and for HDMI also (I didn't check the others yet).

I think the simplest way to handle this is to only write the dpi.timings
in omapdss_dpi_set_timings, and remove the call to dss_mgr_set_timings.
This is not perfect, as a call to omapdss_dpi_set_timings when the
display is enabled would result in changing the dpi.timings, but the
changes wouldn't be actually in use.

However, there are only a few places where set_timings is called, and we
can make sure in those places to first disable the output and then
enable it again.

 Tomi


[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 0/6] HID: picoLCD additional fixes + CIR support
From: Jiri Kosina @ 2012-09-05  9:50 UTC (permalink / raw)
  To: Bruno Prémont
  Cc: linux-input, linux-kernel, Mauro Carvalho Chehab, linux-fbdev,
	linux-media
In-Reply-To: <20120819192859.74136bb0@neptune.home>

On Sun, 19 Aug 2012, Bruno Prémont wrote:

> This series fixes some race conditions in picoLCD driver during remove()
> and adds support for IR functionality.

I have now applied the series, thanks Bruno.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* [PATCH 2/2] ARM: dts: mxs: add oled support for the cfa-10036
From: Maxime Ripard @ 2012-09-05  9:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1346837733-7264-1-git-send-email-maxime.ripard@free-electrons.com>

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Brian Lilly <brian@crystalfontz.com>
---
 arch/arm/boot/dts/imx28-cfa10036.dts |   20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index c03a577..e5ee46e 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -33,11 +33,31 @@
 		};
 
 		apbx@80040000 {
+			pwm: pwm@80064000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pwm4_pins_a>;
+				status = "okay";
+			};
+
 			duart: serial@80074000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&duart_pins_b>;
 				status = "okay";
 			};
+
+			i2c0: i2c@80058000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&i2c0_pins_b>;
+				status = "okay";
+
+				ssd1307: oled@3c {
+					compatible = "solomon,ssd1307fb-i2c";
+					reg = <0x3c>;
+					pwms = <&pwm 4 3000>;
+					reset-gpios = <&gpio2 7 1>;
+					reset-active-low;
+				};
+			};
 		};
 	};
 
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/2] video: Add support for the Solomon SSD1307 OLED Controller
From: Maxime Ripard @ 2012-09-05  9:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1346837733-7264-1-git-send-email-maxime.ripard@free-electrons.com>

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>
---
 .../devicetree/bindings/video/ssd1307fb.txt        |   24 ++
 drivers/video/Kconfig                              |   13 +
 drivers/video/Makefile                             |    1 +
 drivers/video/ssd1307fb.c                          |  386 ++++++++++++++++++++
 4 files changed, 424 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt
 create mode 100644 drivers/video/ssd1307fb.c

diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
new file mode 100644
index 0000000..3d0060c
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -0,0 +1,24 @@
+* Solomon SSD1307 Framebuffer Driver
+
+Required properties:
+  - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
+    now is i2c.
+  - reg: Should contain address of the controller on the I2C bus. Most likely
+         0x3c or 0x3d
+  - pwm: Should contain the pwm to use according to the OF device tree PWM
+         specification [0]
+  - reset-gpios: Should contain the GPIO used to reset the OLED display
+
+Optional properties:
+  - reset-active-low: Is the reset gpio is active on physical low?
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Examples:
+ssd1307: oled@3c {
+        compatible = "solomon,ssd1307fb-i2c";
+        reg = <0x3c>;
+        pwms = <&pwm 4 3000>;
+        reset-gpios = <&gpio2 7>;
+        reset-active-low;
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f74..2776829 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2469,4 +2469,17 @@ config FB_SH_MOBILE_MERAM
 	  Up to 4 memory channels can be configured, allowing 4 RGB or
 	  2 YCbCr framebuffers to be configured.
 
+config FB_SSD1307
+	tristate "Solomon SSD1307 framebuffer support"
+	depends on FB && I2C
+	select FB_SYS_FOPS
+	select FB_SYS_FILLRECT
+	select FB_SYS_COPYAREA
+	select FB_SYS_IMAGEBLIT
+	select FB_DEFERRED_IO
+	select PWM
+	help
+	  This driver implements support for the Solomon SSD1307
+	  OLED controller over I2C.
+
 endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb..6bbb72c 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -164,6 +164,7 @@ obj-$(CONFIG_FB_BFIN_7393)        += bfin_adv7393fb.o
 obj-$(CONFIG_FB_MX3)		  += mx3fb.o
 obj-$(CONFIG_FB_DA8XX)		  += da8xx-fb.o
 obj-$(CONFIG_FB_MXS)		  += mxsfb.o
+obj-$(CONFIG_FB_SSD1307)	  += ssd1307fb.o
 
 # the test framebuffer is last
 obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
new file mode 100644
index 0000000..fc77a8f
--- /dev/null
+++ b/drivers/video/ssd1307fb.c
@@ -0,0 +1,386 @@
+/*
+ * Driver for the Solomon SSD1307 OLED controler
+ *
+ * Copyright 2012 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/uaccess.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#define SSD1307FB_WIDTH			96
+#define SSD1307FB_HEIGHT		16
+
+#define SSD1307FB_DATA			0x40
+#define SSD1307FB_COMMAND		0x80
+
+#define SSD1307FB_CONTRAST		0x81
+#define SSD1307FB_SEG_REMAP_ON		0xa1
+#define SSD1307FB_DISPLAY_OFF		0xae
+#define SSD1307FB_DISPLAY_ON		0xaf
+#define SSD1307FB_START_PAGE_ADDRESS	0xb0
+
+struct ssd1307fb_par {
+	struct i2c_client *client;
+	struct fb_info *info;
+	struct pwm_device *pwm;
+	u32 pwm_period;
+	int reset;
+};
+
+static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = {
+	.id		= "Solomon SSD1307",
+	.type		= FB_TYPE_PACKED_PIXELS,
+	.visual		= FB_VISUAL_MONO10,
+	.xpanstep	= 0,
+	.ypanstep	= 0,
+	.ywrapstep	= 0,
+	.line_length	= SSD1307FB_WIDTH / 8,
+	.accel		= FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo ssd1307fb_var __devinitdata = {
+	.xres		= SSD1307FB_WIDTH,
+	.yres		= SSD1307FB_HEIGHT,
+	.xres_virtual	= SSD1307FB_WIDTH,
+	.yres_virtual	= SSD1307FB_HEIGHT,
+	.bits_per_pixel	= 1,
+};
+
+static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8* cmd, u32 len)
+{
+	u8 *buf;
+	int ret = 0;
+
+	buf = kzalloc(len + 1, GFP_KERNEL);
+	if (!buf) {
+		dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
+		return -ENOMEM;
+	}
+
+	buf[0] = type;
+	memcpy(buf + 1, cmd, len);
+
+	ret = i2c_master_send(client, buf, len + 1);
+	if (ret != len + 1) {
+		dev_err(&client->dev, "Couldn't send I2C command.\n");
+		goto error;
+	}
+
+error:
+	kfree(buf);
+	return ret;
+}
+
+static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+	return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+}
+
+static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
+{
+	return ssd1307fb_write_cmd_array(client, &cmd, 1);
+}
+
+static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+	return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+}
+
+static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
+{
+	return ssd1307fb_write_data_array(client, &data, 1);
+}
+
+static void ssd1307fb_update_display(struct ssd1307fb_par *par)
+{
+	u8 *vmem = par->info->screen_base;
+	int i, j, k;
+
+	/*
+	 * The screen is divided in pages, each having a height of 8
+	 * pixels, and the width of the screen. When sending a byte of
+	 * data to the controller, it gives the 8 bits for the current
+	 * column. I.e, the first byte are the 8 bits of the first
+	 * column, then the 8 bits for the second column, etc.
+	 *
+	 *
+	 * Representation of the screen, assuming it is 5 bits
+	 * wide. Each letter-number combination is a bit that controls
+	 * one pixel.
+	 *
+	 * A0 A1 A2 A3 A4
+	 * B0 B1 B2 B3 B4
+	 * C0 C1 C2 C3 C4
+	 * D0 D1 D2 D3 D4
+	 * E0 E1 E2 E3 E4
+	 * F0 F1 F2 F3 F4
+	 * G0 G1 G2 G3 G4
+	 * H0 H1 H2 H3 H4
+	 *
+	 * If you want to update this screen, you need to send 5 bytes:
+	 *  (1) A0 B0 C0 D0 E0 F0 G0 H0
+	 *  (2) A1 B1 C1 D1 E1 F1 G1 H1
+	 *  (3) A2 B2 C2 D2 E2 F2 G2 H2
+	 *  (4) A3 B3 C3 D3 E3 F3 G3 H3
+	 *  (5) A4 B4 C4 D4 E4 F4 G4 H4
+	 */
+
+	for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
+		ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+		ssd1307fb_write_cmd(par->client, 0x00);
+		ssd1307fb_write_cmd(par->client, 0x10);
+
+		for (j = 0; j < SSD1307FB_WIDTH; j++) {
+			u8 buf = 0;
+			for (k = 0; k < 8; k++) {
+				u32 page_length = SSD1307FB_WIDTH * i;
+				u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+				u8 byte = *(vmem + index);
+				u8 bit = byte & (1 << (7 - (j % 8)));
+				bit = bit >> (7 - (j % 8));
+				buf |= bit << k;
+			}
+			ssd1307fb_write_data(par->client, buf);
+		}
+	}
+}
+
+
+static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct ssd1307fb_par *par = info->par;
+	unsigned long p = *ppos;
+	void *dst;
+	int err = 0;
+
+	dst = (void __force *) (info->screen_base + p);
+
+	if (copy_from_user(dst, buf, count))
+		err = -EFAULT;
+
+	if  (!err)
+		*ppos += count;
+
+	ssd1307fb_update_display(par);
+
+	return (err) ? err : count;
+}
+
+static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_fillrect(info, rect);
+	ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_copyarea(info, area);
+	ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) 
+{
+	struct ssd1307fb_par *par = info->par;
+	sys_imageblit(info, image);
+	ssd1307fb_update_display(par);
+}
+
+static struct fb_ops ssd1307fb_ops = {
+	.owner		= THIS_MODULE,
+	.fb_read	= fb_sys_read,
+	.fb_write	= ssd1307fb_write,
+	.fb_fillrect	= ssd1307fb_fillrect,
+	.fb_copyarea	= ssd1307fb_copyarea,
+	.fb_imageblit	= ssd1307fb_imageblit,
+};
+
+static void ssd1307fb_deferred_io(struct fb_info *info,
+				struct list_head *pagelist)
+{
+	ssd1307fb_update_display(info->par);
+}
+
+static struct fb_deferred_io ssd1307fb_defio = {
+	.delay		= HZ,
+	.deferred_io	= ssd1307fb_deferred_io,
+};
+
+static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct fb_info *info;
+	u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+	struct ssd1307fb_par *par;
+	u8 *vmem;
+	int ret;
+
+	if (!client->dev.of_node) {
+		dev_err(&client->dev, "No device tree data found!\n");
+		return -EINVAL;
+	}
+
+	info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
+	if (!info) {
+		dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
+		return -ENOMEM;
+	}
+
+	vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+	if (!vmem) {
+		dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
+		ret = -ENOMEM;
+		goto fb_alloc_error;
+	}
+
+	info->fbops = &ssd1307fb_ops;
+	info->fix = ssd1307fb_fix;
+	info->fbdefio = &ssd1307fb_defio;
+
+	info->var = ssd1307fb_var;
+	info->var.red.length = 1;
+	info->var.red.offset = 0;
+	info->var.green.length = 1;
+	info->var.green.offset = 0;
+	info->var.blue.length = 1;
+	info->var.blue.offset = 0;
+
+	info->screen_base = (u8 __force __iomem *)vmem;
+	info->fix.smem_start = (unsigned long)vmem;
+	info->fix.smem_len = vmem_size;
+
+	fb_deferred_io_init(info);
+
+	par = info->par;
+	par->info = info;
+	par->client = client;
+
+	par->reset = of_get_named_gpio(client->dev.of_node,
+					 "reset-gpios", 0);
+	if (gpio_is_valid(par->reset)) {
+		int flags = GPIOF_OUT_INIT_HIGH;
+		if (of_get_property(client->dev.of_node,
+				    "reset-active-low", NULL))
+			flags = GPIOF_OUT_INIT_LOW;
+		ret = devm_gpio_request_one(&client->dev, par->reset,
+					    flags, "oled-reset");
+		if (ret) {
+			dev_err(&client->dev,
+				"failed to request gpio %d: %d\n",
+				par->reset, ret);
+			goto reset_oled_error;
+		}
+	}
+
+	par->pwm = pwm_get(&client->dev, NULL);
+	if (IS_ERR(par->pwm)) {
+		dev_err(&client->dev, "Could not get PWM from device tree!\n");
+		ret = PTR_ERR(par->pwm);
+		goto pwm_error;
+	}
+
+	par->pwm_period = pwm_get_period(par->pwm);
+
+	dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
+
+	ret = register_framebuffer(info);
+	if (ret) {
+		dev_err(&client->dev, "Couldn't register the framebuffer\n");
+		goto fbreg_error;
+	}
+
+	i2c_set_clientdata(client, info);
+
+	/* Reset the screen */
+	gpio_set_value(par->reset, 1);
+	udelay(4);
+	gpio_set_value(par->reset, 0);
+	udelay(4);
+
+	/* Enable the PWM */
+	pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+	pwm_enable(par->pwm);
+
+	/* Map column 127 of the OLED to segment 0 */
+	ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
+	if (ret < 0) {
+		dev_err(&client->dev, "Couldn't remap the screen.\n");
+		goto remap_error;
+	}
+
+	/* Turn on the display */
+	ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
+	if (ret < 0) {
+		dev_err(&client->dev, "Couldn't turn the display on.\n");
+		goto remap_error;
+	}
+
+	dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+	return 0;
+
+remap_error:
+	unregister_framebuffer(info);
+	pwm_disable(par->pwm);
+fbreg_error:
+	pwm_put(par->pwm);
+pwm_error:
+reset_oled_error:
+	fb_deferred_io_cleanup(info);
+fb_alloc_error:
+	framebuffer_release(info);
+	return ret;
+}
+
+static int __devexit ssd1307fb_remove(struct i2c_client *client)
+{
+	struct fb_info *info = i2c_get_clientdata(client);
+	struct ssd1307fb_par *par = info->par;
+
+	unregister_framebuffer(info);
+	pwm_disable(par->pwm);
+	pwm_put(par->pwm);
+	fb_deferred_io_cleanup(info);
+	framebuffer_release(info);
+
+	return 0;
+}
+
+static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+	{ "ssd1307fb", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+	{ .compatible = "solomon,ssd1307fb-i2c" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
+static struct i2c_driver ssd1307fb_driver = {
+	.probe = ssd1307fb_probe,
+	.remove = __devexit_p(ssd1307fb_remove),
+	.id_table = ssd1307fb_i2c_id,
+	.driver = {
+		.name = "ssd1307fb",
+		.of_match_table = of_match_ptr(ssd1307fb_of_match),
+		.owner = THIS_MODULE,
+	},
+};
+
+module_i2c_driver(ssd1307fb_driver);
+
+MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


^ permalink raw reply related

* [PATCHv4] Add support for the OLED in the CFA10036
From: Maxime Ripard @ 2012-09-05  9:35 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

This patchset adds support for the solomon SSD1307 OLED controller present
in the CFA-10036 board.

It first adds the framebuffer driver for this controller, and then the
needed bits to enable it in the cfa10036 dts.

Considering that the driver has been around since almost two months now, and
I have not received any comments on the framebuffer part, I'd very much like it
to be included in 3.7 if possible.

Thanks,
Maxime

Changes from v3:
  * Changed the dt property name to remove the leading oled- and changed the
    reset-gpio property to reset-gpios

Changes from v2:
  * Add the pin name as comment to the pwm4 pinctrl declaration
  * Change slightly the documentation of the dt bindings for the gpios to remove
    the mxs-specific cell.

Changes from v1:
  * Did some factorisation of the ssd1307fb_write* functions.
  * Filled the smem_start field of the fb_fix_screeninfo structure
  * Fixed the broken pwm declaration
  * Change the names of oled-reset-gpios and reset-active-low properties in the
    device tree to oled-reset-gpio and oled-reset-active-low for consistency


^ permalink raw reply

* Re: [PATCH 12/17] OMAPDSS: clean up dss_mgr_set_timings
From: Archit Taneja @ 2012-09-05  9:27 UTC (permalink / raw)
  To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev
In-Reply-To: <1346833555-31258-13-git-send-email-tomi.valkeinen@ti.com>

On Wednesday 05 September 2012 01:55 PM, Tomi Valkeinen wrote:
> dss_mgr_set_timings() can only be called when the output is not active.
> This means that most of the code in the function is extra, as there's no
> need to write the values to registers, etc, because that will be handled
> when the output will be enabled.

We need to fix dpi_set_timings() before we can make this change. DPI 
still tries to change timings on the fly, i.e, with manager enabled. We 
need to disable and enable the DPI output like we do for other outputs.

Archit

>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
>   drivers/video/omap2/dss/apply.c |   18 ++++++++----------
>   1 file changed, 8 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
> index 53629dd..1b49019 100644
> --- a/drivers/video/omap2/dss/apply.c
> +++ b/drivers/video/omap2/dss/apply.c
> @@ -1314,21 +1314,19 @@ void dss_mgr_set_timings(struct omap_overlay_manager *mgr,
>   		const struct omap_video_timings *timings)
>   {
>   	unsigned long flags;
> -
> -	mutex_lock(&apply_lock);
> +	struct mgr_priv_data *mp = get_mgr_priv(mgr);
>
>   	spin_lock_irqsave(&data_lock, flags);
>
> -	dss_apply_mgr_timings(mgr, timings);
> -
> -	dss_write_regs();
> -	dss_set_go_bits();
> +	if (mp->enabled) {
> +		DSSERR("cannot set timings for %s: manager needs to be disabled\n",
> +			mgr->name);
> +		goto out;
> +	}
>
> +	dss_apply_mgr_timings(mgr, timings);
> +out:
>   	spin_unlock_irqrestore(&data_lock, flags);
> -
> -	wait_pending_extra_info_updates();
> -
> -	mutex_unlock(&apply_lock);
>   }
>
>   static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr,
>


^ permalink raw reply

* [PATCH 17/17] OMAPDSS: Use WB fifo for GFX overlay
From: Tomi Valkeinen @ 2012-09-05  8:25 UTC (permalink / raw)
  To: linux-omap, linux-fbdev, archit; +Cc: Tomi Valkeinen
In-Reply-To: <1346833555-31258-1-git-send-email-tomi.valkeinen@ti.com>

OMAP4's GFX overlay has smaller fifo than the rest of the overlays
(including writeback "overlay"). This seems to be the reason for
underflows in some more demanding scenarios.

We can avoid the problems by using the WB fifo for GFX overlay, and vice
versa. WB usage is not supported yet, but when it will, it should
perform just fine with smaller fifo as there are no hard realtime
constraints with WB.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/dispc.c |   27 +++++++++++++++++++++++++++
 drivers/video/omap2/dss/dispc.h |    4 ++++
 include/video/omapdss.h         |    1 +
 3 files changed, 32 insertions(+)

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index dc0f372..d512c38 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -95,6 +95,9 @@ struct dispc_features {
 	unsigned long (*calc_core_clk) (enum omap_channel channel,
 		u16 width, u16 height, u16 out_width, u16 out_height);
 	u8 num_fifos;
+
+	/* swap GFX & WB fifos */
+	bool gfx_fifo_workaround:1;
 };
 
 #define DISPC_MAX_NR_FIFOS 5
@@ -1088,6 +1091,29 @@ static void dispc_init_fifos(void)
 		 */
 		dispc.fifo_assignment[fifo] = fifo;
 	}
+
+	/*
+	 * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo
+	 * causes problems with certain use cases, like using the tiler in 2D
+	 * mode. The below hack swaps the fifos of GFX and WB planes, thus
+	 * giving GFX plane a larger fifo. WB but should work fine with a
+	 * smaller fifo.
+	 */
+	if (dispc.feat->gfx_fifo_workaround) {
+		u32 v;
+
+		v = dispc_read_reg(DISPC_GLOBAL_BUFFER);
+
+		v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
+		v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
+		v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
+		v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */
+
+		dispc_write_reg(DISPC_GLOBAL_BUFFER, v);
+
+		dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB;
+		dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX;
+	}
 }
 
 static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
@@ -3780,6 +3806,7 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
 	.calc_scaling		=	dispc_ovl_calc_scaling_44xx,
 	.calc_core_clk		=	calc_core_clk_44xx,
 	.num_fifos		=	5,
+	.gfx_fifo_workaround	=	true,
 };
 
 static int __init dispc_init_features(struct device *dev)
diff --git a/drivers/video/omap2/dss/dispc.h b/drivers/video/omap2/dss/dispc.h
index 92d8a9b..42e56cc 100644
--- a/drivers/video/omap2/dss/dispc.h
+++ b/drivers/video/omap2/dss/dispc.h
@@ -36,6 +36,7 @@
 #define DISPC_CONTROL2			0x0238
 #define DISPC_CONFIG2			0x0620
 #define DISPC_DIVISOR			0x0804
+#define DISPC_GLOBAL_BUFFER		0x0800
 #define DISPC_CONTROL3                  0x0848
 #define DISPC_CONFIG3                   0x084C
 
@@ -355,6 +356,8 @@ static inline u16 DISPC_OVL_BASE(enum omap_plane plane)
 		return 0x014C;
 	case OMAP_DSS_VIDEO3:
 		return 0x0300;
+	case OMAP_DSS_WB:
+		return 0x0500;
 	default:
 		BUG();
 		return 0;
@@ -517,6 +520,7 @@ static inline u16 DISPC_FIFO_SIZE_STATUS_OFFSET(enum omap_plane plane)
 	case OMAP_DSS_VIDEO2:
 		return 0x0018;
 	case OMAP_DSS_VIDEO3:
+	case OMAP_DSS_WB:
 		return 0x0088;
 	default:
 		BUG();
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 24a7fa1..ac2e4cc 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -73,6 +73,7 @@ enum omap_plane {
 	OMAP_DSS_VIDEO1	= 1,
 	OMAP_DSS_VIDEO2	= 2,
 	OMAP_DSS_VIDEO3	= 3,
+	OMAP_DSS_WB	= 4,
 };
 
 enum omap_channel {
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 16/17] OMAPDSS: Improve fifo management code
From: Tomi Valkeinen @ 2012-09-05  8:25 UTC (permalink / raw)
  To: linux-omap, linux-fbdev, archit; +Cc: Tomi Valkeinen
In-Reply-To: <1346833555-31258-1-git-send-email-tomi.valkeinen@ti.com>

OMAP4+ allows assigning the overlay FIFOs freely, but that is not
supported by omapdss yet. This patch takes a step forward by improving
the fifo management to be more flexible.

dispc.c is changed to keep track of the sizes of each fifo, and also the
overlay using each fifo.

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

diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index e934abb..dc0f372 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -94,8 +94,11 @@ struct dispc_features {
 		u16 pos_x, unsigned long *core_clk);
 	unsigned long (*calc_core_clk) (enum omap_channel channel,
 		u16 width, u16 height, u16 out_width, u16 out_height);
+	u8 num_fifos;
 };
 
+#define DISPC_MAX_NR_FIFOS 5
+
 static struct {
 	struct platform_device *pdev;
 	void __iomem    *base;
@@ -105,7 +108,9 @@ static struct {
 	int irq;
 	struct clk *dss_clk;
 
-	u32	fifo_size[MAX_DSS_OVERLAYS];
+	u32 fifo_size[DISPC_MAX_NR_FIFOS];
+	/* maps which plane is using a fifo. fifo-id -> plane-id */
+	int fifo_assignment[DISPC_MAX_NR_FIFOS];
 
 	spinlock_t irq_lock;
 	u32 irq_error_mask;
@@ -1061,10 +1066,10 @@ static void dispc_mgr_set_size(enum omap_channel channel, u16 width,
 	dispc_write_reg(DISPC_SIZE_MGR(channel), val);
 }
 
-static void dispc_read_plane_fifo_sizes(void)
+static void dispc_init_fifos(void)
 {
 	u32 size;
-	int plane;
+	int fifo;
 	u8 start, end;
 	u32 unit;
 
@@ -1072,16 +1077,30 @@ static void dispc_read_plane_fifo_sizes(void)
 
 	dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
 
-	for (plane = 0; plane < dss_feat_get_num_ovls(); ++plane) {
-		size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(plane), start, end);
+	for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
+		size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end);
 		size *= unit;
-		dispc.fifo_size[plane] = size;
+		dispc.fifo_size[fifo] = size;
+
+		/*
+		 * By default fifos are mapped directly to overlays, fifo 0 to
+		 * ovl 0, fifo 1 to ovl 1, etc.
+		 */
+		dispc.fifo_assignment[fifo] = fifo;
 	}
 }
 
 static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
 {
-	return dispc.fifo_size[plane];
+	int fifo;
+	u32 size = 0;
+
+	for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
+		if (dispc.fifo_assignment[fifo] = plane)
+			size += dispc.fifo_size[fifo];
+	}
+
+	return size;
 }
 
 void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
@@ -3708,7 +3727,7 @@ static void _omap_dispc_initial_config(void)
 
 	dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
 
-	dispc_read_plane_fifo_sizes();
+	dispc_init_fifos();
 
 	dispc_configure_burst_sizes();
 
@@ -3724,6 +3743,7 @@ static const struct dispc_features omap24xx_dispc_feats __initconst = {
 	.hp_max			=	256,
 	.calc_scaling		=	dispc_ovl_calc_scaling_24xx,
 	.calc_core_clk		=	calc_core_clk_24xx,
+	.num_fifos		=	3,
 };
 
 static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
@@ -3735,6 +3755,7 @@ static const struct dispc_features omap34xx_rev1_0_dispc_feats __initconst = {
 	.hp_max			=	256,
 	.calc_scaling		=	dispc_ovl_calc_scaling_34xx,
 	.calc_core_clk		=	calc_core_clk_34xx,
+	.num_fifos		=	3,
 };
 
 static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
@@ -3746,6 +3767,7 @@ static const struct dispc_features omap34xx_rev3_0_dispc_feats __initconst = {
 	.hp_max			=	4096,
 	.calc_scaling		=	dispc_ovl_calc_scaling_34xx,
 	.calc_core_clk		=	calc_core_clk_34xx,
+	.num_fifos		=	3,
 };
 
 static const struct dispc_features omap44xx_dispc_feats __initconst = {
@@ -3757,6 +3779,7 @@ static const struct dispc_features omap44xx_dispc_feats __initconst = {
 	.hp_max			=	4096,
 	.calc_scaling		=	dispc_ovl_calc_scaling_44xx,
 	.calc_core_clk		=	calc_core_clk_44xx,
+	.num_fifos		=	5,
 };
 
 static int __init dispc_init_features(struct device *dev)
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 15/17] OMAPDSS: remove extra_info completion code
From: Tomi Valkeinen @ 2012-09-05  8:25 UTC (permalink / raw)
  To: linux-omap, linux-fbdev, archit; +Cc: Tomi Valkeinen
In-Reply-To: <1346833555-31258-1-git-send-email-tomi.valkeinen@ti.com>

Now that fifo merge has been removed, nobody uses the extra_info related
completion code, which can be removed.

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

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 32b5379..2579d15 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -118,7 +118,6 @@ static struct {
 static spinlock_t data_lock;
 /* lock for blocking functions */
 static DEFINE_MUTEX(apply_lock);
-static DECLARE_COMPLETION(extra_updated_completion);
 
 static void dss_register_vsync_isr(void);
 
@@ -355,69 +354,6 @@ static bool need_go(struct omap_overlay_manager *mgr)
 	return false;
 }
 
-/* returns true if an extra_info field is currently being updated */
-static bool extra_info_update_ongoing(void)
-{
-	const int num_mgrs = dss_feat_get_num_mgrs();
-	int i;
-
-	for (i = 0; i < num_mgrs; ++i) {
-		struct omap_overlay_manager *mgr;
-		struct omap_overlay *ovl;
-		struct mgr_priv_data *mp;
-
-		mgr = omap_dss_get_overlay_manager(i);
-		mp = get_mgr_priv(mgr);
-
-		if (!mp->enabled)
-			continue;
-
-		if (!mp->updating)
-			continue;
-
-		if (mp->extra_info_dirty || mp->shadow_extra_info_dirty)
-			return true;
-
-		list_for_each_entry(ovl, &mgr->overlays, list) {
-			struct ovl_priv_data *op = get_ovl_priv(ovl);
-
-			if (op->extra_info_dirty || op->shadow_extra_info_dirty)
-				return true;
-		}
-	}
-
-	return false;
-}
-
-/* wait until no extra_info updates are pending */
-static void wait_pending_extra_info_updates(void)
-{
-	bool updating;
-	unsigned long flags;
-	unsigned long t;
-	int r;
-
-	spin_lock_irqsave(&data_lock, flags);
-
-	updating = extra_info_update_ongoing();
-
-	if (!updating) {
-		spin_unlock_irqrestore(&data_lock, flags);
-		return;
-	}
-
-	init_completion(&extra_updated_completion);
-
-	spin_unlock_irqrestore(&data_lock, flags);
-
-	t = msecs_to_jiffies(500);
-	r = wait_for_completion_timeout(&extra_updated_completion, t);
-	if (r = 0)
-		DSSWARN("timeout in wait_pending_extra_info_updates\n");
-	else if (r < 0)
-		DSSERR("wait_pending_extra_info_updates failed: %d\n", r);
-}
-
 int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
 {
 	unsigned long timeout = msecs_to_jiffies(500);
@@ -823,7 +759,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
 {
 	const int num_mgrs = dss_feat_get_num_mgrs();
 	int i;
-	bool extra_updating;
 
 	spin_lock(&data_lock);
 
@@ -854,10 +789,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
 	dss_write_regs();
 	dss_set_go_bits();
 
-	extra_updating = extra_info_update_ongoing();
-	if (!extra_updating)
-		complete_all(&extra_updated_completion);
-
 	if (!need_isr())
 		dss_unregister_vsync_isr();
 
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 14/17] Revert "OMAPDSS: APPLY: add fifo merge support funcs"
From: Tomi Valkeinen @ 2012-09-05  8:25 UTC (permalink / raw)
  To: linux-omap, linux-fbdev, archit; +Cc: Tomi Valkeinen
In-Reply-To: <1346833555-31258-1-git-send-email-tomi.valkeinen@ti.com>

This reverts commit fb0119742291b6f30cd97026ee137b2d3d1f4de8.

Adding fifo merge feature as an omapdss internal configuration was a
mistake. We cannot hide from the users of omapdss the complexities of
fifo merge.

The previous commit removed fifo merge itself, and this removes the
remaining fifo merge support functions.

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

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 02943a5..32b5379 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -111,9 +111,6 @@ static struct {
 	struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
 	struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
 
-	bool fifo_merge_dirty;
-	bool fifo_merge;
-
 	bool irq_enabled;
 } dss_data;
 
@@ -677,40 +674,11 @@ static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr)
 		mp->shadow_extra_info_dirty = true;
 }
 
-static void dss_write_regs_common(void)
-{
-	const int num_mgrs = omap_dss_get_num_overlay_managers();
-	int i;
-
-	if (!dss_data.fifo_merge_dirty)
-		return;
-
-	for (i = 0; i < num_mgrs; ++i) {
-		struct omap_overlay_manager *mgr;
-		struct mgr_priv_data *mp;
-
-		mgr = omap_dss_get_overlay_manager(i);
-		mp = get_mgr_priv(mgr);
-
-		if (mp->enabled) {
-			if (dss_data.fifo_merge_dirty) {
-				dispc_enable_fifomerge(dss_data.fifo_merge);
-				dss_data.fifo_merge_dirty = false;
-			}
-
-			if (mp->updating)
-				mp->shadow_info_dirty = true;
-		}
-	}
-}
-
 static void dss_write_regs(void)
 {
 	const int num_mgrs = omap_dss_get_num_overlay_managers();
 	int i;
 
-	dss_write_regs_common();
-
 	for (i = 0; i < num_mgrs; ++i) {
 		struct omap_overlay_manager *mgr;
 		struct mgr_priv_data *mp;
@@ -799,8 +767,6 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
 	dss_mgr_write_regs(mgr);
 	dss_mgr_write_regs_extra(mgr);
 
-	dss_write_regs_common();
-
 	mp->updating = true;
 
 	if (!dss_data.irq_enabled && need_isr())
@@ -984,15 +950,6 @@ static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl,
 	op->extra_info_dirty = true;
 }
 
-static void dss_apply_fifo_merge(bool use_fifo_merge)
-{
-	if (dss_data.fifo_merge = use_fifo_merge)
-		return;
-
-	dss_data.fifo_merge = use_fifo_merge;
-	dss_data.fifo_merge_dirty = true;
-}
-
 static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
 {
 	struct ovl_priv_data *op = get_ovl_priv(ovl);
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 13/17] Revert "OMAPDSS: APPLY: add fifo-merge support"
From: Tomi Valkeinen @ 2012-09-05  8:25 UTC (permalink / raw)
  To: linux-omap, linux-fbdev, archit; +Cc: Tomi Valkeinen
In-Reply-To: <1346833555-31258-1-git-send-email-tomi.valkeinen@ti.com>

This reverts commit 1d71f42b35ed66d90a9a39bc515bb16cfe2d4a46.

Adding fifo merge feature as an omapdss internal configuration was a
mistake. We cannot hide from the users of omapdss the complexities of
fifo merge.

This commit removes the fifo merge support, which luckily is easily done
as it was handled totally inside apply.c.

The plan is to try fifo merge again later when it is more clear how the
hardware acts in various situations, and how the omapdrm wants to use
fifo merge.

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

diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 1b49019..02943a5 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -993,11 +993,11 @@ static void dss_apply_fifo_merge(bool use_fifo_merge)
 	dss_data.fifo_merge_dirty = true;
 }
 
-static void dss_ovl_setup_fifo(struct omap_overlay *ovl,
-		bool use_fifo_merge)
+static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
 {
 	struct ovl_priv_data *op = get_ovl_priv(ovl);
 	u32 fifo_low, fifo_high;
+	bool use_fifo_merge = false;
 
 	if (!op->enabled && !op->enabling)
 		return;
@@ -1008,8 +1008,7 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl,
 	dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high);
 }
 
-static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr,
-		bool use_fifo_merge)
+static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
 {
 	struct omap_overlay *ovl;
 	struct mgr_priv_data *mp;
@@ -1020,10 +1019,10 @@ static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr,
 		return;
 
 	list_for_each_entry(ovl, &mgr->overlays, list)
-		dss_ovl_setup_fifo(ovl, use_fifo_merge);
+		dss_ovl_setup_fifo(ovl);
 }
 
-static void dss_setup_fifos(bool use_fifo_merge)
+static void dss_setup_fifos(void)
 {
 	const int num_mgrs = omap_dss_get_num_overlay_managers();
 	struct omap_overlay_manager *mgr;
@@ -1031,91 +1030,15 @@ static void dss_setup_fifos(bool use_fifo_merge)
 
 	for (i = 0; i < num_mgrs; ++i) {
 		mgr = omap_dss_get_overlay_manager(i);
-		dss_mgr_setup_fifos(mgr, use_fifo_merge);
+		dss_mgr_setup_fifos(mgr);
 	}
 }
 
-static int get_num_used_managers(void)
-{
-	const int num_mgrs = omap_dss_get_num_overlay_managers();
-	struct omap_overlay_manager *mgr;
-	struct mgr_priv_data *mp;
-	int i;
-	int enabled_mgrs;
-
-	enabled_mgrs = 0;
-
-	for (i = 0; i < num_mgrs; ++i) {
-		mgr = omap_dss_get_overlay_manager(i);
-		mp = get_mgr_priv(mgr);
-
-		if (!mp->enabled)
-			continue;
-
-		enabled_mgrs++;
-	}
-
-	return enabled_mgrs;
-}
-
-static int get_num_used_overlays(void)
-{
-	const int num_ovls = omap_dss_get_num_overlays();
-	struct omap_overlay *ovl;
-	struct ovl_priv_data *op;
-	struct mgr_priv_data *mp;
-	int i;
-	int enabled_ovls;
-
-	enabled_ovls = 0;
-
-	for (i = 0; i < num_ovls; ++i) {
-		ovl = omap_dss_get_overlay(i);
-		op = get_ovl_priv(ovl);
-
-		if (!op->enabled && !op->enabling)
-			continue;
-
-		mp = get_mgr_priv(ovl->manager);
-
-		if (!mp->enabled)
-			continue;
-
-		enabled_ovls++;
-	}
-
-	return enabled_ovls;
-}
-
-static bool get_use_fifo_merge(void)
-{
-	int enabled_mgrs = get_num_used_managers();
-	int enabled_ovls = get_num_used_overlays();
-
-	if (!dss_has_feature(FEAT_FIFO_MERGE))
-		return false;
-
-	/*
-	 * In theory the only requirement for fifomerge is enabled_ovls <= 1.
-	 * However, if we have two managers enabled and set/unset the fifomerge,
-	 * we need to set the GO bits in particular sequence for the managers,
-	 * and wait in between.
-	 *
-	 * This is rather difficult as new apply calls can happen at any time,
-	 * so we simplify the problem by requiring also that enabled_mgrs <= 1.
-	 * In practice this shouldn't matter, because when only one overlay is
-	 * enabled, most likely only one output is enabled.
-	 */
-
-	return enabled_mgrs <= 1 && enabled_ovls <= 1;
-}
-
 int dss_mgr_enable(struct omap_overlay_manager *mgr)
 {
 	struct mgr_priv_data *mp = get_mgr_priv(mgr);
 	unsigned long flags;
 	int r;
-	bool fifo_merge;
 
 	mutex_lock(&apply_lock);
 
@@ -1133,23 +1056,11 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
 		goto err;
 	}
 
-	/* step 1: setup fifos/fifomerge before enabling the manager */
-
-	fifo_merge = get_use_fifo_merge();
-	dss_setup_fifos(fifo_merge);
-	dss_apply_fifo_merge(fifo_merge);
+	dss_setup_fifos();
 
 	dss_write_regs();
 	dss_set_go_bits();
 
-	spin_unlock_irqrestore(&data_lock, flags);
-
-	/* wait until fifo config is in */
-	wait_pending_extra_info_updates();
-
-	/* step 2: enable the manager */
-	spin_lock_irqsave(&data_lock, flags);
-
 	if (!mgr_manual_update(mgr))
 		mp->updating = true;
 
@@ -1174,7 +1085,6 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
 {
 	struct mgr_priv_data *mp = get_mgr_priv(mgr);
 	unsigned long flags;
-	bool fifo_merge;
 
 	mutex_lock(&apply_lock);
 
@@ -1189,16 +1099,8 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
 	mp->updating = false;
 	mp->enabled = false;
 
-	fifo_merge = get_use_fifo_merge();
-	dss_setup_fifos(fifo_merge);
-	dss_apply_fifo_merge(fifo_merge);
-
-	dss_write_regs();
-	dss_set_go_bits();
-
 	spin_unlock_irqrestore(&data_lock, flags);
 
-	wait_pending_extra_info_updates();
 out:
 	mutex_unlock(&apply_lock);
 }
@@ -1505,7 +1407,6 @@ int dss_ovl_enable(struct omap_overlay *ovl)
 {
 	struct ovl_priv_data *op = get_ovl_priv(ovl);
 	unsigned long flags;
-	bool fifo_merge;
 	int r;
 
 	mutex_lock(&apply_lock);
@@ -1531,22 +1432,7 @@ int dss_ovl_enable(struct omap_overlay *ovl)
 		goto err2;
 	}
 
-	/* step 1: configure fifos/fifomerge for currently enabled ovls */
-
-	fifo_merge = get_use_fifo_merge();
-	dss_setup_fifos(fifo_merge);
-	dss_apply_fifo_merge(fifo_merge);
-
-	dss_write_regs();
-	dss_set_go_bits();
-
-	spin_unlock_irqrestore(&data_lock, flags);
-
-	/* wait for fifo configs to go in */
-	wait_pending_extra_info_updates();
-
-	/* step 2: enable the overlay */
-	spin_lock_irqsave(&data_lock, flags);
+	dss_setup_fifos();
 
 	op->enabling = false;
 	dss_apply_ovl_enable(ovl, true);
@@ -1556,9 +1442,6 @@ int dss_ovl_enable(struct omap_overlay *ovl)
 
 	spin_unlock_irqrestore(&data_lock, flags);
 
-	/* wait for overlay to be enabled */
-	wait_pending_extra_info_updates();
-
 	mutex_unlock(&apply_lock);
 
 	return 0;
@@ -1574,7 +1457,6 @@ int dss_ovl_disable(struct omap_overlay *ovl)
 {
 	struct ovl_priv_data *op = get_ovl_priv(ovl);
 	unsigned long flags;
-	bool fifo_merge;
 	int r;
 
 	mutex_lock(&apply_lock);
@@ -1589,34 +1471,14 @@ int dss_ovl_disable(struct omap_overlay *ovl)
 		goto err;
 	}
 
-	/* step 1: disable the overlay */
 	spin_lock_irqsave(&data_lock, flags);
 
 	dss_apply_ovl_enable(ovl, false);
-
 	dss_write_regs();
 	dss_set_go_bits();
 
 	spin_unlock_irqrestore(&data_lock, flags);
 
-	/* wait for the overlay to be disabled */
-	wait_pending_extra_info_updates();
-
-	/* step 2: configure fifos/fifomerge */
-	spin_lock_irqsave(&data_lock, flags);
-
-	fifo_merge = get_use_fifo_merge();
-	dss_setup_fifos(fifo_merge);
-	dss_apply_fifo_merge(fifo_merge);
-
-	dss_write_regs();
-	dss_set_go_bits();
-
-	spin_unlock_irqrestore(&data_lock, flags);
-
-	/* wait for fifo config to go in */
-	wait_pending_extra_info_updates();
-
 	mutex_unlock(&apply_lock);
 
 	return 0;
-- 
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