mx3fb.c
来自「Linux环境下视频显示卡设备的驱动程序源代码」· C语言 代码 · 共 1,556 行 · 第 1/4 页
C
1,556 行
}/** * sdc_set_window_pos() - set window position of the respective plane. * @mx3fb: mx3fb context. * @channel: IPU DMAC channel ID. * @x_pos: X coordinate relative to the top left corner to place window at. * @y_pos: Y coordinate relative to the top left corner to place window at. * @return: 0 on success or negative error code on failure. */static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel, int16_t x_pos, int16_t y_pos){ x_pos += mx3fb->h_start_width; y_pos += mx3fb->v_start_width; if (channel != IDMAC_SDC_0) return -EINVAL; mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); return 0;}/** * sdc_init_panel() - initialize a synchronous LCD panel. * @mx3fb: mx3fb context. * @panel: panel type. * @pixel_clk: desired pixel clock frequency in Hz. * @width: width of panel in pixels. * @height: height of panel in pixels. * @pixel_fmt: pixel format of buffer as FOURCC ASCII code. * @h_start_width: number of pixel clocks between the HSYNC signal pulse * and the start of valid data. * @h_sync_width: width of the HSYNC signal in units of pixel clocks. * @h_end_width: number of pixel clocks between the end of valid data * and the HSYNC signal for next line. * @v_start_width: number of lines between the VSYNC signal pulse and the * start of valid data. * @v_sync_width: width of the VSYNC signal in units of lines * @v_end_width: number of lines between the end of valid data and the * VSYNC signal for next frame. * @sig: bitfield of signal polarities for LCD interface. * @return: 0 on success or negative error code on failure. */static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, uint32_t pixel_clk, uint16_t width, uint16_t height, enum pixel_fmt pixel_fmt, uint16_t h_start_width, uint16_t h_sync_width, uint16_t h_end_width, uint16_t v_start_width, uint16_t v_sync_width, uint16_t v_end_width, struct ipu_di_signal_cfg sig){ unsigned long lock_flags; uint32_t reg; uint32_t old_conf; uint32_t div; struct clk *ipu_clk; dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); if (v_sync_width == 0 || h_sync_width == 0) return -EINVAL; /* Init panel size and blanking periods */ reg = ((uint32_t) (h_sync_width - 1) << 26) | ((uint32_t) (width + h_start_width + h_end_width - 1) << 16); mx3fb_write_reg(mx3fb, reg, SDC_HOR_CONF);#ifdef DEBUG printk(KERN_CONT " hor_conf %x,", reg);#endif reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L | ((uint32_t) (height + v_start_width + v_end_width - 1) << 16); mx3fb_write_reg(mx3fb, reg, SDC_VER_CONF);#ifdef DEBUG printk(KERN_CONT " ver_conf %x\n", reg);#endif mx3fb->h_start_width = h_start_width; mx3fb->v_start_width = v_start_width; switch (panel) { case IPU_PANEL_SHARP_TFT: mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1); mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2); mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); break; case IPU_PANEL_TFT: mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF); break; default: return -EINVAL; } /* Init clocking */ /* * Calculate divider: fractional part is 4 bits so simply multiple by * 24 to get fractional part, as long as we stay under ~250MHz and on * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz */ dev_dbg(mx3fb->dev, "pixel clk = %d\n", pixel_clk); ipu_clk = clk_get(mx3fb->dev, "ipu_clk"); div = clk_get_rate(ipu_clk) * 16 / pixel_clk; clk_put(ipu_clk); if (div < 0x40) { /* Divider less than 4 */ dev_dbg(mx3fb->dev, "InitPanel() - Pixel clock divider less than 4\n"); div = 0x40; } spin_lock_irqsave(&mx3fb->lock, lock_flags); /* * DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits * fewer. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing * debug. DISP3_IF_CLK_UP_WR is 0 */ mx3fb_write_reg(mx3fb, (((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); /* DI settings */ old_conf = mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF) & 0x78FFFFFF; old_conf |= sig.datamask_en << DI_D3_DATAMSK_SHIFT | sig.clksel_en << DI_D3_CLK_SEL_SHIFT | sig.clkidle_en << DI_D3_CLK_IDLE_SHIFT; mx3fb_write_reg(mx3fb, old_conf, DI_DISP_IF_CONF); old_conf = mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL) & 0xE0FFFFFF; old_conf |= sig.data_pol << DI_D3_DATA_POL_SHIFT | sig.clk_pol << DI_D3_CLK_POL_SHIFT | sig.enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT | sig.Hsync_pol << DI_D3_HSYNC_POL_SHIFT | sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT; mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); switch (pixel_fmt) { case IPU_PIX_FMT_RGB24: mx3fb_write_reg(mx3fb, di_mappings[0], DI_DISP3_B0_MAP); mx3fb_write_reg(mx3fb, di_mappings[1], DI_DISP3_B1_MAP); mx3fb_write_reg(mx3fb, di_mappings[2], DI_DISP3_B2_MAP); mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | ((di_mappings[3] - 1) << 12), DI_DISP_ACC_CC); break; case IPU_PIX_FMT_RGB666: mx3fb_write_reg(mx3fb, di_mappings[4], DI_DISP3_B0_MAP); mx3fb_write_reg(mx3fb, di_mappings[5], DI_DISP3_B1_MAP); mx3fb_write_reg(mx3fb, di_mappings[6], DI_DISP3_B2_MAP); mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | ((di_mappings[7] - 1) << 12), DI_DISP_ACC_CC); break; case IPU_PIX_FMT_BGR666: mx3fb_write_reg(mx3fb, di_mappings[8], DI_DISP3_B0_MAP); mx3fb_write_reg(mx3fb, di_mappings[9], DI_DISP3_B1_MAP); mx3fb_write_reg(mx3fb, di_mappings[10], DI_DISP3_B2_MAP); mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | ((di_mappings[11] - 1) << 12), DI_DISP_ACC_CC); break; default: mx3fb_write_reg(mx3fb, di_mappings[12], DI_DISP3_B0_MAP); mx3fb_write_reg(mx3fb, di_mappings[13], DI_DISP3_B1_MAP); mx3fb_write_reg(mx3fb, di_mappings[14], DI_DISP3_B2_MAP); mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | ((di_mappings[15] - 1) << 12), DI_DISP_ACC_CC); break; } spin_unlock_irqrestore(&mx3fb->lock, lock_flags); dev_dbg(mx3fb->dev, "DI_DISP_IF_CONF = 0x%08X\n", mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF)); dev_dbg(mx3fb->dev, "DI_DISP_SIG_POL = 0x%08X\n", mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL)); dev_dbg(mx3fb->dev, "DI_DISP3_TIME_CONF = 0x%08X\n", mx3fb_read_reg(mx3fb, DI_DISP3_TIME_CONF)); return 0;}/** * sdc_set_color_key() - set the transparent color key for SDC graphic plane. * @mx3fb: mx3fb context. * @channel: IPU DMAC channel ID. * @enable: boolean to enable or disable color keyl. * @color_key: 24-bit RGB color to use as transparent color key. * @return: 0 on success or negative error code on failure. */static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel, bool enable, uint32_t color_key){ uint32_t reg, sdc_conf; unsigned long lock_flags; spin_lock_irqsave(&mx3fb->lock, lock_flags); sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF); if (channel == IDMAC_SDC_0) sdc_conf &= ~SDC_COM_GWSEL; else sdc_conf |= SDC_COM_GWSEL; if (enable) { reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L; mx3fb_write_reg(mx3fb, reg | (color_key & 0x00FFFFFFL), SDC_GW_CTRL); sdc_conf |= SDC_COM_KEY_COLOR_G; } else { sdc_conf &= ~SDC_COM_KEY_COLOR_G; } mx3fb_write_reg(mx3fb, sdc_conf, SDC_COM_CONF); spin_unlock_irqrestore(&mx3fb->lock, lock_flags); return 0;}/** * sdc_set_global_alpha() - set global alpha blending modes. * @mx3fb: mx3fb context. * @enable: boolean to enable or disable global alpha blending. If disabled, * per pixel blending is used. * @alpha: global alpha value. * @return: 0 on success or negative error code on failure. */static int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t alpha){ uint32_t reg; unsigned long lock_flags; spin_lock_irqsave(&mx3fb->lock, lock_flags); if (enable) { reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0x00FFFFFFL; mx3fb_write_reg(mx3fb, reg | ((uint32_t) alpha << 24), SDC_GW_CTRL); reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); mx3fb_write_reg(mx3fb, reg | SDC_COM_GLB_A, SDC_COM_CONF); } else { reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); mx3fb_write_reg(mx3fb, reg & ~SDC_COM_GLB_A, SDC_COM_CONF); } spin_unlock_irqrestore(&mx3fb->lock, lock_flags); return 0;}static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value){ /* This might be board-specific */ mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); return;}static uint32_t bpp_to_pixfmt(int bpp){ uint32_t pixfmt = 0; switch (bpp) { case 24: pixfmt = IPU_PIX_FMT_BGR24; break; case 32: pixfmt = IPU_PIX_FMT_BGR32; break; case 16: pixfmt = IPU_PIX_FMT_RGB565; break; } return pixfmt;}static int mx3fb_blank(int blank, struct fb_info *fbi);static int mx3fb_map_video_memory(struct fb_info *fbi);static int mx3fb_unmap_video_memory(struct fb_info *fbi);/** * mx3fb_set_fix() - set fixed framebuffer parameters from variable settings. * @info: framebuffer information pointer * @return: 0 on success or negative error code on failure. */static int mx3fb_set_fix(struct fb_info *fbi){ struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_var_screeninfo *var = &fbi->var; strncpy(fix->id, "DISP3 BG", 8); fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; fix->type = FB_TYPE_PACKED_PIXELS; fix->accel = FB_ACCEL_NONE; fix->visual = FB_VISUAL_TRUECOLOR; fix->xpanstep = 1; fix->ypanstep = 1; return 0;}static void mx3fb_dma_done(void *arg){ struct idmac_tx_desc *tx_desc = to_tx_desc(arg); struct dma_chan *chan = tx_desc->txd.chan; struct idmac_channel *ichannel = to_idmac_chan(chan); struct mx3fb_data *mx3fb = ichannel->client; struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq); /* We only need one interrupt, it will be re-enabled as needed */ disable_irq(ichannel->eof_irq); complete(&mx3_fbi->flip_cmpl);}/** * mx3fb_set_par() - set framebuffer parameters and change the operating mode. * @fbi: framebuffer information pointer. * @return: 0 on success or negative error code on failure. */static int mx3fb_set_par(struct fb_info *fbi){ 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; size_t screen_size; dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+'); mutex_lock(&mx3_fbi->mutex); /* 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); fbi->fix.smem_len = mem_len; if (mx3fb_map_video_memory(fbi) < 0) { mutex_unlock(&mx3_fbi->mutex); return -ENOMEM; } } screen_size = fbi->fix.line_length * fbi->var.yres; 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));
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?