diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/SOURCES | 1 | ||||
-rw-r--r-- | firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c | 283 | ||||
-rw-r--r-- | firmware/target/arm/imx233/sansa-fuzeplus/lcd-target.h | 6 |
3 files changed, 259 insertions, 31 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES index 87a2df4b1f..18e76a895b 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -1322,7 +1322,6 @@ target/arm/as3525/lcd-as-e200v2-fuze-fuzev2.S | |||
1322 | #endif /* SANSA_FUZEV2 */ | 1322 | #endif /* SANSA_FUZEV2 */ |
1323 | 1323 | ||
1324 | #ifdef SANSA_FUZEPLUS | 1324 | #ifdef SANSA_FUZEPLUS |
1325 | drivers/lcd-memframe.c | ||
1326 | drivers/synaptics-rmi.c | 1325 | drivers/synaptics-rmi.c |
1327 | drivers/generic_i2c.c | 1326 | drivers/generic_i2c.c |
1328 | target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c | 1327 | target/arm/imx233/sansa-fuzeplus/fmradio-i2c-fuzeplus.c |
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c index 8edc4b7758..c84c29c22d 100644 --- a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c +++ b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-fuzeplus.c | |||
@@ -27,15 +27,14 @@ | |||
27 | #include "lcdif-imx233.h" | 27 | #include "lcdif-imx233.h" |
28 | #include "clkctrl-imx233.h" | 28 | #include "clkctrl-imx233.h" |
29 | #include "pinctrl-imx233.h" | 29 | #include "pinctrl-imx233.h" |
30 | #include "dcp-imx233.h" | ||
30 | #include "logf.h" | 31 | #include "logf.h" |
31 | 32 | ||
32 | extern bool lcd_on; /* lcd-memframe.c */ | 33 | #ifdef HAVE_LCD_ENABLE |
33 | 34 | bool lcd_on; /* framebuffer-imx233.c */ | |
34 | /* Copies a rectangle from one framebuffer to another. Can be used in | 35 | #endif |
35 | single transfer mode with width = num pixels, and height = 1 which | 36 | static unsigned lcd_yuv_options = 0; |
36 | allows a full-width rectangle to be copied more efficiently. */ | 37 | static int lcd_dcp_channel = -1; |
37 | extern void lcd_copy_buffer_rect(fb_data *dst, const fb_data *src, | ||
38 | int width, int height); | ||
39 | 38 | ||
40 | static enum lcd_kind_t | 39 | static enum lcd_kind_t |
41 | { | 40 | { |
@@ -357,6 +356,9 @@ static void lcd_init_seq_9325(void) | |||
357 | 356 | ||
358 | void lcd_init_device(void) | 357 | void lcd_init_device(void) |
359 | { | 358 | { |
359 | lcd_dcp_channel = imx233_dcp_acquire_channel(TIMEOUT_NOBLOCK); | ||
360 | if(lcd_dcp_channel < 0) | ||
361 | panicf("imx233_framebuffer_init: imx233_dcp_acquire_channel failed!"); | ||
360 | setup_lcdif(); | 362 | setup_lcdif(); |
361 | setup_lcdif_clock(); | 363 | setup_lcdif_clock(); |
362 | 364 | ||
@@ -387,6 +389,11 @@ void lcd_init_device(void) | |||
387 | } | 389 | } |
388 | 390 | ||
389 | #ifdef HAVE_LCD_ENABLE | 391 | #ifdef HAVE_LCD_ENABLE |
392 | bool lcd_active(void) | ||
393 | { | ||
394 | return lcd_on; | ||
395 | } | ||
396 | |||
390 | static void lcd_enable_7783(bool enable) | 397 | static void lcd_enable_7783(bool enable) |
391 | { | 398 | { |
392 | if(!enable) | 399 | if(!enable) |
@@ -485,32 +492,260 @@ void lcd_enable(bool enable) | |||
485 | 492 | ||
486 | void lcd_update(void) | 493 | void lcd_update(void) |
487 | { | 494 | { |
488 | #ifdef HAVE_LCD_ENABLE | 495 | lcd_update_rect(0, 0, LCD_WIDTH, LCD_HEIGHT); |
496 | } | ||
497 | |||
498 | void lcd_update_rect(int x, int y, int w, int h) | ||
499 | { | ||
500 | #ifdef HAVE_LCD_ENABLE | ||
489 | if(!lcd_on) | 501 | if(!lcd_on) |
490 | return; | 502 | return; |
491 | #endif | 503 | #endif |
492 | imx233_lcdif_wait_ready(); | 504 | imx233_lcdif_wait_ready(); |
493 | lcd_write_reg(0x50, 0); | 505 | lcd_write_reg(0x50, x); |
494 | lcd_write_reg(0x51, LCD_WIDTH - 1); | 506 | lcd_write_reg(0x51, x + w - 1); |
495 | lcd_write_reg(0x52, 0); | 507 | lcd_write_reg(0x52, y); |
496 | lcd_write_reg(0x53, LCD_HEIGHT - 1); | 508 | lcd_write_reg(0x53, y + h - 1); |
497 | lcd_write_reg(0x20, 0); | 509 | lcd_write_reg(0x20, x); |
498 | lcd_write_reg(0x21, 0); | 510 | lcd_write_reg(0x21, y); |
499 | lcd_write_reg(0x22, 0); | 511 | lcd_write_reg(0x22, 0); |
500 | imx233_lcdif_wait_ready(); | 512 | imx233_lcdif_wait_ready(); |
501 | imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_16_BIT); | 513 | imx233_lcdif_set_word_length(HW_LCDIF_CTRL__WORD_LENGTH_16_BIT); |
502 | imx233_lcdif_set_byte_packing_format(0xf); /* two pixels per 32-bit word */ | 514 | imx233_lcdif_set_byte_packing_format(0xf); /* two pixels per 32-bit word */ |
503 | imx233_lcdif_set_data_format(false, false, false); /* RGB565, don't care, don't care */ | 515 | imx233_lcdif_set_data_format(false, false, false); /* RGB565, don't care, don't care */ |
504 | lcd_copy_buffer_rect((fb_data *)FRAME, &lcd_framebuffer[0][0], | 516 | /* there are two cases here: |
505 | LCD_WIDTH * LCD_HEIGHT, 1); | 517 | * - either width = LCD_WIDTH and we can directly memcopy a part of lcd_framebuffer to FRAME |
506 | imx233_lcdif_dma_send((void *)FRAME_PHYS_ADDR, LCD_WIDTH, LCD_HEIGHT); | 518 | * and send it |
519 | * - either width != LCD_WIDTH and we have to build a contiguous copy of the rectangular area | ||
520 | * into FRAME before sending it (which is slower and doesn't use the hardware) | ||
521 | * In all cases, FRAME just acts as a temporary buffer. | ||
522 | * NOTE It's more interesting to do a copy to FRAME in all cases since in system mode | ||
523 | * the clock runs at 24MHz which provides barely 10MB/s bandwidth compared to >100MB/s | ||
524 | * for memcopy operations | ||
525 | */ | ||
526 | if(w == LCD_WIDTH) | ||
527 | { | ||
528 | imx233_dcp_memcpy_ex(lcd_dcp_channel, false, &lcd_framebuffer[y][x], | ||
529 | (void *)FRAME, h * w * sizeof(fb_data)); | ||
530 | } | ||
531 | else | ||
532 | { | ||
533 | for(int i = 0; i < h; i++) | ||
534 | memcpy((fb_data *)FRAME + i * w, &lcd_framebuffer[y + i][x], w * sizeof(fb_data)); | ||
535 | } | ||
536 | /* WARNING The LCDIF has a limitation on the vertical count ! In 16-bit packed mode | ||
537 | * (which we used, ie 16-bit per pixel, 2 pixels per 32-bit words), the v_count | ||
538 | * field must be a multiple of 2. Furthermore, it seems the lcd controller doesn't | ||
539 | * really like when both w and h are even, probably because the writes to the GRAM | ||
540 | * are done on several words and the controller requires dummy writes. | ||
541 | * The workaround is to always make sure that we send a number of pixels which is | ||
542 | * a multiple of 4 so that both the lcdif and the controller are happy. If any | ||
543 | * of w or h is odd, we will send a copy of the first pixels as dummy writes. We will | ||
544 | * send at most 3 bytes. We then send (w * h + 3) / 4 x 4 bytes. | ||
545 | */ | ||
546 | if(w % 2 == 1 || h % 2 == 1) | ||
547 | { | ||
548 | /* copy three pixel after the last one */ | ||
549 | for(int i = 0; i < 3; i++) | ||
550 | *((fb_data *)FRAME + w * h + i) = *((fb_data *)FRAME + i); | ||
551 | /* WARNING we need to update w and h to reflect the pixel count BUT it | ||
552 | * has no relation to w * h (it can even be 2 * prime). Hopefully, w <= 240 and | ||
553 | * h <= 320 so w * h <= 76800 and (w * h + 3) / 4 <= 38400 which fits into | ||
554 | * a 16-bit integer (horizontal count). */ | ||
555 | h = (w * h + 3) / 4; | ||
556 | w = 4; | ||
557 | } | ||
558 | imx233_lcdif_dma_send((void *)FRAME_PHYS_ADDR, w, h); | ||
507 | } | 559 | } |
508 | 560 | ||
509 | void lcd_update_rect(int x, int y, int width, int height) | 561 | void lcd_yuv_set_options(unsigned options) |
562 | { | ||
563 | lcd_yuv_options = options; | ||
564 | } | ||
565 | |||
566 | #define YFAC (74) | ||
567 | #define RVFAC (101) | ||
568 | #define GUFAC (-24) | ||
569 | #define GVFAC (-51) | ||
570 | #define BUFAC (128) | ||
571 | |||
572 | static inline int clamp(int val, int min, int max) | ||
510 | { | 573 | { |
511 | (void) x; | 574 | if (val < min) |
512 | (void) y; | 575 | val = min; |
513 | (void) width; | 576 | else if (val > max) |
514 | (void) height; | 577 | val = max; |
515 | lcd_update(); | 578 | return val; |
579 | } | ||
580 | |||
581 | void lcd_blit_yuv(unsigned char * const src[3], | ||
582 | int src_x, int src_y, int stride, | ||
583 | int x, int y, int width, int height) | ||
584 | { | ||
585 | const unsigned char *ysrc, *usrc, *vsrc; | ||
586 | int linecounter; | ||
587 | fb_data *dst, *row_end; | ||
588 | long z; | ||
589 | |||
590 | /* width and height must be >= 2 and an even number */ | ||
591 | width &= ~1; | ||
592 | linecounter = height >> 1; | ||
593 | |||
594 | #if LCD_WIDTH >= LCD_HEIGHT | ||
595 | dst = &lcd_framebuffer[y][x]; | ||
596 | row_end = dst + width; | ||
597 | #else | ||
598 | dst = &lcd_framebuffer[x][LCD_WIDTH - y - 1]; | ||
599 | row_end = dst + LCD_WIDTH * width; | ||
600 | #endif | ||
601 | |||
602 | z = stride * src_y; | ||
603 | ysrc = src[0] + z + src_x; | ||
604 | usrc = src[1] + (z >> 2) + (src_x >> 1); | ||
605 | vsrc = src[2] + (usrc - src[1]); | ||
606 | |||
607 | /* stride => amount to jump from end of last row to start of next */ | ||
608 | stride -= width; | ||
609 | |||
610 | /* upsampling, YUV->RGB conversion and reduction to RGB565 in one go */ | ||
611 | |||
612 | do | ||
613 | { | ||
614 | do | ||
615 | { | ||
616 | int y, cb, cr, rv, guv, bu, r, g, b; | ||
617 | |||
618 | y = YFAC*(*ysrc++ - 16); | ||
619 | cb = *usrc++ - 128; | ||
620 | cr = *vsrc++ - 128; | ||
621 | |||
622 | rv = RVFAC*cr; | ||
623 | guv = GUFAC*cb + GVFAC*cr; | ||
624 | bu = BUFAC*cb; | ||
625 | |||
626 | r = y + rv; | ||
627 | g = y + guv; | ||
628 | b = y + bu; | ||
629 | |||
630 | if ((unsigned)(r | g | b) > 64*256-1) | ||
631 | { | ||
632 | r = clamp(r, 0, 64*256-1); | ||
633 | g = clamp(g, 0, 64*256-1); | ||
634 | b = clamp(b, 0, 64*256-1); | ||
635 | } | ||
636 | |||
637 | *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9); | ||
638 | |||
639 | #if LCD_WIDTH >= LCD_HEIGHT | ||
640 | dst++; | ||
641 | #else | ||
642 | dst += LCD_WIDTH; | ||
643 | #endif | ||
644 | |||
645 | y = YFAC*(*ysrc++ - 16); | ||
646 | r = y + rv; | ||
647 | g = y + guv; | ||
648 | b = y + bu; | ||
649 | |||
650 | if ((unsigned)(r | g | b) > 64*256-1) | ||
651 | { | ||
652 | r = clamp(r, 0, 64*256-1); | ||
653 | g = clamp(g, 0, 64*256-1); | ||
654 | b = clamp(b, 0, 64*256-1); | ||
655 | } | ||
656 | |||
657 | *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9); | ||
658 | |||
659 | #if LCD_WIDTH >= LCD_HEIGHT | ||
660 | dst++; | ||
661 | #else | ||
662 | dst += LCD_WIDTH; | ||
663 | #endif | ||
664 | } | ||
665 | while (dst < row_end); | ||
666 | |||
667 | ysrc += stride; | ||
668 | usrc -= width >> 1; | ||
669 | vsrc -= width >> 1; | ||
670 | |||
671 | #if LCD_WIDTH >= LCD_HEIGHT | ||
672 | row_end += LCD_WIDTH; | ||
673 | dst += LCD_WIDTH - width; | ||
674 | #else | ||
675 | row_end -= 1; | ||
676 | dst -= LCD_WIDTH*width + 1; | ||
677 | #endif | ||
678 | |||
679 | do | ||
680 | { | ||
681 | int y, cb, cr, rv, guv, bu, r, g, b; | ||
682 | |||
683 | y = YFAC*(*ysrc++ - 16); | ||
684 | cb = *usrc++ - 128; | ||
685 | cr = *vsrc++ - 128; | ||
686 | |||
687 | rv = RVFAC*cr; | ||
688 | guv = GUFAC*cb + GVFAC*cr; | ||
689 | bu = BUFAC*cb; | ||
690 | |||
691 | r = y + rv; | ||
692 | g = y + guv; | ||
693 | b = y + bu; | ||
694 | |||
695 | if ((unsigned)(r | g | b) > 64*256-1) | ||
696 | { | ||
697 | r = clamp(r, 0, 64*256-1); | ||
698 | g = clamp(g, 0, 64*256-1); | ||
699 | b = clamp(b, 0, 64*256-1); | ||
700 | } | ||
701 | |||
702 | *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9); | ||
703 | |||
704 | #if LCD_WIDTH >= LCD_HEIGHT | ||
705 | dst++; | ||
706 | #else | ||
707 | dst += LCD_WIDTH; | ||
708 | #endif | ||
709 | |||
710 | y = YFAC*(*ysrc++ - 16); | ||
711 | r = y + rv; | ||
712 | g = y + guv; | ||
713 | b = y + bu; | ||
714 | |||
715 | if ((unsigned)(r | g | b) > 64*256-1) | ||
716 | { | ||
717 | r = clamp(r, 0, 64*256-1); | ||
718 | g = clamp(g, 0, 64*256-1); | ||
719 | b = clamp(b, 0, 64*256-1); | ||
720 | } | ||
721 | |||
722 | *dst = LCD_RGBPACK_LCD(r >> 9, g >> 8, b >> 9); | ||
723 | |||
724 | #if LCD_WIDTH >= LCD_HEIGHT | ||
725 | dst++; | ||
726 | #else | ||
727 | dst += LCD_WIDTH; | ||
728 | #endif | ||
729 | } | ||
730 | while (dst < row_end); | ||
731 | |||
732 | ysrc += stride; | ||
733 | usrc += stride >> 1; | ||
734 | vsrc += stride >> 1; | ||
735 | |||
736 | #if LCD_WIDTH >= LCD_HEIGHT | ||
737 | row_end += LCD_WIDTH; | ||
738 | dst += LCD_WIDTH - width; | ||
739 | #else | ||
740 | row_end -= 1; | ||
741 | dst -= LCD_WIDTH*width + 1; | ||
742 | #endif | ||
743 | } | ||
744 | while (--linecounter > 0); | ||
745 | |||
746 | #if LCD_WIDTH >= LCD_HEIGHT | ||
747 | lcd_update_rect(x, y, width, height); | ||
748 | #else | ||
749 | lcd_update_rect(LCD_WIDTH - y - height, x, height, width); | ||
750 | #endif | ||
516 | } | 751 | } |
diff --git a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-target.h b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-target.h index 89959244a4..5c1ecdd406 100644 --- a/firmware/target/arm/imx233/sansa-fuzeplus/lcd-target.h +++ b/firmware/target/arm/imx233/sansa-fuzeplus/lcd-target.h | |||
@@ -21,10 +21,4 @@ | |||
21 | #ifndef LCD_TARGET_H | 21 | #ifndef LCD_TARGET_H |
22 | #define LCD_TARGET_H | 22 | #define LCD_TARGET_H |
23 | 23 | ||
24 | #define LCD_FRAMEBUF_ADDR(col, row) ((fb_data *)lcd_framebuffer + (row)*LCD_WIDTH + (col)) | ||
25 | |||
26 | /* Not really optimized, but are unusual */ | ||
27 | #define LCD_OPTIMIZED_UPDATE | ||
28 | #define LCD_OPTIMIZED_UPDATE_RECT | ||
29 | |||
30 | #endif /* LCD_TARGET_H */ | 24 | #endif /* LCD_TARGET_H */ |