* [PATCH v7 5/8] fbmon: add videomode helpers
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Rob Herring, linux-fbdev, dri-devel,
Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
linux-media, Tomi Valkeinen, Stephen Warren, kernel
In-Reply-To: <1351675689-26814-1-git-send-email-s.trumtrar@pengutronix.de>
Add a function to convert from the generic videomode to a fb_videomode.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
drivers/video/fbmon.c | 36 ++++++++++++++++++++++++++++++++++++
include/linux/fb.h | 2 ++
2 files changed, 38 insertions(+)
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index cef6557..b9e6ab3 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -1373,6 +1373,42 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
kfree(timings);
return err;
}
+
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode)
+{
+ fbmode->xres = vm->hactive;
+ fbmode->left_margin = vm->hback_porch;
+ fbmode->right_margin = vm->hfront_porch;
+ fbmode->hsync_len = vm->hsync_len;
+
+ fbmode->yres = vm->vactive;
+ fbmode->upper_margin = vm->vback_porch;
+ fbmode->lower_margin = vm->vfront_porch;
+ fbmode->vsync_len = vm->vsync_len;
+
+ fbmode->pixclock = KHZ2PICOS(vm->pixelclock / 1000);
+
+ fbmode->sync = 0;
+ fbmode->vmode = 0;
+ if (vm->hah)
+ fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+ if (vm->vah)
+ fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+ if (vm->interlaced)
+ fbmode->vmode |= FB_VMODE_INTERLACED;
+ if (vm->doublescan)
+ fbmode->vmode |= FB_VMODE_DOUBLE;
+
+ fbmode->refresh = 60;
+ fbmode->flag = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_to_fb_videomode);
+#endif
+
+
#else
int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
{
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c7a9571..46c665b 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -714,6 +714,8 @@ extern void fb_destroy_modedb(struct fb_videomode *modedb);
extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
+extern int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode);
+
/* drivers/video/modedb.c */
#define VESA_MODEDB_SIZE 34
extern void fb_var_to_videomode(struct fb_videomode *mode,
--
1.7.10.4
^ permalink raw reply related
* [PATCH v7 4/8] video: add videomode helpers
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Rob Herring, linux-fbdev, dri-devel,
Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
linux-media, Tomi Valkeinen, Stephen Warren, kernel
In-Reply-To: <1351675689-26814-1-git-send-email-s.trumtrar@pengutronix.de>
Add helper functions to convert from display timings to a generic videomode
structure. This videomode can then be converted to the corresponding subsystem
mode representation (e.g. fb_videomode).
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
drivers/video/Kconfig | 6 ++++++
drivers/video/Makefile | 1 +
drivers/video/videomode.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/videomode.h | 36 ++++++++++++++++++++++++++++++++++++
4 files changed, 87 insertions(+)
create mode 100644 drivers/video/videomode.c
create mode 100644 include/linux/videomode.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1421fc8..45dd393 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -38,6 +38,12 @@ config DISPLAY_TIMING
help
Say Y here, to use the display timing helpers.
+config VIDEOMODE
+ bool "Enable videomode helpers"
+ help
+ Say Y here, to use the generic videomode helpers. This allows
+ converting from display timings to fb_videomode and drm_display_mode
+
menuconfig FB
tristate "Support for frame buffer devices"
---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 552c045..fc30439 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -168,3 +168,4 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o
#video output switch sysfs driver
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
+obj-$(CONFIG_VIDEOMODE) += videomode.o
diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c
new file mode 100644
index 0000000..a9fe010
--- /dev/null
+++ b/drivers/video/videomode.c
@@ -0,0 +1,44 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/display_timing.h>
+#include <linux/videomode.h>
+
+int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
+ int index)
+{
+ struct display_timing *dt = NULL;
+
+ dt = display_timings_get(disp, index);
+ if (!dt) {
+ pr_err("%s: no signal timings found\n", __func__);
+ return -EINVAL;
+ }
+
+ vm->pixelclock = display_timing_get_value(&dt->pixelclock, 0);
+ vm->hactive = display_timing_get_value(&dt->hactive, 0);
+ vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, 0);
+ vm->hback_porch = display_timing_get_value(&dt->hback_porch, 0);
+ vm->hsync_len = display_timing_get_value(&dt->hsync_len, 0);
+
+ vm->vactive = display_timing_get_value(&dt->vactive, 0);
+ vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, 0);
+ vm->vback_porch = display_timing_get_value(&dt->vback_porch, 0);
+ vm->vsync_len = display_timing_get_value(&dt->vsync_len, 0);
+
+ vm->vah = dt->vsync_pol_active;
+ vm->hah = dt->hsync_pol_active;
+ vm->interlaced = dt->interlaced;
+ vm->doublescan = dt->doublescan;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_from_timing);
diff --git a/include/linux/videomode.h b/include/linux/videomode.h
new file mode 100644
index 0000000..f932147
--- /dev/null
+++ b/include/linux/videomode.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * generic videomode description
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_VIDEOMODE_H
+#define __LINUX_VIDEOMODE_H
+
+#include <linux/display_timing.h>
+
+struct videomode {
+ u32 pixelclock;
+ u32 refreshrate;
+
+ u32 hactive;
+ u32 hfront_porch;
+ u32 hback_porch;
+ u32 hsync_len;
+
+ u32 vactive;
+ u32 vfront_porch;
+ u32 vback_porch;
+ u32 vsync_len;
+
+ u32 hah;
+ u32 vah;
+ bool interlaced;
+ bool doublescan;
+};
+
+int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
+ int index);
+#endif
--
1.7.10.4
^ permalink raw reply related
* [PATCH v7 3/8] of: add generic videomode description
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Philipp Zabel, Rob Herring, linux-fbdev,
dri-devel, Laurent Pinchart, Thierry Reding,
Guennady Liakhovetski, linux-media, Tomi Valkeinen,
Stephen Warren, kernel
In-Reply-To: <1351675689-26814-1-git-send-email-s.trumtrar@pengutronix.de>
Get videomode from devicetree in a format appropriate for the
backend. drm_display_mode and fb_videomode are supported atm.
Uses the display signal timings from of_display_timings
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
drivers/of/Kconfig | 6 ++++++
drivers/of/Makefile | 1 +
drivers/of/of_videomode.c | 47 ++++++++++++++++++++++++++++++++++++++++++
include/linux/of_videomode.h | 15 ++++++++++++++
4 files changed, 69 insertions(+)
create mode 100644 drivers/of/of_videomode.c
create mode 100644 include/linux/of_videomode.h
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 781e773..0575ffe 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -89,4 +89,10 @@ config OF_DISPLAY_TIMINGS
help
helper to parse display timings from the devicetree
+config OF_VIDEOMODE
+ def_bool y
+ depends on VIDEOMODE
+ help
+ helper to get videomodes from the devicetree
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index c8e9603..09d556f 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -12,3 +12,4 @@ obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
obj-$(CONFIG_OF_DISPLAY_TIMINGS) += of_display_timings.o
+obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
new file mode 100644
index 0000000..91a26fc
--- /dev/null
+++ b/drivers/of/of_videomode.c
@@ -0,0 +1,47 @@
+/*
+ * generic videomode helper
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/of.h>
+#include <linux/of_display_timings.h>
+#include <linux/of_videomode.h>
+#include <linux/export.h>
+
+/**
+ * of_get_videomode - get the videomode #<index> from devicetree
+ * @np - devicenode with the display_timings
+ * @vm - set to return value
+ * @index - index into list of display_timings
+ * DESCRIPTION:
+ * Get a list of all display timings and put the one
+ * specified by index into *vm. This function should only be used, if
+ * only one videomode is to be retrieved. A driver that needs to work
+ * with multiple/all videomodes should work with
+ * of_get_display_timing_list instead.
+ **/
+int of_get_videomode(struct device_node *np, struct videomode *vm, int index)
+{
+ struct display_timings *disp;
+ int ret;
+
+ disp = of_get_display_timing_list(np);
+ if (!disp) {
+ pr_err("%s: no timings specified\n", __func__);
+ return -EINVAL;
+ }
+
+ if (index = OF_USE_NATIVE_MODE)
+ index = disp->native_mode;
+
+ ret = videomode_from_timing(disp, vm, index);
+ if (ret)
+ return ret;
+
+ display_timings_release(disp);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_videomode);
diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
new file mode 100644
index 0000000..01518e6
--- /dev/null
+++ b/include/linux/of_videomode.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * videomode of-helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_VIDEOMODE_H
+#define __LINUX_OF_VIDEOMODE_H
+
+#include <linux/videomode.h>
+
+int of_get_videomode(struct device_node *np, struct videomode *vm, int index);
+#endif /* __LINUX_OF_VIDEOMODE_H */
--
1.7.10.4
^ permalink raw reply related
* [PATCH v7 2/8] of: add helper to parse display timings
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Rob Herring, linux-fbdev, dri-devel,
Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
linux-media, Tomi Valkeinen, Stephen Warren, kernel
In-Reply-To: <1351675689-26814-1-git-send-email-s.trumtrar@pengutronix.de>
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
.../devicetree/bindings/video/display-timings.txt | 139 +++++++++++++++
drivers/of/Kconfig | 6 +
drivers/of/Makefile | 1 +
drivers/of/of_display_timings.c | 185 ++++++++++++++++++++
include/linux/of_display_timings.h | 20 +++
5 files changed, 351 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
create mode 100644 drivers/of/of_display_timings.c
create mode 100644 include/linux/of_display_timings.h
diff --git a/Documentation/devicetree/bindings/video/display-timings.txt b/Documentation/devicetree/bindings/video/display-timings.txt
new file mode 100644
index 0000000..04c94a3
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/display-timings.txt
@@ -0,0 +1,139 @@
+display-timings bindings
+=========
+
+display-timings-node
+------------
+
+required properties:
+ - none
+
+optional properties:
+ - native-mode: the native mode for the display, in case multiple modes are
+ provided. When omitted, assume the first node is the native.
+
+timings-subnode
+---------------
+
+required properties:
+ - hactive, vactive: Display resolution
+ - hfront-porch, hback-porch, hsync-len: Horizontal Display timing parameters
+ in pixels
+ vfront-porch, vback-porch, vsync-len: Vertical display timing parameters in
+ lines
+ - clock-frequency: displayclock in Hz
+
+optional properties:
+ - hsync-active : Hsync pulse is active low/high/ignored
+ - vsync-active : Vsync pulse is active low/high/ignored
+ - de-active : Data-Enable pulse is active low/high/ignored
+ - pixelclk-inverted : pixelclock is inverted/non-inverted/ignored
+ - interlaced (bool)
+ - doublescan (bool)
+
+All the optional properties that are not bool follow the following logic:
+ <1> : high active
+ <0> : low active
+ omitted : not used on hardware
+
+There are different ways of describing the capabilities of a display. The devicetree
+representation corresponds to the one commonly found in datasheets for displays.
+If a display supports multiple signal timings, the native-mode can be specified.
+
+The parameters are defined as
+
+struct display_timing
+=========+
+ +----------+---------------------------------------------+----------+-------+
+ | | ↑ | | |
+ | | |vback_porch | | |
+ | | ↓ | | |
+ +----------###############################################----------+-------+
+ | # ↑ # | |
+ | # | # | |
+ | hback # | # hfront | hsync |
+ | porch # | hactive # porch | len |
+ |<-------->#<---------------+--------------------------->#<-------->|<----->|
+ | # | # | |
+ | # |vactive # | |
+ | # | # | |
+ | # ↓ # | |
+ +----------###############################################----------+-------+
+ | | ↑ | | |
+ | | |vfront_porch | | |
+ | | ↓ | | |
+ +----------+---------------------------------------------+----------+-------+
+ | | ↑ | | |
+ | | |vsync_len | | |
+ | | ↓ | | |
+ +----------+---------------------------------------------+----------+-------+
+
+
+Example:
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: 1920p24 {
+ /* 1920x1080p24 */
+ clock = <52000000>;
+ hactive = <1920>;
+ vactive = <1080>;
+ hfront-porch = <25>;
+ hback-porch = <25>;
+ hsync-len = <25>;
+ vback-porch = <2>;
+ vfront-porch = <2>;
+ vsync-len = <2>;
+ hsync-active = <1>;
+ };
+ };
+
+Every required property also supports the use of ranges, so the commonly used
+datasheet description with <min typ max>-tuples can be used.
+
+Example:
+
+ timing1: timing {
+ /* 1920x1080p24 */
+ clock = <148500000>;
+ hactive = <1920>;
+ vactive = <1080>;
+ hsync-len = <0 44 60>;
+ hfront-porch = <80 88 95>;
+ hback-porch = <100 148 160>;
+ vfront-porch = <0 4 6>;
+ vback-porch = <0 36 50>;
+ vsync-len = <0 5 6>;
+ };
+
+
+Usage in backend
+========
+
+A backend driver may choose to use the display-timings directly and convert the timing
+ranges to a suitable mode. Or it may just use the conversion of the display timings
+to the required mode via the generic videomode struct.
+
+ dtb
+ |
+ | of_get_display_timing_list
+ ↓
+ struct display_timings
+ |
+ | videomode_from_timing
+ ↓
+ --- struct videomode ---
+ | |
+ videomode_to_displaymode | | videomode_to_fb_videomode
+ ↓ ↓
+ drm_display_mode fb_videomode
+
+The functions of_get_fb_videomode and of_get_display_mode are provided
+to conveniently get the respective mode representation from the devicetree.
+
+Conversion to videomode
+===========+
+As device drivers normally work with some kind of video mode, the timings can be
+converted (may be just a simple copying of the typical value) to a generic videomode
+structure which then can be converted to the according mode used by the backend.
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index dfba3e6..781e773 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -83,4 +83,10 @@ config OF_MTD
depends on MTD
def_bool y
+config OF_DISPLAY_TIMINGS
+ def_bool y
+ depends on DISPLAY_TIMING
+ help
+ helper to parse display timings from the devicetree
+
endmenu # OF
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index e027f44..c8e9603 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_OF_MDIO) += of_mdio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
obj-$(CONFIG_OF_MTD) += of_mtd.o
+obj-$(CONFIG_OF_DISPLAY_TIMINGS) += of_display_timings.o
diff --git a/drivers/of/of_display_timings.c b/drivers/of/of_display_timings.c
new file mode 100644
index 0000000..388fe4c
--- /dev/null
+++ b/drivers/of/of_display_timings.c
@@ -0,0 +1,185 @@
+/*
+ * OF helpers for parsing display timings
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/of_display_timings.h>
+
+/**
+ * parse_property - parse timing_entry from device_node
+ * @np: device_node with the property
+ * @name: name of the property
+ * @result: will be set to the return value
+ *
+ * DESCRIPTION:
+ * Every display_timing can be specified with either just the typical value or
+ * a range consisting of min/typ/max. This function helps handling this
+ **/
+static int parse_property(struct device_node *np, char *name,
+ struct timing_entry *result)
+{
+ struct property *prop;
+ int length;
+ int cells;
+ int ret;
+
+ prop = of_find_property(np, name, &length);
+ if (!prop) {
+ pr_err("%s: could not find property %s\n", __func__, name);
+ return -EINVAL;
+ }
+
+ cells = length / sizeof(u32);
+ if (cells = 1) {
+ ret = of_property_read_u32_array(np, name, &result->typ, cells);
+ result->min = result->typ;
+ result->max = result->typ;
+ } else if (cells = 3) {
+ ret = of_property_read_u32_array(np, name, &result->min, cells);
+ } else {
+ pr_err("%s: illegal timing specification in %s\n", __func__, name);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * of_get_display_timing - parse display_timing entry from device_node
+ * @np: device_node with the properties
+ **/
+struct display_timing *of_get_display_timing(struct device_node *np)
+{
+ struct display_timing *dt;
+ int ret = 0;
+
+ dt = kzalloc(sizeof(*dt), GFP_KERNEL);
+ if (!dt) {
+ pr_err("%s: could not allocate display_timing struct\n", __func__);
+ return NULL;
+ }
+
+ ret |= parse_property(np, "hback-porch", &dt->hback_porch);
+ ret |= parse_property(np, "hfront-porch", &dt->hfront_porch);
+ ret |= parse_property(np, "hactive", &dt->hactive);
+ ret |= parse_property(np, "hsync-len", &dt->hsync_len);
+ ret |= parse_property(np, "vback-porch", &dt->vback_porch);
+ ret |= parse_property(np, "vfront-porch", &dt->vfront_porch);
+ ret |= parse_property(np, "vactive", &dt->vactive);
+ ret |= parse_property(np, "vsync-len", &dt->vsync_len);
+ ret |= parse_property(np, "clock-frequency", &dt->pixelclock);
+
+ of_property_read_u32(np, "vsync-active", &dt->vsync_pol_active);
+ of_property_read_u32(np, "hsync-active", &dt->hsync_pol_active);
+ of_property_read_u32(np, "de-active", &dt->de_pol_active);
+ of_property_read_u32(np, "pixelclk-inverted", &dt->pixelclk_pol);
+ dt->interlaced = of_property_read_bool(np, "interlaced");
+ dt->doublescan = of_property_read_bool(np, "doublescan");
+
+ if (ret) {
+ pr_err("%s: error reading timing properties\n", __func__);
+ return NULL;
+ }
+
+ return dt;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timing);
+
+/**
+ * of_get_display_timing_list - parse all display_timing entries from a device_node
+ * @np: device_node with the subnodes
+ **/
+struct display_timings *of_get_display_timing_list(struct device_node *np)
+{
+ struct device_node *timings_np;
+ struct device_node *entry;
+ struct device_node *native_mode;
+ struct display_timings *disp;
+
+ if (!np) {
+ pr_err("%s: no devicenode given\n", __func__);
+ return NULL;
+ }
+
+ timings_np = of_find_node_by_name(np, "display-timings");
+ if (!timings_np) {
+ pr_err("%s: could not find display-timings node\n", __func__);
+ return NULL;
+ }
+
+ disp = kzalloc(sizeof(*disp), GFP_KERNEL);
+
+ entry = of_parse_phandle(timings_np, "native-mode", 0);
+ /* assume first child as native mode if none provided */
+ if (!entry)
+ entry = of_get_next_child(np, NULL);
+ if (!entry) {
+ pr_err("%s: no timing specifications given\n", __func__);
+ return NULL;
+ }
+
+ pr_info("%s: using %s as default timing\n", __func__, entry->name);
+
+ native_mode = entry;
+
+ disp->num_timings = of_get_child_count(timings_np);
+ disp->timings = kzalloc(sizeof(struct display_timing *)*disp->num_timings,
+ GFP_KERNEL);
+ disp->num_timings = 0;
+ disp->native_mode = 0;
+
+ for_each_child_of_node(timings_np, entry) {
+ struct display_timing *dt;
+
+ dt = of_get_display_timing(entry);
+ if (!dt) {
+ /* to not encourage wrong devicetrees, fail in case of an error */
+ pr_err("%s: error in timing %d\n", __func__, disp->num_timings+1);
+ return NULL;
+ }
+
+ if (native_mode = entry)
+ disp->native_mode = disp->num_timings;
+
+ disp->timings[disp->num_timings] = dt;
+ disp->num_timings++;
+ }
+ of_node_put(timings_np);
+
+ if (disp->num_timings > 0)
+ pr_info("%s: got %d timings. Using timing #%d as default\n", __func__,
+ disp->num_timings , disp->native_mode + 1);
+ else {
+ pr_err("%s: no valid timings specified\n", __func__);
+ return NULL;
+ }
+ return disp;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timing_list);
+
+/**
+ * of_display_timings_exists - check if a display-timings node is provided
+ * @np: device_node with the timing
+ **/
+int of_display_timings_exists(struct device_node *np)
+{
+ struct device_node *timings_np;
+ struct device_node *default_np;
+
+ if (!np)
+ return -EINVAL;
+
+ timings_np = of_parse_phandle(np, "display-timings", 0);
+ if (!timings_np)
+ return -EINVAL;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(of_display_timings_exists);
diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
new file mode 100644
index 0000000..e4e1f22
--- /dev/null
+++ b/include/linux/of_display_timings.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * display timings of helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_DISPLAY_TIMINGS_H
+#define __LINUX_OF_DISPLAY_TIMINGS_H
+
+#include <linux/display_timing.h>
+
+#define OF_USE_NATIVE_MODE -1
+
+struct display_timings *of_get_display_timing_list(struct device_node *np);
+struct display_timing *of_get_display_timing(struct device_node *np);
+int of_display_timings_exists(struct device_node *np);
+
+#endif
--
1.7.10.4
^ permalink raw reply related
* [PATCH v7 1/8] video: add display_timing struct and helpers
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Rob Herring, linux-fbdev, dri-devel,
Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
linux-media, Tomi Valkeinen, Stephen Warren, kernel
In-Reply-To: <1351675689-26814-1-git-send-email-s.trumtrar@pengutronix.de>
Add display_timing structure and the according helper functions. This allows
the description of a display via its supported timing parameters.
Every timing parameter can be specified as a single value or a range
<min typ max>.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
drivers/video/Kconfig | 5 +++
drivers/video/Makefile | 1 +
drivers/video/display_timing.c | 24 ++++++++++++++
include/linux/display_timing.h | 69 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 99 insertions(+)
create mode 100644 drivers/video/display_timing.c
create mode 100644 include/linux/display_timing.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d799..1421fc8 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -33,6 +33,11 @@ config VIDEO_OUTPUT_CONTROL
This framework adds support for low-level control of the video
output switch.
+config DISPLAY_TIMING
+ bool "Enable display timings helpers"
+ help
+ Say Y here, to use the display timing helpers.
+
menuconfig FB
tristate "Support for frame buffer devices"
---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..552c045 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -167,3 +167,4 @@ obj-$(CONFIG_FB_VIRTUAL) += vfb.o
#video output switch sysfs driver
obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c
new file mode 100644
index 0000000..9ccfdb3
--- /dev/null
+++ b/drivers/video/display_timing.c
@@ -0,0 +1,24 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/slab.h>
+#include <linux/display_timing.h>
+
+void timings_release(struct display_timings *disp)
+{
+ int i;
+
+ for (i = 0; i < disp->num_timings; i++)
+ kfree(disp->timings[i]);
+}
+
+void display_timings_release(struct display_timings *disp)
+{
+ timings_release(disp);
+ kfree(disp->timings);
+}
diff --git a/include/linux/display_timing.h b/include/linux/display_timing.h
new file mode 100644
index 0000000..aa02a12
--- /dev/null
+++ b/include/linux/display_timing.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * description of display timings
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_DISPLAY_TIMINGS_H
+#define __LINUX_DISPLAY_TIMINGS_H
+
+#include <linux/types.h>
+
+struct timing_entry {
+ u32 min;
+ u32 typ;
+ u32 max;
+};
+
+struct display_timing {
+ struct timing_entry pixelclock;
+
+ struct timing_entry hactive;
+ struct timing_entry hfront_porch;
+ struct timing_entry hback_porch;
+ struct timing_entry hsync_len;
+
+ struct timing_entry vactive;
+ struct timing_entry vfront_porch;
+ struct timing_entry vback_porch;
+ struct timing_entry vsync_len;
+
+ unsigned int vsync_pol_active;
+ unsigned int hsync_pol_active;
+ unsigned int de_pol_active;
+ unsigned int pixelclk_pol;
+ bool interlaced;
+ bool doublescan;
+};
+
+struct display_timings {
+ unsigned int num_timings;
+ unsigned int native_mode;
+
+ struct display_timing **timings;
+};
+
+/* placeholder function until ranges are really needed */
+static inline u32 display_timing_get_value(struct timing_entry *te, int index)
+{
+ return te->typ;
+}
+
+static inline struct display_timing *display_timings_get(struct display_timings *disp,
+ int index)
+{
+ struct display_timing *dt;
+
+ if (disp->num_timings > index) {
+ dt = disp->timings[index];
+ return dt;
+ } else
+ return NULL;
+}
+
+void timings_release(struct display_timings *disp);
+void display_timings_release(struct display_timings *disp);
+
+#endif
--
1.7.10.4
^ permalink raw reply related
* [PATCH v7 0/8] of: add display helper
From: Steffen Trumtrar @ 2012-10-31 9:28 UTC (permalink / raw)
To: devicetree-discuss
Cc: Steffen Trumtrar, Rob Herring, linux-fbdev, dri-devel,
Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
linux-media, Tomi Valkeinen, Stephen Warren, kernel
Hi!
Finally, v7 of the series.
Changes since v6:
- get rid of some empty lines etc.
- move functions to their subsystems
- split of_ from non-of_ functions
- add at least some kerneldoc to some functions
Regards,
Steffen
Steffen Trumtrar (8):
video: add display_timing struct and helpers
of: add helper to parse display timings
of: add generic videomode description
video: add videomode helpers
fbmon: add videomode helpers
fbmon: add of_videomode helpers
drm_modes: add videomode helpers
drm_modes: add of_videomode helpers
.../devicetree/bindings/video/display-timings.txt | 139 +++++++++++++++
drivers/gpu/drm/drm_modes.c | 78 +++++++++
drivers/of/Kconfig | 12 ++
drivers/of/Makefile | 2 +
drivers/of/of_display_timings.c | 185 ++++++++++++++++++++
drivers/of/of_videomode.c | 47 +++++
drivers/video/Kconfig | 11 ++
drivers/video/Makefile | 2 +
drivers/video/display_timing.c | 24 +++
drivers/video/fbmon.c | 76 ++++++++
drivers/video/videomode.c | 44 +++++
include/drm/drmP.h | 8 +
include/linux/display_timing.h | 69 ++++++++
include/linux/fb.h | 5 +
include/linux/of_display_timings.h | 20 +++
include/linux/of_videomode.h | 15 ++
include/linux/videomode.h | 36 ++++
17 files changed, 773 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
create mode 100644 drivers/of/of_display_timings.c
create mode 100644 drivers/of/of_videomode.c
create mode 100644 drivers/video/display_timing.c
create mode 100644 drivers/video/videomode.c
create mode 100644 include/linux/display_timing.h
create mode 100644 include/linux/of_display_timings.h
create mode 100644 include/linux/of_videomode.h
create mode 100644 include/linux/videomode.h
--
1.7.10.4
^ permalink raw reply
* [PATCH 2/2] ARM: dts: mxs: add oled support for the cfa-10036
From: Maxime Ripard @ 2012-10-31 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351674774-2891-1-git-send-email-maxime.ripard@free-electrons.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Brian Lilly <brian@crystalfontz.com>
---
arch/arm/boot/dts/imx28-cfa10036.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm/boot/dts/imx28-cfa10036.dts b/arch/arm/boot/dts/imx28-cfa10036.dts
index c03a577..816cae9 100644
--- a/arch/arm/boot/dts/imx28-cfa10036.dts
+++ b/arch/arm/boot/dts/imx28-cfa10036.dts
@@ -33,11 +33,30 @@
};
apbx@80040000 {
+ pwm: pwm@80064000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm4_pins_a>;
+ status = "okay";
+ };
+
duart: serial@80074000 {
pinctrl-names = "default";
pinctrl-0 = <&duart_pins_b>;
status = "okay";
};
+
+ i2c0: i2c@80058000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c0_pins_b>;
+ status = "okay";
+
+ ssd1307: oled@3c {
+ compatible = "solomon,ssd1307fb-i2c";
+ reg = <0x3c>;
+ pwms = <&pwm 4 3000>;
+ reset-gpios = <&gpio2 7 0>;
+ };
+ };
};
};
--
1.7.9.5
^ permalink raw reply related
* [PATCH 1/2] video: Add support for the Solomon SSD1307 OLED Controller
From: Maxime Ripard @ 2012-10-31 9:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1351674774-2891-1-git-send-email-maxime.ripard@free-electrons.com>
This patch adds support for the Solomon SSD1307 OLED
controller found on the Crystalfontz CFA10036 board.
This controller can drive a display with a resolution up
to 128x39 and can operate over I2C or SPI.
The current driver has only been tested on the CFA-10036,
that is using this controller over I2C to driver a 96x16
OLED screen.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Brian Lilly <brian@crystalfontz.com>
---
.../devicetree/bindings/video/ssd1307fb.txt | 24 ++
drivers/video/Kconfig | 13 +
drivers/video/Makefile | 1 +
drivers/video/ssd1307fb.c | 407 ++++++++++++++++++++
4 files changed, 445 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt
create mode 100644 drivers/video/ssd1307fb.c
diff --git a/Documentation/devicetree/bindings/video/ssd1307fb.txt b/Documentation/devicetree/bindings/video/ssd1307fb.txt
new file mode 100644
index 0000000..3d0060c
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/ssd1307fb.txt
@@ -0,0 +1,24 @@
+* Solomon SSD1307 Framebuffer Driver
+
+Required properties:
+ - compatible: Should be "solomon,ssd1307fb-<bus>". The only supported bus for
+ now is i2c.
+ - reg: Should contain address of the controller on the I2C bus. Most likely
+ 0x3c or 0x3d
+ - pwm: Should contain the pwm to use according to the OF device tree PWM
+ specification [0]
+ - reset-gpios: Should contain the GPIO used to reset the OLED display
+
+Optional properties:
+ - reset-active-low: Is the reset gpio is active on physical low?
+
+[0]: Documentation/devicetree/bindings/pwm/pwm.txt
+
+Examples:
+ssd1307: oled@3c {
+ compatible = "solomon,ssd1307fb-i2c";
+ reg = <0x3c>;
+ pwms = <&pwm 4 3000>;
+ reset-gpios = <&gpio2 7>;
+ reset-active-low;
+};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d799..294de9c 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2442,4 +2442,17 @@ config FB_SH_MOBILE_MERAM
Up to 4 memory channels can be configured, allowing 4 RGB or
2 YCbCr framebuffers to be configured.
+config FB_SSD1307
+ tristate "Solomon SSD1307 framebuffer support"
+ depends on FB && I2C
+ select FB_SYS_FOPS
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select PWM
+ help
+ This driver implements support for the Solomon SSD1307
+ OLED controller over I2C.
+
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..768a137 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -161,6 +161,7 @@ obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o
obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
+obj-$(CONFIG_FB_SSD1307) += ssd1307fb.o
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
new file mode 100644
index 0000000..ed18f19
--- /dev/null
+++ b/drivers/video/ssd1307fb.c
@@ -0,0 +1,407 @@
+/*
+ * Driver for the Solomon SSD1307 OLED controler
+ *
+ * Copyright 2012 Free Electrons
+ *
+ * Licensed under the GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/uaccess.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+
+#define SSD1307FB_WIDTH 96
+#define SSD1307FB_HEIGHT 16
+
+#define SSD1307FB_DATA 0x40
+#define SSD1307FB_COMMAND 0x80
+
+#define SSD1307FB_CONTRAST 0x81
+#define SSD1307FB_SEG_REMAP_ON 0xa1
+#define SSD1307FB_DISPLAY_OFF 0xae
+#define SSD1307FB_DISPLAY_ON 0xaf
+#define SSD1307FB_START_PAGE_ADDRESS 0xb0
+
+struct ssd1307fb_par {
+ struct i2c_client *client;
+ struct fb_info *info;
+ struct pwm_device *pwm;
+ u32 pwm_period;
+ int reset;
+};
+
+static struct fb_fix_screeninfo ssd1307fb_fix __devinitdata = {
+ .id = "Solomon SSD1307",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO10,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .line_length = SSD1307FB_WIDTH / 8,
+ .accel = FB_ACCEL_NONE,
+};
+
+static struct fb_var_screeninfo ssd1307fb_var __devinitdata = {
+ .xres = SSD1307FB_WIDTH,
+ .yres = SSD1307FB_HEIGHT,
+ .xres_virtual = SSD1307FB_WIDTH,
+ .yres_virtual = SSD1307FB_HEIGHT,
+ .bits_per_pixel = 1,
+};
+
+static int ssd1307fb_write_array(struct i2c_client *client, u8 type, u8* cmd, u32 len)
+{
+ u8 *buf;
+ int ret = 0;
+
+ buf = kzalloc(len + 1, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Couldn't allocate sending buffer.\n");
+ return -ENOMEM;
+ }
+
+ buf[0] = type;
+ memcpy(buf + 1, cmd, len);
+
+ ret = i2c_master_send(client, buf, len + 1);
+ if (ret != len + 1) {
+ dev_err(&client->dev, "Couldn't send I2C command.\n");
+ goto error;
+ }
+
+error:
+ kfree(buf);
+ return ret;
+}
+
+static inline int ssd1307fb_write_cmd_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+ return ssd1307fb_write_array(client, SSD1307FB_COMMAND, cmd, len);
+}
+
+static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd)
+{
+ return ssd1307fb_write_cmd_array(client, &cmd, 1);
+}
+
+static inline int ssd1307fb_write_data_array(struct i2c_client *client, u8* cmd, u32 len)
+{
+ return ssd1307fb_write_array(client, SSD1307FB_DATA, cmd, len);
+}
+
+static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data)
+{
+ return ssd1307fb_write_data_array(client, &data, 1);
+}
+
+static void ssd1307fb_update_display(struct ssd1307fb_par *par)
+{
+ u8 *vmem = par->info->screen_base;
+ int i, j, k;
+
+ /*
+ * The screen is divided in pages, each having a height of 8
+ * pixels, and the width of the screen. When sending a byte of
+ * data to the controller, it gives the 8 bits for the current
+ * column. I.e, the first byte are the 8 bits of the first
+ * column, then the 8 bits for the second column, etc.
+ *
+ *
+ * Representation of the screen, assuming it is 5 bits
+ * wide. Each letter-number combination is a bit that controls
+ * one pixel.
+ *
+ * A0 A1 A2 A3 A4
+ * B0 B1 B2 B3 B4
+ * C0 C1 C2 C3 C4
+ * D0 D1 D2 D3 D4
+ * E0 E1 E2 E3 E4
+ * F0 F1 F2 F3 F4
+ * G0 G1 G2 G3 G4
+ * H0 H1 H2 H3 H4
+ *
+ * If you want to update this screen, you need to send 5 bytes:
+ * (1) A0 B0 C0 D0 E0 F0 G0 H0
+ * (2) A1 B1 C1 D1 E1 F1 G1 H1
+ * (3) A2 B2 C2 D2 E2 F2 G2 H2
+ * (4) A3 B3 C3 D3 E3 F3 G3 H3
+ * (5) A4 B4 C4 D4 E4 F4 G4 H4
+ */
+
+ for (i = 0; i < (SSD1307FB_HEIGHT / 8); i++) {
+ ssd1307fb_write_cmd(par->client, SSD1307FB_START_PAGE_ADDRESS + (i + 1));
+ ssd1307fb_write_cmd(par->client, 0x00);
+ ssd1307fb_write_cmd(par->client, 0x10);
+
+ for (j = 0; j < SSD1307FB_WIDTH; j++) {
+ u8 buf = 0;
+ for (k = 0; k < 8; k++) {
+ u32 page_length = SSD1307FB_WIDTH * i;
+ u32 index = page_length + (SSD1307FB_WIDTH * k + j) / 8;
+ u8 byte = *(vmem + index);
+ u8 bit = byte & (1 << (7 - (j % 8)));
+ bit = bit >> (7 - (j % 8));
+ buf |= bit << k;
+ }
+ ssd1307fb_write_data(par->client, buf);
+ }
+ }
+}
+
+
+static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct ssd1307fb_par *par = info->par;
+ unsigned long total_size;
+ unsigned long p = *ppos;
+ u8 __iomem *dst;
+ int err = 0;
+
+ total_size = info->screen_size;
+
+ if (total_size = 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ dst = (void __force *) (info->screen_base + p);
+
+ if (copy_from_user(dst, buf, count))
+ err = -EFAULT;
+
+ if (!err)
+ *ppos += count;
+
+ ssd1307fb_update_display(par);
+
+ return (err) ? err : count;
+}
+
+static void ssd1307fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_fillrect(info, rect);
+ ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_copyarea(info, area);
+ ssd1307fb_update_display(par);
+}
+
+static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct ssd1307fb_par *par = info->par;
+ sys_imageblit(info, image);
+ ssd1307fb_update_display(par);
+}
+
+static struct fb_ops ssd1307fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_read = fb_sys_read,
+ .fb_write = ssd1307fb_write,
+ .fb_fillrect = ssd1307fb_fillrect,
+ .fb_copyarea = ssd1307fb_copyarea,
+ .fb_imageblit = ssd1307fb_imageblit,
+};
+
+static void ssd1307fb_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ ssd1307fb_update_display(info->par);
+}
+
+static struct fb_deferred_io ssd1307fb_defio = {
+ .delay = HZ,
+ .deferred_io = ssd1307fb_deferred_io,
+};
+
+static int __devinit ssd1307fb_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct fb_info *info;
+ u32 vmem_size = SSD1307FB_WIDTH * SSD1307FB_HEIGHT / 8;
+ struct ssd1307fb_par *par;
+ u8 *vmem;
+ int ret;
+
+ if (!client->dev.of_node) {
+ dev_err(&client->dev, "No device tree data found!\n");
+ return -EINVAL;
+ }
+
+ info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
+ if (!info) {
+ dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
+ return -ENOMEM;
+ }
+
+ vmem = devm_kzalloc(&client->dev, vmem_size, GFP_KERNEL);
+ if (!vmem) {
+ dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
+ ret = -ENOMEM;
+ goto fb_alloc_error;
+ }
+
+ info->fbops = &ssd1307fb_ops;
+ info->fix = ssd1307fb_fix;
+ info->fbdefio = &ssd1307fb_defio;
+
+ info->var = ssd1307fb_var;
+ info->var.red.length = 1;
+ info->var.red.offset = 0;
+ info->var.green.length = 1;
+ info->var.green.offset = 0;
+ info->var.blue.length = 1;
+ info->var.blue.offset = 0;
+
+ info->screen_base = (u8 __force __iomem *)vmem;
+ info->fix.smem_start = (unsigned long)vmem;
+ info->fix.smem_len = vmem_size;
+
+ fb_deferred_io_init(info);
+
+ par = info->par;
+ par->info = info;
+ par->client = client;
+
+ par->reset = of_get_named_gpio(client->dev.of_node,
+ "reset-gpios", 0);
+ if (!gpio_is_valid(par->reset)) {
+ ret = -EINVAL;
+ goto reset_oled_error;
+ }
+
+ ret = devm_gpio_request_one(&client->dev, par->reset,
+ GPIOF_OUT_INIT_HIGH,
+ "oled-reset");
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to request gpio %d: %d\n",
+ par->reset, ret);
+ goto reset_oled_error;
+ }
+
+ par->pwm = pwm_get(&client->dev, NULL);
+ if (IS_ERR(par->pwm)) {
+ dev_err(&client->dev, "Could not get PWM from device tree!\n");
+ ret = PTR_ERR(par->pwm);
+ goto pwm_error;
+ }
+
+ par->pwm_period = pwm_get_period(par->pwm);
+
+ dev_dbg(&client->dev, "Using PWM%d with a %dns period.\n", par->pwm->pwm, par->pwm_period);
+
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(&client->dev, "Couldn't register the framebuffer\n");
+ goto fbreg_error;
+ }
+
+ i2c_set_clientdata(client, info);
+
+ /* Reset the screen */
+ gpio_set_value(par->reset, 0);
+ udelay(4);
+ gpio_set_value(par->reset, 1);
+ udelay(4);
+
+ /* Enable the PWM */
+ pwm_config(par->pwm, par->pwm_period / 2, par->pwm_period);
+ pwm_enable(par->pwm);
+
+ /* Map column 127 of the OLED to segment 0 */
+ ret = ssd1307fb_write_cmd(client, SSD1307FB_SEG_REMAP_ON);
+ if (ret < 0) {
+ dev_err(&client->dev, "Couldn't remap the screen.\n");
+ goto remap_error;
+ }
+
+ /* Turn on the display */
+ ret = ssd1307fb_write_cmd(client, SSD1307FB_DISPLAY_ON);
+ if (ret < 0) {
+ dev_err(&client->dev, "Couldn't turn the display on.\n");
+ goto remap_error;
+ }
+
+ dev_info(&client->dev, "fb%d: %s framebuffer device registered, using %d bytes of video memory\n", info->node, info->fix.id, vmem_size);
+
+ return 0;
+
+remap_error:
+ unregister_framebuffer(info);
+ pwm_disable(par->pwm);
+fbreg_error:
+ pwm_put(par->pwm);
+pwm_error:
+reset_oled_error:
+ fb_deferred_io_cleanup(info);
+fb_alloc_error:
+ framebuffer_release(info);
+ return ret;
+}
+
+static int __devexit ssd1307fb_remove(struct i2c_client *client)
+{
+ struct fb_info *info = i2c_get_clientdata(client);
+ struct ssd1307fb_par *par = info->par;
+
+ unregister_framebuffer(info);
+ pwm_disable(par->pwm);
+ pwm_put(par->pwm);
+ fb_deferred_io_cleanup(info);
+ framebuffer_release(info);
+
+ return 0;
+}
+
+static const struct i2c_device_id ssd1307fb_i2c_id[] = {
+ { "ssd1307fb", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ssd1307fb_i2c_id);
+
+static const struct of_device_id ssd1307fb_of_match[] = {
+ { .compatible = "solomon,ssd1307fb-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ssd1307fb_of_match);
+
+static struct i2c_driver ssd1307fb_driver = {
+ .probe = ssd1307fb_probe,
+ .remove = __devexit_p(ssd1307fb_remove),
+ .id_table = ssd1307fb_i2c_id,
+ .driver = {
+ .name = "ssd1307fb",
+ .of_match_table = of_match_ptr(ssd1307fb_of_match),
+ .owner = THIS_MODULE,
+ },
+};
+
+module_i2c_driver(ssd1307fb_driver);
+
+MODULE_DESCRIPTION("FB driver for the Solomon SSD1307 OLED controler");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
--
1.7.9.5
^ permalink raw reply related
* [PATCHv6 0/2] Add support for the OLED in the CFA10036
From: Maxime Ripard @ 2012-10-31 9:12 UTC (permalink / raw)
To: linux-arm-kernel
Hi everyone,
This patchset adds support for the solomon SSD1307 OLED controller present
in the CFA-10036 board.
It first adds the framebuffer driver for this controller, and then the
needed bits to enable it in the cfa10036 dts.
Considering that the driver has been around since almost three months now, and
I have not received any comments on the framebuffer part, I'd very much like it
to be included in 3.8 if possible.
Thanks,
Maxime
Changes from v5:
* Fixed bogus reset code
Maxime Ripard (2):
video: Add support for the Solomon SSD1307 OLED Controller
ARM: dts: mxs: add oled support for the cfa-10036
.../devicetree/bindings/video/ssd1307fb.txt | 24 ++
arch/arm/boot/dts/imx28-cfa10036.dts | 19 +
drivers/video/Kconfig | 13 +
drivers/video/Makefile | 1 +
drivers/video/ssd1307fb.c | 407 ++++++++++++++++++++
5 files changed, 464 insertions(+)
create mode 100644 Documentation/devicetree/bindings/video/ssd1307fb.txt
create mode 100644 drivers/video/ssd1307fb.c
--
1.7.9.5
^ permalink raw reply
* Re: [PATCH 05/12] OMAPDSS: DSI: skip odd dividers when pck >= 100MHz
From: Archit Taneja @ 2012-10-31 7:44 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <5090D29A.3050605@ti.com>
On Wednesday 31 October 2012 12:56 PM, Tomi Valkeinen wrote:
> On 2012-10-31 08:45, Archit Taneja wrote:
>> On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
>>> The DSI PLL and HSDivider can be used to generate the pixel clock for
>>> LCD overlay manager, which then goes to DPI output. On the DPI output
>>> pin the voltage of the signal is shifted from the OMAP's internal
>>> minimal voltage to 1.8V range. The shifting is not instant, and the
>>> higher the clock frequency, the less time there is to shift the signal
>>> to nominal voltage.
>>>
>>> If the HSDivider's divider is greater than one and odd, the resulting
>>> pixel clock does not have 50% duty cycle. For example, with a divider of
>>> 3, the duty cycle is 33%.
>>>
>>> When combining high frequency (in the area of 140MHz+) and non-50% duty
>>> cycle, it has been observed the the shifter does not have enough time to
>>> shift the voltage enough, and this leads to bad signal which is rejected
>>> by monitors.
>>
>> Is this something seen on OMAP3 also? I guess it must be since it's the
>> same DSI IP.
>
> I have not seen this on OMAP3, but I'm 99% sure the same problem happens
> there. But I guess there are many small things affecting the signal
> quality, it could be that on omap3 beagleboard the resulting signal
> voltage is still inside the standard range, even if odd dividers weaken it.
>
> And I also think that we have the same problem with logic and pixel
> clock dividers. My understanding is that all these simple dividers (i.e.
> not a PLL or such) are made the same way, and, for example, divider of 3
> is produced by keeping the output clock low for 2 cycles of the original
> clock, and high for 1 cycle. Which leads to 33% duty cycle.
>
> However, as the actual problem only materializes with high frequencies,
> in practice we don't have a problem with pck or lck dividers. The reason
> is that if we used pcd or lcd of 3, and the resulting pixel clock would
> be > 100, the incoming DSS func clock would be around 300. Which is much
> over the limit, and thus this scenario doesn't happen.
>
>>> As a workaround this patch makes the divider calculation skip all odd
>>> dividers when the required pixel clock is over 100MHz.
>>>
>>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
>>> ---
>>> drivers/video/omap2/dss/dsi.c | 5 +++++
>>> 1 file changed, 5 insertions(+)
>>>
>>> diff --git a/drivers/video/omap2/dss/dsi.c
>>> b/drivers/video/omap2/dss/dsi.c
>>> index 7d0db2b..d0e35da 100644
>>> --- a/drivers/video/omap2/dss/dsi.c
>>> +++ b/drivers/video/omap2/dss/dsi.c
>>> @@ -1386,6 +1386,11 @@ retry:
>>> cur.dsi_pll_hsdiv_dispc_clk >>> cur.clkin4ddr / cur.regm_dispc;
>>>
>>> + if (cur.regm_dispc > 1 &&
>>> + cur.regm_dispc % 2 != 0 &&
>>> + req_pck >= 1000000)
>>> + continue;
>>> +
>>
>> Why do we do the req_pck check here? Can't we do it much earlier? We
>> could bail out right in the beginning of dsi_pll_calc_clock_div_pck() if
>> we see that req_pck is greater than 100 Mhz.
>
> I think you misunderstood the patch. We don't skip or fail calculations
> for pck > 100. What we do is we skip odd dividers if pck > 100.
Ah okay. I missed that point.
Archit
^ permalink raw reply
* Re: [PATCH 12/12] OMAPDSS: DPI: always use DSI PLL if available
From: Archit Taneja @ 2012-10-31 7:38 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <1351613409-21186-13-git-send-email-tomi.valkeinen@ti.com>
On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
> We currently get the decision whether to use PRCM or DSI PLL clock for
> DPI from the board file. This is not a good way to handle it, and it
> won't work with device tree.
>
> This patch changes DPI to always use DSI PLL if it's available.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
> drivers/video/omap2/dss/dpi.c | 64 ++++++++++++++++++++++++-----------------
> 1 file changed, 37 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c
> index 267caf0..32e7dd5 100644
> --- a/drivers/video/omap2/dss/dpi.c
> +++ b/drivers/video/omap2/dss/dpi.c
> @@ -49,28 +49,30 @@ static struct {
> struct omap_dss_output output;
> } dpi;
>
> -static struct platform_device *dpi_get_dsidev(enum omap_dss_clk_source clk)
> +static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
> {
> - int dsi_module;
> -
> - dsi_module = clk = OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ? 0 : 1;
> -
> - return dsi_get_dsidev_from_id(dsi_module);
> + switch (channel) {
> + case OMAP_DSS_CHANNEL_LCD:
> + return dsi_get_dsidev_from_id(0);
> + case OMAP_DSS_CHANNEL_LCD2:
> + return dsi_get_dsidev_from_id(1);
> + default:
> + return NULL;
> + }
> }
>
> -static bool dpi_use_dsi_pll(struct omap_dss_device *dssdev)
> +static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
> {
> - if (dssdev->clocks.dispc.dispc_fclk_src =
> - OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
> - dssdev->clocks.dispc.dispc_fclk_src =
> - OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC ||
> - dssdev->clocks.dispc.channel.lcd_clk_src =
> - OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC ||
> - dssdev->clocks.dispc.channel.lcd_clk_src =
> - OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC)
> - return true;
> - else
> - return false;
> + switch (channel) {
> + case OMAP_DSS_CHANNEL_LCD:
> + return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
> + case OMAP_DSS_CHANNEL_LCD2:
> + return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
> + default:
> + /* this shouldn't happen */
> + WARN_ON(1);
> + return OMAP_DSS_CLK_SRC_FCK;
> + }
> }
>
> static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
> @@ -92,7 +94,7 @@ static int dpi_set_dsi_clk(struct omap_dss_device *dssdev,
> return r;
>
> dss_select_lcd_clk_source(mgr->id,
> - dssdev->clocks.dispc.channel.lcd_clk_src);
> + dpi_get_alt_clk_src(mgr->id));
>
> dpi.mgr_config.clock_info = dispc_cinfo;
>
> @@ -385,6 +387,8 @@ static int __init dpi_verify_dsi_pll(struct platform_device *dsidev)
>
> static int __init dpi_init_display(struct omap_dss_device *dssdev)
> {
> + struct platform_device *dsidev;
> +
> DSSDBG("init_display\n");
>
> if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
> @@ -401,17 +405,23 @@ static int __init dpi_init_display(struct omap_dss_device *dssdev)
> dpi.vdds_dsi_reg = vdds_dsi;
> }
>
> - if (dpi_use_dsi_pll(dssdev)) {
> - enum omap_dss_clk_source dispc_fclk_src > - dssdev->clocks.dispc.dispc_fclk_src;
> - dpi.dsidev = dpi_get_dsidev(dispc_fclk_src);
> + /*
> + * XXX We shouldn't need dssdev->channel for this. The dsi pll clock
> + * source for DPI is SoC integration detail, not something that should
> + * be configured in the dssdev
> + */
It is SoC integration detail, but it's flexible, it depends on which
manager is connected to DPI output. If it's connected to LCD1, the
source can be DSI1 PLL, if it's LCD2, it's source can be DSI2 PLL, if
it's connected to TV manager, it's source "has to be" HDMI PLL. And
these connections vary based on which OMAP revision we are on. We can
only be certain on OMAP3 that the source for DPI pixel clock can be
either PRCM or DSI PLL.
At the point of probe, we really don't know which manager is the DPI
output connected to. Hence, we sort of use dssdev->channel to make a
guess what manager we connect to in the future.
The right approach would be to figure this out at the time of enable,
where we know which manager the DPI output is connected to. We could
probably move the verification there too.
Archit
> + dsidev = dpi_get_dsidev(dssdev->channel);
>
> - if (dpi_verify_dsi_pll(dpi.dsidev)) {
> - dpi.dsidev = NULL;
> - DSSWARN("DSI PLL not operational\n");
> - }
> + if (dpi_verify_dsi_pll(dsidev)) {
> + dsidev = NULL;
> + DSSWARN("DSI PLL not operational\n");
> }
>
> + if (dsidev)
> + DSSDBG("using DSI PLL for DPI clock\n");
> +
> + dpi.dsidev = dsidev;
> +
> return 0;
> }
>
>
^ permalink raw reply
* Re: [PATCH 08/12] OMAPDSS: setup default dss fck
From: Tomi Valkeinen @ 2012-10-31 7:32 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <5090C5B3.6020502@ti.com>
[-- Attachment #1: Type: text/plain, Size: 3223 bytes --]
On 2012-10-31 08:31, Archit Taneja wrote:
> On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
>> We don't currently set the dss fck when starting up. This is not a
>> problem, as we setup the fck later when configuring the pixel clocks. Or
>> this is how it was for omap2, for the rest of the omaps this may not be
>> so.
>>
>> For DSI, HDMI and also for DPI when using DSI PLL, we don't need to
>> change the dss fck, and thus it may be left unconfigured. Usually the
>> dss fck is already setup fine by default, but we can't trust this.
>>
>> This patch sets the dss fck to maximum at probe time.
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
>> ---
>> drivers/video/omap2/dss/dss.c | 36
>> ++++++++++++++++++++++++++++++++++++
>> 1 file changed, 36 insertions(+)
>>
>> diff --git a/drivers/video/omap2/dss/dss.c
>> b/drivers/video/omap2/dss/dss.c
>> index 5affa86..034cc1a 100644
>> --- a/drivers/video/omap2/dss/dss.c
>> +++ b/drivers/video/omap2/dss/dss.c
>> @@ -485,6 +485,36 @@ unsigned long dss_get_dpll4_rate(void)
>> return 0;
>> }
>>
>> +static int dss_setup_default_clock(void)
>> +{
>> + unsigned long max_dss_fck, prate;
>> + unsigned fck_div;
>> + struct dss_clock_info dss_cinfo = { 0 };
>> + int r;
>> +
>> + if (dss.dpll4_m4_ck == NULL)
>> + return 0;
>> +
>> + max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
>> +
>> + prate = dss_get_dpll4_rate();
>
> Not related to this patch, but maybe we could change the
> dss_get_dpll4_rate() name and dss.dpll4_m4_clk to something better.
> Maybe something like dss_fck_parent?
I agree. Or, even better, we should fix the omap clk data/code so that
we could set the dss fck, without this trickery. But I have no idea if
that's easy or difficult.
>> +
>> + fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
>> + max_dss_fck);
>> +
>> + dss_cinfo.fck_div = fck_div;
>> +
>> + r = dss_calc_clock_rates(&dss_cinfo);
>> + if (r)
>> + return r;
>> +
>> + r = dss_set_clock_div(&dss_cinfo);
>> + if (r)
>> + return r;
>> +
>> + return 0;
>> +}
>> +
>> int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info
>> *dss_cinfo,
>> struct dispc_clock_info *dispc_cinfo)
>> {
>> @@ -913,6 +943,10 @@ static int __init omap_dsshw_probe(struct
>> platform_device *pdev)
>> dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
>> dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
>>
>> + r = dss_setup_default_clock();
>> + if (r)
>> + goto err_setup_clocks;
>
> Maybe it's safer to call this before we do a dss_runtime_get(). On
> OMAP4, DSS_FCLK is needed to access registers also. Changing it's rate
> might not be liked by the DSS HW. Also, it seems more logical to call it
> after dss_get_clocks() in omap_dsshw_probe(), then we sort of group the
> clock related stuff together.
Yes, good point. I don't think DSS has any problems with the clock
changing, as long as it's not outputting an image (but I'm not sure). In
any case, it makes sense to setup the clocks dss_get_clocks as you said.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH 05/12] OMAPDSS: DSI: skip odd dividers when pck >= 100MHz
From: Tomi Valkeinen @ 2012-10-31 7:26 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <5090C8F9.4060103@ti.com>
[-- Attachment #1: Type: text/plain, Size: 3403 bytes --]
On 2012-10-31 08:45, Archit Taneja wrote:
> On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
>> The DSI PLL and HSDivider can be used to generate the pixel clock for
>> LCD overlay manager, which then goes to DPI output. On the DPI output
>> pin the voltage of the signal is shifted from the OMAP's internal
>> minimal voltage to 1.8V range. The shifting is not instant, and the
>> higher the clock frequency, the less time there is to shift the signal
>> to nominal voltage.
>>
>> If the HSDivider's divider is greater than one and odd, the resulting
>> pixel clock does not have 50% duty cycle. For example, with a divider of
>> 3, the duty cycle is 33%.
>>
>> When combining high frequency (in the area of 140MHz+) and non-50% duty
>> cycle, it has been observed the the shifter does not have enough time to
>> shift the voltage enough, and this leads to bad signal which is rejected
>> by monitors.
>
> Is this something seen on OMAP3 also? I guess it must be since it's the
> same DSI IP.
I have not seen this on OMAP3, but I'm 99% sure the same problem happens
there. But I guess there are many small things affecting the signal
quality, it could be that on omap3 beagleboard the resulting signal
voltage is still inside the standard range, even if odd dividers weaken it.
And I also think that we have the same problem with logic and pixel
clock dividers. My understanding is that all these simple dividers (i.e.
not a PLL or such) are made the same way, and, for example, divider of 3
is produced by keeping the output clock low for 2 cycles of the original
clock, and high for 1 cycle. Which leads to 33% duty cycle.
However, as the actual problem only materializes with high frequencies,
in practice we don't have a problem with pck or lck dividers. The reason
is that if we used pcd or lcd of 3, and the resulting pixel clock would
be > 100, the incoming DSS func clock would be around 300. Which is much
over the limit, and thus this scenario doesn't happen.
>> As a workaround this patch makes the divider calculation skip all odd
>> dividers when the required pixel clock is over 100MHz.
>>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
>> ---
>> drivers/video/omap2/dss/dsi.c | 5 +++++
>> 1 file changed, 5 insertions(+)
>>
>> diff --git a/drivers/video/omap2/dss/dsi.c
>> b/drivers/video/omap2/dss/dsi.c
>> index 7d0db2b..d0e35da 100644
>> --- a/drivers/video/omap2/dss/dsi.c
>> +++ b/drivers/video/omap2/dss/dsi.c
>> @@ -1386,6 +1386,11 @@ retry:
>> cur.dsi_pll_hsdiv_dispc_clk =
>> cur.clkin4ddr / cur.regm_dispc;
>>
>> + if (cur.regm_dispc > 1 &&
>> + cur.regm_dispc % 2 != 0 &&
>> + req_pck >= 1000000)
>> + continue;
>> +
>
> Why do we do the req_pck check here? Can't we do it much earlier? We
> could bail out right in the beginning of dsi_pll_calc_clock_div_pck() if
> we see that req_pck is greater than 100 Mhz.
I think you misunderstood the patch. We don't skip or fail calculations
for pck > 100. What we do is we skip odd dividers if pck > 100.
> Also, we could maybe have a comment (or in the commit message) saying
> that we chose the 100 Mhz to make it a safe bet.
Hmm, yes, I should point out that 100MHz is just a guesstimate.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH 09/12] OMAPDSS: hide dss_select_dispc_clk_source()
From: Tomi Valkeinen @ 2012-10-31 7:17 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <5090CB17.4070400@ti.com>
[-- Attachment #1: Type: text/plain, Size: 2076 bytes --]
On 2012-10-31 08:54, Archit Taneja wrote:
> On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
>> dss.c currently exposes functions to configure the dispc source clock
>> and lcd source clock. There are configured separately from the output
>> drivers.
>>
>> However, there is no safe way for the output drivers to handle dispc
>> clock, as it's shared between the outputs. Thus, if, say, the DSI driver
>> sets up DSI PLL and configures both the dispc and lcd clock sources to
>> that DSI PLL, the resulting dispc clock could be too low for, say, HDMI.
>>
>> Thus the output drivers should really only be concerned about the lcd
>> clock, which is what the output drivers actually use. There's lot to do
>> to clean up the dss clock handling, but this patch takes one step
>> forward and removes the use of dss_select_dispc_clk_source() from the
>> output drivers.
>>
>> After this patch, the output drivers only configure the lcd source
>> clock. On omap4+ the dispc src clock is never changed from the default
>> PRCM source. On omap3, where the dispc and lcd clocks are actually the
>> same, setting the lcd clock source sets the dispc clock source.
>
> Maybe we could have one call to dss_select_dispc_clk_source() in
> omap_dsshw_porbe(). This is not necessary now, but if we support a
> splash screen on bootloader, and skip the hwmod resets, we might want to
> switch back our dispc clock source to PRCM if the output drivers don't
> to it. This is just a point though, we don't necessarily need it right now.
If we're showing the image from the bootloader, we can't change the
clock sources if we want to keep the display working. We either need to
accept and use the config the bootloader did, or reset the dss and start
over with our config.
But you're right, even if we don't support the splash screen from the
bootloader, we can't be sure if the dss hw was reset or not, and what
the clock source is. So it probably is safer to set the clock source at
probe time (and actually all other configs also).
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH 05/12] OMAPDSS: DSI: skip odd dividers when pck >= 100MHz
From: Archit Taneja @ 2012-10-31 6:57 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <1351613409-21186-6-git-send-email-tomi.valkeinen@ti.com>
On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
> The DSI PLL and HSDivider can be used to generate the pixel clock for
> LCD overlay manager, which then goes to DPI output. On the DPI output
> pin the voltage of the signal is shifted from the OMAP's internal
> minimal voltage to 1.8V range. The shifting is not instant, and the
> higher the clock frequency, the less time there is to shift the signal
> to nominal voltage.
>
> If the HSDivider's divider is greater than one and odd, the resulting
> pixel clock does not have 50% duty cycle. For example, with a divider of
> 3, the duty cycle is 33%.
>
> When combining high frequency (in the area of 140MHz+) and non-50% duty
> cycle, it has been observed the the shifter does not have enough time to
> shift the voltage enough, and this leads to bad signal which is rejected
> by monitors.
Is this something seen on OMAP3 also? I guess it must be since it's the
same DSI IP.
>
> As a workaround this patch makes the divider calculation skip all odd
> dividers when the required pixel clock is over 100MHz.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
> drivers/video/omap2/dss/dsi.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
> index 7d0db2b..d0e35da 100644
> --- a/drivers/video/omap2/dss/dsi.c
> +++ b/drivers/video/omap2/dss/dsi.c
> @@ -1386,6 +1386,11 @@ retry:
> cur.dsi_pll_hsdiv_dispc_clk > cur.clkin4ddr / cur.regm_dispc;
>
> + if (cur.regm_dispc > 1 &&
> + cur.regm_dispc % 2 != 0 &&
> + req_pck >= 1000000)
> + continue;
> +
Why do we do the req_pck check here? Can't we do it much earlier? We
could bail out right in the beginning of dsi_pll_calc_clock_div_pck() if
we see that req_pck is greater than 100 Mhz.
Also, we could maybe have a comment (or in the commit message) saying
that we chose the 100 Mhz to make it a safe bet.
Archit
^ permalink raw reply
* Re: [PATCH 09/12] OMAPDSS: hide dss_select_dispc_clk_source()
From: Archit Taneja @ 2012-10-31 6:54 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <1351613409-21186-10-git-send-email-tomi.valkeinen@ti.com>
On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
> dss.c currently exposes functions to configure the dispc source clock
> and lcd source clock. There are configured separately from the output
> drivers.
>
> However, there is no safe way for the output drivers to handle dispc
> clock, as it's shared between the outputs. Thus, if, say, the DSI driver
> sets up DSI PLL and configures both the dispc and lcd clock sources to
> that DSI PLL, the resulting dispc clock could be too low for, say, HDMI.
>
> Thus the output drivers should really only be concerned about the lcd
> clock, which is what the output drivers actually use. There's lot to do
> to clean up the dss clock handling, but this patch takes one step
> forward and removes the use of dss_select_dispc_clk_source() from the
> output drivers.
>
> After this patch, the output drivers only configure the lcd source
> clock. On omap4+ the dispc src clock is never changed from the default
> PRCM source. On omap3, where the dispc and lcd clocks are actually the
> same, setting the lcd clock source sets the dispc clock source.
Maybe we could have one call to dss_select_dispc_clk_source() in
omap_dsshw_porbe(). This is not necessary now, but if we support a
splash screen on bootloader, and skip the hwmod resets, we might want to
switch back our dispc clock source to PRCM if the output drivers don't
to it. This is just a point though, we don't necessarily need it right now.
Archit
^ permalink raw reply
* Re: [PATCH 08/12] OMAPDSS: setup default dss fck
From: Archit Taneja @ 2012-10-31 6:43 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <1351613409-21186-9-git-send-email-tomi.valkeinen@ti.com>
On Tuesday 30 October 2012 09:40 PM, Tomi Valkeinen wrote:
> We don't currently set the dss fck when starting up. This is not a
> problem, as we setup the fck later when configuring the pixel clocks. Or
> this is how it was for omap2, for the rest of the omaps this may not be
> so.
>
> For DSI, HDMI and also for DPI when using DSI PLL, we don't need to
> change the dss fck, and thus it may be left unconfigured. Usually the
> dss fck is already setup fine by default, but we can't trust this.
>
> This patch sets the dss fck to maximum at probe time.
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
> ---
> drivers/video/omap2/dss/dss.c | 36 ++++++++++++++++++++++++++++++++++++
> 1 file changed, 36 insertions(+)
>
> diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
> index 5affa86..034cc1a 100644
> --- a/drivers/video/omap2/dss/dss.c
> +++ b/drivers/video/omap2/dss/dss.c
> @@ -485,6 +485,36 @@ unsigned long dss_get_dpll4_rate(void)
> return 0;
> }
>
> +static int dss_setup_default_clock(void)
> +{
> + unsigned long max_dss_fck, prate;
> + unsigned fck_div;
> + struct dss_clock_info dss_cinfo = { 0 };
> + int r;
> +
> + if (dss.dpll4_m4_ck = NULL)
> + return 0;
> +
> + max_dss_fck = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
> +
> + prate = dss_get_dpll4_rate();
Not related to this patch, but maybe we could change the
dss_get_dpll4_rate() name and dss.dpll4_m4_clk to something better.
Maybe something like dss_fck_parent?
> +
> + fck_div = DIV_ROUND_UP(prate * dss.feat->dss_fck_multiplier,
> + max_dss_fck);
> +
> + dss_cinfo.fck_div = fck_div;
> +
> + r = dss_calc_clock_rates(&dss_cinfo);
> + if (r)
> + return r;
> +
> + r = dss_set_clock_div(&dss_cinfo);
> + if (r)
> + return r;
> +
> + return 0;
> +}
> +
> int dss_calc_clock_div(unsigned long req_pck, struct dss_clock_info *dss_cinfo,
> struct dispc_clock_info *dispc_cinfo)
> {
> @@ -913,6 +943,10 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
> dss.lcd_clk_source[0] = OMAP_DSS_CLK_SRC_FCK;
> dss.lcd_clk_source[1] = OMAP_DSS_CLK_SRC_FCK;
>
> + r = dss_setup_default_clock();
> + if (r)
> + goto err_setup_clocks;
Maybe it's safer to call this before we do a dss_runtime_get(). On
OMAP4, DSS_FCLK is needed to access registers also. Changing it's rate
might not be liked by the DSS HW. Also, it seems more logical to call it
after dss_get_clocks() in omap_dsshw_probe(), then we sort of group the
clock related stuff together.
Archit
> +
> rev = dss_read_reg(DSS_REVISION);
> printk(KERN_INFO "OMAP DSS rev %d.%d\n",
> FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
> @@ -923,6 +957,8 @@ static int __init omap_dsshw_probe(struct platform_device *pdev)
>
> return 0;
>
> +err_setup_clocks:
> + dss_runtime_put();
> err_runtime_get:
> pm_runtime_disable(&pdev->dev);
> dss_put_clocks();
>
^ permalink raw reply
* RE: [PATCH] omap2-clk: Add missing lcdc clock definition
From: Hiremath, Vaibhav @ 2012-10-31 6:38 UTC (permalink / raw)
To: Paul Walmsley
Cc: Tony Lindgren, Pantelis Antoniou, linux-omap@vger.kernel.org,
linux-kernel@vger.kernel.org, Koen Kooi, Porter, Matt, Dill, Russ,
linux-fbdev@vger.kernel.org
In-Reply-To: <alpine.DEB.2.00.1210310547560.8340@utopia.booyaka.com>
On Wed, Oct 31, 2012 at 11:19:44, Paul Walmsley wrote:
> On Wed, 31 Oct 2012, Hiremath, Vaibhav wrote:
>
> > As far as lck clock node is concerned, we had deliberately dropped all leaf-
> > node clocks from the clock tree, please refer to the description mentioned
> > in -
> > http://lists.infradead.org/pipermail/linux-arm-kernel/2012-May/101987.html
>
> Ach, should have remembered that :-( Indeed there is an LCDC hwmod:
>
> static struct omap_hwmod am33xx_lcdc_hwmod = {
> .name = "lcdc",
> .class = &am33xx_lcdc_hwmod_class,
> .clkdm_name = "lcdc_clkdm",
> .mpu_irqs = am33xx_lcdc_irqs,
> .flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
> .main_clk = "lcd_gclk",
> .prcm = {
> .omap4 = {
> .clkctrl_offs = AM33XX_CM_PER_LCDC_CLKCTRL_OFFSET,
> .modulemode = MODULEMODE_SWCTRL,
> },
> },
> };
>
> > >From LCDC driver perspective, driver is using,
> >
> > fb_clk = clk_get(&device->dev, NULL);
> >
> > This I feel needs to be corrected for valid name as per Spec (mostly I would
> > vote for "fck") and then every platform should make sure that it returns
> > valid clock-node for it.
> >
> > Change in Driver would be,
> >
> > fb_clk = clk_get(&device->dev, "fck");
>
Ok, thanks. Let me submit patch for this.
Thanks,
Vaibhav
> Indeed.
>
>
> - Paul
>
^ permalink raw reply
* Re: [PATCH 01/12] OMAPFB: remove use of extended edid block
From: Tomi Valkeinen @ 2012-10-31 6:23 UTC (permalink / raw)
To: Archit Taneja; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <5090C0CD.7070104@ti.com>
[-- Attachment #1: Type: text/plain, Size: 673 bytes --]
On 2012-10-31 08:10, Archit Taneja wrote:
> On Tuesday 30 October 2012 09:39 PM, Tomi Valkeinen wrote:
>> It seems that using the second EDID block causes more problems than is
>> of any help. The first mode in the extended block will get
>> FB_MODE_IS_FIRST set, which will override the first mode from the first
>> EDID block, thus making the default videomode selection not to work
>> properly.
>>
>> This patch removes the use of the extended edid block for now.
>
> Looks like you had posted this one(and also the next one)in the previous
> cleanup/fixes series also.
Ah, so I did. Too many work branches =). Thanks for pointing it out.
Tomi
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply
* Re: [PATCH 01/12] OMAPFB: remove use of extended edid block
From: Archit Taneja @ 2012-10-31 6:22 UTC (permalink / raw)
To: Tomi Valkeinen; +Cc: linux-omap, linux-fbdev, rob
In-Reply-To: <1351613409-21186-2-git-send-email-tomi.valkeinen@ti.com>
On Tuesday 30 October 2012 09:39 PM, Tomi Valkeinen wrote:
> It seems that using the second EDID block causes more problems than is
> of any help. The first mode in the extended block will get
> FB_MODE_IS_FIRST set, which will override the first mode from the first
> EDID block, thus making the default videomode selection not to work
> properly.
>
> This patch removes the use of the extended edid block for now.
Looks like you had posted this one(and also the next one)in the previous
cleanup/fixes series also.
Archit
^ permalink raw reply
* RE: [PATCH] omap2-clk: Add missing lcdc clock definition
From: Paul Walmsley @ 2012-10-31 5:49 UTC (permalink / raw)
To: Hiremath, Vaibhav
Cc: Tony Lindgren, Pantelis Antoniou, linux-omap@vger.kernel.org,
linux-kernel@vger.kernel.org, Koen Kooi, Porter, Matt, Dill, Russ,
linux-fbdev@vger.kernel.org
In-Reply-To: <79CD15C6BA57404B839C016229A409A83EB529E4@DBDE01.ent.ti.com>
On Wed, 31 Oct 2012, Hiremath, Vaibhav wrote:
> As far as lck clock node is concerned, we had deliberately dropped all leaf-
> node clocks from the clock tree, please refer to the description mentioned
> in -
> http://lists.infradead.org/pipermail/linux-arm-kernel/2012-May/101987.html
Ach, should have remembered that :-( Indeed there is an LCDC hwmod:
static struct omap_hwmod am33xx_lcdc_hwmod = {
.name = "lcdc",
.class = &am33xx_lcdc_hwmod_class,
.clkdm_name = "lcdc_clkdm",
.mpu_irqs = am33xx_lcdc_irqs,
.flags = HWMOD_SWSUP_SIDLE | HWMOD_SWSUP_MSTANDBY,
.main_clk = "lcd_gclk",
.prcm = {
.omap4 = {
.clkctrl_offs = AM33XX_CM_PER_LCDC_CLKCTRL_OFFSET,
.modulemode = MODULEMODE_SWCTRL,
},
},
};
> >From LCDC driver perspective, driver is using,
>
> fb_clk = clk_get(&device->dev, NULL);
>
> This I feel needs to be corrected for valid name as per Spec (mostly I would
> vote for "fck") and then every platform should make sure that it returns
> valid clock-node for it.
>
> Change in Driver would be,
>
> fb_clk = clk_get(&device->dev, "fck");
Indeed.
- Paul
^ permalink raw reply
* RE: [PATCH] da8xx: Allow use by am33xx based devices
From: Manjunathappa, Prakash @ 2012-10-31 5:23 UTC (permalink / raw)
To: Pantelis Antoniou, Florian Tobias Schandinat
Cc: linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Koen Kooi, Porter, Matt, Dill, Russ, linux-omap@vger.kernel.org,
davinci-linux-open-source@linux.davincidsp.com
In-Reply-To: <1351698968-3965-1-git-send-email-panto@antoniou-consulting.com>
Hi,
On Wed, Oct 31, 2012 at 21:26:08, Pantelis Antoniou wrote:
> This driver can be used for AM33xx devices, like the popular beaglebone.
>
> Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
> drivers/video/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 9791d10..e7868d8 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -2202,7 +2202,7 @@ config FB_SH7760
>
> config FB_DA8XX
> tristate "DA8xx/OMAP-L1xx Framebuffer support"
> - depends on FB && ARCH_DAVINCI_DA8XX
> + depends on FB && (ARCH_DAVINCI_DA8XX || SOC_AM33XX)
Agreed this is present on da8xx and am33xx, but moving forward for
supporting DT, we should be avoiding these dependencies. So instead
change this to remove machine dependencies.
Thanks,
Prakash
> select FB_CFB_FILLRECT
> select FB_CFB_COPYAREA
> select FB_CFB_IMAGEBLIT
> --
> 1.7.12
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* RE: [PATCH] omap2-clk: Add missing lcdc clock definition
From: Hiremath, Vaibhav @ 2012-10-31 5:09 UTC (permalink / raw)
To: Paul Walmsley, Tony Lindgren
Cc: Pantelis Antoniou, linux-omap@vger.kernel.org,
linux-kernel@vger.kernel.org, Koen Kooi, Porter, Matt, Dill, Russ,
linux-fbdev@vger.kernel.org
In-Reply-To: <alpine.DEB.2.00.1210302324530.6152@utopia.booyaka.com>
On Wed, Oct 31, 2012 at 04:56:40, Paul Walmsley wrote:
> + Vaibhav Hiremath
>
> On Tue, 30 Oct 2012, Tony Lindgren wrote:
>
> > * Pantelis Antoniou <panto@antoniou-consulting.com> [121030 11:04]:
> > > Looks like the lcdc clock definition got dropped.
> > > It is required for the LCD controller to work. Reintroduce.
> >
> > This looks like a regression, can you also add the commit
> > causing it?
>
> Looks like probably a new "feature," in that this clock didn't exist in
> the original check-in. Would be good to get Vaibhav's opinion on this;
> also the common clock patches will need to be updated.
>
Thanks Paul for looping me in, something went wrong with my l-o subscription,
so I didn't receive these Patches.
As far as lck clock node is concerned, we had deliberately dropped all leaf-
node clocks from the clock tree, please refer to the description mentioned
in -
http://lists.infradead.org/pipermail/linux-arm-kernel/2012-May/101987.html
From LCDC driver perspective, driver is using,
fb_clk = clk_get(&device->dev, NULL);
This I feel needs to be corrected for valid name as per Spec (mostly I would
vote for "fck") and then every platform should make sure that it returns
valid clock-node for it.
Change in Driver would be,
fb_clk = clk_get(&device->dev, "fck");
Thanks,
Vaibhav
>
> - Paul
>
^ permalink raw reply
* RE: [PATCH] da8xx: Fix revision check on the da8xx driver
From: Manjunathappa, Prakash @ 2012-10-31 3:51 UTC (permalink / raw)
To: Pantelis Antoniou, Florian Tobias Schandinat
Cc: linux-fbdev@vger.kernel.org, linux-kernel@vger.kernel.org,
Koen Kooi, Porter, Matt, Dill, Russ, linux-omap@vger.kernel.org
In-Reply-To: <1351698984-4007-1-git-send-email-panto@antoniou-consulting.com>
On Wed, Oct 31, 2012 at 21:26:24, Pantelis Antoniou wrote:
> The revision check fails for the beaglebone; Add new revision ID.
>
> Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
> drivers/video/da8xx-fb.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
> index 80665f6..866d804 100644
> --- a/drivers/video/da8xx-fb.c
> +++ b/drivers/video/da8xx-fb.c
> @@ -1283,6 +1283,7 @@ static int __devinit fb_probe(struct platform_device *device)
> lcd_revision = LCD_VERSION_1;
> break;
> case 0x4F200800:
> + case 0x4F201000:
Thanks for Correcting. This is the LCDC revision on am335x silicon in comparison
with to one read(0x4F200800) on emulator platform.
Acked-by: Manjunathappa, Prakash <prakash.pm@ti.com>
> lcd_revision = LCD_VERSION_2;
> break;
> default:
> --
> 1.7.12
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* [PATCH] da8xx: Add standard panel definition
From: Pantelis Antoniou @ 2012-10-30 18:19 UTC (permalink / raw)
To: Florian Tobias Schandinat
Cc: Pantelis Antoniou, linux-fbdev, linux-kernel, Koen Kooi,
Matt Porter, Russ Dill, linux-omap
Add standard panel definition that can work for the beaglebone
DVI cape.
Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
---
drivers/video/da8xx-fb.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 866d804..4462d9e 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -270,6 +270,20 @@ static struct da8xx_panel known_lcd_panels[] = {
.pxl_clk = 7833600,
.invert_pxl_clk = 0,
},
+ [3] = {
+ /* 1024 x 768 @ 60 Hz Reduced blanking VESA CVT 0.79M3-R */
+ .name = "1024x768@60",
+ .width = 1024,
+ .height = 768,
+ .hfp = 48,
+ .hbp = 80,
+ .hsw = 32,
+ .vfp = 3,
+ .vbp = 15,
+ .vsw = 4,
+ .pxl_clk = 56000000,
+ .invert_pxl_clk = 0,
+ },
};
/* Enable the Raster Engine of the LCD Controller */
--
1.7.12
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox