sh_mobile_lcdcfb.c 25.5 KB
Newer Older
M
Magnus Damm 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * SuperH Mobile LCDC Framebuffer
 *
 * Copyright (c) 2008 Magnus Damm
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/clk.h>
17
#include <linux/pm_runtime.h>
M
Magnus Damm 已提交
18 19
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
20
#include <linux/interrupt.h>
21
#include <linux/vmalloc.h>
22
#include <video/sh_mobile_lcdc.h>
23
#include <asm/atomic.h>
M
Magnus Damm 已提交
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

#define PALETTE_NR 16

/* shared registers */
#define _LDDCKR 0x410
#define _LDDCKSTPR 0x414
#define _LDINTR 0x468
#define _LDSR 0x46c
#define _LDCNT1R 0x470
#define _LDCNT2R 0x474
#define _LDDDSR 0x47c
#define _LDDWD0R 0x800
#define _LDDRDR 0x840
#define _LDDWAR 0x900
#define _LDDRAR 0x904

40 41 42 43 44 45 46 47 48 49 50
/* shared registers and their order for context save/restore */
static int lcdc_shared_regs[] = {
	_LDDCKR,
	_LDDCKSTPR,
	_LDINTR,
	_LDDDSR,
	_LDCNT1R,
	_LDCNT2R,
};
#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)

M
Magnus Damm 已提交
51 52
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
53 54
       LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR,
       NR_CH_REGS };
M
Magnus Damm 已提交
55

56
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
M
Magnus Damm 已提交
57 58 59 60 61 62 63
	[LDDCKPAT1R] = 0x400,
	[LDDCKPAT2R] = 0x404,
	[LDMT1R] = 0x418,
	[LDMT2R] = 0x41c,
	[LDMT3R] = 0x420,
	[LDDFR] = 0x424,
	[LDSM1R] = 0x428,
64
	[LDSM2R] = 0x42c,
M
Magnus Damm 已提交
65 66 67 68 69 70 71 72 73
	[LDSA1R] = 0x430,
	[LDMLSR] = 0x438,
	[LDHCNR] = 0x448,
	[LDHSYNR] = 0x44c,
	[LDVLNR] = 0x450,
	[LDVSYNR] = 0x454,
	[LDPMR] = 0x460,
};

74
static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
M
Magnus Damm 已提交
75 76 77 78 79 80 81
	[LDDCKPAT1R] = 0x408,
	[LDDCKPAT2R] = 0x40c,
	[LDMT1R] = 0x600,
	[LDMT2R] = 0x604,
	[LDMT3R] = 0x608,
	[LDDFR] = 0x60c,
	[LDSM1R] = 0x610,
82
	[LDSM2R] = 0x614,
M
Magnus Damm 已提交
83 84 85 86 87 88 89 90 91 92 93 94 95
	[LDSA1R] = 0x618,
	[LDMLSR] = 0x620,
	[LDHCNR] = 0x624,
	[LDHSYNR] = 0x628,
	[LDVLNR] = 0x62c,
	[LDVSYNR] = 0x630,
	[LDPMR] = 0x63c,
};

#define START_LCDC	0x00000001
#define LCDC_RESET	0x00000100
#define DISPLAY_BEU	0x00000008
#define LCDC_ENABLE	0x00000001
96 97
#define LDINTR_FE	0x00000400
#define LDINTR_FS	0x00000004
M
Magnus Damm 已提交
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
struct sh_mobile_lcdc_priv;
struct sh_mobile_lcdc_chan {
	struct sh_mobile_lcdc_priv *lcdc;
	unsigned long *reg_offs;
	unsigned long ldmt1r_value;
	unsigned long enabled; /* ME and SE in LDCNT2R */
	struct sh_mobile_lcdc_chan_cfg cfg;
	u32 pseudo_palette[PALETTE_NR];
	unsigned long saved_ch_regs[NR_CH_REGS];
	struct fb_info *info;
	dma_addr_t dma_handle;
	struct fb_deferred_io defio;
	struct scatterlist *sglist;
	unsigned long frame_end;
	wait_queue_head_t frame_end_wait;
};

struct sh_mobile_lcdc_priv {
	void __iomem *base;
	int irq;
	atomic_t hw_usecnt;
	struct device *dev;
	struct clk *dot_clk;
	unsigned long lddckr;
	struct sh_mobile_lcdc_chan ch[2];
	unsigned long saved_shared_regs[NR_SHARED_REGS];
	int started;
};

M
Magnus Damm 已提交
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
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
			    int reg_nr, unsigned long data)
{
	iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
}

static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
				    int reg_nr)
{
	return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
}

static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
		       unsigned long reg_offs, unsigned long data)
{
	iowrite32(data, priv->base + reg_offs);
}

static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,
			       unsigned long reg_offs)
{
	return ioread32(priv->base + reg_offs);
}

static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
			  unsigned long reg_offs,
			  unsigned long mask, unsigned long until)
{
	while ((lcdc_read(priv, reg_offs) & mask) != until)
		cpu_relax();
}

static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
{
	return chan->cfg.chan == LCDC_CHAN_SUBLCD;
}

static void lcdc_sys_write_index(void *handle, unsigned long data)
{
	struct sh_mobile_lcdc_chan *ch = handle;

	lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
	lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
M
Magnus Damm 已提交
172
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
M
Magnus Damm 已提交
173 174 175 176 177 178 179 180 181
}

static void lcdc_sys_write_data(void *handle, unsigned long data)
{
	struct sh_mobile_lcdc_chan *ch = handle;

	lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
	lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
M
Magnus Damm 已提交
182
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
M
Magnus Damm 已提交
183 184 185 186 187 188 189 190 191 192
}

static unsigned long lcdc_sys_read_data(void *handle)
{
	struct sh_mobile_lcdc_chan *ch = handle;

	lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
	lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
	udelay(1);
M
Magnus Damm 已提交
193
	lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
M
Magnus Damm 已提交
194

195
	return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff;
M
Magnus Damm 已提交
196 197 198 199 200 201 202 203
}

struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
	lcdc_sys_write_index,
	lcdc_sys_write_data,
	lcdc_sys_read_data,
};

204 205
static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
{
206 207
	if (atomic_inc_and_test(&priv->hw_usecnt)) {
		pm_runtime_get_sync(priv->dev);
208 209 210 211 212 213 214
		if (priv->dot_clk)
			clk_enable(priv->dot_clk);
	}
}

static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
215
	if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
216 217
		if (priv->dot_clk)
			clk_disable(priv->dot_clk);
218
		pm_runtime_put(priv->dev);
219 220 221
	}
}

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
static int sh_mobile_lcdc_sginit(struct fb_info *info,
				  struct list_head *pagelist)
{
	struct sh_mobile_lcdc_chan *ch = info->par;
	unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT;
	struct page *page;
	int nr_pages = 0;

	sg_init_table(ch->sglist, nr_pages_max);

	list_for_each_entry(page, pagelist, lru)
		sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);

	return nr_pages;
}

238 239 240 241
static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
				       struct list_head *pagelist)
{
	struct sh_mobile_lcdc_chan *ch = info->par;
242
	unsigned int nr_pages;
243 244 245 246

	/* enable clocks before accessing hardware */
	sh_mobile_lcdc_clk_on(ch->lcdc);

247 248 249
	nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
	dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);

250 251
	/* trigger panel update */
	lcdc_write_chan(ch, LDSM2R, 1);
252 253

	dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
254 255 256 257 258 259 260 261 262 263 264 265 266
}

static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
{
	struct fb_deferred_io *fbdefio = info->fbdefio;

	if (fbdefio)
		schedule_delayed_work(&info->deferred_work, fbdefio->delay);
}

