Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Belisko Marek @ 2013-10-11  8:59 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Dr. H. Nikolaus Schaller, Lars-Peter Clausen,
	Jean-Christophe PLAGNIOL-VILLARD, LKML,
	linux-omap@vger.kernel.org, linux-fbdev
In-Reply-To: <5257B420.10808@ti.com>

Hi Tomi,

On Fri, Oct 11, 2013 at 10:17 AM, Tomi Valkeinen <tomi.valkeinen@ti.com> wrote:
> On 11/10/13 10:42, Dr. H. Nikolaus Schaller wrote:
>
>> I am not sure if there is a SPI driver for a McBSP port [1]? And to make that
>> work (reliably) and tested it might need a lot of work for us. At least I think
>> such a change (e.g. setting up clock polarity etc.) is not done in some minutes.
>> And the only feedback we have from the panel is "does not work"/"works". I.e.
>> if we are not lucky that it works immediately we have no real means to debug.
>>
>> IMHO it also gives more flexibility to board designers to choose GPIOs instead
>> of enforcing some SPI interface by the driver (and encapsulate this arguable
>> protocol in the driver). Maybe some board has 3 spare GPIOs but neither
>> McBSPs nor McSPIs available.
>
> This has been an interesting thread, I've learnt a lot =).
>
> I still think the panel driver should not handle this, but there should
> be a separate spi bitbang driver for it.
>
> I understand you're not enthusiastic going that way, as the current
> version works for you. However, when using DT, we need to think how to
> represent the hardware in the device tree data, and it has to be right
> from the beginning.
>
> That's why I won't allow representing this panel as having 4 gpios in
> the DT data, because that is not correct. The panel has 3 pins. But
> then, the panel does allow reading, which could be implemented using 4
> gpios as you have done. This data should be in the spi-bitbang data, and
> the panel should just use the standard SPI framework.
I disagree. There are different drivers which pass in platform data
gpios (encoder-tfp410.c or encoder-tpd12s015.c)
and those must be covered by DT then. I cannot see problem why to have
for td028 panel 3 or 4 gpios defined in DT.
>
> Using SPI framework does not mean you should use McBSP or McSPI. It's up
> to you how the 3-wire SPI is implemented on the SoC side, the panel
> would just work in all the cases.
>
>  Tomi
>
>

BR,

marek

-- 
as simple and primitive as possible
-------------------------------------------------
Marek Belisko - OPEN-NANDRA
Freelance Developer

Ruska Nova Ves 219 | Presov, 08005 Slovak Republic
Tel: +421 915 052 184
skype: marekwhite
twitter: #opennandra
web: http://open-nandra.com

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Tomi Valkeinen @ 2013-10-11  8:17 UTC (permalink / raw)
  To: Dr. H. Nikolaus Schaller, Lars-Peter Clausen
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <5570E6D7-4F66-40BE-A50C-6CCA8074BD83@goldelico.com>

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

On 11/10/13 10:42, Dr. H. Nikolaus Schaller wrote:

> I am not sure if there is a SPI driver for a McBSP port [1]? And to make that
> work (reliably) and tested it might need a lot of work for us. At least I think
> such a change (e.g. setting up clock polarity etc.) is not done in some minutes.
> And the only feedback we have from the panel is "does not work"/"works". I.e.
> if we are not lucky that it works immediately we have no real means to debug.
> 
> IMHO it also gives more flexibility to board designers to choose GPIOs instead
> of enforcing some SPI interface by the driver (and encapsulate this arguable
> protocol in the driver). Maybe some board has 3 spare GPIOs but neither
> McBSPs nor McSPIs available.

This has been an interesting thread, I've learnt a lot =).

I still think the panel driver should not handle this, but there should
be a separate spi bitbang driver for it.

I understand you're not enthusiastic going that way, as the current
version works for you. However, when using DT, we need to think how to
represent the hardware in the device tree data, and it has to be right
from the beginning.

That's why I won't allow representing this panel as having 4 gpios in
the DT data, because that is not correct. The panel has 3 pins. But
then, the panel does allow reading, which could be implemented using 4
gpios as you have done. This data should be in the spi-bitbang data, and
the panel should just use the standard SPI framework.

Using SPI framework does not mean you should use McBSP or McSPI. It's up
to you how the 3-wire SPI is implemented on the SoC side, the panel
would just work in all the cases.

 Tomi



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

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Dr. H. Nikolaus Schaller @ 2013-10-11  7:42 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Tomi Valkeinen, Marek Belisko, plagnioj, linux-kernel, linux-omap,
	linux-fbdev
In-Reply-To: <5257A402.5030806@metafoo.de>

Hi Lars-Peter,
ah, I didn't see your mail while writing mine - so some overlap.

Am 11.10.2013 um 09:08 schrieb Lars-Peter Clausen:

> On 10/11/2013 06:41 AM, Tomi Valkeinen wrote:
>> On 10/10/13 21:58, Lars-Peter Clausen wrote:
>> 
>>> According to the datasheet the the panel as a dedicated dout pin. Maybe
>>> you did not connect it in your design, which means you won't be able to
>>> read any data from the panel at all.
>> 
>> I don't see a dedicated dout in the datasheet...
>> http://dl.wolfgang-draxinger.net/openmoko/doc/TD028TTEC1.pdf
> 
> Hm, ok, looks like the display controller used in the panel (the jbt6k74) has separate DIN and DOUT, but the panel only has one pin.

Yes

> But the datasheet for the panel is not exactly clear

Yes

> on whether it's DIN pin is both the DIN and DOUT pin from the controller or, just DIN and DOUT not being connected at all.

I think they have a 4-wire controller in the panel and tied the lines together.
This works if they properly control the data direction.
There are some read commands where this direction reversal happens.

> 
>> 
>>> Also your custom bitbang code looks a bit strange:
>>> 
>>>     gpio_set_value(data->dout_gpio, 1);
>>>     if (gpio_get_value(data->din_gpio) = 0)
>>>         return 1;
>>> 
>>> You set the state on the dout pin and then read the din pin, if both do
>>> not match you abort with an error. This suggest that, for whatever
>>> reason, you feed the dout pin back into the din pin in your design. Btw.
>>> this is also the only place where din is used in your driver.
>> 
>> Yes, he said the single "Serial interface data input/output" pin is
>> connected to both din and dout on the SoC. I guess the purpose of that
>> gpio_get_value() is to ensure that the panel is not pulling the line
>> when the SoC is writing to it. Not that I really understand how that can
>> work, but I'm not a HW guy =).
> 
> Hm, ok. I don't think the panel will ever actively drive the line.

Well it will drive it if we issue some read command (but I have no information
about these commands).

> So in my opinion using either the McBSP SPI master or the GPIO bitbang SPI master should work just fine. You just wouldn't be able to read any register from the device. But the driver is not attempting to do this, so it should be fine.

I am not sure if there is a SPI driver for a McBSP port [1]? And to make that
work (reliably) and tested it might need a lot of work for us. At least I think
such a change (e.g. setting up clock polarity etc.) is not done in some minutes.
And the only feedback we have from the panel is "does not work"/"works". I.e.
if we are not lucky that it works immediately we have no real means to debug.

IMHO it also gives more flexibility to board designers to choose GPIOs instead
of enforcing some SPI interface by the driver (and encapsulate this arguable
protocol in the driver). Maybe some board has 3 spare GPIOs but neither
McBSPs nor McSPIs available.

BR,
Nikolaus

http://lists.infradead.org/pipermail/linux-arm-kernel/2011-June/053064.html


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Dr. H. Nikolaus Schaller @ 2013-10-11  7:29 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Lars-Peter Clausen, Marek Belisko, plagnioj, linux-kernel,
	linux-omap, linux-fbdev
In-Reply-To: <5257815F.5070803@ti.com>

Hi all,

Am 11.10.2013 um 06:41 schrieb Tomi Valkeinen:

> On 10/10/13 21:58, Lars-Peter Clausen wrote:
> 
>> According to the datasheet the the panel as a dedicated dout pin. Maybe
>> you did not connect it in your design, which means you won't be able to
>> read any data from the panel at all.
> 
> I don't see a dedicated dout in the datasheet...
> http://dl.wolfgang-draxinger.net/openmoko/doc/TD028TTEC1.pdf
> 
>> Also your custom bitbang code looks a bit strange:
>> 
>>    gpio_set_value(data->dout_gpio, 1);
>>    if (gpio_get_value(data->din_gpio) = 0)
>>        return 1;
>> 
>> You set the state on the dout pin and then read the din pin, if both do
>> not match you abort with an error. This suggest that, for whatever
>> reason,

Detecting hardware failure.

If the panel is not connected or broken (short circuit) reading back
may fail and this can help to detect a hardware failure.

>> you feed the dout pin back into the din pin in your design. Btw.
>> this is also the only place where din is used in your driver.

Ok, I see the issue with that. It assumes this type of feedback which does
not necessarily exist. I.e. if someone else doesn't have this feedback
line the driver will not work.

So a more general solution should work with any setup, even if no
din gpio has been defined. I.e. do this test only if the din_gpio exists.

The main reason that we don't use din_gpio elsewhere is that we have
no jbt_read() function because we lack information what we can
read from the panel controller chip - and have not seen a use-case
for that.

And thanks to your hint, I think either returning 1 is wrong (should be
some -EIO or similar). Or the return r; at the end of td028ttec1_panel_enable
is wrong (should be return rc ? -EIO : 0; ).

We will check that.

> Yes, he said the single "Serial interface data input/output" pin is
> connected to both din and dout on the SoC.

I have found two public links which may describe what we do:

The panel data sheet [1], Section 9 describes the interface as
"3 wire serial interface".

It sometimes calls the data lines DIN and DOUT and describes them
separately, i.e. like a 4 wire interface.

So I guess the jbt6k chip has indeed 4 wires, but (as you said),

din/dout are lines are tied together on the panel side to save
(share) one wire in the flex cable.
 
[2] shows in figure 2 an example how to connect a 3-wire interface
to a 4-wire SPI SoC. This is the way we have done it.


>  I guess the purpose of that
> gpio_get_value() is to ensure that the panel is not pulling the line
> when the SoC is writing to it. Not that I really understand how that can
> work, but I'm not a HW guy =).

Finally, I have checked one detail which rules out to use a standard
SPI driver for OMAP on our board. The reason is that we have a McBSP
port of the OMAP3 and not a McSPI. So we have to run this "3 wire
serial interface protocol" by bitbanging.

IMHO, the most flexible way to hook up the panel (and driver) to arbitrary
hardware is by using GPIOs and not having the panel driver restrict to a
SPI interface.

And having the bitbanging encapsulated in the driver itself (instead on
relying on some spi-bitbang) makes it less dependent on changes
somewhere else. I.e. the driver module is indeed a module.

Marek is preparing an updated patch for further discussion.

BR,
Nikolaus


[1]: http://dl.wolfgang-draxinger.net/openmoko/doc/TD028TTEC1.pdf
[2]: http://www.totalphase.com/support/kb/10059/


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Lars-Peter Clausen @ 2013-10-11  7:08 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Dr. H. Nikolaus Schaller, Marek Belisko, plagnioj, linux-kernel,
	linux-omap, linux-fbdev
In-Reply-To: <5257815F.5070803@ti.com>

On 10/11/2013 06:41 AM, Tomi Valkeinen wrote:
> On 10/10/13 21:58, Lars-Peter Clausen wrote:
>
>> According to the datasheet the the panel as a dedicated dout pin. Maybe
>> you did not connect it in your design, which means you won't be able to
>> read any data from the panel at all.
>
> I don't see a dedicated dout in the datasheet...
> http://dl.wolfgang-draxinger.net/openmoko/doc/TD028TTEC1.pdf

Hm, ok, looks like the display controller used in the panel (the jbt6k74) has 
separate DIN and DOUT, but the panel only has one pin. But the datasheet for 
the panel is not exactly clear on whether it's DIN pin is both the DIN and DOUT 
pin from the controller or, just DIN and DOUT not being connected at all.

>
>> Also your custom bitbang code looks a bit strange:
>>
>>      gpio_set_value(data->dout_gpio, 1);
>>      if (gpio_get_value(data->din_gpio) = 0)
>>          return 1;
>>
>> You set the state on the dout pin and then read the din pin, if both do
>> not match you abort with an error. This suggest that, for whatever
>> reason, you feed the dout pin back into the din pin in your design. Btw.
>> this is also the only place where din is used in your driver.
>
> Yes, he said the single "Serial interface data input/output" pin is
> connected to both din and dout on the SoC. I guess the purpose of that
> gpio_get_value() is to ensure that the panel is not pulling the line
> when the SoC is writing to it. Not that I really understand how that can
> work, but I'm not a HW guy =).

Hm, ok. I don't think the panel will ever actively drive the line. So in my 
opinion using either the McBSP SPI master or the GPIO bitbang SPI master should 
work just fine. You just wouldn't be able to read any register from the device. 
But the driver is not attempting to do this, so it should be fine.

- Lars

^ permalink raw reply

* Re: [PATCH/RFC v3 00/19] Common Display Framework
From: Tomi Valkeinen @ 2013-10-11  6:37 UTC (permalink / raw)
  To: Andrzej Hajda, Laurent Pinchart
  Cc: linux-fbdev, dri-devel, Jesse Barnes, Benjamin Gaignard, Tom Gall,
	Kyungmin Park, linux-media, Stephen Warren, Mark Zhang,
	Alexandre Courbot, Ragesh Radhakrishnan, Thomas Petazzoni,
	Sunil Joshi, Maxime Ripard, Vikas Sajjan, Marcus Lorentzon
In-Reply-To: <52556370.1050102@samsung.com>

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

On 09/10/13 17:08, Andrzej Hajda wrote:

> As I have adopted existing internal driver for MIPI-DSI bus, I did not
> take too much
> care for DT. You are right, 'bta-timeout' is a configuration parameter
> (however its
> minimal value is determined by characteristic of the DSI-slave). On the
> other
> side currently there is no good place for such configuration parameters
> AFAIK.

The minimum bta-timeout should be deducable from the DSI bus speed,
shouldn't it? Thus there's no need to define it anywhere.

>> - enable_hs and enable_te, used to enable/disable HS mode and
>> tearing-elimination
> 
> It seems there should be a way to synchronize TE signal with panel,
> in case signal is provided only to dsi-master. Some callback I suppose?
> Or transfer synchronization should be done by dsi-master.

Hmm, can you explain a bit what you mean?

Do you mean that the panel driver should get a callback when DSI TE
trigger happens?

On OMAP, when using DSI TE trigger, the dsi-master does it all. So the
panel driver just calls update() on the dsi-master, and then the
dsi-master will wait for TE, and then start the transfer. There's also a
callback to the panel driver when the transfer has completed.

>> - set_max_rx_packet_size, used to configure the max rx packet size.
> Similar callbacks should be added to mipi-dsi-bus ops as well, to
> make it complete/generic.

Do you mean the same calls should exist both in the mipi-dbi-bus ops and
on the video ops? If they are called with different values, which one
"wins"?

>> http://article.gmane.org/gmane.comp.video.dri.devel/90651
>> http://article.gmane.org/gmane.comp.video.dri.devel/91269
>> http://article.gmane.org/gmane.comp.video.dri.devel/91272
>>
>> I still think that it's best to consider DSI and DBI as a video bus (not
>> as a separate video bus and a control bus), and provide the packet
>> transfer methods as part of the video ops.
> I have read all posts regarding this issue and currently I tend
> to solution where CDF is used to model only video streams,
> with control bus implemented in different framework.
> The only concerns I have if we should use Linux bus for that.

Ok. I have many other concerns, as I've expressed in the mails =). I
still don't see how it could work. So I'd very much like to see a more
detailed explanation how the separate control & video bus approach would
deal with different scenarios.

Let's consider a DSI-to-HDMI encoder chip. Version A of the chip is
controlled via DSI, version B is controlled via i2c. As the output of
the chip goes to HDMI connector, the DSI bus speed needs to be set
according to the resolution of the HDMI monitor.

So, with version A, the encoder driver would have some kind of pointers
to ctrl_ops and video_ops (or, pointers to dsi_bus instance and
video_bus instance), right? The ctrl_ops would need to have ops like
set_bus_speed, enable_hs, etc, to configure the DSI bus.

When the encoder driver is started, it'd probably set some safe bus
speed, configure the encoder a bit, read the EDID, enable HS,
re-configure the bus speed to match the monitor's video mode, configure
the encoder, and at last enable the video stream.

Version B would have i2c_client and video_ops. When the driver starts,
it'd  probably do the same things as above, except the control messages
would go through i2c. That means that setting the bus speed, enabling
HS, etc, would happen through video_ops, as the i2c side has no
knowledge of the DSI side, right? Would there be identical ops on both
DSI ctrl and video ops?

That sounds very bad. What am I missing here? How would it work?

And, if we want to separate the video and control, I see no reason to
explicitly require the video side to be present. I.e. we could as well
have a DSI peripheral that has only the control bus used. How would that
reflect to, say, the DT presentation? Say, if we have a version A of the
encoder, we could have DT data like this (just a rough example):

soc-dsi {
	encoder {
		input: endpoint {
			remote-endpoint = <&soc-dsi-ep>;
			/* configuration for the DSI lanes */
			dsi-lanes = <0 1 2 3 4 5>;
		};
	};
};

So the encoder would be places inside the SoC's DSI node, similar to how
an i2c device would be placed inside SoC's i2c node. DSI configuration
would be inside the video endpoint data.

Version B would be almost the same:

&i2c0 {
	encoder {
		input: endpoint {
			remote-endpoint = <&soc-dsi-ep>;
			/* configuration for the DSI lanes */
			dsi-lanes = <0 1 2 3 4 5>;
		};
	};
};

Now, how would the video-bus-less device be defined? It'd be inside the
soc-dsi node, that's clear. Where would the DSI lane configuration be?
Not inside 'endpoint' node, as that's for video and wouldn't exist in
this case. Would we have the same lane configuration in two places, once
for video and once for control?

I agree that having DSI/DBI control and video separated would be
elegant. But I'd like to hear what is the technical benefit of that? At
least to me it's clearly more complex to separate them than to keep them
together (to the extent that I don't yet see how it is even possible),
so there must be a good reason for the separation. I don't understand
that reason. What is it?

 Tomi



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

^ permalink raw reply

* Re: [PATCH v3 1/3] video: xilinxfb: Use standard variable name convention
From: Tomi Valkeinen @ 2013-10-11  5:58 UTC (permalink / raw)
  To: Michal Simek
  Cc: linux-kernel, monstr, joe, Jean-Christophe Plagniol-Villard,
	Grant Likely, Rob Herring, linux-fbdev, devicetree
In-Reply-To: <631df9a44b366af4129d00f1d4e1d3baad7d4903.1381386616.git.michal.simek@xilinx.com>

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

On 10/10/13 09:30, Michal Simek wrote:
> s/op/pdev/ in xilinxfb_of_probe().
> No functional chagnes.
> 
> Signed-off-by: Michal Simek <michal.simek@xilinx.com>
> ---
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/video/xilinxfb.c | 18 +++++++++---------
>  1 file changed, 9 insertions(+), 9 deletions(-)

Thanks, queued this series for 3.13.

 Tomi



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

^ permalink raw reply

* Re: linux-next: manual merge of the omap_dss2 tree
From: Tomi Valkeinen @ 2013-10-11  5:18 UTC (permalink / raw)
  To: Mark Brown, Jean-Christophe PLAGNIOL-VILLARD
  Cc: Archit Taneja, linux-omap, linux-fbdev, linux-next, linux-kernel,
	Thierry Reding
In-Reply-To: <20131010175020.GS21581@sirena.org.uk>

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

Hi Mark, Jean-Christophe,

On 10/10/13 20:50, Mark Brown wrote:
> Today's linux-next merge of the omap_dss2 tree got conflicts in
> drivers/video/omap2/dss/hdmi4_core.[ch] caused by ef26958a (omapdss:
> HDMI: Rename hdmi driver files to nicer names) interacting with a range
> of commits from Ricardo Neri in the fbdev tree and possibly some other
> stuff.  git is unfortunately not giving me a useful diff right now :/

Jean-Christophe has slightly different versions of my patches in his
fbdev for-next branch, so they conflict with my updated versions.

Jean-Christophe, I expected this to happen with the current way of you
having a copy of my for-next branch in yours. Can I now take your
atmel_lcdfb patches to my for-next, and you'll remove all patches from
your for-next?

 Tomi



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

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Tomi Valkeinen @ 2013-10-11  4:41 UTC (permalink / raw)
  To: Lars-Peter Clausen, Dr. H. Nikolaus Schaller
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <5256F8DF.2020801@metafoo.de>

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

On 10/10/13 21:58, Lars-Peter Clausen wrote:

> According to the datasheet the the panel as a dedicated dout pin. Maybe
> you did not connect it in your design, which means you won't be able to
> read any data from the panel at all.

I don't see a dedicated dout in the datasheet...
http://dl.wolfgang-draxinger.net/openmoko/doc/TD028TTEC1.pdf

> Also your custom bitbang code looks a bit strange:
> 
>     gpio_set_value(data->dout_gpio, 1);
>     if (gpio_get_value(data->din_gpio) == 0)
>         return 1;
> 
> You set the state on the dout pin and then read the din pin, if both do
> not match you abort with an error. This suggest that, for whatever
> reason, you feed the dout pin back into the din pin in your design. Btw.
> this is also the only place where din is used in your driver.

Yes, he said the single "Serial interface data input/output" pin is
connected to both din and dout on the SoC. I guess the purpose of that
gpio_get_value() is to ensure that the panel is not pulling the line
when the SoC is writing to it. Not that I really understand how that can
work, but I'm not a HW guy =).

 Tomi



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

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Lars-Peter Clausen @ 2013-10-10 18:58 UTC (permalink / raw)
  To: Dr. H. Nikolaus Schaller
  Cc: Tomi Valkeinen, Marek Belisko, plagnioj, linux-kernel, linux-omap,
	linux-fbdev
In-Reply-To: <A7739B52-2632-4DD8-924D-B657A6BFE3ED@goldelico.com>

On 10/10/2013 03:42 PM, Dr. H. Nikolaus Schaller wrote:
>
> Am 10.10.2013 um 14:26 schrieb Lars-Peter Clausen:
>
>> On 10/10/2013 02:13 PM, Tomi Valkeinen wrote:
>>> On 10/10/13 14:52, Dr. H. Nikolaus Schaller wrote:
>>>
>>>> Yes, I agree and I am willing to help if someone comes up with such a SoC.
>>>> At the moment we have connected it to the OMAP3 only.
>>>
>>> True, but even without that kind of SoC, SPI bitbanging should be
>>> handled by an SPI driver, not by the drivers that use the bus.
>>>
>>>> I.e. I want not to do a lot of work for others who we just guess about that they
>>>> might exist...
>>>
>>> Yep. It's fine for me, it's not that much extra code in the panel driver.
>>>
>>>>> The panel hardware has three wires, so the panel driver (if it does
>>>>> handle the bus by bitbanging) can only refer to three gpios.
>>>>
>>>> Hm. The panle hardware has 3 but the SoC (OMAP3) the driver
>>>> is running on has 4.
>>>
>>> Right, but this panel driver is a driver for the panel hardware. Not for
>>> the SoC, or the SoC+panel combination. So the panel driver must only use
>>> resources as seen by the panel hardware.
>>>
>>>>> So either
>>>>> the bus details should be hidden by using the normal spi framework, or
>>>>> if the driver does the bitbanging, use the gpios as specified in the
>>>>> panel spec. The panel driver cannot contain any board specific stuff.
>>>>
>>>> The bitbang driver shown below can handle either 3 or 4 gpios (except
>>>> for initialization).
>>>
>>> It's not a bitbang driver, it's a panel driver. And anyway, if I
>>> understood right, your use of 4 gpios was just a hack to try to make it
>>> look like a normal 4-wire SPI bus. What you really have is 3 wires, 3
>>> gpios. I don't see any reason to use 4 gpios, as two of them are the same.
>
> There is a protection resistor in the one defined as "output" so that
> protocol errors don't have two outputs work against each other.
>
>>>
>>> Hmm, how does it work anyway. Did I understand it right, the panel's
>>> 'DIN' pin is connected to two gpios on the SoC, and one of those gpios
>>> is set as output, and the other as input?
>
> Yes.
>
>>> So the SoC is always pulling
>>> that line up or down, and the panel is also pulling it up or down when
>>> it's sending data. I'm no HW guy but that sounds quite bad =).
>
> Yes, that is the strange thing with this protocol. It does a direction reversal
> after some time. I.e. the panel switches its input to output and the SoC has
> to be fast enough to do the same. Therefore, we have one output GPIO
> (protected by a resistor) and a separate input GPIO.
>
> Sometimes interfacing hardware is indeed strange.
>
>>>
>>> I've never written or studied a bitbanging driver, but shouldn't there
>>> be just one gpio used on the SoC for DIN, and it would be set to either
>>> output or input mode, depending on if we are reading or writing?
>>
>> Back in the OpenMoko days we used the panel in normal 4-wire SPI mode with
>> the GPIO bitbang SPI master. The bitbang code in this driver also looks like
>> normal 4 wire.
>
>
> Thanks for this hint!
>
> Maybe using a standard 4-wire SPI simply works if we only write data and
> make sure the panel is not sending anything...

According to the datasheet the the panel as a dedicated dout pin. Maybe you did 
not connect it in your design, which means you won't be able to read any data 
from the panel at all.

Also your custom bitbang code looks a bit strange:

	gpio_set_value(data->dout_gpio, 1);
	if (gpio_get_value(data->din_gpio) = 0)
		return 1;

You set the state on the dout pin and then read the din pin, if both do not 
match you abort with an error. This suggest that, for whatever reason, you feed 
the dout pin back into the din pin in your design. Btw. this is also the only 
place where din is used in your driver.

- Lars

^ permalink raw reply

* linux-next: manual merge of the omap_dss2 tree
From: Mark Brown @ 2013-10-10 17:50 UTC (permalink / raw)
  To: Archit Taneja, Tomi Valkeinen, Ricardo Neri
  Cc: linux-omap, linux-fbdev, linux-next, linux-kernel, Thierry Reding

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

Today's linux-next merge of the omap_dss2 tree got conflicts in
drivers/video/omap2/dss/hdmi4_core.[ch] caused by ef26958a (omapdss:
HDMI: Rename hdmi driver files to nicer names) interacting with a range
of commits from Ricardo Neri in the fbdev tree and possibly some other
stuff.  git is unfortunately not giving me a useful diff right now :/

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

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Dr. H. Nikolaus Schaller @ 2013-10-10 13:42 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Tomi Valkeinen, Marek Belisko, plagnioj, linux-kernel, linux-omap,
	linux-fbdev
In-Reply-To: <52569CE8.5070201@metafoo.de>


Am 10.10.2013 um 14:26 schrieb Lars-Peter Clausen:

> On 10/10/2013 02:13 PM, Tomi Valkeinen wrote:
>> On 10/10/13 14:52, Dr. H. Nikolaus Schaller wrote:
>> 
>>> Yes, I agree and I am willing to help if someone comes up with such a SoC.
>>> At the moment we have connected it to the OMAP3 only.
>> 
>> True, but even without that kind of SoC, SPI bitbanging should be
>> handled by an SPI driver, not by the drivers that use the bus.
>> 
>>> I.e. I want not to do a lot of work for others who we just guess about that they
>>> might exist...
>> 
>> Yep. It's fine for me, it's not that much extra code in the panel driver.
>> 
>>>> The panel hardware has three wires, so the panel driver (if it does
>>>> handle the bus by bitbanging) can only refer to three gpios.
>>> 
>>> Hm. The panle hardware has 3 but the SoC (OMAP3) the driver
>>> is running on has 4.
>> 
>> Right, but this panel driver is a driver for the panel hardware. Not for
>> the SoC, or the SoC+panel combination. So the panel driver must only use
>> resources as seen by the panel hardware.
>> 
>>>> So either
>>>> the bus details should be hidden by using the normal spi framework, or
>>>> if the driver does the bitbanging, use the gpios as specified in the
>>>> panel spec. The panel driver cannot contain any board specific stuff.
>>> 
>>> The bitbang driver shown below can handle either 3 or 4 gpios (except
>>> for initialization).
>> 
>> It's not a bitbang driver, it's a panel driver. And anyway, if I
>> understood right, your use of 4 gpios was just a hack to try to make it
>> look like a normal 4-wire SPI bus. What you really have is 3 wires, 3
>> gpios. I don't see any reason to use 4 gpios, as two of them are the same.

There is a protection resistor in the one defined as "output" so that
protocol errors don't have two outputs work against each other.

>> 
>> Hmm, how does it work anyway. Did I understand it right, the panel's
>> 'DIN' pin is connected to two gpios on the SoC, and one of those gpios
>> is set as output, and the other as input?

Yes.

>> So the SoC is always pulling
>> that line up or down, and the panel is also pulling it up or down when
>> it's sending data. I'm no HW guy but that sounds quite bad =).

Yes, that is the strange thing with this protocol. It does a direction reversal
after some time. I.e. the panel switches its input to output and the SoC has
to be fast enough to do the same. Therefore, we have one output GPIO
(protected by a resistor) and a separate input GPIO.

Sometimes interfacing hardware is indeed strange.

>> 
>> I've never written or studied a bitbanging driver, but shouldn't there
>> be just one gpio used on the SoC for DIN, and it would be set to either
>> output or input mode, depending on if we are reading or writing?
> 
> Back in the OpenMoko days we used the panel in normal 4-wire SPI mode with
> the GPIO bitbang SPI master. The bitbang code in this driver also looks like
> normal 4 wire.


Thanks for this hint!

Maybe using a standard 4-wire SPI simply works if we only write data and
make sure the panel is not sending anything...

I still hesitate to break working code because it needs quite some time to debug
and we don't even know if it ever works, i.e. sends us to swampy ground...

BR,
Nikolaus



^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Lars-Peter Clausen @ 2013-10-10 12:26 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Dr. H. Nikolaus Schaller, Marek Belisko, plagnioj, linux-kernel,
	linux-omap, linux-fbdev
In-Reply-To: <525699EF.6090803@ti.com>

On 10/10/2013 02:13 PM, Tomi Valkeinen wrote:
> On 10/10/13 14:52, Dr. H. Nikolaus Schaller wrote:
> 
>> Yes, I agree and I am willing to help if someone comes up with such a SoC.
>> At the moment we have connected it to the OMAP3 only.
> 
> True, but even without that kind of SoC, SPI bitbanging should be
> handled by an SPI driver, not by the drivers that use the bus.
> 
>> I.e. I want not to do a lot of work for others who we just guess about that they
>> might exist...
> 
> Yep. It's fine for me, it's not that much extra code in the panel driver.
> 
>>> The panel hardware has three wires, so the panel driver (if it does
>>> handle the bus by bitbanging) can only refer to three gpios.
>>
>> Hm. The panle hardware has 3 but the SoC (OMAP3) the driver
>> is running on has 4.
> 
> Right, but this panel driver is a driver for the panel hardware. Not for
> the SoC, or the SoC+panel combination. So the panel driver must only use
> resources as seen by the panel hardware.
> 
>>> So either
>>> the bus details should be hidden by using the normal spi framework, or
>>> if the driver does the bitbanging, use the gpios as specified in the
>>> panel spec. The panel driver cannot contain any board specific stuff.
>>
>> The bitbang driver shown below can handle either 3 or 4 gpios (except
>> for initialization).
> 
> It's not a bitbang driver, it's a panel driver. And anyway, if I
> understood right, your use of 4 gpios was just a hack to try to make it
> look like a normal 4-wire SPI bus. What you really have is 3 wires, 3
> gpios. I don't see any reason to use 4 gpios, as two of them are the same.
> 
> Hmm, how does it work anyway. Did I understand it right, the panel's
> 'DIN' pin is connected to two gpios on the SoC, and one of those gpios
> is set as output, and the other as input? So the SoC is always pulling
> that line up or down, and the panel is also pulling it up or down when
> it's sending data. I'm no HW guy but that sounds quite bad =).
> 
> I've never written or studied a bitbanging driver, but shouldn't there
> be just one gpio used on the SoC for DIN, and it would be set to either
> output or input mode, depending on if we are reading or writing?

Back in the OpenMoko days we used the panel in normal 4-wire SPI mode with
the GPIO bitbang SPI master. The bitbang code in this driver also looks like
normal 4 wire.

- Lars


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Tomi Valkeinen @ 2013-10-10 12:13 UTC (permalink / raw)
  To: Dr. H. Nikolaus Schaller
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <04AC7291-38E4-421B-979C-BF03D52E94BD@goldelico.com>

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

On 10/10/13 14:52, Dr. H. Nikolaus Schaller wrote:

> Yes, I agree and I am willing to help if someone comes up with such a SoC.
> At the moment we have connected it to the OMAP3 only.

True, but even without that kind of SoC, SPI bitbanging should be
handled by an SPI driver, not by the drivers that use the bus.

> I.e. I want not to do a lot of work for others who we just guess about that they
> might exist...

Yep. It's fine for me, it's not that much extra code in the panel driver.

>> The panel hardware has three wires, so the panel driver (if it does
>> handle the bus by bitbanging) can only refer to three gpios.
> 
> Hm. The panle hardware has 3 but the SoC (OMAP3) the driver
> is running on has 4.

Right, but this panel driver is a driver for the panel hardware. Not for
the SoC, or the SoC+panel combination. So the panel driver must only use
resources as seen by the panel hardware.

>> So either
>> the bus details should be hidden by using the normal spi framework, or
>> if the driver does the bitbanging, use the gpios as specified in the
>> panel spec. The panel driver cannot contain any board specific stuff.
> 
> The bitbang driver shown below can handle either 3 or 4 gpios (except
> for initialization).

It's not a bitbang driver, it's a panel driver. And anyway, if I
understood right, your use of 4 gpios was just a hack to try to make it
look like a normal 4-wire SPI bus. What you really have is 3 wires, 3
gpios. I don't see any reason to use 4 gpios, as two of them are the same.

Hmm, how does it work anyway. Did I understand it right, the panel's
'DIN' pin is connected to two gpios on the SoC, and one of those gpios
is set as output, and the other as input? So the SoC is always pulling
that line up or down, and the panel is also pulling it up or down when
it's sending data. I'm no HW guy but that sounds quite bad =).

I've never written or studied a bitbanging driver, but shouldn't there
be just one gpio used on the SoC for DIN, and it would be set to either
output or input mode, depending on if we are reading or writing?

 Tomi



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

^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Dr. H. Nikolaus Schaller @ 2013-10-10 11:52 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <52568B40.4040802@ti.com>

Hi Tomi,

Am 10.10.2013 um 13:10 schrieb Tomi Valkeinen:

> On 10/10/13 12:34, Dr. H. Nikolaus Schaller wrote:
>> Hi Tomi,
>> 
>> Am 10.10.2013 um 10:19 schrieb Tomi Valkeinen:
>> 
>>> Hi,
>>> 
>>> On 10/10/13 00:08, Marek Belisko wrote:
>>>> For communicating with driver is used gpio bitbanging because TD028 does
>>>> not have a standard compliant SPI interface. It is a 3-wire thing with
>>>> direction reversal.
>>> 
>>> Isn't that SPI_3WIRE?
>> 
>> Maybe - but we have no complete datasheet and I don't know an official spec of
>> a 3-wire SPI protocol to compare how 3-wire should work.
> 
> Yep, and I know only what I read on wikipedia a few hours ago =).
> 
>> And I think (but I may be wrong) that SPI_3WIRE is an optional feature of the SoC
>> specific SPI drivers and in my understanding the OMAP has no such driver:
>> 
>> http://lxr.free-electrons.com/source/drivers/spi/spi-omap2-mcspi.c#L874
>> 
>> And spi-bitbang.c hasn't either.
>> 
>> So I would prefer to leave this as an area for optimizations for future work.
>> Maybe we can add some more comments on this.
> 
> Ok. Well, I guess it's not too bad. But it's really not something that
> should be in the panel driver (assuming it's "standard" 3-wire SPI).
> Some SoC could support 3-wire SPI, and then it'd be good to use the SoCs
> hardware for SPI communication. Having bitbanging hardcoded into the
> driver will prevent that.

Yes, I agree and I am willing to help if someone comes up with such a SoC.
At the moment we have connected it to the OMAP3 only.

I.e. I want not to do a lot of work for others who we just guess about that they
might exist...

> 
>>>> +	int cs_gpio;
>>>> +	int scl_gpio;
>>>> +	int din_gpio;
>>>> +	int dout_gpio;
>>> 
>>> Three wires, but four gpios? What am I missing here...
>> 
>> We have wired up the 3-wire SPI interface of the display
>> to 4 wires of the McSPI interface because we initially thought
>> that it could work in 4 wire mode as well.
>> 
>> The next step we did was to port the driver code from the
>> Openmoko Neo1973 U-Boot which used the gpio-bitbang
>> mechanism.
>> 
>> Since it worked and is used only for enabling/disabling the
>> display and for no other purpose we never felt it important
>> to check or modify the code to use SPI.
>> 
>> But you are right - we don't use the din_gpio really since
>> we never read registers from the chip. So 3 gpios could be
>> sufficient.
>> 
>> Or we must handle the case that din_gpio = dout_gpio
>> in panel_probe to avoid duplicate use of the same gpio.
> 
> The panel hardware has three wires, so the panel driver (if it does
> handle the bus by bitbanging) can only refer to three gpios.

Hm. The panle hardware has 3 but the SoC (OMAP3) the driver
is running on has 4.

> So either
> the bus details should be hidden by using the normal spi framework, or
> if the driver does the bitbanging, use the gpios as specified in the
> panel spec. The panel driver cannot contain any board specific stuff.

The bitbang driver shown below can handle either 3 or 4 gpios (except
for initialization).

> 
>>>> +static int jbt_spi_xfer(struct panel_drv_data *data, int wordnum, int bitlen)
>>> 
>>> Hmm, if it's not SPI, maybe it shouldn't be called SPI?
>> 
>> Yes, we can remove the _spi_ in this name.
> 
> Or maybe use "spi3w" or such, just to make it clear it's not plain
> standard spi. Again, presuming it's really 3-wire spi =).
> 
>>>> +static int td028ttec1_panel_probe(struct platform_device *pdev)
>>>> +{
>>>> +	struct panel_drv_data *ddata;
>>>> +	struct omap_dss_device *dssdev;
>>>> +	int r;
>>>> +
>>>> +	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
>>>> +	if (ddata = NULL)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	platform_set_drvdata(pdev, ddata);
>>>> +
>>>> +	if (dev_get_platdata(&pdev->dev)) {
>>>> +		r = td028ttec1_panel_probe_pdata(pdev);
>>>> +		if (r)
>>>> +			return r;
>>>> +	} else {
>>>> +		return -ENODEV;
>>>> +	}
>>>> +
>>>> +	if (gpio_is_valid(ddata->cs_gpio)) {
>>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->cs_gpio,
>>>> +				GPIOF_OUT_INIT_HIGH, "lcd cs");
>>>> +		if (r)
>>>> +			goto err_gpio;
>>>> +	}
>>>> +
>>>> +	if (gpio_is_valid(ddata->scl_gpio)) {
>>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->scl_gpio,
>>>> +				GPIOF_OUT_INIT_HIGH, "lcd scl");
>>>> +		if (r)
>>>> +			goto err_gpio;
>>>> +	}
>>>> +
>>>> +	if (gpio_is_valid(ddata->dout_gpio)) {
>>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->dout_gpio,
>>>> +				GPIOF_OUT_INIT_LOW, "lcd dout");
>>>> +		if (r)
>>>> +			goto err_gpio;
>>>> +	}
>>>> +
>>>> +	if (gpio_is_valid(ddata->din_gpio)) {
>>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->din_gpio,
>>>> +				GPIOF_IN, "lcd din");
>>>> +		if (r)
>>>> +			goto err_gpio;
>>>> +	}
>>>> +
>>>> +	/* according to data sheet: wait 50ms (Tpos of LCM). However, 50ms
>>>> +	 * seems unreliable with later LCM batches, increasing to 90ms */
>>>> +	mdelay(90);
>>> 
>>> What is this delay for? Usually sleeps/delays should be between two "HW
>>> actions". Here there's nothing below this line that would count as such
>>> an action.
>> 
>> I guess that this delay is intended to power on the display first, then wait some
> 
> I'm not very comfortable with merging a driver, when the driver author
> guesses what parts of the driver do =).

Well, that is the problem with some badly documented chips. We have "inherited"
that from the Openmoko project and I think those guys did have access to some
more precise data sheets but we don't. You can't even find google results for this
strange jbt chip.

It works on all our GTA04 devices for quite a long time (using the old display API).
We just recently upgraded to display-new.

> 
>> time and after that delay enable the DSS clocks and signals and make the
>> controller chip in the panel initialize.
> 
> I don't see the code before the delay doing any power up, it just sets
> the data bus gpios to certain state. Do those gpio initializations power
> up the panel?

No, and they don't need to. The enable_power code is not implemented because
we have no dedicated regulator (but one shared and enabled early in the kernel).

It should be quite straightforward to add it to enable/disable if we need it.

We may want to change this in the next hardware revision, but on the other hand
do not want to postpone upstreaming until we have that, because an upstreamed
driver will help GTA04 users of today.

> 
>> But this of course depends on the order the DSS components are probed
>> and enabled and how power is controlled (we don't interrupt power at all).
>> 
>> I think we can move this delay (sleep) to the beginning of 
>> 
>> td028ttec1_panel_enable() before /* three times command zero */
>> 
>> to make sure that a freshly powered display has enough time to do its
>> internal reset and initializations before registers are programmed.
> 
> The probe function should not enable the panel (well, it can enable the
> panel, but only to read an ID or such and then disable it before exiting
> probe).
> 
> So after probe, the panel should be as dead as possible, power disabled
> etc. All the powering up should happen in enable().

Yes, I agree. And threrefore the safety-delay should indeed be in enable().

BR, Nikolaus


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Tomi Valkeinen @ 2013-10-10 11:10 UTC (permalink / raw)
  To: Dr. H. Nikolaus Schaller
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <7FAC77D4-5E1E-4B11-B067-3A8233C69240@goldelico.com>

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

On 10/10/13 12:34, Dr. H. Nikolaus Schaller wrote:
> Hi Tomi,
> 
> Am 10.10.2013 um 10:19 schrieb Tomi Valkeinen:
> 
>> Hi,
>>
>> On 10/10/13 00:08, Marek Belisko wrote:
>>> For communicating with driver is used gpio bitbanging because TD028 does
>>> not have a standard compliant SPI interface. It is a 3-wire thing with
>>> direction reversal.
>>
>> Isn't that SPI_3WIRE?
> 
> Maybe - but we have no complete datasheet and I don't know an official spec of
> a 3-wire SPI protocol to compare how 3-wire should work.

Yep, and I know only what I read on wikipedia a few hours ago =).

> And I think (but I may be wrong) that SPI_3WIRE is an optional feature of the SoC
> specific SPI drivers and in my understanding the OMAP has no such driver:
> 
> http://lxr.free-electrons.com/source/drivers/spi/spi-omap2-mcspi.c#L874
> 
> And spi-bitbang.c hasn't either.
> 
> So I would prefer to leave this as an area for optimizations for future work.
> Maybe we can add some more comments on this.

Ok. Well, I guess it's not too bad. But it's really not something that
should be in the panel driver (assuming it's "standard" 3-wire SPI).
Some SoC could support 3-wire SPI, and then it'd be good to use the SoCs
hardware for SPI communication. Having bitbanging hardcoded into the
driver will prevent that.

>>> +	int cs_gpio;
>>> +	int scl_gpio;
>>> +	int din_gpio;
>>> +	int dout_gpio;
>>
>> Three wires, but four gpios? What am I missing here...
> 
> We have wired up the 3-wire SPI interface of the display
> to 4 wires of the McSPI interface because we initially thought
> that it could work in 4 wire mode as well.
> 
> The next step we did was to port the driver code from the
> Openmoko Neo1973 U-Boot which used the gpio-bitbang
> mechanism.
> 
> Since it worked and is used only for enabling/disabling the
> display and for no other purpose we never felt it important
> to check or modify the code to use SPI.
> 
> But you are right - we don't use the din_gpio really since
> we never read registers from the chip. So 3 gpios could be
> sufficient.
> 
> Or we must handle the case that din_gpio == dout_gpio
> in panel_probe to avoid duplicate use of the same gpio.

The panel hardware has three wires, so the panel driver (if it does
handle the bus by bitbanging) can only refer to three gpios. So either
the bus details should be hidden by using the normal spi framework, or
if the driver does the bitbanging, use the gpios as specified in the
panel spec. The panel driver cannot contain any board specific stuff.

>>> +static int jbt_spi_xfer(struct panel_drv_data *data, int wordnum, int bitlen)
>>
>> Hmm, if it's not SPI, maybe it shouldn't be called SPI?
> 
> Yes, we can remove the _spi_ in this name.

Or maybe use "spi3w" or such, just to make it clear it's not plain
standard spi. Again, presuming it's really 3-wire spi =).

>>> +static int td028ttec1_panel_probe(struct platform_device *pdev)
>>> +{
>>> +	struct panel_drv_data *ddata;
>>> +	struct omap_dss_device *dssdev;
>>> +	int r;
>>> +
>>> +	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
>>> +	if (ddata == NULL)
>>> +		return -ENOMEM;
>>> +
>>> +	platform_set_drvdata(pdev, ddata);
>>> +
>>> +	if (dev_get_platdata(&pdev->dev)) {
>>> +		r = td028ttec1_panel_probe_pdata(pdev);
>>> +		if (r)
>>> +			return r;
>>> +	} else {
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	if (gpio_is_valid(ddata->cs_gpio)) {
>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->cs_gpio,
>>> +				GPIOF_OUT_INIT_HIGH, "lcd cs");
>>> +		if (r)
>>> +			goto err_gpio;
>>> +	}
>>> +
>>> +	if (gpio_is_valid(ddata->scl_gpio)) {
>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->scl_gpio,
>>> +				GPIOF_OUT_INIT_HIGH, "lcd scl");
>>> +		if (r)
>>> +			goto err_gpio;
>>> +	}
>>> +
>>> +	if (gpio_is_valid(ddata->dout_gpio)) {
>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->dout_gpio,
>>> +				GPIOF_OUT_INIT_LOW, "lcd dout");
>>> +		if (r)
>>> +			goto err_gpio;
>>> +	}
>>> +
>>> +	if (gpio_is_valid(ddata->din_gpio)) {
>>> +		r = devm_gpio_request_one(&pdev->dev, ddata->din_gpio,
>>> +				GPIOF_IN, "lcd din");
>>> +		if (r)
>>> +			goto err_gpio;
>>> +	}
>>> +
>>> +	/* according to data sheet: wait 50ms (Tpos of LCM). However, 50ms
>>> +	 * seems unreliable with later LCM batches, increasing to 90ms */
>>> +	mdelay(90);
>>
>> What is this delay for? Usually sleeps/delays should be between two "HW
>> actions". Here there's nothing below this line that would count as such
>> an action.
> 
> I guess that this delay is intended to power on the display first, then wait some

I'm not very comfortable with merging a driver, when the driver author
guesses what parts of the driver do =).

> time and after that delay enable the DSS clocks and signals and make the
> controller chip in the panel initialize.

I don't see the code before the delay doing any power up, it just sets
the data bus gpios to certain state. Do those gpio initializations power
up the panel?

