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
575
576
577
578
579
580
581
582
583
584
585
static int dpi_init_regulator(void)
{
	struct regulator *vdds_dsi;

	if (!dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
		return 0;

	if (dpi.vdds_dsi_reg)
		return 0;

	vdds_dsi = dss_get_vdds_dsi();

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

	dpi.vdds_dsi_reg = vdds_dsi;

	return 0;
}

static void dpi_init_pll(void)
{
	struct platform_device *dsidev;

	if (dpi.dsidev)
		return;

	dsidev = dpi_get_dsidev(dpi.output.dispc_channel);
	if (!dsidev)
		return;

	if (dpi_verify_dsi_pll(dsidev)) {
		DSSWARN("DSI PLL not operational\n");
		return;
	}

	dpi.dsidev = dsidev;
}

586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
/*
 * 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;
	}
}

616
static struct omap_dss_device *dpi_find_dssdev(struct platform_device *pdev)
617
{
618
	struct omap_dss_board_info *pdata = pdev->dev.platform_data;
619
	const char *def_disp_name = omapdss_get_default_display_name();
620
621
622
623
	struct omap_dss_device *def_dssdev;
	int i;

	def_dssdev = NULL;
624
625
626
627
628
629
630

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

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

631
632
633
634
635
636
637
		if (def_dssdev == NULL)
			def_dssdev = dssdev;

		if (def_disp_name != NULL &&
				strcmp(dssdev->name, def_disp_name) == 0) {
			def_dssdev = dssdev;
			break;
638
		}
639
	}
640

641
642
643
	return def_dssdev;
}

644
static int dpi_probe_pdata(struct platform_device *dpidev)
645
{
646
	struct omap_dss_device *plat_dssdev;
647
648
649
	struct omap_dss_device *dssdev;
	int r;

650
	plat_dssdev = dpi_find_dssdev(dpidev);
651

652
	if (!plat_dssdev)
653
		return 0;
654

655
656
657
658
659
660
	r = dpi_init_regulator();
	if (r)
		return r;

	dpi_init_pll();

661
	dssdev = dss_alloc_and_init_device(&dpidev->dev);
662
	if (!dssdev)
663
		return -ENOMEM;
664

665
666
	dss_copy_device_pdata(dssdev, plat_dssdev);

667
668
669
670
671
	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);
672
		return r;
673
674
	}

675
	r = dss_add_device(dssdev);
676
677
	if (r) {
		DSSERR("device %s register failed: %d\n", dssdev->name, r);
678
		omapdss_output_unset_device(&dpi.output);
679
		dss_put_device(dssdev);
680
		return r;
681
	}
682
683

	return 0;
684
685
}

686
static void dpi_init_output(struct platform_device *pdev)
687
688
689
690
691
692
{
	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
693
	out->name = "dpi.0";
694
	out->dispc_channel = dpi_get_channel();
695
696
697
698
699
700
701
702
703
704
705

	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);
}

706
static int omap_dpi_probe(struct platform_device *pdev)
707
{
708
709
	int r;

710
711
	mutex_init(&dpi.lock);

712
713
	dpi_init_output(pdev);

714
715
716
717
	if (pdev->dev.platform_data) {
		r = dpi_probe_pdata(pdev);
		if (r)
			goto err_probe;
718
	}
719

720
	return 0;
721
722
723
724

err_probe:
	dpi_uninit_output(pdev);
	return r;
725
726
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
727
static int __exit omap_dpi_remove(struct platform_device *pdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
728
{
729
	dss_unregister_child_devices(&pdev->dev);
730

731
732
	dpi_uninit_output(pdev);

733
	return 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
734
735
}

736
static struct platform_driver omap_dpi_driver = {
737
	.probe		= omap_dpi_probe,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
738
	.remove         = __exit_p(omap_dpi_remove),
739
740
741
742
743
744
	.driver         = {
		.name   = "omapdss_dpi",
		.owner  = THIS_MODULE,
	},
};

Tomi Valkeinen's avatar
Tomi Valkeinen committed
745
int __init dpi_init_platform_driver(void)
746
{
747
	return platform_driver_register(&omap_dpi_driver);
748
749
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
750
void __exit dpi_uninit_platform_driver(void)
751
752
753
{
	platform_driver_unregister(&omap_dpi_driver);
}