SUPERH platform development
 help / color / mirror / Atom feed
* Re: [PATCH] sh_tmu / PM: Prevent power from being removed from TMU devices
From: Magnus Damm @ 2012-03-05  8:01 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <201203030041.30244.rjw@sisk.pl>

On Mon, Mar 5, 2012 at 2:47 PM, Paul Mundt <lethal@linux-sh.org> wrote:
> On Sun, Mar 04, 2012 at 10:50:53PM +0100, Rafael J. Wysocki wrote:
>> On Sunday, March 04, 2012, Paul Mundt wrote:
>> > On Sat, Mar 03, 2012 at 12:41:30AM +0100, Rafael J. Wysocki wrote:
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > To prevent TMU devices from losing power define a fake "always busy"
>> > > suspend callback in the sh_tmu driver that will cause all attempts to
>> > > suspend the system to fail.  In addition to that, make the driver
>> > > change the runtime PM settings of TMU devices so that they appear to
>> > > be always "active" and make SH7372 add them to the A4R domain (where
>> > > they physically belong), to make the PM domains core take those
>> > > settings into consideration when attempting to remove power from A4R.
>> > >
>> > > Proprer power management support will be added to the sh_tmu driver
>> > > in the future.
>> > >
>> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Presumably we also need the same for the MTU2 and CMT drivers?
>>
>> Yes, we do in principle, although this isn't strictly necessary for things to
>> work at the moment.
>>
>> I can post analogous patches for the other drivers if you want me to.
>>
> It would be nice to ensure that we don't run in to the same problems on
> the other drivers in the interim at least. I'll take a look at getting
> proper PM support taken care of for them afterwards.

I agree, but I wonder how much of an actual issue it is at this point.
In the long run of course the drivers should be fixed up, but this
patch more looks like a workaround for 3.3-rc.

As for actual hardware timers, MTU2 is not used on any platform with
power domain support today and the CMT is located in a always-on power
domain. So they should not cause any issues for 3.3-rc.

/ magnus

^ permalink raw reply

* Re: [PATCH] sh_tmu / PM: Prevent power from being removed from TMU devices
From: Paul Mundt @ 2012-03-05  5:47 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <201203030041.30244.rjw@sisk.pl>

On Sun, Mar 04, 2012 at 10:50:53PM +0100, Rafael J. Wysocki wrote:
> On Sunday, March 04, 2012, Paul Mundt wrote:
> > On Sat, Mar 03, 2012 at 12:41:30AM +0100, Rafael J. Wysocki wrote:
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > > 
> > > To prevent TMU devices from losing power define a fake "always busy"
> > > suspend callback in the sh_tmu driver that will cause all attempts to
> > > suspend the system to fail.  In addition to that, make the driver
> > > change the runtime PM settings of TMU devices so that they appear to
> > > be always "active" and make SH7372 add them to the A4R domain (where
> > > they physically belong), to make the PM domains core take those
> > > settings into consideration when attempting to remove power from A4R.
> > > 
> > > Proprer power management support will be added to the sh_tmu driver
> > > in the future.
> > > 
> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > Presumably we also need the same for the MTU2 and CMT drivers?
> 
> Yes, we do in principle, although this isn't strictly necessary for things to
> work at the moment.
> 
> I can post analogous patches for the other drivers if you want me to.
> 
It would be nice to ensure that we don't run in to the same problems on
the other drivers in the interim at least. I'll take a look at getting
proper PM support taken care of for them afterwards.

^ permalink raw reply

* Re: [PATCH] sh_tmu / PM: Prevent power from being removed from TMU devices
From: Rafael J. Wysocki @ 2012-03-04 21:50 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <201203030041.30244.rjw@sisk.pl>

On Sunday, March 04, 2012, Paul Mundt wrote:
> On Sat, Mar 03, 2012 at 12:41:30AM +0100, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > To prevent TMU devices from losing power define a fake "always busy"
> > suspend callback in the sh_tmu driver that will cause all attempts to
> > suspend the system to fail.  In addition to that, make the driver
> > change the runtime PM settings of TMU devices so that they appear to
> > be always "active" and make SH7372 add them to the A4R domain (where
> > they physically belong), to make the PM domains core take those
> > settings into consideration when attempting to remove power from A4R.
> > 
> > Proprer power management support will be added to the sh_tmu driver
> > in the future.
> > 
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Presumably we also need the same for the MTU2 and CMT drivers?

Yes, we do in principle, although this isn't strictly necessary for things to
work at the moment.

I can post analogous patches for the other drivers if you want me to.

Thanks,
Rafael

^ permalink raw reply

* Re: [PATCH] sh_tmu / PM: Prevent power from being removed from TMU devices
From: Paul Mundt @ 2012-03-04 21:03 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <201203030041.30244.rjw@sisk.pl>

On Sat, Mar 03, 2012 at 12:41:30AM +0100, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> To prevent TMU devices from losing power define a fake "always busy"
> suspend callback in the sh_tmu driver that will cause all attempts to
> suspend the system to fail.  In addition to that, make the driver
> change the runtime PM settings of TMU devices so that they appear to
> be always "active" and make SH7372 add them to the A4R domain (where
> they physically belong), to make the PM domains core take those
> settings into consideration when attempting to remove power from A4R.
> 
> Proprer power management support will be added to the sh_tmu driver
> in the future.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Presumably we also need the same for the MTU2 and CMT drivers?

^ permalink raw reply

* Re: [PATCH/RFC] mmc: add a device PM QoS constraint when a host is first claimed
From: Rafael J. Wysocki @ 2012-03-03 20:53 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Guennadi Liakhovetski, linux-mmc@vger.kernel.org,
	linux-pm@vger.kernel.org, Chris Ball, linux-sh@vger.kernel.org
In-Reply-To: <4EEF2B54.6050108@stericsson.com>

Hi,

Sorry for the late respones.

On Monday, December 19, 2011, Ulf Hansson wrote:
> Guennadi Liakhovetski wrote:
> > On Wed, 14 Dec 2011, Rafael J. Wysocki wrote:
> > 
> >> On Wednesday, December 14, 2011, Ulf Hansson wrote:
> > 
> > [snip]
> > 
> >>> 2.
> >>> When executing mmc/sd commands/requests the host must always be claimed 
> >>> (and thus the host is always enabled).
> > 
> > Why? Why cannot we save some power between IO operations - if we can do 
> > this quickly and safely without affecting functionality and throughput?
> > 
> >>> But more important some mmc/sd 
> >>> commands must be executed as a sequence, without the host being disabled 
> >>> in between the commands (since a disable might mean that the clock to 
> >>> card gets disabled).
> > 
> > Ok, there might well be such command sequences, but my question is: who 
> > knows about them? Is this mandated by the MMC(/SD/SDIO/...) standard or is 
> > it host-specific? Also "might mean" is actually interesting here. I think 
> > we eventually need a combination of timing-oriented PM constraints and 
> > "stateful" ones. During such a command sequence you would require the card 
> > clock to stay on.
> 
> MMC/SD/SDIO standard sets the requirement and must somehow "notify" the 
> host about how to act in certain scenarios.
> 
> Just for information, I think one interesting host driver to look at is 
> omap_hsmmc. For it's enable function is does pm_runtime_get_sync and for 
> it's disable function it does pm_runtime_put_autosuspend, this to make 
> sure the clock is enabled when it is needed.

That's OK, but that doesn't solve the issue the $subject patch tries to
address.

> >>> To solve this, the mmc_claim_host is used, to make 
> >>> sure the host is enabled during the complete command sequence.
> >>>
> >>> I happily continue this discussion, to see if someone more can break the 
> >>> idea about having get_sync in mmc_host_enable. :-)
> >> I'll leave this one to Guennadi, if you don't mind. :-)
> > 
> > As I said above, I think, we need both - to be able to require a certain 
> > responsiveness / throughput and specific interface parameters like 
> > supplying clock, power, etc.
> > 
> > Also notice, that setting a constraint doesn't affect in principle, when 
> > the device is allowed to suspend. This is done as usual per 
> > pm_runtime_get*() and _put*(). I think, a reasonable solution to use PM 
> > QoS to impose timing constraints but at the same time to not disable the 
> > hardware, when this is disallowed, is to tie pm_runtime_get() and _put() 
> > calls to driver's .set_ios() method, like tmio_mmc and sh_mmcif drivers 
> > currently do. Those drivers only call pm_runtime_put() when the interface 
> > clock is gated. So, as long as the core is aware, that that IO sequence 
> > has to run uninterrupted without stopping the clock between single 
> > transfers, it just has to avoid gating the clock for that period and the 
> > interface will not enter any power-saving mode.
> > 
> > So, I don't think we need to enforce pm_runtime_get_sync() in 
> > mmc_claim_host().
> 
> You might be right; but still we need a way for the mmc framework to 
> prevent the host driver from disabling clock etc to the card for some 
> command sequences. As the omap driver solves it is to implement the 
> enable/disable function and then call for get_sync and put in these 
> functions, maybe that is the way how to solve this!?

You still seem to be confusing preventing the clock from being disabled
at all (which may be necessary for some command sequences etc.) with preventing
the host adapter from being put into an overly deep low-power state _after_
_the_ _clock_ _has_ _been_ _disabled_.  They are two _different_ issues
and there's no way we can address them both by using timeouts.

With PM domains support on SH7372 we can easily have a situation in which
the controller's runtime PM causes a whole domain to go off (or in a more
complicated scenario, the runtime PM of some _other_ device in the same PM
domain causes it to go off while the MMC controller is in the "suspended"
state) and that leads to an overly long delay in the MMC subsystem.  So,
we need to address this issue _in_ _addition_ to ensuring that the controller
is _not_ suspended when it shouldn't be.

Thanks,
Rafael

^ permalink raw reply

* [PATCH] sh_tmu / PM: Prevent power from being removed from TMU devices
From: Rafael J. Wysocki @ 2012-03-02 23:41 UTC (permalink / raw)
  To: linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

To prevent TMU devices from losing power define a fake "always busy"
suspend callback in the sh_tmu driver that will cause all attempts to
suspend the system to fail.  In addition to that, make the driver
change the runtime PM settings of TMU devices so that they appear to
be always "active" and make SH7372 add them to the A4R domain (where
they physically belong), to make the PM domains core take those
settings into consideration when attempting to remove power from A4R.

Proprer power management support will be added to the sh_tmu driver
in the future.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-shmobile/setup-sh7372.c |    2 ++
 drivers/clocksource/sh_tmu.c          |   12 ++++++++++++
 2 files changed, 14 insertions(+)

Index: linux/drivers/clocksource/sh_tmu.c
=================================--- linux.orig/drivers/clocksource/sh_tmu.c
+++ linux/drivers/clocksource/sh_tmu.c
@@ -426,6 +426,10 @@ static int __devinit sh_tmu_probe(struct
 		kfree(p);
 		platform_set_drvdata(pdev, NULL);
 	}
+#ifdef CONFIG_PM_RUNTIME
+	pdev->dev.power.disable_depth = 0;
+	pdev->dev.power.runtime_status = RPM_ACTIVE;
+#endif
 	return ret;
 }
 
@@ -434,11 +438,19 @@ static int __devexit sh_tmu_remove(struc
 	return -EBUSY; /* cannot unregister clockevent and clocksource */
 }
 
+static int sh_tmu_no_suspend(struct device *dev)
+{
+	return -EBUSY;
+}
+
+static SIMPLE_DEV_PM_OPS(sh_tmu_pm_ops, sh_tmu_no_suspend, NULL);
+
 static struct platform_driver sh_tmu_device_driver = {
 	.probe		= sh_tmu_probe,
 	.remove		= __devexit_p(sh_tmu_remove),
 	.driver		= {
 		.name	= "sh_tmu",
+		.pm	= &sh_tmu_pm_ops,
 	}
 };
 
Index: linux/arch/arm/mach-shmobile/setup-sh7372.c
=================================--- linux.orig/arch/arm/mach-shmobile/setup-sh7372.c
+++ linux/arch/arm/mach-shmobile/setup-sh7372.c
@@ -1043,6 +1043,8 @@ void __init sh7372_add_standard_devices(
 	sh7372_add_device_to_domain(&sh7372_a4r, &veu2_device);
 	sh7372_add_device_to_domain(&sh7372_a4r, &veu3_device);
 	sh7372_add_device_to_domain(&sh7372_a4r, &jpu_device);
+	sh7372_add_device_to_domain(&sh7372_a4r, &tmu00_device);
+	sh7372_add_device_to_domain(&sh7372_a4r, &tmu01_device);
 }
 
 void __init sh7372_add_early_devices(void)

^ permalink raw reply

* Re: [PATCH] mackerel: Do not enable TMU timer in default config
From: Rafael J. Wysocki @ 2012-03-02 23:27 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <1329111377-15676-1-git-send-email-horms@verge.net.au>

On Wednesday, February 29, 2012, Simon Horman wrote:
> On Wed, Feb 29, 2012 at 12:59:36AM +0100, Rafael J. Wysocki wrote:
> > On Tuesday, February 28, 2012, Simon Horman wrote:
> > > On Tue, Feb 28, 2012 at 03:09:09PM +0900, Paul Mundt wrote:
> > > > On Tue, Feb 28, 2012 at 02:23:45AM +0100, Rafael J. Wysocki wrote:
> > > > > However, having looked at both sh_tmu and sh_cmt I don't see why the former
> > > > > should break things (during boot), while the latter doesn't, so I've done
> > > > > some more testing and, surprisingly enough, it turns out that disabling runtime
> > > > > PM in sh-sci makes the boot hang with sh_tmu enabled go away.  So perhaps sh_tmu
> > > > > is just a messenger here and the real problem is with sh-sci.
> > > > > 
> > > > > I think that more investigation is needed.
> > > > > 
> > > > Do you see any different behaviour with early printk enabled vs disabled?
> > > > We did have the ordering issues before with the pm calls hanging or
> > > > oopsing in the early path, but all of those should have been fixed
> > > > already. There have been quite a few sh-sci changes though, so if it
> > > > worked previously it should be bisectable at least.
> > > 
> > > Hi Paul, Hi Rafael,
> > > 
> > > I asked Hiep-san (CCed) to see if there was any difference booting a
> > > 3.3-rc3 on a Mackerel without earlyprintk in the kernel commandline.
> > > I tested 3.3-rc5 as well. There does not seem to be any change in behaviour,
> > > other than that specificly relating the bootconsole.
> > > 
> > > Without earlyprintk
> > > ...
> > > SuperH SCI(F) driver initialized
> > > sh-sci.0: ttySC0 at MMIO 0xe6c40000 (irq = 80) is a scifa
> > > sh-sci sh-sci.0: start latency exceeded, new value 6916 ns
> > > console [ttySC0] enabled
> > > [no more output]
> > > 
> > > With earlyprintk
> > > ...
> > > SuperH SCI(F) driver initialized
> > > sh-sci.0: ttySC0 at MMIO 0xe6c40000 (irq = 80) is a scifa
> > > sh-sci sh-sci.0: start latency exceeded, new value 5917 ns
> > > console [ttySC0] enabled, bootconsole disabled
> > > console [ttySC0] enabled, bootconsole disabled
> > > [no more output]
> > 
> > OK, I know what the problem is.
> > 
> > The enabling of sh_tmu apparently triggers the dev_warn() in
> > GENPD_DEV_TIMED_CALLBACK() for sh-sci.0, which leads to a deadlock in the
> > runtime PM core, because synchronous runtime resume for the device is then
> > run from its own runtime suspend callback.
> > 
> > I have an idea how to fix this, but it's going to take a couple of days
> > due to my time constraints.
> 
> Thanks Rafael,
> 
> please don't hesitate to let me know if you need more testing done.

Below is a patch that fixes the boot problem for me.  Please test if it
works for you too.

Unfortunately, we can't detect that deadlock at the core level (at least
I'm not aware of any generally reliable method of doing that) so it has to
be taken care of by the driver.  The alternative would be to remove all
diagnostic messages from runtime PM callbacks that may be used in a
console runtime resume code path, but I think that would be overkill
(those messages are actually useful for other types of devices).

However, this patch only addresses the boot issue, but then, if sh_tmu is
enabled, runtime PM involving the A4R domain and system-level PM lead to
nasty crashes.  Since Paul doesn't like the idea of enabling sh_tmu only
if PM is not enabled, I propose to make it actively fail all PM operations
that would lead to the removal of power from TMU devices.  This is done
in a second patch that will be sent in a reply to this message.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: sh-sci / PM: Avoid deadlocking runtime PM

