Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions os/drivers/lcd/lcd_logo.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,28 @@
#define LOGO_XRES 0
#define LOGO_YRES 0
#endif /* CONFIG_LCD_LOGO_52_340 */
#pragma pack(push, 1)
typedef struct {
uint16_t signature;
uint32_t file_size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t data_offset;
} bmp_header_t;

typedef struct {
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bits_per_pixel;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t colors_used;
uint32_t colors_important;
} bmp_info_header_t;
#pragma pack(pop)
#endif /* __DRIVER_LCD_LOGO_H */

175 changes: 175 additions & 0 deletions os/drivers/lcd/mipi_lcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <errno.h>
#include <semaphore.h>
#include <math.h>
#include <stdio.h>

#define LCD_XRES CONFIG_LCD_XRES
#define LCD_YRES CONFIG_LCD_YRES
Expand Down Expand Up @@ -149,6 +150,64 @@ static int send_to_mipi_dsi(struct mipi_lcd_dev_s *priv, struct mipi_dsi_msg* ms
return transfer_status;
}

int count_trailing_zeros(uint32_t mask)
{
if (mask == 0)
return 0;
int count = 0;
while ((mask & 1) == 0) {
mask >>= 1;
count++;
}
return count;
}

int count_set_bits(uint32_t mask)
{
int count = 0;
while (mask) {
count += mask & 1;
mask >>= 1;
}
return count;
}

static uint16_t convert_pixel_to_rgb565(uint16_t bmp_pixel, uint32_t r_mask, uint32_t g_mask, uint32_t b_mask)
{
uint8_t r, g, b;
// Extract red channel
if (r_mask == 0) {
r = 0;
} else {
uint8_t r_bits = count_set_bits(r_mask);
uint8_t r_shift = count_trailing_zeros(r_mask);
r = ((bmp_pixel & r_mask) >> r_shift) << (5 - r_bits); // Scale to 5 bits
if (r_bits > 5)
r >>= (r_bits - 5); // Scale down if source has more bits
}
// Extract green channel
if (g_mask == 0) {
g = 0;
} else {
uint8_t g_bits = count_set_bits(g_mask);
uint8_t g_shift = count_trailing_zeros(g_mask);
g = ((bmp_pixel & g_mask) >> g_shift) << (6 - g_bits); // Scale to 6 bits
if (g_bits > 6)
g >>= (g_bits - 6); // Scale down if source has more bits
}
// Extract blue channel
if (b_mask == 0) {
b = 0;
} else {
uint8_t b_bits = count_set_bits(b_mask);
uint8_t b_shift = count_trailing_zeros(b_mask);
b = ((bmp_pixel & b_mask) >> b_shift) << (5 - b_bits); // Scale to 5 bits
if (b_bits > 5)
b >>= (b_bits - 5); // Scale down if source has more bits
}
return (r << 11) | (g << 5) | b;
}

static int send_cmd(struct mipi_lcd_dev_s *priv, lcm_setting_table_t command)
{
int transfer_status = OK;
Expand Down Expand Up @@ -563,6 +622,121 @@ static int lcd_setcontrast(FAR struct lcd_dev_s *dev, unsigned int contrast)
return OK;
}

