brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · c40b87f Raw
888 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/video/omap2/dss/dpi.c4 *5 * Copyright (C) 2009 Nokia Corporation6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>7 *8 * Some code and ideas taken from drivers/video/omap/ driver9 * by Imre Deak.10 */11 12#define DSS_SUBSYS_NAME "DPI"13 14#include <linux/kernel.h>15#include <linux/delay.h>16#include <linux/export.h>17#include <linux/err.h>18#include <linux/errno.h>19#include <linux/platform_device.h>20#include <linux/regulator/consumer.h>21#include <linux/string.h>22#include <linux/of.h>23#include <linux/clk.h>24#include <linux/component.h>25 26#include <video/omapfb_dss.h>27 28#include "dss.h"29#include "dss_features.h"30 31#define HSDIV_DISPC	032 33struct dpi_data {34	struct platform_device *pdev;35 36	struct regulator *vdds_dsi_reg;37	struct dss_pll *pll;38 39	struct mutex lock;40 41	struct omap_video_timings timings;42	struct dss_lcd_mgr_config mgr_config;43	int data_lines;44 45	struct omap_dss_device output;46 47	bool port_initialized;48};49 50static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev)51{52	return container_of(dssdev, struct dpi_data, output);53}54 55/* only used in non-DT mode */56static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev)57{58	return platform_get_drvdata(pdev);59}60 61static struct dss_pll *dpi_get_pll(enum omap_channel channel)62{63	/*64	 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL65	 * would also be used for DISPC fclk. Meaning, when the DPI output is66	 * disabled, DISPC clock will be disabled, and TV out will stop.67	 */68	switch (omapdss_get_version()) {69	case OMAPDSS_VER_OMAP24xx:70	case OMAPDSS_VER_OMAP34xx_ES1:71	case OMAPDSS_VER_OMAP34xx_ES3:72	case OMAPDSS_VER_OMAP3630:73	case OMAPDSS_VER_AM35xx:74	case OMAPDSS_VER_AM43xx:75		return NULL;76 77	case OMAPDSS_VER_OMAP4430_ES1:78	case OMAPDSS_VER_OMAP4430_ES2:79	case OMAPDSS_VER_OMAP4:80		switch (channel) {81		case OMAP_DSS_CHANNEL_LCD:82			return dss_pll_find("dsi0");83		case OMAP_DSS_CHANNEL_LCD2:84			return dss_pll_find("dsi1");85		default:86			return NULL;87		}88 89	case OMAPDSS_VER_OMAP5:90		switch (channel) {91		case OMAP_DSS_CHANNEL_LCD:92			return dss_pll_find("dsi0");93		case OMAP_DSS_CHANNEL_LCD3:94			return dss_pll_find("dsi1");95		default:96			return NULL;97		}98 99	case OMAPDSS_VER_DRA7xx:100		switch (channel) {101		case OMAP_DSS_CHANNEL_LCD:102		case OMAP_DSS_CHANNEL_LCD2:103			return dss_pll_find("video0");104		case OMAP_DSS_CHANNEL_LCD3:105			return dss_pll_find("video1");106		default:107			return NULL;108		}109 110	default:111		return NULL;112	}113}114 115static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)116{117	switch (channel) {118	case OMAP_DSS_CHANNEL_LCD:119		return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;120	case OMAP_DSS_CHANNEL_LCD2:121		return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;122	case OMAP_DSS_CHANNEL_LCD3:123		return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;124	default:125		/* this shouldn't happen */126		WARN_ON(1);127		return OMAP_DSS_CLK_SRC_FCK;128	}129}130 131struct dpi_clk_calc_ctx {132	struct dss_pll *pll;133 134	/* inputs */135 136	unsigned long pck_min, pck_max;137 138	/* outputs */139 140	struct dss_pll_clock_info dsi_cinfo;141	unsigned long fck;142	struct dispc_clock_info dispc_cinfo;143};144 145static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,146		unsigned long pck, void *data)147{148	struct dpi_clk_calc_ctx *ctx = data;149 150	/*151	 * Odd dividers give us uneven duty cycle, causing problem when level152	 * shifted. So skip all odd dividers when the pixel clock is on the153	 * higher side.154	 */155	if (ctx->pck_min >= 100000000) {156		if (lckd > 1 && lckd % 2 != 0)157			return false;158 159		if (pckd > 1 && pckd % 2 != 0)160			return false;161	}162 163	ctx->dispc_cinfo.lck_div = lckd;164	ctx->dispc_cinfo.pck_div = pckd;165	ctx->dispc_cinfo.lck = lck;166	ctx->dispc_cinfo.pck = pck;167 168	return true;169}170 171 172static bool dpi_calc_hsdiv_cb(int m_dispc, unsigned long dispc,173		void *data)174{175	struct dpi_clk_calc_ctx *ctx = data;176 177	/*178	 * Odd dividers give us uneven duty cycle, causing problem when level179	 * shifted. So skip all odd dividers when the pixel clock is on the180	 * higher side.181	 */182	if (m_dispc > 1 && m_dispc % 2 != 0 && ctx->pck_min >= 100000000)183		return false;184 185	ctx->dsi_cinfo.mX[HSDIV_DISPC] = m_dispc;186	ctx->dsi_cinfo.clkout[HSDIV_DISPC] = dispc;187 188	return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,189			dpi_calc_dispc_cb, ctx);190}191 192 193static bool dpi_calc_pll_cb(int n, int m, unsigned long fint,194		unsigned long clkdco,195		void *data)196{197	struct dpi_clk_calc_ctx *ctx = data;198 199	ctx->dsi_cinfo.n = n;200	ctx->dsi_cinfo.m = m;201	ctx->dsi_cinfo.fint = fint;202	ctx->dsi_cinfo.clkdco = clkdco;203 204	return dss_pll_hsdiv_calc(ctx->pll, clkdco,205		ctx->pck_min, dss_feat_get_param_max(FEAT_PARAM_DSS_FCK),206		dpi_calc_hsdiv_cb, ctx);207}208 209static bool dpi_calc_dss_cb(unsigned long fck, void *data)210{211	struct dpi_clk_calc_ctx *ctx = data;212 213	ctx->fck = fck;214 215	return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,216			dpi_calc_dispc_cb, ctx);217}218 219static bool dpi_dsi_clk_calc(struct dpi_data *dpi, unsigned long pck,220		struct dpi_clk_calc_ctx *ctx)221{222	unsigned long clkin;223	unsigned long pll_min, pll_max;224 225	memset(ctx, 0, sizeof(*ctx));226	ctx->pll = dpi->pll;227	ctx->pck_min = pck - 1000;228	ctx->pck_max = pck + 1000;229 230	pll_min = 0;231	pll_max = 0;232 233	clkin = clk_get_rate(ctx->pll->clkin);234 235	return dss_pll_calc(ctx->pll, clkin,236			pll_min, pll_max,237			dpi_calc_pll_cb, ctx);238}239 240static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)241{242	int i;243 244	/*245	 * DSS fck gives us very few possibilities, so finding a good pixel246	 * clock may not be possible. We try multiple times to find the clock,247	 * each time widening the pixel clock range we look for, up to248	 * +/- ~15MHz.249	 */250 251	for (i = 0; i < 25; ++i) {252		bool ok;253 254		memset(ctx, 0, sizeof(*ctx));255		if (pck > 1000 * i * i * i)256			ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);257		else258			ctx->pck_min = 0;259		ctx->pck_max = pck + 1000 * i * i * i;260 261		ok = dss_div_calc(pck, ctx->pck_min, dpi_calc_dss_cb, ctx);262		if (ok)263			return ok;264	}265 266	return false;267}268 269 270 271static int dpi_set_dsi_clk(struct dpi_data *dpi, enum omap_channel channel,272		unsigned long pck_req, unsigned long *fck, int *lck_div,273		int *pck_div)274{275	struct dpi_clk_calc_ctx ctx;276	int r;277	bool ok;278 279	ok = dpi_dsi_clk_calc(dpi, pck_req, &ctx);280	if (!ok)281		return -EINVAL;282 283	r = dss_pll_set_config(dpi->pll, &ctx.dsi_cinfo);284	if (r)285		return r;286 287	dss_select_lcd_clk_source(channel,288			dpi_get_alt_clk_src(channel));289 290	dpi->mgr_config.clock_info = ctx.dispc_cinfo;291 292	*fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];293	*lck_div = ctx.dispc_cinfo.lck_div;294	*pck_div = ctx.dispc_cinfo.pck_div;295 296	return 0;297}298 299static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req,300		unsigned long *fck, int *lck_div, int *pck_div)301{302	struct dpi_clk_calc_ctx ctx;303	int r;304	bool ok;305 306	ok = dpi_dss_clk_calc(pck_req, &ctx);307	if (!ok)308		return -EINVAL;309 310	r = dss_set_fck_rate(ctx.fck);311	if (r)312		return r;313 314	dpi->mgr_config.clock_info = ctx.dispc_cinfo;315 316	*fck = ctx.fck;317	*lck_div = ctx.dispc_cinfo.lck_div;318	*pck_div = ctx.dispc_cinfo.pck_div;319 320	return 0;321}322 323static int dpi_set_mode(struct dpi_data *dpi)324{325	struct omap_dss_device *out = &dpi->output;326	struct omap_overlay_manager *mgr = out->manager;327	struct omap_video_timings *t = &dpi->timings;328	int lck_div = 0, pck_div = 0;329	unsigned long fck = 0;330	unsigned long pck;331	int r = 0;332 333	if (dpi->pll)334		r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck,335				&lck_div, &pck_div);336	else337		r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck,338				&lck_div, &pck_div);339	if (r)340		return r;341 342	pck = fck / lck_div / pck_div;343 344	if (pck != t->pixelclock) {345		DSSWARN("Could not find exact pixel clock. Requested %d Hz, got %lu Hz\n",346			t->pixelclock, pck);347 348		t->pixelclock = pck;349	}350 351	dss_mgr_set_timings(mgr, t);352 353	return 0;354}355 356static void dpi_config_lcd_manager(struct dpi_data *dpi)357{358	struct omap_dss_device *out = &dpi->output;359	struct omap_overlay_manager *mgr = out->manager;360 361	dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;362 363	dpi->mgr_config.stallmode = false;364	dpi->mgr_config.fifohandcheck = false;365 366	dpi->mgr_config.video_port_width = dpi->data_lines;367 368	dpi->mgr_config.lcden_sig_polarity = 0;369 370	dss_mgr_set_lcd_config(mgr, &dpi->mgr_config);371}372 373static int dpi_display_enable(struct omap_dss_device *dssdev)374{375	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);376	struct omap_dss_device *out = &dpi->output;377	int r;378 379	mutex_lock(&dpi->lock);380 381	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi->vdds_dsi_reg) {382		DSSERR("no VDSS_DSI regulator\n");383		r = -ENODEV;384		goto err_no_reg;385	}386 387	if (out->manager == NULL) {388		DSSERR("failed to enable display: no output/manager\n");389		r = -ENODEV;390		goto err_no_out_mgr;391	}392 393	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {394		r = regulator_enable(dpi->vdds_dsi_reg);395		if (r)396			goto err_reg_enable;397	}398 399	r = dispc_runtime_get();400	if (r)401		goto err_get_dispc;402 403	r = dss_dpi_select_source(out->port_num, out->manager->id);404	if (r)405		goto err_src_sel;406 407	if (dpi->pll) {408		r = dss_pll_enable(dpi->pll);409		if (r)410			goto err_dsi_pll_init;411	}412 413	r = dpi_set_mode(dpi);414	if (r)415		goto err_set_mode;416 417	dpi_config_lcd_manager(dpi);418 419	mdelay(2);420 421	r = dss_mgr_enable(out->manager);422	if (r)423		goto err_mgr_enable;424 425	mutex_unlock(&dpi->lock);426 427	return 0;428 429err_mgr_enable:430err_set_mode:431	if (dpi->pll)432		dss_pll_disable(dpi->pll);433err_dsi_pll_init:434err_src_sel:435	dispc_runtime_put();436err_get_dispc:437	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))438		regulator_disable(dpi->vdds_dsi_reg);439err_reg_enable:440err_no_out_mgr:441err_no_reg:442	mutex_unlock(&dpi->lock);443	return r;444}445 446static void dpi_display_disable(struct omap_dss_device *dssdev)447{448	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);449	struct omap_overlay_manager *mgr = dpi->output.manager;450 451	mutex_lock(&dpi->lock);452 453	dss_mgr_disable(mgr);454 455	if (dpi->pll) {456		dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);457		dss_pll_disable(dpi->pll);458	}459 460	dispc_runtime_put();461 462	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))463		regulator_disable(dpi->vdds_dsi_reg);464 465	mutex_unlock(&dpi->lock);466}467 468static void dpi_set_timings(struct omap_dss_device *dssdev,469		struct omap_video_timings *timings)470{471	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);472 473	DSSDBG("dpi_set_timings\n");474 475	mutex_lock(&dpi->lock);476 477	dpi->timings = *timings;478 479	mutex_unlock(&dpi->lock);480}481 482static void dpi_get_timings(struct omap_dss_device *dssdev,483		struct omap_video_timings *timings)484{485	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);486 487	mutex_lock(&dpi->lock);488 489	*timings = dpi->timings;490 491	mutex_unlock(&dpi->lock);492}493 494static int dpi_check_timings(struct omap_dss_device *dssdev,495			struct omap_video_timings *timings)496{497	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);498	struct omap_overlay_manager *mgr = dpi->output.manager;499	int lck_div, pck_div;500	unsigned long fck;501	unsigned long pck;502	struct dpi_clk_calc_ctx ctx;503	bool ok;504 505	if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))506		return -EINVAL;507 508	if (timings->pixelclock == 0)509		return -EINVAL;510 511	if (dpi->pll) {512		ok = dpi_dsi_clk_calc(dpi, timings->pixelclock, &ctx);513		if (!ok)514			return -EINVAL;515 516		fck = ctx.dsi_cinfo.clkout[HSDIV_DISPC];517	} else {518		ok = dpi_dss_clk_calc(timings->pixelclock, &ctx);519		if (!ok)520			return -EINVAL;521 522		fck = ctx.fck;523	}524 525	lck_div = ctx.dispc_cinfo.lck_div;526	pck_div = ctx.dispc_cinfo.pck_div;527 528	pck = fck / lck_div / pck_div;529 530	timings->pixelclock = pck;531 532	return 0;533}534 535static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)536{537	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);538 539	mutex_lock(&dpi->lock);540 541	dpi->data_lines = data_lines;542 543	mutex_unlock(&dpi->lock);544}545 546static int dpi_verify_dsi_pll(struct dss_pll *pll)547{548	int r;549 550	/* do initial setup with the PLL to see if it is operational */551 552	r = dss_pll_enable(pll);553	if (r)554		return r;555 556	dss_pll_disable(pll);557 558	return 0;559}560 561static int dpi_init_regulator(struct dpi_data *dpi)562{563	struct regulator *vdds_dsi;564 565	if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))566		return 0;567 568	if (dpi->vdds_dsi_reg)569		return 0;570 571	vdds_dsi = devm_regulator_get(&dpi->pdev->dev, "vdds_dsi");572	if (IS_ERR(vdds_dsi)) {573		if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)574			DSSERR("can't get VDDS_DSI regulator\n");575		return PTR_ERR(vdds_dsi);576	}577 578	dpi->vdds_dsi_reg = vdds_dsi;579 580	return 0;581}582 583static void dpi_init_pll(struct dpi_data *dpi)584{585	struct dss_pll *pll;586 587	if (dpi->pll)588		return;589 590	pll = dpi_get_pll(dpi->output.dispc_channel);591	if (!pll)592		return;593 594	/* On DRA7 we need to set a mux to use the PLL */595	if (omapdss_get_version() == OMAPDSS_VER_DRA7xx)596		dss_ctrl_pll_set_control_mux(pll->id, dpi->output.dispc_channel);597 598	if (dpi_verify_dsi_pll(pll)) {599		DSSWARN("DSI PLL not operational\n");600		return;601	}602 603	dpi->pll = pll;604}605 606/*607 * Return a hardcoded channel for the DPI output. This should work for608 * current use cases, but this can be later expanded to either resolve609 * the channel in some more dynamic manner, or get the channel as a user610 * parameter.611 */612static enum omap_channel dpi_get_channel(int port_num)613{614	switch (omapdss_get_version()) {615	case OMAPDSS_VER_OMAP24xx:616	case OMAPDSS_VER_OMAP34xx_ES1:617	case OMAPDSS_VER_OMAP34xx_ES3:618	case OMAPDSS_VER_OMAP3630:619	case OMAPDSS_VER_AM35xx:620	case OMAPDSS_VER_AM43xx:621		return OMAP_DSS_CHANNEL_LCD;622 623	case OMAPDSS_VER_DRA7xx:624		switch (port_num) {625		case 2:626			return OMAP_DSS_CHANNEL_LCD3;627		case 1:628			return OMAP_DSS_CHANNEL_LCD2;629		case 0:630		default:631			return OMAP_DSS_CHANNEL_LCD;632		}633 634	case OMAPDSS_VER_OMAP4430_ES1:635	case OMAPDSS_VER_OMAP4430_ES2:636	case OMAPDSS_VER_OMAP4:637		return OMAP_DSS_CHANNEL_LCD2;638 639	case OMAPDSS_VER_OMAP5:640		return OMAP_DSS_CHANNEL_LCD3;641 642	default:643		DSSWARN("unsupported DSS version\n");644		return OMAP_DSS_CHANNEL_LCD;645	}646}647 648static int dpi_connect(struct omap_dss_device *dssdev,649		struct omap_dss_device *dst)650{651	struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev);652	struct omap_overlay_manager *mgr;653	int r;654 655	r = dpi_init_regulator(dpi);656	if (r)657		return r;658 659	dpi_init_pll(dpi);660 661	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);662	if (!mgr)663		return -ENODEV;664 665	r = dss_mgr_connect(mgr, dssdev);666	if (r)667		return r;668 669	r = omapdss_output_set_device(dssdev, dst);670	if (r) {671		DSSERR("failed to connect output to new device: %s\n",672				dst->name);673		dss_mgr_disconnect(mgr, dssdev);674		return r;675	}676 677	return 0;678}679 680static void dpi_disconnect(struct omap_dss_device *dssdev,681		struct omap_dss_device *dst)682{683	WARN_ON(dst != dssdev->dst);684 685	if (dst != dssdev->dst)686		return;687 688	omapdss_output_unset_device(dssdev);689 690	if (dssdev->manager)691		dss_mgr_disconnect(dssdev->manager, dssdev);692}693 694static const struct omapdss_dpi_ops dpi_ops = {695	.connect = dpi_connect,696	.disconnect = dpi_disconnect,697 698	.enable = dpi_display_enable,699	.disable = dpi_display_disable,700 701	.check_timings = dpi_check_timings,702	.set_timings = dpi_set_timings,703	.get_timings = dpi_get_timings,704 705	.set_data_lines = dpi_set_data_lines,706};707 708static void dpi_init_output(struct platform_device *pdev)709{710	struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);711	struct omap_dss_device *out = &dpi->output;712 713	out->dev = &pdev->dev;714	out->id = OMAP_DSS_OUTPUT_DPI;715	out->output_type = OMAP_DISPLAY_TYPE_DPI;716	out->name = "dpi.0";717	out->dispc_channel = dpi_get_channel(0);718	out->ops.dpi = &dpi_ops;719	out->owner = THIS_MODULE;720 721	omapdss_register_output(out);722}723 724static void dpi_uninit_output(struct platform_device *pdev)725{726	struct dpi_data *dpi = dpi_get_data_from_pdev(pdev);727	struct omap_dss_device *out = &dpi->output;728 729	omapdss_unregister_output(out);730}731 732static void dpi_init_output_port(struct platform_device *pdev,733	struct device_node *port)734{735	struct dpi_data *dpi = port->data;736	struct omap_dss_device *out = &dpi->output;737	int r;738	u32 port_num;739 740	r = of_property_read_u32(port, "reg", &port_num);741	if (r)742		port_num = 0;743 744	switch (port_num) {745	case 2:746		out->name = "dpi.2";747		break;748	case 1:749		out->name = "dpi.1";750		break;751	case 0:752	default:753		out->name = "dpi.0";754		break;755	}756 757	out->dev = &pdev->dev;758	out->id = OMAP_DSS_OUTPUT_DPI;759	out->output_type = OMAP_DISPLAY_TYPE_DPI;760	out->dispc_channel = dpi_get_channel(port_num);761	out->port_num = port_num;762	out->ops.dpi = &dpi_ops;763	out->owner = THIS_MODULE;764 765	omapdss_register_output(out);766}767 768static void dpi_uninit_output_port(struct device_node *port)769{770	struct dpi_data *dpi = port->data;771	struct omap_dss_device *out = &dpi->output;772 773	omapdss_unregister_output(out);774}775 776static int dpi_bind(struct device *dev, struct device *master, void *data)777{778	struct platform_device *pdev = to_platform_device(dev);779	struct dpi_data *dpi;780 781	dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);782	if (!dpi)783		return -ENOMEM;784 785	dpi->pdev = pdev;786 787	platform_set_drvdata(pdev, dpi);788 789	mutex_init(&dpi->lock);790 791	dpi_init_output(pdev);792 793	return 0;794}795 796static void dpi_unbind(struct device *dev, struct device *master, void *data)797{798	struct platform_device *pdev = to_platform_device(dev);799 800	dpi_uninit_output(pdev);801}802 803static const struct component_ops dpi_component_ops = {804	.bind	= dpi_bind,805	.unbind	= dpi_unbind,806};807 808static int dpi_probe(struct platform_device *pdev)809{810	return component_add(&pdev->dev, &dpi_component_ops);811}812 813static void dpi_remove(struct platform_device *pdev)814{815	component_del(&pdev->dev, &dpi_component_ops);816}817 818static struct platform_driver omap_dpi_driver = {819	.probe		= dpi_probe,820	.remove		= dpi_remove,821	.driver		= {822		.name   = "omapdss_dpi",823		.suppress_bind_attrs = true,824	},825};826 827int __init dpi_init_platform_driver(void)828{829	return platform_driver_register(&omap_dpi_driver);830}831 832void dpi_uninit_platform_driver(void)833{834	platform_driver_unregister(&omap_dpi_driver);835}836 837int dpi_init_port(struct platform_device *pdev, struct device_node *port)838{839	struct dpi_data *dpi;840	struct device_node *ep;841	u32 datalines;842	int r;843 844	dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL);845	if (!dpi)846		return -ENOMEM;847 848	ep = omapdss_of_get_next_endpoint(port, NULL);849	if (!ep)850		return 0;851 852	r = of_property_read_u32(ep, "data-lines", &datalines);853	if (r) {854		DSSERR("failed to parse datalines\n");855		goto err_datalines;856	}857 858	dpi->data_lines = datalines;859 860	of_node_put(ep);861 862	dpi->pdev = pdev;863	port->data = dpi;864 865	mutex_init(&dpi->lock);866 867	dpi_init_output_port(pdev, port);868 869	dpi->port_initialized = true;870 871	return 0;872 873err_datalines:874	of_node_put(ep);875 876	return r;877}878 879void dpi_uninit_port(struct device_node *port)880{881	struct dpi_data *dpi = port->data;882 883	if (!dpi->port_initialized)884		return;885 886	dpi_uninit_output_port(port);887}888