OSDN Git Service

drm/omap: venc: Register a drm_bridge
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Wed, 26 Feb 2020 11:24:57 +0000 (13:24 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Wed, 26 Feb 2020 11:31:59 +0000 (13:31 +0200)
In order to integrate with a chain of drm_bridge, the internal VENC
encoder has to expose the mode valid, fixup and set, the enable and
disable and the get modes operations through the drm_bridge API.
Register a bridge at initialisation time to do so.

Most of those operations are removed from the omap_dss_device as they
are now called through the drm_bridge API by the DRM atomic helpers. The
only exception is the .get_modes() operation that is still invoked
through the omap_dss_device-based pipeline.

For the time being make the next bridge in the chain optional as the
VENC output is still based on omap_dss_device. The create_connector
argument to the bridge attach function is also ignored for the same
reason. This will be changed later when removing the related
omapdrm-specific display drivers.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-38-laurent.pinchart@ideasonboard.com
drivers/gpu/drm/omapdrm/dss/venc.c

index 977d8d5..cb9a689 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/mutex.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/string.h>
@@ -26,6 +25,8 @@
 #include <linux/component.h>
 #include <linux/sys_soc.h>
 
+#include <drm/drm_bridge.h>
+
 #include "omapdss.h"
 #include "dss.h"
 
@@ -289,7 +290,6 @@ static const struct drm_display_mode omap_dss_ntsc_mode = {
 struct venc_device {
        struct platform_device *pdev;
        void __iomem *base;
-       struct mutex venc_lock;
        struct regulator *vdda_dac_reg;
        struct dss_device *dss;
 
@@ -303,9 +303,11 @@ struct venc_device {
        bool requires_tv_dac_clk;
 
        struct omap_dss_device output;
+       struct drm_bridge bridge;
 };
 
 #define dssdev_to_venc(dssdev) container_of(dssdev, struct venc_device, output)
+#define drm_bridge_to_venc(b) container_of(b, struct venc_device, bridge)
 
 static inline void venc_write_reg(struct venc_device *venc, int idx, u32 val)
 {
@@ -477,32 +479,6 @@ static void venc_power_off(struct venc_device *venc)
        venc_runtime_put(venc);
 }
 
-static void venc_display_enable(struct omap_dss_device *dssdev)
-{
-       struct venc_device *venc = dssdev_to_venc(dssdev);
-
-       DSSDBG("venc_display_enable\n");
-
-       mutex_lock(&venc->venc_lock);
-
-       venc_power_on(venc);
-
-       mutex_unlock(&venc->venc_lock);
-}
-
-static void venc_display_disable(struct omap_dss_device *dssdev)
-{
-       struct venc_device *venc = dssdev_to_venc(dssdev);
-
-       DSSDBG("venc_display_disable\n");
-
-       mutex_lock(&venc->venc_lock);
-
-       venc_power_off(venc);
-
-       mutex_unlock(&venc->venc_lock);
-}
-
 static int venc_get_modes(struct omap_dss_device *dssdev,
                          struct drm_connector *connector)
 {
@@ -545,57 +521,6 @@ static enum venc_videomode venc_get_videomode(const struct drm_display_mode *mod
        return VENC_MODE_UNKNOWN;
 }
 
-static void venc_set_timings(struct omap_dss_device *dssdev,
-                            const struct drm_display_mode *mode)
-{
-       struct venc_device *venc = dssdev_to_venc(dssdev);
-       enum venc_videomode venc_mode = venc_get_videomode(mode);
-
-       DSSDBG("venc_set_timings\n");
-
-       mutex_lock(&venc->venc_lock);
-
-       switch (venc_mode) {
-       default:
-               WARN_ON_ONCE(1);
-               /* Fall-through */
-       case VENC_MODE_PAL:
-               venc->config = &venc_config_pal_trm;
-               break;
-
-       case VENC_MODE_NTSC:
-               venc->config = &venc_config_ntsc_trm;
-               break;
-       }
-
-       dispc_set_tv_pclk(venc->dss->dispc, 13500000);
-
-       mutex_unlock(&venc->venc_lock);
-}
-
-static int venc_check_timings(struct omap_dss_device *dssdev,
-                             struct drm_display_mode *mode)
-{
-       DSSDBG("venc_check_timings\n");
-
-       switch (venc_get_videomode(mode)) {
-       case VENC_MODE_PAL:
-               drm_mode_copy(mode, &omap_dss_pal_mode);
-               break;
-
-       case VENC_MODE_NTSC:
-               drm_mode_copy(mode, &omap_dss_ntsc_mode);
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-       drm_mode_set_name(mode);
-       return 0;
-}
-
 static int venc_dump_regs(struct seq_file *s, void *p)
 {
        struct venc_device *venc = s->private;
@@ -689,15 +614,152 @@ static const struct omap_dss_device_ops venc_ops = {
        .connect = venc_connect,
        .disconnect = venc_disconnect,
 
-       .enable = venc_display_enable,
-       .disable = venc_display_disable,
+       .get_modes = venc_get_modes,
+};
 
-       .check_timings = venc_check_timings,
-       .set_timings = venc_set_timings,
+/* -----------------------------------------------------------------------------
+ * DRM Bridge Operations
+ */
 
-       .get_modes = venc_get_modes,
+static int venc_bridge_attach(struct drm_bridge *bridge,
+                             enum drm_bridge_attach_flags flags)
+{
+       struct venc_device *venc = drm_bridge_to_venc(bridge);
+
+       if (venc->output.next_bridge)
+               return 0;
+
+       return drm_bridge_attach(bridge->encoder, venc->output.next_bridge,
+                                bridge, flags);
+}
+
+static enum drm_mode_status
+venc_bridge_mode_valid(struct drm_bridge *bridge,
+                      const struct drm_display_mode *mode)
+{
+       switch (venc_get_videomode(mode)) {
+       case VENC_MODE_PAL:
+       case VENC_MODE_NTSC:
+               return MODE_OK;
+
+       default:
+               return MODE_BAD;
+       }
+}
+
+static bool venc_bridge_mode_fixup(struct drm_bridge *bridge,
+                                  const struct drm_display_mode *mode,
+                                  struct drm_display_mode *adjusted_mode)
+{
+       const struct drm_display_mode *venc_mode;
+
+       switch (venc_get_videomode(adjusted_mode)) {
+       case VENC_MODE_PAL:
+               venc_mode = &omap_dss_pal_mode;
+               break;
+
+       case VENC_MODE_NTSC:
+               venc_mode = &omap_dss_ntsc_mode;
+               break;
+
+       default:
+               return false;
+       }
+
+       drm_mode_copy(adjusted_mode, venc_mode);
+       drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+       drm_mode_set_name(adjusted_mode);
+
+       return true;
+}
+
+static void venc_bridge_mode_set(struct drm_bridge *bridge,
+                                const struct drm_display_mode *mode,
+                                const struct drm_display_mode *adjusted_mode)
+{
+       struct venc_device *venc = drm_bridge_to_venc(bridge);
+       enum venc_videomode venc_mode = venc_get_videomode(adjusted_mode);
+
+       switch (venc_mode) {
+       default:
+               WARN_ON_ONCE(1);
+               /* Fall-through */
+       case VENC_MODE_PAL:
+               venc->config = &venc_config_pal_trm;
+               break;
+
+       case VENC_MODE_NTSC:
+               venc->config = &venc_config_ntsc_trm;
+               break;
+       }
+
+       dispc_set_tv_pclk(venc->dss->dispc, 13500000);
+}
+
+static void venc_bridge_enable(struct drm_bridge *bridge)
+{
+       struct venc_device *venc = drm_bridge_to_venc(bridge);
+
+       venc_power_on(venc);
+}
+
+static void venc_bridge_disable(struct drm_bridge *bridge)
+{
+       struct venc_device *venc = drm_bridge_to_venc(bridge);
+
+       venc_power_off(venc);
+}
+
+static int venc_bridge_get_modes(struct drm_bridge *bridge,
+                                struct drm_connector *connector)
+{
+       static const struct drm_display_mode *modes[] = {
+               &omap_dss_pal_mode,
+               &omap_dss_ntsc_mode,
+       };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(modes); ++i) {
+               struct drm_display_mode *mode;
+
+               mode = drm_mode_duplicate(connector->dev, modes[i]);
+               if (!mode)
+                       return i;
+
+               mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+               drm_mode_set_name(mode);
+               drm_mode_probed_add(connector, mode);
+       }
+
+       return ARRAY_SIZE(modes);
+}
+
+static const struct drm_bridge_funcs venc_bridge_funcs = {
+       .attach = venc_bridge_attach,
+       .mode_valid = venc_bridge_mode_valid,
+       .mode_fixup = venc_bridge_mode_fixup,
+       .mode_set = venc_bridge_mode_set,
+       .enable = venc_bridge_enable,
+       .disable = venc_bridge_disable,
+       .get_modes = venc_bridge_get_modes,
 };
 
+static void venc_bridge_init(struct venc_device *venc)
+{
+       venc->bridge.funcs = &venc_bridge_funcs;
+       venc->bridge.of_node = venc->pdev->dev.of_node;
+       venc->bridge.ops = DRM_BRIDGE_OP_MODES;
+       venc->bridge.type = DRM_MODE_CONNECTOR_SVIDEO;
+       venc->bridge.interlace_allowed = true;
+
+       drm_bridge_add(&venc->bridge);
+}
+
+static void venc_bridge_cleanup(struct venc_device *venc)
+{
+       drm_bridge_remove(&venc->bridge);
+}
+
 /* -----------------------------------------------------------------------------
  * Component Bind & Unbind
  */
@@ -747,6 +809,8 @@ static int venc_init_output(struct venc_device *venc)
        struct omap_dss_device *out = &venc->output;
        int r;
 
+       venc_bridge_init(venc);
+
        out->dev = &venc->pdev->dev;
        out->id = OMAP_DSS_OUTPUT_VENC;
        out->type = OMAP_DISPLAY_TYPE_VENC;
@@ -757,9 +821,11 @@ static int venc_init_output(struct venc_device *venc)
        out->of_port = 0;
        out->ops_flags = OMAP_DSS_DEVICE_OP_MODES;
 
-       r = omapdss_device_init_output(out, NULL);
-       if (r < 0)
+       r = omapdss_device_init_output(out, &venc->bridge);
+       if (r < 0) {
+               venc_bridge_cleanup(venc);
                return r;
+       }
 
        omapdss_device_register(out);
 
@@ -770,6 +836,8 @@ static void venc_uninit_output(struct venc_device *venc)
 {
        omapdss_device_unregister(&venc->output);
        omapdss_device_cleanup_output(&venc->output);
+
+       venc_bridge_cleanup(venc);
 }
 
 static int venc_probe_of(struct venc_device *venc)
@@ -839,8 +907,6 @@ static int venc_probe(struct platform_device *pdev)
        if (soc_device_match(venc_soc_devices))
                venc->requires_tv_dac_clk = true;
 
-       mutex_init(&venc->venc_lock);
-
        venc->config = &venc_config_pal_trm;
 
        venc_mem = platform_get_resource(venc->pdev, IORESOURCE_MEM, 0);