/** * 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; }
static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value) { dev_dbg(mx3fb->dev, "%s: value = %d\n", __func__, value); /* This might be board-specific */ mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); return; }
static void sdc_fb_init(struct mx3fb_info *fbi) { struct mx3fb_data *mx3fb = fbi->mx3fb; uint32_t reg; reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF); }
/* Returns enabled flag before uninit */ static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi) { struct mx3fb_data *mx3fb = fbi->mx3fb; uint32_t reg; reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF); return reg & SDC_COM_BG_EN; }
/** * 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) { if (channel != IDMAC_SDC_0) return -EINVAL; x_pos += mx3fb->h_start_width; y_pos += mx3fb->v_start_width; mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); 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; }
/** * 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 * 2^4 to get fractional part, as long as we stay under ~250MHz and on * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz */ ipu_clk = clk_get(mx3fb->dev, NULL); if (!IS_ERR(ipu_clk)) { div = clk_get_rate(ipu_clk) * 16 / pixel_clk; clk_put(ipu_clk); } else { div = 0; } if (div < 0x40) { /* Divider less than 4 */ dev_dbg(mx3fb->dev, "InitPanel() - Pixel clock divider less than 4\n"); div = 0x40; } dev_dbg(mx3fb->dev, "pixel clk = %u, divider %u.%u\n", pixel_clk, div >> 4, (div & 7) * 125); 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; }
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; }