[PATCH v4 3/3] drm/tiny: Add MIPI DBI compatible SPI driver
Sam Ravnborg
sam at ravnborg.org
Sun Feb 20 19:57:06 UTC 2022
Hi Noralf.
On Sun, Feb 20, 2022 at 07:11:14PM +0100, Noralf Trønnes wrote:
> > Den 20.02.2022 11.04, skrev Sam Ravnborg:
> > > Hi Noralf,
> > >
> > >>> +static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev,
> struct drm_display_mode *mode)
> > >>> +{
> > >>> + struct device *dev = dbidev->drm.dev;
> > >>> + u32 width_mm = 0, height_mm = 0;
> > >>> + struct display_timing timing;
> > >>> + struct videomode vm;
> > >>> + int ret;
> > >>> +
> > >>> + ret = of_get_display_timing(dev->of_node, "panel-timing", &timing);
> > >>> + if (ret) {
> > >>> + dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n",
> dev->of_node, ret);
> > >>> + return ret;
> > >>> + }
> > >>> +
> > >>> + videomode_from_timing(&timing, &vm);
> > >>> +
> > >>> + if (!vm.hactive || vm.hfront_porch || vm.hsync_len ||
> > >>> + (vm.hback_porch + vm.hactive) > 0xffff ||
> > >>> + !vm.vactive || vm.vfront_porch || vm.vsync_len ||
> > >>> + (vm.vback_porch + vm.vactive) > 0xffff ||
> > >>> + vm.flags) {
> > >>> + dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node);
> > >>> + return -EINVAL;
> > >>> + }
> > >> We should have a helper that implements this. Maybe the display_timing
> > >> => display_mode helper could do it.
> > >
> > > It would be nice with a drm_display_timing_to_mode() but that can come
> > > later - the comment above should not be understood that I consider it
> > > mandatory for this driver.
> > >
> >
> > I did consider adding an of_get_drm_panel_mode() fashioned after
> > of_get_drm_display_mode() but I didn't find any other driver that would
> > actually be able to use it and I would have to do some substraction to
> > get back the {h,v}front_porch values that I need and the optional pixel
> > clock calculation becomes more complex acting from a drm_display_mode so
> > I decided against it.
> >
> > Looking at it now, what I could do is add a function like what
> > of_get_videomode() does for "display-timings":
> >
> > /**
> > * of_get_panel_videomode - get the panel-timing videomode from devicetree
> > * @np: devicenode containing the panel-timing subnode
> > * @vm: returns the videomode
> > *
> > * Returns:
> > * Zero on success, negative error code on failure.
> > **/
> > int of_get_panel_videomode(struct device_node *np, struct videomode *vm)
> > {
> > struct display_timing timing;
> > int ret;
> >
> > ret = of_get_display_timing(np, "panel-timing", &timing);
> > if (ret)
> > return ret;
> >
> > videomode_from_timing(&timing, vm);
> >
> > return 0;
> > }
> >
> > This could also be used by panel-lvds and 2 fbdev drivers, the other
> > panel-timing users need/use the display_timing itself, some for bounds
> > checking.
>
> Scratch that, since videomode is to be avoided I tried adding a
> drm_display_mode function and it didn't complicate matter as I though it
> would so I'll do that instead:
I like, but would like to get rid of video_mode entirely.
>
> static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct
> drm_display_mode *mode)
> {
> struct device *dev = dbidev->drm.dev;
> u32 width_mm = 0, height_mm = 0;
> u16 hback_porch, vback_porch;
> struct videomode vm;
> int ret;
>
> ret = of_get_drm_panel_display_mode(dev->of_node, mode, NULL);
> if (ret) {
> dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n",
> dev->of_node, ret);
> return ret;
> }
>
> mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
>
> hback_porch = mode->htotal - mode->hsync_end;
> vback_porch = mode->vtotal - mode->vsync_end;
The accesss functions I posed below could be used here - so we avoid
typing these (trivial) calculations in many places.
>
> if (mode->hsync_end > mode->hdisplay || (hback_porch + mode->hdisplay)
> > 0xffff ||
> mode->vsync_end > mode->vdisplay || (vback_porch + mode->vdisplay)
> > 0xffff ||
> mode->flags) {
> dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node);
> return -EINVAL;
> }
With the display_timing => drm_display_mode I think the above is no
longer required.
>
> /* The driver doesn't use the pixel clock but it is mandatory so fake
> one if not set */
> if (!mode->pixelclock)
> mode->pixelclock = mode->htotal * mode->vtotal * 60 / 1000;
>
> dbidev->top_offset = vback_porch;
> dbidev->left_offset = hback_porch;
>
> return 0;
> }
>
>
> int of_get_drm_panel_display_mode(struct device_node *np,
> struct drm_display_mode *dmode, u32 *bus_flags)
> {
Not sure about the bus_flags argument here - seems misplaced.
> u32 width_mm = 0, height_mm = 0;
> struct display_timing timing;
> struct videomode vm;
> int ret;
>
> ret = of_get_display_timing(np, "panel-timing", &timing);
> if (ret)
> return ret;
>
> videomode_from_timing(&timing, vm);
>
> memset(dmode, 0, sizeof(*dmode));
> drm_display_mode_from_videomode(&vm, dmode);
> if (bus_flags)
> drm_bus_flags_from_videomode(&vm, bus_flags);
This does a:
display_timing => video_mode => drm_display_display_mode
We could do a:
display_timing => drm_display_mode.
Sample implementation could look like this:
void drm_mode_from_display_timing(struct drm_display_mode *mode,
const struct display_timing *dt)
{
mode->hdisplay = dt->hactive.typ;
mode->hsync_start = dt->hactive.typ + dt->hfront_porch.typ;
mode->hsync_end = dt->hactive.typ + dt->hfront_porch.typ + dt->hsync_len.typ;
mode->htotal = dt->hactive.typ + dt->hfront_porch.typ + dt->hsync_len.typ + dt->hback_porch.typ;
mode->vdisplay = dt->vactive.typ;
mode->vsync_start = dt->vactive.typ + dt->vfront_porch.typ;
mode->vsync_end = dt->vactive.typ + dt->vfront_porch.typ + dt->vsync_len.typ;
mode->vtotal = dt->vactive.typ + dt->vfront_porch.typ + dt->vsync_len.typ + dt->vback_porch.typ;
mode->clock = dt->pixelclock.typ / 1000;
mode->flags = 0;
if (dt->flags & DISPLAY_FLAGS_HSYNC_HIGH)
mode->flags |= DRM_MODE_FLAG_PHSYNC;
else if (dt->flags & DISPLAY_FLAGS_HSYNC_LOW)
mode->flags |= DRM_MODE_FLAG_NHSYNC;
if (dt->flags & DISPLAY_FLAGS_VSYNC_HIGH)
mode->flags |= DRM_MODE_FLAG_PVSYNC;
else if (dt->flags & DISPLAY_FLAGS_VSYNC_LOW)
mode->flags |= DRM_MODE_FLAG_NVSYNC;
if (dt->flags & DISPLAY_FLAGS_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
if (dt->flags & DISPLAY_FLAGS_DOUBLESCAN)
mode->flags |= DRM_MODE_FLAG_DBLSCAN;
if (dt->flags & DISPLAY_FLAGS_DOUBLECLK)
mode->flags |= DRM_MODE_FLAG_DBLCLK;
drm_mode_set_name(mode);
}
EXPORT_SYMBOL_GPL(drm_mode_from_display_timing);
If we then on top of this would like easy access to the names we know we
could add a few access functions:
static inline u32 drm_mode_hactive(const struct drm_display_mode *mode)
{
mode->hdisplay;
}
static inline u32 drm_mode_hfront_porch(const struct drm_display_mode *mode)
{
mode->hsync_start - mode->hdisplay;
}
static inline u32 drm_mode_hback_porch(const struct drm_display_mode *mode)
{
mode->htotal - mode->hsync_start;
}
static inline u32 drm_mode_hsync_len(const struct drm_display_mode *mode)
{
return mode->hsync_end - mode->hsync_start;
}
static inline u32 drm_mode_vactive(const struct drm_display_mode *mode)
{
return mode->vdisplay;
}
static inline u32 drm_mode_vfront_porch(const struct drm_display_mode *mode)
{
return mode->vsync_start - mode->vdisplay;
}
static inline u32 drm_mode_vsync_len(const struct drm_display_mode *mode)
{
return mode->vsync_end - mode->vsync_start;
}
static inline u32 drm_mode_vback_porch(const struct drm_display_mode *mode)
{
return mode->vtotal - mode->vsync_end;
}
The above was something I just quickly typed. This was the easy part.
Writing kernel-.doc and fix it so it works is the time consuming part..
Sam
More information about the dri-devel
mailing list