static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{
	struct sh_mobile_lcdc_priv *priv = data;
267
	struct sh_mobile_lcdc_chan *ch;
268
	unsigned long tmp;
269 270
	int is_sub;
	int k;
271 272 273 274 275 276 277

	/* acknowledge interrupt */
	tmp = lcdc_read(priv, _LDINTR);
	tmp &= 0xffffff00; /* mask in high 24 bits */
	tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
	lcdc_write(priv, _LDINTR, tmp);

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	/* figure out if this interrupt is for main or sub lcd */
	is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0;

	/* wake up channel and disable clocks*/
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!ch->enabled)
			continue;

		if (is_sub == lcdc_chan_is_sublcd(ch)) {
			ch->frame_end = 1;
			wake_up(&ch->frame_end_wait);

			sh_mobile_lcdc_clk_off(priv);
		}
	}

296 297 298
	return IRQ_HANDLED;
}

M
Magnus Damm 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
				      int start)
{
	unsigned long tmp = lcdc_read(priv, _LDCNT2R);
	int k;

	/* start or stop the lcdc */
	if (start)
		lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
	else
		lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);

	/* wait until power is applied/stopped on all channels */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
		if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
			while (1) {
				tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
				if (start && tmp == 3)
					break;
				if (!start && tmp == 0)
					break;
				cpu_relax();
			}

	if (!start)
		lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
}

static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct fb_videomode *lcd_cfg;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	unsigned long tmp;
	int k, m;
	int ret = 0;

336 337 338 339 340
	/* enable clocks before accessing the hardware */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
		if (priv->ch[k].enabled)
			sh_mobile_lcdc_clk_on(priv);

M
Magnus Damm 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 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 382
	/* reset */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
	lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);

	/* enable LCDC channels */
	tmp = lcdc_read(priv, _LDCNT2R);
	tmp |= priv->ch[0].enabled;
	tmp |= priv->ch[1].enabled;
	lcdc_write(priv, _LDCNT2R, tmp);

	/* read data from external memory, avoid using the BEU for now */
	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);

	/* stop the lcdc first */
	sh_mobile_lcdc_start_stop(priv, 0);

	/* configure clocks */
	tmp = priv->lddckr;
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		m = ch->cfg.clock_divider;
		if (!m)
			continue;

		if (m == 1)
			m = 1 << 6;
		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);

		lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
	}

	lcdc_write(priv, _LDDCKR, tmp);

	/* start dotclock again */
	lcdc_write(priv, _LDDCKSTPR, 0);
	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);

383
	/* interrupts are disabled to begin with */
M
Magnus Damm 已提交
384 385 386 387 388 389 390 391 392 393 394 395
	lcdc_write(priv, _LDINTR, 0);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
		lcd_cfg = &ch->cfg.lcd_cfg;

		if (!ch->enabled)
			continue;

		tmp = ch->ldmt1r_value;
		tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
		tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
396 397 398 399 400
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
		tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
M
Magnus Damm 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 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 445 446 447 448 449 450 451 452 453 454 455
		lcdc_write_chan(ch, LDMT1R, tmp);

		/* setup SYS bus */
		lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
		lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);

		/* horizontal configuration */
		tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
		tmp += lcd_cfg->left_margin;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; /* HTCN */
		tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
		lcdc_write_chan(ch, LDHCNR, tmp);

		tmp = lcd_cfg->xres;
		tmp += lcd_cfg->right_margin;
		tmp /= 8; /* HSYNP */
		tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
		lcdc_write_chan(ch, LDHSYNR, tmp);

		/* power supply */
		lcdc_write_chan(ch, LDPMR, 0);

		/* vertical configuration */
		tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
		tmp += lcd_cfg->upper_margin;
		tmp += lcd_cfg->lower_margin; /* VTLN */
		tmp |= lcd_cfg->yres << 16; /* VDLN */
		lcdc_write_chan(ch, LDVLNR, tmp);

		tmp = lcd_cfg->yres;
		tmp += lcd_cfg->lower_margin; /* VSYNP */
		tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
		lcdc_write_chan(ch, LDVSYNR, tmp);

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->setup_sys)
			ret = board_cfg->setup_sys(board_cfg->board_data, ch,
						   &sh_mobile_lcdc_sys_bus_ops);
		if (ret)
			return ret;
	}

	/* word and long word swap */
	lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);

	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];

		if (!priv->ch[k].enabled)
			continue;

		/* set bpp format in PKF[4:0] */
		tmp = lcdc_read_chan(ch, LDDFR);
		tmp &= ~(0x0001001f);
