Files

276 lines
6.5 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2024 Amlogic, Inc. All rights reserved.
*/
/*-----------------------------------------------------------------------
* This helps initialize the SPI panel in U-Boot and display the logo.
* It is applicable to drivers for ST7789V, ST7796S, and others.
*/
#include <common.h>
#include <command.h>
#include <dm.h>
#include <errno.h>
#include <spi.h>
#include <asm-generic/gpio.h>
#include <lcd.h>
#include <mipi_display.h>
#define ONLY_CMD 0
#define NOT_ONLY_CMD 1
#define DATA 2
#define SPI_LCD_DEBUG 0
#ifndef CONFIG_DEFAULT_SPI_BUS
#define CONFIG_DEFAULT_SPI_BUS 2
#endif
#ifndef CONFIG_DEFAULT_SPI_MODE
#define CONFIG_DEFAULT_SPI_MODE SPI_MODE_0
#endif
#ifndef NUMARGS
#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__}) / sizeof(int))
#endif
#ifndef write_reg
#define write_reg(...) \
(fb_write_register(NUMARGS(__VA_ARGS__), __VA_ARGS__))
#endif
/*
* The maximum number of transfer commands.
* Example usage: write_reg(par, 0xd0, 0xa4, 0xa1); // In this example, the count is 3.
*/
const int MAX_CMD_COUNT = 30;
vidinfo_t panel_info = {
.vl_col = CONFIG_AML_SPI_LCD_COL,
.vl_row = CONFIG_AML_SPI_LCD_ROW,
.vl_bpix = LCD_BPP,
};
int osd_enabled = 1;
struct spi_slave *slave;
u8 *init_buf;
u8 *logo_buf;
#ifdef CONFIG_SPLASH_SCREEN
int splash_screen_prepare(void)
{
env_set("logoPart", CONFIG_AML_SPI_LCD_LOGO_PART);
env_set("logoPath", CONFIG_AML_SPI_LCD_LOGO_PATH);
env_set_hex("logoLoadAddr", (ulong)CONFIG_AML_SPI_LCD_LOGO_LOAD_ADDR);
env_set("logoLoadCmd", "rdext4pic ${logoPart} ${logoLoadAddr} ${logoPath}");
return run_command("run logoLoadCmd", 0);
}
#endif
static int gpio_option(const char *gpiopin, int dir, bool enable)
{
int ret;
struct gpio_desc gpio_option_desc;
ret = dm_gpio_lookup_name(gpiopin, &gpio_option_desc);
if (ret) {
printf("[LCD]%s: not found\n", gpiopin);
return ret;
}
ret = dm_gpio_request(&gpio_option_desc, gpiopin);
if (ret && ret != -EBUSY) {
printf("[LCD]GPIO: requesting pin %s failed\n", gpiopin);
return ret;
}
ret = dm_gpio_set_dir_flags(&gpio_option_desc, dir);
if (ret) {
printf("[LCD]Set direction failed\n");
return ret;
}
dm_gpio_set_value(&gpio_option_desc, enable ? 1 : 0);
return 0;
}
static int spi_xfer_write(u8 *buf, size_t len, int trans_flag)
{
int ret = 0;
if (trans_flag == ONLY_CMD) {
gpio_option(CONFIG_AML_SPI_LCD_DC_GPIO, GPIOD_IS_OUT, 0);
ret = spi_xfer(slave, 8, &buf[0], NULL, SPI_XFER_ONCE);
#if SPI_LCD_DEBUG
printf("[LCD]Cmd is: 0x%02x\n", buf[0]);
#endif
} else if (trans_flag == NOT_ONLY_CMD) {
gpio_option(CONFIG_AML_SPI_LCD_DC_GPIO, GPIOD_IS_OUT, 0);
ret = spi_xfer(slave, 8, &buf[0], NULL, SPI_XFER_BEGIN);
#if SPI_LCD_DEBUG
printf("[LCD]Cmd is: 0x%02x\n", buf[0]);
#endif
} else if (trans_flag == DATA) {
gpio_option(CONFIG_AML_SPI_LCD_DC_GPIO, GPIOD_IS_OUT, 1);
ret = spi_xfer(slave, (len - 1) * 8, &buf[0], NULL, SPI_XFER_BEGIN);
ret = spi_xfer(slave, 8, &buf[len - 1], NULL, SPI_XFER_END);
#if SPI_LCD_DEBUG
for (int i = 0; i < len; i++)
printf("%02x ", buf[i]);
#endif
} else {
printf("[LCD]%s: Trans flag is invalid\n", __func__);
}
return ret;
}
void fb_write_register(int len, ...)
{
va_list args;
int i;
va_start(args, len);
#if SPI_LCD_DEBUG
printf("[LCD]The number of transfer commands: %d\n", len);
#endif
if (len == 1) {
/* Only write command */
init_buf[0] = va_arg(args, unsigned int);
/* Need keep DC low for all command bytes to transfer */
spi_xfer_write(init_buf, 1, ONLY_CMD);
} else if (len > 1) {
/* Write command and data */
for (i = 0; i < len; i++)
init_buf[i] = va_arg(args, unsigned int);
/* Need keep DC low for all command bytes to transfer */
spi_xfer_write(init_buf, 1, NOT_ONLY_CMD);
/* Need keep DC high for all data bytes to transfer */
spi_xfer_write(init_buf + 1, len - 1, DATA);
}
va_end(args);
}
static void fb_set_addr_win(int xs, int ys, int xe, int ye)
{
write_reg(MIPI_DCS_SET_COLUMN_ADDRESS,
(xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
write_reg(MIPI_DCS_SET_PAGE_ADDRESS,
(ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
write_reg(MIPI_DCS_WRITE_MEMORY_START);
}
static int fb_spi_init(unsigned int bus, unsigned int cs,
unsigned int speed, unsigned int mode)
{
int ret = 0;
int wordlen = 8;
char name[30], *str;
struct udevice *dev;
snprintf(name, sizeof(name), "spi_panel_%d:%d", bus, cs);
str = strdup(name);
if (!str)
return -ENOMEM;
ret = spi_get_bus_and_cs(bus, cs, speed, mode, "spi_generic_drv",
str, &dev, &slave);
if (ret)
return ret;
slave->wordlen = wordlen;
return 0;
}
static int fb_trans_init(void)
{
int ret = 0;
init_buf = malloc(MAX_CMD_COUNT * 8);
ret = spi_claim_bus(slave);
if (ret) {
spi_release_bus(slave);
return ret;
}
return 0;
}
static void fb_trans_release(void)
{
free(init_buf);
spi_release_bus(slave);
}
void fb_lcd_sync(void)
{
int ret = 0;
ret = fb_trans_init();
if (ret) {
printf("[LCD]Error: fb initialization failed\n");
return;
}
fb_set_addr_win(0, 0, CONFIG_AML_SPI_LCD_COL - 1, CONFIG_AML_SPI_LCD_ROW - 1);
ret = spi_xfer_write(logo_buf,
CONFIG_AML_SPI_LCD_COL * CONFIG_AML_SPI_LCD_ROW * 2, DATA);
if (ret)
printf("[LCD]Error: Failed to send logo data via SPI\n");
fb_trans_release();
}
extern void fb_display_init(void);
void lcd_ctrl_init(void *lcdbase)
{
int ret = 0;
#ifdef CONFIG_SPLASH_SCREEN
char addr_str[15];
sprintf(addr_str, "0x%x", CONFIG_AML_SPI_LCD_LOGO_LOAD_ADDR);
env_set("splashimage", addr_str);
#endif
logo_buf = (u8 *)lcdbase;
/* Reset */
gpio_option(CONFIG_AML_SPI_LCD_RESET_GPIO, GPIOD_IS_OUT, 1);
udelay(10);
gpio_option(CONFIG_AML_SPI_LCD_RESET_GPIO, GPIOD_IS_OUT, 0);
udelay(10);
gpio_option(CONFIG_AML_SPI_LCD_RESET_GPIO, GPIOD_IS_OUT, 1);
udelay(10);
/* SPI init */
ret = fb_spi_init(CONFIG_DEFAULT_SPI_BUS, CONFIG_DEFAULT_SPI_MODE,
CONFIG_AML_SPI_LCD_MAX_CLOCK, SPI_MODE_0);
if (ret) {
printf("[LCD]Error: fb SPI initialization failed\n");
return;
}
ret = fb_trans_init();
if (ret) {
printf("[LCD]Error: fb initialization failed\n");
return;
}
/* LCD init */
fb_display_init();
fb_trans_release();
}
extern void fb_display_on(void);
void lcd_enable(void)
{
int ret = 0;
ret = fb_trans_init();
if (ret) {
printf("[LCD]Error: fb initialization failed\n");
return;
}
/* Turn on the display */
fb_display_on();
/* Turn on backlight */
gpio_option(CONFIG_AML_SPI_LCD_BACKLIGHT_GPIO, GPIOD_IS_OUT, 1);
fb_trans_release();
}
void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) {}