dpi.c 15.4 KB
Newer Older
Tomi Valkeinen's avatar
Tomi Valkeinen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*
 * linux/drivers/video/omap2/dss/dpi.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
 *
 * Some code and ideas taken from drivers/video/omap/ driver
 * by Imre Deak.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#define DSS_SUBSYS_NAME "DPI"

#include <linux/kernel.h>
#include <linux/delay.h>
27
#include <linux/export.h>
28
#include <linux/err.h>
Tomi Valkeinen's avatar
Tomi Valkeinen committed
29
#include <linux/errno.h>
30
31
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
32
#include <linux/string.h>
Tomi Valkeinen's avatar
Tomi Valkeinen committed
33

34
#include <video/omapdss.h>
Tomi Valkeinen's avatar
Tomi Valkeinen committed
35
36

#include "dss.h"
37
#include "dss_features.h"
Tomi Valkeinen's avatar
Tomi Valkeinen committed
38
39

static struct {
40
	struct regulator *vdds_dsi_reg;
41
	struct platform_device *dsidev;
42

43
44
	struct mutex lock;

45
	struct omap_video_timings timings;
46
	struct dss_lcd_mgr_config mgr_config;
47
	int data_lines;
48
49

	struct omap_dss_output output;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
50
51
} dpi;

52
static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
53
{
54
55
56
57
58
59
60
61
62
63
64
65
66
	/*
	 * XXX we can't currently use DSI PLL for DPI with OMAP3, as the DSI PLL
	 * would also be used for DISPC fclk. Meaning, when the DPI output is
	 * disabled, DISPC clock will be disabled, and TV out will stop.
	 */
	switch (omapdss_get_version()) {
	case OMAPDSS_VER_OMAP24xx:
	case OMAPDSS_VER_OMAP34xx_ES1:
	case OMAPDSS_VER_OMAP34xx_ES3:
	case OMAPDSS_VER_OMAP3630:
	case OMAPDSS_VER_AM35xx:
		return NULL;

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	case OMAPDSS_VER_OMAP4430_ES1:
	case OMAPDSS_VER_OMAP4430_ES2:
	case OMAPDSS_VER_OMAP4:
		switch (channel) {
		case OMAP_DSS_CHANNEL_LCD:
			return dsi_get_dsidev_from_id(0);
		case OMAP_DSS_CHANNEL_LCD2:
			return dsi_get_dsidev_from_id(1);
		default:
			return NULL;
		}

	case OMAPDSS_VER_OMAP5:
		switch (channel) {
		case OMAP_DSS_CHANNEL_LCD:
			return dsi_get_dsidev_from_id(0);
		case OMAP_DSS_CHANNEL_LCD3:
			return dsi_get_dsidev_from_id(1);
		default:
			return NULL;
		}

89
90
91
	default:
		return NULL;
	}
92
93
}

94
static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
95
{
96
97
98
99
100
101
102
103
104
105
	switch (channel) {
	case OMAP_DSS_CHANNEL_LCD:
		return OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC;
	case OMAP_DSS_CHANNEL_LCD2:
		return OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC;
	default:
		/* this shouldn't happen */
		WARN_ON(1);
		return OMAP_DSS_CLK_SRC_FCK;
	}
106
107
}

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
struct dpi_clk_calc_ctx {
	struct platform_device *dsidev;

	/* inputs */

	unsigned long pck_min, pck_max;

	/* outputs */

	struct dsi_clock_info dsi_cinfo;
	struct dss_clock_info dss_cinfo;
	struct dispc_clock_info dispc_cinfo;
};

static bool dpi_calc_dispc_cb(int lckd, int pckd, unsigned long lck,
		unsigned long pck, void *data)
{
	struct dpi_clk_calc_ctx *ctx = data;

	/*
	 * Odd dividers give us uneven duty cycle, causing problem when level
	 * shifted. So skip all odd dividers when the pixel clock is on the
	 * higher side.
	 */
	if (ctx->pck_min >= 1000000) {
		if (lckd > 1 && lckd % 2 != 0)
			return false;

		if (pckd > 1 && pckd % 2 != 0)
			return false;
	}

	ctx->dispc_cinfo.lck_div = lckd;
	ctx->dispc_cinfo.pck_div = pckd;
	ctx->dispc_cinfo.lck = lck;
	ctx->dispc_cinfo.pck = pck;

	return true;
}