> But this of course depends on the order the DSS components are probed
> and enabled and how power is controlled (we don't interrupt power at all).
> 
> I think we can move this delay (sleep) to the beginning of 
> 
> td028ttec1_panel_enable() before /* three times command zero */
>
> to make sure that a freshly powered display has enough time to do its
> internal reset and initializations before registers are programmed.

The probe function should not enable the panel (well, it can enable the
panel, but only to read an ID or such and then disable it before exiting
probe).

So after probe, the panel should be as dead as possible, power disabled
etc. All the powering up should happen in enable().

 Tomi



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

^ permalink raw reply

* Re: [RFC PATCH 1/4] mipi-dsi-bus: add MIPI DSI bus support
From: Andrzej Hajda @ 2013-10-10  9:57 UTC (permalink / raw)
  To: Bert Kenward, Laurent Pinchart
  Cc: linux-fbdev@vger.kernel.org, Kyungmin Park,
	dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org
In-Reply-To: <F5FE6E5E9429DA44B1C6B8A1A883D135041E001F@SJEXCHMB13.corp.ad.broadcom.com>

On 10/07/2013 12:47 PM, Bert Kenward wrote:
> On Tuesday September 24 2013 at 15:23, Andrzej Hajda wrote:
>> MIPI DSI is a high-speed serial interface to transmit
>> data from/to host to display module.
>>
>> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
>> ---
>>  drivers/video/display/Kconfig        |   4 +
>>  drivers/video/display/Makefile       |   1 +
>>  drivers/video/display/mipi-dsi-bus.c | 332
>> +++++++++++++++++++++++++++++++++++
>>  include/video/display.h              |   3 +
>>  include/video/mipi-dsi-bus.h         | 144 +++++++++++++++
>>  5 files changed, 484 insertions(+)
> <snipped as far as mipi-dsi-bus.h
>
>> diff --git a/include/video/mipi-dsi-bus.h b/include/video/mipi-dsi-bus.h
>> new file mode 100644
>> index 0000000..a78792d
>> --- /dev/null
>> +++ b/include/video/mipi-dsi-bus.h
>> @@ -0,0 +1,144 @@
>> +/*
>> + * MIPI DSI Bus
>> + *
>> + * Copyright (C) 2013, Samsung Electronics, Co., Ltd.
>> + * Andrzej Hajda <a.hajda@samsung.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __MIPI_DSI_BUS_H__
>> +#define __MIPI_DSI_BUS_H__
>> +
>> +#include <linux/device.h>
>> +#include <video/videomode.h>
>> +
>> +struct mipi_dsi_bus;
>> +struct mipi_dsi_device;
>> +
>> +struct mipi_dsi_bus_ops {
>> +	int (*set_power)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			 bool on);
>> +	int (*set_stream)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			  bool on);
>> +	int (*transfer)(struct mipi_dsi_bus *bus, struct mipi_dsi_device
>> *dev,
>> +			u8 type, const u8 *tx_buf, size_t tx_len, u8 *rx_buf,
>> +			size_t rx_len);
>> +};
>> +
>> +#define DSI_MODE_VIDEO			(1 << 0)
>> +#define DSI_MODE_VIDEO_BURST		(1 << 1)
>> +#define DSI_MODE_VIDEO_SYNC_PULSE	(1 << 2)
>> +#define DSI_MODE_VIDEO_AUTO_VERT	(1 << 3)
>> +#define DSI_MODE_VIDEO_HSE		(1 << 4)
>> +#define DSI_MODE_VIDEO_HFP		(1 << 5)
>> +#define DSI_MODE_VIDEO_HBP		(1 << 6)
>> +#define DSI_MODE_VIDEO_HSA		(1 << 7)
>> +#define DSI_MODE_VSYNC_FLUSH		(1 << 8)
>> +#define DSI_MODE_EOT_PACKET		(1 << 9)
>> +
>> +enum mipi_dsi_pixel_format {
>> +	DSI_FMT_RGB888,
>> +	DSI_FMT_RGB666,
>> +	DSI_FMT_RGB666_PACKED,
>> +	DSI_FMT_RGB565,
>> +};
>> +
>> +struct mipi_dsi_interface_params {
>> +	enum mipi_dsi_pixel_format format;
>> +	unsigned long mode;
>> +	unsigned long hs_clk_freq;
>> +	unsigned long esc_clk_freq;
>> +	unsigned char data_lanes;
>> +	unsigned char cmd_allow;
>> +};
>> +
>> +struct mipi_dsi_bus {
>> +	struct device *dev;
>> +	const struct mipi_dsi_bus_ops *ops;
>> +};
>> +
>> +#define MIPI_DSI_MODULE_PREFIX		"mipi-dsi:"
>> +#define MIPI_DSI_NAME_SIZE		32
>> +
>> +struct mipi_dsi_device_id {
>> +	char name[MIPI_DSI_NAME_SIZE];
>> +	__kernel_ulong_t driver_data	/* Data private to the driver */
>> +			__aligned(sizeof(__kernel_ulong_t));
>> +};
>> +
>> +struct mipi_dsi_device {
>> +	char name[MIPI_DSI_NAME_SIZE];
>> +	int id;
>> +	struct device dev;
>> +
>> +	const struct mipi_dsi_device_id *id_entry;
>> +	struct mipi_dsi_bus *bus;
>> +	struct videomode vm;
>> +	struct mipi_dsi_interface_params params;
>> +};
>> +
>> +#define to_mipi_dsi_device(d)	container_of(d, struct
>> mipi_dsi_device, dev)
>> +
>> +int mipi_dsi_device_register(struct mipi_dsi_device *dev,
>> +			     struct mipi_dsi_bus *bus);
>> +void mipi_dsi_device_unregister(struct mipi_dsi_device *dev);
>> +
>> +struct mipi_dsi_driver {
>> +	int(*probe)(struct mipi_dsi_device *);
>> +	int(*remove)(struct mipi_dsi_device *);
>> +	struct device_driver driver;
>> +	const struct mipi_dsi_device_id *id_table;
>> +};
>> +
>> +#define to_mipi_dsi_driver(d)	container_of(d, struct
>> mipi_dsi_driver, driver)
>> +
>> +int mipi_dsi_driver_register(struct mipi_dsi_driver *drv);
>> +void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv);
>> +
>> +static inline void *mipi_dsi_get_drvdata(const struct mipi_dsi_device
>> *dev)
>> +{
>> +	return dev_get_drvdata(&dev->dev);
>> +}
>> +
>> +static inline void mipi_dsi_set_drvdata(struct mipi_dsi_device *dev,
>> +					void *data)
>> +{
>> +	dev_set_drvdata(&dev->dev, data);
>> +}
>> +
>> +int of_mipi_dsi_register_devices(struct mipi_dsi_bus *bus);
>> +void mipi_dsi_unregister_devices(struct mipi_dsi_bus *bus);
>> +
>> +/* module_mipi_dsi_driver() - Helper macro for drivers that don't do
>> + * anything special in module init/exit.  This eliminates a lot of
>> + * boilerplate.  Each module may only use this macro once, and
>> + * calling it replaces module_init() and module_exit()
>> + */
>> +#define module_mipi_dsi_driver(__mipi_dsi_driver) \
>> +	module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
>> +			mipi_dsi_driver_unregister)
>> +
>> +int mipi_dsi_set_power(struct mipi_dsi_device *dev, bool on);
>> +int mipi_dsi_set_stream(struct mipi_dsi_device *dev, bool on);
>> +int mipi_dsi_dcs_write(struct mipi_dsi_device *dev, int channel, const u8
>> *data,
>> +		       size_t len);
>> +int mipi_dsi_dcs_read(struct mipi_dsi_device *dev, int channel, u8 cmd,
>> +		      u8 *data, size_t len);
>> +
>> +#define mipi_dsi_dcs_write_seq(dev, channel, seq...) \
>> +({\
>> +	const u8 d[] = { seq };\
>> +	BUILD_BUG_ON_MSG(ARRAY_SIZE(d) > 64, "DCS sequence too long for
>> stack");\
>> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
>> +
>> +#define mipi_dsi_dcs_write_static_seq(dev, channel, seq...) \
>> +({\
>> +	static const u8 d[] = { seq };\
>> +	mipi_dsi_dcs_write(dev, channel, d, ARRAY_SIZE(d));\
>> +})
>> +
>> +#endif /* __MIPI_DSI_BUS__ */
> I may well have missed something,
>  but I can't see exactly how a command mode
> update would be done with this interface. Would this require repeated calls to
> .transfer? Such transfers would need to be flagged as requiring
> synchronisation with a tearing effect control signal - either the inband
> method or a dedicated line. I suspect many hardware implementations will have
> a specific method for transferring pixel data in a DSI command mode transfer.
>
> The command sending period during video mode should probably be configurable
> on a per-transfer basis. Some commands have to be synchronised with vertical
> blanking, others do not. This could perhaps be combined with a wider
> configuration option for a given panel or interface. Similarly, selection of
> low power (LP) and high speed (HS) mode on a per-transfer basis can be needed
> for some panels.
>
> Is there a mechanism for controlling ultra-low power state (ULPS) entry? Also,
> is there a method for sending arbitrary trigger messages (eg the reset
> trigger)?
Thanks for the feedback.
The current dsi bus implementation was just made to work with the
hw I have. It should be extended to be more generic, but I hope
now it is just matter of adding good opses and parameters.
Feel free to propose new opses.

Andrzej
>
> Thanks,
>
> Bert.


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Dr. H. Nikolaus Schaller @ 2013-10-10  9:34 UTC (permalink / raw)
  To: Tomi Valkeinen
  Cc: Marek Belisko, plagnioj, linux-kernel, linux-omap, linux-fbdev
In-Reply-To: <525662F5.3090600@ti.com>

Hi Tomi,

Am 10.10.2013 um 10:19 schrieb Tomi Valkeinen:

> Hi,
> 
> On 10/10/13 00:08, Marek Belisko wrote:
>> For communicating with driver is used gpio bitbanging because TD028 does
>> not have a standard compliant SPI interface. It is a 3-wire thing with
>> direction reversal.
> 
> Isn't that SPI_3WIRE?

Maybe - but we have no complete datasheet and I don't know an official spec of
a 3-wire SPI protocol to compare how 3-wire should work.

And I think (but I may be wrong) that SPI_3WIRE is an optional feature of the SoC
specific SPI drivers and in my understanding the OMAP has no such driver:

http://lxr.free-electrons.com/source/drivers/spi/spi-omap2-mcspi.c#L874

And spi-bitbang.c hasn't either.

So I would prefer to leave this as an area for optimizations for future work.
Maybe we can add some more comments on this.

> 
>> Communication with display is used only during panel enable/disable so it's
>> not performance issue.
>> 
>> Signed-off-by: Marek Belisko <marek@goldelico.com>
>> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
>> ---
>> drivers/video/omap2/displays-new/Kconfig           |   6 +
>> drivers/video/omap2/displays-new/Makefile          |   1 +
>> .../omap2/displays-new/panel-tpo-td028ttec1.c      | 537 +++++++++++++++++++++
>> include/video/omap-panel-data.h                    |  22 +
>> 4 files changed, 566 insertions(+)
>> create mode 100644 drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
>> 
>> diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
>> index 6c90885..3f86432 100644
>> --- a/drivers/video/omap2/displays-new/Kconfig
>> +++ b/drivers/video/omap2/displays-new/Kconfig
>> @@ -56,6 +56,11 @@ config DISPLAY_PANEL_SHARP_LS037V7DW01
>>         help
>>           LCD Panel used in TI's SDP3430 and EVM boards
>> 
>> +config DISPLAY_PANEL_TPO_TD028TTEC1
>> +        tristate "TPO TD028TTEC1 LCD Panel"
>> +        help
>> +          LCD panel used by Openmoko.
>> +
>> config DISPLAY_PANEL_TPO_TD043MTEA1
>>         tristate "TPO TD043MTEA1 LCD Panel"
>>         depends on SPI
>> @@ -70,4 +75,5 @@ config DISPLAY_PANEL_NEC_NL8048HL11
>> 		This NEC NL8048HL11 panel is TFT LCD used in the
>> 		Zoom2/3/3630 sdp boards.
>> 
>> +
> 
> Extra change.
> 
>> endmenu
>> diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
>> index 5aeb11b..0323a8a 100644
>> --- a/drivers/video/omap2/displays-new/Makefile
>> +++ b/drivers/video/omap2/displays-new/Makefile
>> @@ -8,5 +8,6 @@ obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
>> obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>> obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
>> obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
>> +obj-$(CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
>> obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
>> obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
>> diff --git a/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
>> new file mode 100644
>> index 0000000..b63586e
>> --- /dev/null
>> +++ b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
>> @@ -0,0 +1,537 @@
>> +/*
>> + * Toppoly TD028TTEC1 panel support
>> + *
>> + * Copyright (C) 2008 Nokia Corporation
>> + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
>> + *
>> + * Neo 1973 code (jbt6k74.c):
>> + * Copyright (C) 2006-2007 by OpenMoko, Inc.
>> + * Author: Harald Welte <laforge@openmoko.org>
>> + *
>> + * Ported and adapted from Neo 1973 U-Boot by:
>> + * H. Nikolaus Schaller <hns@goldelico.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2 as published by
>> + * the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful, but WITHOUT
>> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
>> + * more details.
>> + *
>> + * You should have received a copy of the GNU General Public License along with
>> + * this program.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/delay.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/gpio.h>
>> +#include <video/omapdss.h>
>> +#include <video/omap-panel-data.h>
>> +
>> +struct panel_drv_data {
>> +	struct omap_dss_device dssdev;
>> +	struct omap_dss_device *in;
>> +
>> +	int data_lines;
>> +
>> +	struct omap_video_timings videomode;
>> +
>> +	int cs_gpio;
>> +	int scl_gpio;
>> +	int din_gpio;
>> +	int dout_gpio;
> 
> Three wires, but four gpios? What am I missing here...

We have wired up the 3-wire SPI interface of the display
to 4 wires of the McSPI interface because we initially thought
that it could work in 4 wire mode as well.

The next step we did was to port the driver code from the
Openmoko Neo1973 U-Boot which used the gpio-bitbang
mechanism.

Since it worked and is used only for enabling/disabling the
display and for no other purpose we never felt it important
to check or modify the code to use SPI.

But you are right - we don't use the din_gpio really since
we never read registers from the chip. So 3 gpios could be
sufficient.

Or we must handle the case that din_gpio = dout_gpio
in panel_probe to avoid duplicate use of the same gpio.

> 
>> +	u_int16_t tx_buf[4];
> 
> Hmm, what's wrong with "u16"?

Nothing. Maybe a relict from taking years old u-boot code and not
recognising because the compiler did not complain...

> 
>> +};
>> +
>> +static struct omap_video_timings td028ttec1_panel_timings = {
>> +	.x_res		= 480,
>> +	.y_res		= 640,
>> +	.pixel_clock	= 22153,
>> +	.hfp		= 24,
>> +	.hsw		= 8,
>> +	.hbp		= 8,
>> +	.vfp		= 4,
>> +	.vsw		= 2,
>> +	.vbp		= 2,
>> +
>> +	.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
>> +	.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
>> +
>> +	.data_pclk_edge	= OMAPDSS_DRIVE_SIG_FALLING_EDGE,
>> +	.de_level	= OMAPDSS_SIG_ACTIVE_HIGH,
>> +	.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
>> +};
>> +
>> +#define JBT_COMMAND	0x000
>> +#define JBT_DATA	0x100
>> +
>> +/* 150uS minimum clock cycle, we have two of this plus our other
>> + * instructions */
>> +
>> +#define SPI_DELAY()	udelay(200)
> 
> Would usleep_range work here?

Yes, I think that works as well.

The 150uS is a minimum and I think it would also work
much slower.

> 
> See Documentation/timers/timers-howto.txt
> 
>> +static int jbt_spi_xfer(struct panel_drv_data *data, int wordnum, int bitlen)
> 
> Hmm, if it's not SPI, maybe it shouldn't be called SPI?

Yes, we can remove the _spi_ in this name.

> 
>> +{
>> +	u_int16_t tmpdout = 0;
>> +	int   i, j;
>> +
>> +	gpio_set_value(data->cs_gpio, 0);
>> +
>> +	for (i = 0; i < wordnum; i++) {
>> +		tmpdout = data->tx_buf[i];
>> +
>> +		for (j = 0; j < bitlen; j++) {
>> +			gpio_set_value(data->scl_gpio, 0);
>> +			if (tmpdout & (1 << (bitlen-1))) {
>> +				gpio_set_value(data->dout_gpio, 1);
>> +				if (gpio_get_value(data->din_gpio) = 0)
>> +					return 1;
>> +			} else {
>> +				gpio_set_value(data->dout_gpio, 0);
>> +				if (gpio_get_value(data->din_gpio) != 0)
>> +					return 1;
>> +			}
>> +			SPI_DELAY();
>> +			gpio_set_value(data->scl_gpio, 1);
>> +			SPI_DELAY();
>> +			tmpdout <<= 1;
>> +		}
>> +	}
>> +
>> +	gpio_set_value(data->cs_gpio, 1);
>> +
>> +	return 0;
>> +}
>> +
>> +#define JBT_COMMAND	0x000
>> +#define JBT_DATA	0x100
>> +
>> +int jbt_reg_write_nodata(struct panel_drv_data *ddata, u_int8_t reg)
> 
> What's wrong with "u8"? Have I missed a memo? =)
> 
>> +{
>> +	int rc;
>> +
>> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
>> +
>> +	rc = jbt_spi_xfer(ddata, 1, 9);
>> +	if (rc)
>> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
>> +
>> +	return rc;
>> +}
>> +
>> +int jbt_reg_write(struct panel_drv_data *ddata, u_int8_t reg, u_int8_t data)
>> +{
>> +	int rc;
>> +
>> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
>> +	ddata->tx_buf[1] = JBT_DATA | data;
>> +
>> +	rc = jbt_spi_xfer(ddata, 2, 9);
>> +	if (rc)
>> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
>> +
>> +	return rc;
>> +}
>> +
>> +int jbt_reg_write16(struct panel_drv_data *ddata, u_int8_t reg, u_int16_t data)
>> +{
>> +	int rc;
>> +
>> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
>> +	ddata->tx_buf[1] = JBT_DATA | (data >> 8);
>> +	ddata->tx_buf[2] = JBT_DATA | (data & 0xff);
>> +
>> +	rc = jbt_spi_xfer(ddata, 3, 9);
>> +	if (rc)
>> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
>> +
>> +	return rc;
>> +}
>> +
>> +enum jbt_register {
>> +	JBT_REG_SLEEP_IN		= 0x10,
>> +	JBT_REG_SLEEP_OUT		= 0x11,
>> +
>> +	JBT_REG_DISPLAY_OFF		= 0x28,
>> +	JBT_REG_DISPLAY_ON		= 0x29,
>> +
>> +	JBT_REG_RGB_FORMAT		= 0x3a,
>> +	JBT_REG_QUAD_RATE		= 0x3b,
>> +
>> +	JBT_REG_POWER_ON_OFF		= 0xb0,
>> +	JBT_REG_BOOSTER_OP		= 0xb1,
>> +	JBT_REG_BOOSTER_MODE		= 0xb2,
>> +	JBT_REG_BOOSTER_FREQ		= 0xb3,
>> +	JBT_REG_OPAMP_SYSCLK		= 0xb4,
>> +	JBT_REG_VSC_VOLTAGE		= 0xb5,
>> +	JBT_REG_VCOM_VOLTAGE		= 0xb6,
>> +	JBT_REG_EXT_DISPL		= 0xb7,
>> +	JBT_REG_OUTPUT_CONTROL		= 0xb8,
>> +	JBT_REG_DCCLK_DCEV		= 0xb9,
>> +	JBT_REG_DISPLAY_MODE1		= 0xba,
>> +	JBT_REG_DISPLAY_MODE2		= 0xbb,
>> +	JBT_REG_DISPLAY_MODE		= 0xbc,
>> +	JBT_REG_ASW_SLEW		= 0xbd,
>> +	JBT_REG_DUMMY_DISPLAY		= 0xbe,
>> +	JBT_REG_DRIVE_SYSTEM		= 0xbf,
>> +
>> +	JBT_REG_SLEEP_OUT_FR_A		= 0xc0,
>> +	JBT_REG_SLEEP_OUT_FR_B		= 0xc1,
>> +	JBT_REG_SLEEP_OUT_FR_C		= 0xc2,
>> +	JBT_REG_SLEEP_IN_LCCNT_D	= 0xc3,
>> +	JBT_REG_SLEEP_IN_LCCNT_E	= 0xc4,
>> +	JBT_REG_SLEEP_IN_LCCNT_F	= 0xc5,
>> +	JBT_REG_SLEEP_IN_LCCNT_G	= 0xc6,
>> +
>> +	JBT_REG_GAMMA1_FINE_1		= 0xc7,
>> +	JBT_REG_GAMMA1_FINE_2		= 0xc8,
>> +	JBT_REG_GAMMA1_INCLINATION	= 0xc9,
>> +	JBT_REG_GAMMA1_BLUE_OFFSET	= 0xca,
>> +
>> +	JBT_REG_BLANK_CONTROL		= 0xcf,
>> +	JBT_REG_BLANK_TH_TV		= 0xd0,
>> +	JBT_REG_CKV_ON_OFF		= 0xd1,
>> +	JBT_REG_CKV_1_2			= 0xd2,
>> +	JBT_REG_OEV_TIMING		= 0xd3,
>> +	JBT_REG_ASW_TIMING_1		= 0xd4,
>> +	JBT_REG_ASW_TIMING_2		= 0xd5,
>> +
>> +	JBT_REG_HCLOCK_VGA		= 0xec,
>> +	JBT_REG_HCLOCK_QVGA		= 0xed,
>> +};
>> +
>> +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
>> +
>> +static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +	int r;
>> +
>> +	if (omapdss_device_is_connected(dssdev))
>> +		return 0;
>> +
>> +	r = in->ops.dpi->connect(in, dssdev);
>> +	if (r)
>> +		return r;
>> +
>> +	return 0;
>> +}
>> +
>> +static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +
>> +	if (!omapdss_device_is_connected(dssdev))
>> +		return;
>> +
>> +	in->ops.dpi->disconnect(in, dssdev);
>> +}
>> +
>> +static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +	int r;
>> +
>> +	if (!omapdss_device_is_connected(dssdev))
>> +		return -ENODEV;
>> +
>> +	if (omapdss_device_is_enabled(dssdev))
>> +		return 0;
>> +
>> +	in->ops.dpi->set_data_lines(in, ddata->data_lines);
>> +	in->ops.dpi->set_timings(in, &ddata->videomode);
>> +
>> +	r = in->ops.dpi->enable(in);
>> +	if (r)
>> +		return r;
>> +
>> +	dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
>> +		dssdev->state);
>> +
>> +	/* three times command zero */
>> +	r |= jbt_reg_write_nodata(ddata, 0x00);
>> +	udelay(1000);
> 
> These are big delays, and I guess the timing is not strict here, so
> maybe some sleep variant would be better.

Yes, good hint! We will look at it. And add some locks so that enabling
and disabling can't interfere.

> 
>> +	r |= jbt_reg_write_nodata(ddata, 0x00);
>> +	udelay(1000);
>> +	r |= jbt_reg_write_nodata(ddata, 0x00);
>> +	udelay(1000);
>> +
>> +	/* deep standby out */
>> +	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x17);
>> +
>> +	/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
>> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE, 0x80);
>> +
>> +	/* Quad mode off */
>> +	r |= jbt_reg_write(ddata, JBT_REG_QUAD_RATE, 0x00);
>> +
>> +	/* AVDD on, XVDD on */
>> +	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x16);
>> +
>> +	/* Output control */
>> +	r |= jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
>> +
>> +	/* Sleep mode off */
>> +	r |= jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_OUT);
>> +
>> +	/* at this point we have like 50% grey */
>> +
>> +	/* initialize register set */
>> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
>> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
>> +	r |= jbt_reg_write(ddata, JBT_REG_RGB_FORMAT, 0x60);
>> +	r |= jbt_reg_write(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
>> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_OP, 0x56);
>> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_MODE, 0x33);
>> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
>> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
>> +	r |= jbt_reg_write(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
>> +	r |= jbt_reg_write(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
>> +	r |= jbt_reg_write(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
>> +	r |= jbt_reg_write(ddata, JBT_REG_EXT_DISPL, 0x03);
>> +	r |= jbt_reg_write(ddata, JBT_REG_DCCLK_DCEV, 0x04);
>> +	/*
>> +	 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
>> +	 * to avoid red / blue flicker
>> +	 */
>> +	r |= jbt_reg_write(ddata, JBT_REG_ASW_SLEW, 0x04);
>> +	r |= jbt_reg_write(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
>> +
>> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
>> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
>> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
>> +
>> +	r |= jbt_reg_write16(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
>> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
>> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
>> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
>> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
>> +
>> +	r |= jbt_reg_write16(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
>> +	r |= jbt_reg_write(ddata, JBT_REG_BLANK_CONTROL, 0x02);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
>> +
>> +	r |= jbt_reg_write(ddata, JBT_REG_CKV_ON_OFF, 0x01);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_CKV_1_2, 0x0000);
>> +
>> +	r |= jbt_reg_write16(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
>> +	r |= jbt_reg_write16(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
>> +	r |= jbt_reg_write(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
>> +
>> +	r |= jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_ON);
>> +
>> +	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
>> +
>> +	return r;
>> +}
>> +
>> +static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +
>> +	if (!omapdss_device_is_enabled(dssdev))
>> +		return;
>> +
>> +	dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
>> +
>> +	in->ops.dpi->disable(in);
>> +
>> +	jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_OFF);
>> +	jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
>> +	jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_IN);
>> +	jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x00);
>> +
>> +	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
>> +}
>> +
>> +static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
>> +		struct omap_video_timings *timings)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +
>> +	ddata->videomode = *timings;
>> +	dssdev->panel.timings = *timings;
>> +
>> +	in->ops.dpi->set_timings(in, timings);
>> +}
>> +
>> +static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
>> +		struct omap_video_timings *timings)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +
>> +	*timings = ddata->videomode;
>> +}
>> +
>> +static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
>> +		struct omap_video_timings *timings)
>> +{
>> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
>> +	struct omap_dss_device *in = ddata->in;
>> +
>> +	return in->ops.dpi->check_timings(in, timings);
>> +}
>> +
>> +static struct omap_dss_driver td028ttec1_ops = {
>> +	.connect	= td028ttec1_panel_connect,
>> +	.disconnect	= td028ttec1_panel_disconnect,
>> +
>> +	.enable		= td028ttec1_panel_enable,
>> +	.disable	= td028ttec1_panel_disable,
>> +
>> +	.set_timings	= td028ttec1_panel_set_timings,
>> +	.get_timings	= td028ttec1_panel_get_timings,
>> +	.check_timings	= td028ttec1_panel_check_timings,
>> +};
>> +
>> +static int td028ttec1_panel_probe_pdata(struct platform_device *pdev)
>> +{
>> +	const struct panel_tpo_td028tec1_platform_data *pdata;
>> +	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
>> +	struct omap_dss_device *dssdev, *in;
>> +
>> +	pdata = dev_get_platdata(&pdev->dev);
>> +
>> +	in = omap_dss_find_output(pdata->source);
>> +	if (in = NULL) {
>> +		dev_err(&pdev->dev, "failed to find video source '%s'\n",
>> +				pdata->source);
>> +		return -EPROBE_DEFER;
>> +	}
>> +
>> +	ddata->in = in;
>> +
>> +	ddata->data_lines = pdata->data_lines;
>> +
>> +	dssdev = &ddata->dssdev;
>> +	dssdev->name = pdata->name;
>> +
>> +	ddata->cs_gpio = pdata->cs_gpio;
>> +	ddata->scl_gpio = pdata->scl_gpio;
>> +	ddata->din_gpio = pdata->din_gpio;
>> +	ddata->dout_gpio = pdata->dout_gpio;
>> +
>> +	return 0;
>> +}
>> +
>> +static int td028ttec1_panel_probe(struct platform_device *pdev)
>> +{
>> +	struct panel_drv_data *ddata;
>> +	struct omap_dss_device *dssdev;
>> +	int r;
>> +
>> +	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
>> +	if (ddata = NULL)
>> +		return -ENOMEM;
>> +
>> +	platform_set_drvdata(pdev, ddata);
>> +
>> +	if (dev_get_platdata(&pdev->dev)) {
>> +		r = td028ttec1_panel_probe_pdata(pdev);
>> +		if (r)
>> +			return r;
>> +	} else {
>> +		return -ENODEV;
>> +	}
>> +
>> +	if (gpio_is_valid(ddata->cs_gpio)) {
>> +		r = devm_gpio_request_one(&pdev->dev, ddata->cs_gpio,
>> +				GPIOF_OUT_INIT_HIGH, "lcd cs");
>> +		if (r)
>> +			goto err_gpio;
>> +	}
>> +
>> +	if (gpio_is_valid(ddata->scl_gpio)) {
>> +		r = devm_gpio_request_one(&pdev->dev, ddata->scl_gpio,
>> +				GPIOF_OUT_INIT_HIGH, "lcd scl");
>> +		if (r)
>> +			goto err_gpio;
>> +	}
>> +
>> +	if (gpio_is_valid(ddata->dout_gpio)) {
>> +		r = devm_gpio_request_one(&pdev->dev, ddata->dout_gpio,
>> +				GPIOF_OUT_INIT_LOW, "lcd dout");
>> +		if (r)
>> +			goto err_gpio;
>> +	}
>> +
>> +	if (gpio_is_valid(ddata->din_gpio)) {
>> +		r = devm_gpio_request_one(&pdev->dev, ddata->din_gpio,
>> +				GPIOF_IN, "lcd din");
>> +		if (r)
>> +			goto err_gpio;
>> +	}
>> +
>> +	/* according to data sheet: wait 50ms (Tpos of LCM). However, 50ms
>> +	 * seems unreliable with later LCM batches, increasing to 90ms */
>> +	mdelay(90);
> 
> What is this delay for? Usually sleeps/delays should be between two "HW
> actions". Here there's nothing below this line that would count as such
> an action.

I guess that this delay is intended to power on the display first, then wait some
time and after that delay enable the DSS clocks and signals and make the
controller chip in the panel initialize.

But this of course depends on the order the DSS components are probed
and enabled and how power is controlled (we don't interrupt power at all).

I think we can move this delay (sleep) to the beginning of 

td028ttec1_panel_enable() before /* three times command zero */

to make sure that a freshly powered display has enough time to do its
internal reset and initializations before registers are programmed.

> 
>> +	ddata->videomode = td028ttec1_panel_timings;
>> +
>> +	dssdev = &ddata->dssdev;
>> +	dssdev->dev = &pdev->dev;
>> +	dssdev->driver = &td028ttec1_ops;
>> +	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
>> +	dssdev->owner = THIS_MODULE;
>> +	dssdev->panel.timings = ddata->videomode;
>> +	dssdev->phy.dpi.data_lines = ddata->data_lines;
>> +
>> +	r = omapdss_register_display(dssdev);
>> +	if (r) {
>> +		dev_err(&pdev->dev, "Failed to register panel\n");
>> +		goto err_reg;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_reg:
>> +err_gpio:
>> +	omap_dss_put_device(ddata->in);
>> +	return r;
>> +}
>> +
> 
> Tomi

BR,
Nikolaus


^ permalink raw reply

* Re: [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Tomi Valkeinen @ 2013-10-10  8:19 UTC (permalink / raw)
  To: Marek Belisko
  Cc: plagnioj, linux-kernel, linux-omap, linux-fbdev,
	H. Nikolaus Schaller
In-Reply-To: <1381352915-17219-1-git-send-email-marek@goldelico.com>

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

Hi,

On 10/10/13 00:08, Marek Belisko wrote:
> For communicating with driver is used gpio bitbanging because TD028 does
> not have a standard compliant SPI interface. It is a 3-wire thing with
> direction reversal.

Isn't that SPI_3WIRE?

> Communication with display is used only during panel enable/disable so it's
> not performance issue.
> 
> Signed-off-by: Marek Belisko <marek@goldelico.com>
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> ---
>  drivers/video/omap2/displays-new/Kconfig           |   6 +
>  drivers/video/omap2/displays-new/Makefile          |   1 +
>  .../omap2/displays-new/panel-tpo-td028ttec1.c      | 537 +++++++++++++++++++++
>  include/video/omap-panel-data.h                    |  22 +
>  4 files changed, 566 insertions(+)
>  create mode 100644 drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
> 
> diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
> index 6c90885..3f86432 100644
> --- a/drivers/video/omap2/displays-new/Kconfig
> +++ b/drivers/video/omap2/displays-new/Kconfig
> @@ -56,6 +56,11 @@ config DISPLAY_PANEL_SHARP_LS037V7DW01
>          help
>            LCD Panel used in TI's SDP3430 and EVM boards
>  
> +config DISPLAY_PANEL_TPO_TD028TTEC1
> +        tristate "TPO TD028TTEC1 LCD Panel"
> +        help
> +          LCD panel used by Openmoko.
> +
>  config DISPLAY_PANEL_TPO_TD043MTEA1
>          tristate "TPO TD043MTEA1 LCD Panel"
>          depends on SPI
> @@ -70,4 +75,5 @@ config DISPLAY_PANEL_NEC_NL8048HL11
>  		This NEC NL8048HL11 panel is TFT LCD used in the
>  		Zoom2/3/3630 sdp boards.
>  
> +

Extra change.

>  endmenu
> diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
> index 5aeb11b..0323a8a 100644
> --- a/drivers/video/omap2/displays-new/Makefile
> +++ b/drivers/video/omap2/displays-new/Makefile
> @@ -8,5 +8,6 @@ obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
>  obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>  obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
>  obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
> +obj-$(CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
>  obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
>  obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
> diff --git a/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
> new file mode 100644
> index 0000000..b63586e
> --- /dev/null
> +++ b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
> @@ -0,0 +1,537 @@
> +/*
> + * Toppoly TD028TTEC1 panel support
> + *
> + * Copyright (C) 2008 Nokia Corporation
> + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
> + *
> + * Neo 1973 code (jbt6k74.c):
> + * Copyright (C) 2006-2007 by OpenMoko, Inc.
> + * Author: Harald Welte <laforge@openmoko.org>
> + *
> + * Ported and adapted from Neo 1973 U-Boot by:
> + * H. Nikolaus Schaller <hns@goldelico.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio.h>
> +#include <video/omapdss.h>
> +#include <video/omap-panel-data.h>
> +
> +struct panel_drv_data {
> +	struct omap_dss_device dssdev;
> +	struct omap_dss_device *in;
> +
> +	int data_lines;
> +
> +	struct omap_video_timings videomode;
> +
> +	int cs_gpio;
> +	int scl_gpio;
> +	int din_gpio;
> +	int dout_gpio;

Three wires, but four gpios? What am I missing here...

> +	u_int16_t tx_buf[4];

Hmm, what's wrong with "u16"?

> +};
> +
> +static struct omap_video_timings td028ttec1_panel_timings = {
> +	.x_res		= 480,
> +	.y_res		= 640,
> +	.pixel_clock	= 22153,
> +	.hfp		= 24,
> +	.hsw		= 8,
> +	.hbp		= 8,
> +	.vfp		= 4,
> +	.vsw		= 2,
> +	.vbp		= 2,
> +
> +	.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
> +	.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
> +
> +	.data_pclk_edge	= OMAPDSS_DRIVE_SIG_FALLING_EDGE,
> +	.de_level	= OMAPDSS_SIG_ACTIVE_HIGH,
> +	.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
> +};
> +
> +#define JBT_COMMAND	0x000
> +#define JBT_DATA	0x100
> +
> +/* 150uS minimum clock cycle, we have two of this plus our other
> + * instructions */
> +
> +#define SPI_DELAY()	udelay(200)

Would usleep_range work here?

See Documentation/timers/timers-howto.txt

> +static int jbt_spi_xfer(struct panel_drv_data *data, int wordnum, int bitlen)

Hmm, if it's not SPI, maybe it shouldn't be called SPI?

> +{
> +	u_int16_t tmpdout = 0;
> +	int   i, j;
> +
> +	gpio_set_value(data->cs_gpio, 0);
> +
> +	for (i = 0; i < wordnum; i++) {
> +		tmpdout = data->tx_buf[i];
> +
> +		for (j = 0; j < bitlen; j++) {
> +			gpio_set_value(data->scl_gpio, 0);
> +			if (tmpdout & (1 << (bitlen-1))) {
> +				gpio_set_value(data->dout_gpio, 1);
> +				if (gpio_get_value(data->din_gpio) == 0)
> +					return 1;
> +			} else {
> +				gpio_set_value(data->dout_gpio, 0);
> +				if (gpio_get_value(data->din_gpio) != 0)
> +					return 1;
> +			}
> +			SPI_DELAY();
> +			gpio_set_value(data->scl_gpio, 1);
> +			SPI_DELAY();
> +			tmpdout <<= 1;
> +		}
> +	}
> +
> +	gpio_set_value(data->cs_gpio, 1);
> +
> +	return 0;
> +}
> +
> +#define JBT_COMMAND	0x000
> +#define JBT_DATA	0x100
> +
> +int jbt_reg_write_nodata(struct panel_drv_data *ddata, u_int8_t reg)

What's wrong with "u8"? Have I missed a memo? =)

> +{
> +	int rc;
> +
> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
> +
> +	rc = jbt_spi_xfer(ddata, 1, 9);
> +	if (rc)
> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
> +
> +	return rc;
> +}
> +
> +int jbt_reg_write(struct panel_drv_data *ddata, u_int8_t reg, u_int8_t data)
> +{
> +	int rc;
> +
> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
> +	ddata->tx_buf[1] = JBT_DATA | data;
> +
> +	rc = jbt_spi_xfer(ddata, 2, 9);
> +	if (rc)
> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
> +
> +	return rc;
> +}
> +
> +int jbt_reg_write16(struct panel_drv_data *ddata, u_int8_t reg, u_int16_t data)
> +{
> +	int rc;
> +
> +	ddata->tx_buf[0] = JBT_COMMAND | reg;
> +	ddata->tx_buf[1] = JBT_DATA | (data >> 8);
> +	ddata->tx_buf[2] = JBT_DATA | (data & 0xff);
> +
> +	rc = jbt_spi_xfer(ddata, 3, 9);
> +	if (rc)
> +		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
> +
> +	return rc;
> +}
> +
> +enum jbt_register {
> +	JBT_REG_SLEEP_IN		= 0x10,
> +	JBT_REG_SLEEP_OUT		= 0x11,
> +
> +	JBT_REG_DISPLAY_OFF		= 0x28,
> +	JBT_REG_DISPLAY_ON		= 0x29,
> +
> +	JBT_REG_RGB_FORMAT		= 0x3a,
> +	JBT_REG_QUAD_RATE		= 0x3b,
> +
> +	JBT_REG_POWER_ON_OFF		= 0xb0,
> +	JBT_REG_BOOSTER_OP		= 0xb1,
> +	JBT_REG_BOOSTER_MODE		= 0xb2,
> +	JBT_REG_BOOSTER_FREQ		= 0xb3,
> +	JBT_REG_OPAMP_SYSCLK		= 0xb4,
> +	JBT_REG_VSC_VOLTAGE		= 0xb5,
> +	JBT_REG_VCOM_VOLTAGE		= 0xb6,
> +	JBT_REG_EXT_DISPL		= 0xb7,
> +	JBT_REG_OUTPUT_CONTROL		= 0xb8,
> +	JBT_REG_DCCLK_DCEV		= 0xb9,
> +	JBT_REG_DISPLAY_MODE1		= 0xba,
> +	JBT_REG_DISPLAY_MODE2		= 0xbb,
> +	JBT_REG_DISPLAY_MODE		= 0xbc,
> +	JBT_REG_ASW_SLEW		= 0xbd,
> +	JBT_REG_DUMMY_DISPLAY		= 0xbe,
> +	JBT_REG_DRIVE_SYSTEM		= 0xbf,
> +
> +	JBT_REG_SLEEP_OUT_FR_A		= 0xc0,
> +	JBT_REG_SLEEP_OUT_FR_B		= 0xc1,
> +	JBT_REG_SLEEP_OUT_FR_C		= 0xc2,
> +	JBT_REG_SLEEP_IN_LCCNT_D	= 0xc3,
> +	JBT_REG_SLEEP_IN_LCCNT_E	= 0xc4,
> +	JBT_REG_SLEEP_IN_LCCNT_F	= 0xc5,
> +	JBT_REG_SLEEP_IN_LCCNT_G	= 0xc6,
> +
> +	JBT_REG_GAMMA1_FINE_1		= 0xc7,
> +	JBT_REG_GAMMA1_FINE_2		= 0xc8,
> +	JBT_REG_GAMMA1_INCLINATION	= 0xc9,
> +	JBT_REG_GAMMA1_BLUE_OFFSET	= 0xca,
> +
> +	JBT_REG_BLANK_CONTROL		= 0xcf,
> +	JBT_REG_BLANK_TH_TV		= 0xd0,
> +	JBT_REG_CKV_ON_OFF		= 0xd1,
> +	JBT_REG_CKV_1_2			= 0xd2,
> +	JBT_REG_OEV_TIMING		= 0xd3,
> +	JBT_REG_ASW_TIMING_1		= 0xd4,
> +	JBT_REG_ASW_TIMING_2		= 0xd5,
> +
> +	JBT_REG_HCLOCK_VGA		= 0xec,
> +	JBT_REG_HCLOCK_QVGA		= 0xed,
> +};
> +
> +#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
> +
> +static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +	int r;
> +
> +	if (omapdss_device_is_connected(dssdev))
> +		return 0;
> +
> +	r = in->ops.dpi->connect(in, dssdev);
> +	if (r)
> +		return r;
> +
> +	return 0;
> +}
> +
> +static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +
> +	if (!omapdss_device_is_connected(dssdev))
> +		return;
> +
> +	in->ops.dpi->disconnect(in, dssdev);
> +}
> +
> +static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +	int r;
> +
> +	if (!omapdss_device_is_connected(dssdev))
> +		return -ENODEV;
> +
> +	if (omapdss_device_is_enabled(dssdev))
> +		return 0;
> +
> +	in->ops.dpi->set_data_lines(in, ddata->data_lines);
> +	in->ops.dpi->set_timings(in, &ddata->videomode);
> +
> +	r = in->ops.dpi->enable(in);
> +	if (r)
> +		return r;
> +
> +	dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
> +		dssdev->state);
> +
> +	/* three times command zero */
> +	r |= jbt_reg_write_nodata(ddata, 0x00);
> +	udelay(1000);

These are big delays, and I guess the timing is not strict here, so
maybe some sleep variant would be better.

> +	r |= jbt_reg_write_nodata(ddata, 0x00);
> +	udelay(1000);
> +	r |= jbt_reg_write_nodata(ddata, 0x00);
> +	udelay(1000);
> +
> +	/* deep standby out */
> +	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x17);
> +
> +	/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE, 0x80);
> +
> +	/* Quad mode off */
> +	r |= jbt_reg_write(ddata, JBT_REG_QUAD_RATE, 0x00);
> +
> +	/* AVDD on, XVDD on */
> +	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x16);
> +
> +	/* Output control */
> +	r |= jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
> +
> +	/* Sleep mode off */
> +	r |= jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_OUT);
> +
> +	/* at this point we have like 50% grey */
> +
> +	/* initialize register set */
> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
> +	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
> +	r |= jbt_reg_write(ddata, JBT_REG_RGB_FORMAT, 0x60);
> +	r |= jbt_reg_write(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_OP, 0x56);
> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_MODE, 0x33);
> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
> +	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
> +	r |= jbt_reg_write(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
> +	r |= jbt_reg_write(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
> +	r |= jbt_reg_write(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
> +	r |= jbt_reg_write(ddata, JBT_REG_EXT_DISPL, 0x03);
> +	r |= jbt_reg_write(ddata, JBT_REG_DCCLK_DCEV, 0x04);
> +	/*
> +	 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
> +	 * to avoid red / blue flicker
> +	 */
> +	r |= jbt_reg_write(ddata, JBT_REG_ASW_SLEW, 0x04);
> +	r |= jbt_reg_write(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
> +
> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
> +	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
> +	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
> +
> +	r |= jbt_reg_write16(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
> +	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
> +
> +	r |= jbt_reg_write16(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
> +	r |= jbt_reg_write(ddata, JBT_REG_BLANK_CONTROL, 0x02);
> +	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
> +	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
> +
> +	r |= jbt_reg_write(ddata, JBT_REG_CKV_ON_OFF, 0x01);
> +	r |= jbt_reg_write16(ddata, JBT_REG_CKV_1_2, 0x0000);
> +
> +	r |= jbt_reg_write16(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
> +	r |= jbt_reg_write16(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
> +	r |= jbt_reg_write(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
> +
> +	r |= jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_ON);
> +
> +	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
> +
> +	return r;
> +}
> +
> +static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +
> +	if (!omapdss_device_is_enabled(dssdev))
> +		return;
> +
> +	dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
> +
> +	in->ops.dpi->disable(in);
> +
> +	jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_OFF);
> +	jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
> +	jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_IN);
> +	jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x00);
> +
> +	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
> +}
> +
> +static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
> +		struct omap_video_timings *timings)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +
> +	ddata->videomode = *timings;
> +	dssdev->panel.timings = *timings;
> +
> +	in->ops.dpi->set_timings(in, timings);
> +}
> +
> +static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
> +		struct omap_video_timings *timings)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +
> +	*timings = ddata->videomode;
> +}
> +
> +static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
> +		struct omap_video_timings *timings)
> +{
> +	struct panel_drv_data *ddata = to_panel_data(dssdev);
> +	struct omap_dss_device *in = ddata->in;
> +
> +	return in->ops.dpi->check_timings(in, timings);
> +}
> +
> +static struct omap_dss_driver td028ttec1_ops = {
> +	.connect	= td028ttec1_panel_connect,
> +	.disconnect	= td028ttec1_panel_disconnect,
> +
> +	.enable		= td028ttec1_panel_enable,
> +	.disable	= td028ttec1_panel_disable,
> +
> +	.set_timings	= td028ttec1_panel_set_timings,
> +	.get_timings	= td028ttec1_panel_get_timings,
> +	.check_timings	= td028ttec1_panel_check_timings,
> +};
> +
> +static int td028ttec1_panel_probe_pdata(struct platform_device *pdev)
> +{
> +	const struct panel_tpo_td028tec1_platform_data *pdata;
> +	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
> +	struct omap_dss_device *dssdev, *in;
> +
> +	pdata = dev_get_platdata(&pdev->dev);
> +
> +	in = omap_dss_find_output(pdata->source);
> +	if (in == NULL) {
> +		dev_err(&pdev->dev, "failed to find video source '%s'\n",
> +				pdata->source);
> +		return -EPROBE_DEFER;
> +	}
> +
> +	ddata->in = in;
> +
> +	ddata->data_lines = pdata->data_lines;
> +
> +	dssdev = &ddata->dssdev;
> +	dssdev->name = pdata->name;
> +
> +	ddata->cs_gpio = pdata->cs_gpio;
> +	ddata->scl_gpio = pdata->scl_gpio;
> +	ddata->din_gpio = pdata->din_gpio;
> +	ddata->dout_gpio = pdata->dout_gpio;
> +
> +	return 0;
> +}
> +
> +static int td028ttec1_panel_probe(struct platform_device *pdev)
> +{
> +	struct panel_drv_data *ddata;
> +	struct omap_dss_device *dssdev;
> +	int r;
> +
> +	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
> +	if (ddata == NULL)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, ddata);
> +
> +	if (dev_get_platdata(&pdev->dev)) {
> +		r = td028ttec1_panel_probe_pdata(pdev);
> +		if (r)
> +			return r;
> +	} else {
> +		return -ENODEV;
> +	}
> +
> +	if (gpio_is_valid(ddata->cs_gpio)) {
> +		r = devm_gpio_request_one(&pdev->dev, ddata->cs_gpio,
> +				GPIOF_OUT_INIT_HIGH, "lcd cs");
> +		if (r)
> +			goto err_gpio;
> +	}
> +
> +	if (gpio_is_valid(ddata->scl_gpio)) {
> +		r = devm_gpio_request_one(&pdev->dev, ddata->scl_gpio,
> +				GPIOF_OUT_INIT_HIGH, "lcd scl");
> +		if (r)
> +			goto err_gpio;
> +	}
> +
> +	if (gpio_is_valid(ddata->dout_gpio)) {
> +		r = devm_gpio_request_one(&pdev->dev, ddata->dout_gpio,
> +				GPIOF_OUT_INIT_LOW, "lcd dout");
> +		if (r)
> +			goto err_gpio;
> +	}
> +
> +	if (gpio_is_valid(ddata->din_gpio)) {
> +		r = devm_gpio_request_one(&pdev->dev, ddata->din_gpio,
> +				GPIOF_IN, "lcd din");
> +		if (r)
> +			goto err_gpio;
> +	}
> +
> +	/* according to data sheet: wait 50ms (Tpos of LCM). However, 50ms
> +	 * seems unreliable with later LCM batches, increasing to 90ms */
> +	mdelay(90);

