dpi.c 14.9 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
41
	struct platform_device *pdev;

42
	struct regulator *vdds_dsi_reg;
43
	struct platform_device *dsidev;
44

45
46
	struct mutex lock;

47
	struct omap_video_timings timings;
48
	struct dss_lcd_mgr_config mgr_config;
49
	int data_lines;
50

51
	struct omap_dss_device output;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
52
53
} dpi;

54
static struct platform_device *dpi_get_dsidev(enum omap_channel channel)
55
{
56
57
58
59
60
61
62
63
64
65
66
67
68
	/*
	 * 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;

69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	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;
		}

91
92
93
	default:
		return NULL;
	}
94
95
}

96
static enum omap_dss_clk_source dpi_get_alt_clk_src(enum omap_channel channel)
97
{
98
99
100
101
102
103
104
105
106
107
	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;
	}
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
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.
	 */
134
	if (ctx->pck_min >= 100000000) {
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
		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.
	 */
161
	if (regm_dispc > 1 && regm_dispc % 2 != 0 && ctx->pck_min >= 100000000)
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
225
226
		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
227
	 * +/- ~15MHz.
228
229
	 */

230
	for (i = 0; i < 25; ++i) {
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
		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;
}



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

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

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

266
267
	dss_select_lcd_clk_source(channel,
			dpi_get_alt_clk_src(channel));
Tomi Valkeinen's avatar
Tomi Valkeinen committed
268

269
	dpi.mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
270

271
272
273
	*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
274
275
276

	return 0;
}
277

278
279
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
280
{
281
	struct dpi_clk_calc_ctx ctx;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
282
	int r;
283
	bool ok;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
284

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

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

293
	dpi.mgr_config.clock_info = ctx.dispc_cinfo;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
294

295
296
297
	*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
298
299
300
301

	return 0;
}

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

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

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

329
	dss_mgr_set_timings(mgr, t);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
330

331
	return 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
332
333
}

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

338
339
340
	dpi.mgr_config.stallmode = false;
	dpi.mgr_config.fifohandcheck = false;

341
	dpi.mgr_config.video_port_width = dpi.data_lines;
342
343
344

	dpi.mgr_config.lcden_sig_polarity = 0;

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

348
static int dpi_display_enable(struct omap_dss_device *dssdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
349
{
350
	struct omap_dss_device *out = &dpi.output;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
351
352
	int r;

353
354
	mutex_lock(&dpi.lock);

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

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

367
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI)) {
368
369
		r = regulator_enable(dpi.vdds_dsi_reg);
		if (r)
370
			goto err_reg_enable;
371
372
	}

373
	r = dispc_runtime_get();
Tomi Valkeinen's avatar
Tomi Valkeinen committed
374
	if (r)
375
376
		goto err_get_dispc;

377
	r = dss_dpi_select_source(out->manager->id);
378
379
380
	if (r)
		goto err_src_sel;

381
	if (dpi.dsidev) {
382
383
384
385
		r = dsi_runtime_get(dpi.dsidev);
		if (r)
			goto err_get_dsi;

386
		r = dsi_pll_init(dpi.dsidev, 0, 1);
387
		if (r)
388
			goto err_dsi_pll_init;
389
390
	}

391
	r = dpi_set_mode(out->manager);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
392
	if (r)
393
		goto err_set_mode;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
394

395
	dpi_config_lcd_manager(out->manager);
396

Tomi Valkeinen's avatar
Tomi Valkeinen committed
397
398
	mdelay(2);

399
	r = dss_mgr_enable(out->manager);
400
401
	if (r)
		goto err_mgr_enable;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
402

403
404
	mutex_unlock(&dpi.lock);

Tomi Valkeinen's avatar
Tomi Valkeinen committed
405
406
	return 0;

407
err_mgr_enable:
408
err_set_mode:
409
	if (dpi.dsidev)
410
		dsi_pll_uninit(dpi.dsidev, true);
411
err_dsi_pll_init:
412
	if (dpi.dsidev)
413
414
		dsi_runtime_put(dpi.dsidev);
err_get_dsi:
415
err_src_sel:
416
417
	dispc_runtime_put();
err_get_dispc:
418
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
419
		regulator_disable(dpi.vdds_dsi_reg);
420
err_reg_enable:
421
err_no_out_mgr:
422
423
err_no_reg:
	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
424
425
426
	return r;
}

427
static void dpi_display_disable(struct omap_dss_device *dssdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
428
{
429
	struct omap_overlay_manager *mgr = dpi.output.manager;
430

431
432
	mutex_lock(&dpi.lock);

433
	dss_mgr_disable(mgr);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
434

435
	if (dpi.dsidev) {
436
		dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK);
437
		dsi_pll_uninit(dpi.dsidev, true);
438
		dsi_runtime_put(dpi.dsidev);
439
	}
Tomi Valkeinen's avatar
Tomi Valkeinen committed
440

441
	dispc_runtime_put();
Tomi Valkeinen's avatar
Tomi Valkeinen committed
442

443
	if (dss_has_feature(FEAT_DPI_USES_VDDS_DSI))
444
445
		regulator_disable(dpi.vdds_dsi_reg);

446
	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
447
448
}