static bool dpi_calc_hsdiv_cb(int regm_dispc, unsigned long dispc,
		void *data)
{
	struct dpi_clk_calc_ctx *ctx = data;

	/*
	 * Odd dividers give us uneven duty cycle, causing problem when level
	 * shifted. So skip all odd dividers when the pixel clock is on the
	 * higher side.
	 */
	if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 1000000)
		return false;

	ctx->dsi_cinfo.regm_dispc = regm_dispc;
	ctx->dsi_cinfo.dsi_pll_hsdiv_dispc_clk = dispc;

	return dispc_div_calc(dispc, ctx->pck_min, ctx->pck_max,
			dpi_calc_dispc_cb, ctx);
}


static bool dpi_calc_pll_cb(int regn, int regm, unsigned long fint,
		unsigned long pll,
		void *data)
{
	struct dpi_clk_calc_ctx *ctx = data;

	ctx->dsi_cinfo.regn = regn;
	ctx->dsi_cinfo.regm = regm;
	ctx->dsi_cinfo.fint = fint;
	ctx->dsi_cinfo.clkin4ddr = pll;

	return dsi_hsdiv_calc(ctx->dsidev, pll, ctx->pck_min,
			dpi_calc_hsdiv_cb, ctx);
}

static bool dpi_calc_dss_cb(int fckd, unsigned long fck, void *data)
{
	struct dpi_clk_calc_ctx *ctx = data;

	ctx->dss_cinfo.fck = fck;
	ctx->dss_cinfo.fck_div = fckd;

	return dispc_div_calc(fck, ctx->pck_min, ctx->pck_max,
			dpi_calc_dispc_cb, ctx);
}

static bool dpi_dsi_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
{
	unsigned long clkin;
	unsigned long pll_min, pll_max;

	clkin = dsi_get_pll_clkin(dpi.dsidev);

	memset(ctx, 0, sizeof(*ctx));
	ctx->dsidev = dpi.dsidev;
	ctx->pck_min = pck - 1000;
	ctx->pck_max = pck + 1000;
	ctx->dsi_cinfo.clkin = clkin;

	pll_min = 0;
	pll_max = 0;

	return dsi_pll_calc(dpi.dsidev, clkin,
			pll_min, pll_max,
			dpi_calc_pll_cb, ctx);
}

static bool dpi_dss_clk_calc(unsigned long pck, struct dpi_clk_calc_ctx *ctx)
{
	int i;

	/*
	 * DSS fck gives us very few possibilities, so finding a good pixel
	 * clock may not be possible. We try multiple times to find the clock,
	 * each time widening the pixel clock range we look for, up to
225
	 * +/- ~15MHz.
226
227
	 */

228
	for (i = 0; i < 25; ++i) {
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
		bool ok;

		memset(ctx, 0, sizeof(*ctx));
		if (pck > 1000 * i * i * i)
			ctx->pck_min = max(pck - 1000 * i * i * i, 0lu);
		else
			ctx->pck_min = 0;
		ctx->pck_max = pck + 1000 * i * i * i;

		ok = dss_div_calc(ctx->pck_min, dpi_calc_dss_cb, ctx);
		if (ok)
			return ok;
	}

	return false;
}



248
static int dpi_set_dsi_clk(enum omap_channel channel,
249
250
		unsigned long pck_req, unsigned long *fck, int *lck_div,
		int *pck_div)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