456
		tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0;
M
Magnus Damm 已提交
457 458 459
		lcdc_write_chan(ch, LDDFR, tmp);

		/* point out our frame buffer */
460
		lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start);
M
Magnus Damm 已提交
461 462

		/* set line size */
463
		lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length);
M
Magnus Damm 已提交
464

465 466 467 468 469
		/* setup deferred io if SYS bus */
		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
		if (ch->ldmt1r_value & (1 << 12) && tmp) {
			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
			ch->defio.delay = msecs_to_jiffies(tmp);
470 471
			ch->info->fbdefio = &ch->defio;
			fb_deferred_io_init(ch->info);
472 473 474 475 476 477 478 479 480 481 482

			/* one-shot mode */
			lcdc_write_chan(ch, LDSM1R, 1);

			/* enable "Frame End Interrupt Enable" bit */
			lcdc_write(priv, _LDINTR, LDINTR_FE);

		} else {
			/* continuous read mode */
			lcdc_write_chan(ch, LDSM1R, 0);
		}
M
Magnus Damm 已提交
483 484 485 486 487 488 489
	}

	/* display output */
	lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);

	/* start the lcdc */
	sh_mobile_lcdc_start_stop(priv, 1);
490
	priv->started = 1;
M
Magnus Damm 已提交
491 492 493 494

	/* tell the board code to enable the panel */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
M
Magnus Damm 已提交
495 496 497
		if (!ch->enabled)
			continue;

M
Magnus Damm 已提交
498 499 500 501 502 503 504 505 506 507 508 509 510 511
		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->display_on)
			board_cfg->display_on(board_cfg->board_data);
	}

	return 0;
}

static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
{
	struct sh_mobile_lcdc_chan *ch;
	struct sh_mobile_lcdc_board_cfg	*board_cfg;
	int k;

512
	/* clean up deferred io and ask board code to disable panel */
M
Magnus Damm 已提交
513 514
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
		ch = &priv->ch[k];
M
Magnus Damm 已提交
515 516
		if (!ch->enabled)
			continue;
517

518 519 520 521
		/* deferred io mode:
		 * flush frame, and wait for frame end interrupt
		 * clean up deferred io and enable clock
		 */
522
		if (ch->info->fbdefio) {
523
			ch->frame_end = 0;
524
			schedule_delayed_work(&ch->info->deferred_work, 0);
525
			wait_event(ch->frame_end_wait, ch->frame_end);
526 527
			fb_deferred_io_cleanup(ch->info);
			ch->info->fbdefio = NULL;
528
			sh_mobile_lcdc_clk_on(priv);
529
		}
530 531 532 533

		board_cfg = &ch->cfg.board_cfg;
		if (board_cfg->display_off)
			board_cfg->display_off(board_cfg->board_data);
M
Magnus Damm 已提交
534 535 536
	}

	/* stop the lcdc */
537 538 539 540
	if (priv->started) {
		sh_mobile_lcdc_start_stop(priv, 0);
		priv->started = 0;
	}
541

542 543 544 545
	/* stop clocks */
	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
		if (priv->ch[k].enabled)
			sh_mobile_lcdc_clk_off(priv);
M
Magnus Damm 已提交
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 586 587
}