What is this delay for? Usually sleeps/delays should be between two "HW
actions". Here there's nothing below this line that would count as such
an action.

> +	ddata->videomode = td028ttec1_panel_timings;
> +
> +	dssdev = &ddata->dssdev;
> +	dssdev->dev = &pdev->dev;
> +	dssdev->driver = &td028ttec1_ops;
> +	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
> +	dssdev->owner = THIS_MODULE;
> +	dssdev->panel.timings = ddata->videomode;
> +	dssdev->phy.dpi.data_lines = ddata->data_lines;
> +
> +	r = omapdss_register_display(dssdev);
> +	if (r) {
> +		dev_err(&pdev->dev, "Failed to register panel\n");
> +		goto err_reg;
> +	}
> +
> +	return 0;
> +
> +err_reg:
> +err_gpio:
> +	omap_dss_put_device(ddata->in);
> +	return r;
> +}
> +

 Tomi




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

^ permalink raw reply

* [PATCH v3 3/3] video: xilinxfb: Simplify error path
From: Michal Simek @ 2013-10-10  6:30 UTC (permalink / raw)
  To: linux-kernel, monstr
  Cc: joe, Jean-Christophe Plagniol-Villard, Tomi Valkeinen,
	linux-fbdev
In-Reply-To: <631df9a44b366af4129d00f1d4e1d3baad7d4903.1381386616.git.michal.simek@xilinx.com>

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

