On Sun, Aug 13, 2017 at 05:11:30PM +0200, Linus Walleij wrote:
> This adds a new DRM driver for the Faraday Technology TVE200
> block. This "TV Encoder" encodes a ITU-T BT.656 stream and can
> be found in the StorLink SL3516 (later Cortina Systems CS3516)
> as well as the Grain Media GM8180.
> 
> I do not have definitive word from anyone at Faraday that this
> IP block is theirs, but it bears the hallmark of their 3-digit
> version code (200) and is used in two SoCs from completely
> different companies. (Grain Media was fully owned by Faraday
> until it was transferred to NovoTek this january, and
> Faraday did lots of work on the StorLink SoCs.)
> 
> The D-Link DIR-685 uses this in connection with the Ilitek
> ILI9322 panel driver that supports BT.656 input, while the
> GM8180 apparently has been used with the Cirrus Logic CS4954
> digital video encoder. The oldest user seems to be
> something called Techwall 2835.
> 
> This driver is heavily inspired by Eric Anholt's PL111
> driver and therefore I have mentioned all the ancestor authors
> in the header file.
> 
> Signed-off-by: Linus Walleij <linus.wall...@linaro.org>
> ---
>  Documentation/gpu/index.rst               |   1 +
>  Documentation/gpu/tve200.rst              |   6 +
>  MAINTAINERS                               |   6 +
>  drivers/gpu/drm/Kconfig                   |   2 +
>  drivers/gpu/drm/Makefile                  |   1 +
>  drivers/gpu/drm/tve200/Kconfig            |  15 ++
>  drivers/gpu/drm/tve200/Makefile           |   5 +
>  drivers/gpu/drm/tve200/tve200_connector.c | 126 +++++++++++
>  drivers/gpu/drm/tve200/tve200_display.c   | 346 
> ++++++++++++++++++++++++++++++
>  drivers/gpu/drm/tve200/tve200_drm.h       | 129 +++++++++++
>  drivers/gpu/drm/tve200/tve200_drv.c       | 277 ++++++++++++++++++++++++
>  11 files changed, 914 insertions(+)
>  create mode 100644 Documentation/gpu/tve200.rst
>  create mode 100644 drivers/gpu/drm/tve200/Kconfig
>  create mode 100644 drivers/gpu/drm/tve200/Makefile
>  create mode 100644 drivers/gpu/drm/tve200/tve200_connector.c
>  create mode 100644 drivers/gpu/drm/tve200/tve200_display.c
>  create mode 100644 drivers/gpu/drm/tve200/tve200_drm.h
>  create mode 100644 drivers/gpu/drm/tve200/tve200_drv.c
> 
> diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst
> index 35d673bf9b56..c36586dad29d 100644
> --- a/Documentation/gpu/index.rst
> +++ b/Documentation/gpu/index.rst
> @@ -15,6 +15,7 @@ Linux GPU Driver Developer's Guide
>     pl111
>     tegra
>     tinydrm
> +   tve200
>     vc4
>     vga-switcheroo
>     vgaarbiter
> diff --git a/Documentation/gpu/tve200.rst b/Documentation/gpu/tve200.rst
> new file mode 100644
> index 000000000000..69b17b324e12
> --- /dev/null
> +++ b/Documentation/gpu/tve200.rst
> @@ -0,0 +1,6 @@
> +==================================
> + drm/tve200 Faraday TV Encoder 200
> +==================================
> +
> +.. kernel-doc:: drivers/gpu/drm/tve200/tve200_drv.c
> +   :doc: Faraday TV Encoder 200
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e87cba115ea4..c3d42d68253a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4305,6 +4305,12 @@ T:     git git://anongit.freedesktop.org/drm/drm-misc
>  S:   Maintained
>  F:   drivers/gpu/drm/bochs/
>  
> +DRM DRIVER FOR FARADAY TVE200 TV ENCODER
> +M:   Linus Walleij <linus.wall...@linaro.org>
> +T:   git git://anongit.freedesktop.org/drm/drm-misc
> +S:   Maintained
> +F:   drivers/gpu/drm/tve200/
> +
>  DRM DRIVER FOR INTEL I810 VIDEO CARDS
>  S:   Orphan / Obsolete
>  F:   drivers/gpu/drm/i810/
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 83cb2a88c204..c5e1a8409285 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -278,6 +278,8 @@ source "drivers/gpu/drm/tinydrm/Kconfig"
>  
>  source "drivers/gpu/drm/pl111/Kconfig"
>  
> +source "drivers/gpu/drm/tve200/Kconfig"
> +
>  # Keep legacy drivers last
>  
>  menuconfig DRM_LEGACY
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 24a066e1841c..cc81813e2238 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -100,3 +100,4 @@ obj-$(CONFIG_DRM_ZTE)     += zte/
>  obj-$(CONFIG_DRM_MXSFB)      += mxsfb/
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
>  obj-$(CONFIG_DRM_PL111) += pl111/
> +obj-$(CONFIG_DRM_TVE200) += tve200/
> diff --git a/drivers/gpu/drm/tve200/Kconfig b/drivers/gpu/drm/tve200/Kconfig
> new file mode 100644
> index 000000000000..21d9841ddb88
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/Kconfig
> @@ -0,0 +1,15 @@
> +config DRM_TVE200
> +     tristate "DRM Support for Faraday TV Encoder TVE200"
> +     depends on DRM
> +     depends on CMA
> +     depends on ARM || COMPILE_TEST
> +     depends on OF
> +     select DRM_PANEL
> +     select DRM_KMS_HELPER
> +     select DRM_KMS_CMA_HELPER
> +     select DRM_GEM_CMA_HELPER
> +     select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
> +     help
> +       Choose this option for DRM support for the Faraday TV Encoder
> +       TVE200 Controller.
> +       If M is selected the module will be called tve200_drm.
> diff --git a/drivers/gpu/drm/tve200/Makefile b/drivers/gpu/drm/tve200/Makefile
> new file mode 100644
> index 000000000000..a9dba54f7ee5
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/Makefile
> @@ -0,0 +1,5 @@
> +tve200_drm-y +=      tve200_connector.o \
> +             tve200_display.o \
> +             tve200_drv.o
> +
> +obj-$(CONFIG_DRM_TVE200) += tve200_drm.o
> diff --git a/drivers/gpu/drm/tve200/tve200_connector.c 
> b/drivers/gpu/drm/tve200/tve200_connector.c
> new file mode 100644
> index 000000000000..93e99156d375
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/tve200_connector.c
> @@ -0,0 +1,126 @@
> +/*
> + * Copyright (C) 2017 Linus Walleij <linus.wall...@linaro.org>
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (C) 2006-2008 Intel Corporation
> + * Copyright (C) 2007 Amos Lee <amos_...@storlinksemi.com>
> + * Copyright (C) 2007 Dave Airlie <airl...@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + * Copyright (C) 2017 Eric Anholt
> + *
> + * This program is free software and is provided to you under the terms of 
> the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + */
> +
> +/**
> + * tve200_drm_connector.c
> + * Implementation of the connector functions for the Faraday TV Encoder
> + */
> +#include <linux/version.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/dma-buf.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +
> +#include "tve200_drm.h"
> +
> +static void tve200_connector_destroy(struct drm_connector *connector)
> +{
> +     struct tve200_drm_connector *tve200con =
> +             to_tve200_connector(connector);
> +
> +     if (tve200con->panel)
> +             drm_panel_detach(tve200con->panel);
> +
> +     drm_connector_unregister(connector);
> +     drm_connector_cleanup(connector);
> +}
> +
> +static enum drm_connector_status tve200_connector_detect(struct drm_connector
> +                                                     *connector, bool force)
> +{
> +     struct tve200_drm_connector *tve200con =
> +             to_tve200_connector(connector);
> +
> +     return (tve200con->panel ?
> +             connector_status_connected :
> +             connector_status_disconnected);
> +}
> +
> +static int tve200_connector_helper_get_modes(struct drm_connector *connector)
> +{
> +     struct tve200_drm_connector *tve200con =
> +             to_tve200_connector(connector);
> +
> +     if (!tve200con->panel)
> +             return 0;
> +
> +     return drm_panel_get_modes(tve200con->panel);
> +}
> +
> +static const struct drm_connector_funcs connector_funcs = {
> +     .fill_modes = drm_helper_probe_single_connector_modes,
> +     .destroy = tve200_connector_destroy,
> +     .detect = tve200_connector_detect,
> +     .dpms = drm_atomic_helper_connector_dpms,
> +     .reset = drm_atomic_helper_connector_reset,
> +     .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +     .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static const struct drm_connector_helper_funcs connector_helper_funcs = {
> +     .get_modes = tve200_connector_helper_get_modes,
> +};

The new "wrap panel as a bridge endpoint" helper we have in
drm/bridge/panel.c is meant to remove all the need for the above
boilerplate. Even comes with devm_ built-in :-)