251
{
252
	struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
253
	int r;
254
	bool ok;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
255

256
257
258
	ok = dpi_dsi_clk_calc(pck_req, &ctx);
	if (!ok)
		return -EINVAL;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
259

260
	r = dsi_pll_set_clock_div(dpi.dsidev, &ctx.dsi_cinfo);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
261
262
263
	if (r)
		return r;

264
265
	dss_select_lcd_clk_source(channel,
			dpi_get_alt_clk_src(channel));
Tomi Valkeinen's avatar
Tomi Valkeinen committed
266

267
	dpi.mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
268

269
270
271
	*fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
	*lck_div = ctx.dispc_cinfo.lck_div;
	*pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
272
273
274

	return 0;
}
275

276
277
static int dpi_set_dispc_clk(unsigned long pck_req, unsigned long *fck,
		int *lck_div, int *pck_div)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
278
{
279
	struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
280
	int r;
281
	bool ok;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
282

283
284
285
	ok = dpi_dss_clk_calc(pck_req, &ctx);
	if (!ok)
		return -EINVAL;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
286

287
	r = dss_set_clock_div(&ctx.dss_cinfo);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
288
289
290
	if (r)
		return r;

291
	dpi.mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
292

293
294
295
	*fck = ctx.dss_cinfo.fck;
	*lck_div = ctx.dispc_cinfo.lck_div;
	*pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
296
297
298
299

	return 0;
}

300
static int dpi_set_mode(struct omap_overlay_manager *mgr)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
301
{
302
	struct omap_video_timings *t = &dpi.timings;
303
304
	int lck_div = 0, pck_div = 0;
	unsigned long fck = 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
305
306
307
	unsigned long pck;
	int r = 0;

308
	if (dpi.dsidev)
309
		r = dpi_set_dsi_clk(mgr->id, t->pixel_clock * 1000, &fck,
310
				&lck_div, &pck_div);
311
	else
312
		r = dpi_set_dispc_clk(t->pixel_clock * 1000, &fck,
313
				&lck_div, &pck_div);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
314
	if (r)
315
		return r;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
316
317
318
319
320
321
322
323
324
325
326

	pck = fck / lck_div / pck_div / 1000;

	if (pck != t->pixel_clock) {
		DSSWARN("Could not find exact pixel clock. "
				"Requested %d kHz, got %lu kHz\n",
				t->pixel_clock, pck);

		t->pixel_clock = pck;
	}

327
	dss_mgr_set_timings(mgr, t);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
328

329
	return 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
330
331
}

332
static void dpi_config_lcd_manager(struct omap_overlay_manager *mgr)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
333
{
334
	dpi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
335

336
337
338
	dpi.mgr_config.stallmode = false;
	dpi.mgr_config.fifohandcheck = false;

339
	dpi.mgr_config.video_port_width = dpi.data_lines;
340
341
342

	dpi.mgr_config.lcden_sig_polarity = 0;

343
	dss_mgr_set_lcd_config(mgr, &dpi.mgr_config);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
344
345
}

346
int omapdss_dpi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
347
{
348
	struct omap_dss_output *out = &dpi.output;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
349
350
	int r;

351
352
	mutex_lock(&dpi.lock);

353
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) && !dpi.vdds_dsi_reg) {
354
		DSSERR("no VDSS_DSI regulator\n");
355
356
		r = -ENODEV;
		goto err_no_reg;
357
358
	}

359
360
	if (out == NULL || out->manager == NULL) {
		DSSERR("failed to enable display: no output/manager\n");
361
		r = -ENODEV;
362
		goto err_no_out_mgr;
363
364
	}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
365
366
367
	r = omap_dss_start_device(dssdev);
	if (r) {
		DSSERR("failed to start device\n");
368
		goto err_start_dev;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
369
370
	}

371
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
372
373
		r = regulator_enable(dpi.vdds_dsi_reg);
		if (r)
374
			goto err_reg_enable;
375
376
	}

377
	r = dispc_runtime_get();
Tomi Valkeinen's avatar
Tomi Valkeinen committed
378
	if (r)
379
380
		goto err_get_dispc;

381
	r = dss_dpi_select_source(out->manager->id);
382
383
384
	if (r)
		goto err_src_sel;