devm_iounmap is called automatically that's why remove it from the code
dev_set_drvdata(dev, NULL) is called by generic code
after device_release or on probe failure.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>

Reviewed-by: Jingoo Han <jg1.han@samsung.com>
---
Changes in v3: None
Changes in v2:
- Rebased on git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git for-next

 drivers/video/xilinxfb.c | 24 ++++++------------------
 1 file changed, 6 insertions(+), 18 deletions(-)

diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index c420328..9eedf96 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -260,10 +260,9 @@ static int xilinxfb_assign(struct platform_device *pdev,

 		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 		drvdata->regs = devm_ioremap_resource(&pdev->dev, res);
-		if (IS_ERR(drvdata->regs)) {
-			rc = PTR_ERR(drvdata->regs);
-			goto err_region;
-		}
+		if (IS_ERR(drvdata->regs))
+			return PTR_ERR(drvdata->regs);
+
 		drvdata->regs_phys = res->start;
 	}

@@ -279,11 +278,7 @@ static int xilinxfb_assign(struct platform_device *pdev,

 	if (!drvdata->fb_virt) {
 		dev_err(dev, "Could not allocate frame buffer memory\n");
-		rc = -ENOMEM;
-		if (drvdata->flags & BUS_ACCESS_FLAG)
-			goto err_fbmem;
-		else
-			goto err_region;
+		return -ENOMEM;
 	}

 	/* Clear (turn to black) the framebuffer */
@@ -363,11 +358,6 @@ err_cmap:
 	/* Turn off the display */
 	xilinx_fb_out32(drvdata, REG_CTRL, 0);

-err_fbmem:
-	if (drvdata->flags & BUS_ACCESS_FLAG)
-		devm_iounmap(dev, drvdata->regs);
-
-err_region:
 	return rc;
 }