static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
{
	int ifm, miftyp;

	switch (ch->cfg.interface_type) {
	case RGB8: ifm = 0; miftyp = 0; break;
	case RGB9: ifm = 0; miftyp = 4; break;
	case RGB12A: ifm = 0; miftyp = 5; break;
	case RGB12B: ifm = 0; miftyp = 6; break;
	case RGB16: ifm = 0; miftyp = 7; break;
	case RGB18: ifm = 0; miftyp = 10; break;
	case RGB24: ifm = 0; miftyp = 11; break;
	case SYS8A: ifm = 1; miftyp = 0; break;
	case SYS8B: ifm = 1; miftyp = 1; break;
	case SYS8C: ifm = 1; miftyp = 2; break;
	case SYS8D: ifm = 1; miftyp = 3; break;
	case SYS9: ifm = 1; miftyp = 4; break;
	case SYS12: ifm = 1; miftyp = 5; break;
	case SYS16A: ifm = 1; miftyp = 7; break;
	case SYS16B: ifm = 1; miftyp = 8; break;
	case SYS16C: ifm = 1; miftyp = 9; break;
	case SYS18: ifm = 1; miftyp = 10; break;
	case SYS24: ifm = 1; miftyp = 11; break;
	default: goto bad;
	}

	/* SUBLCD only supports SYS interface */
	if (lcdc_chan_is_sublcd(ch)) {
		if (ifm == 0)
			goto bad;
		else
			ifm = 0;
	}

	ch->ldmt1r_value = (ifm << 12) | miftyp;
	return 0;
 bad:
	return -EINVAL;
}

588 589
static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
				       int clock_source,
M
Magnus Damm 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
				       struct sh_mobile_lcdc_priv *priv)
{
	char *str;
	int icksel;

	switch (clock_source) {
	case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
	case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
	case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
	default:
		return -EINVAL;
	}

	priv->lddckr = icksel << 16;

	if (str) {
606 607 608 609
		priv->dot_clk = clk_get(&pdev->dev, str);
		if (IS_ERR(priv->dot_clk)) {
			dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
			return PTR_ERR(priv->dot_clk);
M
Magnus Damm 已提交
610 611
		}
	}
612 613 614 615 616 617 618 619
	atomic_set(&priv->hw_usecnt, -1);

	/* Runtime PM support involves two step for this driver:
	 * 1) Enable Runtime PM
	 * 2) Force Runtime PM Resume since hardware is accessed from probe()
	 */
	pm_runtime_enable(priv->dev);
	pm_runtime_resume(priv->dev);
M
Magnus Damm 已提交
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 647 648 649 650 651 652 653
	return 0;
}

static int sh_mobile_lcdc_setcolreg(u_int regno,
				    u_int red, u_int green, u_int blue,
				    u_int transp, struct fb_info *info)
{
	u32 *palette = info->pseudo_palette;

	if (regno >= PALETTE_NR)
		return -EINVAL;

	/* only FB_VISUAL_TRUECOLOR supported */

	red >>= 16 - info->var.red.length;
	green >>= 16 - info->var.green.length;
	blue >>= 16 - info->var.blue.length;
	transp >>= 16 - info->var.transp.length;

	palette[regno] = (red << info->var.red.offset) |
	  (green << info->var.green.offset) |
	  (blue << info->var.blue.offset) |
	  (transp << info->var.transp.offset);

	return 0;
}

static struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
	.id =		"SH Mobile LCDC",
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_TRUECOLOR,
	.accel =	FB_ACCEL_NONE,
};

654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
static void sh_mobile_lcdc_fillrect(struct fb_info *info,
				    const struct fb_fillrect *rect)
{
	sys_fillrect(info, rect);
	sh_mobile_lcdc_deferred_io_touch(info);
}

static void sh_mobile_lcdc_copyarea(struct fb_info *info,
				    const struct fb_copyarea *area)
{
	sys_copyarea(info, area);
	sh_mobile_lcdc_deferred_io_touch(info);
}

static void sh_mobile_lcdc_imageblit(struct fb_info *info,
				     const struct fb_image *image)
{
	sys_imageblit(info, image);
	sh_mobile_lcdc_deferred_io_touch(info);
}

M
Magnus Damm 已提交
675 676
static struct fb_ops sh_mobile_lcdc_ops = {
	.fb_setcolreg	= sh_mobile_lcdc_setcolreg,
677 678
	.fb_read        = fb_sys_read,
	.fb_write       = fb_sys_write,
679 680 681
	.fb_fillrect	= sh_mobile_lcdc_fillrect,
	.fb_copyarea	= sh_mobile_lcdc_copyarea,
	.fb_imageblit	= sh_mobile_lcdc_imageblit,
M
Magnus Damm 已提交
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
};

static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
{
	switch (bpp) {
	case 16: /* PKF[4:0] = 00011 - RGB 565 */
		var->red.offset = 11;
		var->red.length = 5;
		var->green.offset = 5;
		var->green.length = 6;
		var->blue.offset = 0;
		var->blue.length = 5;
		var->transp.offset = 0;
		var->transp.length = 0;
		break;

	case 32: /* PKF[4:0] = 00000 - RGB 888
		  * sh7722 pdf says 00RRGGBB but reality is GGBB00RR
		  * this may be because LDDDSR has word swap enabled..
		  */
		var->red.offset = 0;
		var->red.length = 8;
		var->green.offset = 24;
		var->green.length = 8;
		var->blue.offset = 16;
		var->blue.length = 8;
		var->transp.offset = 0;
		var->transp.length = 0;
		break;
	default:
		return -EINVAL;
	}
	var->bits_per_pixel = bpp;
	var->red.msb_right = 0;
	var->green.msb_right = 0;
	var->blue.msb_right = 0;
	var->transp.msb_right = 0;
	return 0;
}

722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
static int sh_mobile_lcdc_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);

	sh_mobile_lcdc_stop(platform_get_drvdata(pdev));
	return 0;
}

static int sh_mobile_lcdc_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);

	return sh_mobile_lcdc_start(platform_get_drvdata(pdev));
}

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
static int sh_mobile_lcdc_runtime_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
	struct sh_mobile_lcdc_chan *ch;
	int k, n;

	/* save per-channel registers */
	for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
		ch = &p->ch[k];
		if (!ch->enabled)
			continue;
		for (n = 0; n < NR_CH_REGS; n++)
			ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
	}

	/* save shared registers */
	for (n = 0; n < NR_SHARED_REGS; n++)
		p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);

	/* turn off LCDC hardware */
	lcdc_write(p, _LDCNT1R, 0);
	return 0;
}

static int sh_mobile_lcdc_runtime_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
	struct sh_mobile_lcdc_chan *ch;
	int k, n;

	/* restore per-channel registers */
	for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
		ch = &p->ch[k];
		if (!ch->enabled)
			continue;
		for (n = 0; n < NR_CH_REGS; n++)
			lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
	}

	/* restore shared registers */
	for (n = 0; n < NR_SHARED_REGS; n++)
		lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);

	return 0;
}

785 786 787
static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
	.suspend = sh_mobile_lcdc_suspend,
	.resume = sh_mobile_lcdc_resume,
788 789
	.runtime_suspend = sh_mobile_lcdc_runtime_suspend,
	.runtime_resume = sh_mobile_lcdc_runtime_resume,
790 791
};

M
Magnus Damm 已提交
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
static int sh_mobile_lcdc_remove(struct platform_device *pdev);