385
	if (dpi.dsidev) {
386
387
388
389
		r = dsi_runtime_get(dpi.dsidev);
		if (r)
			goto err_get_dsi;

390
		r = dsi_pll_init(dpi.dsidev, 0, 1);
391
		if (r)
392
			goto err_dsi_pll_init;
393
394
	}

395
	r = dpi_set_mode(out->manager);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
396
	if (r)
397
		goto err_set_mode;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
398

399
	dpi_config_lcd_manager(out->manager);
400

Tomi Valkeinen's avatar
Tomi Valkeinen committed
401
402
	mdelay(2);

403
	r = dss_mgr_enable(out->manager);
404
405
	if (r)
		goto err_mgr_enable;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
406

407
408
	mutex_unlock(&dpi.lock);

Tomi Valkeinen's avatar
Tomi Valkeinen committed
409
410
	return 0;

411
err_mgr_enable:
412
err_set_mode:
413
	if (dpi.dsidev)
414
		dsi_pll_uninit(dpi.dsidev, true);
415
err_dsi_pll_init:
416
	if (dpi.dsidev)
417
418
		dsi_runtime_put(dpi.dsidev);
err_get_dsi:
419
err_src_sel:
420
421
	dispc_runtime_put();
err_get_dispc:
422
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
423
		regulator_disable(dpi.vdds_dsi_reg);
424
err_reg_enable:
Tomi Valkeinen's avatar
Tomi Valkeinen committed
425
	omap_dss_stop_device(dssdev);
426
err_start_dev:
427
err_no_out_mgr:
428
429
err_no_reg:
	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
430
431
	return r;
}
432
EXPORT_SYMBOL(omapdss_dpi_display_enable);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
433

434
void omapdss_dpi_display_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
435
{
436
	struct omap_overlay_manager *mgr = dpi.output.manager;
437

438
439
	mutex_lock(&dpi.lock);

440
	dss_mgr_disable(mgr);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
441

442
	if (dpi.dsidev) {
443
		dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
444
		dsi_pll_uninit(dpi.dsidev, true);
445
		dsi_runtime_put(dpi.dsidev);
446
	}
Tomi Valkeinen's avatar
Tomi Valkeinen committed
447

448
	dispc_runtime_put();
Tomi Valkeinen's avatar
Tomi Valkeinen committed
449

450
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
451
452
		regulator_disable(dpi.vdds_dsi_reg);

Tomi Valkeinen's avatar
Tomi Valkeinen committed
453
	omap_dss_stop_device(dssdev);
454
455

	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
456
}
457
EXPORT_SYMBOL(omapdss_dpi_display_disable);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
458

459
460
void omapdss_dpi_set_timings(struct omap_dss_device *dssdev,
		struct omap_video_timings *timings)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
461
462
{
	DSSDBG("dpi_set_timings\n");
463
464
465

	mutex_lock(&dpi.lock);

466
467
	dpi.timings = *timings;

468
	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
469
}
470
EXPORT_SYMBOL(omapdss_dpi_set_timings);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
471

472
int dpi_check_timings(struct omap_dss_device *dssdev,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
473
474
			struct omap_video_timings *timings)
{
475
	struct omap_overlay_manager *mgr = dpi.output.manager;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
476
477
478
	int lck_div, pck_div;
	unsigned long fck;
	unsigned long pck;
479
480
	struct dpi_clk_calc_ctx ctx;
	bool ok;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
481

482
	if (mgr && !dispc_mgr_timings_ok(mgr->id, timings))
Tomi Valkeinen's avatar
Tomi Valkeinen committed
483
484
485
486
487
		return -EINVAL;