449
static void dpi_set_timings(struct omap_dss_device *dssdev,
450
		struct omap_video_timings *timings)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
451
452
{
	DSSDBG("dpi_set_timings\n");
453
454
455

	mutex_lock(&dpi.lock);

456
457
	dpi.timings = *timings;

458
	mutex_unlock(&dpi.lock);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
459
460
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
461
462
463
464
465
466
467
468
469
470
static void dpi_get_timings(struct omap_dss_device *dssdev,
		struct omap_video_timings *timings)
{
	mutex_lock(&dpi.lock);

	*timings = dpi.timings;

	mutex_unlock(&dpi.lock);
}

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

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

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

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

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

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

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

	pck = fck / lck_div / pck_div / 1000;

	timings->pixel_clock = pck;

	return 0;
}

511
static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines)
512
513
514
515
516
517
518
519
{
	mutex_lock(&dpi.lock);

	dpi.data_lines = data_lines;

	mutex_unlock(&dpi.lock);
}

520
static int dpi_verify_dsi_pll(struct platform_device *dsidev)
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
{
	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;
}

542
543
544
545
546
547
548
549
550
551
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;

552
	vdds_dsi = devm_regulator_get(&dpi.pdev->dev, "vdds_dsi");
553
	if (IS_ERR(vdds_dsi)) {
554
555
		if (PTR_ERR(vdds_dsi) != -EPROBE_DEFER)
			DSSERR("can't get VDDS_DSI regulator\n");
556
		return PTR_ERR(vdds_dsi);
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
	}

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

583
584
585
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
/*
 * 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;
	}
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
static int dpi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
	struct omap_overlay_manager *mgr;
	int r;

	r = dpi_init_regulator();
	if (r)
		return r;

	dpi_init_pll();

	mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel);
	if (!mgr)
		return -ENODEV;

	r = dss_mgr_connect(mgr, dssdev);
	if (r)
		return r;

	r = omapdss_output_set_device(dssdev, dst);
	if (r) {
		DSSERR("failed to connect output to new device: %s\n",
				dst->name);
		dss_mgr_disconnect(mgr, dssdev);
		return r;
	}

	return 0;
}

static void dpi_disconnect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
647
	WARN_ON(dst != dssdev->dst);
Tomi Valkeinen's avatar
Tomi Valkeinen committed
648

649
	if (dst != dssdev->dst)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
650
651
652
653
654
655
656
657
658
659
660
661
		return;

	omapdss_output_unset_device(dssdev);

	if (dssdev->manager)
		dss_mgr_disconnect(dssdev->manager, dssdev);
}

static const struct omapdss_dpi_ops dpi_ops = {
	.connect = dpi_connect,
	.disconnect = dpi_disconnect,

662
663
	.enable = dpi_display_enable,
	.disable = dpi_display_disable,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
664
665

	.check_timings = dpi_check_timings,
666
	.set_timings = dpi_set_timings,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
667
668
	.get_timings = dpi_get_timings,

669
	.set_data_lines = dpi_set_data_lines,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
670
671
};

672
static void dpi_init_output(struct platform_device *pdev)
673
{
674
	struct omap_dss_device *out = &dpi.output;
675

676
	out->dev = &pdev->dev;
677
	out->id = OMAP_DSS_OUTPUT_DPI;
678
	out->output_type = OMAP_DISPLAY_TYPE_DPI;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
679
	out->name = "dpi.0";
680
	out->dispc_channel = dpi_get_channel();
Tomi Valkeinen's avatar
Tomi Valkeinen committed
681
	out->ops.dpi = &dpi_ops;
682
	out->owner = THIS_MODULE;
683

684
	omapdss_register_output(out);
685
686
687
688
}

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

691
	omapdss_unregister_output(out);
692
693
}

694
static int omap_dpi_probe(struct platform_device *pdev)
695
{
696
697
	dpi.pdev = pdev;

698
699
	mutex_init(&dpi.lock);

700
701
	dpi_init_output(pdev);

702
703
704
	return 0;
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
705
static int __exit omap_dpi_remove(struct platform_device *pdev)
Tomi Valkeinen's avatar
Tomi Valkeinen committed
706
{
707
708
	dpi_uninit_output(pdev);

709
	return 0;
Tomi Valkeinen's avatar
Tomi Valkeinen committed
710
711
}

712
static struct platform_driver omap_dpi_driver = {
713
	.probe		= omap_dpi_probe,
Tomi Valkeinen's avatar
Tomi Valkeinen committed
714
	.remove         = __exit_p(omap_dpi_remove),
715
716
717
718
719
720
	.driver         = {
		.name   = "omapdss_dpi",
		.owner  = THIS_MODULE,
	},
};

Tomi Valkeinen's avatar
Tomi Valkeinen committed
721
int __init dpi_init_platform_driver(void)
722
{
723
	return platform_driver_register(&omap_dpi_driver);
724
725
}

Tomi Valkeinen's avatar
Tomi Valkeinen committed
726
void __exit dpi_uninit_platform_driver(void)
727
728
729
{
	platform_driver_unregister(&omap_dpi_driver);
}