The runtime PM of sh-sci devices is enabled when sci_probe() returns,
so the pm_runtime_put_sync() executed by driver_probe_device()
attempts to suspend the device.  Then, in some situations, a
diagnostic message is printed to the console by one of the runtime
suspend routines handling the sh-sci device, which causes synchronous
runtime resume to be started from the device's own runtime suspend
callback.  This causes rpm_resume() to be run eventually, which sees
the RPM_SUSPENDING status set by rpm_suspend() and waits for it to
change.  However, the device's runtime PM status cannot change at
that point, because the routine that has set it waits for the
rpm_suspend() to return.  A deadlock occurs as a result.

To avoid that make sci_init_single() increment the device's
runtime PM usage counter, so that it cannot be suspended by
driver_probe_device().  That counter has to be decremented
eventually, so make sci_startup() do that before starting to
actually use the device and make sci_shutdown() increment it
again before returning to balance the incrementation carried out by
sci_startup().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/tty/serial/sh-sci.c |    5 +++++
 1 file changed, 5 insertions(+)

Index: linux/drivers/tty/serial/sh-sci.c
=================================--- linux.orig/drivers/tty/serial/sh-sci.c
+++ linux/drivers/tty/serial/sh-sci.c
@@ -1710,6 +1710,8 @@ static int sci_startup(struct uart_port
 
 	dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
 
+	pm_runtime_put_noidle(port->dev);
+
 	sci_port_enable(s);
 
 	ret = sci_request_irq(s);
@@ -1737,6 +1739,8 @@ static void sci_shutdown(struct uart_por
 	sci_free_irq(s);
 
 	sci_port_disable(s);
+
+	pm_runtime_get_noresume(port->dev);
 }
 
 static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
@@ -2075,6 +2079,7 @@ static int __devinit sci_init_single(str
 		sci_init_gpios(sci_port);
 
 		pm_runtime_irq_safe(&dev->dev);
+		pm_runtime_get_noresume(&dev->dev);
 		pm_runtime_enable(&dev->dev);
 	}
 

^ permalink raw reply

* Re: [PATCH 00/06] ARM: mach-shmobile: map_io and init_early update
From: Rafael J. Wysocki @ 2012-03-02 20:50 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <20120229123656.28584.90874.sendpatchset@w520>

On Friday, March 02, 2012, Paul Mundt wrote:
> On Thu, Mar 01, 2012 at 10:22:03PM +0100, Rafael J. Wysocki wrote:
> > On Wednesday, February 29, 2012, Magnus Damm wrote:
> > >  ARM: mach-shmobile: map_io and init_early update
> > > 
> > > [PATCH 01/06] ARM: mach-shmobile: sh7367 map_io and init_early update
> > > [PATCH 02/06] ARM: mach-shmobile: sh7377 map_io and init_early update
> > > [PATCH 03/06] ARM: mach-shmobile: sh7372 map_io and init_early update
> > > [PATCH 04/06] ARM: mach-shmobile: sh73a0 map_io and init_early update
> > > [PATCH 05/06] ARM: mach-shmobile: r8a7740 map_io and init_early update
> > > [PATCH 06/06] ARM: mach-shmobile: r8a7779 map_io and init_early update
> > > 
> > > This patch series converts the mach-shmobile subarch to properly
> > > make use of a per-soc map_io and a separate init_early callback for
> > > early serial console support on platforms where that is possible.
> > > 
> > > Signed-off-by: Magnus Damm <damm@opensource.se>
> > 
> > Paul, would you mind if I took this patchset into the renesas tree for v3.4?
> 
> On Thu, Mar 01, 2012 at 10:21:14PM +0100, Rafael J. Wysocki wrote:
> > On Wednesday, February 29, 2012, Magnus Damm wrote:
> > > From: Magnus Damm <damm@opensource.se>
> > > 
> > > Rename clk_init() to shmobile_clk_init() to avoid a potential
> > > future name space collision with the common clock framework.
> > > 
> > > Signed-off-by: Magnus Damm <damm@opensource.se>
> > 
> > Paul, would you mind if I took this patchset into the renesas tree for v3.4?
> 
> Both look fine to me, so feel free to take them at your leisure.

I will, thanks!

^ permalink raw reply

* Re: [PATCH 00/06] ARM: mach-shmobile: map_io and init_early update
From: Paul Mundt @ 2012-03-02  1:39 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <20120229123656.28584.90874.sendpatchset@w520>

On Thu, Mar 01, 2012 at 10:22:03PM +0100, Rafael J. Wysocki wrote:
> On Wednesday, February 29, 2012, Magnus Damm wrote:
> >  ARM: mach-shmobile: map_io and init_early update
> > 
> > [PATCH 01/06] ARM: mach-shmobile: sh7367 map_io and init_early update
> > [PATCH 02/06] ARM: mach-shmobile: sh7377 map_io and init_early update
> > [PATCH 03/06] ARM: mach-shmobile: sh7372 map_io and init_early update
> > [PATCH 04/06] ARM: mach-shmobile: sh73a0 map_io and init_early update
> > [PATCH 05/06] ARM: mach-shmobile: r8a7740 map_io and init_early update
> > [PATCH 06/06] ARM: mach-shmobile: r8a7779 map_io and init_early update
> > 
> > This patch series converts the mach-shmobile subarch to properly
> > make use of a per-soc map_io and a separate init_early callback for
> > early serial console support on platforms where that is possible.
> > 
> > Signed-off-by: Magnus Damm <damm@opensource.se>
> 
> Paul, would you mind if I took this patchset into the renesas tree for v3.4?

On Thu, Mar 01, 2012 at 10:21:14PM +0100, Rafael J. Wysocki wrote:
> On Wednesday, February 29, 2012, Magnus Damm wrote:
> > From: Magnus Damm <damm@opensource.se>
> > 
> > Rename clk_init() to shmobile_clk_init() to avoid a potential
> > future name space collision with the common clock framework.
> > 
> > Signed-off-by: Magnus Damm <damm@opensource.se>
> 
> Paul, would you mind if I took this patchset into the renesas tree for v3.4?

Both look fine to me, so feel free to take them at your leisure.

^ permalink raw reply

* Re: [PATCH 00/06] ARM: mach-shmobile: map_io and init_early update
From: Rafael J. Wysocki @ 2012-03-01 21:22 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <20120229123656.28584.90874.sendpatchset@w520>

On Wednesday, February 29, 2012, Magnus Damm wrote:
>  ARM: mach-shmobile: map_io and init_early update
> 
> [PATCH 01/06] ARM: mach-shmobile: sh7367 map_io and init_early update
> [PATCH 02/06] ARM: mach-shmobile: sh7377 map_io and init_early update
> [PATCH 03/06] ARM: mach-shmobile: sh7372 map_io and init_early update
> [PATCH 04/06] ARM: mach-shmobile: sh73a0 map_io and init_early update
> [PATCH 05/06] ARM: mach-shmobile: r8a7740 map_io and init_early update
> [PATCH 06/06] ARM: mach-shmobile: r8a7779 map_io and init_early update
> 
> This patch series converts the mach-shmobile subarch to properly
> make use of a per-soc map_io and a separate init_early callback for
> early serial console support on platforms where that is possible.
> 
> Signed-off-by: Magnus Damm <damm@opensource.se>

Paul, would you mind if I took this patchset into the renesas tree for v3.4?

Rafael


> ---
> 
>  arch/arm/mach-shmobile/board-ag5evm.c        |   25 +------------
>  arch/arm/mach-shmobile/board-ap4evb.c        |   25 +------------
>  arch/arm/mach-shmobile/board-bonito.c        |   28 +--------------
>  arch/arm/mach-shmobile/board-g3evm.c         |   25 +------------
>  arch/arm/mach-shmobile/board-g4evm.c         |   25 +------------
>  arch/arm/mach-shmobile/board-kota2.c         |   25 +------------
>  arch/arm/mach-shmobile/board-mackerel.c      |   25 +------------
>  arch/arm/mach-shmobile/board-marzen.c        |   48 +-------------------------
>  arch/arm/mach-shmobile/include/mach/common.h |    6 +++
>  arch/arm/mach-shmobile/setup-r8a7740.c       |   35 ++++++++++++++++++
>  arch/arm/mach-shmobile/setup-r8a7779.c       |   39 +++++++++++++++++++++
>  arch/arm/mach-shmobile/setup-sh7367.c        |   22 +++++++++++
>  arch/arm/mach-shmobile/setup-sh7372.c        |   22 +++++++++++
>  arch/arm/mach-shmobile/setup-sh7377.c        |   22 +++++++++++
>  arch/arm/mach-shmobile/setup-sh73a0.c        |   22 +++++++++++
>  15 files changed, 184 insertions(+), 210 deletions(-)
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" 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] ARM: mach-shmobile: rename clk_init() to shmobile_clk_init()
From: Rafael J. Wysocki @ 2012-03-01 21:21 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <20120229124130.29066.74120.sendpatchset@w520>

On Wednesday, February 29, 2012, Magnus Damm wrote:
> From: Magnus Damm <damm@opensource.se>
> 
> Rename clk_init() to shmobile_clk_init() to avoid a potential
> future name space collision with the common clock framework.
> 
> Signed-off-by: Magnus Damm <damm@opensource.se>

Paul, would you mind if I took this patchset into the renesas tree for v3.4?

Rafael


> ---
> 
>  arch/arm/mach-shmobile/clock-r8a7740.c       |    2 +-
>  arch/arm/mach-shmobile/clock-r8a7779.c       |    2 +-
>  arch/arm/mach-shmobile/clock-sh7367.c        |    2 +-
>  arch/arm/mach-shmobile/clock-sh7372.c        |    2 +-
>  arch/arm/mach-shmobile/clock-sh7377.c        |    2 +-
>  arch/arm/mach-shmobile/clock-sh73a0.c        |    2 +-
>  arch/arm/mach-shmobile/clock.c               |    2 +-
>  arch/arm/mach-shmobile/include/mach/common.h |    2 +-
>  8 files changed, 8 insertions(+), 8 deletions(-)
> 
> --- 0015/arch/arm/mach-shmobile/clock-r8a7740.c
> +++ work/arch/arm/mach-shmobile/clock-r8a7740.c	2012-02-29 20:59:39.000000000 +0900
> @@ -376,7 +376,7 @@ void __init r8a7740_clock_init(u8 md_ck)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup r8a7740 clocks\n");
>  }
> --- 0016/arch/arm/mach-shmobile/clock-r8a7779.c
> +++ work/arch/arm/mach-shmobile/clock-r8a7779.c	2012-02-29 20:59:53.000000000 +0900
> @@ -170,7 +170,7 @@ void __init r8a7779_clock_init(void)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup r8a7779 clocks\n");
>  }
> --- 0014/arch/arm/mach-shmobile/clock-sh7367.c
> +++ work/arch/arm/mach-shmobile/clock-sh7367.c	2012-02-29 21:00:12.000000000 +0900
> @@ -349,7 +349,7 @@ void __init sh7367_clock_init(void)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup sh7367 clocks\n");
>  }
> --- 0012/arch/arm/mach-shmobile/clock-sh7372.c
> +++ work/arch/arm/mach-shmobile/clock-sh7372.c	2012-02-29 21:00:34.000000000 +0900
> @@ -710,7 +710,7 @@ void __init sh7372_clock_init(void)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup sh7372 clocks\n");
>  
> --- 0013/arch/arm/mach-shmobile/clock-sh7377.c
> +++ work/arch/arm/mach-shmobile/clock-sh7377.c	2012-02-29 21:00:25.000000000 +0900
> @@ -360,7 +360,7 @@ void __init sh7377_clock_init(void)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup sh7377 clocks\n");
>  }
> --- 0011/arch/arm/mach-shmobile/clock-sh73a0.c
> +++ work/arch/arm/mach-shmobile/clock-sh73a0.c	2012-02-29 21:00:48.000000000 +0900
> @@ -620,7 +620,7 @@ void __init sh73a0_clock_init(void)
>  	clkdev_add_table(lookups, ARRAY_SIZE(lookups));
>  
>  	if (!ret)
> -		clk_init();
> +		shmobile_clk_init();
>  	else
>  		panic("failed to setup sh73a0 clocks\n");
>  }
> --- 0001/arch/arm/mach-shmobile/clock.c
> +++ work/arch/arm/mach-shmobile/clock.c	2012-02-29 20:58:43.000000000 +0900
> @@ -24,7 +24,7 @@
>  #include <linux/sh_clk.h>
>  #include <linux/export.h>
>  
> -int __init clk_init(void)
> +int __init shmobile_clk_init(void)
>  {
>  	/* Kick the child clocks.. */
>  	recalculate_root_clocks();
> --- 0008/arch/arm/mach-shmobile/include/mach/common.h
> +++ work/arch/arm/mach-shmobile/include/mach/common.h	2012-02-29 20:59:15.000000000 +0900
> @@ -6,7 +6,7 @@ extern void shmobile_setup_console(void)
>  extern void shmobile_secondary_vector(void);
>  extern int shmobile_platform_cpu_kill(unsigned int cpu);
>  struct clk;
> -extern int clk_init(void);
> +extern int shmobile_clk_init(void);
>  extern void shmobile_handle_irq_intc(struct pt_regs *);
>  extern struct platform_suspend_ops shmobile_suspend_ops;
>  struct cpuidle_driver;
> 
> 


^ permalink raw reply

* Re: [PATCH v7 0/9] Consolidate cpuidle functionality
From: Rob Lee @ 2012-03-01 20:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1330562578-3410-1-git-send-email-rob.lee@linaro.org>

On Wed, Feb 29, 2012 at 6:42 PM, Robert Lee <rob.lee@linaro.org> wrote:
> This patch series moves various functionality duplicated in platform
> cpuidle drivers to the core cpuidle driver. Also, the platform irq
> disabling was removed as it appears that all calls into
> cpuidle_call_idle will have already called local_irq_disable().
>

I'm told that I forgot to add the Acks from the previous v6 to this version:

Acked-by: Jean Pihet <j-pihet@ti.com> (v6)
Tested-by: Jean Pihet <j-pihet@ti.com> (v6, omap3)
Tested-by: Amit Daniel <amit.kachhap@linaro.org> (v6, Exynos4)
For the generic cpuidle changes:
Reviewed-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>

If anyone sees other omissions or has any suggested changes or
improvements in my patch submissions semantics, please let me know.

Thanks,
Rob

> Rafael,
>
> Could you review this patchset and merge patch 1/9 once its ready?  It
> seems pretty close to being acceptable.  The get_maintainer script shows
> Len Brown as the cpuidle maintainer but I've been unable to get a response
> from him so far.  If you are not the right person, could you suggest
> who I can make this request to?  Thanks.
>
> Note to platform maintainers:
>
> Platform patches (2/9 to 9/9) in this patchset are not required to work
> with patch 1/9 but please review and push these platform changes as possible
> to allow this consolidation to occur.
>
> Based on 3.3-rc5 plus recent exynos cpuidle patch (affects exynos cpuidle only):
> http://www.spinics.net/lists/linux-samsung-soc/msg09467.html
>
> v6 submission tested successfully on Exynos (thanks Amit Kacchap) and OMAP3
> (thanks Jean Pihet) platforms.
>
> v6 submission can be found here:
> http://www.spinics.net/lists/arm-kernel/msg162018.html
> Changes since v6:
> * Made some  struct whitespace alignment changes.
> * Fixed a coding style violation (thanks Jean Pihet)
> * Fixed a bug in davinci cpuidle (thanks Jean Pihet)
> * Corrected the common ARM cpuidle WFI state description to be ARM platform
>  agnostic (thanks Kevin Hilman)
> * Fixed the problem causing x86 and PPC builds to fail (thanks Deepthi)
> * Re-added a line of code that was mistakenly removed (thanks Deepthi)
>
> Robert Lee (9):
>  cpuidle: Add common time keeping and irq enabling
>  ARM: at91: Consolidate time keeping and irq enable
>  ARM: exynos: Consolidate time keeping and irq enable
>  ARM: kirkwood: Consolidate time keeping and irq enable
>  ARM: davinci: Consolidate time keeping and irq enable
>  ARM: omap: Consolidate OMAP3 time keeping and irq enable
>  ARM: omap: Consolidate OMAP4 time keeping and irq enable
>  ARM: shmobile: Consolidate time keeping and irq enable
>  SH: shmobile: Consolidate time keeping and irq enable
>
>  arch/arm/include/asm/cpuidle.h        |   22 +++++++++
>  arch/arm/kernel/Makefile              |    2 +-
>  arch/arm/kernel/cpuidle.c             |   21 ++++++++
>  arch/arm/mach-at91/cpuidle.c          |   67 ++++++++++-----------------
>  arch/arm/mach-davinci/cpuidle.c       |   82 +++++++++++++-------------------
>  arch/arm/mach-exynos/cpuidle.c        |   53 ++-------------------
>  arch/arm/mach-kirkwood/cpuidle.c      |   72 ++++++++--------------------
>  arch/arm/mach-omap2/cpuidle34xx.c     |   42 +++++++----------
>  arch/arm/mach-omap2/cpuidle44xx.c     |   21 +-------
>  arch/arm/mach-shmobile/cpuidle.c      |   31 +++----------
>  arch/sh/kernel/cpu/shmobile/cpuidle.c |   10 +---
>  drivers/cpuidle/cpuidle.c             |   79 +++++++++++++++++++++++++------
>  include/linux/cpuidle.h               |   13 +++++-
>  13 files changed, 233 insertions(+), 282 deletions(-)
>  create mode 100644 arch/arm/include/asm/cpuidle.h
>  create mode 100644 arch/arm/kernel/cpuidle.c
>

^ permalink raw reply

* Re: [PATCH v7 1/9] cpuidle: Add common time keeping and irq enabling
From: Rob Lee @ 2012-03-01 20:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4F4EF7FC.9040403@linux.vnet.ibm.com>

Hello Deepthi,

On Wed, Feb 29, 2012 at 10:15 PM, Deepthi Dharwar
<deepthi@linux.vnet.ibm.com> wrote:
> Hi Rob,
>
> On 03/01/2012 06:12 AM, Robert Lee wrote:
>
>> Make necessary changes to implement time keeping and irq enabling
>> in the core cpuidle code.  This will allow the removal of these
>> functionalities from various platform cpuidle implementations whose
>> timekeeping and irq enabling follows the form in this common code.
>
>
> The generic cpuidle changes look good, but is there a reason as
> to why these changes are enabled only for ARM and not other
> archs ?
>

Besides ARM, this patchset also enables some of this new consolidation
functionality on arch/SH and for archs that use the
CONFIG_ARCH_HAS_CPU_RELAX (maybe x86 uses this?).

For the powerpc P-series, it could probably could be modified to use
the consolidated timekeeping but I didn't feel comfortable making that
change myself for a couple of reasons.  First, the common wrapper also
includes the local_irq_enable() call, but the p-series cpuidle code
doesn't include this call, as instead, it relies on the
local_irq_enable() call in the cpu_idle() function in
arch/powerpc/kernel/idle.c.  Is it OK to remove this
local_irq_enable() once the wrapper is used?  Second, is there any
special coordination needed with the timekeeping functions and the
mfspr() calls?

Looking at the intel and acpi cpuidle implementations, their current
organization does seem to be able to use the common time keeping / irq
enabling wrapper.  Upon first glance, it appears that there are
special timer/timekeeping requirements for x86 that aren't required by
other platforms.  But that may not be correct.

If you look back at v4 of this patch series, you'll see an attempt at
a common timekeeping that could be used by x86 and acpi , but it
causes other compromises that to me aren't worth the extra gain from a
100% common timekeeping / irq enable solution.  I requested
feedback/opinions on this issue after v4 but didn't hear anything
about changes made to the intel or acpi implementations.  So I
continued on with the common wrapper direction from v3 when making v5.

Ultimately, even if the consolidated code only can be used by most and
not all arch or platform cpuidle implementations, it still reduces
some platform cpuidle fragmentation and duplicated code and hopefully
improves the maintainability of the core cpuidle.

^ permalink raw reply

* Re: Single-stepping with UBC on SH7785
From: Thomas Schwinge @ 2012-03-01 15:50 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <87sjidcrrn.fsf@schwinge.name>


[-- Attachment #1.1: Type: text/plain, Size: 5227 bytes --]

Hi Paul!

Thanks for the detailed answer -- that was helpful.  Some further
comments.


On Fri, 24 Feb 2012 14:36:47 +0900, Paul Mundt <lethal@linux-sh.org> wrote:
> On Tue, Feb 14, 2012 at 05:18:52PM +0100, Thomas Schwinge wrote:
> > After learning from the SH7785 manual how to use and program the UBC, it
> > seemed obvious to me that what it implemented nowadays in
> > arch/sh/kernel/cpu/sh4a/ubc.c for programming the UBC does not match how
> > it used to be
> > 
> That would be because we're using it differently. It was originally
> reworked in order to permit use of multiple UBC channels, and geared at
> the ksym tracer (subsequently superseded by generic perf hw_breakpoint
> API utilization). The intent was the model the same single-step behaviour
> as previously over top of the new API, but there are some caveats (ie,
> perf can have all of the available channels locked down, making
> set_single_step() fail).

The interface of user_enable_single_step assumes it cannot fail.  But in
there, set_single_step may in fact fail (as I understand it), but its
return value is currently ignored.  What should happen in this case?
Patch a breakpoint instruction into the code instead of using the UBC for
single stepping?

In set_single_step, currently only ptrace_bps[0] is considered for use --
which is not a problem for the moment, as single stepping is the only
user.


> > After several hours of grief, I came up with the additional
> > 0001-Wire-the-clock-of-the-SH7785-s-UBC-as-expected-in-ub.patch -- and it
> > worked!  (Meh, so simple...)
> > 
> Sorry about that, I prototyped on 7786 and should have more diligently
> grepped for other UBC clock definitions!

At least I learned a lot about all this Linux kernel code.  :-|


> > ... and today I figured out that my first patch isn't even needed -- but
> > I don't understand how the current ubc.c implementation gets away with
> > not using the asid stuff, for example?  And shouldn't it respect the
> > reserved value UBC_CRR_RES as well as UBC_CRR_INIT and UBC_CBR_INIT that
> > I re-introduced?  Also the manual suggests a different order for
> > programming the registers.
> > 
> The reserved bit is functionally immaterial. It's always wired to 1, so
> it makes no difference what is read/written to it. We can add a 1 write
> to it to line up with the manual if you like, but in general the UBC
> chapter has been cut-and-pasted from legacy parts for years, to the
> extent that the manual should really only be considered a loose guideline
> (for some CPUs the register map is nowhere near where the manual
> suggests, for example). Your CRR_INIT value is just setting CRR_RES
> anyways.

Huh, OK...  For me, the manual is the only reference I have.  (For
example, the manual says to always write reserved bits as they are
read/defined.)  This is why first worked a lot on aligning the
implementation with the manual.  Please tell which other documentation
should I be looking at?

> The CBR_INIT value relates to matching conditions for the given channel,
> and their values need to be derived from the channel being used rather
> than a fixed constant. You are correct that there is a bug here though,
> in that the CBR write forces a CCMFR.MF0 match, while we need to set the
> CBR.MFI relative to the channel index (you can tell this was only really
> tested one channel at a time!). This probably would have been caught
> earlier if we had set CBR.MFE, but we don't really need it for anything.
> 
> > As soon as someone starts working on adding user-space controlled
> > hardware breakpoint and/or watchpoint support, this will need further
> > untangling/cleanup.
> > 
> Now that the kernel uses the UBC alongside userspace it's quite possible
> that we'll have to adjust some of the settings, and I'm certainly
> interested in hearing about any troubles you encounter. I tested the
> single-step stuff with the utrace testsuite if I recall correctly, and it
> seemed to work alright (even with the ksym tracer tying down another
> channel for watchpoint use at the time).

For GDB, I have so far only needed one channel for single stepping.  We
are interested in adding user-space watchpoint support.


> I left the ASID stuff out initially to keep things simple (SH-X3 cores
> and later all have extended ASIDs, so we have an extra set of CBR
> registers to program for extended ASID matching -- this applies to
> anything with PTEAEX capabilities). We obviously don't require it on the
> kernel side, but you're right that we should add it back in for the
> userspace case. If you want to hack up some patches for that I'll
> certainly use them, otherwise I'll see about hacking something up for you
> to test.

I will have to learn more about ASIDs.  Is the manual the correct
document to read?


> It wasn't entirely obvious from your mail, but is the general conclusion
> from all this that with the clock properly registered for SH7785 that
> single-stepping works as you expected it to?

That is correct.  For convenience, I'm again attaching the patch here --
please push that one for the moment.


Grüße,
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 0001-Wire-the-clock-of-the-SH7785-s-UBC-as-expected-in-ub.patch --]
[-- Type: text/x-diff, Size: 1109 bytes --]

From c6696f9fcffcce6739449ea681a38c30e4799017 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Tue, 14 Feb 2012 16:19:49 +0100
Subject: [PATCH] Wire the clock of the SH7785's UBC as expected in ubc.c.

Signed-off-by: Thomas Schwinge <thomas@codesourcery.com>
---
 arch/sh/kernel/cpu/sh4a/clock-sh7785.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
index e5b420c..2b31443 100644
--- a/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
+++ b/arch/sh/kernel/cpu/sh4a/clock-sh7785.c
@@ -156,7 +156,7 @@ static struct clk_lookup lookups[] = {
 	CLKDEV_CON_ID("siof_fck", &mstp_clks[MSTP003]),
 	CLKDEV_CON_ID("hspi_fck", &mstp_clks[MSTP002]),
 	CLKDEV_CON_ID("hudi_fck", &mstp_clks[MSTP119]),
-	CLKDEV_CON_ID("ubc_fck", &mstp_clks[MSTP117]),
+	CLKDEV_CON_ID("ubc0", &mstp_clks[MSTP117]),
 	CLKDEV_CON_ID("dmac_11_6_fck", &mstp_clks[MSTP105]),
 	CLKDEV_CON_ID("dmac_5_0_fck", &mstp_clks[MSTP104]),
 	CLKDEV_CON_ID("gdta_fck", &mstp_clks[MSTP100]),
-- 
1.7.5.4


[-- Attachment #2: Type: application/pgp-signature, Size: 489 bytes --]

^ permalink raw reply related

* Re: [PATCH v3 1/7] mtd: sh_flctl: Expand FLCMNCR register bit field
From: Laurent Pinchart @ 2012-03-01 15:19 UTC (permalink / raw)
  To: Bastian Hecht; +Cc: Magnus Damm, linux-mtd, linux-sh
In-Reply-To: <1330595321-2728-1-git-send-email-hechtb@gmail.com>

Hi Bastian,

Thanks for the patches.

On Thursday 01 March 2012 10:48:35 Bastian Hecht wrote:
> Add support for a new hardware generation. The meaning of some bits
> of the FLCMNCR register changed, so some new defines are added
> parallel to the existing ones to keep backward compatibility.
> 
> The defines allow to choose an appropriate clocking scheme.
> 
> Signed-off-by: Bastian Hecht <hechtb@gmail.com>

For the whole set,

Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH v2 57/57] fbdev: sh_mobile_lcdc: Implement overlays support
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |  955 +++++++++++++++++++++++++++++++++++---
 include/video/sh_mobile_lcdc.h   |    7 +
 2 files changed, 890 insertions(+), 72 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 36dad2f..b763fd8 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -12,6 +12,7 @@
 #include <linux/backlight.h>
 #include <linux/clk.h>
 #include <linux/console.h>
+#include <linux/ctype.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
@@ -32,12 +33,176 @@
 
 #include "sh_mobile_lcdcfb.h"
 
+/* ----------------------------------------------------------------------------
+ * Overlay register definitions
+ */
+
+#define LDBCR			0xb00
+#define LDBCR_UPC(n)		(1 << ((n) + 16))
+#define LDBCR_UPF(n)		(1 << ((n) + 8))
+#define LDBCR_UPD(n)		(1 << ((n) + 0))
+#define LDBnBSIFR(n)		(0xb20 + (n) * 0x20 + 0x00)
+#define LDBBSIFR_EN		(1 << 31)
+#define LDBBSIFR_VS		(1 << 29)
+#define LDBBSIFR_BRSEL		(1 << 28)
+#define LDBBSIFR_MX		(1 << 27)
+#define LDBBSIFR_MY		(1 << 26)
+#define LDBBSIFR_CV3		(3 << 24)
+#define LDBBSIFR_CV2		(2 << 24)
+#define LDBBSIFR_CV1		(1 << 24)
+#define LDBBSIFR_CV0		(0 << 24)
+#define LDBBSIFR_CV_MASK	(3 << 24)
+#define LDBBSIFR_LAY_MASK	(0xff << 16)
+#define LDBBSIFR_LAY_SHIFT	16
+#define LDBBSIFR_ROP3_MASK	(0xff << 16)
+#define LDBBSIFR_ROP3_SHIFT	16
+#define LDBBSIFR_AL_PL8		(3 << 14)
+#define LDBBSIFR_AL_PL1		(2 << 14)
+#define LDBBSIFR_AL_PK		(1 << 14)
+#define LDBBSIFR_AL_1		(0 << 14)
+#define LDBBSIFR_AL_MASK	(3 << 14)
+#define LDBBSIFR_SWPL		(1 << 10)
+#define LDBBSIFR_SWPW		(1 << 9)
+#define LDBBSIFR_SWPB		(1 << 8)
+#define LDBBSIFR_RY		(1 << 7)
+#define LDBBSIFR_CHRR_420	(2 << 0)
+#define LDBBSIFR_CHRR_422	(1 << 0)
+#define LDBBSIFR_CHRR_444	(0 << 0)
+#define LDBBSIFR_RPKF_ARGB32	(0x00 << 0)
+#define LDBBSIFR_RPKF_RGB16	(0x03 << 0)
+#define LDBBSIFR_RPKF_RGB24	(0x0b << 0)
+#define LDBBSIFR_RPKF_MASK	(0x1f << 0)
+#define LDBnBSSZR(n)		(0xb20 + (n) * 0x20 + 0x04)
+#define LDBBSSZR_BVSS_MASK	(0xfff << 16)
+#define LDBBSSZR_BVSS_SHIFT	16
+#define LDBBSSZR_BHSS_MASK	(0xfff << 0)
+#define LDBBSSZR_BHSS_SHIFT	0
+#define LDBnBLOCR(n)		(0xb20 + (n) * 0x20 + 0x08)
+#define LDBBLOCR_CVLC_MASK	(0xfff << 16)
+#define LDBBLOCR_CVLC_SHIFT	16
+#define LDBBLOCR_CHLC_MASK	(0xfff << 0)
+#define LDBBLOCR_CHLC_SHIFT	0
+#define LDBnBSMWR(n)		(0xb20 + (n) * 0x20 + 0x0c)
+#define LDBBSMWR_BSMWA_MASK	(0xffff << 16)
+#define LDBBSMWR_BSMWA_SHIFT	16
+#define LDBBSMWR_BSMW_MASK	(0xffff << 0)
+#define LDBBSMWR_BSMW_SHIFT	0
+#define LDBnBSAYR(n)		(0xb20 + (n) * 0x20 + 0x10)
+#define LDBBSAYR_FG1A_MASK	(0xff << 24)
+#define LDBBSAYR_FG1A_SHIFT	24
+#define LDBBSAYR_FG1R_MASK	(0xff << 16)
+#define LDBBSAYR_FG1R_SHIFT	16
+#define LDBBSAYR_FG1G_MASK	(0xff << 8)
+#define LDBBSAYR_FG1G_SHIFT	8
+#define LDBBSAYR_FG1B_MASK	(0xff << 0)
+#define LDBBSAYR_FG1B_SHIFT	0
+#define LDBnBSACR(n)		(0xb20 + (n) * 0x20 + 0x14)
+#define LDBBSACR_FG2A_MASK	(0xff << 24)
+#define LDBBSACR_FG2A_SHIFT	24
+#define LDBBSACR_FG2R_MASK	(0xff << 16)
+#define LDBBSACR_FG2R_SHIFT	16
+#define LDBBSACR_FG2G_MASK	(0xff << 8)
+#define LDBBSACR_FG2G_SHIFT	8
+#define LDBBSACR_FG2B_MASK	(0xff << 0)
+#define LDBBSACR_FG2B_SHIFT	0
+#define LDBnBSAAR(n)		(0xb20 + (n) * 0x20 + 0x18)
+#define LDBBSAAR_AP_MASK	(0xff << 24)
+#define LDBBSAAR_AP_SHIFT	24
+#define LDBBSAAR_R_MASK		(0xff << 16)
+#define LDBBSAAR_R_SHIFT	16
+#define LDBBSAAR_GY_MASK	(0xff << 8)
+#define LDBBSAAR_GY_SHIFT	8
+#define LDBBSAAR_B_MASK		(0xff << 0)
+#define LDBBSAAR_B_SHIFT	0
+#define LDBnBPPCR(n)		(0xb20 + (n) * 0x20 + 0x1c)
+#define LDBBPPCR_AP_MASK	(0xff << 24)
+#define LDBBPPCR_AP_SHIFT	24
+#define LDBBPPCR_R_MASK		(0xff << 16)
+#define LDBBPPCR_R_SHIFT	16
+#define LDBBPPCR_GY_MASK	(0xff << 8)
+#define LDBBPPCR_GY_SHIFT	8
+#define LDBBPPCR_B_MASK		(0xff << 0)
+#define LDBBPPCR_B_SHIFT	0
+#define LDBnBBGCL(n)		(0xb10 + (n) * 0x04)
+#define LDBBBGCL_BGA_MASK	(0xff << 24)
+#define LDBBBGCL_BGA_SHIFT	24
+#define LDBBBGCL_BGR_MASK	(0xff << 16)
+#define LDBBBGCL_BGR_SHIFT	16
+#define LDBBBGCL_BGG_MASK	(0xff << 8)
+#define LDBBBGCL_BGG_SHIFT	8
+#define LDBBBGCL_BGB_MASK	(0xff << 0)
+#define LDBBBGCL_BGB_SHIFT	0
+
 #define SIDE_B_OFFSET 0x1000
 #define MIRROR_OFFSET 0x2000
 
 #define MAX_XRES 1920
 #define MAX_YRES 1080
 
+enum sh_mobile_lcdc_overlay_mode {
+	LCDC_OVERLAY_BLEND,
+	LCDC_OVERLAY_ROP3,
+};
+
+/*
+ * struct sh_mobile_lcdc_overlay - LCDC display overlay
+ *
+ * @channel: LCDC channel this overlay belongs to
+ * @cfg: Overlay configuration
+ * @info: Frame buffer device
+ * @index: Overlay index (0-3)
+ * @base: Overlay registers base address
+ * @enabled: True if the overlay is enabled
+ * @mode: Overlay blending mode (alpha blend or ROP3)
+ * @alpha: Global alpha blending value (0-255, for alpha blending mode)
+ * @rop3: Raster operation (for ROP3 mode)
+ * @fb_mem: Frame buffer virtual memory address
+ * @fb_size: Frame buffer size in bytes
+ * @dma_handle: Frame buffer DMA address
+ * @base_addr_y: Overlay base address (RGB or luma component)
+ * @base_addr_c: Overlay base address (chroma component)
+ * @pan_offset: Current pan offset in bytes
+ * @format: Current pixelf format
+ * @xres: Horizontal visible resolution
+ * @xres_virtual: Horizontal total resolution
+ * @yres: Vertical visible resolution
+ * @yres_virtual: Vertical total resolution
+ * @pitch: Overlay line pitch
+ * @pos_x: Horizontal overlay position
+ * @pos_y: Vertical overlay position
+ */
+struct sh_mobile_lcdc_overlay {
+	struct sh_mobile_lcdc_chan *channel;
+
+	const struct sh_mobile_lcdc_overlay_cfg *cfg;
+	struct fb_info *info;
+
+	unsigned int index;
+	unsigned long base;
+
+	bool enabled;
+	enum sh_mobile_lcdc_overlay_mode mode;
+	unsigned int alpha;
+	unsigned int rop3;
+
+	void *fb_mem;
+	unsigned long fb_size;
+
+	dma_addr_t dma_handle;
+	unsigned long base_addr_y;
+	unsigned long base_addr_c;
+	unsigned long pan_offset;
+
+	const struct sh_mobile_lcdc_format_info *format;
+	unsigned int xres;
+	unsigned int xres_virtual;
+	unsigned int yres;
+	unsigned int yres_virtual;
+	unsigned int pitch;
+	int pos_x;
+	int pos_y;
+};
+
 struct sh_mobile_lcdc_priv {
 	void __iomem *base;
 	int irq;
@@ -45,7 +210,10 @@ struct sh_mobile_lcdc_priv {
 	struct device *dev;
 	struct clk *dot_clk;
 	unsigned long lddckr;
+
 	struct sh_mobile_lcdc_chan ch[2];
+	struct sh_mobile_lcdc_overlay overlays[4];
+
 	struct notifier_block notifier;
 	int started;
 	int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
@@ -141,6 +309,13 @@ static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
 	return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
 }
 
+static void lcdc_write_overlay(struct sh_mobile_lcdc_overlay *ovl,
+			       int reg, unsigned long data)
+{
+	iowrite32(data, ovl->channel->lcdc->base + reg);
+	iowrite32(data, ovl->channel->lcdc->base + reg + SIDE_B_OFFSET);
+}
+
 static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
 		       unsigned long reg_offs, unsigned long data)
 {
@@ -685,6 +860,93 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
 	lcdc_write_chan(ch, LDHAJR, tmp);
 }
 
+static void sh_mobile_lcdc_overlay_setup(struct sh_mobile_lcdc_overlay *ovl)
+{
+	u32 format = 0;
+
+	if (!ovl->enabled) {
+		lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), 0);
+		return;
+	}
+
+	ovl->base_addr_y = ovl->dma_handle;
+	ovl->base_addr_c = ovl->base_addr_y + ovl->xres
+			   * ovl->yres_virtual;
+
+	switch (ovl->mode) {
+	case LCDC_OVERLAY_BLEND:
+		format = LDBBSIFR_EN | (ovl->alpha << LDBBSIFR_LAY_SHIFT);
+		break;
+
+	case LCDC_OVERLAY_ROP3:
+		format = LDBBSIFR_EN | LDBBSIFR_BRSEL
+		       | (ovl->rop3 << LDBBSIFR_ROP3_SHIFT);
+		break;
+	}
+
+	switch (ovl->format->fourcc) {
+	case V4L2_PIX_FMT_RGB565:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV61:
+	case V4L2_PIX_FMT_NV42:
+		format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW;
+		break;
+	case V4L2_PIX_FMT_BGR24:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV24:
+		format |= LDBBSIFR_SWPL | LDBBSIFR_SWPW | LDBBSIFR_SWPB;
+		break;
+	case V4L2_PIX_FMT_BGR32:
+	default:
+		format |= LDBBSIFR_SWPL;
+		break;
+	}
+
+	switch (ovl->format->fourcc) {
+	case V4L2_PIX_FMT_RGB565:
+		format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB16;
+		break;
+	case V4L2_PIX_FMT_BGR24:
+		format |= LDBBSIFR_AL_1 | LDBBSIFR_RY | LDBBSIFR_RPKF_RGB24;
+		break;
+	case V4L2_PIX_FMT_BGR32:
+		format |= LDBBSIFR_AL_PK | LDBBSIFR_RY | LDDFR_PKF_ARGB32;
+		break;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_420;
+		break;
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_422;
+		break;
+	case V4L2_PIX_FMT_NV24:
+	case V4L2_PIX_FMT_NV42:
+		format |= LDBBSIFR_AL_1 | LDBBSIFR_CHRR_444;
+		break;
+	}
+
+	lcdc_write(ovl->channel->lcdc, LDBCR, LDBCR_UPC(ovl->index));
+
+	lcdc_write_overlay(ovl, LDBnBSIFR(ovl->index), format);
+
+	lcdc_write_overlay(ovl, LDBnBSSZR(ovl->index),
+		(ovl->yres << LDBBSSZR_BVSS_SHIFT) |
+		(ovl->xres << LDBBSSZR_BHSS_SHIFT));
+	lcdc_write_overlay(ovl, LDBnBLOCR(ovl->index),
+		(ovl->pos_y << LDBBLOCR_CVLC_SHIFT) |
+		(ovl->pos_x << LDBBLOCR_CHLC_SHIFT));
+	lcdc_write_overlay(ovl, LDBnBSMWR(ovl->index),
+		ovl->pitch << LDBBSMWR_BSMW_SHIFT);
+
+	lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+	lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+	lcdc_write(ovl->channel->lcdc, LDBCR,
+		   LDBCR_UPF(ovl->index) | LDBCR_UPD(ovl->index));
+}
+
 /*
  * __sh_mobile_lcdc_start - Configure and start the LCDC
  * @priv: LCDC device
@@ -891,6 +1153,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		}
 	}
 
+	for (k = 0; k < ARRAY_SIZE(priv->overlays); ++k) {
+		struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[k];
+		sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
 	/* Start the LCDC. */
 	__sh_mobile_lcdc_start(priv);
 
