sdi.c 8.2 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
/*
 * 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>
23
#include <linux/regulator/consumer.h>
24
#include <linux/export.h>
25
#include <linux/platform_device.h>
26
#include <linux/string.h>
T
Tomi Valkeinen 已提交
27
#include <linux/of.h>
T
Tomi Valkeinen 已提交
28

29
#include "omapdss.h"
T
Tomi Valkeinen 已提交
30 31 32
#include "dss.h"

static struct {
33 34
	struct platform_device *pdev;

T
Tomi Valkeinen 已提交
35
	bool update_enabled;
36
	struct regulator *vdds_sdi_reg;
T
Tomi Valkeinen 已提交
37

38
	struct dss_lcd_mgr_config mgr_config;
39
	struct videomode vm;
40
	int datapairs;
41

42
	struct omap_dss_device output;
T
Tomi Valkeinen 已提交
43 44

	bool port_initialized;
45
} sdi;
46

47 48 49
struct sdi_clk_calc_ctx {
	unsigned long pck_min, pck_max;

T
Tomi Valkeinen 已提交
50
	unsigned long fck;
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	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;
}

67
static bool dpi_calc_dss_cb(unsigned long fck, void *data)
68 69 70
{
	struct sdi_clk_calc_ctx *ctx = data;

71
	ctx->fck = fck;
72 73 74 75 76 77

	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,
78
		unsigned long *fck,
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
		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;

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

	return -EINVAL;
}

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

116
	sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS;
117

118 119 120 121 122 123
	sdi.mgr_config.stallmode = false;
	sdi.mgr_config.fifohandcheck = false;

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

124
	dss_mgr_set_lcd_config(channel, &sdi.mgr_config);
T
Tomi Valkeinen 已提交
125 126
}

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

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

142 143
	r = regulator_enable(sdi.vdds_sdi_reg);
	if (r)
144
		goto err_reg_enable;
145

146 147 148
	r = dispc_runtime_get();
	if (r)
		goto err_get_dispc;
T
Tomi Valkeinen 已提交
149 150

	/* 15.5.9.1.2 */
151
	vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE | DISPLAY_FLAGS_SYNC_POSEDGE;
152

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

157
	sdi.mgr_config.clock_info = dispc_cinfo;
T
Tomi Valkeinen 已提交
158

159
	pck = fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div;
T
Tomi Valkeinen 已提交
160

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

165
		vm->pixelclock = pck;
T
Tomi Valkeinen 已提交
166 167 168
	}


169
	dss_mgr_set_timings(channel, vm);
T
Tomi Valkeinen 已提交
170

171
	r = dss_set_fck_rate(fck);
T
Tomi Valkeinen 已提交
172
	if (r)
173
		goto err_set_dss_clock_div;
T
Tomi Valkeinen 已提交
174

175
	sdi_config_lcd_manager(dssdev);
T
Tomi Valkeinen 已提交
176

T
Tomi Valkeinen 已提交
177 178 179 180 181 182 183 184 185 186 187
	/*
	 * 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.
	 */
188
	dispc_mgr_set_clock_div(channel, &sdi.mgr_config.clock_info);
189

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

196
	r = dss_mgr_enable(channel);
197 198
	if (r)
		goto err_mgr_enable;
T
Tomi Valkeinen 已提交
199 200

	return 0;
201

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

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

218
	dss_mgr_disable(channel);
T
Tomi Valkeinen 已提交
219 220 221

	dss_sdi_disable();

222
	dispc_runtime_put();
T
Tomi Valkeinen 已提交
223

224
	regulator_disable(sdi.vdds_sdi_reg);
T
Tomi Valkeinen 已提交
225 226
}

227
static void sdi_set_timings(struct omap_dss_device *dssdev,
228
			    struct videomode *vm)
229
{
230
	sdi.vm = *vm;
231 232
}

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

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

244
	if (!dispc_mgr_timings_ok(channel, vm))
T
Tomi Valkeinen 已提交
245 246
		return -EINVAL;

247
	if (vm->pixelclock == 0)
T
Tomi Valkeinen 已提交
248 249 250 251 252
		return -EINVAL;

	return 0;
}

253
static int sdi_init_regulator(void)
T
Tomi Valkeinen 已提交
254
{
255
	struct regulator *vdds_sdi;
T
Tomi Valkeinen 已提交
256

257 258
	if (sdi.vdds_sdi_reg)
		return 0;
259

260
	vdds_sdi = devm_regulator_get(&sdi.pdev->dev, "vdds_sdi");
261
	if (IS_ERR(vdds_sdi)) {
262 263
		if (PTR_ERR(vdds_sdi) != -EPROBE_DEFER)
			DSSERR("can't get VDDS_SDI regulator\n");
264
		return PTR_ERR(vdds_sdi);
265 266
	}

267 268
	sdi.vdds_sdi_reg = vdds_sdi;

T
Tomi Valkeinen 已提交
269 270 271
	return 0;
}

T
Tomi Valkeinen 已提交
272 273 274
static int sdi_connect(struct omap_dss_device *dssdev,
		struct omap_dss_device *dst)
{
275
	enum omap_channel channel = dssdev->dispc_channel;
T
Tomi Valkeinen 已提交
276 277 278 279 280 281
	int r;

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

282
	r = dss_mgr_connect(channel, dssdev);
T
Tomi Valkeinen 已提交
283 284 285 286 287 288 289
	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);
290
		dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
291 292 293 294 295 296 297 298 299
		return r;
	}

	return 0;
}

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

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

304
	if (dst != dssdev->dst)
T
Tomi Valkeinen 已提交
305 306 307 308
		return;

	omapdss_output_unset_device(dssdev);

309
	dss_mgr_disconnect(channel, dssdev);
T
Tomi Valkeinen 已提交
310 311 312 313 314 315
}

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

316 317
	.enable = sdi_display_enable,
	.disable = sdi_display_disable,
T
Tomi Valkeinen 已提交
318 319

	.check_timings = sdi_check_timings,
320
	.set_timings = sdi_set_timings,
T
Tomi Valkeinen 已提交
321 322 323
	.get_timings = sdi_get_timings,
};

324
static void sdi_init_output(struct platform_device *pdev)
325
{
326
	struct omap_dss_device *out = &sdi.output;
327

328
	out->dev = &pdev->dev;
329
	out->id = OMAP_DSS_OUTPUT_SDI;
330
	out->output_type = OMAP_DISPLAY_TYPE_SDI;
T
Tomi Valkeinen 已提交
331
	out->name = "sdi.0";
332
	out->dispc_channel = OMAP_DSS_CHANNEL_LCD;
T
Tomi Valkeinen 已提交
333 334
	/* We have SDI only on OMAP3, where it's on port 1 */
	out->port_num = 1;
T
Tomi Valkeinen 已提交
335
	out->ops.sdi = &sdi_ops;
336
	out->owner = THIS_MODULE;
337

338
	omapdss_register_output(out);
339 340
}

341
static void sdi_uninit_output(struct platform_device *pdev)
342
{
343
	struct omap_dss_device *out = &sdi.output;
344

345
	omapdss_unregister_output(out);
346 347
}

348
int sdi_init_port(struct platform_device *pdev, struct device_node *port)
T
Tomi Valkeinen 已提交
349 350 351 352 353
{
	struct device_node *ep;
	u32 datapairs;
	int r;

354
	ep = of_get_next_child(port, NULL);
T
Tomi Valkeinen 已提交
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
	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;
}

382
void sdi_uninit_port(struct device_node *port)
T
Tomi Valkeinen 已提交
383 384 385 386 387 388
{
	if (!sdi.port_initialized)
		return;

	sdi_uninit_output(sdi.pdev);
}