linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RFC v3 00/19] Common Display Framework
@ 2013-08-09 23:02 Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 01/19] OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_* Laurent Pinchart
                   ` (18 more replies)
  0 siblings, 19 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:02 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Hi everybody,

Here's the third RFC of the Common Display Framework. This is a resent, the
series I've sent earlier seems not to have made it to the vger mailing lists,
possibly due to a too long list of CCs (the other explanation being that CDF
has been delayed for so long that vger considers it as spam, which I really
hope isn't the case :-)). I've thus dropped the CCs, sorry about that.

I won't repeat all the background information from the versions one and two
here, you can read it at http://lwn.net/Articles/512363/ and
http://lwn.net/Articles/526965/.

This RFC isn't final. Given the high interest in CDF and the urgent tasks that
kept delaying the next version of the patch set, I've decided to release v3
before completing all parts of the implementation. Known missing items are

- documentation: kerneldoc and this cover letter should provide basic
  information, more extensive documentation will likely make it to v4.

- pipeline configuration and control: generic code to configure and control
  display pipelines (in a nutshell, translating high-level mode setting and
  DPMS calls to low-level entity operations) is missing. Video and stream
  control operations have been carried over from v2, but will need to be
  revised for v4.

- DSI support: I still have no DSI hardware I can easily test the code on.

Special thanks go to

- Renesas for inviting me to LinuxCon Japan 2013 where I had the opportunity
  to validate the CDF v3 concepts with Alexandre Courbot (NVidia) and Tomasz
  Figa (Samsung).

- Tomi Valkeinen (TI) for taking the time to deeply brainstorm v3 with me.

- Linaro for inviting me to Linaro Connect Europe 2013, the discussions we had
  there greatly helped moving CDF forward.

- And of course all the developers who showed interest in CDF and spent time
  sharing ideas, reviewing patches and testing code.

I have to confess I was a bit lost and discouraged after all the CDF-related
meetings during which we have discussed how to move from v2 to v3. With every
meeting I was hoping to run the implementation through use cases of various
interesting parties and narrow down the scope of the huge fuzzy beast that CDF
was. With every meeting the scope actually broadened, with no clear path at
sight anywhere.

Earlier this year I was about to drop one of the requirements on which I had
based CDF v2: sharing drivers between DRM/KMS and V4L2. With only two HDMI
transmitters as use cases for that feature (with only out-of-tree drivers so
far), I just thought the involved completely wasn't worth it and that I should
implement CDF v3 as a DRM/KMS-only helper framework. However, a seemingly
unrelated discussion with Xilinx developers showed me that hybrid SoC-FPGA
platforms such as the Xilinx Zynq 7000 have a larger library of IP cores that
can be used in camera capture pipelines and in display pipelines. The two use
cases suddenly became tens or even hundreds of use cases that I couldn't
ignore anymore.

CDF v3 is thus userspace API agnostic. It isn't tied to DRM/KMS or V4L2 and
can be used by any kernel subsystem, potentially including FBDEV (although I
won't personally wrote FBDEV support code, as I've already advocated for FBDEV
to be deprecated).

The code you are about to read is based on the concept of display entities
introduced in v2. Diagrams related to the explanations below are available at
http://ideasonboard.org/media/cdf/20130709-lce-cdf.pdf.


Display Entities
----------------

A display entity abstracts any hardware block that sources, processes or sinks
display-related video streams. It offers an abstract API, implemented by display
entity drivers, that is used by master drivers (such as the main display driver)
to query, configure and control display pipelines.

Display entities are connected to at least one video data bus, and optionally
to a control bus. The video data busses carry display-related video data out
of sources (such as a CRTC in a display controller) to sinks (such as a panel
or a monitor), optionally going through transmitters, encoders, decoders,
bridges or other similar devices. A CRTC or a panel will usually be connected
to a single data bus, while an encoder or a transmitter will be connected to
two data busses.

The simple linear display pipelines we find in most embedded platforms at the
moment are expected to grow more complex with time. CDF needs to accomodate
those needs from the start to be, if not future-proof, at least present-proof
at the time it will get merged in to mainline. For this reason display
entities have data ports through which video streams flow in or out, with link
objects representing the connections between those ports. A typical entity in
a linear display pipeline will have one (for video source and video sink
entities such as CRTCs or panels) or two ports (for video processing entities
such as encoders), but more ports are allowed, and entities can be linked in
complex non-linear pipelines.

Readers might think that this model if extremely similar to the media
controller graph model. They would be right, and given my background this is
most probably not a coincidence. The CDF v3 implementation uses the in-kernel
media controller framework to model the graph of display entities, with the
display entity data structure inheriting from the media entity structure. The
display pipeline graph topology will be automatically exposed to userspace
through the media controller API as an added bonus. However, ussage of the
media controller userspace API in applications is *not* mandatory, and the
current CDF implementation doesn't use the media controller link setup
userspace API to configure the display pipelines.

While some display entities don't require any configuration (DPI panels are a
good example), many of them are connected to a control bus accessible to the
CPU. Control requests can be sent on a dedicated control bus (such as I2C or
SPI) or multiplexed on a mixed control and data bus (such as DBI or DSI). To
support both options the CDF display entity model separates the control and
data busses in different APIs.

Display entities are abstract object that must be implemented by a real
device. The device sits on its control bus and is registered with the Linux
device core and matched with his driver using the control bus specific API.
The CDF doesn't create a display entity class or bus, display entity drivers
thus standard Linux kernel drivers using existing busses. A DBI bus is added
as part of this patch set, but strictly speaking this isn't part of CDF.

When a display entity driver probes a device it must create an instance of the
display_entity structure, initialize it and add it to the CDF core entities
pool. The display entity exposes abstract operations through function
pointers, and the entity driver must implement those operations. Those
operations can act on either the whole entity or on a given port, depending on
the operation. They are divided in two groups, control operations and video
operations.


Control Operations
------------------

Control operations are called by upper-level drivers, usually in response to a
request originating from userspace. They query or control the display entity
state and operation. Currently defined control operations are

- get_size(), to retrieve the entity physical size (applicable to panels only)
- get_modes(), to retrieve the video modes supported at an entity port
- get_params(), to retrieve the data bus parameters at an entity port

- set_state(), to control the state of the entity (off, standby or on)
- update(), to trigger a display update (for entities that implement manual
  update, such as manual-update panels that store frames in their internal
  frame buffer)

The last two operations have been carried from v2 and will be reworked.


Pipeline Control
----------------

The figure on page 4 shows the control model for a linear pipeline. This
differs significantly from CDF v2 where calls where forwarded from entity to
entity using a Russian dolls model. v3 removes the need of neighbour awareness
from entity drivers, simplifying the entity drivers. The complexity of pipeline
configuration is moved to a central location called a pipeline controller
instead of being spread out to all drivers.

Pipeline controllers provide library functions that display drivers can use to
control a pipeline. Several controllers can be implemented to accomodate the
needs of various pipeline topologies and complexities, and display drivers can
even implement their own pipeline control algorithm if needed. I'm working on a
linear pipeline controller for the next version of the patch set.

If pipeline controllers are responsible for propagating a pipeline configuration
on all entity ports in the pipeline, entity drivers are responsible for
propagating the configuration inside entities, from sink (input) to source
(output) ports as illustrated on page 5. The rationale behind this is that
knowledge of the entity internals is located in the entity driver, while
knowledge of the pipeline belongs to the pipeline controller. The controller
will thus configure the pipeline by performing the following steps:

- applying a configuration on sink ports of an entity
- read the configuration that has been propagated by the entity driver on its
  source ports
- optionally, modify the source port configuration (to configure custom timings,
  scaling or other parameters, if supported by the entity)
- propagate the source port configuration to the sink ports of the next entities
  in the pipeline and start over

Beside modifying the active configuration, the entities API will allow trying
configurations without applying them to the hardware. As configuration of a port
possibly depend on the configurations of the other ports, trying a configuration
must be done at the entity level instead of the port level. The implementation
will be based on the concept of configuration store objects that will store the
configuration of all ports for a given entity. Each entity will have a single
active configuration store, and test configuration stores will be created
dynamically to try a configuration on an entity. The get and set operations
implemented by the entity will receive a configuration store pointer, and active
and test code paths in entity drivers will be identical, except for applying the
configuration to the hardware for the active code path.


Video Operations
----------------

Video operations control the video stream state on entity ports. The only
currently defined video operation is

- set_stream(), to start (in continuous or single-shot mode) the video stream
  on an entity port

The call model for video operations differ from the control operations model
described above. The set_stream() operation is called directly by downstream
entities on upstream entities (from a video data bus point of view).
Terminating entities in a pipeline (such as panels) will usually call the
set_stream() operation in their set_state() handler, and intermediate entities
will forward the set_stream() call upstream.


Integration
-----------

The figure on page 8 describes how a panel driver, implemented using CDF as a
display entity, interacts with the other components in the system. The use case
is a simple pipeline made of a display controller and a panel.

The display controller driver receives control request from userspace through
DRM (or FBDEV) API calls. It processes the request and calls the panel driver
through the CDF control operations API. The panel driver will then issue
requests on its control bus (several possible control busses are shown on the
figure, panel drivers typically use one of them only) and call video operations
of the display controller on its left side to control the video stream.


Registration and Notification
-----------------------------

Due to possibly complex dependencies between entities we can't guarantee that
all entities part of the display pipeline will have been successfully probed
when the master display controller driver is probed. For instance a panel can
be a child of the DBI or DSI bus controlled by the display device, or use a
clock provided by that device. We can't defer the display device probe until
the panel is probed and also defer the panel device probe until the display
device is probed. For this reason we need a notification system that allows
entities to register themselves with the CDF core, and display controller
drivers to get notified when entities they need are available.

The notification system has been completely redesigned in v3. This version is
based on the V4L2 asynchronous probing notification code, with large parts of
the code shamelessly copied. This is an interim solution to let me play with
the notification code as needed by CDF. I'm not a fan of code duplication, and
will work on merging the CDF and V4L2 implementations in a later stage when
CDF will reach a mature enough state.

CDF manages a pool of entities and a list of notifiers. Notifiers are
registered by master display drivers with an array of entities match
descriptors. When an entity is added to the CDF entities pool, all notifiers
are searched for a match. If a match is found, the corresponding notifier is
called to notify the master display driver.

The two currently supported match methods are platform match, which uses
device names, and DT match, which uses DT node pointers. More match method
might be added later if needed. Two helper functions exist to build a notifier
from a list of platform device names (in the non-DT case) or a DT
representation of the display pipeline topology.

Once all required entities have been successfully found, the master display
driver is responsible for creating media controller links between all entities
in the pipeline. Two helper functions are also available to automate that
process, one for the non-DT case and one for the DT case. Once again some
DT-related code has been copied from the V4L2 DT code, I will work on merging
both in a future version.

Note that notification brings a different issue after registration, as display
controller and display entity drivers would take a reference to each other.
Those circular references would make driver unloading impossible. One possible
solution to this problem would be to simulate an unplug event for the display
entity, to force the display driver to release the dislay entities it uses. We
would need a userspace API for that though. Better solutions would of course
be welcome.


Device Tree Bindings
--------------------

CDF entities device tree bindings are not documented yet. They describe both
the graph topology and entity-specific information. The graph description uses
the V4L2 DT bindings (which are actually not V4L2-specific) specified at
Documentation/devicetree/bindings/media/video-interfaces.txt. Entity-specific
information will be described in individual DT bindings documentation. The DPI
panel driver uses the display timing bindings documented in
Documentation/devicetree/bindings/video/display-timing.txt.




Please note that most of the display entities on devices I own are just dumb
panels with no control bus, and are thus not the best candidates to design a
framework that needs to take complex panels' needs into account. This is why I
hope to see you using the CDF with your display device and tell me what needs to
be modified/improved/redesigned.

This patch set is split as follows:

- The first patch fixes a Kconfig namespace issue with the OMAP DSS panels. It
  could be applied already independently of this series.
- Patches 02/19 to 07/19 add the CDF core, including the notification system
  and the graph and OF helpers.
- Patch 08/19 adds a MIPI DBI bus. This isn't part of CDF strictly speaking,
  but is needed for the DBI panel drivers.
- Patches 09/19 to 13/19 add panel drivers, a VGA DAC driver and a VGA
  connector driver.
- Patches 14/19 to 18/19 add CDF-compliant reference board code and DT for the
  Renesas Marzen and Lager boards.
- Patch 19/19 port the Renesas R-Car Display Unit driver to CDF.

The patches are available in my git tree at

    git://linuxtv.org/pinchartl/fbdev.git cdf/v3
    http://git.linuxtv.org/pinchartl/fbdev.git/shortlog/refs/heads/cdf/v3

For convenience I've included modifications to the Renesas R-Car Display Unit
driver to use the CDF. You can read the code to see how the driver uses CDF to
interface panels. Please note that the rcar-du-drm implementation is still
work in progress, its set_stream operation implementation doesn't enable and
disable the video stream yet as it should.

As already mentioned in v2, I will appreciate all reviews, comments,
criticisms, ideas, remarks, ... If you can find a clever way to solve the
cyclic references issue described above I'll buy you a beer at the next
conference we will both attend. If you think the proposed solution is too
complex, or too simple, I'm all ears, but I'll have more arguments this time
than I had with v2 

Laurent Pinchart (19):
  OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_*
  video: Add Common Display Framework core
  video: display: Add video and stream control operations
  video: display: Add display entity notifier
  video: display: Graph helpers
  video: display: OF support
  video: display: Add pixel coding definitions
  video: display: Add MIPI DBI bus support
  video: panel: Add DPI panel support
  video: panel: Add R61505 panel support
  video: panel: Add R61517 panel support
  video: display: Add VGA Digital to Analog Converter support
  video: display: Add VGA connector support
  ARM: shmobile: r8a7790: Add DU clocks for DT
  ARM: shmobile: r8a7790: Add DU device node to device tree
  ARM: shmobile: marzen: Port DU platform data to CDF
  ARM: shmobile: lager: Port DU platform data to CDF
  ARM: shmobile: lager-reference: Add display device nodes to device
    tree
  drm/rcar-du: Port to the Common Display Framework

 arch/arm/boot/dts/r8a7790-lager-reference.dts |  92 ++++
 arch/arm/boot/dts/r8a7790.dtsi                |  33 ++
 arch/arm/mach-shmobile/board-lager.c          |  76 ++-
 arch/arm/mach-shmobile/board-marzen.c         |  77 ++-
 arch/arm/mach-shmobile/clock-r8a7790.c        |   5 +
 drivers/gpu/drm/rcar-du/Kconfig               |   3 +-
 drivers/gpu/drm/rcar-du/Makefile              |   7 +-
 drivers/gpu/drm/rcar-du/rcar_du_connector.c   | 164 ++++++
 drivers/gpu/drm/rcar-du/rcar_du_connector.h   |  36 ++
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h        |   2 +-
 drivers/gpu/drm/rcar-du/rcar_du_drv.c         | 279 ++++++++--
 drivers/gpu/drm/rcar-du/rcar_du_drv.h         |  28 +-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c     |  87 ++-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h     |  22 +-
 drivers/gpu/drm/rcar-du/rcar_du_kms.c         | 116 +++-
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c     | 131 -----
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h     |  25 -
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c      |  96 ----
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h      |  23 -
 drivers/video/Kconfig                         |   1 +
 drivers/video/Makefile                        |   1 +
 drivers/video/display/Kconfig                 |  62 +++
 drivers/video/display/Makefile                |   9 +
 drivers/video/display/con-vga.c               | 148 +++++
 drivers/video/display/display-core.c          | 759 ++++++++++++++++++++++++++
 drivers/video/display/display-notifier.c      | 542 ++++++++++++++++++
 drivers/video/display/mipi-dbi-bus.c          | 234 ++++++++
 drivers/video/display/panel-dpi.c             | 207 +++++++
 drivers/video/display/panel-r61505.c          | 567 +++++++++++++++++++
 drivers/video/display/panel-r61517.c          | 460 ++++++++++++++++
 drivers/video/display/vga-dac.c               | 152 ++++++
 drivers/video/omap2/displays-new/Kconfig      |  24 +-
 drivers/video/omap2/displays-new/Makefile     |  24 +-
 include/linux/platform_data/rcar-du.h         |  55 +-
 include/video/display.h                       | 398 ++++++++++++++
 include/video/mipi-dbi-bus.h                  | 125 +++++
 include/video/panel-dpi.h                     |  24 +
 include/video/panel-r61505.h                  |  27 +
 include/video/panel-r61517.h                  |  28 +
 39 files changed, 4615 insertions(+), 534 deletions(-)
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.c
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.h
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/con-vga.c
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 drivers/video/display/display-notifier.c
 create mode 100644 drivers/video/display/mipi-dbi-bus.c
 create mode 100644 drivers/video/display/panel-dpi.c
 create mode 100644 drivers/video/display/panel-r61505.c
 create mode 100644 drivers/video/display/panel-r61517.c
 create mode 100644 drivers/video/display/vga-dac.c
 create mode 100644 include/video/display.h
 create mode 100644 include/video/mipi-dbi-bus.h
 create mode 100644 include/video/panel-dpi.h
 create mode 100644 include/video/panel-r61505.h
 create mode 100644 include/video/panel-r61517.h

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 01/19] OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_*
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 02/19] video: Add Common Display Framework core Laurent Pinchart
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The DISPLAY_ prefix will clash with the Common Display Framework, rename
it.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/omap2/displays-new/Kconfig  | 24 ++++++++++++------------
 drivers/video/omap2/displays-new/Makefile | 24 ++++++++++++------------
 2 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig
index 6c90885..2a44b41 100644
--- a/drivers/video/omap2/displays-new/Kconfig
+++ b/drivers/video/omap2/displays-new/Kconfig
@@ -1,68 +1,68 @@
 menu "OMAP Display Device Drivers (new device model)"
         depends on OMAP2_DSS
 
-config DISPLAY_ENCODER_TFP410
+config OMAP2_DISPLAY_ENCODER_TFP410
         tristate "TFP410 DPI to DVI Encoder"
 	help
 	  Driver for TFP410 DPI to DVI encoder.
 
-config DISPLAY_ENCODER_TPD12S015
+config OMAP2_DISPLAY_ENCODER_TPD12S015
         tristate "TPD12S015 HDMI ESD protection and level shifter"
 	help
 	  Driver for TPD12S015, which offers HDMI ESD protection and level
 	  shifting.
 
-config DISPLAY_CONNECTOR_DVI
+config OMAP2_DISPLAY_CONNECTOR_DVI
         tristate "DVI Connector"
 	depends on I2C
 	help
 	  Driver for a generic DVI connector.
 
-config DISPLAY_CONNECTOR_HDMI
+config OMAP2_DISPLAY_CONNECTOR_HDMI
         tristate "HDMI Connector"
 	help
 	  Driver for a generic HDMI connector.
 
-config DISPLAY_CONNECTOR_ANALOG_TV
+config OMAP2_DISPLAY_CONNECTOR_ANALOG_TV
         tristate "Analog TV Connector"
 	help
 	  Driver for a generic analog TV connector.
 
-config DISPLAY_PANEL_DPI
+config OMAP2_DISPLAY_PANEL_DPI
 	tristate "Generic DPI panel"
 	help
 	  Driver for generic DPI panels.
 
-config DISPLAY_PANEL_DSI_CM
+config OMAP2_DISPLAY_PANEL_DSI_CM
 	tristate "Generic DSI Command Mode Panel"
 	help
 	  Driver for generic DSI command mode panels.
 
-config DISPLAY_PANEL_SONY_ACX565AKM
+config OMAP2_DISPLAY_PANEL_SONY_ACX565AKM
 	tristate "ACX565AKM Panel"
 	depends on SPI && BACKLIGHT_CLASS_DEVICE
 	help
 	  This is the LCD panel used on Nokia N900
 
-config DISPLAY_PANEL_LGPHILIPS_LB035Q02
+config OMAP2_DISPLAY_PANEL_LGPHILIPS_LB035Q02
 	tristate "LG.Philips LB035Q02 LCD Panel"
 	depends on SPI
 	help
 	  LCD Panel used on the Gumstix Overo Palo35
 
-config DISPLAY_PANEL_SHARP_LS037V7DW01
+config OMAP2_DISPLAY_PANEL_SHARP_LS037V7DW01
         tristate "Sharp LS037V7DW01 LCD Panel"
         depends on BACKLIGHT_CLASS_DEVICE
         help
           LCD Panel used in TI's SDP3430 and EVM boards
 
-config DISPLAY_PANEL_TPO_TD043MTEA1
+config OMAP2_DISPLAY_PANEL_TPO_TD043MTEA1
         tristate "TPO TD043MTEA1 LCD Panel"
         depends on SPI
         help
           LCD Panel used in OMAP3 Pandora
 
-config DISPLAY_PANEL_NEC_NL8048HL11
+config OMAP2_DISPLAY_PANEL_NEC_NL8048HL11
 	tristate "NEC NL8048HL11 Panel"
 	depends on SPI
 	depends on BACKLIGHT_CLASS_DEVICE
diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile
index 5aeb11b..768ad94 100644
--- a/drivers/video/omap2/displays-new/Makefile
+++ b/drivers/video/omap2/displays-new/Makefile
@@ -1,12 +1,12 @@
-obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o
-obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o
-obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o
-obj-$(CONFIG_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o
-obj-$(CONFIG_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
-obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
-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_TD043MTEA1) += panel-tpo-td043mtea1.o
-obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
+obj-$(CONFIG_OMAP2_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o
+obj-$(CONFIG_OMAP2_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o
+obj-$(CONFIG_OMAP2_DISPLAY_CONNECTOR_DVI) += connector-dvi.o
+obj-$(CONFIG_OMAP2_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o
+obj-$(CONFIG_OMAP2_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_DPI) += panel-dpi.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
+obj-$(CONFIG_OMAP2_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 02/19] video: Add Common Display Framework core
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 01/19] OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_* Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 03/19] video: display: Add video and stream control operations Laurent Pinchart
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The Common Display Framework (CDF) splits display devices in entities
that interact through an abstract API. Each entity is managed by its own
driver independently of the other entities, with the framework
orchestrating interactions.

This commit introduces the CDF core with entity (un)registration and
core control operations support.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/Kconfig                |   1 +
 drivers/video/Makefile               |   1 +
 drivers/video/display/Kconfig        |   4 +
 drivers/video/display/Makefile       |   2 +
 drivers/video/display/display-core.c | 236 +++++++++++++++++++++++++++++++++++
 include/video/display.h              |  94 ++++++++++++++
 6 files changed, 338 insertions(+)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 include/video/display.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 4cf1e1d..c9ca1d5 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2477,6 +2477,7 @@ source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/mmp/Kconfig"
 source "drivers/video/backlight/Kconfig"
+source "drivers/video/display/Kconfig"
 
 if VT
 	source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e8bae8d..d7fd4a2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -15,6 +15,7 @@ fb-objs                           := $(fb-y)
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-y				  += display/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
new file mode 100644
index 0000000..1d533e7
--- /dev/null
+++ b/drivers/video/display/Kconfig
@@ -0,0 +1,4 @@
+menuconfig DISPLAY_CORE
+	tristate "Display Core"
+	---help---
+	  Support common display framework for graphics devices.
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
new file mode 100644
index 0000000..3054adc
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1,2 @@
+display-y					:= display-core.o
+obj-$(CONFIG_DISPLAY_CORE)			+= display.o
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..82fc58b
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,236 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <media/media-device.h>
+
+#include <video/display.h>
+#include <video/videomode.h>
+
+/* -----------------------------------------------------------------------------
+ * Control operations
+ */
+
+/**
+ * display_entity_get_modes - Get video modes supported by the display entity
+ * @entity: The display entity
+ * @port: The display entity port
+ * @modes: Pointer to an array of modes
+ *
+ * Fill the modes argument with a pointer to an array of video modes. The array
+ * is owned by the display entity.
+ *
+ * Return the number of supported modes on success (including 0 if no mode is
+ * supported) or a negative error code otherwise.
+ */
+int display_entity_get_modes(struct display_entity *entity, unsigned int port,
+			     const struct videomode **modes)
+{
+	if (port >= entity->entity.num_pads)
+		return -EINVAL;
+
+	if (!entity->ops->ctrl || !entity->ops->ctrl->get_modes)
+		return 0;
+
+	return entity->ops->ctrl->get_modes(entity, port, modes);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_modes);
+
+/**
+ * display_entity_get_size - Get display entity physical size
+ * @entity: The display entity
+ * @width: Physical width in millimeters
+ * @height: Physical height in millimeters
+ *
+ * When applicable, for instance for display panels, retrieve the display
+ * physical size in millimeters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height)
+{
+	if (!entity->ops->ctrl || !entity->ops->ctrl->get_size)
+		return -EOPNOTSUPP;
+
+	return entity->ops->ctrl->get_size(entity, width, height);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_size);
+
+/**
+ * display_entity_get_params - Get display entity interface parameters
+ * @entity: The display entity
+ * @port: The display entity port
+ * @params: Pointer to interface parameters
+ *
+ * Fill the parameters structure pointed to by the params argument with display
+ * entity interface parameters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_params(struct display_entity *entity, unsigned int port,
+			      struct display_entity_interface_params *params)
+{
+	if (port >= entity->entity.num_pads)
+		return -EINVAL;
+
+	if (!entity->ops->ctrl || !entity->ops->ctrl->get_params)
+		return -EOPNOTSUPP;
+
+	return entity->ops->ctrl->get_params(entity, port, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+/* -----------------------------------------------------------------------------
+ * Connections
+ */
+
+/**
+ * display_entity_connect - Connect two entities through a video stream
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Set the sink entity source field to the source entity.
+ */
+
+/**
+ * display_entity_disconnect - Disconnect two previously connected entities
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Break a connection between two previously connected entities. The source
+ * entity source field is reset to NULL.
+ */
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+static void display_entity_release(struct kref *ref)
+{
+	struct display_entity *entity =
+		container_of(ref, struct display_entity, ref);
+
+	if (entity->release)
+		entity->release(entity);
+}
+
+/**
+ * display_entity_get - get a reference to a display entity
+ * @display_entity: the display entity
+ *
+ * Return the display entity pointer.
+ */
+struct display_entity *display_entity_get(struct display_entity *entity)
+{
+	if (entity == NULL)
+		return NULL;
+
+	kref_get(&entity->ref);
+	return entity;
+}
+EXPORT_SYMBOL_GPL(display_entity_get);
+
+/**
+ * display_entity_put - release a reference to a display entity
+ * @entity: the display entity
+ *
+ * Releasing the last reference to a display entity releases the display entity
+ * itself.
+ */
+void display_entity_put(struct display_entity *entity)
+{
+	kref_put(&entity->ref, display_entity_release);
+}
+EXPORT_SYMBOL_GPL(display_entity_put);
+
+/**
+ * display_entity_init - Initialize a display entity
+ * @entity: display entity to be registered
+ * @num_sinks: number of sink ports
+ * @num_sources: number of source ports
+ *
+ * Initialize the display entity with the given number of sink and source ports.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_init(struct display_entity *entity, unsigned int num_sinks,
+			unsigned int num_sources)
+{
+	struct media_entity *ment = &entity->entity;
+	struct media_pad *pads;
+	unsigned int num_pads;
+	unsigned int i;
+	int ret;
+
+	kref_init(&entity->ref);
+
+	num_pads = num_sinks + num_sources;
+	pads = kzalloc(sizeof(*pads) * num_pads, GFP_KERNEL);
+	if (pads == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < num_sinks; ++i)
+		pads[i].flags = MEDIA_PAD_FL_SINK;
+	for (; i < num_pads; ++i)
+		pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+	ment->name = entity->name;
+
+	ret = media_entity_init(ment, num_pads, pads, 0);
+	if (ret < 0) {
+		kfree(pads);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_init);
+
+/**
+ * display_entity_cleanup - Clean a display entity up
+ * @entity: display entity to be cleaned up
+ *
+ * Clean the entity up and free all resources allocated by by
+ * display_entity_init().
+ */
+void display_entity_cleanup(struct display_entity *entity)
+{
+	struct media_entity *ment = &entity->entity;
+
+	kfree(ment->pads);
+	media_entity_cleanup(ment);
+
+	display_entity_put(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_cleanup);
+
+int display_entity_register(struct media_device *mdev,
+			    struct display_entity *entity)
+{
+	return media_device_register_entity(mdev, &entity->entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_register);
+
+void display_entity_unregister(struct display_entity *entity)
+{
+	media_device_unregister_entity(&entity->entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Display Core");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
new file mode 100644
index 0000000..74b227d
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,94 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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 __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <media/media-entity.h>
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+struct display_entity;
+struct videomode;
+
+enum display_entity_interface_type {
+	DISPLAY_ENTITY_INTERFACE_DPI,
+	DISPLAY_ENTITY_INTERFACE_DBI,
+	DISPLAY_ENTITY_INTERFACE_LVDS,
+	DISPLAY_ENTITY_INTERFACE_VGA,
+};
+
+struct display_entity_interface_params {
+	enum display_entity_interface_type type;
+};
+
+struct display_entity_control_ops {
+	int (*get_size)(struct display_entity *ent,
+			unsigned int *width, unsigned int *height);
+
+	/* Port operations */
+	int (*get_modes)(struct display_entity *entity, unsigned int port,
+			 const struct videomode **modes);
+	int (*get_params)(struct display_entity *entity, unsigned int port,
+			  struct display_entity_interface_params *params);
+};
+
+struct display_entity_ops {
+	const struct display_entity_control_ops *ctrl;
+};
+
+struct display_entity {
+	struct list_head list;
+	struct device *dev;
+	struct module *owner;
+	struct kref ref;
+
+	char name[32];
+	struct media_entity entity;
+
+	const struct display_entity_ops *ops;
+
+	void(*release)(struct display_entity *ent);
+};
+
+static inline struct display_entity *
+to_display_entity(struct media_entity *entity)
+{
+	return container_of(entity, struct display_entity, entity);
+}
+
+int __must_check display_entity_init(struct display_entity *entity,
+				     unsigned int num_sinks,
+				     unsigned int num_sources);
+void display_entity_cleanup(struct display_entity *entity);
+
+int display_entity_register(struct media_device *mdev,
+			    struct display_entity *entity);
+void display_entity_unregister(struct display_entity *entity);
+
+/* Operations */
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height);
+int display_entity_get_modes(struct display_entity *entity, unsigned int port,
+			     const struct videomode **modes);
+int display_entity_get_params(struct display_entity *entity, unsigned int port,
+			      struct display_entity_interface_params *params);
+
+#endif /* __DISPLAY_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 03/19] video: display: Add video and stream control operations
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 01/19] OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_* Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 02/19] video: Add Common Display Framework core Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 04/19] video: display: Add display entity notifier Laurent Pinchart
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The video and stream control operations handle video stream management,
both from the control point of view (called in response to userspace
requests) and the video stream point of view (called by entities to
control the video stream they receive on their input(s)).

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/display-core.c | 82 ++++++++++++++++++++++++++++++++++++
 include/video/display.h              | 49 +++++++++++++++++++++
 2 files changed, 131 insertions(+)

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 82fc58b..bb18723 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -25,6 +25,57 @@
  */
 
 /**
+ * display_entity_set_state - Set the display entity operation state
+ * @entity: The display entity
+ * @state: Display entity operation state
+ *
+ * See &enum display_entity_state for information regarding the entity states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	int ret;
+
+	if (entity->state == state)
+		return 0;
+
+	if (!entity->ops->ctrl || !entity->ops->ctrl->set_state)
+		return 0;
+
+	ret = entity->ops->ctrl->set_state(entity, state);
+	if (ret < 0)
+		return ret;
+
+	entity->state = state;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_set_state);
+
+/**
+ * display_entity_update - Update the display
+ * @entity: The display entity
+ *
+ * Make the display entity ready to receive pixel data and start frame transfer.
+ * This operation can only be called if the display entity is in STANDBY or ON
+ * state.
+ *
+ * The display entity will call the upstream entity in the video chain to start
+ * the video stream.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_update(struct display_entity *entity)
+{
+	if (!entity->ops->ctrl || !entity->ops->ctrl->update)
+		return 0;
+
+	return entity->ops->ctrl->update(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_update);
+
+/**
  * display_entity_get_modes - Get video modes supported by the display entity
  * @entity: The display entity
  * @port: The display entity port
@@ -95,6 +146,36 @@ int display_entity_get_params(struct display_entity *entity, unsigned int port,
 EXPORT_SYMBOL_GPL(display_entity_get_params);
 
 /* -----------------------------------------------------------------------------
+ * Video operations
+ */
+
+/**
+ * display_entity_set_stream - Control the video stream state
+ * @entity: The display entity
+ * @port: The display entity port
+ * @state: Display video stream state
+ *
+ * Control the video stream state at the entity video output.
+ *
+ * See &enum display_entity_stream_state for information regarding the stream
+ * states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_stream(struct display_entity *entity, unsigned int port,
+			      enum display_entity_stream_state state)
+{
+	if (port >= entity->entity.num_pads)
+		return -EINVAL;
+
+	if (!entity->ops->video || !entity->ops->video->set_stream)
+		return 0;
+
+	return entity->ops->video->set_stream(entity, port, state);
+}
+EXPORT_SYMBOL_GPL(display_entity_set_stream);
+
+/* -----------------------------------------------------------------------------
  * Connections
  */
 
@@ -177,6 +258,7 @@ int display_entity_init(struct display_entity *entity, unsigned int num_sinks,
 	int ret;
 
 	kref_init(&entity->ref);
+	entity->state = DISPLAY_ENTITY_STATE_OFF;
 
 	num_pads = num_sinks + num_sources;
 	pads = kzalloc(sizeof(*pads) * num_pads, GFP_KERNEL);
diff --git a/include/video/display.h b/include/video/display.h
index 74b227d..fef05a68 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -25,6 +25,38 @@
 struct display_entity;
 struct videomode;
 
+/**
+ * enum display_entity_state - State of a display entity
+ * @DISPLAY_ENTITY_STATE_OFF: The entity is turned off completely, possibly
+ *	including its power supplies. Communication with a display entity in
+ *	that state is not possible.
+ * @DISPLAY_ENTITY_STATE_STANDBY: The entity is in a low-power state. Full
+ *	communication with the display entity is supported, including pixel data
+ *	transfer, but the output is kept blanked.
+ * @DISPLAY_ENTITY_STATE_ON: The entity is fully operational.
+ */
+enum display_entity_state {
+	DISPLAY_ENTITY_STATE_OFF,
+	DISPLAY_ENTITY_STATE_STANDBY,
+	DISPLAY_ENTITY_STATE_ON,
+};
+
+/**
+ * enum display_entity_stream_state - State of a video stream
+ * @DISPLAY_ENTITY_STREAM_STOPPED: The video stream is stopped, no frames are
+ *	transferred.
+ * @DISPLAY_ENTITY_STREAM_SINGLE_SHOT: The video stream has been started for
+ *      single shot operation. The source entity will transfer a single frame
+ *      and then stop.
+ * @DISPLAY_ENTITY_STREAM_CONTINUOUS: The video stream is running, frames are
+ *	transferred continuously by the source entity.
+ */
+enum display_entity_stream_state {
+	DISPLAY_ENTITY_STREAM_STOPPED,
+	DISPLAY_ENTITY_STREAM_SINGLE_SHOT,
+	DISPLAY_ENTITY_STREAM_CONTINUOUS,
+};
+
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
 	DISPLAY_ENTITY_INTERFACE_DBI,
@@ -39,6 +71,9 @@ struct display_entity_interface_params {
 struct display_entity_control_ops {
 	int (*get_size)(struct display_entity *ent,
 			unsigned int *width, unsigned int *height);
+	int (*set_state)(struct display_entity *ent,
+			 enum display_entity_state state);
+	int (*update)(struct display_entity *ent);
 
 	/* Port operations */
 	int (*get_modes)(struct display_entity *entity, unsigned int port,
@@ -47,8 +82,14 @@ struct display_entity_control_ops {
 			  struct display_entity_interface_params *params);
 };
 
+struct display_entity_video_ops {
+	int (*set_stream)(struct display_entity *ent, unsigned int port,
+			  enum display_entity_stream_state state);
+};
+
 struct display_entity_ops {
 	const struct display_entity_control_ops *ctrl;
+	const struct display_entity_video_ops *video;
 };
 
 struct display_entity {
@@ -63,6 +104,8 @@ struct display_entity {
 	const struct display_entity_ops *ops;
 
 	void(*release)(struct display_entity *ent);
+
+	enum display_entity_state state;
 };
 
 static inline struct display_entity *
@@ -86,9 +129,15 @@ void display_entity_put(struct display_entity *entity);
 
 int display_entity_get_size(struct display_entity *entity,
 			    unsigned int *width, unsigned int *height);
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state);
+int display_entity_update(struct display_entity *entity);
 int display_entity_get_modes(struct display_entity *entity, unsigned int port,
 			     const struct videomode **modes);
 int display_entity_get_params(struct display_entity *entity, unsigned int port,
 			      struct display_entity_interface_params *params);
 
+int display_entity_set_stream(struct display_entity *entity, unsigned int port,
+			      enum display_entity_stream_state state);
+
 #endif /* __DISPLAY_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 04/19] video: display: Add display entity notifier
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (2 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 03/19] video: display: Add video and stream control operations Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 05/19] video: display: Graph helpers Laurent Pinchart
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Display entities are initialized by they respective drivers
asynchronously with the master display driver. The notifier
infrastructure allows display drivers to create a list of entities they
need (based on platform data) and be notified when those entities are
added to or removed from the system.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/Makefile           |   3 +-
 drivers/video/display/display-notifier.c | 304 +++++++++++++++++++++++++++++++
 include/video/display.h                  |  66 +++++++
 3 files changed, 372 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/display/display-notifier.c

diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 3054adc..b907aad 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,2 +1,3 @@
-display-y					:= display-core.o
+display-y					:= display-core.o \
+						   display-notifier.o
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
new file mode 100644
index 0000000..c9210ec
--- /dev/null
+++ b/drivers/video/display/display-notifier.c
@@ -0,0 +1,304 @@
+/*
+ * Display Notifier
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+
+#include <video/display.h>
+
+static LIST_HEAD(display_entity_list);
+static LIST_HEAD(display_entity_notifiers);
+static DEFINE_MUTEX(display_entity_mutex);
+
+/* -----------------------------------------------------------------------------
+ * Notifiers
+ */
+
+static bool match_platform(struct device *dev,
+			   struct display_entity_match *match)
+{
+	pr_debug("%s: matching device '%s' with name '%s'\n", __func__,
+		 dev_name(dev), match->match.platform.name);
+
+	return !strcmp(match->match.platform.name, dev_name(dev));
+}
+
+static struct display_entity_match *
+display_entity_notifier_match(struct display_entity_notifier *notifier,
+			      struct display_entity *entity)
+{
+	bool (*match_func)(struct device *, struct display_entity_match *);
+	struct display_entity_match *match;
+
+	pr_debug("%s: matching entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	list_for_each_entry(match, &notifier->waiting, list) {
+		switch (match->type) {
+		default:
+		case DISPLAY_ENTITY_BUS_PLATFORM:
+			match_func = match_platform;
+			break;
+		}
+
+		if (match_func(entity->dev, match))
+			return match;
+	}
+
+	return NULL;
+}
+
+static void display_entity_notifier_cleanup(struct display_entity *entity)
+{
+	entity->match = NULL;
+	entity->notifier = NULL;
+}
+
+static int
+display_entity_notifier_notify(struct display_entity_notifier *notifier,
+			       struct display_entity *entity,
+			       struct display_entity_match *match)
+{
+	int ret;
+
+	pr_debug("%s: notifying device '%s' for entity '%s' (ptr 0x%p dev '%s')\n",
+		 __func__, dev_name(notifier->dev), entity->name, entity,
+		 dev_name(entity->dev));
+
+	/* Remove the match from waiting list. */
+	list_del(&match->list);
+	entity->match = match;
+	entity->notifier = notifier;
+
+	if (notifier->bound) {
+		ret = notifier->bound(notifier, entity, match);
+		if (ret < 0)
+			goto error_bound;
+	}
+
+	/* Move the entity from the global list to the notifier's done list. */
+	list_move(&entity->list, &notifier->done);
+
+	if (list_empty(&notifier->waiting) && notifier->complete) {
+		pr_debug("%s: notifying device '%s' of completion\n", __func__,
+			 dev_name(notifier->dev));
+		ret = notifier->complete(notifier);
+		if (ret < 0)
+			goto error_complete;
+	}
+
+	return 0;
+
+error_complete:
+	/* Move the entity back to the global list. */
+	list_move(&entity->list, &display_entity_list);
+	if (notifier->unbind)
+		notifier->unbind(notifier, entity, match);
+error_bound:
+	/* Put the match back to the waiting list. */
+	list_add_tail(&match->list, &notifier->waiting);
+	display_entity_notifier_cleanup(entity);
+
+	return ret;
+}
+
+/**
+ * display_entity_register_notifier - register a display entity notifier
+ * @notifier: display entity notifier structure we want to register
+ *
+ * Display entity notifiers are called to notify drivers of display
+ * entity-related events for matching display_entitys.
+ *
+ * Notifiers and display_entitys are matched through the device they correspond
+ * to. If the notifier dev field is equal to the display entity dev field the
+ * notifier will be called when an event is reported. Notifiers with a NULL dev
+ * field act as catch-all and will be called for all display_entitys.
+ *
+ * Supported events are
+ *
+ * - DISPLAY_ENTITY_NOTIFIER_CONNECT reports display entity connection and is
+ *   sent at display entity or notifier registration time
+ * - DISPLAY_ENTITY_NOTIFIER_DISCONNECT reports display entity disconnection and
+ *   is sent at display entity unregistration time
+ *
+ * Registering a notifier sends DISPLAY_ENTITY_NOTIFIER_CONNECT events for all
+ * previously registered display_entitys that match the notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_register_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity_match *match;
+	struct display_entity *entity;
+	struct display_entity *next;
+	unsigned int i;
+	int ret = 0;
+
+	if (notifier->num_entities == 0)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(&notifier->waiting);
+	INIT_LIST_HEAD(&notifier->done);
+
+	for (i = 0; i < notifier->num_entities; i++) {
+		match = &notifier->entities[i];
+
+		switch (match->type) {
+		case DISPLAY_ENTITY_BUS_PLATFORM:
+			break;
+		default:
+			dev_err(notifier->dev,
+				"%s: Invalid bus type %u on %p\n", __func__,
+				match->type, match);
+			return -EINVAL;
+		}
+
+		list_add_tail(&match->list, &notifier->waiting);
+	}
+
+	mutex_lock(&display_entity_mutex);
+
+	list_add_tail(&notifier->list, &display_entity_notifiers);
+
+	list_for_each_entry_safe(entity, next, &display_entity_list, list) {
+		match = display_entity_notifier_match(notifier, entity);
+		if (!match)
+			continue;
+
+		ret = display_entity_notifier_notify(notifier, entity, match);
+		if (ret)
+			break;
+	}
+
+	mutex_unlock(&display_entity_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(display_entity_register_notifier);
+
+/**
+ * display_entity_unregister_notifier - unregister a display entity notifier
+ * @notifier: display entity notifier to be unregistered
+ *
+ * Unregistration guarantees that the notifier will never be called upon return
+ * of this function.
+ */
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity *entity;
+	struct display_entity *next;
+
+	if (notifier->num_entities == 0)
+		return;
+
+	mutex_lock(&display_entity_mutex);
+
+	list_del(&notifier->list);
+
+	list_for_each_entry_safe(entity, next, &notifier->done, list) {
+		if (notifier->unbind)
+			notifier->unbind(notifier, entity, entity->match);
+
+		/* Move the entity back to the global list. */
+		display_entity_notifier_cleanup(entity);
+		list_move(&entity->list, &display_entity_list);
+	}
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+
+/* -----------------------------------------------------------------------------
+ * Entity Registration
+ */
+
+/**
+ * display_entity_add - add a display entity to the list of available entities
+ * @entity: display entity to be added
+ *
+ * Add the display entity to the list of available entities and send the
+ * DISPLAY_ENTITY_NOTIFIER_CONNECT event to all matching registered notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_add(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier;
+	struct display_entity_match *match = NULL;
+
+	pr_debug("%s: adding entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	mutex_lock(&display_entity_mutex);
+
+	/* Add the entity to the global unbound entities list. It will later be
+	 * moved to the notifier done list by display_entity_notifier_notify().
+	 */
+	list_add_tail(&entity->list, &display_entity_list);
+
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		match = display_entity_notifier_match(notifier, entity);
+		if (match)
+			break;
+	}
+
+	if (match) {
+		/* A match has been found, notify the notifier. */
+		display_entity_notifier_notify(notifier, entity, match);
+	}
+
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_add);
+
+/**
+ * display_entity_remove - removea display entity
+ * @entity: display entity to be removed
+ *
+ * Remove the display entity from the list of available entities and send the
+ * DISPLAY_ENTITY_NOTIFIER_DISCONNECT event to all matching registered
+ * notifiers.
+ */
+void display_entity_remove(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier = entity->notifier;
+	struct display_entity_match *match = entity->match;
+
+	pr_debug("%s: removing entity '%s' (ptr 0x%p dev '%s')\n", __func__,
+		 entity->name, entity, dev_name(entity->dev));
+
+	if (!notifier) {
+		/* Remove the entity from the global list. */
+		list_del(&entity->list);
+		return;
+	}
+
+	mutex_lock(&display_entity_mutex);
+
+	if (notifier->unbind)
+		notifier->unbind(notifier, entity, match);
+
+	/* Remove the entity from the notifier's done list. */
+	display_entity_notifier_cleanup(entity);
+	list_del(&entity->list);
+
+	/* Move the match back to the waiting list. */
+	list_add_tail(&match->list, &notifier->waiting);
+
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_remove);
diff --git a/include/video/display.h b/include/video/display.h
index fef05a68..2063694 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -23,6 +23,8 @@
  */
 
 struct display_entity;
+struct display_entity_match;
+struct display_entity_notify;
 struct videomode;
 
 /**
@@ -101,6 +103,9 @@ struct display_entity {
 	char name[32];
 	struct media_entity entity;
 
+	struct display_entity_match *match;
+	struct display_entity_notifier *notifier;
+
 	const struct display_entity_ops *ops;
 
 	void(*release)(struct display_entity *ent);
@@ -140,4 +145,65 @@ int display_entity_get_params(struct display_entity *entity, unsigned int port,
 int display_entity_set_stream(struct display_entity *entity, unsigned int port,
 			      enum display_entity_stream_state state);
 
+/* -----------------------------------------------------------------------------
+ * Notifier
+ */
+
+enum display_entity_bus_type {
+	DISPLAY_ENTITY_BUS_PLATFORM,
+};
+
+/**
+ * struct display_entity_match - Display entity description
+ * @type: display entity bus type
+ * @match.platform.name: platform device name
+ * @match.dt.node: DT node
+ * @list: link match objects waiting to be matched
+ */
+struct display_entity_match {
+	enum display_entity_bus_type type;
+	union {
+		struct {
+			const char *name;
+		} platform;
+	} match;
+
+	struct list_head list;
+};
+
+/**
+ * display_entity_notifier - display entity notifier
+ * @num_entities: number of display entities
+ * @entities: array of pointers to subdevice descriptors
+ * @waiting: list of struct v4l2_async_subdev, waiting for their drivers
+ * @done: list of struct v4l2_async_subdev_list, already probed
+ * @list: member in a global list of notifiers
+ * @bound: a display entity has been registered
+ * @complete: all display entities have been registered
+ * @unbind: a display entity is being unregistered
+ */
+struct display_entity_notifier {
+	struct device *dev;
+
+	unsigned int num_entities;
+	struct display_entity_match *entities;
+	struct list_head waiting;
+	struct list_head done;
+	struct list_head list;
+
+	int (*bound)(struct display_entity_notifier *notifier,
+		     struct display_entity *entity,
+		     struct display_entity_match *match);
+	int (*complete)(struct display_entity_notifier *notifier);
+	void (*unbind)(struct display_entity_notifier *notifier,
+		       struct display_entity *entity,
+		       struct display_entity_match *match);
+};
+
+int display_entity_register_notifier(struct display_entity_notifier *notifier);
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier);
+
+int display_entity_add(struct display_entity *entity);
+void display_entity_remove(struct display_entity *entity);
+
 #endif /* __DISPLAY_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 05/19] video: display: Graph helpers
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (3 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 04/19] video: display: Add display entity notifier Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 06/19] video: display: OF support Laurent Pinchart
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Add two graph helper functions. display_entity_build_notifier() builds
an entity notifier from an entities graph represented as a flat array,
typically passed from platform data. display_entity_link_graph() can
then be used to create media controller links between all entities in
the graph.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/display-core.c     | 107 +++++++++++++++++++++++++++++++
 drivers/video/display/display-notifier.c |  51 +++++++++++++++
 include/video/display.h                  |  20 ++++++
 3 files changed, 178 insertions(+)

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index bb18723..c3b47d3 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -10,6 +10,7 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/device.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -313,6 +314,112 @@ void display_entity_unregister(struct display_entity *entity)
 }
 EXPORT_SYMBOL_GPL(display_entity_unregister);
 
+/* -----------------------------------------------------------------------------
+ * Graph Helpers
+ */
+
+static int display_entity_link_entity(struct device *dev,
+				      struct display_entity *entity,
+				      struct list_head *entities)
+{
+	const struct display_entity_graph_data *graph = entity->match->data;
+	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+	struct media_entity *local = &entity->entity;
+	unsigned int i;
+	int ret = 0;
+
+	dev_dbg(dev, "creating links for entity %s\n", local->name);
+
+	for (i = 0; i < entity->entity.num_pads; ++i) {
+		const struct display_entity_source_data *source;
+		struct media_pad *local_pad = &local->pads[i];
+		struct media_entity *remote = NULL;
+		struct media_pad *remote_pad;
+		struct display_entity *ent;
+
+		dev_dbg(dev, "processing pad %u\n", i);
+
+		/* Skip source pads, they will be processed from the other end
+		 * of the link.
+		 */
+		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+			dev_dbg(dev, "skipping source pad %s:%u\n",
+				local->name, i);
+			continue;
+		}
+
+		/* Find the remote entity. If not found, just skip the link as
+		 * it goes out of scope of the entities handled by the notifier.
+		 */
+		source = &graph->sources[i];
+		list_for_each_entry(ent, entities, list) {
+			if (strcmp(source->name, dev_name(ent->dev)) == 0) {
+				remote = &ent->entity;
+				break;
+			}
+		}
+
+		if (remote == NULL) {
+			dev_dbg(dev, "no entity found for %s\n", source->name);
+			continue;
+		}
+
+		if (source->port >= remote->num_pads) {
+			dev_err(dev, "invalid port number %u on %s\n",
+				source->port, source->name);
+			ret = -EINVAL;
+			break;
+		}
+
+		remote_pad = &remote->pads[source->port];
+
+		/* Create the media link. */
+		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
+			remote->name, remote_pad->index,
+			local->name, local_pad->index);
+
+		ret = media_entity_create_link(remote, remote_pad->index,
+					       local, local_pad->index,
+					       link_flags);
+		if (ret < 0) {
+			dev_err(dev, "failed to create %s:%u -> %s:%u link\n",
+				remote->name, remote_pad->index,
+				local->name, local_pad->index);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * display_entity_link_graph - Link all entities in a graph
+ * @dev: device used to print debugging and error messages
+ * @entities: list of display entities in the graph
+ *
+ * This function creates media controller links for all entities in a graph
+ * based on graph link data. It relies on the entities match data pointers
+ * having been initialized by the display_entity_build_notifier() function when
+ * building the notifier and thus can't be used when the notifier is built in a
+ * different way.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_link_graph(struct device *dev, struct list_head *entities)
+{
+	struct display_entity *entity;
+	int ret;
+
+	list_for_each_entry(entity, entities, list) {
+		ret = display_entity_link_entity(dev, entity, entities);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_link_graph);
+
 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 MODULE_DESCRIPTION("Display Core");
 MODULE_LICENSE("GPL");
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
index c9210ec..2d752b3 100644
--- a/drivers/video/display/display-notifier.c
+++ b/drivers/video/display/display-notifier.c
@@ -220,6 +220,57 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
 }
 EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
 
+/**
+ * display_entity_build_notifier - build a notifier from graph data
+ * @notifier: display entity notifier to be built
+ * @graph: graph data
+ *
+ * Before registering a notifier drivers must initialize the notifier's list of
+ * entities. This helper function simplifies building the list of entities for
+ * drivers that use an array of struct display_entity_graph_data to describe the
+ * entities graph.
+ *
+ * The function allocates an array of struct display_entity_match, initialize it
+ * from graph data, and sets the notifier entities and num_entities fields.
+ *
+ * The entities array is allocated using the managed memory allocation API on
+ * the notifier device, which must be initialized before calling this function.
+ *
+ * Return 0 on success or a negative error code on error.
+ */
+int display_entity_build_notifier(struct display_entity_notifier *notifier,
+				  const struct display_entity_graph_data *graph)
+{
+	struct display_entity_match *entities;
+	unsigned int num_entities;
+	unsigned int i;
+
+	for (num_entities = 0; graph[num_entities].name; ++num_entities) {
+	}
+
+	if (num_entities == 0)
+		return -EINVAL;
+
+	entities = devm_kzalloc(notifier->dev, sizeof(*notifier->entities) *
+				num_entities, GFP_KERNEL);
+	if (entities == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < num_entities; ++i) {
+		struct display_entity_match *match = &entities[i];
+
+		match->type = DISPLAY_ENTITY_BUS_PLATFORM;
+		match->match.platform.name = graph[i].name;
+		match->data = &graph[i];
+	}
+
+	notifier->num_entities = num_entities;
+	notifier->entities = entities;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_build_notifier);
+
 /* -----------------------------------------------------------------------------
  * Entity Registration
  */
diff --git a/include/video/display.h b/include/video/display.h
index 2063694..58ff0d1 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -159,6 +159,7 @@ enum display_entity_bus_type {
  * @match.platform.name: platform device name
  * @match.dt.node: DT node
  * @list: link match objects waiting to be matched
+ * @data: driver private data, not touched by the core
  */
 struct display_entity_match {
 	enum display_entity_bus_type type;
@@ -169,6 +170,7 @@ struct display_entity_match {
 	} match;
 
 	struct list_head list;
+	const void *data;
 };
 
 /**
@@ -206,4 +208,22 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
 int display_entity_add(struct display_entity *entity);
 void display_entity_remove(struct display_entity *entity);
 
+/* -----------------------------------------------------------------------------
+ * Graph Helpers
+ */
+
+struct display_entity_source_data {
+	const char *name;
+	unsigned int port;
+};
+
+struct display_entity_graph_data {
+	const char *name;
+	const struct display_entity_source_data *sources;
+};
+
+int display_entity_build_notifier(struct display_entity_notifier *notifier,
+				  const struct display_entity_graph_data *graph);
+int display_entity_link_graph(struct device *dev, struct list_head *entities);
+
 #endif /* __DISPLAY_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 06/19] video: display: OF support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (4 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 05/19] video: display: Graph helpers Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-27  9:30   ` Philipp Zabel
  2013-09-04 14:21   ` Philipp Zabel
  2013-08-09 23:03 ` [PATCH/RFC v3 07/19] video: display: Add pixel coding definitions Laurent Pinchart
                   ` (12 subsequent siblings)
  18 siblings, 2 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Extend the notifier with DT node matching support, and add helper functions to
build the notifier and link entities based on a graph representation in
DT.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/display-core.c     | 334 +++++++++++++++++++++++++++++++
 drivers/video/display/display-notifier.c | 187 +++++++++++++++++
 include/video/display.h                  |  45 +++++
 3 files changed, 566 insertions(+)

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index c3b47d3..328ead7 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -14,6 +14,7 @@
 #include <linux/export.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 
 #include <media/media-device.h>
@@ -315,6 +316,184 @@ void display_entity_unregister(struct display_entity *entity)
 EXPORT_SYMBOL_GPL(display_entity_unregister);
 
 /* -----------------------------------------------------------------------------
+ * OF Helpers
+ */
+
+#ifdef CONFIG_OF
+
+/**
+ * display_of_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is not decremented, the caller have to use
+ * of_node_put() on it when done.
+ */
+struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+			     struct device_node *prev)
+{
+	struct device_node *endpoint;
+	struct device_node *port = NULL;
+
+	if (!parent)
+		return NULL;
+
+	if (!prev) {
+		struct device_node *node;
+		/*
+		 * It's the first call, we have to find a port subnode
+		 * within this node or within an optional 'ports' node.
+		 */
+		node = of_get_child_by_name(parent, "ports");
+		if (node)
+			parent = node;
+
+		port = of_get_child_by_name(parent, "port");
+
+		if (port) {
+			/* Found a port, get an endpoint. */
+			endpoint = of_get_next_child(port, NULL);
+			of_node_put(port);
+		} else {
+			endpoint = NULL;
+		}
+
+		if (!endpoint)
+			pr_err("%s(): no endpoint nodes specified for %s\n",
+			       __func__, parent->full_name);
+		of_node_put(node);
+	} else {
+		port = of_get_parent(prev);
+		if (!port)
+			/* Hm, has someone given us the root node ?... */
+			return NULL;
+
+		/* Avoid dropping prev node refcount to 0. */
+		of_node_get(prev);
+		endpoint = of_get_next_child(port, prev);
+		if (endpoint) {
+			of_node_put(port);
+			return endpoint;
+		}
+
+		/* No more endpoints under this port, try the next one. */
+		do {
+			port = of_get_next_child(parent, port);
+			if (!port)
+				return NULL;
+		} while (of_node_cmp(port->name, "port"));
+
+		/* Pick up the first endpoint in this port. */
+		endpoint = of_get_next_child(port, NULL);
+		of_node_put(port);
+	}
+
+	return endpoint;
+}
+
+/**
+ * display_of_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node)
+{
+	struct device_node *np;
+	unsigned int depth;
+
+	/* Get remote endpoint node. */
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+
+	/* Walk 3 levels up only if there is 'ports' node. */
+	for (depth = 3; depth && np; depth--) {
+		np = of_get_next_parent(np);
+		if (depth == 2 && of_node_cmp(np->name, "ports"))
+			break;
+	}
+	return np;
+}
+
+/**
+ * struct display_of_link - a link between two endpoints
+ * @local_node: pointer to device_node of this endpoint
+ * @local_port: identifier of the port this endpoint belongs to
+ * @remote_node: pointer to device_node of the remote endpoint
+ * @remote_port: identifier of the port the remote endpoint belongs to
+ */
+struct display_of_link {
+	struct device_node *local_node;
+	unsigned int local_port;
+	struct device_node *remote_node;
+	unsigned int remote_port;
+};
+
+/**
+ * display_of_parse_link() - parse a link between two endpoints
+ * @node: pointer to the endpoint at the local end of the link
+ * @link: pointer to the display OF link data structure
+ *
+ * Fill the link structure with the local and remote nodes and port numbers.
+ * The local_node and remote_node fields are set to point to the local and
+ * remote port parent nodes respectively (the port parent node being the parent
+ * node of the port node if that node isn't a 'ports' node, or the grand-parent
+ * node of the port node otherwise).
+ *
+ * A reference is taken to both the local and remote nodes, the caller must use
+ * display_of_put_link() to drop the references when done with the link.
+ *
+ * Return: 0 on success, or -ENOLINK if the remote endpoint can't be found.
+ */
+static int display_of_parse_link(const struct device_node *node,
+				 struct display_of_link *link)
+{
+	struct device_node *np;
+
+	memset(link, 0, sizeof(*link));
+
+	np = of_get_parent(node);
+	of_property_read_u32(np, "reg", &link->local_port);
+	np = of_get_next_parent(np);
+	if (of_node_cmp(np->name, "ports") == 0)
+		np = of_get_next_parent(np);
+	link->local_node = np;
+
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+	if (!np) {
+		of_node_put(link->local_node);
+		return -ENOLINK;
+	}
+
+	np = of_get_parent(np);
+	of_property_read_u32(np, "reg", &link->remote_port);
+	np = of_get_next_parent(np);
+	if (of_node_cmp(np->name, "ports") == 0)
+		np = of_get_next_parent(np);
+	link->remote_node = np;
+
+	return 0;
+}
+
+/**
+ * display_of_put_link() - drop references to nodes in a link
+ * @link: pointer to the display OF link data structure
+ *
+ * Drop references to the local and remote nodes in the link. This function must
+ * be called on every link parsed with display_of_parse_link().
+ */
+static void display_of_put_link(struct display_of_link *link)
+{
+	of_node_put(link->local_node);
+	of_node_put(link->remote_node);
+}
+
+#endif /* CONFIG_OF */
+
+/* -----------------------------------------------------------------------------
  * Graph Helpers
  */
 
@@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev, struct list_head *entities)
 }
 EXPORT_SYMBOL_GPL(display_entity_link_graph);
 
+#ifdef CONFIG_OF
+
+static int display_of_entity_link_entity(struct device *dev,
+					 struct display_entity *entity,
+					 struct list_head *entities,
+					 struct display_entity *root)
+{
+	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+	const struct device_node *node = entity->dev->of_node;
+	struct media_entity *local = &entity->entity;
+	struct device_node *ep = NULL;
+	int ret = 0;
+
+	dev_dbg(dev, "creating links for entity %s\n", local->name);
+
+	while (1) {
+		struct media_entity *remote = NULL;
+		struct media_pad *remote_pad;
+		struct media_pad *local_pad;
+		struct display_of_link link;
+		struct display_entity *ent;
+		struct device_node *next;
+
+		/* Get the next endpoint and parse its link. */
+		next = display_of_get_next_endpoint(node, ep);
+		if (next == NULL)
+			break;
+
+		of_node_put(ep);
+		ep = next;
+
+		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
+
+		ret = display_of_parse_link(ep, &link);
+		if (ret < 0) {
+			dev_err(dev, "failed to parse link for %s\n",
+				ep->full_name);
+			continue;
+		}
+
+		/* Skip source pads, they will be processed from the other end of
+		 * the link.
+		 */
+		if (link.local_port >= local->num_pads) {
+			dev_err(dev, "invalid port number %u on %s\n",
+				link.local_port, link.local_node->full_name);
+			display_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		local_pad = &local->pads[link.local_port];
+
+		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+			dev_dbg(dev, "skipping source port %s:%u\n",
+				link.local_node->full_name, link.local_port);
+			display_of_put_link(&link);
+			continue;
+		}
+
+		/* Find the remote entity. If not found, just skip the link as
+		 * it goes out of scope of the entities handled by the notifier.
+		 */
+		list_for_each_entry(ent, entities, list) {
+			if (ent->dev->of_node == link.remote_node) {
+				remote = &ent->entity;
+				break;
+			}
+		}
+
+		if (root->dev->of_node == link.remote_node)
+			remote = &root->entity;
+
+		if (remote == NULL) {
+			dev_dbg(dev, "no entity found for %s\n",
+				link.remote_node->full_name);
+			display_of_put_link(&link);
+			continue;
+		}
+
+		if (link.remote_port >= remote->num_pads) {
+			dev_err(dev, "invalid port number %u on %s\n",
+				link.remote_port, link.remote_node->full_name);
+			display_of_put_link(&link);
+			ret = -EINVAL;
+			break;
+		}
+
+		remote_pad = &remote->pads[link.remote_port];
+
+		display_of_put_link(&link);
+
+		/* Create the media link. */
+		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
+			remote->name, remote_pad->index,
+			local->name, local_pad->index);
+
+		ret = media_entity_create_link(remote, remote_pad->index,
+					       local, local_pad->index,
+					       link_flags);
+		if (ret < 0) {
+			dev_err(dev,
+				"failed to create %s:%u -> %s:%u link\n",
+				remote->name, remote_pad->index,
+				local->name, local_pad->index);
+			break;
+		}
+	}
+
+	of_node_put(ep);
+	return ret;
+}
+
+/**
+ * display_of_entity_link_graph - Link all entities in a graph
+ * @dev: device used to print debugging and error messages
+ * @root: optional root display entity
+ * @entities: list of display entities in the graph
+ *
+ * This function creates media controller links for all entities in a graph
+ * based on the device tree graph representation. It relies on all entities
+ * having been instantiated from the device tree.
+ *
+ * The list of entities is typically taken directly from a display notifier
+ * done list. It will thus not include any display entity not handled by the
+ * notifier, such as entities directly accessible by the caller without going
+ * through the notification process. The optional root entity parameter can be
+ * used to pass such a display entity and include it in the graph. For all
+ * practical purpose the root entity is handled is if it was part of the
+ * entities list.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
+				 struct display_entity *root)
+{
+	struct display_entity *entity;
+	int ret;
+
+	list_for_each_entry(entity, entities, list) {
+		if (WARN_ON(entity->match->type != DISPLAY_ENTITY_BUS_DT))
+			return -EINVAL;
+
+		ret = display_of_entity_link_entity(dev, entity, entities,
+						    root);
+		if (ret < 0)
+			return ret;
+	}
+
+	return display_of_entity_link_entity(dev, root, entities, root);
+}
+EXPORT_SYMBOL_GPL(display_of_entity_link_graph);
+
+#endif
+
 MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
 MODULE_DESCRIPTION("Display Core");
 MODULE_LICENSE("GPL");
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
index 2d752b3..6bede03 100644
--- a/drivers/video/display/display-notifier.c
+++ b/drivers/video/display/display-notifier.c
@@ -16,6 +16,7 @@
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/slab.h>
 
 #include <video/display.h>
 
@@ -36,6 +37,14 @@ static bool match_platform(struct device *dev,
 	return !strcmp(match->match.platform.name, dev_name(dev));
 }
 
+static bool match_dt(struct device *dev, struct display_entity_match *match)
+{
+	pr_debug("%s: matching device node '%s' with node '%s'\n", __func__,
+		 dev->of_node->full_name, match->match.dt.node->full_name);
+
+	return match->match.dt.node == dev->of_node;
+}
+
 static struct display_entity_match *
 display_entity_notifier_match(struct display_entity_notifier *notifier,
 			      struct display_entity *entity)
@@ -52,6 +61,9 @@ display_entity_notifier_match(struct display_entity_notifier *notifier,
 		case DISPLAY_ENTITY_BUS_PLATFORM:
 			match_func = match_platform;
 			break;
+		case DISPLAY_ENTITY_BUS_DT:
+			match_func = match_dt;
+			break;
 		}
 
 		if (match_func(entity->dev, match))
@@ -158,6 +170,7 @@ int display_entity_register_notifier(struct display_entity_notifier *notifier)
 
 		switch (match->type) {
 		case DISPLAY_ENTITY_BUS_PLATFORM:
+		case DISPLAY_ENTITY_BUS_DT:
 			break;
 		default:
 			dev_err(notifier->dev,
@@ -272,6 +285,180 @@ int display_entity_build_notifier(struct display_entity_notifier *notifier,
 EXPORT_SYMBOL_GPL(display_entity_build_notifier);
 
 /* -----------------------------------------------------------------------------
+ * OF Support
+ */
+
+#ifdef CONFIG_OF
+
+struct display_entity_of {
+	struct list_head list;
+	struct device_node *node;
+};
+
+static struct display_entity_of *
+display_of_find_entity(struct list_head *entities,
+		       const struct device_node *node)
+{
+	struct display_entity_of *entity;
+
+	list_for_each_entry(entity, entities, list) {
+		if (entity->node == node)
+			return entity;
+	}
+
+	return NULL;
+}
+
+static int display_of_parse_dt(struct display_entity_notifier *notifier,
+			       struct list_head *entities,
+			       struct device_node *node)
+{
+	struct display_entity_of *entity;
+	struct device_node *remote;
+	struct device_node *ep = NULL;
+	struct device_node *next;
+	unsigned int num_entities = 0;
+	int ret = 0;
+
+	/* Walk the device tree and build a list of nodes. */
+	dev_dbg(notifier->dev, "parsing node %s\n", node->full_name);
+
+	while (1) {
+		next = display_of_get_next_endpoint(node, ep);
+		if (next == NULL)
+			break;
+
+		of_node_put(ep);
+		ep = next;
+
+		dev_dbg(notifier->dev, "handling endpoint %s\n", ep->full_name);
+
+		remote = display_of_get_remote_port_parent(ep);
+		if (remote == NULL)
+			continue;
+
+		/* Skip entities that we have already processed. */
+		if (display_of_find_entity(entities, remote) || remote == node) {
+			dev_dbg(notifier->dev,
+				"entity %s already in list, skipping\n",
+				remote->full_name);
+			continue;
+		}
+
+		entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+		if (entity == NULL) {
+			of_node_put(remote);
+			ret = -ENOMEM;
+			break;
+		}
+
+		dev_dbg(notifier->dev, "adding remote entity %s to list\n",
+			remote->full_name);
+
+		entity->node = remote;
+		list_add_tail(&entity->list, entities);
+		num_entities++;
+	}
+
+	of_node_put(ep);
+
+	if (ret < 0)
+		return ret;
+
+	return num_entities;
+}
+
+/**
+ * display_of_entity_build_notifier - build a notifier from device tree
+ * @notifier: display entity notifier to be built
+ * @node: device tree node
+ *
+ * Before registering a notifier drivers must initialize the notifier's list of
+ * entities. This helper function simplifies building the list of entities for
+ * drivers that use a device tree representation of the graph.
+ *
+ * The function allocates an array of struct display_entity_match, initialize it
+ * from the device tree, and sets the notifier entities and num_entities fields.
+ *
+ * The entities array is allocated using the managed memory allocation API on
+ * the notifier device, which must be initialized before calling this function.
+ *
+ * Return 0 on success or a negative error code on error.
+ */
+int display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+				     struct device_node *node)
+{
+	struct display_entity_match *matches;
+	struct display_entity_of *entity;
+	struct display_entity_of *next;
+	unsigned int num_entities = 0;
+	LIST_HEAD(entities);
+	unsigned int i;
+	int ret;
+
+	/* Add an initial entity that stores the device tree node pointer to the
+	 * list.
+	 */
+	entity = kzalloc(sizeof(*entity), GFP_KERNEL);
+	if (entity == NULL)
+		return -ENOMEM;
+
+	entity->node = node;
+	list_add_tail(&entity->list, &entities);
+
+	/* Parse all entities in the list. New entities will be added at the
+	 * tail when parsing the device tree and will just be processed by the
+	 * next iterations.
+	 */
+	list_for_each_entry(entity, &entities, list) {
+		ret = display_of_parse_dt(notifier, &entities, entity->node);
+		if (ret < 0)
+			goto error;
+
+		num_entities += ret;
+	}
+
+	/* Allocate the entity matches array and fill it. */
+	matches = devm_kzalloc(notifier->dev, sizeof(*notifier->entities) *
+				num_entities, GFP_KERNEL);
+	if (matches == NULL) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	i = 0;
+	list_for_each_entry_safe(entity, next, &entities, list) {
+		struct display_entity_match *match;
+
+		/* Don't add the initial node to the matches array. */
+		if (entity->node != node) {
+			match = &matches[i++];
+			match->type = DISPLAY_ENTITY_BUS_DT;
+			match->match.dt.node = entity->node;
+		}
+
+		list_del(&entity->list);
+		kfree(entity);
+	}
+
+	notifier->num_entities = num_entities;
+	notifier->entities = matches;
+
+	return 0;
+
+error:
+	list_for_each_entry_safe(entity, next, &entities, list) {
+		list_del(&entity->list);
+		kfree(entity);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(display_of_entity_build_notifier);
+
+#endif /* CONFIG_OF */
+
+/* -----------------------------------------------------------------------------
  * Entity Registration
  */
 
diff --git a/include/video/display.h b/include/video/display.h
index 58ff0d1..36ff637 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -22,6 +22,7 @@
  * Display Entity
  */
 
+struct device_node;
 struct display_entity;
 struct display_entity_match;
 struct display_entity_notify;
@@ -145,12 +146,33 @@ int display_entity_get_params(struct display_entity *entity, unsigned int port,
 int display_entity_set_stream(struct display_entity *entity, unsigned int port,
 			      enum display_entity_stream_state state);
 
+#ifdef CONFIG_OF
+struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+			     struct device_node *prev);
+struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node);
+#else
+static inline struct device_node *
+display_of_get_next_endpoint(const struct device_node *parent,
+			     struct device_node *prev)
+{
+	return NULL;
+}
+static inline struct device_node *
+display_of_get_remote_port_parent(const struct device_node *node)
+{
+	return NULL;
+}
+#endif
+
 /* -----------------------------------------------------------------------------
  * Notifier
  */
 
 enum display_entity_bus_type {
 	DISPLAY_ENTITY_BUS_PLATFORM,
+	DISPLAY_ENTITY_BUS_DT,
 };
 
 /**
@@ -167,6 +189,9 @@ struct display_entity_match {
 		struct {
 			const char *name;
 		} platform;
+		struct {
+			const struct device_node *node;
+		} dt;
 	} match;
 
 	struct list_head list;
@@ -226,4 +251,24 @@ int display_entity_build_notifier(struct display_entity_notifier *notifier,
 				  const struct display_entity_graph_data *graph);
 int display_entity_link_graph(struct device *dev, struct list_head *entities);
 
+#ifdef CONFIG_OF
+int display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+				     struct device_node *node);
+int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
+				 struct display_entity *root);
+#else
+static inline int
+display_of_entity_build_notifier(struct display_entity_notifier *notifier,
+				 struct device_node *node)
+{
+	return -ENOSYS;
+}
+static inline int
+display_of_entity_link_graph(struct device *dev,struct list_head *entities,
+			     struct display_entity *root)
+{
+	return -ENOSYS;
+}
+#endif
+
 #endif /* __DISPLAY_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 07/19] video: display: Add pixel coding definitions
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (5 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 06/19] video: display: OF support Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support Laurent Pinchart
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Pixel codings describe how pixels are transmitted on a physical bus. The
information can be communicated between drivers to configure devices.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 include/video/display.h | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)

diff --git a/include/video/display.h b/include/video/display.h
index 36ff637..ba319d6 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -18,6 +18,126 @@
 #include <linux/module.h>
 #include <media/media-entity.h>
 
+#define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
+	(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
+	 ((to) << 5) | (from))
+
+#define DISPLAY_PIXEL_CODING_FROM(coding)	((coding) & 0x1f)
+#define DISPLAY_PIXEL_CODING_TO(coding)		(((coding) >> 5) & 0x1f)
+#define DISPLAY_PIXEL_CODING_VARIANT(coding)	(((coding) >> 10) & 7)
+#define DISPLAY_PIXEL_CODING_TYPE(coding)	(((coding) >> 13) & 0xf)
+
+#define DISPLAY_PIXEL_CODING_TYPE_DBI	0
+#define DISPLAY_PIXEL_CODING_TYPE_DPI	1
+
+/* DBI pixel codings. */
+#define DISPLAY_PIXEL_CODING_DBI(from, to, variant) \
+	DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DBI, \
+				  from, to, variant, 0)
+
+/* Standard DBI codings, defined in the DBI specification. */
+/*   17   16   15   14   13   12   11   10    9    8    7    6    5    4    3    2    1    0 */
+/*    -    -    -    -    -    -    -    -    -    - R0,2 R0,1 R0,0 G0,2 G0,1 G0,0 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_8TO8		DISPLAY_PIXEL_CODING_DBI(8, 8, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,3 R0,2 R0,1 R0,0 G0,3 G0,2 G0,1 G0,0 */
+/*    -    -    -    -    -    -    -    -    -    - B0,3 B0,2 B0,1 B0,0 R1,3 R1,2 R1,1 R1,0 */
+/*    -    -    -    -    -    -    -    -    -    - G1,3 G1,2 G1,1 G1,0 B1,3 B1,2 B1,1 b1,0 */
+#define DISPLAY_PIXEL_CODING_DBI_12TO8		DISPLAY_PIXEL_CODING_DBI(12, 8, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 */
+/*    -    -    -    -    -    -    -    -    -    - G0,2 G0,1 G0,0 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_16TO8		DISPLAY_PIXEL_CODING_DBI(16, 8, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0    -    - */
+/*    -    -    -    -    -    -    -    -    -    - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0    -    - */
+/*    -    -    -    -    -    -    -    -    -    - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0    -    - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8		DISPLAY_PIXEL_CODING_DBI(18, 8, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 */
+/*    -    -    -    -    -    -    -    -    -    - G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 */
+/*    -    -    -    -    -    -    -    -    -    - B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO8		DISPLAY_PIXEL_CODING_DBI(24, 8, 0)
+/*    -    -    -    -    -    -    -    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,4 */
+/*    -    -    -    -    -    -    -    -    - G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO9		DISPLAY_PIXEL_CODING_DBI(18, 9, 0)
+/*    -    - R1,2 R1,1 R1,0 G1,2 G1,1 G1,0 B1,1 B1,0 R0,2 R0,1 R0,0 G0,2 G0,1 G0,0 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_8TO16		DISPLAY_PIXEL_CODING_DBI(8, 16, 0)
+/*    -    -    -    -    -    - R0,3 R0,2 R0,1 R0,0 G0,3 G0,2 G0,1 G0,0 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_12TO16		DISPLAY_PIXEL_CODING_DBI(12, 16, 0)
+/*    -    - R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_16TO16		DISPLAY_PIXEL_CODING_DBI(16, 16, 0)
+/*    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0    -    - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0    -    - */
+/*    -    - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0    -    - R1,5 R1,4 R1,3 R1,2 R1,1 R1,0    -    - */
+/*    -    - G1,5 G1,4 G1,3 G1,2 G1,1 G1,0    -    - B1,5 B1,4 B1,3 B1,2 B1,1 B1,0    -    - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_A	DISPLAY_PIXEL_CODING_DBI(18, 16, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0    -    - */
+/*    -    - G0,5 G0,4 G0,3 G0,2 G0,1 G0,0    -    - B0,5 B0,4 B0,3 B0,2 B0,1 B0,0    -    - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_B	DISPLAY_PIXEL_CODING_DBI(18, 16, 1)
+/*    -    - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 */
+/*    -    - B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 R1,7 R1,6 R1,5 R1,4 R1,3 R1,2 R1,1 R1,0 */
+/*    -    - G1,7 G1,6 G1,5 G1,4 G1,3 G1,2 G1,1 G1,0 B1,7 B1,6 B1,5 B1,4 B1,3 B1,2 B1,1 B1,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO16_A	DISPLAY_PIXEL_CODING_DBI(24, 16, 0)
+/*    -    -    -    -    -    -    -    -    -    - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 */
+/*    -    - G0,7 G0,6 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO16_B	DISPLAY_PIXEL_CODING_DBI(24, 16, 1)
+
+/* Non-standard DBI pixel codings. */
+/*   17   16   15   14   13   12   11   10    9    8    7    6    5    4    3    2    1    0 */
+/*    -    -    -    -    -    -    -    -    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 */
+/*    -    -    -    -    -    -    -    -    -    - G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 */
+/*    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    - B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8_B	DISPLAY_PIXEL_CODING_DBI(18, 8, 1)
+/*    -    -    -    -    -    -    -    -    -    -    -    -    -    -    -    - R0,5 R0,4 */
+/*    -    -    -    -    -    -    -    -    -    - R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 */
+/*    -    -    -    -    -    -    -    -    -    - G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO8_C	DISPLAY_PIXEL_CODING_DBI(18, 8, 2)
+/*    -    - R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 */
+/*    -    - B0,1 B0,0    -    -    -    -    -    -    -    -    -    -    -    -    -    - */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_C	DISPLAY_PIXEL_CODING_DBI(18, 16, 2)
+/*    -    - R0,5 R0,4    -    -    -    -    -    -    -    -    -    -    -    -    -    - */
+/*    -    - R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO16_D	DISPLAY_PIXEL_CODING_DBI(18, 16, 3)
+/*    -    -    -    -    -    - R0,7 R0,6 R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,7 G0,6 G0,5 G0,4 */
+/*    -    -    -    -    -    - G0,3 G0,2 G0,1 G0,0 B0,7 B0,6 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_24TO12		DISPLAY_PIXEL_CODING_DBI(24, 12, 0)
+/* R0,5 R0,4 R0,3 R0,2 R0,1 R0,0 G0,5 G0,4 G0,3 G0,2 G0,1 G0,0 B0,5 B0,4 B0,3 B0,2 B0,1 B0,0 */
+#define DISPLAY_PIXEL_CODING_DBI_18TO18		DISPLAY_PIXEL_CODING_DBI(18, 18, 0)
+
+/* DPI pixel codings. */
+#define DISPLAY_PIXEL_CODING_DPI_RGB(from, to, variant) \
+	DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DPI, \
+				  from, to, variant, 0)
+#define DISPLAY_PIXEL_CODING_DPI_YUV(from, to, variant) \
+	DISPLAY_PIXEL_CODING_TYPE(DISPLAY_PIXEL_CODING_TYPE_DPI, \
+				  from, to, variant, 1)
+
+/* Standard DPI codings, defined in the DPI specification. */
+/* 23 22 21 20 19 28 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 */
+/*  -  -  -  -  -  -  -  - R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO16	DISPLAY_PIXEL_CODING_DPI_RGB(16, 16, 0)
+/*  -  -  -  -  -  - R5 R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO18	DISPLAY_PIXEL_CODING_DPI_RGB(18, 18, 0)
+/*  -  -  - R4 R3 R2 R1 R0  -  - G5 G4 G3 G2 G1 G0  -  -  - B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO20	DISPLAY_PIXEL_CODING_DPI_RGB(16, 20, 0)
+/*  -  - R4 R3 R2 R1 R0  -  -  - G5 G4 G3 G2 G1 G0  -  - B4 B3 B2 B1 B0  - */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_16TO22	DISPLAY_PIXEL_CODING_DPI_RGB(16, 22, 0)
+/*  -  - R5 R4 R3 R2 R1 R0  -  - G5 G4 G3 G2 G1 G0  -  - B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO22	DISPLAY_PIXEL_CODING_DPI_RGB(18, 22, 0)
+/* R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO24	DISPLAY_PIXEL_CODING_DPI_RGB(24, 24, 0)
+
+/* Non-standard DPI pixel codings. */
+/* 23 22 21 20 19 28 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0 */
+/*  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - R7 R6 R5 R4 R3 R2 R1 R0 */
+/*  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - G7 G6 G5 G4 G3 G2 G1 G0 */
+/*  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO8	DISPLAY_PIXEL_CODING_DPI_RGB(24, 8, 0)
+/*  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - R5 R4 R3 R2 R1 R0 G5 G4 G3 */
+/*  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - G2 G1 G0 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_18TO9	DISPLAY_PIXEL_CODING_DPI_RGB(18, 9, 0)
+/*  -  -  -  -  -  -  -  -  -  -  -  - R3 R2 R1 R0 G3 G2 G1 G0 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_12TO12	DISPLAY_PIXEL_CODING_DPI_RGB(12, 12, 0)
+/*  -  -  -  -  -  -  -  -  -  -  -  - R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 */
+/*  -  -  -  -  -  -  -  -  -  -  -  - G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0 */
+#define DISPLAY_PIXEL_CODING_DPI_RGB_24TO12	DISPLAY_PIXEL_CODING_DPI_RGB(24, 12, 0)
+
 /* -----------------------------------------------------------------------------
  * Display Entity
  */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (6 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 07/19] video: display: Add pixel coding definitions Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 09/19] video: panel: Add DPI panel support Laurent Pinchart
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

MIPI DBI is a configurable-width parallel display bus that transmits
commands and data.

Add a new DBI Linux bus type that implements the usual bus
infrastructure (including devices and drivers (un)registration and
matching, and bus configuration and access functions).

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |   8 ++
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/mipi-dbi-bus.c | 234 +++++++++++++++++++++++++++++++++++
 include/video/display.h              |   4 +
 include/video/mipi-dbi-bus.h         | 125 +++++++++++++++++++
 5 files changed, 372 insertions(+)
 create mode 100644 drivers/video/display/mipi-dbi-bus.c
 create mode 100644 include/video/mipi-dbi-bus.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 1d533e7..f7532c1 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -2,3 +2,11 @@ menuconfig DISPLAY_CORE
 	tristate "Display Core"
 	---help---
 	  Support common display framework for graphics devices.
+
+if DISPLAY_CORE
+
+config DISPLAY_MIPI_DBI
+	tristate
+	default n
+
+endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index b907aad..59022d2 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,3 +1,4 @@
 display-y					:= display-core.o \
 						   display-notifier.o
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
+obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
diff --git a/drivers/video/display/mipi-dbi-bus.c b/drivers/video/display/mipi-dbi-bus.c
new file mode 100644
index 0000000..791fb4d
--- /dev/null
+++ b/drivers/video/display/mipi-dbi-bus.c
@@ -0,0 +1,234 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <video/mipi-dbi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width)
+{
+	if (width != 8 && width != 16)
+		return -EINVAL;
+
+	dev->data_width = width;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_set_data_width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd)
+{
+	return dev->bus->ops->write_command(dev->bus, dev, cmd);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_command);
+
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+			size_t len)
+{
+	return dev->bus->ops->write_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_data);
+
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len)
+{
+	return dev->bus->ops->read_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_read_data);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dbi_device_id *
+mipi_dbi_match_id(const struct mipi_dbi_device_id *id,
+		  struct mipi_dbi_device *dev)
+{
+	while (id->name[0]) {
+		if (strcmp(dev->name, id->name) == 0) {
+			dev->id_entry = id;
+			return id;
+		}
+		id++;
+	}
+	return NULL;
+}
+
+static int mipi_dbi_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_drv);
+
+	if (drv->id_table)
+		return mipi_dbi_match_id(drv->id_table, dev) != NULL;
+
+	return (strcmp(dev->name, _drv->name) == 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+	int len = snprintf(buf, PAGE_SIZE, MIPI_DBI_MODULE_PREFIX "%s\n",
+			   dev->name);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dbi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int mipi_dbi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+	add_uevent_var(env, "MODALIAS=%s%s", MIPI_DBI_MODULE_PREFIX,
+		       dev->name);
+	return 0;
+}
+
+static const struct dev_pm_ops mipi_dbi_dev_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dbi_bus_type = {
+	.name		= "mipi-dbi",
+	.dev_attrs	= mipi_dbi_dev_attrs,
+	.match		= mipi_dbi_match,
+	.uevent		= mipi_dbi_uevent,
+	.pm		= &mipi_dbi_dev_pm_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dbi_device_register - register a DBI device
+ * @dev: DBI device we're registering
+ */
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+			      struct mipi_dbi_bus *bus)
+{
+	device_initialize(&dev->dev);
+
+	dev->bus = bus;
+	dev->dev.bus = &mipi_dbi_bus_type;
+	dev->dev.parent = bus->dev;
+
+	if (dev->id != -1)
+		dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
+	else
+		dev_set_name(&dev->dev, "%s", dev->name);
+
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_register);
+
+/**
+ * mipi_dbi_device_unregister - unregister a DBI device
+ * @dev: DBI device we're unregistering
+ */
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev)
+{
+	device_del(&dev->dev);
+	put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_unregister);
+
+static int mipi_dbi_drv_probe(struct device *_dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int mipi_dbi_drv_remove(struct device *_dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+	int ret;
+
+	ret = drv->remove(dev);
+	if (ret < 0)
+		return ret;
+
+	mipi_dbi_set_drvdata(dev, NULL);
+
+	return 0;
+}
+
+/**
+ * mipi_dbi_driver_register - register a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv)
+{
+	drv->driver.bus = &mipi_dbi_bus_type;
+	if (drv->probe)
+		drv->driver.probe = mipi_dbi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dbi_drv_remove;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_register);
+
+/**
+ * mipi_dbi_driver_unregister - unregister a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);
+
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dbi_init(void)
+{
+	return bus_register(&mipi_dbi_bus_type);
+}
+
+static void __exit mipi_dbi_exit(void)
+{
+	bus_unregister(&mipi_dbi_bus_type);
+}
+
+module_init(mipi_dbi_init);
+module_exit(mipi_dbi_exit)
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("MIPI DBI Bus");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
index ba319d6..3138401 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -17,6 +17,7 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <media/media-entity.h>
+#include <video/mipi-dbi-bus.h>
 
 #define DISPLAY_PIXEL_CODING(option, type, from, to, variant) \
 	(((option) << 17) | ((type) << 13) | ((variant) << 10) | \
@@ -189,6 +190,9 @@ enum display_entity_interface_type {
 
 struct display_entity_interface_params {
 	enum display_entity_interface_type type;
+	union {
+		struct mipi_dbi_interface_params dbi;
+	} p;
 };
 
 struct display_entity_control_ops {
diff --git a/include/video/mipi-dbi-bus.h b/include/video/mipi-dbi-bus.h
new file mode 100644
index 0000000..876b69d
--- /dev/null
+++ b/include/video/mipi-dbi-bus.h
@@ -0,0 +1,125 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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_DBI_BUS_H__
+#define __MIPI_DBI_BUS_H__
+
+#include <linux/device.h>
+
+struct mipi_dbi_bus;
+struct mipi_dbi_device;
+
+struct mipi_dbi_bus_ops {
+	int (*write_command)(struct mipi_dbi_bus *bus,
+			     struct mipi_dbi_device *dev, u16 cmd);
+	int (*write_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+			  const u8 *data, size_t len);
+	int (*read_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+			 u8 *data, size_t len);
+};
+
+struct mipi_dbi_bus {
+	struct device *dev;
+	const struct mipi_dbi_bus_ops *ops;
+};
+
+#define MIPI_DBI_MODULE_PREFIX		"mipi-dbi:"
+#define MIPI_DBI_NAME_SIZE		32
+
+struct mipi_dbi_device_id {
+	char name[MIPI_DBI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+enum mipi_dbi_interface_type {
+	MIPI_DBI_INTERFACE_TYPE_A,
+	MIPI_DBI_INTERFACE_TYPE_B,
+};
+
+#define MIPI_DBI_INTERFACE_TE		(1 << 0)
+
+struct mipi_dbi_interface_params {
+	enum mipi_dbi_interface_type type;
+	unsigned int flags;
+
+	unsigned int cs_setup;
+	unsigned int rd_setup;
+	unsigned int rd_latch;
+	unsigned int rd_cycle;
+	unsigned int rd_hold;
+	unsigned int wr_setup;
+	unsigned int wr_cycle;
+	unsigned int wr_hold;
+};
+
+#define MIPI_DBI_FLAG_ALIGN_LEFT	(1 << 0)
+
+struct mipi_dbi_device {
+	const char *name;
+	int id;
+	struct device dev;
+
+	const struct mipi_dbi_device_id *id_entry;
+	struct mipi_dbi_bus *bus;
+
+	unsigned int flags;
+	unsigned int bus_width;
+	unsigned int data_width;
+};
+
+#define to_mipi_dbi_device(d)	container_of(d, struct mipi_dbi_device, dev)
+
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+			     struct mipi_dbi_bus *bus);
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev);
+
+struct mipi_dbi_driver {
+	int(*probe)(struct mipi_dbi_device *);
+	int(*remove)(struct mipi_dbi_device *);
+	struct device_driver driver;
+	const struct mipi_dbi_device_id *id_table;
+};
+
+#define to_mipi_dbi_driver(d)	container_of(d, struct mipi_dbi_driver, driver)
+
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv);
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv);
+
+static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+/* module_mipi_dbi_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_dbi_driver(__mipi_dbi_driver) \
+	module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \
+			mipi_dbi_driver_unregister)
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd);
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len);
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+			size_t len);
+
+#endif /* __MIPI_DBI_BUS__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 09/19] video: panel: Add DPI panel support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (7 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 10/19] video: panel: Add R61505 " Laurent Pinchart
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The Display Pixel Interface is a configurable-width video-only
unidirectional parallel bus standard that defines video formats and
signaling for panel devices.

This driver implements support for simple DPI panels with no runtime
configuration capabilities (GPIOs- and/or regulators-based control can
be implemented later when needed) and exposes it as a display entity.
The panel native video mode is passed to the driver through platform
data or device tree properties.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig     |  10 ++
 drivers/video/display/Makefile    |   1 +
 drivers/video/display/panel-dpi.c | 207 ++++++++++++++++++++++++++++++++++++++
 include/video/panel-dpi.h         |  24 +++++
 4 files changed, 242 insertions(+)
 create mode 100644 drivers/video/display/panel-dpi.c
 create mode 100644 include/video/panel-dpi.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index f7532c1..bce09d6 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -9,4 +9,14 @@ config DISPLAY_MIPI_DBI
 	tristate
 	default n
 
+config DISPLAY_PANEL_DPI
+	tristate "DPI (Parallel) Display Panels"
+	---help---
+	  Support for simple digital (parallel) pixel interface panels. Those
+	  panels receive pixel data through a parallel bus and have no control
+	  bus.
+
+	  If you are in doubt, say N. To compile this driver as a module, choose
+	  M here; the module will be called panel-dpi.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 59022d2..31c017b 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -2,3 +2,4 @@ display-y					:= display-core.o \
 						   display-notifier.o
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
+obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
diff --git a/drivers/video/display/panel-dpi.c b/drivers/video/display/panel-dpi.c
new file mode 100644
index 0000000..b1ecf6d
--- /dev/null
+++ b/drivers/video/display/panel-dpi.c
@@ -0,0 +1,207 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
+
+struct panel_dpi {
+	struct display_entity entity;
+
+	unsigned int width;
+	unsigned int height;
+	struct videomode mode;
+};
+
+static inline struct panel_dpi *to_panel_dpi(struct display_entity *e)
+{
+	return container_of(e, struct panel_dpi, entity);
+}
+
+static const struct display_entity_interface_params panel_dpi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DPI,
+};
+
+static int panel_dpi_set_state(struct display_entity *entity,
+			       enum display_entity_state state)
+{
+	struct media_pad *source;
+
+	source = media_entity_remote_pad(&entity->entity.pads[0]);
+	if (source == NULL)
+		return -EPIPE;
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_STOPPED);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		break;
+	}
+
+	return 0;
+}
+
+static int panel_dpi_get_modes(struct display_entity *entity, unsigned int port,
+			       const struct videomode **modes)
+{
+	struct panel_dpi *panel = to_panel_dpi(entity);
+
+	*modes = &panel->mode;
+	return 1;
+}
+
+static int panel_dpi_get_size(struct display_entity *entity,
+			      unsigned int *width, unsigned int *height)
+{
+	struct panel_dpi *panel = to_panel_dpi(entity);
+
+	*width = panel->width;
+	*height = panel->height;
+	return 0;
+}
+
+static int panel_dpi_get_params(struct display_entity *entity,
+				unsigned int port,
+				struct display_entity_interface_params *params)
+{
+	*params = panel_dpi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops panel_dpi_control_ops = {
+	.set_state = panel_dpi_set_state,
+	.get_modes = panel_dpi_get_modes,
+	.get_size = panel_dpi_get_size,
+	.get_params = panel_dpi_get_params,
+};
+
+static const struct display_entity_ops panel_dpi_ops = {
+	.ctrl = &panel_dpi_control_ops,
+};
+
+static int panel_dpi_remove(struct platform_device *pdev)
+{
+	struct panel_dpi *panel = platform_get_drvdata(pdev);
+
+	display_entity_remove(&panel->entity);
+	display_entity_cleanup(&panel->entity);
+
+	return 0;
+}
+
+static int panel_dpi_parse_pdata(struct panel_dpi *panel,
+				 struct platform_device *pdev)
+{
+	const struct panel_dpi_platform_data *pdata = pdev->dev.platform_data;
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (pdata) {
+		panel->width = pdata->width;
+		panel->height = pdata->height;
+		panel->mode = *pdata->mode;
+	} else if (IS_ENABLED(CONFIG_OF) && np) {
+		/* Width and height are optional. */
+		of_property_read_u32(np, "width-mm", &panel->width);
+		of_property_read_u32(np, "height-mm", &panel->height);
+
+		ret = of_get_videomode(np, &panel->mode, OF_USE_NATIVE_MODE);
+		if (ret < 0)
+			return ret;
+	} else {
+		dev_err(&pdev->dev, "no platform data\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int panel_dpi_probe(struct platform_device *pdev)
+{
+	struct panel_dpi *panel;
+	int ret;
+
+	panel = devm_kzalloc(&pdev->dev, sizeof(*panel), GFP_KERNEL);
+	if (panel == NULL)
+		return -ENOMEM;
+
+	ret = panel_dpi_parse_pdata(panel, pdev);
+	if (ret < 0)
+		return ret;
+
+	panel->entity.dev = &pdev->dev;
+	panel->entity.ops = &panel_dpi_ops;
+	strlcpy(panel->entity.name, "panel-dpi", sizeof(panel->entity.name));
+
+	ret = display_entity_init(&panel->entity, 1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = display_entity_add(&panel->entity);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops panel_dpi_dev_pm_ops = {
+};
+
+static struct platform_device_id panel_dpi_id_table[] = {
+	{ "panel-dpi", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, panel_dpi_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id panel_dpi_of_id_table[] = {
+	{ .compatible = "panel-dpi", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, panel_dpi_of_id_table);
+#endif
+
+static struct platform_driver panel_dpi_driver = {
+	.probe = panel_dpi_probe,
+	.remove = panel_dpi_remove,
+	.id_table = panel_dpi_id_table,
+	.driver = {
+		.name = "panel-dpi",
+		.owner = THIS_MODULE,
+		.pm = &panel_dpi_dev_pm_ops,
+		.of_match_table = of_match_ptr(panel_dpi_of_id_table),
+	},
+};
+
+module_platform_driver(panel_dpi_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("DPI Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-dpi.h b/include/video/panel-dpi.h
new file mode 100644
index 0000000..af85d5b
--- /dev/null
+++ b/include/video/panel-dpi.h
@@ -0,0 +1,24 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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 __PANEL_DPI_H__
+#define __PANEL_DPI_H__
+
+struct videomode;
+
+struct panel_dpi_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+};
+
+#endif /* __PANEL_DPI_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 10/19] video: panel: Add R61505 panel support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (8 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 09/19] video: panel: Add DPI panel support Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 11/19] video: panel: Add R61517 " Laurent Pinchart
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The R61505 is a SYS-80 bus panel controller from Renesas.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |  10 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/panel-r61505.c | 567 +++++++++++++++++++++++++++++++++++
 include/video/panel-r61505.h         |  27 ++
 4 files changed, 605 insertions(+)
 create mode 100644 drivers/video/display/panel-r61505.c
 create mode 100644 include/video/panel-r61505.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index bce09d6..76729ef 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -19,4 +19,14 @@ config DISPLAY_PANEL_DPI
 	  If you are in doubt, say N. To compile this driver as a module, choose
 	  M here; the module will be called panel-dpi.
 
+config DISPLAY_PANEL_R61505
+	tristate "Renesas R61505-based Display Panel"
+	select DISPLAY_MIPI_DBI
+	---help---
+	  Support panels based on the Renesas R61505 panel controller.
+	  Those panels are controlled through a MIPI DBI interface.
+
+	  If you are in doubt, say N. To compile this driver as a module, choose
+	  M here; the module will be called panel-r61505.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 31c017b..db8a4c3 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -3,3 +3,4 @@ display-y					:= display-core.o \
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
+obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
diff --git a/drivers/video/display/panel-r61505.c b/drivers/video/display/panel-r61505.c
new file mode 100644
index 0000000..c86177e
--- /dev/null
+++ b/drivers/video/display/panel-r61505.c
@@ -0,0 +1,567 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on SuperH MigoR Quarter VGA LCD Panel
+ * Copyright (C) 2008 Magnus Damm
+ * Based on lcd_powertip.c from Kenati Technologies Pvt Ltd.
+ * Copyright (c) 2007 Ujjwal Pande
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/panel-r61505.h>
+#include <video/videomode.h>
+
+#define R61505_DEVICE_CODE			0x0000
+#define R61505_DEVICE_CODE_VALUE		0x1505
+#define R61505_DRIVER_OUTPUT_CONTROL		0x0001
+#define R61505_DRIVER_OUTPUT_CONTROL_SM		(1 << 10)
+#define R61505_DRIVER_OUTPUT_CONTROL_SS		(1 << 8)
+#define R61505_LCD_WAVEFORM			0x0002
+#define R61505_LCD_WAVEFORM_BC0			(1 << 9)
+#define R61505_LCD_WAVEFORM_EOR			(1 << 8)
+#define R61505_ENTRY_MODE			0x0003
+#define R61505_ENTRY_MODE_TRIREG		(1 << 15)
+#define R61505_ENTRY_MODE_DFM			(1 << 14)
+#define R61505_ENTRY_MODE_BGR			(1 << 12)
+#define R61505_ENTRY_MODE_HWM			(1 << 9)
+#define R61505_ENTRY_MODE_ORG			(1 << 7)
+#define R61505_ENTRY_MODE_ID1			(1 << 5)
+#define R61505_ENTRY_MODE_ID0			(1 << 4)
+#define R61505_ENTRY_MODE_AM			(1 << 3)
+#define R61505_RESIZE_CONTROL			0x0004
+#define R61505_RESIZE_CONTROL_RCV(n)		(((n) & 3) << 8)
+#define R61505_RESIZE_CONTROL_RCH(n)		(((n) & 3) << 4)
+#define R61505_RESIZE_CONTROL_RSZ_4		(3 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_2		(1 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_1		(0 << 0)
+#define R61505_DISPLAY_CONTROL1			0x0007
+#define R61505_DISPLAY_CONTROL1_PTDE1		(1 << 13)
+#define R61505_DISPLAY_CONTROL1_PTDE0		(1 << 12)
+#define R61505_DISPLAY_CONTROL1_BASEE		(1 << 8)
+#define R61505_DISPLAY_CONTROL1_VON		(1 << 6)
+#define R61505_DISPLAY_CONTROL1_GON		(1 << 5)
+#define R61505_DISPLAY_CONTROL1_DTE		(1 << 4)
+#define R61505_DISPLAY_CONTROL1_COL		(1 << 3)
+#define R61505_DISPLAY_CONTROL1_D1		(1 << 1)
+#define R61505_DISPLAY_CONTROL1_D0		(1 << 0)
+#define R61505_DISPLAY_CONTROL2			0x0008
+#define R61505_DISPLAY_CONTROL2_FP(n)		(((n) & 0xf) << 8)
+#define R61505_DISPLAY_CONTROL2_BP(n)		(((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL3			0x0009
+#define R61505_DISPLAY_CONTROL3_PTS(n)		(((n) & 7) << 8)
+#define R61505_DISPLAY_CONTROL3_PTG(n)		(((n) & 3) << 3)
+#define R61505_DISPLAY_CONTROL3_ICS(n)		(((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL4			0x000a
+#define R61505_DISPLAY_CONTROL4_FMARKOE		(1 << 3)
+#define R61505_DISPLAY_CONTROL4_FMI_6		(5 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_4		(3 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_2		(1 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_1		(0 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1		0x000c
+#define R61505_EXT_DISPLAY_IF_CONTROL1_ENC(n)	(((n) & 7) << 12)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RM	(1 << 8)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_VSYNC	(2 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_RGB	(1 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK	(0 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_6	(2 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_16	(1 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18	(0 << 0)
+#define R61505_FRAME_MARKER_CONTROL		0x000d
+#define R61505_FRAME_MARKER_CONTROL_FMP(n)	(((n) & 0x1ff) << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL2		0x000f
+#define R61505_POWER_CONTROL1			0x0010
+#define R61505_POWER_CONTROL1_SAP		(1 << 12)
+#define R61505_POWER_CONTROL1_BT(n)		(((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL1_APE		(1 << 7)
+#define R61505_POWER_CONTROL1_AP_100		(3 << 4)
+#define R61505_POWER_CONTROL1_AP_075		(2 << 4)
+#define R61505_POWER_CONTROL1_AP_050		(1 << 4)
+#define R61505_POWER_CONTROL1_AP_HALT		(0 << 4)
+#define R61505_POWER_CONTROL1_DSTB		(1 << 2)
+#define R61505_POWER_CONTROL1_SLP		(1 << 1)
+#define R61505_POWER_CONTROL2			0x0011
+#define R61505_POWER_CONTROL2_DC1_HALT		(6 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_256	(4 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_128	(3 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_64	(2 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_32	(1 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_16	(0 << 8)
+#define R61505_POWER_CONTROL2_DC0_HALT		(6 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_16	(4 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_8	(3 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_4	(2 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_2	(1 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC		(0 << 4)
+#define R61505_POWER_CONTROL2_VC_100		(7 << 0)
+#define R61505_POWER_CONTROL2_VC_076		(4 << 0)
+#define R61505_POWER_CONTROL2_VC_089		(1 << 0)
+#define R61505_POWER_CONTROL2_VC_094		(0 << 0)
+#define R61505_POWER_CONTROL3			0x0012
+#define R61505_POWER_CONTROL3_VCMR		(1 << 8)
+#define R61505_POWER_CONTROL3_PSON		(1 << 5)
+#define R61505_POWER_CONTROL3_PON		(1 << 4)
+#define R61505_POWER_CONTROL3_VRH(n)		(((n) & 0xf) << 0)
+#define R61505_POWER_CONTROL4			0x0013
+#define R61505_POWER_CONTROL4_VDV(n)		(((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL5			0x0015
+#define R61505_POWER_CONTROL5_BLDM		(1 << 12)
+#define R61505_POWER_CONTROL6			0x0017
+#define R61505_POWER_CONTROL6_PSE		(1 << 0)
+#define R61505_RAM_ADDR_HORZ			0x0020
+#define R61505_RAM_ADDR_VERT			0x0021
+#define R61505_RAM_DATA				0x0022
+#define R61505_POWER_CONTROL7			0x0029
+#define R61505_POWER_CONTROL7_VCM1(n)		(((n) & 0x1f) << 0)
+#define R61505_GAMMA_CONTROL1			0x0030
+#define R61505_GAMMA_CONTROL2			0x0031
+#define R61505_GAMMA_CONTROL3			0x0032
+#define R61505_GAMMA_CONTROL4			0x0033
+#define R61505_GAMMA_CONTROL5			0x0034
+#define R61505_GAMMA_CONTROL6			0x0035
+#define R61505_GAMMA_CONTROL7			0x0036
+#define R61505_GAMMA_CONTROL8			0x0037
+#define R61505_GAMMA_CONTROL9			0x0038
+#define R61505_GAMMA_CONTROL10			0x0039
+#define R61505_GAMMA_CONTROL11			0x003a
+#define R61505_GAMMA_CONTROL12			0x003b
+#define R61505_GAMMA_CONTROL13			0x003c
+#define R61505_GAMMA_CONTROL14			0x003d
+#define R61505_WINDOW_HORZ_START		0x0050
+#define R61505_WINDOW_HORZ_END			0x0051
+#define R61505_WINDOW_VERT_START		0x0052
+#define R61505_WINDOW_VERT_END			0x0053
+#define R61505_DRIVER_OUTPUT_CONTROL2		0x0060
+#define R61505_DRIVER_OUTPUT_CONTROL2_GS	(1 << 15)
+#define R61505_DRIVER_OUTPUT_CONTROL2_NL(n)	(((n) & 0x3f) << 8)
+#define R61505_DRIVER_OUTPUT_CONTROL2_SCN(n)	(((n) & 0x3f) << 0)
+#define R61505_BASE_IMG_DISPLAY_CONTROL		0x0061
+#define R61505_BASE_IMG_DISPLAY_CONTROL_NDL	(1 << 2)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_VLE	(1 << 1)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_REV	(1 << 0)
+#define R61505_VERTICAL_SCROLL_CONTROL		0x006a
+#define R61505_PANEL_IF_CONTROL1		0x0090
+#define R61505_PANEL_IF_CONTROL1_DIVI(n)	(((n) & 3) << 8)
+#define R61505_PANEL_IF_CONTROL1_RTNI(n)	(((n) & 0x1f) << 0)
+#define R61505_PANEL_IF_CONTROL2		0x0092
+#define R61505_PANEL_IF_CONTROL2_NOWI(n)	(((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL3		0x0093
+#define R61505_PANEL_IF_CONTROL3_MCP(n)		(((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL4		0x0095
+#define R61505_PANEL_IF_CONTROL5		0x0097
+#define R61505_PANEL_IF_CONTROL6		0x0098
+#define R61505_OSCILLATION_CONTROL		0x00a4
+#define R61505_OSCILLATION_CONTROL_CALB		(1 << 0)
+
+struct r61505 {
+	struct display_entity entity;
+	struct mipi_dbi_device *dbi;
+	const struct panel_r61505_platform_data *pdata;
+};
+
+static inline struct r61505 *to_panel(struct display_entity *e)
+{
+	return container_of(e, struct r61505, entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61505_write(struct r61505 *panel, u16 reg, u16 data)
+{
+	u8 buffer[2] = { data >> 8, data & 0xff };
+
+	mipi_dbi_write_command(panel->dbi, reg);
+	mipi_dbi_write_data(panel->dbi, buffer, 2);
+}
+
+static u16 r61505_read(struct r61505 *panel, u16 reg)
+{
+	u8 buffer[2];
+	int ret;
+
+	mipi_dbi_write_command(panel->dbi, reg);
+	ret = mipi_dbi_read_data(panel->dbi, buffer, 2);
+	if (ret < 0)
+		return ret;
+
+	return (buffer[0] << 8) | buffer[1];
+}
+
+static void r61505_write_array(struct r61505 *panel,
+				 const u16 *data, unsigned int len)
+{
+	unsigned int i;
+
+	for (i = 0; i < len; i += 2)
+		r61505_write(panel, data[i], data[i + 1]);
+}
+
+static void r61505_reset(struct r61505 *panel)
+{
+	if (panel->pdata->reset < 0)
+		return;
+
+	gpio_set_value(panel->pdata->reset, 0);
+	usleep_range(2000, 2500);
+	gpio_set_value(panel->pdata->reset, 1);
+	usleep_range(1000, 1500);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const unsigned short sync_data[] = {
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+};
+
+static const unsigned short magic0_data[] = {
+	R61505_DISPLAY_CONTROL2, R61505_DISPLAY_CONTROL2_FP(8) |
+				 R61505_DISPLAY_CONTROL2_BP(8),
+	R61505_PANEL_IF_CONTROL1, R61505_PANEL_IF_CONTROL1_RTNI(26),
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_D0,
+	R61505_POWER_CONTROL6, R61505_POWER_CONTROL6_PSE,
+	0x0019, 0x0000,
+	R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+			       R61505_POWER_CONTROL1_BT(7) |
+			       R61505_POWER_CONTROL1_APE |
+			       R61505_POWER_CONTROL1_AP_100,
+	R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+			       R61505_POWER_CONTROL2_DC0_FOSC_2 | 6,
+	R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+			       R61505_POWER_CONTROL3_PON |
+			       R61505_POWER_CONTROL3_VRH(8),
+	R61505_POWER_CONTROL4, 0x1000 | R61505_POWER_CONTROL4_VDV(4),
+	R61505_POWER_CONTROL7, R61505_POWER_CONTROL7_VCM1(12),
+	R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+			       R61505_POWER_CONTROL3_PSON |
+			       R61505_POWER_CONTROL3_PON |
+			       R61505_POWER_CONTROL3_VRH(8),
+};
+
+static const unsigned short magic1_data[] = {
+	R61505_GAMMA_CONTROL1, 0x0307,
+	R61505_GAMMA_CONTROL2, 0x0303,
+	R61505_GAMMA_CONTROL3, 0x0603,
+	R61505_GAMMA_CONTROL4, 0x0202,
+	R61505_GAMMA_CONTROL5, 0x0202,
+	R61505_GAMMA_CONTROL6, 0x0202,
+	R61505_GAMMA_CONTROL7, 0x1f1f,
+	R61505_GAMMA_CONTROL8, 0x0303,
+	R61505_GAMMA_CONTROL9, 0x0303,
+	R61505_GAMMA_CONTROL10, 0x0603,
+	R61505_GAMMA_CONTROL11, 0x0202,
+	R61505_GAMMA_CONTROL12, 0x0102,
+	R61505_GAMMA_CONTROL13, 0x0204,
+	R61505_GAMMA_CONTROL14, 0x0000,
+	R61505_DRIVER_OUTPUT_CONTROL, R61505_DRIVER_OUTPUT_CONTROL_SS,
+	R61505_LCD_WAVEFORM, R61505_LCD_WAVEFORM_BC0 |
+			     R61505_LCD_WAVEFORM_EOR,
+	R61505_ENTRY_MODE, R61505_ENTRY_MODE_DFM |
+			   R61505_ENTRY_MODE_BGR |
+			   R61505_ENTRY_MODE_ID1 |
+			   R61505_ENTRY_MODE_AM,
+	R61505_RAM_ADDR_HORZ, 239,
+	R61505_RAM_ADDR_VERT, 0,
+	R61505_RESIZE_CONTROL, R61505_RESIZE_CONTROL_RCV(0) |
+			       R61505_RESIZE_CONTROL_RCH(0) |
+			       R61505_RESIZE_CONTROL_RSZ_1,
+	R61505_DISPLAY_CONTROL3, R61505_DISPLAY_CONTROL3_PTS(0) |
+				 R61505_DISPLAY_CONTROL3_PTG(0) |
+				 R61505_DISPLAY_CONTROL3_ICS(0),
+	R61505_DISPLAY_CONTROL4, R61505_DISPLAY_CONTROL4_FMARKOE |
+				 R61505_DISPLAY_CONTROL4_FMI_1,
+	R61505_EXT_DISPLAY_IF_CONTROL1, R61505_EXT_DISPLAY_IF_CONTROL1_ENC(0) |
+					R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK |
+					R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18,
+	R61505_FRAME_MARKER_CONTROL, R61505_FRAME_MARKER_CONTROL_FMP(0),
+	R61505_POWER_CONTROL5, 0x8000,
+};
+
+static const unsigned short magic2_data[] = {
+	R61505_BASE_IMG_DISPLAY_CONTROL, R61505_BASE_IMG_DISPLAY_CONTROL_REV,
+	R61505_PANEL_IF_CONTROL2, R61505_PANEL_IF_CONTROL2_NOWI(1),
+	R61505_PANEL_IF_CONTROL3, R61505_PANEL_IF_CONTROL3_MCP(1),
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_GON |
+				 R61505_DISPLAY_CONTROL1_D0,
+};
+
+static const unsigned short magic3_data[] = {
+	R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+			       R61505_POWER_CONTROL1_BT(6) |
+			       R61505_POWER_CONTROL1_APE |
+			       R61505_POWER_CONTROL1_AP_100,
+	R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+			       R61505_POWER_CONTROL2_DC0_FOSC_2 |
+			       R61505_POWER_CONTROL2_VC_089,
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_VON |
+				 R61505_DISPLAY_CONTROL1_GON |
+				 R61505_DISPLAY_CONTROL1_D0,
+};
+
+static void r61505_enable_panel(struct r61505 *panel)
+{
+	unsigned long hactive = panel->pdata->mode->hactive;
+	unsigned long vactive = panel->pdata->mode->vactive;
+	unsigned int i;
+
+	r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+	r61505_write(panel, R61505_OSCILLATION_CONTROL,
+		     R61505_OSCILLATION_CONTROL_CALB);
+	usleep_range(10000, 11000);
+
+	r61505_write(panel, R61505_DRIVER_OUTPUT_CONTROL2,
+		     R61505_DRIVER_OUTPUT_CONTROL2_NL((hactive / 8) - 1));
+	r61505_write_array(panel, magic0_data, ARRAY_SIZE(magic0_data));
+	usleep_range(100000, 101000);
+
+	r61505_write_array(panel, magic1_data, ARRAY_SIZE(magic1_data));
+
+	r61505_write(panel, R61505_WINDOW_HORZ_START, 239 - (vactive - 1));
+	r61505_write(panel, R61505_WINDOW_HORZ_END, 239);
+	r61505_write(panel, R61505_WINDOW_VERT_START, 0);
+	r61505_write(panel, R61505_WINDOW_VERT_END, hactive - 1);
+
+	r61505_write_array(panel, magic2_data, ARRAY_SIZE(magic2_data));
+	usleep_range(10000, 11000);
+
+	r61505_write_array(panel, magic3_data, ARRAY_SIZE(magic3_data));
+	usleep_range(40000, 41000);
+
+	/* Clear GRAM to avoid displaying garbage. */
+	r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+	r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+
+	for (i = 0; i < (hactive * 256); i++) /* yes, 256 words per line */
+		r61505_write(panel, R61505_RAM_DATA, 0);
+
+	r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+	r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+}
+
+static void r61505_disable_panel(struct r61505 *panel)
+{
+	r61505_reset(panel);
+}
+
+static void r61505_display_on(struct r61505 *panel)
+{
+	r61505_write(panel, R61505_DISPLAY_CONTROL1,
+		     R61505_DISPLAY_CONTROL1_BASEE |
+		     R61505_DISPLAY_CONTROL1_VON |
+		     R61505_DISPLAY_CONTROL1_GON |
+		     R61505_DISPLAY_CONTROL1_DTE |
+		     R61505_DISPLAY_CONTROL1_D1 |
+		     R61505_DISPLAY_CONTROL1_D0);
+	usleep_range(40000, 41000);
+}
+
+static void r61505_display_off(struct r61505 *panel)
+{
+	r61505_write(panel, R61505_DISPLAY_CONTROL1,
+		     R61505_DISPLAY_CONTROL1_VON |
+		     R61505_DISPLAY_CONTROL1_GON |
+		     R61505_DISPLAY_CONTROL1_D0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61505_dbi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DBI,
+	.p.dbi = {
+		.type = MIPI_DBI_INTERFACE_TYPE_B,
+		.cs_setup = 1,
+		.wr_setup = 0,
+		.wr_cycle = 10,
+		.wr_hold = 9,
+		.rd_setup = 14,
+		.rd_latch = 24,
+		.rd_cycle = 52,
+		.rd_hold = 24,
+	},
+};
+
+static int r61505_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		r61505_disable_panel(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state == DISPLAY_ENTITY_STATE_OFF)
+			r61505_enable_panel(panel);
+		else
+			r61505_display_off(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state == DISPLAY_ENTITY_STATE_OFF)
+			r61505_enable_panel(panel);
+
+		r61505_display_on(panel);
+		break;
+	}
+
+	return 0;
+}
+
+static int r61505_update(struct display_entity *entity)
+{
+	struct r61505 *panel = to_panel(entity);
+	struct media_pad *source;
+
+	mipi_dbi_write_command(panel->dbi, R61505_RAM_DATA);
+	usleep_range(100000, 101000);
+
+	source = media_entity_remote_pad(&entity->entity.pads[0]);
+	if (source == NULL)
+		return -EPIPE;
+
+	display_entity_set_stream(to_display_entity(source->entity),
+				  source->index,
+				  DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+	return 0;
+}
+
+static int r61505_get_modes(struct display_entity *entity, unsigned int port,
+			    const struct videomode **modes)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	*modes = panel->pdata->mode;
+	return 1;
+}
+
+static int r61505_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int r61505_get_params(struct display_entity *entity, unsigned int port,
+			     struct display_entity_interface_params *params)
+{
+	*params = r61505_dbi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops r61505_control_ops = {
+	.set_state = r61505_set_state,
+	.update = r61505_update,
+	.get_modes = r61505_get_modes,
+	.get_size = r61505_get_size,
+	.get_params = r61505_get_params,
+};
+
+static const struct display_entity_ops r61505_ops = {
+	.ctrl = &r61505_control_ops,
+};
+
+static int r61505_remove(struct mipi_dbi_device *dev)
+{
+	struct r61505 *panel = mipi_dbi_get_drvdata(dev);
+
+	display_entity_remove(&panel->entity);
+	display_entity_cleanup(&panel->entity);
+
+	return 0;
+}
+
+static int r61505_probe(struct mipi_dbi_device *dev)
+{
+	const struct panel_r61505_platform_data *pdata = dev->dev.platform_data;
+	struct r61505 *panel;
+	int ret;
+
+	if (pdata == NULL)
+		return -ENODEV;
+
+	panel = devm_kzalloc(&dev->dev, sizeof(*panel), GFP_KERNEL);
+	if (panel == NULL)
+		return -ENOMEM;
+
+	panel->pdata = pdata;
+	panel->dbi = dev;
+
+	dev->flags = MIPI_DBI_FLAG_ALIGN_LEFT;
+	dev->bus_width = pdata->bus_width;
+	mipi_dbi_set_data_width(dev, 16);
+
+	r61505_reset(panel);
+	r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+	if (r61505_read(panel, 0) != R61505_DEVICE_CODE_VALUE)
+		return -ENODEV;
+
+	panel->entity.dev = &dev->dev;
+	panel->entity.ops = &r61505_ops;
+
+	ret = display_entity_init(&panel->entity, 1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = display_entity_add(&panel->entity);
+	if (ret < 0)
+		return ret;
+
+	mipi_dbi_set_drvdata(dev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops r61505_dev_pm_ops = {
+};
+
+static struct mipi_dbi_device_id r61505_id_table[] = {
+	{ "panel-r61505", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(mipi_dbi, r61505_id_table);
+
+static struct mipi_dbi_driver r61505_driver = {
+	.probe = r61505_probe,
+	.remove = r61505_remove,
+	.id_table = r61505_id_table,
+	.driver = {
+		.name = "panel-r61505",
+		.owner = THIS_MODULE,
+		.pm = &r61505_dev_pm_ops,
+	},
+};
+
+module_mipi_dbi_driver(r61505_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61505-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61505.h b/include/video/panel-r61505.h
new file mode 100644
index 0000000..ee71f29
--- /dev/null
+++ b/include/video/panel-r61505.h
@@ -0,0 +1,27 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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 __PANEL_R61505_H__
+#define __PANEL_R61505_H__
+
+struct videomode;
+
+struct panel_r61505_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+
+	unsigned int bus_width;
+	int reset;			/* Reset GPIO */
+};
+
+#endif /* __PANEL_R61505_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 11/19] video: panel: Add R61517 panel support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (9 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 10/19] video: panel: Add R61505 " Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 12/19] video: display: Add VGA Digital to Analog Converter support Laurent Pinchart
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

The R61517 is a MIPI DBI panel controller from Renesas.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |  10 +
 drivers/video/display/Makefile       |   1 +
 drivers/video/display/panel-r61517.c | 460 +++++++++++++++++++++++++++++++++++
 include/video/panel-r61517.h         |  28 +++
 4 files changed, 499 insertions(+)
 create mode 100644 drivers/video/display/panel-r61517.c
 create mode 100644 include/video/panel-r61517.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 76729ef..9b44b5f 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -29,4 +29,14 @@ config DISPLAY_PANEL_R61505
 	  If you are in doubt, say N. To compile this driver as a module, choose
 	  M here; the module will be called panel-r61505.
 
+config DISPLAY_PANEL_R61517
+	tristate "Renesas R61517-based Display Panel"
+	select DISPLAY_MIPI_DBI
+	---help---
+	  Support panels based on the Renesas R61517 panel controller.
+	  Those panels are controlled through a MIPI DBI interface.
+
+	  If you are in doubt, say N. To compile this driver as a module, choose
+	  M here; the module will be called panel-r61517.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index db8a4c3..1cdc8d4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_DISPLAY_CORE)			+= display.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
+obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
diff --git a/drivers/video/display/panel-r61517.c b/drivers/video/display/panel-r61517.c
new file mode 100644
index 0000000..ccfec33
--- /dev/null
+++ b/drivers/video/display/panel-r61517.c
@@ -0,0 +1,460 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on KFR2R09 LCD panel support
+ * Copyright (C) 2009 Magnus Damm
+ * Register settings based on the out-of-tree t33fb.c driver
+ * Copyright (C) 2008 Lineo Solutions, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/mipi_display.h>
+#include <video/panel-r61517.h>
+#include <video/videomode.h>
+
+struct r61517 {
+	struct display_entity entity;
+	struct mipi_dbi_device *dbi;
+	const struct panel_r61517_platform_data *pdata;
+};
+
+static inline struct r61517 *to_panel(struct display_entity *e)
+{
+	return container_of(e, struct r61517, entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61517_write_command(struct r61517 *panel, u16 cmd)
+{
+	mipi_dbi_write_command(panel->dbi, cmd);
+}
+
+static void r61517_write_data(struct r61517 *panel, u8 data)
+{
+	mipi_dbi_write_data(panel->dbi, &data, 1);
+}
+
+static int r61517_read_data(struct r61517 *panel)
+{
+	u8 data;
+	int ret;
+
+	ret = mipi_dbi_read_data(panel->dbi, &data, 1);
+	if (ret < 0)
+		return ret;
+
+	return data;
+}
+
+static void r61517_write(struct r61517 *panel, u8 reg, const u8 *data,
+			 size_t len)
+{
+	mipi_dbi_write_command(panel->dbi, reg);
+	mipi_dbi_write_data(panel->dbi, data, len);
+}
+
+static void r61517_write8(struct r61517 *panel, u8 reg, u8 data)
+{
+	r61517_write(panel, reg, &data, 1);
+}
+
+static void r61517_write16(struct r61517 *panel, u8 reg, u16 data)
+{
+	u8 buffer[2] = { (data >> 8) & 0xff, (data >> 0) & 0xff };
+
+	r61517_write(panel, reg, buffer, 2);
+}
+
+static void r61517_write32(struct r61517 *panel, u8 reg, u32 data)
+{
+	u8 buffer[4] = { (data >> 24) & 0xff, (data >> 16) & 0xff,
+			 (data >>  8) & 0xff, (data >>  0) & 0xff };
+
+	r61517_write(panel, reg, buffer, 4);
+}
+
+#define r61517_write_array(p, c, a) \
+	r61517_write((p), (c), (a), ARRAY_SIZE(a))
+
+static void r61517_reset(struct r61517 *panel)
+{
+	gpio_set_value(panel->pdata->protect, 0);	/* PROTECT/ -> L */
+	gpio_set_value(panel->pdata->reset, 0);		/* LCD_RST/ -> L */
+	gpio_set_value(panel->pdata->protect, 1);	/* PROTECT/ -> H */
+	usleep_range(1100, 1200);
+	gpio_set_value(panel->pdata->reset, 1);		/* LCD_RST/ -> H */
+	usleep_range(10, 100);
+	gpio_set_value(panel->pdata->protect, 0);	/* PROTECT/ -> L */
+	msleep(20);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const u8 data_frame_if[] = {
+	0x02, /* WEMODE: 1=cont, 0=one-shot */
+	0x00, 0x00,
+	0x00, /* EPF, DFM */
+	0x02, /* RIM[1] : 1 (18bpp) */
+};
+
+static const u8 data_panel[] = {
+	0x0b,
+	0x63, /* 400 lines */
+	0x04, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00,
+};
+
+static const u8 data_timing[] = {
+	0x00, 0x00, 0x13, 0x08, 0x08,
+};
+
+static const u8 data_timing_src[] = {
+	0x11, 0x01, 0x00, 0x01,
+};
+
+static const u8 data_gamma[] = {
+	0x01, 0x02, 0x08, 0x23,	0x03, 0x0c, 0x00, 0x06,	0x00, 0x00,
+	0x01, 0x00, 0x0c, 0x23, 0x03, 0x08, 0x02, 0x06, 0x00, 0x00,
+};
+
+static const u8 data_power[] = {
+	0x07, 0xc5, 0xdc, 0x02,	0x33, 0x0a,
+};
+
+static const u8 data_vcom[] = {
+	0x00, 0x0f, 0x02,
+};
+
+static unsigned long r61517_read_device_code(struct r61517 *panel)
+{
+	/* access protect OFF */
+	r61517_write8(panel, 0xb0, 0);
+
+	/* deep standby OFF */
+	r61517_write8(panel, 0xb1, 0);
+
+	/* device code command */
+	r61517_write_command(panel, 0xbf);
+	mdelay(50);
+
+	/* dummy read */
+	r61517_read_data(panel);
+
+	/* read device code */
+	return (r61517_read_data(panel) << 24) |
+	       (r61517_read_data(panel) << 16) |
+	       (r61517_read_data(panel) << 8) |
+	       (r61517_read_data(panel) << 0);
+}
+
+static void r61517_write_memory_start(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_WRITE_MEMORY_START);
+}
+
+static void r61517_clear_memory(struct r61517 *panel)
+{
+	unsigned int size = panel->pdata->mode->hactive
+			  * panel->pdata->mode->vactive;
+	unsigned int i;
+
+	r61517_write_memory_start(panel);
+
+	for (i = 0; i < size; i++)
+		r61517_write_data(panel, 0);
+}
+
+static void r61517_enable_panel(struct r61517 *panel)
+{
+	/* access protect off */
+	r61517_write8(panel, 0xb0, 0);
+
+	/* exit deep standby mode */
+	r61517_write8(panel, 0xb1, 0);
+
+	/* frame memory I/F */
+	r61517_write_array(panel, 0xb3, data_frame_if);
+
+	/* display mode and frame memory write mode */
+	r61517_write8(panel, 0xb4, 0); /* DBI, internal clock */
+
+	/* panel */
+	r61517_write_array(panel, 0xc0, data_panel);
+
+	/* timing (normal) */
+	r61517_write_array(panel, 0xc1, data_timing);
+
+	/* timing (partial) */
+	r61517_write_array(panel, 0xc2, data_timing);
+
+	/* timing (idle) */
+	r61517_write_array(panel, 0xc3, data_timing);
+
+	/* timing (source/VCOM/gate driving) */
+	r61517_write_array(panel, 0xc4, data_timing_src);
+
+	/* gamma (red) */
+	r61517_write_array(panel, 0xc8, data_gamma);
+
+	/* gamma (green) */
+	r61517_write_array(panel, 0xc9, data_gamma);
+
+	/* gamma (blue) */
+	r61517_write_array(panel, 0xca, data_gamma);
+
+	/* power (common) */
+	r61517_write_array(panel, 0xd0, data_power);
+
+	/* VCOM */
+	r61517_write_array(panel, 0xd1, data_vcom);
+
+	/* power (normal) */
+	r61517_write16(panel, 0xd2, 0x6324);
+
+	/* power (partial) */
+	r61517_write16(panel, 0xd3, 0x6324);
+
+	/* power (idle) */
+	r61517_write16(panel, 0xd4, 0x6324);
+
+	r61517_write16(panel, 0xd8, 0x7777);
+
+	/* TE signal */
+	r61517_write8(panel, MIPI_DCS_SET_TEAR_ON, 0);
+
+	/* TE signal line */
+	r61517_write16(panel, MIPI_DCS_SET_TEAR_SCANLINE, 0);
+
+	/* column address */
+	r61517_write32(panel, MIPI_DCS_SET_COLUMN_ADDRESS,
+		       panel->pdata->mode->hactive - 1);
+
+	/* page address */
+	r61517_write32(panel, MIPI_DCS_SET_PAGE_ADDRESS,
+		       panel->pdata->mode->vactive - 1);
+
+	/* exit sleep mode */
+	r61517_write_command(panel, MIPI_DCS_EXIT_SLEEP_MODE);
+
+	mdelay(120);
+
+	/* clear vram */
+	r61517_clear_memory(panel);
+}
+
+static void r61517_disable_panel(struct r61517 *panel)
+{
+	r61517_reset(panel);
+}
+
+static void r61517_display_on(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_ON);
+	mdelay(1);
+}
+
+static void r61517_display_off(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61517_dbi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DBI,
+	.p.dbi = {
+		.type = MIPI_DBI_INTERFACE_TYPE_B,
+		.flags = MIPI_DBI_INTERFACE_TE,
+		.cs_setup = 1,
+		.wr_setup = 1,
+		.wr_cycle = 9,
+		.wr_hold = 4,
+		.rd_setup = 1,
+		.rd_latch = 20,
+		.rd_cycle = 41,
+		.rd_hold = 20,
+	},
+};
+
+static int r61517_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		r61517_disable_panel(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state == DISPLAY_ENTITY_STATE_OFF)
+			r61517_enable_panel(panel);
+		else
+			r61517_display_off(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state == DISPLAY_ENTITY_STATE_OFF)
+			r61517_enable_panel(panel);
+
+		r61517_display_on(panel);
+		break;
+	}
+
+	return 0;
+}
+
+static int r61517_update(struct display_entity *entity)
+{
+	struct r61517 *panel = to_panel(entity);
+	struct media_pad *source;
+
+	r61517_write_memory_start(panel);
+
+	source = media_entity_remote_pad(&entity->entity.pads[0]);
+	if (source == NULL)
+		return -EPIPE;
+
+	display_entity_set_stream(to_display_entity(source->entity),
+				  source->index,
+				  DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+	return 0;
+}
+
+static int r61517_get_modes(struct display_entity *entity, unsigned int port,
+			    const struct videomode **modes)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	*modes = panel->pdata->mode;
+	return 1;
+}
+
+static int r61517_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int r61517_get_params(struct display_entity *entity, unsigned int port,
+			     struct display_entity_interface_params *params)
+{
+	*params = r61517_dbi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops r61517_control_ops = {
+	.set_state = r61517_set_state,
+	.update = r61517_update,
+	.get_modes = r61517_get_modes,
+	.get_size = r61517_get_size,
+	.get_params = r61517_get_params,
+};
+
+static const struct display_entity_ops r61517_ops = {
+	.ctrl = &r61517_control_ops,
+};
+
+static int r61517_remove(struct mipi_dbi_device *dev)
+{
+	struct r61517 *panel = mipi_dbi_get_drvdata(dev);
+
+	display_entity_remove(&panel->entity);
+	display_entity_cleanup(&panel->entity);
+
+	return 0;
+}
+
+static int r61517_probe(struct mipi_dbi_device *dev)
+{
+	const struct panel_r61517_platform_data *pdata = dev->dev.platform_data;
+	struct r61517 *panel;
+	int ret;
+
+	if (pdata == NULL)
+		return -ENODEV;
+
+	panel = devm_kzalloc(&dev->dev, sizeof(*panel), GFP_KERNEL);
+	if (panel == NULL)
+		return -ENOMEM;
+
+	panel->pdata = pdata;
+	panel->dbi = dev;
+
+	dev->bus_width = pdata->bus_width;
+	mipi_dbi_set_data_width(dev, 8);
+
+	r61517_reset(panel);
+
+	if (r61517_read_device_code(panel) != 0x01221517)
+		return -ENODEV;
+
+	pr_info("R61517 panel controller detected.\n");
+
+	panel->entity.dev = &dev->dev;
+	panel->entity.ops = &r61517_ops;
+
+	ret = display_entity_init(&panel->entity, 1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = display_entity_add(&panel->entity);
+	if (ret < 0)
+		return ret;
+
+	mipi_dbi_set_drvdata(dev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops r61517_dev_pm_ops = {
+};
+
+static struct mipi_dbi_device_id r61517_id_table[] = {
+	{ "panel-r61517", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(mipi_dbi, r61517_id_table);
+
+static struct mipi_dbi_driver r61517_driver = {
+	.probe = r61517_probe,
+	.remove = r61517_remove,
+	.id_table = r61517_id_table,
+	.driver = {
+		.name = "panel-r61517",
+		.owner = THIS_MODULE,
+		.pm = &r61517_dev_pm_ops,
+	},
+};
+
+module_mipi_dbi_driver(r61517_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61517-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61517.h b/include/video/panel-r61517.h
new file mode 100644
index 0000000..33f16af
--- /dev/null
+++ b/include/video/panel-r61517.h
@@ -0,0 +1,28 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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 __PANEL_R61517_H__
+#define __PANEL_R61517_H__
+
+struct videomode;
+
+struct panel_r61517_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+
+	unsigned int bus_width;
+	int protect;			/* Protect GPIO */
+	int reset;			/* Reset GPIO */
+};
+
+#endif /* __PANEL_R61517_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 12/19] video: display: Add VGA Digital to Analog Converter support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (10 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 11/19] video: panel: Add R61517 " Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 13/19] video: display: Add VGA connector support Laurent Pinchart
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

This driver implements support for VGA Digital to Analog Converters
(DACs) that receive pixel data through a DPI interface and have no
control interface (GPIOs- and/or regulators-based control can be
implemented later when needed). It exposes the devices a display
entities.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/Kconfig   |   9 +++
 drivers/video/display/Makefile  |   1 +
 drivers/video/display/vga-dac.c | 152 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 162 insertions(+)
 create mode 100644 drivers/video/display/vga-dac.c

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 9b44b5f..32ce08d 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -39,4 +39,13 @@ config DISPLAY_PANEL_R61517
 	  If you are in doubt, say N. To compile this driver as a module, choose
 	  M here; the module will be called panel-r61517.
 
+config DISPLAY_VGA_DAC
+	tristate "VGA Digital to Analog Converters"
+	---help---
+	  Support for simple VGA digital to analog converters. Those converters
+	  receive pixel data through a parallel bus and have no control bus.
+
+	  If you are in doubt, say N. To compile this driver as a module, choose
+	  M here: the module will be called vga-dac.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 1cdc8d4..43cd78d 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
 obj-$(CONFIG_DISPLAY_PANEL_R61517)		+= panel-r61517.o
+obj-$(CONFIG_DISPLAY_VGA_DAC)			+= vga-dac.o
diff --git a/drivers/video/display/vga-dac.c b/drivers/video/display/vga-dac.c
new file mode 100644
index 0000000..d0256e6
--- /dev/null
+++ b/drivers/video/display/vga-dac.c
@@ -0,0 +1,152 @@
+/*
+ * VGA Digital to Analog Converter
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+
+#define VGA_DAC_PORT_SINK		0
+#define VGA_DAC_PORT_SOURCE		1
+
+struct vga_dac {
+	struct display_entity entity;
+};
+
+static inline struct vga_dac *to_vga_dac(struct display_entity *e)
+{
+	return container_of(e, struct vga_dac, entity);
+}
+
+static int vga_dac_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	struct media_pad *source;
+
+	source = media_entity_remote_pad(&entity->entity.pads[0]);
+	if (source == NULL)
+		return -EPIPE;
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_STOPPED);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		break;
+	}
+
+	return 0;
+}
+
+static int vga_dac_get_params(struct display_entity *entity, unsigned int port,
+			      struct display_entity_interface_params *params)
+{
+	memset(params, 0, sizeof(*params));
+
+	if (port == VGA_DAC_PORT_SINK)
+		params->type = DISPLAY_ENTITY_INTERFACE_DPI;
+	else
+		params->type = DISPLAY_ENTITY_INTERFACE_VGA;
+
+	return 0;
+}
+
+static const struct display_entity_control_ops vga_dac_control_ops = {
+	.set_state = vga_dac_set_state,
+	.get_params = vga_dac_get_params,
+};
+
+static const struct display_entity_ops vga_dac_ops = {
+	.ctrl = &vga_dac_control_ops,
+};
+
+static int vga_dac_remove(struct platform_device *pdev)
+{
+	struct vga_dac *dac = platform_get_drvdata(pdev);
+
+	display_entity_remove(&dac->entity);
+	display_entity_cleanup(&dac->entity);
+
+	return 0;
+}
+
+static int vga_dac_probe(struct platform_device *pdev)
+{
+	struct vga_dac *dac;
+	int ret;
+
+	dac = devm_kzalloc(&pdev->dev, sizeof(*dac), GFP_KERNEL);
+	if (dac == NULL)
+		return -ENOMEM;
+
+	dac->entity.dev = &pdev->dev;
+	dac->entity.ops = &vga_dac_ops;
+	strlcpy(dac->entity.name, dev_name(&pdev->dev),
+		sizeof(dac->entity.name));
+
+	ret = display_entity_init(&dac->entity, 1, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = display_entity_add(&dac->entity);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, dac);
+
+	return 0;
+}
+
+static const struct dev_pm_ops vga_dac_dev_pm_ops = {
+};
+
+static struct platform_device_id vga_dac_id_table[] = {
+	{ "adv7123", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, vga_dac_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id vga_dac_of_id_table[] = {
+	{ .compatible = "adi,adv7123", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, vga_dac_of_id_table);
+#endif
+
+static struct platform_driver vga_dac_driver = {
+	.probe = vga_dac_probe,
+	.remove = vga_dac_remove,
+	.id_table = vga_dac_id_table,
+	.driver = {
+		.name = "vga-dac",
+		.owner = THIS_MODULE,
+		.pm = &vga_dac_dev_pm_ops,
+		.of_match_table = of_match_ptr(vga_dac_of_id_table),
+	},
+};
+
+module_platform_driver(vga_dac_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("VGA Digital-to-Analog Converter");
+MODULE_LICENSE("GPL");
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 13/19] video: display: Add VGA connector support
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (11 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 12/19] video: display: Add VGA Digital to Analog Converter support Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 14/19] ARM: shmobile: r8a7790: Add DU clocks for DT Laurent Pinchart
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

This driver exposes VGA connectors as display entity devices. The
connectors are passive devices that pass analog VGA signals though. They
optionally cary DDC signals for bidirectional control communications
with the devices connected to the connectors.

EDID retrieval isn't supported yet.

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/video/display/Kconfig   |  11 +++
 drivers/video/display/Makefile  |   1 +
 drivers/video/display/con-vga.c | 148 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 160 insertions(+)
 create mode 100644 drivers/video/display/con-vga.c

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 32ce08d..9b482a8 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -5,6 +5,17 @@ menuconfig DISPLAY_CORE
 
 if DISPLAY_CORE
 
+
+config DISPLAY_CONNECTOR_VGA
+	tristate "VGA Connector"
+	---help---
+	  Support for simple digital (parallel) pixel interface panels. Those
+	  panels receive pixel data through a parallel bus and have no control
+	  bus.
+
+	  If you are in doubt, say N. To compile this driver as a module, choose
+	  M here; the module will be called con-vga.
+
 config DISPLAY_MIPI_DBI
 	tristate
 	default n
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 43cd78d..d03c64a 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,6 +1,7 @@
 display-y					:= display-core.o \
 						   display-notifier.o
 obj-$(CONFIG_DISPLAY_CORE)			+= display.o
+obj-$(CONFIG_DISPLAY_CONNECTOR_VGA)		+= con-vga.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI)			+= mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI)			+= panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505)		+= panel-r61505.o
diff --git a/drivers/video/display/con-vga.c b/drivers/video/display/con-vga.c
new file mode 100644
index 0000000..798ac9e
--- /dev/null
+++ b/drivers/video/display/con-vga.c
@@ -0,0 +1,148 @@
+/*
+ * VGA Connector
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+
+struct con_vga {
+	struct display_entity entity;
+};
+
+static int con_vga_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	struct media_pad *source;
+
+	source = media_entity_remote_pad(&entity->entity.pads[0]);
+	if (source == NULL)
+		return -EPIPE;
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_STOPPED);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		display_entity_set_stream(to_display_entity(source->entity),
+					  source->index,
+					  DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		break;
+	}
+
+	return 0;
+}
+
+static int con_vga_get_modes(struct display_entity *entity, unsigned int port,
+			     const struct videomode **modes)
+{
+	/* TODO: Add EDID retrieval support. */
+	return 0;
+}
+
+static int con_vga_get_params(struct display_entity *entity, unsigned int port,
+			      struct display_entity_interface_params *params)
+{
+	memset(params, 0, sizeof(*params));
+
+	params->type = DISPLAY_ENTITY_INTERFACE_VGA;
+
+	return 0;
+}
+
+static const struct display_entity_control_ops con_vga_control_ops = {
+	.set_state = con_vga_set_state,
+	.get_modes = con_vga_get_modes,
+	.get_params = con_vga_get_params,
+};
+
+static const struct display_entity_ops con_vga_ops = {
+	.ctrl = &con_vga_control_ops,
+};
+
+static int con_vga_remove(struct platform_device *pdev)
+{
+	struct con_vga *con = platform_get_drvdata(pdev);
+
+	display_entity_remove(&con->entity);
+	display_entity_cleanup(&con->entity);
+
+	return 0;
+}
+
+static int con_vga_probe(struct platform_device *pdev)
+{
+	struct con_vga *con;
+	int ret;
+
+	con = devm_kzalloc(&pdev->dev, sizeof(*con), GFP_KERNEL);
+	if (con == NULL)
+		return -ENOMEM;
+
+	con->entity.dev = &pdev->dev;
+	con->entity.ops = &con_vga_ops;
+	strlcpy(con->entity.name, "vga-con", sizeof(con->entity.name));
+
+	ret = display_entity_init(&con->entity, 1, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = display_entity_add(&con->entity);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, con);
+
+	return 0;
+}
+
+static const struct dev_pm_ops con_vga_dev_pm_ops = {
+};
+
+static struct platform_device_id con_vga_id_table[] = {
+	{ "con-vga", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, con_vga_id_table);
+
+#ifdef CONFIG_OF
+static struct of_device_id con_vga_of_id_table[] = {
+	{ .compatible = "con-vga", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, con_vga_of_id_table);
+#endif
+
+static struct platform_driver con_vga_driver = {
+	.probe = con_vga_probe,
+	.remove = con_vga_remove,
+	.id_table = con_vga_id_table,
+	.driver = {
+		.name = "con-vga",
+		.owner = THIS_MODULE,
+		.pm = &con_vga_dev_pm_ops,
+		.of_match_table = of_match_ptr(con_vga_of_id_table),
+	},
+};
+
+module_platform_driver(con_vga_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("VGA Connector");
+MODULE_LICENSE("GPL");
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 14/19] ARM: shmobile: r8a7790: Add DU clocks for DT
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (12 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 13/19] video: display: Add VGA connector support Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 15/19] ARM: shmobile: r8a7790: Add DU device node to device tree Laurent Pinchart
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 arch/arm/mach-shmobile/clock-r8a7790.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/mach-shmobile/clock-r8a7790.c b/arch/arm/mach-shmobile/clock-r8a7790.c
index d99b87b..7229f96 100644
--- a/arch/arm/mach-shmobile/clock-r8a7790.c
+++ b/arch/arm/mach-shmobile/clock-r8a7790.c
@@ -257,10 +257,15 @@ static struct clk_lookup lookups[] = {
 
 	/* MSTP */
 	CLKDEV_ICK_ID("lvds.0", "rcar-du-r8a7790", &mstp_clks[MSTP726]),
+	CLKDEV_ICK_ID("lvds.0", "feb00000.display", &mstp_clks[MSTP726]),
 	CLKDEV_ICK_ID("lvds.1", "rcar-du-r8a7790", &mstp_clks[MSTP725]),
+	CLKDEV_ICK_ID("lvds.1", "feb00000.display", &mstp_clks[MSTP725]),
 	CLKDEV_ICK_ID("du.0", "rcar-du-r8a7790", &mstp_clks[MSTP724]),
+	CLKDEV_ICK_ID("du.0", "feb00000.display", &mstp_clks[MSTP724]),
 	CLKDEV_ICK_ID("du.1", "rcar-du-r8a7790", &mstp_clks[MSTP723]),
+	CLKDEV_ICK_ID("du.1", "feb00000.display", &mstp_clks[MSTP723]),
 	CLKDEV_ICK_ID("du.2", "rcar-du-r8a7790", &mstp_clks[MSTP722]),
+	CLKDEV_ICK_ID("du.2", "feb00000.display", &mstp_clks[MSTP722]),
 	CLKDEV_DEV_ID("sh-sci.0", &mstp_clks[MSTP204]),
 	CLKDEV_DEV_ID("sh-sci.1", &mstp_clks[MSTP203]),
 	CLKDEV_DEV_ID("sh-sci.2", &mstp_clks[MSTP206]),
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 15/19] ARM: shmobile: r8a7790: Add DU device node to device tree
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (13 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 14/19] ARM: shmobile: r8a7790: Add DU clocks for DT Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 16/19] ARM: shmobile: marzen: Port DU platform data to CDF Laurent Pinchart
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 arch/arm/boot/dts/r8a7790.dtsi | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index ab0582c..b63f1a6 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -186,4 +186,37 @@
 		reg = <0 0xe6060000 0 0x250>;
 		#gpio-range-cells = <3>;
 	};
+
+	du: display@feb00000 {
+		compatible = "renesas,du-r8a7790";
+		reg = <0 0xfeb00000 0 0x70000>,
+		      <0 0xfeb90000 0 0x1c>,
+		      <0 0xfeb94000 0 0x1c>;
+		reg-names = "core", "lvds.0", "lvds.1";
+		interrupt-parent = <&gic>;
+		interrupts = <0 256 4
+			      0 268 4
+			      0 269 4>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				du_out_rgb: endpoint {
+				};
+			};
+			port@1 {
+				reg = <1>;
+				du_out_lvds0: endpoint {
+				};
+			};
+			port@2 {
+				reg = <2>;
+				du_out_lvds1: endpoint {
+				};
+			};
+		};
+	};
 };
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 16/19] ARM: shmobile: marzen: Port DU platform data to CDF
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (14 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 15/19] ARM: shmobile: r8a7790: Add DU device node to device tree Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 17/19] ARM: shmobile: lager: " Laurent Pinchart
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 arch/arm/mach-shmobile/board-marzen.c | 77 ++++++++++++++++++++++++-----------
 1 file changed, 53 insertions(+), 24 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c
index 6499f1a..be1b7cb 100644
--- a/arch/arm/mach-shmobile/board-marzen.c
+++ b/arch/arm/mach-shmobile/board-marzen.c
@@ -39,6 +39,8 @@
 #include <linux/mmc/host.h>
 #include <linux/mmc/sh_mobile_sdhi.h>
 #include <linux/mfd/tmio.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
 #include <mach/r8a7779.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
@@ -174,35 +176,56 @@ static struct platform_device hspi_device = {
  * The panel only specifies the [hv]display and [hv]total values. The position
  * and width of the sync pulses don't matter, they're copied from VESA timings.
  */
-static struct rcar_du_encoder_data du_encoders[] = {
+static const struct videomode marzen_panel_mode = {
+	.pixelclock = 65000000,
+	.hactive = 1024,
+	.hfront_porch = 24,
+	.hback_porch = 160,
+	.hsync_len = 136,
+	.vactive = 768,
+	.vfront_porch = 3,
+	.vback_porch = 29,
+	.vsync_len = 6,
+	.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_dpi_platform_data marzen_panel_data = {
+	.width = 210,
+	.height = 158,
+	.mode = &marzen_panel_mode,
+};
+
+static const struct display_entity_graph_data marzen_du_entities[] = {
 	{
-		.type = RCAR_DU_ENCODER_VGA,
-		.output = RCAR_DU_OUTPUT_DPAD0,
+		.name = "adv7123",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "rcar-du",
+				.port = 0,
+			},
+		},
 	}, {
-		.type = RCAR_DU_ENCODER_LVDS,
-		.output = RCAR_DU_OUTPUT_DPAD1,
-		.connector.lvds.panel = {
-			.width_mm = 210,
-			.height_mm = 158,
-			.mode = {
-				.clock = 65000,
-				.hdisplay = 1024,
-				.hsync_start = 1048,
-				.hsync_end = 1184,
-				.htotal = 1344,
-				.vdisplay = 768,
-				.vsync_start = 771,
-				.vsync_end = 777,
-				.vtotal = 806,
-				.flags = 0,
+		.name = "con-vga",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "adv7123",
+				.port = 1,
 			},
 		},
+	}, {
+		.name = "panel-dpi",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "rcar-du",
+				.port = 1,
+			},
+		},
+	}, {
 	},
 };
 
-static const struct rcar_du_platform_data du_pdata __initconst = {
-	.encoders = du_encoders,
-	.num_encoders = ARRAY_SIZE(du_encoders),
+static const struct rcar_du_platform_data marzen_du_pdata __initconst = {
+	.graph = marzen_du_entities,
 };
 
 static const struct resource du_resources[] __initconst = {
@@ -217,8 +240,8 @@ static void __init marzen_add_du_device(void)
 		.id = -1,
 		.res = du_resources,
 		.num_res = ARRAY_SIZE(du_resources),
-		.data = &du_pdata,
-		.size_data = sizeof(du_pdata),
+		.data = &marzen_du_pdata,
+		.size_data = sizeof(marzen_du_pdata),
 		.dma_mask = DMA_BIT_MASK(32),
 	};
 
@@ -327,6 +350,12 @@ static void __init marzen_init(void)
 	r8a7779_add_standard_devices();
 	platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices));
 	marzen_add_du_device();
+
+	platform_device_register_simple("adv7123", -1, NULL, 0);
+	platform_device_register_simple("con-vga", -1, NULL, 0);
+	platform_device_register_data(&platform_bus, "panel-dpi", -1,
+				      &marzen_panel_data,
+				      sizeof(marzen_panel_data));
 }
 
 static const char *marzen_boards_compat_dt[] __initdata = {
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 17/19] ARM: shmobile: lager: Port DU platform data to CDF
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (15 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 16/19] ARM: shmobile: marzen: Port DU platform data to CDF Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 18/19] ARM: shmobile: lager-reference: Add display device nodes to device tree Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 19/19] drm/rcar-du: Port to the Common Display Framework Laurent Pinchart
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 arch/arm/mach-shmobile/board-lager.c | 76 +++++++++++++++++++++++++-----------
 1 file changed, 53 insertions(+), 23 deletions(-)

diff --git a/arch/arm/mach-shmobile/board-lager.c b/arch/arm/mach-shmobile/board-lager.c
index 75a01bc..d61b892 100644
--- a/arch/arm/mach-shmobile/board-lager.c
+++ b/arch/arm/mach-shmobile/board-lager.c
@@ -33,6 +33,8 @@
 #include <linux/regulator/fixed.h>
 #include <linux/regulator/machine.h>
 #include <linux/sh_eth.h>
+#include <video/panel-dpi.h>
+#include <video/videomode.h>
 #include <mach/common.h>
 #include <mach/irqs.h>
 #include <mach/r8a7790.h>
@@ -40,35 +42,56 @@
 #include <asm/mach/arch.h>
 
 /* DU */
-static struct rcar_du_encoder_data lager_du_encoders[] = {
+static const struct videomode lager_panel_mode = {
+	.pixelclock = 65000000,
+	.hactive = 1024,
+	.hfront_porch = 24,
+	.hback_porch = 160,
+	.hsync_len = 136,
+	.vactive = 768,
+	.vfront_porch = 3,
+	.vback_porch = 29,
+	.vsync_len = 6,
+	.flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_dpi_platform_data lager_panel_data = {
+	.width = 210,
+	.height = 158,
+	.mode = &lager_panel_mode,
+};
+
+static const struct display_entity_graph_data lager_du_entities[] = {
 	{
-		.type = RCAR_DU_ENCODER_VGA,
-		.output = RCAR_DU_OUTPUT_DPAD0,
+		.name = "adv7123",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "rcar-du",
+				.port = 0,
+			},
+		},
 	}, {
-		.type = RCAR_DU_ENCODER_NONE,
-		.output = RCAR_DU_OUTPUT_LVDS1,
-		.connector.lvds.panel = {
-			.width_mm = 210,
-			.height_mm = 158,
-			.mode = {
-				.clock = 65000,
-				.hdisplay = 1024,
-				.hsync_start = 1048,
-				.hsync_end = 1184,
-				.htotal = 1344,
-				.vdisplay = 768,
-				.vsync_start = 771,
-				.vsync_end = 777,
-				.vtotal = 806,
-				.flags = 0,
+		.name = "con-vga",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "adv7123",
+				.port = 1,
 			},
 		},
+	}, {
+		.name = "panel-dpi",
+		.sources = (const struct display_entity_source_data[]) {
+			{
+				.name = "rcar-du",
+				.port = 2,
+			},
+		},
+	}, {
 	},
 };
 
 static const struct rcar_du_platform_data lager_du_pdata __initconst = {
-	.encoders = lager_du_encoders,
-	.num_encoders = ARRAY_SIZE(lager_du_encoders),
+	.graph = lager_du_entities,
 };
 
 static const struct resource du_resources[] __initconst = {
@@ -87,8 +110,8 @@ static void __init lager_add_du_device(void)
 		.id = -1,
 		.res = du_resources,
 		.num_res = ARRAY_SIZE(du_resources),
-		.data = &du_resources,
-		.size_data = sizeof(du_resources),
+		.data = &lager_du_pdata,
+		.size_data = sizeof(lager_du_pdata),
 		.dma_mask = DMA_BIT_MASK(32),
 	};
 
@@ -202,6 +225,7 @@ static void __init lager_add_standard_devices(void)
 	r8a7790_pinmux_init();
 
 	r8a7790_add_standard_devices();
+
 	platform_device_register_data(&platform_bus, "leds-gpio", -1,
 				      &lager_leds_pdata,
 				      sizeof(lager_leds_pdata));
@@ -220,6 +244,12 @@ static void __init lager_add_standard_devices(void)
 					  &ether_pdata, sizeof(ether_pdata));
 
 	lager_add_du_device();
+
+	platform_device_register_simple("adv7123", -1, NULL, 0);
+	platform_device_register_simple("con-vga", -1, NULL, 0);
+	platform_device_register_data(&platform_bus, "panel-dpi", -1,
+				      &lager_panel_data,
+				      sizeof(lager_panel_data));
 }
 
 static const char * const lager_boards_compat_dt[] __initconst = {
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 18/19] ARM: shmobile: lager-reference: Add display device nodes to device tree
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (16 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 17/19] ARM: shmobile: lager: " Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  2013-08-09 23:03 ` [PATCH/RFC v3 19/19] drm/rcar-du: Port to the Common Display Framework Laurent Pinchart
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 arch/arm/boot/dts/r8a7790-lager-reference.dts | 92 +++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/arch/arm/boot/dts/r8a7790-lager-reference.dts b/arch/arm/boot/dts/r8a7790-lager-reference.dts
index d9a25d5..ba2469b 100644
--- a/arch/arm/boot/dts/r8a7790-lager-reference.dts
+++ b/arch/arm/boot/dts/r8a7790-lager-reference.dts
@@ -42,6 +42,98 @@
 			gpios = <&gpio5 17 GPIO_ACTIVE_HIGH>;
 		};
 	};