static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
{
	struct fb_info *info;
	struct sh_mobile_lcdc_priv *priv;
	struct sh_mobile_lcdc_info *pdata;
	struct sh_mobile_lcdc_chan_cfg *cfg;
	struct resource *res;
	int error;
	void *buf;
	int i, j;

	if (!pdev->dev.platform_data) {
		dev_err(&pdev->dev, "no platform data defined\n");
		error = -EINVAL;
		goto err0;
	}

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
812 813 814
	i = platform_get_irq(pdev, 0);
	if (!res || i < 0) {
		dev_err(&pdev->dev, "cannot get platform resources\n");
M
Magnus Damm 已提交
815 816 817 818 819 820 821 822 823 824 825
		error = -ENOENT;
		goto err0;
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		dev_err(&pdev->dev, "cannot allocate device data\n");
		error = -ENOMEM;
		goto err0;
	}

826
	error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
827
			    dev_name(&pdev->dev), priv);
828 829 830 831 832 833
	if (error) {
		dev_err(&pdev->dev, "unable to request irq\n");
		goto err1;
	}

	priv->irq = i;
834
	priv->dev = &pdev->dev;
M
Magnus Damm 已提交
835 836 837 838 839 840 841 842 843 844 845 846 847
	platform_set_drvdata(pdev, priv);
	pdata = pdev->dev.platform_data;

	j = 0;
	for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
		priv->ch[j].lcdc = priv;
		memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i]));

		error = sh_mobile_lcdc_check_interface(&priv->ch[i]);
		if (error) {
			dev_err(&pdev->dev, "unsupported interface type\n");
			goto err1;
		}
848
		init_waitqueue_head(&priv->ch[i].frame_end_wait);
M
Magnus Damm 已提交
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869

		switch (pdata->ch[i].chan) {
		case LCDC_CHAN_MAINLCD:
			priv->ch[j].enabled = 1 << 1;
			priv->ch[j].reg_offs = lcdc_offs_mainlcd;
			j++;
			break;
		case LCDC_CHAN_SUBLCD:
			priv->ch[j].enabled = 1 << 2;
			priv->ch[j].reg_offs = lcdc_offs_sublcd;
			j++;
			break;
		}
	}

	if (!j) {
		dev_err(&pdev->dev, "no channels defined\n");
		error = -EINVAL;
		goto err1;
	}

870
	error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
M
Magnus Damm 已提交
871 872 873 874 875 876 877 878 879 880
	if (error) {
		dev_err(&pdev->dev, "unable to setup clocks\n");
		goto err1;
	}

	priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);

	for (i = 0; i < j; i++) {
		cfg = &priv->ch[i].cfg;

881 882 883 884 885 886 887 888
		priv->ch[i].info = framebuffer_alloc(0, &pdev->dev);
		if (!priv->ch[i].info) {
			dev_err(&pdev->dev, "unable to allocate fb_info\n");
			error = -ENOMEM;
			break;
		}

		info = priv->ch[i].info;
M
Magnus Damm 已提交
889 890 891
		info->fbops = &sh_mobile_lcdc_ops;
		info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
		info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres;
892 893
		info->var.width = cfg->lcd_size_cfg.width;
		info->var.height = cfg->lcd_size_cfg.height;
M
Magnus Damm 已提交
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925
		info->var.activate = FB_ACTIVATE_NOW;
		error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
		if (error)
			break;

		info->fix = sh_mobile_lcdc_fix;
		info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
		info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres;

		buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
					 &priv->ch[i].dma_handle, GFP_KERNEL);
		if (!buf) {
			dev_err(&pdev->dev, "unable to allocate buffer\n");
			error = -ENOMEM;
			break;
		}

		info->pseudo_palette = &priv->ch[i].pseudo_palette;
		info->flags = FBINFO_FLAG_DEFAULT;

		error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
		if (error < 0) {
			dev_err(&pdev->dev, "unable to allocate cmap\n");
			dma_free_coherent(&pdev->dev, info->fix.smem_len,
					  buf, priv->ch[i].dma_handle);
			break;
		}

		memset(buf, 0, info->fix.smem_len);
		info->fix.smem_start = priv->ch[i].dma_handle;
		info->screen_base = buf;
		info->device = &pdev->dev;
926
		info->par = &priv->ch[i];