static int lcd_render_bmp(FAR struct lcd_dev_s *dev, const char *bmp_filename)
{
int xres = LCD_XRES;
int yres = LCD_YRES;
uint8_t *fullscreen_buffer = NULL;
FAR struct mipi_lcd_dev_s *priv = (FAR struct mipi_lcd_dev_s *)dev;
FILE *bmp_file = fopen(bmp_filename, "rb");
if (!bmp_file) {
lcddbg("Failed to open BMP file");
return ERROR;
}
bmp_header_t header;
bmp_info_header_t info_header;
if (fread(&header, sizeof(bmp_header_t), 1, bmp_file) != 1) {
lcddbg("Failed to read BMP header");
goto errout;
}
if (header.signature != 0x4D42) {
lcddbg("Invalid BMP file: signature mismatch\n");
goto errout;
}
if (fread(&info_header, sizeof(bmp_info_header_t), 1, bmp_file) != 1) {
lcddbg("Failed to read BMP info header");
goto errout;
}

if (info_header.bits_per_pixel != 16) {
lcddbg("Unsupported BMP format: only 16-bit color is supported (found %d-bit)\n", info_header.bits_per_pixel);
goto errout;
}

uint32_t r_mask = 0, g_mask = 0, b_mask = 0;
if (info_header.compression == 3) { // BI_BITFIELDS
if (fread(&r_mask, sizeof(uint32_t), 1, bmp_file) != 1 ||
fread(&g_mask, sizeof(uint32_t), 1, bmp_file) != 1 ||
fread(&b_mask, sizeof(uint32_t), 1, bmp_file) != 1) {
lcddbg("Failed to read BMP bitmasks");
goto errout;
}
} else if (info_header.compression == 0) { // BI_RGB
r_mask = 0x00007C00; // 0111110000000000 (5 bits for red)
g_mask = 0x000003E0; // 0000001111100000 (5 bits for green)
b_mask = 0x0000001F; // 0000000000011111 (5 bits for blue)
} else {
lcddbg("Unsupported BMP compression: %d. Only BI_RGB (0) and BI_BITFIELDS (3) are supported for 16-bit.\n", info_header.compression);
goto errout;
}

int img_width = info_header.width;
int img_height = info_header.height;
if (img_width > yres || img_height > xres) {
lcddbg("Error: Image too large for screen\n");
goto errout;
}
// For 16-bit, each pixel is 2 bytes. Padding is to the next 4-byte boundary.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you handle when img_width or img_height is larger than LCD width or height.
goto errout for these cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

int padding = (4 - (img_width * 2) % 4) % 4;
#if defined(CONFIG_LCD_SW_ROTATION)
fullscreen_buffer = lcd_buffer[lcd_buffer_index];
lcd_buffer_index = (1 - lcd_buffer_index);
#else
fullscreen_buffer = (uint8_t *)kmm_malloc(xres * yres * 2 + 1);
if (!fullscreen_buffer) {
lcddbg("Failed to allocate memory for fullscreen buffer\n");
goto errout;
}
#endif
memset(fullscreen_buffer, 0, xres * yres * 2);

// Calculate offset to center the rotated image
int x_offset = (xres - img_height) / 2;
int y_offset = (yres - img_width) / 2;
if (x_offset < 0) {
x_offset = 0;
}
if (y_offset < 0) {
y_offset = 0;
}

// BMP stores pixels bottom-to-top. We read row by row.
for (int src_y = 0; src_y < img_height; src_y++) {
int bmp_file_y = img_height - 1 - src_y;
long file_offset_for_row = header.data_offset + (long)bmp_file_y * (img_width * 2 + padding);
if (fseek(bmp_file, file_offset_for_row, SEEK_SET) != 0) {
lcddbg("Failed to seek to BMP row");
goto errout;
}
// Read one row of pixel data (as 16-bit words)
uint16_t temp_row_buffer[img_width];
if (fread(temp_row_buffer, sizeof(uint16_t), img_width, bmp_file) != img_width) {
lcddbg("Failed to read BMP pixel row");
goto errout;
}
for (int src_x = 0; src_x < img_width; src_x++) {
int dest_x_in_rotated = src_y;
int dest_y_in_rotated = (img_width - 1) - src_x;
int target_buffer_index = ((y_offset + dest_y_in_rotated) * xres + (x_offset + dest_x_in_rotated)) * 2;
uint16_t bmp_pixel = temp_row_buffer[src_x];
uint16_t rgb565_pixel = convert_pixel_to_rgb565(bmp_pixel, r_mask, g_mask, b_mask);
fullscreen_buffer[target_buffer_index] = (uint8_t)(rgb565_pixel & 0xFF);
fullscreen_buffer[target_buffer_index + 1] = (uint8_t)((rgb565_pixel >> 8) & 0xFF);
}
}
priv->config->lcd_put_area((u8 *)fullscreen_buffer, 1, 1, CONFIG_LCD_XRES, CONFIG_LCD_YRES);
fclose(bmp_file);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also needed mm_free(fullscreen_buffer); in normal exit case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In normal exit case when SW_ROTATION is disabled
If I free the buffer the LCD will show some random colours

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you're right. I thought lcd_put_area copies the buffer. So it doesn't need buffer any more.

return OK;
errout:
fclose(bmp_file);
#if !defined(CONFIG_LCD_SW_ROTATION)
if (fullscreen_buffer) {
kmm_free(fullscreen_buffer);
}
#endif
return ERROR;
}

FAR void lcd_init_put_image(FAR struct lcd_dev_s *dev)
{
int logo_arr_index = 0;
Expand Down Expand Up @@ -609,6 +783,7 @@ FAR void lcd_init_put_image(FAR struct lcd_dev_s *dev)
priv->config->lcd_put_area((u8 *)lcd_init_fullscreen_image, 1, 1, CONFIG_LCD_XRES, CONFIG_LCD_YRES); // 1, 1 -> Start index of the frame buffer
priv->config->mipi_mode_switch(VIDEO_MODE);
priv->lcdonoff = LCD_ON;
lcd_render_bmp(dev, "/res/kernel/audio/img_logo_samsung.bmp"); // Displaying BMP image on LCD

}

Expand Down