diff mbox

[2/2,v6] of: add generic videomode description

Message ID 1349373560-11128-3-git-send-email-s.trumtrar@pengutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Steffen Trumtrar Oct. 4, 2012, 5:59 p.m. UTC
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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/of/Kconfig           |    5 +
 drivers/of/Makefile          |    1 +
 drivers/of/of_videomode.c    |  212 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/of_videomode.h |   41 ++++++++
 4 files changed, 259 insertions(+)
 create mode 100644 drivers/of/of_videomode.c
 create mode 100644 include/linux/of_videomode.h

Comments

Stephen Warren Oct. 4, 2012, 6:51 p.m. UTC | #1
On 10/04/2012 11:59 AM, Steffen Trumtrar wrote:
> 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

> +++ b/drivers/of/of_videomode.c

> +int videomode_from_timing(struct display_timings *disp, struct videomode *vm,

> +	st = display_timings_get(disp, index);
> +
> +	if (!st) {

It's a little odd to leave a blank line between those two lines.

Only half of the code in this file seems OF-related; the routines to
convert a timing to a videomode or drm display mode seem like they'd be
useful outside device tree, so I wonder if putting them into
of_videomode.c is the correct thing to do. Still, it's probably not a
big deal.
Steffen Trumtrar Oct. 5, 2012, 3:51 p.m. UTC | #2
On Thu, Oct 04, 2012 at 12:51:00PM -0600, Stephen Warren wrote:
> On 10/04/2012 11:59 AM, Steffen Trumtrar wrote:
> > 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
> 
> > +++ b/drivers/of/of_videomode.c
> 
> > +int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
> 
> > +	st = display_timings_get(disp, index);
> > +
> > +	if (!st) {
> 
> It's a little odd to leave a blank line between those two lines.

Hm, well okay. That can be remedied

> 
> Only half of the code in this file seems OF-related; the routines to
> convert a timing to a videomode or drm display mode seem like they'd be
> useful outside device tree, so I wonder if putting them into
> of_videomode.c is the correct thing to do. Still, it's probably not a
> big deal.
> 

I am not sure, what the appropriate way to do this is. I can split it up (again).
Laurent Pinchart Oct. 7, 2012, 1:38 p.m. UTC | #3
Hi Steffen,

On Friday 05 October 2012 17:51:21 Steffen Trumtrar wrote:
> On Thu, Oct 04, 2012 at 12:51:00PM -0600, Stephen Warren wrote:
> > On 10/04/2012 11:59 AM, Steffen Trumtrar wrote:
> > > 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
> > > 
> > > +++ b/drivers/of/of_videomode.c
> > > 
> > > +int videomode_from_timing(struct display_timings *disp, struct
> > > videomode *vm,
> > > 
> > > +	st = display_timings_get(disp, index);
> > > +
> > > +	if (!st) {
> > 
> > It's a little odd to leave a blank line between those two lines.
> 
> Hm, well okay. That can be remedied
> 
> > Only half of the code in this file seems OF-related; the routines to
> > convert a timing to a videomode or drm display mode seem like they'd be
> > useful outside device tree, so I wonder if putting them into
> > of_videomode.c is the correct thing to do. Still, it's probably not a
> > big deal.
> 
> I am not sure, what the appropriate way to do this is. I can split it up
> (again).

I think it would make sense to move them to their respective subsystems.
Tomi Valkeinen Oct. 8, 2012, 7:21 a.m. UTC | #4
On Thu, 2012-10-04 at 19:59 +0200, Steffen Trumtrar wrote:
> 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> ---
>  drivers/of/Kconfig           |    5 +
>  drivers/of/Makefile          |    1 +
>  drivers/of/of_videomode.c    |  212 ++++++++++++++++++++++++++++++++++++++++++
>  include/linux/of_videomode.h |   41 ++++++++
>  4 files changed, 259 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 646deb0..74282e2 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -88,4 +88,9 @@ config OF_DISPLAY_TIMINGS
>  	help
>  	  helper to parse display timings from the devicetree
>  
> +config OF_VIDEOMODE
> +	def_bool y
> +	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..76ac16e
> --- /dev/null
> +++ b/drivers/of/of_videomode.c
> @@ -0,0 +1,212 @@
> +/*
> + * 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/fb.h>
> +#include <linux/slab.h>
> +#include <drm/drm_mode.h>
> +#include <linux/of_display_timings.h>
> +#include <linux/of_videomode.h>
> +
> +void dump_fb_videomode(struct fb_videomode *m)
> +{
> +        pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
> +                m->refresh, m->xres, m->yres, 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);
> +}
> +
> +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);
> +}
> +
> +int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
> +			int index)
> +{
> +	struct signal_timing *st = NULL;
> +
> +	if (!vm)
> +		return -EINVAL;
> +
> +	st = display_timings_get(disp, index);
> +
> +	if (!st) {
> +		pr_err("%s: no signal timings found\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> +
> +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> +
> +	vm->vah = st->vsync_pol_active_high;
> +	vm->hah = st->hsync_pol_active_high;
> +	vm->interlaced = st->interlaced;
> +	vm->doublescan = st->doublescan;
> +
> +	return 0;
> +}
> +
> +int of_get_videomode(struct device_node *np, struct videomode *vm, int index)
> +{
> +	struct display_timings *disp;
> +	int ret = 0;
> +
> +	disp = of_get_display_timing_list(np);
> +
> +	if (!disp) {
> +		pr_err("%s: no timings specified\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (index == OF_DEFAULT_TIMING)
> +		index = disp->default_timing;
> +
> +	ret = videomode_from_timing(disp, vm, index);
> +
> +	if (ret)
> +		return ret;
> +
> +	display_timings_release(disp);
> +
> +	if (!vm) {
> +		pr_err("%s: could not get videomode %d\n", __func__, index);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_videomode);
> +
> +#if defined(CONFIG_DRM)
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> +{
> +	memset(dmode, 0, sizeof(*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;
> +
> +	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 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);
> +#else
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> +{
> +	return 0;
> +}
> +
> +int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode,
> +			int index)
> +{
> +	return 0;
> +}
> +#endif
> +
> +int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode)
> +{
> +	memset(fbmode, 0, sizeof(*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;
> +
> +	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_videomode);
> +
> +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_drm_display_mode);
> diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> new file mode 100644
> index 0000000..96efe01
> --- /dev/null
> +++ b/include/linux/of_videomode.h
> @@ -0,0 +1,41 @@
> +/*
> + * 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 <drm/drmP.h>

You don't need to include this.

> +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;
> +
> +	bool hah;
> +	bool vah;
> +	bool interlaced;
> +	bool doublescan;
> +
> +};

This is not really of related. And actually, neither is the struct
signal_timing in the previous patch. It would be nice to have these in a
common header that fb, drm, and others could use instead of each having
their own timing structs. 

But that's probably out of scope for this series =). Did you check the
timing structs from the video related frameworks in the kernel to see if
your structs contain all the info the others have, so that, at least in
theory, everybody could use these common structs?

 Tomi
Steffen Trumtrar Oct. 8, 2012, 7:57 a.m. UTC | #5
On Mon, Oct 08, 2012 at 10:21:53AM +0300, Tomi Valkeinen wrote:
> On Thu, 2012-10-04 at 19:59 +0200, Steffen Trumtrar wrote:
> > 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > ---
> >  drivers/of/Kconfig           |    5 +
> >  drivers/of/Makefile          |    1 +
> >  drivers/of/of_videomode.c    |  212 ++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/of_videomode.h |   41 ++++++++
> >  4 files changed, 259 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 646deb0..74282e2 100644
> > --- a/drivers/of/Kconfig
> > +++ b/drivers/of/Kconfig
> > @@ -88,4 +88,9 @@ config OF_DISPLAY_TIMINGS
> >  	help
> >  	  helper to parse display timings from the devicetree
> >  
> > +config OF_VIDEOMODE
> > +	def_bool y
> > +	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..76ac16e
> > --- /dev/null
> > +++ b/drivers/of/of_videomode.c
> > @@ -0,0 +1,212 @@
> > +/*
> > + * 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/fb.h>
> > +#include <linux/slab.h>
> > +#include <drm/drm_mode.h>
> > +#include <linux/of_display_timings.h>
> > +#include <linux/of_videomode.h>
> > +
> > +void dump_fb_videomode(struct fb_videomode *m)
> > +{
> > +        pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
> > +                m->refresh, m->xres, m->yres, 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);
> > +}
> > +
> > +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);
> > +}
> > +
> > +int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
> > +			int index)
> > +{
> > +	struct signal_timing *st = NULL;
> > +
> > +	if (!vm)
> > +		return -EINVAL;
> > +
> > +	st = display_timings_get(disp, index);
> > +
> > +	if (!st) {
> > +		pr_err("%s: no signal timings found\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> > +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> > +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> > +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> > +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> > +
> > +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> > +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> > +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> > +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> > +
> > +	vm->vah = st->vsync_pol_active_high;
> > +	vm->hah = st->hsync_pol_active_high;
> > +	vm->interlaced = st->interlaced;
> > +	vm->doublescan = st->doublescan;
> > +
> > +	return 0;
> > +}
> > +
> > +int of_get_videomode(struct device_node *np, struct videomode *vm, int index)
> > +{
> > +	struct display_timings *disp;
> > +	int ret = 0;
> > +
> > +	disp = of_get_display_timing_list(np);
> > +
> > +	if (!disp) {
> > +		pr_err("%s: no timings specified\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (index == OF_DEFAULT_TIMING)
> > +		index = disp->default_timing;
> > +
> > +	ret = videomode_from_timing(disp, vm, index);
> > +
> > +	if (ret)
> > +		return ret;
> > +
> > +	display_timings_release(disp);
> > +
> > +	if (!vm) {
> > +		pr_err("%s: could not get videomode %d\n", __func__, index);
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(of_get_videomode);
> > +
> > +#if defined(CONFIG_DRM)
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> > +{
> > +	memset(dmode, 0, sizeof(*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;
> > +
> > +	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 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);
> > +#else
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> > +{
> > +	return 0;
> > +}
> > +
> > +int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode,
> > +			int index)
> > +{
> > +	return 0;
> > +}
> > +#endif
> > +
> > +int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode)
> > +{
> > +	memset(fbmode, 0, sizeof(*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;
> > +
> > +	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_videomode);
> > +
> > +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_drm_display_mode);
> > diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> > new file mode 100644
> > index 0000000..96efe01
> > --- /dev/null
> > +++ b/include/linux/of_videomode.h
> > @@ -0,0 +1,41 @@
> > +/*
> > + * 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 <drm/drmP.h>
> 
> You don't need to include this.
> 

That is a fix to my liking. Easily done ;-)

> > +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;
> > +
> > +	bool hah;
> > +	bool vah;
> > +	bool interlaced;
> > +	bool doublescan;
> > +
> > +};
> 
> This is not really of related. And actually, neither is the struct
> signal_timing in the previous patch. It would be nice to have these in a
> common header that fb, drm, and others could use instead of each having
> their own timing structs. 
> 
> But that's probably out of scope for this series =). Did you check the
> timing structs from the video related frameworks in the kernel to see if
> your structs contain all the info the others have, so that, at least in
> theory, everybody could use these common structs?
> 
>  Tomi
> 

Yes. Stephen and Laurent already suggested to split it up.
No, all info is not contained. That starts with drm, which has width-mm,..
If time permits, I will go over that.

Regards,
Steffen
Laurent Pinchart Oct. 8, 2012, 12:13 p.m. UTC | #6
Hi Steffen,

Thanks for the patch.

On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
> 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> ---
>  drivers/of/Kconfig           |    5 +
>  drivers/of/Makefile          |    1 +
>  drivers/of/of_videomode.c    |  212 +++++++++++++++++++++++++++++++++++++++
>  include/linux/of_videomode.h |   41 ++++++++
>  4 files changed, 259 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 646deb0..74282e2 100644
> --- a/drivers/of/Kconfig
> +++ b/drivers/of/Kconfig
> @@ -88,4 +88,9 @@ config OF_DISPLAY_TIMINGS
>  	help
>  	  helper to parse display timings from the devicetree
> 
> +config OF_VIDEOMODE
> +	def_bool y
> +	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..76ac16e
> --- /dev/null
> +++ b/drivers/of/of_videomode.c
> @@ -0,0 +1,212 @@
> +/*
> + * 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/fb.h>
> +#include <linux/slab.h>
> +#include <drm/drm_mode.h>
> +#include <linux/of_display_timings.h>
> +#include <linux/of_videomode.h>
> +
> +void dump_fb_videomode(struct fb_videomode *m)
> +{
> +        pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",

That's going to be pretty difficult to read :-) Would it make sense to group 
several attributes logically (for instance using %ux%u for m->xres, m->yres) ?

> +                m->refresh, m->xres, m->yres, 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);
> +}

Shouldn't this (and the other non exported functions below) be 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);
> +}
> +
> +int videomode_from_timing(struct display_timings *disp, struct videomode
> *vm,
> +			int index)
> +{
> +	struct signal_timing *st = NULL;
> +
> +	if (!vm)
> +		return -EINVAL;
> +

What about making vm a mandatory argument ? It looks to me like a caller bug 
if vm is NULL.

> +	st = display_timings_get(disp, index);
> +

You can remove the blank line.

> +	if (!st) {
> +		pr_err("%s: no signal timings found\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> +
> +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> +
> +	vm->vah = st->vsync_pol_active_high;
> +	vm->hah = st->hsync_pol_active_high;
> +	vm->interlaced = st->interlaced;
> +	vm->doublescan = st->doublescan;
> +
> +	return 0;
> +}
> +
> +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> index)

I wonder how to avoid abuse of this functions. It's a useful helper for 
drivers that need to get a video mode once only, but would result in lower 
performances if a driver calls it for every mode. Drivers must call 
of_get_display_timing_list instead in that case and case the display timings. 
I'm wondering whether we should really expose of_get_videomode.

> +{
> +	struct display_timings *disp;
> +	int ret = 0;

No need to assign ret to 0 here.

> +
> +	disp = of_get_display_timing_list(np);
> +

You can remove the blank line.

> +	if (!disp) {
> +		pr_err("%s: no timings specified\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	if (index == OF_DEFAULT_TIMING)
> +		index = disp->default_timing;
> +
> +	ret = videomode_from_timing(disp, vm, index);
> +

No need for a blank line.

> +	if (ret)
> +		return ret;
> +
> +	display_timings_release(disp);
> +
> +	if (!vm) {
> +		pr_err("%s: could not get videomode %d\n", __func__, index);
> +		return -EINVAL;
> +	}

This can't happen. If vm is NULL the videomode_from_timing call above will 
return -EINVAL, and this function will then return immediately without 
reaching this code block.

> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_videomode);
> +
> +#if defined(CONFIG_DRM)
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> *dmode)
> +{
> +	memset(dmode, 0, sizeof(*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;
> +
> +	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 of_get_drm_display_mode(struct device_node *np, struct drm_display_mode
> *dmode,
> +			int index)

Same as above, do we really need to expose this helper function ? If so we 
should at least clearly document (using kerneldoc for instance) that drivers 
should only use it if they need to get a single mode once.

> +{
> +	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);
> +#else
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> *dmode)
> +{
> +	return 0;
> +}
> +
> +int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode
> *dmode,
> +			int index)
> +{
> +	return 0;
> +}

What about not defining those if CONFIG_DRM is not set ? No driver should call 
these functions in that case. If we really need those stubs they should return 
an error.

> +#endif
> +
> +int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode
> *fbmode)
> +{
> +	memset(fbmode, 0, sizeof(*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;
> +
> +	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_videomode);
> +
> +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_drm_display_mode);
> diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> new file mode 100644
> index 0000000..96efe01
> --- /dev/null
> +++ b/include/linux/of_videomode.h
> @@ -0,0 +1,41 @@
> +/*
> + * 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 <drm/drmP.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;
> +
> +	bool hah;
> +	bool vah;
> +	bool interlaced;
> +	bool doublescan;
> +
> +};
> +
> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> *dmode); +int videomode_to_fb_videomode(struct videomode *vm, struct
> fb_videomode *fbmode); +int of_get_videomode(struct device_node *np, struct
> videomode *vm, int index); +int of_get_drm_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 *fb,
> int index); +#endif /* __LINUX_VIDEOMODE_H */
Laurent Pinchart Oct. 8, 2012, 12:19 p.m. UTC | #7
Hi Steffen,

On Monday 08 October 2012 09:57:41 Steffen Trumtrar wrote:
> On Mon, Oct 08, 2012 at 10:21:53AM +0300, Tomi Valkeinen wrote:
> > On Thu, 2012-10-04 at 19:59 +0200, Steffen Trumtrar wrote:

[snip]

> > > diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> > > new file mode 100644
> > > index 0000000..96efe01
> > > --- /dev/null
> > > +++ b/include/linux/of_videomode.h
> > > @@ -0,0 +1,41 @@
> > > +/*
> > > + * 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 <drm/drmP.h>
> > 
> > You don't need to include this.
> 
> That is a fix to my liking. Easily done ;-)
> 
> > > +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;
> > > +
> > > +	bool hah;
> > > +	bool vah;
> > > +	bool interlaced;
> > > +	bool doublescan;
> > > +
> > > +};
> > 
> > This is not really of related. And actually, neither is the struct
> > signal_timing in the previous patch. It would be nice to have these in a
> > common header that fb, drm, and others could use instead of each having
> > their own timing structs.
> > 
> > But that's probably out of scope for this series =). Did you check the
> > timing structs from the video related frameworks in the kernel to see if
> > your structs contain all the info the others have, so that, at least in
> > theory, everybody could use these common structs?
> > 
> >  Tomi
> 
> Yes. Stephen and Laurent already suggested to split it up.
> No, all info is not contained. That starts with drm, which has width-mm,..
> If time permits, I will go over that.

Just to make sure we won't forget it, the V4L2 version of the timings 
structure is struct v4l2_bt_timings in include/linux/videodev2.h.
Steffen Trumtrar Oct. 8, 2012, 12:48 p.m. UTC | #8
On Mon, Oct 08, 2012 at 02:13:50PM +0200, Laurent Pinchart wrote:
> Hi Steffen,
> 
> Thanks for the patch.
> 
> On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
> > 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > ---
> >  drivers/of/Kconfig           |    5 +
> >  drivers/of/Makefile          |    1 +
> >  drivers/of/of_videomode.c    |  212 +++++++++++++++++++++++++++++++++++++++
> >  include/linux/of_videomode.h |   41 ++++++++
> >  4 files changed, 259 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 646deb0..74282e2 100644
> > --- a/drivers/of/Kconfig
> > +++ b/drivers/of/Kconfig
> > @@ -88,4 +88,9 @@ config OF_DISPLAY_TIMINGS
> >  	help
> >  	  helper to parse display timings from the devicetree
> > 
> > +config OF_VIDEOMODE
> > +	def_bool y
> > +	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..76ac16e
> > --- /dev/null
> > +++ b/drivers/of/of_videomode.c
> > @@ -0,0 +1,212 @@
> > +/*
> > + * 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/fb.h>
> > +#include <linux/slab.h>
> > +#include <drm/drm_mode.h>
> > +#include <linux/of_display_timings.h>
> > +#include <linux/of_videomode.h>
> > +
> > +void dump_fb_videomode(struct fb_videomode *m)
> > +{
> > +        pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
> 
> That's going to be pretty difficult to read :-) Would it make sense to group 
> several attributes logically (for instance using %ux%u for m->xres, m->yres) ?
> 

No problem. That can be changed.

> > +                m->refresh, m->xres, m->yres, 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);
> > +}
> 
> Shouldn't this (and the other non exported functions below) be static ?
> 

Yes.

> > +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);
> > +}
> > +
> > +int videomode_from_timing(struct display_timings *disp, struct videomode
> > *vm,
> > +			int index)
> > +{
> > +	struct signal_timing *st = NULL;
> > +
> > +	if (!vm)
> > +		return -EINVAL;
> > +
> 
> What about making vm a mandatory argument ? It looks to me like a caller bug 
> if vm is NULL.
> 

The caller must provide the struct videomode, yes. Wouldn't the kernel hang itself
with a NULL pointer exception, if I just work with it ?

> > +	st = display_timings_get(disp, index);
> > +
> 
> You can remove the blank line.
> 
> > +	if (!st) {
> > +		pr_err("%s: no signal timings found\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> > +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> > +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> > +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> > +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> > +
> > +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> > +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> > +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> > +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> > +
> > +	vm->vah = st->vsync_pol_active_high;
> > +	vm->hah = st->hsync_pol_active_high;
> > +	vm->interlaced = st->interlaced;
> > +	vm->doublescan = st->doublescan;
> > +
> > +	return 0;
> > +}
> > +
> > +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> > index)
> 
> I wonder how to avoid abuse of this functions. It's a useful helper for 
> drivers that need to get a video mode once only, but would result in lower 
> performances if a driver calls it for every mode. Drivers must call 
> of_get_display_timing_list instead in that case and case the display timings. 
> I'm wondering whether we should really expose of_get_videomode.
> 

The intent was to let the driver decide. That way all the other overhead may
be skipped.

> > +{
> > +	struct display_timings *disp;
> > +	int ret = 0;
> 
> No need to assign ret to 0 here.
> 

Ah, yes. Unneeded in this case.

> > +
> > +	disp = of_get_display_timing_list(np);
> > +
> 
> You can remove the blank line.
> 
> > +	if (!disp) {
> > +		pr_err("%s: no timings specified\n", __func__);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (index == OF_DEFAULT_TIMING)
> > +		index = disp->default_timing;
> > +
> > +	ret = videomode_from_timing(disp, vm, index);
> > +
> 
> No need for a blank line.
> 
> > +	if (ret)
> > +		return ret;
> > +
> > +	display_timings_release(disp);
> > +
> > +	if (!vm) {
> > +		pr_err("%s: could not get videomode %d\n", __func__, index);
> > +		return -EINVAL;
> > +	}
> 
> This can't happen. If vm is NULL the videomode_from_timing call above will 
> return -EINVAL, and this function will then return immediately without 
> reaching this code block.
> 

Okay.

> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(of_get_videomode);
> > +
> > +#if defined(CONFIG_DRM)
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> > *dmode)
> > +{
> > +	memset(dmode, 0, sizeof(*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;
> > +
> > +	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 of_get_drm_display_mode(struct device_node *np, struct drm_display_mode
> > *dmode,
> > +			int index)
> 
> Same as above, do we really need to expose this helper function ? If so we 
> should at least clearly document (using kerneldoc for instance) that drivers 
> should only use it if they need to get a single mode once.
> 
> > +{
> > +	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);
> > +#else
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> > *dmode)
> > +{
> > +	return 0;
> > +}
> > +
> > +int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode
> > *dmode,
> > +			int index)
> > +{
> > +	return 0;
> > +}
> 
> What about not defining those if CONFIG_DRM is not set ? No driver should call 
> these functions in that case. If we really need those stubs they should return 
> an error.
> 

Okay. I will remove them.

> > +#endif
> > +
> > +int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode
> > *fbmode)
> > +{
> > +	memset(fbmode, 0, sizeof(*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;
> > +
> > +	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_videomode);
> > +
> > +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_drm_display_mode);
> > diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> > new file mode 100644
> > index 0000000..96efe01
> > --- /dev/null
> > +++ b/include/linux/of_videomode.h
> > @@ -0,0 +1,41 @@
> > +/*
> > + * 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 <drm/drmP.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;
> > +
> > +	bool hah;
> > +	bool vah;
> > +	bool interlaced;
> > +	bool doublescan;
> > +
> > +};
> > +
> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode
> > *dmode); +int videomode_to_fb_videomode(struct videomode *vm, struct
> > fb_videomode *fbmode); +int of_get_videomode(struct device_node *np, struct
> > videomode *vm, int index); +int of_get_drm_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 *fb,
> > int index); +#endif /* __LINUX_VIDEOMODE_H */
> -- 
> Regards,
> 
> Laurent Pinchart
> 


Regards,

Steffen
Laurent Pinchart Oct. 8, 2012, 8:52 p.m. UTC | #9
Hi Steffen,

On Monday 08 October 2012 14:48:01 Steffen Trumtrar wrote:
> On Mon, Oct 08, 2012 at 02:13:50PM +0200, Laurent Pinchart wrote:
> > On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
> > > 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > ---
> > > 
> > >  drivers/of/Kconfig           |    5 +
> > >  drivers/of/Makefile          |    1 +
> > >  drivers/of/of_videomode.c    |  212 +++++++++++++++++++++++++++++++++++
> > >  include/linux/of_videomode.h |   41 ++++++++
> > >  4 files changed, 259 insertions(+)
> > >  create mode 100644 drivers/of/of_videomode.c
> > >  create mode 100644 include/linux/of_videomode.h

[snip]

> > > diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> > > new file mode 100644
> > > index 0000000..76ac16e
> > > --- /dev/null
> > > +++ b/drivers/of/of_videomode.c

[snip]

> > > +int videomode_from_timing(struct display_timings *disp, struct
> > > videomode *vm,
> > > +			int index)
> > > +{
> > > +	struct signal_timing *st = NULL;
> > > +
> > > +	if (!vm)
> > > +		return -EINVAL;
> > > +
> > 
> > What about making vm a mandatory argument ? It looks to me like a caller
> > bug if vm is NULL.
> 
> The caller must provide the struct videomode, yes. Wouldn't the kernel hang
> itself with a NULL pointer exception, if I just work with it ?

The kernel would oops, clearly showing the caller that a non-null vm is needed 
:-)

> > > +	st = display_timings_get(disp, index);
> > > +
> > 
> > You can remove the blank line.
> > 
> > > +	if (!st) {
> > > +		pr_err("%s: no signal timings found\n", __func__);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> > > +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> > > +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> > > +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> > > +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> > > +
> > > +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> > > +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> > > +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> > > +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> > > +
> > > +	vm->vah = st->vsync_pol_active_high;
> > > +	vm->hah = st->hsync_pol_active_high;
> > > +	vm->interlaced = st->interlaced;
> > > +	vm->doublescan = st->doublescan;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> > > index)
> > 
> > I wonder how to avoid abuse of this functions. It's a useful helper for
> > drivers that need to get a video mode once only, but would result in lower
> > performances if a driver calls it for every mode. Drivers must call
> > of_get_display_timing_list instead in that case and case the display
> > timings. I'm wondering whether we should really expose of_get_videomode.
> 
> The intent was to let the driver decide. That way all the other overhead may
> be skipped.

My point is that driver writers might just call of_get_videomode() in a loop, 
not knowing that it's expensive. I want to avoid that. We need to at least add 
kerneldoc to the function stating that this shouldn't be done.

> > > +{
> > > +	struct display_timings *disp;
> > > +	int ret = 0;
> > 
> > No need to assign ret to 0 here.
> 
> Ah, yes. Unneeded in this case.
> 
> > > +
> > > +	disp = of_get_display_timing_list(np);
> > > +
> > 
> > You can remove the blank line.
> > 
> > > +	if (!disp) {
> > > +		pr_err("%s: no timings specified\n", __func__);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	if (index == OF_DEFAULT_TIMING)
> > > +		index = disp->default_timing;
> > > +
> > > +	ret = videomode_from_timing(disp, vm, index);
> > > +
> > 
> > No need for a blank line.
> > 
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	display_timings_release(disp);
> > > +
> > > +	if (!vm) {
> > > +		pr_err("%s: could not get videomode %d\n", __func__, index);
> > > +		return -EINVAL;
> > > +	}
> > 
> > This can't happen. If vm is NULL the videomode_from_timing call above will
> > return -EINVAL, and this function will then return immediately without
> > reaching this code block.
> 
> Okay.
> 
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL_GPL(of_get_videomode);
Steffen Trumtrar Oct. 9, 2012, 7:26 a.m. UTC | #10
Hi Laurent,

On Mon, Oct 08, 2012 at 10:52:04PM +0200, Laurent Pinchart wrote:
> Hi Steffen,
> 
> On Monday 08 October 2012 14:48:01 Steffen Trumtrar wrote:
> > On Mon, Oct 08, 2012 at 02:13:50PM +0200, Laurent Pinchart wrote:
> > > On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
> > > > 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: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > > ---
> > > > 
> > > >  drivers/of/Kconfig           |    5 +
> > > >  drivers/of/Makefile          |    1 +
> > > >  drivers/of/of_videomode.c    |  212 +++++++++++++++++++++++++++++++++++
> > > >  include/linux/of_videomode.h |   41 ++++++++
> > > >  4 files changed, 259 insertions(+)
> > > >  create mode 100644 drivers/of/of_videomode.c
> > > >  create mode 100644 include/linux/of_videomode.h
> 
> [snip]
> 
> > > > diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> > > > new file mode 100644
> > > > index 0000000..76ac16e
> > > > --- /dev/null
> > > > +++ b/drivers/of/of_videomode.c
> 
> [snip]
> 
> > > > +int videomode_from_timing(struct display_timings *disp, struct
> > > > videomode *vm,
> > > > +			int index)
> > > > +{
> > > > +	struct signal_timing *st = NULL;
> > > > +
> > > > +	if (!vm)
> > > > +		return -EINVAL;
> > > > +
> > > 
> > > What about making vm a mandatory argument ? It looks to me like a caller
> > > bug if vm is NULL.
> > 
> > The caller must provide the struct videomode, yes. Wouldn't the kernel hang
> > itself with a NULL pointer exception, if I just work with it ?
> 
> The kernel would oops, clearly showing the caller that a non-null vm is needed 
> :-)
> 

Okay. No error checking it is then.

> > > > +	st = display_timings_get(disp, index);
> > > > +
> > > 
> > > You can remove the blank line.
> > > 
> > > > +	if (!st) {
> > > > +		pr_err("%s: no signal timings found\n", __func__);
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
> > > > +	vm->hactive = signal_timing_get_value(&st->hactive, 0);
> > > > +	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
> > > > +	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
> > > > +	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
> > > > +
> > > > +	vm->vactive = signal_timing_get_value(&st->vactive, 0);
> > > > +	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
> > > > +	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
> > > > +	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
> > > > +
> > > > +	vm->vah = st->vsync_pol_active_high;
> > > > +	vm->hah = st->hsync_pol_active_high;
> > > > +	vm->interlaced = st->interlaced;
> > > > +	vm->doublescan = st->doublescan;
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +
> > > > +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> > > > index)
> > > 
> > > I wonder how to avoid abuse of this functions. It's a useful helper for
> > > drivers that need to get a video mode once only, but would result in lower
> > > performances if a driver calls it for every mode. Drivers must call
> > > of_get_display_timing_list instead in that case and case the display
> > > timings. I'm wondering whether we should really expose of_get_videomode.
> > 
> > The intent was to let the driver decide. That way all the other overhead may
> > be skipped.
> 
> My point is that driver writers might just call of_get_videomode() in a loop, 
> not knowing that it's expensive. I want to avoid that. We need to at least add 
> kerneldoc to the function stating that this shouldn't be done.
> 

You're right. That should be made clear in the code.

> > > > +{
> > > > +	struct display_timings *disp;
> > > > +	int ret = 0;
> > > 
> > > No need to assign ret to 0 here.
> > 
> > Ah, yes. Unneeded in this case.
> > 
> > > > +
> > > > +	disp = of_get_display_timing_list(np);
> > > > +
> > > 
> > > You can remove the blank line.
> > > 
> > > > +	if (!disp) {
> > > > +		pr_err("%s: no timings specified\n", __func__);
> > > > +		return -EINVAL;
> > > > +	}
> > > > +
> > > > +	if (index == OF_DEFAULT_TIMING)
> > > > +		index = disp->default_timing;
> > > > +
> > > > +	ret = videomode_from_timing(disp, vm, index);
> > > > +
> > > 
> > > No need for a blank line.
> > > 
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	display_timings_release(disp);
> > > > +
> > > > +	if (!vm) {
> > > > +		pr_err("%s: could not get videomode %d\n", __func__, index);
> > > > +		return -EINVAL;
> > > > +	}
> > > 
> > > This can't happen. If vm is NULL the videomode_from_timing call above will
> > > return -EINVAL, and this function will then return immediately without
> > > reaching this code block.
> > 
> > Okay.
> > 
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(of_get_videomode);
> 
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> 

Regards,

Steffen
Thierry Reding Oct. 20, 2012, 10:45 a.m. UTC | #11
On Sun, Oct 07, 2012 at 03:38:33PM +0200, Laurent Pinchart wrote:
> Hi Steffen,
> 
> On Friday 05 October 2012 17:51:21 Steffen Trumtrar wrote:
> > On Thu, Oct 04, 2012 at 12:51:00PM -0600, Stephen Warren wrote:
> > > On 10/04/2012 11:59 AM, Steffen Trumtrar wrote:
> > > > 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
> > > > 
> > > > +++ b/drivers/of/of_videomode.c
> > > > 
> > > > +int videomode_from_timing(struct display_timings *disp, struct
> > > > videomode *vm,
> > > > 
> > > > +	st = display_timings_get(disp, index);
> > > > +
> > > > +	if (!st) {
> > > 
> > > It's a little odd to leave a blank line between those two lines.
> > 
> > Hm, well okay. That can be remedied
> > 
> > > Only half of the code in this file seems OF-related; the routines to
> > > convert a timing to a videomode or drm display mode seem like they'd be
> > > useful outside device tree, so I wonder if putting them into
> > > of_videomode.c is the correct thing to do. Still, it's probably not a
> > > big deal.
> > 
> > I am not sure, what the appropriate way to do this is. I can split it up
> > (again).
> 
> I think it would make sense to move them to their respective subsystems.

I agree. While looking at integrating this for Tegra DRM, I came across
the issue that if I build DRM as a module, linking with this code will
fail. The reason for that was that it was that the code, itself builtin,
uses drm_mode_set_name(), which would be exported by the drm module. So
I had to modifiy the Kconfig entries to be "def_tristate DRM". That
obviously isn't very nice since the code can also be used without DRM.

Moving the subsystem specific conversion routines to the respective
subsystems should solve any of these issues.

Thierry
Thierry Reding Oct. 20, 2012, 10:54 a.m. UTC | #12
On Tue, Oct 09, 2012 at 09:26:08AM +0200, Steffen Trumtrar wrote:
> Hi Laurent,
> 
> On Mon, Oct 08, 2012 at 10:52:04PM +0200, Laurent Pinchart wrote:
> > Hi Steffen,
> > 
> > On Monday 08 October 2012 14:48:01 Steffen Trumtrar wrote:
> > > On Mon, Oct 08, 2012 at 02:13:50PM +0200, Laurent Pinchart wrote:
> > > > On Thursday 04 October 2012 19:59:20 Steffen Trumtrar wrote:
[...]
> > > > > +int of_get_videomode(struct device_node *np, struct videomode *vm, int
> > > > > index)
> > > > 
> > > > I wonder how to avoid abuse of this functions. It's a useful helper for
> > > > drivers that need to get a video mode once only, but would result in lower
> > > > performances if a driver calls it for every mode. Drivers must call
> > > > of_get_display_timing_list instead in that case and case the display
> > > > timings. I'm wondering whether we should really expose of_get_videomode.
> > > 
> > > The intent was to let the driver decide. That way all the other overhead may
> > > be skipped.
> > 
> > My point is that driver writers might just call of_get_videomode() in a loop, 
> > not knowing that it's expensive. I want to avoid that. We need to at least add 
> > kerneldoc to the function stating that this shouldn't be done.
> > 
> 
> You're right. That should be made clear in the code.

In that case we should export videomode_from_timing(). For Tegra DRM I
wrote a small utility function that takes a struct display_timings and
converts every timing to a struct videomode which is then converted to
a struct drm_display_mode and added to the DRM connector. The code is
really generic and could be part of the DRM core.

Thierry
Thierry Reding Oct. 20, 2012, 11:04 a.m. UTC | #13
On Thu, Oct 04, 2012 at 07:59:20PM +0200, Steffen Trumtrar wrote:
[...]
> diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
[...]
> +#if defined(CONFIG_DRM)

This should be:

	#if IS_ENABLED(CONFIG_DRM)

or the code below won't be included if DRM is built as a module. But see
my other replies as to how we can probably handle this better by moving
this into the DRM subsystem.

> +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> +{
> +	memset(dmode, 0, sizeof(*dmode));

It appears the usual method to obtain a drm_display_mode to allocate it
using drm_mode_create(), which will allocate it and associate it with
the struct drm_device.

Now, if you do a memset() on the structure you'll overwrite a number of
fields that have previously been initialized and are actually required
to get everything cleaned up properly later on.

So I think we should remove the call to memset().

> +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
> +			int index)
> +{
[...]
> +}
> +EXPORT_SYMBOL_GPL(of_get_drm_display_mode);

This should be:

	EXPORT_SYMBOL_GPL(of_get_fb_videomode);

Thierry
Steffen Trumtrar Oct. 22, 2012, 7:35 a.m. UTC | #14
On Sat, Oct 20, 2012 at 01:04:12PM +0200, Thierry Reding wrote:
> On Thu, Oct 04, 2012 at 07:59:20PM +0200, Steffen Trumtrar wrote:
> [...]
> > diff --git a/drivers/of/of_videomode.c b/drivers/of/of_videomode.c
> [...]
> > +#if defined(CONFIG_DRM)
> 
> This should be:
> 
> 	#if IS_ENABLED(CONFIG_DRM)
> 
> or the code below won't be included if DRM is built as a module. But see
> my other replies as to how we can probably handle this better by moving
> this into the DRM subsystem.
> 

I already started with moving...now I only need some time to finish with it.

> > +int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
> > +{
> > +	memset(dmode, 0, sizeof(*dmode));
> 
> It appears the usual method to obtain a drm_display_mode to allocate it
> using drm_mode_create(), which will allocate it and associate it with
> the struct drm_device.
> 
> Now, if you do a memset() on the structure you'll overwrite a number of
> fields that have previously been initialized and are actually required
> to get everything cleaned up properly later on.
> 
> So I think we should remove the call to memset().
> 

I was not aware of that. The memset has to go than, of course.

> > +int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
> > +			int index)
> > +{
> [...]
> > +}
> > +EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
> 
> This should be:
> 
> 	EXPORT_SYMBOL_GPL(of_get_fb_videomode);

Oops.

Regrads,

Steffen
diff mbox

Patch

diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 646deb0..74282e2 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -88,4 +88,9 @@  config OF_DISPLAY_TIMINGS
 	help
 	  helper to parse display timings from the devicetree
 
+config OF_VIDEOMODE
+	def_bool y
+	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..76ac16e
--- /dev/null
+++ b/drivers/of/of_videomode.c
@@ -0,0 +1,212 @@ 
+/*
+ * 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/fb.h>
+#include <linux/slab.h>
+#include <drm/drm_mode.h>
+#include <linux/of_display_timings.h>
+#include <linux/of_videomode.h>
+
+void dump_fb_videomode(struct fb_videomode *m)
+{
+        pr_debug("fb_videomode = %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+                m->refresh, m->xres, m->yres, 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);
+}
+
+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);
+}
+
+int videomode_from_timing(struct display_timings *disp, struct videomode *vm,
+			int index)
+{
+	struct signal_timing *st = NULL;
+
+	if (!vm)
+		return -EINVAL;
+
+	st = display_timings_get(disp, index);
+
+	if (!st) {
+		pr_err("%s: no signal timings found\n", __func__);
+		return -EINVAL;
+	}
+
+	vm->pixelclock = signal_timing_get_value(&st->pixelclock, 0);
+	vm->hactive = signal_timing_get_value(&st->hactive, 0);
+	vm->hfront_porch = signal_timing_get_value(&st->hfront_porch, 0);
+	vm->hback_porch = signal_timing_get_value(&st->hback_porch, 0);
+	vm->hsync_len = signal_timing_get_value(&st->hsync_len, 0);
+
+	vm->vactive = signal_timing_get_value(&st->vactive, 0);
+	vm->vfront_porch = signal_timing_get_value(&st->vfront_porch, 0);
+	vm->vback_porch = signal_timing_get_value(&st->vback_porch, 0);
+	vm->vsync_len = signal_timing_get_value(&st->vsync_len, 0);
+
+	vm->vah = st->vsync_pol_active_high;
+	vm->hah = st->hsync_pol_active_high;
+	vm->interlaced = st->interlaced;
+	vm->doublescan = st->doublescan;
+
+	return 0;
+}
+
+int of_get_videomode(struct device_node *np, struct videomode *vm, int index)
+{
+	struct display_timings *disp;
+	int ret = 0;
+
+	disp = of_get_display_timing_list(np);
+
+	if (!disp) {
+		pr_err("%s: no timings specified\n", __func__);
+		return -EINVAL;
+	}
+
+	if (index == OF_DEFAULT_TIMING)
+		index = disp->default_timing;
+
+	ret = videomode_from_timing(disp, vm, index);
+
+	if (ret)
+		return ret;
+
+	display_timings_release(disp);
+
+	if (!vm) {
+		pr_err("%s: could not get videomode %d\n", __func__, index);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_videomode);
+
+#if defined(CONFIG_DRM)
+int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
+{
+	memset(dmode, 0, sizeof(*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;
+
+	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 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);
+#else
+int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode)
+{
+	return 0;
+}
+
+int of_get_drm_display_mode(struct device_node *np, struct drm_display_mode *dmode,
+			int index)
+{
+	return 0;
+}
+#endif
+
+int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode)
+{
+	memset(fbmode, 0, sizeof(*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;
+
+	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_videomode);
+
+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_drm_display_mode);
diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
new file mode 100644
index 0000000..96efe01
--- /dev/null
+++ b/include/linux/of_videomode.h
@@ -0,0 +1,41 @@ 
+/*
+ * 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 <drm/drmP.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;
+
+	bool hah;
+	bool vah;
+	bool interlaced;
+	bool doublescan;
+
+};
+
+int videomode_to_display_mode(struct videomode *vm, struct drm_display_mode *dmode);
+int videomode_to_fb_videomode(struct videomode *vm, struct fb_videomode *fbmode);
+int of_get_videomode(struct device_node *np, struct videomode *vm, int index);
+int of_get_drm_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 *fb, int index);
+#endif /* __LINUX_VIDEOMODE_H */