Linux Framebuffer Layer development
 help / color / mirror / Atom feed
* Re: [PATCHv14 4/7] fbmon: add videomode helpers
From: Laurent Pinchart @ 2012-11-23 10:53 UTC (permalink / raw)
  To: Steffen Trumtrar
  Cc: devicetree-discuss, Rob Herring, linux-fbdev, dri-devel,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-5-git-send-email-s.trumtrar@pengutronix.de>

Hi Steffen,

On Friday 23 November 2012 10:04:24 Steffen Trumtrar wrote:
> Add a function to convert from the generic videomode to a fb_videomode.
> 
> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  drivers/video/fbmon.c |   49 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/fb.h    |    6 ++++++
>  2 files changed, 55 insertions(+)
> 
> diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
> index cef6557..bcbfe8f 100644
> --- a/drivers/video/fbmon.c
> +++ b/drivers/video/fbmon.c
> @@ -31,6 +31,7 @@
>  #include <linux/pci.h>
>  #include <linux/slab.h>
>  #include <video/edid.h>
> +#include <linux/videomode.h>

You could move this one line up to keep headers sorted alphabetically 
(assuming they are in the first place).

>  #ifdef CONFIG_PPC_OF
>  #include <asm/prom.h>
>  #include <asm/pci-bridge.h>
> @@ -1373,6 +1374,54 @@ int fb_get_mode(int flags, u32 val, struct
> fb_var_screeninfo *var, struct fb_inf kfree(timings);
>  	return err;
>  }
> +
> +#if IS_ENABLED(CONFIG_VIDEOMODE)
> +int fb_videomode_from_videomode(const struct videomode *vm,
> +				struct fb_videomode *fbmode)

This is inside the #if CONFIG_FB_MODE_HELPERS block, is that intentional ?

> +{
> +	unsigned int htotal, vtotal;
> +
> +	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;
> +
> +	/* prevent division by zero in KHZ2PICOS macro */
> +	fbmode->pixclock = vm->pixelclock ? KHZ2PICOS(vm->pixelclock / 1000) : 0;
> +
> +	fbmode->sync = 0;
> +	fbmode->vmode = 0;
> +	if (vm->hah)
> +		fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
> +	if (vm->vah)
> +		fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
> +	if (vm->interlaced)
> +		fbmode->vmode |= FB_VMODE_INTERLACED;
> +	if (vm->doublescan)
> +		fbmode->vmode |= FB_VMODE_DOUBLE;
> +	fbmode->flag = 0;
> +
> +	htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
> +		 vm->hsync_len;
> +	vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
> +		 vm->vsync_len;
> +	/* prevent division by zero */
> +	if (htotal && vtotal)
> +		fbmode->refresh = vm->pixelclock / (htotal * vtotal);
> +	else
> +		fbmode->refresh = vm->pixelclock;

What about returning an error if htotal * vtotal = 0 ? The input is clearly 
invalid in that case. I would then set fbmode->refresh to 0, setting it to vm-
>pixelclock doesn't really make sense.

> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
> +#endif
> +
> +

A single blank line should be enough.

>  #else
>  int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
>  {
> diff --git a/include/linux/fb.h b/include/linux/fb.h
> index c7a9571..4404ec2 100644
> --- a/include/linux/fb.h
> +++ b/include/linux/fb.h
> @@ -19,6 +19,7 @@ struct vm_area_struct;
>  struct fb_info;
>  struct device;
>  struct file;
> +struct videomode;
> 
>  /* Definitions below are used in the parsed monitor specs */
>  #define FB_DPMS_ACTIVE_OFF	1
> @@ -714,6 +715,11 @@ extern void fb_destroy_modedb(struct fb_videomode
> *modedb); extern int fb_find_mode_cvt(struct fb_videomode *mode, int
> margins, int rb); extern unsigned char *fb_ddc_read(struct i2c_adapter
> *adapter);
> 
> +#if IS_ENABLED(CONFIG_VIDEOMODE)
> +extern int fb_videomode_from_videomode(const struct videomode *vm,
> +				       struct fb_videomode *fbmode);
> +#endif
> +
>  /* drivers/video/modedb.c */
>  #define VESA_MODEDB_SIZE 34
>  extern void fb_var_to_videomode(struct fb_videomode *mode,
-- 
Regards,

Laurent Pinchart


^ permalink raw reply

* Re: [PATCHv14 3/7] video: add of helper for display timings/videomode
From: Steffen Trumtrar @ 2012-11-23 10:34 UTC (permalink / raw)
  To: Leela Krishna Amudala
  Cc: devicetree-discuss, Philipp Zabel, Rob Herring, linux-fbdev,
	dri-devel, Laurent Pinchart, Thierry Reding,
	Guennady Liakhovetski, linux-media, Tomi Valkeinen,
	Stephen Warren, kernel, Florian Tobias Schandinat, David Airlie
In-Reply-To: <CAL1wa8e1KBrikuP-CQdM3hO_LaNN-1=XuPe728XKqRERQm-EFg@mail.gmail.com>

On Fri, Nov 23, 2012 at 03:49:37PM +0530, Leela Krishna Amudala wrote:
> Hello Steffen,
> 
> On Fri, Nov 23, 2012 at 2:34 PM, Steffen Trumtrar
> <s.trumtrar@pengutronix.de> wrote:
> > This adds support for reading display timings from DT into a struct
> > display_timings. The of_display_timing implementation supports multiple
> > subnodes. All children are read into an array, that can be queried.
> >
> > If no native mode is specified, the first subnode will be used.
> >
> > For cases, where the graphics drivers knows, there can be only one
> > mode description or where the driver only supports one mode, a helper
> > function of_get_videomode is added, that gets a struct videomode from DT.
> > (As this function is implemented in an expensive fashion, it should only
> > be used in the aforementioned case).
> >
> > This also demonstrates how of_display_timings may be utilized.
> >
> > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> > Acked-by: Stephen Warren <swarren@nvidia.com>
> > Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  .../devicetree/bindings/video/display-timings.txt  |  107 ++++++++++
> >  drivers/video/Kconfig                              |   15 ++
> >  drivers/video/Makefile                             |    2 +
> >  drivers/video/of_display_timing.c                  |  223 ++++++++++++++++++++
> >  drivers/video/of_videomode.c                       |   48 +++++
> >  include/linux/of_display_timings.h                 |   20 ++
> >  include/linux/of_videomode.h                       |   18 ++
> >  7 files changed, 433 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
> >  create mode 100644 drivers/video/of_display_timing.c
> >  create mode 100644 drivers/video/of_videomode.c
> >  create mode 100644 include/linux/of_display_timings.h
> >  create mode 100644 include/linux/of_videomode.h
> >
> 
> <<<snip>>>
> 
> > diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
> > new file mode 100644
> > index 0000000..645f43d
> > --- /dev/null
> > +++ b/drivers/video/of_display_timing.c
> > @@ -0,0 +1,223 @@
> > +/*
> > + * OF helpers for parsing display timings
> > + *
> > + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
> > + *
> > + * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
> > + *
> > + * This file is released under the GPLv2
> > + */
> > +#include <linux/of.h>
> > +#include <linux/slab.h>
> > +#include <linux/export.h>
> > +#include <linux/of_display_timings.h>
> > +
> > +/**
> > + * parse_property - parse timing_entry from device_node
> > + * @np: device_node with the property
> > + * @name: name of the property
> > + * @result: will be set to the return value
> > + *
> > + * DESCRIPTION:
> > + * Every display_timing can be specified with either just the typical value or
> > + * a range consisting of min/typ/max. This function helps handling this
> > + **/
> > +static int parse_property(const struct device_node *np, const char *name,
> > +                         struct timing_entry *result)
> > +{
> > +       struct property *prop;
> > +       int length, cells, ret;
> > +
> > +       prop = of_find_property(np, name, &length);
> > +       if (!prop) {
> > +               pr_err("%s: could not find property %s\n", __func__, name);
> > +               return -EINVAL;
> > +       }
> > +
> > +       cells = length / sizeof(u32);
> > +       if (cells = 1) {
> > +               ret = of_property_read_u32(np, name, &result->typ);
> > +               result->min = result->typ;
> > +               result->max = result->typ;
> > +       } else if (cells = 3) {
> > +               ret = of_property_read_u32_array(np, name, &result->min, cells);
> > +       } else {
> > +               pr_err("%s: illegal timing specification in %s\n", __func__,
> > +                       name);
> > +               return -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * of_get_display_timing - parse display_timing entry from device_node
> > + * @np: device_node with the properties
> > + **/
> > +static struct display_timing *of_get_display_timing(const struct device_node
> > +                                                   *np)
> > +{
> > +       struct display_timing *dt;
> > +       int ret = 0;
> > +
> > +       dt = kzalloc(sizeof(*dt), GFP_KERNEL);
> > +       if (!dt) {
> > +               pr_err("%s: could not allocate display_timing struct\n",
> > +                       __func__);
> > +               return NULL;
> > +       }
> > +
> > +       ret |= parse_property(np, "hback-porch", &dt->hback_porch);
> > +       ret |= parse_property(np, "hfront-porch", &dt->hfront_porch);
> > +       ret |= parse_property(np, "hactive", &dt->hactive);
> > +       ret |= parse_property(np, "hsync-len", &dt->hsync_len);
> > +       ret |= parse_property(np, "vback-porch", &dt->vback_porch);
> > +       ret |= parse_property(np, "vfront-porch", &dt->vfront_porch);
> > +       ret |= parse_property(np, "vactive", &dt->vactive);
> > +       ret |= parse_property(np, "vsync-len", &dt->vsync_len);
> > +       ret |= parse_property(np, "clock-frequency", &dt->pixelclock);
> > +
> > +       of_property_read_u32(np, "vsync-active", &dt->vsync_pol_active);
> > +       of_property_read_u32(np, "hsync-active", &dt->hsync_pol_active);
> > +       of_property_read_u32(np, "de-active", &dt->de_pol_active);
> > +       of_property_read_u32(np, "pixelclk-inverted", &dt->pixelclk_pol);
> > +       dt->interlaced = of_property_read_bool(np, "interlaced");
> > +       dt->doublescan = of_property_read_bool(np, "doublescan");
> > +
> > +       if (ret) {
> > +               pr_err("%s: error reading timing properties\n", __func__);
> > +               kfree(dt);
> > +               return NULL;
> > +       }
> > +
> > +       return dt;
> > +}
> > +
> > +/**
> > + * of_get_display_timings - parse all display_timing entries from a device_node
> > + * @np: device_node with the subnodes
> > + **/
> > +struct display_timings *of_get_display_timings(struct device_node *np)
> > +{
> > +       struct device_node *timings_np;
> > +       struct device_node *entry;
> > +       struct device_node *native_mode;
> > +       struct display_timings *disp;
> > +
> > +       if (!np) {
> > +               pr_err("%s: no devicenode given\n", __func__);
> > +               return NULL;
> > +       }
> > +
> > +       timings_np = of_find_node_by_name(np, "display-timings");
> > +       if (!timings_np) {
> > +               pr_err("%s: could not find display-timings node\n", __func__);
> > +               return NULL;
> > +       }
> > +
> > +       disp = kzalloc(sizeof(*disp), GFP_KERNEL);
> > +       if (!disp) {
> > +               pr_err("%s: could not allocate struct disp'\n", __func__);
> > +               goto dispfail;
> > +       }
> > +
> > +       entry = of_parse_phandle(timings_np, "native-mode", 0);
> > +       /* assume first child as native mode if none provided */
> > +       if (!entry)
> > +               entry = of_get_next_child(np, NULL);
> > +       /* if there is no child, it is useless to go on */
> > +       if (!entry) {
> > +               pr_err("%s: no timing specifications given\n", __func__);
> > +               goto entryfail;
> > +       }
> > +
> > +       pr_info("%s: using %s as default timing\n", __func__, entry->name);
> > +
> > +       native_mode = entry;
> > +
> > +       disp->num_timings = of_get_child_count(timings_np);
> > +       if (disp->num_timings = 0) {
> > +               /* should never happen, as entry was already found above */
> > +               pr_err("%s: no timings specified\n", __func__);
> > +               goto entryfail;
> > +       }
> > +
> > +       disp->timings = kzalloc(sizeof(struct display_timing *) * disp->num_timings,
> > +                               GFP_KERNEL);
> > +       if (!disp->timings) {
> > +               pr_err("%s: could not allocate timings array\n", __func__);
> > +               goto entryfail;
> > +       }
> > +
> > +       disp->num_timings = 0;
> > +       disp->native_mode = 0;
> > +
> > +       for_each_child_of_node(timings_np, entry) {
> > +               struct display_timing *dt;
> > +
> > +               dt = of_get_display_timing(entry);
> > +               if (!dt) {
> > +                       /*
> > +                        * to not encourage wrong devicetrees, fail in case of
> > +                        * an error
> > +                        */
> > +                       pr_err("%s: error in timing %d\n", __func__,
> > +                              disp->num_timings + 1);
> > +                       goto timingfail;
> > +               }
> > +
> > +               if (native_mode = entry)
> > +                       disp->native_mode = disp->num_timings;
> > +
> > +               disp->timings[disp->num_timings] = dt;
> > +               disp->num_timings++;
> > +       }
> > +       of_node_put(timings_np);
> > +       /*
> > +        * native_mode points to the device_node returned by of_parse_phandle
> > +        * therefore call of_node_put on it
> > +        */
> > +       of_node_put(native_mode);
> > +
> > +       if (disp->num_timings > 0)
> > +               pr_info("%s: got %d timings. Using timing #%d as default\n",
> > +                       __func__, disp->num_timings, disp->native_mode + 1);
> > +       else {
> > +               pr_err("%s: no valid timings specified\n", __func__);
> > +               display_timings_release(disp);
> > +               return NULL;
> > +       }
> > +       return disp;
> > +
> > +timingfail:
> > +       if (native_mode)
> > +               of_node_put(native_mode);
> > +       display_timings_release(disp);
> > +entryfail:
> > +       if (disp)
> > +               kfree(disp);
> > +dispfail:
> > +       of_node_put(timings_np);
> > +       return NULL;
> > +}
> > +EXPORT_SYMBOL_GPL(of_get_display_timings);
> > +
> > +/**
> > + * of_display_timings_exists - check if a display-timings node is provided
> > + * @np: device_node with the timing
> > + **/
> > +int of_display_timings_exists(const struct device_node *np)
> > +{
> > +       struct device_node *timings_np;
> > +
> > +       if (!np)
> > +               return -EINVAL;
> > +
> > +       timings_np = of_parse_phandle(np, "display-timings", 0);
> 
> I'm seeing warning for the above call
> "passing argument 1 of 'of_parse_phandle' discards qualifiers from
> pointer target type
> expected 'struct device_node *' but argument is of type 'const struct
> device_node *' "
> Please take care of it.
> 

I already sent a patch for of_parse_phandle that makes its device_node pointer
const. That fixes this warning. As I got that patch also on my tree, I did
miss removing the const for the time being.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCHv14 3/7] video: add of helper for display timings/videomode
From: Leela Krishna Amudala @ 2012-11-23 10:31 UTC (permalink / raw)
  To: Steffen Trumtrar
  Cc: devicetree-discuss, Philipp Zabel, Rob Herring, linux-fbdev,
	dri-devel, Laurent Pinchart, Thierry Reding,
	Guennady Liakhovetski, linux-media, Tomi Valkeinen,
	Stephen Warren, kernel, Florian Tobias Schandinat, David Airlie
In-Reply-To: <1353661467-28545-4-git-send-email-s.trumtrar@pengutronix.de>

Hello Steffen,

On Fri, Nov 23, 2012 at 2:34 PM, Steffen Trumtrar
<s.trumtrar@pengutronix.de> wrote:
> This adds support for reading display timings from DT into a struct
> display_timings. The of_display_timing implementation supports multiple
> subnodes. All children are read into an array, that can be queried.
>
> If no native mode is specified, the first subnode will be used.
>
> For cases, where the graphics drivers knows, there can be only one
> mode description or where the driver only supports one mode, a helper
> function of_get_videomode is added, that gets a struct videomode from DT.
> (As this function is implemented in an expensive fashion, it should only
> be used in the aforementioned case).
>
> This also demonstrates how of_display_timings may be utilized.
>
> Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
> Acked-by: Stephen Warren <swarren@nvidia.com>
> Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  .../devicetree/bindings/video/display-timings.txt  |  107 ++++++++++
>  drivers/video/Kconfig                              |   15 ++
>  drivers/video/Makefile                             |    2 +
>  drivers/video/of_display_timing.c                  |  223 ++++++++++++++++++++
>  drivers/video/of_videomode.c                       |   48 +++++
>  include/linux/of_display_timings.h                 |   20 ++
>  include/linux/of_videomode.h                       |   18 ++
>  7 files changed, 433 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
>  create mode 100644 drivers/video/of_display_timing.c
>  create mode 100644 drivers/video/of_videomode.c
>  create mode 100644 include/linux/of_display_timings.h
>  create mode 100644 include/linux/of_videomode.h
>

<<<snip>>>

> diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
> new file mode 100644
> index 0000000..645f43d
> --- /dev/null
> +++ b/drivers/video/of_display_timing.c
> @@ -0,0 +1,223 @@
> +/*
> + * OF helpers for parsing display timings
> + *
> + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
> + *
> + * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
> + *
> + * This file is released under the GPLv2
> + */
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/export.h>
> +#include <linux/of_display_timings.h>
> +
> +/**
> + * parse_property - parse timing_entry from device_node
> + * @np: device_node with the property
> + * @name: name of the property
> + * @result: will be set to the return value
> + *
> + * DESCRIPTION:
> + * Every display_timing can be specified with either just the typical value or
> + * a range consisting of min/typ/max. This function helps handling this
> + **/
> +static int parse_property(const struct device_node *np, const char *name,
> +                         struct timing_entry *result)
> +{
> +       struct property *prop;
> +       int length, cells, ret;
> +
> +       prop = of_find_property(np, name, &length);
> +       if (!prop) {
> +               pr_err("%s: could not find property %s\n", __func__, name);
> +               return -EINVAL;
> +       }
> +
> +       cells = length / sizeof(u32);
> +       if (cells = 1) {
> +               ret = of_property_read_u32(np, name, &result->typ);
> +               result->min = result->typ;
> +               result->max = result->typ;
> +       } else if (cells = 3) {
> +               ret = of_property_read_u32_array(np, name, &result->min, cells);
> +       } else {
> +               pr_err("%s: illegal timing specification in %s\n", __func__,
> +                       name);
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * of_get_display_timing - parse display_timing entry from device_node
> + * @np: device_node with the properties
> + **/
> +static struct display_timing *of_get_display_timing(const struct device_node
> +                                                   *np)
> +{
> +       struct display_timing *dt;
> +       int ret = 0;
> +
> +       dt = kzalloc(sizeof(*dt), GFP_KERNEL);
> +       if (!dt) {
> +               pr_err("%s: could not allocate display_timing struct\n",
> +                       __func__);
> +               return NULL;
> +       }
> +
> +       ret |= parse_property(np, "hback-porch", &dt->hback_porch);
> +       ret |= parse_property(np, "hfront-porch", &dt->hfront_porch);
> +       ret |= parse_property(np, "hactive", &dt->hactive);
> +       ret |= parse_property(np, "hsync-len", &dt->hsync_len);
> +       ret |= parse_property(np, "vback-porch", &dt->vback_porch);
> +       ret |= parse_property(np, "vfront-porch", &dt->vfront_porch);
> +       ret |= parse_property(np, "vactive", &dt->vactive);
> +       ret |= parse_property(np, "vsync-len", &dt->vsync_len);
> +       ret |= parse_property(np, "clock-frequency", &dt->pixelclock);
> +
> +       of_property_read_u32(np, "vsync-active", &dt->vsync_pol_active);
> +       of_property_read_u32(np, "hsync-active", &dt->hsync_pol_active);
> +       of_property_read_u32(np, "de-active", &dt->de_pol_active);
> +       of_property_read_u32(np, "pixelclk-inverted", &dt->pixelclk_pol);
> +       dt->interlaced = of_property_read_bool(np, "interlaced");
> +       dt->doublescan = of_property_read_bool(np, "doublescan");
> +
> +       if (ret) {
> +               pr_err("%s: error reading timing properties\n", __func__);
> +               kfree(dt);
> +               return NULL;
> +       }
> +
> +       return dt;
> +}
> +
> +/**
> + * of_get_display_timings - parse all display_timing entries from a device_node
> + * @np: device_node with the subnodes
> + **/
> +struct display_timings *of_get_display_timings(struct device_node *np)
> +{
> +       struct device_node *timings_np;
> +       struct device_node *entry;
> +       struct device_node *native_mode;
> +       struct display_timings *disp;
> +
> +       if (!np) {
> +               pr_err("%s: no devicenode given\n", __func__);
> +               return NULL;
> +       }
> +
> +       timings_np = of_find_node_by_name(np, "display-timings");
> +       if (!timings_np) {
> +               pr_err("%s: could not find display-timings node\n", __func__);
> +               return NULL;
> +       }
> +
> +       disp = kzalloc(sizeof(*disp), GFP_KERNEL);
> +       if (!disp) {
> +               pr_err("%s: could not allocate struct disp'\n", __func__);
> +               goto dispfail;
> +       }
> +
> +       entry = of_parse_phandle(timings_np, "native-mode", 0);
> +       /* assume first child as native mode if none provided */
> +       if (!entry)
> +               entry = of_get_next_child(np, NULL);
> +       /* if there is no child, it is useless to go on */
> +       if (!entry) {
> +               pr_err("%s: no timing specifications given\n", __func__);
> +               goto entryfail;
> +       }
> +
> +       pr_info("%s: using %s as default timing\n", __func__, entry->name);
> +
> +       native_mode = entry;
> +
> +       disp->num_timings = of_get_child_count(timings_np);
> +       if (disp->num_timings = 0) {
> +               /* should never happen, as entry was already found above */
> +               pr_err("%s: no timings specified\n", __func__);
> +               goto entryfail;
> +       }
> +
> +       disp->timings = kzalloc(sizeof(struct display_timing *) * disp->num_timings,
> +                               GFP_KERNEL);
> +       if (!disp->timings) {
> +               pr_err("%s: could not allocate timings array\n", __func__);
> +               goto entryfail;
> +       }
> +
> +       disp->num_timings = 0;
> +       disp->native_mode = 0;
> +
> +       for_each_child_of_node(timings_np, entry) {
> +               struct display_timing *dt;
> +
> +               dt = of_get_display_timing(entry);
> +               if (!dt) {
> +                       /*
> +                        * to not encourage wrong devicetrees, fail in case of
> +                        * an error
> +                        */
> +                       pr_err("%s: error in timing %d\n", __func__,
> +                              disp->num_timings + 1);
> +                       goto timingfail;
> +               }
> +
> +               if (native_mode = entry)
> +                       disp->native_mode = disp->num_timings;
> +
> +               disp->timings[disp->num_timings] = dt;
> +               disp->num_timings++;
> +       }
> +       of_node_put(timings_np);
> +       /*
> +        * native_mode points to the device_node returned by of_parse_phandle
> +        * therefore call of_node_put on it
> +        */
> +       of_node_put(native_mode);
> +
> +       if (disp->num_timings > 0)
> +               pr_info("%s: got %d timings. Using timing #%d as default\n",
> +                       __func__, disp->num_timings, disp->native_mode + 1);
> +       else {
> +               pr_err("%s: no valid timings specified\n", __func__);
> +               display_timings_release(disp);
> +               return NULL;
> +       }
> +       return disp;
> +
> +timingfail:
> +       if (native_mode)
> +               of_node_put(native_mode);
> +       display_timings_release(disp);
> +entryfail:
> +       if (disp)
> +               kfree(disp);
> +dispfail:
> +       of_node_put(timings_np);
> +       return NULL;
> +}
> +EXPORT_SYMBOL_GPL(of_get_display_timings);
> +
> +/**
> + * of_display_timings_exists - check if a display-timings node is provided
> + * @np: device_node with the timing
> + **/
> +int of_display_timings_exists(const struct device_node *np)
> +{
> +       struct device_node *timings_np;
> +
> +       if (!np)
> +               return -EINVAL;
> +
> +       timings_np = of_parse_phandle(np, "display-timings", 0);

I'm seeing warning for the above call
"passing argument 1 of 'of_parse_phandle' discards qualifiers from
pointer target type
expected 'struct device_node *' but argument is of type 'const struct
device_node *' "
Please take care of it.

Best Wishes,
Leela Krishna Amudala

> +       if (!timings_np)
> +               return -EINVAL;
> +
> +       of_node_put(timings_np);
> +       return 1;
> +}
> +EXPORT_SYMBOL_GPL(of_display_timings_exists);
> diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c
> new file mode 100644
> index 0000000..358aa56
> --- /dev/null
> +++ b/drivers/video/of_videomode.c
> @@ -0,0 +1,48 @@
> +/*
> + * generic videomode helper
> + *
> + * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
> + *
> + * This file is released under the GPLv2
> + */
> +#include <linux/of.h>
> +#include <linux/of_display_timings.h>
> +#include <linux/of_videomode.h>
> +#include <linux/export.h>
> +
> +/**
> + * of_get_videomode - get the videomode #<index> from devicetree
> + * @np - devicenode with the display_timings
> + * @vm - set to return value
> + * @index - index into list of display_timings
> + * DESCRIPTION:
> + * Get a list of all display timings and put the one
> + * specified by index into *vm. This function should only be used, if
> + * only one videomode is to be retrieved. A driver that needs to work
> + * with multiple/all videomodes should work with
> + * of_get_display_timings instead.
> + **/
> +int of_get_videomode(struct device_node *np, struct videomode *vm,
> +                    int index)
> +{
> +       struct display_timings *disp;
> +       int ret;
> +
> +       disp = of_get_display_timings(np);
> +       if (!disp) {
> +               pr_err("%s: no timings specified\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       if (index = OF_USE_NATIVE_MODE)
> +               index = disp->native_mode;
> +
> +       ret = videomode_from_timing(disp, vm, index);
> +       if (ret)
> +               return ret;
> +
> +       display_timings_release(disp);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(of_get_videomode);
> diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
> new file mode 100644
> index 0000000..b3e3455
> --- /dev/null
> +++ b/include/linux/of_display_timings.h
> @@ -0,0 +1,20 @@
> +/*
> + * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
> + *
> + * display timings of helpers
> + *
> + * This file is released under the GPLv2
> + */
> +
> +#ifndef __LINUX_OF_DISPLAY_TIMINGS_H
> +#define __LINUX_OF_DISPLAY_TIMINGS_H
> +
> +#include <linux/display_timing.h>
> +#include <linux/of.h>
> +
> +#define OF_USE_NATIVE_MODE -1
> +
> +struct display_timings *of_get_display_timings(struct device_node *np);
> +int of_display_timings_exists(const struct device_node *np);
> +
> +#endif
> diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
> new file mode 100644
> index 0000000..a72ad78
> --- /dev/null
> +++ b/include/linux/of_videomode.h
> @@ -0,0 +1,18 @@
> +/*
> + * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
> + *
> + * videomode of-helpers
> + *
> + * This file is released under the GPLv2
> + */
> +
> +#ifndef __LINUX_OF_VIDEOMODE_H
> +#define __LINUX_OF_VIDEOMODE_H
> +
> +#include <linux/videomode.h>
> +#include <linux/of.h>
> +
> +int of_get_videomode(struct device_node *np, struct videomode *vm,
> +                    int index);
> +
> +#endif /* __LINUX_OF_VIDEOMODE_H */
> --
> 1.7.10.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fbdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCHv14 7/7] drm_modes: add of_videomode helpers
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

Add helper to get drm_display_mode from devicetree.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/drm_modes.c |   34 +++++++++++++++++++++++++++++++++-
 include/drm/drmP.h          |    6 ++++++
 2 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 0073b27..2d6edfa 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -35,7 +35,7 @@
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
-#include <linux/videomode.h>
+#include <linux/of_videomode.h>
 
 /**
  * drm_mode_debug_printmodeline - debug print a mode
@@ -541,6 +541,38 @@ int drm_display_mode_from_videomode(const struct videomode *vm,
 EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
 #endif
 
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+/**
+ * of_get_drm_display_mode - get a drm_display_mode from devicetree
+ * @np: device_node with the timing specification
+ * @dmode: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings and
+ * work with that instead.
+ */
+int of_get_drm_display_mode(struct device_node *np,
+			    struct drm_display_mode *dmode, unsigned int index)
+{
+	struct videomode vm;
+	int ret;
+
+	ret = of_get_videomode(np, &vm, index);
+	if (ret)
+		return ret;
+
+	drm_display_mode_from_videomode(&vm, dmode);
+
+	pr_info("%s: got %dx%d display mode from %s\n", __func__, vm.hactive,
+		vm.vactive, np->name);
+	drm_mode_debug_printmodeline(dmode);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_drm_display_mode);
+#endif
+
 /**
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 3d0ccaa..84ecabd 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -86,6 +86,7 @@ struct drm_file;
 struct drm_device;
 
 struct videomode;
+struct device_node;
 #include <drm/drm_os_linux.h>
 #include <drm/drm_hashtab.h>
 #include <drm/drm_mm.h>
@@ -1459,6 +1460,11 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
 extern int drm_display_mode_from_videomode(const struct videomode *vm,
 					   struct drm_display_mode *dmode);
 #endif
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+extern int of_get_drm_display_mode(struct device_node *np,
+				   struct drm_display_mode *dmode,
+				   unsigned int index);
+#endif
 
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 6/7] drm_modes: add videomode helpers
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

Add conversion from videomode to drm_display_mode

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/drm_modes.c |   37 +++++++++++++++++++++++++++++++++++++
 include/drm/drmP.h          |    6 ++++++
 2 files changed, 43 insertions(+)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 59450f3..0073b27 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -35,6 +35,7 @@
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
+#include <linux/videomode.h>
 
 /**
  * drm_mode_debug_printmodeline - debug print a mode
@@ -504,6 +505,42 @@ drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh,
 }
 EXPORT_SYMBOL(drm_gtf_mode);
 
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int drm_display_mode_from_videomode(const struct videomode *vm,
+				    struct drm_display_mode *dmode)
+{
+	dmode->hdisplay = vm->hactive;
+	dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
+	dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
+	dmode->htotal = dmode->hsync_end + vm->hback_porch;
+
+	dmode->vdisplay = vm->vactive;
+	dmode->vsync_start = dmode->vdisplay + vm->vfront_porch;
+	dmode->vsync_end = dmode->vsync_start + vm->vsync_len;
+	dmode->vtotal = dmode->vsync_end + vm->vback_porch;
+
+	dmode->clock = vm->pixelclock / 1000;
+
+	dmode->flags = 0;
+	if (vm->hah)
+		dmode->flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		dmode->flags |= DRM_MODE_FLAG_NHSYNC;
+	if (vm->vah)
+		dmode->flags |= DRM_MODE_FLAG_PVSYNC;
+	else
+		dmode->flags |= DRM_MODE_FLAG_NVSYNC;
+	if (vm->interlaced)
+		dmode->flags |= DRM_MODE_FLAG_INTERLACE;
+	if (vm->doublescan)
+		dmode->flags |= DRM_MODE_FLAG_DBLSCAN;
+	drm_mode_set_name(dmode);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
+#endif
+
 /**
  * drm_mode_set_name - set the name on a mode
  * @mode: name will be set in this mode
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 3fd8280..3d0ccaa 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -85,6 +85,7 @@ struct module;
 struct drm_file;
 struct drm_device;
 
+struct videomode;
 #include <drm/drm_os_linux.h>
 #include <drm/drm_hashtab.h>
 #include <drm/drm_mm.h>
@@ -1454,6 +1455,11 @@ extern struct drm_display_mode *
 drm_mode_create_from_cmdline_mode(struct drm_device *dev,
 				  struct drm_cmdline_mode *cmd);
 
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+extern int drm_display_mode_from_videomode(const struct videomode *vm,
+					   struct drm_display_mode *dmode);
+#endif
+
 /* Modesetting support */
 extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc);
 extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 5/7] fbmon: add of_videomode helpers
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

Add helper to get fb_videomode from devicetree.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/fbmon.c |   42 +++++++++++++++++++++++++++++++++++++++++-
 include/linux/fb.h    |    6 ++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index bcbfe8f..287e66b 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -31,7 +31,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <video/edid.h>
-#include <linux/videomode.h>
+#include <linux/of_videomode.h>
 #ifdef CONFIG_PPC_OF
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
@@ -1421,6 +1421,46 @@ int fb_videomode_from_videomode(const struct videomode *vm,
 EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
 #endif
 
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+static inline void dump_fb_videomode(const struct fb_videomode *m)
+{
+	pr_debug("fb_videomode = %ux%u@%uHz (%ukHz) %u %u %u %u %u %u %u %u %u\n",
+		 m->xres, m->yres, m->refresh, m->pixclock, m->left_margin,
+		 m->right_margin, m->upper_margin, m->lower_margin,
+		 m->hsync_len, m->vsync_len, m->sync, m->vmode, m->flag);
+}
+
+/**
+ * of_get_fb_videomode - get a fb_videomode from devicetree
+ * @np: device_node with the timing specification
+ * @fb: will be set to the return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function is expensive and should only be used, if only one mode is to be
+ * read from DT. To get multiple modes start with of_get_display_timings ond
+ * work with that instead.
+ */
+int of_get_fb_videomode(struct device_node *np, struct fb_videomode *fb,
+			unsigned int index)
+{
+	struct videomode vm;
+	int ret;
+
+	ret = of_get_videomode(np, &vm, index);
+	if (ret)
+		return ret;
+
+	fb_videomode_from_videomode(&vm, fb);
+
+	pr_info("%s: got %dx%d display mode from %s\n", __func__, vm.hactive,
+		vm.vactive, np->name);
+	dump_fb_videomode(fb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_fb_videomode);
+#endif
 
 #else
 int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 4404ec2..7e1c8cf 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -20,6 +20,7 @@ struct fb_info;
 struct device;
 struct file;
 struct videomode;
+struct device_node;
 
 /* Definitions below are used in the parsed monitor specs */
 #define FB_DPMS_ACTIVE_OFF	1
@@ -715,6 +716,11 @@ extern void fb_destroy_modedb(struct fb_videomode *modedb);
 extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
 extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
 
+#if IS_ENABLED(CONFIG_OF_VIDEOMODE)
+extern int of_get_fb_videomode(struct device_node *np,
+			       struct fb_videomode *fb,
+			       unsigned int index);
+#endif
 #if IS_ENABLED(CONFIG_VIDEOMODE)
 extern int fb_videomode_from_videomode(const struct videomode *vm,
 				       struct fb_videomode *fbmode);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 4/7] fbmon: add videomode helpers
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

Add a function to convert from the generic videomode to a fb_videomode.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/fbmon.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fb.h    |    6 ++++++
 2 files changed, 55 insertions(+)

diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index cef6557..bcbfe8f 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -31,6 +31,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <video/edid.h>
+#include <linux/videomode.h>
 #ifdef CONFIG_PPC_OF
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
@@ -1373,6 +1374,54 @@ int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_inf
 	kfree(timings);
 	return err;
 }
+
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+int fb_videomode_from_videomode(const struct videomode *vm,
+				struct fb_videomode *fbmode)
+{
+	unsigned int htotal, vtotal;
+
+	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;
+
+	/* prevent division by zero in KHZ2PICOS macro */
+	fbmode->pixclock = vm->pixelclock ? KHZ2PICOS(vm->pixelclock / 1000) : 0;
+
+	fbmode->sync = 0;
+	fbmode->vmode = 0;
+	if (vm->hah)
+		fbmode->sync |= FB_SYNC_HOR_HIGH_ACT;
+	if (vm->vah)
+		fbmode->sync |= FB_SYNC_VERT_HIGH_ACT;
+	if (vm->interlaced)
+		fbmode->vmode |= FB_VMODE_INTERLACED;
+	if (vm->doublescan)
+		fbmode->vmode |= FB_VMODE_DOUBLE;
+	fbmode->flag = 0;
+
+	htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
+		 vm->hsync_len;
+	vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
+		 vm->vsync_len;
+	/* prevent division by zero */
+	if (htotal && vtotal)
+		fbmode->refresh = vm->pixelclock / (htotal * vtotal);
+	else
+		fbmode->refresh = vm->pixelclock;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fb_videomode_from_videomode);
+#endif
+
+
 #else
 int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
 {
diff --git a/include/linux/fb.h b/include/linux/fb.h
index c7a9571..4404ec2 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -19,6 +19,7 @@ struct vm_area_struct;
 struct fb_info;
 struct device;
 struct file;
+struct videomode;
 
 /* Definitions below are used in the parsed monitor specs */
 #define FB_DPMS_ACTIVE_OFF	1
@@ -714,6 +715,11 @@ extern void fb_destroy_modedb(struct fb_videomode *modedb);
 extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb);
 extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter);
 
+#if IS_ENABLED(CONFIG_VIDEOMODE)
+extern int fb_videomode_from_videomode(const struct videomode *vm,
+				       struct fb_videomode *fbmode);
+#endif
+
 /* drivers/video/modedb.c */
 #define VESA_MODEDB_SIZE 34
 extern void fb_var_to_videomode(struct fb_videomode *mode,
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 3/7] video: add of helper for display timings/videomode
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Philipp Zabel, Rob Herring, linux-fbdev, dri-devel,
	Laurent Pinchart, Thierry Reding, Guennady Liakhovetski,
	linux-media, Tomi Valkeinen, Stephen Warren, kernel,
	Florian Tobias Schandinat, David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

This adds support for reading display timings from DT into a struct
display_timings. The of_display_timing implementation supports multiple
subnodes. All children are read into an array, that can be queried.

If no native mode is specified, the first subnode will be used.

For cases, where the graphics drivers knows, there can be only one
mode description or where the driver only supports one mode, a helper
function of_get_videomode is added, that gets a struct videomode from DT.
(As this function is implemented in an expensive fashion, it should only
be used in the aforementioned case).

This also demonstrates how of_display_timings may be utilized.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Stephen Warren <swarren@nvidia.com>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 .../devicetree/bindings/video/display-timings.txt  |  107 ++++++++++
 drivers/video/Kconfig                              |   15 ++
 drivers/video/Makefile                             |    2 +
 drivers/video/of_display_timing.c                  |  223 ++++++++++++++++++++
 drivers/video/of_videomode.c                       |   48 +++++
 include/linux/of_display_timings.h                 |   20 ++
 include/linux/of_videomode.h                       |   18 ++
 7 files changed, 433 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
 create mode 100644 drivers/video/of_display_timing.c
 create mode 100644 drivers/video/of_videomode.c
 create mode 100644 include/linux/of_display_timings.h
 create mode 100644 include/linux/of_videomode.h

diff --git a/Documentation/devicetree/bindings/video/display-timings.txt b/Documentation/devicetree/bindings/video/display-timings.txt
new file mode 100644
index 0000000..2b25d58
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/display-timings.txt
@@ -0,0 +1,107 @@
+display-timings bindings
+============
+
+display-timings node
+--------------------
+
+required properties:
+ - none
+
+optional properties:
+ - native-mode: The native mode for the display, in case multiple modes are
+		provided. When omitted, assume the first node is the native.
+
+timings subnode
+---------------
+
+required properties:
+ - hactive, vactive: Display resolution
+ - hfront-porch, hback-porch, hsync-len: Horizontal Display timing parameters
+   in pixels
+   vfront-porch, vback-porch, vsync-len: Vertical display timing parameters in
+   lines
+ - clock-frequency: display clock in Hz
+
+optional properties:
+ - hsync-active: Hsync pulse is active low/high/ignored
+ - vsync-active: Vsync pulse is active low/high/ignored
+ - de-active: Data-Enable pulse is active low/high/ignored
+ - pixelclk-inverted: pixelclock is inverted (active on falling edge)/
+				non-inverted (active on rising edge)/
+				     ignored (ignore property)
+ - interlaced (bool): boolean to enable interlaced mode
+ - doublescan (bool): boolean to enable doublescan mode
+ - doubleclk (bool)
+
+All the optional properties that are not bool follow the following logic:
+    <1>: high active
+    <0>: low active
+    omitted: not used on hardware
+
+There are different ways of describing the capabilities of a display. The devicetree
+representation corresponds to the one commonly found in datasheets for displays.
+If a display supports multiple signal timings, the native-mode can be specified.
+
+The parameters are defined as
+
+  +----------+---------------------------------------------+----------+-------+
+  |          |                ↑                            |          |       |
+  |          |                |vback_porch                 |          |       |
+  |          |                ↓                            |          |       |
+  +----------###############################################----------+-------+
+  |          #                ↑                            #          |       |
+  |          #                |                            #          |       |
+  |  hback   #                |                            #  hfront  | hsync |
+  |   porch  #                |       hactive              #  porch   |  len  |
+  |<-------->#<---------------+--------------------------->#<-------->|<----->|
+  |          #                |                            #          |       |
+  |          #                |vactive                     #          |       |
+  |          #                |                            #          |       |
+  |          #                ↓                            #          |       |
+  +----------###############################################----------+-------+
+  |          |                ↑                            |          |       |
+  |          |                |vfront_porch                |          |       |
+  |          |                ↓                            |          |       |
+  +----------+---------------------------------------------+----------+-------+
+  |          |                ↑                            |          |       |
+  |          |                |vsync_len                   |          |       |
+  |          |                ↓                            |          |       |
+  +----------+---------------------------------------------+----------+-------+
+
+
+Example:
+
+	display-timings {
+		native-mode = <&timing0>;
+		timing0: 1080p24 {
+			/* 1920x1080p24 */
+			clock-frequency = <52000000>;
+			hactive = <1920>;
+			vactive = <1080>;
+			hfront-porch = <25>;
+			hback-porch = <25>;
+			hsync-len = <25>;
+			vback-porch = <2>;
+			vfront-porch = <2>;
+			vsync-len = <2>;
+			hsync-active = <1>;
+		};
+	};
+
+Every required property also supports the use of ranges, so the commonly used
+datasheet description with <min typ max>-tuples can be used.
+
+Example:
+
+	timing1: timing {
+		/* 1920x1080p24 */
+		clock-frequency = <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>;
+	};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2a23b18..c5b7bcf 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -39,6 +39,21 @@ config DISPLAY_TIMING
 config VIDEOMODE
        bool
 
+config OF_DISPLAY_TIMING
+	bool "Enable OF display timing support"
+	depends on OF
+	select DISPLAY_TIMING
+	help
+	  helper to parse display timings from the devicetree
+
+config OF_VIDEOMODE
+	bool "Enable OF videomode support"
+	depends on OF
+	select VIDEOMODE
+	select OF_DISPLAY_TIMING
+	help
+	  helper to get videomodes from the devicetree
+
 menuconfig FB
 	tristate "Support for frame buffer devices"
 	---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index fc30439..b936b00 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -168,4 +168,6 @@ obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
 #video output switch sysfs driver
 obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
 obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
+obj-$(CONFIG_OF_DISPLAY_TIMING) += of_display_timing.o
 obj-$(CONFIG_VIDEOMODE) += videomode.o
+obj-$(CONFIG_OF_VIDEOMODE) += of_videomode.o
diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c
new file mode 100644
index 0000000..645f43d
--- /dev/null
+++ b/drivers/video/of_display_timing.c
@@ -0,0 +1,223 @@
+/*
+ * OF helpers for parsing display timings
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * based on of_videomode.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <linux/of_display_timings.h>
+
+/**
+ * parse_property - parse timing_entry from device_node
+ * @np: device_node with the property
+ * @name: name of the property
+ * @result: will be set to the return value
+ *
+ * DESCRIPTION:
+ * Every display_timing can be specified with either just the typical value or
+ * a range consisting of min/typ/max. This function helps handling this
+ **/
+static int parse_property(const struct device_node *np, const char *name,
+			  struct timing_entry *result)
+{
+	struct property *prop;
+	int length, cells, ret;
+
+	prop = of_find_property(np, name, &length);
+	if (!prop) {
+		pr_err("%s: could not find property %s\n", __func__, name);
+		return -EINVAL;
+	}
+
+	cells = length / sizeof(u32);
+	if (cells = 1) {
+		ret = of_property_read_u32(np, name, &result->typ);
+		result->min = result->typ;
+		result->max = result->typ;
+	} else if (cells = 3) {
+		ret = of_property_read_u32_array(np, name, &result->min, cells);
+	} else {
+		pr_err("%s: illegal timing specification in %s\n", __func__,
+			name);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * of_get_display_timing - parse display_timing entry from device_node
+ * @np: device_node with the properties
+ **/
+static struct display_timing *of_get_display_timing(const struct device_node
+						    *np)
+{
+	struct display_timing *dt;
+	int ret = 0;
+
+	dt = kzalloc(sizeof(*dt), GFP_KERNEL);
+	if (!dt) {
+		pr_err("%s: could not allocate display_timing struct\n",
+			__func__);
+		return NULL;
+	}
+
+	ret |= parse_property(np, "hback-porch", &dt->hback_porch);
+	ret |= parse_property(np, "hfront-porch", &dt->hfront_porch);
+	ret |= parse_property(np, "hactive", &dt->hactive);
+	ret |= parse_property(np, "hsync-len", &dt->hsync_len);
+	ret |= parse_property(np, "vback-porch", &dt->vback_porch);
+	ret |= parse_property(np, "vfront-porch", &dt->vfront_porch);
+	ret |= parse_property(np, "vactive", &dt->vactive);
+	ret |= parse_property(np, "vsync-len", &dt->vsync_len);
+	ret |= parse_property(np, "clock-frequency", &dt->pixelclock);
+
+	of_property_read_u32(np, "vsync-active", &dt->vsync_pol_active);
+	of_property_read_u32(np, "hsync-active", &dt->hsync_pol_active);
+	of_property_read_u32(np, "de-active", &dt->de_pol_active);
+	of_property_read_u32(np, "pixelclk-inverted", &dt->pixelclk_pol);
+	dt->interlaced = of_property_read_bool(np, "interlaced");
+	dt->doublescan = of_property_read_bool(np, "doublescan");
+
+	if (ret) {
+		pr_err("%s: error reading timing properties\n", __func__);
+		kfree(dt);
+		return NULL;
+	}
+
+	return dt;
+}
+
+/**
+ * of_get_display_timings - parse all display_timing entries from a device_node
+ * @np: device_node with the subnodes
+ **/
+struct display_timings *of_get_display_timings(struct device_node *np)
+{
+	struct device_node *timings_np;
+	struct device_node *entry;
+	struct device_node *native_mode;
+	struct display_timings *disp;
+
+	if (!np) {
+		pr_err("%s: no devicenode given\n", __func__);
+		return NULL;
+	}
+
+	timings_np = of_find_node_by_name(np, "display-timings");
+	if (!timings_np) {
+		pr_err("%s: could not find display-timings node\n", __func__);
+		return NULL;
+	}
+
+	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
+	if (!disp) {
+		pr_err("%s: could not allocate struct disp'\n", __func__);
+		goto dispfail;
+	}
+
+	entry = of_parse_phandle(timings_np, "native-mode", 0);
+	/* assume first child as native mode if none provided */
+	if (!entry)
+		entry = of_get_next_child(np, NULL);
+	/* if there is no child, it is useless to go on */
+	if (!entry) {
+		pr_err("%s: no timing specifications given\n", __func__);
+		goto entryfail;
+	}
+
+	pr_info("%s: using %s as default timing\n", __func__, entry->name);
+
+	native_mode = entry;
+
+	disp->num_timings = of_get_child_count(timings_np);
+	if (disp->num_timings = 0) {
+		/* should never happen, as entry was already found above */
+		pr_err("%s: no timings specified\n", __func__);
+		goto entryfail;
+	}
+
+	disp->timings = kzalloc(sizeof(struct display_timing *) * disp->num_timings,
+				GFP_KERNEL);
+	if (!disp->timings) {
+		pr_err("%s: could not allocate timings array\n", __func__);
+		goto entryfail;
+	}
+
+	disp->num_timings = 0;
+	disp->native_mode = 0;
+
+	for_each_child_of_node(timings_np, entry) {
+		struct display_timing *dt;
+
+		dt = of_get_display_timing(entry);
+		if (!dt) {
+			/*
+			 * to not encourage wrong devicetrees, fail in case of
+			 * an error
+			 */
+			pr_err("%s: error in timing %d\n", __func__,
+			       disp->num_timings + 1);
+			goto timingfail;
+		}
+
+		if (native_mode = entry)
+			disp->native_mode = disp->num_timings;
+
+		disp->timings[disp->num_timings] = dt;
+		disp->num_timings++;
+	}
+	of_node_put(timings_np);
+	/*
+	 * native_mode points to the device_node returned by of_parse_phandle
+	 * therefore call of_node_put on it
+	 */
+	of_node_put(native_mode);
+
+	if (disp->num_timings > 0)
+		pr_info("%s: got %d timings. Using timing #%d as default\n",
+			__func__, disp->num_timings, disp->native_mode + 1);
+	else {
+		pr_err("%s: no valid timings specified\n", __func__);
+		display_timings_release(disp);
+		return NULL;
+	}
+	return disp;
+
+timingfail:
+	if (native_mode)
+		of_node_put(native_mode);
+	display_timings_release(disp);
+entryfail:
+	if (disp)
+		kfree(disp);
+dispfail:
+	of_node_put(timings_np);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(of_get_display_timings);
+
+/**
+ * of_display_timings_exists - check if a display-timings node is provided
+ * @np: device_node with the timing
+ **/
+int of_display_timings_exists(const struct device_node *np)
+{
+	struct device_node *timings_np;
+
+	if (!np)
+		return -EINVAL;
+
+	timings_np = of_parse_phandle(np, "display-timings", 0);
+	if (!timings_np)
+		return -EINVAL;
+
+	of_node_put(timings_np);
+	return 1;
+}
+EXPORT_SYMBOL_GPL(of_display_timings_exists);
diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c
new file mode 100644
index 0000000..358aa56
--- /dev/null
+++ b/drivers/video/of_videomode.c
@@ -0,0 +1,48 @@
+/*
+ * generic videomode helper
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+#include <linux/of.h>
+#include <linux/of_display_timings.h>
+#include <linux/of_videomode.h>
+#include <linux/export.h>
+
+/**
+ * of_get_videomode - get the videomode #<index> from devicetree
+ * @np - devicenode with the display_timings
+ * @vm - set to return value
+ * @index - index into list of display_timings
+ * DESCRIPTION:
+ * Get a list of all display timings and put the one
+ * specified by index into *vm. This function should only be used, if
+ * only one videomode is to be retrieved. A driver that needs to work
+ * with multiple/all videomodes should work with
+ * of_get_display_timings instead.
+ **/
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+		     int index)
+{
+	struct display_timings *disp;
+	int ret;
+
+	disp = of_get_display_timings(np);
+	if (!disp) {
+		pr_err("%s: no timings specified\n", __func__);
+		return -EINVAL;
+	}
+
+	if (index = OF_USE_NATIVE_MODE)
+		index = disp->native_mode;
+
+	ret = videomode_from_timing(disp, vm, index);
+	if (ret)
+		return ret;
+
+	display_timings_release(disp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_videomode);
diff --git a/include/linux/of_display_timings.h b/include/linux/of_display_timings.h
new file mode 100644
index 0000000..b3e3455
--- /dev/null
+++ b/include/linux/of_display_timings.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * display timings of helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_DISPLAY_TIMINGS_H
+#define __LINUX_OF_DISPLAY_TIMINGS_H
+
+#include <linux/display_timing.h>
+#include <linux/of.h>
+
+#define OF_USE_NATIVE_MODE -1
+
+struct display_timings *of_get_display_timings(struct device_node *np);
+int of_display_timings_exists(const struct device_node *np);
+
+#endif
diff --git a/include/linux/of_videomode.h b/include/linux/of_videomode.h
new file mode 100644
index 0000000..a72ad78
--- /dev/null
+++ b/include/linux/of_videomode.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * videomode of-helpers
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_OF_VIDEOMODE_H
+#define __LINUX_OF_VIDEOMODE_H
+
+#include <linux/videomode.h>
+#include <linux/of.h>
+
+int of_get_videomode(struct device_node *np, struct videomode *vm,
+		     int index);
+
+#endif /* __LINUX_OF_VIDEOMODE_H */
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 2/7] video: add display_timing and videomode
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

Add display_timing structure and the according helper functions. This allows
the description of a display via its supported timing parameters.

Also, add helper functions to convert from display timings to a generic videomode
structure.

The struct display_timing specifies all needed parameters to describe the signal
properties of a display in one mode. This includes
	- ranges for signals that may have min-, max- and typical values
	- single integers for signals that can be on, off or are ignored
	- booleans for signals that are either on or off

As a display may support multiple modes like this, a struct display_timings is
added, that holds all given struct display_timing pointers and declares the
native mode of the display.

Although a display may state that a signal can be in a range, it is driven with
fixed values that indicate a videomode. Therefore graphic drivers don't need all
the information of struct display_timing, but would generate a videomode from
the given set of supported signal timings and work with that.

The video subsystems all define their own structs that describe a mode and work
with that (e.g. fb_videomode or drm_display_mode). To slowly replace all those
various structures and allow code reuse across those subsystems, add struct
videomode as a generic description.

This patch only includes the most basic fields in struct videomode. All missing
fields that are needed to have a really generic video mode description can be
added at a later stage.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/Kconfig          |    6 +++
 drivers/video/Makefile         |    2 +
 drivers/video/display_timing.c |   24 ++++++++++
 drivers/video/videomode.c      |   45 +++++++++++++++++
 include/linux/display_timing.h |  104 ++++++++++++++++++++++++++++++++++++++++
 include/linux/videomode.h      |   52 ++++++++++++++++++++
 6 files changed, 233 insertions(+)
 create mode 100644 drivers/video/display_timing.c
 create mode 100644 drivers/video/videomode.c
 create mode 100644 include/linux/display_timing.h
 create mode 100644 include/linux/videomode.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d799..2a23b18 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -33,6 +33,12 @@ config VIDEO_OUTPUT_CONTROL
 	  This framework adds support for low-level control of the video 
 	  output switch.
 
+config DISPLAY_TIMING
+       bool
+
+config VIDEOMODE
+       bool
+
 menuconfig FB
 	tristate "Support for frame buffer devices"
 	---help---
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..fc30439 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -167,3 +167,5 @@ obj-$(CONFIG_FB_VIRTUAL)          += vfb.o
 
 #video output switch sysfs driver
 obj-$(CONFIG_VIDEO_OUTPUT_CONTROL) += output.o
+obj-$(CONFIG_DISPLAY_TIMING) += display_timing.o
+obj-$(CONFIG_VIDEOMODE) += videomode.o
diff --git a/drivers/video/display_timing.c b/drivers/video/display_timing.c
new file mode 100644
index 0000000..ac9bbbc
--- /dev/null
+++ b/drivers/video/display_timing.c
@@ -0,0 +1,24 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/display_timing.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+
+void display_timings_release(struct display_timings *disp)
+{
+	if (disp->timings) {
+		unsigned int i;
+
+		for (i = 0; i < disp->num_timings; i++)
+			kfree(disp->timings[i]);
+		kfree(disp->timings);
+	}
+	kfree(disp);
+}
+EXPORT_SYMBOL_GPL(display_timings_release);
diff --git a/drivers/video/videomode.c b/drivers/video/videomode.c
new file mode 100644
index 0000000..86a8558
--- /dev/null
+++ b/drivers/video/videomode.c
@@ -0,0 +1,45 @@
+/*
+ * generic display timing functions
+ *
+ * Copyright (c) 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>, Pengutronix
+ *
+ * This file is released under the GPLv2
+ */
+
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/display_timing.h>
+#include <linux/kernel.h>
+#include <linux/videomode.h>
+
+int videomode_from_timing(const struct display_timings *disp,
+			  struct videomode *vm, unsigned int index)
+{
+	struct display_timing *dt;
+
+	dt = display_timings_get(disp, index);
+	if (!dt)
+		return -EINVAL;
+
+	vm->pixelclock = display_timing_get_value(&dt->pixelclock, TE_TYP);
+	vm->hactive = display_timing_get_value(&dt->hactive, TE_TYP);
+	vm->hfront_porch = display_timing_get_value(&dt->hfront_porch, TE_TYP);
+	vm->hback_porch = display_timing_get_value(&dt->hback_porch, TE_TYP);
+	vm->hsync_len = display_timing_get_value(&dt->hsync_len, TE_TYP);
+
+	vm->vactive = display_timing_get_value(&dt->vactive, TE_TYP);
+	vm->vfront_porch = display_timing_get_value(&dt->vfront_porch, TE_TYP);
+	vm->vback_porch = display_timing_get_value(&dt->vback_porch, TE_TYP);
+	vm->vsync_len = display_timing_get_value(&dt->vsync_len, TE_TYP);
+
+	vm->vah = dt->vsync_pol_active;
+	vm->hah = dt->hsync_pol_active;
+	vm->de = dt->de_pol_active;
+	vm->pixelclk_pol = dt->pixelclk_pol;
+
+	vm->interlaced = dt->interlaced;
+	vm->doublescan = dt->doublescan;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(videomode_from_timing);
diff --git a/include/linux/display_timing.h b/include/linux/display_timing.h
new file mode 100644
index 0000000..b8922ea
--- /dev/null
+++ b/include/linux/display_timing.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012 Steffen Trumtrar <s.trumtrar@pengutronix.de>
+ *
+ * description of display timings
+ *
+ * This file is released under the GPLv2
+ */
+
+#ifndef __LINUX_DISPLAY_TIMINGS_H
+#define __LINUX_DISPLAY_TIMINGS_H
+
+#include <linux/types.h>
+
+/*
+ * A single signal can be specified via a range with a typical value, that lies
+ * somewhere inbetween. Do not use an array, to prevent any confusion about the
+ * meaning of every entry.
+ */
+struct timing_entry {
+	u32 min;
+	u32 typ;
+	u32 max;
+};
+
+enum timing_entry_index {
+	TE_MIN = 0,
+	TE_TYP = 1,
+	TE_MAX = 2,
+};
+
+/*
+ * Single "mode" entry. This describes one set of signal timings a display can
+ * have in one setting. This struct can later be converted to struct videomode
+ * (see include/linux/videomode.h). As each timing_entry can be defined as a
+ * range, one struct display_timing may become multiple struct videomodes.
+ */
+struct display_timing {
+	struct timing_entry pixelclock;
+
+	struct timing_entry hactive;
+	struct timing_entry hfront_porch;
+	struct timing_entry hback_porch;
+	struct timing_entry hsync_len;
+
+	struct timing_entry vactive;
+	struct timing_entry vfront_porch;
+	struct timing_entry vback_porch;
+	struct timing_entry vsync_len;
+
+	unsigned int vsync_pol_active;
+	unsigned int hsync_pol_active;
+	unsigned int de_pol_active;
+	unsigned int pixelclk_pol;
+	bool interlaced;
+	bool doublescan;
+};
+
+/*
+ * This describes all timing settings a display provides.
+ * The native_mode is the default setting for this display. It can be specified
+ * in the devicetree or will be the first that is provided. Drivers that can
+ * handle multiple videomode should work with this struct and convert each entry
+ * to the desired end result.
+ */
+struct display_timings {
+	unsigned int num_timings;
+	unsigned int native_mode;
+
+	struct display_timing **timings;
+};
+
+/* get value specified by index from struct timing_entry */
+static inline u32 display_timing_get_value(const struct timing_entry *te,
+					   enum timing_entry_index index)
+{
+	switch (index) {
+	case TE_MIN:
+		return te->min;
+		break;
+	case TE_TYP:
+		return te->typ;
+		break;
+	case TE_MAX:
+		return te->max;
+		break;
+	default:
+		return te->typ;
+	}
+}
+
+/* get one entry from struct display_timings */
+static inline struct display_timing *display_timings_get(const struct
+							 display_timings *disp,
+							 unsigned int index)
+{
+	if (disp->num_timings > index)
+		return disp->timings[index];
+	else
+		return NULL;
+}
+
+void display_timings_release(struct display_timings *disp);
+
+#endif
diff --git a/include/linux/videomode.h b/include/linux/videomode.h
new file mode 100644
index 0000000..41749f2
--- /dev/null
+++ b/include/linux/videomode.h
@@ -0,0 +1,52 @@
+/*
+ * 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
+
+struct display_timings;
+
+/*
+ * Subsystem independent description of a videomode.
+ * Can be generated from struct display_timing.
+ */
+struct videomode {
+	u32 pixelclock;		/* pixelclock in Hz */
+
+	u32 hactive;
+	u32 hfront_porch;
+	u32 hback_porch;
+	u32 hsync_len;
+
+	u32 vactive;
+	u32 vfront_porch;
+	u32 vback_porch;
+	u32 vsync_len;
+
+	u32 hah;		/* hsync active high */
+	u32 vah;		/* vsync active high */
+	u32 de;			/* data enable */
+	u32 pixelclk_pol;
+
+	bool interlaced;
+	bool doublescan;
+};
+
+/**
+ * videomode_from_timing - convert display timing to videomode
+ * @disp: structure with all possible timing entries
+ * @vm: return value
+ * @index: index into the list of display timings in devicetree
+ *
+ * DESCRIPTION:
+ * This function converts a struct display_timing to a struct videomode.
+ */
+int videomode_from_timing(const struct display_timings *disp,
+			  struct videomode *vm, unsigned int index);
+
+#endif
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 1/7] viafb: rename display_timing to via_display_timing
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <1353661467-28545-1-git-send-email-s.trumtrar@pengutronix.de>

The struct display_timing is specific to the via subsystem. The naming leads to
collisions with the new struct display_timing, that is supposed to be a shared
struct between different subsystems.
To clean this up, prepend the existing struct with the subsystem it is specific
to.

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
---
 drivers/video/via/hw.c              |    6 +++---
 drivers/video/via/hw.h              |    2 +-
 drivers/video/via/lcd.c             |    2 +-
 drivers/video/via/share.h           |    2 +-
 drivers/video/via/via_modesetting.c |    8 ++++----
 drivers/video/via/via_modesetting.h |    6 +++---
 6 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c
index 898590d..5563c67 100644
--- a/drivers/video/via/hw.c
+++ b/drivers/video/via/hw.c
@@ -1467,10 +1467,10 @@ void viafb_set_vclock(u32 clk, int set_iga)
 	via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */
 }
 
-struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var,
 	u16 cxres, u16 cyres)
 {
-	struct display_timing timing;
+	struct via_display_timing timing;
 	u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2;
 
 	timing.hor_addr = cxres;
@@ -1491,7 +1491,7 @@ struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
 void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
 	u16 cxres, u16 cyres, int iga)
 {
-	struct display_timing crt_reg = var_to_timing(var,
+	struct via_display_timing crt_reg = var_to_timing(var,
 		cxres ? cxres : var->xres, cyres ? cyres : var->yres);
 
 	if (iga = IGA1)
diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h
index 6be243c..c3f2572 100644
--- a/drivers/video/via/hw.h
+++ b/drivers/video/via/hw.h
@@ -637,7 +637,7 @@ extern int viafb_LCD_ON;
 extern int viafb_DVI_ON;
 extern int viafb_hotplug;
 
-struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+struct via_display_timing var_to_timing(const struct fb_var_screeninfo *var,
 	u16 cxres, u16 cyres);
 void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
 	u16 cxres, u16 cyres, int iga);
diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c
index 1650379..022b0df 100644
--- a/drivers/video/via/lcd.c
+++ b/drivers/video/via/lcd.c
@@ -549,7 +549,7 @@ void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres,
 	int panel_hres = plvds_setting_info->lcd_panel_hres;
 	int panel_vres = plvds_setting_info->lcd_panel_vres;
 	u32 clock;
-	struct display_timing timing;
+	struct via_display_timing timing;
 	struct fb_var_screeninfo panel_var;
 	const struct fb_videomode *mode_crt_table, *panel_crt_table;
 
diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h
index 3158dfc..65c65c6 100644
--- a/drivers/video/via/share.h
+++ b/drivers/video/via/share.h
@@ -319,7 +319,7 @@ struct crt_mode_table {
 	int refresh_rate;
 	int h_sync_polarity;
 	int v_sync_polarity;
-	struct display_timing crtc;
+	struct via_display_timing crtc;
 };
 
 struct io_reg {
diff --git a/drivers/video/via/via_modesetting.c b/drivers/video/via/via_modesetting.c
index 0e431ae..0b414b0 100644
--- a/drivers/video/via/via_modesetting.c
+++ b/drivers/video/via/via_modesetting.c
@@ -30,9 +30,9 @@
 #include "debug.h"
 
 
-void via_set_primary_timing(const struct display_timing *timing)
+void via_set_primary_timing(const struct via_display_timing *timing)
 {
-	struct display_timing raw;
+	struct via_display_timing raw;
 
 	raw.hor_total = timing->hor_total / 8 - 5;
 	raw.hor_addr = timing->hor_addr / 8 - 1;
@@ -88,9 +88,9 @@ void via_set_primary_timing(const struct display_timing *timing)
 	via_write_reg_mask(VIACR, 0x17, 0x80, 0x80);
 }
 
-void via_set_secondary_timing(const struct display_timing *timing)
+void via_set_secondary_timing(const struct via_display_timing *timing)
 {
-	struct display_timing raw;
+	struct via_display_timing raw;
 
 	raw.hor_total = timing->hor_total - 1;
 	raw.hor_addr = timing->hor_addr - 1;
diff --git a/drivers/video/via/via_modesetting.h b/drivers/video/via/via_modesetting.h
index 06e09fe..f6a6503 100644
--- a/drivers/video/via/via_modesetting.h
+++ b/drivers/video/via/via_modesetting.h
@@ -33,7 +33,7 @@
 #define VIA_PITCH_MAX	0x3FF8
 
 
-struct display_timing {
+struct via_display_timing {
 	u16 hor_total;
 	u16 hor_addr;
 	u16 hor_blank_start;
@@ -49,8 +49,8 @@ struct display_timing {
 };
 
 
-void via_set_primary_timing(const struct display_timing *timing);
-void via_set_secondary_timing(const struct display_timing *timing);
+void via_set_primary_timing(const struct via_display_timing *timing);
+void via_set_secondary_timing(const struct via_display_timing *timing);
 void via_set_primary_address(u32 addr);
 void via_set_secondary_address(u32 addr);
 void via_set_primary_pitch(u32 pitch);
-- 
1.7.10.4


^ permalink raw reply related

* [PATCHv14 0/7] of: add display helper
From: Steffen Trumtrar @ 2012-11-23  9:04 UTC (permalink / raw)
  To: devicetree-discuss
  Cc: Rob Herring, linux-fbdev, dri-devel, Laurent Pinchart,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie

Hi!

Changes since v13:
        - fix "const struct *" warning
                (reported by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>)
        - prevent division by zero in fb_videomode_from_videomode

Changes since v12:
        - rename struct display_timing to via_display_timing in via subsystem
        - fix refreshrate calculation
        - fix "const struct *" warnings
                (reported by: Manjunathappa, Prakash <prakash.pm@ti.com>)
        - some CodingStyle fixes
        - rewrite parts of commit messages and display-timings.txt
        - let display_timing_get_value get all values instead of just typical

Changes since v11:
        - make pointers const where applicable
        - add reviewed-by Laurent Pinchart

Changes since v10:
        - fix function name (drm_)display_mode_from_videomode
        - add acked-by, reviewed-by, tested-by

Changes since v9:
        - don't leak memory when previous timings were correct
        - CodingStyle fixes
        - move blank lines around

Changes since v8:
        - fix memory leaks
        - change API to be more consistent (foo_from_bar(struct bar, struct foo))
        - include headers were necessary
        - misc minor bufixe

Changes since v7:
        - move of_xxx to drivers/video
        - remove non-binding documentation from display-timings.txt
        - squash display_timings and videomode in one patch
        - misc minor fixes

Changes since v6:
        - get rid of some empty lines etc.
        - move functions to their subsystems
        - split of_ from non-of_ functions
        - add at least some kerneldoc to some functions

Changes since v5:
        - removed all display stuff and just describe timings

Changes since v4:
        - refactored functions

Changes since v3:
        - print error messages
        - free alloced memory
        - general cleanup

Changes since v2:
        - use hardware-near property-names
        - provide a videomode structure
        - allow ranges for all properties (<min,typ,max>)
        - functions to get display_mode or fb_videomode


Steffen Trumtrar (7):
  viafb: rename display_timing to via_display_timing
  video: add display_timing and videomode
  video: add of helper for display timings/videomode
  fbmon: add videomode helpers
  fbmon: add of_videomode helpers
  drm_modes: add videomode helpers
  drm_modes: add of_videomode helpers

 .../devicetree/bindings/video/display-timings.txt  |  107 ++++++++++
 drivers/gpu/drm/drm_modes.c                        |   69 ++++++
 drivers/video/Kconfig                              |   21 ++
 drivers/video/Makefile                             |    4 +
 drivers/video/display_timing.c                     |   24 +++
 drivers/video/fbmon.c                              |   89 ++++++++
 drivers/video/of_display_timing.c                  |  223 ++++++++++++++++++++
 drivers/video/of_videomode.c                       |   48 +++++
 drivers/video/via/hw.c                             |    6 +-
 drivers/video/via/hw.h                             |    2 +-
 drivers/video/via/lcd.c                            |    2 +-
 drivers/video/via/share.h                          |    2 +-
 drivers/video/via/via_modesetting.c                |    8 +-
 drivers/video/via/via_modesetting.h                |    6 +-
 drivers/video/videomode.c                          |   45 ++++
 include/drm/drmP.h                                 |   12 ++
 include/linux/display_timing.h                     |  104 +++++++++
 include/linux/fb.h                                 |   12 ++
 include/linux/of_display_timings.h                 |   20 ++
 include/linux/of_videomode.h                       |   18 ++
 include/linux/videomode.h                          |   52 +++++
 21 files changed, 861 insertions(+), 13 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/display-timings.txt
 create mode 100644 drivers/video/display_timing.c
 create mode 100644 drivers/video/of_display_timing.c
 create mode 100644 drivers/video/of_videomode.c
 create mode 100644 drivers/video/videomode.c
 create mode 100644 include/linux/display_timing.h
 create mode 100644 include/linux/of_display_timings.h
 create mode 100644 include/linux/of_videomode.h
 create mode 100644 include/linux/videomode.h

-- 
1.7.10.4


^ permalink raw reply

* Re: [PATCHv13 4/7] fbmon: add videomode helpers
From: Steffen Trumtrar @ 2012-11-23  8:03 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, linux-fbdev, Florian Tobias Schandinat,
	devicetree-discuss, Tomi Valkeinen, kernel, Guennady Liakhovetski,
	linux-media
In-Reply-To: <2692338.s0PjnOCRb5@avalon>

On Fri, Nov 23, 2012 at 12:52:08AM +0100, Laurent Pinchart wrote:
> On Friday 23 November 2012 00:09:49 Steffen Trumtrar wrote:
> > On Thu, Nov 22, 2012 at 07:31:39PM +0100, Laurent Pinchart wrote:
> > > On Thursday 22 November 2012 17:00:12 Steffen Trumtrar wrote:
> > > > Add a function to convert from the generic videomode to a fb_videomode.
> > > > 
> > > > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > > Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > > Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > > Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > > ---
> > > > 
> > > >  drivers/video/fbmon.c |   44 ++++++++++++++++++++++++++++++++++++++++++
> > > >  include/linux/fb.h    |    6 ++++++
> > > >  2 files changed, 50 insertions(+)
> > > > 
> > > > diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
> > > > index cef6557..a6a564d 100644
> > > > --- a/drivers/video/fbmon.c
> > > > +++ b/drivers/video/fbmon.c
> > > > @@ -31,6 +31,7 @@
> > > > 
> > > >  #include <linux/pci.h>
> > > >  #include <linux/slab.h>
> > > >  #include <video/edid.h>
> > > > 
> > > > +#include <linux/videomode.h>
> > > > 
> > > >  #ifdef CONFIG_PPC_OF
> > > >  #include <asm/prom.h>
> > > >  #include <asm/pci-bridge.h>
> > > > 
> > > > @@ -1373,6 +1374,49 @@ int fb_get_mode(int flags, u32 val, struct
> > > > fb_var_screeninfo *var, struct fb_inf kfree(timings);
> > > > 
> > > >  	return err;
> > > >  
> > > >  }
> > > > 
> > > > +
> > > > +#if IS_ENABLED(CONFIG_VIDEOMODE)
> > > > +int fb_videomode_from_videomode(const struct videomode *vm,
> > > > +				struct fb_videomode *fbmode)
> > > > +{
> > > > +	unsigned int htotal, vtotal;
> > > > +
> > > > +	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);
> > > 
> > > This results in a division by 0 if vm->pixelclock is equal to zero. As the
> > > information is missing from many board files, what would you think about
> > > the following ?
> > > 
> > > 	fbmode->pixclock = vm->pixelclock ? KHZ2PICOS(vm->pixelclock / 1000) : 0;
> > 
> > Grrr...you are right. I will fix that...
> 
> Thank you.
> 

I have to thank you for bulletproofing my code o/\o

> > > > +	htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
> > > > +		 vm->hsync_len;
> > > > +	vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
> > > > +		 vm->vsync_len;
> > > > +	fbmode->refresh = vm->pixelclock / (htotal * vtotal);
> > > > +
> > 
> > ...and obviously this, too.
> 
> That one is less of an issue in my opinion. A mode with a zero htotal or 
> vtotal is clearly invalid, while we have modes with no pixel clock value.
> 

Yes, you I are right. But while I'm on it, might as well prevent an error.

Regards,
Steffen
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCHv13 5/7] fbmon: add of_videomode helpers
From: Steffen Trumtrar @ 2012-11-23  7:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: devicetree-discuss, Rob Herring, linux-fbdev, dri-devel,
	Thierry Reding, Guennady Liakhovetski, linux-media,
	Tomi Valkeinen, Stephen Warren, kernel, Florian Tobias Schandinat,
	David Airlie
In-Reply-To: <2117247.Eyo66IqYf0@avalon>

Hi Laurent,

On Thu, Nov 22, 2012 at 06:58:09PM +0100, Laurent Pinchart wrote:
> Hi Steffen,
> 
> On Thursday 22 November 2012 17:00:13 Steffen Trumtrar wrote:
> > Add helper to get fb_videomode from devicetree.
> > 
> > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> This patch results in the following build warning:
> 
> drivers/video/fbmon.c: In function 'of_get_fb_videomode':
> drivers/video/fbmon.c:1445: warning: passing argument 1 of 'of_get_videomode' 
> discards qualifiers from pointer target type
> include/linux/of_videomode.h:15: note: expected 'struct device_node *' but 
> argument is of type 'const struct device_node *'
> 

This I don't get. Well, looking at the code, the warning is correct. I fixed
that now. But, I do not get the warning. Just tried it again before fixing:
cleaned, cleaned and touching file, just "make drivers/video/fbmon.o". Nothing.
Weird.

So, thanks for reporting it.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* [GIT PULL] Exynos DP for v3.8
From: Jingoo Han @ 2012-11-23  6:37 UTC (permalink / raw)
  To: linux-fbdev

SGkgVG9taSwNCg0KVGhhbmsgeW91IGZvciB0YWtpbmcgY2FyZSBvZiBwdWxsIHJlcXVlc3RzIGZv
ciB0aGUgdjMuOCBtZXJnZSB3aW5kb3cuDQpUaGVzZSBwYXRjaGVzIGhhdmUgYmVlbiBzdWJtaXR0
ZWQgZm9yIG1vcmUgdGhhbiBvbmUgbW9udGgsDQphbmQgdGVzdGVkIHdpdGggRXh5bm9zNTI1MC4N
Cg0KVGhlIGZvbGxvd2luZyBjaGFuZ2VzIHNpbmNlIGNvbW1pdCBmNGE3NWQyZWI3YjFlMjIwNjA5
NGI5MDFiZTA5YWRiMzFiYTYzNjgxOg0KDQogIExpbnV4IDMuNy1yYzYgKEZyaSBOb3YgMTYgMTc6
NDI6NDAgMjAxMiAtMDgwMCkNCg0KYXJlIGF2YWlsYWJsZSBpbiB0aGUgZ2l0IHJlcG9zaXRvcnkg
YXQ6DQogIGdpdDovL2dpdGh1Yi5jb20vamluZ29vL2xpbnV4LmdpdCBleHlub3MtZHAtbmV4dA0K
DQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tDQpFeHlub3MgRFAgY2hhbmdlcyBmb3IgdGhlIDMuOCBtZXJnZSB3aW5kb3cuDQoN
Ci0gRGV2aWNlIFRyZWUgc3VwcG9ydCBmb3IgU2Ftc3VuZyBFeHlub3MgRFANCi0gU1cgTGluayB0
cmFpbmluZyBpcyBjbGVhbmVkIHVwLg0KLSBIUEQgaW50ZXJydXB0IGlzIHN1cHBvcnRlZC4NCi0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0NCg0KQWpheSBLdW1hciAoNSk6DQogICAgICB2aWRlbzogZXh5bm9zX2RwOiBBZGQgZGV2
aWNlIHRyZWUgc3VwcG9ydCB0byBEUCBkcml2ZXINCiAgICAgIHZpZGVvOiBleHlub3NfZHA6IGRl
dmljZSB0cmVlIGRvY3VtZW50YXRpb24NCiAgICAgIHZpZGVvOiBleHlub3NfZHA6IFJlc2V0IGFu
ZCBpbml0aWFsaXplIERQIGJlZm9yZSByZXF1ZXN0aW5nIGlycQ0KICAgICAgdmlkZW86IGV4eW5v
c19kcDogRml4IGluY29ycmVjdCBzZXR0aW5nIGZvciBJTlRfQ1RMDQogICAgICB2aWRlbzogZXh5
bm9zX2RwOiByZW1vdmUgcmVkdW5kYW50IHBhcmFtZXRlcnMNCg0KU2VhbiBQYXVsICg4KToNCiAg
ICAgIHZpZGVvOiBleHlub3NfZHA6IENoZWNrIERQQ0QgcmV0dXJuIGNvZGVzDQogICAgICB2aWRl
bzogZXh5bm9zX2RwOiBDbGVhbiB1cCBTVyBsaW5rIHRyYWluaW5nDQogICAgICB2aWRlbzogZXh5
bm9zX2RwOiBHZXQgcGxsIGxvY2sgYmVmb3JlIHBhdHRlcm4gc2V0DQogICAgICB2aWRlbzogZXh5
bm9zX2RwOiBJbXByb3ZlIEVESUQgZXJyb3IgaGFuZGxpbmcNCiAgICAgIHZpZGVvOiBleHlub3Nf
ZHA6IEZpeCBidWcgd2hlbiBjaGVja2luZyBkcC0+aXJxDQogICAgICB2aWRlbzogZXh5bm9zX2Rw
OiBSZW1vdmUgc2luayBjb250cm9sIHRvIEQwDQogICAgICB2aWRlbzogZXh5bm9zX2RwOiBNb3Zl
IGhvdHBsdWcgaW50byBhIHdvcmtxdWV1ZQ0KICAgICAgdmlkZW86IGV4eW5vc19kcDogRW5hYmxl
IGhvdHBsdWcgaW50ZXJydXB0cw0KDQogLi4uL2RldmljZXRyZWUvYmluZGluZ3MvdmlkZW8vZXh5
bm9zX2RwLnR4dCAgICAgICAgfCAgIDgwICsrKw0KIGRyaXZlcnMvdmlkZW8vZXh5bm9zL2V4eW5v
c19kcF9jb3JlLmMgICAgICAgICAgICAgIHwgIDY5NyArKysrKysrKysrKystLS0tLS0tLQ0KIGRy
aXZlcnMvdmlkZW8vZXh5bm9zL2V4eW5vc19kcF9jb3JlLmggICAgICAgICAgICAgIHwgICAyMSAr
LQ0KIGRyaXZlcnMvdmlkZW8vZXh5bm9zL2V4eW5vc19kcF9yZWcuYyAgICAgICAgICAgICAgIHwg
ICA3NyArKy0NCiBkcml2ZXJzL3ZpZGVvL2V4eW5vcy9leHlub3NfZHBfcmVnLmggICAgICAgICAg
ICAgICB8ICAgIDMgKy0NCiA1IGZpbGVzIGNoYW5nZWQsIDU4MyBpbnNlcnRpb25zKCspLCAyOTUg
ZGVsZXRpb25zKC0pDQogY3JlYXRlIG1vZGUgMTAwNjQ0IERvY3VtZW50YXRpb24vZGV2aWNldHJl
ZS9iaW5kaW5ncy92aWRlby9leHlub3NfZHAudHh0DQoNCi0tDQpCZXN0IHJlZ2FyZHMsDQpKaW5n
b28gSGFuDQo


^ permalink raw reply

* [GIT PULL] Samsung Framebuffer for v3.8
From: Jingoo Han @ 2012-11-23  6:36 UTC (permalink / raw)
  To: linux-fbdev

SGkgVG9taSwNCg0KVGhhbmsgeW91IGZvciB0YWtpbmcgY2FyZSBvZiBwdWxsIHJlcXVlc3RzIGZv
ciB0aGUgdjMuOCBtZXJnZSB3aW5kb3cuDQpUaGVzZSBwYXRjaGVzIGhhdmUgYmVlbiBzdWJtaXR0
ZWQgZm9yIGFib3V0IHR3byBtb250aHMsDQphbmQgdGVzdGVkIHdpdGggRXh5bm9zNDIxMCBhbmQg
RXh5bm9zNTI1MC4NCg0KVGhlIGZvbGxvd2luZyBjaGFuZ2VzIHNpbmNlIGNvbW1pdCBmNGE3NWQy
ZWI3YjFlMjIwNjA5NGI5MDFiZTA5YWRiMzFiYTYzNjgxOg0KDQogIExpbnV4IDMuNy1yYzYgKEZy
aSBOb3YgMTYgMTc6NDI6NDAgMjAxMiAtMDgwMCkNCg0KYXJlIGF2YWlsYWJsZSBpbiB0aGUgZ2l0
IHJlcG9zaXRvcnkgYXQ6DQogIGdpdDovL2dpdGh1Yi5jb20vamluZ29vL2xpbnV4LmdpdCBzYW1z
dW5nLWZiLW5leHQNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KU2Ftc3VuZyBGcmFtZWJ1ZmZlciBjaGFuZ2VzIGZvciB0
aGUgMy44IG1lcmdlIHdpbmRvdy4NCg0KLSBUaGUgYml0IGRlZmluaXRpb25zIG9mIGhlYWRlciBm
aWxlIGFyZSB1cGRhdGVkLg0KLSBTb21lIG1pbm9yIHR5cG9zIGFyZSBmaXhlZC4NCi0gU29tZSBt
aW5vciBidWdzIG9mIHMzY19mYl9jaGVja192YXIoKSBhcmUgZml4ZWQuDQotLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCkpp
bmdvbyBIYW4gKDE0KToNCiAgICAgIHZpZGVvOiBzM2MtZmI6IGNsZWFuIHRoZSBiaXQgZGVmaW5p
dGlvbiBmb3IgV0lOQ09OIHJlZ2lzdGVyDQogICAgICB2aWRlbzogczNjLWZiOiBtb3ZlIHRoZSBh
ZGRyZXNzIGRlZmluaXRpb25zIGZvciBWSURUQ09OIHJlZ2lzdGVycw0KICAgICAgdmlkZW86IHMz
Yy1mYjogbW92ZSB0aGUgYWRkcmVzcyBkZWZpbml0aW9uIGZvciBWSURPU0QgcmVnaXN0ZXINCiAg
ICAgIHZpZGVvOiBzM2MtZmI6IG1vdmUgdGhlIGJpdCBkZWZpbml0aW9ucyBmb3IgVklESU5UQ09O
MCByZWdpc3Rlcg0KICAgICAgdmlkZW86IHMzYy1mYjogbW92ZSB0aGUgYml0IGRlZmluaXRpb25z
IGZvciBXSU54TUFQIGFuZCBXUEFMQ09OIHJlZ2lzdGVyDQogICAgICB2aWRlbzogczNjLWZiOiBt
b3ZlIHRoZSBiaXQgZGVmaW5pdGlvbnMgZm9yIERJVEhNT0RFIHJlZ2lzdGVyDQogICAgICB2aWRl
bzogczNjLWZiOiBhZGQgdGhlIGJpdCBkZWZpbml0aW9ucyBmb3IgVklEQ09OMF9WSURPVVRfV0IN
CiAgICAgIHZpZGVvOiBzM2MtZmI6IGZpeCB0eXBvIGluIGNvbW1lbnQNCiAgICAgIHZpZGVvOiBz
M2MtZmI6IGZpeCBoZWxwIG1lc3NhZ2UgZm9yIEZCX1MzQ19ERUJVR19SRUdXUklURQ0KICAgICAg
dmlkZW86IHMzYy1mYjogdXNlIEZJTURfVjhfVklEVENPTjAgZm9yIEVYWU5PUzUgRklNRA0KICAg
ICAgdmlkZW86IHMzYy1mYjogdXNlIGRldl9nZXRfZHJ2ZGF0YSgpIGluc3RlYWQgb2YgcGxhdGZv
cm1fZ2V0X2RydmRhdGEoKQ0KICAgICAgdmlkZW86IHMzYy1mYjogYWRkICJkcm9wIHRocm91Z2gi
IGNvbW1lbnQNCiAgICAgIHZpZGVvOiBzM2MtZmI6IHJldHVybiBhbiBlcnJvciB3aGVuIGJwcCBp
cyBpbnZhbGlkDQogICAgICB2aWRlbzogczNjLWZiOiBmaXggcmVkIG9mZnNldCBhbmQgbGVuZ3Ro
IGZvciBBUkdCMjMyIGZvcm1hdA0KDQogZHJpdmVycy92aWRlby9LY29uZmlnICAgICAgICB8ICAg
IDIgKy0NCiBkcml2ZXJzL3ZpZGVvL3MzYy1mYi5jICAgICAgIHwgICAyNCArKystLS0NCiBpbmNs
dWRlL3ZpZGVvL3NhbXN1bmdfZmltZC5oIHwgIDE2OCArKysrKysrKysrKysrLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0NCiAzIGZpbGVzIGNoYW5nZWQsIDY0IGluc2VydGlvbnMoKyksIDEz
MCBkZWxldGlvbnMoLSkNCg0KLS0NCkJlc3QgcmVnYXJkcywNCkppbmdvbyBIYW4NCg=



^ permalink raw reply

* RE: [PATCH] video: da8xx-fb: clk_get on connection id fck
From: Manjunathappa, Prakash @ 2012-11-23  4:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <50AB7F7E.1070400@ti.com>

SGkgVG9taSwNCg0KT24gVHVlLCBOb3YgMjAsIDIwMTIgYXQgMTg6MzI6NTQsIE5vcmksIFNla2hh
ciB3cm90ZToNCj4gT24gMTEvMjAvMjAxMiA2OjExIFBNLCBNYW5qdW5hdGhhcHBhIHdyb3RlOg0K
PiA+IGRvIGNsa19nZXQgb24gY29ubmVjdGlvbiBpZCAiZmNrIiB0byBzdXBwb3J0IE9NQVAgYmFz
ZWQNCj4gPiBwbGF0Zm9ybXMgaGF2aW5nIG11bHRpcGxlIGNsb2NrcyBmb3IgbW9kdWxlLiBXaXRo
b3V0IHRoaXMNCj4gPiBkcml2ZXIgY2hhbmdlIGNsa19nZXQgZmFpbHMgb24gYW0zMzV4Lg0KPiA+
IA0KPiA+IFRoaXMgcGF0Y2ggaXMgYmFzZWQgb24gdGhlIGRpc2N1c3Npb24gaW4gY29tbXVuaXR5
DQo+ID4gaHR0cDovL21hcmMuaW5mby8/bD1saW51eC1rZXJuZWwmbT0xMzUxNjYwMTg5MDc4Mjcm
dz0yDQo+ID4gDQo+ID4gU2lnbmVkLW9mZi1ieTogTWFuanVuYXRoYXBwYSA8cHJha2FzaC5wbUB0
aS5jb20+DQo+ID4gQ2M6IFZhaWJoYXYgSGlyZW1hdGggPGh2YWliaGF2QHRpLmNvbT4NCj4gDQo+
IEZvciB0aGUgbWFjaC1kYXZpbmNpIGNoYW5nZXM6DQo+IA0KPiBBY2tlZC1ieTogU2VraGFyIE5v
cmkgPG5zZWtoYXJAdGkuY29tPg0KPiANCj4gRmxvcmlhbiwNCj4gDQo+IEkgYXNzdW1lIHlvdSB3
aWxsIHdhbnQgdG8gdGFrZSB0aGlzIHRocm91Z2ggeW91ciB0cmVlPw0KPiANCg0KQ291bGQgeW91
IHBsZWFzZSBwdWxsIGluIHRoaXM/DQoNClRoYW5rcywNClByYWthc2gNCg0KPiBUaGFua3MsDQo+
IFNla2hhcg0KPiANCg0K

^ permalink raw reply

* RE: [PATCH v2 0/2] da8xx-fb: LCDC driver cleanup
From: Manjunathappa, Prakash @ 2012-11-23  4:30 UTC (permalink / raw)
  To: linux-fbdev
In-Reply-To: <1350363196-9340-1-git-send-email-prakash.pm@ti.com>

Hi Tomi,

On Tue, Oct 16, 2012 at 10:23:14, Manjunathappa, Prakash wrote:
> This patch series clean up driver as it is necessary for DT migration.
> 
> Manjunathappa, Prakash (2):
>   da8xx-fb: adopt fb_videomode data for panel information
>   da8xx-fb: cleanup LCDC configurations
> 

There are no review comment on this, Could you please accept this series? 

Thanks,
Prakash

>  arch/arm/mach-davinci/devices-da8xx.c |   22 +----
>  drivers/video/da8xx-fb.c              |  167 +++++++++++++++------------------
>  include/video/da8xx-fb.h              |   25 +----
>  3 files changed, 79 insertions(+), 135 deletions(-)
> 
> 


^ permalink raw reply

* Re: [PATCHv9 1/3] Runtime Interpreted Power Sequences
From: Mark Brown @ 2012-11-23  1:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdaNmBpfONDpt7zFqLaqfiGm+ELpO-v5gZmM0rEi_AzijQ@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 281 bytes --]

On Thu, Nov 22, 2012 at 09:57:22AM +0100, Linus Walleij wrote:

> Is it correct to assume that this library will be useful also for ALSA
> SoC type of devices?

ASoC has facilities for autogenerating the bulk of the sequences which
with modern devices is all that you really need.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCHv13 4/7] fbmon: add videomode helpers
From: Laurent Pinchart @ 2012-11-22 23:52 UTC (permalink / raw)
  To: Steffen Trumtrar
  Cc: dri-devel, linux-fbdev, Florian Tobias Schandinat,
	devicetree-discuss, Tomi Valkeinen, kernel, Guennady Liakhovetski,
	linux-media
In-Reply-To: <20121122230949.GA8698@pengutronix.de>

On Friday 23 November 2012 00:09:49 Steffen Trumtrar wrote:
> On Thu, Nov 22, 2012 at 07:31:39PM +0100, Laurent Pinchart wrote:
> > On Thursday 22 November 2012 17:00:12 Steffen Trumtrar wrote:
> > > Add a function to convert from the generic videomode to a fb_videomode.
> > > 
> > > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> > > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > > ---
> > > 
> > >  drivers/video/fbmon.c |   44 ++++++++++++++++++++++++++++++++++++++++++
> > >  include/linux/fb.h    |    6 ++++++
> > >  2 files changed, 50 insertions(+)
> > > 
> > > diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
> > > index cef6557..a6a564d 100644
> > > --- a/drivers/video/fbmon.c
> > > +++ b/drivers/video/fbmon.c
> > > @@ -31,6 +31,7 @@
> > > 
> > >  #include <linux/pci.h>
> > >  #include <linux/slab.h>
> > >  #include <video/edid.h>
> > > 
> > > +#include <linux/videomode.h>
> > > 
> > >  #ifdef CONFIG_PPC_OF
> > >  #include <asm/prom.h>
> > >  #include <asm/pci-bridge.h>
> > > 
> > > @@ -1373,6 +1374,49 @@ int fb_get_mode(int flags, u32 val, struct
> > > fb_var_screeninfo *var, struct fb_inf kfree(timings);
> > > 
> > >  	return err;
> > >  
> > >  }
> > > 
> > > +
> > > +#if IS_ENABLED(CONFIG_VIDEOMODE)
> > > +int fb_videomode_from_videomode(const struct videomode *vm,
> > > +				struct fb_videomode *fbmode)
> > > +{
> > > +	unsigned int htotal, vtotal;
> > > +
> > > +	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);
> > 
> > This results in a division by 0 if vm->pixelclock is equal to zero. As the
> > information is missing from many board files, what would you think about
> > the following ?
> > 
> > 	fbmode->pixclock = vm->pixelclock ? KHZ2PICOS(vm->pixelclock / 1000) : 0;
> 
> Grrr...you are right. I will fix that...

Thank you.

> > > +	htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
> > > +		 vm->hsync_len;
> > > +	vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
> > > +		 vm->vsync_len;
> > > +	fbmode->refresh = vm->pixelclock / (htotal * vtotal);
> > > +
> 
> ...and obviously this, too.

That one is less of an issue in my opinion. A mode with a zero htotal or 
vtotal is clearly invalid, while we have modes with no pixel clock value.

-- 
Regards,

Laurent Pinchart


^ permalink raw reply

* Re: [PATCHv13 4/7] fbmon: add videomode helpers
From: Steffen Trumtrar @ 2012-11-22 23:09 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: dri-devel, linux-fbdev, Florian Tobias Schandinat,
	devicetree-discuss, Tomi Valkeinen, kernel, Guennady Liakhovetski,
	linux-media
In-Reply-To: <2107534.vAYnU9M0ZA@avalon>

On Thu, Nov 22, 2012 at 07:31:39PM +0100, Laurent Pinchart wrote:
> Hi Steffen,
> 
> On Thursday 22 November 2012 17:00:12 Steffen Trumtrar wrote:
> > Add a function to convert from the generic videomode to a fb_videomode.
> > 
> > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > Reviewed-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Acked-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Thierry Reding <thierry.reding@avionic-design.de>
> > Tested-by: Philipp Zabel <p.zabel@pengutronix.de>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
> > ---
> >  drivers/video/fbmon.c |   44 ++++++++++++++++++++++++++++++++++++++++++++
> >  include/linux/fb.h    |    6 ++++++
> >  2 files changed, 50 insertions(+)
> > 
> > diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
> > index cef6557..a6a564d 100644
> > --- a/drivers/video/fbmon.c
> > +++ b/drivers/video/fbmon.c
> > @@ -31,6 +31,7 @@
> >  #include <linux/pci.h>
> >  #include <linux/slab.h>
> >  #include <video/edid.h>
> > +#include <linux/videomode.h>
> >  #ifdef CONFIG_PPC_OF
> >  #include <asm/prom.h>
> >  #include <asm/pci-bridge.h>
> > @@ -1373,6 +1374,49 @@ int fb_get_mode(int flags, u32 val, struct
> > fb_var_screeninfo *var, struct fb_inf kfree(timings);
> >  	return err;
> >  }
> > +
> > +#if IS_ENABLED(CONFIG_VIDEOMODE)
> > +int fb_videomode_from_videomode(const struct videomode *vm,
> > +				struct fb_videomode *fbmode)
> > +{
> > +	unsigned int htotal, vtotal;
> > +
> > +	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);
> 
> This results in a division by 0 if vm->pixelclock is equal to zero. As the 
> information is missing from many board files, what would you think about the 
> following ?
> 
> 	fbmode->pixclock = vm->pixelclock ? KHZ2PICOS(vm->pixelclock / 1000) : 0;
> 

Grrr...you are right. I will fix that...

> > +	htotal = vm->hactive + vm->hfront_porch + vm->hback_porch +
> > +		 vm->hsync_len;
> > +	vtotal = vm->vactive + vm->vfront_porch + vm->vback_porch +
> > +		 vm->vsync_len;
> > +	fbmode->refresh = vm->pixelclock / (htotal * vtotal);
> > +

...and obviously this, too.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* [RFC v2 5/5] video: panel: Add R61517 panel support
From: Laurent Pinchart @ 2012-11-22 21:45 UTC (permalink / raw)
  To: linux-fbdev, dri-devel
  Cc: linux-media, Archit Taneja, Benjamin Gaignard, Bryan Wu, Inki Dae,
	Jesse Barker, Kyungmin Park, Marcus Lorentzon, Maxime Ripard,
	Philipp Zabel, Ragesh Radhakrishnan, Rob Clark, Sascha Hauer,
	Sebastien Guiriec, Sumit Semwal, Thomas Petazzoni, Tom Gall,
	Tomi Valkeinen, Vikas Sajjan
In-Reply-To: <1353620736-6517-1-git-send-email-laurent.pinchart@ideasonboard.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

The R61517 is a MIPI DBI panel controller from Renesas.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |    9 +
 drivers/video/display/Makefile       |    1 +
 drivers/video/display/panel-r61517.c |  447 ++++++++++++++++++++++++++++++++++
 include/video/panel-r61517.h         |   28 ++
 4 files changed, 485 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/display/panel-r61517.c
 create mode 100644 include/video/panel-r61517.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index c88999c..13b6aaf 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -27,4 +27,13 @@ config DISPLAY_PANEL_R61505
 
 	  If you are in doubt, say N.
 
+config DISPLAY_PANEL_R61517
+	tristate "Renesas R61517-based Display Panel"
+	select DISPLAY_MIPI_DBI
+	---help---
+	  Support panels based on the Renesas R61517 panel controller.
+	  Those panels are controlled through a MIPI DBI interface.
+
+	  If you are in doubt, say N.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 4c68465..482bec7 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_DISPLAY_CORE) += display-core.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
 obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
+obj-$(CONFIG_DISPLAY_PANEL_R61517) += panel-r61517.o
diff --git a/drivers/video/display/panel-r61517.c b/drivers/video/display/panel-r61517.c
new file mode 100644
index 0000000..b4dced4
--- /dev/null
+++ b/drivers/video/display/panel-r61517.c
@@ -0,0 +1,447 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on KFR2R09 LCD panel support
+ * Copyright (C) 2009 Magnus Damm
+ * Register settings based on the out-of-tree t33fb.c driver
+ * Copyright (C) 2008 Lineo Solutions, Inc.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/mipi_display.h>
+#include <video/panel-r61517.h>
+
+struct r61517 {
+	struct display_entity entity;
+	struct mipi_dbi_device *dbi;
+	const struct panel_r61517_platform_data *pdata;
+};
+
+#define to_panel(p)	container_of(p, struct r61517, entity)
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61517_write_command(struct r61517 *panel, u16 cmd)
+{
+	mipi_dbi_write_command(panel->dbi, cmd);
+}
+
+static void r61517_write_data(struct r61517 *panel, u8 data)
+{
+	mipi_dbi_write_data(panel->dbi, &data, 1);
+}
+
+static int r61517_read_data(struct r61517 *panel)
+{
+	u8 data;
+	int ret;
+
+	ret = mipi_dbi_read_data(panel->dbi, &data, 1);
+	if (ret < 0)
+		return ret;
+
+	return data;
+}
+
+static void r61517_write(struct r61517 *panel, u8 reg, const u8 *data,
+			 size_t len)
+{
+	mipi_dbi_write_command(panel->dbi, reg);
+	mipi_dbi_write_data(panel->dbi, data, len);
+}
+
+static void r61517_write8(struct r61517 *panel, u8 reg, u8 data)
+{
+	r61517_write(panel, reg, &data, 1);
+}
+
+static void r61517_write16(struct r61517 *panel, u8 reg, u16 data)
+{
+	u8 buffer[2] = { (data >> 8) & 0xff, (data >> 0) & 0xff };
+
+	r61517_write(panel, reg, buffer, 2);
+}
+
+static void r61517_write32(struct r61517 *panel, u8 reg, u32 data)
+{
+	u8 buffer[4] = { (data >> 24) & 0xff, (data >> 16) & 0xff,
+			 (data >>  8) & 0xff, (data >>  0) & 0xff };
+
+	r61517_write(panel, reg, buffer, 4);
+}
+
+#define r61517_write_array(p, c, a) \
+	r61517_write((p), (c), (a), ARRAY_SIZE(a))
+
+static void r61517_reset(struct r61517 *panel)
+{
+	gpio_set_value(panel->pdata->protect, 0);	/* PROTECT/ -> L */
+	gpio_set_value(panel->pdata->reset, 0);		/* LCD_RST/ -> L */
+	gpio_set_value(panel->pdata->protect, 1);	/* PROTECT/ -> H */
+	usleep_range(1100, 1200);
+	gpio_set_value(panel->pdata->reset, 1);		/* LCD_RST/ -> H */
+	usleep_range(10, 100);
+	gpio_set_value(panel->pdata->protect, 0);	/* PROTECT/ -> L */
+	msleep(20);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const u8 data_frame_if[] = {
+	0x02, /* WEMODE: 1=cont, 0=one-shot */
+	0x00, 0x00,
+	0x00, /* EPF, DFM */
+	0x02, /* RIM[1] : 1 (18bpp) */
+};
+
+static const u8 data_panel[] = {
+	0x0b,
+	0x63, /* 400 lines */
+	0x04, 0x00, 0x00, 0x04, 0x11, 0x00, 0x00,
+};
+
+static const u8 data_timing[] = {
+	0x00, 0x00, 0x13, 0x08, 0x08,
+};
+
+static const u8 data_timing_src[] = {
+	0x11, 0x01, 0x00, 0x01,
+};
+
+static const u8 data_gamma[] = {
+	0x01, 0x02, 0x08, 0x23,	0x03, 0x0c, 0x00, 0x06,	0x00, 0x00,
+	0x01, 0x00, 0x0c, 0x23, 0x03, 0x08, 0x02, 0x06, 0x00, 0x00,
+};
+
+static const u8 data_power[] = {
+	0x07, 0xc5, 0xdc, 0x02,	0x33, 0x0a,
+};
+
+static const u8 data_vcom[] = {
+	0x00, 0x0f, 0x02,
+};
+
+static unsigned long r61517_read_device_code(struct r61517 *panel)
+{
+	/* access protect OFF */
+	r61517_write8(panel, 0xb0, 0);
+
+	/* deep standby OFF */
+	r61517_write8(panel, 0xb1, 0);
+
+	/* device code command */
+	r61517_write_command(panel, 0xbf);
+	mdelay(50);
+
+	/* dummy read */
+	r61517_read_data(panel);
+
+	/* read device code */
+	return (r61517_read_data(panel) << 24) |
+	       (r61517_read_data(panel) << 16) |
+	       (r61517_read_data(panel) << 8) |
+	       (r61517_read_data(panel) << 0);
+}
+
+static void r61517_write_memory_start(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_WRITE_MEMORY_START);
+}
+
+static void r61517_clear_memory(struct r61517 *panel)
+{
+	unsigned int size = panel->pdata->mode->hactive
+			  * panel->pdata->mode->vactive;
+	unsigned int i;
+
+	r61517_write_memory_start(panel);
+
+	for (i = 0; i < size; i++)
+		r61517_write_data(panel, 0);
+}
+
+static void r61517_enable_panel(struct r61517 *panel)
+{
+	/* access protect off */
+	r61517_write8(panel, 0xb0, 0);
+
+	/* exit deep standby mode */
+	r61517_write8(panel, 0xb1, 0);
+
+	/* frame memory I/F */
+	r61517_write_array(panel, 0xb3, data_frame_if);
+
+	/* display mode and frame memory write mode */
+	r61517_write8(panel, 0xb4, 0); /* DBI, internal clock */
+
+	/* panel */
+	r61517_write_array(panel, 0xc0, data_panel);
+
+	/* timing (normal) */
+	r61517_write_array(panel, 0xc1, data_timing);
+
+	/* timing (partial) */
+	r61517_write_array(panel, 0xc2, data_timing);
+
+	/* timing (idle) */
+	r61517_write_array(panel, 0xc3, data_timing);
+
+	/* timing (source/VCOM/gate driving) */
+	r61517_write_array(panel, 0xc4, data_timing_src);
+
+	/* gamma (red) */
+	r61517_write_array(panel, 0xc8, data_gamma);
+
+	/* gamma (green) */
+	r61517_write_array(panel, 0xc9, data_gamma);
+
+	/* gamma (blue) */
+	r61517_write_array(panel, 0xca, data_gamma);
+
+	/* power (common) */
+	r61517_write_array(panel, 0xd0, data_power);
+
+	/* VCOM */
+	r61517_write_array(panel, 0xd1, data_vcom);
+
+	/* power (normal) */
+	r61517_write16(panel, 0xd2, 0x6324);
+
+	/* power (partial) */
+	r61517_write16(panel, 0xd3, 0x6324);
+
+	/* power (idle) */
+	r61517_write16(panel, 0xd4, 0x6324);
+
+	r61517_write16(panel, 0xd8, 0x7777);
+
+	/* TE signal */
+	r61517_write8(panel, MIPI_DCS_SET_TEAR_ON, 0);
+
+	/* TE signal line */
+	r61517_write16(panel, MIPI_DCS_SET_TEAR_SCANLINE, 0);
+
+	/* column address */
+	r61517_write32(panel, MIPI_DCS_SET_COLUMN_ADDRESS,
+		       panel->pdata->mode->hactive - 1);
+
+	/* page address */
+	r61517_write32(panel, MIPI_DCS_SET_PAGE_ADDRESS,
+		       panel->pdata->mode->vactive - 1);
+
+	/* exit sleep mode */
+	r61517_write_command(panel, MIPI_DCS_EXIT_SLEEP_MODE);
+
+	mdelay(120);
+
+	/* clear vram */
+	r61517_clear_memory(panel);
+}
+
+static void r61517_disable_panel(struct r61517 *panel)
+{
+	r61517_reset(panel);
+}
+
+static void r61517_display_on(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_ON);
+	mdelay(1);
+}
+
+static void r61517_display_off(struct r61517 *panel)
+{
+	r61517_write_command(panel, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61517_dbi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DBI,
+	.p.dbi = {
+		.type = MIPI_DBI_INTERFACE_TYPE_B,
+		.flags = MIPI_DBI_INTERFACE_TE,
+		.cs_setup = 1,
+		.wr_setup = 1,
+		.wr_cycle = 9,
+		.wr_hold = 4,
+		.rd_setup = 1,
+		.rd_latch = 20,
+		.rd_cycle = 41,
+		.rd_hold = 20,
+	},
+};
+
+static int r61517_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		r61517_disable_panel(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+			r61517_enable_panel(panel);
+		else
+			r61517_display_off(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+			r61517_enable_panel(panel);
+
+		r61517_display_on(panel);
+		break;
+	}
+
+	return 0;
+}
+
+static int r61517_update(struct display_entity *entity)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	r61517_write_memory_start(panel);
+
+	display_entity_set_stream(entity->source,
+				  DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+	return 0;
+}
+
+static int r61517_get_modes(struct display_entity *entity,
+			    const struct videomode **modes)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	*modes = panel->pdata->mode;
+	return 1;
+}
+
+static int r61517_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int r61517_get_params(struct display_entity *entity,
+			     struct display_entity_interface_params *params)
+{
+	*params = r61517_dbi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops r61517_control_ops = {
+	.set_state = r61517_set_state,
+	.update = r61517_update,
+	.get_modes = r61517_get_modes,
+	.get_size = r61517_get_size,
+	.get_params = r61517_get_params,
+};
+
+static void r61517_release(struct display_entity *entity)
+{
+	struct r61517 *panel = to_panel(entity);
+
+	kfree(panel);
+}
+
+static int r61517_remove(struct mipi_dbi_device *dev)
+{
+	struct r61517 *panel = mipi_dbi_get_drvdata(dev);
+
+	mipi_dbi_set_drvdata(dev, NULL);
+	display_entity_unregister(&panel->entity);
+
+	return 0;
+}
+
+static int __devinit r61517_probe(struct mipi_dbi_device *dev)
+{
+	const struct panel_r61517_platform_data *pdata = dev->dev.platform_data;
+	struct r61517 *panel;
+	int ret;
+
+	if (pdata = NULL)
+		return -ENODEV;
+
+	panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+	if (panel = NULL)
+		return -ENOMEM;
+
+	panel->pdata = pdata;
+	panel->dbi = dev;
+
+	dev->bus_width = pdata->bus_width;
+	mipi_dbi_set_data_width(dev, 8);
+
+	r61517_reset(panel);
+
+	if (r61517_read_device_code(panel) != 0x01221517) {
+		kfree(panel);
+		return -ENODEV;
+	}
+
+	pr_info("R61517 panel controller detected.\n");
+
+	panel->entity.dev = &dev->dev;
+	panel->entity.release = r61517_release;
+	panel->entity.ops.ctrl = &r61517_control_ops;
+
+	ret = display_entity_register(&panel->entity);
+	if (ret < 0) {
+		kfree(panel);
+		return ret;
+	}
+
+	mipi_dbi_set_drvdata(dev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops r61517_dev_pm_ops = {
+};
+
+static struct mipi_dbi_driver r61517_driver = {
+	.probe = r61517_probe,
+	.remove = r61517_remove,
+	.driver = {
+		.name = "panel_r61517",
+		.owner = THIS_MODULE,
+		.pm = &r61517_dev_pm_ops,
+	},
+};
+
+module_mipi_dbi_driver(r61517_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61517-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61517.h b/include/video/panel-r61517.h
new file mode 100644
index 0000000..988f2bc
--- /dev/null
+++ b/include/video/panel-r61517.h
@@ -0,0 +1,28 @@
+/*
+ * Renesas R61517-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PANEL_R61517_H__
+#define __PANEL_R61517_H__
+
+#include <linux/videomode.h>
+
+struct panel_r61517_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+
+	unsigned int bus_width;
+	int protect;			/* Protect GPIO */
+	int reset;			/* Reset GPIO */
+};
+
+#endif /* __PANEL_R61517_H__ */
-- 
1.7.8.6


^ permalink raw reply related

* [RFC v2 4/5] video: panel: Add R61505 panel support
From: Laurent Pinchart @ 2012-11-22 21:45 UTC (permalink / raw)
  To: linux-fbdev, dri-devel
  Cc: linux-media, Archit Taneja, Benjamin Gaignard, Bryan Wu, Inki Dae,
	Jesse Barker, Kyungmin Park, Marcus Lorentzon, Maxime Ripard,
	Philipp Zabel, Ragesh Radhakrishnan, Rob Clark, Sascha Hauer,
	Sebastien Guiriec, Sumit Semwal, Thomas Petazzoni, Tom Gall,
	Tomi Valkeinen, Vikas Sajjan
In-Reply-To: <1353620736-6517-1-git-send-email-laurent.pinchart@ideasonboard.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

The R61505 is a SYS-80 bus panel controller from Renesas.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |    9 +
 drivers/video/display/Makefile       |    1 +
 drivers/video/display/panel-r61505.c |  554 ++++++++++++++++++++++++++++++++++
 include/video/panel-r61505.h         |   27 ++
 4 files changed, 591 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/display/panel-r61505.c
 create mode 100644 include/video/panel-r61505.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index b04c8be..c88999c 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -18,4 +18,13 @@ config DISPLAY_PANEL_DPI
 
 	  If you are in doubt, say N.
 
+config DISPLAY_PANEL_R61505
+	tristate "Renesas R61505-based Display Panel"
+	select DISPLAY_MIPI_DBI
+	---help---
+	  Support panels based on the Renesas R61505 panel controller.
+	  Those panels are controlled through a MIPI DBI interface.
+
+	  If you are in doubt, say N.
+
 endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 00ef1c2..4c68465 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
 obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
+obj-$(CONFIG_DISPLAY_PANEL_R61505) += panel-r61505.o
diff --git a/drivers/video/display/panel-r61505.c b/drivers/video/display/panel-r61505.c
new file mode 100644
index 0000000..d72d324
--- /dev/null
+++ b/drivers/video/display/panel-r61505.c
@@ -0,0 +1,554 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ * Based on SuperH MigoR Quarter VGA LCD Panel
+ * Copyright (C) 2008 Magnus Damm
+ * Based on lcd_powertip.c from Kenati Technologies Pvt Ltd.
+ * Copyright (c) 2007 Ujjwal Pande
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/mipi-dbi-bus.h>
+#include <video/panel-r61505.h>
+
+#define R61505_DEVICE_CODE			0x0000
+#define R61505_DEVICE_CODE_VALUE		0x1505
+#define R61505_DRIVER_OUTPUT_CONTROL		0x0001
+#define R61505_DRIVER_OUTPUT_CONTROL_SM		(1 << 10)
+#define R61505_DRIVER_OUTPUT_CONTROL_SS		(1 << 8)
+#define R61505_LCD_WAVEFORM			0x0002
+#define R61505_LCD_WAVEFORM_BC0			(1 << 9)
+#define R61505_LCD_WAVEFORM_EOR			(1 << 8)
+#define R61505_ENTRY_MODE			0x0003
+#define R61505_ENTRY_MODE_TRIREG		(1 << 15)
+#define R61505_ENTRY_MODE_DFM			(1 << 14)
+#define R61505_ENTRY_MODE_BGR			(1 << 12)
+#define R61505_ENTRY_MODE_HWM			(1 << 9)
+#define R61505_ENTRY_MODE_ORG			(1 << 7)
+#define R61505_ENTRY_MODE_ID1			(1 << 5)
+#define R61505_ENTRY_MODE_ID0			(1 << 4)
+#define R61505_ENTRY_MODE_AM			(1 << 3)
+#define R61505_RESIZE_CONTROL			0x0004
+#define R61505_RESIZE_CONTROL_RCV(n)		(((n) & 3) << 8)
+#define R61505_RESIZE_CONTROL_RCH(n)		(((n) & 3) << 4)
+#define R61505_RESIZE_CONTROL_RSZ_4		(3 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_2		(1 << 0)
+#define R61505_RESIZE_CONTROL_RSZ_1		(0 << 0)
+#define R61505_DISPLAY_CONTROL1			0x0007
+#define R61505_DISPLAY_CONTROL1_PTDE1		(1 << 13)
+#define R61505_DISPLAY_CONTROL1_PTDE0		(1 << 12)
+#define R61505_DISPLAY_CONTROL1_BASEE		(1 << 8)
+#define R61505_DISPLAY_CONTROL1_VON		(1 << 6)
+#define R61505_DISPLAY_CONTROL1_GON		(1 << 5)
+#define R61505_DISPLAY_CONTROL1_DTE		(1 << 4)
+#define R61505_DISPLAY_CONTROL1_COL		(1 << 3)
+#define R61505_DISPLAY_CONTROL1_D1		(1 << 1)
+#define R61505_DISPLAY_CONTROL1_D0		(1 << 0)
+#define R61505_DISPLAY_CONTROL2			0x0008
+#define R61505_DISPLAY_CONTROL2_FP(n)		(((n) & 0xf) << 8)
+#define R61505_DISPLAY_CONTROL2_BP(n)		(((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL3			0x0009
+#define R61505_DISPLAY_CONTROL3_PTS(n)		(((n) & 7) << 8)
+#define R61505_DISPLAY_CONTROL3_PTG(n)		(((n) & 3) << 3)
+#define R61505_DISPLAY_CONTROL3_ICS(n)		(((n) & 0xf) << 0)
+#define R61505_DISPLAY_CONTROL4			0x000a
+#define R61505_DISPLAY_CONTROL4_FMARKOE		(1 << 3)
+#define R61505_DISPLAY_CONTROL4_FMI_6		(5 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_4		(3 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_2		(1 << 0)
+#define R61505_DISPLAY_CONTROL4_FMI_1		(0 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1		0x000c
+#define R61505_EXT_DISPLAY_IF_CONTROL1_ENC(n)	(((n) & 7) << 12)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RM	(1 << 8)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_VSYNC	(2 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_RGB	(1 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK	(0 << 4)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_6	(2 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_16	(1 << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18	(0 << 0)
+#define R61505_FRAME_MARKER_CONTROL		0x000d
+#define R61505_FRAME_MARKER_CONTROL_FMP(n)	(((n) & 0x1ff) << 0)
+#define R61505_EXT_DISPLAY_IF_CONTROL2		0x000f
+#define R61505_POWER_CONTROL1			0x0010
+#define R61505_POWER_CONTROL1_SAP		(1 << 12)
+#define R61505_POWER_CONTROL1_BT(n)		(((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL1_APE		(1 << 7)
+#define R61505_POWER_CONTROL1_AP_100		(3 << 4)
+#define R61505_POWER_CONTROL1_AP_075		(2 << 4)
+#define R61505_POWER_CONTROL1_AP_050		(1 << 4)
+#define R61505_POWER_CONTROL1_AP_HALT		(0 << 4)
+#define R61505_POWER_CONTROL1_DSTB		(1 << 2)
+#define R61505_POWER_CONTROL1_SLP		(1 << 1)
+#define R61505_POWER_CONTROL2			0x0011
+#define R61505_POWER_CONTROL2_DC1_HALT		(6 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_256	(4 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_128	(3 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_64	(2 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_32	(1 << 8)
+#define R61505_POWER_CONTROL2_DC1_FOSC_16	(0 << 8)
+#define R61505_POWER_CONTROL2_DC0_HALT		(6 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_16	(4 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_8	(3 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_4	(2 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC_2	(1 << 4)
+#define R61505_POWER_CONTROL2_DC0_FOSC		(0 << 4)
+#define R61505_POWER_CONTROL2_VC_100		(7 << 0)
+#define R61505_POWER_CONTROL2_VC_076		(4 << 0)
+#define R61505_POWER_CONTROL2_VC_089		(1 << 0)
+#define R61505_POWER_CONTROL2_VC_094		(0 << 0)
+#define R61505_POWER_CONTROL3			0x0012
+#define R61505_POWER_CONTROL3_VCMR		(1 << 8)
+#define R61505_POWER_CONTROL3_PSON		(1 << 5)
+#define R61505_POWER_CONTROL3_PON		(1 << 4)
+#define R61505_POWER_CONTROL3_VRH(n)		(((n) & 0xf) << 0)
+#define R61505_POWER_CONTROL4			0x0013
+#define R61505_POWER_CONTROL4_VDV(n)		(((n) & 0xf) << 8)
+#define R61505_POWER_CONTROL5			0x0015
+#define R61505_POWER_CONTROL5_BLDM		(1 << 12)
+#define R61505_POWER_CONTROL6			0x0017
+#define R61505_POWER_CONTROL6_PSE		(1 << 0)
+#define R61505_RAM_ADDR_HORZ			0x0020
+#define R61505_RAM_ADDR_VERT			0x0021
+#define R61505_RAM_DATA				0x0022
+#define R61505_POWER_CONTROL7			0x0029
+#define R61505_POWER_CONTROL7_VCM1(n)		(((n) & 0x1f) << 0)
+#define R61505_GAMMA_CONTROL1			0x0030
+#define R61505_GAMMA_CONTROL2			0x0031
+#define R61505_GAMMA_CONTROL3			0x0032
+#define R61505_GAMMA_CONTROL4			0x0033
+#define R61505_GAMMA_CONTROL5			0x0034
+#define R61505_GAMMA_CONTROL6			0x0035
+#define R61505_GAMMA_CONTROL7			0x0036
+#define R61505_GAMMA_CONTROL8			0x0037
+#define R61505_GAMMA_CONTROL9			0x0038
+#define R61505_GAMMA_CONTROL10			0x0039
+#define R61505_GAMMA_CONTROL11			0x003a
+#define R61505_GAMMA_CONTROL12			0x003b
+#define R61505_GAMMA_CONTROL13			0x003c
+#define R61505_GAMMA_CONTROL14			0x003d
+#define R61505_WINDOW_HORZ_START		0x0050
+#define R61505_WINDOW_HORZ_END			0x0051
+#define R61505_WINDOW_VERT_START		0x0052
+#define R61505_WINDOW_VERT_END			0x0053
+#define R61505_DRIVER_OUTPUT_CONTROL2		0x0060
+#define R61505_DRIVER_OUTPUT_CONTROL2_GS	(1 << 15)
+#define R61505_DRIVER_OUTPUT_CONTROL2_NL(n)	(((n) & 0x3f) << 8)
+#define R61505_DRIVER_OUTPUT_CONTROL2_SCN(n)	(((n) & 0x3f) << 0)
+#define R61505_BASE_IMG_DISPLAY_CONTROL		0x0061
+#define R61505_BASE_IMG_DISPLAY_CONTROL_NDL	(1 << 2)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_VLE	(1 << 1)
+#define R61505_BASE_IMG_DISPLAY_CONTROL_REV	(1 << 0)
+#define R61505_VERTICAL_SCROLL_CONTROL		0x006a
+#define R61505_PANEL_IF_CONTROL1		0x0090
+#define R61505_PANEL_IF_CONTROL1_DIVI(n)	(((n) & 3) << 8)
+#define R61505_PANEL_IF_CONTROL1_RTNI(n)	(((n) & 0x1f) << 0)
+#define R61505_PANEL_IF_CONTROL2		0x0092
+#define R61505_PANEL_IF_CONTROL2_NOWI(n)	(((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL3		0x0093
+#define R61505_PANEL_IF_CONTROL3_MCP(n)		(((n) & 7) << 8)
+#define R61505_PANEL_IF_CONTROL4		0x0095
+#define R61505_PANEL_IF_CONTROL5		0x0097
+#define R61505_PANEL_IF_CONTROL6		0x0098
+#define R61505_OSCILLATION_CONTROL		0x00a4
+#define R61505_OSCILLATION_CONTROL_CALB		(1 << 0)
+
+struct r61505 {
+	struct display_entity entity;
+	struct mipi_dbi_device *dbi;
+	const struct panel_r61505_platform_data *pdata;
+};
+
+#define to_panel(p)	container_of(p, struct r61505, entity)
+
+/* -----------------------------------------------------------------------------
+ * Read, write and reset
+ */
+
+static void r61505_write(struct r61505 *panel, u16 reg, u16 data)
+{
+	u8 buffer[2] = { data >> 8, data & 0xff };
+
+	mipi_dbi_write_command(panel->dbi, reg);
+	mipi_dbi_write_data(panel->dbi, buffer, 2);
+}
+
+static u16 r61505_read(struct r61505 *panel, u16 reg)
+{
+	u8 buffer[2];
+	int ret;
+
+	mipi_dbi_write_command(panel->dbi, reg);
+	ret = mipi_dbi_read_data(panel->dbi, buffer, 2);
+	if (ret < 0)
+		return ret;
+
+	return (buffer[0] << 8) | buffer[1];
+}
+
+static void r61505_write_array(struct r61505 *panel,
+				 const u16 *data, unsigned int len)
+{
+	unsigned int i;
+
+	for (i = 0; i < len; i += 2)
+		r61505_write(panel, data[i], data[i + 1]);
+}
+
+static void r61505_reset(struct r61505 *panel)
+{
+	if (panel->pdata->reset < 0)
+		return;
+
+	gpio_set_value(panel->pdata->reset, 0);
+	usleep_range(2000, 2500);
+	gpio_set_value(panel->pdata->reset, 1);
+	usleep_range(1000, 1500);
+}
+
+/* -----------------------------------------------------------------------------
+ * Configuration
+ */
+
+static const unsigned short sync_data[] = {
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+	0x0000, 0x0000,
+};
+
+static const unsigned short magic0_data[] = {
+	R61505_DISPLAY_CONTROL2, R61505_DISPLAY_CONTROL2_FP(8) |
+				 R61505_DISPLAY_CONTROL2_BP(8),
+	R61505_PANEL_IF_CONTROL1, R61505_PANEL_IF_CONTROL1_RTNI(26),
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_D0,
+	R61505_POWER_CONTROL6, R61505_POWER_CONTROL6_PSE,
+	0x0019, 0x0000,
+	R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+			       R61505_POWER_CONTROL1_BT(7) |
+			       R61505_POWER_CONTROL1_APE |
+			       R61505_POWER_CONTROL1_AP_100,
+	R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+			       R61505_POWER_CONTROL2_DC0_FOSC_2 | 6,
+	R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+			       R61505_POWER_CONTROL3_PON |
+			       R61505_POWER_CONTROL3_VRH(8),
+	R61505_POWER_CONTROL4, 0x1000 | R61505_POWER_CONTROL4_VDV(4),
+	R61505_POWER_CONTROL7, R61505_POWER_CONTROL7_VCM1(12),
+	R61505_POWER_CONTROL3, R61505_POWER_CONTROL3_VCMR | 0x80 |
+			       R61505_POWER_CONTROL3_PSON |
+			       R61505_POWER_CONTROL3_PON |
+			       R61505_POWER_CONTROL3_VRH(8),
+};
+
+static const unsigned short magic1_data[] = {
+	R61505_GAMMA_CONTROL1, 0x0307,
+	R61505_GAMMA_CONTROL2, 0x0303,
+	R61505_GAMMA_CONTROL3, 0x0603,
+	R61505_GAMMA_CONTROL4, 0x0202,
+	R61505_GAMMA_CONTROL5, 0x0202,
+	R61505_GAMMA_CONTROL6, 0x0202,
+	R61505_GAMMA_CONTROL7, 0x1f1f,
+	R61505_GAMMA_CONTROL8, 0x0303,
+	R61505_GAMMA_CONTROL9, 0x0303,
+	R61505_GAMMA_CONTROL10, 0x0603,
+	R61505_GAMMA_CONTROL11, 0x0202,
+	R61505_GAMMA_CONTROL12, 0x0102,
+	R61505_GAMMA_CONTROL13, 0x0204,
+	R61505_GAMMA_CONTROL14, 0x0000,
+	R61505_DRIVER_OUTPUT_CONTROL, R61505_DRIVER_OUTPUT_CONTROL_SS,
+	R61505_LCD_WAVEFORM, R61505_LCD_WAVEFORM_BC0 |
+			     R61505_LCD_WAVEFORM_EOR,
+	R61505_ENTRY_MODE, R61505_ENTRY_MODE_DFM |
+			   R61505_ENTRY_MODE_BGR |
+			   R61505_ENTRY_MODE_ID1 |
+			   R61505_ENTRY_MODE_AM,
+	R61505_RAM_ADDR_HORZ, 239,
+	R61505_RAM_ADDR_VERT, 0,
+	R61505_RESIZE_CONTROL, R61505_RESIZE_CONTROL_RCV(0) |
+			       R61505_RESIZE_CONTROL_RCH(0) |
+			       R61505_RESIZE_CONTROL_RSZ_1,
+	R61505_DISPLAY_CONTROL3, R61505_DISPLAY_CONTROL3_PTS(0) |
+				 R61505_DISPLAY_CONTROL3_PTG(0) |
+				 R61505_DISPLAY_CONTROL3_ICS(0),
+	R61505_DISPLAY_CONTROL4, R61505_DISPLAY_CONTROL4_FMARKOE |
+				 R61505_DISPLAY_CONTROL4_FMI_1,
+	R61505_EXT_DISPLAY_IF_CONTROL1, R61505_EXT_DISPLAY_IF_CONTROL1_ENC(0) |
+					R61505_EXT_DISPLAY_IF_CONTROL1_DM_ICLK |
+					R61505_EXT_DISPLAY_IF_CONTROL1_RIM_18,
+	R61505_FRAME_MARKER_CONTROL, R61505_FRAME_MARKER_CONTROL_FMP(0),
+	R61505_POWER_CONTROL5, 0x8000,
+};
+
+static const unsigned short magic2_data[] = {
+	R61505_BASE_IMG_DISPLAY_CONTROL, R61505_BASE_IMG_DISPLAY_CONTROL_REV,
+	R61505_PANEL_IF_CONTROL2, R61505_PANEL_IF_CONTROL2_NOWI(1),
+	R61505_PANEL_IF_CONTROL3, R61505_PANEL_IF_CONTROL3_MCP(1),
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_GON |
+				 R61505_DISPLAY_CONTROL1_D0,
+};
+
+static const unsigned short magic3_data[] = {
+	R61505_POWER_CONTROL1, R61505_POWER_CONTROL1_SAP |
+			       R61505_POWER_CONTROL1_BT(6) |
+			       R61505_POWER_CONTROL1_APE |
+			       R61505_POWER_CONTROL1_AP_100,
+	R61505_POWER_CONTROL2, R61505_POWER_CONTROL2_DC1_FOSC_32 |
+			       R61505_POWER_CONTROL2_DC0_FOSC_2 |
+			       R61505_POWER_CONTROL2_VC_089,
+	R61505_DISPLAY_CONTROL1, R61505_DISPLAY_CONTROL1_VON |
+				 R61505_DISPLAY_CONTROL1_GON |
+				 R61505_DISPLAY_CONTROL1_D0,
+};
+
+static void r61505_enable_panel(struct r61505 *panel)
+{
+	unsigned long hactive = panel->pdata->mode->hactive;
+	unsigned long vactive = panel->pdata->mode->vactive;
+	unsigned int i;
+
+	r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+	r61505_write(panel, R61505_OSCILLATION_CONTROL,
+		     R61505_OSCILLATION_CONTROL_CALB);
+	usleep_range(10000, 11000);
+
+	r61505_write(panel, R61505_DRIVER_OUTPUT_CONTROL2,
+		     R61505_DRIVER_OUTPUT_CONTROL2_NL((hactive / 8) - 1));
+	r61505_write_array(panel, magic0_data, ARRAY_SIZE(magic0_data));
+	usleep_range(100000, 101000);
+
+	r61505_write_array(panel, magic1_data, ARRAY_SIZE(magic1_data));
+
+	r61505_write(panel, R61505_WINDOW_HORZ_START, 239 - (vactive - 1));
+	r61505_write(panel, R61505_WINDOW_HORZ_END, 239);
+	r61505_write(panel, R61505_WINDOW_VERT_START, 0);
+	r61505_write(panel, R61505_WINDOW_VERT_END, hactive - 1);
+
+	r61505_write_array(panel, magic2_data, ARRAY_SIZE(magic2_data));
+	usleep_range(10000, 11000);
+
+	r61505_write_array(panel, magic3_data, ARRAY_SIZE(magic3_data));
+	usleep_range(40000, 41000);
+
+	/* Clear GRAM to avoid displaying garbage. */
+	r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+	r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+
+	for (i = 0; i < (hactive * 256); i++) /* yes, 256 words per line */
+		r61505_write(panel, R61505_RAM_DATA, 0);
+
+	r61505_write(panel, R61505_RAM_ADDR_HORZ, 0);
+	r61505_write(panel, R61505_RAM_ADDR_VERT, 0);
+}
+
+static void r61505_disable_panel(struct r61505 *panel)
+{
+	r61505_reset(panel);
+}
+
+static void r61505_display_on(struct r61505 *panel)
+{
+	r61505_write(panel, R61505_DISPLAY_CONTROL1,
+		     R61505_DISPLAY_CONTROL1_BASEE |
+		     R61505_DISPLAY_CONTROL1_VON |
+		     R61505_DISPLAY_CONTROL1_GON |
+		     R61505_DISPLAY_CONTROL1_DTE |
+		     R61505_DISPLAY_CONTROL1_D1 |
+		     R61505_DISPLAY_CONTROL1_D0);
+	usleep_range(40000, 41000);
+}
+
+static void r61505_display_off(struct r61505 *panel)
+{
+	r61505_write(panel, R61505_DISPLAY_CONTROL1,
+		     R61505_DISPLAY_CONTROL1_VON |
+		     R61505_DISPLAY_CONTROL1_GON |
+		     R61505_DISPLAY_CONTROL1_D0);
+}
+
+/* -----------------------------------------------------------------------------
+ * Panel operations
+ */
+
+static const struct display_entity_interface_params r61505_dbi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DBI,
+	.p.dbi = {
+		.type = MIPI_DBI_INTERFACE_TYPE_B,
+		.cs_setup = 1,
+		.wr_setup = 0,
+		.wr_cycle = 10,
+		.wr_hold = 9,
+		.rd_setup = 14,
+		.rd_latch = 24,
+		.rd_cycle = 52,
+		.rd_hold = 24,
+	},
+};
+
+static int r61505_set_state(struct display_entity *entity,
+			    enum display_entity_state state)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+		r61505_disable_panel(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+			r61505_enable_panel(panel);
+		else
+			r61505_display_off(panel);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		if (entity->state = DISPLAY_ENTITY_STATE_OFF)
+			r61505_enable_panel(panel);
+
+		r61505_display_on(panel);
+		break;
+	}
+
+	return 0;
+}
+
+static int r61505_update(struct display_entity *entity)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	mipi_dbi_write_command(panel->dbi, R61505_RAM_DATA);
+	usleep_range(100000, 101000);
+
+	display_entity_set_stream(entity->source,
+				  DISPLAY_ENTITY_STREAM_SINGLE_SHOT);
+	return 0;
+}
+
+static int r61505_get_modes(struct display_entity *entity,
+			    const struct videomode **modes)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	*modes = panel->pdata->mode;
+	return 1;
+}
+
+static int r61505_get_size(struct display_entity *entity,
+			   unsigned int *width, unsigned int *height)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int r61505_get_params(struct display_entity *entity,
+			     struct display_entity_interface_params *params)
+{
+	*params = r61505_dbi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops r61505_control_ops = {
+	.set_state = r61505_set_state,
+	.update = r61505_update,
+	.get_modes = r61505_get_modes,
+	.get_size = r61505_get_size,
+	.get_params = r61505_get_params,
+};
+
+static void r61505_release(struct display_entity *entity)
+{
+	struct r61505 *panel = to_panel(entity);
+
+	kfree(panel);
+}
+
+static int r61505_remove(struct mipi_dbi_device *dev)
+{
+	struct r61505 *panel = mipi_dbi_get_drvdata(dev);
+
+	mipi_dbi_set_drvdata(dev, NULL);
+	display_entity_unregister(&panel->entity);
+
+	return 0;
+}
+
+static int __devinit r61505_probe(struct mipi_dbi_device *dev)
+{
+	const struct panel_r61505_platform_data *pdata = dev->dev.platform_data;
+	struct r61505 *panel;
+	int ret;
+
+	if (pdata = NULL)
+		return -ENODEV;
+
+	panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+	if (panel = NULL)
+		return -ENOMEM;
+
+	panel->pdata = pdata;
+	panel->dbi = dev;
+
+	dev->flags = MIPI_DBI_FLAG_ALIGN_LEFT;
+	dev->bus_width = pdata->bus_width;
+	mipi_dbi_set_data_width(dev, 16);
+
+	r61505_reset(panel);
+	r61505_write_array(panel, sync_data, ARRAY_SIZE(sync_data));
+
+	if (r61505_read(panel, 0) != R61505_DEVICE_CODE_VALUE) {
+		kfree(panel);
+		return -ENODEV;
+	}
+
+	panel->entity.dev = &dev->dev;
+	panel->entity.release = r61505_release;
+	panel->entity.ops.ctrl = &r61505_control_ops;
+
+	ret = display_entity_register(&panel->entity);
+	if (ret < 0) {
+		kfree(panel);
+		return ret;
+	}
+
+	mipi_dbi_set_drvdata(dev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops r61505_dev_pm_ops = {
+};
+
+static struct mipi_dbi_driver r61505_driver = {
+	.probe = r61505_probe,
+	.remove = r61505_remove,
+	.driver = {
+		.name = "panel_r61505",
+		.owner = THIS_MODULE,
+		.pm = &r61505_dev_pm_ops,
+	},
+};
+
+module_mipi_dbi_driver(r61505_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas R61505-based Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-r61505.h b/include/video/panel-r61505.h
new file mode 100644
index 0000000..fe4a368
--- /dev/null
+++ b/include/video/panel-r61505.h
@@ -0,0 +1,27 @@
+/*
+ * Renesas R61505-based Display Panels
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PANEL_R61505_H__
+#define __PANEL_R61505_H__
+
+#include <linux/videomode.h>
+
+struct panel_r61505_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+
+	unsigned int bus_width;
+	int reset;			/* Reset GPIO */
+};
+
+#endif /* __PANEL_R61505_H__ */
-- 
1.7.8.6


^ permalink raw reply related

* [RFC v2 3/5] video: display: Add MIPI DBI bus support
From: Laurent Pinchart @ 2012-11-22 21:45 UTC (permalink / raw)
  To: linux-fbdev, dri-devel
  Cc: linux-media, Archit Taneja, Benjamin Gaignard, Bryan Wu, Inki Dae,
	Jesse Barker, Kyungmin Park, Marcus Lorentzon, Maxime Ripard,
	Philipp Zabel, Ragesh Radhakrishnan, Rob Clark, Sascha Hauer,
	Sebastien Guiriec, Sumit Semwal, Thomas Petazzoni, Tom Gall,
	Tomi Valkeinen, Vikas Sajjan
In-Reply-To: <1353620736-6517-1-git-send-email-laurent.pinchart@ideasonboard.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig        |    4 +
 drivers/video/display/Makefile       |    1 +
 drivers/video/display/mipi-dbi-bus.c |  228 ++++++++++++++++++++++++++++++++++
 include/video/display.h              |    5 +
 include/video/mipi-dbi-bus.h         |  125 +++++++++++++++++++
 5 files changed, 363 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/display/mipi-dbi-bus.c
 create mode 100644 include/video/mipi-dbi-bus.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 0f9b990..b04c8be 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -5,6 +5,10 @@ menuconfig DISPLAY_CORE
 
 if DISPLAY_CORE
 
+config DISPLAY_MIPI_DBI
+	tristate
+	default n
+
 config DISPLAY_PANEL_DPI
 	tristate "DPI (Parallel) Display Panels"
 	---help---
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index 47978d4..00ef1c2 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_MIPI_DBI) += mipi-dbi-bus.o
 obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
diff --git a/drivers/video/display/mipi-dbi-bus.c b/drivers/video/display/mipi-dbi-bus.c
new file mode 100644
index 0000000..bd39a97
--- /dev/null
+++ b/drivers/video/display/mipi-dbi-bus.c
@@ -0,0 +1,228 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+#include <video/mipi-dbi-bus.h>
+
+/* -----------------------------------------------------------------------------
+ * Bus operations
+ */
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width)
+{
+	if (width != 8 && width != 16)
+		return -EINVAL;
+
+	dev->data_width = width;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_set_data_width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd)
+{
+	return dev->bus->ops->write_command(dev->bus, dev, cmd);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_command);
+
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+			size_t len)
+{
+	return dev->bus->ops->write_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_write_data);
+
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len)
+{
+	return dev->bus->ops->read_data(dev->bus, dev, data, len);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_read_data);
+
+/* -----------------------------------------------------------------------------
+ * Bus type
+ */
+
+static const struct mipi_dbi_device_id *
+mipi_dbi_match_id(const struct mipi_dbi_device_id *id,
+		  struct mipi_dbi_device *dev)
+{
+	while (id->name[0]) {
+		if (strcmp(dev->name, id->name) = 0) {
+			dev->id_entry = id;
+			return id;
+		}
+		id++;
+	}
+	return NULL;
+}
+
+static int mipi_dbi_match(struct device *_dev, struct device_driver *_drv)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_drv);
+
+	if (drv->id_table)
+		return mipi_dbi_match_id(drv->id_table, dev) != NULL;
+
+	return (strcmp(dev->name, _drv->name) = 0);
+}
+
+static ssize_t modalias_show(struct device *_dev, struct device_attribute *a,
+			     char *buf)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+	int len = snprintf(buf, PAGE_SIZE, MIPI_DBI_MODULE_PREFIX "%s\n",
+			   dev->name);
+
+	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static struct device_attribute mipi_dbi_dev_attrs[] = {
+	__ATTR_RO(modalias),
+	__ATTR_NULL,
+};
+
+static int mipi_dbi_uevent(struct device *_dev, struct kobj_uevent_env *env)
+{
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+	add_uevent_var(env, "MODALIAS=%s%s", MIPI_DBI_MODULE_PREFIX,
+		       dev->name);
+	return 0;
+}
+
+static const struct dev_pm_ops mipi_dbi_dev_pm_ops = {
+	.runtime_suspend = pm_generic_runtime_suspend,
+	.runtime_resume = pm_generic_runtime_resume,
+	.runtime_idle = pm_generic_runtime_idle,
+	.suspend = pm_generic_suspend,
+	.resume = pm_generic_resume,
+	.freeze = pm_generic_freeze,
+	.thaw = pm_generic_thaw,
+	.poweroff = pm_generic_poweroff,
+	.restore = pm_generic_restore,
+};
+
+static struct bus_type mipi_dbi_bus_type = {
+	.name		= "mipi-dbi",
+	.dev_attrs	= mipi_dbi_dev_attrs,
+	.match		= mipi_dbi_match,
+	.uevent		= mipi_dbi_uevent,
+	.pm		= &mipi_dbi_dev_pm_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device and driver (un)registration
+ */
+
+/**
+ * mipi_dbi_device_register - register a DBI device
+ * @dev: DBI device we're registering
+ */
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+			      struct mipi_dbi_bus *bus)
+{
+	device_initialize(&dev->dev);
+
+	dev->bus = bus;
+	dev->dev.bus = &mipi_dbi_bus_type;
+	dev->dev.parent = bus->dev;
+
+	if (dev->id != -1)
+		dev_set_name(&dev->dev, "%s.%d", dev->name,  dev->id);
+	else
+		dev_set_name(&dev->dev, "%s", dev->name);
+
+	return device_add(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_register);
+
+/**
+ * mipi_dbi_device_unregister - unregister a DBI device
+ * @dev: DBI device we're unregistering
+ */
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev)
+{
+	device_del(&dev->dev);
+	put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_device_unregister);
+
+static int mipi_dbi_drv_probe(struct device *_dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+	return drv->probe(dev);
+}
+
+static int mipi_dbi_drv_remove(struct device *_dev)
+{
+	struct mipi_dbi_driver *drv = to_mipi_dbi_driver(_dev->driver);
+	struct mipi_dbi_device *dev = to_mipi_dbi_device(_dev);
+
+	return drv->remove(dev);
+}
+
+/**
+ * mipi_dbi_driver_register - register a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv)
+{
+	drv->driver.bus = &mipi_dbi_bus_type;
+	if (drv->probe)
+		drv->driver.probe = mipi_dbi_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = mipi_dbi_drv_remove;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_register);
+
+/**
+ * mipi_dbi_driver_unregister - unregister a driver for DBI devices
+ * @drv: DBI driver structure
+ */
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(mipi_dbi_driver_unregister);
+
+/* -----------------------------------------------------------------------------
+ * Init/exit
+ */
+
+static int __init mipi_dbi_init(void)
+{
+	return bus_register(&mipi_dbi_bus_type);
+}
+
+static void __exit mipi_dbi_exit(void)
+{
+	bus_unregister(&mipi_dbi_bus_type);
+}
+
+module_init(mipi_dbi_init);
+module_exit(mipi_dbi_exit)
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("MIPI DBI Bus");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
index 90d18ca..75ba270 100644
--- a/include/video/display.h
+++ b/include/video/display.h
@@ -16,6 +16,7 @@
 #include <linux/kref.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <video/mipi-dbi-bus.h>
 
 /* -----------------------------------------------------------------------------
  * Display Entity
@@ -68,10 +69,14 @@ enum display_entity_stream_state {
 
 enum display_entity_interface_type {
 	DISPLAY_ENTITY_INTERFACE_DPI,
+	DISPLAY_ENTITY_INTERFACE_DBI,
 };
 
 struct display_entity_interface_params {
 	enum display_entity_interface_type type;
+	union {
+		struct mipi_dbi_interface_params dbi;
+	} p;
 };
 
 struct display_entity_control_ops {
diff --git a/include/video/mipi-dbi-bus.h b/include/video/mipi-dbi-bus.h
new file mode 100644
index 0000000..876b69d
--- /dev/null
+++ b/include/video/mipi-dbi-bus.h
@@ -0,0 +1,125 @@
+/*
+ * MIPI DBI Bus
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MIPI_DBI_BUS_H__
+#define __MIPI_DBI_BUS_H__
+
+#include <linux/device.h>
+
+struct mipi_dbi_bus;
+struct mipi_dbi_device;
+
+struct mipi_dbi_bus_ops {
+	int (*write_command)(struct mipi_dbi_bus *bus,
+			     struct mipi_dbi_device *dev, u16 cmd);
+	int (*write_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+			  const u8 *data, size_t len);
+	int (*read_data)(struct mipi_dbi_bus *bus, struct mipi_dbi_device *dev,
+			 u8 *data, size_t len);
+};
+
+struct mipi_dbi_bus {
+	struct device *dev;
+	const struct mipi_dbi_bus_ops *ops;
+};
+
+#define MIPI_DBI_MODULE_PREFIX		"mipi-dbi:"
+#define MIPI_DBI_NAME_SIZE		32
+
+struct mipi_dbi_device_id {
+	char name[MIPI_DBI_NAME_SIZE];
+	__kernel_ulong_t driver_data	/* Data private to the driver */
+			__aligned(sizeof(__kernel_ulong_t));
+};
+
+enum mipi_dbi_interface_type {
+	MIPI_DBI_INTERFACE_TYPE_A,
+	MIPI_DBI_INTERFACE_TYPE_B,
+};
+
+#define MIPI_DBI_INTERFACE_TE		(1 << 0)
+
+struct mipi_dbi_interface_params {
+	enum mipi_dbi_interface_type type;
+	unsigned int flags;
+
+	unsigned int cs_setup;
+	unsigned int rd_setup;
+	unsigned int rd_latch;
+	unsigned int rd_cycle;
+	unsigned int rd_hold;
+	unsigned int wr_setup;
+	unsigned int wr_cycle;
+	unsigned int wr_hold;
+};
+
+#define MIPI_DBI_FLAG_ALIGN_LEFT	(1 << 0)
+
+struct mipi_dbi_device {
+	const char *name;
+	int id;
+	struct device dev;
+
+	const struct mipi_dbi_device_id *id_entry;
+	struct mipi_dbi_bus *bus;
+
+	unsigned int flags;
+	unsigned int bus_width;
+	unsigned int data_width;
+};
+
+#define to_mipi_dbi_device(d)	container_of(d, struct mipi_dbi_device, dev)
+
+int mipi_dbi_device_register(struct mipi_dbi_device *dev,
+			     struct mipi_dbi_bus *bus);
+void mipi_dbi_device_unregister(struct mipi_dbi_device *dev);
+
+struct mipi_dbi_driver {
+	int(*probe)(struct mipi_dbi_device *);
+	int(*remove)(struct mipi_dbi_device *);
+	struct device_driver driver;
+	const struct mipi_dbi_device_id *id_table;
+};
+
+#define to_mipi_dbi_driver(d)	container_of(d, struct mipi_dbi_driver, driver)
+
+int mipi_dbi_driver_register(struct mipi_dbi_driver *drv);
+void mipi_dbi_driver_unregister(struct mipi_dbi_driver *drv);
+
+static inline void *mipi_dbi_get_drvdata(const struct mipi_dbi_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void mipi_dbi_set_drvdata(struct mipi_dbi_device *dev,
+					void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+/* module_mipi_dbi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_mipi_dbi_driver(__mipi_dbi_driver) \
+	module_driver(__mipi_dbi_driver, mipi_dbi_driver_register, \
+			mipi_dbi_driver_unregister)
+
+int mipi_dbi_set_data_width(struct mipi_dbi_device *dev, unsigned int width);
+
+int mipi_dbi_write_command(struct mipi_dbi_device *dev, u16 cmd);
+int mipi_dbi_read_data(struct mipi_dbi_device *dev, u8 *data, size_t len);
+int mipi_dbi_write_data(struct mipi_dbi_device *dev, const u8 *data,
+			size_t len);
+
+#endif /* __MIPI_DBI_BUS__ */
-- 
1.7.8.6


^ permalink raw reply related

* [RFC v2 2/5] video: panel: Add DPI panel support
From: Laurent Pinchart @ 2012-11-22 21:45 UTC (permalink / raw)
  To: linux-fbdev, dri-devel
  Cc: linux-media, Archit Taneja, Benjamin Gaignard, Bryan Wu, Inki Dae,
	Jesse Barker, Kyungmin Park, Marcus Lorentzon, Maxime Ripard,
	Philipp Zabel, Ragesh Radhakrishnan, Rob Clark, Sascha Hauer,
	Sebastien Guiriec, Sumit Semwal, Thomas Petazzoni, Tom Gall,
	Tomi Valkeinen, Vikas Sajjan
In-Reply-To: <1353620736-6517-1-git-send-email-laurent.pinchart@ideasonboard.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/display/Kconfig     |   13 +++
 drivers/video/display/Makefile    |    1 +
 drivers/video/display/panel-dpi.c |  147 +++++++++++++++++++++++++++++++++++++
 include/video/panel-dpi.h         |   24 ++++++
 4 files changed, 185 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/display/panel-dpi.c
 create mode 100644 include/video/panel-dpi.h

diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
index 1d533e7..0f9b990 100644
--- a/drivers/video/display/Kconfig
+++ b/drivers/video/display/Kconfig
@@ -2,3 +2,16 @@ menuconfig DISPLAY_CORE
 	tristate "Display Core"
 	---help---
 	  Support common display framework for graphics devices.
+
+if DISPLAY_CORE
+
+config DISPLAY_PANEL_DPI
+	tristate "DPI (Parallel) Display Panels"
+	---help---
+	  Support for simple digital (parallel) pixel interface panels. Those
+	  panels receive pixel data through a parallel bus and have no control
+	  bus.
+
+	  If you are in doubt, say N.
+
+endif # DISPLAY_CORE
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
index bd93496..47978d4 100644
--- a/drivers/video/display/Makefile
+++ b/drivers/video/display/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_DISPLAY_CORE) += display-core.o
+obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o
diff --git a/drivers/video/display/panel-dpi.c b/drivers/video/display/panel-dpi.c
new file mode 100644
index 0000000..c56197a
--- /dev/null
+++ b/drivers/video/display/panel-dpi.c
@@ -0,0 +1,147 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <video/display.h>
+#include <video/panel-dpi.h>
+
+struct panel_dpi {
+	struct display_entity entity;
+	const struct panel_dpi_platform_data *pdata;
+};
+
+#define to_panel_dpi(p)		container_of(p, struct panel_dpi, entity)
+
+static const struct display_entity_interface_params panel_dpi_params = {
+	.type = DISPLAY_ENTITY_INTERFACE_DPI,
+};
+
+static int panel_dpi_set_state(struct display_entity *entity,
+			       enum display_entity_state state)
+{
+	switch (state) {
+	case DISPLAY_ENTITY_STATE_OFF:
+	case DISPLAY_ENTITY_STATE_STANDBY:
+		display_entity_set_stream(entity->source,
+					  DISPLAY_ENTITY_STREAM_STOPPED);
+		break;
+
+	case DISPLAY_ENTITY_STATE_ON:
+		display_entity_set_stream(entity->source,
+					  DISPLAY_ENTITY_STREAM_CONTINUOUS);
+		break;
+	}
+
+	return 0;
+}
+
+static int panel_dpi_get_modes(struct display_entity *entity,
+			       const struct videomode **modes)
+{
+	struct panel_dpi *panel = to_panel_dpi(entity);
+
+	*modes = panel->pdata->mode;
+	return 1;
+}
+
+static int panel_dpi_get_size(struct display_entity *entity,
+			      unsigned int *width, unsigned int *height)
+{
+	struct panel_dpi *panel = to_panel_dpi(entity);
+
+	*width = panel->pdata->width;
+	*height = panel->pdata->height;
+	return 0;
+}
+
+static int panel_dpi_get_params(struct display_entity *entity,
+				struct display_entity_interface_params *params)
+{
+	*params = panel_dpi_params;
+	return 0;
+}
+
+static const struct display_entity_control_ops panel_dpi_control_ops = {
+	.set_state = panel_dpi_set_state,
+	.get_modes = panel_dpi_get_modes,
+	.get_size = panel_dpi_get_size,
+	.get_params = panel_dpi_get_params,
+};
+
+static void panel_dpi_release(struct display_entity *entity)
+{
+	struct panel_dpi *panel = to_panel_dpi(entity);
+
+	kfree(panel);
+}
+
+static int panel_dpi_remove(struct platform_device *pdev)
+{
+	struct panel_dpi *panel = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+	display_entity_unregister(&panel->entity);
+
+	return 0;
+}
+
+static int __devinit panel_dpi_probe(struct platform_device *pdev)
+{
+	const struct panel_dpi_platform_data *pdata = pdev->dev.platform_data;
+	struct panel_dpi *panel;
+	int ret;
+
+	if (pdata = NULL)
+		return -ENODEV;
+
+	panel = kzalloc(sizeof(*panel), GFP_KERNEL);
+	if (panel = NULL)
+		return -ENOMEM;
+
+	panel->pdata = pdata;
+	panel->entity.dev = &pdev->dev;
+	panel->entity.release = panel_dpi_release;
+	panel->entity.ops.ctrl = &panel_dpi_control_ops;
+
+	ret = display_entity_register(&panel->entity);
+	if (ret < 0) {
+		kfree(panel);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, panel);
+
+	return 0;
+}
+
+static const struct dev_pm_ops panel_dpi_dev_pm_ops = {
+};
+
+static struct platform_driver panel_dpi_driver = {
+	.probe = panel_dpi_probe,
+	.remove = panel_dpi_remove,
+	.driver = {
+		.name = "panel_dpi",
+		.owner = THIS_MODULE,
+		.pm = &panel_dpi_dev_pm_ops,
+	},
+};
+
+module_platform_driver(panel_dpi_driver);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("DPI Display Panel");
+MODULE_LICENSE("GPL");
diff --git a/include/video/panel-dpi.h b/include/video/panel-dpi.h
new file mode 100644
index 0000000..0547b4a
--- /dev/null
+++ b/include/video/panel-dpi.h
@@ -0,0 +1,24 @@
+/*
+ * DPI Display Panel
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PANEL_DPI_H__
+#define __PANEL_DPI_H__
+
+#include <linux/videomode.h>
+
+struct panel_dpi_platform_data {
+	unsigned long width;		/* Panel width in mm */
+	unsigned long height;		/* Panel height in mm */
+	const struct videomode *mode;
+};
+
+#endif /* __PANEL_DPI_H__ */
-- 
1.7.8.6


^ permalink raw reply related

* [RFC v2 1/5] video: Add generic display entity core
From: Laurent Pinchart @ 2012-11-22 21:45 UTC (permalink / raw)
  To: linux-fbdev, dri-devel
  Cc: linux-media, Archit Taneja, Benjamin Gaignard, Bryan Wu, Inki Dae,
	Jesse Barker, Kyungmin Park, Marcus Lorentzon, Maxime Ripard,
	Philipp Zabel, Ragesh Radhakrishnan, Rob Clark, Sascha Hauer,
	Sebastien Guiriec, Sumit Semwal, Thomas Petazzoni, Tom Gall,
	Tomi Valkeinen, Vikas Sajjan
In-Reply-To: <1353620736-6517-1-git-send-email-laurent.pinchart@ideasonboard.com>

From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/video/Kconfig                |    1 +
 drivers/video/Makefile               |    1 +
 drivers/video/display/Kconfig        |    4 +
 drivers/video/display/Makefile       |    1 +
 drivers/video/display/display-core.c |  362 ++++++++++++++++++++++++++++++++++
 include/video/display.h              |  150 ++++++++++++++
 6 files changed, 519 insertions(+), 0 deletions(-)
 create mode 100644 drivers/video/display/Kconfig
 create mode 100644 drivers/video/display/Makefile
 create mode 100644 drivers/video/display/display-core.c
 create mode 100644 include/video/display.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index c5b7bcf..e91f03e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2442,6 +2442,7 @@ source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/backlight/Kconfig"
+source "drivers/video/display/Kconfig"
 
 if VT
 	source "drivers/video/console/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index b936b00..0a4cfea 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -14,6 +14,7 @@ fb-objs                           := $(fb-y)
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-y				  += display/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
diff --git a/drivers/video/display/Kconfig b/drivers/video/display/Kconfig
new file mode 100644
index 0000000..1d533e7
--- /dev/null
+++ b/drivers/video/display/Kconfig
@@ -0,0 +1,4 @@
+menuconfig DISPLAY_CORE
+	tristate "Display Core"
+	---help---
+	  Support common display framework for graphics devices.
diff --git a/drivers/video/display/Makefile b/drivers/video/display/Makefile
new file mode 100644
index 0000000..bd93496
--- /dev/null
+++ b/drivers/video/display/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DISPLAY_CORE) += display-core.o
diff --git a/drivers/video/display/display-core.c b/drivers/video/display/display-core.c
new file mode 100644
index 0000000..358c089
--- /dev/null
+++ b/drivers/video/display/display-core.c
@@ -0,0 +1,362 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/videomode.h>
+
+#include <video/display.h>
+
+static LIST_HEAD(display_entity_list);
+static LIST_HEAD(display_entity_notifiers);
+static DEFINE_MUTEX(display_entity_mutex);
+
+/* -----------------------------------------------------------------------------
+ * Control operations
+ */
+
+/**
+ * display_entity_set_state - Set the display entity operation state
+ * @entity: The display entity
+ * @state: Display entity operation state
+ *
+ * See &enum display_entity_state for information regarding the entity states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state)
+{
+	int ret;
+
+	if (entity->state = state)
+		return 0;
+
+	if (!entity->ops.ctrl || !entity->ops.ctrl->set_state)
+		return 0;
+
+	ret = entity->ops.ctrl->set_state(entity, state);
+	if (ret < 0)
+		return ret;
+
+	entity->state = state;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_set_state);
+
+/**
+ * display_entity_update - Update the display
+ * @entity: The display entity
+ *
+ * Make the display entity ready to receive pixel data and start frame transfer.
+ * This operation can only be called if the display entity is in STANDBY or ON
+ * state.
+ *
+ * The display entity will call the upstream entity in the video chain to start
+ * the video stream.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_update(struct display_entity *entity)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->update)
+		return 0;
+
+	return entity->ops.ctrl->update(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_update);
+
+/**
+ * display_entity_get_modes - Get video modes supported by the display entity
+ * @entity The display entity
+ * @modes: Pointer to an array of modes
+ *
+ * Fill the modes argument with a pointer to an array of video modes. The array
+ * is owned by the display entity.
+ *
+ * Return the number of supported modes on success (including 0 if no mode is
+ * supported) or a negative error code otherwise.
+ */
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+		return 0;
+
+	return entity->ops.ctrl->get_modes(entity, modes);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_modes);
+
+/**
+ * display_entity_get_size - Get display entity physical size
+ * @entity: The display entity
+ * @width: Physical width in millimeters
+ * @height: Physical height in millimeters
+ *
+ * When applicable, for instance for display panels, retrieve the display
+ * physical size in millimeters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_size)
+		return -EOPNOTSUPP;
+
+	return entity->ops.ctrl->get_size(entity, width, height);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_size);
+
+/**
+ * display_entity_get_params - Get display entity interface parameters
+ * @entity: The display entity
+ * @params: Pointer to interface parameters
+ *
+ * Fill the parameters structure pointed to by the params argument with display
+ * entity interface parameters.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params)
+{
+	if (!entity->ops.ctrl || !entity->ops.ctrl->get_modes)
+		return -EOPNOTSUPP;
+
+	return entity->ops.ctrl->get_params(entity, params);
+}
+EXPORT_SYMBOL_GPL(display_entity_get_params);
+
+/* -----------------------------------------------------------------------------
+ * Video operations
+ */
+
+/**
+ * display_entity_set_stream - Control the video stream state
+ * @entity: The display entity
+ * @state: Display video stream state
+ *
+ * Control the video stream state at the entity video output.
+ *
+ * See &enum display_entity_stream_state for information regarding the stream
+ * states.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+int display_entity_set_stream(struct display_entity *entity,
+			      enum display_entity_stream_state state)
+{
+	if (!entity->ops.video || !entity->ops.video->set_stream)
+		return 0;
+
+	return entity->ops.video->set_stream(entity, state);
+}
+EXPORT_SYMBOL_GPL(display_entity_set_stream);
+
+/* -----------------------------------------------------------------------------
+ * Connections
+ */
+
+/**
+ * display_entity_connect - Connect two entities through a video stream
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Set the sink entity source field to the source entity.
+ */
+
+/**
+ * display_entity_disconnect - Disconnect two previously connected entities
+ * @source: The video stream source
+ * @sink: The video stream sink
+ *
+ * Break a connection between two previously connected entities. The source
+ * entity source field is reset to NULL.
+ */
+
+/* -----------------------------------------------------------------------------
+ * Registration and notification
+ */
+
+static void display_entity_release(struct kref *ref)
+{
+	struct display_entity *entity +		container_of(ref, struct display_entity, ref);
+
+	if (entity->release)
+		entity->release(entity);
+}
+
+/**
+ * display_entity_get - get a reference to a display entity
+ * @display_entity: the display entity
+ *
+ * Return the display entity pointer.
+ */
+struct display_entity *display_entity_get(struct display_entity *entity)
+{
+	if (entity = NULL)
+		return NULL;
+
+	kref_get(&entity->ref);
+	return entity;
+}
+EXPORT_SYMBOL_GPL(display_entity_get);
+
+/**
+ * display_entity_put - release a reference to a display entity
+ * @display_entity: the display entity
+ *
+ * Releasing the last reference to a display entity releases the display entity
+ * itself.
+ */
+void display_entity_put(struct display_entity *entity)
+{
+	kref_put(&entity->ref, display_entity_release);
+}
+EXPORT_SYMBOL_GPL(display_entity_put);
+
+static int display_entity_notifier_match(struct display_entity *entity,
+				struct display_entity_notifier *notifier)
+{
+	return notifier->dev = NULL || notifier->dev = entity->dev;
+}
+
+/**
+ * display_entity_register_notifier - register a display entity notifier
+ * @notifier: display entity notifier structure we want to register
+ *
+ * Display entity notifiers are called to notify drivers of display
+ * entity-related events for matching display_entitys.
+ *
+ * Notifiers and display_entitys are matched through the device they correspond
+ * to. If the notifier dev field is equal to the display entity dev field the
+ * notifier will be called when an event is reported. Notifiers with a NULL dev
+ * field act as catch-all and will be called for all display_entitys.
+ *
+ * Supported events are
+ *
+ * - DISPLAY_ENTITY_NOTIFIER_CONNECT reports display entity connection and is
+ *   sent at display entity or notifier registration time
+ * - DISPLAY_ENTITY_NOTIFIER_DISCONNECT reports display entity disconnection and
+ *   is sent at display entity unregistration time
+ *
+ * Registering a notifier sends DISPLAY_ENTITY_NOTIFIER_CONNECT events for all
+ * previously registered display_entitys that match the notifiers.
+ *
+ * Return 0 on success.
+ */
+int display_entity_register_notifier(struct display_entity_notifier *notifier)
+{
+	struct display_entity *entity;
+
+	mutex_lock(&display_entity_mutex);
+	list_add_tail(&notifier->list, &display_entity_notifiers);
+
+	list_for_each_entry(entity, &display_entity_list, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		if (notifier->notify(notifier, entity,
+				     DISPLAY_ENTITY_NOTIFIER_CONNECT))
+			break;
+	}
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(display_entity_register_notifier);
+
+/**
+ * display_entity_unregister_notifier - unregister a display entity notifier
+ * @notifier: display entity notifier to be unregistered
+ *
+ * Unregistration guarantees that the notifier will never be called upon return
+ * of this function.
+ */
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier)
+{
+	mutex_lock(&display_entity_mutex);
+	list_del(&notifier->list);
+	mutex_unlock(&display_entity_mutex);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister_notifier);
+
+/**
+ * display_entity_register - register a display entity
+ * @display_entity: display entity to be registered
+ *
+ * Register the display entity and send the DISPLAY_ENTITY_NOTIFIER_CONNECT
+ * event to all matching registered notifiers.
+ *
+ * Return 0 on success.
+ */
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner)
+{
+	struct display_entity_notifier *notifier;
+
+	kref_init(&entity->ref);
+	entity->owner = owner;
+	entity->state = DISPLAY_ENTITY_STATE_OFF;
+
+	mutex_lock(&display_entity_mutex);
+	list_add(&entity->list, &display_entity_list);
+
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		if (notifier->notify(notifier, entity,
+				     DISPLAY_ENTITY_NOTIFIER_CONNECT))
+			break;
+	}
+	mutex_unlock(&display_entity_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__display_entity_register);
+
+/**
+ * display_entity_unregister - unregister a display entity
+ * @display_entity: display entity to be unregistered
+ *
+ * Unregister the display entity and send the DISPLAY_ENTITY_NOTIFIER_DISCONNECT
+ * event to all matching registered notifiers.
+ */
+void display_entity_unregister(struct display_entity *entity)
+{
+	struct display_entity_notifier *notifier;
+
+	mutex_lock(&display_entity_mutex);
+	list_for_each_entry(notifier, &display_entity_notifiers, list) {
+		if (!display_entity_notifier_match(entity, notifier))
+			continue;
+
+		notifier->notify(notifier, entity,
+				 DISPLAY_ENTITY_NOTIFIER_DISCONNECT);
+	}
+
+	list_del(&entity->list);
+	mutex_unlock(&display_entity_mutex);
+
+	display_entity_put(entity);
+}
+EXPORT_SYMBOL_GPL(display_entity_unregister);
+
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Display Core");
+MODULE_LICENSE("GPL");
diff --git a/include/video/display.h b/include/video/display.h
new file mode 100644
index 0000000..90d18ca
--- /dev/null
+++ b/include/video/display.h
@@ -0,0 +1,150 @@
+/*
+ * Display Core
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __DISPLAY_H__
+#define __DISPLAY_H__
+
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/module.h>
+
+/* -----------------------------------------------------------------------------
+ * Display Entity
+ */
+
+struct display_entity;
+struct videomode;
+
+#define DISPLAY_ENTITY_NOTIFIER_CONNECT		1
+#define DISPLAY_ENTITY_NOTIFIER_DISCONNECT	2
+
+struct display_entity_notifier {
+	int (*notify)(struct display_entity_notifier *, struct display_entity *,
+		      int);
+	struct device *dev;
+	struct list_head list;
+};
+
+/**
+ * enum display_entity_state - State of a display entity
+ * @DISPLAY_ENTITY_STATE_OFF: The entity is turned off completely, possibly
+ *	including its power supplies. Communication with a display entity in
+ *	that state is not possible.
+ * @DISPLAY_ENTITY_STATE_STANDBY: The entity is in a low-power state. Full
+ *	communication with the display entity is supported, including pixel data
+ *	transfer, but the output is kept blanked.
+ * @DISPLAY_ENTITY_STATE_ON: The entity is fully operational.
+ */
+enum display_entity_state {
+	DISPLAY_ENTITY_STATE_OFF,
+	DISPLAY_ENTITY_STATE_STANDBY,
+	DISPLAY_ENTITY_STATE_ON,
+};
+
+/**
+ * enum display_entity_stream_state - State of a video stream
+ * @DISPLAY_ENTITY_STREAM_STOPPED: The video stream is stopped, no frames are
+ *	transferred.
+ * @DISPLAY_ENTITY_STREAM_SINGLE_SHOT: The video stream has been started for
+ *      single shot operation. The source entity will transfer a single frame
+ *      and then stop.
+ * @DISPLAY_ENTITY_STREAM_CONTINUOUS: The video stream is running, frames are
+ *	transferred continuously by the source entity.
+ */
+enum display_entity_stream_state {
+	DISPLAY_ENTITY_STREAM_STOPPED,
+	DISPLAY_ENTITY_STREAM_SINGLE_SHOT,
+	DISPLAY_ENTITY_STREAM_CONTINUOUS,
+};
+
+enum display_entity_interface_type {
+	DISPLAY_ENTITY_INTERFACE_DPI,
+};
+
+struct display_entity_interface_params {
+	enum display_entity_interface_type type;
+};
+
+struct display_entity_control_ops {
+	int (*set_state)(struct display_entity *ent,
+			 enum display_entity_state state);
+	int (*update)(struct display_entity *ent);
+	int (*get_modes)(struct display_entity *ent,
+			 const struct videomode **modes);
+	int (*get_params)(struct display_entity *ent,
+			  struct display_entity_interface_params *params);
+	int (*get_size)(struct display_entity *ent,
+			unsigned int *width, unsigned int *height);
+};
+
+struct display_entity_video_ops {
+	int (*set_stream)(struct display_entity *ent,
+			  enum display_entity_stream_state state);
+};
+
+struct display_entity {
+	struct list_head list;
+	struct device *dev;
+	struct module *owner;
+	struct kref ref;
+
+	struct display_entity *source;
+
+	struct {
+		const struct display_entity_control_ops *ctrl;
+		const struct display_entity_video_ops *video;
+	} ops;
+
+	void(*release)(struct display_entity *ent);
+
+	enum display_entity_state state;
+};
+
+int display_entity_set_state(struct display_entity *entity,
+			     enum display_entity_state state);
+int display_entity_update(struct display_entity *entity);
+int display_entity_get_modes(struct display_entity *entity,
+			     const struct videomode **modes);
+int display_entity_get_params(struct display_entity *entity,
+			      struct display_entity_interface_params *params);
+int display_entity_get_size(struct display_entity *entity,
+			    unsigned int *width, unsigned int *height);
+
+int display_entity_set_stream(struct display_entity *entity,
+			      enum display_entity_stream_state state);
+
+static inline void display_entity_connect(struct display_entity *source,
+					  struct display_entity *sink)
+{
+	sink->source = source;
+}
+
+static inline void display_entity_disconnect(struct display_entity *source,
+					     struct display_entity *sink)
+{
+	sink->source = NULL;
+}
+
+struct display_entity *display_entity_get(struct display_entity *entity);
+void display_entity_put(struct display_entity *entity);
+
+int __must_check __display_entity_register(struct display_entity *entity,
+					   struct module *owner);
+void display_entity_unregister(struct display_entity *entity);
+
+int display_entity_register_notifier(struct display_entity_notifier *notifier);
+void display_entity_unregister_notifier(struct display_entity_notifier *notifier);
+
+#define display_entity_register(display_entity) \
+	__display_entity_register(display_entity, THIS_MODULE)
+
+#endif /* __DISPLAY_H__ */
-- 
1.7.8.6


^ permalink raw reply related


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