1 /*
2  * Copyright (c) 2015 Gurjant Kalsi <me@gurjantkalsi.com>
3  *
4  * Use of this source code is governed by a MIT-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/MIT
7  */
8 
9 #include <lk/err.h>
10 #include <lk/debug.h>
11 #include <lk/trace.h>
12 #include <rand.h>
13 #include <stdlib.h>
14 #include <assert.h>
15 
16 #include <dev/display.h>
17 #include <platform/gpio.h>
18 #include <target/memory_lcd.h>
19 
20 #if defined (LCD_LS013B7DH06)
21 #include <target/display/LS013B7DH06.h>
22 #elif defined (LCD_LS027B7DH01)
23 #include <target/display/LS027B7DH01.h>
24 #elif defined (LCD_LS013B7DH03)
25 #include <target/display/LS013B7DH03.h>
26 #endif
27 
28 #define LOCAL_TRACE 0
29 
30 SPI_HandleTypeDef SpiHandle;
31 
32 #define MLCD_WR 0x01  // LCD Write Command
33 #define MLCD_CM 0x04  // LCD Clear Memory Command
34 #define MLCD_NO 0x00  // LCD No-op command
35 
36 // 5 bytes used as control bytes, MLCD_BYTES_LINE bytes used to data
37 #define MLCD_BUF_SIZE  (MLCD_BYTES_LINE + 5)
38 
39 #define VCOM_HI 0x02
40 #define VCOM_LO 0x00
41 
42 static struct display_framebuffer default_fb;
43 static uint8_t vcom_state;
44 
chip_select(bool s)45 static void chip_select(bool s) {
46     if (s) {
47         gpio_set(GPIO(GPIO_PORT_B, 12), GPIO_PIN_SET);
48     } else {
49         gpio_set(GPIO(GPIO_PORT_B, 12), GPIO_PIN_RESET);
50     }
51 }
52 
lcd_power(bool s)53 static void lcd_power(bool s) {
54     if (s) {
55         gpio_set(GPIO(GPIO_PORT_K, 6), GPIO_PIN_SET);
56     } else {
57         gpio_set(GPIO(GPIO_PORT_K, 6), GPIO_PIN_RESET);
58     }
59 }
60 
mlcd_clear(void)61 static void mlcd_clear(void) {
62 
63     uint8_t clear[2];
64     clear[0] = MLCD_CM;
65     clear[1] = 0;
66 
67     chip_select(true);
68     HAL_SPI_Transmit(&SpiHandle, clear, 2, HAL_MAX_DELAY);
69     chip_select(false);
70 }
71 
72 
memory_lcd_init(void)73 status_t memory_lcd_init(void) {
74     SpiHandle.Instance               = SPI2;
75     SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
76     SpiHandle.Init.Direction         = SPI_DIRECTION_1LINE;
77     SpiHandle.Init.CLKPhase          = SPI_PHASE_1EDGE;
78     SpiHandle.Init.CLKPolarity       = SPI_POLARITY_LOW;
79     SpiHandle.Init.DataSize          = SPI_DATASIZE_8BIT;
80     SpiHandle.Init.FirstBit          = SPI_FIRSTBIT_LSB;
81     SpiHandle.Init.TIMode            = SPI_TIMODE_DISABLE;
82     SpiHandle.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
83     SpiHandle.Init.CRCPolynomial     = 7;
84     SpiHandle.Init.NSS               = SPI_NSS_SOFT;
85     SpiHandle.Init.Mode              = SPI_MODE_MASTER;
86 
87     if (HAL_SPI_Init(&SpiHandle) != HAL_OK) {
88         return ERR_GENERIC;
89     }
90 
91     vcom_state = VCOM_LO;
92 
93     lcd_power(true);
94 
95     mlcd_clear();
96 
97     return NO_ERROR;
98 }
99 
mlcd_flush(uint starty,uint endy)100 static void mlcd_flush(uint starty, uint endy) {
101     display_present(&default_fb.image, starty, endy);
102 }
103 
display_get_framebuffer(struct display_framebuffer * fb)104 status_t display_get_framebuffer(struct display_framebuffer *fb) {
105     DEBUG_ASSERT(fb);
106     if (!default_fb.image.pixels) {
107         switch (MLCD_FORMAT) {
108             // Use closest match format supported by gfx lib
109             case DISPLAY_FORMAT_RGB_111:
110                 default_fb.image.format = IMAGE_FORMAT_RGB_332;
111                 default_fb.image.stride = MLCD_WIDTH;
112                 default_fb.image.rowbytes = MLCD_WIDTH;
113                 break;
114             case DISPLAY_FORMAT_MONO_1:
115                 default_fb.image.format = IMAGE_FORMAT_MONO_8;
116                 default_fb.image.stride = MLCD_WIDTH;
117                 default_fb.image.rowbytes = MLCD_WIDTH;
118                 break;
119             default:
120                 DEBUG_ASSERT(false);
121                 return ERR_NOT_SUPPORTED;
122         }
123         default_fb.image.pixels = malloc(MLCD_HEIGHT *
124                                          default_fb.image.rowbytes);
125         default_fb.image.width = MLCD_WIDTH;
126         default_fb.image.height = MLCD_HEIGHT;
127         default_fb.flush = mlcd_flush;
128         default_fb.format = MLCD_FORMAT;
129     }
130     *fb = default_fb;
131     return NO_ERROR;
132 }
133 
display_present(struct display_image * image,uint starty,uint endy)134 status_t display_present(struct display_image *image, uint starty, uint endy) {
135     DEBUG_ASSERT(image);
136     status_t status = NO_ERROR;
137     chip_select(true);
138 
139     static uint8_t localbuf[MLCD_BUF_SIZE];
140     uint8_t *bufptr = localbuf;
141     uint8_t trailer = 0;
142 
143     // The first line is preceded with a write command.
144     *bufptr++ = MLCD_WR | vcom_state;
145 
146     vcom_state = vcom_state == VCOM_HI ? VCOM_LO : VCOM_HI;
147 
148     // Send the image data.
149     for (uint j = starty; j <= endy; ++j) {
150         *bufptr++ = (j + 1);
151 
152         bufptr += lcd_get_line(image, j, bufptr);
153 
154         // 8 bit trailer per line
155         *bufptr++ = trailer;
156         if (j == endy) {
157             // 16 bit trailer on the last line
158             *bufptr++ = trailer;
159         }
160 
161         if (HAL_SPI_Transmit(&SpiHandle, localbuf, bufptr - localbuf,
162                              HAL_MAX_DELAY) != HAL_OK) {
163             status = ERR_GENERIC;
164             goto finish;
165         }
166 
167         bufptr = localbuf;
168     }
169 
170 finish:
171     chip_select(false);
172 
173     return status;
174 }
175 
display_get_info(struct display_info * info)176 status_t display_get_info(struct display_info *info) {
177     DEBUG_ASSERT(info);
178     LTRACEF("display_info %p\n", info);
179 
180     info->format = MLCD_FORMAT;
181     info->width = MLCD_WIDTH;
182     info->height = MLCD_HEIGHT;
183 
184     return NO_ERROR;
185 }
186