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