| #include "asrfb.h" |
| #include "spi_drv.h" |
| |
| static struct s_spi_ctx g_spi_ctx; |
| |
| static void spi_set_dcx(u8 flag) |
| { |
| #ifdef CONFIG_FB_ASR_SPI_DCX_GPIO |
| gpio_direction_output(CONFIG_FB_ASR_SPI_DCX_GPIO, flag); |
| #else |
| lcd_write_bits(LCD_SPI_CTRL, flag, MASK1, 31); |
| #endif |
| } |
| |
| static int set_spi_clk(unsigned int src_clk, unsigned int spi_clk) |
| { |
| unsigned int dividor; |
| |
| dividor = src_clk / spi_clk; |
| if (dividor > 0xFF) { |
| printf("%s: src_clk/clk can't large than 0xFF\n", __func__); |
| return -1; |
| } |
| lcd_write_bits(LCD_SPI_CTRL, dividor, MASK8, 24); |
| return 0; |
| } |
| |
| static void set_spi_path(struct s_spi_ctx *spi_ctx, unsigned int path) |
| { |
| if (path == SPI_PATH_IMAGE) |
| lcd_set_bits(LCD_MISC_CNTL, BIT_0); |
| else |
| lcd_clear_bits(LCD_MISC_CNTL, BIT_0); |
| spi_ctx->cur_path = path; |
| } |
| |
| static void trigger_spi(void) |
| { |
| /* polling mode */ |
| u32 reg = 0; |
| int count = 0; |
| lcd_set_bits(LCD_SPI_CTRL, BIT_0); |
| reg = lcd_read(SPU_IRQ_ISR_RAW); |
| while (!(reg & IRQ_SPI_DONE)) { |
| count++; |
| if (count >= 100) { |
| printf("%s: wait to finish timeout!\n", __func__); |
| break; |
| } |
| udelay(10); |
| reg = lcd_read(SPU_IRQ_ISR_RAW); |
| } |
| lcd_write(SPU_IRQ_ISR_RAW, ~IRQ_SPI_DONE); |
| lcd_clear_bits(LCD_SPI_CTRL, BIT_0); |
| } |
| |
| struct s_spi_ctx *spi_init(unsigned int sclk, struct spi_info *info) |
| { |
| int ret; |
| int dividor; |
| struct s_spi_ctx *spi_ctx = &g_spi_ctx; |
| int reg = 0; |
| |
| if ((info->format != SPI_FORMAT_RGB565) && |
| (info->format != SPI_FORMAT_RGB666) && (info->data_lane_num == 1)) { |
| printf("%s: Format error!\n", __func__); |
| return NULL; |
| } |
| |
| dividor = sclk / info->timing->rclk; |
| if ((dividor > 0xFF) || (dividor < 2)) { |
| printf("%s: Invalid read timing!(s:%d, r:%d)\n", |
| __func__, sclk, info->timing->rclk); |
| return NULL; |
| } |
| |
| dividor = sclk / info->timing->wclk; |
| if ((dividor > 0xFF) || (dividor < 2)) { |
| printf("%s: Invalid write timing!(s:%d, w:%d)\n", |
| __func__, sclk, info->timing->wclk); |
| return NULL; |
| } |
| |
| memset(spi_ctx, 0, sizeof(struct s_spi_ctx)); |
| spi_ctx->base_addr = CONFIG_FB_ASR_REG_BASE; |
| spi_ctx->sclk = sclk; |
| memcpy(&spi_ctx->info, info, sizeof(struct spi_info)); |
| |
| if (info->interface_id == 1) |
| reg |= BIT_1; |
| |
| if (info->device_id == 1) |
| reg |= BIT_2; |
| |
| if (info->endian == SPI_ENDIAN_LSB) |
| reg |= BIT_5 | BIT_4; |
| |
| if (info->sample_edge == SPI_EDGE_FALLING) |
| reg |= BIT_7; |
| |
| /* enable spi */ |
| reg |= BIT_3; |
| lcd_write(LCD_SPI_CTRL, reg); |
| |
| reg = 0; |
| if (info->line_num == 3) |
| reg |= BIT_3; |
| |
| if (info->data_lane_num == 2) |
| reg |= BIT_2; |
| |
| reg |= BIT_1; /* should be set, otherwith, color will error */ |
| |
| if (info->format == SPI_FORMAT_RGB666_2_3) |
| reg |= BIT_5; |
| else if (info->format == SPI_FORMAT_RGB888_2_3) |
| reg |= BIT_4; |
| lcd_write(LCD_MISC_CNTL, reg); |
| |
| if (info->line_num == 4) |
| spi_set_dcx(0); |
| |
| /* spi need set bit13, otherwith will lost some data */ |
| /* spi need set bit6, otherwith color will error */ |
| /* spi need open mcu, otherwidth can't send image data */ |
| //lcd_set_bits(SMPN_CTRL, BIT_13 | BIT_6 | BIT_0); |
| lcd_set_bits(LCD_SMPN_CTRL, BIT_13 | BIT_0); |
| |
| switch (info->format) { |
| case SPI_FORMAT_RGB565: |
| if (info->data_lane_num == 2) { |
| lcd_write_bits(LCD_SMPN_CTRL, 5, MASK4, 8); |
| } |
| else { |
| lcd_write_bits(LCD_SMPN_CTRL, 2, MASK4, 8); |
| } |
| break; |
| case SPI_FORMAT_RGB666: |
| if (info->data_lane_num == 2) { |
| lcd_write_bits(LCD_SMPN_CTRL, 4, MASK4, 8); |
| } |
| else { |
| lcd_write_bits(LCD_SMPN_CTRL, 1, MASK4, 8); |
| } |
| break; |
| case SPI_FORMAT_RGB666_2_3: |
| lcd_write_bits(LCD_SMPN_CTRL, 6, MASK4, 8); |
| break; |
| case SPI_FORMAT_RGB888: |
| lcd_write_bits(LCD_SMPN_CTRL, 3, MASK4, 8); |
| break; |
| case SPI_FORMAT_RGB888_2_3: |
| lcd_write_bits(LCD_SMPN_CTRL, 0, MASK4, 8); |
| break; |
| default: |
| printf("%s: Invalid format!\n", __func__); |
| return NULL; |
| } |
| |
| /* set write clk as default */ |
| ret = set_spi_clk(sclk, info->timing->wclk); |
| if (-1 == ret) { |
| printf("%s: set spi clk error!\n", __func__); |
| return NULL; |
| } |
| |
| /*set register path as default*/ |
| set_spi_path(spi_ctx, SPI_PATH_REGISTER); |
| spi_ctx->status = SPI_STATUS_INIT; |
| return spi_ctx; |
| } |
| |
| int spi_set_cs(struct s_spi_ctx *spi_ctx, unsigned int enable) |
| { |
| if (spi_ctx->info.force_cs == 0) { |
| printf("%s: Not force CS mode, can't set CS!\n", __func__); |
| return -1; |
| } |
| |
| if (enable) |
| lcd_set_bits(LCD_SPI_CTRL, BIT_6); |
| else |
| lcd_clear_bits(LCD_SPI_CTRL, BIT_6); |
| spi_ctx->cur_cs = enable; |
| return 0; |
| } |
| |
| int spi_write_cmd(struct s_spi_ctx *spi_ctx, unsigned int cmd, unsigned int bits) |
| { |
| unsigned int wcmd, wbits; |
| |
| if (spi_ctx->info.force_cs == 1 && spi_ctx->cur_cs == 0) { |
| printf("%s: Invalid CS status\n", __func__); |
| return -1; |
| } |
| |
| if (spi_ctx->cur_path == SPI_PATH_IMAGE) |
| set_spi_path(spi_ctx, SPI_PATH_REGISTER); |
| |
| if (3 == spi_ctx->info.line_num) { /* 3 line mode */ |
| if (bits == 32) { |
| printf("%s: too many write bits for 3 line mode!\n", __func__); |
| return -1; |
| } |
| wbits = bits + 1; |
| wcmd = cmd; /* 0 - command, 1 - data */ |
| } |
| else { /* 4 line mode */ |
| wbits = bits; |
| wcmd = cmd; |
| } |
| |
| if (4 == spi_ctx->info.line_num) |
| spi_set_dcx(0); |
| |
| lcd_write(LCD_SPI_TXDATA, wcmd); |
| lcd_write_bits(LCD_SPI_CTRL, wbits - 1, MASK16, 8); |
| trigger_spi(); |
| return 0; |
| } |
| |
| int spi_write_data(struct s_spi_ctx *spi_ctx, unsigned int data, unsigned int bits) |
| { |
| unsigned int wdata, wbits; |
| |
| if (spi_ctx->info.force_cs == 1 && spi_ctx->cur_cs == 0) { |
| printf("%s: Invalid CS status\n", __func__); |
| return -1; |
| } |
| |
| if (spi_ctx->cur_path == SPI_PATH_IMAGE) |
| set_spi_path(spi_ctx, SPI_PATH_REGISTER); |
| |
| if (3 == spi_ctx->info.line_num) { |
| if (bits == 32) { |
| printf("%s: too many write bits for 3 line mode!\n", __func__); |
| return -1; |
| } |
| wbits = bits + 1; |
| wdata = (1 << bits) | data; /* 0 - command, 1 - data */ |
| } |
| else { /*4 line mode*/ |
| wbits = bits; |
| wdata = data; |
| } |
| |
| if (4 == spi_ctx->info.line_num) |
| spi_set_dcx(1); |
| |
| lcd_write(LCD_SPI_TXDATA, wdata); |
| lcd_write_bits(LCD_SPI_CTRL, wbits - 1, MASK16, 8); |
| trigger_spi(); |
| return 0; |
| } |
| |
| int spi_read_data(struct s_spi_ctx *spi_ctx, unsigned int cmd, unsigned int cmd_bits, |
| unsigned int *data, unsigned int data_bits) |
| { |
| unsigned int wcmd, wbits, rbits; |
| int ret; |
| |
| if (spi_ctx->info.force_cs == 1 && spi_ctx->cur_cs == 0) { |
| printf("%s: Invalid CS status\n", __func__); |
| return -1; |
| } |
| |
| ret = set_spi_clk(spi_ctx->sclk, spi_ctx->info.timing->rclk); |
| if (-1 == ret) { |
| printf("%s: set spi clk error!\n", __func__); |
| return -1; |
| } |
| |
| if (spi_ctx->cur_path == SPI_PATH_IMAGE) |
| set_spi_path(spi_ctx, SPI_PATH_REGISTER); |
| |
| if (data_bits > 8) |
| rbits = data_bits; |
| else |
| rbits = data_bits - 1; |
| |
| if (3 == spi_ctx->info.line_num) { |
| if (cmd == 32) { |
| printf("%s: too many write bits for 3 line mode!\n", __func__); |
| return -1; |
| } |
| wbits = cmd_bits; |
| wcmd = cmd; /* 0 -command, 1-data */ |
| } |
| else { /* 4 line mode */ |
| wbits = cmd_bits - 1; |
| wcmd = cmd; |
| } |
| |
| if (4 == spi_ctx->info.line_num) |
| spi_set_dcx(0); |
| |
| lcd_write(LCD_SPI_TXDATA, wcmd); |
| lcd_write_bits(LCD_SPI_CTRL, wbits, MASK8, 8); |
| lcd_write_bits(LCD_SPI_CTRL, rbits, MASK8, 16); |
| trigger_spi(); |
| |
| *data = lcd_read(LCD_SPI_RXDATA); |
| /* set write clk as default */ |
| ret = set_spi_clk(spi_ctx->sclk, spi_ctx->info.timing->wclk); |
| if (-1 == ret) { |
| printf("%s: spi_read_data, set spi clk error!\n", __func__); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int spi_before_refresh(struct s_spi_ctx *spi_ctx) |
| { |
| if (spi_ctx->cur_path != SPI_PATH_IMAGE) |
| set_spi_path(spi_ctx, SPI_PATH_IMAGE); |
| |
| if (2 == spi_ctx->info.data_lane_num) { |
| /* 2 data lane */ |
| switch (spi_ctx->info.format) { |
| case SPI_FORMAT_RGB565: |
| lcd_write_bits(LCD_SPI_CTRL, 15, MASK8, 8); |
| break; |
| case SPI_FORMAT_RGB666: |
| lcd_write_bits(LCD_SPI_CTRL, 17, MASK8, 8); |
| break; |
| case SPI_FORMAT_RGB666_2_3: |
| lcd_write_bits(LCD_SPI_CTRL, 11, MASK8, 8); |
| break; |
| case SPI_FORMAT_RGB888: |
| lcd_write_bits(LCD_SPI_CTRL, 23, MASK8, 8); |
| break; |
| case SPI_FORMAT_RGB888_2_3: |
| lcd_write_bits(LCD_SPI_CTRL, 15, MASK8, 8); |
| break; |
| } |
| } |
| else{ |
| /* 1 data lane */ |
| lcd_write_bits(LCD_SPI_CTRL, 7, MASK8, 8); |
| } |
| |
| if (4 == spi_ctx->info.line_num) |
| spi_set_dcx(1); |
| |
| return 0; |
| } |
| |
| int spi_after_refresh(struct s_spi_ctx *spi_ctx) |
| { |
| if (spi_ctx->cur_path != SPI_PATH_REGISTER) |
| set_spi_path(spi_ctx, SPI_PATH_REGISTER); |
| return 0; |
| } |
| |
| void spi_uninit(struct s_spi_ctx *spi_ctx) |
| { |
| lcd_write(LCD_SPI_CTRL, 0); |
| lcd_write(LCD_MISC_CNTL, 0); |
| spi_ctx->status = SPI_STATUS_UNINIT; |
| } |