@@ -392,11 +382,9 @@ static int xilinxfb_release(struct device *dev)
 	/* Turn off the display */
 	xilinx_fb_out32(drvdata, REG_CTRL, 0);

-	/* Release the resources, as allocated based on interface */
-	if (drvdata->flags & BUS_ACCESS_FLAG)
-		devm_iounmap(dev, drvdata->regs);
 #ifdef CONFIG_PPC_DCR
-	else
+	/* Release the resources, as allocated based on interface */
+	if (!(drvdata->flags & BUS_ACCESS_FLAG))
 		dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
 #endif

--
1.8.2.3


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

^ permalink raw reply related

* [PATCH v3 2/3] video: xilinxfb: Use devm_kzalloc instead of kzalloc
From: Michal Simek @ 2013-10-10  6:30 UTC (permalink / raw)
  To: linux-kernel, monstr
  Cc: joe, Jean-Christophe Plagniol-Villard, Tomi Valkeinen,
	linux-fbdev
In-Reply-To: <631df9a44b366af4129d00f1d4e1d3baad7d4903.1381386616.git.michal.simek@xilinx.com>

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

Simplify driver probe and release function.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Reviewed-by: Jingoo Han <jg1.han@samsung.com>
---
Changes in v3:
- Remove the unnecessary OOM message

Changes in v2:
- Rebased on git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git for-next

 drivers/video/xilinxfb.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index d12345f..c420328 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -368,8 +368,6 @@ err_fbmem:
 		devm_iounmap(dev, drvdata->regs);

 err_region:
-	kfree(drvdata);
-
 	return rc;
 }

@@ -402,8 +400,6 @@ static int xilinxfb_release(struct device *dev)
 		dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
 #endif

-	kfree(drvdata);
-
 	return 0;
 }

@@ -423,11 +419,9 @@ static int xilinxfb_of_probe(struct platform_device *pdev)
 	pdata = xilinx_fb_default_pdata;

 	/* Allocate the driver data region */
-	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
-	if (!drvdata) {
-		dev_err(&pdev->dev, "Couldn't allocate device private record\n");
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
 		return -ENOMEM;
-	}

 	/*
 	 * To check whether the core is connected directly to DCR or BUS
@@ -451,7 +445,6 @@ static int xilinxfb_of_probe(struct platform_device *pdev)
 		drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len);
 		if (!DCR_MAP_OK(drvdata->dcr_host)) {
 			dev_err(&op->dev, "invalid DCR address\n");
-			kfree(drvdata);
 			return -ENODEV;
 		}
 	}
--
1.8.2.3


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

^ permalink raw reply related

* [PATCH v3 1/3] video: xilinxfb: Use standard variable name convention
From: Michal Simek @ 2013-10-10  6:30 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	monstr-pSz03upnqPeHXe+LvDLADg
  Cc: joe-6d6DIl74uiNBDgjK7y7TUQ, Jean-Christophe Plagniol-Villard,
	Tomi Valkeinen, Grant Likely, Rob Herring,
	linux-fbdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

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

s/op/pdev/ in xilinxfb_of_probe().
No functional chagnes.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
Changes in v3: None
Changes in v2: None

 drivers/video/xilinxfb.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
index 0e1dd33..d12345f 100644
--- a/drivers/video/xilinxfb.c
+++ b/drivers/video/xilinxfb.c
@@ -411,7 +411,7 @@ static int xilinxfb_release(struct device *dev)
  * OF bus binding
  */