@@ -974,8 +1241,539 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
 			sh_mobile_lcdc_clk_off(priv);
 }
 
+static int __sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+				      struct fb_info *info)
+{
+	if (var->xres > MAX_XRES || var->yres > MAX_YRES)
+		return -EINVAL;
+
+	/* Make sure the virtual resolution is at least as big as the visible
+	 * resolution.
+	 */
+	if (var->xres_virtual < var->xres)
+		var->xres_virtual = var->xres;
+	if (var->yres_virtual < var->yres)
+		var->yres_virtual = var->yres;
+
+	if (sh_mobile_format_is_fourcc(var)) {
+		const struct sh_mobile_lcdc_format_info *format;
+
+		format = sh_mobile_format_info(var->grayscale);
+		if (format = NULL)
+			return -EINVAL;
+		var->bits_per_pixel = format->bpp;
+
+		/* Default to RGB and JPEG color-spaces for RGB and YUV formats
+		 * respectively.
+		 */
+		if (!format->yuv)
+			var->colorspace = V4L2_COLORSPACE_SRGB;
+		else if (var->colorspace != V4L2_COLORSPACE_REC709)
+			var->colorspace = V4L2_COLORSPACE_JPEG;
+	} else {
+		if (var->bits_per_pixel <= 16) {		/* RGB 565 */
+			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;
+		} else if (var->bits_per_pixel <= 24) {		/* RGB 888 */
+			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;
+		} else if (var->bits_per_pixel <= 32) {		/* RGBA 888 */
+			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;
+		} else
+			return -EINVAL;
+
+		var->red.msb_right = 0;
+		var->green.msb_right = 0;
+		var->blue.msb_right = 0;
+		var->transp.msb_right = 0;
+	}
+
+	/* Make sure we don't exceed our allocated memory. */
+	if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
+	    info->fix.smem_len)
+		return -EINVAL;
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations - Overlays
+ */
+
+static ssize_t
+overlay_alpha_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->alpha);
+}
+
+static ssize_t
+overlay_alpha_store(struct device *dev, struct device_attribute *attr,
+		    const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	unsigned int alpha;
+	char *endp;
+
+	alpha = simple_strtoul(buf, &endp, 10);
+	if (isspace(*endp))
+		endp++;
+
+	if (endp - buf != count)
+		return -EINVAL;
+
+	if (alpha > 255)
+		return -EINVAL;
+
+	if (ovl->alpha != alpha) {
+		ovl->alpha = alpha;
+
+		if (ovl->mode = LCDC_OVERLAY_BLEND && ovl->enabled)
+			sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
+	return count;
+}
+
+static ssize_t
+overlay_enabled_show(struct device *dev, struct device_attribute *attr,
+		     char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->enabled);
+}
+
+static ssize_t
+overlay_enabled_store(struct device *dev, struct device_attribute *attr,
+		      const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	bool enabled;
+	char *endp;
+
+	enabled = !!simple_strtoul(buf, &endp, 10);
+	if (isspace(*endp))
+		endp++;
+
+	if (endp - buf != count)
+		return -EINVAL;
+
+	if (ovl->enabled != enabled) {
+		ovl->enabled = enabled;
+		sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
+	return count;
+}
+
+static ssize_t
+overlay_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->mode);
+}
+
+static ssize_t
+overlay_mode_store(struct device *dev, struct device_attribute *attr,
+		   const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	unsigned int mode;
+	char *endp;
+
+	mode = simple_strtoul(buf, &endp, 10);
+	if (isspace(*endp))
+		endp++;
+
+	if (endp - buf != count)
+		return -EINVAL;
+
+	if (mode != LCDC_OVERLAY_BLEND && mode != LCDC_OVERLAY_ROP3)
+		return -EINVAL;
+
+	if (ovl->mode != mode) {
+		ovl->mode = mode;
+
+		if (ovl->enabled)
+			sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
+	return count;
+}
+
+static ssize_t
+overlay_position_show(struct device *dev, struct device_attribute *attr,
+		      char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	return scnprintf(buf, PAGE_SIZE, "%d,%d\n", ovl->pos_x, ovl->pos_y);
+}
+
+static ssize_t
+overlay_position_store(struct device *dev, struct device_attribute *attr,
+		       const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	char *endp;
+	int pos_x;
+	int pos_y;
+
+	pos_x = simple_strtol(buf, &endp, 10);
+	if (*endp != ',')
+		return -EINVAL;
+
+	pos_y = simple_strtol(endp + 1, &endp, 10);
+	if (isspace(*endp))
+		endp++;
+
+	if (endp - buf != count)
+		return -EINVAL;
+
+	if (ovl->pos_x != pos_x || ovl->pos_y != pos_y) {
+		ovl->pos_x = pos_x;
+		ovl->pos_y = pos_y;
+
+		if (ovl->enabled)
+			sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
+	return count;
+}
+
+static ssize_t
+overlay_rop3_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", ovl->rop3);
+}
+
+static ssize_t
+overlay_rop3_store(struct device *dev, struct device_attribute *attr,
+		    const char *buf, size_t count)
+{
+	struct fb_info *info = dev_get_drvdata(dev);
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	unsigned int rop3;
+	char *endp;
+
+	rop3 = !!simple_strtoul(buf, &endp, 10);
+	if (isspace(*endp))
+		endp++;
+
+	if (endp - buf != count)
+		return -EINVAL;
+
+	if (rop3 > 255)
+		return -EINVAL;
+
+	if (ovl->rop3 != rop3) {
+		ovl->rop3 = rop3;
+
+		if (ovl->mode = LCDC_OVERLAY_ROP3 && ovl->enabled)
+			sh_mobile_lcdc_overlay_setup(ovl);
+	}
+
+	return count;
+}
+
+static const struct device_attribute overlay_sysfs_attrs[] = {
+	__ATTR(ovl_alpha, S_IRUGO|S_IWUSR,
+	       overlay_alpha_show, overlay_alpha_store),
+	__ATTR(ovl_enabled, S_IRUGO|S_IWUSR,
+	       overlay_enabled_show, overlay_enabled_store),
+	__ATTR(ovl_mode, S_IRUGO|S_IWUSR,
+	       overlay_mode_show, overlay_mode_store),
+	__ATTR(ovl_position, S_IRUGO|S_IWUSR,
+	       overlay_position_show, overlay_position_store),
+	__ATTR(ovl_rop3, S_IRUGO|S_IWUSR,
+	       overlay_rop3_show, overlay_rop3_store),
+};
+
+static const struct fb_fix_screeninfo sh_mobile_lcdc_overlay_fix  = {
+	.id =		"SH Mobile LCDC",
+	.type =		FB_TYPE_PACKED_PIXELS,
+	.visual =	FB_VISUAL_TRUECOLOR,
+	.accel =	FB_ACCEL_NONE,
+	.xpanstep =	0,
+	.ypanstep =	1,
+	.ywrapstep =	0,
+	.capabilities =	FB_CAP_FOURCC,
+};
+
+static int sh_mobile_lcdc_overlay_pan(struct fb_var_screeninfo *var,
+				    struct fb_info *info)
+{
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+	unsigned long base_addr_y;
+	unsigned long base_addr_c;
+	unsigned long pan_offset;
+	unsigned long c_offset;
+
+	if (!ovl->format->yuv)
+		pan_offset = var->yoffset * ovl->pitch
+			   + var->xoffset * (ovl->format->bpp / 8);
+	else
+		pan_offset = var->yoffset * ovl->pitch + var->xoffset;
+
+	if (pan_offset = ovl->pan_offset)
+		return 0;	/* No change, do nothing */
+
+	/* Set the source address for the next refresh */
+	base_addr_y = ovl->dma_handle + pan_offset;
+
+	ovl->base_addr_y = base_addr_y;
+	ovl->base_addr_c = base_addr_y;
+
+	if (ovl->format->yuv) {
+		/* Set Y offset */
+		c_offset = var->yoffset * ovl->pitch
+			 * (ovl->format->bpp - 8) / 8;
+		base_addr_c = ovl->dma_handle
+			    + ovl->xres * ovl->yres_virtual
+			    + c_offset;
+		/* Set X offset */
+		if (ovl->format->fourcc = V4L2_PIX_FMT_NV24)
+			base_addr_c += 2 * var->xoffset;
+		else
+			base_addr_c += var->xoffset;
+
+		ovl->base_addr_c = base_addr_c;
+	}
+
+	lcdc_write_overlay(ovl, LDBnBSAYR(ovl->index), ovl->base_addr_y);
+	lcdc_write_overlay(ovl, LDBnBSACR(ovl->index), ovl->base_addr_c);
+
+	ovl->pan_offset = pan_offset;
+
+	return 0;
+}
+
+static int sh_mobile_lcdc_overlay_ioctl(struct fb_info *info, unsigned int cmd,
+				      unsigned long arg)
+{
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	switch (cmd) {
+	case FBIO_WAITFORVSYNC:
+		return sh_mobile_lcdc_wait_for_vsync(ovl->channel);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static int sh_mobile_lcdc_overlay_check_var(struct fb_var_screeninfo *var,
+					  struct fb_info *info)
+{
+	return __sh_mobile_lcdc_check_var(var, info);
+}
+
+static int sh_mobile_lcdc_overlay_set_par(struct fb_info *info)
+{
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	ovl->format +		sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+
+	ovl->xres = info->var.xres;
+	ovl->xres_virtual = info->var.xres_virtual;
+	ovl->yres = info->var.yres;
+	ovl->yres_virtual = info->var.yres_virtual;
+
+	if (ovl->format->yuv)
+		ovl->pitch = info->var.xres;
+	else
+		ovl->pitch = info->var.xres * ovl->format->bpp / 8;
+
+	sh_mobile_lcdc_overlay_setup(ovl);
+
+	info->fix.line_length = ovl->pitch;
+
+	if (sh_mobile_format_is_fourcc(&info->var)) {
+		info->fix.type = FB_TYPE_FOURCC;
+		info->fix.visual = FB_VISUAL_FOURCC;
+	} else {
+		info->fix.type = FB_TYPE_PACKED_PIXELS;
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+	}
+
+	return 0;
+}
+
+/* Overlay blanking. Disable the overlay when blanked. */
+static int sh_mobile_lcdc_overlay_blank(int blank, struct fb_info *info)
+{
+	struct sh_mobile_lcdc_overlay *ovl = info->par;
+
+	printk(KERN_INFO "%s(%u)\n", __func__, blank);
+	ovl->enabled = !blank;
+
+	return 0;
+}
+
+static struct fb_ops sh_mobile_lcdc_overlay_ops = {
+	.owner          = THIS_MODULE,
+	.fb_read        = fb_sys_read,
+	.fb_write       = fb_sys_write,
+	.fb_fillrect	= sys_fillrect,
+	.fb_copyarea	= sys_copyarea,
+	.fb_imageblit	= sys_imageblit,
+	.fb_blank	= sh_mobile_lcdc_overlay_blank,
+	.fb_pan_display = sh_mobile_lcdc_overlay_pan,
+	.fb_ioctl       = sh_mobile_lcdc_overlay_ioctl,
+	.fb_check_var	= sh_mobile_lcdc_overlay_check_var,
+	.fb_set_par	= sh_mobile_lcdc_overlay_set_par,
+};
+
+static void
+sh_mobile_lcdc_overlay_fb_unregister(struct sh_mobile_lcdc_overlay *ovl)
+{
+	struct fb_info *info = ovl->info;
+
+	if (info = NULL || info->dev = NULL)
+		return;
+
+	unregister_framebuffer(ovl->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_register(struct sh_mobile_lcdc_overlay *ovl)
+{
+	struct sh_mobile_lcdc_priv *lcdc = ovl->channel->lcdc;
+	struct fb_info *info = ovl->info;
+	unsigned int i;
+	int ret;
+
+	if (info = NULL)
+		return 0;
+
+	ret = register_framebuffer(info);
+	if (ret < 0)
+		return ret;
+
+	dev_info(lcdc->dev, "registered %s/overlay %u as %dx%d %dbpp.\n",
+		 dev_name(lcdc->dev), ovl->index, info->var.xres,
+		 info->var.yres, info->var.bits_per_pixel);
+
+	for (i = 0; i < ARRAY_SIZE(overlay_sysfs_attrs); ++i) {
+		ret = device_create_file(info->dev, &overlay_sysfs_attrs[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void
+sh_mobile_lcdc_overlay_fb_cleanup(struct sh_mobile_lcdc_overlay *ovl)
+{
+	struct fb_info *info = ovl->info;
+
+	if (info = NULL || info->device = NULL)
+		return;
+
+	framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_overlay_fb_init(struct sh_mobile_lcdc_overlay *ovl)
+{
+	struct sh_mobile_lcdc_priv *priv = ovl->channel->lcdc;
+	struct fb_var_screeninfo *var;
+	struct fb_info *info;
+
+	/* Allocate and initialize the frame buffer device. */
+	info = framebuffer_alloc(0, priv->dev);
+	if (info = NULL) {
+		dev_err(priv->dev, "unable to allocate fb_info\n");
+		return -ENOMEM;
+	}
+
+	ovl->info = info;
+
+	info->flags = FBINFO_FLAG_DEFAULT;
+	info->fbops = &sh_mobile_lcdc_overlay_ops;
+	info->device = priv->dev;
+	info->screen_base = ovl->fb_mem;
+	info->par = ovl;
+
+	/* Initialize fixed screen information. Restrict pan to 2 lines steps
+	 * for NV12 and NV21.
+	 */
+	info->fix = sh_mobile_lcdc_overlay_fix;
+	snprintf(info->fix.id, sizeof(info->fix.id),
+		 "SH Mobile LCDC Overlay %u", ovl->index);
+	info->fix.smem_start = ovl->dma_handle;
+	info->fix.smem_len = ovl->fb_size;
+	info->fix.line_length = ovl->pitch;
+
+	if (ovl->format->yuv)
+		info->fix.visual = FB_VISUAL_FOURCC;
+	else
+		info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+	if (ovl->format->fourcc = V4L2_PIX_FMT_NV12 ||
+	    ovl->format->fourcc = V4L2_PIX_FMT_NV21)
+		info->fix.ypanstep = 2;
+
+	/* Initialize variable screen information. */
+	var = &info->var;
+	memset(var, 0, sizeof(*var));
+	var->xres = ovl->xres;
+	var->yres = ovl->yres;
+	var->xres_virtual = ovl->xres_virtual;
+	var->yres_virtual = ovl->yres_virtual;
+	var->activate = FB_ACTIVATE_NOW;
+
+	/* Use the legacy API by default for RGB formats, and the FOURCC API
+	 * for YUV formats.
+	 */
+	if (!ovl->format->yuv)
+		var->bits_per_pixel = ovl->format->bpp;
+	else
+		var->grayscale = ovl->format->fourcc;
+
+	return sh_mobile_lcdc_overlay_check_var(var, info);
+}
+
 /* -----------------------------------------------------------------------------
- * Frame buffer operations
+ * Frame buffer operations - main frame buffer
  */
 
 static int sh_mobile_lcdc_setcolreg(u_int regno,
@@ -1201,9 +1999,7 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
 	unsigned int best_xres = 0;
 	unsigned int best_yres = 0;
 	unsigned int i;
-
-	if (var->xres > MAX_XRES || var->yres > MAX_YRES)
-		return -EINVAL;
+	int ret;
 
 	/* If board code provides us with a list of available modes, make sure
 	 * we use one of them. Find the mode closest to the requested one. The
@@ -1238,73 +2034,9 @@ static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
 		var->yres = best_yres;
 	}
 
-	/* Make sure the virtual resolution is at least as big as the visible
-	 * resolution.
-	 */
-	if (var->xres_virtual < var->xres)
-		var->xres_virtual = var->xres;
-	if (var->yres_virtual < var->yres)
-		var->yres_virtual = var->yres;
-
-	if (sh_mobile_format_is_fourcc(var)) {
-		const struct sh_mobile_lcdc_format_info *format;
-
-		format = sh_mobile_format_info(var->grayscale);
-		if (format = NULL)
-			return -EINVAL;
-		var->bits_per_pixel = format->bpp;
-
-		/* Default to RGB and JPEG color-spaces for RGB and YUV formats
-		 * respectively.
-		 */
-		if (!format->yuv)
-			var->colorspace = V4L2_COLORSPACE_SRGB;
-		else if (var->colorspace != V4L2_COLORSPACE_REC709)
-			var->colorspace = V4L2_COLORSPACE_JPEG;
-	} else {
-		if (var->bits_per_pixel <= 16) {		/* RGB 565 */
-			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;
-		} else if (var->bits_per_pixel <= 24) {		/* RGB 888 */
-			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;
-		} else if (var->bits_per_pixel <= 32) {		/* RGBA 888 */
-			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;
-		} else
-			return -EINVAL;
-
-		var->red.msb_right = 0;
-		var->green.msb_right = 0;
-		var->blue.msb_right = 0;
-		var->transp.msb_right = 0;
-	}
-
-	/* Make sure we don't exceed our allocated memory. */
-	if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 >
-	    info->fix.smem_len)
-		return -EINVAL;
+	ret = __sh_mobile_lcdc_check_var(var, info);
+	if (ret < 0)
+		return ret;
 
 	/* only accept the forced_fourcc for dual channel configurations */
 	if (p->forced_fourcc &&
@@ -1713,15 +2445,20 @@ static const struct fb_videomode default_720p __devinitconst = {
 static int sh_mobile_lcdc_remove(struct platform_device *pdev)
 {
 	struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
-	int i;
+	unsigned int i;
 
 	fb_unregister_client(&priv->notifier);
 
+	for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+		sh_mobile_lcdc_overlay_fb_unregister(&priv->overlays[i]);
 	for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
 		sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
 
 	sh_mobile_lcdc_stop(priv);
 
+	for (i = 0; i < ARRAY_SIZE(priv->overlays); i++)
+		sh_mobile_lcdc_overlay_fb_cleanup(&priv->overlays[i]);
+
 	for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
 		struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
 
@@ -1797,6 +2534,61 @@ static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *
 }
 
 static int __devinit
+sh_mobile_lcdc_overlay_init(struct sh_mobile_lcdc_priv *priv,
+			  struct sh_mobile_lcdc_overlay *ovl)
+{
+	const struct sh_mobile_lcdc_format_info *format;
+	int ret;
+
+	if (ovl->cfg->fourcc = 0)
+		return 0;
+
+	/* Validate the format. */
+	format = sh_mobile_format_info(ovl->cfg->fourcc);
+	if (format = NULL) {
+		dev_err(priv->dev, "Invalid FOURCC %08x\n", ovl->cfg->fourcc);
+		return -EINVAL;
+	}
+
+	ovl->enabled = false;
+	ovl->mode = LCDC_OVERLAY_BLEND;
+	ovl->alpha = 255;
+	ovl->rop3 = 0;
+	ovl->pos_x = 0;
+	ovl->pos_y = 0;
+
+	/* The default Y virtual resolution is twice the panel size to allow for
+	 * double-buffering.
+	 */
+	ovl->format = format;
+	ovl->xres = ovl->cfg->max_xres;
+	ovl->xres_virtual = ovl->xres;
+	ovl->yres = ovl->cfg->max_yres;
+	ovl->yres_virtual = ovl->yres * 2;
+
+	if (!format->yuv)
+		ovl->pitch = ovl->xres * format->bpp / 8;
+	else
+		ovl->pitch = ovl->xres;
+
+	/* Allocate frame buffer memory. */
+	ovl->fb_size = ovl->cfg->max_xres * ovl->cfg->max_yres
+		       * format->bpp / 8 * 2;
+	ovl->fb_mem = dma_alloc_coherent(priv->dev, ovl->fb_size,
+					   &ovl->dma_handle, GFP_KERNEL);
+	if (!ovl->fb_mem) {
+		dev_err(priv->dev, "unable to allocate buffer\n");
+		return -ENOMEM;
+	}
+
+	ret = sh_mobile_lcdc_overlay_fb_init(ovl);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int __devinit
 sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
 			    struct sh_mobile_lcdc_chan *ch)
 {
@@ -2004,6 +2796,17 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 			goto err1;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+		struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+		ovl->cfg = &pdata->overlays[i];
+		ovl->channel = &priv->ch[0];
+
+		error = sh_mobile_lcdc_overlay_init(priv, ovl);
+		if (error)
+			goto err1;
+	}
+
 	error = sh_mobile_lcdc_start(priv);
 	if (error) {
 		dev_err(&pdev->dev, "unable to start hardware\n");
@@ -2018,6 +2821,14 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 			goto err1;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(pdata->overlays); i++) {
+		struct sh_mobile_lcdc_overlay *ovl = &priv->overlays[i];
+
+		error = sh_mobile_lcdc_overlay_fb_register(ovl);
+		if (error)
+			goto err1;
+	}
+
 	/* Failure ignored */
 	priv->notifier.notifier_call = sh_mobile_lcdc_notify;
 	fb_register_client(&priv->notifier);
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 7571b27..ff43ffc 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -166,6 +166,12 @@ struct sh_mobile_lcdc_bl_info {
 	int (*get_brightness)(void);
 };
 
+struct sh_mobile_lcdc_overlay_cfg {
+	int fourcc;
+	unsigned int max_xres;
+	unsigned int max_yres;
+};
+
 struct sh_mobile_lcdc_chan_cfg {
 	int chan;
 	int fourcc;
@@ -186,6 +192,7 @@ struct sh_mobile_lcdc_chan_cfg {
 struct sh_mobile_lcdc_info {
 	int clock_source;
 	struct sh_mobile_lcdc_chan_cfg ch[2];
+	struct sh_mobile_lcdc_overlay_cfg overlays[4];
 	struct sh_mobile_meram_info *meram_dev;
 };
 
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 56/57] fbdev: sh_mobile_lcdc: Rename fb operation handlers with a common prefix
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

Make all fb operation handlers start with sh_mobile_lcdc_ in preparation
for the multi-plane support.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |   48 +++++++++++++++++++------------------
 1 files changed, 25 insertions(+), 23 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index b6fb8a4..36dad2f 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -384,8 +384,8 @@ sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
 	return true;
 }
 
-static int sh_mobile_check_var(struct fb_var_screeninfo *var,
-			       struct fb_info *info);
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+				    struct fb_info *info);
 
 static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
 					 enum sh_mobile_lcdc_entity_event event,
@@ -439,7 +439,7 @@ static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
 		fb_videomode_to_var(&var, mode);
 		var.bits_per_pixel = info->var.bits_per_pixel;
 		var.grayscale = info->var.grayscale;
-		ret = sh_mobile_check_var(&var, info);
+		ret = sh_mobile_lcdc_check_var(&var, info);
 		break;
 	}
 
@@ -585,7 +585,7 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
-static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+static int sh_mobile_lcdc_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
 {
 	unsigned long ldintr;
 	int ret;
@@ -686,7 +686,7 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
 }
 
 /*
- * __sh_mobile_lcdc_start - Configure and tart the LCDC
+ * __sh_mobile_lcdc_start - Configure and start the LCDC
  * @priv: LCDC device
  *
  * Configure all enabled channels and start the LCDC device. All external
@@ -1034,8 +1034,8 @@ static void sh_mobile_lcdc_imageblit(struct fb_info *info,
 	sh_mobile_lcdc_deferred_io_touch(info);
 }
 
-static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
-				     struct fb_info *info)
+static int sh_mobile_lcdc_pan(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 	struct sh_mobile_lcdc_priv *priv = ch->lcdc;
@@ -1098,14 +1098,15 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
 	return 0;
 }
 
-static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
-		       unsigned long arg)
+static int sh_mobile_lcdc_ioctl(struct fb_info *info, unsigned int cmd,
+				unsigned long arg)
 {
+	struct sh_mobile_lcdc_chan *ch = info->par;
 	int retval;
 
 	switch (cmd) {
 	case FBIO_WAITFORVSYNC:
-		retval = sh_mobile_wait_for_vsync(info->par);
+		retval = sh_mobile_lcdc_wait_for_vsync(ch);
 		break;
 
 	default:
@@ -1157,7 +1158,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
  * Locking: both .fb_release() and .fb_open() are called with info->lock held if
  * user = 1, or with console sem held, if user = 0.
  */
-static int sh_mobile_release(struct fb_info *info, int user)
+static int sh_mobile_lcdc_release(struct fb_info *info, int user)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 
@@ -1178,7 +1179,7 @@ static int sh_mobile_release(struct fb_info *info, int user)
 	return 0;
 }
 
-static int sh_mobile_open(struct fb_info *info, int user)
+static int sh_mobile_lcdc_open(struct fb_info *info, int user)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 
@@ -1191,7 +1192,8 @@ static int sh_mobile_open(struct fb_info *info, int user)
 	return 0;
 }
 
-static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+static int sh_mobile_lcdc_check_var(struct fb_var_screeninfo *var,
+				    struct fb_info *info)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 	struct sh_mobile_lcdc_priv *p = ch->lcdc;
@@ -1312,7 +1314,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
 	return 0;
 }
 
-static int sh_mobile_set_par(struct fb_info *info)
+static int sh_mobile_lcdc_set_par(struct fb_info *info)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 	int ret;
@@ -1382,8 +1384,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
 		 * mode will reenable the clocks and update the screen in time,
 		 * so it does not need this. */
 		if (!info->fbdefio) {
-			sh_mobile_wait_for_vsync(ch);
-			sh_mobile_wait_for_vsync(ch);
+			sh_mobile_lcdc_wait_for_vsync(ch);
+			sh_mobile_lcdc_wait_for_vsync(ch);
 		}
 		sh_mobile_lcdc_clk_off(p);
 	}
@@ -1401,12 +1403,12 @@ static struct fb_ops sh_mobile_lcdc_ops = {
 	.fb_copyarea	= sh_mobile_lcdc_copyarea,
 	.fb_imageblit	= sh_mobile_lcdc_imageblit,
 	.fb_blank	= sh_mobile_lcdc_blank,
-	.fb_pan_display = sh_mobile_fb_pan_display,
-	.fb_ioctl       = sh_mobile_ioctl,
-	.fb_open	= sh_mobile_open,
-	.fb_release	= sh_mobile_release,
-	.fb_check_var	= sh_mobile_check_var,
-	.fb_set_par	= sh_mobile_set_par,
+	.fb_pan_display = sh_mobile_lcdc_pan,
+	.fb_ioctl       = sh_mobile_lcdc_ioctl,
+	.fb_open	= sh_mobile_lcdc_open,
+	.fb_release	= sh_mobile_lcdc_release,
+	.fb_check_var	= sh_mobile_lcdc_check_var,
+	.fb_set_par	= sh_mobile_lcdc_set_par,
 };
 
 static void
@@ -1536,7 +1538,7 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
 	else
 		var->grayscale = ch->format->fourcc;
 
-	ret = sh_mobile_check_var(var, info);
+	ret = sh_mobile_lcdc_check_var(var, info);
 	if (ret)
 		return ret;
 
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 55/57] fbdev: sh_mobile_lcdc: Constify sh_mobile_lcdc_fix structure
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

The structure is only read, make it const.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 7a0b301..b6fb8a4 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -1002,7 +1002,7 @@ static int sh_mobile_lcdc_setcolreg(u_int regno,
 	return 0;
 }
 
-static struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
+static const struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
 	.id =		"SH Mobile LCDC",
 	.type =		FB_TYPE_PACKED_PIXELS,
 	.visual =	FB_VISUAL_TRUECOLOR,
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 54/57] fbdev: sh_mobile_meram: Remove unneeded sanity checks
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

The meram_register(), meram_unregister() and meram_update() operations
check that the pointers they get from the caller are not NULL. Those
checks can be remove, as the caller already ensures that the pointers
are valid.

The platform sanity checks can also be removed, as the operations can't
be accessed without valid platform data anyway.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |    5 +----
 drivers/video/sh_mobile_meram.c  |   32 ++++++--------------------------
 include/video/sh_mobile_meram.h  |   15 +++++++--------
 3 files changed, 14 insertions(+), 38 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 4b54cd5..7a0b301 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -1072,14 +1072,11 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
 
 	if (ch->meram) {
 		struct sh_mobile_meram_info *mdev;
-		int ret;
 
 		mdev = priv->meram_dev;
-		ret = mdev->ops->meram_update(mdev, ch->meram,
+		mdev->ops->meram_update(mdev, ch->meram,
 					base_addr_y, base_addr_c,
 					&base_addr_y, &base_addr_c);
-		if (ret)
-			return ret;
 	}
 
 	ch->base_addr_y = base_addr_y;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 2ce0d8f..d9f7a44 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -451,21 +451,15 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
 				      unsigned int *pitch)
 {
 	struct sh_mobile_meram_fb_cache *cache;
-	struct sh_mobile_meram_priv *priv;
-	struct platform_device *pdev;
+	struct sh_mobile_meram_priv *priv = pdata->priv;
+	struct platform_device *pdev = pdata->pdev;
 	unsigned int out_pitch;
 
-	if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
-		return ERR_PTR(-EINVAL);
-
 	if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
 	    pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
 	    pixelformat != SH_MOBILE_MERAM_PF_RGB)
 		return ERR_PTR(-EINVAL);
 
-	priv = pdata->priv;
-	pdev = pdata->pdev;
-
 	dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
 		!pixelformat ? "yuv" : "rgb");
 
@@ -500,16 +494,11 @@ err:
 	return cache;
 }
 
-static int
+static void
 sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
 {
 	struct sh_mobile_meram_fb_cache *cache = data;
-	struct sh_mobile_meram_priv *priv;
-
-	if (!pdata || !pdata->priv || !data)
-		return -EINVAL;
-
-	priv = pdata->priv;
+	struct sh_mobile_meram_priv *priv = pdata->priv;
 
 	mutex_lock(&priv->lock);
 
@@ -521,22 +510,15 @@ sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
 	meram_free(priv, cache);
 
 	mutex_unlock(&priv->lock);
-
-	return 0;
 }
 
-static int
+static void
 sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
 		       unsigned long base_addr_y, unsigned long base_addr_c,
 		       unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 {
 	struct sh_mobile_meram_fb_cache *cache = data;
-	struct sh_mobile_meram_priv *priv;
-
-	if (!pdata || !pdata->priv || !data)
-		return -EINVAL;
-
-	priv = pdata->priv;
+	struct sh_mobile_meram_priv *priv = pdata->priv;
 
 	mutex_lock(&priv->lock);
 
@@ -544,8 +526,6 @@ sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
 	meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
 
 	mutex_unlock(&priv->lock);
-
-	return 0;
 }
 
 static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 553335c..29b2fd3 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -49,16 +49,15 @@ struct sh_mobile_meram_ops {
 				unsigned int *pitch);
 
 	/* unregister usage of meram */
-	int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
-				void *data);
+	void (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
+				 void *data);
 
 	/* update meram settings */
-	int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
-			    void *data,
-			    unsigned long base_addr_y,
-			    unsigned long base_addr_c,
-			    unsigned long *icb_addr_y,
-			    unsigned long *icb_addr_c);
+	void (*meram_update)(struct sh_mobile_meram_info *meram_dev, void *data,
+			     unsigned long base_addr_y,
+			     unsigned long base_addr_c,
+			     unsigned long *icb_addr_y,
+			     unsigned long *icb_addr_c);
 };
 
 #endif /* __VIDEO_SH_MOBILE_MERAM_H__  */
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 53/57] fbdev: sh_mobile_meram: Don't perform update in register operation
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

