static int setup_disp_channel1(struct fb_info *fbi) { ipu_channel_params_t params; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; memset(¶ms, 0, sizeof(params)); params.mem_dp_bg_sync.di = mxc_fbi->ipu_di; debug("%s called\n", __func__); /* * Assuming interlaced means yuv output, below setting also * valid for mem_dc_sync. FG should have the same vmode as BG. */ if (fbi->var.vmode & FB_VMODE_INTERLACED) { params.mem_dp_bg_sync.interlaced = 1; params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_YUV444; } else { if (mxc_fbi->ipu_di_pix_fmt) { params.mem_dp_bg_sync.out_pixel_fmt = mxc_fbi->ipu_di_pix_fmt; } else { params.mem_dp_bg_sync.out_pixel_fmt = IPU_PIX_FMT_RGB666; } } params.mem_dp_bg_sync.in_pixel_fmt = bpp_to_pixfmt(fbi); if (mxc_fbi->alpha_chan_en) params.mem_dp_bg_sync.alpha_chan_en = 1; ipu_init_channel(mxc_fbi->ipu_ch, ¶ms); return 0; }
static int setup_disp_channel2(struct fb_info *fbi) { int retval = 0; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; mxc_fbi->cur_ipu_buf = 1; if (mxc_fbi->alpha_chan_en) mxc_fbi->cur_ipu_alpha_buf = 1; fbi->var.xoffset = fbi->var.yoffset = 0; debug("%s: %x %d %d %d %lx %lx\n", __func__, mxc_fbi->ipu_ch, fbi->var.xres, fbi->var.yres, fbi->fix.line_length, fbi->fix.smem_start, fbi->fix.smem_start + (fbi->fix.line_length * fbi->var.yres)); retval = ipu_init_channel_buffer(mxc_fbi->ipu_ch, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi), fbi->var.xres, fbi->var.yres, fbi->fix.line_length, fbi->fix.smem_start + (fbi->fix.line_length * fbi->var.yres), fbi->fix.smem_start, 0, 0); if (retval) printf("ipu_init_channel_buffer error %d\n", retval); return retval; }
/* * This routine actually sets the video mode. It's in here where we * the hardware state info->par and fix which can be affected by the * change in par. For this driver it doesn't do much. * */ static int mxc_elcdif_fb_set_par(struct fb_info *fbi) { struct mxc_elcdif_fb_data *data = (struct mxc_elcdif_fb_data *)fbi->par; struct elcdif_signal_cfg sig_cfg; int mem_len; dev_dbg(fbi->device, "Reconfiguring framebuffer\n"); /* If parameter no change, don't reconfigure. */ if (mxc_elcdif_fb_par_equal(fbi, data)) return 0; sema_init(&data->flip_sem, 1); /* release prev panel */ if (!g_elcdif_pix_clk_enable) { clk_enable(g_elcdif_pix_clk); g_elcdif_pix_clk_enable = true; } mxc_elcdif_blank_panel(FB_BLANK_POWERDOWN); mxc_elcdif_stop(); release_dotclk_panel(); mxc_elcdif_dma_release(); mxc_elcdif_fb_set_fix(fbi); if (g_elcdif_pix_clk_enable) { clk_disable(g_elcdif_pix_clk); g_elcdif_pix_clk_enable = false; } mem_len = fbi->var.yres_virtual * fbi->fix.line_length; if (!fbi->fix.smem_start || (mem_len > fbi->fix.smem_len)) { if (fbi->fix.smem_start) mxc_elcdif_fb_unmap_video_memory(fbi); if (mxc_elcdif_fb_map_video_memory(fbi) < 0) return -ENOMEM; } if (data->next_blank != FB_BLANK_UNBLANK) return 0; /* init next panel */ if (!g_elcdif_pix_clk_enable) { clk_enable(g_elcdif_pix_clk); g_elcdif_pix_clk_enable = true; } mxc_init_elcdif(); mxc_elcdif_init_panel(); dev_dbg(fbi->device, "pixclock = %u Hz\n", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); memset(&sig_cfg, 0, sizeof(sig_cfg)); if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) sig_cfg.Hsync_pol = true; if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) sig_cfg.Vsync_pol = true; if (fbi->var.sync & FB_SYNC_CLK_LAT_FALL) sig_cfg.clk_pol = true; if (!(fbi->var.sync & FB_SYNC_OE_LOW_ACT)) sig_cfg.enable_pol = true; setup_dotclk_panel((PICOS2KHZ(fbi->var.pixclock)) * 1000UL, fbi->var.vsync_len, fbi->var.upper_margin + fbi->var.yres + fbi->var.lower_margin, fbi->var.upper_margin, fbi->var.yres, fbi->var.hsync_len, fbi->var.left_margin + fbi->var.xres + fbi->var.right_margin, fbi->var.left_margin, fbi->var.xres, bpp_to_pixfmt(fbi), data->output_pix_fmt, sig_cfg, 1); mxc_elcdif_frame_addr_setup(fbi->fix.smem_start); mxc_elcdif_run(); mxc_elcdif_blank_panel(FB_BLANK_UNBLANK); fbi->mode = (struct fb_videomode *)fb_match_mode(&fbi->var, &fbi->modelist); data->var = fbi->var; return 0; }
int mxcfb_set_refresh_mode(struct fb_info *fbi, int mode, struct mxcfb_rect *update_region) { unsigned long start_addr; int ret_mode; uint32_t dummy; ipu_channel_params_t params; struct mxcfb_rect rect; struct mxcfb_info *mxc_fbi = (struct mxcfb_info *)fbi->par; uint32_t stride_pixels = (fbi->fix.line_length * 8) / fbi->var.bits_per_pixel; uint32_t memsize = fbi->fix.smem_len; if (mxc_fbi->cur_update_mode == mode) return mode; ret_mode = mxc_fbi->cur_update_mode; ipu_disable_irq(IPU_IRQ_ADC_SYS1_EOF); ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0); #ifdef PARTIAL_REFRESH ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF); ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE, 0, 0, 0); #endif ipu_disable_channel(ADC_SYS1, true); ipu_clear_irq(IPU_IRQ_ADC_SYS1_EOF); #ifdef PARTIAL_REFRESH ipu_disable_channel(ADC_SYS2, true); ipu_clear_irq(IPU_IRQ_ADC_SYS2_EOF); #endif ipu_adc_get_snooping_status(&dummy, &dummy); mxc_fbi->cur_update_mode = mode; switch (mode) { case MXCFB_REFRESH_OFF: if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0) < 0) dev_err(fbi->device, "Error enabling auto refesh.\n"); if (ipu_adc_set_update_mode(ADC_SYS2, IPU_ADC_REFRESH_NONE, 0, 0, 0) < 0) dev_err(fbi->device, "Error enabling auto refesh.\n"); #if 0 ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), 1, 1, 4, IPU_ROTATE_NONE, fbi->fix.smem_start, fbi->fix.smem_start, 0, 0); ipu_enable_channel(ADC_SYS2); ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 0); ipu_select_buffer(ADC_SYS2, IPU_INPUT_BUFFER, 1); msleep(10); #endif ipu_uninit_channel(ADC_SYS1); #ifdef PARTIAL_REFRESH ipu_uninit_channel(ADC_SYS2); #endif break; case MXCFB_REFRESH_PARTIAL: #ifdef PARTIAL_REFRESH ipu_adc_get_snooping_status(&dummy, &dummy); params.adc_sys2.disp = DISP0; params.adc_sys2.ch_mode = WriteTemplateNonSeq; params.adc_sys2.out_left = 0; params.adc_sys2.out_top = 0; ipu_init_channel(ADC_SYS2, ¶ms); if (ipu_adc_set_update_mode(ADC_SYS1, IPU_ADC_REFRESH_NONE, 0, 0, 0) < 0) { dev_err(fbi->device, "Error enabling auto refesh.\n"); } if (ipu_adc_set_update_mode (ADC_SYS2, IPU_ADC_AUTO_REFRESH_SNOOP, 30, fbi->fix.smem_start, &memsize) < 0) { dev_err(fbi->device, "Error enabling auto refesh.\n"); } mxc_fbi->snoop_window_size = memsize; ipu_init_channel_buffer(ADC_SYS2, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), 1, 1, 4, IPU_ROTATE_NONE, fbi->fix.smem_start, 0, 0, 0); params.adc_sys1.disp = mxc_fbi->disp_num; params.adc_sys1.ch_mode = WriteTemplateNonSeq; params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET; params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET; ipu_init_channel(ADC_SYS1, ¶ms); ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), MXCFB_SCREEN_WIDTH, MXCFB_SCREEN_HEIGHT, stride_pixels, IPU_ROTATE_NONE, fbi->fix.smem_start, 0, 0, 0); ipu_enable_channel(ADC_SYS1); ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0); ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF); break; #endif case MXCFB_REFRESH_AUTO: if (update_region == NULL) { update_region = ▭ rect.top = 0; rect.left = 0; rect.height = MXCFB_SCREEN_HEIGHT; rect.width = MXCFB_SCREEN_WIDTH; } params.adc_sys1.disp = mxc_fbi->disp_num; params.adc_sys1.ch_mode = WriteTemplateNonSeq; params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET + update_region->left; params.adc_sys1.out_top = MXCFB_SCREEN_TOP_OFFSET + update_region->top; ipu_init_channel(ADC_SYS1, ¶ms); /* Address aligned to line */ start_addr = update_region->top * fbi->fix.line_length; start_addr += fbi->fix.smem_start; start_addr += update_region->left * fbi->var.bits_per_pixel / 8; ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), update_region->width, update_region->height, stride_pixels, IPU_ROTATE_NONE, start_addr, 0, 0, 0); ipu_enable_channel(ADC_SYS1); ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0); if (ipu_adc_set_update_mode (ADC_SYS1, IPU_ADC_AUTO_REFRESH_SNOOP, 30, fbi->fix.smem_start, &memsize) < 0) dev_err(fbi->device, "Error enabling auto refesh.\n"); mxc_fbi->snoop_window_size = memsize; break; } return ret_mode; }
static irqreturn_t mxcfb_sys2_eof_irq_handler(int irq, void *dev_id) { ipu_channel_params_t params; struct fb_info *fbi = dev_id; struct mxcfb_info *mxc_fbi = fbi->par; uint32_t stat[2], seg_size; uint32_t lsb, msb; uint32_t update_height, start_line, start_addr, end_line, end_addr; uint32_t stride_pixels = (fbi->fix.line_length * 8) / fbi->var.bits_per_pixel; ipu_adc_get_snooping_status(&stat[0], &stat[1]); if (!stat[0] && !stat[1]) { dev_err(fbi->device, "error no bus snooping bits set\n"); return IRQ_HANDLED; } ipu_disable_irq(IPU_IRQ_ADC_SYS2_EOF); lsb = ffs(stat[0]); if (lsb) { lsb--; } else { lsb = ffs(stat[1]); lsb += 32 - 1; } msb = fls(stat[1]); if (msb) { msb += 32; } else { msb = fls(stat[0]); } seg_size = mxc_fbi->snoop_window_size / 64; start_addr = lsb * seg_size; /* starting address offset */ start_line = start_addr / fbi->fix.line_length; start_addr = start_line * fbi->fix.line_length; /* Addr aligned to line */ start_addr += fbi->fix.smem_start; end_addr = msb * seg_size; /* ending address offset */ end_line = end_addr / fbi->fix.line_length; end_line++; if (end_line > fbi->var.yres) { end_line = fbi->var.yres; } update_height = end_line - start_line; dev_dbg(fbi->device, "updating rows %d to %d, start addr = 0x%08X\n", start_line, end_line, start_addr); ipu_uninit_channel(ADC_SYS1); params.adc_sys1.disp = mxc_fbi->disp_num; params.adc_sys1.ch_mode = WriteTemplateNonSeq; params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET; params.adc_sys1.out_top = start_line; ipu_init_channel(ADC_SYS1, ¶ms); ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), MXCFB_SCREEN_WIDTH, update_height, stride_pixels, IPU_ROTATE_NONE, (dma_addr_t) start_addr, 0, 0, 0); ipu_enable_channel(ADC_SYS1); ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0); ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF); return IRQ_HANDLED; }
static int __set_par(struct fb_info *fbi, bool lock) { u32 mem_len; struct ipu_di_signal_cfg sig_cfg; enum ipu_panel mode = IPU_PANEL_TFT; struct mx3fb_info *mx3_fbi = fbi->par; struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; struct idmac_channel *ichan = mx3_fbi->idmac_channel; struct idmac_video_param *video = &ichan->params.video; struct scatterlist *sg = mx3_fbi->sg; /* Total cleanup */ if (mx3_fbi->txd) sdc_disable_channel(mx3_fbi); mx3fb_set_fix(fbi); mem_len = fbi->var.yres_virtual * fbi->fix.line_length; if (mem_len > fbi->fix.smem_len) { if (fbi->fix.smem_start) mx3fb_unmap_video_memory(fbi); if (mx3fb_map_video_memory(fbi, mem_len, lock) < 0) return -ENOMEM; } sg_init_table(&sg[0], 1); sg_init_table(&sg[1], 1); sg_dma_address(&sg[0]) = fbi->fix.smem_start; sg_set_page(&sg[0], virt_to_page(fbi->screen_base), fbi->fix.smem_len, offset_in_page(fbi->screen_base)); if (mx3_fbi->ipu_ch == IDMAC_SDC_0) { memset(&sig_cfg, 0, sizeof(sig_cfg)); if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) sig_cfg.Hsync_pol = true; if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) sig_cfg.Vsync_pol = true; if (fbi->var.sync & FB_SYNC_CLK_INVERT) sig_cfg.clk_pol = true; if (fbi->var.sync & FB_SYNC_DATA_INVERT) sig_cfg.data_pol = true; if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH) sig_cfg.enable_pol = true; if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) sig_cfg.clkidle_en = true; if (fbi->var.sync & FB_SYNC_CLK_SEL_EN) sig_cfg.clksel_en = true; if (fbi->var.sync & FB_SYNC_SHARP_MODE) mode = IPU_PANEL_SHARP_TFT; dev_dbg(fbi->device, "pixclock = %ul Hz\n", (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); if (sdc_init_panel(mx3fb, mode, (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, fbi->var.xres, fbi->var.yres, (fbi->var.sync & FB_SYNC_SWAP_RGB) ? IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666, fbi->var.left_margin, fbi->var.hsync_len, fbi->var.right_margin + fbi->var.hsync_len, fbi->var.upper_margin, fbi->var.vsync_len, fbi->var.lower_margin + fbi->var.vsync_len, sig_cfg) != 0) { dev_err(fbi->device, "mx3fb: Error initializing panel.\n"); return -EINVAL; } } sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, 0, 0); mx3_fbi->cur_ipu_buf = 0; video->out_pixel_fmt = bpp_to_pixfmt(fbi->var.bits_per_pixel); video->out_width = fbi->var.xres; video->out_height = fbi->var.yres; video->out_stride = fbi->var.xres_virtual; if (mx3_fbi->blank == FB_BLANK_UNBLANK) sdc_enable_channel(mx3_fbi); return 0; }
static void mxcfb_update_region(struct fb_info *fbi, uint32_t statl, uint32_t stath) { ipu_channel_params_t params; struct mxcfb_info *mxc_fbi = fbi->par; uint32_t seg_size; uint32_t lsb, msb; uint32_t update_height, start_line, start_addr, end_line, end_addr; uint32_t stride_pixels = (fbi->fix.line_length * 8) / fbi->var.bits_per_pixel; lsb = ffs(statl); if (lsb) { lsb--; } else { lsb = ffs(stath); lsb += 32 - 1; } msb = fls(stath); if (msb) { msb += 32; } else { msb = fls(statl); } seg_size = mxc_fbi->snoop_window_size / 64; start_addr = lsb * seg_size; // starting address offset start_line = start_addr / fbi->fix.line_length; start_addr = start_line * fbi->fix.line_length; // Addr aligned to line start_addr += fbi->fix.smem_start; end_addr = msb * seg_size; // ending address offset end_line = end_addr / fbi->fix.line_length; end_line++; if (end_line > fbi->var.yres) { end_line = fbi->var.yres; } update_height = end_line - start_line; dev_dbg(fbi->device, "updating rows %d to %d, start addr = 0x%08X\n", start_line, end_line, start_addr); ipu_uninit_channel(ADC_SYS1); params.adc_sys1.disp = mxc_fbi->disp_num; params.adc_sys1.ch_mode = WriteTemplateNonSeq; params.adc_sys1.out_left = MXCFB_SCREEN_LEFT_OFFSET; params.adc_sys1.out_top = start_line; ipu_init_channel(ADC_SYS1, ¶ms); ipu_init_channel_buffer(ADC_SYS1, IPU_INPUT_BUFFER, bpp_to_pixfmt(fbi->var.bits_per_pixel), MXCFB_SCREEN_WIDTH, update_height, stride_pixels, IPU_ROTATE_NONE, (dma_addr_t) start_addr, 0, 0, 0); ipu_enable_channel(ADC_SYS1); ipu_select_buffer(ADC_SYS1, IPU_INPUT_BUFFER, 0); ipu_enable_irq(IPU_IRQ_ADC_SYS1_EOF); }