static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) { struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; unsigned long h_total, hsync_pos, display_h_total; u32 tmp; tmp = ch->ldmt1r_value; tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; tmp |= (var->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 */ h_total = display_var->xres + display_var->hsync_len + display_var->left_margin + display_var->right_margin; tmp = h_total / 8; /* HTCN */ tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ lcdc_write_chan(ch, LDHCNR, tmp); hsync_pos = display_var->xres + display_var->right_margin; tmp = hsync_pos / 8; /* HSYNP */ tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ lcdc_write_chan(ch, LDHSYNR, tmp); /* vertical configuration */ tmp = display_var->yres + display_var->vsync_len + display_var->upper_margin + display_var->lower_margin; /* VTLN */ tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ lcdc_write_chan(ch, LDVLNR, tmp); tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ tmp |= display_var->vsync_len << 16; /* VSYNW */ lcdc_write_chan(ch, LDVSYNR, tmp); /* Adjust horizontal synchronisation for HDMI */ display_h_total = display_var->xres + display_var->hsync_len + display_var->left_margin + display_var->right_margin; tmp = ((display_var->xres & 7) << 24) | ((display_h_total & 7) << 16) | ((display_var->hsync_len & 7) << 8) | hsync_pos; lcdc_write_chan(ch, LDHAJR, tmp); }
static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); /* * It's possible to get here without anything on the pagelist via * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() * invocation. In the former case, the acceleration routines are * stepped in to when using the framebuffer console causing the * workqueue to be scheduled without any dirty pages on the list. * * Despite this, a panel update is still needed given that the * acceleration routines have their own methods for writing in * that still need to be updated. * * The fsync() and empty pagelist case could be optimized for, * but we don't bother, as any application exhibiting such * behaviour is fundamentally broken anyways. */ if (!list_empty(pagelist)) { unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); /* trigger panel update */ dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, 1); dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); } else { if (bcfg->start_transfer) bcfg->start_transfer(bcfg->board_data, ch, &sh_mobile_lcdc_sys_bus_ops); lcdc_write_chan(ch, LDSM2R, 1); } }
static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; unsigned int nr_pages; sh_mobile_lcdc_clk_on(ch->lcdc); nr_pages = sh_mobile_lcdc_sginit(info, pagelist); dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); lcdc_write_chan(ch, LDSM2R, 1); dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); }
static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagelist) { struct sh_mobile_lcdc_chan *ch = info->par; unsigned int nr_pages; /* enable clocks before accessing hardware */ sh_mobile_lcdc_clk_on(ch->lcdc); nr_pages = sh_mobile_lcdc_sginit(info, pagelist); dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); /* trigger panel update */ lcdc_write_chan(ch, LDSM2R, 1); dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); }
static int sh_mobile_lcdc_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); struct sh_mobile_lcdc_chan *ch; int k, n; /* restore per-channel registers */ for (k = 0; k < ARRAY_SIZE(p->ch); k++) { ch = &p->ch[k]; if (!ch->enabled) continue; for (n = 0; n < NR_CH_REGS; n++) lcdc_write_chan(ch, n, ch->saved_ch_regs[n]); } /* restore shared registers */ for (n = 0; n < NR_SHARED_REGS; n++) lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]); return 0; }
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; }
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; }