	if (timings->pixel_clock == 0)
		return -EINVAL;

488
	if (dpi.dsidev) {
489
490
491
		ok = dpi_dsi_clk_calc(timings->pixel_clock * 1000, &ctx);
		if (!ok)
			return -EINVAL;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
492

493
		fck = ctx.dsi_cinfo.dsi_pll_hsdiv_dispc_clk;
494
	} else {
495
496
497
		ok = dpi_dss_clk_calc(timings->pixel_clock * 1000, &ctx);
		if (!ok)
			return -EINVAL;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
498

499
		fck = ctx.dss_cinfo.fck;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
500
	}
501

502
503
	lck_div = ctx.dispc_cinfo.lck_div;
	pck_div = ctx.dispc_cinfo.pck_div;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
504
505
506
507
508
509
510

	pck = fck / lck_div / pck_div / 1000;

	timings->pixel_clock = pck;

	return 0;
}
511
EXPORT_SYMBOL(dpi_check_timings);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
512

513
514
515
516
517
518
519
520
521
522
void omapdss_dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
{
	mutex_lock(&dpi.lock);

	dpi.data_lines = data_lines;

	mutex_unlock(&dpi.lock);
}
EXPORT_SYMBOL(omapdss_dpi_set_data_lines);

523
static int dpi_verify_dsi_pll(struct platform_device *dsidev)
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
{
	int r;

	/* do initial setup with the PLL to see if it is operational */

	r = dsi_runtime_get(dsidev);
	if (r)
		return r;

	r = dsi_pll_init(dsidev, 0, 1);
	if (r) {
		dsi_runtime_put(dsidev);
		return r;
	}

	dsi_pll_uninit(dsidev, true);
	dsi_runtime_put(dsidev);

	return 0;
}

545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
/*
 * Return a hardcoded channel for the DPI output. This should work for
 * current use cases, but this can be later expanded to either resolve
 * the channel in some more dynamic manner, or get the channel as a user
 * parameter.
 */
static enum omap_channel dpi_get_channel(void)
{
	switch (omapdss_get_version()) {
	case OMAPDSS_VER_OMAP24xx:
	case OMAPDSS_VER_OMAP34xx_ES1:
	case OMAPDSS_VER_OMAP34xx_ES3:
	case OMAPDSS_VER_OMAP3630:
	case OMAPDSS_VER_AM35xx:
		return OMAP_DSS_CHANNEL_LCD;

	case OMAPDSS_VER_OMAP4430_ES1:
	case OMAPDSS_VER_OMAP4430_ES2:
	case OMAPDSS_VER_OMAP4:
		return OMAP_DSS_CHANNEL_LCD2;

	case OMAPDSS_VER_OMAP5:
		return OMAP_DSS_CHANNEL_LCD3;

	default:
		DSSWARN("unsupported DSS version\n");
		return OMAP_DSS_CHANNEL_LCD;
	}
}

575
static int dpi_init_display(struct omap_dss_device *dssdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
576
{
577
578
	struct platform_device *dsidev;

Tomi Valkeinen's avatar
Tomi Valkeinen committed
579
580
	DSSDBG("init_display\n");

581
582
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI) &&
					dpi.vdds_dsi_reg == NULL) {
583
		struct regulator *vdds_dsi;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
584

585
586
587
		vdds_dsi = dss_get_vdds_dsi();

		if (IS_ERR(vdds_dsi)) {
588
			DSSERR("can't get VDDS_DSI regulator\n");
589
			return PTR_ERR(vdds_dsi);
590
		}
591
592

		dpi.vdds_dsi_reg = vdds_dsi;
593
594
	}

595
	dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
596

597
	if (dsidev && dpi_verify_dsi_pll(dsidev)) {
598
599
		dsidev = NULL;
		DSSWARN("DSI PLL not operational\n");
600
601
	}

602
603
604
605
606
	if (dsidev)
		DSSDBG("using DSI PLL for DPI clock\n");

	dpi.dsidev = dsidev;

Tomi Valkeinen's avatar
Tomi Valkeinen committed
607
608
609
	return 0;
}

