Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [RFC 0/5] Generic panel framework
From: Laurent Pinchart @ 2012-10-31 13:13 UTC (permalink / raw)
  To: Tomi Valkeinen, Richard Purdie
  Cc: linux-fbdev, dri-devel, linux-leds, linux-media, Marcus Lorentzon,
	Sumit Semwal, Archit Taneja, Sebastien Guiriec, Inki Dae,
	Kyungmin Park, Sascha Hauer, Alexandre Courbot
In-Reply-To: <1348055129.2565.54.camel@deskari>

Hi Tomi,

On Wednesday 19 September 2012 14:45:29 Tomi Valkeinen wrote:
> On Fri, 2012-08-17 at 02:49 +0200, Laurent Pinchart wrote:
> > Hi everybody,
> > 
> > While working on DT bindings for the Renesas Mobile SoC display controller
> > (a.k.a. LCDC) I quickly realized that display panel implementation based
> > on board code callbacks would need to be replaced by a driver-based panel
> > framework.
> 
> I thought I'd try to approach the common panel framework by creating
> device tree examples that OMAP would need. I only thought about the
> connections and organization, not individual properties, so I have not
> filled any "compatible" or such properties.
> 
> What I have below is first DT data for OMAP SoC (more or less what omap4
> has), then display examples of different board setups. The hardware
> examples I have here are all real, so no imaginary use cases =).
> 
> We don't need to implement support for all these at once, but I think
> the DT data structure should be right from the start, so I'm posting
> this to get discussion about the format.

Thank you for working on this proposal.

> OMAP SoC
> ====
> 
> So here's first the SoC specific display nodes. OMAP has a DSS (display
> subsystem) block, which contains the following elements:
> 
> - DISPC (display controller) reads the pixels from memory and outputs
> them using specified video timings. DISPC has three outputs, LCD0, LCD1
> and TV. These are SoC internal outputs, they do not go outside the SoC.
> 
> - DPI gets its data from DISPC's LCD0, and outputs MIPI DPI (parallel
> RBG)
> 
> - Two independent DSI modules, which get their data from LCD0 or LCD1,
> and output MIPI DSI (a serial two-way video bus)
> 
> - HDMI, gets data from DISPC's TV output and outputs HDMI
> 
> / {
> 	ocp {
> 		dss {
> 			dispc {
> 				dss-lcd0: output@0 {
> 				};
> 
> 				dss-lcd1: output@1 {
> 				};
> 
> 				dss-tv: output@2 {
> 				};
> 			};
> 
> 			dpi: dpi {
> 				video-source = <&dss-lcd0>;
> 			};
> 
> 			dsi0: dsi@0 {
> 				video-source = <&dss-lcd0>;
> 			};
> 
> 			dsi1: dsi@1 {
> 				video-source = <&dss-lcd1>;
> 			};
> 
> 			hdmi: hdmi {
> 				video-source = <&dss-tv>;
> 			};
> 		};
> 	};
> };
> 
> I have defined all the relevant nodes, and video-source property is used
> to point to the source for video data. I also define aliases for the SoC
> outputs so that panels can use them.
> 
> One thing to note is that the video sources for some of the blocks, like
> DSI, are not hardcoded in the HW, so dsi0 could get its data from LCD0
> or LCD1.

What about the source that are hardwired in hardware ? Shouldn't those be 
hardcoded in the driver instead ?

> However, I don't think they are usually changed during runtime, and the dss
> driver cannot change them independently for sure (meaning that some upper
> layer should tell it how to change the config). Thus I specify sane defaults
> here, but the board dts files can of course override the video sources.

I'm not sure whether default settings like those really belong to the DT. I'm 
no expert on that topic though.

> Another thing to note is that we have more outputs from OMAP than we have
> outputs from DISPC. This means that the same video source is used by
> multiple sinks (LCD0 used by DPI and DSI0). DPI and DSI0 cannot be used at
> the same time, obviously.

It might not be really obvious, as I don't see what prevents DPI and DSI0 to 
be used at the same time :-) Do they share physical pins ?

> And third thing to note, DISPC node defines outputs explicitly, as it has
> multiple outputs, whereas the external outputs do not as they have only one
> output. Thus the node's output is implicitly the node itself. So, instead of
> having:
> 
> ds0: dsi@0 {
> 	video-source = <&dss-lcd0>;
> 	dsi0-out0: output@0 {
> 	};
> };
> 
> I have:
> 
> dsi0: dsi@0 {
> 	video-source = <&dss-lcd0>;
> };

What about defining the data sinks instead of the data sources ? I find it 
more logical for the DSS to get the panel it's connected to than the other way 
around.

> Of this I'm a bit unsure. I believe in most cases there's only one output,
> so it'd be nice to have a shorter representation, but I'm not sure if it's
> good to handle the cases for single and multiple outputs differently. Or if
> it's good to mix control and data busses, as, for example, dsi0 can be used
> as both control and data bus. Having the output defined explicitly would
> separate the control and data bus nodes.
> 
> 
> Simple DPI panel
> ========
> 
> Here a board has a DPI panel, which is controlled via i2c. Panel nodes
> are children of the control bus, so in this case we define the panel
> under i2c2.
> 
> &i2c2 {
> 	dpi-lcd-panel {
> 		video-source = <&dpi>;
> 
> 	};
> };
> 
> 
> HDMI
> ==
> 
> OMAP has a HDMI output, but it cannot be connected directly to an HDMI
> cable. TI uses tpd12s015 chip in its board, which provides ESD,
> level-shifting and whatnot (I'm an SW guy, google for the chip to read
> the details =). tpd12s015 has a few GPIOs and powers that need to be
> controlled, so we need a driver for it.
> 
> There's no control bus for the tpd12s015, so it's platform device. Then
> there's the device for the HDMI monitor, and the DDC lines are connected
> to OMAP's i2c4, thus the hdmi monitor device is a child of i2c.
> 
> / {
> 	hdmi-connector: tpd12s015 {
> 		video-source = <&hdmi>;
> 	};
> };
> 
> &i2c4 {
> 	hdmi-monitor {
> 		video-source = <&hdmi-connector>;
> 	};
> };

So this implied we would have the following chain ?

DISPC (on SoC) -> HDMI (on SoC) -> TPD12S015 (on board) -> HDMI monitor (off 
board) 

Should we then have a driver for the HDMI monitor ?

> DSI bridge chip
> =======> 
> In this board we have a DSI bridge chip that is controlled via DSI, and
> gets the pixel data via DSI video mode stream. The chip converts this to
> LVDS. We then have an LVDS panel connected to this bridge chip, which is
> controlled via SPI.
> 
> &dsi0 {
> 	dsi2lvds: dsi2lvds {
> 		video-source = <&dsi0>;
> 
> 	};
> };
> 
> &spi2 {
> 	lvds-lcd-panel {
> 		video-source = <&dsi2lvds>;
> 	};
> };
> 
> 
> High res dual-DSI panel
> ===========> 
> Here we have a DSI video mode panel that gets its data from two DSI
> buses. This allows it to get the combined bandwidth of the DSI buses,
> achieving higher resolution than with single DSI bus. The panel is
> controlled via the first DSI bus.
> 
> &dsi0 {
> 	dual-dsi-panel {
> 		video-source-1 = <&dsi0>;
> 		video-source-2 = <&dsi1>;
> 
> 	};
> };
> 
> 
> DSI buffer chip with two outputs
> ================
> 
> This board has a DSI command mode buffer chip, that contains its own
> framebuffer. The buffer chip has two DPI outputs, which get the pixels
> from the framebuffer. Similar to OMAP's DISPC this chip has multiple
> outputs so they need to be defined explicitly. And we also have two
> dummy DPI panels connected to this buffer chip.
> 
> &dsi0 {
> 	dsibuf {
> 		video-source = <&dsi0>;
> 
> 		dsibuf-dpi0: output@0 {
> 		};
> 
> 		dsibuf-dpi1: output@1 {
> 		};
> 	};
> };
> 
> / {
> 	dpi-lcd-panel@0 {
> 		video-source = <&dsibuf-dpi0>;
> 
> 	};
> 
> 	dpi-lcd-panel@1 {
> 		video-source = <&dsibuf-dpi1>;
> 
> 	};
> };

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* [PATCH v7 8/8] drm_modes: add of_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 to get drm_display_mode from devicetree.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/gpu/drm/drm_modes.c |   42 ++++++++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h          |    5 +++++
 2 files changed, 47 insertions(+)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 049624d..d51afe6 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -35,6 +35,7 @@
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <linux/of.h>
 #include <linux/videomode.h>
 
 /**
@@ -540,6 +541,47 @@ int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmo
 EXPORT_SYMBOL_GPL(videomode_to_display_mode);
 #endif
 
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+static void dump_drm_displaymode(struct drm_display_mode *m)
+{
+	pr_debug("drm_displaymode = %d %d %d %d %d %d %d %d %d\n",
+		 m->hdisplay, m->hsync_start, m->hsync_end, m->htotal,
+		 m->vdisplay, m->vsync_start, m->vsync_end, m->vtotal,
+		 m->clock);
+}
+
+/**
+ * of_get_drm_display_mode - get a drm_display_mode from devicetree
+ * @np: device_node with the timing specification
+ * @dmode: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ * 
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timing_list ond
+ * work with that instead.
+ */
+int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode,
+			int index)
+{
+	struct videomode vm;
+	int ret;
+
+	ret = of_get_videomode(np, &vm, index);
+	if (ret)
+		return ret;
+
+	videomode_to_display_mode(&vm, dmode);
+
+	pr_info("%s: got %dx%d display mode from %s\n", __func__, vm.hactive,
+		vm.vactive, np->name);
+	dump_drm_displaymode(dmode);
+
+	return 0;
+
+}
+EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
+#endif
 /**
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index e9fa1e3..4f9c44e 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -56,6 +56,7 @@
 #include <linux/cdev.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/of.h>
 #include <linux/videomode.h>
 #if defined(__alpha__) || defined(__powerpc__)
 #include <asm/pgtable.h>	/* For pte_wrprotect */
@@ -1457,6 +1458,10 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
 
 extern int videomode_to_display_mode(struct videomode *vm,
 				     struct drm_display_mode *dmode);
+extern int of_get_drm_display_mode(struct device_node *np,
+				   struct drm_display_mode *dmode,
+				   int index);
+
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
 extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH v7 7/8] drm_modes: 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 conversion from videomode to drm_display_mode

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/gpu/drm/drm_modes.c |   36 ++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h          |    3 +++
 2 files changed, 39 insertions(+)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 59450f3..049624d 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -35,6 +35,7 @@
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <linux/videomode.h>
 
 /**
  * drm_mode_debug_printmodeline - debug print a mode
@@ -504,6 +505,41 @@ drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
 }
 EXPORT_SYMBOL(drm_gtf_mode);
 
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
+{
+	dmode->hdisplay = vm->hactive;
+	dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
+	dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
+	dmode->htotal = dmode->hsync_end + vm->hback_porch;
+
+	dmode->vdisplay = vm->vactive;
+	dmode->vsync_start = dmode->vdisplay + vm->vfront_porch;
+	dmode->vsync_end = dmode->vsync_start + vm->vsync_len;
+	dmode->vtotal = dmode->vsync_end + vm->vback_porch;
+
+	dmode->clock = vm->pixelclock / 1000;
+
+	dmode->flags = 0;
+	if (vm->hah)
+		dmode->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		dmode->flags |= DRM_MODE_FLAG_NHSYNC;
+	if (vm->vah)
+		dmode->flags |= DRM_MODE_FLAG_PVSYNC;
+	else
+		dmode->flags |= DRM_MODE_FLAG_NVSYNC;
+	if (vm->interlaced)
+		dmode->flags |= DRM_MODE_FLAG_INTERLACE;
+	if (vm->doublescan)
+		dmode->flags |= DRM_MODE_FLAG_DBLSCAN;
+	drm_mode_set_name(dmode);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_to_display_mode);
+#endif
+
 /**
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 3fd8280..e9fa1e3 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -56,6 +56,7 @@
 #include <linux/cdev.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/videomode.h>
 #if defined(__alpha__) || defined(__powerpc__)
 #include <asm/pgtable.h>	/* For pte_wrprotect */
 #endif
@@ -1454,6 +1455,8 @@ extern struct drm_display_mode *
 drm_mode_create_from_cmdline_mode(struct drm_device *dev,
 				  struct drm_cmdline_mode *cmd);
 
+extern int videomode_to_display_mode(struct videomode *vm,
+				     struct drm_display_mode *dmode);
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
 extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCH v7 6/8] fbmon: add of_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 to get fb_videomode from devicetree.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/video/fbmon.c |   40 ++++++++++++++++++++++++++++++++++++++++
 include/linux/fb.h    |    3 +++
 2 files changed, 43 insertions(+)

diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index b9e6ab3..871aa76 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -1408,6 +1408,46 @@ int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode)
 EXPORT_SYMBOL_GPL(videomode_to_fb_videomode);
 #endif
 
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+static void dump_fb_videomode(struct fb_videomode *m)
+{
+	pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
+		 m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
+		 m->right_margin, m->upper_margin, m->lower_margin,
+		 m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+/**
+ * of_get_fb_videomode - get a fb_videomode from devicetree
+ * @np: device_node with the timing specification
+ * @fb: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timing_list ond
+ * work with that instead.
+ */
+int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
+			int index)
+{
+	struct videomode vm;
+	int ret;
+
+	ret = of_get_videomode(np, &vm, index);
+	if (ret)
+		return ret;
+
+	videomode_to_fb_videomode(&vm, fb);
+
+	pr_info("%s: got %dx%d display mode from %s\n", __func__, vm.hactive,
+		vm.vactive, np->name);
+	dump_fb_videomode(fb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_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 46c665b..9892fd6 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -14,6 +14,8 @@
 #include <linux/backlight.h>
 #include <linux/slab.h>
 #include <asm/io.h>
+#include <linux/of.h>
+#include <linux/of_videomode.h>
 
 struct vm_area_struct;
 struct fb_info;
@@ -714,6 +716,7 @@ 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 of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb, int index);
 extern int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode);
 
 /* drivers/video/modedb.c */
-- 
1.7.10.4


^ permalink raw reply related

* [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


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