Please check it out.

You're using the simple helpers, but I think they should fully mesh with
the bridge stuff.

Otherwise looks all neat and tidy.

Acked-by: Daniel Vetter <daniel.vet...@ffwll.ch>

Wrt merging/maintaining: Want to include it in the drm-misc pile? We'll
happily throw commit rights at every driver submission (and honestly
expect that, since it helps tremendously with balance maintainer loads for
the oddball trivial patch).

Thanks, Daniel

> +
> +/*
> + * Walks the OF graph to find the panel node and then asks DRM to look
> + * up the panel.
> + */
> +static struct drm_panel *tve200_get_panel(struct device *dev)
> +{
> +     struct device_node *endpoint, *panel_node;
> +     struct device_node *np = dev->of_node;
> +     struct drm_panel *panel;
> +
> +     endpoint = of_graph_get_next_endpoint(np, NULL);
> +     if (!endpoint) {
> +             dev_err(dev, "no endpoint to fetch panel\n");
> +             return NULL;
> +     }
> +
> +     /* Don't proceed if we have an endpoint but no panel_node tied to it */
> +     panel_node = of_graph_get_remote_port_parent(endpoint);
> +     of_node_put(endpoint);
> +     if (!panel_node) {
> +             dev_err(dev, "no valid panel node\n");
> +             return NULL;
> +     }
> +
> +     panel = of_drm_find_panel(panel_node);
> +     of_node_put(panel_node);
> +
> +     return panel;
> +}
> +
> +int tve200_connector_init(struct drm_device *dev)
> +{
> +     struct tve200_drm_dev_private *priv = dev->dev_private;
> +     struct tve200_drm_connector *tve200con = &priv->connector;
> +     struct drm_connector *connector = &tve200con->connector;
> +
> +     drm_connector_init(dev, connector, &connector_funcs,
> +                        DRM_MODE_CONNECTOR_DPI);
> +     drm_connector_helper_add(connector, &connector_helper_funcs);
> +
> +     tve200con->panel = tve200_get_panel(dev->dev);
> +     if (tve200con->panel)
> +             drm_panel_attach(tve200con->panel, connector);
> +
> +     return 0;
> +}
> diff --git a/drivers/gpu/drm/tve200/tve200_display.c 
> b/drivers/gpu/drm/tve200/tve200_display.c
> new file mode 100644
> index 000000000000..027553aacb33
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/tve200_display.c
> @@ -0,0 +1,346 @@
> +/*
> + * Copyright (C) 2017 Linus Walleij <linus.wall...@linaro.org>
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (C) 2006-2008 Intel Corporation
> + * Copyright (C) 2007 Amos Lee <amos_...@storlinksemi.com>
> + * Copyright (C) 2007 Dave Airlie <airl...@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + * Copyright (C) 2017 Eric Anholt
> + *
> + * This program is free software and is provided to you under the terms of 
> the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + */
> +#include <linux/clk.h>
> +#include <linux/version.h>
> +#include <linux/dma-buf.h>
> +#include <linux/of_graph.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +
> +#include "tve200_drm.h"
> +
> +irqreturn_t tve200_irq(int irq, void *data)
> +{
> +     struct tve200_drm_dev_private *priv = data;
> +     u32 stat;
> +     u32 val;
> +
> +     stat = readl(priv->regs + TVE200_INT_STAT);
> +
> +     if (!stat)
> +             return IRQ_NONE;
> +
> +     /*
> +      * Vblank IRQ
> +      *
> +      * The hardware is a bit tilted: the line stays high after clearing
> +      * the vblank IRQ, fireing many more interrupts. We counter this
> +      * by toggling the IRQ back and forth from fireing at vblank and
> +      * fireing at start of active image, which works around the problem
> +      * since those occur strictly in sequence, and we get two IRQs for each
> +      * frame, one at start of Vblank (that we make call into the CRTC) and
> +      * another one at the start of the image (that we discard).
> +      */
> +     if (stat & TVE200_INT_V_STATUS) {
> +             val = readl(priv->regs + TVE200_CTRL);
> +             /* We have an actual start of vsync */
> +             if (!(val & TVE200_VSTSTYPE_BITS)) {
> +                     drm_crtc_handle_vblank(&priv->pipe.crtc);
> +                     /* Toggle trigger to start of active image */
> +                     val |= TVE200_VSTSTYPE_VAI;
> +             } else {
> +                     /* Toggle trigger back to start of vsync */
> +                     val &= ~TVE200_VSTSTYPE_BITS;
> +             }
> +             writel(val, priv->regs + TVE200_CTRL);
> +     } else
> +             dev_err(priv->drm->dev, "stray IRQ %08x\n", stat);
> +
> +     /* Clear the interrupt once done */
> +     writel(stat, priv->regs + TVE200_INT_CLR);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static int tve200_display_check(struct drm_simple_display_pipe *pipe,
> +                            struct drm_plane_state *pstate,
> +                            struct drm_crtc_state *cstate)
> +{
> +     const struct drm_display_mode *mode = &cstate->mode;
> +     struct drm_framebuffer *old_fb = pipe->plane.state->fb;
> +     struct drm_framebuffer *fb = pstate->fb;
> +     struct drm_connector *connector = pipe->connector;
> +     struct drm_device *drm = connector->dev;
> +
> +     /*
> +      * We support these specific resolutions and nothing else.
> +      */
> +     if (!(mode->hdisplay == 352 && mode->vdisplay == 240) && /* SIF(525) */
> +         !(mode->hdisplay == 352 && mode->vdisplay == 288) && /* CIF(625) */
> +         !(mode->hdisplay == 640 && mode->vdisplay == 480) && /* VGA */
> +         !(mode->hdisplay == 720 && mode->vdisplay == 480) && /* D1 */
> +         !(mode->hdisplay == 720 && mode->vdisplay == 576)) { /* D1 */
> +             dev_err(drm->dev, "unsupported display mode (%u x %u)\n",
> +                     mode->hdisplay, mode->vdisplay);
> +             return -EINVAL;
> +     }
> +
> +     if (fb) {
> +             u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0);
> +
> +             /* FB base address must be dword aligned. */
> +             if (offset & 3) {
> +                     dev_err(drm->dev, "FB not 32-bit aligned\n");
> +                     return -EINVAL;
> +             }
> +
> +             /*
> +              * There's no pitch register, the mode's hdisplay
> +              * controls this.
> +              */
> +             if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) {
> +                     dev_err(drm->dev, "can't handle pitches\n");
> +                     return -EINVAL;
> +             }
> +
> +             /*
> +              * We can't change the FB format in a flicker-free
> +              * manner (and only update it during CRTC enable).
> +              */
> +             if (old_fb && old_fb->format != fb->format)
> +                     cstate->mode_changed = true;
> +     }
> +
> +     return 0;
> +}
> +
> +static void tve200_display_enable(struct drm_simple_display_pipe *pipe,
> +                              struct drm_crtc_state *cstate)
> +{
> +     struct drm_crtc *crtc = &pipe->crtc;
> +     struct drm_plane *plane = &pipe->plane;
> +     struct drm_device *drm = crtc->dev;
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +     const struct drm_display_mode *mode = &cstate->mode;
> +     struct drm_framebuffer *fb = plane->state->fb;
> +     struct drm_connector *connector = &priv->connector.connector;
> +     u32 format = fb->format->format;
> +     u32 ctrl1 = 0;
> +
> +     clk_prepare_enable(priv->clk);
> +
> +     /* Function 1 */
> +     ctrl1 |= TVE200_CTRL_CSMODE;
> +     /* Interlace mode for CCIR656: parameterize? */
> +     ctrl1 |= TVE200_CTRL_NONINTERLACE;
> +     /* 32 words per burst */
> +     ctrl1 |= TVE200_CTRL_BURST_32_WORDS;
> +     /* 16 retries */
> +     ctrl1 |= TVE200_CTRL_RETRYCNT_16;
> +     /* NTSC mode: parametrize? */
> +     ctrl1 |= TVE200_CTRL_NTSC;
> +
> +     /* Vsync IRQ at start of Vsync at first */
> +     ctrl1 |= TVE200_VSTSTYPE_VSYNC;
> +
> +     if (connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE)
> +             ctrl1 |= TVE200_CTRL_TVCLKP;
> +
> +     if ((mode->hdisplay == 352 && mode->vdisplay == 240) || /* SIF(525) */
> +         (mode->hdisplay == 352 && mode->vdisplay == 288)) { /* CIF(625) */
> +             ctrl1 |= TVE200_CTRL_IPRESOL_CIF;
> +             dev_info(drm->dev, "CIF mode\n");
> +     } else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
> +             ctrl1 |= TVE200_CTRL_IPRESOL_VGA;
> +             dev_info(drm->dev, "VGA mode\n");
> +     } else if ((mode->hdisplay == 720 && mode->vdisplay == 480) ||
> +                (mode->hdisplay == 720 && mode->vdisplay == 576)) {
> +             ctrl1 |= TVE200_CTRL_IPRESOL_D1;
> +             dev_info(drm->dev, "D1 mode\n");
> +     }
> +
> +     if (format & DRM_FORMAT_BIG_ENDIAN) {
> +             ctrl1 |= TVE200_CTRL_BBBP;
> +             format &= ~DRM_FORMAT_BIG_ENDIAN;
> +     }
> +
> +     switch (format) {
> +     case DRM_FORMAT_XRGB8888:
> +             ctrl1 |= TVE200_IPDMOD_RGB888;
> +             break;
> +     case DRM_FORMAT_RGB565:
> +             ctrl1 |= TVE200_IPDMOD_RGB565;
> +             break;
> +     case DRM_FORMAT_XRGB1555:
> +             ctrl1 |= TVE200_IPDMOD_RGB555;
> +             break;
> +     case DRM_FORMAT_XBGR8888:
> +             ctrl1 |= TVE200_IPDMOD_RGB888 | TVE200_BGR;
> +             break;
> +     case DRM_FORMAT_BGR565:
> +             ctrl1 |= TVE200_IPDMOD_RGB565 | TVE200_BGR;
> +             break;
> +     case DRM_FORMAT_XBGR1555:
> +             ctrl1 |= TVE200_IPDMOD_RGB555 | TVE200_BGR;
> +             break;
> +     case DRM_FORMAT_YUYV:
> +             ctrl1 |= TVE200_IPDMOD_YUV422;
> +             ctrl1 |= TVE200_CTRL_YCBCRODR_CR0Y1CB0Y0;
> +             break;
> +     case DRM_FORMAT_YVYU:
> +             ctrl1 |= TVE200_IPDMOD_YUV422;
> +             ctrl1 |= TVE200_CTRL_YCBCRODR_CB0Y1CR0Y0;
> +             break;
> +     case DRM_FORMAT_UYVY:
> +             ctrl1 |= TVE200_IPDMOD_YUV422;
> +             ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CR0Y0CB0;
> +             break;
> +     case DRM_FORMAT_VYUY:
> +             ctrl1 |= TVE200_IPDMOD_YUV422;
> +             ctrl1 |= TVE200_CTRL_YCBCRODR_Y1CB0Y0CR0;
> +             break;
> +     case DRM_FORMAT_YUV420:
> +             ctrl1 |= TVE200_CTRL_YUV420;
> +             ctrl1 |= TVE200_IPDMOD_YUV420;
> +             break;
> +     default:
> +             dev_err(drm->dev, "Unknown FB format 0x%08x\n",
> +                     fb->format->format);
> +             break;
> +     }
> +
> +     ctrl1 |= TVE200_TVEEN;
> +
> +     drm_panel_prepare(priv->connector.panel);
> +
> +     /* Turn it on */
> +     writel(ctrl1, priv->regs + TVE200_CTRL);
> +
> +     drm_panel_enable(priv->connector.panel);
> +
> +     drm_crtc_vblank_on(crtc);
> +}
> +
> +void tve200_display_disable(struct drm_simple_display_pipe *pipe)
> +{
> +     struct drm_crtc *crtc = &pipe->crtc;
> +     struct drm_device *drm = crtc->dev;
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +
> +     drm_crtc_vblank_off(crtc);
> +
> +     drm_panel_disable(priv->connector.panel);
> +
> +     /* Disable and Power Down */
> +     writel(0, priv->regs + TVE200_CTRL);
> +
> +     drm_panel_unprepare(priv->connector.panel);
> +
> +     clk_disable_unprepare(priv->clk);
> +}
> +
> +static void tve200_display_update(struct drm_simple_display_pipe *pipe,
> +                              struct drm_plane_state *old_pstate)
> +{
> +     struct drm_crtc *crtc = &pipe->crtc;
> +     struct drm_device *drm = crtc->dev;
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +     struct drm_pending_vblank_event *event = crtc->state->event;
> +     struct drm_plane *plane = &pipe->plane;
> +     struct drm_plane_state *pstate = plane->state;
> +     struct drm_framebuffer *fb = pstate->fb;
> +
> +     if (fb) {
> +             /* For RGB, the Y component is used as base address */
> +             writel(drm_fb_cma_get_gem_addr(fb, pstate, 0),
> +                    priv->regs + TVE200_Y_FRAME_BASE_ADDR);
> +
> +             /* For three plane YUV we need two more addresses */
> +             if (fb->format->format == DRM_FORMAT_YUV420) {
> +                     writel(drm_fb_cma_get_gem_addr(fb, pstate, 1),
> +                            priv->regs + TVE200_U_FRAME_BASE_ADDR);
> +                     writel(drm_fb_cma_get_gem_addr(fb, pstate, 2),
> +                            priv->regs + TVE200_V_FRAME_BASE_ADDR);
> +             }
> +     }
> +
> +     if (event) {
> +             crtc->state->event = NULL;
> +
> +             spin_lock_irq(&crtc->dev->event_lock);
> +             if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
> +                     drm_crtc_arm_vblank_event(crtc, event);
> +             else
> +                     drm_crtc_send_vblank_event(crtc, event);
> +             spin_unlock_irq(&crtc->dev->event_lock);
> +     }
> +}
> +
> +int tve200_enable_vblank(struct drm_device *drm, unsigned int crtc)
> +{
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +
> +     writel(TVE200_INT_V_STATUS, priv->regs + TVE200_INT_EN);
> +     return 0;
> +}
> +
> +void tve200_disable_vblank(struct drm_device *drm, unsigned int crtc)
> +{
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +
> +     writel(0, priv->regs + TVE200_INT_EN);
> +}
> +
> +static int tve200_display_prepare_fb(struct drm_simple_display_pipe *pipe,
> +                                 struct drm_plane_state *plane_state)
> +{
> +     return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
> +}
> +
> +const struct drm_simple_display_pipe_funcs tve200_display_funcs = {
> +     .check = tve200_display_check,
> +     .enable = tve200_display_enable,
> +     .disable = tve200_display_disable,
> +     .update = tve200_display_update,
> +     .prepare_fb = tve200_display_prepare_fb,
> +};
> +
> +int tve200_display_init(struct drm_device *drm)
> +{
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +     int ret;
> +     static const u32 formats[] = {
> +             DRM_FORMAT_XRGB8888,
> +             DRM_FORMAT_XBGR8888,
> +             DRM_FORMAT_RGB565,
> +             DRM_FORMAT_BGR565,
> +             DRM_FORMAT_XRGB1555,
> +             DRM_FORMAT_XBGR1555,
> +             /*
> +              * The controller actually supports any YCbCr ordering,
> +              * for packed YCbCr. This just lists the orderings that
> +              * DRM supports.
> +              */
> +             DRM_FORMAT_YUYV,
> +             DRM_FORMAT_YVYU,
> +             DRM_FORMAT_UYVY,
> +             DRM_FORMAT_VYUY,
> +             /* This uses three planes */
> +             DRM_FORMAT_YUV420,
> +     };
> +
> +     ret = drm_simple_display_pipe_init(drm, &priv->pipe,
> +                                        &tve200_display_funcs,
> +                                        formats, ARRAY_SIZE(formats),
> +                                        &priv->connector.connector);
> +     if (ret)
> +             return ret;
> +
> +     return 0;
> +}
> diff --git a/drivers/gpu/drm/tve200/tve200_drm.h 
> b/drivers/gpu/drm/tve200/tve200_drm.h
> new file mode 100644
> index 000000000000..f00fc47a6bd1
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/tve200_drm.h
> @@ -0,0 +1,129 @@
> +/*
> + * Copyright (C) 2017 Linus Walleij <linus.wall...@linaro.org>
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (C) 2006-2008 Intel Corporation
> + * Copyright (C) 2007 Amos Lee <amos_...@storlinksemi.com>
> + * Copyright (C) 2007 Dave Airlie <airl...@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + * Copyright (C) 2017 Eric Anholt
> + *
> + * This program is free software and is provided to you under the terms of 
> the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + */
> +
> +#ifndef _TVE200_DRM_H_
> +#define _TVE200_DRM_H_
> +
> +/* Bits 2-31 are valid physical base addresses */
> +#define TVE200_Y_FRAME_BASE_ADDR     0x00
> +#define TVE200_U_FRAME_BASE_ADDR     0x04
> +#define TVE200_V_FRAME_BASE_ADDR     0x08
> +
> +#define TVE200_INT_EN                        0x0C
> +#define TVE200_INT_CLR                       0x10
> +#define TVE200_INT_STAT                      0x14
> +#define TVE200_INT_BUS_ERR           BIT(7)
> +#define TVE200_INT_V_STATUS          BIT(6) /* vertical blank */
> +#define TVE200_INT_V_NEXT_FRAME              BIT(5)
> +#define TVE200_INT_U_NEXT_FRAME              BIT(4)
> +#define TVE200_INT_Y_NEXT_FRAME              BIT(3)
> +#define TVE200_INT_V_FIFO_UNDERRUN   BIT(2)
> +#define TVE200_INT_U_FIFO_UNDERRUN   BIT(1)
> +#define TVE200_INT_Y_FIFO_UNDERRUN   BIT(0)
> +#define TVE200_FIFO_UNDERRUNS                (TVE200_INT_V_FIFO_UNDERRUN | \
> +                                      TVE200_INT_U_FIFO_UNDERRUN | \
> +                                      TVE200_INT_Y_FIFO_UNDERRUN)
> +
> +#define TVE200_CTRL                  0x18
> +#define TVE200_CTRL_YUV420           BIT(31)
> +#define TVE200_CTRL_CSMODE           BIT(30)
> +#define TVE200_CTRL_NONINTERLACE     BIT(28) /* 0 = non-interlace CCIR656 */
> +#define TVE200_CTRL_TVCLKP           BIT(27) /* Inverted clock phase */
> +/* Bits 24..26 define the burst size after arbitration on the bus */
> +#define TVE200_CTRL_BURST_4_WORDS    (0 << 24)
> +#define TVE200_CTRL_BURST_8_WORDS    (1 << 24)
> +#define TVE200_CTRL_BURST_16_WORDS   (2 << 24)
> +#define TVE200_CTRL_BURST_32_WORDS   (3 << 24)
> +#define TVE200_CTRL_BURST_64_WORDS   (4 << 24)
> +#define TVE200_CTRL_BURST_128_WORDS  (5 << 24)
> +#define TVE200_CTRL_BURST_256_WORDS  (6 << 24)
> +#define TVE200_CTRL_BURST_0_WORDS    (7 << 24) /* ? */
> +/*
> + * Bits 16..23 is the retry count*16 before issueing a new AHB transfer
> + * on the AHB bus.
> + */
> +#define TVE200_CTRL_RETRYCNT_MASK    GENMASK(23, 16)
> +#define TVE200_CTRL_RETRYCNT_16              (1 << 16)
> +#define TVE200_CTRL_BBBP             BIT(15) /* 0 = little-endian */
> +/* Bits 12..14 define the YCbCr ordering */
> +#define TVE200_CTRL_YCBCRODR_CB0Y0CR0Y1      (0 << 12)
> +#define TVE200_CTRL_YCBCRODR_Y0CB0Y1CR0      (1 << 12)
> +#define TVE200_CTRL_YCBCRODR_CR0Y0CB0Y1      (2 << 12)
> +#define TVE200_CTRL_YCBCRODR_Y1CB0Y0CR0      (3 << 12)
> +#define TVE200_CTRL_YCBCRODR_CR0Y1CB0Y0      (4 << 12)
> +#define TVE200_CTRL_YCBCRODR_Y1CR0Y0CB0      (5 << 12)
> +#define TVE200_CTRL_YCBCRODR_CB0Y1CR0Y0      (6 << 12)
> +#define TVE200_CTRL_YCBCRODR_Y0CR0Y1CB0      (7 << 12)
> +/* Bits 10..11 define the input resolution (framebuffer size) */
> +#define TVE200_CTRL_IPRESOL_CIF              (0 << 10)
> +#define TVE200_CTRL_IPRESOL_VGA              (1 << 10)
> +#define TVE200_CTRL_IPRESOL_D1               (2 << 10)
> +#define TVE200_CTRL_NTSC             BIT(9) /* 0 = PAL, 1 = NTSC */
> +#define TVE200_CTRL_INTERLACE                BIT(8) /* 1 = interlace, only 
> for D1 */
> +#define TVE200_IPDMOD_RGB555         (0 << 6) /* TVE200_CTRL_YUV420 = 0 */
> +#define TVE200_IPDMOD_RGB565         (1 << 6)
> +#define TVE200_IPDMOD_RGB888         (2 << 6)
> +#define TVE200_IPDMOD_YUV420         (2 << 6) /* TVE200_CTRL_YUV420 = 1 */
> +#define TVE200_IPDMOD_YUV422         (3 << 6)
> +/* Bits 4 & 5 define when to fire the vblank IRQ */
> +#define TVE200_VSTSTYPE_VSYNC                (0 << 4) /* start of vsync */
> +#define TVE200_VSTSTYPE_VBP          (1 << 4) /* start of v back porch */
> +#define TVE200_VSTSTYPE_VAI          (2 << 4) /* start of v active image */
> +#define TVE200_VSTSTYPE_VFP          (3 << 4) /* start of v front porch */
> +#define TVE200_VSTSTYPE_BITS         (BIT(4) | BIT(5))
> +#define TVE200_BGR                   BIT(1) /* 0 = RGB, 1 = BGR */
> +#define TVE200_TVEEN                 BIT(0) /* Enable TVE block */
> +
> +#define TVE200_CTRL_2                        0x1c
> +#define TVE200_CTRL_3                        0x20
> +
> +#define TVE200_CTRL_4                        0x24
> +#define TVE200_CTRL_4_RESET          BIT(0) /* triggers reset of TVE200 */
> +
> +#include <drm/drm_gem.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +struct tve200_drm_connector {
> +     struct drm_connector connector;
> +     struct drm_panel *panel;
> +};
> +
> +struct tve200_drm_dev_private {
> +     struct drm_device *drm;
> +
> +     struct tve200_drm_connector connector;
> +     struct drm_simple_display_pipe pipe;
> +     struct drm_fbdev_cma *fbdev;
> +
> +     void *regs;
> +     struct clk *pclk;
> +     struct clk *clk;
> +};
> +
> +#define to_tve200_connector(x) \
> +     container_of(x, struct tve200_drm_connector, connector)
> +
> +int tve200_display_init(struct drm_device *dev);
> +int tve200_enable_vblank(struct drm_device *drm, unsigned int crtc);
> +void tve200_disable_vblank(struct drm_device *drm, unsigned int crtc);
> +irqreturn_t tve200_irq(int irq, void *data);
> +int tve200_connector_init(struct drm_device *dev);
> +int tve200_encoder_init(struct drm_device *dev);
> +int tve200_dumb_create(struct drm_file *file_priv,
> +                   struct drm_device *dev,
> +                   struct drm_mode_create_dumb *args);
> +
> +#endif /* _TVE200_DRM_H_ */
> diff --git a/drivers/gpu/drm/tve200/tve200_drv.c 
> b/drivers/gpu/drm/tve200/tve200_drv.c
> new file mode 100644
> index 000000000000..9e34147e2617
> --- /dev/null
> +++ b/drivers/gpu/drm/tve200/tve200_drv.c
> @@ -0,0 +1,277 @@
> +/*
> + * Copyright (C) 2017 Linus Walleij <linus.wall...@linaro.org>
> + * Parts of this file were based on sources as follows:
> + *
> + * Copyright (C) 2006-2008 Intel Corporation
> + * Copyright (C) 2007 Amos Lee <amos_...@storlinksemi.com>
> + * Copyright (C) 2007 Dave Airlie <airl...@linux.ie>
> + * Copyright (C) 2011 Texas Instruments
> + * Copyright (C) 2017 Eric Anholt
> + *
> + * This program is free software and is provided to you under the terms of 
> the
> + * GNU General Public License version 2 as published by the Free Software
> + * Foundation, and any use by you of this program is subject to the terms of
> + * such GNU licence.
> + */
> +
> +/**
> + * DOC: Faraday TV Encoder TVE200 DRM Driver
> + *
> + * The Faraday TV Encoder TVE200 is also known as the Gemini TV Interface
> + * Controller (TVC) and is found in the Gemini Chipset from Storlink
> + * Semiconductor (later Storm Semiconductor, later Cortina Systems)
> + * but also in the Grain Media GM8180 chipset. On the Gemini the module
> + * is connected to 8 data lines and a single clock line, comprising an
> + * 8-bit BT.656 interface.
> + *
> + * This is a very basic YUV display driver. The datasheet specifies that
> + * it supports the ITU BT.656 standard. It requires a 27 MHz clock which is
> + * the hallmark of any TV encoder supporting both PAL and NTSC.
> + *
> + * This driver exposes a standard KMS interface for this TV encoder.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dma-buf.h>
> +#include <linux/irq.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/shmem_fs.h>
> +#include <linux/slab.h>
> +#include <linux/version.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_panel.h>
> +
> +#include "tve200_drm.h"
> +
> +#define DRIVER_DESC      "DRM module for Faraday TVE200"
> +
> +struct drm_mode_config_funcs mode_config_funcs = {
> +     .fb_create = drm_fb_cma_create,
> +     .atomic_check = drm_atomic_helper_check,
> +     .atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static int tve200_modeset_init(struct drm_device *dev)
> +{
> +     struct drm_mode_config *mode_config;
> +     struct tve200_drm_dev_private *priv = dev->dev_private;
> +     int ret = 0;
> +
> +     drm_mode_config_init(dev);
> +     mode_config = &dev->mode_config;
> +     mode_config->funcs = &mode_config_funcs;
> +     mode_config->min_width = 352;
> +     mode_config->max_width = 720;
> +     mode_config->min_height = 240;
> +     mode_config->max_height = 576;
> +
> +     ret = tve200_connector_init(dev);
> +     if (ret) {
> +             dev_err(dev->dev, "Failed to create tve200_drm_connector\n");
> +             goto out_config;
> +     }
> +
> +     /*
> +      * Don't actually attach if we didn't find a drm_panel
> +      * attached to us.
> +      */
> +     if (!priv->connector.panel) {
> +             dev_info(dev->dev,
> +                      "deferring due to lack of DRM panel device\n");
> +             ret = -EPROBE_DEFER;
> +             goto out_config;
> +     }
> +     dev_info(dev->dev, "attached to panel %s\n",
> +              dev_name(priv->connector.panel->dev));
> +
> +     ret = tve200_display_init(dev);
> +     if (ret) {
> +             dev_err(dev->dev, "failed to init display\n");
> +             goto out_config;
> +     }
> +
> +     ret = drm_vblank_init(dev, 1);
> +     if (ret) {
> +             dev_err(dev->dev, "failed to init vblank\n");
> +             goto out_config;
> +     }
> +
> +     drm_mode_config_reset(dev);
> +
> +     /*
> +      * Passing in 16 here will make the RGB656 mode the default
> +      * Passing in 32 will use XRGB8888 mode
> +      */
> +     priv->fbdev = drm_fbdev_cma_init(dev, 16,
> +                                      dev->mode_config.num_connector);
> +     drm_kms_helper_poll_init(dev);
> +
> +     goto finish;
> +
> +out_config:
> +     drm_mode_config_cleanup(dev);
> +finish:
> +     return ret;
> +}
> +
> +DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
> +
> +static void tve200_lastclose(struct drm_device *dev)
> +{
> +     struct tve200_drm_dev_private *priv = dev->dev_private;
> +
> +     drm_fbdev_cma_restore_mode(priv->fbdev);
> +}
> +
> +static struct drm_driver tve200_drm_driver = {
> +     .driver_features =
> +             DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
> +     .lastclose = tve200_lastclose,
> +     .ioctls = NULL,
> +     .fops = &drm_fops,
> +     .name = "tve200",
> +     .desc = DRIVER_DESC,
> +     .date = "20170703",
> +     .major = 1,
> +     .minor = 0,
> +     .patchlevel = 0,
> +     .dumb_create = drm_gem_cma_dumb_create,
> +     .dumb_destroy = drm_gem_dumb_destroy,
> +     .dumb_map_offset = drm_gem_cma_dumb_map_offset,
> +     .gem_free_object = drm_gem_cma_free_object,
> +     .gem_vm_ops = &drm_gem_cma_vm_ops,
> +
> +     .enable_vblank = tve200_enable_vblank,
> +     .disable_vblank = tve200_disable_vblank,
> +
> +     .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
> +     .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
> +     .gem_prime_import = drm_gem_prime_import,
> +     .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
> +     .gem_prime_export = drm_gem_prime_export,
> +     .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
> +};
> +
> +static int tve200_probe(struct platform_device *pdev)
> +{
> +     struct device *dev = &pdev->dev;
> +     struct tve200_drm_dev_private *priv;
> +     struct drm_device *drm;
> +     struct resource *res;
> +     int irq;
> +     int ret;
> +
> +     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +     if (!priv)
> +             return -ENOMEM;
> +
> +     drm = drm_dev_alloc(&tve200_drm_driver, dev);
> +     if (IS_ERR(drm))
> +             return PTR_ERR(drm);
> +     platform_set_drvdata(pdev, drm);
> +     priv->drm = drm;
> +     drm->dev_private = priv;
> +
> +     /* Clock the silicon so we can access the registers */
> +     priv->pclk = devm_clk_get(dev, "PCLK");
> +     if (IS_ERR(priv->pclk)) {
> +             dev_err(dev, "unable to get PCLK\n");
> +             ret = PTR_ERR(priv->pclk);
> +             goto dev_unref;
> +     }
> +     ret = clk_prepare_enable(priv->pclk);
> +     if (ret) {
> +             dev_err(dev, "failed to enable PCLK\n");
> +             goto dev_unref;
> +     }
> +
> +     /* This clock is for the pixels (27MHz) */
> +     priv->clk = devm_clk_get(dev, "TVE");
> +     if (IS_ERR(priv->clk)) {
> +             dev_err(dev, "unable to get TVE clock\n");
> +             ret = PTR_ERR(priv->clk);
> +             goto clk_disable;
> +     }
> +
> +     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +     priv->regs = devm_ioremap_resource(dev, res);
> +     if (!priv->regs) {
> +             dev_err(dev, "%s failed mmio\n", __func__);
> +             ret = -EINVAL;
> +             goto dev_unref;
> +     }
> +
> +     irq = platform_get_irq(pdev, 0);
> +     if (!irq) {
> +             ret = -EINVAL;
> +             goto dev_unref;
> +     }
> +
> +     /* turn off interrupts before requesting the irq */
> +     writel(0, priv->regs + TVE200_INT_EN);
> +
> +     ret = devm_request_irq(dev, irq, tve200_irq, 0, "tve200", priv);
> +     if (ret) {
> +             dev_err(dev, "failed to request irq %d\n", ret);
> +             return ret;
> +     }
> +
> +     ret = tve200_modeset_init(drm);
> +     if (ret)
> +             goto dev_unref;
> +
> +     ret = drm_dev_register(drm, 0);
> +     if (ret < 0)
> +             goto dev_unref;
> +
> +     return 0;
> +
> +clk_disable:
> +     clk_disable_unprepare(priv->pclk);
> +dev_unref:
> +     drm_dev_unref(drm);
> +     return ret;
> +}
> +
> +static int tve200_remove(struct platform_device *pdev)
> +{
> +     struct drm_device *drm = platform_get_drvdata(pdev);
> +     struct tve200_drm_dev_private *priv = drm->dev_private;
> +
> +     drm_dev_unregister(drm);
> +     if (priv->fbdev)
> +             drm_fbdev_cma_fini(priv->fbdev);
> +     drm_mode_config_cleanup(drm);
> +     clk_disable_unprepare(priv->pclk);
> +     drm_dev_unref(drm);
> +
> +     return 0;
> +}
> +
> +static const struct of_device_id tve200_of_match[] = {
> +     {
> +             .compatible = "faraday,tve200",
> +     },
> +     {},
> +};
> +
> +static struct platform_driver tve200_driver = {
> +     .driver = {
> +             .name           = "tve200",
> +             .of_match_table = of_match_ptr(tve200_of_match),
> +     },
> +     .probe = tve200_probe,
> +     .remove = tve200_remove,
> +};
> +module_platform_driver(tve200_driver);
> +
> +MODULE_DESCRIPTION(DRIVER_DESC);
> +MODULE_AUTHOR("Linus Walleij <linus.wall...@linaro.org>");
> +MODULE_LICENSE("GPL");
> -- 
> 2.13.4
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to