610
static struct omap_dss_device *dpi_find_dssdev(struct platform_device *pdev)
611
{
612
	struct omap_dss_board_info *pdata = pdev->dev.platform_data;
613
	const char *def_disp_name = omapdss_get_default_display_name();
614
615
616
617
	struct omap_dss_device *def_dssdev;
	int i;

	def_dssdev = NULL;
618
619
620
621
622
623
624

	for (i = 0; i < pdata->num_devices; ++i) {
		struct omap_dss_device *dssdev = pdata->devices[i];

		if (dssdev->type != OMAP_DISPLAY_TYPE_DPI)
			continue;

625
626
627
628
629
630
631
		if (def_dssdev == NULL)
			def_dssdev = dssdev;

		if (def_disp_name != NULL &&
				strcmp(dssdev->name, def_disp_name) == 0) {
			def_dssdev = dssdev;
			break;
632
		}
633
	}
634

635
636
637
	return def_dssdev;
}

638
static int dpi_probe_pdata(struct platform_device *dpidev)
639
{
640
	struct omap_dss_device *plat_dssdev;
641
642
643
	struct omap_dss_device *dssdev;
	int r;

644
	plat_dssdev = dpi_find_dssdev(dpidev);
645

646
	if (!plat_dssdev)
647
		return 0;
648
649

	dssdev = dss_alloc_and_init_device(&dpidev->dev);
650
	if (!dssdev)
651
		return -ENOMEM;
652

653
654
	dss_copy_device_pdata(dssdev, plat_dssdev);

655
656
657
	r = dpi_init_display(dssdev);
	if (r) {
		DSSERR("device %s init failed: %d\n", dssdev->name, r);
658
		dss_put_device(dssdev);
659
		return r;
660
661
	}

662
663
664
665
666
	r = omapdss_output_set_device(&dpi.output, dssdev);
	if (r) {
		DSSERR("failed to connect output to new device: %s\n",
				dssdev->name);
		dss_put_device(dssdev);
667
		return r;
668
669
	}

670
	r = dss_add_device(dssdev);
671
672
	if (r) {
		DSSERR("device %s register failed: %d\n", dssdev->name, r);
673
		omapdss_output_unset_device(&dpi.output);
674
		dss_put_device(dssdev);
675
		return r;
676
	}
677
678

	return 0;
679
680
}

681
static void dpi_init_output(struct platform_device *pdev)
682
683
684
685
686
687
{
	struct omap_dss_output *out = &dpi.output;

	out->pdev = pdev;
	out->id = OMAP_DSS_OUTPUT_DPI;
	out->type = OMAP_DISPLAY_TYPE_DPI;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
688
	out->name = "dpi.0";
689
	out->dispc_channel = dpi_get_channel();
690
691
692
693
694
695
696
697
698
699
700

	dss_register_output(out);
}

static void __exit dpi_uninit_output(struct platform_device *pdev)
{
	struct omap_dss_output *out = &dpi.output;

	dss_unregister_output(out);
}

701
static int omap_dpi_probe(struct platform_device *pdev)
702
{
703
704
	int r;

705
706
	mutex_init(&dpi.lock);

707
708
	dpi_init_output(pdev);

709
710
711
712
713
	r = dpi_probe_pdata(pdev);
	if (r) {
		dpi_uninit_output(pdev);
		return r;
	}
714

715
716
717
	return 0;
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
718
static int __exit omap_dpi_remove(struct platform_device *pdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
719
{
720
	dss_unregister_child_devices(&pdev->dev);
721

722
723
	dpi_uninit_output(pdev);

724
	return 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
725
726
}

727
static struct platform_driver omap_dpi_driver = {
728
	.probe		= omap_dpi_probe,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
729
	.remove         = __exit_p(omap_dpi_remove),
730
731
732
733
734
735
	.driver         = {
		.name   = "omapdss_dpi",
		.owner  = THIS_MODULE,
	},
};

Tomi Valkeinen's avatar
Tomi Valkeinen committed
736
int __init dpi_init_platform_driver(void)
737
{
738
	return platform_driver_register(&omap_dpi_driver);
739
740
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
741
void __exit dpi_uninit_platform_driver(void)
742
743
744
{
	platform_driver_unregister(&omap_dpi_driver);
}