sh_mobile_lcdcfb.c revision 505c7de51fe5ebb81fac096cb8cebd7cb45b7955
1cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm/*
2cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * SuperH Mobile LCDC Framebuffer
3cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm *
4cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * Copyright (c) 2008 Magnus Damm
5cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm *
6cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * This file is subject to the terms and conditions of the GNU General Public
7cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * License.  See the file "COPYING" in the main directory of this archive
8cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * for more details.
9cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm */
10cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
11cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/kernel.h>
12cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/init.h>
13cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/delay.h>
14cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/mm.h>
15cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/clk.h>
160246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm#include <linux/pm_runtime.h>
17cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/platform_device.h>
18cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/dma-mapping.h>
198564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm#include <linux/interrupt.h>
201c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt#include <linux/vmalloc.h>
2140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy#include <linux/ioctl.h>
225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
23dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski#include <linux/console.h>
243b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot#include <linux/backlight.h>
253b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot#include <linux/gpio.h>
26225c9a8d1da274bf23efec43ec28b1c9e45e12f8Paul Mundt#include <video/sh_mobile_lcdc.h>
2760063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.h>
28cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
296de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski#include "sh_mobile_lcdcfb.h"
307caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia#include "sh_mobile_meram.h"
316de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski
32a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define SIDE_B_OFFSET 0x1000
33a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define MIRROR_OFFSET 0x2000
34cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
350246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm/* shared registers and their order for context save/restore */
360246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int lcdc_shared_regs[] = {
370246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDDCKR,
380246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDDCKSTPR,
390246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDINTR,
400246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDDDSR,
410246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDCNT1R,
420246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	_LDCNT2R,
430246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm};
440246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs)
450246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
46d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski#define MAX_XRES 1920
47d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski#define MAX_YRES 1080
48cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
490246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
50cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDCKPAT1R] = 0x400,
51cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDCKPAT2R] = 0x404,
52cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT1R] = 0x418,
53cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT2R] = 0x41c,
54cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT3R] = 0x420,
55cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDFR] = 0x424,
56cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDSM1R] = 0x428,
578564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	[LDSM2R] = 0x42c,
58cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDSA1R] = 0x430,
5953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	[LDSA2R] = 0x434,
60cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMLSR] = 0x438,
61cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDHCNR] = 0x448,
62cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDHSYNR] = 0x44c,
63cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDVLNR] = 0x450,
64cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDVSYNR] = 0x454,
65cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDPMR] = 0x460,
666011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	[LDHAJR] = 0x4a0,
67cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
68cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
690246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
70cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDCKPAT1R] = 0x408,
71cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDCKPAT2R] = 0x40c,
72cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT1R] = 0x600,
73cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT2R] = 0x604,
74cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMT3R] = 0x608,
75cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDDFR] = 0x60c,
76cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDSM1R] = 0x610,
778564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	[LDSM2R] = 0x614,
78cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDSA1R] = 0x618,
79cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDMLSR] = 0x620,
80cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDHCNR] = 0x624,
81cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDHSYNR] = 0x628,
82cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDVLNR] = 0x62c,
83cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDVSYNR] = 0x630,
84cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	[LDPMR] = 0x63c,
85cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
86cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
87c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetskistatic const struct fb_videomode default_720p = {
88c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.name = "HDMI 720p",
89c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.xres = 1280,
90c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.yres = 720,
91c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski
925ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski	.left_margin = 220,
935ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski	.right_margin = 110,
945ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski	.hsync_len = 40,
95c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski
96c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.upper_margin = 20,
97c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.lower_margin = 5,
98c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.vsync_len = 5,
99c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski
100c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.pixclock = 13468,
1015ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski	.refresh = 60,
102c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski	.sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
1030246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm};
1040246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
1050246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstruct sh_mobile_lcdc_priv {
1060246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	void __iomem *base;
1070246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	int irq;
1080246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	atomic_t hw_usecnt;
1090246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct device *dev;
1100246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct clk *dot_clk;
1110246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	unsigned long lddckr;
1120246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct sh_mobile_lcdc_chan ch[2];
1136011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	struct notifier_block notifier;
1140246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	unsigned long saved_shared_regs[NR_SHARED_REGS];
1150246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	int started;
116417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	int forced_bpp; /* 2 channel LCDC must share bpp setting */
1177caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia	struct sh_mobile_meram_info *meram_dev;
1180246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm};
1190246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
120a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthystatic bool banked(int reg_nr)
121a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy{
122a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	switch (reg_nr) {
123a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDMT1R:
124a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDMT2R:
125a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDMT3R:
126a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDDFR:
127a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDSM1R:
128a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDSA1R:
12953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	case LDSA2R:
130a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDMLSR:
131a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDHCNR:
132a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDHSYNR:
133a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDVLNR:
134a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	case LDVSYNR:
135a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy		return true;
136a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	}
137a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	return false;
138a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy}
139a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy
140cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
141cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			    int reg_nr, unsigned long data)
142cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
143cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
144a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	if (banked(reg_nr))
145a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy		iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] +
146a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy			  SIDE_B_OFFSET);
147a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy}
148a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy
149a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthystatic void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan,
150a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy			    int reg_nr, unsigned long data)
151a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy{
152a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy	iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] +
153a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy		  MIRROR_OFFSET);
154cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
155cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
156cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
157cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				    int reg_nr)
158cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
159cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
160cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
161cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
162cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_write(struct sh_mobile_lcdc_priv *priv,
163cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		       unsigned long reg_offs, unsigned long data)
164cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
165cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	iowrite32(data, priv->base + reg_offs);
166cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
167cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
168cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,
169cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			       unsigned long reg_offs)
170cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
171cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return ioread32(priv->base + reg_offs);
172cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
173cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
174cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
175cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			  unsigned long reg_offs,
176cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			  unsigned long mask, unsigned long until)
177cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
178cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	while ((lcdc_read(priv, reg_offs) & mask) != until)
179cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		cpu_relax();
180cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
181cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
182cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
183cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
184cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return chan->cfg.chan == LCDC_CHAN_SUBLCD;
185cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
186cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
187cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_sys_write_index(void *handle, unsigned long data)
188cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
189cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_chan *ch = handle;
190cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
191ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT);
192ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
193ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
194ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		   (lcdc_chan_is_sublcd(ch) ? 2 : 0));
195ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
196cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
197cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
198cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_sys_write_data(void *handle, unsigned long data)
199cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
200cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_chan *ch = handle;
201cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
202ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDWD0R, data | LDDWDxR_WDACT | LDDWDxR_RSW);
203ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
204ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDWAR, LDDWAR_WA |
205ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		   (lcdc_chan_is_sublcd(ch) ? 2 : 0));
206ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
207cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
208cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
209cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_sys_read_data(void *handle)
210cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
211cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_chan *ch = handle;
212cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
213ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDRDR, LDDRDR_RSR);
214ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
215ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(ch->lcdc, _LDDRAR, LDDRAR_RA |
216ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		   (lcdc_chan_is_sublcd(ch) ? 2 : 0));
217cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	udelay(1);
218ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(ch->lcdc, _LDSR, LDSR_AS, 0);
219cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
220ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	return lcdc_read(ch->lcdc, _LDDRDR) & LDDRDR_DRD_MASK;
221cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
222cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
223cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstruct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
224cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_sys_write_index,
225cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_sys_write_data,
226cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_sys_read_data,
227cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
228cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
2298564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
2308564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
2310246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	if (atomic_inc_and_test(&priv->hw_usecnt)) {
2328564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		if (priv->dot_clk)
2338564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			clk_enable(priv->dot_clk);
234f1ad90da5c0fcb8841cc5e6d66c56f4005d8c960Laurent Pinchart		pm_runtime_get_sync(priv->dev);
235ec19b9e0fa808d82ad996d73358a5b06a565b78bDamian Hobson-Garcia		if (priv->meram_dev && priv->meram_dev->pdev)
236ec19b9e0fa808d82ad996d73358a5b06a565b78bDamian Hobson-Garcia			pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
2378564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	}
2388564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
2398564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
2408564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
2418564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
2420246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
243ec19b9e0fa808d82ad996d73358a5b06a565b78bDamian Hobson-Garcia		if (priv->meram_dev && priv->meram_dev->pdev)
244ec19b9e0fa808d82ad996d73358a5b06a565b78bDamian Hobson-Garcia			pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
2450246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		pm_runtime_put(priv->dev);
246f1ad90da5c0fcb8841cc5e6d66c56f4005d8c960Laurent Pinchart		if (priv->dot_clk)
247f1ad90da5c0fcb8841cc5e6d66c56f4005d8c960Laurent Pinchart			clk_disable(priv->dot_clk);
2488564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	}
2498564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
2508564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
2511c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundtstatic int sh_mobile_lcdc_sginit(struct fb_info *info,
2521c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt				  struct list_head *pagelist)
2531c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt{
2541c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	struct sh_mobile_lcdc_chan *ch = info->par;
2551c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT;
2561c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	struct page *page;
2571c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	int nr_pages = 0;
2581c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
2591c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	sg_init_table(ch->sglist, nr_pages_max);
2601c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
2611c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	list_for_each_entry(page, pagelist, lru)
2621c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
2631c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
2641c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt	return nr_pages;
2651c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt}
2661c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
2678564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_deferred_io(struct fb_info *info,
2688564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm				       struct list_head *pagelist)
2698564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
2708564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	struct sh_mobile_lcdc_chan *ch = info->par;
271ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm	struct sh_mobile_lcdc_board_cfg	*bcfg = &ch->cfg.board_cfg;
2728564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
2738564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	/* enable clocks before accessing hardware */
2748564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sh_mobile_lcdc_clk_on(ch->lcdc);
2758564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
2765c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	/*
2775c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * It's possible to get here without anything on the pagelist via
2785c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
2795c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * invocation. In the former case, the acceleration routines are
2805c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * stepped in to when using the framebuffer console causing the
2815c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * workqueue to be scheduled without any dirty pages on the list.
2825c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 *
2835c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * Despite this, a panel update is still needed given that the
2845c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * acceleration routines have their own methods for writing in
2855c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * that still need to be updated.
2865c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 *
2875c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * The fsync() and empty pagelist case could be optimized for,
2885c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * but we don't bother, as any application exhibiting such
2895c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 * behaviour is fundamentally broken anyways.
2905c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	 */
2915c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt	if (!list_empty(pagelist)) {
2925c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt		unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
2935c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt
2945c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt		/* trigger panel update */
2955c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt		dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
296ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm		if (bcfg->start_transfer)
297ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm			bcfg->start_transfer(bcfg->board_data, ch,
298ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm					     &sh_mobile_lcdc_sys_bus_ops);
299ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
3005c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt		dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
301ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm	} else {
302ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm		if (bcfg->start_transfer)
303ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm			bcfg->start_transfer(bcfg->board_data, ch,
304ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm					     &sh_mobile_lcdc_sys_bus_ops);
305ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
306ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm	}
3078564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
3088564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
3098564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
3108564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
3118564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	struct fb_deferred_io *fbdefio = info->fbdefio;
3128564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
3138564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	if (fbdefio)
3148564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		schedule_delayed_work(&info->deferred_work, fbdefio->delay);
3158564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
3168564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
3178564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
3188564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
3198564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	struct sh_mobile_lcdc_priv *priv = data;
3202feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	struct sh_mobile_lcdc_chan *ch;
3219dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	unsigned long ldintr;
3222feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	int is_sub;
3232feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	int k;
3248564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
325dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	/* Acknowledge interrupts and disable further VSYNC End IRQs. */
326dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	ldintr = lcdc_read(priv, _LDINTR);
327dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	lcdc_write(priv, _LDINTR, (ldintr ^ LDINTR_STATUS_MASK) & ~LDINTR_VEE);
3288564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
3292feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	/* figure out if this interrupt is for main or sub lcd */
330ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	is_sub = (lcdc_read(priv, _LDSR) & LDSR_MSS) ? 1 : 0;
3312feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
3329dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	/* wake up channel and disable clocks */
3332feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
3342feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		ch = &priv->ch[k];
3352feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
3362feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		if (!ch->enabled)
3372feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm			continue;
3382feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
339dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart		/* Frame End */
3409dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy		if (ldintr & LDINTR_FS) {
3419dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy			if (is_sub == lcdc_chan_is_sublcd(ch)) {
3429dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy				ch->frame_end = 1;
3439dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy				wake_up(&ch->frame_end_wait);
3442feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
3459dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy				sh_mobile_lcdc_clk_off(priv);
3469dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy			}
3479dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy		}
3489dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
3499dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy		/* VSYNC End */
35040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		if (ldintr & LDINTR_VES)
35140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy			complete(&ch->vsync_completion);
3522feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	}
3532feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
3548564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	return IRQ_HANDLED;
3558564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
3568564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
357cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
358cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				      int start)
359cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
360cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	unsigned long tmp = lcdc_read(priv, _LDCNT2R);
361cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	int k;
362cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
363cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* start or stop the lcdc */
364cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (start)
365ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		lcdc_write(priv, _LDCNT2R, tmp | LDCNT2R_DO);
366cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	else
367ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		lcdc_write(priv, _LDCNT2R, tmp & ~LDCNT2R_DO);
368cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
369cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* wait until power is applied/stopped on all channels */
370cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
371cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
372cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			while (1) {
373ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp = lcdc_read_chan(&priv->ch[k], LDPMR)
374ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				    & LDPMR_LPS;
375ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				if (start && tmp == LDPMR_LPS)
376cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm					break;
377cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				if (!start && tmp == 0)
378cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm					break;
379cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				cpu_relax();
380cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			}
381cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
382cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (!start)
383cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
384cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
385cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
3866011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetskistatic void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
3876011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski{
3881c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var;
3891c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	unsigned long h_total, hsync_pos, display_h_total;
3906011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	u32 tmp;
3916011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
3926011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	tmp = ch->ldmt1r_value;
393ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
394ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
395ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
396ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
397ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
398ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
399ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
4006011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDMT1R, tmp);
4016011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4026011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	/* setup SYS bus */
4036011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
4046011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
4056011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4066011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	/* horizontal configuration */
4071c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	h_total = display_var->xres + display_var->hsync_len +
4081c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		display_var->left_margin + display_var->right_margin;
4096011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	tmp = h_total / 8; /* HTCN */
4101c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */
4116011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDHCNR, tmp);
4126011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4131c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	hsync_pos = display_var->xres + display_var->right_margin;
4146011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	tmp = hsync_pos / 8; /* HSYNP */
4151c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */
4166011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDHSYNR, tmp);
4176011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4186011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	/* vertical configuration */
4191c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp = display_var->yres + display_var->vsync_len +
4201c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		display_var->upper_margin + display_var->lower_margin; /* VTLN */
4211c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */
4226011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDVLNR, tmp);
4236011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4241c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp = display_var->yres + display_var->lower_margin; /* VSYNP */
4251c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp |= display_var->vsync_len << 16; /* VSYNW */
4266011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDVSYNR, tmp);
4276011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
4286011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	/* Adjust horizontal synchronisation for HDMI */
4291c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	display_h_total = display_var->xres + display_var->hsync_len +
4301c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		display_var->left_margin + display_var->right_margin;
4311c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski	tmp = ((display_var->xres & 7) << 24) |
4321c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		((display_h_total & 7) << 16) |
4331c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		((display_var->hsync_len & 7) << 8) |
4346011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		hsync_pos;
4356011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	lcdc_write_chan(ch, LDHAJR, tmp);
4366011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski}
4376011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
438cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
439cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
440cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_chan *ch;
441cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_board_cfg	*board_cfg;
442cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	unsigned long tmp;
443417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	int bpp = 0;
44453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	unsigned long ldddsr;
445554cc1028603587e28ae49e9594b1508df5f29aaPaul Mundt	int k, m, ret;
446cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
4478564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	/* enable clocks before accessing the hardware */
448417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
449417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		if (priv->ch[k].enabled) {
4508564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			sh_mobile_lcdc_clk_on(priv);
451417d48274e755e537bae60461558c1f63a4e14deMagnus Damm			if (!bpp)
452417d48274e755e537bae60461558c1f63a4e14deMagnus Damm				bpp = priv->ch[k].info->var.bits_per_pixel;
453417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		}
454417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	}
4558564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
456cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* reset */
457ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR);
458ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
459cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
460cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* enable LCDC channels */
461cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	tmp = lcdc_read(priv, _LDCNT2R);
462cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	tmp |= priv->ch[0].enabled;
463cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	tmp |= priv->ch[1].enabled;
464cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_write(priv, _LDCNT2R, tmp);
465cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
466cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* read data from external memory, avoid using the BEU for now */
467ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~LDCNT2R_MD);
468cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
469cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* stop the lcdc first */
470cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	sh_mobile_lcdc_start_stop(priv, 0);
471cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
472cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* configure clocks */
473cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	tmp = priv->lddckr;
474cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
475cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		ch = &priv->ch[k];
476cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
477cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (!priv->ch[k].enabled)
478cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			continue;
479cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
480cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		m = ch->cfg.clock_divider;
481cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (!m)
482cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			continue;
483cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
484505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart		/* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider
485505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart		 * denominator.
486505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart		 */
487505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart		lcdc_write_chan(ch, LDDCKPAT1R, 0);
488505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart		lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
489505c7de51fe5ebb81fac096cb8cebd7cb45b7955Laurent Pinchart
490cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (m == 1)
491ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			m = LDDCKR_MOSEL;
492cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
493cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
494cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
495cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_write(priv, _LDDCKR, tmp);
496cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
497cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* start dotclock again */
498cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_write(priv, _LDDCKSTPR, 0);
499cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
500cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
5018564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	/* interrupts are disabled to begin with */
502cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	lcdc_write(priv, _LDINTR, 0);
503cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
504cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
505cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		ch = &priv->ch[k];
506cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
507cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (!ch->enabled)
508cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			continue;
509cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
5106011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		sh_mobile_lcdc_geometry(ch);
511cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
512cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		/* power supply */
513cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		lcdc_write_chan(ch, LDPMR, 0);
514cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
515cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		board_cfg = &ch->cfg.board_cfg;
51669843ba7f24950f8ef5dadacfbfbd08f53e3455bGuennadi Liakhovetski		if (board_cfg->setup_sys) {
517554cc1028603587e28ae49e9594b1508df5f29aaPaul Mundt			ret = board_cfg->setup_sys(board_cfg->board_data,
51869843ba7f24950f8ef5dadacfbfbd08f53e3455bGuennadi Liakhovetski						ch, &sh_mobile_lcdc_sys_bus_ops);
51969843ba7f24950f8ef5dadacfbfbd08f53e3455bGuennadi Liakhovetski			if (ret)
52069843ba7f24950f8ef5dadacfbfbd08f53e3455bGuennadi Liakhovetski				return ret;
52169843ba7f24950f8ef5dadacfbfbd08f53e3455bGuennadi Liakhovetski		}
522cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
523cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
524cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* word and long word swap */
52553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	ldddsr = lcdc_read(priv, _LDDDSR);
52653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	if  (priv->ch[0].info->var.nonstd)
527ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		ldddsr |= LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
52853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	else {
52953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		switch (bpp) {
53053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 16:
531ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			ldddsr |= LDDDSR_LS | LDDDSR_WS;
53253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			break;
53353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 24:
534ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			ldddsr |= LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
53553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			break;
53653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 32:
537ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			ldddsr |= LDDDSR_LS;
53853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			break;
53953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		}
540417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	}
541ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(priv, _LDDDSR, ldddsr);
542cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
543cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
5447caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		unsigned long base_addr_y;
5457caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		unsigned long base_addr_c = 0;
5467caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		int pitch;
547cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		ch = &priv->ch[k];
548cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
549cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (!priv->ch[k].enabled)
550cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			continue;
551cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
552cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		/* set bpp format in PKF[4:0] */
553cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		tmp = lcdc_read_chan(ch, LDDFR);
554ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		tmp &= ~(LDDFR_CF0 | LDDFR_CC | LDDFR_YF_MASK | LDDFR_PKF_MASK);
55553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		if (ch->info->var.nonstd) {
55653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			tmp |= (ch->info->var.nonstd << 16);
55753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			switch (ch->info->var.bits_per_pixel) {
55853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 12:
55953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
56053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 16:
561ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp |= LDDFR_YF_422;
56253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
56353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 24:
564ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp |= LDDFR_YF_444;
56553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
56653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			}
56753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		} else {
56853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			switch (ch->info->var.bits_per_pixel) {
56953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 16:
570ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp |= LDDFR_PKF_RGB16;
57153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
57253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 24:
573ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp |= LDDFR_PKF_RGB24;
57453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
57553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			case 32:
576ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart				tmp |= LDDFR_PKF_ARGB32;
57753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				break;
57853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			}
579417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		}
580cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		lcdc_write_chan(ch, LDDFR, tmp);
581cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
5827caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		base_addr_y = ch->info->fix.smem_start;
5837caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		base_addr_c = base_addr_y +
5847caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				ch->info->var.xres *
5857caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				ch->info->var.yres_virtual;
5867caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		pitch = ch->info->fix.line_length;
5877caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
5887caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		/* test if we can enable meram */
589eae9b85b5f65027df64130d8a3eeb2de9d094edaDamian Hobson-Garcia		if (ch->cfg.meram_cfg && priv->meram_dev &&
590eae9b85b5f65027df64130d8a3eeb2de9d094edaDamian Hobson-Garcia				priv->meram_dev->ops) {
5917caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			struct sh_mobile_meram_cfg *cfg;
5927caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			struct sh_mobile_meram_info *mdev;
5937caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			unsigned long icb_addr_y, icb_addr_c;
5947caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			int icb_pitch;
5957caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			int pf;
5967caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
5977caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			cfg = ch->cfg.meram_cfg;
5987caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			mdev = priv->meram_dev;
5997caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			/* we need to de-init configured ICBs before we
6007caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			 * we can re-initialize them.
6017caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			 */
6027caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			if (ch->meram_enabled)
6037caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				mdev->ops->meram_unregister(mdev, cfg);
6047caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
6057caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			ch->meram_enabled = 0;
6067caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
6073fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia			if (ch->info->var.nonstd) {
6083fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia				if (ch->info->var.bits_per_pixel == 24)
6093fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia					pf = SH_MOBILE_MERAM_PF_NV24;
6103fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia				else
6113fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia					pf = SH_MOBILE_MERAM_PF_NV;
6123fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia			} else {
6137caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				pf = SH_MOBILE_MERAM_PF_RGB;
6143fedd2ac7662a10ab2973d3b6f11cdce87b7171aDamian Hobson-Garcia			}
6157caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
6167caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			ret = mdev->ops->meram_register(mdev, cfg, pitch,
6177caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						ch->info->var.yres,
6187caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						pf,
6197caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						base_addr_y,
6207caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						base_addr_c,
6217caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						&icb_addr_y,
6227caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						&icb_addr_c,
6237caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia						&icb_pitch);
6247caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			if (!ret)  {
6257caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				/* set LDSA1R value */
6267caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				base_addr_y = icb_addr_y;
6277caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				pitch = icb_pitch;
6287caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
6297caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				/* set LDSA2R value if required */
6307caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				if (base_addr_c)
6317caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia					base_addr_c = icb_addr_c;
6327caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
6337caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia				ch->meram_enabled = 1;
6347caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			}
6357caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		}
6367caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
637cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		/* point out our frame buffer */
6387caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		lcdc_write_chan(ch, LDSA1R, base_addr_y);
63953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		if (ch->info->var.nonstd)
6407caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			lcdc_write_chan(ch, LDSA2R, base_addr_c);
641cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
642cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		/* set line size */
6437caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		lcdc_write_chan(ch, LDMLSR, pitch);
644cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
6458564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		/* setup deferred io if SYS bus */
6468564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
647ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
6488564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
6498564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			ch->defio.delay = msecs_to_jiffies(tmp);
650e33afddca174171a68d57476ead8947476ab9240Paul Mundt			ch->info->fbdefio = &ch->defio;
651e33afddca174171a68d57476ead8947476ab9240Paul Mundt			fb_deferred_io_init(ch->info);
6528564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
6538564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			/* one-shot mode */
654ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
6558564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
6568564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			/* enable "Frame End Interrupt Enable" bit */
6578564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			lcdc_write(priv, _LDINTR, LDINTR_FE);
6588564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
6598564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		} else {
6608564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			/* continuous read mode */
6618564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			lcdc_write_chan(ch, LDSM1R, 0);
6628564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		}
663cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
664cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
665cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* display output */
666ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	lcdc_write(priv, _LDCNT1R, LDCNT1R_DE);
667cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
668cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* start the lcdc */
669cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	sh_mobile_lcdc_start_stop(priv, 1);
6708e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm	priv->started = 1;
671cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
672cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* tell the board code to enable the panel */
673cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
674cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		ch = &priv->ch[k];
67521bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm		if (!ch->enabled)
67621bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm			continue;
67721bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm
678cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		board_cfg = &ch->cfg.board_cfg;
679247f99386100d1d1c369ba98120d2edebf5426fcAlexandre Courbot		if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
680c2439398170be9d7af28eb3ab59593369cb303f3Guennadi Liakhovetski			board_cfg->display_on(board_cfg->board_data, ch->info);
6816de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski			module_put(board_cfg->owner);
6826de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski		}
6833b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
6843b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		if (ch->bl) {
6853b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			ch->bl->props.power = FB_BLANK_UNBLANK;
6863b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			backlight_update_status(ch->bl);
6873b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		}
688cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
689cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
690cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
691cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
692cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
693cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
694cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
695cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_chan *ch;
696cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_board_cfg	*board_cfg;
697cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	int k;
698cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
6992feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	/* clean up deferred io and ask board code to disable panel */
700cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
701cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		ch = &priv->ch[k];
70221bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm		if (!ch->enabled)
70321bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm			continue;
7048564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
7052feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		/* deferred io mode:
7062feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		 * flush frame, and wait for frame end interrupt
7072feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		 * clean up deferred io and enable clock
7082feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		 */
7095ef6b505d9df45558402bdb823a078840a6a26c4Guennadi Liakhovetski		if (ch->info && ch->info->fbdefio) {
7102feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm			ch->frame_end = 0;
711e33afddca174171a68d57476ead8947476ab9240Paul Mundt			schedule_delayed_work(&ch->info->deferred_work, 0);
7122feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm			wait_event(ch->frame_end_wait, ch->frame_end);
713e33afddca174171a68d57476ead8947476ab9240Paul Mundt			fb_deferred_io_cleanup(ch->info);
714e33afddca174171a68d57476ead8947476ab9240Paul Mundt			ch->info->fbdefio = NULL;
7152feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm			sh_mobile_lcdc_clk_on(priv);
7168564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		}
7172feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
7183b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		if (ch->bl) {
7193b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			ch->bl->props.power = FB_BLANK_POWERDOWN;
7203b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			backlight_update_status(ch->bl);
7213b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		}
7223b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
7232feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		board_cfg = &ch->cfg.board_cfg;
724247f99386100d1d1c369ba98120d2edebf5426fcAlexandre Courbot		if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
7252feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm			board_cfg->display_off(board_cfg->board_data);
7266de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski			module_put(board_cfg->owner);
7276de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski		}
7287caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
7297caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		/* disable the meram */
7307caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		if (ch->meram_enabled) {
7317caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			struct sh_mobile_meram_cfg *cfg;
7327caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			struct sh_mobile_meram_info *mdev;
7337caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			cfg = ch->cfg.meram_cfg;
7347caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			mdev = priv->meram_dev;
7357caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			mdev->ops->meram_unregister(mdev, cfg);
7367caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			ch->meram_enabled = 0;
7377caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		}
7387caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
739cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
740cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
741cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* stop the lcdc */
7428e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm	if (priv->started) {
7438e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm		sh_mobile_lcdc_start_stop(priv, 0);
7448e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm		priv->started = 0;
7458e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm	}
746b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm
7478564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	/* stop clocks */
7488564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
7498564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		if (priv->ch[k].enabled)
7508564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			sh_mobile_lcdc_clk_off(priv);
751cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
752cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
753cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
754cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
755ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	int interface_type = ch->cfg.interface_type;
756ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart
757ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	switch (interface_type) {
758ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB8:
759ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB9:
760ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB12A:
761ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB12B:
762ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB16:
763ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB18:
764ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case RGB24:
765ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS8A:
766ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS8B:
767ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS8C:
768ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS8D:
769ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS9:
770ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS12:
771ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS16A:
772ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS16B:
773ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS16C:
774ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS18:
775ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case SYS24:
776ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		break;
777ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	default:
778ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		return -EINVAL;
779cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
780cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
781cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* SUBLCD only supports SYS interface */
782cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (lcdc_chan_is_sublcd(ch)) {
783ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		if (!(interface_type & LDMT1R_IFM))
784ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			return -EINVAL;
785ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart
786ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		interface_type &= ~LDMT1R_IFM;
787cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
788cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
789ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	ch->ldmt1r_value = interface_type;
790cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
791cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
792cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
793b51339fff240ff179730f8963a758147fd60f3ecMagnus Dammstatic int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
794b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm				       int clock_source,
795cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				       struct sh_mobile_lcdc_priv *priv)
796cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
797cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	char *str;
798cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
799cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	switch (clock_source) {
800ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case LCDC_CLK_BUS:
801ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		str = "bus_clk";
802ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		priv->lddckr = LDDCKR_ICKSEL_BUS;
803ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		break;
804ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case LCDC_CLK_PERIPHERAL:
805ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		str = "peripheral_clk";
806ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		priv->lddckr = LDDCKR_ICKSEL_MIPI;
807ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		break;
808ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart	case LCDC_CLK_EXTERNAL:
809ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		str = NULL;
810ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		priv->lddckr = LDDCKR_ICKSEL_HDMI;
811ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart		break;
812cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	default:
813cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		return -EINVAL;
814cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
815cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
816cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (str) {
817b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm		priv->dot_clk = clk_get(&pdev->dev, str);
818b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm		if (IS_ERR(priv->dot_clk)) {
819b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm			dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
820b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm			return PTR_ERR(priv->dot_clk);
821cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		}
822cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
8230246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
8240246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* Runtime PM support involves two step for this driver:
8250246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	 * 1) Enable Runtime PM
8260246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	 * 2) Force Runtime PM Resume since hardware is accessed from probe()
8270246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	 */
8288bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski	priv->dev = &pdev->dev;
8290246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	pm_runtime_enable(priv->dev);
8300246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	pm_runtime_resume(priv->dev);
831cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
832cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
833cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
834cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_setcolreg(u_int regno,
835cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				    u_int red, u_int green, u_int blue,
836cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm				    u_int transp, struct fb_info *info)
837cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
838cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	u32 *palette = info->pseudo_palette;
839cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
840cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (regno >= PALETTE_NR)
841cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		return -EINVAL;
842cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
843cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	/* only FB_VISUAL_TRUECOLOR supported */
844cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
845cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	red >>= 16 - info->var.red.length;
846cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	green >>= 16 - info->var.green.length;
847cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	blue >>= 16 - info->var.blue.length;
848cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	transp >>= 16 - info->var.transp.length;
849cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
850cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	palette[regno] = (red << info->var.red.offset) |
851cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	  (green << info->var.green.offset) |
852cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	  (blue << info->var.blue.offset) |
853cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	  (transp << info->var.transp.offset);
854cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
855cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
856cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
857cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
858cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct fb_fix_screeninfo sh_mobile_lcdc_fix  = {
859cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.id =		"SH Mobile LCDC",
860cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.type =		FB_TYPE_PACKED_PIXELS,
861cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.visual =	FB_VISUAL_TRUECOLOR,
862cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.accel =	FB_ACCEL_NONE,
8639dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	.xpanstep =	0,
8649dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	.ypanstep =	1,
8659dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	.ywrapstep =	0,
866cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
867cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
8688564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_fillrect(struct fb_info *info,
8698564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm				    const struct fb_fillrect *rect)
8708564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
8718564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sys_fillrect(info, rect);
8728564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sh_mobile_lcdc_deferred_io_touch(info);
8738564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
8748564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
8758564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_copyarea(struct fb_info *info,
8768564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm				    const struct fb_copyarea *area)
8778564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
8788564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sys_copyarea(info, area);
8798564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sh_mobile_lcdc_deferred_io_touch(info);
8808564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
8818564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
8828564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_imageblit(struct fb_info *info,
8838564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm				     const struct fb_image *image)
8848564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{
8858564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sys_imageblit(info, image);
8868564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	sh_mobile_lcdc_deferred_io_touch(info);
8878564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm}
8888564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
8899dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthystatic int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
8909dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy				     struct fb_info *info)
8919dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy{
8929dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	struct sh_mobile_lcdc_chan *ch = info->par;
89392e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	struct sh_mobile_lcdc_priv *priv = ch->lcdc;
89492e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	unsigned long ldrcntr;
89592e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	unsigned long new_pan_offset;
89653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	unsigned long base_addr_y, base_addr_c;
89753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	unsigned long c_offset;
89892e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy
89953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	if (!var->nonstd)
90053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		new_pan_offset = (var->yoffset * info->fix.line_length) +
90153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			(var->xoffset * (info->var.bits_per_pixel / 8));
90253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	else
90353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		new_pan_offset = (var->yoffset * info->fix.line_length) +
90453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			(var->xoffset);
9059dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
90692e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	if (new_pan_offset == ch->pan_offset)
9079dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy		return 0;	/* No change, do nothing */
9089dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
90992e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	ldrcntr = lcdc_read(priv, _LDRCNTR);
9109dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
91192e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	/* Set the source address for the next refresh */
91253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	base_addr_y = ch->dma_handle + new_pan_offset;
91353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	if (var->nonstd) {
91453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		/* Set y offset */
91553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		c_offset = (var->yoffset *
91653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			info->fix.line_length *
91753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			(info->var.bits_per_pixel - 8)) / 8;
91853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		base_addr_c = ch->dma_handle + var->xres * var->yres_virtual +
91953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			c_offset;
92053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		/* Set x offset */
92153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		if (info->var.bits_per_pixel == 24)
92253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			base_addr_c += 2 * var->xoffset;
92353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		else
92453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			base_addr_c += var->xoffset;
92553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	} else
92653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		base_addr_c = 0;
92753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia
9287caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia	if (!ch->meram_enabled) {
9297caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
9307caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		if (base_addr_c)
9317caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
9327caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia	} else {
9337caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		struct sh_mobile_meram_cfg *cfg;
9347caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		struct sh_mobile_meram_info *mdev;
9357caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		unsigned long icb_addr_y, icb_addr_c;
9367caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		int ret;
9377caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
9387caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		cfg = ch->cfg.meram_cfg;
9397caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		mdev = priv->meram_dev;
9407caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		ret = mdev->ops->meram_update(mdev, cfg,
9417caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia					base_addr_y, base_addr_c,
9427caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia					&icb_addr_y, &icb_addr_c);
9437caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		if (ret)
9447caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			return ret;
9457caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
9467caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		lcdc_write_chan_mirror(ch, LDSA1R, icb_addr_y);
9477caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia		if (icb_addr_c)
9487caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia			lcdc_write_chan_mirror(ch, LDSA2R, icb_addr_c);
9497caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
9507caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia	}
95153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia
95292e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	if (lcdc_chan_is_sublcd(ch))
95392e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy		lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS);
95492e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	else
95592e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy		lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS);
95692e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy
95792e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	ch->pan_offset = new_pan_offset;
95892e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy
95992e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy	sh_mobile_lcdc_deferred_io_touch(info);
9609dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
9619dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	return 0;
9629dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy}
9639dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy
96440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthystatic int sh_mobile_wait_for_vsync(struct fb_info *info)
96540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy{
96640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	struct sh_mobile_lcdc_chan *ch = info->par;
96740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	unsigned long ldintr;
96840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	int ret;
96940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
970dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	/* Enable VSync End interrupt and be careful not to acknowledge any
971dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	 * pending interrupt.
972dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	 */
97340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	ldintr = lcdc_read(ch->lcdc, _LDINTR);
974dc48665fae5aa360e80dfdb2d6cab4fa58b27ee4Laurent Pinchart	ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
97540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	lcdc_write(ch->lcdc, _LDINTR, ldintr);
97640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
97740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
97840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy							msecs_to_jiffies(100));
97940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	if (!ret)
98040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		return -ETIMEDOUT;
98140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
98240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	return 0;
98340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy}
98440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
98540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthystatic int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
98640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		       unsigned long arg)
98740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy{
98840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	int retval;
98940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
99040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	switch (cmd) {
99140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	case FBIO_WAITFORVSYNC:
99240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		retval = sh_mobile_wait_for_vsync(info);
99340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		break;
99440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
99540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	default:
99640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		retval = -ENOIOCTLCMD;
99740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy		break;
99840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	}
99940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	return retval;
100040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy}
100140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
1002dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic void sh_mobile_fb_reconfig(struct fb_info *info)
1003dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{
1004dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct sh_mobile_lcdc_chan *ch = info->par;
1005dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct fb_videomode mode1, mode2;
1006dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct fb_event event;
1007dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	int evnt = FB_EVENT_MODE_CHANGE_ALL;
1008dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1009dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par))
1010dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		/* More framebuffer users are active */
1011dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		return;
1012dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1013dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	fb_var_to_videomode(&mode1, &ch->display_var);
1014dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	fb_var_to_videomode(&mode2, &info->var);
1015dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1016dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	if (fb_mode_is_equal(&mode1, &mode2))
1017dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		return;
1018dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1019dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	/* Display has been re-plugged, framebuffer is free now, reconfigure */
1020dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	if (fb_set_var(info, &ch->display_var) < 0)
1021dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		/* Couldn't reconfigure, hopefully, can continue as before */
1022dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		return;
1023dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
102453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	if (info->var.nonstd)
102553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		info->fix.line_length = mode1.xres;
102653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	else
102753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8);
1028dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1029dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	/*
1030dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	 * fb_set_var() calls the notifier change internally, only if
1031dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	 * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a
1032dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	 * user event, we have to call the chain ourselves.
1033dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	 */
1034dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	event.info = info;
1035cc267ec5dfa29eba34cbf4eae3e5db9ca499c179Arnd Hannemann	event.data = &mode1;
1036dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	fb_notifier_call_chain(evnt, &event);
1037dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski}
1038dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1039dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski/*
1040dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * Locking: both .fb_release() and .fb_open() are called with info->lock held if
1041dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * user == 1, or with console sem held, if user == 0.
1042dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski */
1043dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_release(struct fb_info *info, int user)
1044dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{
1045dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct sh_mobile_lcdc_chan *ch = info->par;
1046dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1047dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	mutex_lock(&ch->open_lock);
1048dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
1049dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1050dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	ch->use_count--;
1051dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1052dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	/* Nothing to reconfigure, when called from fbcon */
1053dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	if (user) {
1054ac751efa6a0d70f2c9daef5c7e3a92270f5c2dffTorben Hohn		console_lock();
1055dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		sh_mobile_fb_reconfig(info);
1056ac751efa6a0d70f2c9daef5c7e3a92270f5c2dffTorben Hohn		console_unlock();
1057dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	}
1058dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1059dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	mutex_unlock(&ch->open_lock);
1060dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1061dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	return 0;
1062dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski}
1063dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1064dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_open(struct fb_info *info, int user)
1065dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{
1066dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct sh_mobile_lcdc_chan *ch = info->par;
1067dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1068dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	mutex_lock(&ch->open_lock);
1069dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	ch->use_count++;
1070dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1071dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count);
1072dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	mutex_unlock(&ch->open_lock);
1073dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1074dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	return 0;
1075dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski}
1076dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1077dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1078dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{
1079dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	struct sh_mobile_lcdc_chan *ch = info->par;
1080417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	struct sh_mobile_lcdc_priv *p = ch->lcdc;
1081dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1082d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski	if (var->xres > MAX_XRES || var->yres > MAX_YRES ||
1083dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	    var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) {
1084830539d14379d0f5cb07832a3e4466418011f843Paul Mundt		dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n",
1085d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski			 var->left_margin, var->xres, var->right_margin, var->hsync_len,
1086d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski			 var->upper_margin, var->yres, var->lower_margin, var->vsync_len,
1087d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski			 PICOS2KHZ(var->pixclock));
1088dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		return -EINVAL;
1089dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	}
1090417d48274e755e537bae60461558c1f63a4e14deMagnus Damm
1091417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	/* only accept the forced_bpp for dual channel configurations */
1092417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel)
1093417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		return -EINVAL;
1094417d48274e755e537bae60461558c1f63a4e14deMagnus Damm
1095417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	switch (var->bits_per_pixel) {
1096417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	case 16: /* PKF[4:0] = 00011 - RGB 565 */
1097417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	case 24: /* PKF[4:0] = 01011 - RGB 888 */
1098417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	case 32: /* PKF[4:0] = 00000 - RGBA 888 */
1099417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		break;
1100417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	default:
1101417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		return -EINVAL;
1102417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	}
1103417d48274e755e537bae60461558c1f63a4e14deMagnus Damm
1104dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	return 0;
1105dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski}
110640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy
11078857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot/*
11088857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * Screen blanking. Behavior is as follows:
11098857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * FB_BLANK_UNBLANK: screen unblanked, clocks enabled
11108857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * FB_BLANK_NORMAL: screen blanked, clocks enabled
11118857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * FB_BLANK_VSYNC,
11128857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * FB_BLANK_HSYNC,
11138857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot * FB_BLANK_POWEROFF: screen blanked, clocks disabled
11148857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot */
11158857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbotstatic int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
11168857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot{
11178857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	struct sh_mobile_lcdc_chan *ch = info->par;
11188857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	struct sh_mobile_lcdc_priv *p = ch->lcdc;
11198857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot
11208857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	/* blank the screen? */
11218857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) {
11228857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		struct fb_fillrect rect = {
11238857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot			.width = info->var.xres,
11248857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot			.height = info->var.yres,
11258857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		};
11268857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		sh_mobile_lcdc_fillrect(info, &rect);
11278857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	}
11288857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	/* turn clocks on? */
11298857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	if (blank <= FB_BLANK_NORMAL && ch->blank_status > FB_BLANK_NORMAL) {
11308857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		sh_mobile_lcdc_clk_on(p);
11318857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	}
11328857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	/* turn clocks off? */
11338857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	if (blank > FB_BLANK_NORMAL && ch->blank_status <= FB_BLANK_NORMAL) {
11348857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		/* make sure the screen is updated with the black fill before
11358857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		 * switching the clocks off. one vsync is not enough since
11368857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		 * blanking may occur in the middle of a refresh. deferred io
11378857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		 * mode will reenable the clocks and update the screen in time,
11388857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		 * so it does not need this. */
11398857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		if (!info->fbdefio) {
11408857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot			sh_mobile_wait_for_vsync(info);
11418857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot			sh_mobile_wait_for_vsync(info);
11428857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		}
11438857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot		sh_mobile_lcdc_clk_off(p);
11448857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	}
11458857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot
11468857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	ch->blank_status = blank;
11478857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	return 0;
11488857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot}
11498857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot
1150cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct fb_ops sh_mobile_lcdc_ops = {
11519dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	.owner          = THIS_MODULE,
1152cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.fb_setcolreg	= sh_mobile_lcdc_setcolreg,
11532540c111ead82cad605ec2b14a1905ad914cc124Magnus Damm	.fb_read        = fb_sys_read,
11542540c111ead82cad605ec2b14a1905ad914cc124Magnus Damm	.fb_write       = fb_sys_write,
11558564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	.fb_fillrect	= sh_mobile_lcdc_fillrect,
11568564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	.fb_copyarea	= sh_mobile_lcdc_copyarea,
11578564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	.fb_imageblit	= sh_mobile_lcdc_imageblit,
11588857b9aa7e64a70852545ee01fa638481cb08a76Alexandre Courbot	.fb_blank	= sh_mobile_lcdc_blank,
11599dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy	.fb_pan_display = sh_mobile_fb_pan_display,
116040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy	.fb_ioctl       = sh_mobile_ioctl,
1161dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	.fb_open	= sh_mobile_open,
1162dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	.fb_release	= sh_mobile_release,
1163dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski	.fb_check_var	= sh_mobile_check_var,
1164cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
1165cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
11663b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
11673b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot{
11683b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
11693b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
11703b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	int brightness = bdev->props.brightness;
11713b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11723b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	if (bdev->props.power != FB_BLANK_UNBLANK ||
11733b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	    bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
11743b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		brightness = 0;
11753b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11763b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	return cfg->set_brightness(cfg->board_data, brightness);
11773b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot}
11783b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11793b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
11803b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot{
11813b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
11823b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
11833b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11843b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	return cfg->get_brightness(cfg->board_data);
11853b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot}
11863b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11873b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
11883b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot				   struct fb_info *info)
11893b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot{
11903b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	return (info->bl_dev == bdev);
11913b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot}
11923b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
11933b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic struct backlight_ops sh_mobile_lcdc_bl_ops = {
11943b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	.options	= BL_CORE_SUSPENDRESUME,
11953b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	.update_status	= sh_mobile_lcdc_update_bl,
11963b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	.get_brightness	= sh_mobile_lcdc_get_brightness,
11973b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	.check_fb	= sh_mobile_lcdc_check_fb,
11983b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot};
11993b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
12003b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
12013b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot					       struct sh_mobile_lcdc_chan *ch)
12023b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot{
12033b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	struct backlight_device *bl;
12043b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
12053b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
12063b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot				       &sh_mobile_lcdc_bl_ops, NULL);
1207beee1f20a185c7e79fd33bb83e04fe44ecd75af3Dan Carpenter	if (IS_ERR(bl)) {
1208beee1f20a185c7e79fd33bb83e04fe44ecd75af3Dan Carpenter		dev_err(parent, "unable to register backlight device: %ld\n",
1209beee1f20a185c7e79fd33bb83e04fe44ecd75af3Dan Carpenter			PTR_ERR(bl));
12103b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		return NULL;
12113b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	}
12123b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
12133b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	bl->props.max_brightness = ch->cfg.bl_info.max_brightness;
12143b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	bl->props.brightness = bl->props.max_brightness;
12153b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	backlight_update_status(bl);
12163b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
12173b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	return bl;
12183b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot}
12193b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
12203b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbotstatic void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
12213b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot{
12223b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	backlight_device_unregister(bdev);
12233b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot}
12243b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
122553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garciastatic int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp,
122653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				   int nonstd)
1227cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
122853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	if (nonstd) {
122953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		switch (bpp) {
123053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 12:
123153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 16:
123253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		case 24:
123353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			var->bits_per_pixel = bpp;
123453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			var->nonstd = nonstd;
123553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			return 0;
123653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		default:
123753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			return -EINVAL;
123853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		}
123953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia	}
124053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia
1241cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	switch (bpp) {
1242cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	case 16: /* PKF[4:0] = 00011 - RGB 565 */
1243cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->red.offset = 11;
1244cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->red.length = 5;
1245cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->green.offset = 5;
1246cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->green.length = 6;
1247cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->blue.offset = 0;
1248cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->blue.length = 5;
1249cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->transp.offset = 0;
1250cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->transp.length = 0;
1251cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		break;
1252cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1253417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	case 24: /* PKF[4:0] = 01011 - RGB 888 */
1254417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->red.offset = 16;
1255cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->red.length = 8;
1256417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->green.offset = 8;
1257cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->green.length = 8;
1258417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->blue.offset = 0;
1259cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->blue.length = 8;
1260cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->transp.offset = 0;
1261cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		var->transp.length = 0;
1262cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		break;
1263417d48274e755e537bae60461558c1f63a4e14deMagnus Damm
1264417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	case 32: /* PKF[4:0] = 00000 - RGBA 888 */
1265417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->red.offset = 16;
1266417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->red.length = 8;
1267417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->green.offset = 8;
1268417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->green.length = 8;
1269417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->blue.offset = 0;
1270417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->blue.length = 8;
1271417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->transp.offset = 24;
1272417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		var->transp.length = 8;
1273417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		break;
1274cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	default:
1275cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		return -EINVAL;
1276cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1277cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	var->bits_per_pixel = bpp;
1278cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	var->red.msb_right = 0;
1279cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	var->green.msb_right = 0;
1280cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	var->blue.msb_right = 0;
1281cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	var->transp.msb_right = 0;
1282cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
1283cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
1284cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
12852feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Dammstatic int sh_mobile_lcdc_suspend(struct device *dev)
12862feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm{
12872feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	struct platform_device *pdev = to_platform_device(dev);
12882feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
12892feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	sh_mobile_lcdc_stop(platform_get_drvdata(pdev));
12902feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	return 0;
12912feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm}
12922feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
12932feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Dammstatic int sh_mobile_lcdc_resume(struct device *dev)
12942feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm{
12952feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	struct platform_device *pdev = to_platform_device(dev);
12962feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
12972feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	return sh_mobile_lcdc_start(platform_get_drvdata(pdev));
12982feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm}
12992feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
13000246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int sh_mobile_lcdc_runtime_suspend(struct device *dev)
13010246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm{
13020246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct platform_device *pdev = to_platform_device(dev);
13030246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
13040246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct sh_mobile_lcdc_chan *ch;
13050246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	int k, n;
13060246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13070246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* save per-channel registers */
13080246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
13090246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		ch = &p->ch[k];
13100246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		if (!ch->enabled)
13110246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm			continue;
13120246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		for (n = 0; n < NR_CH_REGS; n++)
13130246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm			ch->saved_ch_regs[n] = lcdc_read_chan(ch, n);
13140246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	}
13150246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13160246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* save shared registers */
13170246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	for (n = 0; n < NR_SHARED_REGS; n++)
13180246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]);
13190246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13200246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* turn off LCDC hardware */
13210246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	lcdc_write(p, _LDCNT1R, 0);
13220246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	return 0;
13230246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm}
13240246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13250246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int sh_mobile_lcdc_runtime_resume(struct device *dev)
13260246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm{
13270246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct platform_device *pdev = to_platform_device(dev);
13280246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev);
13290246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	struct sh_mobile_lcdc_chan *ch;
13300246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	int k, n;
13310246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13320246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* restore per-channel registers */
13330246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	for (k = 0; k < ARRAY_SIZE(p->ch); k++) {
13340246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		ch = &p->ch[k];
13350246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		if (!ch->enabled)
13360246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm			continue;
13370246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		for (n = 0; n < NR_CH_REGS; n++)
13380246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm			lcdc_write_chan(ch, n, ch->saved_ch_regs[n]);
13390246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	}
13400246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13410246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	/* restore shared registers */
13420246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	for (n = 0; n < NR_SHARED_REGS; n++)
13430246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm		lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]);
13440246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
13450246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	return 0;
13460246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm}
13470246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
1348471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
13492feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	.suspend = sh_mobile_lcdc_suspend,
13502feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm	.resume = sh_mobile_lcdc_resume,
13510246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	.runtime_suspend = sh_mobile_lcdc_runtime_suspend,
13520246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm	.runtime_resume = sh_mobile_lcdc_runtime_resume,
13532feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm};
13542feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm
13556de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski/* locking: called with info->lock held */
13566011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetskistatic int sh_mobile_lcdc_notify(struct notifier_block *nb,
13576011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski				 unsigned long action, void *data)
13586011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski{
13596011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	struct fb_event *event = data;
13606011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	struct fb_info *info = event->info;
13616011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	struct sh_mobile_lcdc_chan *ch = info->par;
13626011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	struct sh_mobile_lcdc_board_cfg	*board_cfg = &ch->cfg.board_cfg;
13636011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
13646011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	if (&ch->lcdc->notifier != nb)
1365baf163749952ca5e33dd2d6a74da023e385c3a00Guennadi Liakhovetski		return NOTIFY_DONE;
13666011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
13676011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	dev_dbg(info->dev, "%s(): action = %lu, data = %p\n",
13686011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		__func__, action, event->data);
13696011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
13706011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	switch(action) {
13716011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	case FB_EVENT_SUSPEND:
1372247f99386100d1d1c369ba98120d2edebf5426fcAlexandre Courbot		if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
13736011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski			board_cfg->display_off(board_cfg->board_data);
13746de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski			module_put(board_cfg->owner);
13756de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski		}
1376afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski		sh_mobile_lcdc_stop(ch->lcdc);
13776011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		break;
13786011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	case FB_EVENT_RESUME:
1379dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		mutex_lock(&ch->open_lock);
1380dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		sh_mobile_fb_reconfig(info);
1381dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		mutex_unlock(&ch->open_lock);
13826011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
13836011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		/* HDMI must be enabled before LCDC configuration */
1384247f99386100d1d1c369ba98120d2edebf5426fcAlexandre Courbot		if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
1385dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski			board_cfg->display_on(board_cfg->board_data, info);
13866de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski			module_put(board_cfg->owner);
13876011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		}
13886011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
1389ebe5e12d00f4785092a9650845ad3451bbf4b311Guennadi Liakhovetski		sh_mobile_lcdc_start(ch->lcdc);
13906011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	}
13916011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
1392baf163749952ca5e33dd2d6a74da023e385c3a00Guennadi Liakhovetski	return NOTIFY_OK;
13936011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski}
13946011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
1395cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_remove(struct platform_device *pdev);
1396cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1397c2e13037e6794bd0d9de3f9ecabf5615f15c160bUwe Kleine-Königstatic int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
1398cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
1399cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct fb_info *info;
1400cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_priv *priv;
140101ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski	struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data;
1402cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct resource *res;
1403cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	int error;
1404cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	void *buf;
1405cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	int i, j;
1406cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
140701ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski	if (!pdata) {
1408cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_err(&pdev->dev, "no platform data defined\n");
14098bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski		return -EINVAL;
1410cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1411cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1412cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
14138564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	i = platform_get_irq(pdev, 0);
14148564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	if (!res || i < 0) {
14158564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		dev_err(&pdev->dev, "cannot get platform resources\n");
14168bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski		return -ENOENT;
1417cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1418cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1419cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
1420cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (!priv) {
1421cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_err(&pdev->dev, "cannot allocate device data\n");
14228bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski		return -ENOMEM;
1423cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1424cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
14258bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski	platform_set_drvdata(pdev, priv);
14268bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski
14278564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
14287ad33e74857f16f1202cbc5746faf52e88e8b376Kay Sievers			    dev_name(&pdev->dev), priv);
14298564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	if (error) {
14308564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		dev_err(&pdev->dev, "unable to request irq\n");
14318564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		goto err1;
14328564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	}
14338564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
14348564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	priv->irq = i;
14355ef6b505d9df45558402bdb823a078840a6a26c4Guennadi Liakhovetski	atomic_set(&priv->hw_usecnt, -1);
1436cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1437cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	j = 0;
1438cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
143901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		struct sh_mobile_lcdc_chan *ch = priv->ch + j;
1440cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
144101ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		ch->lcdc = priv;
144201ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
1443cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
144401ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		error = sh_mobile_lcdc_check_interface(ch);
1445cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (error) {
1446cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			dev_err(&pdev->dev, "unsupported interface type\n");
1447cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			goto err1;
1448cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		}
144901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		init_waitqueue_head(&ch->frame_end_wait);
145001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		init_completion(&ch->vsync_completion);
145101ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		ch->pan_offset = 0;
1452cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
14533b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		/* probe the backlight is there is one defined */
14543b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		if (ch->cfg.bl_info.max_brightness)
14553b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
14563b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
1457cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		switch (pdata->ch[i].chan) {
1458cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		case LCDC_CHAN_MAINLCD:
1459ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			ch->enabled = LDCNT2R_ME;
146001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski			ch->reg_offs = lcdc_offs_mainlcd;
1461cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			j++;
1462cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			break;
1463cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		case LCDC_CHAN_SUBLCD:
1464ce1c0b0873bf4e970970a49612105cf6c36d81e3Laurent Pinchart			ch->enabled = LDCNT2R_SE;
146501ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski			ch->reg_offs = lcdc_offs_sublcd;
1466cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			j++;
1467cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			break;
1468cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		}
1469cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1470cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1471cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (!j) {
1472cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_err(&pdev->dev, "no channels defined\n");
1473cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		error = -EINVAL;
1474cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		goto err1;
1475cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1476cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1477417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	/* for dual channel LCDC (MAIN + SUB) force shared bpp setting */
1478417d48274e755e537bae60461558c1f63a4e14deMagnus Damm	if (j == 2)
1479417d48274e755e537bae60461558c1f63a4e14deMagnus Damm		priv->forced_bpp = pdata->ch[0].bpp;
1480417d48274e755e537bae60461558c1f63a4e14deMagnus Damm
1481dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski	priv->base = ioremap_nocache(res->start, resource_size(res));
1482dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski	if (!priv->base)
1483dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski		goto err1;
1484dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski
1485b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm	error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
1486cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (error) {
1487cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_err(&pdev->dev, "unable to setup clocks\n");
1488cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		goto err1;
1489cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1490cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
14917caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia	priv->meram_dev = pdata->meram_dev;
14927caa4342ca5b37d2d178b464c16badd4228b3b7bDamian Hobson-Garcia
1493cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (i = 0; i < j; i++) {
14946011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		struct fb_var_screeninfo *var;
149571d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski		const struct fb_videomode *lcd_cfg, *max_cfg = NULL;
149601ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		struct sh_mobile_lcdc_chan *ch = priv->ch + i;
1497c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
1498c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		const struct fb_videomode *mode = cfg->lcd_cfg;
149971d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski		unsigned long max_size = 0;
150071d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski		int k;
15015fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski		int num_cfg;
1502cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
150301ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		ch->info = framebuffer_alloc(0, &pdev->dev);
150401ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		if (!ch->info) {
1505e33afddca174171a68d57476ead8947476ab9240Paul Mundt			dev_err(&pdev->dev, "unable to allocate fb_info\n");
1506e33afddca174171a68d57476ead8947476ab9240Paul Mundt			error = -ENOMEM;
1507e33afddca174171a68d57476ead8947476ab9240Paul Mundt			break;
1508e33afddca174171a68d57476ead8947476ab9240Paul Mundt		}
1509e33afddca174171a68d57476ead8947476ab9240Paul Mundt
151001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		info = ch->info;
15116011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		var = &info->var;
1512cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		info->fbops = &sh_mobile_lcdc_ops;
1513c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		info->par = ch;
1514dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1515dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski		mutex_init(&ch->open_lock);
1516dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski
1517c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		for (k = 0, lcd_cfg = mode;
1518c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		     k < cfg->num_cfg && lcd_cfg;
151971d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski		     k++, lcd_cfg++) {
152071d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski			unsigned long size = lcd_cfg->yres * lcd_cfg->xres;
152153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			/* NV12 buffers must have even number of lines */
152253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			if ((cfg->nonstd) && cfg->bpp == 12 &&
152353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia					(lcd_cfg->yres & 0x1)) {
152453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				dev_err(&pdev->dev, "yres must be multiple of 2"
152553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia						" for YCbCr420 mode.\n");
152653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				error = -EINVAL;
152753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia				goto err1;
152853b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			}
152971d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski
153071d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski			if (size > max_size) {
153171d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski				max_cfg = lcd_cfg;
153271d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski				max_size = size;
153371d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski			}
153471d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski		}
153571d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski
1536c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		if (!mode)
1537d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski			max_size = MAX_XRES * MAX_YRES;
1538c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		else if (max_cfg)
1539c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski			dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n",
1540c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski				max_cfg->xres, max_cfg->yres);
154171d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski
1542cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		info->fix = sh_mobile_lcdc_fix;
154353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		info->fix.smem_len = max_size * 2 * cfg->bpp / 8;
154453b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia
154553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		 /* Only pan in 2 line steps for NV12 */
154653b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		if (cfg->nonstd && cfg->bpp == 12)
154753b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			info->fix.ypanstep = 2;
1548cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
15495fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski		if (!mode) {
1550c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski			mode = &default_720p;
15515fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski			num_cfg = 1;
15525fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski		} else {
1553e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski			num_cfg = cfg->num_cfg;
15545fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski		}
15555fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski
15565fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski		fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
1557c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski
1558c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski		fb_videomode_to_var(var, mode);
1559e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski		var->width = cfg->lcd_size_cfg.width;
1560e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski		var->height = cfg->lcd_size_cfg.height;
15619dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy		/* Default Y virtual resolution is 2x panel size */
15626011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		var->yres_virtual = var->yres * 2;
15636011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		var->activate = FB_ACTIVATE_NOW;
15646011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
156553b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		error = sh_mobile_lcdc_set_bpp(var, cfg->bpp, cfg->nonstd);
1566cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (error)
1567cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			break;
1568cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1569cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
157001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski					 &ch->dma_handle, GFP_KERNEL);
1571cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (!buf) {
1572cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			dev_err(&pdev->dev, "unable to allocate buffer\n");
1573cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			error = -ENOMEM;
1574cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			break;
1575cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		}
1576cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
157701ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		info->pseudo_palette = &ch->pseudo_palette;
1578cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		info->flags = FBINFO_FLAG_DEFAULT;
1579cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1580cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
1581cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (error < 0) {
1582cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			dev_err(&pdev->dev, "unable to allocate cmap\n");
1583cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			dma_free_coherent(&pdev->dev, info->fix.smem_len,
158401ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski					  buf, ch->dma_handle);
1585cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			break;
1586cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		}
1587cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
158801ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski		info->fix.smem_start = ch->dma_handle;
158953b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		if (var->nonstd)
159053b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			info->fix.line_length = var->xres;
159153b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia		else
159253b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia			info->fix.line_length = var->xres * (cfg->bpp / 8);
159353b5031430bb3a7941b340b453afe4eabeb5340cDamian Hobson-Garcia
1594cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		info->screen_base = buf;
1595cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		info->device = &pdev->dev;
15961c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski		ch->display_var = *var;
1597cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1598cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1599cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (error)
1600cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		goto err1;
1601cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1602cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	error = sh_mobile_lcdc_start(priv);
1603cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (error) {
1604cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_err(&pdev->dev, "unable to start hardware\n");
1605cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		goto err1;
1606cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1607cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1608cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (i = 0; i < j; i++) {
16091c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		struct sh_mobile_lcdc_chan *ch = priv->ch + i;
16101c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
1611e33afddca174171a68d57476ead8947476ab9240Paul Mundt		info = ch->info;
16121c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
16131c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		if (info->fbdefio) {
16148bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski			ch->sglist = vmalloc(sizeof(struct scatterlist) *
16151c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt					info->fix.smem_len >> PAGE_SHIFT);
16168bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski			if (!ch->sglist) {
16171c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt				dev_err(&pdev->dev, "cannot allocate sglist\n");
16181c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt				goto err1;
16191c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt			}
16201c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		}
16211c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
16223b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		info->bl_dev = ch->bl;
16233b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
16241c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		error = register_framebuffer(info);
1625cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		if (error < 0)
1626cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			goto err1;
1627cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1628cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		dev_info(info->dev,
1629cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			 "registered %s/%s as %dx%d %dbpp.\n",
1630cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			 pdev->name,
16311c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt			 (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
1632cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			 "mainlcd" : "sublcd",
1633c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski			 info->var.xres, info->var.yres,
16341c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt			 ch->cfg.bpp);
16358564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm
16368564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		/* deferred io mode: disable clock to save power */
16376011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski		if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
16388564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm			sh_mobile_lcdc_clk_off(priv);
1639cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1640cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
16416011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	/* Failure ignored */
16426011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	priv->notifier.notifier_call = sh_mobile_lcdc_notify;
16436011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	fb_register_client(&priv->notifier);
16446011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
1645cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
16468bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetskierr1:
1647cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	sh_mobile_lcdc_remove(pdev);
16488bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski
1649cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return error;
1650cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
1651cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1652cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_remove(struct platform_device *pdev)
1653cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
1654cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
1655cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	struct fb_info *info;
1656cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	int i;
1657cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
16586011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski	fb_unregister_client(&priv->notifier);
16596011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski
1660cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
16618bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski		if (priv->ch[i].info && priv->ch[i].info->dev)
1662e33afddca174171a68d57476ead8947476ab9240Paul Mundt			unregister_framebuffer(priv->ch[i].info);
1663cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1664cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	sh_mobile_lcdc_stop(priv);
1665cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1666cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
1667e33afddca174171a68d57476ead8947476ab9240Paul Mundt		info = priv->ch[i].info;
1668cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1669e33afddca174171a68d57476ead8947476ab9240Paul Mundt		if (!info || !info->device)
1670cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm			continue;
1671cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
16721c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt		if (priv->ch[i].sglist)
16731c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt			vfree(priv->ch[i].sglist);
16741c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt
16751ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm		if (info->screen_base)
16761ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm			dma_free_coherent(&pdev->dev, info->fix.smem_len,
16771ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm					  info->screen_base,
16781ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm					  priv->ch[i].dma_handle);
1679cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		fb_dealloc_cmap(&info->cmap);
1680e33afddca174171a68d57476ead8947476ab9240Paul Mundt		framebuffer_release(info);
1681cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	}
1682cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
16833b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
16843b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot		if (priv->ch[i].bl)
16853b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot			sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
16863b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot	}
16873b0fd9d75598584478d1d3f6551f8a8a9696c34eAlexandre Courbot
1688b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm	if (priv->dot_clk)
1689b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm		clk_put(priv->dot_clk);
16900246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm
16918bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski	if (priv->dev)
16928bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski		pm_runtime_disable(priv->dev);
1693cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1694cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	if (priv->base)
1695cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		iounmap(priv->base);
1696cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
16978564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm	if (priv->irq)
16988564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm		free_irq(priv->irq, priv);
1699cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	kfree(priv);
1700cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return 0;
1701cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
1702cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1703cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct platform_driver sh_mobile_lcdc_driver = {
1704cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.driver		= {
1705cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		.name		= "sh_mobile_lcdc_fb",
1706cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm		.owner		= THIS_MODULE,
17072feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm		.pm		= &sh_mobile_lcdc_dev_pm_ops,
1708cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	},
1709cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.probe		= sh_mobile_lcdc_probe,
1710cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	.remove		= sh_mobile_lcdc_remove,
1711cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm};
1712cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1713cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int __init sh_mobile_lcdc_init(void)
1714cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
1715cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	return platform_driver_register(&sh_mobile_lcdc_driver);
1716cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
1717cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1718cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void __exit sh_mobile_lcdc_exit(void)
1719cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{
1720cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm	platform_driver_unregister(&sh_mobile_lcdc_driver);
1721cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}
1722cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1723cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammmodule_init(sh_mobile_lcdc_init);
1724cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammmodule_exit(sh_mobile_lcdc_exit);
1725cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm
1726cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver");
1727cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
1728cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_LICENSE("GPL v2");
1729