Remove the RGB or Y/C base address update from the meram_register()
operation, as this belongs to the meram_update() operation.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |    8 +++++---
 drivers/video/sh_mobile_meram.c  |   15 ++-------------
 include/video/sh_mobile_meram.h  |    4 ----
 3 files changed, 7 insertions(+), 20 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index 0338516..4b54cd5 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -882,11 +882,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 
 		meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
 					ch->pitch, ch->yres, pixelformat,
-					ch->base_addr_y, ch->base_addr_c,
-					&ch->base_addr_y, &ch->base_addr_c,
 					&ch->pitch);
-		if (!IS_ERR(meram))
+		if (!IS_ERR(meram)) {
+			mdev->ops->meram_update(mdev, meram,
+					ch->base_addr_y, ch->base_addr_c,
+					&ch->base_addr_y, &ch->base_addr_c);
 			ch->meram = meram;
+		}
 	}
 
 	/* Start the LCDC. */
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 085c49a..2ce0d8f 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -448,10 +448,6 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
 				      const struct sh_mobile_meram_cfg *cfg,
 				      unsigned int xres, unsigned int yres,
 				      unsigned int pixelformat,
-				      unsigned long base_addr_y,
-				      unsigned long base_addr_c,
-				      unsigned long *icb_addr_y,
-				      unsigned long *icb_addr_c,
 				      unsigned int *pitch)
 {
 	struct sh_mobile_meram_fb_cache *cache;
@@ -470,9 +466,8 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
 	priv = pdata->priv;
 	pdev = pdata->pdev;
 
-	dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
-		xres, yres, (!pixelformat) ? "yuv" : "rgb",
-		base_addr_y, base_addr_c);
+	dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
+		!pixelformat ? "yuv" : "rgb");
 
 	/* we can't handle wider than 8192px */
 	if (xres > 8192) {
@@ -500,12 +495,6 @@ static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
 		meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
 			&out_pitch);
 
