18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/video/omap2/dss/sdi.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nokia Corporation
68c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define DSS_SUBSYS_NAME "SDI"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
158c2ecf20Sopenharmony_ci#include <linux/export.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/component.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <video/omapfb_dss.h>
228c2ecf20Sopenharmony_ci#include "dss.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct {
258c2ecf20Sopenharmony_ci	struct platform_device *pdev;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	bool update_enabled;
288c2ecf20Sopenharmony_ci	struct regulator *vdds_sdi_reg;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	struct dss_lcd_mgr_config mgr_config;
318c2ecf20Sopenharmony_ci	struct omap_video_timings timings;
328c2ecf20Sopenharmony_ci	int datapairs;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	struct omap_dss_device output;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	bool port_initialized;
378c2ecf20Sopenharmony_ci} sdi;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct sdi_clk_calc_ctx {
408c2ecf20Sopenharmony_ci	unsigned long pck_min, pck_max;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	unsigned long fck;
438c2ecf20Sopenharmony_ci	struct dispc_clock_info dispc_cinfo;
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
478c2ecf20Sopenharmony_ci		unsigned long pck, void *data)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct sdi_clk_calc_ctx *ctx = data;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	ctx->dispc_cinfo.lck_div = lckd;
528c2ecf20Sopenharmony_ci	ctx->dispc_cinfo.pck_div = pckd;
538c2ecf20Sopenharmony_ci	ctx->dispc_cinfo.lck = lck;
548c2ecf20Sopenharmony_ci	ctx->dispc_cinfo.pck = pck;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return true;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic bool dpi_calc_dss_cb(unsigned long fck, void *data)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct sdi_clk_calc_ctx *ctx = data;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ctx->fck = fck;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
668c2ecf20Sopenharmony_ci			dpi_calc_dispc_cb, ctx);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int sdi_calc_clock_div(unsigned long pclk,
708c2ecf20Sopenharmony_ci		unsigned long *fck,
718c2ecf20Sopenharmony_ci		struct dispc_clock_info *dispc_cinfo)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	int i;
748c2ecf20Sopenharmony_ci	struct sdi_clk_calc_ctx ctx;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * DSS fclk gives us very few possibilities, so finding a good pixel
788c2ecf20Sopenharmony_ci	 * clock may not be possible. We try multiple times to find the clock,
798c2ecf20Sopenharmony_ci	 * each time widening the pixel clock range we look for, up to
808c2ecf20Sopenharmony_ci	 * +/- 1MHz.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	for (i = 0; i < 10; ++i) {
848c2ecf20Sopenharmony_ci		bool ok;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		memset(&ctx, 0, sizeof(ctx));
878c2ecf20Sopenharmony_ci		if (pclk > 1000 * i * i * i)
888c2ecf20Sopenharmony_ci			ctx.pck_min = max(pclk - 1000 * i * i * i, 0lu);
898c2ecf20Sopenharmony_ci		else
908c2ecf20Sopenharmony_ci			ctx.pck_min = 0;
918c2ecf20Sopenharmony_ci		ctx.pck_max = pclk + 1000 * i * i * i;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx);
948c2ecf20Sopenharmony_ci		if (ok) {
958c2ecf20Sopenharmony_ci			*fck = ctx.fck;
968c2ecf20Sopenharmony_ci			*dispc_cinfo = ctx.dispc_cinfo;
978c2ecf20Sopenharmony_ci			return 0;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return -EINVAL;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct omap_overlay_manager *mgr = sdi.output.manager;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	sdi.mgr_config.stallmode = false;
1118c2ecf20Sopenharmony_ci	sdi.mgr_config.fifohandcheck = false;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	sdi.mgr_config.video_port_width = 24;
1148c2ecf20Sopenharmony_ci	sdi.mgr_config.lcden_sig_polarity = 1;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	dss_mgr_set_lcd_config(mgr, &sdi.mgr_config);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int sdi_display_enable(struct omap_dss_device *dssdev)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct omap_dss_device *out = &sdi.output;
1228c2ecf20Sopenharmony_ci	struct omap_video_timings *t = &sdi.timings;
1238c2ecf20Sopenharmony_ci	unsigned long fck;
1248c2ecf20Sopenharmony_ci	struct dispc_clock_info dispc_cinfo;
1258c2ecf20Sopenharmony_ci	unsigned long pck;
1268c2ecf20Sopenharmony_ci	int r;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (out->manager == NULL) {
1298c2ecf20Sopenharmony_ci		DSSERR("failed to enable display: no output/manager\n");
1308c2ecf20Sopenharmony_ci		return -ENODEV;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	r = regulator_enable(sdi.vdds_sdi_reg);
1348c2ecf20Sopenharmony_ci	if (r)
1358c2ecf20Sopenharmony_ci		goto err_reg_enable;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	r = dispc_runtime_get();
1388c2ecf20Sopenharmony_ci	if (r)
1398c2ecf20Sopenharmony_ci		goto err_get_dispc;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/* 15.5.9.1.2 */
1428c2ecf20Sopenharmony_ci	t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
1438c2ecf20Sopenharmony_ci	t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	r = sdi_calc_clock_div(t->pixelclock, &fck, &dispc_cinfo);
1468c2ecf20Sopenharmony_ci	if (r)
1478c2ecf20Sopenharmony_ci		goto err_calc_clock_div;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	sdi.mgr_config.clock_info = dispc_cinfo;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (pck != t->pixelclock) {
1548c2ecf20Sopenharmony_ci		DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",
1558c2ecf20Sopenharmony_ci			t->pixelclock, pck);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci		t->pixelclock = pck;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	dss_mgr_set_timings(out->manager, t);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	r = dss_set_fck_rate(fck);
1648c2ecf20Sopenharmony_ci	if (r)
1658c2ecf20Sopenharmony_ci		goto err_set_dss_clock_div;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	sdi_config_lcd_manager(dssdev);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * LCLK and PCLK divisors are located in shadow registers, and we
1718c2ecf20Sopenharmony_ci	 * normally write them to DISPC registers when enabling the output.
1728c2ecf20Sopenharmony_ci	 * However, SDI uses pck-free as source clock for its PLL, and pck-free
1738c2ecf20Sopenharmony_ci	 * is affected by the divisors. And as we need the PLL before enabling
1748c2ecf20Sopenharmony_ci	 * the output, we need to write the divisors early.
1758c2ecf20Sopenharmony_ci	 *
1768c2ecf20Sopenharmony_ci	 * It seems just writing to the DISPC register is enough, and we don't
1778c2ecf20Sopenharmony_ci	 * need to care about the shadow register mechanism for pck-free. The
1788c2ecf20Sopenharmony_ci	 * exact reason for this is unknown.
1798c2ecf20Sopenharmony_ci	 */
1808c2ecf20Sopenharmony_ci	dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	dss_sdi_init(sdi.datapairs);
1838c2ecf20Sopenharmony_ci	r = dss_sdi_enable();
1848c2ecf20Sopenharmony_ci	if (r)
1858c2ecf20Sopenharmony_ci		goto err_sdi_enable;
1868c2ecf20Sopenharmony_ci	mdelay(2);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	r = dss_mgr_enable(out->manager);
1898c2ecf20Sopenharmony_ci	if (r)
1908c2ecf20Sopenharmony_ci		goto err_mgr_enable;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cierr_mgr_enable:
1958c2ecf20Sopenharmony_ci	dss_sdi_disable();
1968c2ecf20Sopenharmony_cierr_sdi_enable:
1978c2ecf20Sopenharmony_cierr_set_dss_clock_div:
1988c2ecf20Sopenharmony_cierr_calc_clock_div:
1998c2ecf20Sopenharmony_ci	dispc_runtime_put();
2008c2ecf20Sopenharmony_cierr_get_dispc:
2018c2ecf20Sopenharmony_ci	regulator_disable(sdi.vdds_sdi_reg);
2028c2ecf20Sopenharmony_cierr_reg_enable:
2038c2ecf20Sopenharmony_ci	return r;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void sdi_display_disable(struct omap_dss_device *dssdev)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct omap_overlay_manager *mgr = sdi.output.manager;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	dss_mgr_disable(mgr);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	dss_sdi_disable();
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	dispc_runtime_put();
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	regulator_disable(sdi.vdds_sdi_reg);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void sdi_set_timings(struct omap_dss_device *dssdev,
2208c2ecf20Sopenharmony_ci		struct omap_video_timings *timings)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	sdi.timings = *timings;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void sdi_get_timings(struct omap_dss_device *dssdev,
2268c2ecf20Sopenharmony_ci		struct omap_video_timings *timings)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	*timings = sdi.timings;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic int sdi_check_timings(struct omap_dss_device *dssdev,
2328c2ecf20Sopenharmony_ci			struct omap_video_timings *timings)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct omap_overlay_manager *mgr = sdi.output.manager;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
2378c2ecf20Sopenharmony_ci		return -EINVAL;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (timings->pixelclock == 0)
2408c2ecf20Sopenharmony_ci		return -EINVAL;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	return 0;
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	sdi.datapairs = datapairs;
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int sdi_init_regulator(void)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct regulator *vdds_sdi;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (sdi.vdds_sdi_reg)
2558c2ecf20Sopenharmony_ci		return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");
2588c2ecf20Sopenharmony_ci	if (IS_ERR(vdds_sdi)) {
2598c2ecf20Sopenharmony_ci		if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER)
2608c2ecf20Sopenharmony_ci			DSSERR("can't get VDDS_SDI regulator\n");
2618c2ecf20Sopenharmony_ci		return PTR_ERR(vdds_sdi);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	sdi.vdds_sdi_reg = vdds_sdi;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int sdi_connect(struct omap_dss_device *dssdev,
2708c2ecf20Sopenharmony_ci		struct omap_dss_device *dst)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	struct omap_overlay_manager *mgr;
2738c2ecf20Sopenharmony_ci	int r;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	r = sdi_init_regulator();
2768c2ecf20Sopenharmony_ci	if (r)
2778c2ecf20Sopenharmony_ci		return r;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
2808c2ecf20Sopenharmony_ci	if (!mgr)
2818c2ecf20Sopenharmony_ci		return -ENODEV;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	r = dss_mgr_connect(mgr, dssdev);
2848c2ecf20Sopenharmony_ci	if (r)
2858c2ecf20Sopenharmony_ci		return r;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	r = omapdss_output_set_device(dssdev, dst);
2888c2ecf20Sopenharmony_ci	if (r) {
2898c2ecf20Sopenharmony_ci		DSSERR("failed to connect output to new device: %s\n",
2908c2ecf20Sopenharmony_ci				dst->name);
2918c2ecf20Sopenharmony_ci		dss_mgr_disconnect(mgr, dssdev);
2928c2ecf20Sopenharmony_ci		return r;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void sdi_disconnect(struct omap_dss_device *dssdev,
2998c2ecf20Sopenharmony_ci		struct omap_dss_device *dst)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	WARN_ON(dst != dssdev->dst);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (dst != dssdev->dst)
3048c2ecf20Sopenharmony_ci		return;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	omapdss_output_unset_device(dssdev);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (dssdev->manager)
3098c2ecf20Sopenharmony_ci		dss_mgr_disconnect(dssdev->manager, dssdev);
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic const struct omapdss_sdi_ops sdi_ops = {
3138c2ecf20Sopenharmony_ci	.connect = sdi_connect,
3148c2ecf20Sopenharmony_ci	.disconnect = sdi_disconnect,
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	.enable = sdi_display_enable,
3178c2ecf20Sopenharmony_ci	.disable = sdi_display_disable,
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	.check_timings = sdi_check_timings,
3208c2ecf20Sopenharmony_ci	.set_timings = sdi_set_timings,
3218c2ecf20Sopenharmony_ci	.get_timings = sdi_get_timings,
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	.set_datapairs = sdi_set_datapairs,
3248c2ecf20Sopenharmony_ci};
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void sdi_init_output(struct platform_device *pdev)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct omap_dss_device *out = &sdi.output;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	out->dev = &pdev->dev;
3318c2ecf20Sopenharmony_ci	out->id = OMAP_DSS_OUTPUT_SDI;
3328c2ecf20Sopenharmony_ci	out->output_type = OMAP_DISPLAY_TYPE_SDI;
3338c2ecf20Sopenharmony_ci	out->name = "sdi.0";
3348c2ecf20Sopenharmony_ci	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
3358c2ecf20Sopenharmony_ci	/* We have SDI only on OMAP3, where it's on port 1 */
3368c2ecf20Sopenharmony_ci	out->port_num = 1;
3378c2ecf20Sopenharmony_ci	out->ops.sdi = &sdi_ops;
3388c2ecf20Sopenharmony_ci	out->owner = THIS_MODULE;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	omapdss_register_output(out);
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void sdi_uninit_output(struct platform_device *pdev)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct omap_dss_device *out = &sdi.output;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	omapdss_unregister_output(out);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int sdi_bind(struct device *dev, struct device *master, void *data)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	sdi.pdev = pdev;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	sdi_init_output(pdev);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void sdi_unbind(struct device *dev, struct device *master, void *data)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	sdi_uninit_output(pdev);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic const struct component_ops sdi_component_ops = {
3698c2ecf20Sopenharmony_ci	.bind	= sdi_bind,
3708c2ecf20Sopenharmony_ci	.unbind	= sdi_unbind,
3718c2ecf20Sopenharmony_ci};
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int sdi_probe(struct platform_device *pdev)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	return component_add(&pdev->dev, &sdi_component_ops);
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int sdi_remove(struct platform_device *pdev)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	component_del(&pdev->dev, &sdi_component_ops);
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic struct platform_driver omap_sdi_driver = {
3858c2ecf20Sopenharmony_ci	.probe		= sdi_probe,
3868c2ecf20Sopenharmony_ci	.remove         = sdi_remove,
3878c2ecf20Sopenharmony_ci	.driver         = {
3888c2ecf20Sopenharmony_ci		.name   = "omapdss_sdi",
3898c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
3908c2ecf20Sopenharmony_ci	},
3918c2ecf20Sopenharmony_ci};
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ciint __init sdi_init_platform_driver(void)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	return platform_driver_register(&omap_sdi_driver);
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_civoid sdi_uninit_platform_driver(void)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	platform_driver_unregister(&omap_sdi_driver);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciint sdi_init_port(struct platform_device *pdev, struct device_node *port)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct device_node *ep;
4068c2ecf20Sopenharmony_ci	u32 datapairs;
4078c2ecf20Sopenharmony_ci	int r;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	ep = omapdss_of_get_next_endpoint(port, NULL);
4108c2ecf20Sopenharmony_ci	if (!ep)
4118c2ecf20Sopenharmony_ci		return 0;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	r = of_property_read_u32(ep, "datapairs", &datapairs);
4148c2ecf20Sopenharmony_ci	if (r) {
4158c2ecf20Sopenharmony_ci		DSSERR("failed to parse datapairs\n");
4168c2ecf20Sopenharmony_ci		goto err_datapairs;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	sdi.datapairs = datapairs;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	of_node_put(ep);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	sdi.pdev = pdev;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	sdi_init_output(pdev);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	sdi.port_initialized = true;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cierr_datapairs:
4328c2ecf20Sopenharmony_ci	of_node_put(ep);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	return r;
4358c2ecf20Sopenharmony_ci}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_civoid sdi_uninit_port(struct device_node *port)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	if (!sdi.port_initialized)
4408c2ecf20Sopenharmony_ci		return;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	sdi_uninit_output(sdi.pdev);
4438c2ecf20Sopenharmony_ci}
444