fbdev: sh_mobile_lcdc: Split LCDC start code from sh_mobile_lcdc_start
Splitting the LCDC start code from clock, MERAM and panel management will make the code usable by runtime PM. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
parent
505c7de51f
commit
9a217e3444
|
@ -435,48 +435,42 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
|
||||||
lcdc_write_chan(ch, LDHAJR, tmp);
|
lcdc_write_chan(ch, LDHAJR, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
|
/*
|
||||||
|
* __sh_mobile_lcdc_start - Configure and tart the LCDC
|
||||||
|
* @priv: LCDC device
|
||||||
|
*
|
||||||
|
* Configure all enabled channels and start the LCDC device. All external
|
||||||
|
* devices (clocks, MERAM, panels, ...) are not touched by this function.
|
||||||
|
*/
|
||||||
|
static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
|
||||||
{
|
{
|
||||||
struct sh_mobile_lcdc_chan *ch;
|
struct sh_mobile_lcdc_chan *ch;
|
||||||
struct sh_mobile_lcdc_board_cfg *board_cfg;
|
|
||||||
unsigned long tmp;
|
unsigned long tmp;
|
||||||
int bpp = 0;
|
int bpp = 0;
|
||||||
unsigned long ldddsr;
|
int k, m;
|
||||||
int k, m, ret;
|
|
||||||
|
|
||||||
/* enable clocks before accessing the hardware */
|
/* Enable LCDC channels. Read data from external memory, avoid using the
|
||||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
* BEU for now.
|
||||||
if (priv->ch[k].enabled) {
|
*/
|
||||||
sh_mobile_lcdc_clk_on(priv);
|
lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled);
|
||||||
if (!bpp)
|
|
||||||
bpp = priv->ch[k].info->var.bits_per_pixel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reset */
|
/* Stop the LCDC first and disable all interrupts. */
|
||||||
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR);
|
|
||||||
lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
|
|
||||||
|
|
||||||
/* enable LCDC channels */
|
|
||||||
tmp = lcdc_read(priv, _LDCNT2R);
|
|
||||||
tmp |= priv->ch[0].enabled;
|
|
||||||
tmp |= priv->ch[1].enabled;
|
|
||||||
lcdc_write(priv, _LDCNT2R, tmp);
|
|
||||||
|
|
||||||
/* read data from external memory, avoid using the BEU for now */
|
|
||||||
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~LDCNT2R_MD);
|
|
||||||
|
|
||||||
/* stop the lcdc first */
|
|
||||||
sh_mobile_lcdc_start_stop(priv, 0);
|
sh_mobile_lcdc_start_stop(priv, 0);
|
||||||
|
lcdc_write(priv, _LDINTR, 0);
|
||||||
|
|
||||||
/* configure clocks */
|
/* Configure power supply, dot clocks and start them. */
|
||||||
tmp = priv->lddckr;
|
tmp = priv->lddckr;
|
||||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
ch = &priv->ch[k];
|
ch = &priv->ch[k];
|
||||||
|
if (!ch->enabled)
|
||||||
if (!priv->ch[k].enabled)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!bpp)
|
||||||
|
bpp = ch->info->var.bits_per_pixel;
|
||||||
|
|
||||||
|
/* Power supply */
|
||||||
|
lcdc_write_chan(ch, LDPMR, 0);
|
||||||
|
|
||||||
m = ch->cfg.clock_divider;
|
m = ch->cfg.clock_divider;
|
||||||
if (!m)
|
if (!m)
|
||||||
continue;
|
continue;
|
||||||
|
@ -493,187 +487,186 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
|
||||||
}
|
}
|
||||||
|
|
||||||
lcdc_write(priv, _LDDCKR, tmp);
|
lcdc_write(priv, _LDDCKR, tmp);
|
||||||
|
|
||||||
/* start dotclock again */
|
|
||||||
lcdc_write(priv, _LDDCKSTPR, 0);
|
lcdc_write(priv, _LDDCKSTPR, 0);
|
||||||
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
|
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
|
||||||
|
|
||||||
/* interrupts are disabled to begin with */
|
/* Setup geometry, format, frame buffer memory and operation mode. */
|
||||||
lcdc_write(priv, _LDINTR, 0);
|
|
||||||
|
|
||||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
ch = &priv->ch[k];
|
ch = &priv->ch[k];
|
||||||
|
|
||||||
if (!ch->enabled)
|
if (!ch->enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
sh_mobile_lcdc_geometry(ch);
|
sh_mobile_lcdc_geometry(ch);
|
||||||
|
|
||||||
/* power supply */
|
|
||||||
lcdc_write_chan(ch, LDPMR, 0);
|
|
||||||
|
|
||||||
board_cfg = &ch->cfg.board_cfg;
|
|
||||||
if (board_cfg->setup_sys) {
|
|
||||||
ret = board_cfg->setup_sys(board_cfg->board_data,
|
|
||||||
ch, &sh_mobile_lcdc_sys_bus_ops);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* word and long word swap */
|
|
||||||
ldddsr = lcdc_read(priv, _LDDDSR);
|
|
||||||
if (priv->ch[0].info->var.nonstd)
|
|
||||||
ldddsr |= LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
|
||||||
else {
|
|
||||||
switch (bpp) {
|
|
||||||
case 16:
|
|
||||||
ldddsr |= LDDDSR_LS | LDDDSR_WS;
|
|
||||||
break;
|
|
||||||
case 24:
|
|
||||||
ldddsr |= LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
ldddsr |= LDDDSR_LS;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lcdc_write(priv, _LDDDSR, ldddsr);
|
|
||||||
|
|
||||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
|
||||||
unsigned long base_addr_y;
|
|
||||||
unsigned long base_addr_c = 0;
|
|
||||||
int pitch;
|
|
||||||
ch = &priv->ch[k];
|
|
||||||
|
|
||||||
if (!priv->ch[k].enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* set bpp format in PKF[4:0] */
|
|
||||||
tmp = lcdc_read_chan(ch, LDDFR);
|
|
||||||
tmp &= ~(LDDFR_CF0 | LDDFR_CC | LDDFR_YF_MASK | LDDFR_PKF_MASK);
|
|
||||||
if (ch->info->var.nonstd) {
|
if (ch->info->var.nonstd) {
|
||||||
tmp |= (ch->info->var.nonstd << 16);
|
tmp = (ch->info->var.nonstd << 16);
|
||||||
switch (ch->info->var.bits_per_pixel) {
|
switch (ch->info->var.bits_per_pixel) {
|
||||||
case 12:
|
case 12:
|
||||||
|
tmp |= LDDFR_YF_420;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
tmp |= LDDFR_YF_422;
|
tmp |= LDDFR_YF_422;
|
||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
|
default:
|
||||||
tmp |= LDDFR_YF_444;
|
tmp |= LDDFR_YF_444;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (ch->info->var.bits_per_pixel) {
|
switch (ch->info->var.bits_per_pixel) {
|
||||||
case 16:
|
case 16:
|
||||||
tmp |= LDDFR_PKF_RGB16;
|
tmp = LDDFR_PKF_RGB16;
|
||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
tmp |= LDDFR_PKF_RGB24;
|
tmp = LDDFR_PKF_RGB24;
|
||||||
break;
|
break;
|
||||||
case 32:
|
case 32:
|
||||||
tmp |= LDDFR_PKF_ARGB32;
|
default:
|
||||||
|
tmp = LDDFR_PKF_ARGB32;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lcdc_write_chan(ch, LDDFR, tmp);
|
lcdc_write_chan(ch, LDDFR, tmp);
|
||||||
|
lcdc_write_chan(ch, LDMLSR, ch->pitch);
|
||||||
|
lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
|
||||||
|
if (ch->info->var.nonstd)
|
||||||
|
lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
|
||||||
|
|
||||||
base_addr_y = ch->info->fix.smem_start;
|
/* When using deferred I/O mode, configure the LCDC for one-shot
|
||||||
base_addr_c = base_addr_y +
|
* operation and enable the frame end interrupt. Otherwise use
|
||||||
ch->info->var.xres *
|
* continuous read mode.
|
||||||
ch->info->var.yres_virtual;
|
*/
|
||||||
pitch = ch->info->fix.line_length;
|
if (ch->ldmt1r_value & LDMT1R_IFM &&
|
||||||
|
ch->cfg.sys_bus_cfg.deferred_io_msec) {
|
||||||
|
lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
|
||||||
|
lcdc_write(priv, _LDINTR, LDINTR_FE);
|
||||||
|
} else {
|
||||||
|
lcdc_write_chan(ch, LDSM1R, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* test if we can enable meram */
|
/* Word and long word swap. */
|
||||||
if (ch->cfg.meram_cfg && priv->meram_dev &&
|
if (priv->ch[0].info->var.nonstd)
|
||||||
priv->meram_dev->ops) {
|
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
||||||
struct sh_mobile_meram_cfg *cfg;
|
else {
|
||||||
struct sh_mobile_meram_info *mdev;
|
switch (bpp) {
|
||||||
unsigned long icb_addr_y, icb_addr_c;
|
case 16:
|
||||||
int icb_pitch;
|
tmp = LDDDSR_LS | LDDDSR_WS;
|
||||||
int pf;
|
break;
|
||||||
|
case 24:
|
||||||
|
tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
default:
|
||||||
|
tmp = LDDDSR_LS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lcdc_write(priv, _LDDDSR, tmp);
|
||||||
|
|
||||||
cfg = ch->cfg.meram_cfg;
|
/* Enable the display output. */
|
||||||
mdev = priv->meram_dev;
|
lcdc_write(priv, _LDCNT1R, LDCNT1R_DE);
|
||||||
/* we need to de-init configured ICBs before we
|
sh_mobile_lcdc_start_stop(priv, 1);
|
||||||
* we can re-initialize them.
|
priv->started = 1;
|
||||||
*/
|
}
|
||||||
if (ch->meram_enabled)
|
|
||||||
mdev->ops->meram_unregister(mdev, cfg);
|
|
||||||
|
|
||||||
|
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
|
||||||
|
{
|
||||||
|
struct sh_mobile_meram_info *mdev = priv->meram_dev;
|
||||||
|
struct sh_mobile_lcdc_board_cfg *board_cfg;
|
||||||
|
struct sh_mobile_lcdc_chan *ch;
|
||||||
|
unsigned long tmp;
|
||||||
|
int ret;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
/* enable clocks before accessing the hardware */
|
||||||
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
|
if (priv->ch[k].enabled)
|
||||||
|
sh_mobile_lcdc_clk_on(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset */
|
||||||
|
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR);
|
||||||
|
lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
|
||||||
|
|
||||||
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
|
ch = &priv->ch[k];
|
||||||
|
|
||||||
|
if (!ch->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
board_cfg = &ch->cfg.board_cfg;
|
||||||
|
if (board_cfg->setup_sys) {
|
||||||
|
ret = board_cfg->setup_sys(board_cfg->board_data, ch,
|
||||||
|
&sh_mobile_lcdc_sys_bus_ops);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compute frame buffer base address and pitch for each channel. */
|
||||||
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
|
struct sh_mobile_meram_cfg *cfg;
|
||||||
|
int pixelformat;
|
||||||
|
|
||||||
|
ch = &priv->ch[k];
|
||||||
|
if (!ch->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ch->base_addr_y = ch->info->fix.smem_start;
|
||||||
|
ch->base_addr_c = ch->base_addr_y
|
||||||
|
+ ch->info->var.xres
|
||||||
|
* ch->info->var.yres_virtual;
|
||||||
|
ch->pitch = ch->info->fix.line_length;
|
||||||
|
|
||||||
|
/* Enable MERAM if possible. */
|
||||||
|
cfg = ch->cfg.meram_cfg;
|
||||||
|
if (mdev == NULL || mdev->ops == NULL || cfg == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* we need to de-init configured ICBs before we can
|
||||||
|
* re-initialize them.
|
||||||
|
*/
|
||||||
|
if (ch->meram_enabled) {
|
||||||
|
mdev->ops->meram_unregister(mdev, cfg);
|
||||||
ch->meram_enabled = 0;
|
ch->meram_enabled = 0;
|
||||||
|
|
||||||
if (ch->info->var.nonstd) {
|
|
||||||
if (ch->info->var.bits_per_pixel == 24)
|
|
||||||
pf = SH_MOBILE_MERAM_PF_NV24;
|
|
||||||
else
|
|
||||||
pf = SH_MOBILE_MERAM_PF_NV;
|
|
||||||
} else {
|
|
||||||
pf = SH_MOBILE_MERAM_PF_RGB;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mdev->ops->meram_register(mdev, cfg, pitch,
|
|
||||||
ch->info->var.yres,
|
|
||||||
pf,
|
|
||||||
base_addr_y,
|
|
||||||
base_addr_c,
|
|
||||||
&icb_addr_y,
|
|
||||||
&icb_addr_c,
|
|
||||||
&icb_pitch);
|
|
||||||
if (!ret) {
|
|
||||||
/* set LDSA1R value */
|
|
||||||
base_addr_y = icb_addr_y;
|
|
||||||
pitch = icb_pitch;
|
|
||||||
|
|
||||||
/* set LDSA2R value if required */
|
|
||||||
if (base_addr_c)
|
|
||||||
base_addr_c = icb_addr_c;
|
|
||||||
|
|
||||||
ch->meram_enabled = 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* point out our frame buffer */
|
if (!ch->info->var.nonstd)
|
||||||
lcdc_write_chan(ch, LDSA1R, base_addr_y);
|
pixelformat = SH_MOBILE_MERAM_PF_RGB;
|
||||||
if (ch->info->var.nonstd)
|
else if (ch->info->var.bits_per_pixel == 24)
|
||||||
lcdc_write_chan(ch, LDSA2R, base_addr_c);
|
pixelformat = SH_MOBILE_MERAM_PF_NV24;
|
||||||
|
else
|
||||||
|
pixelformat = SH_MOBILE_MERAM_PF_NV;
|
||||||
|
|
||||||
/* set line size */
|
ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
|
||||||
lcdc_write_chan(ch, LDMLSR, pitch);
|
ch->info->var.yres, pixelformat,
|
||||||
|
ch->base_addr_y, ch->base_addr_c,
|
||||||
|
&ch->base_addr_y, &ch->base_addr_c,
|
||||||
|
&ch->pitch);
|
||||||
|
if (!ret)
|
||||||
|
ch->meram_enabled = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start the LCDC. */
|
||||||
|
__sh_mobile_lcdc_start(priv);
|
||||||
|
|
||||||
|
/* Setup deferred I/O, tell the board code to enable the panels, and
|
||||||
|
* turn backlight on.
|
||||||
|
*/
|
||||||
|
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
||||||
|
ch = &priv->ch[k];
|
||||||
|
if (!ch->enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* setup deferred io if SYS bus */
|
|
||||||
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
|
tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
|
||||||
if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
|
if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
|
||||||
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
|
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
|
||||||
ch->defio.delay = msecs_to_jiffies(tmp);
|
ch->defio.delay = msecs_to_jiffies(tmp);
|
||||||
ch->info->fbdefio = &ch->defio;
|
ch->info->fbdefio = &ch->defio;
|
||||||
fb_deferred_io_init(ch->info);
|
fb_deferred_io_init(ch->info);
|
||||||
|
|
||||||
/* one-shot mode */
|
|
||||||
lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
|
|
||||||
|
|
||||||
/* enable "Frame End Interrupt Enable" bit */
|
|
||||||
lcdc_write(priv, _LDINTR, LDINTR_FE);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* continuous read mode */
|
|
||||||
lcdc_write_chan(ch, LDSM1R, 0);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* display output */
|
|
||||||
lcdc_write(priv, _LDCNT1R, LDCNT1R_DE);
|
|
||||||
|
|
||||||
/* start the lcdc */
|
|
||||||
sh_mobile_lcdc_start_stop(priv, 1);
|
|
||||||
priv->started = 1;
|
|
||||||
|
|
||||||
/* tell the board code to enable the panel */
|
|
||||||
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
|
|
||||||
ch = &priv->ch[k];
|
|
||||||
if (!ch->enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
board_cfg = &ch->cfg.board_cfg;
|
board_cfg = &ch->cfg.board_cfg;
|
||||||
if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
|
if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
|
||||||
|
|
|
@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv;
|
||||||
struct fb_info;
|
struct fb_info;
|
||||||
struct backlight_device;
|
struct backlight_device;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct sh_mobile_lcdc_chan - LCDC display channel
|
||||||
|
*
|
||||||
|
* @base_addr_y: Frame buffer viewport base address (luma component)
|
||||||
|
* @base_addr_c: Frame buffer viewport base address (chroma component)
|
||||||
|
* @pitch: Frame buffer line pitch
|
||||||
|
*/
|
||||||
struct sh_mobile_lcdc_chan {
|
struct sh_mobile_lcdc_chan {
|
||||||
struct sh_mobile_lcdc_priv *lcdc;
|
struct sh_mobile_lcdc_priv *lcdc;
|
||||||
unsigned long *reg_offs;
|
unsigned long *reg_offs;
|
||||||
|
@ -40,6 +47,10 @@ struct sh_mobile_lcdc_chan {
|
||||||
int blank_status;
|
int blank_status;
|
||||||
struct mutex open_lock; /* protects the use counter */
|
struct mutex open_lock; /* protects the use counter */
|
||||||
int meram_enabled;
|
int meram_enabled;
|
||||||
|
|
||||||
|
unsigned long base_addr_y;
|
||||||
|
unsigned long base_addr_c;
|
||||||
|
unsigned int pitch;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue