| From 10c7583e1d260cb9e66c6060c4c7608e317353a4 Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart@ideasonboard.com> |
| Date: Wed, 13 Jul 2011 12:13:47 +0200 |
| Subject: 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> |
| (cherry picked from commit 9a217e3444ec0c3a0dba35f7b4221af6671da67b) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| drivers/video/sh_mobile_lcdcfb.c | 309 +++++++++++++++++++------------------- |
| drivers/video/sh_mobile_lcdcfb.h | 11 ++ |
| 2 files changed, 162 insertions(+), 158 deletions(-) |
| |
| diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c |
| index 2d935db..292cfc0 100644 |
| --- a/drivers/video/sh_mobile_lcdcfb.c |
| +++ b/drivers/video/sh_mobile_lcdcfb.c |
| @@ -435,48 +435,42 @@ static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) |
| 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_board_cfg *board_cfg; |
| unsigned long tmp; |
| int bpp = 0; |
| - unsigned long ldddsr; |
| - int k, m, ret; |
| + int k, m; |
| |
| - /* 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); |
| - if (!bpp) |
| - bpp = priv->ch[k].info->var.bits_per_pixel; |
| - } |
| - } |
| - |
| - /* reset */ |
| - 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); |
| + /* Enable LCDC channels. Read data from external memory, avoid using the |
| + * BEU for now. |
| + */ |
| + lcdc_write(priv, _LDCNT2R, priv->ch[0].enabled | priv->ch[1].enabled); |
| |
| - /* stop the lcdc first */ |
| + /* Stop the LCDC first and disable all interrupts. */ |
| 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; |
| for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
| ch = &priv->ch[k]; |
| - |
| - if (!priv->ch[k].enabled) |
| + if (!ch->enabled) |
| continue; |
| |
| + if (!bpp) |
| + bpp = ch->info->var.bits_per_pixel; |
| + |
| + /* Power supply */ |
| + lcdc_write_chan(ch, LDPMR, 0); |
| + |
| m = ch->cfg.clock_divider; |
| if (!m) |
| continue; |
| @@ -493,188 +487,187 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) |
| } |
| |
| lcdc_write(priv, _LDDCKR, tmp); |
| - |
| - /* start dotclock again */ |
| lcdc_write(priv, _LDDCKSTPR, 0); |
| lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); |
| |
| - /* interrupts are disabled to begin with */ |
| - lcdc_write(priv, _LDINTR, 0); |
| - |
| + /* Setup geometry, format, frame buffer memory and operation mode. */ |
| for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
| ch = &priv->ch[k]; |
| - |
| if (!ch->enabled) |
| continue; |
| |
| 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) { |
| - tmp |= (ch->info->var.nonstd << 16); |
| + tmp = (ch->info->var.nonstd << 16); |
| switch (ch->info->var.bits_per_pixel) { |
| case 12: |
| + tmp |= LDDFR_YF_420; |
| break; |
| case 16: |
| tmp |= LDDFR_YF_422; |
| break; |
| case 24: |
| + default: |
| tmp |= LDDFR_YF_444; |
| break; |
| } |
| } else { |
| switch (ch->info->var.bits_per_pixel) { |
| case 16: |
| - tmp |= LDDFR_PKF_RGB16; |
| + tmp = LDDFR_PKF_RGB16; |
| break; |
| case 24: |
| - tmp |= LDDFR_PKF_RGB24; |
| + tmp = LDDFR_PKF_RGB24; |
| break; |
| case 32: |
| - tmp |= LDDFR_PKF_ARGB32; |
| + default: |
| + tmp = LDDFR_PKF_ARGB32; |
| break; |
| } |
| } |
| + |
| 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; |
| - base_addr_c = base_addr_y + |
| - ch->info->var.xres * |
| - ch->info->var.yres_virtual; |
| - pitch = ch->info->fix.line_length; |
| + /* When using deferred I/O mode, configure the LCDC for one-shot |
| + * operation and enable the frame end interrupt. Otherwise use |
| + * continuous read mode. |
| + */ |
| + 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 */ |
| - if (ch->cfg.meram_cfg && priv->meram_dev && |
| - priv->meram_dev->ops) { |
| - struct sh_mobile_meram_cfg *cfg; |
| - struct sh_mobile_meram_info *mdev; |
| - unsigned long icb_addr_y, icb_addr_c; |
| - int icb_pitch; |
| - int pf; |
| + /* Word and long word swap. */ |
| + if (priv->ch[0].info->var.nonstd) |
| + tmp = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; |
| + else { |
| + switch (bpp) { |
| + case 16: |
| + tmp = LDDDSR_LS | LDDDSR_WS; |
| + 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; |
| - mdev = priv->meram_dev; |
| - /* we need to de-init configured ICBs before we |
| - * we can re-initialize them. |
| - */ |
| - if (ch->meram_enabled) |
| - mdev->ops->meram_unregister(mdev, cfg); |
| + /* Enable the display output. */ |
| + lcdc_write(priv, _LDCNT1R, LDCNT1R_DE); |
| + sh_mobile_lcdc_start_stop(priv, 1); |
| + priv->started = 1; |
| +} |
| |
| - ch->meram_enabled = 0; |
| +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; |
| |
| - 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; |
| - } |
| + /* 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); |
| + } |
| |
| - 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; |
| - } |
| - } |
| + /* reset */ |
| + lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LDCNT2R_BR); |
| + lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0); |
| |
| - /* point out our frame buffer */ |
| - lcdc_write_chan(ch, LDSA1R, base_addr_y); |
| - if (ch->info->var.nonstd) |
| - lcdc_write_chan(ch, LDSA2R, base_addr_c); |
| + for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { |
| + ch = &priv->ch[k]; |
| |
| - /* set line size */ |
| - lcdc_write_chan(ch, LDMLSR, pitch); |
| + if (!ch->enabled) |
| + continue; |
| |
| - /* setup deferred io if SYS bus */ |
| - tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; |
| - if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { |
| - ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; |
| - ch->defio.delay = msecs_to_jiffies(tmp); |
| - ch->info->fbdefio = &ch->defio; |
| - fb_deferred_io_init(ch->info); |
| + 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; |
| + } |
| + } |
| |
| - /* one-shot mode */ |
| - lcdc_write_chan(ch, LDSM1R, LDSM1R_OS); |
| + /* 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; |
| |
| - /* enable "Frame End Interrupt Enable" bit */ |
| - lcdc_write(priv, _LDINTR, LDINTR_FE); |
| + ch = &priv->ch[k]; |
| + if (!ch->enabled) |
| + continue; |
| |
| - } else { |
| - /* continuous read mode */ |
| - lcdc_write_chan(ch, LDSM1R, 0); |
| + 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; |
| } |
| - } |
| |
| - /* display output */ |
| - lcdc_write(priv, _LDCNT1R, LDCNT1R_DE); |
| + if (!ch->info->var.nonstd) |
| + pixelformat = SH_MOBILE_MERAM_PF_RGB; |
| + else if (ch->info->var.bits_per_pixel == 24) |
| + pixelformat = SH_MOBILE_MERAM_PF_NV24; |
| + else |
| + pixelformat = SH_MOBILE_MERAM_PF_NV; |
| + |
| + ret = mdev->ops->meram_register(mdev, cfg, ch->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_stop(priv, 1); |
| - priv->started = 1; |
| + /* Start the LCDC. */ |
| + __sh_mobile_lcdc_start(priv); |
| |
| - /* tell the board code to enable the panel */ |
| + /* 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; |
| |
| + tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; |
| + if (ch->ldmt1r_value & LDMT1R_IFM && tmp) { |
| + ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; |
| + ch->defio.delay = msecs_to_jiffies(tmp); |
| + ch->info->fbdefio = &ch->defio; |
| + fb_deferred_io_init(ch->info); |
| + } |
| + |
| board_cfg = &ch->cfg.board_cfg; |
| if (board_cfg->display_on && try_module_get(board_cfg->owner)) { |
| board_cfg->display_on(board_cfg->board_data, ch->info); |
| diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h |
| index aeed668..a06219b 100644 |
| --- a/drivers/video/sh_mobile_lcdcfb.h |
| +++ b/drivers/video/sh_mobile_lcdcfb.h |
| @@ -18,6 +18,13 @@ struct sh_mobile_lcdc_priv; |
| struct fb_info; |
| 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_priv *lcdc; |
| unsigned long *reg_offs; |
| @@ -40,6 +47,10 @@ struct sh_mobile_lcdc_chan { |
| int blank_status; |
| struct mutex open_lock; /* protects the use counter */ |
| int meram_enabled; |
| + |
| + unsigned long base_addr_y; |
| + unsigned long base_addr_c; |
| + unsigned int pitch; |
| }; |
| |
| #endif |
| -- |
| 1.7.10 |
| |