sdi.c 9.4 KB
Newer Older
T
Tomi Valkeinen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * linux/drivers/video/omap2/dss/sdi.c
 *
 * Copyright (C) 2009 Nokia Corporation
 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
 *
 * 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 "SDI"

#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
25
#include <linux/regulator/consumer.h>
26
#include <linux/export.h>
27
#include <linux/platform_device.h>
28
#include <linux/string.h>
T
Tomi Valkeinen 已提交
29
#include <linux/of.h>
T
Tomi Valkeinen 已提交
30
#include <linux/component.h>
T
Tomi Valkeinen 已提交
31

32
#include "omapdss.h"
T
Tomi Valkeinen 已提交
33 34 35
#include "dss.h"

static struct {
36 37
	struct platform_device *pdev;

T
Tomi Valkeinen 已提交
38
	bool update_enabled;
39
	struct regulator *vdds_sdi_reg;
T
Tomi Valkeinen 已提交
40

41
	struct dss_lcd_mgr_config mgr_config;
42
	struct videomode vm;
43
	int datapairs;
44

45
	struct omap_dss_device output;
T
Tomi Valkeinen 已提交
46 47

	bool port_initialized;
48
} sdi;
49

50 51 52
struct sdi_clk_calc_ctx {
	unsigned long pck_min, pck_max;

T
Tomi Valkeinen 已提交
53
	unsigned long fck;
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	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 sdi_clk_calc_ctx *ctx = data;

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

	return true;
}

70
static bool dpi_calc_dss_cb(unsigned long fck, void *data)
71 72 73
{
	struct sdi_clk_calc_ctx *ctx = data;

74
	ctx->fck = fck;
75 76 77 78 79 80

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

static int sdi_calc_clock_div(unsigned long pclk,
81
		unsigned long *fck,
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
		struct dispc_clock_info *dispc_cinfo)
{
	int i;
	struct sdi_clk_calc_ctx ctx;

	/*
	 * DSS fclk 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
	 * +/- 1MHz.
	 */

	for (i = 0; i < 10; ++i) {
		bool ok;

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

104
		ok = dss_div_calc(pclk, ctx.pck_min, dpi_calc_dss_cb, &ctx);
105
		if (ok) {
106
			*fck = ctx.fck;
107 108 109 110 111 112 113 114
			*dispc_cinfo = ctx.dispc_cinfo;
			return 0;
		}
	}

	return -EINVAL;
}

115
static void sdi_config_lcd_manager(struct omap_dss_device *dssdev)
T
Tomi Valkeinen 已提交
116
{
117
	enum omap_channel channel = dssdev->dispc_channel;
118

119
	sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
120

121 122 123 124 125 126
	sdi.mgr_config.stallmode = false;
	sdi.mgr_config.fifohandcheck = false;

	sdi.mgr_config.video_port_width = 24;
	sdi.mgr_config.lcden_sig_polarity = 1;

127
	dss_mgr_set_lcd_config(channel, &sdi.mgr_config);
T
Tomi Valkeinen 已提交
128 129
}

130
static int sdi_display_enable(struct omap_dss_device *dssdev)
T
Tomi Valkeinen 已提交
131
{
132
	struct omap_dss_device *out = &sdi.output;
133
	enum omap_channel channel = dssdev->dispc_channel;
134
	struct videomode *vm = &sdi.vm;
135
	unsigned long fck;
T
Tomi Valkeinen 已提交
136 137 138 139
	struct dispc_clock_info dispc_cinfo;
	unsigned long pck;
	int r;

140
	if (!out->dispc_channel_connected) {
141
		DSSERR("failed to enable display: no output/manager\n");
142 143 144
		return -ENODEV;
	}

145 146
	r = regulator_enable(sdi.vdds_sdi_reg);
	if (r)
147
		goto err_reg_enable;
148

149 150 151
	r = dispc_runtime_get();
	if (r)
		goto err_get_dispc;
T
Tomi Valkeinen 已提交
152 153

	/* 15.5.9.1.2 */
154
	vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE | DISPLAY_FLAGS_SYNC_POSEDGE;
155

156
	r = sdi_calc_clock_div(vm->pixelclock, &fck, &dispc_cinfo);
T
Tomi Valkeinen 已提交
157
	if (r)
158
		goto err_calc_clock_div;
T
Tomi Valkeinen 已提交
159

160
	sdi.mgr_config.clock_info = dispc_cinfo;
T
Tomi Valkeinen 已提交
161

162
	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
T
Tomi Valkeinen 已提交
163

164
	if (pck != vm->pixelclock) {
165
		DSSWARN("Could not find exact pixel clock. Requested %lu Hz, got %lu Hz\n",
166
			vm->pixelclock, pck);
T
Tomi Valkeinen 已提交
167

168
		vm->pixelclock = pck;
T
Tomi Valkeinen 已提交
169 170 171
	}


172
	dss_mgr_set_timings(channel, vm);
T
Tomi Valkeinen 已提交
173

174
	r = dss_set_fck_rate(fck);
T
Tomi Valkeinen 已提交
175
	if (r)
176
		goto err_set_dss_clock_div;
T
Tomi Valkeinen 已提交
177

178
	sdi_config_lcd_manager(dssdev);
T
Tomi Valkeinen 已提交
179

T
Tomi Valkeinen 已提交
180 181 182 183 184 185 186 187 188 189 190
	/*
	 * LCLK and PCLK divisors are located in shadow registers, and we
	 * normally write them to DISPC registers when enabling the output.
	 * However, SDI uses pck-free as source clock for its PLL, and pck-free
	 * is affected by the divisors. And as we need the PLL before enabling
	 * the output, we need to write the divisors early.
	 *
	 * It seems just writing to the DISPC register is enough, and we don't
	 * need to care about the shadow register mechanism for pck-free. The
	 * exact reason for this is unknown.
	 */
191
	dispc_mgr_set_clock_div(channel, &sdi.mgr_config.clock_info);
192

193
	dss_sdi_init(sdi.datapairs);
194 195
	r = dss_sdi_enable();
	if (r)
196
		goto err_sdi_enable;
197
	mdelay(2);
T
Tomi Valkeinen 已提交
198

199
	r = dss_mgr_enable(channel);
200 201
	if (r)
		goto err_mgr_enable;
T
Tomi Valkeinen 已提交
202 203

	return 0;
204

205 206
err_mgr_enable:
	dss_sdi_disable();
207 208 209 210 211
err_sdi_enable:
err_set_dss_clock_div:
err_calc_clock_div:
	dispc_runtime_put();
err_get_dispc:
212
	regulator_disable(sdi.vdds_sdi_reg);
213
err_reg_enable:
T
Tomi Valkeinen 已提交
214 215 216
	return r;
}

217
static void sdi_display_disable(struct omap_dss_device *dssdev)
T
Tomi Valkeinen 已提交
218
{
219
	enum omap_channel channel = dssdev->dispc_channel;
220

221
	dss_mgr_disable(channel);
T
Tomi Valkeinen 已提交
222 223 224

	dss_sdi_disable();

225
	dispc_runtime_put();
T
Tomi Valkeinen 已提交
226

227
	regulator_disable(sdi.vdds_sdi_reg);
T
Tomi Valkeinen 已提交
228 229
}

230
static void sdi_set_timings(struct omap_dss_device *dssdev,
231
			    struct videomode *vm)
232
{
233
	sdi.vm = *vm;
234 235
}

T
Tomi Valkeinen 已提交
236
static void sdi_get_timings(struct omap_dss_device *dssdev,
237
			    struct videomode *vm)
T
Tomi Valkeinen 已提交
238
{
239
	*vm = sdi.vm;
T
Tomi Valkeinen 已提交
240 241 242
}

static int sdi_check_timings(struct omap_dss_device *dssdev,
243
			     struct videomode *vm)
T
Tomi Valkeinen 已提交
244
{
245
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
246

247
	if (!dispc_mgr_timings_ok(channel, vm))
T
Tomi Valkeinen 已提交
248 249
		return -EINVAL;

250
	if (vm->pixelclock == 0)
T
Tomi Valkeinen 已提交
251 252 253 254 255
		return -EINVAL;

	return 0;
}

256
static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs)
257 258 259 260
{
	sdi.datapairs = datapairs;
}

261
static int sdi_init_regulator(void)
T
Tomi Valkeinen 已提交
262
{
263
	struct regulator *vdds_sdi;
T
Tomi Valkeinen 已提交
264

265 266
	if (sdi.vdds_sdi_reg)
		return 0;
267

268
	vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");
269
	if (IS_ERR(vdds_sdi)) {
270 271
		if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER)
			DSSERR("can't get VDDS_SDI regulator\n");
272
		return PTR_ERR(vdds_sdi);
273 274
	}

275 276
	sdi.vdds_sdi_reg = vdds_sdi;

T
Tomi Valkeinen 已提交
277 278 279
	return 0;
}

T
Tomi Valkeinen 已提交
280 281 282
static int sdi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
283
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
284 285 286 287 288 289
	int r;

	r = sdi_init_regulator();
	if (r)
		return r;

290
	r = dss_mgr_connect(channel, dssdev);
T
Tomi Valkeinen 已提交
291 292 293 294 295 296 297
	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);
298
		dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
299 300 301 302 303 304 305 306 307
		return r;
	}

	return 0;
}

static void sdi_disconnect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
308 309
	enum omap_channel channel = dssdev->dispc_channel;

310
	WARN_ON(dst != dssdev->dst);
T
Tomi Valkeinen 已提交
311

