static void lcdc_sys_write_data(void *handle, unsigned long data) { struct sh_mobile_lcdc_chan *ch = handle; lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); }
static unsigned long lcdc_sys_read_data(void *handle) { struct sh_mobile_lcdc_chan *ch = handle; lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); udelay(1); lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff; }
static int 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; /* 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) | LCDC_RESET); lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); /* enable LCDC channels */ tmp = lcdc_read(priv, _LDCNT2R); tmp |= priv->ch[0].enabled; tmp |= priv->ch[1].enabled; lcdc_write(priv, _LDCNT2R, tmp); /* read data from external memory, avoid using the BEU for now */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); /* stop the lcdc first */ sh_mobile_lcdc_start_stop(priv, 0); /* configure clocks */ tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; m = ch->cfg.clock_divider; if (!m) continue; if (m == 1) m = 1 << 6; tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */ lcdc_write_chan(ch, LDDCKPAT1R, 0); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); } lcdc_write(priv, _LDDCKR, tmp); /* start dotclock again */ lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); /* interrupts are disabled to begin with */ lcdc_write(priv, _LDINTR, 0); 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) lcdc_write(priv, _LDDDSR, ldddsr | 7); else { switch (bpp) { case 16: lcdc_write(priv, _LDDDSR, ldddsr | 6); break; case 24: lcdc_write(priv, _LDDDSR, ldddsr | 7); break; case 32: lcdc_write(priv, _LDDDSR, ldddsr | 4); break; } } 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 &= ~0x0003031f; if (ch->info->var.nonstd) { tmp |= (ch->info->var.nonstd << 16); switch (ch->info->var.bits_per_pixel) { case 12: break; case 16: tmp |= (0x1 << 8); break; case 24: tmp |= (0x2 << 8); break; } } else { switch (ch->info->var.bits_per_pixel) { case 16: tmp |= 0x03; break; case 24: tmp |= 0x0b; break; case 32: break; } } lcdc_write_chan(ch, LDDFR, tmp); 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; /* 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; 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); 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 */ lcdc_write_chan(ch, LDSA1R, base_addr_y); if (ch->info->var.nonstd) lcdc_write_chan(ch, LDSA2R, base_addr_c); /* set line size */ lcdc_write_chan(ch, LDMLSR, pitch); /* setup deferred io if SYS bus */ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & (1 << 12) && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); ch->info->fbdefio = &ch->defio; fb_deferred_io_init(ch->info); /* one-shot mode */ lcdc_write_chan(ch, LDSM1R, 1); /* enable "Frame End Interrupt Enable" bit */ lcdc_write(priv, _LDINTR, LDINTR_FE); } else { /* continuous read mode */ lcdc_write_chan(ch, LDSM1R, 0); } } /* display output */ lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); /* 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; if (board_cfg->display_on && try_module_get(board_cfg->owner)) { board_cfg->display_on(board_cfg->board_data, ch->info); module_put(board_cfg->owner); } if (ch->bl) { ch->bl->props.power = FB_BLANK_UNBLANK; backlight_update_status(ch->bl); } } return 0; }
static int 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; int k, m; int ret = 0; /* 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) | LCDC_RESET); lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); /* enable LCDC channels */ tmp = lcdc_read(priv, _LDCNT2R); tmp |= priv->ch[0].enabled; tmp |= priv->ch[1].enabled; lcdc_write(priv, _LDCNT2R, tmp); /* read data from external memory, avoid using the BEU for now */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); /* stop the lcdc first */ sh_mobile_lcdc_start_stop(priv, 0); /* configure clocks */ tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; m = ch->cfg.clock_divider; if (!m) continue; if (m == 1) m = 1 << 6; tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */ lcdc_write_chan(ch, LDDCKPAT1R, 0); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); } lcdc_write(priv, _LDDCKR, tmp); /* start dotclock again */ lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); /* interrupts are disabled to begin with */ lcdc_write(priv, _LDINTR, 0); 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 */ switch (bpp) { case 16: lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); break; case 24: lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7); break; case 32: lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4); break; } for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; /* set bpp format in PKF[4:0] */ tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~0x0001001f; switch (ch->info->var.bits_per_pixel) { case 16: tmp |= 0x03; break; case 24: tmp |= 0x0b; break; case 32: break; } lcdc_write_chan(ch, LDDFR, tmp); /* point out our frame buffer */ lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); /* set line size */ lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); /* setup deferred io if SYS bus */ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & (1 << 12) && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); ch->info->fbdefio = &ch->defio; fb_deferred_io_init(ch->info); /* one-shot mode */ lcdc_write_chan(ch, LDSM1R, 1); /* enable "Frame End Interrupt Enable" bit */ lcdc_write(priv, _LDINTR, LDINTR_FE); } else { /* continuous read mode */ lcdc_write_chan(ch, LDSM1R, 0); } } /* display output */ lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); /* 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; if (try_module_get(board_cfg->owner) && board_cfg->display_on) { board_cfg->display_on(board_cfg->board_data, ch->info); module_put(board_cfg->owner); } } return 0; }
/* * shmob_drm_crtc_start - Configure and start the LCDC * @scrtc: the SH Mobile CRTC * * Configure and start the LCDC device. External devices (clocks, MERAM, panels, * ...) are not touched by this function. */ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) { struct drm_crtc *crtc = &scrtc->crtc; struct shmob_drm_device *sdev = crtc->dev->dev_private; const struct shmob_drm_interface_data *idata = &sdev->pdata->iface; const struct shmob_drm_format_info *format; struct drm_device *dev = sdev->ddev; struct drm_plane *plane; u32 value; int ret; if (scrtc->started) return; format = shmob_drm_format_info(crtc->primary->fb->pixel_format); if (WARN_ON(format == NULL)) return; /* Enable clocks before accessing the hardware. */ ret = shmob_drm_clk_on(sdev); if (ret < 0) return; /* Reset and enable the LCDC. */ lcdc_write(sdev, LDCNT2R, lcdc_read(sdev, LDCNT2R) | LDCNT2R_BR); lcdc_wait_bit(sdev, LDCNT2R, LDCNT2R_BR, 0); lcdc_write(sdev, LDCNT2R, LDCNT2R_ME); /* Stop the LCDC first and disable all interrupts. */ shmob_drm_crtc_start_stop(scrtc, false); lcdc_write(sdev, LDINTR, 0); /* Configure power supply, dot clocks and start them. */ lcdc_write(sdev, LDPMR, 0); value = sdev->lddckr; if (idata->clk_div) { /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider * denominator. */ lcdc_write(sdev, LDDCKPAT1R, 0); lcdc_write(sdev, LDDCKPAT2R, (1 << (idata->clk_div / 2)) - 1); if (idata->clk_div == 1) value |= LDDCKR_MOSEL; else value |= idata->clk_div; } lcdc_write(sdev, LDDCKR, value); lcdc_write(sdev, LDDCKSTPR, 0); lcdc_wait_bit(sdev, LDDCKSTPR, ~0, 0); /* TODO: Setup SYS panel */ /* Setup geometry, format, frame buffer memory and operation mode. */ shmob_drm_crtc_setup_geometry(scrtc); /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */ lcdc_write(sdev, LDDFR, format->lddfr | LDDFR_CF1); lcdc_write(sdev, LDMLSR, scrtc->line_size); lcdc_write(sdev, LDSA1R, scrtc->dma[0]); if (format->yuv) lcdc_write(sdev, LDSA2R, scrtc->dma[1]); lcdc_write(sdev, LDSM1R, 0); /* Word and long word swap. */ switch (format->fourcc) { case DRM_FORMAT_RGB565: case DRM_FORMAT_NV21: case DRM_FORMAT_NV61: case DRM_FORMAT_NV42: value = LDDDSR_LS | LDDDSR_WS; break; case DRM_FORMAT_RGB888: case DRM_FORMAT_NV12: case DRM_FORMAT_NV16: case DRM_FORMAT_NV24: value = LDDDSR_LS | LDDDSR_WS | LDDDSR_BS; break; case DRM_FORMAT_ARGB8888: default: value = LDDDSR_LS; break; } lcdc_write(sdev, LDDDSR, value); /* Setup planes. */ drm_for_each_legacy_plane(plane, dev) { if (plane->crtc == crtc) shmob_drm_plane_setup(plane); } /* Enable the display output. */ lcdc_write(sdev, LDCNT1R, LDCNT1R_DE); shmob_drm_crtc_start_stop(scrtc, true); scrtc->started = true; }
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; struct fb_videomode *lcd_cfg; struct sh_mobile_lcdc_board_cfg *board_cfg; unsigned long tmp; int k, m; int ret = 0; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) if (priv->ch[k].enabled) sh_mobile_lcdc_clk_on(priv); lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); tmp = lcdc_read(priv, _LDCNT2R); tmp |= priv->ch[0].enabled; tmp |= priv->ch[1].enabled; lcdc_write(priv, _LDCNT2R, tmp); lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); sh_mobile_lcdc_start_stop(priv, 0); tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; m = ch->cfg.clock_divider; if (!m) continue; if (m == 1) m = 1 << 6; tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); } lcdc_write(priv, _LDDCKR, tmp); lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); lcdc_write(priv, _LDINTR, 0); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; lcd_cfg = &ch->cfg.lcd_cfg; if (!ch->enabled) continue; tmp = ch->ldmt1r_value; tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; lcdc_write_chan(ch, LDMT1R, tmp); lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); tmp = lcd_cfg->xres + lcd_cfg->hsync_len; tmp += lcd_cfg->left_margin; tmp += lcd_cfg->right_margin; tmp /= 8; tmp |= (lcd_cfg->xres / 8) << 16; lcdc_write_chan(ch, LDHCNR, tmp); tmp = lcd_cfg->xres; tmp += lcd_cfg->right_margin; tmp /= 8; tmp |= (lcd_cfg->hsync_len / 8) << 16; lcdc_write_chan(ch, LDHSYNR, tmp); lcdc_write_chan(ch, LDPMR, 0); tmp = lcd_cfg->yres + lcd_cfg->vsync_len; tmp += lcd_cfg->upper_margin; tmp += lcd_cfg->lower_margin; tmp |= lcd_cfg->yres << 16; lcdc_write_chan(ch, LDVLNR, tmp); tmp = lcd_cfg->yres; tmp += lcd_cfg->lower_margin; tmp |= lcd_cfg->vsync_len << 16; lcdc_write_chan(ch, LDVSYNR, tmp); board_cfg = &ch->cfg.board_cfg; if (board_cfg->setup_sys) ret = board_cfg->setup_sys(board_cfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); if (ret) return ret; } lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~(0x0001001f); tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; lcdc_write_chan(ch, LDDFR, tmp); lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & (1 << 12) && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); ch->info->fbdefio = &ch->defio; fb_deferred_io_init(ch->info); lcdc_write_chan(ch, LDSM1R, 1); lcdc_write(priv, _LDINTR, LDINTR_FE); } else { lcdc_write_chan(ch, LDSM1R, 0); } } lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); sh_mobile_lcdc_start_stop(priv, 1); priv->started = 1; 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->display_on) board_cfg->display_on(board_cfg->board_data); } return 0; }
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) { struct sh_mobile_lcdc_chan *ch; struct fb_videomode *lcd_cfg; struct sh_mobile_lcdc_board_cfg *board_cfg; unsigned long tmp; int k, m; int ret = 0; /* 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) | LCDC_RESET); lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); /* enable LCDC channels */ tmp = lcdc_read(priv, _LDCNT2R); tmp |= priv->ch[0].enabled; tmp |= priv->ch[1].enabled; lcdc_write(priv, _LDCNT2R, tmp); /* read data from external memory, avoid using the BEU for now */ lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); /* stop the lcdc first */ sh_mobile_lcdc_start_stop(priv, 0); /* configure clocks */ tmp = priv->lddckr; for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; m = ch->cfg.clock_divider; if (!m) continue; if (m == 1) m = 1 << 6; tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000); lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); } lcdc_write(priv, _LDDCKR, tmp); /* start dotclock again */ lcdc_write(priv, _LDDCKSTPR, 0); lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); /* interrupts are disabled to begin with */ lcdc_write(priv, _LDINTR, 0); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; lcd_cfg = &ch->cfg.lcd_cfg; if (!ch->enabled) continue; tmp = ch->ldmt1r_value; tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; lcdc_write_chan(ch, LDMT1R, tmp); /* setup SYS bus */ lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); /* horizontal configuration */ tmp = lcd_cfg->xres + lcd_cfg->hsync_len; tmp += lcd_cfg->left_margin; tmp += lcd_cfg->right_margin; tmp /= 8; /* HTCN */ tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */ lcdc_write_chan(ch, LDHCNR, tmp); tmp = lcd_cfg->xres; tmp += lcd_cfg->right_margin; tmp /= 8; /* HSYNP */ tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */ lcdc_write_chan(ch, LDHSYNR, tmp); /* power supply */ lcdc_write_chan(ch, LDPMR, 0); /* vertical configuration */ tmp = lcd_cfg->yres + lcd_cfg->vsync_len; tmp += lcd_cfg->upper_margin; tmp += lcd_cfg->lower_margin; /* VTLN */ tmp |= lcd_cfg->yres << 16; /* VDLN */ lcdc_write_chan(ch, LDVLNR, tmp); tmp = lcd_cfg->yres; tmp += lcd_cfg->lower_margin; /* VSYNP */ tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */ lcdc_write_chan(ch, LDVSYNR, tmp); board_cfg = &ch->cfg.board_cfg; if (board_cfg->setup_sys) ret = board_cfg->setup_sys(board_cfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); if (ret) return ret; } /* word and long word swap */ lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { ch = &priv->ch[k]; if (!priv->ch[k].enabled) continue; /* set bpp format in PKF[4:0] */ tmp = lcdc_read_chan(ch, LDDFR); tmp &= ~(0x0001001f); tmp |= (ch->info->var.bits_per_pixel == 16) ? 3 : 0; lcdc_write_chan(ch, LDDFR, tmp); /* point out our frame buffer */ lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); /* set line size */ lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); /* setup deferred io if SYS bus */ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; if (ch->ldmt1r_value & (1 << 12) && tmp) { ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; ch->defio.delay = msecs_to_jiffies(tmp); ch->info->fbdefio = &ch->defio; fb_deferred_io_init(ch->info); /* one-shot mode */ lcdc_write_chan(ch, LDSM1R, 1); /* enable "Frame End Interrupt Enable" bit */ lcdc_write(priv, _LDINTR, LDINTR_FE); } else { /* continuous read mode */ lcdc_write_chan(ch, LDSM1R, 0); } } /* display output */ lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); /* 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; if (board_cfg->display_on) board_cfg->display_on(board_cfg->board_data); } return 0; }