xref: /linux/drivers/gpu/drm/omapdrm/dss/sdi.c (revision d0034a7a4ac7fae708146ac0059b9c47a1543f0d)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29960aa7cSTomi Valkeinen /*
39960aa7cSTomi Valkeinen  * Copyright (C) 2009 Nokia Corporation
46505d75cSTomi Valkeinen  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
59960aa7cSTomi Valkeinen  */
69960aa7cSTomi Valkeinen 
79960aa7cSTomi Valkeinen #define DSS_SUBSYS_NAME "SDI"
89960aa7cSTomi Valkeinen 
99960aa7cSTomi Valkeinen #include <linux/delay.h>
109960aa7cSTomi Valkeinen #include <linux/err.h>
119960aa7cSTomi Valkeinen #include <linux/export.h>
1213d2d52fSLaurent Pinchart #include <linux/kernel.h>
139960aa7cSTomi Valkeinen #include <linux/of.h>
1413d2d52fSLaurent Pinchart #include <linux/platform_device.h>
1513d2d52fSLaurent Pinchart #include <linux/regulator/consumer.h>
1613d2d52fSLaurent Pinchart #include <linux/string.h>
179960aa7cSTomi Valkeinen 
188bef8a6dSLaurent Pinchart #include <drm/drm_bridge.h>
198bef8a6dSLaurent Pinchart 
209960aa7cSTomi Valkeinen #include "dss.h"
2113d2d52fSLaurent Pinchart #include "omapdss.h"
229960aa7cSTomi Valkeinen 
2324aac601SLaurent Pinchart struct sdi_device {
249960aa7cSTomi Valkeinen 	struct platform_device *pdev;
25d7157dfeSLaurent Pinchart 	struct dss_device *dss;
269960aa7cSTomi Valkeinen 
279960aa7cSTomi Valkeinen 	bool update_enabled;
289960aa7cSTomi Valkeinen 	struct regulator *vdds_sdi_reg;
299960aa7cSTomi Valkeinen 
309960aa7cSTomi Valkeinen 	struct dss_lcd_mgr_config mgr_config;
31e5906f76SLaurent Pinchart 	unsigned long pixelclock;
329960aa7cSTomi Valkeinen 	int datapairs;
339960aa7cSTomi Valkeinen 
349960aa7cSTomi Valkeinen 	struct omap_dss_device output;
358bef8a6dSLaurent Pinchart 	struct drm_bridge bridge;
3624aac601SLaurent Pinchart };
379960aa7cSTomi Valkeinen 
388bef8a6dSLaurent Pinchart #define drm_bridge_to_sdi(bridge) \
398bef8a6dSLaurent Pinchart 	container_of(bridge, struct sdi_device, bridge)
409960aa7cSTomi Valkeinen 
419960aa7cSTomi Valkeinen struct sdi_clk_calc_ctx {
4224aac601SLaurent Pinchart 	struct sdi_device *sdi;
439960aa7cSTomi Valkeinen 	unsigned long pck_min, pck_max;
449960aa7cSTomi Valkeinen 
459960aa7cSTomi Valkeinen 	unsigned long fck;
469960aa7cSTomi Valkeinen 	struct dispc_clock_info dispc_cinfo;
479960aa7cSTomi Valkeinen };
489960aa7cSTomi Valkeinen 
dpi_calc_dispc_cb(int lckd,int pckd,unsigned long lck,unsigned long pck,void * data)499960aa7cSTomi Valkeinen static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
509960aa7cSTomi Valkeinen 		unsigned long pck, void *data)
519960aa7cSTomi Valkeinen {
529960aa7cSTomi Valkeinen 	struct sdi_clk_calc_ctx *ctx = data;
539960aa7cSTomi Valkeinen 
549960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck_div = lckd;
559960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck_div = pckd;
569960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.lck = lck;
579960aa7cSTomi Valkeinen 	ctx->dispc_cinfo.pck = pck;
589960aa7cSTomi Valkeinen 
599960aa7cSTomi Valkeinen 	return true;
609960aa7cSTomi Valkeinen }
619960aa7cSTomi Valkeinen 
dpi_calc_dss_cb(unsigned long fck,void * data)629960aa7cSTomi Valkeinen static bool dpi_calc_dss_cb(unsigned long fck, void *data)
639960aa7cSTomi Valkeinen {
649960aa7cSTomi Valkeinen 	struct sdi_clk_calc_ctx *ctx = data;
659960aa7cSTomi Valkeinen 
669960aa7cSTomi Valkeinen 	ctx->fck = fck;
679960aa7cSTomi Valkeinen 
6824aac601SLaurent Pinchart 	return dispc_div_calc(ctx->sdi->dss->dispc, fck,
698a7eda76SLaurent Pinchart 			      ctx->pck_min, ctx->pck_max,
709960aa7cSTomi Valkeinen 			      dpi_calc_dispc_cb, ctx);
719960aa7cSTomi Valkeinen }
729960aa7cSTomi Valkeinen 
sdi_calc_clock_div(struct sdi_device * sdi,unsigned long pclk,unsigned long * fck,struct dispc_clock_info * dispc_cinfo)7324aac601SLaurent Pinchart static int sdi_calc_clock_div(struct sdi_device *sdi, unsigned long pclk,
749960aa7cSTomi Valkeinen 			      unsigned long *fck,
759960aa7cSTomi Valkeinen 			      struct dispc_clock_info *dispc_cinfo)
769960aa7cSTomi Valkeinen {
779960aa7cSTomi Valkeinen 	int i;
782bc5ff0bSTomi Valkeinen 	struct sdi_clk_calc_ctx ctx;
799960aa7cSTomi Valkeinen 
809960aa7cSTomi Valkeinen 	/*
819960aa7cSTomi Valkeinen 	 * DSS fclk gives us very few possibilities, so finding a good pixel
829960aa7cSTomi Valkeinen 	 * clock may not be possible. We try multiple times to find the clock,
839960aa7cSTomi Valkeinen 	 * each time widening the pixel clock range we look for, up to
849960aa7cSTomi Valkeinen 	 * +/- 1MHz.
859960aa7cSTomi Valkeinen 	 */
869960aa7cSTomi Valkeinen 
879960aa7cSTomi Valkeinen 	for (i = 0; i < 10; ++i) {
889960aa7cSTomi Valkeinen 		bool ok;
899960aa7cSTomi Valkeinen 
909960aa7cSTomi Valkeinen 		memset(&ctx, 0, sizeof(ctx));
912bc5ff0bSTomi Valkeinen 
922bc5ff0bSTomi Valkeinen 		ctx.sdi = sdi;
932bc5ff0bSTomi Valkeinen 
949960aa7cSTomi Valkeinen 		if (pclk > 1000 * i * i * i)
959960aa7cSTomi Valkeinen 			ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu);
969960aa7cSTomi Valkeinen 		else
979960aa7cSTomi Valkeinen 			ctx.pck_min = 0;
989960aa7cSTomi Valkeinen 		ctx.pck_max = pclk + 1000 * i * i * i;
999960aa7cSTomi Valkeinen 
10024aac601SLaurent Pinchart 		ok = dss_div_calc(sdi->dss, pclk, ctx.pck_min,
10160f9c59fSLaurent Pinchart 				  dpi_calc_dss_cb, &ctx);
1029960aa7cSTomi Valkeinen 		if (ok) {
1039960aa7cSTomi Valkeinen 			*fck = ctx.fck;
1049960aa7cSTomi Valkeinen 			*dispc_cinfo = ctx.dispc_cinfo;
1059960aa7cSTomi Valkeinen 			return 0;
1069960aa7cSTomi Valkeinen 		}
1079960aa7cSTomi Valkeinen 	}
1089960aa7cSTomi Valkeinen 
1099960aa7cSTomi Valkeinen 	return -EINVAL;
1109960aa7cSTomi Valkeinen }
1119960aa7cSTomi Valkeinen 
sdi_config_lcd_manager(struct sdi_device * sdi)11224aac601SLaurent Pinchart static void sdi_config_lcd_manager(struct sdi_device *sdi)
1139960aa7cSTomi Valkeinen {
11424aac601SLaurent Pinchart 	sdi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
1159960aa7cSTomi Valkeinen 
11624aac601SLaurent Pinchart 	sdi->mgr_config.stallmode = false;
11724aac601SLaurent Pinchart 	sdi->mgr_config.fifohandcheck = false;
1189960aa7cSTomi Valkeinen 
11924aac601SLaurent Pinchart 	sdi->mgr_config.video_port_width = 24;
12024aac601SLaurent Pinchart 	sdi->mgr_config.lcden_sig_polarity = 1;
1219960aa7cSTomi Valkeinen 
12224aac601SLaurent Pinchart 	dss_mgr_set_lcd_config(&sdi->output, &sdi->mgr_config);
1239960aa7cSTomi Valkeinen }
1249960aa7cSTomi Valkeinen 
1258bef8a6dSLaurent Pinchart /* -----------------------------------------------------------------------------
1268bef8a6dSLaurent Pinchart  * DRM Bridge Operations
1278bef8a6dSLaurent Pinchart  */
1288bef8a6dSLaurent Pinchart 
sdi_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)1298bef8a6dSLaurent Pinchart static int sdi_bridge_attach(struct drm_bridge *bridge,
1308bef8a6dSLaurent Pinchart 			     enum drm_bridge_attach_flags flags)
1319960aa7cSTomi Valkeinen {
1328bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
1338bef8a6dSLaurent Pinchart 
1348bef8a6dSLaurent Pinchart 	if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
1358bef8a6dSLaurent Pinchart 		return -EINVAL;
1368bef8a6dSLaurent Pinchart 
1378bef8a6dSLaurent Pinchart 	return drm_bridge_attach(bridge->encoder, sdi->output.next_bridge,
1388bef8a6dSLaurent Pinchart 				 bridge, flags);
1398bef8a6dSLaurent Pinchart }
1408bef8a6dSLaurent Pinchart 
1418bef8a6dSLaurent Pinchart static enum drm_mode_status
sdi_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)1428bef8a6dSLaurent Pinchart sdi_bridge_mode_valid(struct drm_bridge *bridge,
14312c683e1SLaurent Pinchart 		      const struct drm_display_info *info,
1448bef8a6dSLaurent Pinchart 		      const struct drm_display_mode *mode)
1458bef8a6dSLaurent Pinchart {
1468bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
1478bef8a6dSLaurent Pinchart 	unsigned long pixelclock = mode->clock * 1000;
1488bef8a6dSLaurent Pinchart 	struct dispc_clock_info dispc_cinfo;
1498bef8a6dSLaurent Pinchart 	unsigned long fck;
1508bef8a6dSLaurent Pinchart 	int ret;
1518bef8a6dSLaurent Pinchart 
1528bef8a6dSLaurent Pinchart 	if (pixelclock == 0)
1538bef8a6dSLaurent Pinchart 		return MODE_NOCLOCK;
1548bef8a6dSLaurent Pinchart 
1558bef8a6dSLaurent Pinchart 	ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
1568bef8a6dSLaurent Pinchart 	if (ret < 0)
1578bef8a6dSLaurent Pinchart 		return MODE_CLOCK_RANGE;
1588bef8a6dSLaurent Pinchart 
1598bef8a6dSLaurent Pinchart 	return MODE_OK;
1608bef8a6dSLaurent Pinchart }
1618bef8a6dSLaurent Pinchart 
sdi_bridge_mode_fixup(struct drm_bridge * bridge,const struct drm_display_mode * mode,struct drm_display_mode * adjusted_mode)1628bef8a6dSLaurent Pinchart static bool sdi_bridge_mode_fixup(struct drm_bridge *bridge,
1638bef8a6dSLaurent Pinchart 				  const struct drm_display_mode *mode,
1648bef8a6dSLaurent Pinchart 				  struct drm_display_mode *adjusted_mode)
1658bef8a6dSLaurent Pinchart {
1668bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
1678bef8a6dSLaurent Pinchart 	unsigned long pixelclock = mode->clock * 1000;
1688bef8a6dSLaurent Pinchart 	struct dispc_clock_info dispc_cinfo;
1698bef8a6dSLaurent Pinchart 	unsigned long fck;
1708bef8a6dSLaurent Pinchart 	unsigned long pck;
1718bef8a6dSLaurent Pinchart 	int ret;
1728bef8a6dSLaurent Pinchart 
1738bef8a6dSLaurent Pinchart 	ret = sdi_calc_clock_div(sdi, pixelclock, &fck, &dispc_cinfo);
1748bef8a6dSLaurent Pinchart 	if (ret < 0)
1758bef8a6dSLaurent Pinchart 		return false;
1768bef8a6dSLaurent Pinchart 
1778bef8a6dSLaurent Pinchart 	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
1788bef8a6dSLaurent Pinchart 
1798bef8a6dSLaurent Pinchart 	if (pck != pixelclock)
1808bef8a6dSLaurent Pinchart 		dev_dbg(&sdi->pdev->dev,
1818bef8a6dSLaurent Pinchart 			"pixel clock adjusted from %lu Hz to %lu Hz\n",
1828bef8a6dSLaurent Pinchart 			pixelclock, pck);
1838bef8a6dSLaurent Pinchart 
1848bef8a6dSLaurent Pinchart 	adjusted_mode->clock = pck / 1000;
1858bef8a6dSLaurent Pinchart 
1868bef8a6dSLaurent Pinchart 	return true;
1878bef8a6dSLaurent Pinchart }
1888bef8a6dSLaurent Pinchart 
sdi_bridge_mode_set(struct drm_bridge * bridge,const struct drm_display_mode * mode,const struct drm_display_mode * adjusted_mode)1898bef8a6dSLaurent Pinchart static void sdi_bridge_mode_set(struct drm_bridge *bridge,
1908bef8a6dSLaurent Pinchart 				const struct drm_display_mode *mode,
1918bef8a6dSLaurent Pinchart 				const struct drm_display_mode *adjusted_mode)
1928bef8a6dSLaurent Pinchart {
1938bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
1948bef8a6dSLaurent Pinchart 
1958bef8a6dSLaurent Pinchart 	sdi->pixelclock = adjusted_mode->clock * 1000;
1968bef8a6dSLaurent Pinchart }
1978bef8a6dSLaurent Pinchart 
sdi_bridge_enable(struct drm_bridge * bridge)198*fd4e788eSTomi Valkeinen static void sdi_bridge_enable(struct drm_bridge *bridge)
1998bef8a6dSLaurent Pinchart {
2008bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
2019960aa7cSTomi Valkeinen 	struct dispc_clock_info dispc_cinfo;
20296fc64c7SLaurent Pinchart 	unsigned long fck;
2039960aa7cSTomi Valkeinen 	int r;
2049960aa7cSTomi Valkeinen 
20524aac601SLaurent Pinchart 	r = regulator_enable(sdi->vdds_sdi_reg);
2069960aa7cSTomi Valkeinen 	if (r)
20719b4200dSLaurent Pinchart 		return;
2089960aa7cSTomi Valkeinen 
20924aac601SLaurent Pinchart 	r = dispc_runtime_get(sdi->dss->dispc);
2109960aa7cSTomi Valkeinen 	if (r)
2119960aa7cSTomi Valkeinen 		goto err_get_dispc;
2129960aa7cSTomi Valkeinen 
213e5906f76SLaurent Pinchart 	r = sdi_calc_clock_div(sdi, sdi->pixelclock, &fck, &dispc_cinfo);
2149960aa7cSTomi Valkeinen 	if (r)
2159960aa7cSTomi Valkeinen 		goto err_calc_clock_div;
2169960aa7cSTomi Valkeinen 
21724aac601SLaurent Pinchart 	sdi->mgr_config.clock_info = dispc_cinfo;
2189960aa7cSTomi Valkeinen 
21924aac601SLaurent Pinchart 	r = dss_set_fck_rate(sdi->dss, fck);
2209960aa7cSTomi Valkeinen 	if (r)
2219960aa7cSTomi Valkeinen 		goto err_set_dss_clock_div;
2229960aa7cSTomi Valkeinen 
22324aac601SLaurent Pinchart 	sdi_config_lcd_manager(sdi);
2249960aa7cSTomi Valkeinen 
2259960aa7cSTomi Valkeinen 	/*
2269960aa7cSTomi Valkeinen 	 * LCLK and PCLK divisors are located in shadow registers, and we
2279960aa7cSTomi Valkeinen 	 * normally write them to DISPC registers when enabling the output.
2289960aa7cSTomi Valkeinen 	 * However, SDI uses pck-free as source clock for its PLL, and pck-free
2299960aa7cSTomi Valkeinen 	 * is affected by the divisors. And as we need the PLL before enabling
2309960aa7cSTomi Valkeinen 	 * the output, we need to write the divisors early.
2319960aa7cSTomi Valkeinen 	 *
2329960aa7cSTomi Valkeinen 	 * It seems just writing to the DISPC register is enough, and we don't
2339960aa7cSTomi Valkeinen 	 * need to care about the shadow register mechanism for pck-free. The
2349960aa7cSTomi Valkeinen 	 * exact reason for this is unknown.
2359960aa7cSTomi Valkeinen 	 */
23624aac601SLaurent Pinchart 	dispc_mgr_set_clock_div(sdi->dss->dispc, sdi->output.dispc_channel,
23724aac601SLaurent Pinchart 				&sdi->mgr_config.clock_info);
2389960aa7cSTomi Valkeinen 
23924aac601SLaurent Pinchart 	dss_sdi_init(sdi->dss, sdi->datapairs);
24024aac601SLaurent Pinchart 	r = dss_sdi_enable(sdi->dss);
2419960aa7cSTomi Valkeinen 	if (r)
2429960aa7cSTomi Valkeinen 		goto err_sdi_enable;
2439960aa7cSTomi Valkeinen 	mdelay(2);
2449960aa7cSTomi Valkeinen 
24524aac601SLaurent Pinchart 	r = dss_mgr_enable(&sdi->output);
2469960aa7cSTomi Valkeinen 	if (r)
2479960aa7cSTomi Valkeinen 		goto err_mgr_enable;
2489960aa7cSTomi Valkeinen 
24919b4200dSLaurent Pinchart 	return;
2509960aa7cSTomi Valkeinen 
2519960aa7cSTomi Valkeinen err_mgr_enable:
25224aac601SLaurent Pinchart 	dss_sdi_disable(sdi->dss);
2539960aa7cSTomi Valkeinen err_sdi_enable:
2549960aa7cSTomi Valkeinen err_set_dss_clock_div:
2559960aa7cSTomi Valkeinen err_calc_clock_div:
25624aac601SLaurent Pinchart 	dispc_runtime_put(sdi->dss->dispc);
2579960aa7cSTomi Valkeinen err_get_dispc:
25824aac601SLaurent Pinchart 	regulator_disable(sdi->vdds_sdi_reg);
2599960aa7cSTomi Valkeinen }
2609960aa7cSTomi Valkeinen 
sdi_bridge_disable(struct drm_bridge * bridge)261*fd4e788eSTomi Valkeinen static void sdi_bridge_disable(struct drm_bridge *bridge)
2629960aa7cSTomi Valkeinen {
2638bef8a6dSLaurent Pinchart 	struct sdi_device *sdi = drm_bridge_to_sdi(bridge);
2649960aa7cSTomi Valkeinen 
26524aac601SLaurent Pinchart 	dss_mgr_disable(&sdi->output);
2669960aa7cSTomi Valkeinen 
26724aac601SLaurent Pinchart 	dss_sdi_disable(sdi->dss);
2689960aa7cSTomi Valkeinen 
26924aac601SLaurent Pinchart 	dispc_runtime_put(sdi->dss->dispc);
27024aac601SLaurent Pinchart 
27124aac601SLaurent Pinchart 	regulator_disable(sdi->vdds_sdi_reg);
2729960aa7cSTomi Valkeinen }
2739960aa7cSTomi Valkeinen 
2748bef8a6dSLaurent Pinchart static const struct drm_bridge_funcs sdi_bridge_funcs = {
2758bef8a6dSLaurent Pinchart 	.attach = sdi_bridge_attach,
2768bef8a6dSLaurent Pinchart 	.mode_valid = sdi_bridge_mode_valid,
2778bef8a6dSLaurent Pinchart 	.mode_fixup = sdi_bridge_mode_fixup,
2788bef8a6dSLaurent Pinchart 	.mode_set = sdi_bridge_mode_set,
279*fd4e788eSTomi Valkeinen 	.enable = sdi_bridge_enable,
280*fd4e788eSTomi Valkeinen 	.disable = sdi_bridge_disable,
2819960aa7cSTomi Valkeinen };
2829960aa7cSTomi Valkeinen 
sdi_bridge_init(struct sdi_device * sdi)2838bef8a6dSLaurent Pinchart static void sdi_bridge_init(struct sdi_device *sdi)
2848bef8a6dSLaurent Pinchart {
2858bef8a6dSLaurent Pinchart 	sdi->bridge.funcs = &sdi_bridge_funcs;
2868bef8a6dSLaurent Pinchart 	sdi->bridge.of_node = sdi->pdev->dev.of_node;
2878bef8a6dSLaurent Pinchart 	sdi->bridge.type = DRM_MODE_CONNECTOR_LVDS;
2888bef8a6dSLaurent Pinchart 
2898bef8a6dSLaurent Pinchart 	drm_bridge_add(&sdi->bridge);
2908bef8a6dSLaurent Pinchart }
2918bef8a6dSLaurent Pinchart 
sdi_bridge_cleanup(struct sdi_device * sdi)2928bef8a6dSLaurent Pinchart static void sdi_bridge_cleanup(struct sdi_device *sdi)
2938bef8a6dSLaurent Pinchart {
2948bef8a6dSLaurent Pinchart 	drm_bridge_remove(&sdi->bridge);
2958bef8a6dSLaurent Pinchart }
2968bef8a6dSLaurent Pinchart 
2978bef8a6dSLaurent Pinchart /* -----------------------------------------------------------------------------
2988bef8a6dSLaurent Pinchart  * Initialisation and Cleanup
2998bef8a6dSLaurent Pinchart  */
3008bef8a6dSLaurent Pinchart 
sdi_init_output(struct sdi_device * sdi)30127d62452SLaurent Pinchart static int sdi_init_output(struct sdi_device *sdi)
3029960aa7cSTomi Valkeinen {
30324aac601SLaurent Pinchart 	struct omap_dss_device *out = &sdi->output;
30471316556SLaurent Pinchart 	int r;
3059960aa7cSTomi Valkeinen 
3068bef8a6dSLaurent Pinchart 	sdi_bridge_init(sdi);
3078bef8a6dSLaurent Pinchart 
30824aac601SLaurent Pinchart 	out->dev = &sdi->pdev->dev;
3099960aa7cSTomi Valkeinen 	out->id = OMAP_DSS_OUTPUT_SDI;
3100dbfc396SLaurent Pinchart 	out->type = OMAP_DISPLAY_TYPE_SDI;
3119960aa7cSTomi Valkeinen 	out->name = "sdi.0";
3129960aa7cSTomi Valkeinen 	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
3139960aa7cSTomi Valkeinen 	/* We have SDI only on OMAP3, where it's on port 1 */
314c83fefd7SLaurent Pinchart 	out->of_port = 1;
31588bc4178SLaurent Pinchart 	out->bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE	/* 15.5.9.1.2 */
31688bc4178SLaurent Pinchart 		       | DRM_BUS_FLAG_SYNC_DRIVE_POSEDGE;
3179960aa7cSTomi Valkeinen 
3188bef8a6dSLaurent Pinchart 	r = omapdss_device_init_output(out, &sdi->bridge);
3198bef8a6dSLaurent Pinchart 	if (r < 0) {
3208bef8a6dSLaurent Pinchart 		sdi_bridge_cleanup(sdi);
32171316556SLaurent Pinchart 		return r;
3228bef8a6dSLaurent Pinchart 	}
32371316556SLaurent Pinchart 
324de57e9dbSLaurent Pinchart 	omapdss_device_register(out);
32527d62452SLaurent Pinchart 
32627d62452SLaurent Pinchart 	return 0;
3279960aa7cSTomi Valkeinen }
3289960aa7cSTomi Valkeinen 
sdi_uninit_output(struct sdi_device * sdi)32924aac601SLaurent Pinchart static void sdi_uninit_output(struct sdi_device *sdi)
3309960aa7cSTomi Valkeinen {
331de57e9dbSLaurent Pinchart 	omapdss_device_unregister(&sdi->output);
332d17eb453SLaurent Pinchart 	omapdss_device_cleanup_output(&sdi->output);
3338bef8a6dSLaurent Pinchart 
3348bef8a6dSLaurent Pinchart 	sdi_bridge_cleanup(sdi);
3359960aa7cSTomi Valkeinen }
3369960aa7cSTomi Valkeinen 
sdi_init_port(struct dss_device * dss,struct platform_device * pdev,struct device_node * port)337d7157dfeSLaurent Pinchart int sdi_init_port(struct dss_device *dss, struct platform_device *pdev,
338d7157dfeSLaurent Pinchart 		  struct device_node *port)
3399960aa7cSTomi Valkeinen {
34024aac601SLaurent Pinchart 	struct sdi_device *sdi;
3419960aa7cSTomi Valkeinen 	struct device_node *ep;
3429960aa7cSTomi Valkeinen 	u32 datapairs;
3439960aa7cSTomi Valkeinen 	int r;
3449960aa7cSTomi Valkeinen 
34524aac601SLaurent Pinchart 	sdi = kzalloc(sizeof(*sdi), GFP_KERNEL);
34624aac601SLaurent Pinchart 	if (!sdi)
34724aac601SLaurent Pinchart 		return -ENOMEM;
34824aac601SLaurent Pinchart 
34909bffa6eSRob Herring 	ep = of_get_next_child(port, NULL);
35024aac601SLaurent Pinchart 	if (!ep) {
35124aac601SLaurent Pinchart 		r = 0;
35224aac601SLaurent Pinchart 		goto err_free;
35324aac601SLaurent Pinchart 	}
3549960aa7cSTomi Valkeinen 
3559960aa7cSTomi Valkeinen 	r = of_property_read_u32(ep, "datapairs", &datapairs);
35666aacfe2SLaurent Pinchart 	of_node_put(ep);
3579960aa7cSTomi Valkeinen 	if (r) {
3589960aa7cSTomi Valkeinen 		DSSERR("failed to parse datapairs\n");
35966aacfe2SLaurent Pinchart 		goto err_free;
3609960aa7cSTomi Valkeinen 	}
3619960aa7cSTomi Valkeinen 
36224aac601SLaurent Pinchart 	sdi->datapairs = datapairs;
36324aac601SLaurent Pinchart 	sdi->dss = dss;
3649960aa7cSTomi Valkeinen 
36524aac601SLaurent Pinchart 	sdi->pdev = pdev;
36624aac601SLaurent Pinchart 	port->data = sdi;
3679960aa7cSTomi Valkeinen 
3688a36357aSLaurent Pinchart 	sdi->vdds_sdi_reg = devm_regulator_get(&pdev->dev, "vdds_sdi");
3698a36357aSLaurent Pinchart 	if (IS_ERR(sdi->vdds_sdi_reg)) {
3708a36357aSLaurent Pinchart 		r = PTR_ERR(sdi->vdds_sdi_reg);
3718a36357aSLaurent Pinchart 		if (r != -EPROBE_DEFER)
3728a36357aSLaurent Pinchart 			DSSERR("can't get VDDS_SDI regulator\n");
3738a36357aSLaurent Pinchart 		goto err_free;
3748a36357aSLaurent Pinchart 	}
3758a36357aSLaurent Pinchart 
37627d62452SLaurent Pinchart 	r = sdi_init_output(sdi);
37727d62452SLaurent Pinchart 	if (r)
37827d62452SLaurent Pinchart 		goto err_free;
3799960aa7cSTomi Valkeinen 
3809960aa7cSTomi Valkeinen 	return 0;
3819960aa7cSTomi Valkeinen 
38224aac601SLaurent Pinchart err_free:
38324aac601SLaurent Pinchart 	kfree(sdi);
3849960aa7cSTomi Valkeinen 
3859960aa7cSTomi Valkeinen 	return r;
3869960aa7cSTomi Valkeinen }
3879960aa7cSTomi Valkeinen 
sdi_uninit_port(struct device_node * port)3889960aa7cSTomi Valkeinen void sdi_uninit_port(struct device_node *port)
3899960aa7cSTomi Valkeinen {
39024aac601SLaurent Pinchart 	struct sdi_device *sdi = port->data;
39124aac601SLaurent Pinchart 
39224aac601SLaurent Pinchart 	if (!sdi)
3939960aa7cSTomi Valkeinen 		return;
3949960aa7cSTomi Valkeinen 
39524aac601SLaurent Pinchart 	sdi_uninit_output(sdi);
39624aac601SLaurent Pinchart 	kfree(sdi);
3979960aa7cSTomi Valkeinen }
398