-	meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
-	meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
-
-	dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
-		*icb_addr_y, *icb_addr_c);
-
 err:
 	mutex_unlock(&priv->lock);
 	return cache;
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index 8979607..553335c 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -46,10 +46,6 @@ struct sh_mobile_meram_ops {
 				const struct sh_mobile_meram_cfg *cfg,
 				unsigned int xres, unsigned int yres,
 				unsigned int pixelformat,
-				unsigned long base_addr_y,
-				unsigned long base_addr_c,
-				unsigned long *icb_addr_y,
-				unsigned long *icb_addr_c,
 				unsigned int *pitch);
 
 	/* unregister usage of meram */
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 52/57] arm: mach-shmobile: Constify sh_mobile_meram_cfg structures
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

The structures, passed to the sh_mobile_lcdcfb driver through platform
data, are read only by the driver. Make them const.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 arch/arm/mach-shmobile/board-ap4evb.c   |    5 +++--
 arch/arm/mach-shmobile/board-mackerel.c |    4 ++--
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 9d6ad2d..28a2301 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -587,7 +587,8 @@ static const struct fb_videomode ap4evb_lcdc_modes[] = {
 #endif
 	},
 };
-static struct sh_mobile_meram_cfg lcd_meram_cfg = {
+
+static const struct sh_mobile_meram_cfg lcd_meram_cfg = {
 	.icb[0] = {
 		.meram_size     = 0x40,
 	},
@@ -864,7 +865,7 @@ static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
 	return error;
 }
 
-static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
+static const struct sh_mobile_meram_cfg hdmi_meram_cfg = {
 	.icb[0] = {
 		.meram_size     = 0x100,
 	},
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 4acd748..ca084dd 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -374,7 +374,7 @@ static int mackerel_get_brightness(void)
 	return gpio_get_value(GPIO_PORT31);
 }
 
-static struct sh_mobile_meram_cfg lcd_meram_cfg = {
+static const struct sh_mobile_meram_cfg lcd_meram_cfg = {
 	.icb[0] = {
 		.meram_size     = 0x40,
 	},
@@ -460,7 +460,7 @@ static struct platform_device hdmi_device = {
 	},
 };
 
-static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
+static const struct sh_mobile_meram_cfg hdmi_meram_cfg = {
 	.icb[0] = {
 		.meram_size     = 0x100,
 	},
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 51/57] fbdev: sh_mobile_lcdc: Don't store copy of platform data
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

Instead of copying the whole platform data structure to struct
sh_mobile_lcdc_chan, store a const pointer to the channel platform data.

MERAM configuration information needs to be changed at runtime, so copy
it to struct sh_mobile_lcdc_chan.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |   71 ++++++++++++++++++-------------------
 drivers/video/sh_mobile_lcdcfb.h |    3 +-
 include/video/sh_mobile_lcdc.h   |    2 +-
 3 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index d0c9026..0338516 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -116,7 +116,7 @@ static bool banked(int reg_nr)
 
 static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
 {
-	return chan->cfg.chan = LCDC_CHAN_SUBLCD;
+	return chan->cfg->chan = LCDC_CHAN_SUBLCD;
 }
 
 static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
@@ -289,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
 				       struct list_head *pagelist)
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
-	struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+	const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
 
 	/* enable clocks before accessing hardware */
 	sh_mobile_lcdc_clk_on(ch->lcdc);
@@ -336,7 +336,7 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
 
 static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
 {
-	struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+	const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
 
 	if (ch->tx_dev) {
 		int ret;
@@ -356,7 +356,7 @@ static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
 
 static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch)
 {
-	struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg.panel_cfg;
+	const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
 
 	if (panel->display_off)
 		panel->display_off();
@@ -644,16 +644,16 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
 	tmp = ch->ldmt1r_value;
 	tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
 	tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
-	tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
-	tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
-	tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
-	tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
-	tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
+	tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
+	tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
+	tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
+	tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
+	tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
 	lcdc_write_chan(ch, LDMT1R, tmp);
 
 	/* setup SYS bus */
-	lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
-	lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+	lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r);
+	lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r);
 
 	/* horizontal configuration */
 	h_total = mode->xres + mode->hsync_len + mode->left_margin
@@ -717,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		/* Power supply */
 		lcdc_write_chan(ch, LDPMR, 0);
 
-		m = ch->cfg.clock_divider;
+		m = ch->cfg->clock_divider;
 		if (!m)
 			continue;
 
@@ -768,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		 * continuous read mode.
 		 */
 		if (ch->ldmt1r_value & LDMT1R_IFM &&
-		    ch->cfg.sys_bus_cfg.deferred_io_msec) {
+		    ch->cfg->sys_bus_cfg.deferred_io_msec) {
 			lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
 			lcdc_write(priv, _LDINTR, LDINTR_FE);
 		} else {
@@ -822,13 +822,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 	lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
 
 	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
-		struct sh_mobile_lcdc_panel_cfg *panel;
+		const struct sh_mobile_lcdc_panel_cfg *panel;
 
 		ch = &priv->ch[k];
 		if (!ch->enabled)
 			continue;
 
-		panel = &ch->cfg.panel_cfg;
+		panel = &ch->cfg->panel_cfg;
 		if (panel->setup_sys) {
 			ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops);
 			if (ret)
@@ -838,7 +838,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 
 	/* Compute frame buffer base address and pitch for each channel. */
 	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
-		struct sh_mobile_meram_cfg *cfg;
 		int pixelformat;
 		void *meram;
 
@@ -850,8 +849,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
 
 		/* Enable MERAM if possible. */
-		cfg = ch->cfg.meram_cfg;
-		if (mdev = NULL || mdev->ops = NULL || cfg = NULL)
+		if (mdev = NULL || mdev->ops = NULL ||
+		    ch->cfg->meram_cfg = NULL)
 			continue;
 
 		/* we need to de-init configured ICBs before we can
@@ -881,8 +880,8 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 			break;
 		}
 
-		meram = mdev->ops->meram_register(mdev, cfg, ch->pitch,
-					ch->yres, pixelformat,
+		meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+					ch->pitch, ch->yres, pixelformat,
 					ch->base_addr_y, ch->base_addr_c,
 					&ch->base_addr_y, &ch->base_addr_c,
 					&ch->pitch);
@@ -901,7 +900,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		if (!ch->enabled)
 			continue;
 
-		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+		tmp = ch->cfg->sys_bus_cfg.deferred_io_msec;
 		if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
 			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
 			ch->defio.delay = msecs_to_jiffies(tmp);
@@ -1210,8 +1209,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
 	 * distance between two modes is defined as the size of the
 	 * non-overlapping parts of the two rectangles.
 	 */
-	for (i = 0; i < ch->cfg.num_modes; ++i) {
-		const struct fb_videomode *mode = &ch->cfg.lcd_modes[i];
+	for (i = 0; i < ch->cfg->num_modes; ++i) {
+		const struct fb_videomode *mode = &ch->cfg->lcd_modes[i];
 		unsigned int dist;
 
 		/* We can only round up. */
@@ -1230,7 +1229,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
 	}
 
 	/* If no available mode can be used, return an error. */
-	if (ch->cfg.num_modes != 0) {
+	if (ch->cfg->num_modes != 0) {
 		if (best_dist = (unsigned int)-1)
 			return -EINVAL;
 
@@ -1440,7 +1439,7 @@ sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch)
 		return ret;
 
 	dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n",
-		 dev_name(ch->lcdc->dev), (ch->cfg.chan = LCDC_CHAN_MAINLCD) ?
+		 dev_name(ch->lcdc->dev), (ch->cfg->chan = LCDC_CHAN_MAINLCD) ?
 		 "mainlcd" : "sublcd", info->var.xres, info->var.yres,
 		 info->var.bits_per_pixel);
 
@@ -1525,8 +1524,8 @@ sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
 	 */
 	var = &info->var;
 	fb_videomode_to_var(var, mode);
-	var->width = ch->cfg.panel_cfg.width;
-	var->height = ch->cfg.panel_cfg.height;
+	var->width = ch->cfg->panel_cfg.width;
+	var->height = ch->cfg->panel_cfg.height;
 	var->yres_virtual = var->yres * 2;
 	var->activate = FB_ACTIVATE_NOW;
 
@@ -1558,14 +1557,14 @@ static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
 	    bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
 		brightness = 0;
 
-	return ch->cfg.bl_info.set_brightness(brightness);
+	return ch->cfg->bl_info.set_brightness(brightness);
 }
 
 static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
 {
 	struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
 
-	return ch->cfg.bl_info.get_brightness();
+	return ch->cfg->bl_info.get_brightness();
 }
 
 static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
@@ -1586,7 +1585,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
 {
 	struct backlight_device *bl;
 
-	bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
+	bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch,
 				       &sh_mobile_lcdc_bl_ops, NULL);
 	if (IS_ERR(bl)) {
 		dev_err(parent, "unable to register backlight device: %ld\n",
@@ -1594,7 +1593,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
 		return NULL;
 	}
 
-	bl->props.max_brightness = ch->cfg.bl_info.max_brightness;
+	bl->props.max_brightness = ch->cfg->bl_info.max_brightness;
 	bl->props.brightness = bl->props.max_brightness;
 	backlight_update_status(bl);
 
@@ -1727,7 +1726,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
 
 		if (ch->tx_dev) {
 			ch->tx_dev->lcdc = NULL;
-			module_put(ch->cfg.tx_dev->dev.driver->owner);
+			module_put(ch->cfg->tx_dev->dev.driver->owner);
 		}
 
 		sh_mobile_lcdc_channel_fb_cleanup(ch);
@@ -1758,7 +1757,7 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
 
 static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
 {
-	int interface_type = ch->cfg.interface_type;
+	int interface_type = ch->cfg->interface_type;
 
 	switch (interface_type) {
 	case RGB8:
@@ -1801,7 +1800,7 @@ sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
 			    struct sh_mobile_lcdc_chan *ch)
 {
 	const struct sh_mobile_lcdc_format_info *format;
-	struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
+	const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg;
 	const struct fb_videomode *max_mode;
 	const struct fb_videomode *mode;
 	unsigned int num_modes;
@@ -1944,7 +1943,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 		struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels;
 
 		ch->lcdc = priv;
-		memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
+		ch->cfg = &pdata->ch[i];
 
 		error = sh_mobile_lcdc_check_interface(ch);
 		if (error) {
@@ -1956,7 +1955,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
 		ch->pan_offset = 0;
 
 		/* probe the backlight is there is one defined */
-		if (ch->cfg.bl_info.max_brightness)
+		if (ch->cfg->bl_info.max_brightness)
 			ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
 
 		switch (pdata->ch[i].chan) {
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index bf1707c..da1c26e 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -54,7 +54,7 @@ struct sh_mobile_lcdc_entity {
 struct sh_mobile_lcdc_chan {
 	struct sh_mobile_lcdc_priv *lcdc;
 	struct sh_mobile_lcdc_entity *tx_dev;
-	struct sh_mobile_lcdc_chan_cfg cfg;
+	const struct sh_mobile_lcdc_chan_cfg *cfg;
 
 	unsigned long *reg_offs;
 	unsigned long ldmt1r_value;
@@ -66,6 +66,7 @@ struct sh_mobile_lcdc_chan {
 
 	void *fb_mem;
 	unsigned long fb_size;
+
 	dma_addr_t dma_handle;
 	unsigned long pan_offset;
 
diff --git a/include/video/sh_mobile_lcdc.h b/include/video/sh_mobile_lcdc.h
index 484b0a2..7571b27 100644
--- a/include/video/sh_mobile_lcdc.h
+++ b/include/video/sh_mobile_lcdc.h
@@ -178,7 +178,7 @@ struct sh_mobile_lcdc_chan_cfg {
 	struct sh_mobile_lcdc_panel_cfg panel_cfg;
 	struct sh_mobile_lcdc_bl_info bl_info;
 	struct sh_mobile_lcdc_sys_bus_cfg sys_bus_cfg; /* only for SYSn I/F */
-	struct sh_mobile_meram_cfg *meram_cfg;
+	const struct sh_mobile_meram_cfg *meram_cfg;
 
 	struct platform_device *tx_dev;	/* HDMI/DSI transmitter device */
 };
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 50/57] fbdev: sh_mobile_meram: Remove unused sh_mobile_meram_icb_cfg fields
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

The marker_icb and cache_icb fields are not used anymore, remove them.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/video/sh_mobile_meram.h |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index e0f650c..8979607 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -31,8 +31,6 @@ struct sh_mobile_meram_info {
 
 /* icb config */
 struct sh_mobile_meram_icb_cfg {
-	unsigned int marker_icb;	/* ICB # for Marker ICB */
-	unsigned int cache_icb;		/* ICB # for Cache ICB */
 	unsigned int meram_size;	/* MERAM Buffer Size to use */
 };
 
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 49/57] arm: mach-shmobile: Don't set MERAM ICB numbers in platform data
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

The marker and cache ICBs are now allocated automatically, there's no
need to specify them manually anymore.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 arch/arm/mach-shmobile/board-ap4evb.c   |    8 --------
 arch/arm/mach-shmobile/board-mackerel.c |    8 --------
 2 files changed, 0 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index f215818..9d6ad2d 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -589,13 +589,9 @@ static const struct fb_videomode ap4evb_lcdc_modes[] = {
 };
 static struct sh_mobile_meram_cfg lcd_meram_cfg = {
 	.icb[0] = {
-		.marker_icb     = 28,
-		.cache_icb      = 24,
 		.meram_size     = 0x40,
 	},
 	.icb[1] = {
-		.marker_icb     = 29,
-		.cache_icb      = 25,
 		.meram_size     = 0x40,
 	},
 };
@@ -870,13 +866,9 @@ static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
 
 static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
 	.icb[0] = {
-		.marker_icb     = 30,
-		.cache_icb      = 26,
 		.meram_size     = 0x100,
 	},
 	.icb[1] = {
-		.marker_icb     = 31,
-		.cache_icb      = 27,
 		.meram_size     = 0x100,
 	},
 };
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 3b85509..4acd748 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -376,13 +376,9 @@ static int mackerel_get_brightness(void)
 
 static struct sh_mobile_meram_cfg lcd_meram_cfg = {
 	.icb[0] = {
-		.marker_icb     = 28,
-		.cache_icb      = 24,
 		.meram_size     = 0x40,
 	},
 	.icb[1] = {
-		.marker_icb     = 29,
-		.cache_icb      = 25,
 		.meram_size     = 0x40,
 	},
 };
@@ -466,13 +462,9 @@ static struct platform_device hdmi_device = {
 
 static struct sh_mobile_meram_cfg hdmi_meram_cfg = {
 	.icb[0] = {
-		.marker_icb     = 30,
-		.cache_icb      = 26,
 		.meram_size     = 0x100,
 	},
 	.icb[1] = {
-		.marker_icb     = 31,
-		.cache_icb      = 27,
 		.meram_size     = 0x100,
 	},
 };
-- 
1.7.3.4


^ permalink raw reply related

* [PATCH v2 48/57] fbdev: sh_mobile_meram: Allocate ICBs automatically
From: Laurent Pinchart @ 2012-03-01 15:08 UTC (permalink / raw)
  To: linux-fbdev

Instead of manually specifying the ICBs to use in platform data,
allocate them automatically at runtime. The range of reserved ICBs (for
instance to be used through UIO), if any, is passed in the platform data
reserved_icbs field as a bitmask.

The MERAM registration function now returns a pointer to an opaque MERAM
object, which is passed to the update and unregistration functions.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/sh_mobile_lcdcfb.c |   27 ++--
 drivers/video/sh_mobile_lcdcfb.h |    2 +-
 drivers/video/sh_mobile_meram.c  |  345 ++++++++++++++++++++-----------------
 include/video/sh_mobile_meram.h  |   27 ++--
 4 files changed, 215 insertions(+), 186 deletions(-)

diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index adc911f..d0c9026 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -840,6 +840,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
 		struct sh_mobile_meram_cfg *cfg;
 		int pixelformat;
+		void *meram;
 
 		ch = &priv->ch[k];
 		if (!ch->enabled)
@@ -856,9 +857,9 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 		/* we need to de-init configured ICBs before we can
 		 * re-initialize them.
 		 */
-		if (ch->meram_enabled) {
-			mdev->ops->meram_unregister(mdev, cfg);
-			ch->meram_enabled = 0;
+		if (ch->meram) {
+			mdev->ops->meram_unregister(mdev, ch->meram);
+			ch->meram = NULL;
 		}
 
 		switch (ch->format->fourcc) {
@@ -880,13 +881,13 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
 			break;
 		}
 
-		ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
+		meram = mdev->ops->meram_register(mdev, cfg, ch->pitch,
 					ch->yres, pixelformat,
 					ch->base_addr_y, ch->base_addr_c,
 					&ch->base_addr_y, &ch->base_addr_c,
 					&ch->pitch);
-		if (!ret)
-			ch->meram_enabled = 1;
+		if (!IS_ERR(meram))
+			ch->meram = meram;
 	}
 
 	/* Start the LCDC. */
@@ -951,13 +952,11 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
 		sh_mobile_lcdc_display_off(ch);
 
 		/* disable the meram */
-		if (ch->meram_enabled) {
-			struct sh_mobile_meram_cfg *cfg;
+		if (ch->meram) {
 			struct sh_mobile_meram_info *mdev;
-			cfg = ch->cfg.meram_cfg;
 			mdev = priv->meram_dev;
-			mdev->ops->meram_unregister(mdev, cfg);
-			ch->meram_enabled = 0;
+			mdev->ops->meram_unregister(mdev, ch->meram);
+			ch->meram = 0;
 		}
 
 	}
@@ -1070,14 +1069,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
 			base_addr_c += var->xoffset;
 	}
 
-	if (ch->meram_enabled) {
-		struct sh_mobile_meram_cfg *cfg;
+	if (ch->meram) {
 		struct sh_mobile_meram_info *mdev;
 		int ret;
 
-		cfg = ch->cfg.meram_cfg;
 		mdev = priv->meram_dev;
-		ret = mdev->ops->meram_update(mdev, cfg,
+		ret = mdev->ops->meram_update(mdev, ch->meram,
 					base_addr_y, base_addr_c,
 					&base_addr_y, &base_addr_c);
 		if (ret)
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index 19a4cd7..bf1707c 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -59,7 +59,7 @@ struct sh_mobile_lcdc_chan {
 	unsigned long *reg_offs;
 	unsigned long ldmt1r_value;
 	unsigned long enabled; /* ME and SE in LDCNT2R */
-	int meram_enabled;
+	void *meram;
 
 	struct mutex open_lock;		/* protects the use counter */
 	int use_count;
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index 92dc9bd..085c49a 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -10,6 +10,7 @@
  */
 
 #include <linux/device.h>
+#include <linux/err.h>
 #include <linux/genalloc.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -106,14 +107,16 @@ static const unsigned long icb_regs[] = {
 /*
  * sh_mobile_meram_icb - MERAM ICB information
  * @regs: Registers cache
+ * @index: ICB index
  * @offset: MERAM block offset
- * @size: MERAM block size in bytes
+ * @size: MERAM block size in KiB
  * @cache_unit: Bytes to cache per ICB
  * @pixelformat: Video pixel format of the data stored in the ICB
  * @current_reg: Which of Start Address Register A (0) or B (1) is in use
  */
 struct sh_mobile_meram_icb {
 	unsigned long regs[ICB_REGS_SIZE];
+	unsigned int index;
 	unsigned long offset;
 	unsigned int size;
 
@@ -124,6 +127,16 @@ struct sh_mobile_meram_icb {
 
 #define MERAM_ICB_NUM			32
 
+struct sh_mobile_meram_fb_plane {
+	struct sh_mobile_meram_icb *marker;
+	struct sh_mobile_meram_icb *cache;
+};
+
+struct sh_mobile_meram_fb_cache {
+	unsigned int nplanes;
+	struct sh_mobile_meram_fb_plane planes[2];
+};
+
 /*
  * sh_mobile_meram_priv - MERAM device
  * @base: Registers base address
@@ -184,54 +197,46 @@ static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
  * Allocation
  */
 
-/* Check if there's no overlaps in MERAM allocation. */
-static int meram_check_overlap(struct sh_mobile_meram_priv *priv,
-			       const struct sh_mobile_meram_icb_cfg *new)
+/* Allocate ICBs and MERAM for a plane. */
+static int __meram_alloc(struct sh_mobile_meram_priv *priv,
+			 struct sh_mobile_meram_fb_plane *plane,
+			 size_t size)
 {
-	/* valid ICB? */
-	if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
-		return 1;
-
-	if (test_bit(new->marker_icb, &priv->used_icb) ||
-	    test_bit(new->cache_icb,  &priv->used_icb))
-		return  1;
+	unsigned long mem;
+	unsigned long idx;
 
-	return 0;
-}
+	idx = find_first_zero_bit(&priv->used_icb, 28);
+	if (idx = 28)
+		return -ENOMEM;
+	plane->cache = &priv->icbs[idx];
 
-/* Allocate memory for the ICBs and mark them as used. */
-static int meram_alloc(struct sh_mobile_meram_priv *priv,
-		       const struct sh_mobile_meram_icb_cfg *new,
-		       int pixelformat)
-{
-	struct sh_mobile_meram_icb *marker = &priv->icbs[new->marker_icb];
-	unsigned long mem;
+	idx = find_next_zero_bit(&priv->used_icb, 32, 28);
+	if (idx = 32)
+		return -ENOMEM;
+	plane->marker = &priv->icbs[idx];
 
-	mem = gen_pool_alloc(priv->pool, new->meram_size * 1024);
+	mem = gen_pool_alloc(priv->pool, size * 1024);
 	if (mem = 0)
 		return -ENOMEM;
 
-	__set_bit(new->marker_icb, &priv->used_icb);
-	__set_bit(new->cache_icb, &priv->used_icb);
+	__set_bit(plane->marker->index, &priv->used_icb);
+	__set_bit(plane->cache->index, &priv->used_icb);
 
-	marker->offset = mem - priv->meram;
-	marker->size = new->meram_size * 1024;
-	marker->current_reg = 1;
-	marker->pixelformat = pixelformat;
+	plane->marker->offset = mem - priv->meram;
+	plane->marker->size = size;
 
 	return 0;
 }
 
-/* Unmark the specified ICB as used. */
-static void meram_free(struct sh_mobile_meram_priv *priv,
-		       const struct sh_mobile_meram_icb_cfg *icb)
+/* Free ICBs and MERAM for a plane. */
+static void __meram_free(struct sh_mobile_meram_priv *priv,
+			 struct sh_mobile_meram_fb_plane *plane)
 {
-	struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
+	gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
+		      plane->marker->size * 1024);
 
-	gen_pool_free(priv->pool, priv->meram + marker->offset, marker->size);
-
-	__clear_bit(icb->marker_icb, &priv->used_icb);
-	__clear_bit(icb->cache_icb, &priv->used_icb);
+	__clear_bit(plane->marker->index, &priv->used_icb);
+	__clear_bit(plane->cache->index, &priv->used_icb);
 }
 
 /* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
@@ -243,42 +248,96 @@ static int is_nvcolor(int cspace)
 	return 0;
 }
 
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_alloc(struct sh_mobile_meram_priv *priv,
+	    const struct sh_mobile_meram_cfg *cfg,
+	    int pixelformat)
+{
+	struct sh_mobile_meram_fb_cache *cache;
+	unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+	int ret;
+
+	if (cfg->icb[0].meram_size = 0)
+		return ERR_PTR(-EINVAL);
+
+	if (nplanes = 2 && cfg->icb[1].meram_size = 0)
+		return ERR_PTR(-EINVAL);
+
+	cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+	if (cache = NULL)
+		return ERR_PTR(-ENOMEM);
+
+	cache->nplanes = nplanes;
+
+	ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
+	if (ret < 0)
+		goto error;
+
+	cache->planes[0].marker->current_reg = 1;
+	cache->planes[0].marker->pixelformat = pixelformat;
+
+	if (cache->nplanes = 1)
+		return cache;
+
+	ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
+	if (ret < 0) {
+		__meram_free(priv, &cache->planes[0]);
+		goto error;
+	}
+
+	return cache;
+
+error:
+	kfree(cache);
+	return ERR_PTR(-ENOMEM);
+}
+
+/* Unmark the specified ICB as used. */
+static void meram_free(struct sh_mobile_meram_priv *priv,
+		       struct sh_mobile_meram_fb_cache *cache)
+{
+	__meram_free(priv, &cache->planes[0]);
+	if (cache->nplanes = 2)
+		__meram_free(priv, &cache->planes[1]);
+
+	kfree(cache);
+}
+
 /* Set the next address to fetch. */
 static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
-				const struct sh_mobile_meram_cfg *cfg,
+				struct sh_mobile_meram_fb_cache *cache,
 				unsigned long base_addr_y,
 				unsigned long base_addr_c)
 {
-	struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
+	struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
 	unsigned long target;
 
 	icb->current_reg ^= 1;
 	target = icb->current_reg ? MExxSARB : MExxSARA;
 
 	/* set the next address to fetch */
-	meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
+	meram_write_icb(priv->base, cache->planes[0].cache->index, target,
 			base_addr_y);
-	meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
-			base_addr_y +
-			priv->icbs[cfg->icb[0].marker_icb].cache_unit);
-
-	if (is_nvcolor(icb->pixelformat)) {
-		meram_write_icb(priv->base, cfg->icb[1].cache_icb,  target,
-				base_addr_c);
-		meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
-				base_addr_c +
-				priv->icbs[cfg->icb[1].marker_icb].cache_unit);
+	meram_write_icb(priv->base, cache->planes[0].marker->index, target,
+			base_addr_y + cache->planes[0].marker->cache_unit);
+
+	if (cache->nplanes = 2) {
+		meram_write_icb(priv->base, cache->planes[1].cache->index,
+				target, base_addr_c);
+		meram_write_icb(priv->base, cache->planes[1].marker->index,
+				target, base_addr_c +
+				cache->planes[1].marker->cache_unit);
 	}
 }
 
 /* Get the next ICB address. */
 static void
 meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
-			const struct sh_mobile_meram_cfg *cfg,
+			struct sh_mobile_meram_fb_cache *cache,
 			unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 {
-	struct sh_mobile_meram_priv *priv = pdata->priv;
-	struct sh_mobile_meram_icb *icb = &priv->icbs[cfg->icb[0].marker_icb];
+	struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
 	unsigned long icb_offset;
 
 	if (pdata->addr_mode = SH_MOBILE_MERAM_MODE0)
@@ -286,9 +345,10 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
 	else
 		icb_offset = 0xc0000000 | (icb->current_reg << 23);
 
-	*icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
-	if (is_nvcolor(icb->pixelformat))
-		*icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
+	*icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
+	if (cache->nplanes = 2)
+		*icb_addr_c = icb_offset
+			    | (cache->planes[1].marker->index << 24);
 }
 
 #define MERAM_CALC_BYTECOUNT(x, y) \
@@ -296,11 +356,11 @@ meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
 
 /* Initialize MERAM. */
 static int meram_init(struct sh_mobile_meram_priv *priv,
-		      const struct sh_mobile_meram_icb_cfg *icb,
+		      struct sh_mobile_meram_fb_plane *plane,
 		      unsigned int xres, unsigned int yres,
 		      unsigned int *out_pitch)
 {
-	struct sh_mobile_meram_icb *marker = &priv->icbs[icb->marker_icb];
+	struct sh_mobile_meram_icb *marker = plane->marker;
 	unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
 	unsigned long bnm;
 	unsigned int lcdc_pitch;
@@ -319,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
 		lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
 		line_cnt = total_byte_count >> 11;
 		*out_pitch = xres;
-		save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
+		save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
 		save_lines *= MERAM_SEC_LINE;
 	} else {
 		xpitch = xres;
 		line_cnt = yres;
 		*out_pitch = lcdc_pitch;
-		save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
+		save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
 		save_lines &= 0xff;
 	}
 	bnm = (save_lines - 1) << 16;
@@ -333,20 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
 	/* TODO: we better to check if we have enough MERAM buffer size */
 
 	/* set up ICB */
-	meram_write_icb(priv->base, icb->cache_icb,  MExxBSIZE,
+	meram_write_icb(priv->base, plane->cache->index,  MExxBSIZE,
 			MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
-	meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
+	meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
 			MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
 
-	meram_write_icb(priv->base, icb->cache_icb,  MExxMNCF, bnm);
-	meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
+	meram_write_icb(priv->base, plane->cache->index,  MExxMNCF, bnm);
+	meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
 
-	meram_write_icb(priv->base, icb->cache_icb,  MExxSBSIZE, xpitch);
-	meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
+	meram_write_icb(priv->base, plane->cache->index,  MExxSBSIZE, xpitch);
+	meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
 
 	/* save a cache unit size */
-	priv->icbs[icb->cache_icb].cache_unit = xres * save_lines;
-	priv->icbs[icb->marker_icb].cache_unit = xres * save_lines;
+	plane->cache->cache_unit = xres * save_lines;
+	plane->marker->cache_unit = xres * save_lines;
 
 	/*
 	 * Set MERAM for framebuffer
@@ -354,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
 	 * we also chain the cache_icb and the marker_icb.
 	 * we also split the allocated MERAM buffer between two ICBs.
 	 */
-	meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
-			MERAM_MExxCTL_VAL(icb->marker_icb, marker->offset) |
-			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+	meram_write_icb(priv->base, plane->cache->index, MExxCTL,
+			MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
+			| MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
 			MExxCTL_MD_FB);
-	meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
-			MERAM_MExxCTL_VAL(icb->cache_icb, marker->offset +
-					  icb->meram_size / 2) |
+	meram_write_icb(priv->base, plane->marker->index, MExxCTL,
+			MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
+					  plane->marker->size / 2) |
 			MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
 			MExxCTL_MD_FB);
 
@@ -368,45 +428,44 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
 }
 
 static void meram_deinit(struct sh_mobile_meram_priv *priv,
-			 const struct sh_mobile_meram_icb_cfg *icb)
+			 struct sh_mobile_meram_fb_plane *plane)
 {
 	/* disable ICB */
-	meram_write_icb(priv->base, icb->cache_icb,  MExxCTL,
+	meram_write_icb(priv->base, plane->cache->index,  MExxCTL,
 			MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
-	meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
+	meram_write_icb(priv->base, plane->marker->index, MExxCTL,
 			MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
 
-	priv->icbs[icb->cache_icb].cache_unit = 0;
-	priv->icbs[icb->marker_icb].cache_unit = 0;
+	plane->cache->cache_unit = 0;
+	plane->marker->cache_unit = 0;
 }
 
 /* -----------------------------------------------------------------------------
  * Registration/unregistration
  */
 
-static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
-				    const struct sh_mobile_meram_cfg *cfg,
-				    unsigned int xres, unsigned int yres,
-				    unsigned int pixelformat,
-				    unsigned long base_addr_y,
-				    unsigned long base_addr_c,
-				    unsigned long *icb_addr_y,
-				    unsigned long *icb_addr_c,
-				    unsigned int *pitch)
+static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
+				      const struct sh_mobile_meram_cfg *cfg,
+				      unsigned int xres, unsigned int yres,
+				      unsigned int pixelformat,
+				      unsigned long base_addr_y,
+				      unsigned long base_addr_c,
+				      unsigned long *icb_addr_y,
+				      unsigned long *icb_addr_c,
+				      unsigned int *pitch)
 {
-	struct platform_device *pdev;
+	struct sh_mobile_meram_fb_cache *cache;
 	struct sh_mobile_meram_priv *priv;
+	struct platform_device *pdev;
 	unsigned int out_pitch;
-	unsigned int n;
-	int error = 0;
 
 	if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
 	    pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
 	    pixelformat != SH_MOBILE_MERAM_PF_RGB)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	priv = pdata->priv;
 	pdev = pdata->pdev;
@@ -418,120 +477,82 @@ static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
 	/* we can't handle wider than 8192px */
 	if (xres > 8192) {
 		dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
-		return -EINVAL;
-	}
-
-	/* do we have at least one ICB config? */
-	if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
-		dev_err(&pdev->dev, "at least one ICB is required.");
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	mutex_lock(&priv->lock);
 
-	/* make sure that there's no overlaps */
-	if (meram_check_overlap(priv, &cfg->icb[0])) {
-		dev_err(&pdev->dev, "conflicting config detected.");
-		error = -EINVAL;
-		goto err;
-	}
-	n = 1;
-
-	/* do the same if we have the second ICB set */
-	if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
-		if (meram_check_overlap(priv, &cfg->icb[1])) {
-			dev_err(&pdev->dev, "conflicting config detected.");
-			error = -EINVAL;
-			goto err;
-		}
-		n = 2;
-	}
-
-	if (is_nvcolor(pixelformat) && n != 2) {
-		dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
-		error =  -EINVAL;
-		goto err;
-	}
-
 	/* We now register the ICBs and allocate the MERAM regions. */
-	error = meram_alloc(priv, &cfg->icb[0], pixelformat);
-	if (error < 0)
+	cache = meram_alloc(priv, cfg, pixelformat);
+	if (IS_ERR(cache)) {
+		dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
+			PTR_ERR(cache));
 		goto err;
-
-	if (is_nvcolor(pixelformat)) {
-		error = meram_alloc(priv, &cfg->icb[1], pixelformat);
-		if (error < 0) {
-			meram_free(priv, &cfg->icb[0]);
-			goto err;
-		}
 	}
 
 	/* initialize MERAM */
-	meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
+	meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
 	*pitch = out_pitch;
 	if (pixelformat = SH_MOBILE_MERAM_PF_NV)
-		meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
+		meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
 			&out_pitch);
 	else if (pixelformat = SH_MOBILE_MERAM_PF_NV24)
-		meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
+		meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
 			&out_pitch);
 
-	meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
-	meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
+	meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+	meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
 
 	dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
 		*icb_addr_y, *icb_addr_c);
 
 err:
 	mutex_unlock(&priv->lock);
-	return error;
+	return cache;
 }
 
-static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
-				      const struct sh_mobile_meram_cfg *cfg)
+static int
+sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
 {
+	struct sh_mobile_meram_fb_cache *cache = data;
 	struct sh_mobile_meram_priv *priv;
-	struct sh_mobile_meram_icb *icb;
 
-	if (!pdata || !pdata->priv || !cfg)
+	if (!pdata || !pdata->priv || !data)
 		return -EINVAL;
 
 	priv = pdata->priv;
-	icb = &priv->icbs[cfg->icb[0].marker_icb];
 
 	mutex_lock(&priv->lock);
 
-	/* deinit & unmark */
-	if (is_nvcolor(icb->pixelformat)) {
-		meram_deinit(priv, &cfg->icb[1]);
-		meram_free(priv, &cfg->icb[1]);
-	}
-	meram_deinit(priv, &cfg->icb[0]);
-	meram_free(priv, &cfg->icb[0]);
+	/* deinit & free */
+	meram_deinit(priv, &cache->planes[0]);
+	if (cache->nplanes = 2)
+		meram_deinit(priv, &cache->planes[1]);
+
+	meram_free(priv, cache);
 
 	mutex_unlock(&priv->lock);
 
 	return 0;
 }
 
-static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
-				  const struct sh_mobile_meram_cfg *cfg,
-				  unsigned long base_addr_y,
-				  unsigned long base_addr_c,
-				  unsigned long *icb_addr_y,
-				  unsigned long *icb_addr_c)
+static int
+sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
+		       unsigned long base_addr_y, unsigned long base_addr_c,
+		       unsigned long *icb_addr_y, unsigned long *icb_addr_c)
 {
+	struct sh_mobile_meram_fb_cache *cache = data;
 	struct sh_mobile_meram_priv *priv;
 
-	if (!pdata || !pdata->priv || !cfg)
+	if (!pdata || !pdata->priv || !data)
 		return -EINVAL;
 
 	priv = pdata->priv;
 
 	mutex_lock(&priv->lock);
 
-	meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
-	meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
+	meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+	meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
 
 	mutex_unlock(&priv->lock);
 
@@ -607,6 +628,7 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
 	struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
 	struct resource *regs;
 	struct resource *meram;
+	unsigned int i;
 	int error;
 
 	if (!pdata) {
@@ -627,8 +649,13 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
 		return -ENOMEM;
 	}
 
-	/* initialize private data */
+	/* Initialize private data. */
 	mutex_init(&priv->lock);
+	priv->used_icb = pdata->reserved_icbs;
+
+	for (i = 0; i < MERAM_ICB_NUM; ++i)
+		priv->icbs[i].index = i;
+
 	pdata->ops = &sh_mobile_meram_ops;
 	pdata->priv = priv;
 	pdata->pdev = pdev;
diff --git a/include/video/sh_mobile_meram.h b/include/video/sh_mobile_meram.h
index f7700fc..e0f650c 100644
--- a/include/video/sh_mobile_meram.h
+++ b/include/video/sh_mobile_meram.h
@@ -17,8 +17,13 @@ enum {
 struct sh_mobile_meram_priv;
 struct sh_mobile_meram_ops;
 
+/*
+ * struct sh_mobile_meram_info - MERAM platform data
+ * @reserved_icbs: Bitmask of reserved ICBs (for instance used through UIO)
+ */
 struct sh_mobile_meram_info {
 	int				addr_mode;
+	u32				reserved_icbs;
 	struct sh_mobile_meram_ops	*ops;
 	struct sh_mobile_meram_priv	*priv;
 	struct platform_device		*pdev;
@@ -39,23 +44,23 @@ struct module;
 struct sh_mobile_meram_ops {
 	struct module	*module;
 	/* register usage of meram */
-	int (*meram_register)(struct sh_mobile_meram_info *meram_dev,
-			      const struct sh_mobile_meram_cfg *cfg,
-			      unsigned int xres, unsigned int yres,
-			      unsigned int pixelformat,
-			      unsigned long base_addr_y,
-			      unsigned long base_addr_c,
-			      unsigned long *icb_addr_y,
-			      unsigned long *icb_addr_c,
-			      unsigned int *pitch);
+	void *(*meram_register)(struct sh_mobile_meram_info *meram_dev,
+				const struct sh_mobile_meram_cfg *cfg,
+				unsigned int xres, unsigned int yres,
+				unsigned int pixelformat,
+				unsigned long base_addr_y,
+				unsigned long base_addr_c,
+				unsigned long *icb_addr_y,
+				unsigned long *icb_addr_c,
+				unsigned int *pitch);
 
 	/* unregister usage of meram */
 	int (*meram_unregister)(struct sh_mobile_meram_info *meram_dev,
-				const struct sh_mobile_meram_cfg *cfg);
+				void *data);
 
 	/* update meram settings */
 	int (*meram_update)(struct sh_mobile_meram_info *meram_dev,
-			    const struct sh_mobile_meram_cfg *cfg,
+			    void *data,
 			    unsigned long base_addr_y,
 			    unsigned long base_addr_c,
 			    unsigned long *icb_addr_y,
-- 
1.7.3.4


^ 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