M
Magnus Damm 已提交
927 928 929 930 931 932 933 934 935 936 937 938
	}

	if (error)
		goto err1;

	error = sh_mobile_lcdc_start(priv);
	if (error) {
		dev_err(&pdev->dev, "unable to start hardware\n");
		goto err1;
	}

	for (i = 0; i < j; i++) {
939 940
		struct sh_mobile_lcdc_chan *ch = priv->ch + i;

941
		info = ch->info;
942 943 944 945 946 947 948 949 950 951 952

		if (info->fbdefio) {
			priv->ch->sglist = vmalloc(sizeof(struct scatterlist) *
					info->fix.smem_len >> PAGE_SHIFT);
			if (!priv->ch->sglist) {
				dev_err(&pdev->dev, "cannot allocate sglist\n");
				goto err1;
			}
		}

		error = register_framebuffer(info);
M
Magnus Damm 已提交
953 954 955 956 957 958
		if (error < 0)
			goto err1;

		dev_info(info->dev,
			 "registered %s/%s as %dx%d %dbpp.\n",
			 pdev->name,
959
			 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
M
Magnus Damm 已提交
960
			 "mainlcd" : "sublcd",
961 962 963
			 (int) ch->cfg.lcd_cfg.xres,
			 (int) ch->cfg.lcd_cfg.yres,
			 ch->cfg.bpp);
964 965 966 967

		/* deferred io mode: disable clock to save power */
		if (info->fbdefio)
			sh_mobile_lcdc_clk_off(priv);
M
Magnus Damm 已提交
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983
	}

	return 0;
 err1:
	sh_mobile_lcdc_remove(pdev);
 err0:
	return error;
}

static int sh_mobile_lcdc_remove(struct platform_device *pdev)
{
	struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
	struct fb_info *info;
	int i;

	for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
984 985
		if (priv->ch[i].info->dev)
			unregister_framebuffer(priv->ch[i].info);
M
Magnus Damm 已提交
986 987 988 989

	sh_mobile_lcdc_stop(priv);

	for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
990
		info = priv->ch[i].info;
M
Magnus Damm 已提交
991

992
		if (!info || !info->device)
M
Magnus Damm 已提交
993 994
			continue;

995 996 997
		if (priv->ch[i].sglist)
			vfree(priv->ch[i].sglist);

M
Magnus Damm 已提交
998 999 1000
		dma_free_coherent(&pdev->dev, info->fix.smem_len,
				  info->screen_base, priv->ch[i].dma_handle);
		fb_dealloc_cmap(&info->cmap);
1001
		framebuffer_release(info);
M
Magnus Damm 已提交
1002 1003
	}

1004 1005
	if (priv->dot_clk)
		clk_put(priv->dot_clk);
1006 1007

	pm_runtime_disable(priv->dev);
M
Magnus Damm 已提交
1008 1009 1010 1011

	if (priv->base)
		iounmap(priv->base);

1012 1013
	if (priv->irq)
		free_irq(priv->irq, priv);
M
Magnus Damm 已提交
1014 1015 1016 1017 1018 1019 1020 1021
	kfree(priv);
	return 0;
}

static struct platform_driver sh_mobile_lcdc_driver = {
	.driver		= {
		.name		= "sh_mobile_lcdc_fb",
		.owner		= THIS_MODULE,
1022
		.pm		= &sh_mobile_lcdc_dev_pm_ops,
M
Magnus Damm 已提交
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
	},
	.probe		= sh_mobile_lcdc_probe,
	.remove		= sh_mobile_lcdc_remove,
};

static int __init sh_mobile_lcdc_init(void)
{
	return platform_driver_register(&sh_mobile_lcdc_driver);
}

static void __exit sh_mobile_lcdc_exit(void)
{
	platform_driver_unregister(&sh_mobile_lcdc_driver);
}

module_init(sh_mobile_lcdc_init);
module_exit(sh_mobile_lcdc_exit);

MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver");
MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
MODULE_LICENSE("GPL v2");