* [RFC 01/10] video: Add generic display entity core
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 02/10] video: display: Update the display with the video mode data Pawel Moll
` (8 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Laurent Pinchart
From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
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 | 1 +
drivers/video/display/display-core.c | 362 ++++++++++++++++++++++++++++++++++
include/video/display.h | 150 ++++++++++++++
6 files changed, 519 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 4c1546f..281e548 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2456,6 +2456,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 9df3873..b989e8e 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..bd93496
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPLAY_CORE) += display-core.o
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..d2daa15
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+#include <video/display.h>
+#include <video/videomode.h>
+
+static LIST_HEAD(display_entity_list);
+static LIST_HEAD(display_entity_notifiers);
+static DEFINE_MUTEX(display_entity_mutex);
+
+/* -----------------------------------------------------------------------------
+ * Control operations
+ */
+
+/**
+ * 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
+ * @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,
+ const struct videomode **modes)
+{
+ if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+ return 0;
+
+ return entity->ops.ctrl->get_modes(entity, 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
+ * @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,
+ struct display_entity_interface_params *params)
+{
+ if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+ return -EOPNOTSUPP;
+
+ return entity->ops.ctrl->get_params(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+/* -----------------------------------------------------------------------------
+ * Video operations
+ */
+
+/**
+ * display_entity_set_stream - Control the video stream state
+ * @entity: The display entity
+ * @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,
+ enum display_entity_stream_state state)
+{
+ if (!entity->ops.video || !entity->ops.video->set_stream)
+ return 0;
+
+ return entity->ops.video->set_stream(entity, state);
+}
+EXPORT_SYMBOL_GPL(display_entity_set_stream);
+
+/* -----------------------------------------------------------------------------
+ * 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.
+ */
+
+/* -----------------------------------------------------------------------------
+ * Registration and notification
+ */
+
+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
+ * @display_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);
+
+static int display_entity_notifier_match(struct display_entity *entity,
+ struct display_entity_notifier *notifier)
+{
+ return notifier->dev == NULL || notifier->dev == entity->dev;
+}
+
+/**
+ * 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 *entity;
+
+ mutex_lock(&display_entity_mutex);
+ list_add_tail(¬ifier->list, &display_entity_notifiers);
+
+ list_for_each_entry(entity, &display_entity_list, list) {
+ if (!display_entity_notifier_match(entity, notifier))
+ continue;
+
+ if (notifier->notify(notifier, entity,
+ DISPLAY_ENTITY_NOTIFIER_CONNECT))
+ break;
+ }
+ mutex_unlock(&display_entity_mutex);
+
+ return 0;
+}
+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)
+{
+ mutex_lock(&display_entity_mutex);
+ list_del(¬ifier->list);
+ mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+
+/**
+ * display_entity_register - register a display entity
+ * @display_entity: display entity to be registered
+ *
+ * Register the display entity and send the DISPLAY_ENTITY_NOTIFIER_CONNECT
+ * event to all matching registered notifiers.
+ *
+ * Return 0 on success.
+ */
+int __must_check __display_entity_register(struct display_entity *entity,
+ struct module *owner)
+{
+ struct display_entity_notifier *notifier;
+
+ kref_init(&entity->ref);
+ entity->owner = owner;
+ entity->state = DISPLAY_ENTITY_STATE_OFF;
+
+ mutex_lock(&display_entity_mutex);
+ list_add(&entity->list, &display_entity_list);
+
+ list_for_each_entry(notifier, &display_entity_notifiers, list) {
+ if (!display_entity_notifier_match(entity, notifier))
+ continue;
+
+ if (notifier->notify(notifier, entity,
+ DISPLAY_ENTITY_NOTIFIER_CONNECT))
+ break;
+ }
+ mutex_unlock(&display_entity_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__display_entity_register);
+
+/**
+ * display_entity_unregister - unregister a display entity
+ * @display_entity: display entity to be unregistered
+ *
+ * Unregister the display entity and send the DISPLAY_ENTITY_NOTIFIER_DISCONNECT
+ * event to all matching registered notifiers.
+ */
+void display_entity_unregister(struct display_entity *entity)
+{
+ struct display_entity_notifier *notifier;
+
+ mutex_lock(&display_entity_mutex);
+ list_for_each_entry(notifier, &display_entity_notifiers, list) {
+ if (!display_entity_notifier_match(entity, notifier))
+ continue;
+
+ notifier->notify(notifier, entity,
+ DISPLAY_ENTITY_NOTIFIER_DISCONNECT);
+ }
+
+ list_del(&entity->list);
+ mutex_unlock(&display_entity_mutex);
+
+ display_entity_put(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..90d18ca
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,150 @@
+/*
+ * 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>
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+struct display_entity;
+struct videomode;
+
+#define DISPLAY_ENTITY_NOTIFIER_CONNECT 1
+#define DISPLAY_ENTITY_NOTIFIER_DISCONNECT 2
+
+struct display_entity_notifier {
+ int (*notify)(struct display_entity_notifier *, struct display_entity *,
+ int);
+ struct device *dev;
+ struct list_head list;
+};
+
+/**
+ * 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,
+};
+
+struct display_entity_interface_params {
+ enum display_entity_interface_type type;
+};
+
+struct display_entity_control_ops {
+ int (*set_state)(struct display_entity *ent,
+ enum display_entity_state state);
+ int (*update)(struct display_entity *ent);
+ int (*get_modes)(struct display_entity *ent,
+ const struct videomode **modes);
+ int (*get_params)(struct display_entity *ent,
+ struct display_entity_interface_params *params);
+ int (*get_size)(struct display_entity *ent,
+ unsigned int *width, unsigned int *height);
+};
+
+struct display_entity_video_ops {
+ int (*set_stream)(struct display_entity *ent,
+ enum display_entity_stream_state state);
+};
+
+struct display_entity {
+ struct list_head list;
+ struct device *dev;
+ struct module *owner;
+ struct kref ref;
+
+ struct display_entity *source;
+
+ struct {
+ const struct display_entity_control_ops *ctrl;
+ const struct display_entity_video_ops *video;
+ } ops;
+
+ void(*release)(struct display_entity *ent);
+
+ enum display_entity_state state;
+};
+
+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,
+ const struct videomode **modes);
+int display_entity_get_params(struct display_entity *entity,
+ struct display_entity_interface_params *params);
+int display_entity_get_size(struct display_entity *entity,
+ unsigned int *width, unsigned int *height);
+
+int display_entity_set_stream(struct display_entity *entity,
+ enum display_entity_stream_state state);
+
+static inline void display_entity_connect(struct display_entity *source,
+ struct display_entity *sink)
+{
+ sink->source = source;
+}
+
+static inline void display_entity_disconnect(struct display_entity *source,
+ struct display_entity *sink)
+{
+ sink->source = NULL;
+}
+
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+ struct module *owner);
+void display_entity_unregister(struct display_entity *entity);
+
+int display_entity_register_notifier(struct display_entity_notifier *notifier);
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier);
+
+#define display_entity_register(display_entity) \
+ __display_entity_register(display_entity, THIS_MODULE)
+
+#endif /* __DISPLAY_H__ */
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 02/10] video: display: Update the display with the video mode data
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
2013-04-17 15:17 ` [RFC 01/10] video: Add generic display entity core Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 03/10] video: display: Add Device Tree bindings Pawel Moll
` (7 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
The display entity (sink) may need to know about the mode being
changed, eg. to update timings.
Alternatively there could be a separate set_mode() operation...
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
drivers/video/display/display-core.c | 5 +++--
include/video/display.h | 6 ++++--
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index d2daa15..4b8e45a 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -69,12 +69,13 @@ EXPORT_SYMBOL_GPL(display_entity_set_state);
*
* Return 0 on success or a negative error code otherwise.
*/
-int display_entity_update(struct display_entity *entity)
+int display_entity_update(struct display_entity *entity,
+ const struct videomode *mode)
{
if (!entity->ops.ctrl || !entity->ops.ctrl->update)
return 0;
- return entity->ops.ctrl->update(entity);
+ return entity->ops.ctrl->update(entity, mode);
}
EXPORT_SYMBOL_GPL(display_entity_update);
diff --git a/include/video/display.h b/include/video/display.h
index 90d18ca..64f84d5 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -77,7 +77,8 @@ struct display_entity_interface_params {
struct display_entity_control_ops {
int (*set_state)(struct display_entity *ent,
enum display_entity_state state);
- int (*update)(struct display_entity *ent);
+ int (*update)(struct display_entity *ent,
+ const struct videomode *mode);
int (*get_modes)(struct display_entity *ent,
const struct videomode **modes);
int (*get_params)(struct display_entity *ent,
@@ -111,7 +112,8 @@ struct display_entity {
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_update(struct display_entity *entity,
+ const struct videomode *mode);
int display_entity_get_modes(struct display_entity *entity,
const struct videomode **modes);
int display_entity_get_params(struct display_entity *entity,
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 03/10] video: display: Add Device Tree bindings
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
2013-04-17 15:17 ` [RFC 01/10] video: Add generic display entity core Pawel Moll
2013-04-17 15:17 ` [RFC 02/10] video: display: Update the display with the video mode data Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 04/10] video: display: Add generic TFT display type Pawel Moll
` (6 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
Modelled after the common clock solution, the bindings
are based on the idea of display entity "providers" and
"consumers".
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
.../devicetree/bindings/video/display-bindings.txt | 75 +++++++++++++++++
drivers/video/display/display-core.c | 84 ++++++++++++++++++++
include/video/display.h | 11 +++
3 files changed, 170 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/display-bindings.txt
diff --git a/Documentation/devicetree/bindings/video/display-bindings.txt b/Documentation/devicetree/bindings/video/display-bindings.txt
new file mode 100644
index 0000000..6d8b888
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/display-bindings.txt
@@ -0,0 +1,75 @@
+[this is an RFC]
+
+Common Display Framework define a display entity (eg. LCD panel),
+being a sink for video data generated by a video signal generator
+(eg. LCD controller/driver).
+
+This set of bindings allow to represent connections between them
+in the Device Tree.
+
+Devices nodes representing display sinks are called "display
+providers" and nodes representing display sources are called
+"display consumers".
+
+Notice that in both cases a device represented by a node can
+provide or consume more than one display entity. For example
+a LCD controller can be able to driver more than one LCD
+panel at the same time, while a panel (or a special signal
+multiplexer) may have more than one input (sink) and switch
+between them.
+
+== Display provider ==
+
+Required properties:
+
+* #clock-cells: Number of cells in the display specifier. Typically
+ 0 for nodes providing single display entity and 1
+ for nodes providing multiple displays.
+
+Example:
+ dvi-output: dvi-output@0 {
+ #display-cells = <0>;
+ };
+
+== Display consumer ==
+
+Required properties:
+
+* display: List of phandle and clock specifier pairs, one pair
+ for each display (sink). Note: if the display provider
+ specifies '0' for #display-cells, then only the phandle
+ portion of the pair will appear.
+
+Example:
+
+ display-driver {
+ display = <&dvi-output>;
+ };
+
+== Larger example ==
+
+ clcd@10020000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0x10020000 0x1000>;
+ interrupts = <0 44 4>;
+ clocks = <&oscclk1>, <&oscclk2>;
+ clock-names = "clcdclk", "apb_pclk";
+ label = "V2P-CA9 CLCD";
+ display = <&v2m_muxfpga 0xf>;
+ max-hactive = <1024>;
+ max-vactive = <768>;
+ max-bpp = <16>;
+ };
+
+ v2m_muxfpga: muxfpga@0 {
+ compatible = "arm,vexpress-muxfpga";
+ arm,vexpress-sysreg,func = <7 0>;
+ #display-cells = <1>;
+ display = <&v2m_dvimode>;
+ };
+
+ v2m_dvimode: dvimode@0 {
+ compatible = "arm,vexpress-dvimode";
+ arm,vexpress-sysreg,func = <11 0>;
+ #display-cells = <0>;
+ };
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
index 4b8e45a..9827a5d 100644
--- a/drivers/video/display/display-core.c
+++ b/drivers/video/display/display-core.c
@@ -15,6 +15,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/slab.h>
#include <video/display.h>
#include <video/videomode.h>
@@ -230,6 +231,89 @@ void display_entity_put(struct display_entity *entity)
}
EXPORT_SYMBOL_GPL(display_entity_put);
+#if defined(CONFIG_OF)
+struct of_display_entity_provider {
+ struct list_head list;
+ struct device_node *node;
+ struct display_entity *(*get)(struct of_phandle_args *spec, void *data);
+ void *data;
+};
+
+static LIST_HEAD(of_display_entity_providers);
+static DEFINE_MUTEX(of_display_entity_providers_lock);
+
+int of_display_entity_add_provider(struct device_node *node,
+ struct display_entity *(*get)(struct of_phandle_args *spec,
+ void *data), void *data)
+{
+ struct of_display_entity_provider *provider =
+ kzalloc(sizeof(*provider), GFP_KERNEL);
+
+ if (!provider)
+ return -ENOMEM;
+
+ provider->node = of_node_get(node);
+ provider->get = get;
+ provider->data = data;
+
+ mutex_lock(&of_display_entity_providers_lock);
+ list_add(&provider->list, &of_display_entity_providers);
+ mutex_unlock(&of_display_entity_providers_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_add_provider);
+
+struct display_entity *of_display_entity_provider_simple_get(
+ struct of_phandle_args *spec, void *data)
+{
+ return data;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_provider_simple_get);
+
+void of_display_entity_remove_provider(struct device_node *node)
+{
+ struct of_display_entity_provider *provider, *p;
+
+ mutex_lock(&of_display_entity_providers_lock);
+ list_for_each_entry_safe(provider, p, &of_display_entity_providers,
+ list) {
+ if (provider->node == node) {
+ list_del(&provider->list);
+ of_node_put(provider->node);
+ kfree(node);
+ break;
+ }
+ }
+ mutex_unlock(&of_display_entity_providers_lock);
+}
+EXPORT_SYMBOL_GPL(of_display_entity_remove_provider);
+
+struct display_entity *of_display_entity_get(struct device_node *node,
+ int index)
+{
+ struct of_phandle_args spec;
+ struct of_display_entity_provider *provider;
+ struct display_entity *entity = NULL;
+
+ if (of_parse_phandle_with_args(node, "display", "#display-cells",
+ index, &spec) != 0)
+ return NULL;
+
+ mutex_lock(&of_display_entity_providers_lock);
+ list_for_each_entry(provider, &of_display_entity_providers, list) {
+ if (provider->node == spec.np) {
+ entity = provider->get(&spec, provider->data);
+ break;
+ }
+ }
+ mutex_unlock(&of_display_entity_providers_lock);
+
+ return entity;
+}
+EXPORT_SYMBOL_GPL(of_display_entity_get);
+#endif
+
static int display_entity_notifier_match(struct display_entity *entity,
struct display_entity_notifier *notifier)
{
diff --git a/include/video/display.h b/include/video/display.h
index 64f84d5..7fe8b2f 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -16,6 +16,7 @@
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/of.h>
/* -----------------------------------------------------------------------------
* Display Entity
@@ -149,4 +150,14 @@ void display_entity_unregister_notifier(struct display_entity_notifier *notifier
#define display_entity_register(display_entity) \
__display_entity_register(display_entity, THIS_MODULE)
+struct display_entity *of_display_entity_get(struct device_node *node,
+ int index);
+
+int of_display_entity_add_provider(struct device_node *node,
+ struct display_entity *(*get)(struct of_phandle_args *spec,
+ void *data), void *data);
+void of_display_entity_remove_provider(struct device_node *node);
+struct display_entity *of_display_entity_provider_simple_get(
+ struct of_phandle_args *spec, void *data);
+
#endif /* __DISPLAY_H__ */
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 04/10] video: display: Add generic TFT display type
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (2 preceding siblings ...)
2013-04-17 15:17 ` [RFC 03/10] video: display: Add Device Tree bindings Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 05/10] fbmon: Add extra video helper Pawel Moll
` (5 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
TFT panels may be interfaced via a simple parallel interface
carrying RGB data, pixel clock and synchronisation signals.
From the video generator point of view the width of the data
channels (number of bits per R/G/B components) may be an
important factor in setting up the display model.
Above information is based on the presentations by Dave Anders
available here: http://elinux.org/Elc-lcd
This patch adds the parallel TFT display type and basic parameters
structure. Maybe it should be split into a separate header, eg.
include/video/tft.h? Or maybe it's just the INTERFACE_DPI I'm
talking about?
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
include/video/display.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/include/video/display.h b/include/video/display.h
index 7fe8b2f..875e230 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -69,10 +69,19 @@ enum display_entity_stream_state {
enum display_entity_interface_type {
DISPLAY_ENTITY_INTERFACE_DPI,
+ DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL,
+};
+
+struct tft_parallel_interface_params {
+ int r_bits, g_bits, b_bits;
+ int r_b_swapped;
};
struct display_entity_interface_params {
enum display_entity_interface_type type;
+ union {
+ struct tft_parallel_interface_params tft_parallel;
+ } p;
};
struct display_entity_control_ops {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 05/10] fbmon: Add extra video helper
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (3 preceding siblings ...)
2013-04-17 15:17 ` [RFC 04/10] video: display: Add generic TFT display type Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 06/10] video: ARM CLCD: Add DT & CDF support Pawel Moll
` (4 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
This function converts the fb_var_screeninfo to the videomode
structure, to be used in fbdev drivers working with the
Common Display Framework.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
drivers/video/fbmon.c | 29 +++++++++++++++++++++++++++++
include/linux/fb.h | 3 +++
2 files changed, 32 insertions(+)
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 7f67099..f0ff2bf 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -1424,6 +1424,35 @@ int fb_videomode_from_videomode(const struct videomode *vm,
return 0;
}
EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+
+void videomode_from_fb_var_screeninfo(const struct fb_var_screeninfo *var,
+ struct videomode *vm)
+{
+ vm->pixelclock = PICOS2KHZ(var->pixclock) * 1000;
+
+ vm->hactive = var->xres;
+ vm->hfront_porch = var->right_margin;
+ vm->hback_porch = var->left_margin;
+ vm->hsync_len = var->hsync_len;
+
+ vm->vactive = var->yres;
+ vm->vfront_porch = var->lower_margin;
+ vm->vback_porch = var->upper_margin;
+ vm->vsync_len = var->vsync_len;
+
+ vm->dmt_flags = 0;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ vm->dmt_flags |= VESA_DMT_HSYNC_HIGH;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ vm->dmt_flags |= VESA_DMT_VSYNC_HIGH;
+
+ vm->data_flags = 0;
+ if (var->vmode & FB_VMODE_INTERLACED)
+ vm->data_flags |= DISPLAY_FLAGS_INTERLACED;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ vm->data_flags |= DISPLAY_FLAGS_DOUBLESCAN;
+}
+EXPORT_SYMBOL_GPL(videomode_from_fb_var_screeninfo);
#endif
#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 58b9860..aae2ed3 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -721,6 +721,9 @@ extern int of_get_fb_videomode(struct device_node *np,
int index);
extern int fb_videomode_from_videomode(const struct videomode *vm,
struct fb_videomode *fbmode);
+extern void videomode_from_fb_var_screeninfo(
+ const struct fb_var_screeninfo *var,
+ struct videomode *vm);
/* drivers/video/modedb.c */
#define VESA_MODEDB_SIZE 34
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 06/10] video: ARM CLCD: Add DT & CDF support
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (4 preceding siblings ...)
2013-04-17 15:17 ` [RFC 05/10] fbmon: Add extra video helper Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-18 10:24 ` Russell King - ARM Linux
2013-04-17 15:17 ` [RFC 07/10] mfd: vexpress: Allow external drivers to parse site ids Pawel Moll
` (3 subsequent siblings)
9 siblings, 1 reply; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
This patch adds basic DT bindings for the PL11x CLCD cells
and make their fbdev driver use them, together with the
Common Display Framework.
The DT provides information about the hardware configuration
and limitations (eg. the largest supported resolution)
but the video modes come exclusively from the Common
Display Framework drivers, referenced to by the standard CDF
binding.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
.../devicetree/bindings/video/arm,pl11x.txt | 35 +++
drivers/video/Kconfig | 1 +
drivers/video/amba-clcd.c | 244 ++++++++++++++++++++
include/linux/amba/clcd.h | 2 +
4 files changed, 282 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/arm,pl11x.txt
diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
new file mode 100644
index 0000000..ee9534a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -0,0 +1,35 @@
+* ARM PrimeCell Color LCD Controller (CLCD) PL110/PL111
+
+Required properties:
+
+- compatible : must be one of:
+ "arm,pl110", "arm,primecell"
+ "arm,pl111", "arm,primecell"
+- reg : base address and size of the control registers block
+- interrupts : the combined interrupt
+- clocks : phandles to the CLCD (pixel) clock and the APB clocks
+- clock-names : "clcdclk", "apb_pclk"
+- display : phandle to the display entity connected to the controller
+
+Optional properties:
+
+- label : string describing the controller location and/or usage
+- video-ram : phandle to DT node of the specialized video RAM to be used
+- max-hactive : maximum frame buffer width in pixels
+- max-vactive : maximum frame buffer height in pixels
+- max-bpp : maximum number of bits per pixel
+- big-endian-pixels : defining this property makes the pixel bytes being
+ accessed in Big Endian organization
+
+Example:
+
+ clcd@1f0000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0x1f0000 0x1000>;
+ interrupts = <14>;
+ clocks = <&v2m_oscclk1>, <&smbclk>;
+ clock-names = "clcdclk", "apb_pclk";
+ label = "IOFPGA CLCD";
+ video-ram = <&v2m_vram>;
+ display = <&v2m_muxfpga>;
+ };
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 281e548..bad8166 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -340,6 +340,7 @@ config FB_ARMCLCD
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS if OF
help
This framebuffer device driver is for the ARM PrimeCell PL110
Colour LCD controller. ARM PrimeCells provide the building
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 0a2cce7..778dc03 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -25,6 +25,11 @@
#include <linux/amba/clcd.h>
#include <linux/clk.h>
#include <linux/hardirq.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <video/display.h>
+#include <video/videomode.h>
#include <asm/sizes.h>
@@ -62,6 +67,10 @@ static void clcdfb_disable(struct clcd_fb *fb)
{
u32 val;
+ if (fb->panel->display)
+ display_entity_set_state(fb->panel->display,
+ DISPLAY_ENTITY_STATE_OFF);
+
if (fb->board->disable)
fb->board->disable(fb);
@@ -115,6 +124,11 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
*/
if (fb->board->enable)
fb->board->enable(fb);
+
+ if (fb->panel->display)
+ display_entity_set_state(fb->panel->display,
+ DISPLAY_ENTITY_STATE_ON);
+
}
static int
@@ -304,6 +318,13 @@ static int clcdfb_set_par(struct fb_info *info)
clcdfb_enable(fb, regs.cntl);
+ if (fb->panel->display) {
+ struct videomode mode;
+
+ videomode_from_fb_var_screeninfo(&fb->fb.var, &mode);
+ display_entity_update(fb->panel->display, &mode);
+ }
+
#ifdef DEBUG
printk(KERN_INFO
"CLCD: Registers set to\n"
@@ -542,6 +563,226 @@ static int clcdfb_register(struct clcd_fb *fb)
return ret;
}
+#if defined(CONFIG_OF)
+static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
+ struct display_entity_interface_params *params)
+{
+ int r = params->p.tft_parallel.r_bits;
+ int g = params->p.tft_parallel.g_bits;
+ int b = params->p.tft_parallel.b_bits;
+
+ /* Bypass pixel clock divider, data output on the falling edge */
+ panel->tim2 = TIM2_BCD | TIM2_IPC;
+
+ /* TFT display, vert. comp. interrupt at the start of the back porch */
+ panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+
+ if (params->p.tft_parallel.r_b_swapped)
+ panel->cntl |= CNTL_BGR;
+
+ if (r >= 4 && g >= 4 && b >= 4)
+ panel->caps |= CLCD_CAP_444;
+ if (r >= 5 && g >= 5 && b >= 5)
+ panel->caps |= CLCD_CAP_5551;
+ if (r >= 5 && g >= 6 && b >= 5)
+ panel->caps |= CLCD_CAP_565;
+ if (r >= 8 && g >= 8 && b >= 8)
+ panel->caps |= CLCD_CAP_888;
+
+ return 0;
+}
+
+static int clcdfb_of_init_display(struct clcd_fb *fb)
+{
+ struct device_node *node = fb->dev->dev.of_node;
+ struct display_entity_interface_params params;
+ const struct videomode *modes;
+ int modes_num;
+ int best_mode = -1;
+ u32 max_bpp = 32;
+ u32 max_hactive = (u32)~0UL;
+ u32 max_vactive = (u32)~0UL;
+ unsigned int width, height;
+ char *mode_name;
+ int i, err;
+
+ fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
+ if (!fb->panel)
+ return -ENOMEM;
+
+ fb->panel->display = of_display_entity_get(node, 0);
+ if (!fb->panel->display)
+ return -EPROBE_DEFER;
+
+ modes_num = display_entity_get_modes(fb->panel->display, &modes);
+ if (modes_num < 0)
+ return modes_num;
+
+ /* Pick the "best" (the widest, then the highest) mode from the list */
+ of_property_read_u32(node, "max-hactive", &max_hactive);
+ of_property_read_u32(node, "max-vactive", &max_vactive);
+ for (i = 0; i < modes_num; i++) {
+ if (modes[i].hactive > max_hactive ||
+ modes[i].vactive > max_vactive)
+ continue;
+ if (best_mode < 0 ||
+ (modes[i].hactive >= modes[best_mode].hactive &&
+ modes[i].vactive > modes[best_mode].vactive))
+ best_mode = i;
+ }
+ if (best_mode < 0)
+ return -ENODEV;
+
+ err = fb_videomode_from_videomode(&modes[best_mode], &fb->panel->mode);
+ if (err)
+ return err;
+
+ i = snprintf(NULL, 0, "%ux%u@%u", fb->panel->mode.xres,
+ fb->panel->mode.yres, fb->panel->mode.refresh);
+ mode_name = devm_kzalloc(&fb->dev->dev, i + 1, GFP_KERNEL);
+ snprintf(mode_name, i + 1, "%ux%u@%u", fb->panel->mode.xres,
+ fb->panel->mode.yres, fb->panel->mode.refresh);
+ fb->panel->mode.name = mode_name;
+
+ of_property_read_u32(node, "max-bpp", &max_bpp);
+ fb->panel->bpp = max_bpp;
+
+ if (of_property_read_bool(node, "big-endian-pixels"))
+ fb->panel->cntl |= CNTL_BEBO;
+
+ if (display_entity_get_size(fb->panel->display, &width, &height) != 0)
+ width = height = -1;
+ fb->panel->width = width;
+ fb->panel->height = height;
+
+ err = display_entity_get_params(fb->panel->display, ¶ms);
+ if (err)
+ return err;
+
+ switch (params.type) {
+ case DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL:
+ return clcdfb_of_get_tft_parallel_panel(fb->panel, ¶ms);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int clcdfb_of_vram_setup(struct clcd_fb *fb)
+{
+ const __be32 *prop = of_get_property(fb->dev->dev.of_node, "video-ram",
+ NULL);
+ struct device_node *node = of_find_node_by_phandle(be32_to_cpup(prop));
+ u64 size;
+ int err;
+
+ if (!node)
+ return -ENODEV;
+
+ err = clcdfb_of_init_display(fb);
+ if (err)
+ return err;
+
+ fb->fb.screen_base = of_iomap(node, 0);
+ if (!fb->fb.screen_base)
+ return -ENOMEM;
+
+ fb->fb.fix.smem_start = of_translate_address(node,
+ of_get_address(node, 0, &size, NULL));
+ fb->fb.fix.smem_len = size;
+
+ return 0;
+}
+
+static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ unsigned long off, user_size, kernel_size;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ user_size = vma->vm_end - vma->vm_start;
+ kernel_size = fb->fb.fix.smem_len;
+
+ if (off >= kernel_size || user_size > (kernel_size - off))
+ return -ENXIO;
+
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff,
+ user_size,
+ pgprot_writecombine(vma->vm_page_prot));
+}
+
+static void clcdfb_of_vram_remove(struct clcd_fb *fb)
+{
+ iounmap(fb->fb.screen_base);
+}
+
+static int clcdfb_of_dma_setup(struct clcd_fb *fb)
+{
+ unsigned long framesize;
+ dma_addr_t dma;
+ int err;
+
+ err = clcdfb_of_init_display(fb);
+ if (err)
+ return err;
+
+ framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+ fb->panel->bpp / 8;
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+ &dma, GFP_KERNEL);
+ if (!fb->fb.screen_base)
+ return -ENOMEM;
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = framesize;
+
+ return 0;
+}
+
+static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base,
+ fb->fb.fix.smem_start, fb->fb.fix.smem_len);
+}
+
+static void clcdfb_of_dma_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
+{
+ struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board),
+ GFP_KERNEL);
+ struct device_node *node = dev->dev.of_node;
+
+ if (!board)
+ return NULL;
+
+ board->name = of_get_property(node, "label", NULL);
+ if (!board->name)
+ board->name = of_node_full_name(node);
+ board->check = clcdfb_check;
+ board->decode = clcdfb_decode;
+ if (of_find_property(node, "video-ram", NULL)) {
+ board->setup = clcdfb_of_vram_setup;
+ board->mmap = clcdfb_of_vram_mmap;
+ board->remove = clcdfb_of_vram_remove;
+ } else {
+ board->setup = clcdfb_of_dma_setup;
+ board->mmap = clcdfb_of_dma_mmap;
+ board->remove = clcdfb_of_dma_remove;
+ }
+
+ return board;
+}
+#else
+static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev)
+{
+ return NULL;
+}
+#endif
+
static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
{
struct clcd_board *board = dev->dev.platform_data;
@@ -549,6 +790,9 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
int ret;
if (!board)
+ board = clcdfb_of_get_board(dev);
+
+ if (!board)
return -EINVAL;
ret = amba_request_regions(dev, NULL);
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..73b199b 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,6 +10,7 @@
* for more details.
*/
#include <linux/fb.h>
+#include <video/display.h>
/*
* CLCD Controller Internal Register addresses
@@ -105,6 +106,7 @@ struct clcd_panel {
fixedtimings:1,
grayscale:1;
unsigned int connector;
+ struct display_entity *display;
};
struct clcd_regs {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [RFC 06/10] video: ARM CLCD: Add DT & CDF support
2013-04-17 15:17 ` [RFC 06/10] video: ARM CLCD: Add DT & CDF support Pawel Moll
@ 2013-04-18 10:24 ` Russell King - ARM Linux
2013-04-18 17:33 ` [RFC v2] " Pawel Moll
0 siblings, 1 reply; 14+ messages in thread
From: Russell King - ARM Linux @ 2013-04-18 10:24 UTC (permalink / raw)
To: Pawel Moll
Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel, Laurent Pinchart, Linus Walleij
On Wed, Apr 17, 2013 at 04:17:18PM +0100, Pawel Moll wrote:
> +#if defined(CONFIG_OF)
> +static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
> + struct display_entity_interface_params *params)
> +{
> + int r = params->p.tft_parallel.r_bits;
> + int g = params->p.tft_parallel.g_bits;
> + int b = params->p.tft_parallel.b_bits;
> +
> + /* Bypass pixel clock divider, data output on the falling edge */
> + panel->tim2 = TIM2_BCD | TIM2_IPC;
> +
> + /* TFT display, vert. comp. interrupt at the start of the back porch */
> + panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
> +
> + if (params->p.tft_parallel.r_b_swapped)
> + panel->cntl |= CNTL_BGR;
NAK. Do not set this explicitly. Note the driver talks about this being
"the old way" - this should not be used with the panel capabilities - and
in fact it will be overwritten.
Instead, you need to encode this into the capabilities by masking the
below with CLCD_CAP_RGB or CLCD_CAP_BGR depending on the ordering.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC v2] video: ARM CLCD: Add DT & CDF support
2013-04-18 10:24 ` Russell King - ARM Linux
@ 2013-04-18 17:33 ` Pawel Moll
2013-04-22 14:28 ` Russell King - ARM Linux
0 siblings, 1 reply; 14+ messages in thread
From: Pawel Moll @ 2013-04-18 17:33 UTC (permalink / raw)
To: Russell King - ARM Linux
Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel, Laurent Pinchart, Linus Walleij, Pawel Moll
This patch adds basic DT bindings for the PL11x CLCD cells
and make their fbdev driver use them, together with the
Common Display Framework.
The DT provides information about the hardware configuration
and limitations (eg. the largest supported resolution)
but the video modes come exclusively from the Common
Display Framework drivers, referenced to by the standard CDF
binding.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
.../devicetree/bindings/video/arm,pl11x.txt | 35 +++
drivers/video/Kconfig | 1 +
drivers/video/amba-clcd.c | 247 ++++++++++++++++++++
include/linux/amba/clcd.h | 2 +
4 files changed, 285 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/arm,pl11x.txt
diff --git a/Documentation/devicetree/bindings/video/arm,pl11x.txt b/Documentation/devicetree/bindings/video/arm,pl11x.txt
new file mode 100644
index 0000000..ee9534a
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/arm,pl11x.txt
@@ -0,0 +1,35 @@
+* ARM PrimeCell Color LCD Controller (CLCD) PL110/PL111
+
+Required properties:
+
+- compatible : must be one of:
+ "arm,pl110", "arm,primecell"
+ "arm,pl111", "arm,primecell"
+- reg : base address and size of the control registers block
+- interrupts : the combined interrupt
+- clocks : phandles to the CLCD (pixel) clock and the APB clocks
+- clock-names : "clcdclk", "apb_pclk"
+- display : phandle to the display entity connected to the controller
+
+Optional properties:
+
+- label : string describing the controller location and/or usage
+- video-ram : phandle to DT node of the specialized video RAM to be used
+- max-hactive : maximum frame buffer width in pixels
+- max-vactive : maximum frame buffer height in pixels
+- max-bpp : maximum number of bits per pixel
+- big-endian-pixels : defining this property makes the pixel bytes being
+ accessed in Big Endian organization
+
+Example:
+
+ clcd@1f0000 {
+ compatible = "arm,pl111", "arm,primecell";
+ reg = <0x1f0000 0x1000>;
+ interrupts = <14>;
+ clocks = <&v2m_oscclk1>, <&smbclk>;
+ clock-names = "clcdclk", "apb_pclk";
+ label = "IOFPGA CLCD";
+ video-ram = <&v2m_vram>;
+ display = <&v2m_muxfpga>;
+ };
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 281e548..bad8166 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -340,6 +340,7 @@ config FB_ARMCLCD
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_MODE_HELPERS if OF
help
This framebuffer device driver is for the ARM PrimeCell PL110
Colour LCD controller. ARM PrimeCells provide the building
diff --git a/drivers/video/amba-clcd.c b/drivers/video/amba-clcd.c
index 0a2cce7..853f74c 100644
--- a/drivers/video/amba-clcd.c
+++ b/drivers/video/amba-clcd.c
@@ -25,6 +25,11 @@
#include <linux/amba/clcd.h>
#include <linux/clk.h>
#include <linux/hardirq.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <video/display.h>
+#include <video/videomode.h>
#include <asm/sizes.h>
@@ -62,6 +67,10 @@ static void clcdfb_disable(struct clcd_fb *fb)
{
u32 val;
+ if (fb->panel->display)
+ display_entity_set_state(fb->panel->display,
+ DISPLAY_ENTITY_STATE_OFF);
+
if (fb->board->disable)
fb->board->disable(fb);
@@ -115,6 +124,11 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
*/
if (fb->board->enable)
fb->board->enable(fb);
+
+ if (fb->panel->display)
+ display_entity_set_state(fb->panel->display,
+ DISPLAY_ENTITY_STATE_ON);
+
}
static int
@@ -304,6 +318,13 @@ static int clcdfb_set_par(struct fb_info *info)
clcdfb_enable(fb, regs.cntl);
+ if (fb->panel->display) {
+ struct videomode mode;
+
+ videomode_from_fb_var_screeninfo(&fb->fb.var, &mode);
+ display_entity_update(fb->panel->display, &mode);
+ }
+
#ifdef DEBUG
printk(KERN_INFO
"CLCD: Registers set to\n"
@@ -542,6 +563,229 @@ static int clcdfb_register(struct clcd_fb *fb)
return ret;
}
+#if defined(CONFIG_OF)
+static int clcdfb_of_get_tft_parallel_panel(struct clcd_panel *panel,
+ struct display_entity_interface_params *params)
+{
+ int r = params->p.tft_parallel.r_bits;
+ int g = params->p.tft_parallel.g_bits;
+ int b = params->p.tft_parallel.b_bits;
+
+ /* Bypass pixel clock divider, data output on the falling edge */
+ panel->tim2 = TIM2_BCD | TIM2_IPC;
+
+ /* TFT display, vert. comp. interrupt at the start of the back porch */
+ panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
+
+ if (r >= 4 && g >= 4 && b >= 4)
+ panel->caps |= CLCD_CAP_444;
+ if (r >= 5 && g >= 5 && b >= 5)
+ panel->caps |= CLCD_CAP_5551;
+ if (r >= 5 && g >= 6 && b >= 5)
+ panel->caps |= CLCD_CAP_565;
+ if (r >= 8 && g >= 8 && b >= 8)
+ panel->caps |= CLCD_CAP_888;
+
+ if (params->p.tft_parallel.r_b_swapped)
+ panel->caps &= ~CLCD_CAP_RGB;
+ else
+ panel->caps &= ~CLCD_CAP_BGR;
+
+ return 0;
+}
+
+static int clcdfb_of_init_display(struct clcd_fb *fb)
+{
+ struct device_node *node = fb->dev->dev.of_node;
+ struct display_entity_interface_params params;
+ const struct videomode *modes;
+ int modes_num;
+ int best_mode = -1;
+ u32 max_bpp = 32;
+ u32 max_hactive = (u32)~0UL;
+ u32 max_vactive = (u32)~0UL;
+ unsigned int width, height;
+ char *mode_name;
+ int i, err;
+
+ fb->panel = devm_kzalloc(&fb->dev->dev, sizeof(*fb->panel), GFP_KERNEL);
+ if (!fb->panel)
+ return -ENOMEM;
+
+ fb->panel->display = of_display_entity_get(node, 0);
+ if (!fb->panel->display)
+ return -EPROBE_DEFER;
+
+ modes_num = display_entity_get_modes(fb->panel->display, &modes);
+ if (modes_num < 0)
+ return modes_num;
+
+ /* Pick the "best" (the widest, then the highest) mode from the list */
+ of_property_read_u32(node, "max-hactive", &max_hactive);
+ of_property_read_u32(node, "max-vactive", &max_vactive);
+ for (i = 0; i < modes_num; i++) {
+ if (modes[i].hactive > max_hactive ||
+ modes[i].vactive > max_vactive)
+ continue;
+ if (best_mode < 0 ||
+ (modes[i].hactive >= modes[best_mode].hactive &&
+ modes[i].vactive > modes[best_mode].vactive))
+ best_mode = i;
+ }
+ if (best_mode < 0)
+ return -ENODEV;
+
+ err = fb_videomode_from_videomode(&modes[best_mode], &fb->panel->mode);
+ if (err)
+ return err;
+
+ i = snprintf(NULL, 0, "%ux%u@%u", fb->panel->mode.xres,
+ fb->panel->mode.yres, fb->panel->mode.refresh);
+ mode_name = devm_kzalloc(&fb->dev->dev, i + 1, GFP_KERNEL);
+ snprintf(mode_name, i + 1, "%ux%u@%u", fb->panel->mode.xres,
+ fb->panel->mode.yres, fb->panel->mode.refresh);
+ fb->panel->mode.name = mode_name;
+
+ of_property_read_u32(node, "max-bpp", &max_bpp);
+ fb->panel->bpp = max_bpp;
+
+ if (of_property_read_bool(node, "big-endian-pixels"))
+ fb->panel->cntl |= CNTL_BEBO;
+
+ if (display_entity_get_size(fb->panel->display, &width, &height) != 0)
+ width = height = -1;
+ fb->panel->width = width;
+ fb->panel->height = height;
+
+ err = display_entity_get_params(fb->panel->display, ¶ms);
+ if (err)
+ return err;
+
+ switch (params.type) {
+ case DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL:
+ return clcdfb_of_get_tft_parallel_panel(fb->panel, ¶ms);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int clcdfb_of_vram_setup(struct clcd_fb *fb)
+{
+ const __be32 *prop = of_get_property(fb->dev->dev.of_node, "video-ram",
+ NULL);
+ struct device_node *node = of_find_node_by_phandle(be32_to_cpup(prop));
+ u64 size;
+ int err;
+
+ if (!node)
+ return -ENODEV;
+
+ err = clcdfb_of_init_display(fb);
+ if (err)
+ return err;
+
+ fb->fb.screen_base = of_iomap(node, 0);
+ if (!fb->fb.screen_base)
+ return -ENOMEM;
+
+ fb->fb.fix.smem_start = of_translate_address(node,
+ of_get_address(node, 0, &size, NULL));
+ fb->fb.fix.smem_len = size;
+
+ return 0;
+}
+
+static int clcdfb_of_vram_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ unsigned long off, user_size, kernel_size;
+
+ off = vma->vm_pgoff << PAGE_SHIFT;
+ user_size = vma->vm_end - vma->vm_start;
+ kernel_size = fb->fb.fix.smem_len;
+
+ if (off >= kernel_size || user_size > (kernel_size - off))
+ return -ENXIO;
+
+ return remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(fb->fb.fix.smem_start) + vma->vm_pgoff,
+ user_size,
+ pgprot_writecombine(vma->vm_page_prot));
+}
+
+static void clcdfb_of_vram_remove(struct clcd_fb *fb)
+{
+ iounmap(fb->fb.screen_base);
+}
+
+static int clcdfb_of_dma_setup(struct clcd_fb *fb)
+{
+ unsigned long framesize;
+ dma_addr_t dma;
+ int err;
+
+ err = clcdfb_of_init_display(fb);
+ if (err)
+ return err;
+
+ framesize = fb->panel->mode.xres * fb->panel->mode.yres *
+ fb->panel->bpp / 8;
+ fb->fb.screen_base = dma_alloc_writecombine(&fb->dev->dev, framesize,
+ &dma, GFP_KERNEL);
+ if (!fb->fb.screen_base)
+ return -ENOMEM;
+
+ fb->fb.fix.smem_start = dma;
+ fb->fb.fix.smem_len = framesize;
+
+ return 0;
+}
+
+static int clcdfb_of_dma_mmap(struct clcd_fb *fb, struct vm_area_struct *vma)
+{
+ return dma_mmap_writecombine(&fb->dev->dev, vma, fb->fb.screen_base,
+ fb->fb.fix.smem_start, fb->fb.fix.smem_len);
+}
+
+static void clcdfb_of_dma_remove(struct clcd_fb *fb)
+{
+ dma_free_writecombine(&fb->dev->dev, fb->fb.fix.smem_len,
+ fb->fb.screen_base, fb->fb.fix.smem_start);
+}
+
+static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
+{
+ struct clcd_board *board = devm_kzalloc(&dev->dev, sizeof(*board),
+ GFP_KERNEL);
+ struct device_node *node = dev->dev.of_node;
+
+ if (!board)
+ return NULL;
+
+ board->name = of_get_property(node, "label", NULL);
+ if (!board->name)
+ board->name = of_node_full_name(node);
+ board->caps = CLCD_CAP_ALL;
+ board->check = clcdfb_check;
+ board->decode = clcdfb_decode;
+ if (of_find_property(node, "video-ram", NULL)) {
+ board->setup = clcdfb_of_vram_setup;
+ board->mmap = clcdfb_of_vram_mmap;
+ board->remove = clcdfb_of_vram_remove;
+ } else {
+ board->setup = clcdfb_of_dma_setup;
+ board->mmap = clcdfb_of_dma_mmap;
+ board->remove = clcdfb_of_dma_remove;
+ }
+
+ return board;
+}
+#else
+static struct clcd_board *clcdfb_of_get_board(struct amba_dev *dev)
+{
+ return NULL;
+}
+#endif
+
static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
{
struct clcd_board *board = dev->dev.platform_data;
@@ -549,6 +793,9 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
int ret;
if (!board)
+ board = clcdfb_of_get_board(dev);
+
+ if (!board)
return -EINVAL;
ret = amba_request_regions(dev, NULL);
diff --git a/include/linux/amba/clcd.h b/include/linux/amba/clcd.h
index e82e3ee..73b199b 100644
--- a/include/linux/amba/clcd.h
+++ b/include/linux/amba/clcd.h
@@ -10,6 +10,7 @@
* for more details.
*/
#include <linux/fb.h>
+#include <video/display.h>
/*
* CLCD Controller Internal Register addresses
@@ -105,6 +106,7 @@ struct clcd_panel {
fixedtimings:1,
grayscale:1;
unsigned int connector;
+ struct display_entity *display;
};
struct clcd_regs {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [RFC v2] video: ARM CLCD: Add DT & CDF support
2013-04-18 17:33 ` [RFC v2] " Pawel Moll
@ 2013-04-22 14:28 ` Russell King - ARM Linux
0 siblings, 0 replies; 14+ messages in thread
From: Russell King - ARM Linux @ 2013-04-22 14:28 UTC (permalink / raw)
To: Pawel Moll
Cc: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel, Laurent Pinchart, Linus Walleij
On Thu, Apr 18, 2013 at 06:33:21PM +0100, Pawel Moll wrote:
> This patch adds basic DT bindings for the PL11x CLCD cells
> and make their fbdev driver use them, together with the
> Common Display Framework.
>
> The DT provides information about the hardware configuration
> and limitations (eg. the largest supported resolution)
> but the video modes come exclusively from the Common
> Display Framework drivers, referenced to by the standard CDF
> binding.
>
> Signed-off-by: Pawel Moll <pawel.moll@arm.com>
Much better.
I will point out though that there be all sorts of worms here when you
come to the previous ARM evaluation boards (which is why the capabilities
stuff got written in the first place) where there's a horrid mixture of
BGR/RGB ordering at various levels of the system - some of which must be
set correctly because the CLCD output isn't strictly used as R bits
G bits and B bits (to support different formats from the CLCD's native
formats.)
^ permalink raw reply [flat|nested] 14+ messages in thread
* [RFC 07/10] mfd: vexpress: Allow external drivers to parse site ids
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (5 preceding siblings ...)
2013-04-17 15:17 ` [RFC 06/10] video: ARM CLCD: Add DT & CDF support Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 08/10] video: Versatile Express MUXFPGA driver Pawel Moll
` (2 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
... by providing a function translating the MASTER
value into the currently valid site number and
a _LAST constant providing all possible site id values.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
drivers/mfd/vexpress-sysreg.c | 5 +++++
include/linux/vexpress.h | 2 ++
2 files changed, 7 insertions(+)
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index bf75e96..4158e26 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -81,6 +81,11 @@ void vexpress_flags_set(u32 data)
writel(data, vexpress_sysreg_base + SYS_FLAGSSET);
}
+u32 vexpress_get_site(int site)
+{
+ return site == VEXPRESS_SITE_MASTER ? vexpress_master_site : site;
+}
+
u32 vexpress_get_procid(int site)
{
if (site == VEXPRESS_SITE_MASTER)
diff --git a/include/linux/vexpress.h b/include/linux/vexpress.h
index 7581874..1ebbcf5 100644
--- a/include/linux/vexpress.h
+++ b/include/linux/vexpress.h
@@ -19,6 +19,7 @@
#define VEXPRESS_SITE_MB 0
#define VEXPRESS_SITE_DB1 1
#define VEXPRESS_SITE_DB2 2
+#define __VEXPRESS_SITE_LAST 3
#define VEXPRESS_SITE_MASTER 0xf
#define VEXPRESS_CONFIG_STATUS_DONE 0
@@ -103,6 +104,7 @@ int vexpress_config_write(struct vexpress_config_func *func, int offset,
/* Platform control */
+u32 vexpress_get_site(int site);
u32 vexpress_get_procid(int site);
u32 vexpress_get_hbi(int site);
void *vexpress_get_24mhz_clock_base(void);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 08/10] video: Versatile Express MUXFPGA driver
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (6 preceding siblings ...)
2013-04-17 15:17 ` [RFC 07/10] mfd: vexpress: Allow external drivers to parse site ids Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 09/10] video: Versatile Express DVI mode driver Pawel Moll
2013-04-17 15:17 ` [RFC 10/10] ARM: vexpress: Add CLCD Device Tree properties Pawel Moll
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
Versatile Express' DVI video output can be connected to one the three
sources - motherboard's CLCD controller or a video signal generated
by one of the daughterboards.
This driver provides a Common Display Framework driver for the
muxer FPGA, which acts as a switch selecting one of the video data
sources. The default source is selected basing on the priority
list (which itself can be modified via module paramter), but
the user can make his own decision about it using the device's
sysfs "source" attribute.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
.../testing/sysfs-driver-video-vexpress-muxfpga | 5 +
drivers/video/Makefile | 3 +
drivers/video/vexpress-muxfpga.c | 228 ++++++++++++++++++++
3 files changed, 236 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
create mode 100644 drivers/video/vexpress-muxfpga.c
diff --git a/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga b/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
new file mode 100644
index 0000000..bfd568d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-video-vexpress-muxfpga
@@ -0,0 +1,5 @@
+What: /sys/bus/platform/drivers/vexpress-muxfpga/<muxfpga device>/source
+Date: April 2013
+Contant: Pawel Moll <pawel.moll@arm.com>
+Description: This file stores the id of the video signal source
+ supposed to be routed to the board's DVI output.
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index b989e8e..84c6083 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -176,3 +176,6 @@ obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
obj-$(CONFIG_VIDEOMODE) += videomode.o
obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
+
+# platform specific output drivers
+obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-muxfpga.o
diff --git a/drivers/video/vexpress-muxfpga.c b/drivers/video/vexpress-muxfpga.c
new file mode 100644
index 0000000..1731ad0
--- /dev/null
+++ b/drivers/video/vexpress-muxfpga.c
@@ -0,0 +1,228 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-muxfpga: " fmt
+
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+#include <video/display.h>
+#include <video/videomode.h>
+
+
+static struct vexpress_config_func *vexpress_muxfpga_func;
+static struct display_entity *vexpress_muxfpga_output;
+
+
+static struct vexpress_muxfpga_source {
+ struct display_entity display;
+ struct videomode mode;
+ bool updated;
+} vexpress_muxfpga_sources[__VEXPRESS_SITE_LAST];
+static u32 vexpress_muxfpga_source_site = VEXPRESS_SITE_MB;
+static bool vexpress_muxfpga_source_stored;
+
+
+static int vexpress_muxfpga_set_site(u32 site)
+{
+ int err;
+
+ if (site >= ARRAY_SIZE(vexpress_muxfpga_sources))
+ return -EINVAL;
+
+ err = vexpress_config_write(vexpress_muxfpga_func, 0, site);
+ if (!err) {
+ pr_debug("Selected site %d as source\n", site);
+ vexpress_muxfpga_source_site = site;
+ } else {
+ pr_warn("Failed to select site %d as source! (%d)\n",
+ site, err);
+ }
+
+ return err;
+}
+
+static unsigned int vexpress_muxfpga_preferred_sites[] = {
+ VEXPRESS_SITE_MASTER,
+ VEXPRESS_SITE_DB1,
+ VEXPRESS_SITE_DB2,
+ VEXPRESS_SITE_MB,
+};
+static unsigned int vexpress_muxfpga_preferred_sites_num =
+ ARRAY_SIZE(vexpress_muxfpga_preferred_sites);
+module_param_array_named(preferred_sites, vexpress_muxfpga_preferred_sites,
+ uint, &vexpress_muxfpga_preferred_sites_num, S_IRUGO);
+MODULE_PARM_DESC(preferred_sites, "Preferred order of MUXFPGA (DVI output) "
+ "sources; values can be a daughterboard site ID (1-2), the "
+ "motherboard ID (0) or a value describing the master site "
+ "(0xf).");
+
+static int vexpress_muxfpga_get_priority(u32 site)
+{
+ int i;
+
+ for (i = 0; i < vexpress_muxfpga_preferred_sites_num; i++) {
+ u32 preference = vexpress_muxfpga_preferred_sites[i];
+
+ if (site == vexpress_get_site(preference))
+ return i;
+ }
+
+ return INT_MAX;
+}
+
+static void vexpress_muxfpga_set_preffered_site(u32 site)
+{
+ int current_priority = vexpress_muxfpga_get_priority(
+ vexpress_muxfpga_source_site);
+ int new_priority = vexpress_muxfpga_get_priority(site);
+
+ if (new_priority <= current_priority)
+ vexpress_muxfpga_set_site(site);
+}
+
+
+static int vexpress_muxfpga_display_update(struct display_entity *display,
+ const struct videomode *mode)
+{
+ int err = display_entity_update(vexpress_muxfpga_output, mode);
+
+ if (!err) {
+ struct vexpress_muxfpga_source *source = container_of(display,
+ struct vexpress_muxfpga_source, display);
+
+ source->updated = true;
+ source->mode = *mode;
+ }
+
+ return err;
+}
+
+static int vexpress_muxfpga_display_get_modes(struct display_entity *display,
+ const struct videomode **modes)
+{
+ return display_entity_get_modes(vexpress_muxfpga_output, modes);
+}
+
+static int vexpress_muxfpga_display_get_params(struct display_entity *display,
+ struct display_entity_interface_params *params)
+{
+ return display_entity_get_params(vexpress_muxfpga_output, params);
+}
+
+static const struct display_entity_control_ops vexpress_muxfpga_display_ops = {
+ .update = vexpress_muxfpga_display_update,
+ .get_modes = vexpress_muxfpga_display_get_modes,
+ .get_params = vexpress_muxfpga_display_get_params,
+};
+
+
+static ssize_t vexpress_muxfpga_show_source(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ return sprintf(buf, "%u\n", vexpress_muxfpga_source_site);
+}
+
+static ssize_t vexpress_muxfpga_store_source(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ u32 site;
+ int err = kstrtou32(buf, 0, &site);
+
+ if (!err) {
+ site = vexpress_get_site(site);
+ err = vexpress_muxfpga_set_site(site);
+ }
+
+ if (!err)
+ vexpress_muxfpga_source_stored = true;
+
+ if (!err && vexpress_muxfpga_sources[site].updated)
+ vexpress_muxfpga_display_update(
+ &vexpress_muxfpga_sources[site].display,
+ &vexpress_muxfpga_sources[site].mode);
+
+ return err ? err : count;
+}
+
+DEVICE_ATTR(source, S_IRUGO | S_IWUSR, vexpress_muxfpga_show_source,
+ vexpress_muxfpga_store_source);
+
+static struct display_entity *vexpress_muxfpga_display_get(
+ struct of_phandle_args *spec, void *data)
+{
+ u32 site = vexpress_get_site(spec->args[0]);
+
+ if (WARN_ON(spec->args_count != 1 ||
+ site >= ARRAY_SIZE(vexpress_muxfpga_sources)))
+ return NULL;
+
+ /* Skip source selection if the user made his choice */
+ if (!vexpress_muxfpga_source_stored)
+ vexpress_muxfpga_set_preffered_site(site);
+
+ return &vexpress_muxfpga_sources[site].display;
+}
+
+
+static struct of_device_id vexpress_muxfpga_of_match[] = {
+ { .compatible = "arm,vexpress-muxfpga", },
+ {}
+};
+
+static int vexpress_muxfpga_probe(struct platform_device *pdev)
+{
+ struct display_entity_interface_params params;
+ int i;
+
+ vexpress_muxfpga_output = of_display_entity_get(pdev->dev.of_node, 0);
+ if (!vexpress_muxfpga_output)
+ return -EPROBE_DEFER;
+
+ if (display_entity_get_params(vexpress_muxfpga_output, ¶ms) != 0 ||
+ params.type != DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL)
+ return -EINVAL;
+
+ vexpress_muxfpga_func = vexpress_config_func_get_by_dev(&pdev->dev);
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_muxfpga_sources); i++) {
+ struct vexpress_muxfpga_source *source =
+ &vexpress_muxfpga_sources[i];
+
+ source->display.dev = &pdev->dev;
+ source->display.ops.ctrl = &vexpress_muxfpga_display_ops;
+ WARN_ON(display_entity_register(&source->display));
+ of_display_entity_add_provider(pdev->dev.of_node,
+ vexpress_muxfpga_display_get, NULL);
+ }
+
+ device_create_file(&pdev->dev, &dev_attr_source);
+
+ return 0;
+}
+
+static struct platform_driver vexpress_muxfpga_driver = {
+ .probe = vexpress_muxfpga_probe,
+ .driver = {
+ .name = "vexpress-muxfpga",
+ .of_match_table = vexpress_muxfpga_of_match,
+ },
+};
+
+static int __init vexpress_muxfpga_init(void)
+{
+ return platform_driver_register(&vexpress_muxfpga_driver);
+}
+device_initcall(vexpress_muxfpga_init);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 09/10] video: Versatile Express DVI mode driver
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (7 preceding siblings ...)
2013-04-17 15:17 ` [RFC 08/10] video: Versatile Express MUXFPGA driver Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
2013-04-17 15:17 ` [RFC 10/10] ARM: vexpress: Add CLCD Device Tree properties Pawel Moll
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
Versatile Express DVI output is driven by a Sii9022 chip. It can be
controller to a limited extend by the Motherboard Config Controller,
and that's what the driver is doing now. It is a temporary measure
till there's a full I2C driver for the chip.
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
drivers/video/Makefile | 1 +
drivers/video/vexpress-dvimode.c | 158 ++++++++++++++++++++++++++++++++++++++
2 files changed, 159 insertions(+)
create mode 100644 drivers/video/vexpress-dvimode.c
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 84c6083..9347e00 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -179,3 +179,4 @@ obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
# platform specific output drivers
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-muxfpga.o
+obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-dvimode.o
diff --git a/drivers/video/vexpress-dvimode.c b/drivers/video/vexpress-dvimode.c
new file mode 100644
index 0000000..85d5608
--- /dev/null
+++ b/drivers/video/vexpress-dvimode.c
@@ -0,0 +1,158 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2013 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-dvimode: " fmt
+
+#include <linux/fb.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+#include <video/display.h>
+#include <video/videomode.h>
+
+
+static struct vexpress_config_func *vexpress_dvimode_func;
+
+
+static int vexpress_dvimode_display_update(struct display_entity *display,
+ const struct videomode *mode)
+{
+ static const struct {
+ u32 hactive, vactive, dvimode;
+ } dvimodes[] = {
+ { 640, 480, 0 }, /* VGA */
+ { 800, 600, 1 }, /* SVGA */
+ { 1024, 768, 2 }, /* XGA */
+ { 1280, 1024, 3 }, /* SXGA */
+ { 1600, 1200, 4 }, /* UXGA */
+ { 1920, 1080, 5 }, /* HD1080 */
+ };
+ int err = -ENOENT;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dvimodes); i++) {
+ if (dvimodes[i].hactive == mode->hactive &&
+ dvimodes[i].vactive == mode->vactive) {
+ pr_debug("mode: %ux%u = %d\n", mode->hactive,
+ mode->vactive, dvimodes[i].dvimode);
+ err = vexpress_config_write(vexpress_dvimode_func, 0,
+ dvimodes[i].dvimode);
+ break;
+ }
+ }
+
+ if (err)
+ pr_warn("Failed to set %ux%u mode! (%d)\n", mode->hactive,
+ mode->vactive, err);
+
+ return err;
+}
+
+static int vexpress_dvimode_display_get_modes(struct display_entity *display,
+ const struct videomode **modes)
+{
+ static const struct videomode m[] = {
+ {
+ /* VGA */
+ .pixelclock = 25175000,
+ .hactive = 640,
+ .hback_porch = 40,
+ .hfront_porch = 24,
+ .vfront_porch = 11,
+ .hsync_len = 96,
+ .vactive = 480,
+ .vback_porch = 32,
+ .vsync_len = 2,
+ }, {
+ /* XGA */
+ .pixelclock = 63500127,
+ .hactive = 1024,
+ .hback_porch = 152,
+ .hfront_porch = 48,
+ .hsync_len = 104,
+ .vactive = 768,
+ .vback_porch = 23,
+ .vfront_porch = 3,
+ .vsync_len = 4,
+ }, {
+ /* SXGA */
+ .pixelclock = 108000000,
+ .hactive = 1280,
+ .hback_porch = 248,
+ .hfront_porch = 48,
+ .hsync_len = 112,
+ .vactive = 1024,
+ .vback_porch = 38,
+ .vfront_porch = 1,
+ .vsync_len = 3,
+ },
+ };
+
+ *modes = m;
+
+ return ARRAY_SIZE(m);
+}
+
+static int vexpress_dvimode_display_get_params(struct display_entity *display,
+ struct display_entity_interface_params *params)
+{
+ params->type = DISPLAY_ENTITY_INTERFACE_TFT_PARALLEL;
+ params->p.tft_parallel.r_bits = 8;
+ params->p.tft_parallel.g_bits = 8;
+ params->p.tft_parallel.b_bits = 8;
+ params->p.tft_parallel.r_b_swapped = 0;
+
+ return 0;
+}
+
+static const struct display_entity_control_ops vexpress_dvimode_display_ops = {
+ .update = vexpress_dvimode_display_update,
+ .get_modes = vexpress_dvimode_display_get_modes,
+ .get_params = vexpress_dvimode_display_get_params,
+};
+
+static struct display_entity vexpress_dvimode_display = {
+ .ops.ctrl = &vexpress_dvimode_display_ops,
+};
+
+static struct of_device_id vexpress_dvimode_of_match[] = {
+ { .compatible = "arm,vexpress-dvimode", },
+ {}
+};
+
+static int vexpress_dvimode_probe(struct platform_device *pdev)
+{
+ vexpress_dvimode_func = vexpress_config_func_get_by_dev(&pdev->dev);
+
+ vexpress_dvimode_display.dev = &pdev->dev;
+ display_entity_register(&vexpress_dvimode_display);
+ of_display_entity_add_provider(pdev->dev.of_node,
+ of_display_entity_provider_simple_get,
+ &vexpress_dvimode_display);
+
+ return 0;
+}
+
+static struct platform_driver vexpress_dvimode_driver = {
+ .probe = vexpress_dvimode_probe,
+ .driver = {
+ .name = "vexpress-dvimode",
+ .of_match_table = vexpress_dvimode_of_match,
+ },
+};
+
+static int __init vexpress_dvimode_init(void)
+{
+ return platform_driver_register(&vexpress_dvimode_driver);
+}
+device_initcall(vexpress_dvimode_init);
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [RFC 10/10] ARM: vexpress: Add CLCD Device Tree properties
2013-04-17 15:17 [RFC 00/10] Versatile Express CLCD DVI output support Pawel Moll
` (8 preceding siblings ...)
2013-04-17 15:17 ` [RFC 09/10] video: Versatile Express DVI mode driver Pawel Moll
@ 2013-04-17 15:17 ` Pawel Moll
9 siblings, 0 replies; 14+ messages in thread
From: Pawel Moll @ 2013-04-17 15:17 UTC (permalink / raw)
To: linux-fbdev, linux-media, dri-devel, devicetree-discuss,
linux-arm-kernel
Cc: Laurent Pinchart, Linus Walleij, Russell King - ARM Linux,
Pawel Moll
Signed-off-by: Pawel Moll <pawel.moll@arm.com>
---
arch/arm/boot/dts/vexpress-v2m-rs1.dtsi | 17 +++++++++++++----
arch/arm/boot/dts/vexpress-v2m.dtsi | 17 +++++++++++++----
arch/arm/boot/dts/vexpress-v2p-ca9.dts | 5 +++++
3 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
index ac870fb..aac9459 100644
--- a/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m-rs1.dtsi
@@ -41,7 +41,7 @@
bank-width = <4>;
};
- vram@2,00000000 {
+ v2m_vram: vram@2,00000000 {
compatible = "arm,vexpress-vram";
reg = <2 0x00000000 0x00800000>;
};
@@ -233,6 +233,12 @@
interrupts = <14>;
clocks = <&v2m_oscclk1>, <&smbclk>;
clock-names = "clcdclk", "apb_pclk";
+ label = "IOFPGA CLCD";
+ video-ram = <&v2m_vram>;
+ display = <&v2m_muxfpga 0>;
+ max-hactive = <640>;
+ max-vactive = <480>;
+ max-bpp = <16>;
};
};
@@ -282,7 +288,7 @@
/* CLCD clock */
compatible = "arm,vexpress-osc";
arm,vexpress-sysreg,func = <1 1>;
- freq-range = <23750000 63500000>;
+ freq-range = <23750000 65000000>;
#clock-cells = <0>;
clock-output-names = "v2m:oscclk1";
};
@@ -317,9 +323,11 @@
arm,vexpress-sysreg,func = <5 0>;
};
- muxfpga@0 {
+ v2m_muxfpga: muxfpga@0 {
compatible = "arm,vexpress-muxfpga";
arm,vexpress-sysreg,func = <7 0>;
+ #display-cells = <1>;
+ display = <&v2m_dvimode>;
};
shutdown@0 {
@@ -332,9 +340,10 @@
arm,vexpress-sysreg,func = <9 0>;
};
- dvimode@0 {
+ v2m_dvimode: dvimode@0 {
compatible = "arm,vexpress-dvimode";
arm,vexpress-sysreg,func = <11 0>;
+ #display-cells = <0>;
};
};
};
diff --git a/arch/arm/boot/dts/vexpress-v2m.dtsi b/arch/arm/boot/dts/vexpress-v2m.dtsi
index f142036..4d080d0 100644
--- a/arch/arm/boot/dts/vexpress-v2m.dtsi
+++ b/arch/arm/boot/dts/vexpress-v2m.dtsi
@@ -40,7 +40,7 @@
bank-width = <4>;
};
- vram@3,00000000 {
+ v2m_vram: vram@3,00000000 {
compatible = "arm,vexpress-vram";
reg = <3 0x00000000 0x00800000>;
};
@@ -232,6 +232,12 @@
interrupts = <14>;
clocks = <&v2m_oscclk1>, <&smbclk>;
clock-names = "clcdclk", "apb_pclk";
+ label = "IOFPGA CLCD";
+ video-ram = <&v2m_vram>;
+ display = <&v2m_muxfpga 0>;
+ max-hactive = <640>;
+ max-vactive = <480>;
+ max-bpp = <16>;
};
};
@@ -281,7 +287,7 @@
/* CLCD clock */
compatible = "arm,vexpress-osc";
arm,vexpress-sysreg,func = <1 1>;
- freq-range = <23750000 63500000>;
+ freq-range = <23750000 65000000>;
#clock-cells = <0>;
clock-output-names = "v2m:oscclk1";
};
@@ -316,9 +322,11 @@
arm,vexpress-sysreg,func = <5 0>;
};
- muxfpga@0 {
+ v2m_muxfpga: muxfpga@0 {
compatible = "arm,vexpress-muxfpga";
arm,vexpress-sysreg,func = <7 0>;
+ #display-cells = <1>;
+ display = <&v2m_dvimode>;
};
shutdown@0 {
@@ -331,9 +339,10 @@
arm,vexpress-sysreg,func = <9 0>;
};
- dvimode@0 {
+ v2m_dvimode: dvimode@0 {
compatible = "arm,vexpress-dvimode";
arm,vexpress-sysreg,func = <11 0>;
+ #display-cells = <0>;
};
};
};
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca9.dts b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
index 1420bb1..2a63510 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca9.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca9.dts
@@ -73,6 +73,11 @@
interrupts = <0 44 4>;
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
+ label = "V2P-CA9 CLCD";
+ display = <&v2m_muxfpga 0xf>;
+ max-hactive = <1024>;
+ max-vactive = <768>;
+ max-bpp = <16>;
};
memory-controller@100e0000 {
--
1.7.10.4
^ permalink raw reply related [flat|nested] 14+ messages in thread