312
	if (dst != dssdev->dst)
T
Tomi Valkeinen 已提交
313 314 315 316
		return;

	omapdss_output_unset_device(dssdev);

317
	dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
318 319 320 321 322 323
}

static const struct omapdss_sdi_ops sdi_ops = {
	.connect = sdi_connect,
	.disconnect = sdi_disconnect,

324 325
	.enable = sdi_display_enable,
	.disable = sdi_display_disable,
T
Tomi Valkeinen 已提交
326 327

	.check_timings = sdi_check_timings,
328
	.set_timings = sdi_set_timings,
T
Tomi Valkeinen 已提交
329 330
	.get_timings = sdi_get_timings,

331
	.set_datapairs = sdi_set_datapairs,
T
Tomi Valkeinen 已提交
332 333
};

334
static void sdi_init_output(struct platform_device *pdev)
335
{
336
	struct omap_dss_device *out = &sdi.output;
337

338
	out->dev = &pdev->dev;
339
	out->id = OMAP_DSS_OUTPUT_SDI;
340
	out->output_type = OMAP_DISPLAY_TYPE_SDI;
T
Tomi Valkeinen 已提交
341
	out->name = "sdi.0";
342
	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
T
Tomi Valkeinen 已提交
343 344
	/* We have SDI only on OMAP3, where it's on port 1 */
	out->port_num = 1;
T
Tomi Valkeinen 已提交
345
	out->ops.sdi = &sdi_ops;
346
	out->owner = THIS_MODULE;
347

348
	omapdss_register_output(out);
349 350
}

351
static void sdi_uninit_output(struct platform_device *pdev)
352
{
353
	struct omap_dss_device *out = &sdi.output;
354

355
	omapdss_unregister_output(out);
356 357
}

T
Tomi Valkeinen 已提交
358
static int sdi_bind(struct device *dev, struct device *master, void *data)
359
{
T
Tomi Valkeinen 已提交
360 361
	struct platform_device *pdev = to_platform_device(dev);

362 363
	sdi.pdev = pdev;

364 365
	sdi_init_output(pdev);

T
Tomi Valkeinen 已提交
366 367 368
	return 0;
}

T
Tomi Valkeinen 已提交
369
static void sdi_unbind(struct device *dev, struct device *master, void *data)
T
Tomi Valkeinen 已提交
370
{
T
Tomi Valkeinen 已提交
371 372
	struct platform_device *pdev = to_platform_device(dev);

373
	sdi_uninit_output(pdev);
T
Tomi Valkeinen 已提交
374 375 376 377 378 379
}

static const struct component_ops sdi_component_ops = {
	.bind	= sdi_bind,
	.unbind	= sdi_unbind,
};
380

T
Tomi Valkeinen 已提交
381 382 383 384 385 386 387 388
static int sdi_probe(struct platform_device *pdev)
{
	return component_add(&pdev->dev, &sdi_component_ops);
}

static int sdi_remove(struct platform_device *pdev)
{
	component_del(&pdev->dev, &sdi_component_ops);
389 390 391 392
	return 0;
}

static struct platform_driver omap_sdi_driver = {
T
Tomi Valkeinen 已提交
393 394
	.probe		= sdi_probe,
	.remove         = sdi_remove,
395 396
	.driver         = {
		.name   = "omapdss_sdi",
T
Tomi Valkeinen 已提交
397
		.suppress_bind_attrs = true,
398 399 400
	},
};

T
Tomi Valkeinen 已提交
401
int __init sdi_init_platform_driver(void)
402
{
403
	return platform_driver_register(&omap_sdi_driver);
404 405
}

406
void sdi_uninit_platform_driver(void)
407 408
{
	platform_driver_unregister(&omap_sdi_driver);
T
Tomi Valkeinen 已提交
409
}
T
Tomi Valkeinen 已提交
410

411
int sdi_init_port(struct platform_device *pdev, struct device_node *port)
T
Tomi Valkeinen 已提交
412 413 414 415 416
{
	struct device_node *ep;
	u32 datapairs;
	int r;

417
	ep = of_get_next_child(port, NULL);
T
Tomi Valkeinen 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
	if (!ep)
		return 0;

	r = of_property_read_u32(ep, "datapairs", &datapairs);
	if (r) {
		DSSERR("failed to parse datapairs\n");
		goto err_datapairs;
	}

	sdi.datapairs = datapairs;

	of_node_put(ep);

	sdi.pdev = pdev;

	sdi_init_output(pdev);

	sdi.port_initialized = true;

	return 0;

err_datapairs:
	of_node_put(ep);

	return r;
}

445
void sdi_uninit_port(struct device_node *port)
T
Tomi Valkeinen 已提交
446 447 448 449 450 451
{
	if (!sdi.port_initialized)
		return;

	sdi_uninit_output(sdi.pdev);
}