Commit 8bef8a6d authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Tomi Valkeinen
Browse files

drm/omap: sdi: Register a drm_bridge



In order to integrate with a chain of drm_bridge, the internal SDI
output has to expose its operations through the drm_bridge API.
Register a bridge at initialisation time to do so and remove the
omap_dss_device operations that are now unused.

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-51-laurent.pinchart@ideasonboard.com
parent 13d2d52f
Loading
Loading
Loading
Loading
+109 −59
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
#include <linux/regulator/consumer.h>
#include <linux/string.h>

#include <drm/drm_bridge.h>

#include "dss.h"
#include "omapdss.h"

@@ -30,9 +32,11 @@ struct sdi_device {
	int datapairs;

	struct omap_dss_device output;
	struct drm_bridge bridge;
};

#define dssdev_to_sdi(dssdev) container_of(dssdev, struct sdi_device, output)
#define drm_bridge_to_sdi(bridge) \
	container_of(bridge, struct sdi_device, bridge)

struct sdi_clk_calc_ctx {
	struct sdi_device *sdi;
@@ -118,9 +122,82 @@ static void sdi_config_lcd_manager(struct sdi_device *sdi)
	dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config);
}

static void sdi_display_enable(struct omap_dss_device *dssdev)
/* -----------------------------------------------------------------------------
 * DRM Bridge Operations
 */

static int sdi_bridge_attach(struct drm_bridge *bridge,
			     enum drm_bridge_attach_flags flags)
{
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);

	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
		return -EINVAL;

	return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge,
				 bridge, flags);
}

static enum drm_mode_status
sdi_bridge_mode_valid(struct drm_bridge *bridge,
		      const struct drm_display_mode *mode)
{
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
	unsigned long pixelclock = mode->clock * 1000;
	struct dispc_clock_info dispc_cinfo;
	unsigned long fck;
	int ret;

	if (pixelclock == 0)
		return MODE_NOCLOCK;

	ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
	if (ret < 0)
		return MODE_CLOCK_RANGE;

	return MODE_OK;
}

static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge,
				  const struct drm_display_mode *mode,
				  struct drm_display_mode *adjusted_mode)
{
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
	unsigned long pixelclock = mode->clock * 1000;
	struct dispc_clock_info dispc_cinfo;
	unsigned long fck;
	unsigned long pck;
	int ret;

	ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
	if (ret < 0)
		return false;

	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;

	if (pck != pixelclock)
		dev_dbg(&sdi->pdev->dev,
			"pixel clock adjusted from %lu Hz to %lu Hz\n",
			pixelclock, pck);

	adjusted_mode->clock = pck / 1000;

	return true;
}

static void sdi_bridge_mode_set(struct drm_bridge *bridge,
				const struct drm_display_mode *mode,
				const struct drm_display_mode *adjusted_mode)
{
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);

	sdi->pixelclock = adjusted_mode->clock * 1000;
}

static void sdi_bridge_enable(struct drm_bridge *bridge,
			      struct drm_bridge_state *bridge_state)
{
	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
	struct dispc_clock_info dispc_cinfo;
	unsigned long fck;
	int r;
@@ -181,9 +258,10 @@ err_get_dispc:
	regulator_disable(sdi->vdds_sdi_reg);
}

static void sdi_display_disable(struct omap_dss_device *dssdev)
static void sdi_bridge_disable(struct drm_bridge *bridge,
			       struct drm_bridge_state *bridge_state)
{
	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);

	dss_mgr_disable(&sdi->output);

@@ -194,71 +272,40 @@ static void sdi_display_disable(struct omap_dss_device *dssdev)
	regulator_disable(sdi->vdds_sdi_reg);
}

static void sdi_set_timings(struct omap_dss_device *dssdev,
			    const struct drm_display_mode *mode)
{
	struct sdi_device *sdi = dssdev_to_sdi(dssdev);

	sdi->pixelclock = mode->clock * 1000;
}
static const struct drm_bridge_funcs sdi_bridge_funcs = {
	.attach = sdi_bridge_attach,
	.mode_valid = sdi_bridge_mode_valid,
	.mode_fixup = sdi_bridge_mode_fixup,
	.mode_set = sdi_bridge_mode_set,
	.atomic_enable = sdi_bridge_enable,
	.atomic_disable = sdi_bridge_disable,
};

static int sdi_check_timings(struct omap_dss_device *dssdev,
			     struct drm_display_mode *mode)
static void sdi_bridge_init(struct sdi_device *sdi)
{
	struct sdi_device *sdi = dssdev_to_sdi(dssdev);
	struct dispc_clock_info dispc_cinfo;
	unsigned long pixelclock = mode->clock * 1000;
	unsigned long fck;
	unsigned long pck;
	int r;

	if (pixelclock == 0)
		return -EINVAL;

	r = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
	if (r)
		return r;

	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;

	if (pck != pixelclock) {
		DSSWARN("Pixel clock adjusted from %lu Hz to %lu Hz\n",
			pixelclock, pck);

		mode->clock = pck / 1000;
	}

	return 0;
}
	sdi->bridge.funcs = &sdi_bridge_funcs;
	sdi->bridge.of_node = sdi->pdev->dev.of_node;
	sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS;

static int sdi_connect(struct omap_dss_device *src,
		       struct omap_dss_device *dst)
{
	return omapdss_device_connect(dst->dss, dst, dst->next);
	drm_bridge_add(&sdi->bridge);
}

static void sdi_disconnect(struct omap_dss_device *src,
			   struct omap_dss_device *dst)
static void sdi_bridge_cleanup(struct sdi_device *sdi)
{
	omapdss_device_disconnect(dst, dst->next);
	drm_bridge_remove(&sdi->bridge);
}

static const struct omap_dss_device_ops sdi_ops = {
	.connect = sdi_connect,
	.disconnect = sdi_disconnect,

	.enable = sdi_display_enable,
	.disable = sdi_display_disable,

	.check_timings = sdi_check_timings,
	.set_timings = sdi_set_timings,
};
/* -----------------------------------------------------------------------------
 * Initialisation and Cleanup
 */

static int sdi_init_output(struct sdi_device *sdi)
{
	struct omap_dss_device *out = &sdi->output;
	int r;

	sdi_bridge_init(sdi);

	out->dev = &sdi->pdev->dev;
	out->id = OMAP_DSS_OUTPUT_SDI;
	out->type = OMAP_DISPLAY_TYPE_SDI;
@@ -266,14 +313,15 @@ static int sdi_init_output(struct sdi_device *sdi)
	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
	/* We have SDI only on OMAP3, where it's on port 1 */
	out->of_port = 1;
	out->ops = &sdi_ops;
	out->owner = THIS_MODULE;
	out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE	/* 15.5.9.1.2 */
		       | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;

	r = omapdss_device_init_output(out, NULL);
	if (r < 0)
	r = omapdss_device_init_output(out, &sdi->bridge);
	if (r < 0) {
		sdi_bridge_cleanup(sdi);
		return r;
	}

	omapdss_device_register(out);

@@ -284,6 +332,8 @@ static void sdi_uninit_output(struct sdi_device *sdi)
{
	omapdss_device_unregister(&sdi->output);
	omapdss_device_cleanup_output(&sdi->output);

	sdi_bridge_cleanup(sdi);
}

int sdi_init_port(struct dss_device *dss, struct platform_device *pdev,