-static int xilinxfb_of_probe(struct platform_device *op)
+static int xilinxfb_of_probe(struct platform_device *pdev)
 {
 	const u32 *prop;
 	u32 tft_access = 0;
@@ -425,7 +425,7 @@ static int xilinxfb_of_probe(struct platform_device *op)
 	/* Allocate the driver data region */
 	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
 	if (!drvdata) {
-		dev_err(&op->dev, "Couldn't allocate device private record\n");
+		dev_err(&pdev->dev, "Couldn't allocate device private record\n");
 		return -ENOMEM;
 	}

@@ -433,7 +433,7 @@ static int xilinxfb_of_probe(struct platform_device *op)
 	 * To check whether the core is connected directly to DCR or BUS
 	 * interface and initialize the tft_access accordingly.
 	 */
-	of_property_read_u32(op->dev.of_node, "xlnx,dcr-splb-slave-if",
+	of_property_read_u32(pdev->dev.of_node, "xlnx,dcr-splb-slave-if",
 			     &tft_access);

 	/*
@@ -457,29 +457,29 @@ static int xilinxfb_of_probe(struct platform_device *op)
 	}
 #endif

-	prop = of_get_property(op->dev.of_node, "phys-size", &size);
+	prop = of_get_property(pdev->dev.of_node, "phys-size", &size);
 	if ((prop) && (size >= sizeof(u32)*2)) {
 		pdata.screen_width_mm = prop[0];
 		pdata.screen_height_mm = prop[1];
 	}

-	prop = of_get_property(op->dev.of_node, "resolution", &size);
+	prop = of_get_property(pdev->dev.of_node, "resolution", &size);
 	if ((prop) && (size >= sizeof(u32)*2)) {
 		pdata.xres = prop[0];
 		pdata.yres = prop[1];
 	}

-	prop = of_get_property(op->dev.of_node, "virtual-resolution", &size);
+	prop = of_get_property(pdev->dev.of_node, "virtual-resolution", &size);
 	if ((prop) && (size >= sizeof(u32)*2)) {
 		pdata.xvirt = prop[0];
 		pdata.yvirt = prop[1];
 	}

-	if (of_find_property(op->dev.of_node, "rotate-display", NULL))
+	if (of_find_property(pdev->dev.of_node, "rotate-display", NULL))
 		pdata.rotate_screen = 1;

-	dev_set_drvdata(&op->dev, drvdata);
-	return xilinxfb_assign(op, drvdata, &pdata);
+	dev_set_drvdata(&pdev->dev, drvdata);
+	return xilinxfb_assign(pdev, drvdata, &pdata);
 }

 static int xilinxfb_of_remove(struct platform_device *op)
--
1.8.2.3


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

^ permalink raw reply related

* Re: [PATCH v2 2/3] video: xilinxfb: Use devm_kzalloc instead of kzalloc
From: Michal Simek @ 2013-10-10  6:24 UTC (permalink / raw)
  To: Joe Perches
  Cc: Michal Simek, linux-kernel, monstr,
	Jean-Christophe Plagniol-Villard, Tomi Valkeinen, linux-fbdev
In-Reply-To: <1381331583.2050.11.camel@joe-AO722>

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

On 10/09/2013 05:13 PM, Joe Perches wrote:
> On Wed, 2013-10-09 at 12:52 +0200, Michal Simek wrote:
>> Simplify driver probe and release function.
> []
>> diff --git a/drivers/video/xilinxfb.c b/drivers/video/xilinxfb.c
> []
>> @@ -423,7 +419,7 @@ static int xilinxfb_of_probe(struct platform_device *pdev)
>>  	pdata = xilinx_fb_default_pdata;
>>
>>  	/* Allocate the driver data region */
>> -	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
>> +	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
>>  	if (!drvdata) {
>>  		dev_err(&pdev->dev, "Couldn't allocate device private record\n");
> 
> Be nice to remove the unnecessary OOM message.
> There's already a generic dump_stack on OOM.

Ah yeah - this series was made before I knew this.
Will send v3.

Thanks,
Michal


-- 
Michal Simek, Ing. (M.Eng), OpenPGP -> KeyID: FE3D1F91
w: www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel - Microblaze cpu - http://www.monstr.eu/fdt/
Maintainer of Linux kernel - Xilinx Zynq ARM architecture
Microblaze U-BOOT custodian and responsible for u-boot arm zynq platform



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

^ permalink raw reply

* [PATCH] video: da8xx-fb: remove unwanted define
From: Manish Badarkhe @ 2013-10-10  4:40 UTC (permalink / raw)
  To: linux-fbdev, linux-kernel
  Cc: plagnioj, tomi.valkeinen, nsekhar, badarkhe.manish

Remove unwanted define "WSI_TIMEOUT" present in code.

Signed-off-by: Manish Badarkhe <badarkhe.manish@gmail.com>
---
:100644 100644 e030e17... fa61323... M	drivers/video/da8xx-fb.c
 drivers/video/da8xx-fb.c |    1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index e030e17..fa61323 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -129,7 +129,6 @@
 
 #define LCD_NUM_BUFFERS	2
 
-#define WSI_TIMEOUT	50
 #define PALETTE_SIZE	256
 
 #define	CLK_MIN_DIV	2
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH] omapdss: Add new panel driver for Topolly td028ttec1 LCD.
From: Marek Belisko @ 2013-10-09 21:08 UTC (permalink / raw)
  To: tomi.valkeinen, plagnioj
  Cc: linux-kernel, linux-omap, linux-fbdev, Marek Belisko,
	H. Nikolaus Schaller

For communicating with driver is used gpio bitbanging because TD028 does
not have a standard compliant SPI interface. It is a 3-wire thing with
direction reversal.

Communication with display is used only during panel enable/disable so it's
not performance issue.

Signed-off-by: Marek Belisko <marek@goldelico.com>
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
 drivers/video/omap2/displays-new/Kconfig           |   6 +
 drivers/video/omap2/displays-new/Makefile          |   1 +
 .../omap2/displays-new/panel-tpo-td028ttec1.c      | 537 +++++++++++++++++++++
 include/video/omap-panel-data.h                    |  22 +
 4 files changed, 566 insertions(+)
 create mode 100644 drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c

diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
index 6c90885..3f86432 100644
--- a/drivers/video/omap2/displays-new/Kconfig
+++ b/drivers/video/omap2/displays-new/Kconfig
@@ -56,6 +56,11 @@ config DISPLAY_PANEL_SHARP_LS037V7DW01
         help
           LCD Panel used in TI's SDP3430 and EVM boards
 
+config DISPLAY_PANEL_TPO_TD028TTEC1
+        tristate "TPO TD028TTEC1 LCD Panel"
+        help
+          LCD panel used by Openmoko.
+
 config DISPLAY_PANEL_TPO_TD043MTEA1
         tristate "TPO TD043MTEA1 LCD Panel"
         depends on SPI
@@ -70,4 +75,5 @@ config DISPLAY_PANEL_NEC_NL8048HL11
 		This NEC NL8048HL11 panel is TFT LCD used in the
 		Zoom2/3/3630 sdp boards.
 
+
 endmenu
diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
index 5aeb11b..0323a8a 100644
--- a/drivers/video/omap2/displays-new/Makefile
+++ b/drivers/video/omap2/displays-new/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
 obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
 obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+obj-$(CONFIG_DISPLAY_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
diff --git a/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
new file mode 100644
index 0000000..b63586e
--- /dev/null
+++ b/drivers/video/omap2/displays-new/panel-tpo-td028ttec1.c
@@ -0,0 +1,537 @@
+/*
+ * Toppoly TD028TTEC1 panel support
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
+ *
+ * Neo 1973 code (jbt6k74.c):
+ * Copyright (C) 2006-2007 by OpenMoko, Inc.
+ * Author: Harald Welte <laforge@openmoko.org>
+ *
+ * Ported and adapted from Neo 1973 U-Boot by:
+ * H. Nikolaus Schaller <hns@goldelico.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+
+struct panel_drv_data {
+	struct omap_dss_device dssdev;
+	struct omap_dss_device *in;
+
+	int data_lines;
+
+	struct omap_video_timings videomode;
+
+	int cs_gpio;
+	int scl_gpio;
+	int din_gpio;
+	int dout_gpio;
+
+	u_int16_t tx_buf[4];
+};
+
+static struct omap_video_timings td028ttec1_panel_timings = {
+	.x_res		= 480,
+	.y_res		= 640,
+	.pixel_clock	= 22153,
+	.hfp		= 24,
+	.hsw		= 8,
+	.hbp		= 8,
+	.vfp		= 4,
+	.vsw		= 2,
+	.vbp		= 2,
+
+	.vsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
+	.hsync_level	= OMAPDSS_SIG_ACTIVE_LOW,
+
+	.data_pclk_edge	= OMAPDSS_DRIVE_SIG_FALLING_EDGE,
+	.de_level	= OMAPDSS_SIG_ACTIVE_HIGH,
+	.sync_pclk_edge	= OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES,
+};
+
+#define JBT_COMMAND	0x000
+#define JBT_DATA	0x100
+
+/* 150uS minimum clock cycle, we have two of this plus our other
+ * instructions */
+
+#define SPI_DELAY()	udelay(200)
+
+static int jbt_spi_xfer(struct panel_drv_data *data, int wordnum, int bitlen)
+{
+	u_int16_t tmpdout = 0;
+	int   i, j;
+
+	gpio_set_value(data->cs_gpio, 0);
+
+	for (i = 0; i < wordnum; i++) {
+		tmpdout = data->tx_buf[i];
+
+		for (j = 0; j < bitlen; j++) {
+			gpio_set_value(data->scl_gpio, 0);
+			if (tmpdout & (1 << (bitlen-1))) {
+				gpio_set_value(data->dout_gpio, 1);
+				if (gpio_get_value(data->din_gpio) = 0)
+					return 1;
+			} else {
+				gpio_set_value(data->dout_gpio, 0);
+				if (gpio_get_value(data->din_gpio) != 0)
+					return 1;
+			}
+			SPI_DELAY();
+			gpio_set_value(data->scl_gpio, 1);
+			SPI_DELAY();
+			tmpdout <<= 1;
+		}
+	}
+
+	gpio_set_value(data->cs_gpio, 1);
+
+	return 0;
+}
+
+#define JBT_COMMAND	0x000
+#define JBT_DATA	0x100
+
+int jbt_reg_write_nodata(struct panel_drv_data *ddata, u_int8_t reg)
+{
+	int rc;
+
+	ddata->tx_buf[0] = JBT_COMMAND | reg;
+
+	rc = jbt_spi_xfer(ddata, 1, 9);
+	if (rc)
+		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
+
+	return rc;
+}
+
+int jbt_reg_write(struct panel_drv_data *ddata, u_int8_t reg, u_int8_t data)
+{
+	int rc;
+
+	ddata->tx_buf[0] = JBT_COMMAND | reg;
+	ddata->tx_buf[1] = JBT_DATA | data;
+
+	rc = jbt_spi_xfer(ddata, 2, 9);
+	if (rc)
+		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
+
+	return rc;
+}
+
+int jbt_reg_write16(struct panel_drv_data *ddata, u_int8_t reg, u_int16_t data)
+{
+	int rc;
+
+	ddata->tx_buf[0] = JBT_COMMAND | reg;
+	ddata->tx_buf[1] = JBT_DATA | (data >> 8);
+	ddata->tx_buf[2] = JBT_DATA | (data & 0xff);
+
+	rc = jbt_spi_xfer(ddata, 3, 9);
+	if (rc)
+		dev_warn(ddata->dssdev.dev, "Failed to write reg: %x\n", reg);
+
+	return rc;
+}
+
+enum jbt_register {
+	JBT_REG_SLEEP_IN		= 0x10,
+	JBT_REG_SLEEP_OUT		= 0x11,
+
+	JBT_REG_DISPLAY_OFF		= 0x28,
+	JBT_REG_DISPLAY_ON		= 0x29,
+
+	JBT_REG_RGB_FORMAT		= 0x3a,
+	JBT_REG_QUAD_RATE		= 0x3b,
+
+	JBT_REG_POWER_ON_OFF		= 0xb0,
+	JBT_REG_BOOSTER_OP		= 0xb1,
+	JBT_REG_BOOSTER_MODE		= 0xb2,
+	JBT_REG_BOOSTER_FREQ		= 0xb3,
+	JBT_REG_OPAMP_SYSCLK		= 0xb4,
+	JBT_REG_VSC_VOLTAGE		= 0xb5,
+	JBT_REG_VCOM_VOLTAGE		= 0xb6,
+	JBT_REG_EXT_DISPL		= 0xb7,
+	JBT_REG_OUTPUT_CONTROL		= 0xb8,
+	JBT_REG_DCCLK_DCEV		= 0xb9,
+	JBT_REG_DISPLAY_MODE1		= 0xba,
+	JBT_REG_DISPLAY_MODE2		= 0xbb,
+	JBT_REG_DISPLAY_MODE		= 0xbc,
+	JBT_REG_ASW_SLEW		= 0xbd,
+	JBT_REG_DUMMY_DISPLAY		= 0xbe,
+	JBT_REG_DRIVE_SYSTEM		= 0xbf,
+
+	JBT_REG_SLEEP_OUT_FR_A		= 0xc0,
+	JBT_REG_SLEEP_OUT_FR_B		= 0xc1,
+	JBT_REG_SLEEP_OUT_FR_C		= 0xc2,
+	JBT_REG_SLEEP_IN_LCCNT_D	= 0xc3,
+	JBT_REG_SLEEP_IN_LCCNT_E	= 0xc4,
+	JBT_REG_SLEEP_IN_LCCNT_F	= 0xc5,
+	JBT_REG_SLEEP_IN_LCCNT_G	= 0xc6,
+
+	JBT_REG_GAMMA1_FINE_1		= 0xc7,
+	JBT_REG_GAMMA1_FINE_2		= 0xc8,
+	JBT_REG_GAMMA1_INCLINATION	= 0xc9,
+	JBT_REG_GAMMA1_BLUE_OFFSET	= 0xca,
+
+	JBT_REG_BLANK_CONTROL		= 0xcf,
+	JBT_REG_BLANK_TH_TV		= 0xd0,
+	JBT_REG_CKV_ON_OFF		= 0xd1,
+	JBT_REG_CKV_1_2			= 0xd2,
+	JBT_REG_OEV_TIMING		= 0xd3,
+	JBT_REG_ASW_TIMING_1		= 0xd4,
+	JBT_REG_ASW_TIMING_2		= 0xd5,
+
+	JBT_REG_HCLOCK_VGA		= 0xec,
+	JBT_REG_HCLOCK_QVGA		= 0xed,
+};
+
+#define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
+
+static int td028ttec1_panel_connect(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+	int r;
+
+	if (omapdss_device_is_connected(dssdev))
+		return 0;
+
+	r = in->ops.dpi->connect(in, dssdev);
+	if (r)
+		return r;
+
+	return 0;
+}
+
+static void td028ttec1_panel_disconnect(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+
+	if (!omapdss_device_is_connected(dssdev))
+		return;
+
+	in->ops.dpi->disconnect(in, dssdev);
+}
+
+static int td028ttec1_panel_enable(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+	int r;
+
+	if (!omapdss_device_is_connected(dssdev))
+		return -ENODEV;
+
+	if (omapdss_device_is_enabled(dssdev))
+		return 0;
+
+	in->ops.dpi->set_data_lines(in, ddata->data_lines);
+	in->ops.dpi->set_timings(in, &ddata->videomode);
+
+	r = in->ops.dpi->enable(in);
+	if (r)
+		return r;
+
+	dev_dbg(dssdev->dev, "td028ttec1_panel_enable() - state %d\n",
+		dssdev->state);
+
+	/* three times command zero */
+	r |= jbt_reg_write_nodata(ddata, 0x00);
+	udelay(1000);
+	r |= jbt_reg_write_nodata(ddata, 0x00);
+	udelay(1000);
+	r |= jbt_reg_write_nodata(ddata, 0x00);
+	udelay(1000);
+
+	/* deep standby out */
+	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x17);
+
+	/* RGB I/F on, RAM write off, QVGA through, SIGCON enable */
+	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE, 0x80);
+
+	/* Quad mode off */
+	r |= jbt_reg_write(ddata, JBT_REG_QUAD_RATE, 0x00);
+
+	/* AVDD on, XVDD on */
+	r |= jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x16);
+
+	/* Output control */
+	r |= jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0xfff9);
+
+	/* Sleep mode off */
+	r |= jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_OUT);
+
+	/* at this point we have like 50% grey */
+
+	/* initialize register set */
+	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE1, 0x01);
+	r |= jbt_reg_write(ddata, JBT_REG_DISPLAY_MODE2, 0x00);
+	r |= jbt_reg_write(ddata, JBT_REG_RGB_FORMAT, 0x60);
+	r |= jbt_reg_write(ddata, JBT_REG_DRIVE_SYSTEM, 0x10);
+	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_OP, 0x56);
+	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_MODE, 0x33);
+	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
+	r |= jbt_reg_write(ddata, JBT_REG_BOOSTER_FREQ, 0x11);
+	r |= jbt_reg_write(ddata, JBT_REG_OPAMP_SYSCLK, 0x02);
+	r |= jbt_reg_write(ddata, JBT_REG_VSC_VOLTAGE, 0x2b);
+	r |= jbt_reg_write(ddata, JBT_REG_VCOM_VOLTAGE, 0x40);
+	r |= jbt_reg_write(ddata, JBT_REG_EXT_DISPL, 0x03);
+	r |= jbt_reg_write(ddata, JBT_REG_DCCLK_DCEV, 0x04);
+	/*
+	 * default of 0x02 in JBT_REG_ASW_SLEW responsible for 72Hz requirement
+	 * to avoid red / blue flicker
+	 */
+	r |= jbt_reg_write(ddata, JBT_REG_ASW_SLEW, 0x04);
+	r |= jbt_reg_write(ddata, JBT_REG_DUMMY_DISPLAY, 0x00);
+
+	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_A, 0x11);
+	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_B, 0x11);
+	r |= jbt_reg_write(ddata, JBT_REG_SLEEP_OUT_FR_C, 0x11);
+	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_D, 0x2040);
+	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_E, 0x60c0);
+	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_F, 0x1020);
+	r |= jbt_reg_write16(ddata, JBT_REG_SLEEP_IN_LCCNT_G, 0x60c0);
+
+	r |= jbt_reg_write16(ddata, JBT_REG_GAMMA1_FINE_1, 0x5533);
+	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_FINE_2, 0x00);
+	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_INCLINATION, 0x00);
+	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
+	r |= jbt_reg_write(ddata, JBT_REG_GAMMA1_BLUE_OFFSET, 0x00);
+
+	r |= jbt_reg_write16(ddata, JBT_REG_HCLOCK_VGA, 0x1f0);
+	r |= jbt_reg_write(ddata, JBT_REG_BLANK_CONTROL, 0x02);
+	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
+	r |= jbt_reg_write16(ddata, JBT_REG_BLANK_TH_TV, 0x0804);
+
+	r |= jbt_reg_write(ddata, JBT_REG_CKV_ON_OFF, 0x01);
+	r |= jbt_reg_write16(ddata, JBT_REG_CKV_1_2, 0x0000);
+
+	r |= jbt_reg_write16(ddata, JBT_REG_OEV_TIMING, 0x0d0e);
+	r |= jbt_reg_write16(ddata, JBT_REG_ASW_TIMING_1, 0x11a4);
+	r |= jbt_reg_write(ddata, JBT_REG_ASW_TIMING_2, 0x0e);
+
+	r |= jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_ON);
+
+	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+	return r;
+}
+
+static void td028ttec1_panel_disable(struct omap_dss_device *dssdev)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+
+	if (!omapdss_device_is_enabled(dssdev))
+		return;
+
+	dev_dbg(dssdev->dev, "td028ttec1_panel_disable()\n");
+
+	in->ops.dpi->disable(in);
+
+	jbt_reg_write_nodata(ddata, JBT_REG_DISPLAY_OFF);
+	jbt_reg_write16(ddata, JBT_REG_OUTPUT_CONTROL, 0x8002);
+	jbt_reg_write_nodata(ddata, JBT_REG_SLEEP_IN);
+	jbt_reg_write(ddata, JBT_REG_POWER_ON_OFF, 0x00);
+
+	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static void td028ttec1_panel_set_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+
+	ddata->videomode = *timings;
+	dssdev->panel.timings = *timings;
+
+	in->ops.dpi->set_timings(in, timings);
+}
+
+static void td028ttec1_panel_get_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+
+	*timings = ddata->videomode;
+}
+
+static int td028ttec1_panel_check_timings(struct omap_dss_device *dssdev,
+		struct omap_video_timings *timings)
+{
+	struct panel_drv_data *ddata = to_panel_data(dssdev);
+	struct omap_dss_device *in = ddata->in;
+
+	return in->ops.dpi->check_timings(in, timings);
+}
+
+static struct omap_dss_driver td028ttec1_ops = {
+	.connect	= td028ttec1_panel_connect,
+	.disconnect	= td028ttec1_panel_disconnect,
+
+	.enable		= td028ttec1_panel_enable,
+	.disable	= td028ttec1_panel_disable,
+
+	.set_timings	= td028ttec1_panel_set_timings,
+	.get_timings	= td028ttec1_panel_get_timings,
+	.check_timings	= td028ttec1_panel_check_timings,
+};
+
+static int td028ttec1_panel_probe_pdata(struct platform_device *pdev)
+{
+	const struct panel_tpo_td028tec1_platform_data *pdata;
+	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+	struct omap_dss_device *dssdev, *in;
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	in = omap_dss_find_output(pdata->source);
+	if (in = NULL) {
+		dev_err(&pdev->dev, "failed to find video source '%s'\n",
+				pdata->source);
+		return -EPROBE_DEFER;
+	}
+
+	ddata->in = in;
+
+	ddata->data_lines = pdata->data_lines;
+
+	dssdev = &ddata->dssdev;
+	dssdev->name = pdata->name;
+
+	ddata->cs_gpio = pdata->cs_gpio;
+	ddata->scl_gpio = pdata->scl_gpio;
+	ddata->din_gpio = pdata->din_gpio;
+	ddata->dout_gpio = pdata->dout_gpio;
+
+	return 0;
+}
+
+static int td028ttec1_panel_probe(struct platform_device *pdev)
+{
+	struct panel_drv_data *ddata;
+	struct omap_dss_device *dssdev;
+	int r;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (ddata = NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ddata);
+
+	if (dev_get_platdata(&pdev->dev)) {
+		r = td028ttec1_panel_probe_pdata(pdev);
+		if (r)
+			return r;
+	} else {
+		return -ENODEV;
+	}
+
+	if (gpio_is_valid(ddata->cs_gpio)) {
+		r = devm_gpio_request_one(&pdev->dev, ddata->cs_gpio,
+				GPIOF_OUT_INIT_HIGH, "lcd cs");
+		if (r)
+			goto err_gpio;
+	}
+
+	if (gpio_is_valid(ddata->scl_gpio)) {
+		r = devm_gpio_request_one(&pdev->dev, ddata->scl_gpio,
+				GPIOF_OUT_INIT_HIGH, "lcd scl");
+		if (r)
+			goto err_gpio;
+	}
+
+	if (gpio_is_valid(ddata->dout_gpio)) {
+		r = devm_gpio_request_one(&pdev->dev, ddata->dout_gpio,
+				GPIOF_OUT_INIT_LOW, "lcd dout");
+		if (r)
+			goto err_gpio;
+	}
+
+	if (gpio_is_valid(ddata->din_gpio)) {
+		r = devm_gpio_request_one(&pdev->dev, ddata->din_gpio,
+				GPIOF_IN, "lcd din");
+		if (r)
+			goto err_gpio;
+	}
+
+	/* according to data sheet: wait 50ms (Tpos of LCM). However, 50ms
+	 * seems unreliable with later LCM batches, increasing to 90ms */
+	mdelay(90);
+
+	ddata->videomode = td028ttec1_panel_timings;
+
+	dssdev = &ddata->dssdev;
+	dssdev->dev = &pdev->dev;
+	dssdev->driver = &td028ttec1_ops;
+	dssdev->type = OMAP_DISPLAY_TYPE_DPI;
+	dssdev->owner = THIS_MODULE;
+	dssdev->panel.timings = ddata->videomode;
+	dssdev->phy.dpi.data_lines = ddata->data_lines;
+
+	r = omapdss_register_display(dssdev);
+	if (r) {
+		dev_err(&pdev->dev, "Failed to register panel\n");
+		goto err_reg;
+	}
+
+	return 0;
+
+err_reg:
+err_gpio:
+	omap_dss_put_device(ddata->in);
+	return r;
+}
+
+static int __exit td028ttec1_panel_remove(struct platform_device *pdev)
+{
+	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
+	struct omap_dss_device *dssdev = &ddata->dssdev;
+	struct omap_dss_device *in = ddata->in;
+
+	omapdss_unregister_display(dssdev);
+
+	td028ttec1_panel_disable(dssdev);
+	td028ttec1_panel_disconnect(dssdev);
+
+	omap_dss_put_device(in);
+
+	return 0;
+}
+
+static struct platform_driver td028ttec1_driver = {
+	.probe		= td028ttec1_panel_probe,
+	.remove		= __exit_p(td028ttec1_panel_remove),
+
+	.driver         = {
+		.name   = "panel-tpo-td028ttec1",
+		.owner  = THIS_MODULE,
+	},
+};
+
+module_platform_driver(td028ttec1_driver);
+
+MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
+MODULE_DESCRIPTION("Toppoly TD028TTEC1 panel driver");
+MODULE_LICENSE("GPL");
diff --git a/include/video/omap-panel-data.h b/include/video/omap-panel-data.h
index f7ac8d9..02565ff 100644
--- a/include/video/omap-panel-data.h
+++ b/include/video/omap-panel-data.h
@@ -238,4 +238,26 @@ struct panel_nec_nl8048hl11_platform_data {
 	int qvga_gpio;
 };
 
+/**
+ * panel-tpo-td028tec1 platform data
+ * @name: name for display netity
+ * @source: name of the display entity used as a video source
+ * @data_lines: number of DPI datalines
+ * @cs_gpio: CS gpio
+ * @scl_gpio: CLK gpio
+ * @din_gpio: input data gpio
+ * @dout_gpio: output data gpio
+ */
+struct panel_tpo_td028tec1_platform_data {
+	const char *name;
+	const char *source;
+
+	int data_lines;
+
+	int cs_gpio;
+	int scl_gpio;
+	int din_gpio;
+	int dout_gpio;
+};
+
 #endif /* __OMAP_PANEL_DATA_H */
-- 
1.8.1.2


^ permalink raw reply related


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