void lcd_init_device(void) { semaphore_init(&g_wait_sema, 1, 0); /* I'm not really sure this pin is related to power, it does not seem to do anything */ imx233_pinctrl_acquire(1, 8, "lcd_power"); imx233_pinctrl_acquire(1, 9, "lcd_spi_sdo"); imx233_pinctrl_acquire(1, 10, "lcd_spi_scl"); imx233_pinctrl_acquire(1, 11, "lcd_spi_cs"); imx233_pinctrl_set_function(1, 9, PINCTRL_FUNCTION_GPIO); imx233_pinctrl_set_function(1, 10, PINCTRL_FUNCTION_GPIO); imx233_pinctrl_set_function(1, 11, PINCTRL_FUNCTION_GPIO); imx233_pinctrl_set_function(1, 8, PINCTRL_FUNCTION_GPIO); imx233_pinctrl_enable_gpio(1, 8, true); /** lcd is 320x240, data bus is 8-bit, depth is 24-bit so we need 3clk/pix * by running PIX clock at 24MHz we can sustain ~100 fps */ imx233_clkctrl_enable(CLK_PIX, false); imx233_clkctrl_set_div(CLK_PIX, 2); imx233_clkctrl_set_bypass(CLK_PIX, true); /* use XTAL */ imx233_clkctrl_enable(CLK_PIX, true); imx233_lcdif_init(); imx233_lcdif_setup_dotclk_pins(8, false); imx233_lcdif_set_word_length(8); imx233_lcdif_set_underflow_cb(&lcd_underflow); imx233_lcdif_enable_underflow_irq(true); imx233_dma_clkgate_channel(APB_LCDIF, true); imx233_dma_reset_channel(APB_LCDIF); /** Datasheet states: * 257H >= VBP >= 3H, VBP > VLW, VFP >= 1H * 1533clk >= HBP >= 24clk, HBP > HLW, HFP >= 4clk * * Take VLW=1H, VBP=3H, VFP=1H, HLW=8, HBP=24, HFP=4 * Take 3clk/pix because we send 24-bit/pix with 8-bit data bus * Keep consistent with register setting in lcd_init_seq */ imx233_lcdif_setup_dotclk_ex(/*v_pulse_width*/1, /*v_back_porch*/3, /*v_front_porch*/1, /*h_pulse_width*/8, /*h_back_porch*/24, /*h_front_porch*/4, LCD_WIDTH, LCD_HEIGHT, /*clk_per_pix*/3, /*enable_present*/false); imx233_lcdif_set_byte_packing_format(0xf); imx233_lcdif_enable_sync_signals(true); // we need frame signals during init // setup dma unsigned size = IMX233_FRAMEBUFFER_SIZE; uint8_t *frame_p = FRAME; for(int i = 0; i < NR_CMDS; i++) { unsigned xfer = MIN(IMX233_MAX_SINGLE_DMA_XFER_SIZE, size); lcdif_dma[i].dma.next = &lcdif_dma[(i + 1) % NR_CMDS].dma; lcdif_dma[i].dma.cmd = BF_OR3(APB_CHx_CMD, CHAIN(1), COMMAND(BV_APB_CHx_CMD_COMMAND__READ), XFER_COUNT(xfer)); lcdif_dma[i].dma.buffer = frame_p; size -= xfer; frame_p += xfer; } // first transfer: enable run, dotclk and so on lcdif_dma[0].dma.cmd |= BF_OR1(APB_CHx_CMD, CMDWORDS(1)); lcdif_dma[0].ctrl = BF_OR4(LCDIF_CTRL, BYPASS_COUNT(1), DOTCLK_MODE(1), RUN(1), WORD_LENGTH(1)); // enable lcd_enable(true); }
void imx233_lcdif_setup_dotclk(unsigned v_pulse_width, unsigned v_period, unsigned v_wait_cnt, unsigned v_active, unsigned h_pulse_width, unsigned h_period, unsigned h_wait_cnt, unsigned h_active, bool enable_present) { HW_LCDIF_VDCTRL0 = BF_OR4(LCDIF_VDCTRL0, ENABLE_PRESENT(enable_present), VSYNC_PERIOD_UNIT(1), VSYNC_PULSE_WIDTH_UNIT(1), DOTCLK_V_VALID_DATA_CNT(v_active)); HW_LCDIF_VDCTRL1 = BF_OR2(LCDIF_VDCTRL1, VSYNC_PERIOD(v_period), VSYNC_PULSE_WIDTH(v_pulse_width)); HW_LCDIF_VDCTRL2 = BF_OR3(LCDIF_VDCTRL2, HSYNC_PULSE_WIDTH(h_pulse_width), HSYNC_PERIOD(h_period), DOTCLK_H_VALID_DATA_CNT(h_active)); HW_LCDIF_VDCTRL3 = BF_OR2(LCDIF_VDCTRL3, VERTICAL_WAIT_CNT(v_wait_cnt), HORIZONTAL_WAIT_CNT(h_wait_cnt)); // setup dotclk mode, always bypass count, apparently data select is needed HW_LCDIF_CTRL_SET = BM_OR3(LCDIF_CTRL, DOTCLK_MODE, BYPASS_COUNT, DATA_SELECT); }
void imx233_pwm_setup(int channel, int period, int cdiv, int active, int active_state, int inactive, int inactive_state) { /* stop */ bool enable = imx233_pwm_is_enabled(channel); if(enable) imx233_pwm_enable(channel, false); /* setup pin */ imx233_pinctrl_setup_vpin(VPIN_PWM(channel), "pwm", PINCTRL_DRIVE_4mA, false); /* watch the order ! active THEN period * NOTE: the register value is period-1 */ HW_PWM_ACTIVEn(channel) = BF_OR2(PWM_ACTIVEn, ACTIVE(active), INACTIVE(inactive)); HW_PWM_PERIODn(channel) = BF_OR4(PWM_PERIODn, PERIOD(period - 1), ACTIVE_STATE(active_state), INACTIVE_STATE(inactive_state), CDIV(cdiv)); /* restore */ imx233_pwm_enable(channel, enable); }
enum imx233_i2c_error_t imx233_i2c_add(bool start, bool transmit, void *buffer, unsigned size, bool stop) { if(i2c_nr_stages == I2C_NR_STAGES) return I2C_ERROR; /* align buffer end on cache boundary */ uint32_t start_off = CACHEALIGN_UP(i2c_buffer_end); uint32_t end_off = start_off + size; if(end_off > I2C_BUFFER_SIZE) { panicf("die"); return I2C_BUFFER_FULL; } i2c_buffer_end = end_off; if(transmit) { /* copy data to buffer */ memcpy(i2c_buffer + start_off, buffer, size); } else { /* record pointers for finalization */ i2c_stage[i2c_nr_stages].src = i2c_buffer + start_off; i2c_stage[i2c_nr_stages].dst = buffer; } if(i2c_nr_stages > 0) { i2c_stage[i2c_nr_stages - 1].dma.next = &i2c_stage[i2c_nr_stages].dma; i2c_stage[i2c_nr_stages - 1].dma.cmd |= BM_APB_CHx_CMD_CHAIN; if(!start) i2c_stage[i2c_nr_stages - 1].ctrl0 |= BM_I2C_CTRL0_RETAIN_CLOCK; } i2c_stage[i2c_nr_stages].dma.buffer = i2c_buffer + start_off; i2c_stage[i2c_nr_stages].dma.next = NULL; i2c_stage[i2c_nr_stages].dma.cmd = BF_OR4(APB_CHx_CMD, COMMAND(transmit ? BV_APB_CHx_CMD_COMMAND__READ : BV_APB_CHx_CMD_COMMAND__WRITE), WAIT4ENDCMD(1), CMDWORDS(1), XFER_COUNT(size)); /* assume that any read is final (send nak on last) */ i2c_stage[i2c_nr_stages].ctrl0 = BF_OR6(I2C_CTRL0, XFER_COUNT(size), DIRECTION(transmit), SEND_NAK_ON_LAST(!transmit), PRE_SEND_START(start), POST_SEND_STOP(stop), MASTER_MODE(1)); i2c_nr_stages++; return I2C_SUCCESS; }
HW_HAVE_8_([HW_FREQ_8] = { 0x1, 0x3, 0x17, 0xe00 } ,) HW_HAVE_11_([HW_FREQ_11] = { 0x1, 0x3, 0x11, 0x37 } ,) HW_HAVE_12_([HW_FREQ_12] = { 0x1, 0x3, 0xf, 0x13ff },) HW_HAVE_16_([HW_FREQ_16] = { 0x1, 0x1, 0x17, 0xe00},) HW_HAVE_22_([HW_FREQ_22] = { 0x1, 0x1, 0x11, 0x37 },) HW_HAVE_24_([HW_FREQ_24] = { 0x1, 0x1, 0xf, 0x13ff },) HW_HAVE_32_([HW_FREQ_32] = { 0x1, 0x0, 0x17, 0xe00},) HW_HAVE_44_([HW_FREQ_44] = { 0x1, 0x0, 0x11, 0x37 },) HW_HAVE_48_([HW_FREQ_48] = { 0x1, 0x0, 0xf, 0x13ff },) HW_HAVE_64_([HW_FREQ_64] = { 0x2, 0x0, 0x17, 0xe00},) HW_HAVE_88_([HW_FREQ_88] = { 0x2, 0x0, 0x11, 0x37 },) HW_HAVE_96_([HW_FREQ_96] = { 0x2, 0x0, 0xf, 0x13ff },) }; HW_AUDIOOUT_DACSRR = BF_OR4(AUDIOOUT_DACSRR, SRC_FRAC(dacssr[fsel].src_frac), SRC_INT(dacssr[fsel].src_int), SRC_HOLD(dacssr[fsel].src_hold), BASEMULT(dacssr[fsel].base_mult)); #if 0 /* Select base_mult and src_hold depending on the audio range: * 0 < f <= 12000 --> base_mult = 1, src_hold = 3 (div by 4) * 12000 < f <= 24000 --> base_mult = 1, src_hold = 1 (div by 2) * 24000 < f <= 48000 --> base_mult = 1, src_hold = 0 (div by 1) * 48000 < f <= 96000 --> base_mult = 2, src_hold = 0 (mul by 2) * 96000 < f <= ..... --> base_mult = 4, src_hold = 0 (mul by 4) */ int base_mult = 1; int src_hold = 0; if(f > 96000) base_mult = 4; else if(f > 48000) base_mult = 2;
void imx233_lcdif_set_timings(unsigned data_setup, unsigned data_hold, unsigned cmd_setup, unsigned cmd_hold) { HW_LCDIF_TIMING = BF_OR4(LCDIF_TIMING, DATA_SETUP(data_setup), DATA_HOLD(data_hold), CMD_SETUP(cmd_setup), CMD_HOLD(cmd_hold)); }