From: Rob Herring <robherring2@gmail.com>
To: Steffen Trumtrar <s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
Laurent Pinchart
<laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org,
Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Subject: Re: [PATCH v4] of: Add videomode helper
Date: Mon, 24 Sep 2012 13:42:12 +0000 [thread overview]
Message-ID: <50606334.7030902@gmail.com> (raw)
In-Reply-To: <1348042843-24673-1-git-send-email-s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
On 09/19/2012 03:20 AM, Steffen Trumtrar wrote:
> This patch adds a helper function for parsing videomodes from the devicetree.
> The videomode can be either converted to a struct drm_display_mode or a
> struct fb_videomode.
>
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> ---
>
> Hi!
>
> changes since v3:
> - print error messages
> - free alloced memory
> - general cleanup
>
> Regards
> Steffen
>
> .../devicetree/bindings/video/displaymode | 74 +++++
> drivers/of/Kconfig | 5 +
> drivers/of/Makefile | 1 +
> drivers/of/of_videomode.c | 283 ++++++++++++++++++++
> include/linux/of_videomode.h | 56 ++++
> 5 files changed, 419 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/video/displaymode
> create mode 100644 drivers/of/of_videomode.c
> create mode 100644 include/linux/of_videomode.h
>
> diff --git a/Documentation/devicetree/bindings/video/displaymode b/Documentation/devicetree/bindings/video/displaymode
> new file mode 100644
> index 0000000..990ca52
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/displaymode
> @@ -0,0 +1,74 @@
> +videomode bindings
> +=========
> +
> +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: displayclock in Hz
A major piece missing is the LCD controller to display interface width
and component ordering.
> +
> +Optional properties:
> + - width-mm, height-mm: Display dimensions in mm
> + - hsync-active-high (bool): Hsync pulse is active high
> + - vsync-active-high (bool): Vsync pulse is active high
> + - interlaced (bool): This is an interlaced mode
> + - doublescan (bool): This is a doublescan mode
> +
> +There are different ways of describing a display mode. The devicetree representation
> +corresponds to the one commonly found in datasheets for displays.
> +The description of the display and its mode is split in two parts: first the display
> +properties like size in mm and (optionally) multiple subnodes with the supported modes.
> +
> +Example:
> +
> + display@0 {
It would be useful to have a compatible string here. We may not always
know the panel type or have a fixed panel though. We could define
"generic-lcd" or something for cases where the panel type is unknown.
> + width-mm = <800>;
> + height-mm = <480>;
> + modes {
> + mode0: mode@0 {
> + /* 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-high;
> + };
> + };
> + };
> +
> +Every property also supports the use of ranges, so the commonly used datasheet
> +description with <min typ max>-tuples can be used.
> +
> +Example:
> +
> + mode1: mode@1 {
> + /* 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>;
> + };
> +
> +The videomode can be linked to a connector via phandles. The connector has to
> +support the display- and default-mode-property to link to and select a mode.
Could also be phandle in the lcd controller node? What are the '-' for?
Is "display-blah" a valid name or something?
"default-mode" is pretty generic. How about display-mode or
display-default-mode?
Rob
> +
> +Example:
> +
> + hdmi@00120000 {
> + status = "okay";
> + display = <&benq>;
> + default-mode = <&mode1>;
> + };
> +
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index dfba3e6..a3acaa3 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -83,4 +83,9 @@ config OF_MTD
> depends on MTD
> def_bool y
>
> +config OF_VIDEOMODE
> + def_bool y
> + help
> + helper to parse videomodes from the devicetree
> +
> endmenu # OF
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index e027f44..80e6db3 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_VIDEOMODE) += of_videomode.o
> diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> new file mode 100644
> index 0000000..52bfc74
> --- /dev/null
> +++ b/drivers/of/of_videomode.c
> @@ -0,0 +1,283 @@
> +/*
> + * OF helpers for parsing display modes
> + *
> + * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
> + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
> + *
> + * This file is released under the GPLv2
> + */
> +#include <linux/of.h>
> +#include <linux/fb.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <linux/of_videomode.h>
> +
> +static u32 of_video_get_value(struct mode_property *prop)
> +{
> + return (prop->min >= prop->typ) ? prop->min : prop->typ;
> +}
> +
> +/* read property into new mode_property */
> +static int of_video_parse_property(struct device_node *np, char *name,
> + struct mode_property *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);
> +
> + memset(result, 0, sizeof(*result));
> +
> + ret = of_property_read_u32_array(np, name, &result->min, cells);
> +
> + return ret;
> +}
> +
> +static int of_video_free(struct display *disp)
> +{
> + int i;
> +
> + for (i=0; i<disp->num_modes; i++)
> + kfree(disp->modes[i]);
> + kfree(disp->modes);
> +
> + return 0;
> +}
> +
> +int videomode_to_display_mode(struct display *disp, struct drm_display_mode *dmode, int index)
> +{
> + struct videomode *vm;
> +
> + memset(dmode, 0, sizeof(*dmode));
> +
> + if (index > disp->num_modes) {
> + pr_err("%s: wrong index: %d from %d\n", __func__, index, disp->num_modes);
> + return -EINVAL;
> + }
> +
> + vm = disp->modes[index];
> +
> + dmode->hdisplay = of_video_get_value(&vm->hactive);
> + dmode->hsync_start = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch);
> + dmode->hsync_end = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch)
> + + of_video_get_value(&vm->hsync_len);
> + dmode->htotal = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch)
> + + of_video_get_value(&vm->hsync_len) + of_video_get_value(&vm->hback_porch);
> +
> + dmode->vdisplay = of_video_get_value(&vm->vactive);
> + dmode->vsync_start = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch);
> + dmode->vsync_end = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch)
> + + of_video_get_value(&vm->vsync_len);
> + dmode->vtotal = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch) +
> + of_video_get_value(&vm->vsync_len) + of_video_get_value(&vm->vback_porch);
> +
> + dmode->width_mm = disp->width_mm;
> + dmode->height_mm = disp->height_mm;
> +
> + dmode->clock = of_video_get_value(&vm->clock) / 1000;
> +
> + 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);
> +
> +int videomode_to_fb_mode(struct display *disp, struct fb_videomode *fbmode, int index)
> +{
> + struct videomode *vm;
> +
> + memset(fbmode, 0, sizeof(*fbmode));
> +
> + if (index > disp->num_modes) {
> + pr_err("%s: wrong index: %d from %d\n", __func__, index, disp->num_modes);
> + return -EINVAL;
> + }
> +
> + vm = disp->modes[index];
> +
> + fbmode->xres = of_video_get_value(&vm->hactive);
> + fbmode->left_margin = of_video_get_value(&vm->hback_porch);
> + fbmode->right_margin = of_video_get_value(&vm->hfront_porch);
> + fbmode->hsync_len = of_video_get_value(&vm->hsync_len);
> +
> + fbmode->yres = of_video_get_value(&vm->vactive);
> + fbmode->upper_margin = of_video_get_value(&vm->vback_porch);
> + fbmode->lower_margin = of_video_get_value(&vm->vfront_porch);
> + fbmode->vsync_len = of_video_get_value(&vm->vsync_len);
> +
> + fbmode->pixclock = KHZ2PICOS(of_video_get_value(&vm->clock) / 1000);
> +
> + 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;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(videomode_to_fb_mode);
> +
> +int of_get_video_modes(struct device_node *np, struct display *disp)
> +{
> + struct device_node *display_np;
> + struct device_node *mode_np;
> + struct device_node *modes_np;
> + char *default_mode;
> +
> + int ret = 0;
> +
> + memset(disp, 0, sizeof(*disp));
> + disp->modes = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> +
> + if (!np) {
> + pr_err("%s: no node provided\n", __func__);
> + return -EINVAL;
> + }
> +
> + display_np = of_parse_phandle(np, "display", 0);
> +
> + if (!display_np) {
> + pr_err("%s: could not find display node\n", __func__);
> + return -EINVAL;
> + }
> +
> + of_property_read_u32(display_np, "width-mm", &disp->width_mm);
> + of_property_read_u32(display_np, "height-mm", &disp->height_mm);
> +
> + mode_np = of_parse_phandle(np, "default-mode", 0);
> +
> + if (!mode_np) {
> + pr_info("%s: no default-mode specified.\n", __func__);
> + mode_np = of_find_node_by_name(np, "mode");
> + }
> +
> + if (!mode_np) {
> + pr_err("%s: could not find any mode specification\n", __func__);
> + return -EINVAL;
> + }
> +
> + default_mode = (char *)mode_np->full_name;
> +
> + modes_np = of_find_node_by_name(np, "modes");
> + for_each_child_of_node(modes_np, mode_np) {
> + struct videomode *vm;
> +
> + vm = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> + disp->modes[disp->num_modes] = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> +
> + ret |= of_video_parse_property(mode_np, "hback-porch", &vm->hback_porch);
> + ret |= of_video_parse_property(mode_np, "hfront-porch", &vm->hfront_porch);
> + ret |= of_video_parse_property(mode_np, "hactive", &vm->hactive);
> + ret |= of_video_parse_property(mode_np, "hsync-len", &vm->hsync_len);
> + ret |= of_video_parse_property(mode_np, "vback-porch", &vm->vback_porch);
> + ret |= of_video_parse_property(mode_np, "vfront-porch", &vm->vfront_porch);
> + ret |= of_video_parse_property(mode_np, "vactive", &vm->vactive);
> + ret |= of_video_parse_property(mode_np, "vsync-len", &vm->vsync_len);
> + ret |= of_video_parse_property(mode_np, "clock", &vm->clock);
> +
> + if (ret)
> + return -EINVAL;
> +
> + vm->hah = of_property_read_bool(mode_np, "hsync-active-high");
> + vm->vah = of_property_read_bool(mode_np, "vsync-active-high");
> + vm->interlaced = of_property_read_bool(mode_np, "interlaced");
> + vm->doublescan = of_property_read_bool(mode_np, "doublescan");
> +
> + if (strcmp(default_mode,mode_np->full_name) = 0)
> + disp->default_mode = disp->num_modes;
> +
> + disp->modes[disp->num_modes] = vm;
> + disp->num_modes++;
> + }
> + of_node_put(display_np);
> +
> + pr_info("%s: found %d modelines. Using #%d as default\n", __func__,
> + disp->num_modes, disp->default_mode + 1);
> +
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_video_modes);
> +
> +int of_video_mode_exists(struct device_node *np)
> +{
> + struct device_node *display_np;
> + struct device_node *mode_np;
> +
> + if (!np)
> + return -EINVAL;
> +
> + display_np = of_parse_phandle(np, "display", 0);
> +
> + if (!display_np)
> + return -EINVAL;
> +
> + mode_np = of_parse_phandle(np, "default-mode", 0);
> +
> + if (mode_np)
> + return 0;
> +
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(of_video_mode_exists);
> +
> +int of_get_display_mode(struct device_node *np, struct drm_display_mode *dmode, int index)
> +{
> + struct display disp;
> +
> + of_get_video_modes(np, &disp);
> +
> + if (index = OF_MODE_SELECTION)
> + index = disp.default_mode;
> + if (dmode)
> + videomode_to_display_mode(&disp, dmode, index);
> +
> + of_video_free(&disp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_display_mode);
> +
> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fbmode, int index)
> +{
> + struct display disp;
> +
> + of_get_video_modes(np, &disp);
> +
> + if (index = OF_MODE_SELECTION)
> + index = disp.default_mode;
> + if (fbmode)
> + videomode_to_fb_mode(&disp, fbmode, index);
> +
> + of_video_free(&disp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_fb_videomode);
> diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> new file mode 100644
> index 0000000..5571ce3
> --- /dev/null
> +++ b/include/linux/of_videomode.h
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright 2012 Sascha Hauer <s.hauer@pengutronix.de>
> + * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
> + *
> + * OF helpers for videomodes.
> + *
> + * This file is released under the GPLv2
> + */
> +
> +#ifndef __LINUX_OF_VIDEOMODE_H
> +#define __LINUX_OF_VIDEOMODE_H
> +
> +#define OF_MODE_SELECTION -1
> +
> +struct mode_property {
> + u32 min;
> + u32 typ;
> + u32 max;
> +};
> +
> +struct display {
> + u32 width_mm;
> + u32 height_mm;
> + struct videomode **modes;
> + int default_mode;
> + int num_modes;
> +};
> +
> +/* describe videomode in terms of hardware parameters */
> +struct videomode {
> + struct mode_property hback_porch;
> + struct mode_property hfront_porch;
> + struct mode_property hactive;
> + struct mode_property hsync_len;
> +
> + struct mode_property vback_porch;
> + struct mode_property vfront_porch;
> + struct mode_property vactive;
> + struct mode_property vsync_len;
> +
> + struct mode_property clock;
> +
> + bool hah;
> + bool vah;
> + bool interlaced;
> + bool doublescan;
> +};
> +
> +int of_video_mode_exists(struct device_node *np);
> +int videomode_to_display_mode(struct display *disp, struct drm_display_mode *dmode, int index);
> +int videomode_to_fb_mode(struct display *disp, struct fb_videomode *fbmode, int index);
> +int of_get_video_modes(struct device_node *np, struct display *disp);
> +int of_video_mode_exists(struct device_node *np);
> +int of_get_display_mode(struct device_node *np, struct drm_display_mode *dmode, int index);
> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fbmode, int index);
> +#endif /* __LINUX_OF_VIDEOMODE_H */
>
WARNING: multiple messages have this Message-ID (diff)
From: Rob Herring <robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: Steffen Trumtrar <s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Cc: linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org,
Laurent Pinchart
<laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org>,
kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org,
Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
Subject: Re: [PATCH v4] of: Add videomode helper
Date: Mon, 24 Sep 2012 08:42:12 -0500 [thread overview]
Message-ID: <50606334.7030902@gmail.com> (raw)
In-Reply-To: <1348042843-24673-1-git-send-email-s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
On 09/19/2012 03:20 AM, Steffen Trumtrar wrote:
> This patch adds a helper function for parsing videomodes from the devicetree.
> The videomode can be either converted to a struct drm_display_mode or a
> struct fb_videomode.
>
> Signed-off-by: Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> Signed-off-by: Steffen Trumtrar <s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> ---
>
> Hi!
>
> changes since v3:
> - print error messages
> - free alloced memory
> - general cleanup
>
> Regards
> Steffen
>
> .../devicetree/bindings/video/displaymode | 74 +++++
> drivers/of/Kconfig | 5 +
> drivers/of/Makefile | 1 +
> drivers/of/of_videomode.c | 283 ++++++++++++++++++++
> include/linux/of_videomode.h | 56 ++++
> 5 files changed, 419 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/video/displaymode
> create mode 100644 drivers/of/of_videomode.c
> create mode 100644 include/linux/of_videomode.h
>
> diff --git a/Documentation/devicetree/bindings/video/displaymode b/Documentation/devicetree/bindings/video/displaymode
> new file mode 100644
> index 0000000..990ca52
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/video/displaymode
> @@ -0,0 +1,74 @@
> +videomode bindings
> +==================
> +
> +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: displayclock in Hz
A major piece missing is the LCD controller to display interface width
and component ordering.
> +
> +Optional properties:
> + - width-mm, height-mm: Display dimensions in mm
> + - hsync-active-high (bool): Hsync pulse is active high
> + - vsync-active-high (bool): Vsync pulse is active high
> + - interlaced (bool): This is an interlaced mode
> + - doublescan (bool): This is a doublescan mode
> +
> +There are different ways of describing a display mode. The devicetree representation
> +corresponds to the one commonly found in datasheets for displays.
> +The description of the display and its mode is split in two parts: first the display
> +properties like size in mm and (optionally) multiple subnodes with the supported modes.
> +
> +Example:
> +
> + display@0 {
It would be useful to have a compatible string here. We may not always
know the panel type or have a fixed panel though. We could define
"generic-lcd" or something for cases where the panel type is unknown.
> + width-mm = <800>;
> + height-mm = <480>;
> + modes {
> + mode0: mode@0 {
> + /* 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-high;
> + };
> + };
> + };
> +
> +Every property also supports the use of ranges, so the commonly used datasheet
> +description with <min typ max>-tuples can be used.
> +
> +Example:
> +
> + mode1: mode@1 {
> + /* 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>;
> + };
> +
> +The videomode can be linked to a connector via phandles. The connector has to
> +support the display- and default-mode-property to link to and select a mode.
Could also be phandle in the lcd controller node? What are the '-' for?
Is "display-blah" a valid name or something?
"default-mode" is pretty generic. How about display-mode or
display-default-mode?
Rob
> +
> +Example:
> +
> + hdmi@00120000 {
> + status = "okay";
> + display = <&benq>;
> + default-mode = <&mode1>;
> + };
> +
> diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
> index dfba3e6..a3acaa3 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -83,4 +83,9 @@ config OF_MTD
> depends on MTD
> def_bool y
>
> +config OF_VIDEOMODE
> + def_bool y
> + help
> + helper to parse videomodes from the devicetree
> +
> endmenu # OF
> diff --git a/drivers/of/Makefile b/drivers/of/Makefile
> index e027f44..80e6db3 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_VIDEOMODE) += of_videomode.o
> diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> new file mode 100644
> index 0000000..52bfc74
> --- /dev/null
> +++ b/drivers/of/of_videomode.c
> @@ -0,0 +1,283 @@
> +/*
> + * OF helpers for parsing display modes
> + *
> + * Copyright (c) 2012 Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>, Pengutronix
> + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>, Pengutronix
> + *
> + * This file is released under the GPLv2
> + */
> +#include <linux/of.h>
> +#include <linux/fb.h>
> +#include <linux/export.h>
> +#include <linux/slab.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc.h>
> +#include <linux/of_videomode.h>
> +
> +static u32 of_video_get_value(struct mode_property *prop)
> +{
> + return (prop->min >= prop->typ) ? prop->min : prop->typ;
> +}
> +
> +/* read property into new mode_property */
> +static int of_video_parse_property(struct device_node *np, char *name,
> + struct mode_property *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);
> +
> + memset(result, 0, sizeof(*result));
> +
> + ret = of_property_read_u32_array(np, name, &result->min, cells);
> +
> + return ret;
> +}
> +
> +static int of_video_free(struct display *disp)
> +{
> + int i;
> +
> + for (i=0; i<disp->num_modes; i++)
> + kfree(disp->modes[i]);
> + kfree(disp->modes);
> +
> + return 0;
> +}
> +
> +int videomode_to_display_mode(struct display *disp, struct drm_display_mode *dmode, int index)
> +{
> + struct videomode *vm;
> +
> + memset(dmode, 0, sizeof(*dmode));
> +
> + if (index > disp->num_modes) {
> + pr_err("%s: wrong index: %d from %d\n", __func__, index, disp->num_modes);
> + return -EINVAL;
> + }
> +
> + vm = disp->modes[index];
> +
> + dmode->hdisplay = of_video_get_value(&vm->hactive);
> + dmode->hsync_start = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch);
> + dmode->hsync_end = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch)
> + + of_video_get_value(&vm->hsync_len);
> + dmode->htotal = of_video_get_value(&vm->hactive) + of_video_get_value(&vm->hfront_porch)
> + + of_video_get_value(&vm->hsync_len) + of_video_get_value(&vm->hback_porch);
> +
> + dmode->vdisplay = of_video_get_value(&vm->vactive);
> + dmode->vsync_start = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch);
> + dmode->vsync_end = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch)
> + + of_video_get_value(&vm->vsync_len);
> + dmode->vtotal = of_video_get_value(&vm->vactive) + of_video_get_value(&vm->vfront_porch) +
> + of_video_get_value(&vm->vsync_len) + of_video_get_value(&vm->vback_porch);
> +
> + dmode->width_mm = disp->width_mm;
> + dmode->height_mm = disp->height_mm;
> +
> + dmode->clock = of_video_get_value(&vm->clock) / 1000;
> +
> + 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);
> +
> +int videomode_to_fb_mode(struct display *disp, struct fb_videomode *fbmode, int index)
> +{
> + struct videomode *vm;
> +
> + memset(fbmode, 0, sizeof(*fbmode));
> +
> + if (index > disp->num_modes) {
> + pr_err("%s: wrong index: %d from %d\n", __func__, index, disp->num_modes);
> + return -EINVAL;
> + }
> +
> + vm = disp->modes[index];
> +
> + fbmode->xres = of_video_get_value(&vm->hactive);
> + fbmode->left_margin = of_video_get_value(&vm->hback_porch);
> + fbmode->right_margin = of_video_get_value(&vm->hfront_porch);
> + fbmode->hsync_len = of_video_get_value(&vm->hsync_len);
> +
> + fbmode->yres = of_video_get_value(&vm->vactive);
> + fbmode->upper_margin = of_video_get_value(&vm->vback_porch);
> + fbmode->lower_margin = of_video_get_value(&vm->vfront_porch);
> + fbmode->vsync_len = of_video_get_value(&vm->vsync_len);
> +
> + fbmode->pixclock = KHZ2PICOS(of_video_get_value(&vm->clock) / 1000);
> +
> + 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;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(videomode_to_fb_mode);
> +
> +int of_get_video_modes(struct device_node *np, struct display *disp)
> +{
> + struct device_node *display_np;
> + struct device_node *mode_np;
> + struct device_node *modes_np;
> + char *default_mode;
> +
> + int ret = 0;
> +
> + memset(disp, 0, sizeof(*disp));
> + disp->modes = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> +
> + if (!np) {
> + pr_err("%s: no node provided\n", __func__);
> + return -EINVAL;
> + }
> +
> + display_np = of_parse_phandle(np, "display", 0);
> +
> + if (!display_np) {
> + pr_err("%s: could not find display node\n", __func__);
> + return -EINVAL;
> + }
> +
> + of_property_read_u32(display_np, "width-mm", &disp->width_mm);
> + of_property_read_u32(display_np, "height-mm", &disp->height_mm);
> +
> + mode_np = of_parse_phandle(np, "default-mode", 0);
> +
> + if (!mode_np) {
> + pr_info("%s: no default-mode specified.\n", __func__);
> + mode_np = of_find_node_by_name(np, "mode");
> + }
> +
> + if (!mode_np) {
> + pr_err("%s: could not find any mode specification\n", __func__);
> + return -EINVAL;
> + }
> +
> + default_mode = (char *)mode_np->full_name;
> +
> + modes_np = of_find_node_by_name(np, "modes");
> + for_each_child_of_node(modes_np, mode_np) {
> + struct videomode *vm;
> +
> + vm = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> + disp->modes[disp->num_modes] = kmalloc(sizeof(struct videomode*), GFP_KERNEL);
> +
> + ret |= of_video_parse_property(mode_np, "hback-porch", &vm->hback_porch);
> + ret |= of_video_parse_property(mode_np, "hfront-porch", &vm->hfront_porch);
> + ret |= of_video_parse_property(mode_np, "hactive", &vm->hactive);
> + ret |= of_video_parse_property(mode_np, "hsync-len", &vm->hsync_len);
> + ret |= of_video_parse_property(mode_np, "vback-porch", &vm->vback_porch);
> + ret |= of_video_parse_property(mode_np, "vfront-porch", &vm->vfront_porch);
> + ret |= of_video_parse_property(mode_np, "vactive", &vm->vactive);
> + ret |= of_video_parse_property(mode_np, "vsync-len", &vm->vsync_len);
> + ret |= of_video_parse_property(mode_np, "clock", &vm->clock);
> +
> + if (ret)
> + return -EINVAL;
> +
> + vm->hah = of_property_read_bool(mode_np, "hsync-active-high");
> + vm->vah = of_property_read_bool(mode_np, "vsync-active-high");
> + vm->interlaced = of_property_read_bool(mode_np, "interlaced");
> + vm->doublescan = of_property_read_bool(mode_np, "doublescan");
> +
> + if (strcmp(default_mode,mode_np->full_name) == 0)
> + disp->default_mode = disp->num_modes;
> +
> + disp->modes[disp->num_modes] = vm;
> + disp->num_modes++;
> + }
> + of_node_put(display_np);
> +
> + pr_info("%s: found %d modelines. Using #%d as default\n", __func__,
> + disp->num_modes, disp->default_mode + 1);
> +
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_video_modes);
> +
> +int of_video_mode_exists(struct device_node *np)
> +{
> + struct device_node *display_np;
> + struct device_node *mode_np;
> +
> + if (!np)
> + return -EINVAL;
> +
> + display_np = of_parse_phandle(np, "display", 0);
> +
> + if (!display_np)
> + return -EINVAL;
> +
> + mode_np = of_parse_phandle(np, "default-mode", 0);
> +
> + if (mode_np)
> + return 0;
> +
> + return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(of_video_mode_exists);
> +
> +int of_get_display_mode(struct device_node *np, struct drm_display_mode *dmode, int index)
> +{
> + struct display disp;
> +
> + of_get_video_modes(np, &disp);
> +
> + if (index == OF_MODE_SELECTION)
> + index = disp.default_mode;
> + if (dmode)
> + videomode_to_display_mode(&disp, dmode, index);
> +
> + of_video_free(&disp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_display_mode);
> +
> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fbmode, int index)
> +{
> + struct display disp;
> +
> + of_get_video_modes(np, &disp);
> +
> + if (index == OF_MODE_SELECTION)
> + index = disp.default_mode;
> + if (fbmode)
> + videomode_to_fb_mode(&disp, fbmode, index);
> +
> + of_video_free(&disp);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_fb_videomode);
> diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> new file mode 100644
> index 0000000..5571ce3
> --- /dev/null
> +++ b/include/linux/of_videomode.h
> @@ -0,0 +1,56 @@
> +/*
> + * Copyright 2012 Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + * Copyright 2012 Steffen Trumtrar <s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
> + *
> + * OF helpers for videomodes.
> + *
> + * This file is released under the GPLv2
> + */
> +
> +#ifndef __LINUX_OF_VIDEOMODE_H
> +#define __LINUX_OF_VIDEOMODE_H
> +
> +#define OF_MODE_SELECTION -1
> +
> +struct mode_property {
> + u32 min;
> + u32 typ;
> + u32 max;
> +};
> +
> +struct display {
> + u32 width_mm;
> + u32 height_mm;
> + struct videomode **modes;
> + int default_mode;
> + int num_modes;
> +};
> +
> +/* describe videomode in terms of hardware parameters */
> +struct videomode {
> + struct mode_property hback_porch;
> + struct mode_property hfront_porch;
> + struct mode_property hactive;
> + struct mode_property hsync_len;
> +
> + struct mode_property vback_porch;
> + struct mode_property vfront_porch;
> + struct mode_property vactive;
> + struct mode_property vsync_len;
> +
> + struct mode_property clock;
> +
> + bool hah;
> + bool vah;
> + bool interlaced;
> + bool doublescan;
> +};
> +
> +int of_video_mode_exists(struct device_node *np);
> +int videomode_to_display_mode(struct display *disp, struct drm_display_mode *dmode, int index);
> +int videomode_to_fb_mode(struct display *disp, struct fb_videomode *fbmode, int index);
> +int of_get_video_modes(struct device_node *np, struct display *disp);
> +int of_video_mode_exists(struct device_node *np);
> +int of_get_display_mode(struct device_node *np, struct drm_display_mode *dmode, int index);
> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fbmode, int index);
> +#endif /* __LINUX_OF_VIDEOMODE_H */
>
next prev parent reply other threads:[~2012-09-24 13:42 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-09-19 8:20 [PATCH v4] of: Add videomode helper Steffen Trumtrar
2012-09-19 8:20 ` Steffen Trumtrar
[not found] ` <1348042843-24673-1-git-send-email-s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2012-09-19 9:19 ` Tomi Valkeinen
2012-09-19 9:19 ` Tomi Valkeinen
2012-09-20 21:29 ` Laurent Pinchart
2012-09-20 21:29 ` Laurent Pinchart
2012-09-21 12:47 ` Steffen Trumtrar
2012-09-21 12:47 ` Steffen Trumtrar
2012-09-24 13:42 ` Rob Herring [this message]
2012-09-24 13:42 ` Rob Herring
[not found] ` <50606334.7030902-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-09-24 14:12 ` Sascha Hauer
2012-09-24 14:12 ` Sascha Hauer
[not found] ` <20120924141233.GN1322-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2012-09-24 15:18 ` Rob Herring
2012-09-24 15:18 ` Rob Herring
[not found] ` <506079CF.40902-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-09-24 19:16 ` Sascha Hauer
2012-09-24 19:16 ` Sascha Hauer
[not found] ` <20120924191628.GR1322-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2012-09-25 7:51 ` Steffen Trumtrar
2012-09-25 7:51 ` Steffen Trumtrar
2012-09-24 15:45 ` Stephen Warren
2012-09-24 15:45 ` Stephen Warren
[not found] ` <50608000.6050007-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2012-09-24 18:26 ` Rob Herring
2012-09-24 18:26 ` Rob Herring
[not found] ` <5060A5D5.7040908-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2012-09-24 23:09 ` Stephen Warren
2012-09-24 23:09 ` Stephen Warren
[not found] ` <5060E82A.3030404-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2012-09-25 8:03 ` Steffen Trumtrar
2012-09-25 8:03 ` Steffen Trumtrar
2012-09-25 11:23 ` Laurent Pinchart
2012-09-25 11:23 ` Laurent Pinchart
2012-09-21 13:07 ` Hans Verkuil
2012-09-21 13:07 ` Hans Verkuil
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=50606334.7030902@gmail.com \
--to=robherring2@gmail.com \
--cc=devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org \
--cc=dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org \
--cc=kernel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
--cc=laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw@public.gmane.org \
--cc=linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
--cc=s.trumtrar-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.