+
+	adv7123 {
+		compatible = "adi,adv7123";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				adv7123_in: endpoint {
+					remote-endpoint = <&du_out_rgb>;
+				};
+			};
+			port@1 {
+				reg = <1>;
+				adv7123_out: endpoint {
+					remote-endpoint = <&con_vga_in>;
+				};
+			};
+		};
+	};
+
+	con-vga {
+		compatible = "con-vga";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				con_vga_in: endpoint {
+					remote-endpoint = <&adv7123_out>;
+				};
+			};
+		};
+	};
+
+	panel-dpi {
+		compatible = "panel-dpi";
+
+		width-mm = <210>;
+		height-mm = <158>;
+
+		display-timings {
+			timing {
+				/* 1024x768 @65Hz */
+				clock-frequency = <65000000>;
+				hactive = <1024>;
+				vactive = <768>;
+				hsync-len = <136>;
+				hfront-porch = <20>;
+				hback-porch = <160>;
+				vfront-porch = <3>;
+				vback-porch = <29>;
+				vsync-len = <6>;
+			};
+		};
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				panel_in: endpoint {
+					remote-endpoint = <&du_out_lvds1>;
+				};
+			};
+		};
+	};
+};
+
+&du {
+	pinctrl-0 = <&du_pins>;
+	pinctrl-names = "default";
+};
+
+&du_out_rgb {
+	remote-endpoint = <&adv7123_in>;
+};
+
+&du_out_lvds1 {
+	remote-endpoint = <&panel_in>;
+};
+
+&pfc {
+	du_pins: du {
+		renesas,groups = "du_rgb666", "du_sync_1", "du_clk_out_0";
+		renesas,function = "du";
+	};
 };
 
 &pfc {
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* [PATCH/RFC v3 19/19] drm/rcar-du: Port to the Common Display Framework
  2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
                   ` (17 preceding siblings ...)
  2013-08-09 23:03 ` [PATCH/RFC v3 18/19] ARM: shmobile: lager-reference: Add display device nodes to device tree Laurent Pinchart
@ 2013-08-09 23:03 ` Laurent Pinchart
  18 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-09 23:03 UTC (permalink / raw)
  To: dri-devel, linux-fbdev, linux-media

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/Kconfig             |   3 +-
 drivers/gpu/drm/rcar-du/Makefile            |   7 +-
 drivers/gpu/drm/rcar-du/rcar_du_connector.c | 164 ++++++++++++++++
 drivers/gpu/drm/rcar-du/rcar_du_connector.h |  36 ++++
 drivers/gpu/drm/rcar-du/rcar_du_crtc.h      |   2 +-
 drivers/gpu/drm/rcar-du/rcar_du_drv.c       | 279 ++++++++++++++++++++++++----
 drivers/gpu/drm/rcar-du/rcar_du_drv.h       |  28 ++-
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c   |  87 ++++-----
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h   |  22 +--
 drivers/gpu/drm/rcar-du/rcar_du_kms.c       | 116 ++++++++++--
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c   | 131 -------------
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h   |  25 ---
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c    |  96 ----------
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h    |  23 ---
 include/linux/platform_data/rcar-du.h       |  55 +-----
 15 files changed, 611 insertions(+), 463 deletions(-)
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.c
 create mode 100644 drivers/gpu/drm/rcar-du/rcar_du_connector.h
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
 delete mode 100644 drivers/gpu/drm/rcar-du/rcar_du_vgacon.h

diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index c590cd9..a54eeba 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -1,9 +1,10 @@
 config DRM_RCAR_DU
 	tristate "DRM Support for R-Car Display Unit"
-	depends on DRM && ARM
+	depends on DRM && ARM && OF
 	select DRM_KMS_HELPER
 	select DRM_KMS_CMA_HELPER
 	select DRM_GEM_CMA_HELPER
+	select VIDEOMODE_HELPERS
 	help
 	  Choose this option if you have an R-Car chipset.
 	  If M is selected the module will be called rcar-du-drm.
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 12b8d44..3ac8566 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -1,11 +1,10 @@
-rcar-du-drm-y := rcar_du_crtc.o \
+rcar-du-drm-y := rcar_du_connector.o \
+		 rcar_du_crtc.o \
 		 rcar_du_drv.o \
 		 rcar_du_encoder.o \
 		 rcar_du_group.o \
 		 rcar_du_kms.o \
-		 rcar_du_lvdscon.o \
-		 rcar_du_plane.o \
-		 rcar_du_vgacon.o
+		 rcar_du_plane.o
 
 rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS)	+= rcar_du_lvdsenc.o
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_connector.c b/drivers/gpu/drm/rcar-du/rcar_du_connector.c
new file mode 100644
index 0000000..a09aada
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_connector.c
@@ -0,0 +1,164 @@
+/*
+ * rcar_du_connector.c  --  R-Car Display Unit Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/export.h>
+#include <video/videomode.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rcar_du_drv.h"
+#include "rcar_du_connector.h"
+#include "rcar_du_encoder.h"
+#include "rcar_du_kms.h"
+
+static int rcar_du_connector_get_modes(struct drm_connector *connector)
+{
+	struct rcar_du_connector *rcon = to_rcar_connector(connector);
+	struct display_entity *entity = to_display_entity(rcon->pad->entity);
+	const struct videomode *modes;
+	int ret;
+	int i;
+
+	ret = display_entity_get_modes(entity, rcon->pad->index, &modes);
+	if (ret <= 0)
+		return drm_add_modes_noedid(connector, 1280, 768);
+
+	for (i = 0; i < ret; ++i) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_create(connector->dev);
+		if (mode == NULL)
+			break;
+
+		mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
+		drm_display_mode_from_videomode(&modes[i], mode);
+		drm_mode_probed_add(connector, mode);
+	}
+
+	return i;
+}
+
+static int rcar_du_connector_mode_valid(struct drm_connector *connector,
+					struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+rcar_du_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rcar_du_connector *rcon = to_rcar_connector(connector);
+
+	return &rcon->encoder->encoder;
+}
+
+static const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = rcar_du_connector_get_modes,
+	.mode_valid = rcar_du_connector_mode_valid,
+	.best_encoder = rcar_du_connector_best_encoder,
+};
+
+static void rcar_du_connector_destroy(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status
+rcar_du_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = rcar_du_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = rcar_du_connector_destroy,
+};
+
+struct rcar_du_connector *
+rcar_du_connector_create(struct rcar_du_device *rcdu,
+			 struct rcar_du_encoder *renc, struct media_pad *pad)
+{
+	struct display_entity *entity = to_display_entity(pad->entity);
+	struct display_entity_interface_params params;
+	struct rcar_du_connector *rcon;
+	struct drm_connector *connector;
+	int connector_type;
+	unsigned int width;
+	unsigned int height;
+	int ret;
+
+	rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
+	if (rcon == NULL)
+		return ERR_PTR(-ENOMEM);
+
+	rcon->encoder = renc;
+	rcon->pad = pad;
+
+	connector = &rcon->connector;
+
+	ret = display_entity_get_size(entity, &width, &height);
+	if (ret < 0) {
+		connector->display_info.width_mm = 0;
+		connector->display_info.height_mm = 0;
+	} else {
+		connector->display_info.width_mm = width;
+		connector->display_info.height_mm = height;
+	}
+
+	ret = display_entity_get_params(entity, pad->index, &params);
+	if (ret < 0) {
+		dev_dbg(rcdu->dev,
+			"failed to retrieve connector %s parameters\n",
+			entity->name);
+		return ERR_PTR(ret);
+	}
+
+	switch (params.type) {
+	case DISPLAY_ENTITY_INTERFACE_VGA:
+		connector_type = DRM_MODE_CONNECTOR_VGA;
+		break;
+	case DISPLAY_ENTITY_INTERFACE_LVDS:
+		connector_type = DRM_MODE_CONNECTOR_LVDS;
+		break;
+	default:
+		connector_type = DRM_MODE_CONNECTOR_Unknown;
+		break;
+	}
+
+	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
+				 connector_type);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+	ret = drm_sysfs_connector_add(connector);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	drm_object_property_set_value(&connector->base,
+		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
+
+	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	connector->encoder = &renc->encoder;
+
+	return rcon;
+}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_connector.h b/drivers/gpu/drm/rcar-du/rcar_du_connector.h
new file mode 100644
index 0000000..b9a0833
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_connector.h
@@ -0,0 +1,36 @@
+/*
+ * rcar_du_connector.h  --  R-Car Display Unit Connector
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __RCAR_DU_CONNECTOR_H__
+#define __RCAR_DU_CONNECTOR_H__
+
+#include <drm/drm_crtc.h>
+
+struct media_pad;
+struct rcar_du_device;
+struct rcar_du_encoder;
+
+struct rcar_du_connector {
+	struct drm_connector connector;
+	struct rcar_du_encoder *encoder;
+	struct media_pad *pad;
+};
+
+#define to_rcar_connector(c) \
+	container_of(c, struct rcar_du_connector, connector)
+
+struct rcar_du_connector *
+rcar_du_connector_create(struct rcar_du_device *rcdu,
+			 struct rcar_du_encoder *renc, struct media_pad *pad);
+
+#endif /* __RCAR_DU_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 43e7575..4e8bfff 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -15,11 +15,11 @@
 #define __RCAR_DU_CRTC_H__
 
 #include <linux/mutex.h>
-#include <linux/platform_data/rcar-du.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 
+enum rcar_du_output;
 struct rcar_du_group;
 struct rcar_du_plane;
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 0a9f1bb..eda9ca9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -15,6 +15,8 @@
 #include <linux/io.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_data/rcar-du.h>
 #include <linux/platform_device.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
@@ -30,6 +32,153 @@
 #include "rcar_du_regs.h"
 
 /* -----------------------------------------------------------------------------
+ * Display Entities
+ */
+
+static int rcdu_entity_set_stream(struct display_entity *ent, unsigned int port,
+				  enum display_entity_stream_state state)
+{
+	return 0;
+}
+
+static const struct display_entity_video_ops rcdu_entity_video_ops = {
+	.set_stream = rcdu_entity_set_stream,
+};
+
+static const struct display_entity_ops rcdu_entity_ops = {
+	.video = &rcdu_entity_video_ops,
+};
+
+static struct media_pad *rcar_du_find_remote_pad(struct rcar_du_device *rcdu,
+						 unsigned int local_pad)
+{
+	struct display_entity *entity;
+	unsigned int i;
+
+	list_for_each_entry(entity, &rcdu->notifier.done, list) {
+		const struct display_entity_graph_data *graph =
+			entity->match->data;
+		const struct display_entity_source_data *source =
+			graph->sources;
+
+		for (i = 0; i < entity->entity.num_pads; ++i) {
+			struct media_pad *pad = &entity->entity.pads[i];
+
+			if (pad->flags & MEDIA_PAD_FL_SOURCE)
+				continue;
+
+			if (!strcmp(source->name, "rcar-du") &&
+			   source->port == local_pad)
+				return pad;
+		}
+	}
+
+	return NULL;
+}
+
+static int rcar_du_create_local_links(struct rcar_du_device *rcdu)
+{
+	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
+	unsigned int i;
+
+	for (i = 0; i < rcdu->entity.entity.num_pads; ++i) {
+		struct media_pad *pad;
+		int ret;
+
+		pad = rcar_du_find_remote_pad(rcdu, i);
+		if (pad == NULL)
+			continue;
+
+		ret = media_entity_create_link(&rcdu->entity.entity, i,
+					       pad->entity, pad->index,
+					       link_flags);
+		if (ret < 0) {
+			dev_err(rcdu->dev,
+				"failed to create %s:%u -> %s:%u link\n",
+				rcdu->entity.entity.name, i,
+				pad->entity->name, pad->index);
+			return ret;
+		}
+	}
+
+	return 0;
+};
+
+static int rcar_du_graph_init(struct rcar_du_device *rcdu)
+{
+	struct display_entity *entity;
+	unsigned int num_pads;
+	unsigned int i;
+	int ret;
+
+	/* Count the number of output pads and initialize the DU entity. */
+	for (i = 0, num_pads = 0; rcdu->info->routes[i].output; ++i)
+		num_pads++;
+
+	rcdu->entity.dev = rcdu->dev;
+	rcdu->entity.ops = &rcdu_entity_ops;
+	strcpy(rcdu->entity.name, "R-Car DU");
+
+	ret = display_entity_init(&rcdu->entity, 0, num_pads);
+	if (ret < 0)
+		return ret;
+
+	/* Create links between all entities. In the non-DT case, this is a two
+	 * steps process, we first create links between all external entities,
+	 * and then between the DU entity and the external entities.
+	 */
+	if (rcdu->dev->of_node) {
+		ret = display_of_entity_link_graph(rcdu->dev,
+						   &rcdu->notifier.done,
+						   &rcdu->entity);
+	} else {
+		ret = display_entity_link_graph(rcdu->dev,
+						&rcdu->notifier.done);
+		if (!ret)
+			ret = rcar_du_create_local_links(rcdu);
+	}
+
+	if (ret < 0) {
+		dev_err(rcdu->dev, "unable to link graph\n");
+		return ret;
+	}
+
+	/* Register the media device and all entities. */
+	rcdu->mdev.dev = rcdu->dev;
+	strlcpy(rcdu->mdev.model, dev_name(rcdu->dev),
+		sizeof(rcdu->mdev.model));
+
+	ret = media_device_register(&rcdu->mdev);
+	if (ret < 0)
+		return ret;
+
+	list_for_each_entry(entity, &rcdu->notifier.done, list) {
+		ret = display_entity_register(&rcdu->mdev, entity);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = display_entity_register(&rcdu->mdev, &rcdu->entity);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void rcar_du_graph_cleanup(struct rcar_du_device *rcdu)
+{
+	struct display_entity *entity;
+
+	list_for_each_entry(entity, &rcdu->notifier.done, list)
+		display_entity_unregister(entity);
+
+	display_entity_unregister(&rcdu->entity);
+	display_entity_cleanup(&rcdu->entity);
+
+	media_device_unregister(&rcdu->mdev);
+}
+
+/* -----------------------------------------------------------------------------
  * DRM operations
  */
 
@@ -44,6 +193,8 @@ static int rcar_du_unload(struct drm_device *dev)
 	drm_mode_config_cleanup(dev);
 	drm_vblank_cleanup(dev);
 
+	rcar_du_graph_cleanup(rcdu);
+
 	dev->irq_enabled = 0;
 	dev->dev_private = NULL;
 
@@ -53,25 +204,10 @@ static int rcar_du_unload(struct drm_device *dev)
 static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 {
 	struct platform_device *pdev = dev->platformdev;
-	struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
-	struct rcar_du_device *rcdu;
+	struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
 	struct resource *mem;
 	int ret;
 
-	if (pdata == NULL) {
-		dev_err(dev->dev, "no platform data\n");
-		return -ENODEV;
-	}
-
-	rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
-	if (rcdu == NULL) {
-		dev_err(dev->dev, "failed to allocate private data\n");
-		return -ENOMEM;
-	}
-
-	rcdu->dev = &pdev->dev;
-	rcdu->pdata = pdata;
-	rcdu->info = (struct rcar_du_device_info *)pdev->id_entry->driver_data;
 	rcdu->ddev = dev;
 	dev->dev_private = rcdu;
 
@@ -81,6 +217,13 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 	if (IS_ERR(rcdu->mmio))
 		return PTR_ERR(rcdu->mmio);
 
+	/* Display Entities */
+	ret = rcar_du_graph_init(rcdu);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to initialize display entities\n");
+		goto done;
+	}
+
 	/* DRM/KMS objects */
 	ret = rcar_du_modeset_init(rcdu);
 	if (ret < 0) {
@@ -97,8 +240,6 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 
 	dev->irq_enabled = 1;
 
-	platform_set_drvdata(pdev, rcdu);
-
 done:
 	if (ret)
 		rcar_du_unload(dev);
@@ -218,33 +359,23 @@ static const struct dev_pm_ops rcar_du_pm_ops = {
  * Platform driver
  */
 
-static int rcar_du_probe(struct platform_device *pdev)
-{
-	return drm_platform_init(&rcar_du_driver, pdev);
-}
-
-static int rcar_du_remove(struct platform_device *pdev)
-{
-	drm_platform_exit(&rcar_du_driver, pdev);
-
-	return 0;
-}
-
 static const struct rcar_du_device_info rcar_du_r8a7779_info = {
 	.features = 0,
 	.num_crtcs = 2,
-	.routes = {
+	.routes = (const struct rcar_du_output_routing[]) {
 		/* R8A7779 has two RGB outputs and one (currently unsupported)
 		 * TCON output.
 		 */
-		[RCAR_DU_OUTPUT_DPAD0] = {
+		{
+			.output = RCAR_DU_OUTPUT_DPAD0,
 			.possible_crtcs = BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_NONE,
-		},
-		[RCAR_DU_OUTPUT_DPAD1] = {
+		}, {
+			.output = RCAR_DU_OUTPUT_DPAD1,
 			.possible_crtcs = BIT(1) | BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_NONE,
 		},
+		{ },
 	},
 	.num_lvds = 0,
 };
@@ -253,22 +384,24 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = {
 	.features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_ALIGN_128B
 		  | RCAR_DU_FEATURE_DEFR8,
 	.num_crtcs = 3,
-	.routes = {
+	.routes = (const struct rcar_du_output_routing[]) {
 		/* R8A7790 has one RGB output, two LVDS outputs and one
 		 * (currently unsupported) TCON output.
 		 */
-		[RCAR_DU_OUTPUT_DPAD0] = {
+		{
+			.output = RCAR_DU_OUTPUT_DPAD0,
 			.possible_crtcs = BIT(2) | BIT(1) | BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_NONE,
-		},
-		[RCAR_DU_OUTPUT_LVDS0] = {
+		}, {
+			.output = RCAR_DU_OUTPUT_LVDS0,
 			.possible_crtcs = BIT(0),
 			.encoder_type = DRM_MODE_ENCODER_LVDS,
-		},
-		[RCAR_DU_OUTPUT_LVDS1] = {
+		}, {
+			.output = RCAR_DU_OUTPUT_LVDS1,
 			.possible_crtcs = BIT(2) | BIT(1),
 			.encoder_type = DRM_MODE_ENCODER_LVDS,
 		},
+		{ },
 	},
 	.num_lvds = 2,
 };
@@ -278,9 +411,72 @@ static const struct platform_device_id rcar_du_id_table[] = {
 	{ "rcar-du-r8a7790", (kernel_ulong_t)&rcar_du_r8a7790_info },
 	{ }
 };
-
 MODULE_DEVICE_TABLE(platform, rcar_du_id_table);
 
+static const struct of_device_id rcar_du_of_table[] = {
+	{ .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info },
+	{ .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rcar_du_of_table);
+
+static int rcar_du_notifier_complete(struct display_entity_notifier *notifier)
+{
+	struct platform_device *pdev = to_platform_device(notifier->dev);
+
+	return drm_platform_init(&rcar_du_driver, pdev);
+}
+
+static int rcar_du_probe(struct platform_device *pdev)
+{
+	struct rcar_du_platform_data *pdata = pdev->dev.platform_data;
+	struct device_node *np = pdev->dev.of_node;
+	struct rcar_du_device *rcdu;
+	int ret;
+
+	rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL);
+	if (rcdu == NULL)
+		return -ENOMEM;
+
+	rcdu->dev = &pdev->dev;
+
+	if (np)
+		rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
+	else
+		rcdu->info = (void *)platform_get_device_id(pdev)->driver_data;
+
+	platform_set_drvdata(pdev, rcdu);
+
+	rcdu->notifier.dev = rcdu->dev;
+	rcdu->notifier.complete = rcar_du_notifier_complete;
+
+	if (np) {
+		ret = display_of_entity_build_notifier(&rcdu->notifier, np);
+	} else if (pdata) {
+		ret = display_entity_build_notifier(&rcdu->notifier,
+						    pdata->graph);
+	} else {
+		dev_err(&pdev->dev, "no platform data");
+		ret = -ENXIO;
+	}
+
+	if (ret < 0)
+		return ret;
+
+	return display_entity_register_notifier(&rcdu->notifier);
+}
+
+static int rcar_du_remove(struct platform_device *pdev)
+{
+	struct rcar_du_device *rcdu = platform_get_drvdata(pdev);
+
+	display_entity_unregister_notifier(&rcdu->notifier);
+
+	drm_platform_exit(&rcar_du_driver, pdev);
+
+	return 0;
+}
+
 static struct platform_driver rcar_du_platform_driver = {
 	.probe		= rcar_du_probe,
 	.remove		= rcar_du_remove,
@@ -288,6 +484,7 @@ static struct platform_driver rcar_du_platform_driver = {
 		.owner	= THIS_MODULE,
 		.name	= "rcar-du",
 		.pm	= &rcar_du_pm_ops,
+		.of_match_table = of_match_ptr(rcar_du_of_table),
 	},
 	.id_table	= rcar_du_id_table,
 };
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index 65d2d63..49338e1 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -15,7 +15,8 @@
 #define __RCAR_DU_DRV_H__
 
 #include <linux/kernel.h>
-#include <linux/platform_data/rcar-du.h>
+#include <media/media-device.h>
+#include <video/display.h>
 
 #include "rcar_du_crtc.h"
 #include "rcar_du_group.h"
@@ -31,16 +32,28 @@ struct rcar_du_lvdsenc;
 #define RCAR_DU_FEATURE_ALIGN_128B	(1 << 1)	/* Align pitches to 128 bytes */
 #define RCAR_DU_FEATURE_DEFR8		(1 << 2)	/* Has DEFR8 register */
 
+enum rcar_du_output {
+	RCAR_DU_OUTPUT_NONE,
+	RCAR_DU_OUTPUT_DPAD0,
+	RCAR_DU_OUTPUT_DPAD1,
+	RCAR_DU_OUTPUT_LVDS0,
+	RCAR_DU_OUTPUT_LVDS1,
+	RCAR_DU_OUTPUT_TCON,
+	RCAR_DU_OUTPUT_MAX,
+};
+
 /*
  * struct rcar_du_output_routing - Output routing specification
+ * @output: DU output
  * @possible_crtcs: bitmask of possible CRTCs for the output
  * @encoder_type: DRM type of the internal encoder associated with the output
  *
  * The DU has 5 possible outputs (DPAD0/1, LVDS0/1, TCON). Output routing data
- * specify the valid SoC outputs, which CRTCs can drive the output, and the type
- * of in-SoC encoder for the output.
+ * specify an SoC output, which CRTCs can drive it, and the type of in-SoC
+ * encoder for the output.
  */
 struct rcar_du_output_routing {
+	enum rcar_du_output output;
 	unsigned int possible_crtcs;
 	unsigned int encoder_type;
 };
@@ -49,23 +62,26 @@ struct rcar_du_output_routing {
  * struct rcar_du_device_info - DU model-specific information
  * @features: device features (RCAR_DU_FEATURE_*)
  * @num_crtcs: total number of CRTCs
- * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
+ * @routes: array of CRTC to output routes, indexed by port number
  * @num_lvds: number of internal LVDS encoders
  */
 struct rcar_du_device_info {
 	unsigned int features;
 	unsigned int num_crtcs;
-	struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
+	const struct rcar_du_output_routing *routes;
 	unsigned int num_lvds;
 };
 
 struct rcar_du_device {
 	struct device *dev;
-	const struct rcar_du_platform_data *pdata;
 	const struct rcar_du_device_info *info;
 
 	void __iomem *mmio;
 
+	struct media_device mdev;
+	struct display_entity entity;
+	struct display_entity_notifier notifier;
+
 	struct drm_device *ddev;
 	struct drm_fbdev_cma *fbdev;
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 3daa7a1..9991fb0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -20,25 +20,7 @@
 #include "rcar_du_drv.h"
 #include "rcar_du_encoder.h"
 #include "rcar_du_kms.h"
-#include "rcar_du_lvdscon.h"
 #include "rcar_du_lvdsenc.h"
-#include "rcar_du_vgacon.h"
-
-/* -----------------------------------------------------------------------------
- * Common connector functions
- */
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector)
-{
-	struct rcar_du_connector *rcon = to_rcar_connector(connector);
-
-	return &rcon->encoder->encoder;
-}
-
-/* -----------------------------------------------------------------------------
- * Encoder
- */
 
 static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode)
 {
@@ -139,10 +121,9 @@ static const struct drm_encoder_funcs encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
 
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-			 enum rcar_du_encoder_type type,
-			 enum rcar_du_output output,
-			 const struct rcar_du_encoder_data *data)
+struct rcar_du_encoder *
+rcar_du_encoder_create(struct rcar_du_device *rcdu, unsigned int port,
+		       struct media_pad *pad)
 {
 	struct rcar_du_encoder *renc;
 	unsigned int encoder_type;
@@ -150,11 +131,13 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 
 	renc = devm_kzalloc(rcdu->dev, sizeof(*renc), GFP_KERNEL);
 	if (renc == NULL)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
-	renc->output = output;
+	renc->port = port;
+	renc->output = rcdu->info->routes[port].output;
 
-	switch (output) {
+	/* Do we have an internal LVDS encoder? */
+	switch (renc->output) {
 	case RCAR_DU_OUTPUT_LVDS0:
 		renc->lvds = rcdu->lvds[0];
 		break;
@@ -167,36 +150,44 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 		break;
 	}
 
-	switch (type) {
-	case RCAR_DU_ENCODER_VGA:
-		encoder_type = DRM_MODE_ENCODER_DAC;
-		break;
-	case RCAR_DU_ENCODER_LVDS:
-		encoder_type = DRM_MODE_ENCODER_LVDS;
-		break;
-	case RCAR_DU_ENCODER_NONE:
-	default:
+	/* Find the encoder type. */
+	if (pad) {
+		struct display_entity *entity = to_display_entity(pad->entity);
+		struct display_entity_interface_params params;
+
+		ret = display_entity_get_params(entity, pad->index, &params);
+		if (ret < 0) {
+			dev_dbg(rcdu->dev,
+				"failed to retrieve encoder %s parameters\n",
+				entity->name);
+			return ERR_PTR(ret);
+		}
+
+		switch (params.type) {
+		case DISPLAY_ENTITY_INTERFACE_DPI:
+		case DISPLAY_ENTITY_INTERFACE_DBI:
+		default:
+			encoder_type = DRM_MODE_ENCODER_NONE;
+			break;
+		case DISPLAY_ENTITY_INTERFACE_LVDS:
+			encoder_type = DRM_MODE_ENCODER_DAC;
+			break;
+		case DISPLAY_ENTITY_INTERFACE_VGA:
+			encoder_type = DRM_MODE_ENCODER_DAC;
+			break;
+		}
+	} else {
 		/* No external encoder, use the internal encoder type. */
-		encoder_type = rcdu->info->routes[output].encoder_type;
-		break;
+		encoder_type = rcdu->info->routes[port].encoder_type;
 	}
 
+	/* Initialize the encoder. */
 	ret = drm_encoder_init(rcdu->ddev, &renc->encoder, &encoder_funcs,
 			       encoder_type);
 	if (ret < 0)
-		return ret;
+		return ERR_PTR(ret);
 
 	drm_encoder_helper_add(&renc->encoder, &encoder_helper_funcs);
 
-	switch (encoder_type) {
-	case DRM_MODE_ENCODER_LVDS:
-		return rcar_du_lvds_connector_init(rcdu, renc,
-						   &data->connector.lvds.panel);
-
-	case DRM_MODE_ENCODER_DAC:
-		return rcar_du_vga_connector_init(rcdu, renc);
-
-	default:
-		return -EINVAL;
-	}
+	return renc;
 }
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 0e5a65e..7a19d5b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -14,15 +14,15 @@
 #ifndef __RCAR_DU_ENCODER_H__
 #define __RCAR_DU_ENCODER_H__
 
-#include <linux/platform_data/rcar-du.h>
-
 #include <drm/drm_crtc.h>
 
+struct media_pad;
 struct rcar_du_device;
 struct rcar_du_lvdsenc;
 
 struct rcar_du_encoder {
 	struct drm_encoder encoder;
+	unsigned int port;
 	enum rcar_du_output output;
 	struct rcar_du_lvdsenc *lvds;
 };
@@ -30,20 +30,8 @@ struct rcar_du_encoder {
 #define to_rcar_encoder(e) \
 	container_of(e, struct rcar_du_encoder, encoder)
 
-struct rcar_du_connector {
-	struct drm_connector connector;
-	struct rcar_du_encoder *encoder;
-};
-
-#define to_rcar_connector(c) \
-	container_of(c, struct rcar_du_connector, connector)
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector);
-
-int rcar_du_encoder_init(struct rcar_du_device *rcdu,
-			 enum rcar_du_encoder_type type,
-			 enum rcar_du_output output,
-			 const struct rcar_du_encoder_data *data);
+struct rcar_du_encoder *
+rcar_du_encoder_create(struct rcar_du_device *rcdu, unsigned int port,
+		       struct media_pad *pad);
 
 #endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index b31ac08..2dc9e80 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -17,6 +17,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
+#include "rcar_du_connector.h"
 #include "rcar_du_crtc.h"
 #include "rcar_du_drv.h"
 #include "rcar_du_encoder.h"
@@ -179,6 +180,89 @@ static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
 	.output_poll_changed = rcar_du_output_poll_changed,
 };
 
+/* -----------------------------------------------------------------------------
+ * Pipeline and Mode Setting Initialization
+ */
+
+static int rcar_du_pipe_init(struct rcar_du_device *rcdu, unsigned int port,
+			     struct media_pad *remote)
+{
+	struct media_pad *pad_encoder = NULL;
+	struct media_pad *pad_connector = remote;
+	struct rcar_du_encoder *renc;
+	struct rcar_du_connector *rcon;
+	struct media_entity *entity;
+
+	/* We need to expose one KMS encoder and one KMS connector for an
+	 * arbitrarily long chain of external entities. Walk the chain and map
+	 * the last entity to the connector, and the next-to-last entity to the
+	 * encoder.
+	 */
+
+	/* Start at the entity connected to the DU output. */
+	entity = remote->entity;
+
+	dev_dbg(rcdu->dev, "%s: starting at entity %s pad %u\n", __func__,
+		entity->name, remote->index);
+
+	while (1) {
+		struct media_link *next = NULL;
+		unsigned int i;
+
+		/* Search the entity for an output link. As we only support
+		 * linear pipelines, return an error if multiple output links
+		 * are found.
+		 */
+		dev_dbg(rcdu->dev,
+			"%s: searching for an output link on entity %s\n",
+			__func__, entity->name);
+
+		for (i = 0; i < entity->num_links; ++i) {
+			struct media_link *link = &entity->links[i];
+
+			if (link->source->entity != entity)
+				continue;
+
+			if (next)
+				return -EPIPE;
+
+			next = link;
+		}
+
+		if (next == NULL) {
+			dev_dbg(rcdu->dev, "%s: not output link found\n",
+				__func__);
+			break;
+		}
+
+		dev_dbg(rcdu->dev,
+			"%s: output link %s:%u -> %s:%u found\n", __func__,
+			next->source->entity->name, next->source->index,
+			next->sink->entity->name, next->sink->index);
+
+		pad_encoder = next->source;
+		pad_connector = next->sink;
+		entity = pad_connector->entity;
+	}
+
+	dev_dbg(rcdu->dev,
+		"%s: encoder pad %s/%u connector pad %s/%u\n", __func__,
+		pad_encoder ? pad_encoder->entity->name : NULL,
+		pad_encoder ? pad_encoder->index : 0,
+		pad_connector->entity->name, pad_connector->index);
+
+	/* Create the encoder and connector. */
+	renc = rcar_du_encoder_create(rcdu, port, pad_encoder);
+	if (IS_ERR(renc))
+		return PTR_ERR(renc);
+
+	rcon = rcar_du_connector_create(rcdu, renc, pad_connector);
+	if (IS_ERR(rcon))
+		return PTR_ERR(rcon);
+
+	return 0;
+}
+
 int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 {
 	static const unsigned int mmio_offsets[] = {
@@ -188,6 +272,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 	struct drm_device *dev = rcdu->ddev;
 	struct drm_encoder *encoder;
 	struct drm_fbdev_cma *fbdev;
+	unsigned int num_encoders = 0;
 	unsigned int num_groups;
 	unsigned int i;
 	int ret;
@@ -226,29 +311,26 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 			return ret;
 	}
 
-	/* Initialize the encoders. */
+	/* Initialize the internal encoders. */
 	ret = rcar_du_lvdsenc_init(rcdu);
 	if (ret < 0)
 		return ret;
 
-	for (i = 0; i < rcdu->pdata->num_encoders; ++i) {
-		const struct rcar_du_encoder_data *pdata =
-			&rcdu->pdata->encoders[i];
-		const struct rcar_du_output_routing *route =
-			&rcdu->info->routes[pdata->output];
+	/* Create an encoder and a connector for each output connected to
+	 * external entities.
+	 */
+	for (i = 0; i < rcdu->entity.entity.num_pads; ++i) {
+		struct media_pad *pad;
 
-		if (pdata->type == RCAR_DU_ENCODER_UNUSED)
+		pad = media_entity_remote_pad(&rcdu->entity.entity.pads[i]);
+		if (pad == NULL)
 			continue;
 
-		if (pdata->output >= RCAR_DU_OUTPUT_MAX ||
-		    route->possible_crtcs == 0) {
-			dev_warn(rcdu->dev,
-				 "encoder %u references unexisting output %u, skipping\n",
-				 i, pdata->output);
-			continue;
-		}
+		ret = rcar_du_pipe_init(rcdu, i, pad);
+		if (ret < 0)
+			return ret;
 
-		rcar_du_encoder_init(rcdu, pdata->type, pdata->output, pdata);
+		num_encoders++;
 	}
 
 	/* Set the possible CRTCs and possible clones. There's always at least
@@ -258,10 +340,10 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
 		const struct rcar_du_output_routing *route =
-			&rcdu->info->routes[renc->output];
+			&rcdu->info->routes[renc->port];
 
 		encoder->possible_crtcs = route->possible_crtcs;
-		encoder->possible_clones = (1 << rcdu->pdata->num_encoders) - 1;
+		encoder->possible_clones = (1 << num_encoders) - 1;
 	}
 
 	/* Now that the CRTCs have been initialized register the planes. */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
deleted file mode 100644
index 4f3ba93..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * rcar_du_lvdscon.c  --  R-Car Display Unit LVDS Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_lvdscon.h"
-
-struct rcar_du_lvds_connector {
-	struct rcar_du_connector connector;
-
-	const struct rcar_du_panel_data *panel;
-};
-
-#define to_rcar_lvds_connector(c) \
-	container_of(c, struct rcar_du_lvds_connector, connector.connector)
-
-static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
-{
-	struct rcar_du_lvds_connector *lvdscon =
-		to_rcar_lvds_connector(connector);
-	struct drm_display_mode *mode;
-
-	mode = drm_mode_create(connector->dev);
-	if (mode == NULL)
-		return 0;
-
-	mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
-	mode->clock = lvdscon->panel->mode.clock;
-	mode->hdisplay = lvdscon->panel->mode.hdisplay;
-	mode->hsync_start = lvdscon->panel->mode.hsync_start;
-	mode->hsync_end = lvdscon->panel->mode.hsync_end;
-	mode->htotal = lvdscon->panel->mode.htotal;
-	mode->vdisplay = lvdscon->panel->mode.vdisplay;
-	mode->vsync_start = lvdscon->panel->mode.vsync_start;
-	mode->vsync_end = lvdscon->panel->mode.vsync_end;
-	mode->vtotal = lvdscon->panel->mode.vtotal;
-	mode->flags = lvdscon->panel->mode.flags;
-
-	drm_mode_set_name(mode);
-	drm_mode_probed_add(connector, mode);
-
-	return 1;
-}
-
-static int rcar_du_lvds_connector_mode_valid(struct drm_connector *connector,
-					    struct drm_display_mode *mode)
-{
-	return MODE_OK;
-}
-
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = rcar_du_lvds_connector_get_modes,
-	.mode_valid = rcar_du_lvds_connector_mode_valid,
-	.best_encoder = rcar_du_connector_best_encoder,
-};
-
-static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
-{
-	drm_sysfs_connector_remove(connector);
-	drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status
-rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
-{
-	return connector_status_connected;
-}
-
-static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
-	.detect = rcar_du_lvds_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = rcar_du_lvds_connector_destroy,
-};
-
-int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
-				struct rcar_du_encoder *renc,
-				const struct rcar_du_panel_data *panel)
-{
-	struct rcar_du_lvds_connector *lvdscon;
-	struct drm_connector *connector;
-	int ret;
-
-	lvdscon = devm_kzalloc(rcdu->dev, sizeof(*lvdscon), GFP_KERNEL);
-	if (lvdscon == NULL)
-		return -ENOMEM;
-
-	lvdscon->panel = panel;
-
-	connector = &lvdscon->connector.connector;
-	connector->display_info.width_mm = panel->width_mm;
-	connector->display_info.height_mm = panel->height_mm;
-
-	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
-				 DRM_MODE_CONNECTOR_LVDS);
-	if (ret < 0)
-		return ret;
-
-	drm_connector_helper_add(connector, &connector_helper_funcs);
-	ret = drm_sysfs_connector_add(connector);
-	if (ret < 0)
-		return ret;
-
-	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
-
-	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
-	if (ret < 0)
-		return ret;
-
-	connector->encoder = &renc->encoder;
-	lvdscon->connector.encoder = renc;
-
-	return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
deleted file mode 100644
index bff8683..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * rcar_du_lvdscon.h  --  R-Car Display Unit LVDS Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __RCAR_DU_LVDSCON_H__
-#define __RCAR_DU_LVDSCON_H__
-
-struct rcar_du_device;
-struct rcar_du_encoder;
-struct rcar_du_panel_data;
-
-int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
-				struct rcar_du_encoder *renc,
-				const struct rcar_du_panel_data *panel);
-
-#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
deleted file mode 100644
index 72312f7..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * rcar_du_vgacon.c  --  R-Car Display Unit VGA Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drmP.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-
-#include "rcar_du_drv.h"
-#include "rcar_du_encoder.h"
-#include "rcar_du_kms.h"
-#include "rcar_du_vgacon.h"
-
-static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
-{
-	return drm_add_modes_noedid(connector, 1280, 768);
-}
-
-static int rcar_du_vga_connector_mode_valid(struct drm_connector *connector,
-					    struct drm_display_mode *mode)
-{
-	return MODE_OK;
-}
-
-static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.get_modes = rcar_du_vga_connector_get_modes,
-	.mode_valid = rcar_du_vga_connector_mode_valid,
-	.best_encoder = rcar_du_connector_best_encoder,
-};
-
-static void rcar_du_vga_connector_destroy(struct drm_connector *connector)
-{
-	drm_sysfs_connector_remove(connector);
-	drm_connector_cleanup(connector);
-}
-
-static enum drm_connector_status
-rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
-{
-	return connector_status_connected;
-}
-
-static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
-	.detect = rcar_du_vga_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = rcar_du_vga_connector_destroy,
-};
-
-int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
-			       struct rcar_du_encoder *renc)
-{
-	struct rcar_du_connector *rcon;
-	struct drm_connector *connector;
-	int ret;
-
-	rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
-	if (rcon == NULL)
-		return -ENOMEM;
-
-	connector = &rcon->connector;
-	connector->display_info.width_mm = 0;
-	connector->display_info.height_mm = 0;
-
-	ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
-				 DRM_MODE_CONNECTOR_VGA);
-	if (ret < 0)
-		return ret;
-
-	drm_connector_helper_add(connector, &connector_helper_funcs);
-	ret = drm_sysfs_connector_add(connector);
-	if (ret < 0)
-		return ret;
-
-	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
-	drm_object_property_set_value(&connector->base,
-		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
-
-	ret = drm_mode_connector_attach_encoder(connector, &renc->encoder);
-	if (ret < 0)
-		return ret;
-
-	connector->encoder = &renc->encoder;
-	rcon->encoder = renc;
-
-	return 0;
-}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
deleted file mode 100644
index b12b0cf..0000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * rcar_du_vgacon.h  --  R-Car Display Unit VGA Connector
- *
- * Copyright (C) 2013 Renesas Corporation
- *
- * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __RCAR_DU_VGACON_H__
-#define __RCAR_DU_VGACON_H__
-
-struct rcar_du_device;
-struct rcar_du_encoder;
-
-int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
-			       struct rcar_du_encoder *renc);
-
-#endif /* __RCAR_DU_VGACON_H__ */
diff --git a/include/linux/platform_data/rcar-du.h b/include/linux/platform_data/rcar-du.h
index 1a2e990..b424b75 100644
--- a/include/linux/platform_data/rcar-du.h
+++ b/include/linux/platform_data/rcar-du.h
@@ -14,61 +14,10 @@
 #ifndef __RCAR_DU_H__
 #define __RCAR_DU_H__
 
-#include <drm/drm_mode.h>
-
-enum rcar_du_output {
-	RCAR_DU_OUTPUT_DPAD0,
-	RCAR_DU_OUTPUT_DPAD1,
-	RCAR_DU_OUTPUT_LVDS0,
-	RCAR_DU_OUTPUT_LVDS1,
-	RCAR_DU_OUTPUT_TCON,
-	RCAR_DU_OUTPUT_MAX,
-};
-
-enum rcar_du_encoder_type {
-	RCAR_DU_ENCODER_UNUSED = 0,
-	RCAR_DU_ENCODER_NONE,
-	RCAR_DU_ENCODER_VGA,
-	RCAR_DU_ENCODER_LVDS,
-};
-
-struct rcar_du_panel_data {
-	unsigned int width_mm;		/* Panel width in mm */
-	unsigned int height_mm;		/* Panel height in mm */
-	struct drm_mode_modeinfo mode;
-};
-
-struct rcar_du_connector_lvds_data {
-	struct rcar_du_panel_data panel;
-};
-
-struct rcar_du_connector_vga_data {
-	/* TODO: Add DDC information for EDID retrieval */
-};
-
-/*
- * struct rcar_du_encoder_data - Encoder platform data
- * @type: the encoder type (RCAR_DU_ENCODER_*)
- * @output: the DU output the connector is connected to (RCAR_DU_OUTPUT_*)
- * @connector.lvds: platform data for LVDS connectors
- * @connector.vga: platform data for VGA connectors
- *
- * Encoder platform data describes an on-board encoder, its associated DU SoC
- * output, and the connector.
- */
-struct rcar_du_encoder_data {
-	enum rcar_du_encoder_type type;
-	enum rcar_du_output output;
-
-	union {
-		struct rcar_du_connector_lvds_data lvds;
-		struct rcar_du_connector_vga_data vga;
-	} connector;
-};
+#include <video/display.h>
 
 struct rcar_du_platform_data {
-	struct rcar_du_encoder_data *encoders;
-	unsigned int num_encoders;
+	const struct display_entity_graph_data *graph;
 };
 
 #endif /* __RCAR_DU_H__ */
-- 
1.8.1.5


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-08-09 23:03 ` [PATCH/RFC v3 06/19] video: display: OF support Laurent Pinchart
@ 2013-08-27  9:30   ` Philipp Zabel
  2013-08-30  0:47     ` Laurent Pinchart
  2013-09-04 14:21   ` Philipp Zabel
  1 sibling, 1 reply; 26+ messages in thread
From: Philipp Zabel @ 2013-08-27  9:30 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: dri-devel, linux-fbdev, linux-media

Hi Laurent,

I have another small issue with the graph helpers below:

Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
[...]
> +/* -----------------------------------------------------------------------------
>   * Graph Helpers
>   */
>  
> @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev, struct list_head *entities)
>  }
>  EXPORT_SYMBOL_GPL(display_entity_link_graph);
>  
> +#ifdef CONFIG_OF
> +
> +static int display_of_entity_link_entity(struct device *dev,
> +					 struct display_entity *entity,
> +					 struct list_head *entities,
> +					 struct display_entity *root)
> +{
> +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> +	const struct device_node *node = entity->dev->of_node;
> +	struct media_entity *local = &entity->entity;
> +	struct device_node *ep = NULL;
> +	int ret = 0;
> +
> +	dev_dbg(dev, "creating links for entity %s\n", local->name);
> +
> +	while (1) {
> +		struct media_entity *remote = NULL;
> +		struct media_pad *remote_pad;
> +		struct media_pad *local_pad;
> +		struct display_of_link link;
> +		struct display_entity *ent;
> +		struct device_node *next;
> +
> +		/* Get the next endpoint and parse its link. */
> +		next = display_of_get_next_endpoint(node, ep);
> +		if (next == NULL)
> +			break;
> +
> +		of_node_put(ep);
> +		ep = next;
> +
> +		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
> +
> +		ret = display_of_parse_link(ep, &link);
> +		if (ret < 0) {
> +			dev_err(dev, "failed to parse link for %s\n",
> +				ep->full_name);
> +			continue;
> +		}
> +
> +		/* Skip source pads, they will be processed from the other end of
> +		 * the link.
> +		 */
> +		if (link.local_port >= local->num_pads) {
> +			dev_err(dev, "invalid port number %u on %s\n",
> +				link.local_port, link.local_node->full_name);
> +			display_of_put_link(&link);
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		local_pad = &local->pads[link.local_port];
> +
> +		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
> +			dev_dbg(dev, "skipping source port %s:%u\n",
> +				link.local_node->full_name, link.local_port);
> +			display_of_put_link(&link);
> +			continue;
> +		}
> +
> +		/* Find the remote entity. If not found, just skip the link as
> +		 * it goes out of scope of the entities handled by the notifier.
> +		 */
> +		list_for_each_entry(ent, entities, list) {
> +			if (ent->dev->of_node == link.remote_node) {
> +				remote = &ent->entity;
> +				break;
> +			}
> +		}
> +
> +		if (root->dev->of_node == link.remote_node)
> +			remote = &root->entity;
> +
> +		if (remote == NULL) {
> +			dev_dbg(dev, "no entity found for %s\n",
> +				link.remote_node->full_name);
> +			display_of_put_link(&link);
> +			continue;
> +		}
> +
> +		if (link.remote_port >= remote->num_pads) {
> +			dev_err(dev, "invalid port number %u on %s\n",
> +				link.remote_port, link.remote_node->full_name);
> +			display_of_put_link(&link);
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		remote_pad = &remote->pads[link.remote_port];
> +
> +		display_of_put_link(&link);
> +
> +		/* Create the media link. */
> +		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
> +			remote->name, remote_pad->index,
> +			local->name, local_pad->index);
> +
> +		ret = media_entity_create_link(remote, remote_pad->index,
> +					       local, local_pad->index,
> +					       link_flags);
> +		if (ret < 0) {
> +			dev_err(dev,
> +				"failed to create %s:%u -> %s:%u link\n",
> +				remote->name, remote_pad->index,
> +				local->name, local_pad->index);
> +			break;
> +		}
> +	}
> +
> +	of_node_put(ep);
> +	return ret;
> +}
> +
> +/**
> + * display_of_entity_link_graph - Link all entities in a graph
> + * @dev: device used to print debugging and error messages
> + * @root: optional root display entity
> + * @entities: list of display entities in the graph
> + *
> + * This function creates media controller links for all entities in a graph
> + * based on the device tree graph representation. It relies on all entities
> + * having been instantiated from the device tree.
> + *
> + * The list of entities is typically taken directly from a display notifier
> + * done list. It will thus not include any display entity not handled by the
> + * notifier, such as entities directly accessible by the caller without going
> + * through the notification process. The optional root entity parameter can be
> + * used to pass such a display entity and include it in the graph. For all
> + * practical purpose the root entity is handled is if it was part of the
> + * entities list.
> + *
> + * Return 0 on success or a negative error code otherwise.
> + */
> +int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
> +				 struct display_entity *root)
> +{
> +	struct display_entity *entity;
> +	int ret;
> +
> +	list_for_each_entry(entity, entities, list) {
> +		if (WARN_ON(entity->match->type != DISPLAY_ENTITY_BUS_DT))
> +			return -EINVAL;
> +
> +		ret = display_of_entity_link_entity(dev, entity, entities,
> +						    root);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return display_of_entity_link_entity(dev, root, entities, root);
> +}
> +EXPORT_SYMBOL_GPL(display_of_entity_link_graph);

The root display entity given to display_of_entity_link_graph is documented
to be optional. Therefore, do not try to dereference root if it is NULL:

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 328ead7..6c8094f 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -669,7 +669,7 @@ static int display_of_entity_link_entity(struct device *dev,
 			}
 		}
 
-		if (root->dev->of_node == link.remote_node)
+		if (root && root->dev->of_node == link.remote_node)
 			remote = &root->entity;
 
 		if (remote == NULL) {
@@ -748,6 +748,9 @@ int display_of_entity_link_graph(struct device *dev, struct list_head *entities,
 			return ret;
 	}
 
+	if (!root)
+		return 0;
+
 	return display_of_entity_link_entity(dev, root, entities, root);
 }
 EXPORT_SYMBOL_GPL(display_of_entity_link_graph);
-- 
1.8.4.rc3

regards
Philipp


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-08-27  9:30   ` Philipp Zabel
@ 2013-08-30  0:47     ` Laurent Pinchart
  0 siblings, 0 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-08-30  0:47 UTC (permalink / raw)
  To: Philipp Zabel; +Cc: Laurent Pinchart, dri-devel, linux-fbdev, linux-media

Hi Philipp,

On Tuesday 27 August 2013 11:30:51 Philipp Zabel wrote:
> Hi Laurent,
> 
> I have another small issue with the graph helpers below:
> 
> Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
> [...]
> 
> > +/*
> > -------------------------------------------------------------------------
> > ----> 
> >   * Graph Helpers
> >   */
> > 
> > @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev,
> > struct list_head *entities)> 
> >  }
> >  EXPORT_SYMBOL_GPL(display_entity_link_graph);
> > 
> > +#ifdef CONFIG_OF
> > +
> > +static int display_of_entity_link_entity(struct device *dev,
> > +					 struct display_entity *entity,
> > +					 struct list_head *entities,
> > +					 struct display_entity *root)
> > +{
> > +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> > +	const struct device_node *node = entity->dev->of_node;
> > +	struct media_entity *local = &entity->entity;
> > +	struct device_node *ep = NULL;
> > +	int ret = 0;
> > +
> > +	dev_dbg(dev, "creating links for entity %s\n", local->name);
> > +
> > +	while (1) {
> > +		struct media_entity *remote = NULL;
> > +		struct media_pad *remote_pad;
> > +		struct media_pad *local_pad;
> > +		struct display_of_link link;
> > +		struct display_entity *ent;
> > +		struct device_node *next;
> > +
> > +		/* Get the next endpoint and parse its link. */
> > +		next = display_of_get_next_endpoint(node, ep);
> > +		if (next == NULL)
> > +			break;
> > +
> > +		of_node_put(ep);
> > +		ep = next;
> > +
> > +		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
> > +
> > +		ret = display_of_parse_link(ep, &link);
> > +		if (ret < 0) {
> > +			dev_err(dev, "failed to parse link for %s\n",
> > +				ep->full_name);
> > +			continue;
> > +		}
> > +
> > +		/* Skip source pads, they will be processed from the other end of
> > +		 * the link.
> > +		 */
> > +		if (link.local_port >= local->num_pads) {
> > +			dev_err(dev, "invalid port number %u on %s\n",
> > +				link.local_port, link.local_node->full_name);
> > +			display_of_put_link(&link);
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		local_pad = &local->pads[link.local_port];
> > +
> > +		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
> > +			dev_dbg(dev, "skipping source port %s:%u\n",
> > +				link.local_node->full_name, link.local_port);
> > +			display_of_put_link(&link);
> > +			continue;
> > +		}
> > +
> > +		/* Find the remote entity. If not found, just skip the link as
> > +		 * it goes out of scope of the entities handled by the notifier.
> > +		 */
> > +		list_for_each_entry(ent, entities, list) {
> > +			if (ent->dev->of_node == link.remote_node) {
> > +				remote = &ent->entity;
> > +				break;
> > +			}
> > +		}
> > +
> > +		if (root->dev->of_node == link.remote_node)
> > +			remote = &root->entity;
> > +
> > +		if (remote == NULL) {
> > +			dev_dbg(dev, "no entity found for %s\n",
> > +				link.remote_node->full_name);
> > +			display_of_put_link(&link);
> > +			continue;
> > +		}
> > +
> > +		if (link.remote_port >= remote->num_pads) {
> > +			dev_err(dev, "invalid port number %u on %s\n",
> > +				link.remote_port, link.remote_node->full_name);
> > +			display_of_put_link(&link);
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		remote_pad = &remote->pads[link.remote_port];
> > +
> > +		display_of_put_link(&link);
> > +
> > +		/* Create the media link. */
> > +		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
> > +			remote->name, remote_pad->index,
> > +			local->name, local_pad->index);
> > +
> > +		ret = media_entity_create_link(remote, remote_pad->index,
> > +					       local, local_pad->index,
> > +					       link_flags);
> > +		if (ret < 0) {
> > +			dev_err(dev,
> > +				"failed to create %s:%u -> %s:%u link\n",
> > +				remote->name, remote_pad->index,
> > +				local->name, local_pad->index);
> > +			break;
> > +		}
> > +	}
> > +
> > +	of_node_put(ep);
> > +	return ret;
> > +}
> > +
> > +/**
> > + * display_of_entity_link_graph - Link all entities in a graph
> > + * @dev: device used to print debugging and error messages
> > + * @root: optional root display entity
> > + * @entities: list of display entities in the graph
> > + *
> > + * This function creates media controller links for all entities in a
> > graph + * based on the device tree graph representation. It relies on all
> > entities + * having been instantiated from the device tree.
> > + *
> > + * The list of entities is typically taken directly from a display
> > notifier + * done list. It will thus not include any display entity not
> > handled by the + * notifier, such as entities directly accessible by the
> > caller without going + * through the notification process. The optional
> > root entity parameter can be + * used to pass such a display entity and
> > include it in the graph. For all + * practical purpose the root entity is
> > handled is if it was part of the + * entities list.
> > + *
> > + * Return 0 on success or a negative error code otherwise.
> > + */
> > +int display_of_entity_link_graph(struct device *dev, struct list_head
> > *entities, +				 struct display_entity *root)
> > +{
> > +	struct display_entity *entity;
> > +	int ret;
> > +
> > +	list_for_each_entry(entity, entities, list) {
> > +		if (WARN_ON(entity->match->type != DISPLAY_ENTITY_BUS_DT))
> > +			return -EINVAL;
> > +
> > +		ret = display_of_entity_link_entity(dev, entity, entities,
> > +						    root);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +
> > +	return display_of_entity_link_entity(dev, root, entities, root);
> > +}
> > +EXPORT_SYMBOL_GPL(display_of_entity_link_graph);
> 
> The root display entity given to display_of_entity_link_graph is documented
> to be optional. Therefore, do not try to dereference root if it is NULL:

Good catch ! I'll fix it in v4, thank you.

> diff --git a/drivers/video/display/display-core.c
> b/drivers/video/display/display-core.c index 328ead7..6c8094f 100644
> --- a/drivers/video/display/display-core.c
> +++ b/drivers/video/display/display-core.c
> @@ -669,7 +669,7 @@ static int display_of_entity_link_entity(struct device
> *dev, }
>  		}
> 
> -		if (root->dev->of_node == link.remote_node)
> +		if (root && root->dev->of_node == link.remote_node)
>  			remote = &root->entity;
> 
>  		if (remote == NULL) {
> @@ -748,6 +748,9 @@ int display_of_entity_link_graph(struct device *dev,
> struct list_head *entities, return ret;
>  	}
> 
> +	if (!root)
> +		return 0;
> +
>  	return display_of_entity_link_entity(dev, root, entities, root);
>  }
>  EXPORT_SYMBOL_GPL(display_of_entity_link_graph);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-08-09 23:03 ` [PATCH/RFC v3 06/19] video: display: OF support Laurent Pinchart
  2013-08-27  9:30   ` Philipp Zabel
@ 2013-09-04 14:21   ` Philipp Zabel
  2013-09-11 11:33     ` Laurent Pinchart
  1 sibling, 1 reply; 26+ messages in thread
From: Philipp Zabel @ 2013-09-04 14:21 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: dri-devel, linux-fbdev, linux-media

Hi Laurent,

Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
> Extend the notifier with DT node matching support, and add helper functions to
> build the notifier and link entities based on a graph representation in
> DT.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> ---
>  drivers/video/display/display-core.c     | 334 +++++++++++++++++++++++++++++++
>  drivers/video/display/display-notifier.c | 187 +++++++++++++++++
>  include/video/display.h                  |  45 +++++
>  3 files changed, 566 insertions(+)
> 
> diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
> index c3b47d3..328ead7 100644
> --- a/drivers/video/display/display-core.c
> +++ b/drivers/video/display/display-core.c
[...]
> @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev, struct list_head *entities)
>  }
>  EXPORT_SYMBOL_GPL(display_entity_link_graph);
>  
> +#ifdef CONFIG_OF
> +
> +static int display_of_entity_link_entity(struct device *dev,
> +					 struct display_entity *entity,
> +					 struct list_head *entities,
> +					 struct display_entity *root)
> +{
> +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> +	const struct device_node *node = entity->dev->of_node;

the current device tree matching implementation only allows one display
entity per linux device. How about adding an of_node pointer to struct
display_entity directly and allow multiple display entity nodes below in
a single device node in the device tree?

lvds-encoder {
	channel@0 {
		port@0 {
			lvds0_input: endpoint {
			};
		};
		port@1 {
			lvds0_output: endpoint {
			};
		};
	};
	channel@1 {
		port@0 {
			lvds1_input: endpoint {
			};
		};
		lvds1: port@1 {
			lvds1_output: endpoint {
			};
		};
	};
};

> +	struct media_entity *local = &entity->entity;
> +	struct device_node *ep = NULL;
> +	int ret = 0;
> +
> +	dev_dbg(dev, "creating links for entity %s\n", local->name);
> +
> +	while (1) {
> +		struct media_entity *remote = NULL;
> +		struct media_pad *remote_pad;
> +		struct media_pad *local_pad;
> +		struct display_of_link link;
> +		struct display_entity *ent;
> +		struct device_node *next;
> +
> +		/* Get the next endpoint and parse its link. */
> +		next = display_of_get_next_endpoint(node, ep);
> +		if (next == NULL)
> +			break;
> +
> +		of_node_put(ep);
> +		ep = next;
> +
> +		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
> +
> +		ret = display_of_parse_link(ep, &link);
> +		if (ret < 0) {
> +			dev_err(dev, "failed to parse link for %s\n",
> +				ep->full_name);
> +			continue;
> +		}
> +
> +		/* Skip source pads, they will be processed from the other end of
> +		 * the link.
> +		 */
> +		if (link.local_port >= local->num_pads) {
> +			dev_err(dev, "invalid port number %u on %s\n",
> +				link.local_port, link.local_node->full_name);
> +			display_of_put_link(&link);
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		local_pad = &local->pads[link.local_port];
> +
> +		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
> +			dev_dbg(dev, "skipping source port %s:%u\n",
> +				link.local_node->full_name, link.local_port);
> +			display_of_put_link(&link);
> +			continue;
> +		}
> +
> +		/* Find the remote entity. If not found, just skip the link as
> +		 * it goes out of scope of the entities handled by the notifier.
> +		 */
> +		list_for_each_entry(ent, entities, list) {
> +			if (ent->dev->of_node == link.remote_node) {
> +				remote = &ent->entity;
> +				break;
> +			}
> +		}
> +
> +		if (root->dev->of_node == link.remote_node)
> +			remote = &root->entity;
> +
> +		if (remote == NULL) {
> +			dev_dbg(dev, "no entity found for %s\n",
> +				link.remote_node->full_name);
> +			display_of_put_link(&link);
> +			continue;
> +		}
> +
> +		if (link.remote_port >= remote->num_pads) {
> +			dev_err(dev, "invalid port number %u on %s\n",
> +				link.remote_port, link.remote_node->full_name);
> +			display_of_put_link(&link);
> +			ret = -EINVAL;
> +			break;
> +		}
> +
> +		remote_pad = &remote->pads[link.remote_port];
> +
> +		display_of_put_link(&link);
> +
> +		/* Create the media link. */
> +		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
> +			remote->name, remote_pad->index,
> +			local->name, local_pad->index);
> +
> +		ret = media_entity_create_link(remote, remote_pad->index,
> +					       local, local_pad->index,
> +					       link_flags);
> +		if (ret < 0) {
> +			dev_err(dev,
> +				"failed to create %s:%u -> %s:%u link\n",
> +				remote->name, remote_pad->index,
> +				local->name, local_pad->index);
> +			break;
> +		}
> +	}
> +
> +	of_node_put(ep);
> +	return ret;
> +}
[...]

For example like this:

diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 7910c23..a04feed 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -302,6 +302,9 @@ int display_entity_init(struct display_entity *entity, unsigned int num_sinks,
 	kref_init(&entity->ref);
 	entity->state = DISPLAY_ENTITY_STATE_OFF;
 
+	if (!entity->of_node && entity->dev)
+		entity->of_node = entity->dev->of_node;
+
 	num_pads = num_sinks + num_sources;
 	pads = kzalloc(sizeof(*pads) * num_pads, GFP_KERNEL);
 	if (pads == NULL)
@@ -665,7 +668,7 @@ static int display_of_entity_link_entity(struct device *dev,
 					 struct display_entity *root)
 {
 	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
-	const struct device_node *node = entity->dev->of_node;
+	const struct device_node *node = entity->of_node;
 	struct media_entity *local = &entity->entity;
 	struct device_node *ep = NULL;
 	int num_sink, ret = 0;
@@ -727,13 +730,13 @@ static int display_of_entity_link_entity(struct device *dev,
 		 * it goes out of scope of the entities handled by the notifier.
 		 */
 		list_for_each_entry(ent, entities, list) {
-			if (ent->dev->of_node == link.remote_node) {
+			if (ent->of_node == link.remote_node) {
 				remote = &ent->entity;
 				break;
 			}
 		}
 
-		if (root && root->dev->of_node == link.remote_node)
+		if (root && root->of_node == link.remote_node)
 			remote = &root->entity;
 
 		if (remote == NULL) {
diff --git a/drivers/video/display/display-notifier.c b/drivers/video/display/display-notifier.c
index a3998c7..d0da6e5 100644
--- a/drivers/video/display/display-notifier.c
+++ b/drivers/video/display/display-notifier.c
@@ -28,28 +28,30 @@ static DEFINE_MUTEX(display_entity_mutex);
  * Notifiers
  */
 
-static bool match_platform(struct device *dev,
+static bool match_platform(struct display_entity *entity,
 			   struct display_entity_match *match)
 {
 	pr_debug("%s: matching device '%s' with name '%s'\n", __func__,
-		 dev_name(dev), match->match.platform.name);
+		 dev_name(entity->dev), match->match.platform.name);
 
-	return !strcmp(match->match.platform.name, dev_name(dev));
+	return !strcmp(match->match.platform.name, dev_name(entity->dev));
 }
 
-static bool match_dt(struct device *dev, struct display_entity_match *match)
+static bool match_dt(struct display_entity *entity,
+		     struct display_entity_match *match)
 {
 	pr_debug("%s: matching device node '%s' with node '%s'\n", __func__,
-		 dev->of_node->full_name, match->match.dt.node->full_name);
+		 entity->of_node->full_name, match->match.dt.node->full_name);
 
-	return match->match.dt.node == dev->of_node;
+	return match->match.dt.node == entity->of_node;
 }
 
 static struct display_entity_match *
 display_entity_notifier_match(struct display_entity_notifier *notifier,
 			      struct display_entity *entity)
 {
-	bool (*match_func)(struct device *, struct display_entity_match *);
+	bool (*match_func)(struct display_entity *,
+			   struct display_entity_match *);
 	struct display_entity_match *match;
 
 	pr_debug("%s: matching entity '%s' (ptr 0x%p dev '%s')\n", __func__,
@@ -66,7 +68,7 @@ display_entity_notifier_match(struct display_entity_notifier *notifier,
 			break;
 		}
 
-		if (match_func(entity->dev, match))
+		if (match_func(entity, match))
 			return match;
 	}
 
diff --git a/include/video/display.h b/include/video/display.h
index 4c402bee..d1f8833 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -228,6 +228,7 @@ struct display_entity {
 	struct list_head list;
 	struct device *dev;
 	struct module *owner;
+	struct device_node *of_node;
 	struct kref ref;
 
 	char name[32];
-- 
1.8.4.rc3

regards
Philipp


^ permalink raw reply related	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-09-04 14:21   ` Philipp Zabel
@ 2013-09-11 11:33     ` Laurent Pinchart
  2013-09-11 13:14       ` Philipp Zabel
  2013-09-11 13:48       ` Hans Verkuil
  0 siblings, 2 replies; 26+ messages in thread
From: Laurent Pinchart @ 2013-09-11 11:33 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: Laurent Pinchart, dri-devel, linux-fbdev, linux-media,
	Guennadi Liakhovetski, Hans Verkuil, sylvester.nawrocki,
	sakari.ailus

Hi Philipp,

On Wednesday 04 September 2013 16:21:38 Philipp Zabel wrote:
> Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
> > Extend the notifier with DT node matching support, and add helper
> > functions to build the notifier and link entities based on a graph
> > representation in DT.
> > 
> > Signed-off-by: Laurent Pinchart
> > <laurent.pinchart+renesas@ideasonboard.com>
> > ---
> > 
> >  drivers/video/display/display-core.c     | 334 ++++++++++++++++++++++++++
> >  drivers/video/display/display-notifier.c | 187 +++++++++++++++++
> >  include/video/display.h                  |  45 +++++
> >  3 files changed, 566 insertions(+)
> > 
> > diff --git a/drivers/video/display/display-core.c
> > b/drivers/video/display/display-core.c index c3b47d3..328ead7 100644
> > --- a/drivers/video/display/display-core.c
> > +++ b/drivers/video/display/display-core.c
> 
> [...]
> 
> > @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev,
> > struct list_head *entities)> 
> >  }
> >  EXPORT_SYMBOL_GPL(display_entity_link_graph);
> > 
> > +#ifdef CONFIG_OF
> > +
> > +static int display_of_entity_link_entity(struct device *dev,
> > +					 struct display_entity *entity,
> > +					 struct list_head *entities,
> > +					 struct display_entity *root)
> > +{
> > +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> > +	const struct device_node *node = entity->dev->of_node;
> 
> the current device tree matching implementation only allows one display
> entity per linux device. How about adding an of_node pointer to struct
> display_entity directly and allow multiple display entity nodes below in a
> single device node in the device tree?

That's a very good point. We had a similar issues in V4L2, with sensors that 
would create several entities. However, in those cases, the sensors would be 
connected to the rest of the pipeline through a single entity :

Sensor Entity 1 -> ... -> Sensor Entity N -> V4L2 pipeline ...

The core code thus had to care about a single sensor entity when building the 
pipeline. We could solve the problem in a similar way for panels, but encoders 
need a more elaborate solution.

I see (at least) two possibilities here, either explicitly describing all 
entities that make the device in DT (as you have proposed below), or creating 
a hierarchy of entities, with parent entities that can contain several child 
entities. I've CC'ed Guennadi, Hans, Sylwester and Sakari to get their opinion 
on the matter.

> lvds-encoder {
> 	channel@0 {

If I understand this correctly, your LVDS encoder has two independent 
channels. In the general case a device made of multiple entities might have 
those entities chained, so "channel" might not be the best term. "entity" 
might be a better choice.

> 		port@0 {
> 			lvds0_input: endpoint {
> 			};
> 		};
> 		port@1 {
> 			lvds0_output: endpoint {
> 			};
> 		};
> 	};
> 	channel@1 {
> 		port@0 {
> 			lvds1_input: endpoint {
> 			};
> 		};
> 		lvds1: port@1 {
> 			lvds1_output: endpoint {
> 			};
> 		};
> 	};
> };
> 
> > +	struct media_entity *local = &entity->entity;
> > +	struct device_node *ep = NULL;
> > +	int ret = 0;
> > +
> > +	dev_dbg(dev, "creating links for entity %s\n", local->name);
> > +
> > +	while (1) {
> > +		struct media_entity *remote = NULL;
> > +		struct media_pad *remote_pad;
> > +		struct media_pad *local_pad;
> > +		struct display_of_link link;
> > +		struct display_entity *ent;
> > +		struct device_node *next;
> > +
> > +		/* Get the next endpoint and parse its link. */
> > +		next = display_of_get_next_endpoint(node, ep);
> > +		if (next == NULL)
> > +			break;
> > +
> > +		of_node_put(ep);
> > +		ep = next;
> > +
> > +		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
> > +
> > +		ret = display_of_parse_link(ep, &link);
> > +		if (ret < 0) {
> > +			dev_err(dev, "failed to parse link for %s\n",
> > +				ep->full_name);
> > +			continue;
> > +		}
> > +
> > +		/* Skip source pads, they will be processed from the other end of
> > +		 * the link.
> > +		 */
> > +		if (link.local_port >= local->num_pads) {
> > +			dev_err(dev, "invalid port number %u on %s\n",
> > +				link.local_port, link.local_node->full_name);
> > +			display_of_put_link(&link);
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		local_pad = &local->pads[link.local_port];
> > +
> > +		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
> > +			dev_dbg(dev, "skipping source port %s:%u\n",
> > +				link.local_node->full_name, link.local_port);
> > +			display_of_put_link(&link);
> > +			continue;
> > +		}
> > +
> > +		/* Find the remote entity. If not found, just skip the link as
> > +		 * it goes out of scope of the entities handled by the notifier.
> > +		 */
> > +		list_for_each_entry(ent, entities, list) {
> > +			if (ent->dev->of_node == link.remote_node) {
> > +				remote = &ent->entity;
> > +				break;
> > +			}
> > +		}
> > +
> > +		if (root->dev->of_node == link.remote_node)
> > +			remote = &root->entity;
> > +
> > +		if (remote == NULL) {
> > +			dev_dbg(dev, "no entity found for %s\n",
> > +				link.remote_node->full_name);
> > +			display_of_put_link(&link);
> > +			continue;
> > +		}
> > +
> > +		if (link.remote_port >= remote->num_pads) {
> > +			dev_err(dev, "invalid port number %u on %s\n",
> > +				link.remote_port, link.remote_node->full_name);
> > +			display_of_put_link(&link);
> > +			ret = -EINVAL;
> > +			break;
> > +		}
> > +
> > +		remote_pad = &remote->pads[link.remote_port];
> > +
> > +		display_of_put_link(&link);
> > +
> > +		/* Create the media link. */
> > +		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
> > +			remote->name, remote_pad->index,
> > +			local->name, local_pad->index);
> > +
> > +		ret = media_entity_create_link(remote, remote_pad->index,
> > +					       local, local_pad->index,
> > +					       link_flags);
> > +		if (ret < 0) {
> > +			dev_err(dev,
> > +				"failed to create %s:%u -> %s:%u link\n",
> > +				remote->name, remote_pad->index,
> > +				local->name, local_pad->index);
> > +			break;
> > +		}
> > +	}
> > +
> > +	of_node_put(ep);
> > +	return ret;
> > +}
> 
> [...]
> 
> For example like this:
> 
> diff --git a/drivers/video/display/display-core.c
> b/drivers/video/display/display-core.c index 7910c23..a04feed 100644
> --- a/drivers/video/display/display-core.c
> +++ b/drivers/video/display/display-core.c
> @@ -302,6 +302,9 @@ int display_entity_init(struct display_entity *entity,
> unsigned int num_sinks, kref_init(&entity->ref);
>  	entity->state = DISPLAY_ENTITY_STATE_OFF;
> 
> +	if (!entity->of_node && entity->dev)
> +		entity->of_node = entity->dev->of_node;
> +
>  	num_pads = num_sinks + num_sources;
>  	pads = kzalloc(sizeof(*pads) * num_pads, GFP_KERNEL);
>  	if (pads == NULL)
> @@ -665,7 +668,7 @@ static int display_of_entity_link_entity(struct device
> *dev, struct display_entity *root)
>  {
>  	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> -	const struct device_node *node = entity->dev->of_node;
> +	const struct device_node *node = entity->of_node;
>  	struct media_entity *local = &entity->entity;
>  	struct device_node *ep = NULL;
>  	int num_sink, ret = 0;
> @@ -727,13 +730,13 @@ static int display_of_entity_link_entity(struct device
> *dev, * it goes out of scope of the entities handled by the notifier. */
>  		list_for_each_entry(ent, entities, list) {
> -			if (ent->dev->of_node == link.remote_node) {
> +			if (ent->of_node == link.remote_node) {
>  				remote = &ent->entity;
>  				break;
>  			}
>  		}
> 
> -		if (root && root->dev->of_node == link.remote_node)
> +		if (root && root->of_node == link.remote_node)
>  			remote = &root->entity;
> 
>  		if (remote == NULL) {
> diff --git a/drivers/video/display/display-notifier.c
> b/drivers/video/display/display-notifier.c index a3998c7..d0da6e5 100644
> --- a/drivers/video/display/display-notifier.c
> +++ b/drivers/video/display/display-notifier.c
> @@ -28,28 +28,30 @@ static DEFINE_MUTEX(display_entity_mutex);
>   * Notifiers
>   */
> 
> -static bool match_platform(struct device *dev,
> +static bool match_platform(struct display_entity *entity,
>  			   struct display_entity_match *match)
>  {
>  	pr_debug("%s: matching device '%s' with name '%s'\n", __func__,
> -		 dev_name(dev), match->match.platform.name);
> +		 dev_name(entity->dev), match->match.platform.name);
> 
> -	return !strcmp(match->match.platform.name, dev_name(dev));
> +	return !strcmp(match->match.platform.name, dev_name(entity->dev));
>  }
> 
> -static bool match_dt(struct device *dev, struct display_entity_match
> *match) +static bool match_dt(struct display_entity *entity,
> +		     struct display_entity_match *match)
>  {
>  	pr_debug("%s: matching device node '%s' with node '%s'\n", __func__,
> -		 dev->of_node->full_name, match->match.dt.node->full_name);
> +		 entity->of_node->full_name, match->match.dt.node->full_name);
> 
> -	return match->match.dt.node == dev->of_node;
> +	return match->match.dt.node == entity->of_node;
>  }
> 
>  static struct display_entity_match *
>  display_entity_notifier_match(struct display_entity_notifier *notifier,
>  			      struct display_entity *entity)
>  {
> -	bool (*match_func)(struct device *, struct display_entity_match *);
> +	bool (*match_func)(struct display_entity *,
> +			   struct display_entity_match *);
>  	struct display_entity_match *match;
> 
>  	pr_debug("%s: matching entity '%s' (ptr 0x%p dev '%s')\n", __func__,
> @@ -66,7 +68,7 @@ display_entity_notifier_match(struct
> display_entity_notifier *notifier, break;
>  		}
> 
> -		if (match_func(entity->dev, match))
> +		if (match_func(entity, match))
>  			return match;
>  	}
> 
> diff --git a/include/video/display.h b/include/video/display.h
> index 4c402bee..d1f8833 100644
> --- a/include/video/display.h
> +++ b/include/video/display.h
> @@ -228,6 +228,7 @@ struct display_entity {
>  	struct list_head list;
>  	struct device *dev;
>  	struct module *owner;
> +	struct device_node *of_node;
>  	struct kref ref;
> 
>  	char name[32];
-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-09-11 11:33     ` Laurent Pinchart
@ 2013-09-11 13:14       ` Philipp Zabel
  2013-09-11 13:48       ` Hans Verkuil
  1 sibling, 0 replies; 26+ messages in thread
From: Philipp Zabel @ 2013-09-11 13:14 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Laurent Pinchart, dri-devel, linux-fbdev, linux-media,
	Guennadi Liakhovetski, Hans Verkuil, sylvester.nawrocki,
	sakari.ailus

Am Mittwoch, den 11.09.2013, 13:33 +0200 schrieb Laurent Pinchart:
> Hi Philipp,
> 
> On Wednesday 04 September 2013 16:21:38 Philipp Zabel wrote:
> > Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
> > > Extend the notifier with DT node matching support, and add helper
> > > functions to build the notifier and link entities based on a graph
> > > representation in DT.
> > > 
> > > Signed-off-by: Laurent Pinchart
> > > <laurent.pinchart+renesas@ideasonboard.com>
> > > ---
> > > 
> > >  drivers/video/display/display-core.c     | 334 ++++++++++++++++++++++++++
> > >  drivers/video/display/display-notifier.c | 187 +++++++++++++++++
> > >  include/video/display.h                  |  45 +++++
> > >  3 files changed, 566 insertions(+)
> > > 
> > > diff --git a/drivers/video/display/display-core.c
> > > b/drivers/video/display/display-core.c index c3b47d3..328ead7 100644
> > > --- a/drivers/video/display/display-core.c
> > > +++ b/drivers/video/display/display-core.c
> > 
> > [...]
> > 
> > > @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev,
> > > struct list_head *entities)> 
> > >  }
> > >  EXPORT_SYMBOL_GPL(display_entity_link_graph);
> > > 
> > > +#ifdef CONFIG_OF
> > > +
> > > +static int display_of_entity_link_entity(struct device *dev,
> > > +					 struct display_entity *entity,
> > > +					 struct list_head *entities,
> > > +					 struct display_entity *root)
> > > +{
> > > +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
> > > +	const struct device_node *node = entity->dev->of_node;
> > 
> > the current device tree matching implementation only allows one display
> > entity per linux device. How about adding an of_node pointer to struct
> > display_entity directly and allow multiple display entity nodes below in a
> > single device node in the device tree?
> 
> That's a very good point. We had a similar issues in V4L2, with sensors that 
> would create several entities. However, in those cases, the sensors would be 
> connected to the rest of the pipeline through a single entity :
> 
> Sensor Entity 1 -> ... -> Sensor Entity N -> V4L2 pipeline ...
> 
> The core code thus had to care about a single sensor entity when building the 
> pipeline. We could solve the problem in a similar way for panels, but encoders 
> need a more elaborate solution.
> 
> I see (at least) two possibilities here, either explicitly describing all 
> entities that make the device in DT (as you have proposed below), or creating 
> a hierarchy of entities, with parent entities that can contain several child 
> entities. I've CC'ed Guennadi, Hans, Sylwester and Sakari to get their opinion 
> on the matter.

When you say hierarchy of entities, I imagine something like GStreamer
bins? I suspect hierarchically encapsulated entities would complicate
the pipeline/graph traversal code quite a bit, although it would
probably help to organise the graph and reduce the amount of boilerplate
needed in the device tree.

> > lvds-encoder {
> > 	channel@0 {
> 
> If I understand this correctly, your LVDS encoder has two independent 
> channels.

In this example, yes. In reality the i.MX LDB has a mux in each path, so
both inputs can be routed to both outputs. With an entity hierarchy this
could be described as a single entity with two inputs and two outputs,
containing two multiplexer entites and two encoder entities.

LDB entity with two input pads, four internal entities, and two output
pads:
  ,----------------------------------------.
  |-----.  LDB   ,------.  ,------.  ,-----|
--| pad |--------| mux0 |--| enc0 |--| pad |--
  |  0  |--.  ,--|      |  |      |  |  2  |
  |-----´   \/   `------´  `------´  `-----|
  |-----.   /\   ,------.  ,------.  .-----|
--| pad |--´  `--| mux1 |  | enc1 |--| pad |--
  |  1  |--------|      |--|      |  |  3  |
  |-----´        `------´  `------´  `-----|
  `----------------------------------------´
(In guess the mux and enc entities could each be combined into one)

> In the general case a device made of multiple entities might have 
> those entities chained, so "channel" might not be the best term.
> "entity" might be a better choice.

On the other hand, when describing subdevice entities in the device
tree, maybe the generic type of entity (sensor, scaler, encoder, mux,
etc.) would be useful information?

Another module where I'd like to describe the (outward facing) contained
entities in the device tree is the i.MX Image Processing Unit, which has
two capture interfaces and two display interfaces (all parallel). Those
can't be combined into a single entity because there are other internal
entities connected to them, and because the capture interfaces are v4l2
subdevices, whereas the display interfaces will be display entites.
Alternatively, this could also be described as a single entity
containing an internal structure.

IPU entity with two input pads, two internal capture entities (csi), two
display entities (di), and two output pads:
  ,----------------------------------------.
  |-----.  ,------.  IPU   ,------.  ,-----|
--| pad |--| csi0 |        | di0  |--| pad |--
  |  0  |  |      |...     |      |  |  2  |
  |-----´  `------´        `------´  `-----|
  |-----.  ,------.        ,------.  .-----|
--| pad |--| csi1 |        | di1  |--| pad |--
  |  1  |  |      |...     |      |  |  3  |
  |-----´  `------´        `------´  `-----|
  `----------------------------------------´

regards
Philipp


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [PATCH/RFC v3 06/19] video: display: OF support
  2013-09-11 11:33     ` Laurent Pinchart
  2013-09-11 13:14       ` Philipp Zabel
@ 2013-09-11 13:48       ` Hans Verkuil
  1 sibling, 0 replies; 26+ messages in thread
From: Hans Verkuil @ 2013-09-11 13:48 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Philipp Zabel, Laurent Pinchart, dri-devel, linux-fbdev,
	linux-media, Guennadi Liakhovetski, sylvester.nawrocki,
	sakari.ailus



On 09/11/2013 01:33 PM, Laurent Pinchart wrote:
> Hi Philipp,
> 
> On Wednesday 04 September 2013 16:21:38 Philipp Zabel wrote:
>> Am Samstag, den 10.08.2013, 01:03 +0200 schrieb Laurent Pinchart:
>>> Extend the notifier with DT node matching support, and add helper
>>> functions to build the notifier and link entities based on a graph
>>> representation in DT.
>>>
>>> Signed-off-by: Laurent Pinchart
>>> <laurent.pinchart+renesas@ideasonboard.com>
>>> ---
>>>
>>>  drivers/video/display/display-core.c     | 334 ++++++++++++++++++++++++++
>>>  drivers/video/display/display-notifier.c | 187 +++++++++++++++++
>>>  include/video/display.h                  |  45 +++++
>>>  3 files changed, 566 insertions(+)
>>>
>>> diff --git a/drivers/video/display/display-core.c
>>> b/drivers/video/display/display-core.c index c3b47d3..328ead7 100644
>>> --- a/drivers/video/display/display-core.c
>>> +++ b/drivers/video/display/display-core.c
>>
>> [...]
>>
>>> @@ -420,6 +599,161 @@ int display_entity_link_graph(struct device *dev,
>>> struct list_head *entities)> 
>>>  }
>>>  EXPORT_SYMBOL_GPL(display_entity_link_graph);
>>>
>>> +#ifdef CONFIG_OF
>>> +
>>> +static int display_of_entity_link_entity(struct device *dev,
>>> +					 struct display_entity *entity,
>>> +					 struct list_head *entities,
>>> +					 struct display_entity *root)
>>> +{
>>> +	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
>>> +	const struct device_node *node = entity->dev->of_node;
>>
>> the current device tree matching implementation only allows one display
>> entity per linux device. How about adding an of_node pointer to struct
>> display_entity directly and allow multiple display entity nodes below in a
>> single device node in the device tree?
> 
> That's a very good point. We had a similar issues in V4L2, with sensors that 
> would create several entities. However, in those cases, the sensors would be 
> connected to the rest of the pipeline through a single entity :
> 
> Sensor Entity 1 -> ... -> Sensor Entity N -> V4L2 pipeline ...
> 
> The core code thus had to care about a single sensor entity when building the 
> pipeline. We could solve the problem in a similar way for panels, but encoders 
> need a more elaborate solution.

Why? Sorry, I don't see why an encoder is different in this respect than a panel.
I'm sure I'm missing something here.

> 
> I see (at least) two possibilities here, either explicitly describing all 
> entities that make the device in DT (as you have proposed below), or creating 
> a hierarchy of entities, with parent entities that can contain several child 
> entities. I've CC'ed Guennadi, Hans, Sylwester and Sakari to get their opinion 
> on the matter.

I think the way this is done today in complex devices is that the driver just
exposes itself as a single sub-device, but internally it has its own pipeline of
sub-devices. The only one that I know of (platform/s5p-tv/hdmi_drv) doesn't expose
them to a media controller, they are completely hidden inside the hdmi driver.

The ability to support hierarchies of entities would be very nice. However, I
don't know how much work that would be to implement and if it is worth the
effort.

Regards,

	Hans

> 
>> lvds-encoder {
>> 	channel@0 {
> 
> If I understand this correctly, your LVDS encoder has two independent 
> channels. In the general case a device made of multiple entities might have 
> those entities chained, so "channel" might not be the best term. "entity" 
> might be a better choice.
> 
>> 		port@0 {
>> 			lvds0_input: endpoint {
>> 			};
>> 		};
>> 		port@1 {
>> 			lvds0_output: endpoint {
>> 			};
>> 		};
>> 	};
>> 	channel@1 {
>> 		port@0 {
>> 			lvds1_input: endpoint {
>> 			};
>> 		};
>> 		lvds1: port@1 {
>> 			lvds1_output: endpoint {
>> 			};
>> 		};
>> 	};
>> };
>>
>>> +	struct media_entity *local = &entity->entity;
>>> +	struct device_node *ep = NULL;
>>> +	int ret = 0;
>>> +
>>> +	dev_dbg(dev, "creating links for entity %s\n", local->name);
>>> +
>>> +	while (1) {
>>> +		struct media_entity *remote = NULL;
>>> +		struct media_pad *remote_pad;
>>> +		struct media_pad *local_pad;
>>> +		struct display_of_link link;
>>> +		struct display_entity *ent;
>>> +		struct device_node *next;
>>> +
>>> +		/* Get the next endpoint and parse its link. */
>>> +		next = display_of_get_next_endpoint(node, ep);
>>> +		if (next == NULL)
>>> +			break;
>>> +
>>> +		of_node_put(ep);
>>> +		ep = next;
>>> +
>>> +		dev_dbg(dev, "processing endpoint %s\n", ep->full_name);
>>> +
>>> +		ret = display_of_parse_link(ep, &link);
>>> +		if (ret < 0) {
>>> +			dev_err(dev, "failed to parse link for %s\n",
>>> +				ep->full_name);
>>> +			continue;
>>> +		}
>>> +
>>> +		/* Skip source pads, they will be processed from the other end of
>>> +		 * the link.
>>> +		 */
>>> +		if (link.local_port >= local->num_pads) {
>>> +			dev_err(dev, "invalid port number %u on %s\n",
>>> +				link.local_port, link.local_node->full_name);
>>> +			display_of_put_link(&link);
>>> +			ret = -EINVAL;
>>> +			break;
>>> +		}
>>> +
>>> +		local_pad = &local->pads[link.local_port];
>>> +
>>> +		if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
>>> +			dev_dbg(dev, "skipping source port %s:%u\n",
>>> +				link.local_node->full_name, link.local_port);
>>> +			display_of_put_link(&link);
>>> +			continue;
>>> +		}
>>> +
>>> +		/* Find the remote entity. If not found, just skip the link as
>>> +		 * it goes out of scope of the entities handled by the notifier.
>>> +		 */
>>> +		list_for_each_entry(ent, entities, list) {
>>> +			if (ent->dev->of_node == link.remote_node) {
>>> +				remote = &ent->entity;
>>> +				break;
>>> +			}
>>> +		}
>>> +
>>> +		if (root->dev->of_node == link.remote_node)
>>> +			remote = &root->entity;
>>> +
>>> +		if (remote == NULL) {
>>> +			dev_dbg(dev, "no entity found for %s\n",
>>> +				link.remote_node->full_name);
>>> +			display_of_put_link(&link);
>>> +			continue;
>>> +		}
>>> +
>>> +		if (link.remote_port >= remote->num_pads) {
>>> +			dev_err(dev, "invalid port number %u on %s\n",
>>> +				link.remote_port, link.remote_node->full_name);
>>> +			display_of_put_link(&link);
>>> +			ret = -EINVAL;
>>> +			break;
>>> +		}
>>> +
>>> +		remote_pad = &remote->pads[link.remote_port];
>>> +
>>> +		display_of_put_link(&link);
>>> +
>>> +		/* Create the media link. */
>>> +		dev_dbg(dev, "creating %s:%u -> %s:%u link\n",
>>> +			remote->name, remote_pad->index,
>>> +			local->name, local_pad->index);
>>> +
>>> +		ret = media_entity_create_link(remote, remote_pad->index,
>>> +					       local, local_pad->index,
>>> +					       link_flags);
>>> +		if (ret < 0) {
>>> +			dev_err(dev,
>>> +				"failed to create %s:%u -> %s:%u link\n",
>>> +				remote->name, remote_pad->index,
>>> +				local->name, local_pad->index);
>>> +			break;
>>> +		}
>>> +	}
>>> +
>>> +	of_node_put(ep);
>>> +	return ret;
>>> +}
>>
>> [...]
>>
>> For example like this:
>>
>> diff --git a/drivers/video/display/display-core.c
>> b/drivers/video/display/display-core.c index 7910c23..a04feed 100644
>> --- a/drivers/video/display/display-core.c
>> +++ b/drivers/video/display/display-core.c
>> @@ -302,6 +302,9 @@ int display_entity_init(struct display_entity *entity,
>> unsigned int num_sinks, kref_init(&entity->ref);
>>  	entity->state = DISPLAY_ENTITY_STATE_OFF;
>>
>> +	if (!entity->of_node && entity->dev)
>> +		entity->of_node = entity->dev->of_node;
>> +
>>  	num_pads = num_sinks + num_sources;
>>  	pads = kzalloc(sizeof(*pads) * num_pads, GFP_KERNEL);
>>  	if (pads == NULL)
>> @@ -665,7 +668,7 @@ static int display_of_entity_link_entity(struct device
>> *dev, struct display_entity *root)
>>  {
>>  	u32 link_flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
>> -	const struct device_node *node = entity->dev->of_node;
>> +	const struct device_node *node = entity->of_node;
>>  	struct media_entity *local = &entity->entity;
>>  	struct device_node *ep = NULL;
>>  	int num_sink, ret = 0;
>> @@ -727,13 +730,13 @@ static int display_of_entity_link_entity(struct device
>> *dev, * it goes out of scope of the entities handled by the notifier. */
>>  		list_for_each_entry(ent, entities, list) {
>> -			if (ent->dev->of_node == link.remote_node) {
>> +			if (ent->of_node == link.remote_node) {
>>  				remote = &ent->entity;
>>  				break;
>>  			}
>>  		}
>>
>> -		if (root && root->dev->of_node == link.remote_node)
>> +		if (root && root->of_node == link.remote_node)
>>  			remote = &root->entity;
>>
>>  		if (remote == NULL) {
>> diff --git a/drivers/video/display/display-notifier.c
>> b/drivers/video/display/display-notifier.c index a3998c7..d0da6e5 100644
>> --- a/drivers/video/display/display-notifier.c
>> +++ b/drivers/video/display/display-notifier.c
>> @@ -28,28 +28,30 @@ static DEFINE_MUTEX(display_entity_mutex);
>>   * Notifiers
>>   */
>>
>> -static bool match_platform(struct device *dev,
>> +static bool match_platform(struct display_entity *entity,
>>  			   struct display_entity_match *match)
>>  {
>>  	pr_debug("%s: matching device '%s' with name '%s'\n", __func__,
>> -		 dev_name(dev), match->match.platform.name);
>> +		 dev_name(entity->dev), match->match.platform.name);
>>
>> -	return !strcmp(match->match.platform.name, dev_name(dev));
>> +	return !strcmp(match->match.platform.name, dev_name(entity->dev));
>>  }
>>
>> -static bool match_dt(struct device *dev, struct display_entity_match
>> *match) +static bool match_dt(struct display_entity *entity,
>> +		     struct display_entity_match *match)
>>  {
>>  	pr_debug("%s: matching device node '%s' with node '%s'\n", __func__,
>> -		 dev->of_node->full_name, match->match.dt.node->full_name);
>> +		 entity->of_node->full_name, match->match.dt.node->full_name);
>>
>> -	return match->match.dt.node == dev->of_node;
>> +	return match->match.dt.node == entity->of_node;
>>  }
>>
>>  static struct display_entity_match *
>>  display_entity_notifier_match(struct display_entity_notifier *notifier,
>>  			      struct display_entity *entity)
>>  {
>> -	bool (*match_func)(struct device *, struct display_entity_match *);
>> +	bool (*match_func)(struct display_entity *,
>> +			   struct display_entity_match *);
>>  	struct display_entity_match *match;
>>
>>  	pr_debug("%s: matching entity '%s' (ptr 0x%p dev '%s')\n", __func__,
>> @@ -66,7 +68,7 @@ display_entity_notifier_match(struct
>> display_entity_notifier *notifier, break;
>>  		}
>>
>> -		if (match_func(entity->dev, match))
>> +		if (match_func(entity, match))
>>  			return match;
>>  	}
>>
>> diff --git a/include/video/display.h b/include/video/display.h
>> index 4c402bee..d1f8833 100644
>> --- a/include/video/display.h
>> +++ b/include/video/display.h
>> @@ -228,6 +228,7 @@ struct display_entity {
>>  	struct list_head list;
>>  	struct device *dev;
>>  	struct module *owner;
>> +	struct device_node *of_node;
>>  	struct kref ref;
>>
>>  	char name[32];

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2013-09-11 13:48 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-09 23:02 [PATCH/RFC v3 00/19] Common Display Framework Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 01/19] OMAPDSS: panels: Rename Kconfig options to OMAP2_DISPLAY_* Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 02/19] video: Add Common Display Framework core Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 03/19] video: display: Add video and stream control operations Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 04/19] video: display: Add display entity notifier Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 05/19] video: display: Graph helpers Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 06/19] video: display: OF support Laurent Pinchart
2013-08-27  9:30   ` Philipp Zabel
2013-08-30  0:47     ` Laurent Pinchart
2013-09-04 14:21   ` Philipp Zabel
2013-09-11 11:33     ` Laurent Pinchart
2013-09-11 13:14       ` Philipp Zabel
2013-09-11 13:48       ` Hans Verkuil
2013-08-09 23:03 ` [PATCH/RFC v3 07/19] video: display: Add pixel coding definitions Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 08/19] video: display: Add MIPI DBI bus support Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 09/19] video: panel: Add DPI panel support Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 10/19] video: panel: Add R61505 " Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 11/19] video: panel: Add R61517 " Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 12/19] video: display: Add VGA Digital to Analog Converter support Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 13/19] video: display: Add VGA connector support Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 14/19] ARM: shmobile: r8a7790: Add DU clocks for DT Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 15/19] ARM: shmobile: r8a7790: Add DU device node to device tree Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 16/19] ARM: shmobile: marzen: Port DU platform data to CDF Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 17/19] ARM: shmobile: lager: " Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 18/19] ARM: shmobile: lager-reference: Add display device nodes to device tree Laurent Pinchart
2013-08-09 23:03 ` [PATCH/RFC v3 19/19] drm/rcar-du: Port to the Common Display Framework Laurent Pinchart

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).