Commit 2f004792 authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Tomi Valkeinen
Browse files

drm/omap: venc: Register a drm_bridge



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: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Tested-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Reviewed-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200226112514.12455-38-laurent.pinchart@ideasonboard.com
parent 4fcbfbae
Loading
Loading
Loading
Loading
+154 −88
Original line number Diff line number Diff line
@@ -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,
};

/* -----------------------------------------------------------------------------
 * DRM Bridge Operations
 */

	.check_timings = venc_check_timings,
	.set_timings = venc_set_timings,
static int venc_bridge_attach(struct drm_bridge *bridge,
			      enum drm_bridge_attach_flags flags)
{
	struct venc_device *venc = drm_bridge_to_venc(bridge);

	.get_modes = venc_get_modes,
	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);