* Fixing boot-time hiccups in your display
From: jonsmirl @ 2014-10-05 17:09 UTC (permalink / raw)
To: linux-arm-kernel
I edited the subject line to something more appropriate. This impacts
a lot of platforms and we should be getting more replies from people
on the ARM kernel list. This is likely something that deserves a
Kernel Summit discussion.
To summarize the problem....
The BIOS (uboot, etc) may have set various devices up into a working
state before handing off to the kernel. The most obvious example of
this is the boot display.
So how do we transition onto the kernel provided device specific
drivers without interrupting these functioning devices?
This used to be simple, just build everything into the kernel. But
then along came multi-architecture kernels where most drivers are not
built in. Those kernels clean up everything (ie turn off unused
clocks, regulators, etc) right before user space starts. That's done
as a power saving measure.
Unfortunately that code turns off the clocks and regulators providing
the display on your screen. Which then promptly gets turned back on a
half second later when the boot scripts load the display driver. Let's
all hope the boot doesn't fail while the display is turned off.
Below is part of the discussion on how to fix this, but so far no
really good solution has been discovered.
For the whole history search for the simple-framebuffer threads. There
have been about 1,000 messages.
On Sun, Oct 5, 2014 at 12:34 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
> On Sun, Oct 5, 2014 at 11:36 AM, Chen-Yu Tsai <wens@csie.org> wrote:
>> On Sun, Oct 5, 2014 at 11:29 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>>> On Sun, Oct 5, 2014 at 11:17 AM, Chen-Yu Tsai <wens@csie.org> wrote:
>>>> On Sun, Oct 5, 2014 at 11:07 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>>>>> On Sun, Oct 5, 2014 at 10:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 10/05/2014 02:52 PM, jonsmirl@gmail.com wrote:
>>>>>>> On Sun, Oct 5, 2014 at 5:03 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On 10/04/2014 02:38 PM, jonsmirl@gmail.com wrote:
>>>>>>>>> On Sat, Oct 4, 2014 at 5:50 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>>>> Hi,
>>>>>>>>>>
>>>>>>>>>> On 10/04/2014 12:56 AM, jonsmirl@gmail.com wrote:
>>>>>>>>>>> On Fri, Oct 3, 2014 at 4:08 PM, Rob Herring <robherring2@gmail.com> wrote:
>>>>>>>>>>>> On Fri, Oct 3, 2014 at 12:34 PM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>>>>>>> Hi,
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 10/03/2014 05:57 PM, Rob Herring wrote:
>>>>>>>>>>>>>> On Fri, Oct 3, 2014 at 9:05 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>>>>>>>>> A simple-framebuffer node represents a framebuffer setup by the firmware /
>>>>>>>>>>>>>>> bootloader. Such a framebuffer may have a number of clocks in use, add a
>>>>>>>>>>>>>>> property to communicate this to the OS.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>>>>>>>>>>>>> Reviewed-by: Mike Turquette <mturquette@linaro.org>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> --
>>>>>>>>>>>>>>> Changes in v2:
>>>>>>>>>>>>>>> -Added Reviewed-by: Mike Turquette <mturquette@linaro.org>
>>>>>>>>>>>>>>> Changes in v3:
>>>>>>>>>>>>>>> -Updated description to make clear simplefb deals with more then just memory
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> NAK. "Fixing" the description is not what I meant and does not address
>>>>>>>>>>>>>> my concerns. Currently, simplefb is configuration data. It is
>>>>>>>>>>>>>> auxiliary data about how a chunk of memory is used. Using it or not
>>>>>>>>>>>>>> has no side effects on the hardware setup, but you are changing that
>>>>>>>>>>>>>> aspect. You are mixing in a hardware description that is simply
>>>>>>>>>>>>>> inaccurate.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Memory is hardware too, what simplefb is is best seen as a virtual device, and
>>>>>>>>>>>>> even virtual devices have hardware resources they need, such as a chunk of memory,
>>>>>>>>>>>>> which the kernel should not touch other then use it as a framebuffer, likewise
>>>>>>>>>>>>> on some systems the virtual device needs clocks to keep running.
>>>>>>>>>>>>>
>>>>>>>>>>>>>> The kernel has made the decision to turn off "unused" clocks. If its
>>>>>>>>>>>>>> determination of what is unused is wrong, then it is not a problem to
>>>>>>>>>>>>>> fix in DT.
>>>>>>>>>>>>>
>>>>>>>>>>>>> No, it is up to DT to tell the kernel what clocks are used, that is how it works
>>>>>>>>>>>>> for any other device. I don't understand why some people keep insisting simplefb
>>>>>>>>>>>>> for some reason is o so very very special, because it is not special, it needs
>>>>>>>>>>>>> resources, and it needs to tell the kernel about this or bad things happen.
>>>>>>>>>>>>
>>>>>>>>>>>> No, the DT describes the connections of clocks from h/w block to h/w
>>>>>>>>>>>> block. Their use is implied by the connection.
>>>>>>>>>>>>
>>>>>>>>>>>> And yes, as the other thread mentioned DT is more than just hardware
>>>>>>>>>>>> information. However, what you are adding IS hardware information and
>>>>>>>>>>>> clearly has a place somewhere else. And adding anything which is not
>>>>>>>>>>>> hardware description gets much more scrutiny.
>>>>>>>>>>>>
>>>>>>>>>>>>> More over it is a bit late to start making objections now. This has already been
>>>>>>>>>>>>> discussed to death for weeks now, and every argument against this patch has already
>>>>>>>>>>>>> been countered multiple times (including the one you are making now). Feel free to
>>>>>>>>>>>>> read the entire thread in the archives under the subject:
>>>>>>>>>>>>> "[PATCH 4/4] simplefb: add clock handling code"
>>>>>>>>>>>>
>>>>>>>>>>>> You are on v2 and I hardly see any consensus on the v1 thread. Others
>>>>>>>>>>>> have made suggestions which I would agree with and you've basically
>>>>>>>>>>>> ignored them.
>>>>>>>>>>>>
>>>>>>>>>>>>> At one point in time we need to stop bickering and make a decision, that time has
>>>>>>>>>>>>> come now, so please lets get this discussion over with and merge this, so that
>>>>>>>>>>>>> we can all move on and spend out time in a more productive manner.
>>>>>>>>>>>>
>>>>>>>>>>>> Not an effective argument to get things merged.
>>>>>>>>>>>
>>>>>>>>>>> If there is not good solution to deferring clock clean up in the
>>>>>>>>>>> kernel, another approach is to change how simple-framebuffer is
>>>>>>>>>>> described in the device tree....
>>>>>>>>>>>
>>>>>>>>>>> Right now it is a stand-alone item that looks like a device node, but
>>>>>>>>>>> it isn't a device.
>>>>>>>>>>>
>>>>>>>>>>> framebuffer {
>>>>>>>>>>> compatible = "simple-framebuffer";
>>>>>>>>>>> reg = <0x1d385000 (1600 * 1200 * 2)>;
>>>>>>>>>>> width = <1600>;
>>>>>>>>>>> height = <1200>;
>>>>>>>>>>> stride = <(1600 * 2)>;
>>>>>>>>>>> format = "r5g6b5";
>>>>>>>>>>> };
>>>>>>>>>>>
>>>>>>>>>>> How about something like this?
>>>>>>>>>>>
>>>>>>>>>>> reserved-memory {
>>>>>>>>>>> #address-cells = <1>;
>>>>>>>>>>> #size-cells = <1>;
>>>>>>>>>>> ranges;
>>>>>>>>>>>
>>>>>>>>>>> display_reserved: framebuffer@78000000 {
>>>>>>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>>>>>>>>>>> };
>>>>>>>>>>> };
>>>>>>>>>>>
>>>>>>>>>>> lcd0: lcd-controller@820000 {
>>>>>>>>>>> compatible = "marvell,dove-lcd";
>>>>>>>>>>> reg = <0x820000 0x1000>;
>>>>>>>>>>> interrupts = <47>;
>>>>>>>>>>> clocks = <&si5351 0>;
>>>>>>>>>>> clock-names = "ext_ref_clk_1";
>>>>>>>>>>> };
>>>>>>>>>>>
>>>>>>>>>>> chosen {
>>>>>>>>>>> boot-framebuffer {
>>>>>>>>>>> compatible = "simple-framebuffer";
>>>>>>>>>>> device = <&lcd0>;
>>>>>>>>>>> framebuffer = <&display_reserved>;
>>>>>>>>>>> width = <1600>;
>>>>>>>>>>> height = <1200>;
>>>>>>>>>>> stride = <(1600 * 2)>;
>>>>>>>>>>> format = "r5g6b5";
>>>>>>>>>>> };
>>>>>>>>>>> }
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> This moves the definition of the boot framebuffer setup into the area
>>>>>>>>>>> where bootloader info is suppose to go. Then you can use the phandle
>>>>>>>>>>> to follow the actual device chains and protect the clocks and
>>>>>>>>>>> regulators. To make that work you are required to provide an accurate
>>>>>>>>>>> description of the real video hardware so that this chain can be
>>>>>>>>>>> followed.
>>>>>>>>>>
>>>>>>>>>> This will not work, first of all multiple blocks may be involved, so
>>>>>>>>>> the device = in the boot-framebuffer would need to be a list. That in
>>>>>>>>>> itself is not a problem, the problem is that the blocks used may have
>>>>>>>>>> multiple clocks, of which the setup mode likely uses only a few.
>>>>>>>>>>
>>>>>>>>>> So if we do things this way, we end up keeping way to many clocks
>>>>>>>>>> enabled.
>>>>>>>>>
>>>>>>>>> That doesn't hurt anything.
>>>>>>>>
>>>>>>>> <snip lots of stuff based on the above>
>>>>>>>>
>>>>>>>> Wrong, that does hurt things. As already discussed simply stopping the
>>>>>>>> clocks from being disabled by the unused_clks mechanism is not enough,
>>>>>>>> since clocks may be shared, and we must stop another device driver
>>>>>>>> sharing the clock and doing clk_enable; probe; clk_disable; disabling
>>>>>>>> the shared clk, which means calling clk_enable on it to mark it as
>>>>>>>> being in use. So this will hurt cause it will cause us to enable a bunch
>>>>>>>> of clks which should not be enabled.
>>>>>>>
>>>>>>> I said earlier that you would need to add a protected mechanism to
>>>>>>> clocks to handle this phase. When a clock/regulator is protected you
>>>>>>> can turn it on but you can't turn it off. When simplefb exits it will
>>>>>>> clear this protected status. When the protected status gets cleared
>>>>>>> treat it as a ref count decrement and turn off the clock/regulator if
>>>>>>> indicated to do so. If a clock is protected, all of it parents get the
>>>>>>> protected bit set too.
>>>>>>>
>>>>>>> Protected mode
>>>>>>> you can turn clocks on, but not off
>>>>>>> it is ref counted
>>>>>>> when it hits zero, look at the normal refcount and set that state
>>>>>>>
>>>>>>> Protected mode is not long lived. It only hangs around until the real
>>>>>>> device driver loads.
>>>>>>
>>>>>> And that has already been nacked by the clk maintainer because it is
>>>>>> too complicated, and I agree this is tons more complicated then simply
>>>>>> adding the list of clocks to the simplefb node.
>>>>>>
>>>>>>>> I've been thinking more about this, and I understand that people don't
>>>>>>>> want to put hardware description in the simplefb node, but this is
>>>>>>>> not hardware description.
>>>>>>>>
>>>>>>>> u-boot sets up the display-pipeline to scan out of a certain part of
>>>>>>>> memory, in order to do this it writes the memory address to some registers
>>>>>>>> in the display pipeline, so what it is passing is not hardware description
>>>>>>>> (it is not passing all possible memory addresses for the framebuffer), but
>>>>>>>> it is passing information about the state in which it has left the display
>>>>>>>> pipeline, iow it is passing state information.
>>>>>>>
>>>>>>> Giving the buffer to a piece of hardware is more than setting state.
>>>>>>> The hardware now owns that buffer. That ownership needs to be managed
>>>>>>> so that if the hardware decides it doesn't want the buffer it can be
>>>>>>> returned to the global pool.
>>>>>>>
>>>>>>> That's why the buffer has to go into that reserved memory section, not
>>>>>>> the chosen section.
>>>>>>
>>>>>> But is not in the reserved memory section, it is in the simplefb
>>>>>> section, and we're stuck with this because of backward compatibility.
>>>>>>
>>>>>> An OS doesn't have to process chosen, it is just
>>>>>>> there for informational purposes. To keep the OS from thinking it owns
>>>>>>> the memory in that video buffer and can use it for OS purposes, it has
>>>>>>> to go into that reserved memory section.
>>>>>>>
>>>>>>> The clocks are different. We know exactly who owns those clocks, the
>>>>>>> graphics hardware.
>>>>>>>
>>>>>>> You want something like this where the state of the entire video path
>>>>>>> is encoded into the chosen section. But that info is going to vary all
>>>>>>> over the place with TV output, HDMI output, LCD panels, etc. simplefb
>>>>>>> isn't going to be simple any more. Plus the purposes of adding all of
>>>>>>> this complexity is just to handle the half second transition from boot
>>>>>>> loader control to real driver.
>>>>>>>
>>>>>>> reserved-memory {
>>>>>>> #address-cells = <1>;
>>>>>>> #size-cells = <1>;
>>>>>>> ranges;
>>>>>>>
>>>>>>> display_reserved: framebuffer@78000000 {
>>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>>>>>>> };
>>>>>>> };
>>>>>>>
>>>>>>> lcd0: lcd-controller@820000 {
>>>>>>> compatible = "marvell,dove-lcd";
>>>>>>> reg = <0x820000 0x1000>;
>>>>>>> interrupts = <47>;
>>>>>>> framebuffer = <&display_reserved>;
>>>>>>> clocks = <&si5351 0>;
>>>>>>> clock-names = "ext_ref_clk_1";
>>>>>>> };
>>>>>>>
>>>>>>> chosen {
>>>>>>> boot-framebuffer {
>>>>>>> compatible = "simple-framebuffer";
>>>>>>> state {
>>>>>>> device = <&lcd0>;
>>>>>>> width = <1600>;
>>>>>>> height = <1200>;
>>>>>>> stride = <(1600 * 2)>;
>>>>>>> format = "r5g6b5";
>>>>>>> clocks = <&si5351 on 10mhz>;
>>>>>>
>>>>>> Right, so here we get a list of clocks which are actually in use
>>>>>> by the simplefb, just as I've been advocating all the time already.
>>>>>>
>>>>>> Note that the clock speed is not necessary, all the kernel needs to
>>>>>> know is that it must not change it.
>>>>>>
>>>>>> So as you seem to agree that we need to pass some sort of clock state
>>>>>> info, then all we need to agree on now is where to put it, as said adding
>>>>>> the reg property to a reserved-memory node is out of the question because
>>>>>> of backward compat, likewise storing width, height and format in a state
>>>>>> sub-node are out of the question for the same reason. But other then that
>>>>>> I'm fine with putting the simplefb node under chosen and putting clocks
>>>>>> in there (without the state subnode) as you suggest above.
>>>>>>
>>>>>> So we seem to be in agreement here :)
>>>>>>
>>>>>>> output = "hdmi";
>>>>>>> state {
>>>>>>> device = <&hdmi>
>>>>>>> clocks = <&xyz on 12mhz>;
>>>>>>> }
>>>>>>
>>>>>> That level of detail won't be necessary, simplefb is supposed to be
>>>>>> simple, the kernel does not need to know which outputs there are, there
>>>>>> will always be only one for simplefb.
>>>>>
>>>>> I don't agree, but you are going to do it any way so I'll try and help
>>>>> tkeep problem side effects I know of to a minimum. You are relying on
>>>>> the BIOS to provide detailed, accurate information. Relying on BIOSes
>>>>> to do that has historically been a very bad idea.
>>>>>
>>>>> If you go the way of putting this info into the chosen section you are
>>>>> going to have to mark the clocks/regulators in use for all of the
>>>>> outputs too -- hdmi, TV, LCD, backlights, etc, etc. Not going to be
>>>>> useful if the backlight turns off because simplefb hasn't grabbed it.
>>>>>
>>>>> This is the only real difference between the proposals - you want
>>>>> uboot to enumerate what needs to be protected. I don't trust the BIOS
>>>>> to do that reliably so I'd preferred to just protect everything in the
>>>>> device hardware chain until the device specific drivers load.
>>>>>
>>>>> -------------------------------------------------------
>>>>>
>>>>> I also still believe this is a problem up in Linux that we shouldn't
>>>>> be using the device tree to fix.
>>>>>
>>>>> It seems to me that the need for something like a 'protected' mode is
>>>>> a generic problem that could be extended to all hardware. In protected
>>>>> mode things can be turned on but nothing can be turned off. Only
>>>>> after the kernel has all of the necessary drivers loaded would a small
>>>>> app run that hits an IOCTL and says, ok protected mode is over now
>>>>> clean up these things wasting power.
>>>>
>>>> What happens if some clock needs to be disabled?
>>>> Like clocks that must be gated before setting a new clock rate
>>>> or reparenting. The kernel supports these, and it wouldn't surprise me
>>>> if some driver actually requires this. You might end up blocking that driver
>>>> or breaking its probe function.
>>>
>>> Arggh, using those phandles in the chosen section means uboot is going
>>> to have to get recompiled every time the DTS changes. I think we need
>>> to come up with a scheme that doesn't need for uboot to be aware of
>>> phandles.
>>
>> Why is that? U-boot is perfectly capable of patching device tree blobs.
>>
>> Mainline u-boot already updates the memory node, and if needed,
>> the reserved-memory node as well.
>>
>> Someone just has to write the (ugly) code to do it, which Luc
>> has already done for clock phandles for sunxi.
>
> So uboot is going to contain code to hunt through the Linux provided
> DTB to find the correct phandles for the clocks/regulators and then
> patch those phandles into the chosen section? How is uboot going to
> reconcile it's concept of what those clock/regulators are with a Linux
> provided DTB that is constant state of flux?
>
> I think trying to get uboot to manipulate phandles in a Linux provided
> DTB is an incredibly fragile mechanism that will be prone to breakage.
>
> Much better to come with a scheme where uboot just inserts fixed
> strings into the DTB. That last example device tree I posted removed
> all of the phandles from the chosen section, but it relies on the
> kernel gaining 'boot' mode.
>
>
>>
>> U-boot itself does not need to use the dtb, though that seems
>> like the direction it's headed.
>>
>>> Something like this...
>>> uboot adds the chosen section then Linux would error out if the
>>> framebuffer in the chosen section doesn't match the reserved memory it
>>> is expecting. Or make uboot smart enough to hunt down the reserved
>>> memory section and patch it like it does with dramsize.
>>
>> And someone probably will. Why is that a problem?
>>
>>
>> ChenYu
>>
>>
>>>
>>> reserved-memory {
>>> #address-cells = <1>;
>>> #size-cells = <1>;
>>> ranges;
>>>
>>> display_reserved: framebuffer@78000000 {
>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>>> };
>>> };
>>>
>>> lcd0: lcd-controller@820000 {
>>> compatible = "marvell,dove-lcd";
>>> reg = <0x820000 0x1000>;
>>> interrupts = <47>;
>>> framebuffer = <&display_reserved>;
>>> clocks = <&si5351 0>;
>>> clock-names = "ext_ref_clk_1";
>>> };
>>>
>>> chosen {
>>> boot-framebuffer {
>>> framebuffer = <0x78000000>;
>>> width = <1600>;
>>> height = <1200>;
>>> stride = <(1600 * 2)>;
>>> format = "r5g6b5";
>>> };
>>> }
>>>
>>>
>>>
>>>>
>>>> And what if reset controls are asserted then de-asserted in the probe function?
>>>> IIRC there are drivers that do this to reset the underlying hardware.
>>>>
>>>>> Maybe it should be renamed 'boot' mode. To implement this the various
>>>>> 'turn off' functions would get a 'boot' mode flag. In boot mode they
>>>>> track the ref counts but don't turn things off when the ref count hits
>>>>> zero. Then that ioctl() that the user space app calls runs the chains
>>>>> of all of the clocks/regulators/etc and if the ref count is zero turns
>>>>> them off again and clears 'boot' mode. Doesn't matter if you turn off
>>>>> something again that is already off.
>>>>
>>>> And what if something just happened to be left on that some driver
>>>> wants to turn off? You are assuming everything not used is off,
>>>> which is not always the case.
>
>
>
> --
> Jon Smirl
> jonsmirl@gmail.com
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Mike Turquette @ 2014-10-05 20:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKON4OxRFCvMPMfZhxYNVaxscgAs5AX46YzK-4SU5o6E-iTD1w@mail.gmail.com>
Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
> I edited the subject line to something more appropriate. This impacts
> a lot of platforms and we should be getting more replies from people
> on the ARM kernel list. This is likely something that deserves a
> Kernel Summit discussion.
ELC-E and LPC are just around the corner as well. I am attending both. I
suppose some of the others interested in this topic will be present?
>
> To summarize the problem....
>
> The BIOS (uboot, etc) may have set various devices up into a working
> state before handing off to the kernel. The most obvious example of
> this is the boot display.
>
> So how do we transition onto the kernel provided device specific
> drivers without interrupting these functioning devices?
>
> This used to be simple, just build everything into the kernel. But
> then along came multi-architecture kernels where most drivers are not
> built in. Those kernels clean up everything (ie turn off unused
> clocks, regulators, etc) right before user space starts. That's done
> as a power saving measure.
>
> Unfortunately that code turns off the clocks and regulators providing
> the display on your screen. Which then promptly gets turned back on a
> half second later when the boot scripts load the display driver. Let's
> all hope the boot doesn't fail while the display is turned off.
I would say this is one half of the discussion. How do you ever really
know when it is safe to disable these things? In a world with loadable
modules the kernel cannot know that everything that is going to be
loaded has been loaded. There really is no boundary that makes it easy
to say, "OK, now it is truly safe for me to disable these things because
I know every possible piece of code that might claim these resources has
probed".
Somebody (Geert?) proposed an ioctl for userspace to send such an "all
clear" signal, but I dislike that idea. We're talking about managing
fairly low-level hardware bits (clocks, regulators) and getting
userspace involved feels wrong.
Additionally clocks and regulators should be managed by PM Runtime
implementations (via generic power domains and friends), so any solution
that we come up with today that is specific for the clock and regulator
frameworks would need to scale up to other abstraction layers, because
those layers would probably like to know that they are not really
idle/gated in hardware because the ioctl/garbage collector has not yet
happened.
The second half of the discussion is specific to simple framebuffer and
the desire to keep it extremely simple and narrowly focused. I can
understand both sides of the argument and I hope to stay well out of the
flame zone.
Even if we do leave simple framebuffer alone, the *idea* behind a very
simple, entirely data-driven driver implementation that is meant to be
compiled into the kernel image, claim resources early, ensure they stay
enabled and then hand-off to a real driver that may be a loadable module
is very interesting. It doesn't have to be simplefb. It could be
something new. Additionally if this design pattern becomes common across
more than just displays then we might want to consider ways of Doing It
Right.
Regards,
Mike
>
> Below is part of the discussion on how to fix this, but so far no
> really good solution has been discovered.
>
> For the whole history search for the simple-framebuffer threads. There
> have been about 1,000 messages.
>
> On Sun, Oct 5, 2014 at 12:34 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
> > On Sun, Oct 5, 2014 at 11:36 AM, Chen-Yu Tsai <wens@csie.org> wrote:
> >> On Sun, Oct 5, 2014 at 11:29 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
> >>> On Sun, Oct 5, 2014 at 11:17 AM, Chen-Yu Tsai <wens@csie.org> wrote:
> >>>> On Sun, Oct 5, 2014 at 11:07 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
> >>>>> On Sun, Oct 5, 2014 at 10:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> >>>>>> Hi,
> >>>>>>
> >>>>>> On 10/05/2014 02:52 PM, jonsmirl@gmail.com wrote:
> >>>>>>> On Sun, Oct 5, 2014 at 5:03 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> >>>>>>>> Hi,
> >>>>>>>>
> >>>>>>>> On 10/04/2014 02:38 PM, jonsmirl@gmail.com wrote:
> >>>>>>>>> On Sat, Oct 4, 2014 at 5:50 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> >>>>>>>>>> Hi,
> >>>>>>>>>>
> >>>>>>>>>> On 10/04/2014 12:56 AM, jonsmirl@gmail.com wrote:
> >>>>>>>>>>> On Fri, Oct 3, 2014 at 4:08 PM, Rob Herring <robherring2@gmail.com> wrote:
> >>>>>>>>>>>> On Fri, Oct 3, 2014 at 12:34 PM, Hans de Goede <hdegoede@redhat.com> wrote:
> >>>>>>>>>>>>> Hi,
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> On 10/03/2014 05:57 PM, Rob Herring wrote:
> >>>>>>>>>>>>>> On Fri, Oct 3, 2014 at 9:05 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> >>>>>>>>>>>>>>> A simple-framebuffer node represents a framebuffer setup by the firmware /
> >>>>>>>>>>>>>>> bootloader. Such a framebuffer may have a number of clocks in use, add a
> >>>>>>>>>>>>>>> property to communicate this to the OS.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> >>>>>>>>>>>>>>> Reviewed-by: Mike Turquette <mturquette@linaro.org>
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> --
> >>>>>>>>>>>>>>> Changes in v2:
> >>>>>>>>>>>>>>> -Added Reviewed-by: Mike Turquette <mturquette@linaro.org>
> >>>>>>>>>>>>>>> Changes in v3:
> >>>>>>>>>>>>>>> -Updated description to make clear simplefb deals with more then just memory
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> NAK. "Fixing" the description is not what I meant and does not address
> >>>>>>>>>>>>>> my concerns. Currently, simplefb is configuration data. It is
> >>>>>>>>>>>>>> auxiliary data about how a chunk of memory is used. Using it or not
> >>>>>>>>>>>>>> has no side effects on the hardware setup, but you are changing that
> >>>>>>>>>>>>>> aspect. You are mixing in a hardware description that is simply
> >>>>>>>>>>>>>> inaccurate.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Memory is hardware too, what simplefb is is best seen as a virtual device, and
> >>>>>>>>>>>>> even virtual devices have hardware resources they need, such as a chunk of memory,
> >>>>>>>>>>>>> which the kernel should not touch other then use it as a framebuffer, likewise
> >>>>>>>>>>>>> on some systems the virtual device needs clocks to keep running.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>>> The kernel has made the decision to turn off "unused" clocks. If its
> >>>>>>>>>>>>>> determination of what is unused is wrong, then it is not a problem to
> >>>>>>>>>>>>>> fix in DT.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> No, it is up to DT to tell the kernel what clocks are used, that is how it works
> >>>>>>>>>>>>> for any other device. I don't understand why some people keep insisting simplefb
> >>>>>>>>>>>>> for some reason is o so very very special, because it is not special, it needs
> >>>>>>>>>>>>> resources, and it needs to tell the kernel about this or bad things happen.
> >>>>>>>>>>>>
> >>>>>>>>>>>> No, the DT describes the connections of clocks from h/w block to h/w
> >>>>>>>>>>>> block. Their use is implied by the connection.
> >>>>>>>>>>>>
> >>>>>>>>>>>> And yes, as the other thread mentioned DT is more than just hardware
> >>>>>>>>>>>> information. However, what you are adding IS hardware information and
> >>>>>>>>>>>> clearly has a place somewhere else. And adding anything which is not
> >>>>>>>>>>>> hardware description gets much more scrutiny.
> >>>>>>>>>>>>
> >>>>>>>>>>>>> More over it is a bit late to start making objections now. This has already been
> >>>>>>>>>>>>> discussed to death for weeks now, and every argument against this patch has already
> >>>>>>>>>>>>> been countered multiple times (including the one you are making now). Feel free to
> >>>>>>>>>>>>> read the entire thread in the archives under the subject:
> >>>>>>>>>>>>> "[PATCH 4/4] simplefb: add clock handling code"
> >>>>>>>>>>>>
> >>>>>>>>>>>> You are on v2 and I hardly see any consensus on the v1 thread. Others
> >>>>>>>>>>>> have made suggestions which I would agree with and you've basically
> >>>>>>>>>>>> ignored them.
> >>>>>>>>>>>>
> >>>>>>>>>>>>> At one point in time we need to stop bickering and make a decision, that time has
> >>>>>>>>>>>>> come now, so please lets get this discussion over with and merge this, so that
> >>>>>>>>>>>>> we can all move on and spend out time in a more productive manner.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Not an effective argument to get things merged.
> >>>>>>>>>>>
> >>>>>>>>>>> If there is not good solution to deferring clock clean up in the
> >>>>>>>>>>> kernel, another approach is to change how simple-framebuffer is
> >>>>>>>>>>> described in the device tree....
> >>>>>>>>>>>
> >>>>>>>>>>> Right now it is a stand-alone item that looks like a device node, but
> >>>>>>>>>>> it isn't a device.
> >>>>>>>>>>>
> >>>>>>>>>>> framebuffer {
> >>>>>>>>>>> compatible = "simple-framebuffer";
> >>>>>>>>>>> reg = <0x1d385000 (1600 * 1200 * 2)>;
> >>>>>>>>>>> width = <1600>;
> >>>>>>>>>>> height = <1200>;
> >>>>>>>>>>> stride = <(1600 * 2)>;
> >>>>>>>>>>> format = "r5g6b5";
> >>>>>>>>>>> };
> >>>>>>>>>>>
> >>>>>>>>>>> How about something like this?
> >>>>>>>>>>>
> >>>>>>>>>>> reserved-memory {
> >>>>>>>>>>> #address-cells = <1>;
> >>>>>>>>>>> #size-cells = <1>;
> >>>>>>>>>>> ranges;
> >>>>>>>>>>>
> >>>>>>>>>>> display_reserved: framebuffer@78000000 {
> >>>>>>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
> >>>>>>>>>>> };
> >>>>>>>>>>> };
> >>>>>>>>>>>
> >>>>>>>>>>> lcd0: lcd-controller@820000 {
> >>>>>>>>>>> compatible = "marvell,dove-lcd";
> >>>>>>>>>>> reg = <0x820000 0x1000>;
> >>>>>>>>>>> interrupts = <47>;
> >>>>>>>>>>> clocks = <&si5351 0>;
> >>>>>>>>>>> clock-names = "ext_ref_clk_1";
> >>>>>>>>>>> };
> >>>>>>>>>>>
> >>>>>>>>>>> chosen {
> >>>>>>>>>>> boot-framebuffer {
> >>>>>>>>>>> compatible = "simple-framebuffer";
> >>>>>>>>>>> device = <&lcd0>;
> >>>>>>>>>>> framebuffer = <&display_reserved>;
> >>>>>>>>>>> width = <1600>;
> >>>>>>>>>>> height = <1200>;
> >>>>>>>>>>> stride = <(1600 * 2)>;
> >>>>>>>>>>> format = "r5g6b5";
> >>>>>>>>>>> };
> >>>>>>>>>>> }
> >>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> This moves the definition of the boot framebuffer setup into the area
> >>>>>>>>>>> where bootloader info is suppose to go. Then you can use the phandle
> >>>>>>>>>>> to follow the actual device chains and protect the clocks and
> >>>>>>>>>>> regulators. To make that work you are required to provide an accurate
> >>>>>>>>>>> description of the real video hardware so that this chain can be
> >>>>>>>>>>> followed.
> >>>>>>>>>>
> >>>>>>>>>> This will not work, first of all multiple blocks may be involved, so
> >>>>>>>>>> the device = in the boot-framebuffer would need to be a list. That in
> >>>>>>>>>> itself is not a problem, the problem is that the blocks used may have
> >>>>>>>>>> multiple clocks, of which the setup mode likely uses only a few.
> >>>>>>>>>>
> >>>>>>>>>> So if we do things this way, we end up keeping way to many clocks
> >>>>>>>>>> enabled.
> >>>>>>>>>
> >>>>>>>>> That doesn't hurt anything.
> >>>>>>>>
> >>>>>>>> <snip lots of stuff based on the above>
> >>>>>>>>
> >>>>>>>> Wrong, that does hurt things. As already discussed simply stopping the
> >>>>>>>> clocks from being disabled by the unused_clks mechanism is not enough,
> >>>>>>>> since clocks may be shared, and we must stop another device driver
> >>>>>>>> sharing the clock and doing clk_enable; probe; clk_disable; disabling
> >>>>>>>> the shared clk, which means calling clk_enable on it to mark it as
> >>>>>>>> being in use. So this will hurt cause it will cause us to enable a bunch
> >>>>>>>> of clks which should not be enabled.
> >>>>>>>
> >>>>>>> I said earlier that you would need to add a protected mechanism to
> >>>>>>> clocks to handle this phase. When a clock/regulator is protected you
> >>>>>>> can turn it on but you can't turn it off. When simplefb exits it will
> >>>>>>> clear this protected status. When the protected status gets cleared
> >>>>>>> treat it as a ref count decrement and turn off the clock/regulator if
> >>>>>>> indicated to do so. If a clock is protected, all of it parents get the
> >>>>>>> protected bit set too.
> >>>>>>>
> >>>>>>> Protected mode
> >>>>>>> you can turn clocks on, but not off
> >>>>>>> it is ref counted
> >>>>>>> when it hits zero, look at the normal refcount and set that state
> >>>>>>>
> >>>>>>> Protected mode is not long lived. It only hangs around until the real
> >>>>>>> device driver loads.
> >>>>>>
> >>>>>> And that has already been nacked by the clk maintainer because it is
> >>>>>> too complicated, and I agree this is tons more complicated then simply
> >>>>>> adding the list of clocks to the simplefb node.
> >>>>>>
> >>>>>>>> I've been thinking more about this, and I understand that people don't
> >>>>>>>> want to put hardware description in the simplefb node, but this is
> >>>>>>>> not hardware description.
> >>>>>>>>
> >>>>>>>> u-boot sets up the display-pipeline to scan out of a certain part of
> >>>>>>>> memory, in order to do this it writes the memory address to some registers
> >>>>>>>> in the display pipeline, so what it is passing is not hardware description
> >>>>>>>> (it is not passing all possible memory addresses for the framebuffer), but
> >>>>>>>> it is passing information about the state in which it has left the display
> >>>>>>>> pipeline, iow it is passing state information.
> >>>>>>>
> >>>>>>> Giving the buffer to a piece of hardware is more than setting state.
> >>>>>>> The hardware now owns that buffer. That ownership needs to be managed
> >>>>>>> so that if the hardware decides it doesn't want the buffer it can be
> >>>>>>> returned to the global pool.
> >>>>>>>
> >>>>>>> That's why the buffer has to go into that reserved memory section, not
> >>>>>>> the chosen section.
> >>>>>>
> >>>>>> But is not in the reserved memory section, it is in the simplefb
> >>>>>> section, and we're stuck with this because of backward compatibility.
> >>>>>>
> >>>>>> An OS doesn't have to process chosen, it is just
> >>>>>>> there for informational purposes. To keep the OS from thinking it owns
> >>>>>>> the memory in that video buffer and can use it for OS purposes, it has
> >>>>>>> to go into that reserved memory section.
> >>>>>>>
> >>>>>>> The clocks are different. We know exactly who owns those clocks, the
> >>>>>>> graphics hardware.
> >>>>>>>
> >>>>>>> You want something like this where the state of the entire video path
> >>>>>>> is encoded into the chosen section. But that info is going to vary all
> >>>>>>> over the place with TV output, HDMI output, LCD panels, etc. simplefb
> >>>>>>> isn't going to be simple any more. Plus the purposes of adding all of
> >>>>>>> this complexity is just to handle the half second transition from boot
> >>>>>>> loader control to real driver.
> >>>>>>>
> >>>>>>> reserved-memory {
> >>>>>>> #address-cells = <1>;
> >>>>>>> #size-cells = <1>;
> >>>>>>> ranges;
> >>>>>>>
> >>>>>>> display_reserved: framebuffer@78000000 {
> >>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
> >>>>>>> };
> >>>>>>> };
> >>>>>>>
> >>>>>>> lcd0: lcd-controller@820000 {
> >>>>>>> compatible = "marvell,dove-lcd";
> >>>>>>> reg = <0x820000 0x1000>;
> >>>>>>> interrupts = <47>;
> >>>>>>> framebuffer = <&display_reserved>;
> >>>>>>> clocks = <&si5351 0>;
> >>>>>>> clock-names = "ext_ref_clk_1";
> >>>>>>> };
> >>>>>>>
> >>>>>>> chosen {
> >>>>>>> boot-framebuffer {
> >>>>>>> compatible = "simple-framebuffer";
> >>>>>>> state {
> >>>>>>> device = <&lcd0>;
> >>>>>>> width = <1600>;
> >>>>>>> height = <1200>;
> >>>>>>> stride = <(1600 * 2)>;
> >>>>>>> format = "r5g6b5";
> >>>>>>> clocks = <&si5351 on 10mhz>;
> >>>>>>
> >>>>>> Right, so here we get a list of clocks which are actually in use
> >>>>>> by the simplefb, just as I've been advocating all the time already.
> >>>>>>
> >>>>>> Note that the clock speed is not necessary, all the kernel needs to
> >>>>>> know is that it must not change it.
> >>>>>>
> >>>>>> So as you seem to agree that we need to pass some sort of clock state
> >>>>>> info, then all we need to agree on now is where to put it, as said adding
> >>>>>> the reg property to a reserved-memory node is out of the question because
> >>>>>> of backward compat, likewise storing width, height and format in a state
> >>>>>> sub-node are out of the question for the same reason. But other then that
> >>>>>> I'm fine with putting the simplefb node under chosen and putting clocks
> >>>>>> in there (without the state subnode) as you suggest above.
> >>>>>>
> >>>>>> So we seem to be in agreement here :)
> >>>>>>
> >>>>>>> output = "hdmi";
> >>>>>>> state {
> >>>>>>> device = <&hdmi>
> >>>>>>> clocks = <&xyz on 12mhz>;
> >>>>>>> }
> >>>>>>
> >>>>>> That level of detail won't be necessary, simplefb is supposed to be
> >>>>>> simple, the kernel does not need to know which outputs there are, there
> >>>>>> will always be only one for simplefb.
> >>>>>
> >>>>> I don't agree, but you are going to do it any way so I'll try and help
> >>>>> tkeep problem side effects I know of to a minimum. You are relying on
> >>>>> the BIOS to provide detailed, accurate information. Relying on BIOSes
> >>>>> to do that has historically been a very bad idea.
> >>>>>
> >>>>> If you go the way of putting this info into the chosen section you are
> >>>>> going to have to mark the clocks/regulators in use for all of the
> >>>>> outputs too -- hdmi, TV, LCD, backlights, etc, etc. Not going to be
> >>>>> useful if the backlight turns off because simplefb hasn't grabbed it.
> >>>>>
> >>>>> This is the only real difference between the proposals - you want
> >>>>> uboot to enumerate what needs to be protected. I don't trust the BIOS
> >>>>> to do that reliably so I'd preferred to just protect everything in the
> >>>>> device hardware chain until the device specific drivers load.
> >>>>>
> >>>>> -------------------------------------------------------
> >>>>>
> >>>>> I also still believe this is a problem up in Linux that we shouldn't
> >>>>> be using the device tree to fix.
> >>>>>
> >>>>> It seems to me that the need for something like a 'protected' mode is
> >>>>> a generic problem that could be extended to all hardware. In protected
> >>>>> mode things can be turned on but nothing can be turned off. Only
> >>>>> after the kernel has all of the necessary drivers loaded would a small
> >>>>> app run that hits an IOCTL and says, ok protected mode is over now
> >>>>> clean up these things wasting power.
> >>>>
> >>>> What happens if some clock needs to be disabled?
> >>>> Like clocks that must be gated before setting a new clock rate
> >>>> or reparenting. The kernel supports these, and it wouldn't surprise me
> >>>> if some driver actually requires this. You might end up blocking that driver
> >>>> or breaking its probe function.
> >>>
> >>> Arggh, using those phandles in the chosen section means uboot is going
> >>> to have to get recompiled every time the DTS changes. I think we need
> >>> to come up with a scheme that doesn't need for uboot to be aware of
> >>> phandles.
> >>
> >> Why is that? U-boot is perfectly capable of patching device tree blobs.
> >>
> >> Mainline u-boot already updates the memory node, and if needed,
> >> the reserved-memory node as well.
> >>
> >> Someone just has to write the (ugly) code to do it, which Luc
> >> has already done for clock phandles for sunxi.
> >
> > So uboot is going to contain code to hunt through the Linux provided
> > DTB to find the correct phandles for the clocks/regulators and then
> > patch those phandles into the chosen section? How is uboot going to
> > reconcile it's concept of what those clock/regulators are with a Linux
> > provided DTB that is constant state of flux?
> >
> > I think trying to get uboot to manipulate phandles in a Linux provided
> > DTB is an incredibly fragile mechanism that will be prone to breakage.
> >
> > Much better to come with a scheme where uboot just inserts fixed
> > strings into the DTB. That last example device tree I posted removed
> > all of the phandles from the chosen section, but it relies on the
> > kernel gaining 'boot' mode.
> >
> >
> >>
> >> U-boot itself does not need to use the dtb, though that seems
> >> like the direction it's headed.
> >>
> >>> Something like this...
> >>> uboot adds the chosen section then Linux would error out if the
> >>> framebuffer in the chosen section doesn't match the reserved memory it
> >>> is expecting. Or make uboot smart enough to hunt down the reserved
> >>> memory section and patch it like it does with dramsize.
> >>
> >> And someone probably will. Why is that a problem?
> >>
> >>
> >> ChenYu
> >>
> >>
> >>>
> >>> reserved-memory {
> >>> #address-cells = <1>;
> >>> #size-cells = <1>;
> >>> ranges;
> >>>
> >>> display_reserved: framebuffer@78000000 {
> >>> reg = <0x78000000 (1600 * 1200 * 2)>;
> >>> };
> >>> };
> >>>
> >>> lcd0: lcd-controller@820000 {
> >>> compatible = "marvell,dove-lcd";
> >>> reg = <0x820000 0x1000>;
> >>> interrupts = <47>;
> >>> framebuffer = <&display_reserved>;
> >>> clocks = <&si5351 0>;
> >>> clock-names = "ext_ref_clk_1";
> >>> };
> >>>
> >>> chosen {
> >>> boot-framebuffer {
> >>> framebuffer = <0x78000000>;
> >>> width = <1600>;
> >>> height = <1200>;
> >>> stride = <(1600 * 2)>;
> >>> format = "r5g6b5";
> >>> };
> >>> }
> >>>
> >>>
> >>>
> >>>>
> >>>> And what if reset controls are asserted then de-asserted in the probe function?
> >>>> IIRC there are drivers that do this to reset the underlying hardware.
> >>>>
> >>>>> Maybe it should be renamed 'boot' mode. To implement this the various
> >>>>> 'turn off' functions would get a 'boot' mode flag. In boot mode they
> >>>>> track the ref counts but don't turn things off when the ref count hits
> >>>>> zero. Then that ioctl() that the user space app calls runs the chains
> >>>>> of all of the clocks/regulators/etc and if the ref count is zero turns
> >>>>> them off again and clears 'boot' mode. Doesn't matter if you turn off
> >>>>> something again that is already off.
> >>>>
> >>>> And what if something just happened to be left on that some driver
> >>>> wants to turn off? You are assuming everything not used is off,
> >>>> which is not always the case.
> >
> >
> >
> > --
> > Jon Smirl
> > jonsmirl@gmail.com
>
>
>
> --
> Jon Smirl
> jonsmirl@gmail.com
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: jonsmirl @ 2014-10-05 20:34 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20141005200150.4379.28017@quantum>
On Sun, Oct 5, 2014 at 4:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>> I edited the subject line to something more appropriate. This impacts
>> a lot of platforms and we should be getting more replies from people
>> on the ARM kernel list. This is likely something that deserves a
>> Kernel Summit discussion.
>
> ELC-E and LPC are just around the corner as well. I am attending both. I
> suppose some of the others interested in this topic will be present?
>
>>
>> To summarize the problem....
>>
>> The BIOS (uboot, etc) may have set various devices up into a working
>> state before handing off to the kernel. The most obvious example of
>> this is the boot display.
>>
>> So how do we transition onto the kernel provided device specific
>> drivers without interrupting these functioning devices?
>>
>> This used to be simple, just build everything into the kernel. But
>> then along came multi-architecture kernels where most drivers are not
>> built in. Those kernels clean up everything (ie turn off unused
>> clocks, regulators, etc) right before user space starts. That's done
>> as a power saving measure.
>>
>> Unfortunately that code turns off the clocks and regulators providing
>> the display on your screen. Which then promptly gets turned back on a
>> half second later when the boot scripts load the display driver. Let's
>> all hope the boot doesn't fail while the display is turned off.
>
> I would say this is one half of the discussion. How do you ever really
> know when it is safe to disable these things? In a world with loadable
> modules the kernel cannot know that everything that is going to be
> loaded has been loaded. There really is no boundary that makes it easy
> to say, "OK, now it is truly safe for me to disable these things because
> I know every possible piece of code that might claim these resources has
> probed".
Humans know where this boundary is and can insert the clean up command
at the right point in the bootscript. It is also not fatal if the
command is inserted at the wrong point, things will just needlessly
flicker. It not even fatal if you never run this command, you'll just
leave clocks/regulators turned on that could be turned off.
>
> Somebody (Geert?) proposed an ioctl for userspace to send such an "all
> clear" signal, but I dislike that idea. We're talking about managing
> fairly low-level hardware bits (clocks, regulators) and getting
> userspace involved feels wrong.
I proposed it, Geert has been commenting on it. This 'all clear' just
says it is ok now to go clean up the mess the BIOS left.
User space already got involved when we turned the bulk of the device
drivers into loadable modules on multi-arch kernels. On these kernels
the system isn't really up when user space starts, it still needs to
load all of these loadable device drivers. And some of those device
drivers implement your display.
So I think of it as loading the rest of the kernel via user space,
then run a command saying 'do power clean up'. Currently 'do power
clear up' occurs right before user space starts - which is clearly the
wrong point in time if most of the drivers haven't had a chance to
load.
>
> Additionally clocks and regulators should be managed by PM Runtime
> implementations (via generic power domains and friends), so any solution
> that we come up with today that is specific for the clock and regulator
> frameworks would need to scale up to other abstraction layers, because
> those layers would probably like to know that they are not really
> idle/gated in hardware because the ioctl/garbage collector has not yet
> happened.
Maybe that is another way to look at this. PM should not be running
until all of the device drivers have a chance to load. By that I mean
all of the ones in the boot scripts, not one loaded two hours later by
hand.
After the bootscript gets the drivers loaded it starts PM running.
First thing PM does is use the IOCTL to clear out 'boot' mode and turn
off stuff the BIOS turned on and no Linux driver claimed.
I think it should be pretty obvious to a human when to start PM
running in the bootscripts.
>
> The second half of the discussion is specific to simple framebuffer and
> the desire to keep it extremely simple and narrowly focused. I can
> understand both sides of the argument and I hope to stay well out of the
> flame zone.
That discussion is a mess because the first discussion is not solved.
>
> Even if we do leave simple framebuffer alone, the *idea* behind a very
> simple, entirely data-driven driver implementation that is meant to be
> compiled into the kernel image, claim resources early, ensure they stay
> enabled and then hand-off to a real driver that may be a loadable module
> is very interesting. It doesn't have to be simplefb. It could be
> something new. Additionally if this design pattern becomes common across
> more than just displays then we might want to consider ways of Doing It
> Right.
>
> Regards,
> Mike
>
>>
>> Below is part of the discussion on how to fix this, but so far no
>> really good solution has been discovered.
>>
>> For the whole history search for the simple-framebuffer threads. There
>> have been about 1,000 messages.
>>
>> On Sun, Oct 5, 2014 at 12:34 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>> > On Sun, Oct 5, 2014 at 11:36 AM, Chen-Yu Tsai <wens@csie.org> wrote:
>> >> On Sun, Oct 5, 2014 at 11:29 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>> >>> On Sun, Oct 5, 2014 at 11:17 AM, Chen-Yu Tsai <wens@csie.org> wrote:
>> >>>> On Sun, Oct 5, 2014 at 11:07 PM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>> >>>>> On Sun, Oct 5, 2014 at 10:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> >>>>>> Hi,
>> >>>>>>
>> >>>>>> On 10/05/2014 02:52 PM, jonsmirl@gmail.com wrote:
>> >>>>>>> On Sun, Oct 5, 2014 at 5:03 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> >>>>>>>> Hi,
>> >>>>>>>>
>> >>>>>>>> On 10/04/2014 02:38 PM, jonsmirl@gmail.com wrote:
>> >>>>>>>>> On Sat, Oct 4, 2014 at 5:50 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> >>>>>>>>>> Hi,
>> >>>>>>>>>>
>> >>>>>>>>>> On 10/04/2014 12:56 AM, jonsmirl@gmail.com wrote:
>> >>>>>>>>>>> On Fri, Oct 3, 2014 at 4:08 PM, Rob Herring <robherring2@gmail.com> wrote:
>> >>>>>>>>>>>> On Fri, Oct 3, 2014 at 12:34 PM, Hans de Goede <hdegoede@redhat.com> wrote:
>> >>>>>>>>>>>>> Hi,
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> On 10/03/2014 05:57 PM, Rob Herring wrote:
>> >>>>>>>>>>>>>> On Fri, Oct 3, 2014 at 9:05 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> >>>>>>>>>>>>>>> A simple-framebuffer node represents a framebuffer setup by the firmware /
>> >>>>>>>>>>>>>>> bootloader. Such a framebuffer may have a number of clocks in use, add a
>> >>>>>>>>>>>>>>> property to communicate this to the OS.
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> >>>>>>>>>>>>>>> Reviewed-by: Mike Turquette <mturquette@linaro.org>
>> >>>>>>>>>>>>>>>
>> >>>>>>>>>>>>>>> --
>> >>>>>>>>>>>>>>> Changes in v2:
>> >>>>>>>>>>>>>>> -Added Reviewed-by: Mike Turquette <mturquette@linaro.org>
>> >>>>>>>>>>>>>>> Changes in v3:
>> >>>>>>>>>>>>>>> -Updated description to make clear simplefb deals with more then just memory
>> >>>>>>>>>>>>>>
>> >>>>>>>>>>>>>> NAK. "Fixing" the description is not what I meant and does not address
>> >>>>>>>>>>>>>> my concerns. Currently, simplefb is configuration data. It is
>> >>>>>>>>>>>>>> auxiliary data about how a chunk of memory is used. Using it or not
>> >>>>>>>>>>>>>> has no side effects on the hardware setup, but you are changing that
>> >>>>>>>>>>>>>> aspect. You are mixing in a hardware description that is simply
>> >>>>>>>>>>>>>> inaccurate.
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> Memory is hardware too, what simplefb is is best seen as a virtual device, and
>> >>>>>>>>>>>>> even virtual devices have hardware resources they need, such as a chunk of memory,
>> >>>>>>>>>>>>> which the kernel should not touch other then use it as a framebuffer, likewise
>> >>>>>>>>>>>>> on some systems the virtual device needs clocks to keep running.
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>>> The kernel has made the decision to turn off "unused" clocks. If its
>> >>>>>>>>>>>>>> determination of what is unused is wrong, then it is not a problem to
>> >>>>>>>>>>>>>> fix in DT.
>> >>>>>>>>>>>>>
>> >>>>>>>>>>>>> No, it is up to DT to tell the kernel what clocks are used, that is how it works
>> >>>>>>>>>>>>> for any other device. I don't understand why some people keep insisting simplefb
>> >>>>>>>>>>>>> for some reason is o so very very special, because it is not special, it needs
>> >>>>>>>>>>>>> resources, and it needs to tell the kernel about this or bad things happen.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> No, the DT describes the connections of clocks from h/w block to h/w
>> >>>>>>>>>>>> block. Their use is implied by the connection.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> And yes, as the other thread mentioned DT is more than just hardware
>> >>>>>>>>>>>> information. However, what you are adding IS hardware information and
>> >>>>>>>>>>>> clearly has a place somewhere else. And adding anything which is not
>> >>>>>>>>>>>> hardware description gets much more scrutiny.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>>> More over it is a bit late to start making objections now. This has already been
>> >>>>>>>>>>>>> discussed to death for weeks now, and every argument against this patch has already
>> >>>>>>>>>>>>> been countered multiple times (including the one you are making now). Feel free to
>> >>>>>>>>>>>>> read the entire thread in the archives under the subject:
>> >>>>>>>>>>>>> "[PATCH 4/4] simplefb: add clock handling code"
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> You are on v2 and I hardly see any consensus on the v1 thread. Others
>> >>>>>>>>>>>> have made suggestions which I would agree with and you've basically
>> >>>>>>>>>>>> ignored them.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>>> At one point in time we need to stop bickering and make a decision, that time has
>> >>>>>>>>>>>>> come now, so please lets get this discussion over with and merge this, so that
>> >>>>>>>>>>>>> we can all move on and spend out time in a more productive manner.
>> >>>>>>>>>>>>
>> >>>>>>>>>>>> Not an effective argument to get things merged.
>> >>>>>>>>>>>
>> >>>>>>>>>>> If there is not good solution to deferring clock clean up in the
>> >>>>>>>>>>> kernel, another approach is to change how simple-framebuffer is
>> >>>>>>>>>>> described in the device tree....
>> >>>>>>>>>>>
>> >>>>>>>>>>> Right now it is a stand-alone item that looks like a device node, but
>> >>>>>>>>>>> it isn't a device.
>> >>>>>>>>>>>
>> >>>>>>>>>>> framebuffer {
>> >>>>>>>>>>> compatible = "simple-framebuffer";
>> >>>>>>>>>>> reg = <0x1d385000 (1600 * 1200 * 2)>;
>> >>>>>>>>>>> width = <1600>;
>> >>>>>>>>>>> height = <1200>;
>> >>>>>>>>>>> stride = <(1600 * 2)>;
>> >>>>>>>>>>> format = "r5g6b5";
>> >>>>>>>>>>> };
>> >>>>>>>>>>>
>> >>>>>>>>>>> How about something like this?
>> >>>>>>>>>>>
>> >>>>>>>>>>> reserved-memory {
>> >>>>>>>>>>> #address-cells = <1>;
>> >>>>>>>>>>> #size-cells = <1>;
>> >>>>>>>>>>> ranges;
>> >>>>>>>>>>>
>> >>>>>>>>>>> display_reserved: framebuffer@78000000 {
>> >>>>>>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>> >>>>>>>>>>> };
>> >>>>>>>>>>> };
>> >>>>>>>>>>>
>> >>>>>>>>>>> lcd0: lcd-controller@820000 {
>> >>>>>>>>>>> compatible = "marvell,dove-lcd";
>> >>>>>>>>>>> reg = <0x820000 0x1000>;
>> >>>>>>>>>>> interrupts = <47>;
>> >>>>>>>>>>> clocks = <&si5351 0>;
>> >>>>>>>>>>> clock-names = "ext_ref_clk_1";
>> >>>>>>>>>>> };
>> >>>>>>>>>>>
>> >>>>>>>>>>> chosen {
>> >>>>>>>>>>> boot-framebuffer {
>> >>>>>>>>>>> compatible = "simple-framebuffer";
>> >>>>>>>>>>> device = <&lcd0>;
>> >>>>>>>>>>> framebuffer = <&display_reserved>;
>> >>>>>>>>>>> width = <1600>;
>> >>>>>>>>>>> height = <1200>;
>> >>>>>>>>>>> stride = <(1600 * 2)>;
>> >>>>>>>>>>> format = "r5g6b5";
>> >>>>>>>>>>> };
>> >>>>>>>>>>> }
>> >>>>>>>>>>>
>> >>>>>>>>>>>
>> >>>>>>>>>>> This moves the definition of the boot framebuffer setup into the area
>> >>>>>>>>>>> where bootloader info is suppose to go. Then you can use the phandle
>> >>>>>>>>>>> to follow the actual device chains and protect the clocks and
>> >>>>>>>>>>> regulators. To make that work you are required to provide an accurate
>> >>>>>>>>>>> description of the real video hardware so that this chain can be
>> >>>>>>>>>>> followed.
>> >>>>>>>>>>
>> >>>>>>>>>> This will not work, first of all multiple blocks may be involved, so
>> >>>>>>>>>> the device = in the boot-framebuffer would need to be a list. That in
>> >>>>>>>>>> itself is not a problem, the problem is that the blocks used may have
>> >>>>>>>>>> multiple clocks, of which the setup mode likely uses only a few.
>> >>>>>>>>>>
>> >>>>>>>>>> So if we do things this way, we end up keeping way to many clocks
>> >>>>>>>>>> enabled.
>> >>>>>>>>>
>> >>>>>>>>> That doesn't hurt anything.
>> >>>>>>>>
>> >>>>>>>> <snip lots of stuff based on the above>
>> >>>>>>>>
>> >>>>>>>> Wrong, that does hurt things. As already discussed simply stopping the
>> >>>>>>>> clocks from being disabled by the unused_clks mechanism is not enough,
>> >>>>>>>> since clocks may be shared, and we must stop another device driver
>> >>>>>>>> sharing the clock and doing clk_enable; probe; clk_disable; disabling
>> >>>>>>>> the shared clk, which means calling clk_enable on it to mark it as
>> >>>>>>>> being in use. So this will hurt cause it will cause us to enable a bunch
>> >>>>>>>> of clks which should not be enabled.
>> >>>>>>>
>> >>>>>>> I said earlier that you would need to add a protected mechanism to
>> >>>>>>> clocks to handle this phase. When a clock/regulator is protected you
>> >>>>>>> can turn it on but you can't turn it off. When simplefb exits it will
>> >>>>>>> clear this protected status. When the protected status gets cleared
>> >>>>>>> treat it as a ref count decrement and turn off the clock/regulator if
>> >>>>>>> indicated to do so. If a clock is protected, all of it parents get the
>> >>>>>>> protected bit set too.
>> >>>>>>>
>> >>>>>>> Protected mode
>> >>>>>>> you can turn clocks on, but not off
>> >>>>>>> it is ref counted
>> >>>>>>> when it hits zero, look at the normal refcount and set that state
>> >>>>>>>
>> >>>>>>> Protected mode is not long lived. It only hangs around until the real
>> >>>>>>> device driver loads.
>> >>>>>>
>> >>>>>> And that has already been nacked by the clk maintainer because it is
>> >>>>>> too complicated, and I agree this is tons more complicated then simply
>> >>>>>> adding the list of clocks to the simplefb node.
>> >>>>>>
>> >>>>>>>> I've been thinking more about this, and I understand that people don't
>> >>>>>>>> want to put hardware description in the simplefb node, but this is
>> >>>>>>>> not hardware description.
>> >>>>>>>>
>> >>>>>>>> u-boot sets up the display-pipeline to scan out of a certain part of
>> >>>>>>>> memory, in order to do this it writes the memory address to some registers
>> >>>>>>>> in the display pipeline, so what it is passing is not hardware description
>> >>>>>>>> (it is not passing all possible memory addresses for the framebuffer), but
>> >>>>>>>> it is passing information about the state in which it has left the display
>> >>>>>>>> pipeline, iow it is passing state information.
>> >>>>>>>
>> >>>>>>> Giving the buffer to a piece of hardware is more than setting state.
>> >>>>>>> The hardware now owns that buffer. That ownership needs to be managed
>> >>>>>>> so that if the hardware decides it doesn't want the buffer it can be
>> >>>>>>> returned to the global pool.
>> >>>>>>>
>> >>>>>>> That's why the buffer has to go into that reserved memory section, not
>> >>>>>>> the chosen section.
>> >>>>>>
>> >>>>>> But is not in the reserved memory section, it is in the simplefb
>> >>>>>> section, and we're stuck with this because of backward compatibility.
>> >>>>>>
>> >>>>>> An OS doesn't have to process chosen, it is just
>> >>>>>>> there for informational purposes. To keep the OS from thinking it owns
>> >>>>>>> the memory in that video buffer and can use it for OS purposes, it has
>> >>>>>>> to go into that reserved memory section.
>> >>>>>>>
>> >>>>>>> The clocks are different. We know exactly who owns those clocks, the
>> >>>>>>> graphics hardware.
>> >>>>>>>
>> >>>>>>> You want something like this where the state of the entire video path
>> >>>>>>> is encoded into the chosen section. But that info is going to vary all
>> >>>>>>> over the place with TV output, HDMI output, LCD panels, etc. simplefb
>> >>>>>>> isn't going to be simple any more. Plus the purposes of adding all of
>> >>>>>>> this complexity is just to handle the half second transition from boot
>> >>>>>>> loader control to real driver.
>> >>>>>>>
>> >>>>>>> reserved-memory {
>> >>>>>>> #address-cells = <1>;
>> >>>>>>> #size-cells = <1>;
>> >>>>>>> ranges;
>> >>>>>>>
>> >>>>>>> display_reserved: framebuffer@78000000 {
>> >>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>> >>>>>>> };
>> >>>>>>> };
>> >>>>>>>
>> >>>>>>> lcd0: lcd-controller@820000 {
>> >>>>>>> compatible = "marvell,dove-lcd";
>> >>>>>>> reg = <0x820000 0x1000>;
>> >>>>>>> interrupts = <47>;
>> >>>>>>> framebuffer = <&display_reserved>;
>> >>>>>>> clocks = <&si5351 0>;
>> >>>>>>> clock-names = "ext_ref_clk_1";
>> >>>>>>> };
>> >>>>>>>
>> >>>>>>> chosen {
>> >>>>>>> boot-framebuffer {
>> >>>>>>> compatible = "simple-framebuffer";
>> >>>>>>> state {
>> >>>>>>> device = <&lcd0>;
>> >>>>>>> width = <1600>;
>> >>>>>>> height = <1200>;
>> >>>>>>> stride = <(1600 * 2)>;
>> >>>>>>> format = "r5g6b5";
>> >>>>>>> clocks = <&si5351 on 10mhz>;
>> >>>>>>
>> >>>>>> Right, so here we get a list of clocks which are actually in use
>> >>>>>> by the simplefb, just as I've been advocating all the time already.
>> >>>>>>
>> >>>>>> Note that the clock speed is not necessary, all the kernel needs to
>> >>>>>> know is that it must not change it.
>> >>>>>>
>> >>>>>> So as you seem to agree that we need to pass some sort of clock state
>> >>>>>> info, then all we need to agree on now is where to put it, as said adding
>> >>>>>> the reg property to a reserved-memory node is out of the question because
>> >>>>>> of backward compat, likewise storing width, height and format in a state
>> >>>>>> sub-node are out of the question for the same reason. But other then that
>> >>>>>> I'm fine with putting the simplefb node under chosen and putting clocks
>> >>>>>> in there (without the state subnode) as you suggest above.
>> >>>>>>
>> >>>>>> So we seem to be in agreement here :)
>> >>>>>>
>> >>>>>>> output = "hdmi";
>> >>>>>>> state {
>> >>>>>>> device = <&hdmi>
>> >>>>>>> clocks = <&xyz on 12mhz>;
>> >>>>>>> }
>> >>>>>>
>> >>>>>> That level of detail won't be necessary, simplefb is supposed to be
>> >>>>>> simple, the kernel does not need to know which outputs there are, there
>> >>>>>> will always be only one for simplefb.
>> >>>>>
>> >>>>> I don't agree, but you are going to do it any way so I'll try and help
>> >>>>> tkeep problem side effects I know of to a minimum. You are relying on
>> >>>>> the BIOS to provide detailed, accurate information. Relying on BIOSes
>> >>>>> to do that has historically been a very bad idea.
>> >>>>>
>> >>>>> If you go the way of putting this info into the chosen section you are
>> >>>>> going to have to mark the clocks/regulators in use for all of the
>> >>>>> outputs too -- hdmi, TV, LCD, backlights, etc, etc. Not going to be
>> >>>>> useful if the backlight turns off because simplefb hasn't grabbed it.
>> >>>>>
>> >>>>> This is the only real difference between the proposals - you want
>> >>>>> uboot to enumerate what needs to be protected. I don't trust the BIOS
>> >>>>> to do that reliably so I'd preferred to just protect everything in the
>> >>>>> device hardware chain until the device specific drivers load.
>> >>>>>
>> >>>>> -------------------------------------------------------
>> >>>>>
>> >>>>> I also still believe this is a problem up in Linux that we shouldn't
>> >>>>> be using the device tree to fix.
>> >>>>>
>> >>>>> It seems to me that the need for something like a 'protected' mode is
>> >>>>> a generic problem that could be extended to all hardware. In protected
>> >>>>> mode things can be turned on but nothing can be turned off. Only
>> >>>>> after the kernel has all of the necessary drivers loaded would a small
>> >>>>> app run that hits an IOCTL and says, ok protected mode is over now
>> >>>>> clean up these things wasting power.
>> >>>>
>> >>>> What happens if some clock needs to be disabled?
>> >>>> Like clocks that must be gated before setting a new clock rate
>> >>>> or reparenting. The kernel supports these, and it wouldn't surprise me
>> >>>> if some driver actually requires this. You might end up blocking that driver
>> >>>> or breaking its probe function.
>> >>>
>> >>> Arggh, using those phandles in the chosen section means uboot is going
>> >>> to have to get recompiled every time the DTS changes. I think we need
>> >>> to come up with a scheme that doesn't need for uboot to be aware of
>> >>> phandles.
>> >>
>> >> Why is that? U-boot is perfectly capable of patching device tree blobs.
>> >>
>> >> Mainline u-boot already updates the memory node, and if needed,
>> >> the reserved-memory node as well.
>> >>
>> >> Someone just has to write the (ugly) code to do it, which Luc
>> >> has already done for clock phandles for sunxi.
>> >
>> > So uboot is going to contain code to hunt through the Linux provided
>> > DTB to find the correct phandles for the clocks/regulators and then
>> > patch those phandles into the chosen section? How is uboot going to
>> > reconcile it's concept of what those clock/regulators are with a Linux
>> > provided DTB that is constant state of flux?
>> >
>> > I think trying to get uboot to manipulate phandles in a Linux provided
>> > DTB is an incredibly fragile mechanism that will be prone to breakage.
>> >
>> > Much better to come with a scheme where uboot just inserts fixed
>> > strings into the DTB. That last example device tree I posted removed
>> > all of the phandles from the chosen section, but it relies on the
>> > kernel gaining 'boot' mode.
>> >
>> >
>> >>
>> >> U-boot itself does not need to use the dtb, though that seems
>> >> like the direction it's headed.
>> >>
>> >>> Something like this...
>> >>> uboot adds the chosen section then Linux would error out if the
>> >>> framebuffer in the chosen section doesn't match the reserved memory it
>> >>> is expecting. Or make uboot smart enough to hunt down the reserved
>> >>> memory section and patch it like it does with dramsize.
>> >>
>> >> And someone probably will. Why is that a problem?
>> >>
>> >>
>> >> ChenYu
>> >>
>> >>
>> >>>
>> >>> reserved-memory {
>> >>> #address-cells = <1>;
>> >>> #size-cells = <1>;
>> >>> ranges;
>> >>>
>> >>> display_reserved: framebuffer@78000000 {
>> >>> reg = <0x78000000 (1600 * 1200 * 2)>;
>> >>> };
>> >>> };
>> >>>
>> >>> lcd0: lcd-controller@820000 {
>> >>> compatible = "marvell,dove-lcd";
>> >>> reg = <0x820000 0x1000>;
>> >>> interrupts = <47>;
>> >>> framebuffer = <&display_reserved>;
>> >>> clocks = <&si5351 0>;
>> >>> clock-names = "ext_ref_clk_1";
>> >>> };
>> >>>
>> >>> chosen {
>> >>> boot-framebuffer {
>> >>> framebuffer = <0x78000000>;
>> >>> width = <1600>;
>> >>> height = <1200>;
>> >>> stride = <(1600 * 2)>;
>> >>> format = "r5g6b5";
>> >>> };
>> >>> }
>> >>>
>> >>>
>> >>>
>> >>>>
>> >>>> And what if reset controls are asserted then de-asserted in the probe function?
>> >>>> IIRC there are drivers that do this to reset the underlying hardware.
>> >>>>
>> >>>>> Maybe it should be renamed 'boot' mode. To implement this the various
>> >>>>> 'turn off' functions would get a 'boot' mode flag. In boot mode they
>> >>>>> track the ref counts but don't turn things off when the ref count hits
>> >>>>> zero. Then that ioctl() that the user space app calls runs the chains
>> >>>>> of all of the clocks/regulators/etc and if the ref count is zero turns
>> >>>>> them off again and clears 'boot' mode. Doesn't matter if you turn off
>> >>>>> something again that is already off.
>> >>>>
>> >>>> And what if something just happened to be left on that some driver
>> >>>> wants to turn off? You are assuming everything not used is off,
>> >>>> which is not always the case.
>> >
>> >
>> >
>> > --
>> > Jon Smirl
>> > jonsmirl@gmail.com
>>
>>
>>
>> --
>> Jon Smirl
>> jonsmirl@gmail.com
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [linux-sunxi] Re: Fixing boot-time hiccups in your display
From: Julian Calaby @ 2014-10-05 22:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKON4OxunQNC_Ebf5u9yh9EnGS2A+DdWCRyoFcJWS7GSNoLoqA@mail.gmail.com>
Hi Jon,
On Mon, Oct 6, 2014 at 7:34 AM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
> On Sun, Oct 5, 2014 at 4:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>> I edited the subject line to something more appropriate. This impacts
>>> a lot of platforms and we should be getting more replies from people
>>> on the ARM kernel list. This is likely something that deserves a
>>> Kernel Summit discussion.
>>
>> ELC-E and LPC are just around the corner as well. I am attending both. I
>> suppose some of the others interested in this topic will be present?
>>
>>>
>>> To summarize the problem....
>>>
>>> The BIOS (uboot, etc) may have set various devices up into a working
>>> state before handing off to the kernel. The most obvious example of
>>> this is the boot display.
>>>
>>> So how do we transition onto the kernel provided device specific
>>> drivers without interrupting these functioning devices?
>>>
>>> This used to be simple, just build everything into the kernel. But
>>> then along came multi-architecture kernels where most drivers are not
>>> built in. Those kernels clean up everything (ie turn off unused
>>> clocks, regulators, etc) right before user space starts. That's done
>>> as a power saving measure.
>>>
>>> Unfortunately that code turns off the clocks and regulators providing
>>> the display on your screen. Which then promptly gets turned back on a
>>> half second later when the boot scripts load the display driver. Let's
>>> all hope the boot doesn't fail while the display is turned off.
>>
>> I would say this is one half of the discussion. How do you ever really
>> know when it is safe to disable these things? In a world with loadable
>> modules the kernel cannot know that everything that is going to be
>> loaded has been loaded. There really is no boundary that makes it easy
>> to say, "OK, now it is truly safe for me to disable these things because
>> I know every possible piece of code that might claim these resources has
>> probed".
>
> Humans know where this boundary is and can insert the clean up command
> at the right point in the bootscript. It is also not fatal if the
> command is inserted at the wrong point, things will just needlessly
> flicker. It not even fatal if you never run this command, you'll just
> leave clocks/regulators turned on that could be turned off.
What about distros? Would this "all clear" point be at the same point
in the boot process for every sub-architecture? Would it ever change?
Thanks,
--
Julian Calaby
Email: julian.calaby@gmail.com
Profile: http://www.google.com/profiles/julian.calaby/
^ permalink raw reply
* Re: [linux-sunxi] Re: Fixing boot-time hiccups in your display
From: jonsmirl @ 2014-10-05 23:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAGRGNgUcHxWEZmdsM947UuKW-20u+T20ztNqgWxms6iK8-8ugw@mail.gmail.com>
On Sun, Oct 5, 2014 at 6:36 PM, Julian Calaby <julian.calaby@gmail.com> wrote:
> Hi Jon,
>
> On Mon, Oct 6, 2014 at 7:34 AM, jonsmirl@gmail.com <jonsmirl@gmail.com> wrote:
>> On Sun, Oct 5, 2014 at 4:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
>>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>>> I edited the subject line to something more appropriate. This impacts
>>>> a lot of platforms and we should be getting more replies from people
>>>> on the ARM kernel list. This is likely something that deserves a
>>>> Kernel Summit discussion.
>>>
>>> ELC-E and LPC are just around the corner as well. I am attending both. I
>>> suppose some of the others interested in this topic will be present?
>>>
>>>>
>>>> To summarize the problem....
>>>>
>>>> The BIOS (uboot, etc) may have set various devices up into a working
>>>> state before handing off to the kernel. The most obvious example of
>>>> this is the boot display.
>>>>
>>>> So how do we transition onto the kernel provided device specific
>>>> drivers without interrupting these functioning devices?
>>>>
>>>> This used to be simple, just build everything into the kernel. But
>>>> then along came multi-architecture kernels where most drivers are not
>>>> built in. Those kernels clean up everything (ie turn off unused
>>>> clocks, regulators, etc) right before user space starts. That's done
>>>> as a power saving measure.
>>>>
>>>> Unfortunately that code turns off the clocks and regulators providing
>>>> the display on your screen. Which then promptly gets turned back on a
>>>> half second later when the boot scripts load the display driver. Let's
>>>> all hope the boot doesn't fail while the display is turned off.
>>>
>>> I would say this is one half of the discussion. How do you ever really
>>> know when it is safe to disable these things? In a world with loadable
>>> modules the kernel cannot know that everything that is going to be
>>> loaded has been loaded. There really is no boundary that makes it easy
>>> to say, "OK, now it is truly safe for me to disable these things because
>>> I know every possible piece of code that might claim these resources has
>>> probed".
>>
>> Humans know where this boundary is and can insert the clean up command
>> at the right point in the bootscript. It is also not fatal if the
>> command is inserted at the wrong point, things will just needlessly
>> flicker. It not even fatal if you never run this command, you'll just
>> leave clocks/regulators turned on that could be turned off.
>
> What about distros? Would this "all clear" point be at the same point
> in the boot process for every sub-architecture? Would it ever change?
It is not really an "all clear". It is a "BIOS cleanup" command. All
this clean up does is potentially turn off some clocks/regulators that
the BIOS left on and no one cares about. It runs around to each
clock/regulator in the system that the kernel thinks is off, and runs
the code necessary to ensure that the clock/regulator is really off.
The timing of the "BIOS cleanup" point is not really critical.
Currently it is happening right before user space starts. There are a
bunch of late_initcalls() that turn off all of the clocks/regulators
that the BIOS enabled and no Linux driver has claimed. Unfortunately
if your display driver hasn't loaded, it is going to turn off your
display. Of course your display will come right back as soon as the
device driver loads.
So the proposal is to turn these late_initcalls() into an IOCTL. The
power management frameworks would then get a command for calling that
IOCTL. The logical place to put this command is in your bootscript
right after all of the loadable drivers have loaded. But... it is not
critical. If you do it too early your display will still flicker. It
you don't do it at all you'll do is waste some power. Just move it
later in the scripts until the things you care about stop flickering.
Nothing fatal happens if you get it wrong - it is just a power saving
function.
So how does this get implemented? Is it enough just to add a single
bit on each clock/regulator that starts off as 1 (ie boot mode). Then
as the various drivers claim these clocks regulators this bit get
cleared. A hole in this scheme is someone who turns off a root clock
which has children still in boot mode. You can't allow this clock to
be turned off until all of it's children are out of boot mode. Maybe
run the kids and look for boot mode? then turn the root clock boot
mode bit back on and ignore the request to turn it off? All of these
details need design work.
>
> Thanks,
>
> --
> Julian Calaby
>
> Email: julian.calaby@gmail.com
> Profile: http://www.google.com/profiles/julian.calaby/
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: [linux-sunxi] Re: [PATCH v3] dt-bindings: Add a clocks property to the simple-framebuffer bindin
From: Hans de Goede @ 2014-10-06 7:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKON4OyE8Zw-yA-mKhPEs=yz-R4=wkZSwA1g8-61pD4RPp1gNQ@mail.gmail.com>
Hi,
On 10/05/2014 05:17 PM, jonsmirl@gmail.com wrote:
> On Sun, Oct 5, 2014 at 11:16 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> Hi,
>>
>> On 10/05/2014 05:07 PM, jonsmirl@gmail.com wrote:
>>> On Sun, Oct 5, 2014 at 10:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>> Hi,
>>>>
>>>> On 10/05/2014 02:52 PM, jonsmirl@gmail.com wrote:
>>>>> On Sun, Oct 5, 2014 at 5:03 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>> Hi,
>>>>>>
>>>>>> On 10/04/2014 02:38 PM, jonsmirl@gmail.com wrote:
>>>>>>> On Sat, Oct 4, 2014 at 5:50 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>> Hi,
>>>>>>>>
>>>>>>>> On 10/04/2014 12:56 AM, jonsmirl@gmail.com wrote:
>>>>>>>>> On Fri, Oct 3, 2014 at 4:08 PM, Rob Herring <robherring2@gmail.com> wrote:
>>>>>>>>>> On Fri, Oct 3, 2014 at 12:34 PM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>>>>> Hi,
>>>>>>>>>>>
>>>>>>>>>>> On 10/03/2014 05:57 PM, Rob Herring wrote:
>>>>>>>>>>>> On Fri, Oct 3, 2014 at 9:05 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>>>>>>>>>>>>> A simple-framebuffer node represents a framebuffer setup by the firmware /
>>>>>>>>>>>>> bootloader. Such a framebuffer may have a number of clocks in use, add a
>>>>>>>>>>>>> property to communicate this to the OS.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>>>>>>>>>>> Reviewed-by: Mike Turquette <mturquette@linaro.org>
>>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> Changes in v2:
>>>>>>>>>>>>> -Added Reviewed-by: Mike Turquette <mturquette@linaro.org>
>>>>>>>>>>>>> Changes in v3:
>>>>>>>>>>>>> -Updated description to make clear simplefb deals with more then just memory
>>>>>>>>>>>>
>>>>>>>>>>>> NAK. "Fixing" the description is not what I meant and does not address
>>>>>>>>>>>> my concerns. Currently, simplefb is configuration data. It is
>>>>>>>>>>>> auxiliary data about how a chunk of memory is used. Using it or not
>>>>>>>>>>>> has no side effects on the hardware setup, but you are changing that
>>>>>>>>>>>> aspect. You are mixing in a hardware description that is simply
>>>>>>>>>>>> inaccurate.
>>>>>>>>>>>
>>>>>>>>>>> Memory is hardware too, what simplefb is is best seen as a virtual device, and
>>>>>>>>>>> even virtual devices have hardware resources they need, such as a chunk of memory,
>>>>>>>>>>> which the kernel should not touch other then use it as a framebuffer, likewise
>>>>>>>>>>> on some systems the virtual device needs clocks to keep running.
>>>>>>>>>>>
>>>>>>>>>>>> The kernel has made the decision to turn off "unused" clocks. If its
>>>>>>>>>>>> determination of what is unused is wrong, then it is not a problem to
>>>>>>>>>>>> fix in DT.
>>>>>>>>>>>
>>>>>>>>>>> No, it is up to DT to tell the kernel what clocks are used, that is how it works
>>>>>>>>>>> for any other device. I don't understand why some people keep insisting simplefb
>>>>>>>>>>> for some reason is o so very very special, because it is not special, it needs
>>>>>>>>>>> resources, and it needs to tell the kernel about this or bad things happen.
>>>>>>>>>>
>>>>>>>>>> No, the DT describes the connections of clocks from h/w block to h/w
>>>>>>>>>> block. Their use is implied by the connection.
>>>>>>>>>>
>>>>>>>>>> And yes, as the other thread mentioned DT is more than just hardware
>>>>>>>>>> information. However, what you are adding IS hardware information and
>>>>>>>>>> clearly has a place somewhere else. And adding anything which is not
>>>>>>>>>> hardware description gets much more scrutiny.
>>>>>>>>>>
>>>>>>>>>>> More over it is a bit late to start making objections now. This has already been
>>>>>>>>>>> discussed to death for weeks now, and every argument against this patch has already
>>>>>>>>>>> been countered multiple times (including the one you are making now). Feel free to
>>>>>>>>>>> read the entire thread in the archives under the subject:
>>>>>>>>>>> "[PATCH 4/4] simplefb: add clock handling code"
>>>>>>>>>>
>>>>>>>>>> You are on v2 and I hardly see any consensus on the v1 thread. Others
>>>>>>>>>> have made suggestions which I would agree with and you've basically
>>>>>>>>>> ignored them.
>>>>>>>>>>
>>>>>>>>>>> At one point in time we need to stop bickering and make a decision, that time has
>>>>>>>>>>> come now, so please lets get this discussion over with and merge this, so that
>>>>>>>>>>> we can all move on and spend out time in a more productive manner.
>>>>>>>>>>
>>>>>>>>>> Not an effective argument to get things merged.
>>>>>>>>>
>>>>>>>>> If there is not good solution to deferring clock clean up in the
>>>>>>>>> kernel, another approach is to change how simple-framebuffer is
>>>>>>>>> described in the device tree....
>>>>>>>>>
>>>>>>>>> Right now it is a stand-alone item that looks like a device node, but
>>>>>>>>> it isn't a device.
>>>>>>>>>
>>>>>>>>> framebuffer {
>>>>>>>>> compatible = "simple-framebuffer";
>>>>>>>>> reg = <0x1d385000 (1600 * 1200 * 2)>;
>>>>>>>>> width = <1600>;
>>>>>>>>> height = <1200>;
>>>>>>>>> stride = <(1600 * 2)>;
>>>>>>>>> format = "r5g6b5";
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> How about something like this?
>>>>>>>>>
>>>>>>>>> reserved-memory {
>>>>>>>>> #address-cells = <1>;
>>>>>>>>> #size-cells = <1>;
>>>>>>>>> ranges;
>>>>>>>>>
>>>>>>>>> display_reserved: framebuffer@78000000 {
>>>>>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>>>>>>>>> };
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> lcd0: lcd-controller@820000 {
>>>>>>>>> compatible = "marvell,dove-lcd";
>>>>>>>>> reg = <0x820000 0x1000>;
>>>>>>>>> interrupts = <47>;
>>>>>>>>> clocks = <&si5351 0>;
>>>>>>>>> clock-names = "ext_ref_clk_1";
>>>>>>>>> };
>>>>>>>>>
>>>>>>>>> chosen {
>>>>>>>>> boot-framebuffer {
>>>>>>>>> compatible = "simple-framebuffer";
>>>>>>>>> device = <&lcd0>;
>>>>>>>>> framebuffer = <&display_reserved>;
>>>>>>>>> width = <1600>;
>>>>>>>>> height = <1200>;
>>>>>>>>> stride = <(1600 * 2)>;
>>>>>>>>> format = "r5g6b5";
>>>>>>>>> };
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> This moves the definition of the boot framebuffer setup into the area
>>>>>>>>> where bootloader info is suppose to go. Then you can use the phandle
>>>>>>>>> to follow the actual device chains and protect the clocks and
>>>>>>>>> regulators. To make that work you are required to provide an accurate
>>>>>>>>> description of the real video hardware so that this chain can be
>>>>>>>>> followed.
>>>>>>>>
>>>>>>>> This will not work, first of all multiple blocks may be involved, so
>>>>>>>> the device = in the boot-framebuffer would need to be a list. That in
>>>>>>>> itself is not a problem, the problem is that the blocks used may have
>>>>>>>> multiple clocks, of which the setup mode likely uses only a few.
>>>>>>>>
>>>>>>>> So if we do things this way, we end up keeping way to many clocks
>>>>>>>> enabled.
>>>>>>>
>>>>>>> That doesn't hurt anything.
>>>>>>
>>>>>> <snip lots of stuff based on the above>
>>>>>>
>>>>>> Wrong, that does hurt things. As already discussed simply stopping the
>>>>>> clocks from being disabled by the unused_clks mechanism is not enough,
>>>>>> since clocks may be shared, and we must stop another device driver
>>>>>> sharing the clock and doing clk_enable; probe; clk_disable; disabling
>>>>>> the shared clk, which means calling clk_enable on it to mark it as
>>>>>> being in use. So this will hurt cause it will cause us to enable a bunch
>>>>>> of clks which should not be enabled.
>>>>>
>>>>> I said earlier that you would need to add a protected mechanism to
>>>>> clocks to handle this phase. When a clock/regulator is protected you
>>>>> can turn it on but you can't turn it off. When simplefb exits it will
>>>>> clear this protected status. When the protected status gets cleared
>>>>> treat it as a ref count decrement and turn off the clock/regulator if
>>>>> indicated to do so. If a clock is protected, all of it parents get the
>>>>> protected bit set too.
>>>>>
>>>>> Protected mode
>>>>> you can turn clocks on, but not off
>>>>> it is ref counted
>>>>> when it hits zero, look at the normal refcount and set that state
>>>>>
>>>>> Protected mode is not long lived. It only hangs around until the real
>>>>> device driver loads.
>>>>
>>>> And that has already been nacked by the clk maintainer because it is
>>>> too complicated, and I agree this is tons more complicated then simply
>>>> adding the list of clocks to the simplefb node.
>>>>
>>>>>> I've been thinking more about this, and I understand that people don't
>>>>>> want to put hardware description in the simplefb node, but this is
>>>>>> not hardware description.
>>>>>>
>>>>>> u-boot sets up the display-pipeline to scan out of a certain part of
>>>>>> memory, in order to do this it writes the memory address to some registers
>>>>>> in the display pipeline, so what it is passing is not hardware description
>>>>>> (it is not passing all possible memory addresses for the framebuffer), but
>>>>>> it is passing information about the state in which it has left the display
>>>>>> pipeline, iow it is passing state information.
>>>>>
>>>>> Giving the buffer to a piece of hardware is more than setting state.
>>>>> The hardware now owns that buffer. That ownership needs to be managed
>>>>> so that if the hardware decides it doesn't want the buffer it can be
>>>>> returned to the global pool.
>>>>>
>>>>> That's why the buffer has to go into that reserved memory section, not
>>>>> the chosen section.
>>>>
>>>> But is not in the reserved memory section, it is in the simplefb
>>>> section, and we're stuck with this because of backward compatibility.
>>>>
>>>> An OS doesn't have to process chosen, it is just
>>>>> there for informational purposes. To keep the OS from thinking it owns
>>>>> the memory in that video buffer and can use it for OS purposes, it has
>>>>> to go into that reserved memory section.
>>>>>
>>>>> The clocks are different. We know exactly who owns those clocks, the
>>>>> graphics hardware.
>>>>>
>>>>> You want something like this where the state of the entire video path
>>>>> is encoded into the chosen section. But that info is going to vary all
>>>>> over the place with TV output, HDMI output, LCD panels, etc. simplefb
>>>>> isn't going to be simple any more. Plus the purposes of adding all of
>>>>> this complexity is just to handle the half second transition from boot
>>>>> loader control to real driver.
>>>>>
>>>>> reserved-memory {
>>>>> #address-cells = <1>;
>>>>> #size-cells = <1>;
>>>>> ranges;
>>>>>
>>>>> display_reserved: framebuffer@78000000 {
>>>>> reg = <0x78000000 (1600 * 1200 * 2)>;
>>>>> };
>>>>> };
>>>>>
>>>>> lcd0: lcd-controller@820000 {
>>>>> compatible = "marvell,dove-lcd";
>>>>> reg = <0x820000 0x1000>;
>>>>> interrupts = <47>;
>>>>> framebuffer = <&display_reserved>;
>>>>> clocks = <&si5351 0>;
>>>>> clock-names = "ext_ref_clk_1";
>>>>> };
>>>>>
>>>>> chosen {
>>>>> boot-framebuffer {
>>>>> compatible = "simple-framebuffer";
>>>>> state {
>>>>> device = <&lcd0>;
>>>>> width = <1600>;
>>>>> height = <1200>;
>>>>> stride = <(1600 * 2)>;
>>>>> format = "r5g6b5";
>>>>> clocks = <&si5351 on 10mhz>;
>>>>
>>>> Right, so here we get a list of clocks which are actually in use
>>>> by the simplefb, just as I've been advocating all the time already.
>>>>
>>>> Note that the clock speed is not necessary, all the kernel needs to
>>>> know is that it must not change it.
>>>>
>>>> So as you seem to agree that we need to pass some sort of clock state
>>>> info, then all we need to agree on now is where to put it, as said adding
>>>> the reg property to a reserved-memory node is out of the question because
>>>> of backward compat, likewise storing width, height and format in a state
>>>> sub-node are out of the question for the same reason. But other then that
>>>> I'm fine with putting the simplefb node under chosen and putting clocks
>>>> in there (without the state subnode) as you suggest above.
>>>>
>>>> So we seem to be in agreement here :)
>>>>
>>>>> output = "hdmi";
>>>>> state {
>>>>> device = <&hdmi>
>>>>> clocks = <&xyz on 12mhz>;
>>>>> }
>>>>
>>>> That level of detail won't be necessary, simplefb is supposed to be
>>>> simple, the kernel does not need to know which outputs there are, there
>>>> will always be only one for simplefb.
>>>
>>> I don't agree, but you are going to do it any way so I'll try and help
>>> tkeep problem side effects I know of to a minimum. You are relying on
>>> the BIOS to provide detailed, accurate information. Relying on BIOSes
>>> to do that has historically been a very bad idea.
>>
>> And here we come to the gist of your objections, I don't agree for
>> various reasons:
>>
>> 1) Despite firmware sometimes having bugs, we simply have to trust the firmware,
>> e.g. the reg property of simplefb could be set to point to some mmio rather then
>> just the framebuffer, and writing that mmio could do real bad things, yet we trust
>> it not to do that.
>>
>> 2) The firmware we're talking about here, at least for now, is u-boot which
>> is fully FOSS and under our control.
>>
>> 3) "I don't trust foo" is not a technical argument, and decisions like this
>> should be made on technical arguments.
>
> You also realize that uboot now is going to have to be compiled with
> the same DTS that the kernel is using so that the phandles will match
> up. And now you are going to have to recompile your uboot everytime
> that DTS changes.
That is simply not true. Please stop spreading FUD about this without actually
knowing how it works / having read the code.
Regards,
Hans
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Hans de Goede @ 2014-10-06 7:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKON4OxunQNC_Ebf5u9yh9EnGS2A+DdWCRyoFcJWS7GSNoLoqA@mail.gmail.com>
Hi,
On 10/05/2014 10:34 PM, jonsmirl@gmail.com wrote:
> On Sun, Oct 5, 2014 at 4:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>> I edited the subject line to something more appropriate. This impacts
>>> a lot of platforms and we should be getting more replies from people
>>> on the ARM kernel list. This is likely something that deserves a
>>> Kernel Summit discussion.
>>
>> ELC-E and LPC are just around the corner as well. I am attending both. I
>> suppose some of the others interested in this topic will be present?
>>
>>>
>>> To summarize the problem....
>>>
>>> The BIOS (uboot, etc) may have set various devices up into a working
>>> state before handing off to the kernel. The most obvious example of
>>> this is the boot display.
>>>
>>> So how do we transition onto the kernel provided device specific
>>> drivers without interrupting these functioning devices?
>>>
>>> This used to be simple, just build everything into the kernel. But
>>> then along came multi-architecture kernels where most drivers are not
>>> built in. Those kernels clean up everything (ie turn off unused
>>> clocks, regulators, etc) right before user space starts. That's done
>>> as a power saving measure.
>>>
>>> Unfortunately that code turns off the clocks and regulators providing
>>> the display on your screen. Which then promptly gets turned back on a
>>> half second later when the boot scripts load the display driver. Let's
>>> all hope the boot doesn't fail while the display is turned off.
>>
>> I would say this is one half of the discussion. How do you ever really
>> know when it is safe to disable these things? In a world with loadable
>> modules the kernel cannot know that everything that is going to be
>> loaded has been loaded. There really is no boundary that makes it easy
>> to say, "OK, now it is truly safe for me to disable these things because
>> I know every possible piece of code that might claim these resources has
>> probed".
>
> Humans know where this boundary is and can insert the clean up command
> at the right point in the bootscript.
No they don't, we've been over this so many times already it just is
not funny anymore. So I'm not even go to repeat the same old technical
arguments why this is not true.
There is only one 100% correct moment when it is safe to turn of resources
used by something like simplefb, which is when a real driver takes over.
The same for any other resources used by any other firmware setup things,
the right moment to release those resources is at handover time, and
the handover time may differ from driver to driver, so there is no
single magic moment to disable this.
Also this non-solution completely discards the use case where e.g. simplefb
is used as an early bringup mechanism and there may complete be no real
driver for a long time (months if not years). So then again there is no
right magic moment to turn the resources off, because in this use case the
magic moment is *never*.
I'm all for finding a proper solution for this, but you seem to be blind
to anything other then your own idea that this is just a boot ordering problem,
which it is not, the problem is that things like simplefb simply need to claim
the resources they use, and then all ordering problems go away.
We've tried this "ordering magic" kind of solutions before, see e.g. the
scsi_wait_scan module hack, which was not enough, so then initrd-s started
inserting sleeps to wait for storage to be available, and the hacks just got
uglier and uglier, until we moved to an event based system, and instead
of waiting for a "magic moment", actually waited for the storage device
we're looking for to show up, which is exactly the same as what we should
do here, wait for the real driver to show up.
This also means that we need to tie resources to devices like simplefb,
because the event causing their release will be that the real driver for
the display pipeline loaded, which is not a single event for all similar
drivers. And since there is no single event, there is no single moment
to do the magic ioctl for this.
Really this is a solved problem, The only 100% correct solution is to tie
the ordering of releasing the resources to the lifetime of the simplefb,
which is easily achieved by making the simplefb properly claim the resources
it needs.
Regards,
Hans
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Hans de Goede @ 2014-10-06 7:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20141005200150.4379.28017@quantum>
Hi,
On 10/05/2014 10:01 PM, Mike Turquette wrote:
> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>> I edited the subject line to something more appropriate. This impacts
>> a lot of platforms and we should be getting more replies from people
>> on the ARM kernel list. This is likely something that deserves a
>> Kernel Summit discussion.
>
> ELC-E and LPC are just around the corner as well. I am attending both. I
> suppose some of the others interested in this topic will be present?
Having a get together about this there sounds very good. I'm willing to
organize this. If you want to attend please reply to this thread (or drop
me a personal mail) with when you will be available to attend, then I'll
pick a time where hopefully we will all be available, and I'll try to get
a room for this, not sure if I'll be able to get a room, if not we will
just need to find a quiet spot in the lobby or some such.
I'll be present for the entire week too. Monday I'll be at the u-boot
mini-conf. Thursday and Friday I'll be at the Linux Kernel Media mini-summit,
and Friday afternoon I'll be at the Wayland mini-conf.
If this causes scheduling problems, I can miss parts of the media mini-summit,
and if I really have to I can maybe miss some parts of the u-boot mini-conf,
I cannot walk out of the Wayland track as I'm one of the session leaders
there.
>> To summarize the problem....
>>
>> The BIOS (uboot, etc) may have set various devices up into a working
>> state before handing off to the kernel. The most obvious example of
>> this is the boot display.
>>
>> So how do we transition onto the kernel provided device specific
>> drivers without interrupting these functioning devices?
>>
>> This used to be simple, just build everything into the kernel. But
>> then along came multi-architecture kernels where most drivers are not
>> built in. Those kernels clean up everything (ie turn off unused
>> clocks, regulators, etc) right before user space starts. That's done
>> as a power saving measure.
>>
>> Unfortunately that code turns off the clocks and regulators providing
>> the display on your screen. Which then promptly gets turned back on a
>> half second later when the boot scripts load the display driver. Let's
>> all hope the boot doesn't fail while the display is turned off.
>
> I would say this is one half of the discussion. How do you ever really
> know when it is safe to disable these things? In a world with loadable
> modules the kernel cannot know that everything that is going to be
> loaded has been loaded. There really is no boundary that makes it easy
> to say, "OK, now it is truly safe for me to disable these things because
> I know every possible piece of code that might claim these resources has
> probed".
>
> Somebody (Geert?) proposed an ioctl for userspace to send such an "all
> clear" signal, but I dislike that idea. We're talking about managing
> fairly low-level hardware bits (clocks, regulators) and getting
> userspace involved feels wrong.
Ack, also see my other reply as on why this can never work 100% reliable.
> Additionally clocks and regulators should be managed by PM Runtime
> implementations (via generic power domains and friends), so any solution
> that we come up with today that is specific for the clock and regulator
> frameworks would need to scale up to other abstraction layers, because
> those layers would probably like to know that they are not really
> idle/gated in hardware because the ioctl/garbage collector has not yet
> happened.
>
> The second half of the discussion is specific to simple framebuffer and
> the desire to keep it extremely simple and narrowly focused. I can
> understand both sides of the argument and I hope to stay well out of the
> flame zone.
>
> Even if we do leave simple framebuffer alone, the *idea* behind a very
> simple, entirely data-driven driver implementation that is meant to be
> compiled into the kernel image, claim resources early, ensure they stay
> enabled and then hand-off to a real driver that may be a loadable module
> is very interesting. It doesn't have to be simplefb. It could be
> something new.
Right, as I already said earlier if this is just about keeping simplefb
simple, I'm more then happy to clone the simplefb binding to a new binding,
called say firmwarefb, and add resource management there.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH v2 5/5] simplefb: add clock handling code
From: Maxime Ripard @ 2014-10-06 8:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1412337125-14388-6-git-send-email-hdegoede@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 4301 bytes --]
Hi,
On Fri, Oct 03, 2014 at 01:52:05PM +0200, Hans de Goede wrote:
> From: Luc Verhaegen <libv@skynet.be>
>
> This claims and enables clocks listed in the simple framebuffer dt node.
> This is needed so that the display engine, in case the required clocks
> are known by the kernel code and are described in the dt, will remain
> properly enabled.
>
> Signed-off-by: Luc Verhaegen <libv@skynet.be>
> [hdegoede@redhat.com: drop dev_err on kzalloc failure]
> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> ---
> drivers/video/fbdev/simplefb.c | 100 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 98 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c
> index b7d5c08..f329cc1 100644
> --- a/drivers/video/fbdev/simplefb.c
> +++ b/drivers/video/fbdev/simplefb.c
> @@ -26,6 +26,7 @@
> #include <linux/module.h>
> #include <linux/platform_data/simplefb.h>
> #include <linux/platform_device.h>
> +#include <linux/clk-provider.h>
>
> static struct fb_fix_screeninfo simplefb_fix = {
> .id = "simple",
> @@ -165,8 +166,98 @@ static int simplefb_parse_pd(struct platform_device *pdev,
> return 0;
> }
>
> +/*
> + * Clock handling code.
> + *
> + * Here we handle the clocks property of our "simple-framebuffer" dt node.
> + * This is necessary so that we can make sure that any clocks needed by
> + * the display engine that the bootloader set up for us (and for which it
> + * provided a simplefb dt node), stay up, for the life of the simplefb
> + * driver.
> + *
> + * When the driver unloads, we cleanly disable, and then release the clocks.
> + */
> +struct simplefb_clock {
> + struct list_head list;
> + struct clk *clock;
> +};
> +
> +/*
> + * We only complain about errors here, no action is taken as the most likely
> + * error can only happen due to a mismatch between the bootloader which set
> + * up simplefb, and the clock definitions in the device tree. Chances are
> + * that there are no adverse effects, and if there are, a clean teardown of
> + * the fb probe will not help us much either. So just complain and carry on,
> + * and hope that the user actually gets a working fb at the end of things.
> + */
> +static void
> +simplefb_clocks_init(struct platform_device *pdev, struct list_head *list)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + int clock_count, i;
> +
> + INIT_LIST_HEAD(list);
> +
> + if (dev_get_platdata(&pdev->dev) || !np)
> + return;
> +
> + clock_count = of_clk_get_parent_count(np);
This looks like it does what you expect, but its name and the fact
that it's in the clk-provider.h file makes me wonder if you're not
using the wrong side of the abstraction.
> + for (i = 0; i < clock_count; i++) {
> + struct simplefb_clock *entry;
> + struct clk *clock = of_clk_get(np, i);
devm_clk_get?
> + int ret;
> +
> + if (IS_ERR(clock)) {
> + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
> + __func__, i, PTR_ERR(clock));
> + continue;
> + }
> +
> + ret = clk_prepare_enable(clock);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "%s: failed to enable clock %d: %d\n",
> + __func__, i, ret);
> + clk_put(clock);
> + continue;
> + }
> +
> + entry = kzalloc(sizeof(struct simplefb_clock), GFP_KERNEL);
> + if (!entry) {
> + clk_disable_unprepare(clock);
> + clk_put(clock);
> + continue;
> + }
> +
> + entry->clock = clock;
> + /*
> + * add to the front of the list, so we disable clocks in the
> + * correct order.
> + */
> + list_add(&entry->list, list);
I really don't think this whole list dance is necessary, especially
after reading this comment.
It doesn't make any sense to release the clock in a particular order,
when you don't even know in what order the bootloader actually set
them (and you haven't set any requirement on the order in the binding
either). For all you know, this could be a purely random order,
without any meaning, and it would be fine, so let's just not worry
about that.
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [linux-sunxi] Re: [PATCH v2 5/5] simplefb: add clock handling code
From: Hans de Goede @ 2014-10-06 9:11 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20141006085536.GD4090@lukather>
Hi,
On 10/06/2014 10:55 AM, Maxime Ripard wrote:
> Hi,
>
> On Fri, Oct 03, 2014 at 01:52:05PM +0200, Hans de Goede wrote:
>> From: Luc Verhaegen <libv@skynet.be>
>>
>> This claims and enables clocks listed in the simple framebuffer dt node.
>> This is needed so that the display engine, in case the required clocks
>> are known by the kernel code and are described in the dt, will remain
>> properly enabled.
>>
>> Signed-off-by: Luc Verhaegen <libv@skynet.be>
>> [hdegoede@redhat.com: drop dev_err on kzalloc failure]
>> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>> ---
>> drivers/video/fbdev/simplefb.c | 100 ++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 98 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c
>> index b7d5c08..f329cc1 100644
>> --- a/drivers/video/fbdev/simplefb.c
>> +++ b/drivers/video/fbdev/simplefb.c
>> @@ -26,6 +26,7 @@
>> #include <linux/module.h>
>> #include <linux/platform_data/simplefb.h>
>> #include <linux/platform_device.h>
>> +#include <linux/clk-provider.h>
>>
>> static struct fb_fix_screeninfo simplefb_fix = {
>> .id = "simple",
>> @@ -165,8 +166,98 @@ static int simplefb_parse_pd(struct platform_device *pdev,
>> return 0;
>> }
>>
>> +/*
>> + * Clock handling code.
>> + *
>> + * Here we handle the clocks property of our "simple-framebuffer" dt node.
>> + * This is necessary so that we can make sure that any clocks needed by
>> + * the display engine that the bootloader set up for us (and for which it
>> + * provided a simplefb dt node), stay up, for the life of the simplefb
>> + * driver.
>> + *
>> + * When the driver unloads, we cleanly disable, and then release the clocks.
>> + */
>> +struct simplefb_clock {
>> + struct list_head list;
>> + struct clk *clock;
>> +};
>> +
>> +/*
>> + * We only complain about errors here, no action is taken as the most likely
>> + * error can only happen due to a mismatch between the bootloader which set
>> + * up simplefb, and the clock definitions in the device tree. Chances are
>> + * that there are no adverse effects, and if there are, a clean teardown of
>> + * the fb probe will not help us much either. So just complain and carry on,
>> + * and hope that the user actually gets a working fb at the end of things.
>> + */
>> +static void
>> +simplefb_clocks_init(struct platform_device *pdev, struct list_head *list)
>> +{
>> + struct device_node *np = pdev->dev.of_node;
>> + int clock_count, i;
>> +
>> + INIT_LIST_HEAD(list);
>> +
>> + if (dev_get_platdata(&pdev->dev) || !np)
>> + return;
>> +
>> + clock_count = of_clk_get_parent_count(np);
>
> This looks like it does what you expect, but its name and the fact
> that it's in the clk-provider.h file makes me wonder if you're not
> using the wrong side of the abstraction.
I'll check to see if there is something better, assuming Luc does not
beat me to it.
>
>> + for (i = 0; i < clock_count; i++) {
>> + struct simplefb_clock *entry;
>> + struct clk *clock = of_clk_get(np, i);
>
> devm_clk_get?
Yes that would be better.
>> + int ret;
>> +
>> + if (IS_ERR(clock)) {
>> + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
>> + __func__, i, PTR_ERR(clock));
>> + continue;
>> + }
>> +
>> + ret = clk_prepare_enable(clock);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "%s: failed to enable clock %d: %d\n",
>> + __func__, i, ret);
>> + clk_put(clock);
>> + continue;
>> + }
>> +
>> + entry = kzalloc(sizeof(struct simplefb_clock), GFP_KERNEL);
>> + if (!entry) {
>> + clk_disable_unprepare(clock);
>> + clk_put(clock);
>> + continue;
>> + }
>> +
>> + entry->clock = clock;
>> + /*
>> + * add to the front of the list, so we disable clocks in the
>> + * correct order.
>> + */
>> + list_add(&entry->list, list);
>
> I really don't think this whole list dance is necessary, especially
> after reading this comment.
So you're suggesting to just make this an array, with say 5 entries, or ... ?
Regards,
Hans
^ permalink raw reply
* Re: [linux-sunxi] Re: [PATCH v2 5/5] simplefb: add clock handling code
From: Maxime Ripard @ 2014-10-06 9:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <54325CD0.1000308@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 4713 bytes --]
On Mon, Oct 06, 2014 at 11:11:44AM +0200, Hans de Goede wrote:
> Hi,
>
> On 10/06/2014 10:55 AM, Maxime Ripard wrote:
> > Hi,
> >
> > On Fri, Oct 03, 2014 at 01:52:05PM +0200, Hans de Goede wrote:
> >> From: Luc Verhaegen <libv@skynet.be>
> >>
> >> This claims and enables clocks listed in the simple framebuffer dt node.
> >> This is needed so that the display engine, in case the required clocks
> >> are known by the kernel code and are described in the dt, will remain
> >> properly enabled.
> >>
> >> Signed-off-by: Luc Verhaegen <libv@skynet.be>
> >> [hdegoede@redhat.com: drop dev_err on kzalloc failure]
> >> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
> >> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
> >> ---
> >> drivers/video/fbdev/simplefb.c | 100 ++++++++++++++++++++++++++++++++++++++++-
> >> 1 file changed, 98 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c
> >> index b7d5c08..f329cc1 100644
> >> --- a/drivers/video/fbdev/simplefb.c
> >> +++ b/drivers/video/fbdev/simplefb.c
> >> @@ -26,6 +26,7 @@
> >> #include <linux/module.h>
> >> #include <linux/platform_data/simplefb.h>
> >> #include <linux/platform_device.h>
> >> +#include <linux/clk-provider.h>
> >>
> >> static struct fb_fix_screeninfo simplefb_fix = {
> >> .id = "simple",
> >> @@ -165,8 +166,98 @@ static int simplefb_parse_pd(struct platform_device *pdev,
> >> return 0;
> >> }
> >>
> >> +/*
> >> + * Clock handling code.
> >> + *
> >> + * Here we handle the clocks property of our "simple-framebuffer" dt node.
> >> + * This is necessary so that we can make sure that any clocks needed by
> >> + * the display engine that the bootloader set up for us (and for which it
> >> + * provided a simplefb dt node), stay up, for the life of the simplefb
> >> + * driver.
> >> + *
> >> + * When the driver unloads, we cleanly disable, and then release the clocks.
> >> + */
> >> +struct simplefb_clock {
> >> + struct list_head list;
> >> + struct clk *clock;
> >> +};
> >> +
> >> +/*
> >> + * We only complain about errors here, no action is taken as the most likely
> >> + * error can only happen due to a mismatch between the bootloader which set
> >> + * up simplefb, and the clock definitions in the device tree. Chances are
> >> + * that there are no adverse effects, and if there are, a clean teardown of
> >> + * the fb probe will not help us much either. So just complain and carry on,
> >> + * and hope that the user actually gets a working fb at the end of things.
> >> + */
> >> +static void
> >> +simplefb_clocks_init(struct platform_device *pdev, struct list_head *list)
> >> +{
> >> + struct device_node *np = pdev->dev.of_node;
> >> + int clock_count, i;
> >> +
> >> + INIT_LIST_HEAD(list);
> >> +
> >> + if (dev_get_platdata(&pdev->dev) || !np)
> >> + return;
> >> +
> >> + clock_count = of_clk_get_parent_count(np);
> >
> > This looks like it does what you expect, but its name and the fact
> > that it's in the clk-provider.h file makes me wonder if you're not
> > using the wrong side of the abstraction.
>
> I'll check to see if there is something better, assuming Luc does not
> beat me to it.
>
> >
> >> + for (i = 0; i < clock_count; i++) {
> >> + struct simplefb_clock *entry;
> >> + struct clk *clock = of_clk_get(np, i);
> >
> > devm_clk_get?
>
> Yes that would be better.
>
> >> + int ret;
> >> +
> >> + if (IS_ERR(clock)) {
> >> + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
> >> + __func__, i, PTR_ERR(clock));
> >> + continue;
> >> + }
> >> +
> >> + ret = clk_prepare_enable(clock);
> >> + if (ret) {
> >> + dev_err(&pdev->dev,
> >> + "%s: failed to enable clock %d: %d\n",
> >> + __func__, i, ret);
> >> + clk_put(clock);
> >> + continue;
> >> + }
> >> +
> >> + entry = kzalloc(sizeof(struct simplefb_clock), GFP_KERNEL);
> >> + if (!entry) {
> >> + clk_disable_unprepare(clock);
> >> + clk_put(clock);
> >> + continue;
> >> + }
> >> +
> >> + entry->clock = clock;
> >> + /*
> >> + * add to the front of the list, so we disable clocks in the
> >> + * correct order.
> >> + */
> >> + list_add(&entry->list, list);
> >
> > I really don't think this whole list dance is necessary, especially
> > after reading this comment.
>
> So you're suggesting to just make this an array, with say 5 entries,
> or ... ?
Maybe something smarter, like a kmalloc'd array with the number of
clocks needed?
--
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: David Herrmann @ 2014-10-06 9:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <54324720.1040005@redhat.com>
Hi
On Mon, Oct 6, 2014 at 9:39 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> Hi,
>
> On 10/05/2014 10:01 PM, Mike Turquette wrote:
>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>> I edited the subject line to something more appropriate. This impacts
>>> a lot of platforms and we should be getting more replies from people
>>> on the ARM kernel list. This is likely something that deserves a
>>> Kernel Summit discussion.
>>
>> ELC-E and LPC are just around the corner as well. I am attending both. I
>> suppose some of the others interested in this topic will be present?
>
> Having a get together about this there sounds very good. I'm willing to
> organize this. If you want to attend please reply to this thread (or drop
> me a personal mail) with when you will be available to attend, then I'll
> pick a time where hopefully we will all be available, and I'll try to get
> a room for this, not sure if I'll be able to get a room, if not we will
> just need to find a quiet spot in the lobby or some such.
>
> I'll be present for the entire week too. Monday I'll be at the u-boot
> mini-conf. Thursday and Friday I'll be at the Linux Kernel Media mini-summit,
> and Friday afternoon I'll be at the Wayland mini-conf.
>
> If this causes scheduling problems, I can miss parts of the media mini-summit,
> and if I really have to I can maybe miss some parts of the u-boot mini-conf,
> I cannot walk out of the Wayland track as I'm one of the session leaders
> there.
I've been working on the gfx-device hand-over with SimpleDRM for some
time now. I fully agree with Hans. This has to be a per-device
hand-over. This works totally fine on x86 already and it should be
easy to get done for other devices. I have ported SimpleDRM to
non-VESA drivers and it works just fine, and I'm open to rename it to
firmware-drm / fwdrm / whatever and add real hardware support.
simplefb was meant to be hw-agnostic, so I understand that Stephen
doesn't want to extend it. But before we start adding more fbdev
drivers, I honestly think we should at least try pushing such a DRM
driver upstream.
I will be talking about this at XDC this week, and in the
Wayland-microconf at Plumbers. I am open for discussion. If people
want this, I can refresh my sysfb+simpledrm series and send a pull
request to Dave. He was already willing to merge it, but I wanted to
cleanup the drm BKL first..
Thanks
David
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Geert Uytterhoeven @ 2014-10-06 9:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20141005200150.4379.28017@quantum>
On Sun, Oct 5, 2014 at 10:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>> I edited the subject line to something more appropriate. This impacts
>> a lot of platforms and we should be getting more replies from people
>> on the ARM kernel list. This is likely something that deserves a
>> Kernel Summit discussion.
>
> ELC-E and LPC are just around the corner as well. I am attending both. I
> suppose some of the others interested in this topic will be present?
I'll be at ELC-E.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: [PATCH v2 5/5] simplefb: add clock handling code
From: Geert Uytterhoeven @ 2014-10-06 10:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1412337125-14388-6-git-send-email-hdegoede@redhat.com>
On Fri, Oct 3, 2014 at 1:52 PM, Hans de Goede <hdegoede@redhat.com> wrote:
> +static void
> +simplefb_clocks_init(struct platform_device *pdev, struct list_head *list)
> +{
> + struct device_node *np = pdev->dev.of_node;
> + int clock_count, i;
> +
> + INIT_LIST_HEAD(list);
> +
> + if (dev_get_platdata(&pdev->dev) || !np)
> + return;
> +
> + clock_count = of_clk_get_parent_count(np);
> + for (i = 0; i < clock_count; i++) {
> + struct simplefb_clock *entry;
> + struct clk *clock = of_clk_get(np, i);
> + int ret;
> +
> + if (IS_ERR(clock)) {
> + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n",
> + __func__, i, PTR_ERR(clock));
> + continue;
> + }
> +
> + ret = clk_prepare_enable(clock);
> + if (ret) {
> + dev_err(&pdev->dev,
> + "%s: failed to enable clock %d: %d\n",
> + __func__, i, ret);
> + clk_put(clock);
> + continue;
> + }
> +
> + entry = kzalloc(sizeof(struct simplefb_clock), GFP_KERNEL);
> + if (!entry) {
> + clk_disable_unprepare(clock);
> + clk_put(clock);
> + continue;
> + }
I'd allocate memory first, to avoid calling clk_disable_unprepare() (which
will kill your display) if memory allocation fails.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: jonsmirl @ 2014-10-06 11:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <54324445.9070400@redhat.com>
On Mon, Oct 6, 2014 at 3:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
> Hi,
>
> On 10/05/2014 10:34 PM, jonsmirl@gmail.com wrote:
>> On Sun, Oct 5, 2014 at 4:01 PM, Mike Turquette <mturquette@linaro.org> wrote:
>>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>>> I edited the subject line to something more appropriate. This impacts
>>>> a lot of platforms and we should be getting more replies from people
>>>> on the ARM kernel list. This is likely something that deserves a
>>>> Kernel Summit discussion.
>>>
>>> ELC-E and LPC are just around the corner as well. I am attending both. I
>>> suppose some of the others interested in this topic will be present?
>>>
>>>>
>>>> To summarize the problem....
>>>>
>>>> The BIOS (uboot, etc) may have set various devices up into a working
>>>> state before handing off to the kernel. The most obvious example of
>>>> this is the boot display.
>>>>
>>>> So how do we transition onto the kernel provided device specific
>>>> drivers without interrupting these functioning devices?
>>>>
>>>> This used to be simple, just build everything into the kernel. But
>>>> then along came multi-architecture kernels where most drivers are not
>>>> built in. Those kernels clean up everything (ie turn off unused
>>>> clocks, regulators, etc) right before user space starts. That's done
>>>> as a power saving measure.
>>>>
>>>> Unfortunately that code turns off the clocks and regulators providing
>>>> the display on your screen. Which then promptly gets turned back on a
>>>> half second later when the boot scripts load the display driver. Let's
>>>> all hope the boot doesn't fail while the display is turned off.
>>>
>>> I would say this is one half of the discussion. How do you ever really
>>> know when it is safe to disable these things? In a world with loadable
>>> modules the kernel cannot know that everything that is going to be
>>> loaded has been loaded. There really is no boundary that makes it easy
>>> to say, "OK, now it is truly safe for me to disable these things because
>>> I know every possible piece of code that might claim these resources has
>>> probed".
>>
>> Humans know where this boundary is and can insert the clean up command
>> at the right point in the bootscript.
>
> No they don't, we've been over this so many times already it just is
> not funny anymore. So I'm not even go to repeat the same old technical
> arguments why this is not true.
>
> There is only one 100% correct moment when it is safe to turn of resources
> used by something like simplefb, which is when a real driver takes over.
>
> The same for any other resources used by any other firmware setup things,
> the right moment to release those resources is at handover time, and
> the handover time may differ from driver to driver, so there is no
> single magic moment to disable this.
Process works like this...
boot kernel with built in drivers
user space starts
loadable drivers load
- load device specific framebuffer which claims resources from BIOS
all the loadable drivers are loaded
now run the 'clean up' command
The 'clean up' command only releases resources that no one was
claimed. The device specific framebuffer loaded and claimed all of the
video resources, so this command has no impact on those resources.
>
> Also this non-solution completely discards the use case where e.g. simplefb
> is used as an early bringup mechanism and there may complete be no real
> driver for a long time (months if not years). So then again there is no
I in no way support long term use of simplefb after the boot process.
The problems with this model are legendary on the x86. Try running
your X server right now on the VBIOS driver, see if it functions.
I will point out:
a) if you are crazy enough to do this, you can do it by simply not
running the 'clean up' command
b) write a device specific framebuffer driver while you wait years for
KMS to appear. Should take under a week to get the device specific
framebuffer driver going.
> right magic moment to turn the resources off, because in this use case the
> magic moment is *never*.
>
> I'm all for finding a proper solution for this, but you seem to be blind
> to anything other then your own idea that this is just a boot ordering problem,
Because this needs to be fixed in the OS without relying on detailed
communication with the BIOS. Of course you can get this going on one
box with one BIOS and one kernel. The problems occur when you try to
get this going on all boxes, all BIOS and all kernels.
> which it is not, the problem is that things like simplefb simply need to claim
> the resources they use, and then all ordering problems go away.
>
> We've tried this "ordering magic" kind of solutions before, see e.g. the
> scsi_wait_scan module hack, which was not enough, so then initrd-s started
> inserting sleeps to wait for storage to be available, and the hacks just got
> uglier and uglier, until we moved to an event based system, and instead
> of waiting for a "magic moment", actually waited for the storage device
> we're looking for to show up, which is exactly the same as what we should
> do here, wait for the real driver to show up.
>
> This also means that we need to tie resources to devices like simplefb,
> because the event causing their release will be that the real driver for
> the display pipeline loaded, which is not a single event for all similar
> drivers. And since there is no single event, there is no single moment
> to do the magic ioctl for this.
>
> Really this is a solved problem, The only 100% correct solution is to tie
> the ordering of releasing the resources to the lifetime of the simplefb,
> which is easily achieved by making the simplefb properly claim the resources
> it needs.
...and make sure every BIOS properly describes this. Something that
never happened in the x86 world in the last thirty years.
>
> Regards,
>
> Hans
--
Jon Smirl
jonsmirl@gmail.com
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Hans de Goede @ 2014-10-06 12:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAKON4Ozum1SeAhecR14uuAmk1NqfMuLZAEyGZnv3Yr0CwAavzA@mail.gmail.com>
Hi,
On 10/06/2014 01:26 PM, jonsmirl@gmail.com wrote:
> On Mon, Oct 6, 2014 at 3:27 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> Hi,
>>
>> On 10/05/2014 10:34 PM, jonsmirl@gmail.com wrote:
<snip>
> The 'clean up' command only releases resources that no one was
> claimed. The device specific framebuffer loaded and claimed all of the
> video resources, so this command has no impact on those resources.
Except that those resources may be turned off if they are shared by
another device with a clk_disable call. Yes I know you're suggesting
to increment the usage count of all clks, regulators, foobars and whatnots
by one, only to release them at the magic release moment, but that
means adding code for this to all affected subsystems, and as such is
not a good solution.
Besides that it has already been nacked by involved subsystem maintainers,
because it is just too ugly.
What we're talking about here from a technical pov is quite simple, we need
to keep various resources alive during the lifetime of the (fake) device using
them, e.g. simplefb. The most KISS solution for that is to simply tie them to
the lifetime of the device.
Not only is this KISS it is also obviously the right solution from a technical
viewpoint. But instead of coming with technical arguments why this is not a
good idea, you come with ...
> Because this needs to be fixed in the OS without relying on detailed
> communication with the BIOS. Of course you can get this going on one
> box with one BIOS and one kernel. The problems occur when you try to
> get this going on all boxes, all BIOS and all kernels.
FUD about how we cannot trust firmware. What we are doing here is
defining a firmware <-> OS interface, if we cannot trust the firmware, then
we should not be adding such interfaces at all, yet simplefb already exists,
all I'm doing is trying to extend it.
>> Also this non-solution completely discards the use case where e.g. simplefb
>> is used as an early bringup mechanism and there may complete be no real
>> driver for a long time (months if not years). So then again there is no
>
> I in no way support long term use of simplefb after the boot process.
You do realize that this is what it was actually *designed* for ? And that
only later the idea of using it as an early console came to bear ?
> The problems with this model are legendary on the x86. Try running
> your X server right now on the VBIOS driver, see if it functions.
And yet more fear for bad firmware.
I'm afraid that this is going to be my last reply to any objections from you,
as I simply cannot rationally argue against arguments which are mostly
based on fear.
Regards,
Hans
^ permalink raw reply
* Re: Fixing boot-time hiccups in your display
From: Hans de Goede @ 2014-10-06 12:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CANq1E4S6g0vv7odeMQytefMKu3VQJyKfDz1t5DCP_gPiBTD+CQ@mail.gmail.com>
Hi,
On 10/06/2014 11:48 AM, David Herrmann wrote:
> Hi
>
> On Mon, Oct 6, 2014 at 9:39 AM, Hans de Goede <hdegoede@redhat.com> wrote:
>> Hi,
>>
>> On 10/05/2014 10:01 PM, Mike Turquette wrote:
>>> Quoting jonsmirl@gmail.com (2014-10-05 10:09:52)
>>>> I edited the subject line to something more appropriate. This impacts
>>>> a lot of platforms and we should be getting more replies from people
>>>> on the ARM kernel list. This is likely something that deserves a
>>>> Kernel Summit discussion.
>>>
>>> ELC-E and LPC are just around the corner as well. I am attending both. I
>>> suppose some of the others interested in this topic will be present?
>>
>> Having a get together about this there sounds very good. I'm willing to
>> organize this. If you want to attend please reply to this thread (or drop
>> me a personal mail) with when you will be available to attend, then I'll
>> pick a time where hopefully we will all be available, and I'll try to get
>> a room for this, not sure if I'll be able to get a room, if not we will
>> just need to find a quiet spot in the lobby or some such.
>>
>> I'll be present for the entire week too. Monday I'll be at the u-boot
>> mini-conf. Thursday and Friday I'll be at the Linux Kernel Media mini-summit,
>> and Friday afternoon I'll be at the Wayland mini-conf.
>>
>> If this causes scheduling problems, I can miss parts of the media mini-summit,
>> and if I really have to I can maybe miss some parts of the u-boot mini-conf,
>> I cannot walk out of the Wayland track as I'm one of the session leaders
>> there.
>
> I've been working on the gfx-device hand-over with SimpleDRM for some
> time now. I fully agree with Hans. This has to be a per-device
> hand-over. This works totally fine on x86 already and it should be
> easy to get done for other devices. I have ported SimpleDRM to
> non-VESA drivers and it works just fine, and I'm open to rename it to
> firmware-drm / fwdrm / whatever and add real hardware support.
> simplefb was meant to be hw-agnostic, so I understand that Stephen
> doesn't want to extend it. But before we start adding more fbdev
> drivers, I honestly think we should at least try pushing such a DRM
> driver upstream.
>
> I will be talking about this at XDC this week, and in the
> Wayland-microconf at Plumbers. I am open for discussion. If people
> want this, I can refresh my sysfb+simpledrm series and send a pull
> request to Dave. He was already willing to merge it, but I wanted to
> cleanup the drm BKL first..
I'm very happy to hear that you're working on getting SimpleDRM ready
for merging into the mainline, I actually wanted to discuss this with
you at XDC and/or Plumbers, but I see that you're already on it, which
is great.
As for the whole discussion about adding resource management /
"real hw support" to the simplefb bindings (which I assume SimpleDRM
will support too), I too am fine with doing a new set of bindings for
this under a different compatible string if that is what people want.
Regards,
Hans
^ permalink raw reply
* Re: [PATCH] videomode: provide dummy of_get_display_timing helper
From: Tomi Valkeinen @ 2014-10-06 12:18 UTC (permalink / raw)
To: linux-fbdev
In-Reply-To: <1957575.to7BPgiTer@wuerfel>
[-- Attachment #1: Type: text/plain, Size: 2898 bytes --]
On 29/09/14 23:43, Laurent Pinchart wrote:
> Hi Arnd,
>
> Thank you for the patch.
>
> On Monday 29 September 2014 18:00:33 Arnd Bergmann wrote:
>> If CONFIG_OF is disabled but we build any driver that tries to
>> use of_get_display_timing, we get this link error:
>>
>> ERROR: "of_get_display_timing" [drivers/gpu/drm/rcar-du/rcar-du-drm.ko]
>> undefined!
>>
>> This adds an empty inline function as an alternative, to avoid changing
>> each driver using this function when we want to build them without
>> CONFIG_OF.
>>
>> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> Shouldn't empty functions also be defined for of_get_display_timings and
> of_display_timings_exist ?
I took the liberty of updating the patch.
Btw, if I just select "Renesas ARM SoCs (non-multiplatform)", without any of
the "Renesas ARM SoCs System Type" items, I get compile prints like "warning:
"__LINUX_ARM_ARCH__" is not defined".
And if I select only "ARCH_SH7372", I get "arm-linux-gnueabihf-ld: no machine
record defined".
In any case, I was able to compile the drivers/gpu/drm/rcar-du/rcar-du-drm.ko,
so I believe the patch is ok.
Tomi
From bd1815e4bff50b5569d1989aaf9096d14ce71d12 Mon Sep 17 00:00:00 2001
From: Tomi Valkeinen <tomi.valkeinen@ti.com>
Date: Mon, 6 Oct 2014 15:03:57 +0300
Subject: [PATCH] videomode: provide dummy inline functions for !CONFIG_OF
If CONFIG_OF is disabled but we build any driver that tries to
use of_get_display_timing, we get this link error:
ERROR: "of_get_display_timing" [drivers/gpu/drm/rcar-du/rcar-du-drm.ko] undefined!
This adds an empty inline function as an alternative, to avoid changing
each driver using this function when we want to build them without
CONFIG_OF.
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
include/video/of_display_timing.h | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/include/video/of_display_timing.h b/include/video/of_display_timing.h
index 79e6697af6cf..3a180c097e7c 100644
--- a/include/video/of_display_timing.h
+++ b/include/video/of_display_timing.h
@@ -15,9 +15,26 @@ struct display_timings;
#define OF_USE_NATIVE_MODE -1
+#ifdef CONFIG_OF
int of_get_display_timing(struct device_node *np, const char *name,
struct display_timing *dt);
struct display_timings *of_get_display_timings(struct device_node *np);
int of_display_timings_exist(struct device_node *np);
+#else
+static inline int of_get_display_timing(struct device_node *np, const char *name,
+ struct display_timing *dt)
+{
+ return -ENOSYS;
+}
+static inline struct display_timings *of_get_display_timings(struct device_node *np)
+{
+ return NULL;
+}
+static inline int of_display_timings_exist(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
+#endif
#endif
--
2.1.1
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply related
* Xylon framebuffer driver
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Hi,
Following patches implement Xylon framebuffer driver for logiCVC IP
core device. logiCVC is Configurable Video Controller developed and
optimized for Xilinx FPGA Spartan 6 and Series 7 devices.
Driver is used within Xilinx Linux kernel for ARM by Xylon and Xilinx
Zynq SoC reference designs or custom applications to get fb console or
GUI (Ubuntu desktop, Android) on LCD monitors or embedded device LCDs by
wide number of users accross the world.
Thanks,
Davor
^ permalink raw reply
* [RFC 01/10] video: fbdev: Xylon framebuffer driver
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver registration and OpenFirmware matching and parsing.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_main.c | 532 +++++++++++++++++++++++++++++++
1 file changed, 532 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/xylonfb_main.c
diff --git a/drivers/video/fbdev/xylon/xylonfb_main.c b/drivers/video/fbdev/xylon/xylonfb_main.c
new file mode 100644
index 0000000..743ba2a
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_main.c
@@ -0,0 +1,532 @@
+/*
+ * Xylon logiCVC frame buffer Open Firmware driver
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+static void xylonfb_init_ctrl(struct device_node *dn, enum display_flags flags,
+ u32 *ctrl)
+{
+ u32 ctrl_reg = (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+ LOGICVC_CTRL_DATA_ENABLE);
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (of_property_read_bool(dn, "hsync-active-low") ||
+ (flags & DISPLAY_FLAGS_HSYNC_LOW))
+ ctrl_reg |= LOGICVC_CTRL_HSYNC_INVERT;
+ if (of_property_read_bool(dn, "vsync-active-low") ||
+ (flags & DISPLAY_FLAGS_VSYNC_LOW))
+ ctrl_reg |= LOGICVC_CTRL_VSYNC_INVERT;
+ if (of_property_read_bool(dn, "data-enable-active-low") ||
+ (flags & DISPLAY_FLAGS_DE_LOW))
+ ctrl_reg |= LOGICVC_CTRL_DATA_ENABLE_INVERT;
+ if (of_property_read_bool(dn, "pixel-data-invert"))
+ ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_INVERT;
+ if (of_property_read_bool(dn, "pixel-data-output-trigger-high") ||
+ (flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+ ctrl_reg |= LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT;
+
+ *ctrl = ctrl_reg;
+}
+
+static int xylonfb_layer_set_format(struct xylonfb_layer_fix_data *fd,
+ struct device *dev)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ switch (fd->type) {
+ case LOGICVC_LAYER_ALPHA:
+ fd->format = XYLONFB_FORMAT_A8;
+ break;
+
+ case LOGICVC_LAYER_RGB:
+ switch (fd->bpp) {
+ case 8:
+ switch (fd->transparency) {
+ case LOGICVC_ALPHA_CLUT_16BPP:
+ fd->format = XYLONFB_FORMAT_C8;
+ fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB6565;
+ break;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ fd->format = XYLONFB_FORMAT_C8;
+ fd->format_clut = XYLONFB_FORMAT_CLUT_ARGB8888;
+ break;
+ case LOGICVC_ALPHA_LAYER:
+ fd->format = XYLONFB_FORMAT_RGB332;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 16:
+ if (fd->transparency != LOGICVC_ALPHA_LAYER)
+ return -EINVAL;
+
+ fd->format = XYLONFB_FORMAT_RGB565;
+ break;
+ case 32:
+ switch (fd->transparency) {
+ case LOGICVC_ALPHA_LAYER:
+ fd->format = XYLONFB_FORMAT_XRGB8888;
+ break;
+ case LOGICVC_ALPHA_PIXEL:
+ fd->format = XYLONFB_FORMAT_ARGB8888;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ }
+ break;
+
+ case LOGICVC_LAYER_YUV:
+ switch (fd->bpp) {
+ case 8:
+ if (fd->transparency != LOGICVC_ALPHA_CLUT_32BPP)
+ return -EINVAL;
+
+ fd->format = XYLONFB_FORMAT_C8;
+ fd->format_clut = XYLONFB_FORMAT_CLUT_AYUV8888;
+ break;
+ case 16:
+ if (fd->transparency != LOGICVC_ALPHA_LAYER)
+ return -EINVAL;
+
+ fd->format = XYLONFB_FORMAT_YUYV;
+ break;
+ case 32:
+ if (fd->transparency != LOGICVC_ALPHA_PIXEL)
+ return -EINVAL;
+
+ fd->format = XYLONFB_FORMAT_AYUV;
+ break;
+ }
+ break;
+
+ default:
+ dev_err(dev, "unsupported layer type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int xylonfb_parse_layer_info(struct device_node *parent_dn,
+ struct xylonfb_data *data, int id)
+{
+ struct device *dev = &data->pdev->dev;
+ struct device_node *dn;
+ struct xylonfb_layer_fix_data *fd;
+ int ret;
+ char layer_name[10];
+ const char *string;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ snprintf(layer_name, sizeof(layer_name), "layer_%d", id);
+ dn = of_get_child_by_name(parent_dn, layer_name);
+ if (!dn)
+ return 0;
+
+ data->layers++;
+
+ fd = devm_kzalloc(&data->pdev->dev,
+ sizeof(struct xylonfb_layer_fix_data), GFP_KERNEL);
+ if (!fd) {
+ dev_err(dev, "failed allocate layer fix data (%d)\n", id);
+ return -ENOMEM;
+ }
+
+ data->fd[id] = fd;
+
+ fd->id = id;
+
+ ret = of_property_read_u32(dn, "address", &fd->address);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get address\n");
+ return ret;
+ }
+ ret = of_property_read_u32_index(dn, "address", 1, &fd->address_range);
+
+ ret = of_property_read_u32(dn, "buffer-offset", &fd->buffer_offset);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get buffer-offset\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(dn, "bits-per-pixel", &fd->bpp);
+ if (ret) {
+ dev_err(dev, "failed get bits-per-pixel\n");
+ return ret;
+ }
+ switch (fd->bpp) {
+ case 8:
+ case 16:
+ case 32:
+ break;
+ default:
+ dev_err(dev, "invalid bits-per-pixel value\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_string(dn, "type", &string);
+ if (ret) {
+ dev_err(dev, "failed get type\n");
+ return ret;
+ }
+ if (!strcmp(string, "alpha")) {
+ fd->type = LOGICVC_LAYER_ALPHA;
+ } else if (!strcmp(string, "rgb")) {
+ fd->type = LOGICVC_LAYER_RGB;
+ } else if (!strcmp(string, "yuv")) {
+ fd->type = LOGICVC_LAYER_YUV;
+ } else {
+ dev_err(dev, "unsupported layer type\n");
+ return -EINVAL;
+ }
+
+ if (fd->type != LOGICVC_LAYER_ALPHA) {
+ ret = of_property_read_string(dn, "transparency", &string);
+ if (ret) {
+ dev_err(dev, "failed get transparency\n");
+ return ret;
+ }
+ if (!strcmp(string, "clut16")) {
+ fd->transparency = LOGICVC_ALPHA_CLUT_16BPP;
+ } else if (!strcmp(string, "clut32")) {
+ fd->transparency = LOGICVC_ALPHA_CLUT_32BPP;
+ } else if (!strcmp(string, "layer")) {
+ fd->transparency = LOGICVC_ALPHA_LAYER;
+ } else if (!strcmp(string, "pixel")) {
+ fd->transparency = LOGICVC_ALPHA_PIXEL;
+ } else {
+ dev_err(dev, "unsupported layer transparency\n");
+ return -EINVAL;
+ }
+ }
+
+ if (of_property_read_bool(dn, "component-swap"))
+ fd->component_swap = true;
+
+ fd->width = data->pixel_stride;
+
+ ret = xylonfb_layer_set_format(fd, dev);
+ if (ret) {
+ dev_err(dev, "failed set layer format\n");
+ return ret;
+ }
+
+ of_node_put(dn);
+
+ return id + 1;
+}
+
+static int xylon_parse_hw_info(struct device_node *dn,
+ struct xylonfb_data *data)
+{
+ struct device *dev = &data->pdev->dev;
+ int ret;
+ const char *string;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ ret = of_property_read_u32(dn, "background-layer-bits-per-pixel",
+ &data->bg_layer_bpp);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get bg-layer-bits-per-pixel\n");
+ return ret;
+ } else if (ret = 0) {
+ data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER;
+
+ ret = of_property_read_string(dn, "background-layer-type",
+ &string);
+ if (ret) {
+ dev_err(dev, "failed get bg-layer-type\n");
+ return ret;
+ }
+ if (!strcmp(string, "rgb")) {
+ data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_RGB;
+ } else if (!strcmp(string, "yuv")) {
+ data->flags |= XYLONFB_FLAGS_BACKGROUND_LAYER_YUV;
+ } else {
+ dev_err(dev, "unsupported bg layer type\n");
+ return -EINVAL;
+ }
+ }
+
+ if (of_property_read_bool(dn, "display-interface-itu656"))
+ data->flags |= XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656;
+
+ if (of_property_read_bool(dn, "readable-regs"))
+ data->flags |= XYLONFB_FLAGS_READABLE_REGS;
+ else
+ dev_warn(dev, "logicvc registers not readable\n");
+
+ if (of_property_read_bool(dn, "size-position"))
+ data->flags |= XYLONFB_FLAGS_SIZE_POSITION;
+ else
+ dev_warn(dev, "logicvc size-position disabled\n");
+
+ ret = of_property_read_u32(dn, "pixel-stride", &data->pixel_stride);
+ if (ret) {
+ dev_err(dev, "failed get pixel-stride\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(dn, "power-delay", &data->pwr_delay);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get power-delay\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(dn, "signal-delay", &data->sig_delay);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get signal\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id logicvc_of_match[] = {
+ { .compatible = "xylon,logicvc-3.02.b" },
+ { .compatible = "xylon,logicvc-4.01.b" },
+ {/* end of table */}
+};
+
+static int xylonfb_get_logicvc_configuration(struct xylonfb_data *data)
+{
+ struct device *dev = &data->pdev->dev;
+ struct device_node *dn = data->device;
+ const struct of_device_id *match;
+ struct videomode vm;
+ int i, ret;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ match = of_match_node(logicvc_of_match, dn);
+ if (!match) {
+ dev_err(dev, "failed match logicvc\n");
+ return -ENODEV;
+ }
+
+ ret = of_address_to_resource(dn, 0, &data->resource_mem);
+ if (ret) {
+ dev_err(dev, "failed get mem resource\n");
+ return ret;
+ }
+ data->irq = of_irq_to_resource(dn, 0, &data->resource_irq);
+ if (data->irq = 0) {
+ dev_err(dev, "failed get irq resource\n");
+ return ret;
+ }
+
+ ret = xylon_parse_hw_info(dn, data);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < LOGICVC_MAX_LAYERS; i++) {
+ ret = xylonfb_parse_layer_info(dn, data, i);
+ if (ret < 0)
+ return ret;
+ if (ret = 0)
+ break;
+ }
+
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER &&
+ data->layers = LOGICVC_MAX_LAYERS) {
+ data->flags &= ~XYLONFB_FLAGS_BACKGROUND_LAYER;
+ data->layers--;
+ if (data->console_layer = data->layers)
+ data->console_layer--;
+
+ dev_warn(dev, "invalid last layer configuration\n");
+ }
+
+ if (data->vm.name[0] = 0) {
+ ret = of_get_videomode(dn, &vm, OF_USE_NATIVE_MODE);
+ if (!ret) {
+ fb_videomode_from_videomode(&vm, &data->vm.vmode);
+
+ sprintf(data->vm.name, "%dx%d",
+ data->vm.vmode.xres, data->vm.vmode.yres);
+
+ data->flags |= XYLONFB_FLAGS_VMODE_CUSTOM;
+ }
+ }
+
+ xylonfb_init_ctrl(dn, vm.flags, &data->vm.ctrl);
+
+ return 0;
+}
+
+static int xylonfb_get_driver_configuration(struct xylonfb_data *data)
+{
+ struct device *dev = &data->pdev->dev;
+ struct device_node *dn = data->pdev->dev.of_node;
+ int ret;
+ const char *string;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ data->device = of_parse_phandle(dn, "device", 0);
+ if (!data->device) {
+ dev_err(dev, "failed get device\n");
+ return -ENODEV;
+ }
+
+ data->encoder = of_parse_phandle(dn, "encoder", 0);
+ if (!data->encoder)
+ dev_warn(dev, "no available encoder\n");
+
+ data->pixel_clock = of_parse_phandle(dn, "clocks", 0);
+ if (!data->pixel_clock)
+ dev_warn(dev, "no available clocks\n");
+
+ ret = of_property_read_u32(dn, "console-layer", &data->console_layer);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get console-layer\n");
+ return ret;
+ } else {
+ data->flags |= XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+ }
+
+ if (of_property_read_bool(dn, "vsync-irq"))
+ data->flags |= XYLONFB_FLAGS_VSYNC_IRQ;
+
+ ret = of_property_read_string(dn, "video-mode", &string);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "failed get video-mode\n");
+ return ret;
+ } else if (ret = 0) {
+ strcpy(data->vm.name, string);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int xylonfb_probe(struct platform_device *pdev)
+{
+ struct xylonfb_data *data;
+ int ret;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct xylonfb_data),
+ GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "failed allocate init data\n");
+ return -ENOMEM;
+ }
+
+ data->pdev = pdev;
+
+ ret = xylonfb_get_driver_configuration(data);
+ if (ret)
+ goto xylonfb_probe_error;
+
+ ret = xylonfb_get_logicvc_configuration(data);
+ if (ret)
+ goto xylonfb_probe_error;
+
+ ret = xylonfb_init_core(data);
+
+xylonfb_probe_error:
+ return ret;
+}
+
+static int xylonfb_remove(struct platform_device *pdev)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ return xylonfb_deinit_core(pdev);
+}
+
+static const struct of_device_id xylonfb_of_match[] = {
+ { .compatible = "xylon,fb-3.00.a" },
+ {/* end of table */},
+};
+MODULE_DEVICE_TABLE(of, xylonfb_of_match);
+
+static struct platform_driver xylonfb_driver = {
+ .probe = xylonfb_probe,
+ .remove = xylonfb_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = XYLONFB_DEVICE_NAME,
+ .of_match_table = xylonfb_of_match,
+ },
+};
+
+static int xylonfb_get_params(char *options)
+{
+ char *this_opt;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt)
+ continue;
+ xylonfb_mode_option = this_opt;
+ }
+ return 0;
+}
+
+static int xylonfb_init(void)
+{
+ char *option = NULL;
+ /*
+ * Kernel boot options (in 'video=xxxfb:<options>' format)
+ */
+ if (fb_get_options(XYLONFB_DRIVER_NAME, &option))
+ return -ENODEV;
+ /* Set internal module parameters */
+ xylonfb_get_params(option);
+
+ if (platform_driver_register(&xylonfb_driver)) {
+ pr_err("failed %s driver registration\n", XYLONFB_DRIVER_NAME);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void __exit xylonfb_exit(void)
+{
+ platform_driver_unregister(&xylonfb_driver);
+}
+
+module_init(xylonfb_init);
+module_exit(xylonfb_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION(XYLONFB_DRIVER_DESCRIPTION);
+MODULE_VERSION(XYLONFB_DRIVER_VERSION);
--
1.9.1
^ permalink raw reply related
* [RFC 02/10] video: fbdev: Xylon: Framebuffer driver core
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver fb_ops core functionality.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_core.c | 1685 ++++++++++++++++++++++++++++++
drivers/video/fbdev/xylon/xylonfb_core.h | 253 +++++
2 files changed, 1938 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/xylonfb_core.c
create mode 100644 drivers/video/fbdev/xylon/xylonfb_core.h
diff --git a/drivers/video/fbdev/xylon/xylonfb_core.c b/drivers/video/fbdev/xylon/xylonfb_core.c
new file mode 100644
index 0000000..ba6b25e
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.c
@@ -0,0 +1,1685 @@
+/*
+ * Xylon logiCVC frame buffer driver core functions
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include "xylonfb_core.h"
+#include "logicvc.h"
+
+#define LOGICVC_PIX_FMT_AYUV v4l2_fourcc('A', 'Y', 'U', 'V')
+#define LOGICVC_PIX_FMT_AVUY v4l2_fourcc('A', 'V', 'U', 'Y')
+#define LOGICVC_PIX_FMT_ALPHA v4l2_fourcc('A', '8', ' ', ' ')
+
+#define XYLONFB_PSEUDO_PALETTE_SIZE 256
+#define XYLONFB_VRES_DEFAULT 1080
+
+#define LOGICVC_COLOR_RGB_BLACK 0
+#define LOGICVC_COLOR_RGB_WHITE 0xFFFFFF
+#define LOGICVC_COLOR_YUV888_BLACK 0x8080
+#define LOGICVC_COLOR_YUV888_WHITE 0xFF8080
+
+char *xylonfb_mode_option;
+
+static const struct xylonfb_vmode xylonfb_vm = {
+ .vmode = {
+ .refresh = 60,
+ .xres = 1024,
+ .yres = 768,
+ .pixclock = KHZ2PICOS(65000),
+ .left_margin = 160,
+ .right_margin = 24,
+ .upper_margin = 29,
+ .lower_margin = 3,
+ .hsync_len = 136,
+ .vsync_len = 6,
+ .vmode = FB_VMODE_NONINTERLACED
+ },
+ .name = "1024x768"
+};
+
+static int xylonfb_set_timings(struct fb_info *fbi, int bpp);
+static void xylonfb_logicvc_disp_ctrl(struct fb_info *fbi, bool enable);
+static void xylonfb_enable_logicvc_output(struct fb_info *fbi);
+static void xylonfb_disable_logicvc_output(struct fb_info *fbi);
+static void xylonfb_logicvc_layer_enable(struct fb_info *fbi, bool enable);
+static void xylonfb_fbi_update(struct fb_info *fbi);
+
+static u32 xylonfb_get_reg(void __iomem *base, unsigned int offset,
+ struct xylonfb_layer_data *ld)
+{
+ return readl(base + offset);
+}
+
+static void xylonfb_set_reg(u32 value, void __iomem *base, unsigned int offset,
+ struct xylonfb_layer_data *ld)
+{
+ writel(value, (base + offset));
+}
+
+static unsigned long xylonfb_get_reg_mem_addr(void __iomem *base,
+ unsigned int offset,
+ struct xylonfb_layer_data *ld)
+{
+ unsigned int ordinal = offset / LOGICVC_REG_STRIDE;
+
+ if ((unsigned long)base - (unsigned long)ld->data->dev_base) {
+ return (unsigned long)(&ld->regs) + (ordinal * sizeof(u32));
+ } else {
+ ordinal -= (LOGICVC_CTRL_ROFF / LOGICVC_REG_STRIDE);
+ return (unsigned long)(&ld->data->regs) +
+ (ordinal * sizeof(u32));
+ }
+}
+
+static u32 xylonfb_get_reg_mem(void __iomem *base, unsigned int offset,
+ struct xylonfb_layer_data *ld)
+{
+ return *((unsigned long *)xylonfb_get_reg_mem_addr(base, offset, ld));
+}
+
+static void xylonfb_set_reg_mem(u32 value, void __iomem *base,
+ unsigned int offset,
+ struct xylonfb_layer_data *ld)
+{
+ unsigned long *reg_mem_addr + (unsigned long *)xylonfb_get_reg_mem_addr(base, offset, ld);
+ *reg_mem_addr = value;
+ writel((*reg_mem_addr), (base + offset));
+}
+
+static irqreturn_t xylonfb_isr(int irq, void *dev_id)
+{
+ struct fb_info **afbi = dev_get_drvdata(dev_id);
+ struct fb_info *fbi = afbi[0];
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ void __iomem *dev_base = data->dev_base;
+ u32 isr;
+
+ XYLONFB_DBG(CORE, "%s", __func__);
+
+ isr = readl(dev_base + LOGICVC_INT_STAT_ROFF);
+ if (isr & LOGICVC_INT_V_SYNC) {
+ writel(LOGICVC_INT_V_SYNC, dev_base + LOGICVC_INT_STAT_ROFF);
+
+ data->vsync.count++;
+
+ if (waitqueue_active(&data->vsync.wait))
+ wake_up_interruptible(&data->vsync.wait);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int xylonfb_open(struct fb_info *fbi, int user)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ bool enable = false;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (atomic_read(&ld->refcount) = 0) {
+ if (ld->flags & XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN) {
+ enable = true;
+ ld->flags &= ~XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN;
+ } else if (fbi->var.activate = FB_ACTIVATE_NOW) {
+ enable = true;
+ } else if (fbi->var.activate = FB_ACTIVATE_NXTOPEN) {
+ ld->flags |= XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN;
+ } else if (fbi->var.activate = FB_ACTIVATE_VBL) {
+ if (xylonfb_vsync_wait(0, fbi) > 0)
+ enable = true;
+ }
+
+ if (enable) {
+ xylonfb_logicvc_layer_enable(fbi, true);
+ atomic_inc(&data->refcount);
+ }
+ }
+
+ atomic_inc(&ld->refcount);
+
+ return 0;
+}
+
+static int xylonfb_release(struct fb_info *fbi, int user)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ atomic_dec(&ld->refcount);
+
+ if (atomic_read(&ld->refcount) = 0) {
+ xylonfb_logicvc_layer_enable(fbi, false);
+ atomic_dec(&data->refcount);
+ }
+
+ return 0;
+}
+
+static int xylonfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (var->xres < LOGICVC_MIN_XRES)
+ var->xres = LOGICVC_MIN_XRES;
+ if (var->xres > LOGICVC_MAX_XRES)
+ var->xres = LOGICVC_MAX_XRES;
+ if (var->yres < LOGICVC_MIN_VRES)
+ var->yres = LOGICVC_MIN_VRES;
+ if (var->yres > LOGICVC_MAX_VRES)
+ var->yres = LOGICVC_MAX_VRES;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->xres_virtual > fd->width)
+ var->xres_virtual = fd->width;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+ if (var->yres_virtual > fd->height)
+ var->yres_virtual = fd->height;
+
+ /* YUV 4:2:2 layer type can only have even layer xoffset */
+ if (fd->format = XYLONFB_FORMAT_YUYV)
+ var->xoffset &= ~((unsigned long) + 1);
+
+ if ((var->xoffset + var->xres) >= var->xres_virtual)
+ var->xoffset = var->xres_virtual - var->xres - 1;
+ if ((var->yoffset + var->yres) >= var->yres_virtual)
+ var->yoffset = var->yres_virtual - var->yres - 1;
+
+ if (var->bits_per_pixel != fbi->var.bits_per_pixel) {
+ if (var->bits_per_pixel = 24)
+ var->bits_per_pixel = 32;
+ else
+ var->bits_per_pixel = fbi->var.bits_per_pixel;
+ }
+
+ var->grayscale = fbi->var.grayscale;
+
+ var->transp.offset = fbi->var.transp.offset;
+ var->transp.length = fbi->var.transp.length;
+ var->transp.msb_right = fbi->var.transp.msb_right;
+ var->red.offset = fbi->var.red.offset;
+ var->red.length = fbi->var.red.length;
+ var->red.msb_right = fbi->var.red.msb_right;
+ var->green.offset = fbi->var.green.offset;
+ var->green.length = fbi->var.green.length;
+ var->green.msb_right = fbi->var.green.msb_right;
+ var->blue.offset = fbi->var.blue.offset;
+ var->blue.length = fbi->var.blue.length;
+ var->blue.msb_right = fbi->var.blue.msb_right;
+ var->height = fbi->var.height;
+ var->width = fbi->var.width;
+ var->sync = fbi->var.sync;
+ var->rotate = fbi->var.rotate;
+
+ return 0;
+}
+
+static int xylonfb_set_par(struct fb_info *fbi)
+{
+ struct device *dev = fbi->dev;
+ struct fb_info **afbi = NULL;
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ unsigned long f;
+ int i, bpp;
+ int ret = 0;
+ char vmode_opt[VMODE_NAME_SIZE];
+ bool resolution_change, layer_on[LOGICVC_MAX_LAYERS];
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (data->flags & XYLONFB_FLAGS_VMODE_SET)
+ return 0;
+
+ if ((fbi->var.xres = data->vm_active.vmode.xres) ||
+ (fbi->var.yres = data->vm_active.vmode.yres)) {
+ resolution_change = false;
+ } else {
+ resolution_change = true;
+ }
+
+ if (resolution_change ||
+ (data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+ struct xylonfb_layer_data *ld;
+
+ afbi = dev_get_drvdata(fbi->device);
+ for (i = 0; i < data->layers; i++) {
+ ld = afbi[i]->par;
+ if (ld->flags & XYLONFB_FLAGS_LAYER_ENABLED)
+ layer_on[i] = true;
+ else
+ layer_on[i] = false;
+ }
+ }
+
+ xylonfb_disable_logicvc_output(fbi);
+ xylonfb_logicvc_disp_ctrl(fbi, false);
+
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_INIT)) {
+ data->vm_active.vmode.refresh = 60;
+ sprintf(vmode_opt, "%dx%d%s-%d@%d%s",
+ fbi->var.xres, fbi->var.yres,
+ data->vm_active.opts_cvt,
+ fbi->var.bits_per_pixel,
+ data->vm_active.vmode.refresh,
+ data->vm_active.opts_ext);
+ if (!strcmp(data->vm.name, vmode_opt)) {
+ data->vm_active = data->vm;
+ } else {
+ bpp = fbi->var.bits_per_pixel;
+ xylonfb_mode_option = vmode_opt;
+ ret = xylonfb_set_timings(fbi, bpp);
+ xylonfb_mode_option = NULL;
+ }
+ }
+ if (!ret) {
+ f = PICOS2KHZ(data->vm_active.vmode.pixclock);
+ if (data->flags & XYLONFB_FLAGS_PIXCLK_VALID)
+ if (xylonfb_hw_pixclk_set(&data->pdev->dev,
+ data->pixel_clock, f))
+ dev_err(dev,
+ "failed set pixel clock\n");
+
+ xylonfb_fbi_update(fbi);
+ XYLONFB_DBG(INFO, "video mode: %dx%d%s-%d@%d%s\n",
+ fbi->var.xres, fbi->var.yres,
+ data->vm_active.opts_cvt,
+ fbi->var.bits_per_pixel,
+ data->vm_active.vmode.refresh,
+ data->vm_active.opts_ext);
+ }
+
+ xylonfb_enable_logicvc_output(fbi);
+ xylonfb_logicvc_disp_ctrl(fbi, true);
+
+ if (data->flags & XYLONFB_FLAGS_VMODE_INIT)
+ data->flags |= XYLONFB_FLAGS_VMODE_SET;
+
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_SET)) {
+ if (!afbi) {
+ xylonfb_logicvc_layer_enable(fbi, true);
+ return ret;
+ }
+
+ for (i = 0; i < data->layers; i++) {
+ if (layer_on[i])
+ xylonfb_logicvc_layer_enable(afbi[i],
+ true);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void xylonfb_set_color_hw_rgb2yuv(u16 t, u16 r, u16 g, u16 b, u32 *yuv,
+ struct xylonfb_layer_data *ld)
+{
+ struct xylonfb_data *data = ld->data;
+ u32 y, u, v;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ y = ((data->coeff.cyr * (r & 0xFF)) + (data->coeff.cyg * (g & 0xFF)) +
+ (data->coeff.cyb * (b & 0xFF)) + data->coeff.cy) /
+ LOGICVC_YUV_NORM;
+ u = ((-data->coeff.cur * (r & 0xFF)) - (data->coeff.cug * (g & 0xFF)) +
+ (data->coeff.cub * (b & 0xFF)) + LOGICVC_COEFF_U) /
+ LOGICVC_YUV_NORM;
+ v = ((data->coeff.cvr * (r & 0xFF)) - (data->coeff.cvg * (g & 0xFF)) -
+ (data->coeff.cvb * (b & 0xFF)) + LOGICVC_COEFF_V) /
+ LOGICVC_YUV_NORM;
+
+ *yuv = ((t & 0xFF) << 24) | (y << 16) | (u << 8) | v;
+}
+
+static int xylonfb_set_color_hw(u16 *t, u16 *r, u16 *g, u16 *b,
+ int len, int id, struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 pixel, pixel_clut;
+ u16 a = 0xFF;
+ int bpp, to, ro, go, bo;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ bpp = fd->bpp;
+
+ to = fbi->var.transp.offset;
+ ro = fbi->var.red.offset;
+ go = fbi->var.green.offset;
+ bo = fbi->var.blue.offset;
+
+ switch (fbi->fix.visual) {
+ case FB_VISUAL_PSEUDOCOLOR:
+ if ((id > (LOGICVC_CLUT_SIZE - 1)) || (len > LOGICVC_CLUT_SIZE))
+ return -EINVAL;
+
+ switch (fd->format_clut) {
+ case XYLONFB_FORMAT_CLUT_ARGB6565:
+ while (len > 0) {
+ if (t)
+ a = t[id];
+ pixel_clut = ((((a & 0xFC) >> 2) << to) |
+ (((r[id] & 0xF8) >> 3) << ro) |
+ (((g[id] & 0xFC) >> 2) << go) |
+ (((b[id] & 0xF8) >> 3) << bo));
+ writel(pixel_clut, ld->clut_base +
+ (id*LOGICVC_CLUT_REGISTER_SIZE));
+ len--;
+ id++;
+ }
+ break;
+ case XYLONFB_FORMAT_CLUT_ARGB8888:
+ while (len > 0) {
+ if (t)
+ a = t[id];
+ pixel_clut = (((a & 0xFF) << to) |
+ ((r[id] & 0xFF) << ro) |
+ ((g[id] & 0xFF) << go) |
+ ((b[id] & 0xFF) << bo));
+ writel(pixel_clut, ld->clut_base +
+ (id*LOGICVC_CLUT_REGISTER_SIZE));
+ len--;
+ id++;
+ }
+ break;
+ case XYLONFB_FORMAT_CLUT_AYUV8888:
+ while (len > 0) {
+ if (t)
+ a = t[id];
+ xylonfb_set_color_hw_rgb2yuv(a, r[id],
+ g[id], b[id],
+ &pixel_clut,
+ ld);
+ writel(pixel_clut, ld->clut_base +
+ (id*LOGICVC_CLUT_REGISTER_SIZE));
+ len--;
+ id++;
+ }
+ break;
+ }
+ break;
+ case FB_VISUAL_TRUECOLOR:
+ switch (fd->format) {
+ case XYLONFB_FORMAT_RGB332:
+ while (len > 0) {
+ pixel = ((((r[id] & 0xE0) >> 5) << ro) |
+ (((g[id] & 0xE0) >> 5) << go) |
+ (((b[id] & 0xC0) >> 6) << bo));
+ ((u32 *)(fbi->pseudo_palette))[id] + (pixel << 24) | (pixel << 16) |
+ (pixel << 8) | pixel;
+ len--;
+ id++;
+ }
+ break;
+ case XYLONFB_FORMAT_RGB565:
+ while (len > 0) {
+ pixel = ((((r[id] & 0xF8) >> 3) << ro) |
+ (((g[id] & 0xFC) >> 2) << go) |
+ (((b[id] & 0xF8) >> 3) << bo));
+ ((u32 *)(fbi->pseudo_palette))[id] + (pixel << 16) | pixel;
+ len--;
+ id++;
+ }
+ break;
+ case XYLONFB_FORMAT_XRGB8888:
+ while (len > 0) {
+ ((u32 *)(fbi->pseudo_palette))[id] + (((r[id] & 0xFF) << ro) |
+ ((g[id] & 0xFF) << go) |
+ ((b[id] & 0xFF) << bo));
+ len--;
+ id++;
+ }
+ break;
+ case XYLONFB_FORMAT_ARGB8888:
+ while (len > 0) {
+ if (t)
+ a = t[id];
+ ((u32 *)(fbi->pseudo_palette))[id] + (((t[id] & 0xFF) << to) |
+ ((r[id] & 0xFF) << ro) |
+ ((g[id] & 0xFF) << go) |
+ ((b[id] & 0xFF) << bo));
+ len--;
+ id++;
+ }
+ break;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int xylonfb_set_color(unsigned regno,
+ unsigned red, unsigned green, unsigned blue,
+ unsigned transp, struct fb_info *fbi)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ return xylonfb_set_color_hw((u16 *)&transp,
+ (u16 *)&red, (u16 *)&green, (u16 *)&blue,
+ 1, regno, fbi);
+}
+
+static int xylonfb_set_cmap(struct fb_cmap *cmap, struct fb_info *fbi)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ return xylonfb_set_color_hw(cmap->transp,
+ cmap->red, cmap->green, cmap->blue,
+ cmap->len, cmap->start, fbi);
+}
+
+static void xylonfb_set_pixels(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld,
+ int bpp, unsigned int pix)
+{
+ u32 *vmem;
+ u8 *vmem8;
+ u16 *vmem16;
+ u32 *vmem32;
+ int x, y, pixoffset;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ vmem = ld->fb_base + (fbi->var.xoffset * (fbi->var.bits_per_pixel/4)) +
+ (fbi->var.yoffset * fbi->var.xres_virtual *
+ (fbi->var.bits_per_pixel/4));
+
+ switch (bpp) {
+ case 8:
+ vmem8 = (u8 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem8[pixoffset + x] = pix;
+ }
+ break;
+ case 16:
+ vmem16 = (u16 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem16[pixoffset + x] = pix;
+ }
+ break;
+ case 32:
+ vmem32 = (u32 *)vmem;
+ for (y = fbi->var.yoffset; y < fbi->var.yres; y++) {
+ pixoffset = (y * fbi->var.xres_virtual);
+ for (x = fbi->var.xoffset; x < fbi->var.xres; x++)
+ vmem32[pixoffset + x] = pix;
+ }
+ break;
+ }
+}
+
+static int xylonfb_blank(int blank_mode, struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ void __iomem *dev_base = data->dev_base;
+ u32 ctrl = data->reg_access.get_reg_val(dev_base,
+ LOGICVC_CTRL_ROFF,
+ ld);
+ u32 power = readl(dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ XYLONFB_DBG(INFO, "FB_BLANK_UNBLANK");
+ ctrl |= (LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+ LOGICVC_CTRL_DATA_ENABLE);
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ power |= LOGICVC_V_EN_MSK;
+ writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ mdelay(50);
+ break;
+
+ case FB_BLANK_NORMAL:
+ XYLONFB_DBG(INFO, "FB_BLANK_NORMAL");
+ switch (fd->format) {
+ case XYLONFB_FORMAT_C8:
+ xylonfb_set_color(0, 0, 0, 0, 0xFF, fbi);
+ xylonfb_set_pixels(fbi, ld, 8, 0);
+ break;
+ case XYLONFB_FORMAT_RGB332:
+ xylonfb_set_pixels(fbi, ld, 8, 0x00);
+ break;
+ case XYLONFB_FORMAT_RGB565:
+ xylonfb_set_pixels(fbi, ld, 16, 0x0000);
+ break;
+ case XYLONFB_FORMAT_XRGB8888:
+ case XYLONFB_FORMAT_ARGB8888:
+ xylonfb_set_pixels(fbi, ld, 32, 0xFF000000);
+ break;
+ }
+ break;
+
+ case FB_BLANK_POWERDOWN:
+ XYLONFB_DBG(INFO, "FB_BLANK_POWERDOWN");
+ ctrl &= ~(LOGICVC_CTRL_HSYNC | LOGICVC_CTRL_VSYNC |
+ LOGICVC_CTRL_DATA_ENABLE);
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ power &= ~LOGICVC_V_EN_MSK;
+ writel(power, dev_base + LOGICVC_POWER_CTRL_ROFF);
+
+ mdelay(50);
+ break;
+
+ case FB_BLANK_VSYNC_SUSPEND:
+ XYLONFB_DBG(INFO, "FB_BLANK_VSYNC_SUSPEND");
+ ctrl &= ~LOGICVC_CTRL_VSYNC;
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+ break;
+
+ case FB_BLANK_HSYNC_SUSPEND:
+ XYLONFB_DBG(INFO, "FB_BLANK_HSYNC_SUSPEND");
+ ctrl &= ~LOGICVC_CTRL_HSYNC;
+ data->reg_access.set_reg_val(ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+ break;
+ }
+
+ return 0;
+}
+
+static int xylonfb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+ return -EINVAL;
+
+ if ((fbi->var.xoffset = var->xoffset) &&
+ (fbi->var.yoffset = var->yoffset))
+ return 0;
+
+ if (fbi->var.vmode & FB_VMODE_YWRAP) {
+ return -EINVAL;
+ } else {
+ if (((var->xoffset + fbi->var.xres) > fbi->var.xres_virtual) ||
+ ((var->yoffset + fbi->var.yres) > fbi->var.yres_virtual))
+ return -EINVAL;
+ }
+
+ if (fd->format = XYLONFB_FORMAT_YUYV)
+ var->xoffset &= ~((unsigned long) + 1);
+
+ fbi->var.xoffset = var->xoffset;
+ fbi->var.yoffset = var->yoffset;
+
+ if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+ data->reg_access.set_reg_val(var->xoffset, ld->base,
+ LOGICVC_LAYER_HOFF_ROFF, ld);
+ data->reg_access.set_reg_val(var->yoffset, ld->base,
+ LOGICVC_LAYER_VOFF_ROFF, ld);
+ }
+ data->reg_access.set_reg_val((fbi->var.xres - 1), ld->base,
+ LOGICVC_LAYER_HPOS_ROFF, ld);
+ data->reg_access.set_reg_val((fbi->var.yres - 1), ld->base,
+ LOGICVC_LAYER_VPOS_ROFF, ld);
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+ ld->fb_pbase_active = ld->fb_pbase +
+ ((var->xoffset * (fd->bpp / 8)) +
+ (var->yoffset * fd->width *
+ (fd->bpp / 8)));
+ data->reg_access.set_reg_val(ld->fb_pbase_active, ld->base,
+ LOGICVC_LAYER_ADDR_ROFF, ld);
+ }
+
+ return 0;
+}
+
+static struct fb_ops xylonfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = xylonfb_open,
+ .fb_release = xylonfb_release,
+ .fb_check_var = xylonfb_check_var,
+ .fb_set_par = xylonfb_set_par,
+ .fb_setcolreg = xylonfb_set_color,
+ .fb_setcmap = xylonfb_set_cmap,
+ .fb_blank = xylonfb_blank,
+ .fb_pan_display = xylonfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_ioctl = xylonfb_ioctl,
+};
+
+static int xylonfb_find_next_layer(struct xylonfb_data *data, int layers,
+ int id)
+{
+ dma_addr_t address = data->fd[id]->address;
+ dma_addr_t temp_address = ((unsigned long) - 1);
+ dma_addr_t loop_address;
+ int next = -1;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ for (i = 0; i < layers; i++) {
+ loop_address = data->fd[i]->address;
+ if ((address < loop_address) && (loop_address < temp_address)) {
+ next = i;
+ temp_address = loop_address;
+ }
+ }
+
+ return next;
+}
+
+static void xylonfb_get_vmem_height(struct xylonfb_data *data, int layers,
+ int id)
+{
+ struct xylonfb_layer_fix_data *fd = data->fd[id];
+ dma_addr_t vmem_start = fd->address;
+ dma_addr_t vmem_end;
+ int next;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (fd->address_range && (id < (layers - 1))) {
+ fd->height = fd->address_range / (fd->width * (fd->bpp / 8));
+ return;
+ }
+
+ vmem_start = fd->address;
+
+ next = xylonfb_find_next_layer(data, layers, id);
+ if (next = -1) {
+ if (fd->address_range) {
+ fd->height = fd->address_range /
+ (fd->width * (fd->bpp / 8));
+ } else {
+ if (fd->buffer_offset)
+ fd->height = fd->buffer_offset *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ else
+ fd->height = XYLONFB_VRES_DEFAULT;
+ }
+ } else {
+ vmem_end = data->fd[next]->address;
+ fd->height = (vmem_end - vmem_start) /
+ (fd->width * (fd->bpp / 8));
+ }
+
+ if (fd->height > (LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS))
+ fd->height = LOGICVC_MAX_VRES * LOGICVC_MAX_LAYER_BUFFERS;
+}
+
+static void xylonfb_set_fbi_var_screeninfo(struct fb_var_screeninfo *var,
+ struct xylonfb_data *data)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ var->xres = data->vm_active.vmode.xres;
+ var->yres = data->vm_active.vmode.yres;
+ var->pixclock = data->vm_active.vmode.pixclock;
+ var->left_margin = data->vm_active.vmode.left_margin;
+ var->right_margin = data->vm_active.vmode.right_margin;
+ var->upper_margin = data->vm_active.vmode.upper_margin;
+ var->lower_margin = data->vm_active.vmode.lower_margin;
+ var->hsync_len = data->vm_active.vmode.hsync_len;
+ var->vsync_len = data->vm_active.vmode.vsync_len;
+ var->sync = data->vm_active.vmode.sync;
+ var->vmode = data->vm_active.vmode.vmode;
+}
+
+static void xylonfb_fbi_update(struct fb_info *fbi)
+{
+ struct fb_info **afbi = dev_get_drvdata(fbi->device);
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i, layers, id;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (!afbi)
+ return;
+
+ layers = data->layers;
+ id = ld->fd->id;
+
+ for (i = 0; i < layers; i++) {
+ if (i = id)
+ continue;
+
+ xylonfb_set_fbi_var_screeninfo(&afbi[i]->var, data);
+ afbi[i]->monspecs = afbi[id]->monspecs;
+ }
+}
+
+static void xylonfb_set_hw_specifics(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld,
+ struct xylonfb_layer_fix_data *fd)
+{
+ struct xylonfb_data *data = ld->data;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ fbi->fix.smem_start = ld->fb_pbase;
+ fbi->fix.smem_len = ld->fb_size;
+ if (fd->type = LOGICVC_LAYER_RGB) {
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ } else if (fd->type = LOGICVC_LAYER_YUV) {
+ if (fd->format = XYLONFB_FORMAT_C8)
+ fbi->fix.type = FB_TYPE_PACKED_PIXELS;
+ else
+ fbi->fix.type = FB_TYPE_FOURCC;
+ }
+ if ((fd->type = LOGICVC_LAYER_YUV) ||
+ (fd->type = LOGICVC_LAYER_ALPHA)) {
+ if (fd->format = XYLONFB_FORMAT_C8)
+ fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ fbi->fix.visual = FB_VISUAL_FOURCC;
+ } else if (fd->format = XYLONFB_FORMAT_C8) {
+ fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ } else {
+ /*
+ * Other logiCVC layer pixel formats:
+ * - 8 bpp: LAYER or PIXEL alpha
+ * It is not true color, RGB triplet is stored in 8 bits.
+ * - 16 bpp:
+ * LAYER alpha: RGB triplet is stored in 16 bits
+ * - 32 bpp: LAYER or PIXEL alpha
+ * True color, RGB triplet or ARGB quadriplet
+ * is stored in 32 bits.
+ */
+ fbi->fix.visual = FB_VISUAL_TRUECOLOR;
+ }
+
+ fbi->fix.xpanstep = 1;
+ fbi->fix.ypanstep = 1;
+ fbi->fix.ywrapstep = 0;
+ fbi->fix.line_length = fd->width * (fd->bpp / 8);
+ fbi->fix.mmio_start = ld->pbase;
+ fbi->fix.mmio_len = LOGICVC_LAYER_REGISTERS_RANGE;
+ fbi->fix.accel = FB_ACCEL_NONE;
+
+ fbi->var.xres_virtual = fd->width;
+ fbi->var.yres_virtual = fd->height;
+
+ fbi->var.bits_per_pixel = fd->bpp;
+
+ switch (fd->type) {
+ case LOGICVC_LAYER_ALPHA:
+ fbi->var.grayscale = LOGICVC_PIX_FMT_ALPHA;
+ break;
+ case LOGICVC_LAYER_RGB:
+ fbi->var.grayscale = 0;
+ break;
+ case LOGICVC_LAYER_YUV:
+ if (fd->format = XYLONFB_FORMAT_C8) {
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+ } else if (fd->format = XYLONFB_FORMAT_YUYV) {
+ if (fd->component_swap)
+ fbi->var.grayscale = V4L2_PIX_FMT_VYUY;
+ else
+ fbi->var.grayscale = V4L2_PIX_FMT_YVYU;
+ } else if (fd->format = XYLONFB_FORMAT_AYUV) {
+ if (fd->component_swap)
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AVUY;
+ else
+ fbi->var.grayscale = LOGICVC_PIX_FMT_AYUV;
+ }
+ break;
+ }
+
+ /*
+ * Set values according to logiCVC layer data width configuration:
+ * layer data width can be 1, 2, 4 bytes
+ */
+ if (fd->transparency = LOGICVC_ALPHA_LAYER) {
+ fbi->var.transp.offset = 0;
+ fbi->var.transp.length = 0;
+ }
+
+ switch (fd->format) {
+ case XYLONFB_FORMAT_A8:
+ fbi->var.transp.offset = 0;
+ fbi->var.transp.length = 8;
+ break;
+ case XYLONFB_FORMAT_C8:
+ switch (fd->format_clut) {
+ case XYLONFB_FORMAT_CLUT_ARGB6565:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 6;
+ fbi->var.red.offset = 19;
+ fbi->var.red.length = 5;
+ fbi->var.green.offset = 10;
+ fbi->var.green.length = 6;
+ fbi->var.blue.offset = 3;
+ fbi->var.blue.length = 5;
+ break;
+ case XYLONFB_FORMAT_CLUT_ARGB8888:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ case XYLONFB_FORMAT_CLUT_AYUV8888:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ }
+ break;
+ case XYLONFB_FORMAT_RGB332:
+ fbi->var.red.offset = 5;
+ fbi->var.red.length = 3;
+ fbi->var.green.offset = 2;
+ fbi->var.green.length = 3;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 2;
+ break;
+ case XYLONFB_FORMAT_RGB565:
+ fbi->var.red.offset = 11;
+ fbi->var.red.length = 5;
+ fbi->var.green.offset = 5;
+ fbi->var.green.length = 6;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 5;
+ break;
+ case XYLONFB_FORMAT_ARGB8888:
+ case XYLONFB_FORMAT_AYUV:
+ fbi->var.transp.offset = 24;
+ fbi->var.transp.length = 8;
+ case XYLONFB_FORMAT_XRGB8888:
+ fbi->var.red.offset = 16;
+ fbi->var.red.length = 8;
+ fbi->var.green.offset = 8;
+ fbi->var.green.length = 8;
+ fbi->var.blue.offset = 0;
+ fbi->var.blue.length = 8;
+ break;
+ }
+ fbi->var.transp.msb_right = 0;
+ fbi->var.red.msb_right = 0;
+ fbi->var.green.msb_right = 0;
+ fbi->var.blue.msb_right = 0;
+ fbi->var.activate = FB_ACTIVATE_NOW;
+ fbi->var.height = 0;
+ fbi->var.width = 0;
+ fbi->var.sync = 0;
+ if (!(data->vm_active.ctrl & LOGICVC_CTRL_HSYNC_INVERT))
+ fbi->var.sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (!(data->vm_active.ctrl & LOGICVC_CTRL_VSYNC_INVERT))
+ fbi->var.sync |= FB_SYNC_VERT_HIGH_ACT;
+ fbi->var.rotate = 0;
+}
+
+static int xylonfb_set_timings(struct fb_info *fbi, int bpp)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct fb_var_screeninfo fb_var;
+ struct fb_videomode *vm;
+ int rc;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if ((data->flags & XYLONFB_FLAGS_VMODE_INIT) &&
+ (data->flags & XYLONFB_FLAGS_VMODE_CUSTOM) &&
+ memchr(data->vm.name, 'x', 10)) {
+ data->vm_active = data->vm;
+ vm = &data->vm.vmode;
+ data->vm_active.vmode.refresh + DIV_ROUND_CLOSEST((PICOS2KHZ(vm->pixclock) * 1000),
+ ((vm->xres + vm->left_margin +
+ vm->right_margin + vm->hsync_len) *
+ (vm->yres + vm->upper_margin +
+ vm->lower_margin + vm->vsync_len)));
+ return 0;
+ }
+
+ rc = fb_find_mode(&fb_var, fbi, xylonfb_mode_option, NULL, 0,
+ &xylonfb_vm.vmode, bpp);
+
+ switch (rc) {
+ case 0:
+ dev_err(fbi->dev, "failed find video mode\n"
+ "using driver default mode %dx%dM-%d@%d\n",
+ xylonfb_vm.vmode.xres,
+ xylonfb_vm.vmode.yres,
+ bpp,
+ xylonfb_vm.vmode.refresh);
+ break;
+ case 1:
+ dev_dbg(fbi->dev, "video mode %s", xylonfb_mode_option);
+ break;
+ case 2:
+ dev_warn(fbi->dev, "video mode %s with ignored refresh rate\n",
+ xylonfb_mode_option);
+ break;
+ case 3:
+ dev_warn(fbi->dev, "default video mode %dx%dM-%d@%d\n",
+ xylonfb_vm.vmode.xres,
+ xylonfb_vm.vmode.yres,
+ bpp,
+ xylonfb_vm.vmode.refresh);
+ break;
+ case 4:
+ dev_warn(fbi->dev, "video mode fallback\n");
+ break;
+ default:
+ break;
+ }
+
+ data->vm_active.ctrl = data->vm.ctrl;
+ data->vm_active.vmode.xres = fb_var.xres;
+ data->vm_active.vmode.yres = fb_var.yres;
+ data->vm_active.vmode.pixclock = fb_var.pixclock;
+ data->vm_active.vmode.left_margin = fb_var.left_margin;
+ data->vm_active.vmode.right_margin = fb_var.right_margin;
+ data->vm_active.vmode.upper_margin = fb_var.upper_margin;
+ data->vm_active.vmode.lower_margin = fb_var.lower_margin;
+ data->vm_active.vmode.hsync_len = fb_var.hsync_len;
+ data->vm_active.vmode.vsync_len = fb_var.vsync_len;
+ data->vm_active.vmode.sync = fb_var.sync;
+ data->vm_active.vmode.vmode = fb_var.vmode;
+ data->vm_active.vmode.refresh + DIV_ROUND_CLOSEST((PICOS2KHZ(fb_var.pixclock) * 1000),
+ ((fb_var.xres + fb_var.left_margin +
+ fb_var.right_margin + fb_var.hsync_len) *
+ (fb_var.yres + fb_var.upper_margin +
+ fb_var.lower_margin + fb_var.vsync_len)));
+ strcpy(data->vm_active.opts_cvt, data->vm.opts_cvt);
+ strcpy(data->vm_active.opts_ext, data->vm.opts_ext);
+ sprintf(data->vm_active.name, "%dx%d%s-%d@%d%s",
+ fb_var.xres, fb_var.yres,
+ data->vm_active.opts_cvt,
+ fb_var.bits_per_pixel,
+ data->vm_active.vmode.refresh,
+ data->vm_active.opts_ext);
+
+ if (!memchr(data->vm.name, 'x', 10))
+ data->vm = data->vm_active;
+
+ return 0;
+}
+
+static int xylonfb_register_fb(struct fb_info *fbi,
+ struct xylonfb_layer_data *ld, int id,
+ int *regfb)
+{
+ struct device *dev = fbi->dev;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ int transp;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ fbi->flags = FBINFO_DEFAULT;
+ fbi->screen_base = (char __iomem *)ld->fb_base;
+ fbi->screen_size = ld->fb_size;
+ fbi->pseudo_palette = kzalloc(sizeof(u32) * XYLONFB_PSEUDO_PALETTE_SIZE,
+ GFP_KERNEL);
+ fbi->fbops = &xylonfb_ops;
+
+ sprintf(fbi->fix.id, "Xylon FB%d", id);
+ xylonfb_set_hw_specifics(fbi, ld, fd);
+
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_DEFAULT)) {
+ if (!xylonfb_set_timings(fbi, fbi->var.bits_per_pixel))
+ data->flags |= XYLONFB_FLAGS_VMODE_DEFAULT;
+ else
+ dev_err(dev, "videomode not set\n");
+ }
+ xylonfb_set_fbi_var_screeninfo(&fbi->var, data);
+ fbi->mode = &data->vm_active.vmode;
+ fbi->mode->name = data->vm_active.name;
+
+ if (fd->transparency = LOGICVC_ALPHA_LAYER)
+ transp = 0;
+ else
+ transp = 1;
+ if (fb_alloc_cmap(&fbi->cmap, XYLONFB_PSEUDO_PALETTE_SIZE, transp))
+ return -ENOMEM;
+
+ /*
+ * After fb driver registration, values in struct fb_info
+ * must not be changed anywhere else in driver except in
+ * xylonfb_set_par() function
+ */
+ *regfb = register_framebuffer(fbi);
+ if (*regfb) {
+ dev_err(dev, "failed register fb %d\n", id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void xylonfb_layer_initialize(struct xylonfb_layer_data *ld)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 reg;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (fd->component_swap) {
+ reg = ld->data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ reg |= LOGICVC_LAYER_CTRL_PIXEL_FORMAT_BIT_ABGR;
+ ld->data->reg_access.set_reg_val(reg, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ }
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)
+ data->reg_access.set_reg_val(ld->fb_pbase, ld->base,
+ LOGICVC_LAYER_ADDR_ROFF,
+ ld);
+}
+
+static int xylonfb_vmem_init(struct xylonfb_layer_data *ld, int id, bool *mmap)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ struct device *dev = &data->pdev->dev;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (fd->address) {
+ ld->fb_pbase = fd->address;
+
+ xylonfb_get_vmem_height(data, data->layers, id);
+ ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+ if (*mmap) {
+ ld->fb_base = (__force void *)ioremap_wc(ld->fb_pbase,
+ ld->fb_size);
+ if (!ld->fb_base) {
+ dev_err(dev, "failed map video memory\n");
+ return -EINVAL;
+ }
+ }
+ } else {
+ if (fd->buffer_offset)
+ fd->height = fd->buffer_offset *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ else
+ fd->height = XYLONFB_VRES_DEFAULT *
+ LOGICVC_MAX_LAYER_BUFFERS;
+ ld->fb_size = fd->width * (fd->bpp / 8) * fd->height;
+
+ ld->fb_base = dma_alloc_coherent(&data->pdev->dev,
+ PAGE_ALIGN(ld->fb_size),
+ &ld->fb_pbase, GFP_KERNEL);
+ if (!ld->fb_base) {
+ dev_err(dev, "failed allocate video buffer ID%d\n", id);
+ return -ENOMEM;
+ }
+
+ data->flags |= XYLONFB_FLAGS_DMA_BUFFER;
+ }
+
+ ld->fb_pbase_active = ld->fb_pbase;
+
+ *mmap = false;
+
+ return 0;
+}
+
+static void xylonfb_logicvc_disp_ctrl(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ void __iomem *dev_base = data->dev_base;
+ u32 val;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (enable) {
+ val = LOGICVC_EN_VDD_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ mdelay(data->pwr_delay);
+ val |= LOGICVC_V_EN_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ mdelay(data->sig_delay);
+ val |= LOGICVC_EN_BLIGHT_MSK;
+ writel(val, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ } else {
+ writel(0, dev_base + LOGICVC_POWER_CTRL_ROFF);
+ }
+}
+
+static void xylonfb_logicvc_layer_enable(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ u32 reg;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ reg = ld->data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+
+ if (enable) {
+ reg |= LOGICVC_LAYER_CTRL_ENABLE;
+ ld->flags |= XYLONFB_FLAGS_LAYER_ENABLED;
+ } else {
+ reg &= ~LOGICVC_LAYER_CTRL_ENABLE;
+ ld->flags &= ~XYLONFB_FLAGS_LAYER_ENABLED;
+ }
+
+ ld->data->reg_access.set_reg_val(reg, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+}
+
+static void xylonfb_enable_logicvc_output(struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ void __iomem *dev_base = data->dev_base;
+ struct fb_videomode *vm = &data->vm_active.vmode;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ writel(vm->right_margin - 1, dev_base + LOGICVC_HSYNC_FRONT_PORCH_ROFF);
+ writel(vm->hsync_len - 1, dev_base + LOGICVC_HSYNC_ROFF);
+ writel(vm->left_margin - 1, dev_base + LOGICVC_HSYNC_BACK_PORCH_ROFF);
+ writel(vm->xres - 1, dev_base + LOGICVC_HRES_ROFF);
+ writel(vm->lower_margin - 1, dev_base + LOGICVC_VSYNC_FRONT_PORCH_ROFF);
+ writel(vm->vsync_len - 1, dev_base + LOGICVC_VSYNC_ROFF);
+ writel(vm->upper_margin - 1, dev_base + LOGICVC_VSYNC_BACK_PORCH_ROFF);
+ writel(vm->yres - 1, dev_base + LOGICVC_VRES_ROFF);
+ data->reg_access.set_reg_val(data->vm_active.ctrl, dev_base,
+ LOGICVC_CTRL_ROFF, ld);
+
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV)
+ data->reg_access.set_reg_val(LOGICVC_COLOR_YUV888_BLACK,
+ dev_base,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ld);
+ else
+ data->reg_access.set_reg_val(LOGICVC_COLOR_RGB_BLACK,
+ dev_base,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ld);
+
+ writel(LOGICVC_DTYPE_REG_INIT, dev_base + LOGICVC_DTYPE_ROFF);
+
+ XYLONFB_DBG(INFO, "logiCVC HW parameters:\n" \
+ " Horizontal Front Porch: %d pixclks\n" \
+ " Horizontal Sync: %d pixclks\n" \
+ " Horizontal Back Porch: %d pixclks\n" \
+ " Vertical Front Porch: %d pixclks\n" \
+ " Vertical Sync: %d pixclks\n" \
+ " Vertical Back Porch: %d pixclks\n" \
+ " Pixel Clock: %d ps\n" \
+ " Horizontal Resolution: %d pixels\n" \
+ " Vertical Resolution: %d lines\n", \
+ vm->right_margin, vm->hsync_len, vm->left_margin,
+ vm->lower_margin, vm->vsync_len, vm->upper_margin,
+ vm->pixclock, vm->xres, vm->yres);
+}
+
+static void xylonfb_disable_logicvc_output(struct fb_info *fbi)
+{
+ struct fb_info **afbi = dev_get_drvdata(fbi->device);
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (afbi)
+ for (i = 0; i < data->layers; i++)
+ xylonfb_logicvc_layer_enable(afbi[i], false);
+}
+
+static void xylonfb_start(struct fb_info **afbi, int layers)
+{
+ struct fb_info *fbi = afbi[0];
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ for (i = 0; i < layers; i++) {
+ ld = afbi[i]->par;
+ if (ld->flags & XYLONFB_FLAGS_LAYER_ENABLED)
+ continue;
+
+ xylonfb_logicvc_layer_enable(afbi[i], false);
+ }
+
+ if (data->flags & XYLONFB_FLAGS_VSYNC_IRQ) {
+ writel(LOGICVC_INT_V_SYNC,
+ data->dev_base + LOGICVC_INT_STAT_ROFF);
+ data->reg_access.set_reg_val(~LOGICVC_INT_V_SYNC,
+ data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+ }
+
+ for (i = 0; i < layers; i++) {
+ ld = afbi[i]->par;
+ XYLONFB_DBG(INFO, "logiCVC layer %d\n" \
+ " Registers Base Address: 0x%lX\n" \
+ " Layer Video Memory Address: 0x%lX\n" \
+ " X resolution: %d\n" \
+ " Y resolution: %d\n" \
+ " X resolution (virtual): %d\n" \
+ " Y resolution (virtual): %d\n" \
+ " Line length (bytes): %d\n" \
+ " Bits per Pixel: %d\n" \
+ "\n", \
+ i,
+ (unsigned long)ld->pbase,
+ (unsigned long)ld->fb_pbase,
+ afbi[i]->var.xres,
+ afbi[i]->var.yres,
+ afbi[i]->var.xres_virtual,
+ afbi[i]->var.yres_virtual,
+ afbi[i]->fix.line_length,
+ afbi[i]->var.bits_per_pixel);
+ }
+}
+
+static void xylonfb_get_vmode_opts(struct xylonfb_data *data)
+{
+ char *s, *opt, *ext, *c;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ s = data->vm.name;
+ opt = data->vm.opts_cvt;
+ ext = data->vm.opts_ext;
+
+ data->vm.vmode.vmode = 0;
+
+ c = strchr(s, 'M');
+ if (c)
+ *opt++ = *c;
+ c = strchr(s, 'R');
+ if (c)
+ *opt = *c;
+ c = strchr(s, 'i');
+ if (c) {
+ *ext++ = *c;
+ data->vm.vmode.vmode |= FB_VMODE_INTERLACED;
+ }
+ c = strchr(s, 'm');
+ if (c)
+ *ext = *c;
+}
+
+static bool xylonfb_allow_console(struct xylonfb_layer_fix_data *fd)
+{
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ switch (fd->format) {
+ case XYLONFB_FORMAT_C8:
+ case XYLONFB_FORMAT_RGB332:
+ case XYLONFB_FORMAT_RGB565:
+ case XYLONFB_FORMAT_XRGB8888:
+ case XYLONFB_FORMAT_ARGB8888:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int xylonfb_init_core(struct xylonfb_data *data)
+{
+ struct device *dev = &data->pdev->dev;
+ struct fb_info **afbi, *fbi;
+ struct xylonfb_layer_data *ld;
+ void __iomem *dev_base;
+ u32 ip_ver;
+ int i, ret, layers, console_layer;
+ int regfb[LOGICVC_MAX_LAYERS];
+ size_t size;
+ unsigned short layer_base_off[] = {
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_0_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_1_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_2_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_3_OFFSET),
+ (LOGICVC_LAYER_BASE_OFFSET + LOGICVC_LAYER_4_OFFSET)
+ };
+ unsigned short clut_base_off[] = {
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L0_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L1_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L2_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L3_CLUT_0_OFFSET),
+ (LOGICVC_CLUT_BASE_OFFSET + LOGICVC_CLUT_L4_CLUT_0_OFFSET),
+ };
+ bool memmap;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ dev_base = devm_ioremap_resource(dev, &data->resource_mem);
+ if (IS_ERR(dev_base)) {
+ dev_err(dev, "failed ioremap mem resource\n");
+ return PTR_ERR(dev_base);
+ }
+ data->dev_base = dev_base;
+
+ data->irq = data->resource_irq.start;
+ ret = devm_request_irq(dev, data->irq, xylonfb_isr, IRQF_TRIGGER_HIGH,
+ XYLONFB_DEVICE_NAME, dev);
+ if (ret)
+ return ret;
+
+ ip_ver = readl(dev_base + LOGICVC_IP_VERSION_ROFF);
+ data->major = (ip_ver >> LOGICVC_MAJOR_REVISION_SHIFT) &
+ LOGICVC_MAJOR_REVISION_MASK;
+ data->minor = (ip_ver >> LOGICVC_MINOR_REVISION_SHIFT) &
+ LOGICVC_MINOR_REVISION_MASK;
+ data->patch = ip_ver & LOGICVC_PATCH_LEVEL_MASK;
+ dev_info(dev, "logiCVC IP core %d.%02d.%c\n",
+ data->major, data->minor, ('a' + data->patch));
+
+ if (data->major >= 4)
+ data->flags |= XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS;
+
+ layers = data->layers;
+ if (layers = 0) {
+ dev_err(dev, "no available layers\n");
+ return -ENODEV;
+ }
+ console_layer = data->console_layer;
+ if (console_layer >= layers) {
+ dev_err(dev, "invalid console layer ID\n");
+ console_layer = 0;
+ }
+
+ if (data->flags & XYLONFB_FLAGS_CHECK_CONSOLE_LAYER) {
+ if (!xylonfb_allow_console(data->fd[console_layer])) {
+ dev_err(dev, "invalid console layer format\n");
+ return -EINVAL;
+ }
+ data->flags &= ~XYLONFB_FLAGS_CHECK_CONSOLE_LAYER;
+ }
+
+ size = sizeof(struct fb_info *);
+ afbi = devm_kzalloc(dev, (size * layers), GFP_KERNEL);
+ if (!afbi) {
+ dev_err(dev, "failed allocate internal data\n");
+ return -ENOMEM;
+ }
+
+ if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+ data->reg_access.get_reg_val = xylonfb_get_reg;
+ data->reg_access.set_reg_val = xylonfb_set_reg;
+ } else {
+ size = sizeof(struct xylonfb_registers);
+ data->reg_access.get_reg_val = xylonfb_get_reg_mem;
+ data->reg_access.set_reg_val = xylonfb_set_reg_mem;
+ }
+
+ data->coeff.cyr = LOGICVC_COEFF_Y_R;
+ data->coeff.cyg = LOGICVC_COEFF_Y_G;
+ data->coeff.cyb = LOGICVC_COEFF_Y_B;
+ if (data->flags & XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656) {
+ data->coeff.cy = LOGICVC_COEFF_ITU656_Y;
+ data->coeff.cur = LOGICVC_COEFF_ITU656_U_R;
+ data->coeff.cug = LOGICVC_COEFF_ITU656_U_G;
+ data->coeff.cub = LOGICVC_COEFF_ITU656_U_B;
+ data->coeff.cvr = LOGICVC_COEFF_ITU656_V_R;
+ data->coeff.cvg = LOGICVC_COEFF_ITU656_V_G;
+ data->coeff.cvb = LOGICVC_COEFF_ITU656_V_B;
+ } else {
+ data->coeff.cy = LOGICVC_COEFF_Y;
+ data->coeff.cur = LOGICVC_COEFF_U_R;
+ data->coeff.cug = LOGICVC_COEFF_U_G;
+ data->coeff.cub = LOGICVC_COEFF_U_B;
+ data->coeff.cvr = LOGICVC_COEFF_V_R;
+ data->coeff.cvg = LOGICVC_COEFF_V_G;
+ data->coeff.cvb = LOGICVC_COEFF_V_B;
+ }
+
+ atomic_set(&data->refcount, 0);
+
+ data->flags |= XYLONFB_FLAGS_VMODE_INIT;
+
+ sprintf(data->vm.name, "%s-%d@%d",
+ data->vm.name, data->fd[console_layer]->bpp,
+ data->vm.vmode.refresh);
+ if (!(data->flags & XYLONFB_FLAGS_VMODE_CUSTOM))
+ xylonfb_mode_option = data->vm.name;
+ xylonfb_get_vmode_opts(data);
+
+ if (data->pixel_clock) {
+ if (xylonfb_hw_pixclk_supported(dev, data->pixel_clock)) {
+ data->flags |= XYLONFB_FLAGS_PIXCLK_VALID;
+ } else {
+ dev_warn(dev, "pixel clock not supported\n");
+ ret = -EPROBE_DEFER;
+ goto err_probe;
+
+ }
+ } else {
+ dev_warn(dev, "external pixel clock\n");
+ }
+
+ ld = NULL;
+
+ for (i = 0; i < layers; i++)
+ regfb[i] = -1;
+ memmap = true;
+
+ /*
+ * /dev/fb0 will be default console layer,
+ * no matter how logiCVC layers are sorted in memory
+ */
+ for (i = console_layer; i < layers; i++) {
+ if (regfb[i] != -1)
+ continue;
+
+ size = sizeof(struct xylonfb_layer_data);
+ fbi = framebuffer_alloc(size, dev);
+ if (!fbi) {
+ dev_err(dev, "failed allocate fb info\n");
+ ret = -ENOMEM;
+ goto err_probe;
+ }
+ fbi->dev = dev;
+ afbi[i] = fbi;
+
+ ld = fbi->par;
+ ld->data = data;
+ ld->fd = data->fd[i];
+
+ atomic_set(&ld->refcount, 0);
+
+ ld->pbase = data->resource_mem.start + layer_base_off[i];
+ ld->base = dev_base + layer_base_off[i];
+ ld->clut_base = dev_base + clut_base_off[i];
+
+ ret = xylonfb_vmem_init(ld, i, &memmap);
+ if (ret)
+ goto err_probe;
+
+ xylonfb_layer_initialize(ld);
+
+ ret = xylonfb_register_fb(fbi, ld, i, ®fb[i]);
+ if (ret)
+ goto err_probe;
+
+ if (console_layer >= 0)
+ fbi->monspecs = afbi[console_layer]->monspecs;
+
+ mutex_init(&ld->mutex);
+
+ XYLONFB_DBG(INFO, "Layer parameters\n" \
+ " ID %d\n" \
+ " Width %d pixels\n" \
+ " Height %d lines\n" \
+ " Bits per pixel %d\n" \
+ " Buffer size %d bytes\n", \
+ ld->fd->id,
+ ld->fd->width,
+ ld->fd->height,
+ ld->fd->bpp,
+ ld->fb_size);
+
+ if (console_layer > 0) {
+ i = -1;
+ console_layer = -1;
+ }
+ }
+
+ if (ld) {
+ if (!(data->flags & XYLONFB_FLAGS_READABLE_REGS))
+ data->reg_access.set_reg_val(0xFFFF, dev_base,
+ LOGICVC_INT_MASK_ROFF,
+ ld);
+ } else {
+ dev_warn(dev, "initialization not completed\n");
+ }
+
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER)
+ dev_info(dev, "BG layer: %s@%dbpp",
+ data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_RGB ? \
+ "RGB" : "YUV", data->bg_layer_bpp);
+
+ mutex_init(&data->irq_mutex);
+ init_waitqueue_head(&data->vsync.wait);
+ atomic_set(&data->refcount, 0);
+
+ dev_set_drvdata(dev, (void *)afbi);
+
+ data->flags &= ~(XYLONFB_FLAGS_VMODE_INIT |
+ XYLONFB_FLAGS_VMODE_DEFAULT | XYLONFB_FLAGS_VMODE_SET);
+ xylonfb_mode_option = NULL;
+
+ xylonfb_start(afbi, layers);
+
+ return 0;
+
+err_probe:
+ for (i = layers - 1; i >= 0; i--) {
+ fbi = afbi[i];
+ if (!fbi)
+ continue;
+ ld = fbi->par;
+ if (regfb[i] = 0)
+ unregister_framebuffer(fbi);
+ else
+ regfb[i] = 0;
+ if (fbi->cmap.red)
+ fb_dealloc_cmap(&fbi->cmap);
+ if (ld) {
+ if (data->flags & XYLONFB_FLAGS_DMA_BUFFER) {
+ dma_free_coherent(dev,
+ PAGE_ALIGN(ld->fb_size),
+ ld->fb_base, ld->fb_pbase);
+ } else {
+ if (ld->fb_base)
+ iounmap((void __iomem *)ld->fb_base);
+ }
+ kfree(fbi->pseudo_palette);
+ framebuffer_release(fbi);
+ }
+ }
+
+ return ret;
+}
+
+int xylonfb_deinit_core(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct fb_info **afbi = dev_get_drvdata(dev);
+ struct fb_info *fbi = afbi[0];
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int i;
+
+ XYLONFB_DBG(INFO, "%s", __func__);
+
+ if (atomic_read(&data->refcount) != 0) {
+ dev_err(dev, "driver in use\n");
+ return -EINVAL;
+ }
+
+ xylonfb_disable_logicvc_output(fbi);
+
+ xylonfb_hw_pixclk_unload(data->pixel_clock);
+
+ for (i = data->layers - 1; i >= 0; i--) {
+ fbi = afbi[i];
+ ld = fbi->par;
+
+ unregister_framebuffer(fbi);
+ fb_dealloc_cmap(&fbi->cmap);
+ if (data->flags & XYLONFB_FLAGS_DMA_BUFFER) {
+ dma_free_coherent(dev,
+ PAGE_ALIGN(ld->fb_size),
+ ld->fb_base, ld->fb_pbase);
+ }
+ kfree(fbi->pseudo_palette);
+ framebuffer_release(fbi);
+ }
+
+ return 0;
+}
diff --git a/drivers/video/fbdev/xylon/xylonfb_core.h b/drivers/video/fbdev/xylon/xylonfb_core.h
new file mode 100644
index 0000000..2508928
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_core.h
@@ -0,0 +1,253 @@
+/*
+ * Xylon logiCVC frame buffer driver internal data structures
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __XYLONFB_CORE_H__
+#define __XYLONFB_CORE_H__
+
+#include <linux/fb.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+#define XYLONFB_DRIVER_NAME "xylonfb"
+#define XYLONFB_DEVICE_NAME "logicvc"
+#define XYLONFB_DRIVER_DESCRIPTION "Xylon logiCVC frame buffer driver"
+#define XYLONFB_DRIVER_VERSION "3.0"
+
+#define DEBUG_LEVEL 1
+#define CORE 1
+#define INFO 2
+
+#ifdef DEBUG
+#define XYLONFB_DBG(level, format, ...) \
+ do { \
+ if (level > DEBUG_LEVEL) \
+ pr_info(format "\n", ## __VA_ARGS__); \
+ } while (0)
+#else
+#define XYLONFB_DBG(format, ...) do { } while (0)
+#endif
+
+#define LOGICVC_MAX_LAYERS 5
+
+/* Xylon FB driver flags */
+#define XYLONFB_FLAGS_READABLE_REGS (1 << 0)
+#define XYLONFB_FLAGS_SIZE_POSITION (1 << 1)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER (1 << 2)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_RGB (1 << 3)
+#define XYLONFB_FLAGS_BACKGROUND_LAYER_YUV (1 << 4)
+#define XYLONFB_FLAGS_DISPLAY_INTERFACE_ITU656 (1 << 5)
+#define XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS (1 << 6)
+#define XYLONFB_FLAGS_CHECK_CONSOLE_LAYER (1 << 7)
+#define XYLONFB_FLAGS_PIXCLK_VALID (1 << 8)
+#define XYLONFB_FLAGS_DMA_BUFFER (1 << 9)
+#define XYLONFB_FLAGS_VSYNC_IRQ (1 << 10)
+#define XYLONFB_FLAGS_VMODE_CUSTOM (1 << 11)
+#define XYLONFB_FLAGS_VMODE_INIT (1 << 12)
+#define XYLONFB_FLAGS_VMODE_DEFAULT (1 << 13)
+#define XYLONFB_FLAGS_VMODE_SET (1 << 14)
+#define XYLONFB_FLAGS_LAYER_ENABLED (1 << 15)
+#define XYLONFB_FLAGS_ACTIVATE_NEXT_OPEN (1 << 16)
+
+/* Xylon FB driver color formats */
+enum xylonfb_color_format {
+ XYLONFB_FORMAT_A8,
+ XYLONFB_FORMAT_C8,
+ XYLONFB_FORMAT_RGB332,
+ XYLONFB_FORMAT_RGB565,
+ XYLONFB_FORMAT_XRGB8888,
+ XYLONFB_FORMAT_ARGB8888,
+ XYLONFB_FORMAT_YUYV,
+ XYLONFB_FORMAT_AYUV,
+ XYLONFB_FORMAT_CLUT_ARGB6565,
+ XYLONFB_FORMAT_CLUT_ARGB8888,
+ XYLONFB_FORMAT_CLUT_AYUV8888
+};
+
+struct xylonfb_layer_data;
+struct xylonfb_data;
+
+#define VMODE_NAME_SIZE 21
+#define VMODE_OPTS_SIZE 3
+
+struct xylonfb_vmode {
+ u32 ctrl;
+ struct fb_videomode vmode;
+ char name[VMODE_NAME_SIZE];
+ char opts_cvt[VMODE_OPTS_SIZE];
+ char opts_ext[VMODE_OPTS_SIZE];
+};
+
+struct xylonfb_registers {
+ u32 ctrl;
+ u32 dtype;
+ u32 bg;
+ u32 unused[3];
+ u32 int_mask;
+};
+
+union xylonfb_layer_reg_0 {
+ u32 addr;
+ u32 hoff;
+};
+
+union xylonfb_layer_reg_1 {
+ u32 unused;
+ u32 voff;
+};
+
+struct xylonfb_layer_registers {
+ union xylonfb_layer_reg_0 reg_0;
+ union xylonfb_layer_reg_1 reg_1;
+ u32 hpos;
+ u32 vpos;
+ u32 hsize;
+ u32 vsize;
+ u32 alpha;
+ u32 ctrl;
+ u32 transp;
+};
+
+struct xylonfb_register_access {
+ u32 (*get_reg_val)(void __iomem *dev_base, unsigned int offset,
+ struct xylonfb_layer_data *layer_data);
+ void (*set_reg_val)(u32 value, void __iomem *dev_base,
+ unsigned int offset,
+ struct xylonfb_layer_data *layer_data);
+};
+
+struct xylonfb_layer_fix_data {
+ unsigned int id;
+ u32 address;
+ u32 address_range;
+ u32 buffer_offset;
+ u32 bpp;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 format_clut;
+ u32 transparency;
+ u32 type;
+ bool component_swap;
+};
+
+struct xylonfb_layer_data {
+ struct mutex mutex;
+
+ atomic_t refcount;
+
+ struct xylonfb_data *data;
+ struct xylonfb_layer_fix_data *fd;
+ struct xylonfb_layer_registers regs;
+
+ dma_addr_t pbase;
+ void __iomem *base;
+ void __iomem *clut_base;
+
+ dma_addr_t fb_pbase;
+ void *fb_base;
+ u32 fb_size;
+
+ dma_addr_t fb_pbase_active;
+
+ u32 flags;
+};
+
+struct xylonfb_rgb2yuv_coeff {
+ s32 cy;
+ s32 cyr;
+ s32 cyg;
+ s32 cyb;
+ s32 cur;
+ s32 cug;
+ s32 cub;
+ s32 cvr;
+ s32 cvg;
+ s32 cvb;
+};
+
+struct xylonfb_sync {
+ wait_queue_head_t wait;
+ unsigned int count;
+};
+
+struct xylonfb_data {
+ struct platform_device *pdev;
+
+ struct device_node *device;
+ struct device_node *encoder;
+ struct device_node *pixel_clock;
+
+ struct resource resource_mem;
+ struct resource resource_irq;
+
+ void __iomem *dev_base;
+
+ struct mutex irq_mutex;
+
+ struct xylonfb_register_access reg_access;
+ struct xylonfb_sync vsync;
+ struct xylonfb_vmode vm;
+ struct xylonfb_vmode vm_active;
+ struct xylonfb_rgb2yuv_coeff coeff;
+
+ struct xylonfb_layer_fix_data *fd[LOGICVC_MAX_LAYERS];
+ struct xylonfb_registers regs;
+
+ u32 bg_layer_bpp;
+ u32 console_layer;
+ u32 pixel_stride;
+
+ atomic_t refcount;
+
+ u32 flags;
+ int irq;
+ u8 layers;
+ /*
+ * Delay after applying display power and
+ * before applying display signals
+ */
+ u32 pwr_delay;
+ /*
+ * Delay after applying display signal and
+ * before applying display backlight power supply
+ */
+ u32 sig_delay;
+ /* IP version */
+ u8 major;
+ u8 minor;
+ u8 patch;
+};
+
+/* Xylon FB video mode options */
+extern char *xylonfb_mode_option;
+
+/* Xylon FB core pixel clock interface functions */
+extern bool xylonfb_hw_pixclk_supported(struct device *dev,
+ struct device_node *dn);
+extern void xylonfb_hw_pixclk_unload(struct device_node *dn);
+extern int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+ unsigned long pixclk_khz);
+
+/* Xylon FB core V sync wait function */
+extern int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi);
+
+/* Xylon FB core interface functions */
+extern int xylonfb_init_core(struct xylonfb_data *data);
+extern int xylonfb_deinit_core(struct platform_device *pdev);
+extern int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd,
+ unsigned long arg);
+
+#endif /* __XYLONFB_CORE_H__ */
--
1.9.1
^ permalink raw reply related
* [RFC 03/10] video: fbdev: Xylon: Framebuffer driver ioctl
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver IOCTL providing advanced video controller functionality to be
used by user space applications.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_ioctl.c | 676 ++++++++++++++++++++++++++++++
1 file changed, 676 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/xylonfb_ioctl.c
diff --git a/drivers/video/fbdev/xylon/xylonfb_ioctl.c b/drivers/video/fbdev/xylon/xylonfb_ioctl.c
new file mode 100644
index 0000000..296aef80
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_ioctl.c
@@ -0,0 +1,676 @@
+/*
+ * Xylon logiCVC frame buffer driver IOCTL functionality
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/xylonfb.h>
+
+#include "logicvc.h"
+#include "xylonfb_core.h"
+
+static int xylonfb_get_vblank(struct fb_vblank *vblank, struct fb_info *fbi)
+{
+ vblank->flags |= FB_VBLANK_HAVE_VSYNC;
+
+ return 0;
+}
+
+static void xylonfb_vsync_ctrl(struct fb_info *fbi, bool enable)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ u32 imr;
+
+ mutex_lock(&data->irq_mutex);
+
+ imr = data->reg_access.get_reg_val(data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+ if (enable) {
+ imr &= (~LOGICVC_INT_V_SYNC);
+ writel(LOGICVC_INT_V_SYNC,
+ data->dev_base + LOGICVC_INT_STAT_ROFF);
+ } else {
+ imr |= LOGICVC_INT_V_SYNC;
+ }
+
+ data->reg_access.set_reg_val(imr, data->dev_base,
+ LOGICVC_INT_MASK_ROFF, ld);
+
+ mutex_unlock(&data->irq_mutex);
+}
+
+int xylonfb_vsync_wait(u32 crt, struct fb_info *fbi)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ int ret, count;
+
+ mutex_lock(&data->irq_mutex);
+
+ count = data->vsync.count;
+
+ ret = wait_event_interruptible_timeout(data->vsync.wait,
+ (count != data->vsync.count),
+ HZ/10);
+
+ mutex_unlock(&data->irq_mutex);
+
+ if (ret < 0)
+ return ret;
+ else if (ret = 0)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static unsigned int alpha_normalized(unsigned int alpha, unsigned int used_bits,
+ bool set)
+{
+ if (set)
+ return alpha / (255 / ((1 << used_bits) - 1));
+ else
+ return (((255 << 16) / ((1 << used_bits) - 1)) * alpha) >> 16;
+}
+
+static int xylonfb_layer_alpha(struct xylonfb_layer_data *ld, u8 *alpha,
+ bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ unsigned int used_bits;
+ u32 val;
+
+ if (fd->transparency != LOGICVC_ALPHA_LAYER)
+ return -EPERM;
+
+ switch (fd->type) {
+ case LOGICVC_LAYER_YUV:
+ used_bits = 8;
+ break;
+ case LOGICVC_LAYER_RGB:
+ switch (fd->bpp) {
+ case 8:
+ used_bits = 3;
+ break;
+ case 16:
+ used_bits = 6;
+ break;
+ case 32:
+ used_bits = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!set) {
+ val = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_ALPHA_ROFF,
+ ld);
+ *alpha = (u8)(val & (0xFF >> (8 - used_bits)));
+ }
+
+ /* get/set normalized alpha value */
+ *alpha = alpha_normalized(*alpha, used_bits, set);
+
+ if (set)
+ data->reg_access.set_reg_val(*alpha, ld->base,
+ LOGICVC_LAYER_ALPHA_ROFF,
+ ld);
+
+ return 0;
+}
+
+static int xylonfb_layer_buff(struct fb_info *fbi,
+ struct xylonfb_layer_buffer *layer_buff,
+ bool set)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ unsigned int layer_id = ld->fd->id;
+ u32 reg;
+
+ if (set) {
+ if (layer_buff->id >= LOGICVC_MAX_LAYER_BUFFERS)
+ return -EINVAL;
+
+ reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+ reg |= (1 << (10 + layer_id));
+ reg &= ~(0x03 << (layer_id << 1));
+ reg |= (layer_buff->id << (layer_id << 1));
+ writel(reg, ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+
+ xylonfb_vsync_wait(0, fbi);
+ } else {
+ reg = readl(ld->data->dev_base + LOGICVC_VBUFF_SELECT_ROFF);
+ reg >>= ((layer_id << 1));
+ layer_buff->id = reg & 0x03;
+ }
+
+ return 0;
+}
+
+static void xylonfb_rgb_yuv(u32 c1, u32 c2, u32 c3, u32 *pixel,
+ struct xylonfb_layer_data *ld, bool rgb2yuv)
+{
+ struct xylonfb_data *data = ld->data;
+ u32 r, g, b, y, u, v;
+
+ if (rgb2yuv) {
+ y = ((data->coeff.cyr * c1) + (data->coeff.cyg * c2) +
+ (data->coeff.cyb * c3) + data->coeff.cy) /
+ LOGICVC_YUV_NORM;
+ u = ((-data->coeff.cur * c1) - (data->coeff.cug * c2) +
+ (data->coeff.cub * c3) + LOGICVC_COEFF_U) /
+ LOGICVC_YUV_NORM;
+ v = ((data->coeff.cvr * c1) - (data->coeff.cvg * c2) -
+ (data->coeff.cvb * c3) + LOGICVC_COEFF_V) /
+ LOGICVC_YUV_NORM;
+
+ *pixel = (0xFF << 24) | (y << 16) | (u << 8) | v;
+ } else {
+ r = ((c1 * LOGICVC_RGB_NORM) + (LOGICVC_COEFF_R_U * c2) -
+ LOGICVC_COEFF_R) / LOGICVC_RGB_NORM;
+ g = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_G_U * c2) -
+ (LOGICVC_COEFF_G_V * c3) + LOGICVC_COEFF_G) /
+ LOGICVC_RGB_NORM;
+ b = ((c1 * LOGICVC_RGB_NORM) - (LOGICVC_COEFF_B_V * c3) -
+ LOGICVC_COEFF_B) / LOGICVC_RGB_NORM;
+
+ *pixel = (0xFF << 24) | (r << 16) | (g << 8) | b;
+ }
+}
+
+static int xylonfb_layer_color_rgb(struct xylonfb_layer_data *ld,
+ struct xylonfb_layer_color *layer_color,
+ unsigned int reg_offset, bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ void __iomem *base;
+ u32 r = 0, g = 0, b = 0;
+ u32 raw_rgb, y, u, v;
+ int bpp, transparency;
+
+ if (reg_offset = LOGICVC_LAYER_TRANSP_COLOR_ROFF) {
+ base = ld->base;
+ bpp = fd->bpp;
+ transparency = fd->transparency;
+ } else /* if (reg_offset = LOGICVC_BACKGROUND_COLOR_ROFF) */ {
+ base = data->dev_base;
+ bpp = data->bg_layer_bpp;
+ transparency = -1;
+ }
+
+ if (set) {
+ if (layer_color->use_raw) {
+ raw_rgb = layer_color->raw_rgb;
+ } else if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+ r = layer_color->r;
+ g = layer_color->g;
+ b = layer_color->b;
+ xylonfb_rgb_yuv(r, g, b, &raw_rgb, ld, true);
+ } else {
+ r = layer_color->r;
+ g = layer_color->g;
+ b = layer_color->b;
+check_bpp_set:
+ switch (bpp) {
+ case 8:
+ switch (transparency) {
+ case LOGICVC_ALPHA_CLUT_16BPP:
+ bpp = 16;
+ goto check_bpp_set;
+ break;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_set;
+ break;
+ default:
+ raw_rgb = (r & 0xE0) |
+ ((g & 0xE0) >> 3) |
+ ((b & 0xC0) >> 6);
+ break;
+ }
+ break;
+ case 16:
+ raw_rgb = ((r & 0xF8) << 8) |
+ ((g & 0xFC) << 3) |
+ ((b & 0xF8) >> 3);
+ break;
+ case 32:
+ raw_rgb = (r << 16) | (g << 8) | b;
+ break;
+ default:
+ raw_rgb = 0;
+ }
+ }
+ data->reg_access.set_reg_val(raw_rgb, base, reg_offset, ld);
+ } else {
+ raw_rgb = data->reg_access.get_reg_val(base, reg_offset, ld);
+check_bpp_get:
+ if (data->flags & XYLONFB_FLAGS_BACKGROUND_LAYER_YUV) {
+ y = (raw_rgb >> 16) & 0xFF;
+ u = (raw_rgb >> 8) & 0xFF;
+ v = raw_rgb & 0xFF;
+ xylonfb_rgb_yuv(y, u, v, &raw_rgb, ld, false);
+ } else {
+ switch (bpp) {
+ case 8:
+ switch (transparency) {
+ case LOGICVC_ALPHA_CLUT_16BPP:
+ bpp = 16;
+ goto check_bpp_get;
+ break;
+ case LOGICVC_ALPHA_CLUT_32BPP:
+ bpp = 32;
+ goto check_bpp_get;
+ break;
+ default:
+ r = raw_rgb >> 5;
+ r = (((r << 3) | r) << 2) | (r >> 1);
+ g = (raw_rgb >> 2) & 0x07;
+ g = (((g << 3) | g) << 2) | (g >> 1);
+ b = raw_rgb & 0x03;
+ b = (b << 6) | (b << 4) | (b << 2) | b;
+ break;
+ }
+ break;
+ case 16:
+ r = raw_rgb >> 11;
+ r = (r << 3) | (r >> 2);
+ g = (raw_rgb >> 5) & 0x3F;
+ g = (g << 2) | (g >> 4);
+ b = raw_rgb & 0x1F;
+ b = (b << 3) | (b >> 2);
+ break;
+ case 32:
+ r = raw_rgb >> 16;
+ g = (raw_rgb >> 8) & 0xFF;
+ b = raw_rgb & 0xFF;
+ break;
+ default:
+ raw_rgb = r = g = b = 0;
+ }
+ }
+ layer_color->raw_rgb = raw_rgb;
+ layer_color->r = (u8)r;
+ layer_color->g = (u8)g;
+ layer_color->b = (u8)b;
+ }
+
+ return 0;
+}
+
+static int xylonfb_layer_geometry(struct fb_info *fbi,
+ struct xylonfb_layer_geometry *layer_geometry,
+ bool set)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 x, y, width, height, xoff, yoff, xres, yres;
+
+ xres = fbi->var.xres;
+ yres = fbi->var.yres;
+
+ if (set) {
+ x = layer_geometry->x;
+ y = layer_geometry->y;
+ width = layer_geometry->width;
+ height = layer_geometry->height;
+
+ if ((x > xres) || (y > yres))
+ return -EINVAL;
+
+ if ((width = 0) || (height = 0))
+ return -EINVAL;
+
+ if ((x + width) > xres) {
+ width = xres - x;
+ layer_geometry->width = width;
+ }
+ if ((y + height) > yres) {
+ height = yres - y;
+ layer_geometry->height = height;
+ }
+ /* YUV 4:2:2 layer type can only have even layer width */
+ if ((width > 2) && (fd->type = LOGICVC_LAYER_YUV) &&
+ (fd->bpp = 16))
+ width &= ~((unsigned long) + 1);
+
+ /*
+ * logiCVC 3.x registers write sequence:
+ * offset, size, position with implicit last write to
+ * LOGICVC_LAYER_VPOS_ROFF
+ * logiCVC 4.x registers write sequence:
+ * size, position with implicit last write to
+ * LOGICVC_LAYER_ADDR_ROFF
+ */
+ if (!(data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS)) {
+ data->reg_access.set_reg_val(layer_geometry->x_offset,
+ ld->base,
+ LOGICVC_LAYER_HOFF_ROFF,
+ ld);
+ data->reg_access.set_reg_val(layer_geometry->y_offset,
+ ld->base,
+ LOGICVC_LAYER_VOFF_ROFF,
+ ld);
+ }
+ data->reg_access.set_reg_val((width - 1), ld->base,
+ LOGICVC_LAYER_HSIZE_ROFF,
+ ld);
+ data->reg_access.set_reg_val((height - 1), ld->base,
+ LOGICVC_LAYER_VSIZE_ROFF,
+ ld);
+ data->reg_access.set_reg_val((xres - (x + 1)), ld->base,
+ LOGICVC_LAYER_HPOS_ROFF,
+ ld);
+ data->reg_access.set_reg_val((yres - (y + 1)), ld->base,
+ LOGICVC_LAYER_VPOS_ROFF,
+ ld);
+ if (data->flags & XYLONFB_FLAGS_DYNAMIC_LAYER_ADDRESS) {
+ xoff = layer_geometry->x_offset * (ld->fd->bpp / 8);
+ yoff = layer_geometry->y_offset * ld->fd->width *
+ (ld->fd->bpp / 8);
+
+ ld->fb_pbase_active = ld->fb_pbase + xoff + yoff;
+
+ data->reg_access.set_reg_val(ld->fb_pbase_active,
+ ld->base,
+ LOGICVC_LAYER_ADDR_ROFF,
+ ld);
+ }
+ } else {
+ x = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_HPOS_ROFF,
+ ld);
+ layer_geometry->x = xres - (x + 1);
+ y = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_VPOS_ROFF,
+ ld);
+ layer_geometry->y = yres - (y + 1);
+ layer_geometry->width + data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_HSIZE_ROFF,
+ ld);
+ layer_geometry->width += 1;
+ layer_geometry->height + data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_VSIZE_ROFF,
+ ld);
+ layer_geometry->height += 1;
+ }
+
+ return 0;
+}
+
+static int xylonfb_layer_reg_access(struct xylonfb_layer_data *ld,
+ struct xylonfb_hw_access *hw_access,
+ bool set)
+{
+ struct xylonfb_data *data = ld->data;
+ struct xylonfb_layer_fix_data *fd = ld->fd;
+ u32 offset;
+ u32 rel_offset;
+
+ if ((hw_access->offset < LOGICVC_LAYER_BASE_OFFSET) ||
+ (hw_access->offset > LOGICVC_LAYER_BASE_END))
+ return -EPERM;
+
+ if (data->flags & XYLONFB_FLAGS_READABLE_REGS) {
+ offset = hw_access->offset;
+ if (set)
+ data->reg_access.set_reg_val(hw_access->value,
+ data->dev_base,
+ offset,
+ ld);
+ else
+ hw_access->value + data->reg_access.get_reg_val(data->dev_base,
+ offset,
+ ld);
+ return 0;
+ }
+
+ rel_offset = hw_access->offset - (fd->id * 0x80) -
+ LOGICVC_LAYER_BASE_OFFSET;
+
+ if (rel_offset > LOGICVC_LAYER_BASE_END)
+ return -EINVAL;
+
+ if (set)
+ data->reg_access.set_reg_val(hw_access->value, ld->base,
+ rel_offset, ld);
+ else
+ hw_access->value = data->reg_access.get_reg_val(ld->base,
+ rel_offset, ld);
+
+ return 0;
+}
+
+int xylonfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
+{
+ struct xylonfb_layer_data *ld = fbi->par;
+ struct xylonfb_data *data = ld->data;
+ union {
+ struct fb_vblank vblank;
+ struct xylonfb_hw_access hw_access;
+ struct xylonfb_layer_buffer layer_buff;
+ struct xylonfb_layer_color layer_color;
+ struct xylonfb_layer_geometry layer_geometry;
+ struct xylonfb_layer_transparency layer_transp;
+ } ioctl;
+ void __user *argp = (void __user *)arg;
+ unsigned long val;
+ u32 var32;
+ int ret = 0;
+ bool flag;
+
+ switch (cmd) {
+ case FBIOGET_VBLANK:
+ if (copy_from_user(&ioctl.vblank, argp, sizeof(ioctl.vblank)))
+ return -EFAULT;
+
+ ret = xylonfb_get_vblank(&ioctl.vblank, fbi);
+ if (!ret &&
+ copy_to_user(argp, &ioctl.vblank, sizeof(ioctl.vblank)))
+ ret = -EFAULT;
+ break;
+
+ case FBIO_WAITFORVSYNC:
+ if (get_user(var32, (u32 __user *)arg))
+ return -EFAULT;
+
+ ret = xylonfb_vsync_wait(var32, fbi);
+ break;
+
+ case XYLONFB_VSYNC_CTRL:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ xylonfb_vsync_ctrl(fbi, flag);
+ break;
+
+ case XYLONFB_LAYER_IDX:
+ var32 = ld->fd->id;
+ put_user(var32, (u32 __user *)arg);
+ break;
+
+ case XYLONFB_LAYER_ALPHA:
+ if (copy_from_user(&ioctl.layer_transp, argp,
+ sizeof(ioctl.layer_transp)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_alpha(ld, &ioctl.layer_transp.alpha,
+ ioctl.layer_transp.set);
+ if (!ret && !ioctl.layer_transp.set)
+ if (copy_to_user(argp, &ioctl.layer_transp,
+ sizeof(ioctl.layer_transp)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_COLOR_TRANSP_CTRL:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ var32 = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ if (flag)
+ var32 |= LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+ else
+ var32 &= ~LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT;
+ data->reg_access.set_reg_val(var32, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_COLOR_TRANSP:
+ if (copy_from_user(&ioctl.layer_color, argp,
+ sizeof(ioctl.layer_color)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+ LOGICVC_LAYER_TRANSP_COLOR_ROFF,
+ ioctl.layer_color.set);
+ if (!ret && !ioctl.layer_color.set)
+ if (copy_to_user(argp, &ioctl.layer_color,
+ sizeof(ioctl.layer_color)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_GEOMETRY:
+ if (!(data->flags & XYLONFB_FLAGS_SIZE_POSITION))
+ return -EINVAL;
+
+ if (copy_from_user(&ioctl.layer_geometry, argp,
+ sizeof(ioctl.layer_geometry)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_geometry(fbi, &ioctl.layer_geometry,
+ ioctl.layer_geometry.set);
+ if (!ret && !ioctl.layer_geometry.set)
+ if (copy_to_user(argp, &ioctl.layer_geometry,
+ sizeof(ioctl.layer_geometry)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_BUFFER:
+ if (data->major >= 4)
+ return -EPERM;
+
+ if (copy_from_user(&ioctl.layer_buff, argp,
+ sizeof(ioctl.layer_buff)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_buff(fbi, &ioctl.layer_buff,
+ ioctl.layer_buff.set);
+ if (!ret && !ioctl.layer_buff.set)
+ if (copy_to_user(argp, &ioctl.layer_buff,
+ sizeof(ioctl.layer_buff)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_BUFFER_OFFSET:
+ if (data->major < 4) {
+ var32 = readl(ld->data->dev_base +
+ LOGICVC_VBUFF_SELECT_ROFF);
+ var32 >>= (ld->fd->id << 1);
+ var32 &= 0x03;
+ val = ld->fd->buffer_offset;
+ val *= var32;
+ } else {
+ val = ld->fd->buffer_offset;
+ }
+ put_user(val, (unsigned long __user *)arg);
+ break;
+
+ case XYLONFB_BACKGROUND_COLOR:
+ if (data->bg_layer_bpp = 0)
+ return -EPERM;
+
+ if (copy_from_user(&ioctl.layer_color, argp,
+ sizeof(ioctl.layer_color)))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ ret = xylonfb_layer_color_rgb(ld, &ioctl.layer_color,
+ LOGICVC_BACKGROUND_COLOR_ROFF,
+ ioctl.layer_color.set);
+ if (!ret && !ioctl.layer_color.set)
+ if (copy_to_user(argp, &ioctl.layer_color,
+ sizeof(ioctl.layer_color)))
+ ret = -EFAULT;
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_LAYER_EXT_BUFF_SWITCH:
+ if (get_user(flag, (u8 __user *)arg))
+ return -EFAULT;
+
+ mutex_lock(&ld->mutex);
+ var32 = data->reg_access.get_reg_val(ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ if (flag)
+ var32 |= LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+ else
+ var32 &= ~LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH;
+ data->reg_access.set_reg_val(var32, ld->base,
+ LOGICVC_LAYER_CTRL_ROFF,
+ ld);
+ mutex_unlock(&ld->mutex);
+ break;
+
+ case XYLONFB_HW_ACCESS:
+ if (copy_from_user(&ioctl.hw_access, argp,
+ sizeof(ioctl.hw_access)))
+ return -EFAULT;
+
+ ret = xylonfb_layer_reg_access(ld, &ioctl.hw_access,
+ ioctl.hw_access.set);
+ if (!ret && !ioctl.hw_access.set)
+ if (copy_to_user(argp, &ioctl.hw_access,
+ sizeof(ioctl.hw_access)))
+ ret = -EFAULT;
+ break;
+
+ case XYLONFB_IP_CORE_VERSION:
+ var32 = (data->major << 16) | (data->minor << 8) | data->patch;
+ if (copy_to_user(argp, &var32, sizeof(u32)))
+ ret = -EFAULT;
+ break;
+
+ default:
+ dev_err(&data->pdev->dev, "unknown ioctl");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
--
1.9.1
^ permalink raw reply related
* [RFC 04/10] video: fbdev: Xylon: Framebuffer driver pixel clock
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver pixel clock control functionality supporting Xylon logiCLK IP
core clock generator and Silicon labs Si570 I2C clock generator.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/xylonfb_pixclk.c | 140 +++++++++++++++++++++++++++++
1 file changed, 140 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/xylonfb_pixclk.c
diff --git a/drivers/video/fbdev/xylon/xylonfb_pixclk.c b/drivers/video/fbdev/xylon/xylonfb_pixclk.c
new file mode 100644
index 0000000..c7b8674
--- /dev/null
+++ b/drivers/video/fbdev/xylon/xylonfb_pixclk.c
@@ -0,0 +1,140 @@
+/*
+ * Xylon logiCVC frame buffer driver pixel clock control
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+struct xylonfb_pixclk {
+ struct device *dev;
+ struct device_node *dn;
+ struct clk *clk;
+};
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+static const struct of_device_id logiclk_of_match[] = {
+ { .compatible = "xylon,logiclk-1.02.b" },
+ {/* end of table */}
+};
+static struct xylonfb_pixclk logiclk;
+#endif
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_SI570)
+static const struct of_device_id si570_of_match[] = {
+ { .compatible = "silabs,si570" },
+ {/* end of table */}
+};
+static struct xylonfb_pixclk si570;
+#endif
+
+bool xylonfb_hw_pixclk_supported(struct device *dev, struct device_node *dn);
+void xylonfb_hw_pixclk_unload(struct device_node *dn);
+int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+ unsigned long pixclk_khz);
+
+#if defined(CONFIG_FB_XYLON_PIXCLK)
+static int xylonfb_hw_pixclk_set_freq(struct device *dev,
+ struct device_node *dn,
+ unsigned long freq_khz)
+{
+ struct clk *clk = NULL;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+ if (dn = logiclk.dn)
+ clk = logiclk.clk;
+#endif
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_SI570)
+ if (dn = si570.dn)
+ clk = si570.clk;
+#endif
+
+ if (clk && clk_set_rate(clk, (freq_khz * 1000))) {
+ dev_err(dev, "failed set pixel clock frequency\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
+bool xylonfb_hw_pixclk_supported(struct device *dev, struct device_node *dn)
+{
+ struct clk **clk;
+ bool clk_dev = false;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+ if (of_match_node(logiclk_of_match, of_get_parent(dn))) {
+ clk = &logiclk.clk;
+ logiclk.dev = dev;
+ logiclk.dn = dn;
+ clk_dev = true;
+ }
+#endif
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_SI570)
+ if (of_match_node(si570_of_match, dn)) {
+ clk = &si570.clk;
+ si570.dev = dev;
+ si570.dn = dn;
+ clk_dev = true;
+ }
+#endif
+
+ if (clk_dev) {
+ *clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(*clk)) {
+ dev_err(dev, "failed get pixel clock\n");
+ return false;
+ }
+ if (clk_prepare_enable(*clk)) {
+ dev_err(dev,
+ "failed prepare/enable pixel clock\n");
+ return false;
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void xylonfb_hw_pixclk_unload(struct device_node *dn)
+{
+ struct clk *clk = NULL;
+
+#if defined(CONFIG_FB_XYLON_PIXCLK_LOGICLK)
+ clk = logiclk.clk;
+#endif
+#if defined(CONFIG_FB_XYLON_PIXCLK_SI570)
+ clk = si570.clk;
+#endif
+
+ if (clk)
+ clk_disable_unprepare(clk);
+}
+
+int xylonfb_hw_pixclk_set(struct device *dev, struct device_node *dn,
+ unsigned long pixclk_khz)
+{
+#if defined(CONFIG_FB_XYLON_PIXCLK)
+ return xylonfb_hw_pixclk_set_freq(dev, dn, pixclk_khz);
+#else
+ dev_warn(dev, "pixel clock control not supported\n");
+ return -ENODEV;
+#endif
+}
--
1.9.1
^ permalink raw reply related
* [RFC 05/10] video: fbdev: Xylon: logiCVC IP core definitions
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver hw parameters and definitions for logiCVC IP core.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/logicvc.h | 192 ++++++++++++++++++++++++++++++++++++
1 file changed, 192 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/logicvc.h
diff --git a/drivers/video/fbdev/xylon/logicvc.h b/drivers/video/fbdev/xylon/logicvc.h
new file mode 100644
index 0000000..07b28b5
--- /dev/null
+++ b/drivers/video/fbdev/xylon/logicvc.h
@@ -0,0 +1,192 @@
+/*
+ * Xylon logiCVC IP core parameters
+ *
+ * Copyright (C) 2014 Xylon d.o.o.
+ * Author: Davor Joja <davor.joja@logicbricks.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LOGICVC_H__
+#define __LOGICVC_H__
+
+/*
+ * All logiCVC registers are only 32-bit accessible.
+ * All logiCVC registers are aligned to 8 byte boundary.
+ */
+#define LOGICVC_REG_STRIDE 8
+#define LOGICVC_HSYNC_FRONT_PORCH_ROFF (0 * LOGICVC_REG_STRIDE)
+#define LOGICVC_HSYNC_ROFF (1 * LOGICVC_REG_STRIDE)
+#define LOGICVC_HSYNC_BACK_PORCH_ROFF (2 * LOGICVC_REG_STRIDE)
+#define LOGICVC_HRES_ROFF (3 * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_FRONT_PORCH_ROFF (4 * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_ROFF (5 * LOGICVC_REG_STRIDE)
+#define LOGICVC_VSYNC_BACK_PORCH_ROFF (6 * LOGICVC_REG_STRIDE)
+#define LOGICVC_VRES_ROFF (7 * LOGICVC_REG_STRIDE)
+#define LOGICVC_CTRL_ROFF (8 * LOGICVC_REG_STRIDE)
+#define LOGICVC_DTYPE_ROFF (9 * LOGICVC_REG_STRIDE)
+#define LOGICVC_BACKGROUND_COLOR_ROFF (10 * LOGICVC_REG_STRIDE)
+#define LOGICVC_CLUT_SELECT_ROFF (12 * LOGICVC_REG_STRIDE)
+#define LOGICVC_INT_STAT_ROFF (13 * LOGICVC_REG_STRIDE)
+#define LOGICVC_INT_MASK_ROFF (14 * LOGICVC_REG_STRIDE)
+#define LOGICVC_POWER_CTRL_ROFF (15 * LOGICVC_REG_STRIDE)
+#define LOGICVC_IP_VERSION_ROFF (31 * LOGICVC_REG_STRIDE)
+/* Version 3.x */
+#define LOGICVC_VBUFF_SELECT_ROFF (11 * LOGICVC_REG_STRIDE)
+
+/*
+ * logiCVC layer registers offsets (common for each layer)
+ * Last possible logiCVC layer (No.4) implements only "Layer memory address"
+ * and "Layer control" registers.
+ */
+/* Version 3.x */
+#define LOGICVC_LAYER_HOFF_ROFF (0 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VOFF_ROFF (1 * LOGICVC_REG_STRIDE)
+/* Version 4.x */
+#define LOGICVC_LAYER_ADDR_ROFF (0 * LOGICVC_REG_STRIDE)
+/* Common */
+#define LOGICVC_LAYER_HPOS_ROFF (2 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VPOS_ROFF (3 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_HSIZE_ROFF (4 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_VSIZE_ROFF (5 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_ALPHA_ROFF (6 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_CTRL_ROFF (7 * LOGICVC_REG_STRIDE)
+#define LOGICVC_LAYER_TRANSP_COLOR_ROFF (8 * LOGICVC_REG_STRIDE)
+
+/* logiCVC interrupt bits */
+#define LOGICVC_INT_L0_UPDATED (1 << 0)
+#define LOGICVC_INT_L1_UPDATED (1 << 1)
+#define LOGICVC_INT_L2_UPDATED (1 << 2)
+#define LOGICVC_INT_L3_UPDATED (1 << 3)
+#define LOGICVC_INT_L4_UPDATED (1 << 4)
+#define LOGICVC_INT_V_SYNC (1 << 5)
+#define LOGICVC_INT_E_VIDEO_VALID (1 << 6)
+#define LOGICVC_INT_FIFO_UNDERRUN (1 << 7)
+#define LOGICVC_INT_L0_CLUT_SW (1 << 8)
+#define LOGICVC_INT_L1_CLUT_SW (1 << 9)
+#define LOGICVC_INT_L2_CLUT_SW (1 << 10)
+#define LOGICVC_INT_L3_CLUT_SW (1 << 11)
+#define LOGICVC_INT_L4_CLUT_SW (1 << 12)
+
+/* logiCVC layer base offsets */
+#define LOGICVC_LAYER_OFFSET 0x80
+#define LOGICVC_LAYER_BASE_OFFSET 0x100
+#define LOGICVC_LAYER_BASE_END 0x338
+#define LOGICVC_LAYER_0_OFFSET (0 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_1_OFFSET (1 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_2_OFFSET (2 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_3_OFFSET (3 * LOGICVC_LAYER_OFFSET)
+#define LOGICVC_LAYER_4_OFFSET (4 * LOGICVC_LAYER_OFFSET)
+
+/* logiCVC layer CLUT base offsets */
+#define LOGICVC_CLUT_OFFSET 0x800
+#define LOGICVC_CLUT_BASE_OFFSET 0x1000
+#define LOGICVC_CLUT_L0_CLUT_0_OFFSET (0 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L0_CLUT_1_OFFSET (1 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L1_CLUT_0_OFFSET (2 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L1_CLUT_1_OFFSET (3 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L2_CLUT_0_OFFSET (4 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L2_CLUT_1_OFFSET (5 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L3_CLUT_0_OFFSET (6 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L3_CLUT_1_OFFSET (7 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L4_CLUT_0_OFFSET (8 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_L4_CLUT_1_OFFSET (9 * LOGICVC_CLUT_OFFSET)
+#define LOGICVC_CLUT_REGISTER_SIZE 8
+#define LOGICVC_CLUT_0_INDEX_OFFSET 2
+#define LOGICVC_CLUT_1_INDEX_OFFSET 1
+
+/* logiCVC registers range */
+#define LOGICVC_REGISTERS_RANGE 0x6000
+#define LOGICVC_LAYER_REGISTERS_RANGE 0x80
+
+/* logiCVC control register bits */
+#define LOGICVC_CTRL_HSYNC (1 << 0)
+#define LOGICVC_CTRL_HSYNC_INVERT (1 << 1)
+#define LOGICVC_CTRL_VSYNC (1 << 2)
+#define LOGICVC_CTRL_VSYNC_INVERT (1 << 3)
+#define LOGICVC_CTRL_DATA_ENABLE (1 << 4)
+#define LOGICVC_CTRL_DATA_ENABLE_INVERT (1 << 5)
+#define LOGICVC_CTRL_PIXEL_DATA_INVERT (1 << 7)
+#define LOGICVC_CTRL_PIXEL_DATA_TRIGGER_INVERT (1 << 8)
+#define LOGICVC_CTRL_DISABLE_LAYER_UPDATE (1 << 9)
+
+/* logiCVC layer control register bits */
+#define LOGICVC_LAYER_CTRL_ENABLE (1 << 0)
+#define LOGICVC_LAYER_CTRL_COLOR_TRANSPARENCY_BIT (1 << 1)
+#define LOGICVC_LAYER_CTRL_EXTERNAL_BUFFER_SWITCH (1 << 2)
+#define LOGICVC_LAYER_CTRL_INTERLACE_BIT (1 << 3)
+#define LOGICVC_LAYER_CTRL_PIXEL_FORMAT_BIT_ABGR (1 << 4)
+
+/* logiCVC register initial values */
+#define LOGICVC_DTYPE_REG_INIT 0
+
+/* logiCVC display power signals */
+#define LOGICVC_EN_BLIGHT_MSK (1 << 0)
+#define LOGICVC_EN_VDD_MSK (1 << 1)
+#define LOGICVC_EN_VEE_MSK (1 << 2)
+#define LOGICVC_V_EN_MSK (1 << 3)
+
+/* logiCVC various definitions */
+#define LOGICVC_MAJOR_REVISION_SHIFT 11
+#define LOGICVC_MAJOR_REVISION_MASK 0x3F
+#define LOGICVC_MINOR_REVISION_SHIFT 5
+#define LOGICVC_MINOR_REVISION_MASK 0x3F
+#define LOGICVC_PATCH_LEVEL_MASK 0x1F
+
+#define LOGICVC_MAX_LAYER_BUFFERS 3
+#define LOGICVC_MIN_XRES 64
+#define LOGICVC_MIN_VRES 1
+#define LOGICVC_MAX_XRES 2048
+#define LOGICVC_MAX_VRES 2048
+#define LOGICVC_CLUT_SIZE 256
+
+#define LOGICVC_COEFF_Y_R 29900
+#define LOGICVC_COEFF_Y_G 58700
+#define LOGICVC_COEFF_Y_B 11400
+#define LOGICVC_COEFF_Y 0
+#define LOGICVC_COEFF_U 12800000
+#define LOGICVC_COEFF_V 12800000
+#define LOGICVC_COEFF_U_R 16868
+#define LOGICVC_COEFF_U_G 33107
+#define LOGICVC_COEFF_U_B 49970
+#define LOGICVC_COEFF_V_R 49980
+#define LOGICVC_COEFF_V_G 41850
+#define LOGICVC_COEFF_V_B 8128
+#define LOGICVC_COEFF_ITU656_Y 1600000
+#define LOGICVC_COEFF_ITU656_U_R 17258
+#define LOGICVC_COEFF_ITU656_U_G 33881
+#define LOGICVC_COEFF_ITU656_U_B 51140
+#define LOGICVC_COEFF_ITU656_V_R 51138
+#define LOGICVC_COEFF_ITU656_V_G 42820
+#define LOGICVC_COEFF_ITU656_V_B 8316
+#define LOGICVC_YUV_NORM 100000
+#define LOGICVC_COEFF_R_U 1402524
+#define LOGICVC_COEFF_R 179000000
+#define LOGICVC_COEFF_G_U 714403
+#define LOGICVC_COEFF_G_V 344340
+#define LOGICVC_COEFF_G 135000000
+#define LOGICVC_COEFF_B_V 1773049
+#define LOGICVC_COEFF_B 226000000
+#define LOGICVC_RGB_NORM 1000000
+
+enum xylonfb_layer_type {
+ LOGICVC_LAYER_RGB,
+ LOGICVC_LAYER_YUV,
+ LOGICVC_LAYER_ALPHA
+};
+
+enum xylonfb_alpha_type {
+ LOGICVC_ALPHA_LAYER,
+ LOGICVC_ALPHA_PIXEL,
+ LOGICVC_ALPHA_CLUT_16BPP,
+ LOGICVC_ALPHA_CLUT_32BPP
+};
+
+#endif /* __LOGICVC_H__ */
--
1.9.1
^ permalink raw reply related
* [RFC 06/10] video: fbdev: Xylon: Framebuffer driver kernel and make configuration
From: Davor Joja @ 2014-10-06 13:32 UTC (permalink / raw)
To: linux-fbdev
Driver kernel and build configuration.
Signed-off-by: Davor Joja <davorjoja@logicbricks.com>
---
drivers/video/fbdev/xylon/Kconfig | 40 ++++++++++++++++++++++++++++++++++++++
drivers/video/fbdev/xylon/Makefile | 3 +++
2 files changed, 43 insertions(+)
create mode 100644 drivers/video/fbdev/xylon/Kconfig
create mode 100644 drivers/video/fbdev/xylon/Makefile
diff --git a/drivers/video/fbdev/xylon/Kconfig b/drivers/video/fbdev/xylon/Kconfig
new file mode 100644
index 0000000..25ae308
--- /dev/null
+++ b/drivers/video/fbdev/xylon/Kconfig
@@ -0,0 +1,40 @@
+menuconfig FB_XYLON
+ tristate "Xylon logiCVC frame buffer support"
+ depends on FB
+ default n
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select VIDEOMODE_HELPERS
+ help
+ Choose this option if you want to use the Xylon logiCVC as frame
+ buffer device. Without the support of PCI & AGP.
+
+config FB_XYLON_PIXCLK
+ bool "Xylon logiCVC pixel clock"
+ depends on FB_XYLON
+ default n
+ help
+ logiCVC pixel clock generated from:
+ - External generator not controllable by Xylon framebuffer driver
+ This is default selection.
+ - Generators controllable by Xylon framebuffer driver
+
+config FB_XYLON_PIXCLK_LOGICLK
+ bool "Xylon logiCLK pixel clock generator"
+ depends on FB_XYLON && FB_XYLON_PIXCLK
+ default n
+ select COMMON_CLK_LOGICLK
+ help
+ Support for controlling pixel clock generation from
+ Xylon logiCLK FGPA IP core.
+
+config FB_XYLON_PIXCLK_SI570
+ bool "Si570 pixel clock generator"
+ depends on FB_XYLON && FB_XYLON_PIXCLK
+ default n
+ select I2C
+ select COMMON_CLK_SI570
+ help
+ Support for controlling pixel clock generation from
+ Si570 clock generator.
diff --git a/drivers/video/fbdev/xylon/Makefile b/drivers/video/fbdev/xylon/Makefile
new file mode 100644
index 0000000..3baa96a
--- /dev/null
+++ b/drivers/video/fbdev/xylon/Makefile
@@ -0,0 +1,3 @@
+xylonfb-y := xylonfb_main.o xylonfb_core.o xylonfb_ioctl.o xylonfb_pixclk.o
+
+obj-$(CONFIG_FB_XYLON) += xylonfb.o
--
1.9.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox